refactor: remove useless folders

This commit is contained in:
2025-03-04 01:03:39 +08:00
parent 2844e1fc32
commit 408d211f27
405 changed files with 9988 additions and 28082 deletions

15
apps/webui/.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
# Local
.DS_Store
*.local
*.log*
# Dist
node_modules
dist/
# IDE
.vscode/*
!.vscode/extensions.json
.idea
.env.local
.env.*.lcoal

29
apps/webui/README.md Normal file
View File

@@ -0,0 +1,29 @@
# Rsbuild project
## Setup
Install the dependencies:
```bash
pnpm install
```
## Get started
Start the dev server:
```bash
pnpm dev
```
Build the app for production:
```bash
pnpm build
```
Preview the production build locally:
```bash
pnpm preview
```

14
apps/webui/index.html Normal file
View File

@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

31
apps/webui/package.json Normal file
View File

@@ -0,0 +1,31 @@
{
"name": "webui",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "rsbuild build",
"dev": "rsbuild dev",
"preview": "rsbuild preview"
},
"dependencies": {
"@ark-ui/solid": "^4.10.2",
"@corvu/drawer": "^0.2.3",
"@corvu/otp-field": "^0.1.4",
"@corvu/resizable": "^0.2.4",
"@kobalte/core": "^0.13.9",
"@kobalte/tailwindcss": "^0.9.0",
"@solid-primitives/refs": "^1.1.0",
"@tanstack/solid-router": "^1.112.2",
"chart.js": "^4.4.8",
"cmdk-solid": "^1.1.2",
"embla-carousel-solid": "^8.5.2",
"solid-js": "^1.9.5",
"solid-sonner": "^0.2.8",
"tailwindcss": "^3"
},
"devDependencies": {
"@rsbuild/plugin-solid": "^1.0.5",
"@tanstack/react-router": "^1.112.0"
}
}

View File

@@ -0,0 +1,5 @@
export default {
plugins: {
tailwindcss: {},
},
};

View File

View File

@@ -0,0 +1,7 @@
<html>
<body>
not found :-(
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,44 @@
import { defineConfig } from '@rsbuild/core';
import { pluginBabel } from '@rsbuild/plugin-babel';
import { pluginSolid } from '@rsbuild/plugin-solid';
import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack';
export default defineConfig({
html: {
favicon: './public/assets/favicon.ico',
},
plugins: [
pluginBabel({
include: /\.(?:jsx|tsx)$/,
}),
pluginSolid(),
],
tools: {
rspack: {
plugins: [
TanStackRouterRspack({ target: 'solid', autoCodeSplitting: true }),
],
},
},
source: {
entry: {
index: './src/main.tsx',
},
define: {
'process.env.AUTH_TYPE': JSON.stringify(process.env.AUTH_TYPE),
'process.env.OIDC_CLIENT_ID': JSON.stringify(process.env.OIDC_CLIENT_ID),
'process.env.OIDC_CLIENT_SECRET': JSON.stringify(
process.env.OIDC_CLIENT_SECRET
),
'process.env.OIDC_ISSUER': JSON.stringify(process.env.OIDC_ISSUER),
'process.env.OIDC_AUDIENCE': JSON.stringify(process.env.OIDC_AUDIENCE),
'process.env.OIDC_EXTRA_SCOPES': JSON.stringify(
process.env.OIDC_EXTRA_SCOPES
),
},
},
server: {
host: '0.0.0.0',
port: 5000,
},
});

114
apps/webui/src/app.css Normal file
View File

@@ -0,0 +1,114 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 240 10% 3.9%;
--muted: 240 4.8% 95.9%;
--muted-foreground: 240 3.8% 46.1%;
--popover: 0 0% 100%;
--popover-foreground: 240 10% 3.9%;
--border: 240 5.9% 90%;
--input: 240 5.9% 90%;
--card: 0 0% 100%;
--card-foreground: 240 10% 3.9%;
--primary: 240 5.9% 10%;
--primary-foreground: 0 0% 98%;
--secondary: 240 4.8% 95.9%;
--secondary-foreground: 240 5.9% 10%;
--accent: 240 4.8% 95.9%;
--accent-foreground: 240 5.9% 10%;
--destructive: 0 84.2% 60.2%;
--destructive-foreground: 0 0% 98%;
--info: 204 94% 94%;
--info-foreground: 199 89% 48%;
--success: 149 80% 90%;
--success-foreground: 160 84% 39%;
--warning: 48 96% 89%;
--warning-foreground: 25 95% 53%;
--error: 0 93% 94%;
--error-foreground: 0 84% 60%;
--ring: 240 5.9% 10%;
--radius: 0.5rem;
}
.dark,
[data-kb-theme="dark"] {
--background: 240 10% 3.9%;
--foreground: 0 0% 98%;
--muted: 240 3.7% 15.9%;
--muted-foreground: 240 5% 64.9%;
--accent: 240 3.7% 15.9%;
--accent-foreground: 0 0% 98%;
--popover: 240 10% 3.9%;
--popover-foreground: 0 0% 98%;
--border: 240 3.7% 15.9%;
--input: 240 3.7% 15.9%;
--card: 240 10% 3.9%;
--card-foreground: 0 0% 98%;
--primary: 0 0% 98%;
--primary-foreground: 240 5.9% 10%;
--secondary: 240 3.7% 15.9%;
--secondary-foreground: 0 0% 98%;
--destructive: 0 62.8% 30.6%;
--destructive-foreground: 0 0% 98%;
--info: 204 94% 94%;
--info-foreground: 199 89% 48%;
--success: 149 80% 90%;
--success-foreground: 160 84% 39%;
--warning: 48 96% 89%;
--warning-foreground: 25 95% 53%;
--error: 0 93% 94%;
--error-foreground: 0 84% 60%;
--ring: 240 4.9% 83.9%;
--radius: 0.5rem;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings:
"rlig" 1,
"calt" 1;
}
}
@media (max-width: 640px) {
.container {
@apply px-4;
}
}

View File

@@ -0,0 +1,83 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as AccordionPrimitive from "@kobalte/core/accordion"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const Accordion = AccordionPrimitive.Root
type AccordionItemProps<T extends ValidComponent = "div"> =
AccordionPrimitive.AccordionItemProps<T> & {
class?: string | undefined
}
const AccordionItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, AccordionItemProps<T>>
) => {
const [local, others] = splitProps(props as AccordionItemProps, ["class"])
return <AccordionPrimitive.Item class={cn("border-b", local.class)} {...others} />
}
type AccordionTriggerProps<T extends ValidComponent = "button"> =
AccordionPrimitive.AccordionTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const AccordionTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, AccordionTriggerProps<T>>
) => {
const [local, others] = splitProps(props as AccordionTriggerProps, ["class", "children"])
return (
<AccordionPrimitive.Header class="flex">
<AccordionPrimitive.Trigger
class={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-expanded]>svg]:rotate-180",
local.class
)}
{...others}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4 shrink-0 transition-transform duration-200"
>
<path d="M6 9l6 6l6 -6" />
</svg>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
)
}
type AccordionContentProps<T extends ValidComponent = "div"> =
AccordionPrimitive.AccordionContentProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const AccordionContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, AccordionContentProps<T>>
) => {
const [local, others] = splitProps(props as AccordionContentProps, ["class", "children"])
return (
<AccordionPrimitive.Content
class={cn(
"animate-accordion-up overflow-hidden text-sm transition-all data-[expanded]:animate-accordion-down",
local.class
)}
{...others}
>
<div class="pb-4 pt-0">{local.children}</div>
</AccordionPrimitive.Content>
)
}
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }

View File

@@ -0,0 +1,112 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as AlertDialogPrimitive from "@kobalte/core/alert-dialog"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const AlertDialog = AlertDialogPrimitive.Root
const AlertDialogTrigger = AlertDialogPrimitive.Trigger
const AlertDialogPortal = AlertDialogPrimitive.Portal
type AlertDialogOverlayProps<T extends ValidComponent = "div"> =
AlertDialogPrimitive.AlertDialogOverlayProps<T> & {
class?: string | undefined
}
const AlertDialogOverlay = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, AlertDialogOverlayProps<T>>
) => {
const [local, others] = splitProps(props as AlertDialogOverlayProps, ["class"])
return (
<AlertDialogPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
local.class
)}
{...others}
/>
)
}
type AlertDialogContentProps<T extends ValidComponent = "div"> =
AlertDialogPrimitive.AlertDialogContentProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const AlertDialogContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, AlertDialogContentProps<T>>
) => {
const [local, others] = splitProps(props as AlertDialogContentProps, ["class", "children"])
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
class={cn(
"fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
local.class
)}
{...others}
>
{local.children}
<AlertDialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[expanded]:bg-accent data-[expanded]:text-muted-foreground">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M18 6l-12 12" />
<path d="M6 6l12 12" />
</svg>
<span class="sr-only">Close</span>
</AlertDialogPrimitive.CloseButton>
</AlertDialogPrimitive.Content>
</AlertDialogPortal>
)
}
type AlertDialogTitleProps<T extends ValidComponent = "h2"> =
AlertDialogPrimitive.AlertDialogTitleProps<T> & {
class?: string | undefined
}
const AlertDialogTitle = <T extends ValidComponent = "h2">(
props: PolymorphicProps<T, AlertDialogTitleProps<T>>
) => {
const [local, others] = splitProps(props as AlertDialogTitleProps, ["class"])
return <AlertDialogPrimitive.Title class={cn("text-lg font-semibold", local.class)} {...others} />
}
type AlertDialogDescriptionProps<T extends ValidComponent = "p"> =
AlertDialogPrimitive.AlertDialogDescriptionProps<T> & {
class?: string | undefined
}
const AlertDialogDescription = <T extends ValidComponent = "p">(
props: PolymorphicProps<T, AlertDialogDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as AlertDialogDescriptionProps, ["class"])
return (
<AlertDialogPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...others}
/>
)
}
export {
AlertDialog,
AlertDialogPortal,
AlertDialogOverlay,
AlertDialogTrigger,
AlertDialogContent,
AlertDialogTitle,
AlertDialogDescription
}

View File

@@ -0,0 +1,50 @@
import type { Component, ComponentProps, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as AlertPrimitive from "@kobalte/core/alert"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
const alertVariants = cva(
"relative w-full rounded-lg border p-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7",
{
variants: {
variant: {
default: "bg-background text-foreground",
destructive:
"border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive"
}
},
defaultVariants: {
variant: "default"
}
}
)
type AlertRootProps<T extends ValidComponent = "div"> = AlertPrimitive.AlertRootProps<T> &
VariantProps<typeof alertVariants> & { class?: string | undefined }
const Alert = <T extends ValidComponent = "div">(props: PolymorphicProps<T, AlertRootProps<T>>) => {
const [local, others] = splitProps(props as AlertRootProps, ["class", "variant"])
return (
<AlertPrimitive.Root
class={cn(alertVariants({ variant: props.variant }), local.class)}
{...others}
/>
)
}
const AlertTitle: Component<ComponentProps<"h5">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <h5 class={cn("mb-1 font-medium leading-none tracking-tight", local.class)} {...others} />
}
const AlertDescription: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div class={cn("text-sm [&_p]:leading-relaxed", local.class)} {...others} />
}
export { Alert, AlertTitle, AlertDescription }

View File

@@ -0,0 +1,31 @@
import type { Component, ComponentProps } from "solid-js"
import { mergeProps, splitProps } from "solid-js"
type AspectRatioProps = ComponentProps<"div"> & { ratio?: number }
const AspectRatio: Component<AspectRatioProps> = (rawProps) => {
const props = mergeProps({ ratio: 1 / 1 }, rawProps)
const [local, others] = splitProps(props, ["ratio"])
return (
<div
style={{
position: "relative",
width: "100%",
"padding-bottom": `${100 / local.ratio}%`
}}
>
<div
style={{
position: "absolute",
top: 0,
right: 0,
bottom: 0,
left: 0
}}
{...others}
/>
</div>
)
}
export { AspectRatio }

View File

@@ -0,0 +1,51 @@
import type { ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as ImagePrimitive from "@kobalte/core/image"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
type AvatarRootProps<T extends ValidComponent = "span"> = ImagePrimitive.ImageRootProps<T> & {
class?: string | undefined
}
const Avatar = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, AvatarRootProps<T>>
) => {
const [local, others] = splitProps(props as AvatarRootProps, ["class"])
return (
<ImagePrimitive.Root
class={cn("relative flex size-10 shrink-0 overflow-hidden rounded-full", local.class)}
{...others}
/>
)
}
type AvatarImageProps<T extends ValidComponent = "img"> = ImagePrimitive.ImageImgProps<T> & {
class?: string | undefined
}
const AvatarImage = <T extends ValidComponent = "img">(
props: PolymorphicProps<T, AvatarImageProps<T>>
) => {
const [local, others] = splitProps(props as AvatarImageProps, ["class"])
return <ImagePrimitive.Img class={cn("aspect-square size-full", local.class)} {...others} />
}
type AvatarFallbackProps<T extends ValidComponent = "span"> =
ImagePrimitive.ImageFallbackProps<T> & { class?: string | undefined }
const AvatarFallback = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, AvatarFallbackProps<T>>
) => {
const [local, others] = splitProps(props as AvatarFallbackProps, ["class"])
return (
<ImagePrimitive.Fallback
class={cn("flex size-full items-center justify-center bg-muted", local.class)}
{...others}
/>
)
}
export { Avatar, AvatarImage, AvatarFallback }

View File

@@ -0,0 +1,144 @@
import type { Component, JSXElement } from "solid-js"
import { createEffect, on, splitProps } from "solid-js"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
import type { BadgeProps } from "~/components/ui/badge"
import { Badge } from "~/components/ui/badge"
type DeltaType = "increase" | "moderateIncrease" | "unchanged" | "moderateDecrease" | "decrease"
const badgeDeltaVariants = cva("", {
variants: {
variant: {
success: "bg-success text-success-foreground hover:bg-success",
warning: "bg-warning text-warning-foreground hover:bg-warning",
error: "bg-error text-error-foreground hover:bg-error"
}
}
})
type DeltaVariant = NonNullable<VariantProps<typeof badgeDeltaVariants>["variant"]>
const iconMap: { [key in DeltaType]: (props: { class?: string }) => JSXElement } = {
increase: (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={props.class}
>
<path d="M12 5l0 14" />
<path d="M18 11l-6 -6" />
<path d="M6 11l6 -6" />
</svg>
),
moderateIncrease: (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={props.class}
>
<path d="M17 7l-10 10" />
<path d="M8 7l9 0l0 9" />
</svg>
),
unchanged: (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={props.class}
>
<path d="M5 12l14 0" />
<path d="M13 18l6 -6" />
<path d="M13 6l6 6" />
</svg>
),
moderateDecrease: (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={props.class}
>
<path d="M7 7l10 10" />
<path d="M17 8l0 9l-9 0" />
</svg>
),
decrease: (props) => (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class={props.class}
>
<path d="M12 5l0 14" />
<path d="M18 13l-6 6" />
<path d="M6 13l6 6" />
</svg>
)
}
const variantMap: { [key in DeltaType]: DeltaVariant } = {
increase: "success",
moderateIncrease: "success",
unchanged: "warning",
moderateDecrease: "error",
decrease: "error"
}
type BadgeDeltaProps = Omit<BadgeProps, "variant"> & {
deltaType: DeltaType
}
const BadgeDelta: Component<BadgeDeltaProps> = (props) => {
const [local, others] = splitProps(props, ["class", "children", "deltaType"])
// eslint-disable-next-line solid/reactivity
let Icon = iconMap[local.deltaType]
createEffect(
on(
() => local.deltaType,
() => {
Icon = iconMap[local.deltaType]
}
)
)
return (
<Badge
class={cn(badgeDeltaVariants({ variant: variantMap[local.deltaType] }), local.class)}
{...others}
>
<span class="flex gap-1">
<Icon class="size-4" />
{local.children}
</span>
</Badge>
)
}
export { BadgeDelta }

View File

@@ -0,0 +1,48 @@
import type { Component, ComponentProps } from "solid-js"
import { splitProps } from "solid-js"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
const badgeVariants = cva(
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default: "border-transparent bg-primary text-primary-foreground",
secondary: "border-transparent bg-secondary text-secondary-foreground",
outline: "text-foreground",
success: "border-success-foreground bg-success text-success-foreground",
warning: "border-warning-foreground bg-warning text-warning-foreground",
error: "border-error-foreground bg-error text-error-foreground"
}
},
defaultVariants: {
variant: "default"
}
}
)
type BadgeProps = ComponentProps<"div"> &
VariantProps<typeof badgeVariants> & {
round?: boolean
}
const Badge: Component<BadgeProps> = (props) => {
const [local, others] = splitProps(props, ["class", "variant", "round"])
return (
<div
class={cn(
badgeVariants({ variant: local.variant }),
local.round && "rounded-full",
local.class
)}
{...others}
/>
)
}
export type { BadgeProps }
export { Badge, badgeVariants }

View File

@@ -0,0 +1,96 @@
import type { ComponentProps, JSX } from "solid-js"
import { For, mergeProps, Show, splitProps } from "solid-js"
import { Dynamic } from "solid-js/web"
import { cn } from "~/styles/utils"
type Bar<T> = T & {
value: number
name: JSX.Element
icon?: (props: ComponentProps<"svg">) => JSX.Element
href?: string
target?: string
}
type SortOrder = "ascending" | "descending" | "none"
type ValueFormatter = (value: number) => string
const defaultValueFormatter: ValueFormatter = (value: number) => value.toString()
type BarListProps<T> = ComponentProps<"div"> & {
data: Bar<T>[]
valueFormatter?: ValueFormatter
sortOrder?: SortOrder
}
const BarList = <T,>(rawProps: BarListProps<T>) => {
const props = mergeProps(
{
valueFormatter: defaultValueFormatter,
sortOrder: "descending" as SortOrder
},
rawProps
)
const [local, others] = splitProps(props, ["class", "data", "valueFormatter", "sortOrder"])
const sortedData = () => {
if (local.sortOrder === "none") {
return local.data
}
return local.data.sort((a, b) =>
local.sortOrder === "ascending" ? a.value - b.value : b.value - a.value
)
}
const widths = () => {
const maxValue = Math.max(...sortedData().map((item) => item.value), 0)
return sortedData().map((item) =>
item.value === 0 ? 0 : Math.max((item.value / maxValue) * 100, 2)
)
}
return (
<div
class={cn("flex flex-col space-y-1.5", local.class)}
aria-sort={local.sortOrder}
{...others}
>
<For each={sortedData()}>
{(bar, idx) => {
return (
<div class="row flex w-full justify-between space-x-6">
<div class="grow">
<div
class={cn("flex h-8 items-center rounded-md bg-secondary px-2")}
style={{
width: `${widths()[idx()]}%`
}}
>
<Show when={bar.icon}>
{(icon) => <Dynamic component={icon()} class="mr-2 size-5 flex-none" />}
</Show>
<Show when={bar.href} fallback={<p>{bar.name}</p>}>
{(href) => (
<a
href={href()}
target={bar.target ?? "_blank"}
rel="noreferrer"
class="hover:underline"
>
{bar.name}
</a>
)}
</Show>
</div>
</div>
<div class="flex items-center">{local.valueFormatter(bar.value)}</div>
</div>
)
}}
</For>
</div>
)
}
export { BarList }

View File

@@ -0,0 +1,111 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { Show, splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core"
import * as BreadcrumbPrimitive from "@kobalte/core/breadcrumbs"
import { cn } from "~/styles/utils"
const Breadcrumb = BreadcrumbPrimitive.Root
const BreadcrumbList: Component<ComponentProps<"ol">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<ol
class={cn(
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5",
local.class
)}
{...others}
/>
)
}
const BreadcrumbItem: Component<ComponentProps<"li">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <li class={cn("inline-flex items-center gap-1.5", local.class)} {...others} />
}
type BreadcrumbLinkProps<T extends ValidComponent = "a"> =
BreadcrumbPrimitive.BreadcrumbsLinkProps<T> & { class?: string | undefined }
const BreadcrumbLink = <T extends ValidComponent = "a">(
props: PolymorphicProps<T, BreadcrumbLinkProps<T>>
) => {
const [local, others] = splitProps(props as BreadcrumbLinkProps, ["class"])
return (
<BreadcrumbPrimitive.Link
class={cn(
"transition-colors hover:text-foreground data-[current]:font-normal data-[current]:text-foreground",
local.class
)}
{...others}
/>
)
}
type BreadcrumbSeparatorProps<T extends ValidComponent = "span"> =
BreadcrumbPrimitive.BreadcrumbsSeparatorProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const BreadcrumbSeparator = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, BreadcrumbSeparatorProps<T>>
) => {
const [local, others] = splitProps(props as BreadcrumbSeparatorProps, ["class", "children"])
return (
<BreadcrumbPrimitive.Separator class={cn("[&>svg]:size-3.5", local.class)} {...others}>
<Show
when={local.children}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M9 6l6 6l-6 6" />
</svg>
}
>
{local.children}
</Show>
</BreadcrumbPrimitive.Separator>
)
}
const BreadcrumbEllipsis: Component<ComponentProps<"span">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<span class={cn("flex size-9 items-center justify-center", local.class)} {...others}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M19 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
</svg>
<span class="sr-only">More</span>
</span>
)
}
export {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbSeparator,
BreadcrumbEllipsis
}

View File

@@ -0,0 +1,53 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as ButtonPrimitive from "@kobalte/core/button"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/90",
destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
outline: "border border-input hover:bg-accent hover:text-accent-foreground",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
link: "text-primary underline-offset-4 hover:underline"
},
size: {
default: "h-10 px-4 py-2",
sm: "h-9 px-3 text-xs",
lg: "h-11 px-8",
icon: "size-10"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
)
type ButtonProps<T extends ValidComponent = "button"> = ButtonPrimitive.ButtonRootProps<T> &
VariantProps<typeof buttonVariants> & { class?: string | undefined; children?: JSX.Element }
const Button = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, ButtonProps<T>>
) => {
const [local, others] = splitProps(props as ButtonProps, ["variant", "size", "class"])
return (
<ButtonPrimitive.Root
class={cn(buttonVariants({ variant: local.variant, size: local.size }), local.class)}
{...others}
/>
)
}
export type { ButtonProps }
export { Button, buttonVariants }

View File

@@ -0,0 +1,40 @@
import type { Component, ComponentProps } from "solid-js"
import { splitProps } from "solid-js"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
const calloutVariants = cva("rounded-md border-l-4 p-2 pl-4", {
variants: {
variant: {
default: "border-info-foreground bg-info text-info-foreground",
success: "border-success-foreground bg-success text-success-foreground",
warning: "border-warning-foreground bg-warning text-warning-foreground",
error: "border-error-foreground bg-error text-error-foreground"
}
},
defaultVariants: {
variant: "default"
}
})
type CalloutProps = ComponentProps<"div"> & VariantProps<typeof calloutVariants>
const Callout: Component<CalloutProps> = (props) => {
const [local, others] = splitProps(props, ["class", "variant"])
return <div class={cn(calloutVariants({ variant: local.variant }), local.class)} {...others} />
}
const CalloutTitle: Component<ComponentProps<"h3">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <h3 class={cn("font-semibold", local.class)} {...others} />
}
const CalloutContent: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div class={cn("mt-2", local.class)} {...others} />
}
export { Callout, CalloutTitle, CalloutContent }

View File

@@ -0,0 +1,43 @@
import type { Component, ComponentProps } from "solid-js"
import { splitProps } from "solid-js"
import { cn } from "~/styles/utils"
const Card: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div
class={cn("rounded-lg border bg-card text-card-foreground shadow-sm", local.class)}
{...others}
/>
)
}
const CardHeader: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div class={cn("flex flex-col space-y-1.5 p-6", local.class)} {...others} />
}
const CardTitle: Component<ComponentProps<"h3">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<h3 class={cn("text-lg font-semibold leading-none tracking-tight", local.class)} {...others} />
)
}
const CardDescription: Component<ComponentProps<"p">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <p class={cn("text-sm text-muted-foreground", local.class)} {...others} />
}
const CardContent: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div class={cn("p-6 pt-0", local.class)} {...others} />
}
const CardFooter: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div class={cn("flex items-center p-6 pt-0", local.class)} {...others} />
}
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }

View File

@@ -0,0 +1,263 @@
import type { Accessor, Component, ComponentProps, VoidProps } from "solid-js"
import {
createContext,
createEffect,
createMemo,
createSignal,
mergeProps,
splitProps,
useContext
} from "solid-js"
import type { CreateEmblaCarouselType } from "embla-carousel-solid"
import createEmblaCarousel from "embla-carousel-solid"
import { cn } from "~/styles/utils"
import type { ButtonProps } from "~/components/ui/button"
import { Button } from "~/components/ui/button"
export type CarouselApi = CreateEmblaCarouselType[1]
type UseCarouselParameters = Parameters<typeof createEmblaCarousel>
type CarouselOptions = NonNullable<UseCarouselParameters[0]>
type CarouselPlugin = NonNullable<UseCarouselParameters[1]>
type CarouselProps = {
opts?: ReturnType<CarouselOptions>
plugins?: ReturnType<CarouselPlugin>
orientation?: "horizontal" | "vertical"
setApi?: (api: CarouselApi) => void
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof createEmblaCarousel>[0]
api: ReturnType<typeof createEmblaCarousel>[1]
scrollPrev: () => void
scrollNext: () => void
canScrollPrev: Accessor<boolean>
canScrollNext: Accessor<boolean>
} & CarouselProps
const CarouselContext = createContext<Accessor<CarouselContextProps> | null>(null)
const useCarousel = () => {
const context = useContext(CarouselContext)
if (!context) {
throw new Error("useCarousel must be used within a <Carousel />")
}
return context()
}
const Carousel: Component<CarouselProps & ComponentProps<"div">> = (rawProps) => {
const props = mergeProps<(CarouselProps & ComponentProps<"div">)[]>(
{ orientation: "horizontal" },
rawProps
)
const [local, others] = splitProps(props, [
"orientation",
"opts",
"setApi",
"plugins",
"class",
"children"
])
const [carouselRef, api] = createEmblaCarousel(
() => ({
...local.opts,
axis: local.orientation === "horizontal" ? "x" : "y"
}),
() => (local.plugins === undefined ? [] : local.plugins)
)
const [canScrollPrev, setCanScrollPrev] = createSignal(false)
const [canScrollNext, setCanScrollNext] = createSignal(false)
const onSelect = (api: NonNullable<ReturnType<CarouselApi>>) => {
setCanScrollPrev(api.canScrollPrev())
setCanScrollNext(api.canScrollNext())
}
const scrollPrev = () => {
api()?.scrollPrev()
}
const scrollNext = () => {
api()?.scrollNext()
}
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "ArrowLeft") {
event.preventDefault()
scrollPrev()
} else if (event.key === "ArrowRight") {
event.preventDefault()
scrollNext()
}
}
createEffect(() => {
if (!api() || !local.setApi) {
return
}
local.setApi(api)
})
createEffect(() => {
if (!api()) {
return
}
onSelect(api()!)
api()!.on("reInit", onSelect)
api()!.on("select", onSelect)
return () => {
api()?.off("select", onSelect)
}
})
const value = createMemo(
() =>
({
carouselRef,
api,
opts: local.opts,
orientation: local.orientation || (local.opts?.axis === "y" ? "vertical" : "horizontal"),
scrollPrev,
scrollNext,
canScrollPrev,
canScrollNext
}) satisfies CarouselContextProps
)
return (
<CarouselContext.Provider value={value}>
<div
onKeyDown={handleKeyDown}
class={cn("relative", local.class)}
role="region"
aria-roledescription="carousel"
{...others}
>
{local.children}
</div>
</CarouselContext.Provider>
)
}
const CarouselContent: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
const { carouselRef, orientation } = useCarousel()
return (
<div ref={carouselRef} class="overflow-hidden">
<div
class={cn("flex", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", local.class)}
{...others}
/>
</div>
)
}
const CarouselItem: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
const { orientation } = useCarousel()
return (
<div
role="group"
aria-roledescription="slide"
class={cn(
"min-w-0 shrink-0 grow-0 basis-full",
orientation === "horizontal" ? "pl-4" : "pt-4",
local.class
)}
{...others}
/>
)
}
type CarouselButtonProps = VoidProps<ButtonProps>
const CarouselPrevious: Component<CarouselButtonProps> = (rawProps) => {
const props = mergeProps<CarouselButtonProps[]>({ variant: "outline", size: "icon" }, rawProps)
const [local, others] = splitProps(props, ["class", "variant", "size"])
const { orientation, scrollPrev, canScrollPrev } = useCarousel()
return (
<Button
variant={local.variant}
size={local.size}
class={cn(
"absolute size-8 touch-manipulation rounded-full",
orientation === "horizontal"
? "-left-12 top-1/2 -translate-y-1/2"
: "-top-12 left-1/2 -translate-x-1/2 rotate-90",
local.class
)}
disabled={!canScrollPrev()}
onClick={scrollPrev}
{...others}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l14 0" />
<path d="M5 12l6 6" />
<path d="M5 12l6 -6" />
</svg>
<span class="sr-only">Previous slide</span>
</Button>
)
}
const CarouselNext: Component<CarouselButtonProps> = (rawProps) => {
const props = mergeProps<CarouselButtonProps[]>({ variant: "outline", size: "icon" }, rawProps)
const [local, others] = splitProps(props, ["class", "variant", "size"])
const { orientation, scrollNext, canScrollNext } = useCarousel()
return (
<Button
variant={local.variant}
size={local.size}
class={cn(
"absolute size-8 touch-manipulation rounded-full",
orientation === "horizontal"
? "-right-12 top-1/2 -translate-y-1/2"
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90",
local.class
)}
disabled={!canScrollNext()}
onClick={scrollNext}
{...others}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l14 0" />
<path d="M13 18l6 -6" />
<path d="M13 6l6 6" />
</svg>
<span class="sr-only">Next slide</span>
</Button>
)
}
export { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext }

View File

@@ -0,0 +1,292 @@
import type { Component } from "solid-js"
import { createEffect, createSignal, mergeProps, on, onCleanup, onMount } from "solid-js"
import { unwrap } from "solid-js/store"
import type { Ref } from "@solid-primitives/refs"
import { mergeRefs } from "@solid-primitives/refs"
import type {
ChartComponent,
ChartData,
ChartItem,
ChartOptions,
Plugin as ChartPlugin,
ChartType,
ChartTypeRegistry,
TooltipModel
} from "chart.js"
import {
ArcElement,
BarController,
BarElement,
BubbleController,
CategoryScale,
Chart,
Colors,
DoughnutController,
Filler,
Legend,
LinearScale,
LineController,
LineElement,
PieController,
PointElement,
PolarAreaController,
RadarController,
RadialLinearScale,
ScatterController,
Tooltip
} from "chart.js"
type TypedChartProps = {
data: ChartData
options?: ChartOptions
plugins?: ChartPlugin[]
ref?: Ref<HTMLCanvasElement | null>
width?: number | undefined
height?: number | undefined
}
type ChartProps = TypedChartProps & {
type: ChartType
}
type ChartContext = {
chart: Chart
tooltip: TooltipModel<keyof ChartTypeRegistry>
}
const BaseChart: Component<ChartProps> = (rawProps) => {
const [canvasRef, setCanvasRef] = createSignal<HTMLCanvasElement | null>()
const [chart, setChart] = createSignal<Chart>()
const props = mergeProps(
{
width: 512,
height: 512,
options: { responsive: true } as ChartOptions,
plugins: [] as ChartPlugin[]
},
rawProps
)
const init = () => {
const ctx = canvasRef()?.getContext("2d") as ChartItem
const config = unwrap(props)
const chart = new Chart(ctx, {
type: config.type,
data: config.data,
options: config.options,
plugins: config.plugins
})
setChart(chart)
}
onMount(() => init())
createEffect(
on(
() => props.data,
() => {
chart()!.data = props.data
chart()!.update()
},
{ defer: true }
)
)
createEffect(
on(
() => props.options,
() => {
chart()!.options = props.options
chart()!.update()
},
{ defer: true }
)
)
createEffect(
on(
[() => props.width, () => props.height],
() => {
chart()!.resize(props.width, props.height)
},
{ defer: true }
)
)
createEffect(
on(
() => props.type,
() => {
const dimensions = [chart()!.width, chart()!.height]
chart()!.destroy()
init()
chart()!.resize(...dimensions)
},
{ defer: true }
)
)
onCleanup(() => {
chart()?.destroy()
mergeRefs(props.ref, null)
})
Chart.register(Colors, Filler, Legend, Tooltip)
return (
<canvas
ref={mergeRefs(props.ref, (el) => setCanvasRef(el))}
height={props.height}
width={props.width}
/>
)
}
function showTooltip(context: ChartContext) {
let el = document.getElementById("chartjs-tooltip")
if (!el) {
el = document.createElement("div")
el.id = "chartjs-tooltip"
document.body.appendChild(el)
}
const model = context.tooltip
if (model.opacity === 0 || !model.body) {
el.style.opacity = "0"
return
}
el.className = `p-2 bg-card text-card-foreground rounded-lg border shadow-sm text-sm ${
model.yAlign ?? `no-transform`
}`
let content = ""
model.title.forEach((title) => {
content += `<h3 class="font-semibold leading-none tracking-tight">${title}</h3>`
})
content += `<div class="mt-1 text-muted-foreground">`
const body = model.body.flatMap((body) => body.lines)
body.forEach((line, i) => {
const colors = model.labelColors[i]
content += `
<div class="flex items-center">
<span class="inline-block h-2 w-2 mr-1 rounded-full border" style="background: ${colors.backgroundColor}; border-color: ${colors.borderColor}"></span>
${line}
</div>`
})
content += `</div>`
el.innerHTML = content
const pos = context.chart.canvas.getBoundingClientRect()
el.style.opacity = "1"
el.style.position = "absolute"
el.style.left = `${pos.left + window.scrollX + model.caretX}px`
el.style.top = `${pos.top + window.scrollY + model.caretY}px`
el.style.pointerEvents = "none"
}
function createTypedChart(
type: ChartType,
components: ChartComponent[]
): Component<TypedChartProps> {
const chartsWithScales: ChartType[] = ["bar", "line", "scatter"]
const chartsWithLegends: ChartType[] = ["bar", "line"]
const options: ChartOptions = {
responsive: true,
maintainAspectRatio: false,
scales: chartsWithScales.includes(type)
? {
x: {
border: { display: false },
grid: { display: false }
},
y: {
border: {
dash: [3],
dashOffset: 3,
display: false
},
grid: {
color: "hsla(240, 3.8%, 46.1%, 0.4)"
}
}
}
: {},
plugins: {
legend: chartsWithLegends.includes(type)
? {
display: true,
align: "end",
labels: {
usePointStyle: true,
boxWidth: 6,
boxHeight: 6,
color: "hsl(240, 3.8%, 46.1%)",
font: { size: 14 }
}
}
: { display: false },
tooltip: {
enabled: false,
external: (context) => showTooltip(context)
}
}
}
Chart.register(...components)
return (props) => <BaseChart type={type} options={options} {...props} />
}
const BarChart = /* #__PURE__ */ createTypedChart("bar", [
BarController,
BarElement,
CategoryScale,
LinearScale
])
const BubbleChart = /* #__PURE__ */ createTypedChart("bubble", [
BubbleController,
PointElement,
LinearScale
])
const DonutChart = /* #__PURE__ */ createTypedChart("doughnut", [DoughnutController, ArcElement])
const LineChart = /* #__PURE__ */ createTypedChart("line", [
LineController,
LineElement,
PointElement,
CategoryScale,
LinearScale
])
const PieChart = /* #__PURE__ */ createTypedChart("pie", [PieController, ArcElement])
const PolarAreaChart = /* #__PURE__ */ createTypedChart("polarArea", [
PolarAreaController,
ArcElement,
RadialLinearScale
])
const RadarChart = /* #__PURE__ */ createTypedChart("radar", [
RadarController,
LineElement,
PointElement,
RadialLinearScale
])
const ScatterChart = /* #__PURE__ */ createTypedChart("scatter", [
ScatterController,
PointElement,
LinearScale
])
export {
BaseChart as Chart,
BarChart,
BubbleChart,
DonutChart,
LineChart,
PieChart,
PolarAreaChart,
RadarChart,
ScatterChart
}

View File

@@ -0,0 +1,60 @@
import type { ValidComponent } from "solid-js"
import { Match, splitProps, Switch } from "solid-js"
import * as CheckboxPrimitive from "@kobalte/core/checkbox"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
type CheckboxRootProps<T extends ValidComponent = "div"> =
CheckboxPrimitive.CheckboxRootProps<T> & { class?: string | undefined }
const Checkbox = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, CheckboxRootProps<T>>
) => {
const [local, others] = splitProps(props as CheckboxRootProps, ["class"])
return (
<CheckboxPrimitive.Root
class={cn("items-top group relative flex space-x-2", local.class)}
{...others}
>
<CheckboxPrimitive.Input class="peer" />
<CheckboxPrimitive.Control class="size-4 shrink-0 rounded-sm border border-primary ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 peer-focus-visible:outline-none peer-focus-visible:ring-2 peer-focus-visible:ring-ring peer-focus-visible:ring-offset-2 data-[checked]:border-none data-[indeterminate]:border-none data-[checked]:bg-primary data-[indeterminate]:bg-primary data-[checked]:text-primary-foreground data-[indeterminate]:text-primary-foreground">
<CheckboxPrimitive.Indicator>
<Switch>
<Match when={!others.indeterminate}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l5 5l10 -10" />
</svg>
</Match>
<Match when={others.indeterminate}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l14 0" />
</svg>
</Match>
</Switch>
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Control>
</CheckboxPrimitive.Root>
)
}
export { Checkbox }

View File

@@ -0,0 +1,9 @@
import * as CollapsiblePrimitive from "@kobalte/core/collapsible"
const Collapsible = CollapsiblePrimitive.Root
const CollapsibleTrigger = CollapsiblePrimitive.Trigger
const CollapsibleContent = CollapsiblePrimitive.Content
export { Collapsible, CollapsibleTrigger, CollapsibleContent }

View File

@@ -0,0 +1,192 @@
import type { JSX, ValidComponent } from "solid-js"
import { Show, splitProps } from "solid-js"
import * as ComboboxPrimitive from "@kobalte/core/combobox"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const Combobox = ComboboxPrimitive.Root
const ComboboxItemLabel = ComboboxPrimitive.ItemLabel
const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect
type ComboboxItemProps<T extends ValidComponent = "li"> = ComboboxPrimitive.ComboboxItemProps<T> & {
class?: string | undefined
}
const ComboboxItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, ComboboxItemProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxItemProps, ["class"])
return (
<ComboboxPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center justify-between rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled]:pointer-events-none data-[highlighted]:bg-accent data-[highlighted]:text-accent-foreground data-[disabled]:opacity-50",
local.class
)}
{...others}
/>
)
}
type ComboboxItemIndicatorProps<T extends ValidComponent = "div"> =
ComboboxPrimitive.ComboboxItemIndicatorProps<T> & {
children?: JSX.Element
}
const ComboboxItemIndicator = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ComboboxItemIndicatorProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxItemIndicatorProps, ["children"])
return (
<ComboboxPrimitive.ItemIndicator {...others}>
<Show
when={local.children}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l5 5l10 -10" />
</svg>
}
>
{(children) => children()}
</Show>
</ComboboxPrimitive.ItemIndicator>
)
}
type ComboboxSectionProps<T extends ValidComponent = "li"> =
ComboboxPrimitive.ComboboxSectionProps<T> & { class?: string | undefined }
const ComboboxSection = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, ComboboxSectionProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxSectionProps, ["class"])
return (
<ComboboxPrimitive.Section
class={cn(
"overflow-hidden p-1 px-2 py-1.5 text-xs font-medium text-muted-foreground ",
local.class
)}
{...others}
/>
)
}
type ComboboxControlProps<
U,
T extends ValidComponent = "div"
> = ComboboxPrimitive.ComboboxControlProps<U, T> & {
class?: string | undefined
}
const ComboboxControl = <T, U extends ValidComponent = "div">(
props: PolymorphicProps<U, ComboboxControlProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxControlProps<T>, ["class"])
return (
<ComboboxPrimitive.Control
class={cn("flex h-10 items-center rounded-md border px-3", local.class)}
{...others}
/>
)
}
type ComboboxInputProps<T extends ValidComponent = "input"> =
ComboboxPrimitive.ComboboxInputProps<T> & { class?: string | undefined }
const ComboboxInput = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, ComboboxInputProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxInputProps, ["class"])
return (
<ComboboxPrimitive.Input
class={cn(
"flex size-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
local.class
)}
{...others}
/>
)
}
type ComboboxTriggerProps<T extends ValidComponent = "button"> =
ComboboxPrimitive.ComboboxTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const ComboboxTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, ComboboxTriggerProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxTriggerProps, ["class", "children"])
return (
<ComboboxPrimitive.Trigger class={cn("size-4 opacity-50", local.class)} {...others}>
<ComboboxPrimitive.Icon>
<Show
when={local.children}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M8 9l4 -4l4 4" />
<path d="M16 15l-4 4l-4 -4" />
</svg>
}
>
{(children) => children()}
</Show>
</ComboboxPrimitive.Icon>
</ComboboxPrimitive.Trigger>
)
}
type ComboboxContentProps<T extends ValidComponent = "div"> =
ComboboxPrimitive.ComboboxContentProps<T> & { class?: string | undefined }
const ComboboxContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ComboboxContentProps<T>>
) => {
const [local, others] = splitProps(props as ComboboxContentProps, ["class"])
return (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Content
class={cn(
"relative z-50 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-80",
local.class
)}
{...others}
>
<ComboboxPrimitive.Listbox class="m-0 p-1" />
</ComboboxPrimitive.Content>
</ComboboxPrimitive.Portal>
)
}
export {
Combobox,
ComboboxItem,
ComboboxItemLabel,
ComboboxItemIndicator,
ComboboxSection,
ComboboxControl,
ComboboxTrigger,
ComboboxInput,
ComboboxHiddenSelect,
ComboboxContent
}

View File

@@ -0,0 +1,145 @@
import type { Component, ComponentProps, ParentProps, VoidProps } from "solid-js"
import { splitProps } from "solid-js"
import type { DialogRootProps } from "@kobalte/core/dialog"
import * as CommandPrimitive from "cmdk-solid"
import { cn } from "~/styles/utils"
import { Dialog, DialogContent } from "~/components/ui/dialog"
const Command: Component<ParentProps<CommandPrimitive.CommandRootProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<CommandPrimitive.CommandRoot
class={cn(
"flex size-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground blur-none",
local.class
)}
{...others}
/>
)
}
const CommandDialog: Component<ParentProps<DialogRootProps>> = (props) => {
const [local, others] = splitProps(props, ["children"])
return (
<Dialog {...others}>
<DialogContent class="overflow-hidden p-0">
<Command class="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:size-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:size-5">
{local.children}
</Command>
</DialogContent>
</Dialog>
)
}
const CommandInput: Component<VoidProps<CommandPrimitive.CommandInputProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div class="flex items-center border-b px-3" cmdk-input-wrapper="">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="mr-2 size-4 shrink-0 opacity-50"
>
<path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0" />
<path d="M21 21l-6 -6" />
</svg>
<CommandPrimitive.CommandInput
class={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
local.class
)}
{...others}
/>
</div>
)
}
const CommandList: Component<ParentProps<CommandPrimitive.CommandListProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<CommandPrimitive.CommandList
class={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", local.class)}
{...others}
/>
)
}
const CommandEmpty: Component<ParentProps<CommandPrimitive.CommandEmptyProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<CommandPrimitive.CommandEmpty
class={cn("py-6 text-center text-sm", local.class)}
{...others}
/>
)
}
const CommandGroup: Component<ParentProps<CommandPrimitive.CommandGroupProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<CommandPrimitive.CommandGroup
class={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
local.class
)}
{...others}
/>
)
}
const CommandSeparator: Component<VoidProps<CommandPrimitive.CommandSeparatorProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <CommandPrimitive.CommandSeparator class={cn("h-px bg-border", local.class)} {...others} />
}
const CommandItem: Component<ParentProps<CommandPrimitive.CommandItemProps>> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<CommandPrimitive.CommandItem
cmdk-item=""
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
local.class
)}
{...others}
/>
)
}
const CommandShortcut: Component<ComponentProps<"span">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<span
class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)}
{...others}
/>
)
}
export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator
}

View File

@@ -0,0 +1,249 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as ContextMenuPrimitive from "@kobalte/core/context-menu"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const ContextMenuTrigger = ContextMenuPrimitive.Trigger
const ContextMenuPortal = ContextMenuPrimitive.Portal
const ContextMenuSub = ContextMenuPrimitive.Sub
const ContextMenuGroup = ContextMenuPrimitive.Group
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup
const ContextMenu: Component<ContextMenuPrimitive.ContextMenuRootProps> = (props) => {
return <ContextMenuPrimitive.Root gutter={4} {...props} />
}
type ContextMenuContentProps<T extends ValidComponent = "div"> =
ContextMenuPrimitive.ContextMenuContentProps<T> & {
class?: string | undefined
}
const ContextMenuContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ContextMenuContentProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuContentProps, ["class"])
return (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
class={cn(
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in",
local.class
)}
{...others}
/>
</ContextMenuPrimitive.Portal>
)
}
type ContextMenuItemProps<T extends ValidComponent = "div"> =
ContextMenuPrimitive.ContextMenuItemProps<T> & {
class?: string | undefined
}
const ContextMenuItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ContextMenuItemProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuItemProps, ["class"])
return (
<ContextMenuPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class
)}
{...others}
/>
)
}
const ContextMenuShortcut: Component<ComponentProps<"span">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <span class={cn("ml-auto text-xs tracking-widest opacity-60", local.class)} {...others} />
}
type ContextMenuSeparatorProps<T extends ValidComponent = "hr"> =
ContextMenuPrimitive.ContextMenuSeparatorProps<T> & {
class?: string | undefined
}
const ContextMenuSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, ContextMenuSeparatorProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuSeparatorProps, ["class"])
return (
<ContextMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", local.class)}
{...others}
/>
)
}
type ContextMenuSubTriggerProps<T extends ValidComponent = "div"> =
ContextMenuPrimitive.ContextMenuSubTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const ContextMenuSubTrigger = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ContextMenuSubTriggerProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuSubTriggerProps, ["class", "children"])
return (
<ContextMenuPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
local.class
)}
{...others}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="ml-auto size-4"
>
<path d="M9 6l6 6l-6 6" />
</svg>
</ContextMenuPrimitive.SubTrigger>
)
}
type ContextMenuSubContentProps<T extends ValidComponent = "div"> =
ContextMenuPrimitive.ContextMenuSubContentProps<T> & {
class?: string | undefined
}
const ContextMenuSubContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ContextMenuSubContentProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuSubContentProps, ["class"])
return (
<ContextMenuPrimitive.SubContent
class={cn(
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in",
local.class
)}
{...others}
/>
)
}
type ContextMenuCheckboxItemProps<T extends ValidComponent = "div"> =
ContextMenuPrimitive.ContextMenuCheckboxItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ContextMenuCheckboxItemProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuCheckboxItemProps, ["class", "children"])
return (
<ContextMenuPrimitive.CheckboxItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class
)}
{...others}
>
<span class="absolute left-2 flex size-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l5 5l10 -10" />
</svg>
</ContextMenuPrimitive.ItemIndicator>
</span>
{local.children}
</ContextMenuPrimitive.CheckboxItem>
)
}
type ContextMenuGroupLabelProps<T extends ValidComponent = "span"> =
ContextMenuPrimitive.ContextMenuGroupLabelProps<T> & {
class?: string | undefined
}
const ContextMenuGroupLabel = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, ContextMenuGroupLabelProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuGroupLabelProps, ["class"])
return (
<ContextMenuPrimitive.GroupLabel
class={cn("px-2 py-1.5 text-sm font-semibold", local.class)}
{...others}
/>
)
}
type ContextMenuRadioItemProps<T extends ValidComponent = "div"> =
ContextMenuPrimitive.ContextMenuRadioItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const ContextMenuRadioItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ContextMenuRadioItemProps<T>>
) => {
const [local, others] = splitProps(props as ContextMenuRadioItemProps, ["class", "children"])
return (
<ContextMenuPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class
)}
{...others}
>
<span class="absolute left-2 flex size-3.5 items-center justify-center">
<ContextMenuPrimitive.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-2 fill-current"
>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
</svg>
</ContextMenuPrimitive.ItemIndicator>
</span>
{local.children}
</ContextMenuPrimitive.RadioItem>
)
}
export {
ContextMenu,
ContextMenuTrigger,
ContextMenuPortal,
ContextMenuContent,
ContextMenuItem,
ContextMenuShortcut,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubTrigger,
ContextMenuSubContent,
ContextMenuCheckboxItem,
ContextMenuGroup,
ContextMenuGroupLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem
}

View File

@@ -0,0 +1,283 @@
import { children, Show, splitProps } from "solid-js"
import { DatePicker as DatePickerPrimitive } from "@ark-ui/solid"
import { cn } from "~/styles/utils"
import { buttonVariants } from "~/components/ui/button"
const DatePicker = DatePickerPrimitive.Root
const DatePickerLabel = DatePickerPrimitive.Label
const DatePickerContext = DatePickerPrimitive.Context
const DatePickerTableHead = DatePickerPrimitive.TableHead
const DatePickerTableBody = DatePickerPrimitive.TableBody
const DatePickerYearSelect = DatePickerPrimitive.YearSelect
const DatePickerMonthSelect = DatePickerPrimitive.MonthSelect
const DatePickerPositioner = DatePickerPrimitive.Positioner
const DatePickerControl = (props: DatePickerPrimitive.ControlProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.Control
class={cn("inline-flex items-center gap-1", local.class)}
{...others}
/>
)
}
const DatePickerInput = (props: DatePickerPrimitive.InputProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.Input
class={cn(
"h-9 w-full rounded-md border border-border bg-background px-3 py-1 text-sm shadow-sm transition-shadow placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
local.class
)}
{...others}
/>
)
}
const DatePickerTrigger = (props: DatePickerPrimitive.TriggerProps) => {
const [local, others] = splitProps(props, ["class", "children"])
// prevents rendering children twice
const resolvedChildren = children(() => local.children)
const hasChildren = () => resolvedChildren.toArray().length !== 0
return (
<DatePickerPrimitive.Trigger
class={cn(
"flex min-h-9 min-w-9 items-center justify-center rounded-md border border-border bg-background transition-[box-shadow,background-color] hover:bg-accent/50 focus-visible:outline-none focus-visible:ring-[1.5px] focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>svg]:size-4",
local.class
)}
{...others}
>
<Show when={!hasChildren()} fallback={resolvedChildren()}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M4 7a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12z" />
<path d="M16 3v4" />
<path d="M8 3v4" />
<path d="M4 11h16" />
<path d="M11 15h1" />
<path d="M12 15v3" />
<title>Calendar</title>
</svg>
</Show>
</DatePickerPrimitive.Trigger>
)
}
const DatePickerContent = (props: DatePickerPrimitive.ContentProps) => {
const [local, others] = splitProps(props, ["class", "children"])
return (
<DatePickerPrimitive.Content
class={cn(
"z-50 rounded-md border bg-popover p-3 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
local.class
)}
{...others}
>
{local.children}
</DatePickerPrimitive.Content>
)
}
const DatePickerView = (props: DatePickerPrimitive.ViewProps) => {
const [local, others] = splitProps(props, ["class"])
return <DatePickerPrimitive.View class={cn("space-y-4", local.class)} {...others} />
}
const DatePickerViewControl = (props: DatePickerPrimitive.ViewControlProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.ViewControl
class={cn("flex items-center justify-between gap-4", local.class)}
{...others}
/>
)
}
const DatePickerPrevTrigger = (props: DatePickerPrimitive.PrevTriggerProps) => {
const [local, others] = splitProps(props, ["class", "children"])
// prevents rendering children twice
const resolvedChildren = children(() => local.children)
const hasChildren = () => resolvedChildren.toArray().length !== 0
return (
<DatePickerPrimitive.PrevTrigger
class={cn(
buttonVariants({
variant: "outline"
}),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
local.class
)}
{...others}
>
<Show when={!hasChildren()} fallback={resolvedChildren()}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M15 6l-6 6l6 6" />
<title>Previous</title>
</svg>
</Show>
</DatePickerPrimitive.PrevTrigger>
)
}
const DatePickerNextTrigger = (props: DatePickerPrimitive.NextTriggerProps) => {
const [local, others] = splitProps(props, ["class", "children"])
// prevents rendering children twice
const resolvedChildren = children(() => local.children)
const hasChildren = () => resolvedChildren.toArray().length !== 0
return (
<DatePickerPrimitive.NextTrigger
class={cn(
buttonVariants({
variant: "outline"
}),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
local.class
)}
{...others}
>
<Show when={!hasChildren()} fallback={resolvedChildren()}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M9 6l6 6l-6 6" />
<title>Next</title>
</svg>
</Show>
</DatePickerPrimitive.NextTrigger>
)
}
const DatePickerViewTrigger = (props: DatePickerPrimitive.ViewTriggerProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.ViewTrigger
class={cn(buttonVariants({ variant: "ghost" }), "h-7", local.class)}
{...others}
/>
)
}
const DatePickerRangeText = (props: DatePickerPrimitive.RangeTextProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.RangeText class={cn("text-sm font-medium", local.class)} {...others} />
)
}
const DatePickerTable = (props: DatePickerPrimitive.TableProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.Table
class={cn("w-full border-collapse space-y-1", local.class)}
{...others}
/>
)
}
const DatePickerTableRow = (props: DatePickerPrimitive.TableRowProps) => {
const [local, others] = splitProps(props, ["class"])
return <DatePickerPrimitive.TableRow class={cn("mt-2 flex w-full", local.class)} {...others} />
}
const DatePickerTableHeader = (props: DatePickerPrimitive.TableHeaderProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.TableHeader
class={cn("w-8 flex-1 text-[0.8rem] font-normal text-muted-foreground", local.class)}
{...others}
/>
)
}
const DatePickerTableCell = (props: DatePickerPrimitive.TableCellProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.TableCell
class={cn(
"flex-1 p-0 text-center text-sm has-[[data-range-end]]:rounded-r-md has-[[data-range-start]]:rounded-l-md has-[[data-in-range]]:bg-accent has-[[data-outside-range][data-in-range]]:bg-accent/50 has-[[data-in-range]]:first-of-type:rounded-l-md has-[[data-in-range]]:last-of-type:rounded-r-md",
local.class
)}
{...others}
/>
)
}
const DatePickerTableCellTrigger = (props: DatePickerPrimitive.TableCellTriggerProps) => {
const [local, others] = splitProps(props, ["class"])
return (
<DatePickerPrimitive.TableCellTrigger
class={cn(
buttonVariants({ variant: "ghost" }),
"size-8 w-full p-0 font-normal data-[selected]:opacity-100",
"data-[today]:bg-accent data-[today]:text-accent-foreground",
"[&:is([data-today][data-selected])]:bg-primary [&:is([data-today][data-selected])]:text-primary-foreground",
"data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:bg-primary data-[selected]:hover:text-primary-foreground",
"data-[disabled]:text-muted-foreground data-[disabled]:opacity-50",
"data-[outside-range]:text-muted-foreground data-[outside-range]:opacity-50",
"[&:is([data-outside-range][data-in-range])]:bg-accent/50 [&:is([data-outside-range][data-in-range])]:text-muted-foreground [&:is([data-outside-range][data-in-range])]:opacity-30",
local.class
)}
{...others}
/>
)
}
export {
DatePicker,
DatePickerLabel,
DatePickerControl,
DatePickerInput,
DatePickerTrigger,
DatePickerContent,
DatePickerView,
DatePickerViewControl,
DatePickerPrevTrigger,
DatePickerNextTrigger,
DatePickerViewTrigger,
DatePickerRangeText,
DatePickerContext,
DatePickerTable,
DatePickerTableHead,
DatePickerTableBody,
DatePickerTableRow,
DatePickerTableHeader,
DatePickerTableCell,
DatePickerTableCellTrigger,
DatePickerYearSelect,
DatePickerMonthSelect,
DatePickerPositioner
}

View File

@@ -0,0 +1,58 @@
import { mergeProps, Show, splitProps, type Component, type ComponentProps } from "solid-js"
import { cn } from "~/styles/utils"
type DeltaBarProps = ComponentProps<"div"> & {
value: number
isIncreasePositive?: boolean
}
const DeltaBar: Component<DeltaBarProps> = (rawProps) => {
const props = mergeProps(
{
isIncreasePositive: true
},
rawProps
)
const [local, others] = splitProps(props, ["value", "isIncreasePositive", "class"])
return (
<div
class={cn("relative flex h-2 w-full items-center rounded-full bg-secondary", local.class)}
{...others}
>
<div class="flex h-full w-1/2 justify-end">
<Show when={local.value < 0}>
<div
class={cn(
"rounded-l-full",
(local.value > 0 && local.isIncreasePositive) ||
(local.value < 0 && !local.isIncreasePositive)
? "bg-success-foreground"
: "bg-error-foreground"
)}
style={{ width: `${Math.abs(local.value)}%` }}
/>
</Show>
</div>
<div class={cn("z-10 h-4 w-1 rounded-full bg-primary ring-2 ring-background")} />
<div class="flex h-full w-1/2 justify-start">
<Show when={local.value > 0}>
<div
class={cn(
"rounded-r-full",
(local.value > 0 && local.isIncreasePositive) ||
(local.value < 0 && !local.isIncreasePositive)
? "bg-success-foreground"
: "bg-error-foreground"
)}
style={{ width: `${Math.abs(local.value)}%` }}
/>
</Show>
</div>
</div>
)
}
export type { DeltaBarProps }
export { DeltaBar }

View File

@@ -0,0 +1,141 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as DialogPrimitive from "@kobalte/core/dialog"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const Dialog = DialogPrimitive.Root
const DialogTrigger = DialogPrimitive.Trigger
const DialogPortal: Component<DialogPrimitive.DialogPortalProps> = (props) => {
const [, rest] = splitProps(props, ["children"])
return (
<DialogPrimitive.Portal {...rest}>
<div class="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
{props.children}
</div>
</DialogPrimitive.Portal>
)
}
type DialogOverlayProps<T extends ValidComponent = "div"> =
DialogPrimitive.DialogOverlayProps<T> & { class?: string | undefined }
const DialogOverlay = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DialogOverlayProps<T>>
) => {
const [, rest] = splitProps(props as DialogOverlayProps, ["class"])
return (
<DialogPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-background/80 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0",
props.class
)}
{...rest}
/>
)
}
type DialogContentProps<T extends ValidComponent = "div"> =
DialogPrimitive.DialogContentProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const DialogContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DialogContentProps<T>>
) => {
const [, rest] = splitProps(props as DialogContentProps, ["class", "children"])
return (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
class={cn(
"fixed left-1/2 top-1/2 z-50 grid max-h-screen w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto border bg-background p-6 shadow-lg duration-200 data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 data-[closed]:slide-out-to-left-1/2 data-[closed]:slide-out-to-top-[48%] data-[expanded]:slide-in-from-left-1/2 data-[expanded]:slide-in-from-top-[48%] sm:rounded-lg",
props.class
)}
{...rest}
>
{props.children}
<DialogPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[expanded]:bg-accent data-[expanded]:text-muted-foreground">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M18 6l-12 12" />
<path d="M6 6l12 12" />
</svg>
<span class="sr-only">Close</span>
</DialogPrimitive.CloseButton>
</DialogPrimitive.Content>
</DialogPortal>
)
}
const DialogHeader: Component<ComponentProps<"div">> = (props) => {
const [, rest] = splitProps(props, ["class"])
return (
<div class={cn("flex flex-col space-y-1.5 text-center sm:text-left", props.class)} {...rest} />
)
}
const DialogFooter: Component<ComponentProps<"div">> = (props) => {
const [, rest] = splitProps(props, ["class"])
return (
<div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", props.class)}
{...rest}
/>
)
}
type DialogTitleProps<T extends ValidComponent = "h2"> = DialogPrimitive.DialogTitleProps<T> & {
class?: string | undefined
}
const DialogTitle = <T extends ValidComponent = "h2">(
props: PolymorphicProps<T, DialogTitleProps<T>>
) => {
const [, rest] = splitProps(props as DialogTitleProps, ["class"])
return (
<DialogPrimitive.Title
class={cn("text-lg font-semibold leading-none tracking-tight", props.class)}
{...rest}
/>
)
}
type DialogDescriptionProps<T extends ValidComponent = "p"> =
DialogPrimitive.DialogDescriptionProps<T> & {
class?: string | undefined
}
const DialogDescription = <T extends ValidComponent = "p">(
props: PolymorphicProps<T, DialogDescriptionProps<T>>
) => {
const [, rest] = splitProps(props as DialogDescriptionProps, ["class"])
return (
<DialogPrimitive.Description
class={cn("text-sm text-muted-foreground", props.class)}
{...rest}
/>
)
}
export {
Dialog,
DialogTrigger,
DialogContent,
DialogHeader,
DialogFooter,
DialogTitle,
DialogDescription
}

View File

@@ -0,0 +1,121 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type {
ContentProps,
DescriptionProps,
DynamicProps,
LabelProps,
OverlayProps
} from "@corvu/drawer"
import DrawerPrimitive from "@corvu/drawer"
import { cn } from "~/styles/utils"
const Drawer = DrawerPrimitive
const DrawerTrigger = DrawerPrimitive.Trigger
const DrawerPortal = DrawerPrimitive.Portal
const DrawerClose = DrawerPrimitive.Close
type DrawerOverlayProps<T extends ValidComponent = "div"> = OverlayProps<T> & { class?: string }
const DrawerOverlay = <T extends ValidComponent = "div">(
props: DynamicProps<T, DrawerOverlayProps<T>>
) => {
const [, rest] = splitProps(props as DrawerOverlayProps, ["class"])
const drawerContext = DrawerPrimitive.useContext()
return (
<DrawerPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 data-[transitioning]:transition-colors data-[transitioning]:duration-300",
props.class
)}
style={{
"background-color": `rgb(0 0 0 / ${0.8 * drawerContext.openPercentage()})`
}}
{...rest}
/>
)
}
type DrawerContentProps<T extends ValidComponent = "div"> = ContentProps<T> & {
class?: string
children?: JSX.Element
}
const DrawerContent = <T extends ValidComponent = "div">(
props: DynamicProps<T, DrawerContentProps<T>>
) => {
const [, rest] = splitProps(props as DrawerContentProps, ["class", "children"])
return (
<DrawerPortal>
<DrawerOverlay />
<DrawerPrimitive.Content
class={cn(
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background after:absolute after:inset-x-0 after:top-full after:h-1/2 after:bg-inherit data-[transitioning]:transition-transform data-[transitioning]:duration-300 md:select-none",
props.class
)}
{...rest}
>
<div class="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
{props.children}
</DrawerPrimitive.Content>
</DrawerPortal>
)
}
const DrawerHeader: Component<ComponentProps<"div">> = (props) => {
const [, rest] = splitProps(props, ["class"])
return <div class={cn("grid gap-1.5 p-4 text-center sm:text-left", props.class)} {...rest} />
}
const DrawerFooter: Component<ComponentProps<"div">> = (props) => {
const [, rest] = splitProps(props, ["class"])
return <div class={cn("t-auto flex flex-col gap-2 p-4", props.class)} {...rest} />
}
type DrawerTitleProps<T extends ValidComponent = "div"> = LabelProps<T> & { class?: string }
const DrawerTitle = <T extends ValidComponent = "div">(
props: DynamicProps<T, DrawerTitleProps<T>>
) => {
const [, rest] = splitProps(props as DrawerTitleProps, ["class"])
return (
<DrawerPrimitive.Label
class={cn("text-lg font-semibold leading-none tracking-tight", props.class)}
{...rest}
/>
)
}
type DrawerDescriptionProps<T extends ValidComponent = "div"> = DescriptionProps<T> & {
class?: string
}
const DrawerDescription = <T extends ValidComponent = "div">(
props: DynamicProps<T, DrawerDescriptionProps<T>>
) => {
const [, rest] = splitProps(props as DrawerDescriptionProps, ["class"])
return (
<DrawerPrimitive.Description
class={cn("text-sm text-muted-foreground", props.class)}
{...rest}
/>
)
}
export {
Drawer,
DrawerPortal,
DrawerOverlay,
DrawerTrigger,
DrawerClose,
DrawerContent,
DrawerHeader,
DrawerFooter,
DrawerTitle,
DrawerDescription
}

View File

@@ -0,0 +1,260 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as DropdownMenuPrimitive from "@kobalte/core/dropdown-menu"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger
const DropdownMenuPortal = DropdownMenuPrimitive.Portal
const DropdownMenuSub = DropdownMenuPrimitive.Sub
const DropdownMenuGroup = DropdownMenuPrimitive.Group
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup
const DropdownMenu: Component<DropdownMenuPrimitive.DropdownMenuRootProps> = (props) => {
return <DropdownMenuPrimitive.Root gutter={4} {...props} />
}
type DropdownMenuContentProps<T extends ValidComponent = "div"> =
DropdownMenuPrimitive.DropdownMenuContentProps<T> & {
class?: string | undefined
}
const DropdownMenuContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DropdownMenuContentProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuContentProps, ["class"])
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
class={cn(
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-content-hide overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[expanded]:animate-content-show",
props.class
)}
{...rest}
/>
</DropdownMenuPrimitive.Portal>
)
}
type DropdownMenuItemProps<T extends ValidComponent = "div"> =
DropdownMenuPrimitive.DropdownMenuItemProps<T> & {
class?: string | undefined
}
const DropdownMenuItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DropdownMenuItemProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuItemProps, ["class"])
return (
<DropdownMenuPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
props.class
)}
{...rest}
/>
)
}
const DropdownMenuShortcut: Component<ComponentProps<"span">> = (props) => {
const [, rest] = splitProps(props, ["class"])
return <span class={cn("ml-auto text-xs tracking-widest opacity-60", props.class)} {...rest} />
}
const DropdownMenuLabel: Component<ComponentProps<"div"> & { inset?: boolean }> = (props) => {
const [, rest] = splitProps(props, ["class", "inset"])
return (
<div
class={cn("px-2 py-1.5 text-sm font-semibold", props.inset && "pl-8", props.class)}
{...rest}
/>
)
}
type DropdownMenuSeparatorProps<T extends ValidComponent = "hr"> =
DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & {
class?: string | undefined
}
const DropdownMenuSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuSeparatorProps, ["class"])
return (
<DropdownMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", props.class)}
{...rest}
/>
)
}
type DropdownMenuSubTriggerProps<T extends ValidComponent = "div"> =
DropdownMenuPrimitive.DropdownMenuSubTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const DropdownMenuSubTrigger = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DropdownMenuSubTriggerProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuSubTriggerProps, ["class", "children"])
return (
<DropdownMenuPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
props.class
)}
{...rest}
>
{props.children}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="ml-auto size-4"
>
<path d="M9 6l6 6l-6 6" />
</svg>
</DropdownMenuPrimitive.SubTrigger>
)
}
type DropdownMenuSubContentProps<T extends ValidComponent = "div"> =
DropdownMenuPrimitive.DropdownMenuSubContentProps<T> & {
class?: string | undefined
}
const DropdownMenuSubContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DropdownMenuSubContentProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuSubContentProps, ["class"])
return (
<DropdownMenuPrimitive.SubContent
class={cn(
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in",
props.class
)}
{...rest}
/>
)
}
type DropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> =
DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children"])
return (
<DropdownMenuPrimitive.CheckboxItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
props.class
)}
{...rest}
>
<span class="absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l5 5l10 -10" />
</svg>
</DropdownMenuPrimitive.ItemIndicator>
</span>
{props.children}
</DropdownMenuPrimitive.CheckboxItem>
)
}
type DropdownMenuGroupLabelProps<T extends ValidComponent = "span"> =
DropdownMenuPrimitive.DropdownMenuGroupLabelProps<T> & {
class?: string | undefined
}
const DropdownMenuGroupLabel = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, DropdownMenuGroupLabelProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuGroupLabelProps, ["class"])
return (
<DropdownMenuPrimitive.GroupLabel
class={cn("px-2 py-1.5 text-sm font-semibold", props.class)}
{...rest}
/>
)
}
type DropdownMenuRadioItemProps<T extends ValidComponent = "div"> =
DropdownMenuPrimitive.DropdownMenuRadioItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DropdownMenuRadioItemProps<T>>
) => {
const [, rest] = splitProps(props as DropdownMenuRadioItemProps, ["class", "children"])
return (
<DropdownMenuPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
props.class
)}
{...rest}
>
<span class="absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-2 fill-current"
>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
</svg>
</DropdownMenuPrimitive.ItemIndicator>
</span>
{props.children}
</DropdownMenuPrimitive.RadioItem>
)
}
export {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuPortal,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuShortcut,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
DropdownMenuCheckboxItem,
DropdownMenuGroup,
DropdownMenuGroupLabel,
DropdownMenuRadioGroup,
DropdownMenuRadioItem
}

View File

@@ -0,0 +1,70 @@
import type { Component, ComponentProps } from "solid-js"
import { mergeProps, splitProps } from "solid-js"
import { cn } from "~/styles/utils"
type JustifyContent = "start" | "end" | "center" | "between" | "around" | "evenly"
type AlignItems = "start" | "end" | "center" | "baseline" | "stretch"
type FlexDirection = "row" | "col" | "row-reverse" | "col-reverse"
type FlexProps = ComponentProps<"div"> & {
flexDirection?: FlexDirection
justifyContent?: JustifyContent
alignItems?: AlignItems
}
const Flex: Component<FlexProps> = (rawProps) => {
const props = mergeProps(
{
flexDirection: "row",
justifyContent: "between",
alignItems: "center"
} satisfies FlexProps,
rawProps
)
const [local, others] = splitProps(props, [
"flexDirection",
"justifyContent",
"alignItems",
"class"
])
return (
<div
class={cn(
"flex w-full",
flexDirectionClassNames[local.flexDirection],
justifyContentClassNames[local.justifyContent],
alignItemsClassNames[local.alignItems],
local.class
)}
{...others}
/>
)
}
export { Flex }
const justifyContentClassNames: { [key in JustifyContent]: string } = {
start: "justify-start",
end: "justify-end",
center: "justify-center",
between: "justify-between",
around: "justify-around",
evenly: "justify-evenly"
}
const alignItemsClassNames: { [key in AlignItems]: string } = {
start: "items-start",
end: "items-end",
center: "items-center",
baseline: "items-baseline",
stretch: "items-stretch"
}
const flexDirectionClassNames: { [key in FlexDirection]: string } = {
row: "flex-row",
col: "flex-col",
"row-reverse": "flex-row-reverse",
"col-reverse": "flex-col-reverse"
}

View File

@@ -0,0 +1,188 @@
import type { Component, ComponentProps } from "solid-js"
import { mergeProps, splitProps } from "solid-js"
import { cn } from "~/styles/utils"
type Cols = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12
type Span = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13
type GridProps = ComponentProps<"div"> & {
cols?: Cols
colsSm?: Cols
colsMd?: Cols
colsLg?: Cols
}
const Grid: Component<GridProps> = (rawProps) => {
const props = mergeProps({ cols: 1 } satisfies GridProps, rawProps)
const [local, others] = splitProps(props, ["cols", "colsSm", "colsMd", "colsLg", "class"])
return (
<div
class={cn(
"grid",
gridCols[local.cols],
local.colsSm && gridColsSm[local.colsSm],
local.colsMd && gridColsMd[local.colsMd],
local.colsLg && gridColsLg[local.colsLg],
local.class
)}
{...others}
/>
)
}
type ColProps = ComponentProps<"div"> & {
span?: Span
spanSm?: Span
spanMd?: Span
spanLg?: Span
}
const Col: Component<ColProps> = (rawProps) => {
const props = mergeProps({ span: 1 as Span }, rawProps)
const [local, others] = splitProps(props, ["span", "spanSm", "spanMd", "spanLg", "class"])
return (
<div
class={cn(
colSpan[local.span],
local.spanSm && colSpanSm[local.spanSm],
local.spanMd && colSpanMd[local.spanMd],
local.spanLg && colSpanLg[local.spanLg],
local.class
)}
{...others}
/>
)
}
export { Grid, Col }
const gridCols: { [key in Cols]: string } = {
0: "grid-cols-none",
1: "grid-cols-1",
2: "grid-cols-2",
3: "grid-cols-3",
4: "grid-cols-4",
5: "grid-cols-5",
6: "grid-cols-6",
7: "grid-cols-7",
8: "grid-cols-8",
9: "grid-cols-9",
10: "grid-cols-10",
11: "grid-cols-11",
12: "grid-cols-12"
}
const gridColsSm: { [key in Cols]: string } = {
0: "sm:grid-cols-none",
1: "sm:grid-cols-1",
2: "sm:grid-cols-2",
3: "sm:grid-cols-3",
4: "sm:grid-cols-4",
5: "sm:grid-cols-5",
6: "sm:grid-cols-6",
7: "sm:grid-cols-7",
8: "sm:grid-cols-8",
9: "sm:grid-cols-9",
10: "sm:grid-cols-10",
11: "sm:grid-cols-11",
12: "sm:grid-cols-12"
}
const gridColsMd: { [key in Cols]: string } = {
0: "md:grid-cols-none",
1: "md:grid-cols-1",
2: "md:grid-cols-2",
3: "md:grid-cols-3",
4: "md:grid-cols-4",
5: "md:grid-cols-5",
6: "md:grid-cols-6",
7: "md:grid-cols-7",
8: "md:grid-cols-8",
9: "md:grid-cols-9",
10: "md:grid-cols-10",
11: "md:grid-cols-11",
12: "md:grid-cols-12"
}
const gridColsLg: { [key in Cols]: string } = {
0: "lg:grid-cols-none",
1: "lg:grid-cols-1",
2: "lg:grid-cols-2",
3: "lg:grid-cols-3",
4: "lg:grid-cols-4",
5: "lg:grid-cols-5",
6: "lg:grid-cols-6",
7: "lg:grid-cols-7",
8: "lg:grid-cols-8",
9: "lg:grid-cols-9",
10: "lg:grid-cols-10",
11: "lg:grid-cols-11",
12: "lg:grid-cols-12"
}
const colSpan: { [key in Span]: string } = {
1: "col-span-1",
2: "col-span-2",
3: "col-span-3",
4: "col-span-4",
5: "col-span-5",
6: "col-span-6",
7: "col-span-7",
8: "col-span-8",
9: "col-span-9",
10: "col-span-10",
11: "col-span-11",
12: "col-span-12",
13: "col-span-13"
}
const colSpanSm: { [key in Span]: string } = {
1: "sm:col-span-1",
2: "sm:col-span-2",
3: "sm:col-span-3",
4: "sm:col-span-4",
5: "sm:col-span-5",
6: "sm:col-span-6",
7: "sm:col-span-7",
8: "sm:col-span-8",
9: "sm:col-span-9",
10: "sm:col-span-10",
11: "sm:col-span-11",
12: "sm:col-span-12",
13: "sm:col-span-13"
}
const colSpanMd: { [key in Span]: string } = {
1: "md:col-span-1",
2: "md:col-span-2",
3: "md:col-span-3",
4: "md:col-span-4",
5: "md:col-span-5",
6: "md:col-span-6",
7: "md:col-span-7",
8: "md:col-span-8",
9: "md:col-span-9",
10: "md:col-span-10",
11: "md:col-span-11",
12: "md:col-span-12",
13: "md:col-span-13"
}
const colSpanLg: { [key in Span]: string } = {
1: "lg:col-span-1",
2: "lg:col-span-2",
3: "lg:col-span-3",
4: "lg:col-span-4",
5: "lg:col-span-5",
6: "lg:col-span-6",
7: "lg:col-span-7",
8: "lg:col-span-8",
9: "lg:col-span-9",
10: "lg:col-span-10",
11: "lg:col-span-11",
12: "lg:col-span-12",
13: "lg:col-span-13"
}

View File

@@ -0,0 +1,37 @@
import type { Component, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as HoverCardPrimitive from "@kobalte/core/hover-card"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const HoverCardTrigger = HoverCardPrimitive.Trigger
const HoverCard: Component<HoverCardPrimitive.HoverCardRootProps> = (props) => {
return <HoverCardPrimitive.Root gutter={4} {...props} />
}
type HoverCardContentProps<T extends ValidComponent = "div"> =
HoverCardPrimitive.HoverCardContentProps<T> & {
class?: string | undefined
}
const HoverCardContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, HoverCardContentProps<T>>
) => {
const [local, others] = splitProps(props as HoverCardContentProps, ["class"])
return (
<HoverCardPrimitive.Portal>
<HoverCardPrimitive.Content
class={cn(
"z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
local.class
)}
{...others}
/>
</HoverCardPrimitive.Portal>
)
}
export { HoverCard, HoverCardTrigger, HoverCardContent }

View File

@@ -0,0 +1,19 @@
import type { Component, ComponentProps } from "solid-js"
import { splitProps } from "solid-js"
import { cn } from "~/styles/utils"
const Label: Component<ComponentProps<"label">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<label
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
local.class
)}
{...others}
/>
)
}
export { Label }

View File

@@ -0,0 +1,313 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as MenubarPrimitive from "@kobalte/core/menubar"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const MenubarGroup = MenubarPrimitive.Group
const MenubarPortal = MenubarPrimitive.Portal
const MenubarSub = MenubarPrimitive.Sub
const MenubarRadioGroup = MenubarPrimitive.RadioGroup
type MenubarRootProps<T extends ValidComponent = "div"> = MenubarPrimitive.MenubarRootProps<T> & {
class?: string | undefined
}
const Menubar = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarRootProps<T>>
) => {
const [local, others] = splitProps(props as MenubarRootProps, ["class"])
return (
<MenubarPrimitive.Root
class={cn(
"flex h-10 items-center space-x-1 rounded-md border bg-background p-1",
local.class
)}
{...others}
/>
)
}
const MenubarMenu: Component<MenubarPrimitive.MenubarMenuProps> = (props) => {
return <MenubarPrimitive.Menu gutter={8} {...props} />
}
type MenubarTriggerProps<T extends ValidComponent = "button"> =
MenubarPrimitive.MenubarTriggerProps<T> & { class?: string | undefined }
const MenubarTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, MenubarTriggerProps<T>>
) => {
const [local, others] = splitProps(props as MenubarTriggerProps, ["class"])
return (
<MenubarPrimitive.Trigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
local.class
)}
{...others}
/>
)
}
type MenubarContentProps<T extends ValidComponent = "div"> =
MenubarPrimitive.MenubarContentProps<T> & { class?: string | undefined }
const MenubarContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarContentProps<T>>
) => {
const [local, others] = splitProps(props as MenubarContentProps, ["class"])
return (
<MenubarPrimitive.Portal>
<MenubarPrimitive.Content
class={cn(
"z-50 min-w-48 origin-[var(--kb-menu-content-transform-origin)] animate-content-hide overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[expanded]:animate-content-show",
local.class
)}
{...others}
/>
</MenubarPrimitive.Portal>
)
}
type MenubarSubTriggerProps<T extends ValidComponent = "div"> =
MenubarPrimitive.MenubarSubTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
inset?: boolean
}
const MenubarSubTrigger = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarSubTriggerProps<T>>
) => {
const [local, others] = splitProps(props as MenubarSubTriggerProps, [
"class",
"children",
"inset"
])
return (
<MenubarPrimitive.SubTrigger
class={cn(
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
local.inset && "pl-8",
local.class
)}
{...others}
>
{local.children}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="ml-auto size-4"
>
<path d="M9 6l6 6l-6 6" />
</svg>
</MenubarPrimitive.SubTrigger>
)
}
type MenubarSubContentProps<T extends ValidComponent = "div"> =
MenubarPrimitive.MenubarSubContentProps<T> & {
class?: string | undefined
}
const MenubarSubContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarSubContentProps<T>>
) => {
const [local, others] = splitProps(props as MenubarSubContentProps, ["class"])
return (
<MenubarPrimitive.Portal>
<MenubarPrimitive.SubContent
class={cn(
"z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md animate-in",
local.class
)}
{...others}
/>
</MenubarPrimitive.Portal>
)
}
type MenubarItemProps<T extends ValidComponent = "div"> = MenubarPrimitive.MenubarItemProps<T> & {
class?: string | undefined
inset?: boolean
}
const MenubarItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarItemProps<T>>
) => {
const [local, others] = splitProps(props as MenubarItemProps, ["class", "inset"])
return (
<MenubarPrimitive.Item
class={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.inset && "pl-8",
local.class
)}
{...others}
/>
)
}
type MenubarCheckboxItemProps<T extends ValidComponent = "div"> =
MenubarPrimitive.MenubarCheckboxItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const MenubarCheckboxItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarCheckboxItemProps<T>>
) => {
const [local, others] = splitProps(props as MenubarCheckboxItemProps, ["class", "children"])
return (
<MenubarPrimitive.CheckboxItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class
)}
{...others}
>
<span class="absolute left-2 flex size-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M5 12l5 5l10 -10" />
</svg>
</MenubarPrimitive.ItemIndicator>
</span>
{local.children}
</MenubarPrimitive.CheckboxItem>
)
}
type MenubarRadioItemProps<T extends ValidComponent = "div"> =
MenubarPrimitive.MenubarRadioItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const MenubarRadioItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarRadioItemProps<T>>
) => {
const [local, others] = splitProps(props as MenubarRadioItemProps, ["class", "children"])
return (
<MenubarPrimitive.RadioItem
class={cn(
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class
)}
{...others}
>
<span class="absolute left-2 flex size-3.5 items-center justify-center">
<MenubarPrimitive.ItemIndicator>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-2 fill-current"
>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
</svg>
</MenubarPrimitive.ItemIndicator>
</span>
{local.children}
</MenubarPrimitive.RadioItem>
)
}
type MenubarItemLabelProps<T extends ValidComponent = "div"> =
MenubarPrimitive.MenubarItemLabelProps<T> & {
class?: string | undefined
inset?: boolean
}
const MenubarItemLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, MenubarItemLabelProps<T>>
) => {
const [local, others] = splitProps(props as MenubarItemLabelProps, ["class", "inset"])
return (
<MenubarPrimitive.ItemLabel
class={cn("px-2 py-1.5 text-sm font-semibold", local.inset && "pl-8", local.class)}
{...others}
/>
)
}
type MenubarGroupLabelProps<T extends ValidComponent = "span"> =
MenubarPrimitive.MenubarGroupLabelProps<T> & {
class?: string | undefined
inset?: boolean
}
const MenubarGroupLabel = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, MenubarGroupLabelProps<T>>
) => {
const [local, others] = splitProps(props as MenubarGroupLabelProps, ["class", "inset"])
return (
<MenubarPrimitive.GroupLabel
class={cn("px-2 py-1.5 text-sm font-semibold", local.inset && "pl-8", local.class)}
{...others}
/>
)
}
type MenubarSeparatorProps<T extends ValidComponent = "hr"> =
MenubarPrimitive.MenubarSeparatorProps<T> & { class?: string | undefined }
const MenubarSeparator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, MenubarSeparatorProps<T>>
) => {
const [local, others] = splitProps(props as MenubarSeparatorProps, ["class"])
return (
<MenubarPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-muted", local.class)} {...others} />
)
}
const MenubarShortcut: Component<ComponentProps<"span">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<span
class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)}
{...others}
/>
)
}
export {
Menubar,
MenubarMenu,
MenubarTrigger,
MenubarContent,
MenubarItem,
MenubarSeparator,
MenubarItemLabel,
MenubarGroupLabel,
MenubarCheckboxItem,
MenubarRadioGroup,
MenubarRadioItem,
MenubarPortal,
MenubarSubContent,
MenubarSubTrigger,
MenubarGroup,
MenubarSub,
MenubarShortcut
}

View File

@@ -0,0 +1,186 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core"
import * as NavigationMenuPrimitive from "@kobalte/core/navigation-menu"
import { cn } from "~/styles/utils"
const NavigationMenuItem = NavigationMenuPrimitive.Menu
type NavigationMenuProps<T extends ValidComponent = "ul"> =
NavigationMenuPrimitive.NavigationMenuRootProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const NavigationMenu = <T extends ValidComponent = "ul">(
props: PolymorphicProps<T, NavigationMenuProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuProps, ["class", "children"])
return (
<NavigationMenuPrimitive.Root
gutter={6}
class={cn(
"group/menu flex w-max flex-1 list-none items-center justify-center data-[orientation=vertical]:flex-col [&>li]:w-full",
local.class
)}
{...others}
>
{local.children}
<NavigationMenuViewport />
</NavigationMenuPrimitive.Root>
)
}
type NavigationMenuTriggerProps<T extends ValidComponent = "button"> =
NavigationMenuPrimitive.NavigationMenuTriggerProps<T> & {
class?: string | undefined
}
const NavigationMenuTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, NavigationMenuTriggerProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuTriggerProps, ["class"])
return (
<NavigationMenuPrimitive.Trigger
class={cn(
"group/trigger inline-flex h-10 w-full items-center justify-center whitespace-nowrap rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[expanded]:bg-accent/50",
local.class
)}
{...others}
/>
)
}
const NavigationMenuIcon = () => {
return (
<NavigationMenuPrimitive.Icon aria-hidden="true">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="relative top-px ml-1 size-3 transition duration-200 group-data-[expanded]/trigger:rotate-180 group-data-[orientation=vertical]/menu:-rotate-90 group-data-[orientation=vertical]/menu:group-data-[expanded]/trigger:rotate-90"
>
<path d="M6 9l6 6l6 -6" />
</svg>
</NavigationMenuPrimitive.Icon>
)
}
type NavigationMenuViewportProps<T extends ValidComponent = "li"> =
NavigationMenuPrimitive.NavigationMenuViewportProps<T> & { class?: string | undefined }
const NavigationMenuViewport = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, NavigationMenuViewportProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuViewportProps, ["class"])
return (
<NavigationMenuPrimitive.Viewport
class={cn(
// base settings
"pointer-events-none z-[1000] flex h-[var(--kb-navigation-menu-viewport-height)] w-[var(--kb-navigation-menu-viewport-width)] origin-[var(--kb-menu-content-transform-origin)] items-center justify-center overflow-x-clip overflow-y-visible rounded-md border bg-popover opacity-0 shadow-lg data-[expanded]:pointer-events-auto data-[orientation=vertical]:overflow-y-clip data-[orientation=vertical]:overflow-x-visible data-[expanded]:rounded-md",
// animate
"animate-content-hide transition-[width,height] duration-200 ease-in data-[expanded]:animate-content-show data-[expanded]:opacity-100 data-[expanded]:ease-out",
local.class
)}
{...others}
/>
)
}
type NavigationMenuContentProps<T extends ValidComponent = "ul"> =
NavigationMenuPrimitive.NavigationMenuContentProps<T> & {
class?: string | undefined
}
const NavigationMenuContent = <T extends ValidComponent = "ul">(
props: PolymorphicProps<T, NavigationMenuContentProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuContentProps, ["class"])
return (
<NavigationMenuPrimitive.Portal>
<NavigationMenuPrimitive.Content
class={cn(
// base settings
"pointer-events-none absolute left-0 top-0 box-border p-4 focus:outline-none data-[expanded]:pointer-events-auto",
// base animation settings
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out",
// left to right
"data-[orientation=horizontal]:data-[motion=from-start]:slide-in-from-left-52 data-[orientation=horizontal]:data-[motion=to-end]:slide-out-to-right-52",
// right to left
"data-[orientation=horizontal]:data-[motion=from-end]:slide-in-from-right-52 data-[orientation=horizontal]:data-[motion=to-start]:slide-out-to-left-52",
// top to bottom
"data-[orientation=vertical]:data-[motion=from-start]:slide-in-from-top-52 data-[orientation=vertical]:data-[motion=to-end]:slide-out-to-bottom-52",
//bottom to top
"data-[orientation=vertical]:data-[motion=from-end]:slide-in-from-bottom-52 data-[orientation=vertical]:data-[motion=to-start]:slide-out-to-bottom-52",
local.class
)}
{...others}
/>
</NavigationMenuPrimitive.Portal>
)
}
type NavigationMenuLinkProps<T extends ValidComponent = "a"> =
NavigationMenuPrimitive.NavigationMenuItemProps<T> & { class?: string | undefined }
const NavigationMenuLink = <T extends ValidComponent = "a">(
props: PolymorphicProps<T, NavigationMenuLinkProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuLinkProps, ["class"])
return (
<NavigationMenuPrimitive.Item
class={cn(
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
local.class
)}
{...others}
/>
)
}
type NavigationMenuLabelProps<T extends ValidComponent = "div"> =
NavigationMenuPrimitive.NavigationMenuItemLabelProps<T> & { class?: string | undefined }
const NavigationMenuLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, NavigationMenuLabelProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuLabelProps, ["class"])
return (
<NavigationMenuPrimitive.ItemLabel
class={cn("text-sm font-medium leading-none", local.class)}
{...others}
/>
)
}
type NavigationMenuDescriptionProps<T extends ValidComponent = "div"> =
NavigationMenuPrimitive.NavigationMenuItemDescriptionProps<T> & { class?: string | undefined }
const NavigationMenuDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, NavigationMenuDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as NavigationMenuDescriptionProps, ["class"])
return (
<NavigationMenuPrimitive.ItemDescription
class={cn("text-sm leading-snug text-muted-foreground", local.class)}
{...others}
/>
)
}
export {
NavigationMenu,
NavigationMenuItem,
NavigationMenuTrigger,
NavigationMenuIcon,
NavigationMenuViewport,
NavigationMenuContent,
NavigationMenuLink,
NavigationMenuLabel,
NavigationMenuDescription
}

View File

@@ -0,0 +1,195 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { Show, splitProps } from "solid-js"
import * as NumberFieldPrimitive from "@kobalte/core/number-field"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
const NumberField = NumberFieldPrimitive.Root
const NumberFieldGroup: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div
class={cn(
"relative rounded-md focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
local.class
)}
{...others}
/>
)
}
type NumberFieldLabelProps<T extends ValidComponent = "label"> =
NumberFieldPrimitive.NumberFieldLabelProps<T> & {
class?: string | undefined
}
const NumberFieldLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, NumberFieldLabelProps<T>>
) => {
const [local, others] = splitProps(props as NumberFieldLabelProps, ["class"])
return (
<NumberFieldPrimitive.Label
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
local.class
)}
{...others}
/>
)
}
type NumberFieldInputProps<T extends ValidComponent = "input"> =
NumberFieldPrimitive.NumberFieldInputProps<T> & {
class?: string | undefined
}
const NumberFieldInput = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, NumberFieldInputProps<T>>
) => {
const [local, others] = splitProps(props as NumberFieldInputProps, ["class"])
return (
<NumberFieldPrimitive.Input
class={cn(
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-error-foreground data-[invalid]:text-error-foreground",
local.class
)}
{...others}
/>
)
}
type NumberFieldIncrementTriggerProps<T extends ValidComponent = "button"> =
NumberFieldPrimitive.NumberFieldIncrementTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const NumberFieldIncrementTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, NumberFieldIncrementTriggerProps<T>>
) => {
const [local, others] = splitProps(props as NumberFieldIncrementTriggerProps, [
"class",
"children"
])
return (
<NumberFieldPrimitive.IncrementTrigger
class={cn(
"absolute right-1 top-1 inline-flex size-4 items-center justify-center",
local.class
)}
{...others}
>
<Show
when={local.children}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M6 15l6 -6l6 6" />
</svg>
}
>
{(children) => children()}
</Show>
</NumberFieldPrimitive.IncrementTrigger>
)
}
type NumberFieldDecrementTriggerProps<T extends ValidComponent = "button"> =
NumberFieldPrimitive.NumberFieldDecrementTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const NumberFieldDecrementTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, NumberFieldDecrementTriggerProps<T>>
) => {
const [local, others] = splitProps(props as NumberFieldDecrementTriggerProps, [
"class",
"children"
])
return (
<NumberFieldPrimitive.DecrementTrigger
class={cn(
"absolute bottom-1 right-1 inline-flex size-4 items-center justify-center",
local.class
)}
{...others}
>
<Show
when={local.children}
fallback={
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M6 9l6 6l6 -6" />
</svg>
}
>
{(children) => children()}
</Show>
</NumberFieldPrimitive.DecrementTrigger>
)
}
type NumberFieldDescriptionProps<T extends ValidComponent = "div"> =
NumberFieldPrimitive.NumberFieldDescriptionProps<T> & {
class?: string | undefined
}
const NumberFieldDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, NumberFieldDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as NumberFieldDescriptionProps, ["class"])
return (
<NumberFieldPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...others}
/>
)
}
type NumberFieldErrorMessageProps<T extends ValidComponent = "div"> =
NumberFieldPrimitive.NumberFieldErrorMessageProps<T> & {
class?: string | undefined
}
const NumberFieldErrorMessage = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, NumberFieldErrorMessageProps<T>>
) => {
const [local, others] = splitProps(props as NumberFieldErrorMessageProps, ["class"])
return (
<NumberFieldPrimitive.ErrorMessage
class={cn("text-sm text-error-foreground", local.class)}
{...others}
/>
)
}
export {
NumberField,
NumberFieldGroup,
NumberFieldLabel,
NumberFieldInput,
NumberFieldIncrementTrigger,
NumberFieldDecrementTrigger,
NumberFieldDescription,
NumberFieldErrorMessage
}

View File

@@ -0,0 +1,84 @@
import type { Component, ComponentProps, ValidComponent } from "solid-js"
import { Show, splitProps } from "solid-js"
import type { DynamicProps, RootProps } from "@corvu/otp-field"
import OtpField from "@corvu/otp-field"
import { cn } from "~/styles/utils"
export const REGEXP_ONLY_DIGITS = "^\\d*$"
export const REGEXP_ONLY_CHARS = "^[a-zA-Z]*$"
export const REGEXP_ONLY_DIGITS_AND_CHARS = "^[a-zA-Z0-9]*$"
type OTPFieldProps<T extends ValidComponent = "div"> = RootProps<T> & { class?: string }
const OTPField = <T extends ValidComponent = "div">(props: DynamicProps<T, OTPFieldProps<T>>) => {
const [local, others] = splitProps(props as OTPFieldProps, ["class"])
return (
<OtpField
class={cn(
"flex items-center gap-2 disabled:cursor-not-allowed has-[:disabled]:opacity-50",
local.class
)}
{...others}
/>
)
}
const OTPFieldInput = OtpField.Input
const OTPFieldGroup: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div class={cn("flex items-center", local.class)} {...others} />
}
const OTPFieldSlot: Component<ComponentProps<"div"> & { index: number }> = (props) => {
const [local, others] = splitProps(props, ["class", "index"])
const context = OtpField.useContext()
const char = () => context.value()[local.index]
const showFakeCaret = () => context.value().length === local.index && context.isInserting()
return (
<div
class={cn(
"group relative flex size-10 items-center justify-center border-y border-r border-input text-sm first:rounded-l-md first:border-l last:rounded-r-md",
local.class
)}
{...others}
>
<div
class={cn(
"absolute inset-0 z-10 transition-all group-first:rounded-l-md group-last:rounded-r-md",
context.activeSlots().includes(local.index) && "ring-2 ring-ring ring-offset-background"
)}
/>
{char()}
<Show when={showFakeCaret()}>
<div class="pointer-events-none absolute inset-0 flex items-center justify-center">
<div class="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
</div>
</Show>
</div>
)
}
const OTPFieldSeparator: Component<ComponentProps<"div">> = (props) => {
return (
<div {...props}>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-6"
>
<circle cx="12.1" cy="12.1" r="1" />
</svg>
</div>
)
}
export { OTPField, OTPFieldInput, OTPFieldGroup, OTPFieldSlot, OTPFieldSeparator }

View File

@@ -0,0 +1,182 @@
import type { JSX, ValidComponent } from "solid-js"
import { Show, splitProps } from "solid-js"
import * as PaginationPrimitive from "@kobalte/core/pagination"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cn } from "~/styles/utils"
import { buttonVariants } from "~/components/ui/button"
const PaginationItems = PaginationPrimitive.Items
type PaginationRootProps<T extends ValidComponent = "nav"> =
PaginationPrimitive.PaginationRootProps<T> & { class?: string | undefined }
const Pagination = <T extends ValidComponent = "nav">(
props: PolymorphicProps<T, PaginationRootProps<T>>
) => {
const [local, others] = splitProps(props as PaginationRootProps, ["class"])
return (
<PaginationPrimitive.Root
class={cn("[&>*]:flex [&>*]:flex-row [&>*]:items-center [&>*]:gap-1", local.class)}
{...others}
/>
)
}
type PaginationItemProps<T extends ValidComponent = "button"> =
PaginationPrimitive.PaginationItemProps<T> & { class?: string | undefined }
const PaginationItem = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, PaginationItemProps<T>>
) => {
const [local, others] = splitProps(props as PaginationItemProps, ["class"])
return (
<PaginationPrimitive.Item
class={cn(
buttonVariants({
variant: "ghost"
}),
"size-10 data-[current]:border",
local.class
)}
{...others}
/>
)
}
type PaginationEllipsisProps<T extends ValidComponent = "div"> =
PaginationPrimitive.PaginationEllipsisProps<T> & {
class?: string | undefined
}
const PaginationEllipsis = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, PaginationEllipsisProps<T>>
) => {
const [local, others] = splitProps(props as PaginationEllipsisProps, ["class"])
return (
<PaginationPrimitive.Ellipsis
class={cn("flex size-10 items-center justify-center", local.class)}
{...others}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<circle cx="12" cy="12" r="1" />
<circle cx="19" cy="12" r="1" />
<circle cx="5" cy="12" r="1" />
</svg>
<span class="sr-only">More pages</span>
</PaginationPrimitive.Ellipsis>
)
}
type PaginationPreviousProps<T extends ValidComponent = "button"> =
PaginationPrimitive.PaginationPreviousProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const PaginationPrevious = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, PaginationPreviousProps<T>>
) => {
const [local, others] = splitProps(props as PaginationPreviousProps, ["class", "children"])
return (
<PaginationPrimitive.Previous
class={cn(
buttonVariants({
variant: "ghost"
}),
"gap-1 pl-2.5",
local.class
)}
{...others}
>
<Show
when={local.children}
fallback={
<>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M15 6l-6 6l6 6" />
</svg>
<span>Previous</span>
</>
}
>
{(children) => children()}
</Show>
</PaginationPrimitive.Previous>
)
}
type PaginationNextProps<T extends ValidComponent = "button"> =
PaginationPrimitive.PaginationNextProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const PaginationNext = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, PaginationNextProps<T>>
) => {
const [local, others] = splitProps(props as PaginationNextProps, ["class", "children"])
return (
<PaginationPrimitive.Next
class={cn(
buttonVariants({
variant: "ghost"
}),
"gap-1 pl-2.5",
local.class
)}
{...others}
>
<Show
when={local.children}
fallback={
<>
<span>Next</span>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M9 6l6 6l-6 6" />
</svg>
</>
}
>
{(children) => children()}
</Show>
</PaginationPrimitive.Next>
)
}
export {
Pagination,
PaginationItems,
PaginationItem,
PaginationEllipsis,
PaginationPrevious,
PaginationNext
}

View File

@@ -0,0 +1,35 @@
import type { Component, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as PopoverPrimitive from "@kobalte/core/popover"
import { cn } from "~/styles/utils"
const PopoverTrigger = PopoverPrimitive.Trigger
const Popover: Component<PopoverPrimitive.PopoverRootProps> = (props) => {
return <PopoverPrimitive.Root gutter={4} {...props} />
}
type PopoverContentProps<T extends ValidComponent = "div"> =
PopoverPrimitive.PopoverContentProps<T> & { class?: string | undefined }
const PopoverContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, PopoverContentProps<T>>
) => {
const [local, others] = splitProps(props as PopoverContentProps, ["class"])
return (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
class={cn(
"z-50 w-72 origin-[var(--kb-popover-content-transform-origin)] rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[expanded]:animate-in data-[closed]:animate-out data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95",
local.class
)}
{...others}
/>
</PopoverPrimitive.Portal>
)
}
export { Popover, PopoverTrigger, PopoverContent }

View File

@@ -0,0 +1,94 @@
import type { Component, ComponentProps } from "solid-js"
import { mergeProps, splitProps } from "solid-js"
import { cn } from "~/styles/utils"
type Size = "xs" | "sm" | "md" | "lg" | "xl"
const sizes: Record<Size, { radius: number; strokeWidth: number }> = {
xs: { radius: 15, strokeWidth: 3 },
sm: { radius: 19, strokeWidth: 4 },
md: { radius: 32, strokeWidth: 6 },
lg: { radius: 52, strokeWidth: 8 },
xl: { radius: 80, strokeWidth: 10 }
}
type ProgressCircleProps = ComponentProps<"div"> & {
value?: number
size?: Size
radius?: number
strokeWidth?: number
showAnimation?: boolean
}
const ProgressCircle: Component<ProgressCircleProps> = (rawProps) => {
const props = mergeProps({ size: "md" as Size, showAnimation: true }, rawProps)
const [local, others] = splitProps(props, [
"class",
"children",
"value",
"size",
"radius",
"strokeWidth",
"showAnimation"
])
const value = () => getLimitedValue(local.value)
const radius = () => local.radius ?? sizes[local.size].radius
const strokeWidth = () => local.strokeWidth ?? sizes[local.size].strokeWidth
const normalizedRadius = () => radius() - strokeWidth() / 2
const circumference = () => normalizedRadius() * 2 * Math.PI
const strokeDashoffset = () => (value() / 100) * circumference()
const offset = () => circumference() - strokeDashoffset()
return (
<div class={cn("flex flex-col items-center justify-center", local.class)} {...others}>
<svg
width={radius() * 2}
height={radius() * 2}
viewBox={`0 0 ${radius() * 2} ${radius() * 2}`}
class="-rotate-90"
>
<circle
r={normalizedRadius()}
cx={radius()}
cy={radius()}
stroke-width={strokeWidth()}
fill="transparent"
stroke=""
stroke-linecap="round"
class={cn("stroke-secondary transition-colors ease-linear")}
/>
{value() >= 0 ? (
<circle
r={normalizedRadius()}
cx={radius()}
cy={radius()}
stroke-width={strokeWidth()}
stroke-dasharray={circumference() + " " + circumference()}
stroke-dashoffset={offset()}
fill="transparent"
stroke=""
stroke-linecap="round"
class={cn(
"stroke-primary transition-colors ease-linear",
local.showAnimation ? "transition-all duration-300 ease-in-out" : ""
)}
/>
) : null}
</svg>
<div class={cn("absolute flex")}>{local.children}</div>
</div>
)
}
function getLimitedValue(input: number | undefined) {
if (input === undefined) {
return 0
} else if (input > 100) {
return 100
}
return input
}
export { ProgressCircle }

View File

@@ -0,0 +1,34 @@
import type { Component, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as ProgressPrimitive from "@kobalte/core/progress"
import { Label } from "~/components/ui/label"
type ProgressRootProps<T extends ValidComponent = "div"> =
ProgressPrimitive.ProgressRootProps<T> & { children?: JSX.Element }
const Progress = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ProgressRootProps<T>>
) => {
const [local, others] = splitProps(props as ProgressRootProps, ["children"])
return (
<ProgressPrimitive.Root {...others}>
{local.children}
<ProgressPrimitive.Track class="relative h-2 w-full overflow-hidden rounded-full bg-secondary">
<ProgressPrimitive.Fill class="h-full w-[var(--kb-progress-fill-width)] flex-1 bg-primary transition-all" />
</ProgressPrimitive.Track>
</ProgressPrimitive.Root>
)
}
const ProgressLabel: Component<ProgressPrimitive.ProgressLabelProps> = (props) => {
return <ProgressPrimitive.Label as={Label} {...props} />
}
const ProgressValueLabel: Component<ProgressPrimitive.ProgressValueLabelProps> = (props) => {
return <ProgressPrimitive.ValueLabel as={Label} {...props} />
}
export { Progress, ProgressLabel, ProgressValueLabel }

View File

@@ -0,0 +1,73 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as RadioGroupPrimitive from "@kobalte/core/radio-group"
import { cn } from "~/styles/utils"
type RadioGroupRootProps<T extends ValidComponent = "div"> =
RadioGroupPrimitive.RadioGroupRootProps<T> & { class?: string | undefined }
const RadioGroup = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, RadioGroupRootProps<T>>
) => {
const [local, others] = splitProps(props as RadioGroupRootProps, ["class"])
return <RadioGroupPrimitive.Root class={cn("grid gap-2", local.class)} {...others} />
}
type RadioGroupItemProps<T extends ValidComponent = "div"> =
RadioGroupPrimitive.RadioGroupItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const RadioGroupItem = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, RadioGroupItemProps<T>>
) => {
const [local, others] = splitProps(props as RadioGroupItemProps, ["class", "children"])
return (
<RadioGroupPrimitive.Item class={cn("flex items-center space-x-2", local.class)} {...others}>
<RadioGroupPrimitive.ItemInput />
<RadioGroupPrimitive.ItemControl class="aspect-square size-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50">
<RadioGroupPrimitive.ItemIndicator class="flex h-full items-center justify-center ">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-2.5 fill-current text-current"
>
<path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" />
</svg>
</RadioGroupPrimitive.ItemIndicator>
</RadioGroupPrimitive.ItemControl>
{local.children}
</RadioGroupPrimitive.Item>
)
}
type RadioGroupLabelProps<T extends ValidComponent = "label"> =
RadioGroupPrimitive.RadioGroupLabelProps<T> & {
class?: string | undefined
}
const RadioGroupItemLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, RadioGroupLabelProps<T>>
) => {
const [local, others] = splitProps(props as RadioGroupLabelProps, ["class"])
return (
<RadioGroupPrimitive.ItemLabel
class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
local.class
)}
{...others}
/>
)
}
export { RadioGroup, RadioGroupItem, RadioGroupItemLabel }

View File

@@ -0,0 +1,65 @@
import type { ValidComponent } from "solid-js"
import { Show, splitProps } from "solid-js"
import type { DynamicProps, HandleProps, RootProps } from "@corvu/resizable"
import ResizablePrimitive from "@corvu/resizable"
import { cn } from "~/styles/utils"
type ResizableProps<T extends ValidComponent = "div"> = RootProps<T> & { class?: string }
const Resizable = <T extends ValidComponent = "div">(props: DynamicProps<T, ResizableProps<T>>) => {
const [, rest] = splitProps(props as ResizableProps, ["class"])
return (
<ResizablePrimitive
class={cn("flex size-full data-[orientation=vertical]:flex-col", props.class)}
{...rest}
/>
)
}
const ResizablePanel = ResizablePrimitive.Panel
type ResizableHandleProps<T extends ValidComponent = "button"> = HandleProps<T> & {
class?: string
withHandle?: boolean
}
const ResizableHandle = <T extends ValidComponent = "button">(
props: DynamicProps<T, ResizableHandleProps<T>>
) => {
const [, rest] = splitProps(props as ResizableHandleProps, ["class", "withHandle"])
return (
<ResizablePrimitive.Handle
class={cn(
"relative flex w-px shrink-0 items-center justify-center bg-border after:absolute after:inset-y-0 after:left-1/2 after:w-1 after:-translate-x-1/2 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring focus-visible:ring-offset-1 data-[orientation=vertical]:h-px data-[orientation=vertical]:w-full data-[orientation=vertical]:after:left-0 data-[orientation=vertical]:after:h-1 data-[orientation=vertical]:after:w-full data-[orientation=vertical]:after:-translate-y-1/2 data-[orientation=vertical]:after:translate-x-0 [&[data-orientation=vertical]>div]:rotate-90",
props.class
)}
{...rest}
>
<Show when={props.withHandle}>
<div class="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-2.5"
>
<path d="M9 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M9 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M9 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M15 5m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M15 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
<path d="M15 19m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" />
</svg>
</div>
</Show>
</ResizablePrimitive.Handle>
)
}
export { Resizable, ResizablePanel, ResizableHandle }

View File

@@ -0,0 +1,181 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as SelectPrimitive from "@kobalte/core/select"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
const Select = SelectPrimitive.Root
const SelectValue = SelectPrimitive.Value
const SelectHiddenSelect = SelectPrimitive.HiddenSelect
type SelectTriggerProps<T extends ValidComponent = "button"> =
SelectPrimitive.SelectTriggerProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const SelectTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, SelectTriggerProps<T>>
) => {
const [local, others] = splitProps(props as SelectTriggerProps, ["class", "children"])
return (
<SelectPrimitive.Trigger
class={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
local.class
)}
{...others}
>
{local.children}
<SelectPrimitive.Icon
as="svg"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4 opacity-50"
>
<path d="M8 9l4 -4l4 4" />
<path d="M16 15l-4 4l-4 -4" />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
)
}
type SelectContentProps<T extends ValidComponent = "div"> =
SelectPrimitive.SelectContentProps<T> & { class?: string | undefined }
const SelectContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SelectContentProps<T>>
) => {
const [local, others] = splitProps(props as SelectContentProps, ["class"])
return (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
class={cn(
"relative z-50 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md animate-in fade-in-80",
local.class
)}
{...others}
>
<SelectPrimitive.Listbox class="m-0 p-1" />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
)
}
type SelectItemProps<T extends ValidComponent = "li"> = SelectPrimitive.SelectItemProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const SelectItem = <T extends ValidComponent = "li">(
props: PolymorphicProps<T, SelectItemProps<T>>
) => {
const [local, others] = splitProps(props as SelectItemProps, ["class", "children"])
return (
<SelectPrimitive.Item
class={cn(
"relative mt-0 flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
local.class
)}
{...others}
>
<SelectPrimitive.ItemIndicator class="absolute right-2 flex size-3.5 items-center justify-center">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M5 12l5 5l10 -10" />
</svg>
</SelectPrimitive.ItemIndicator>
<SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel>
</SelectPrimitive.Item>
)
}
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
{
variants: {
variant: {
label: "data-[invalid]:text-destructive",
description: "font-normal text-muted-foreground",
error: "text-xs text-destructive"
}
},
defaultVariants: {
variant: "label"
}
}
)
type SelectLabelProps<T extends ValidComponent = "label"> = SelectPrimitive.SelectLabelProps<T> & {
class?: string | undefined
}
const SelectLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, SelectLabelProps<T>>
) => {
const [local, others] = splitProps(props as SelectLabelProps, ["class"])
return <SelectPrimitive.Label class={cn(labelVariants(), local.class)} {...others} />
}
type SelectDescriptionProps<T extends ValidComponent = "div"> =
SelectPrimitive.SelectDescriptionProps<T> & {
class?: string | undefined
}
const SelectDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SelectDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as SelectDescriptionProps, ["class"])
return (
<SelectPrimitive.Description
class={cn(labelVariants({ variant: "description" }), local.class)}
{...others}
/>
)
}
type SelectErrorMessageProps<T extends ValidComponent = "div"> =
SelectPrimitive.SelectErrorMessageProps<T> & {
class?: string | undefined
}
const SelectErrorMessage = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SelectErrorMessageProps<T>>
) => {
const [local, others] = splitProps(props as SelectErrorMessageProps, ["class"])
return (
<SelectPrimitive.ErrorMessage
class={cn(labelVariants({ variant: "error" }), local.class)}
{...others}
/>
)
}
export {
Select,
SelectValue,
SelectHiddenSelect,
SelectTrigger,
SelectContent,
SelectItem,
SelectLabel,
SelectDescription,
SelectErrorMessage
}

View File

@@ -0,0 +1,29 @@
import type { ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as SeparatorPrimitive from "@kobalte/core/separator"
import { cn } from "~/styles/utils"
type SeparatorRootProps<T extends ValidComponent = "hr"> =
SeparatorPrimitive.SeparatorRootProps<T> & { class?: string | undefined }
const Separator = <T extends ValidComponent = "hr">(
props: PolymorphicProps<T, SeparatorRootProps<T>>
) => {
const [local, others] = splitProps(props as SeparatorRootProps, ["class", "orientation"])
return (
<SeparatorPrimitive.Root
orientation={local.orientation ?? "horizontal"}
class={cn(
"shrink-0 bg-border",
local.orientation === "vertical" ? "h-full w-px" : "h-px w-full",
local.class
)}
{...others}
/>
)
}
export { Separator }

View File

@@ -0,0 +1,172 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import * as SheetPrimitive from "@kobalte/core/dialog"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "~/styles/utils"
const Sheet = SheetPrimitive.Root
const SheetTrigger = SheetPrimitive.Trigger
const SheetClose = SheetPrimitive.CloseButton
const portalVariants = cva("fixed inset-0 z-50 flex", {
variants: {
position: {
top: "items-start",
bottom: "items-end",
left: "justify-start",
right: "justify-end"
}
},
defaultVariants: { position: "right" }
})
type PortalProps = SheetPrimitive.DialogPortalProps & VariantProps<typeof portalVariants>
const SheetPortal: Component<PortalProps> = (props) => {
const [local, others] = splitProps(props, ["position", "children"])
return (
<SheetPrimitive.Portal {...others}>
<div class={portalVariants({ position: local.position })}>{local.children}</div>
</SheetPrimitive.Portal>
)
}
type DialogOverlayProps<T extends ValidComponent = "div"> = SheetPrimitive.DialogOverlayProps<T> & {
class?: string | undefined
}
const SheetOverlay = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DialogOverlayProps<T>>
) => {
const [local, others] = splitProps(props as DialogOverlayProps, ["class"])
return (
<SheetPrimitive.Overlay
class={cn(
"fixed inset-0 z-50 bg-black/80 data-[expanded=]:animate-in data-[closed=]:animate-out data-[closed=]:fade-out-0 data-[expanded=]:fade-in-0",
local.class
)}
{...others}
/>
)
}
const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[closed=]:duration-300 data-[expanded=]:duration-500 data-[expanded=]:animate-in data-[closed=]:animate-out",
{
variants: {
position: {
top: "inset-x-0 top-0 border-b data-[closed=]:slide-out-to-top data-[expanded=]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[closed=]:slide-out-to-bottom data-[expanded=]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[closed=]:slide-out-to-left data-[expanded=]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[closed=]:slide-out-to-right data-[expanded=]:slide-in-from-right sm:max-w-sm"
}
},
defaultVariants: {
position: "right"
}
}
)
type DialogContentProps<T extends ValidComponent = "div"> = SheetPrimitive.DialogContentProps<T> &
VariantProps<typeof sheetVariants> & { class?: string | undefined; children?: JSX.Element }
const SheetContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, DialogContentProps<T>>
) => {
const [local, others] = splitProps(props as DialogContentProps, ["position", "class", "children"])
return (
<SheetPortal position={local.position}>
<SheetOverlay />
<SheetPrimitive.Content
class={cn(
sheetVariants({ position: local.position }),
local.class,
"max-h-screen overflow-y-auto"
)}
{...others}
>
{local.children}
<SheetPrimitive.CloseButton class="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M18 6l-12 12" />
<path d="M6 6l12 12" />
</svg>
<span class="sr-only">Close</span>
</SheetPrimitive.CloseButton>
</SheetPrimitive.Content>
</SheetPortal>
)
}
const SheetHeader: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div class={cn("flex flex-col space-y-2 text-center sm:text-left", local.class)} {...others} />
)
}
const SheetFooter: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", local.class)}
{...others}
/>
)
}
type DialogTitleProps<T extends ValidComponent = "h2"> = SheetPrimitive.DialogTitleProps<T> & {
class?: string | undefined
}
const SheetTitle = <T extends ValidComponent = "h2">(
props: PolymorphicProps<T, DialogTitleProps<T>>
) => {
const [local, others] = splitProps(props as DialogTitleProps, ["class"])
return (
<SheetPrimitive.Title
class={cn("text-lg font-semibold text-foreground", local.class)}
{...others}
/>
)
}
type DialogDescriptionProps<T extends ValidComponent = "p"> =
SheetPrimitive.DialogDescriptionProps<T> & { class?: string | undefined }
const SheetDescription = <T extends ValidComponent = "p">(
props: PolymorphicProps<T, DialogDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as DialogDescriptionProps, ["class"])
return (
<SheetPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)}
{...others}
/>
)
}
export {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription
}

View File

@@ -0,0 +1,691 @@
import type { Accessor, Component, ComponentProps, JSX, ValidComponent } from "solid-js"
import {
createContext,
createEffect,
createMemo,
createSignal,
Match,
mergeProps,
onCleanup,
Show,
splitProps,
Switch,
useContext
} from "solid-js"
import type { PolymorphicProps } from "@kobalte/core"
import { Polymorphic } from "@kobalte/core"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
import type { ButtonProps } from "~/components/ui/button"
import { Button } from "~/components/ui/button"
import { Separator } from "~/components/ui/separator"
import { Sheet, SheetContent } from "~/components/ui/sheet"
import { Skeleton } from "~/components/ui/skeleton"
import { TextField, TextFieldInput } from "~/components/ui/text-field"
import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip"
const MOBILE_BREAKPOINT = 768
const SIDEBAR_COOKIE_NAME = "sidebar:state"
const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7
const SIDEBAR_WIDTH = "16rem"
const SIDEBAR_WIDTH_MOBILE = "18rem"
const SIDEBAR_WIDTH_ICON = "3rem"
const SIDEBAR_KEYBOARD_SHORTCUT = "b"
type SidebarContext = {
state: Accessor<"expanded" | "collapsed">
open: Accessor<boolean>
setOpen: (open: boolean) => void
openMobile: Accessor<boolean>
setOpenMobile: (open: boolean) => void
isMobile: Accessor<boolean>
toggleSidebar: () => void
}
const SidebarContext = createContext<SidebarContext | null>(null)
function useSidebar() {
const context = useContext(SidebarContext)
if (!context) {
throw new Error("useSidebar must be used within a Sidebar.")
}
return context
}
export function useIsMobile(fallback = false) {
const [isMobile, setIsMobile] = createSignal(fallback)
createEffect(() => {
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
const onChange = (e: MediaQueryListEvent | MediaQueryList) => {
setIsMobile(e.matches)
}
mql.addEventListener("change", onChange)
onChange(mql)
onCleanup(() => mql.removeEventListener("change", onChange))
})
return isMobile
}
type SidebarProviderProps = Omit<ComponentProps<"div">, "style"> & {
defaultOpen?: boolean
open?: boolean
onOpenChange?: (open: boolean) => void
style?: JSX.CSSProperties
}
const SidebarProvider: Component<SidebarProviderProps> = (rawProps) => {
const props = mergeProps({ defaultOpen: true }, rawProps)
const [local, others] = splitProps(props, [
"defaultOpen",
"open",
"onOpenChange",
"class",
"style",
"children"
])
const isMobile = useIsMobile()
const [openMobile, setOpenMobile] = createSignal(false)
// This is the internal state of the sidebar.
// We use open and onOpenChange for control from outside the component.
const [_open, _setOpen] = createSignal(local.defaultOpen)
const open = () => local.open ?? _open()
const setOpen = (value: boolean | ((value: boolean) => boolean)) => {
if (local.onOpenChange) {
return local.onOpenChange?.(typeof value === "function" ? value(open()) : value)
}
_setOpen(value)
// This sets the cookie to keep the sidebar state.
document.cookie = `${SIDEBAR_COOKIE_NAME}=${open()}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`
}
// Helper to toggle the sidebar.
const toggleSidebar = () => {
return isMobile() ? setOpenMobile((open) => !open) : setOpen((open) => !open)
}
// Adds a keyboard shortcut to toggle the sidebar.
createEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {
event.preventDefault()
toggleSidebar()
}
}
window.addEventListener("keydown", handleKeyDown)
onCleanup(() => window.removeEventListener("keydown", handleKeyDown))
})
// We add a state so that we can do data-state="expanded" or "collapsed".
// This makes it easier to style the sidebar with Tailwind classes.
const state = () => (open() ? "expanded" : "collapsed")
const contextValue = {
state,
open,
setOpen,
isMobile,
openMobile,
setOpenMobile,
toggleSidebar
}
return (
<SidebarContext.Provider value={contextValue}>
<div
style={{
"--sidebar-width": SIDEBAR_WIDTH,
"--sidebar-width-icon": SIDEBAR_WIDTH_ICON,
...local.style
}}
class={cn(
"group/sidebar-wrapper flex min-h-svh w-full text-sidebar-foreground has-[[data-variant=inset]]:bg-sidebar",
local.class
)}
{...others}
>
{local.children}
</div>
</SidebarContext.Provider>
)
}
type SidebarProps = ComponentProps<"div"> & {
side?: "left" | "right"
variant?: "sidebar" | "floating" | "inset"
collapsible?: "offcanvas" | "icon" | "none"
}
const Sidebar: Component<SidebarProps> = (rawProps) => {
const props = mergeProps<SidebarProps[]>(
{
side: "left",
variant: "sidebar",
collapsible: "offcanvas"
},
rawProps
)
const [local, others] = splitProps(props, ["side", "variant", "collapsible", "class", "children"])
const { isMobile, state, openMobile, setOpenMobile } = useSidebar()
return (
<Switch>
<Match when={local.collapsible === "none"}>
<div
class={cn(
"test flex h-full w-[--sidebar-width] flex-col bg-sidebar text-sidebar-foreground",
local.class
)}
{...others}
>
{local.children}
</div>
</Match>
<Match when={isMobile()}>
<Sheet open={openMobile()} onOpenChange={setOpenMobile} {...others}>
<SheetContent
data-sidebar="sidebar"
data-mobile="true"
class="w-[--sidebar-width] bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden"
style={{
"--sidebar-width": SIDEBAR_WIDTH_MOBILE
}}
position={local.side}
>
<div class="flex size-full flex-col">{local.children}</div>
</SheetContent>
</Sheet>
</Match>
<Match when={!isMobile()}>
<div
class="group peer hidden md:block"
data-state={state()}
data-collapsible={state() === "collapsed" ? local.collapsible : ""}
data-variant={local.variant}
data-side={local.side}
>
{/* This is what handles the sidebar gap on desktop */}
<div
class={cn(
"relative h-svh w-[--sidebar-width] bg-transparent transition-[width] duration-200 ease-linear",
"group-data-[collapsible=offcanvas]:w-0",
"group-data-[side=right]:rotate-180",
local.variant === "floating" || local.variant === "inset"
? "group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4))]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon]"
)}
/>
<div
class={cn(
"fixed inset-y-0 z-10 hidden h-svh w-[--sidebar-width] transition-[left,right,width] duration-200 ease-linear md:flex",
local.side === "left"
? "left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]"
: "right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]",
// Adjust the padding for floating and inset variants.
local.variant === "floating" || local.variant === "inset"
? "p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)_+_theme(spacing.4)_+2px)]"
: "group-data-[collapsible=icon]:w-[--sidebar-width-icon] group-data-[side=left]:border-r group-data-[side=right]:border-l",
local.class
)}
{...others}
>
<div
data-sidebar="sidebar"
class="flex size-full flex-col bg-sidebar group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:border-sidebar-border group-data-[variant=floating]:shadow"
>
{local.children}
</div>
</div>
</div>
</Match>
</Switch>
)
}
type SidebarTriggerProps<T extends ValidComponent = "button"> = ButtonProps<T> & {
onClick?: (event: MouseEvent) => void
}
const SidebarTrigger = <T extends ValidComponent = "button">(props: SidebarTriggerProps<T>) => {
const [local, others] = splitProps(props as SidebarTriggerProps, ["class", "onClick"])
const { toggleSidebar } = useSidebar()
return (
<Button
data-sidebar="trigger"
variant="ghost"
size="icon"
class={cn("size-7", local.class)}
onClick={(event: MouseEvent) => {
local.onClick?.(event)
toggleSidebar()
}}
{...others}
>
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<rect width="18" height="18" x="3" y="3" rx="2" />
<path d="M9 3v18" />
</svg>
<span class="sr-only">Toggle Sidebar</span>
</Button>
)
}
const SidebarRail: Component<ComponentProps<"button">> = (props) => {
const [local, others] = splitProps(props, ["class"])
const { toggleSidebar } = useSidebar()
return (
<button
data-sidebar="rail"
aria-label="Toggle Sidebar"
tabIndex={-1}
onClick={toggleSidebar}
title="Toggle Sidebar"
class={cn(
"absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-all ease-linear after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] hover:after:bg-sidebar-border group-data-[side=left]:-right-4 group-data-[side=right]:left-0 sm:flex",
"[[data-side=left]_&]:cursor-w-resize [[data-side=right]_&]:cursor-e-resize",
"[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize",
"group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full group-data-[collapsible=offcanvas]:hover:bg-sidebar",
"[[data-side=left][data-collapsible=offcanvas]_&]:-right-2",
"[[data-side=right][data-collapsible=offcanvas]_&]:-left-2",
local.class
)}
{...others}
/>
)
}
const SidebarInset: Component<ComponentProps<"main">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<main
class={cn(
"relative flex min-h-svh flex-1 flex-col bg-background",
"peer-data-[variant=inset]:min-h-[calc(100svh-theme(spacing.4))] md:peer-data-[variant=inset]:m-2 md:peer-data-[state=collapsed]:peer-data-[variant=inset]:ml-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow",
local.class
)}
{...others}
/>
)
}
type SidebarInputProps<T extends ValidComponent = "input"> = ComponentProps<
typeof TextFieldInput<T>
>
const SidebarInput = <T extends ValidComponent = "input">(props: SidebarInputProps<T>) => {
const [local, others] = splitProps(props as SidebarInputProps, ["class"])
return (
<TextField>
<TextFieldInput
data-sidebar="input"
class={cn(
"h-8 w-full bg-background shadow-none focus-visible:ring-2 focus-visible:ring-sidebar-ring",
local.class
)}
{...others}
/>
</TextField>
)
}
const SidebarHeader: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div data-sidebar="header" class={cn("flex flex-col gap-2 p-2", local.class)} {...others} />
)
}
const SidebarFooter: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div data-sidebar="footer" class={cn("flex flex-col gap-2 p-2", local.class)} {...others} />
)
}
type SidebarSeparatorProps<T extends ValidComponent = "hr"> = ComponentProps<typeof Separator<T>>
const SidebarSeparator = <T extends ValidComponent = "hr">(props: SidebarSeparatorProps<T>) => {
const [local, others] = splitProps(props as SidebarSeparatorProps, ["class"])
return (
<Separator
data-sidebar="separator"
class={cn("mx-2 w-auto bg-sidebar-border", local.class)}
{...others}
/>
)
}
const SidebarContent: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div
data-sidebar="content"
class={cn(
"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden",
local.class
)}
{...others}
/>
)
}
const SidebarGroup: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div
data-sidebar="group"
class={cn("relative flex w-full min-w-0 flex-col p-2", local.class)}
{...others}
/>
)
}
type SidebarGroupLabelProps<T extends ValidComponent = "div"> = ComponentProps<T>
const SidebarGroupLabel = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SidebarGroupLabelProps<T>>
) => {
const [local, others] = splitProps(props as SidebarGroupLabelProps, ["class"])
return (
<Polymorphic<SidebarGroupLabelProps>
as="div"
data-sidebar="group-label"
class={cn(
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 outline-none ring-sidebar-ring transition-[margin,opa] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
"group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0",
local.class
)}
{...others}
/>
)
}
type SidebarGroupActionProps<T extends ValidComponent = "button"> = ComponentProps<T>
const SidebarGroupAction = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, SidebarGroupActionProps<T>>
) => {
const [local, others] = splitProps(props as SidebarGroupActionProps, ["class"])
return (
<Polymorphic<SidebarGroupActionProps>
as="button"
data-sidebar="group-action"
class={cn(
"absolute right-3 top-3.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 after:md:hidden",
"group-data-[collapsible=icon]:hidden",
local.class
)}
{...others}
/>
)
}
const SidebarGroupContent: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <div data-sidebar="group-content" class={cn("w-full text-sm", local.class)} {...others} />
}
const SidebarMenu: Component<ComponentProps<"ul">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<ul
data-sidebar="menu"
class={cn("flex w-full min-w-0 flex-col gap-1", local.class)}
{...others}
/>
)
}
const SidebarMenuItem: Component<ComponentProps<"li">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<li data-sidebar="menu-item" class={cn("group/menu-item relative", local.class)} {...others} />
)
}
const sidebarMenuButtonVariants = cva(
"peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-none ring-sidebar-ring transition-[width,height,padding] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-[[data-sidebar=menu-action]]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:!size-8 group-data-[collapsible=icon]:!p-2 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0",
{
variants: {
variant: {
default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
outline:
"bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
},
size: {
default: "h-8 text-sm",
sm: "h-7 text-xs",
lg: "h-12 text-sm group-data-[collapsible=icon]:!p-0"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
)
type SidebarMenuButtonProps<T extends ValidComponent = "button"> = ComponentProps<T> &
VariantProps<typeof sidebarMenuButtonVariants> & {
isActive?: boolean
tooltip?: string
}
const SidebarMenuButton = <T extends ValidComponent = "button">(
rawProps: PolymorphicProps<T, SidebarMenuButtonProps<T>>
) => {
const props = mergeProps({ isActive: false, variant: "default", size: "default" }, rawProps)
const [local, others] = splitProps(props as SidebarMenuButtonProps, [
"isActive",
"tooltip",
"variant",
"size",
"class"
])
const { isMobile, state } = useSidebar()
const button = (
<Polymorphic<SidebarMenuButtonProps>
as="button"
data-sidebar="menu-button"
data-size={local.size}
data-active={local.isActive}
class={cn(
sidebarMenuButtonVariants({ variant: local.variant, size: local.size }),
local.class
)}
{...others}
/>
)
return (
<Show when={local.tooltip} fallback={button}>
<Tooltip placement="right">
<TooltipTrigger class="w-full">{button}</TooltipTrigger>
<TooltipContent hidden={state() !== "collapsed" || isMobile()}>
{local.tooltip}
</TooltipContent>
</Tooltip>
</Show>
)
}
type SidebarMenuActionProps<T extends ValidComponent = "button"> = ComponentProps<T> & {
showOnHover?: boolean
}
const SidebarMenuAction = <T extends ValidComponent = "button">(
rawProps: PolymorphicProps<T, SidebarMenuActionProps<T>>
) => {
const props = mergeProps({ showOnHover: false }, rawProps)
const [local, others] = splitProps(props as SidebarMenuActionProps, ["class", "showOnHover"])
return (
<Polymorphic<SidebarMenuActionProps>
as="button"
data-sidebar="menu-action"
class={cn(
"absolute right-1 top-1.5 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground outline-none ring-sidebar-ring transition-transform hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 peer-hover/menu-button:text-sidebar-accent-foreground [&>svg]:size-4 [&>svg]:shrink-0",
// Increases the hit area of the button on mobile.
"after:absolute after:-inset-2 after:md:hidden",
"peer-data-[size=sm]/menu-button:top-1",
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
local.showOnHover &&
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 peer-data-[active=true]/menu-button:text-sidebar-accent-foreground md:opacity-0",
local.class
)}
{...others}
/>
)
}
const SidebarMenuBadge: Component<ComponentProps<"div">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div
data-sidebar="menu-badge"
class={cn(
"pointer-events-none absolute right-1 flex h-5 min-w-5 select-none items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums text-sidebar-foreground",
"peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground",
"peer-data-[size=sm]/menu-button:top-1",
"peer-data-[size=default]/menu-button:top-1.5",
"peer-data-[size=lg]/menu-button:top-2.5",
"group-data-[collapsible=icon]:hidden",
local.class
)}
{...others}
/>
)
}
type SidebarMenuSkeletonProps = ComponentProps<"div"> & {
showIcon?: boolean
}
const SidebarMenuSkeleton: Component<SidebarMenuSkeletonProps> = (rawProps) => {
const props = mergeProps({ showIcon: false }, rawProps)
const [local, others] = splitProps(props, ["class", "showIcon"])
// Random width between 50 to 90%.
const width = createMemo(() => `${Math.floor(Math.random() * 40) + 50}%`)
return (
<div
data-sidebar="menu-skeleton"
class={cn("flex h-8 items-center gap-2 rounded-md px-2", local.class)}
{...others}
>
{local.showIcon && <Skeleton class="size-4 rounded-md" data-sidebar="menu-skeleton-icon" />}
<Skeleton
class="h-4 max-w-[--skeleton-width] flex-1"
data-sidebar="menu-skeleton-text"
style={{
"--skeleton-width": width()
}}
/>
</div>
)
}
const SidebarMenuSub: Component<ComponentProps<"ul">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<ul
data-sidebar="menu-sub"
class={cn(
"mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l border-sidebar-border px-2.5 py-0.5",
"group-data-[collapsible=icon]:hidden",
local.class
)}
{...others}
/>
)
}
const SidebarMenuSubItem: Component<ComponentProps<"li">> = (props) => <li {...props} />
type SidebarMenuSubButtonProps<T extends ValidComponent = "a"> = ComponentProps<T> & {
size?: "sm" | "md"
isActive?: boolean
}
const SidebarMenuSubButton = <T extends ValidComponent = "a">(
rawProps: PolymorphicProps<T, SidebarMenuSubButtonProps<T>>
) => {
const props = mergeProps({ size: "md" }, rawProps)
const [local, others] = splitProps(props as SidebarMenuSubButtonProps, [
"size",
"isActive",
"class"
])
return (
<Polymorphic<SidebarMenuSubButtonProps>
as="a"
data-sidebar="menu-sub-button"
data-size={local.size}
data-active={local.isActive}
class={cn(
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground outline-none ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
"data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground",
local.size === "sm" && "text-xs",
local.size === "md" && "text-sm",
"group-data-[collapsible=icon]:hidden",
local.class
)}
{...others}
/>
)
}
export {
Sidebar,
SidebarContent,
SidebarFooter,
SidebarGroup,
SidebarGroupAction,
SidebarGroupContent,
SidebarGroupLabel,
SidebarHeader,
SidebarInput,
SidebarInset,
SidebarMenu,
SidebarMenuAction,
SidebarMenuBadge,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSkeleton,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
SidebarProvider,
SidebarRail,
SidebarSeparator,
SidebarTrigger,
useSidebar
}

View File

@@ -0,0 +1,24 @@
import type { ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as SkeletonPrimitive from "@kobalte/core/skeleton"
import { cn } from "~/styles/utils"
type SkeletonRootProps<T extends ValidComponent = "div"> =
SkeletonPrimitive.SkeletonRootProps<T> & { class?: string | undefined }
const Skeleton = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SkeletonRootProps<T>>
) => {
const [local, others] = splitProps(props as SkeletonRootProps, ["class"])
return (
<SkeletonPrimitive.Root
class={cn("bg-primary/10 data-[animate='true']:animate-pulse", local.class)}
{...others}
/>
)
}
export { Skeleton }

View File

@@ -0,0 +1,92 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as SliderPrimitive from "@kobalte/core/slider"
import { cn } from "~/styles/utils"
import { Label } from "~/components/ui/label"
type SliderRootProps<T extends ValidComponent = "div"> = SliderPrimitive.SliderRootProps<T> & {
class?: string | undefined
}
const Slider = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SliderRootProps<T>>
) => {
const [local, others] = splitProps(props as SliderRootProps, ["class"])
return (
<SliderPrimitive.Root
class={cn("relative flex w-full touch-none select-none flex-col items-center", local.class)}
{...others}
/>
)
}
type SliderTrackProps<T extends ValidComponent = "div"> = SliderPrimitive.SliderTrackProps<T> & {
class?: string | undefined
}
const SliderTrack = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SliderTrackProps<T>>
) => {
const [local, others] = splitProps(props as SliderTrackProps, ["class"])
return (
<SliderPrimitive.Track
class={cn("relative h-2 w-full grow rounded-full bg-secondary", local.class)}
{...others}
/>
)
}
type SliderFillProps<T extends ValidComponent = "div"> = SliderPrimitive.SliderFillProps<T> & {
class?: string | undefined
}
const SliderFill = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SliderFillProps<T>>
) => {
const [local, others] = splitProps(props as SliderFillProps, ["class"])
return (
<SliderPrimitive.Fill
class={cn("absolute h-full rounded-full bg-primary", local.class)}
{...others}
/>
)
}
type SliderThumbProps<T extends ValidComponent = "span"> = SliderPrimitive.SliderThumbProps<T> & {
class?: string | undefined
children?: JSX.Element
}
const SliderThumb = <T extends ValidComponent = "span">(
props: PolymorphicProps<T, SliderThumbProps<T>>
) => {
const [local, others] = splitProps(props as SliderThumbProps, ["class", "children"])
return (
<SliderPrimitive.Thumb
class={cn(
"top-[-6px] block size-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
local.class
)}
{...others}
>
<SliderPrimitive.Input />
</SliderPrimitive.Thumb>
)
}
const SliderLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, SliderPrimitive.SliderLabelProps<T>>
) => {
return <SliderPrimitive.Label as={Label} {...props} />
}
const SliderValueLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, SliderPrimitive.SliderValueLabelProps<T>>
) => {
return <SliderPrimitive.ValueLabel as={Label} {...props} />
}
export { Slider, SliderTrack, SliderFill, SliderThumb, SliderLabel, SliderValueLabel }

View File

@@ -0,0 +1,25 @@
import type { Component, ComponentProps } from "solid-js"
import { Toaster as Sonner } from "solid-sonner"
type ToasterProps = ComponentProps<typeof Sonner>
const Toaster: Component<ToasterProps> = (props) => {
return (
<Sonner
class="toaster group"
toastOptions={{
classes: {
toast:
"group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
description: "group-[.toast]:text-muted-foreground",
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground"
}
}}
{...props}
/>
)
}
export { Toaster }

View File

@@ -0,0 +1,77 @@
import type { JSX, ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core"
import * as SwitchPrimitive from "@kobalte/core/switch"
import { cn } from "~/styles/utils"
const Switch = SwitchPrimitive.Root
const SwitchDescription = SwitchPrimitive.Description
const SwitchErrorMessage = SwitchPrimitive.ErrorMessage
type SwitchControlProps = SwitchPrimitive.SwitchControlProps & {
class?: string | undefined
children?: JSX.Element
}
const SwitchControl = <T extends ValidComponent = "input">(
props: PolymorphicProps<T, SwitchControlProps>
) => {
const [local, others] = splitProps(props as SwitchControlProps, ["class", "children"])
return (
<>
<SwitchPrimitive.Input
class={cn(
"[&:focus-visible+div]:outline-none [&:focus-visible+div]:ring-2 [&:focus-visible+div]:ring-ring [&:focus-visible+div]:ring-offset-2 [&:focus-visible+div]:ring-offset-background",
local.class
)}
/>
<SwitchPrimitive.Control
class={cn(
"inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent bg-input transition-[color,background-color,box-shadow] data-[disabled]:cursor-not-allowed data-[checked]:bg-primary data-[disabled]:opacity-50",
local.class
)}
{...others}
>
{local.children}
</SwitchPrimitive.Control>
</>
)
}
type SwitchThumbProps = SwitchPrimitive.SwitchThumbProps & { class?: string | undefined }
const SwitchThumb = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, SwitchThumbProps>
) => {
const [local, others] = splitProps(props as SwitchThumbProps, ["class"])
return (
<SwitchPrimitive.Thumb
class={cn(
"pointer-events-none block size-5 translate-x-0 rounded-full bg-background shadow-lg ring-0 transition-transform data-[checked]:translate-x-5",
local.class
)}
{...others}
/>
)
}
type SwitchLabelProps = SwitchPrimitive.SwitchLabelProps & { class?: string | undefined }
const SwitchLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, SwitchLabelProps>
) => {
const [local, others] = splitProps(props as SwitchLabelProps, ["class"])
return (
<SwitchPrimitive.Label
class={cn(
"text-sm font-medium leading-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70",
local.class
)}
{...others}
/>
)
}
export { Switch, SwitchControl, SwitchThumb, SwitchLabel, SwitchDescription, SwitchErrorMessage }

View File

@@ -0,0 +1,70 @@
import type { Component, ComponentProps } from "solid-js"
import { splitProps } from "solid-js"
import { cn } from "~/styles/utils"
const Table: Component<ComponentProps<"table">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<div class="relative w-full overflow-auto">
<table class={cn("w-full caption-bottom text-sm", local.class)} {...others} />
</div>
)
}
const TableHeader: Component<ComponentProps<"thead">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <thead class={cn("[&_tr]:border-b", local.class)} {...others} />
}
const TableBody: Component<ComponentProps<"tbody">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <tbody class={cn("[&_tr:last-child]:border-0", local.class)} {...others} />
}
const TableFooter: Component<ComponentProps<"tfoot">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<tfoot class={cn("bg-primary font-medium text-primary-foreground", local.class)} {...others} />
)
}
const TableRow: Component<ComponentProps<"tr">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<tr
class={cn(
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
local.class
)}
{...others}
/>
)
}
const TableHead: Component<ComponentProps<"th">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<th
class={cn(
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
local.class
)}
{...others}
/>
)
}
const TableCell: Component<ComponentProps<"td">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return (
<td class={cn("p-2 align-middle [&:has([role=checkbox])]:pr-0", local.class)} {...others} />
)
}
const TableCaption: Component<ComponentProps<"caption">> = (props) => {
const [local, others] = splitProps(props, ["class"])
return <caption class={cn("mt-4 text-sm text-muted-foreground", local.class)} {...others} />
}
export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }

View File

@@ -0,0 +1,87 @@
import type { ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as TabsPrimitive from "@kobalte/core/tabs"
import { cn } from "~/styles/utils"
const Tabs = TabsPrimitive.Root
type TabsListProps<T extends ValidComponent = "div"> = TabsPrimitive.TabsListProps<T> & {
class?: string | undefined
}
const TabsList = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TabsListProps<T>>
) => {
const [local, others] = splitProps(props as TabsListProps, ["class"])
return (
<TabsPrimitive.List
class={cn(
"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
local.class
)}
{...others}
/>
)
}
type TabsTriggerProps<T extends ValidComponent = "button"> = TabsPrimitive.TabsTriggerProps<T> & {
class?: string | undefined
}
const TabsTrigger = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, TabsTriggerProps<T>>
) => {
const [local, others] = splitProps(props as TabsTriggerProps, ["class"])
return (
<TabsPrimitive.Trigger
class={cn(
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[selected]:bg-background data-[selected]:text-foreground data-[selected]:shadow-sm",
local.class
)}
{...others}
/>
)
}
type TabsContentProps<T extends ValidComponent = "div"> = TabsPrimitive.TabsContentProps<T> & {
class?: string | undefined
}
const TabsContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TabsContentProps<T>>
) => {
const [local, others] = splitProps(props as TabsContentProps, ["class"])
return (
<TabsPrimitive.Content
class={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
local.class
)}
{...others}
/>
)
}
type TabsIndicatorProps<T extends ValidComponent = "div"> = TabsPrimitive.TabsIndicatorProps<T> & {
class?: string | undefined
}
const TabsIndicator = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TabsIndicatorProps<T>>
) => {
const [local, others] = splitProps(props as TabsIndicatorProps, ["class"])
return (
<TabsPrimitive.Indicator
class={cn(
"duration-250ms absolute transition-all data-[orientation=horizontal]:-bottom-px data-[orientation=vertical]:-right-px data-[orientation=horizontal]:h-[2px] data-[orientation=vertical]:w-[2px]",
local.class
)}
{...others}
/>
)
}
export { Tabs, TabsList, TabsTrigger, TabsContent, TabsIndicator }

View File

@@ -0,0 +1,152 @@
import type { ValidComponent } from "solid-js"
import { mergeProps, splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core"
import * as TextFieldPrimitive from "@kobalte/core/text-field"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
type TextFieldRootProps<T extends ValidComponent = "div"> =
TextFieldPrimitive.TextFieldRootProps<T> & {
class?: string | undefined
}
const TextField = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TextFieldRootProps<T>>
) => {
const [local, others] = splitProps(props as TextFieldRootProps, ["class"])
return <TextFieldPrimitive.Root class={cn("flex flex-col gap-1", local.class)} {...others} />
}
type TextFieldInputProps<T extends ValidComponent = "input"> =
TextFieldPrimitive.TextFieldInputProps<T> & {
class?: string | undefined
type?:
| "button"
| "checkbox"
| "color"
| "date"
| "datetime-local"
| "email"
| "file"
| "hidden"
| "image"
| "month"
| "number"
| "password"
| "radio"
| "range"
| "reset"
| "search"
| "submit"
| "tel"
| "text"
| "time"
| "url"
| "week"
}
const TextFieldInput = <T extends ValidComponent = "input">(
rawProps: PolymorphicProps<T, TextFieldInputProps<T>>
) => {
const props = mergeProps<TextFieldInputProps<T>[]>({ type: "text" }, rawProps)
const [local, others] = splitProps(props as TextFieldInputProps, ["type", "class"])
return (
<TextFieldPrimitive.Input
type={local.type}
class={cn(
"flex h-10 w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-error-foreground data-[invalid]:text-error-foreground",
local.class
)}
{...others}
/>
)
}
type TextFieldTextAreaProps<T extends ValidComponent = "textarea"> =
TextFieldPrimitive.TextFieldTextAreaProps<T> & { class?: string | undefined }
const TextFieldTextArea = <T extends ValidComponent = "textarea">(
props: PolymorphicProps<T, TextFieldTextAreaProps<T>>
) => {
const [local, others] = splitProps(props as TextFieldTextAreaProps, ["class"])
return (
<TextFieldPrimitive.TextArea
class={cn(
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
local.class
)}
{...others}
/>
)
}
const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
{
variants: {
variant: {
label: "data-[invalid]:text-destructive",
description: "font-normal text-muted-foreground",
error: "text-xs text-destructive"
}
},
defaultVariants: {
variant: "label"
}
}
)
type TextFieldLabelProps<T extends ValidComponent = "label"> =
TextFieldPrimitive.TextFieldLabelProps<T> & { class?: string | undefined }
const TextFieldLabel = <T extends ValidComponent = "label">(
props: PolymorphicProps<T, TextFieldLabelProps<T>>
) => {
const [local, others] = splitProps(props as TextFieldLabelProps, ["class"])
return <TextFieldPrimitive.Label class={cn(labelVariants(), local.class)} {...others} />
}
type TextFieldDescriptionProps<T extends ValidComponent = "div"> =
TextFieldPrimitive.TextFieldDescriptionProps<T> & {
class?: string | undefined
}
const TextFieldDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TextFieldDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as TextFieldDescriptionProps, ["class"])
return (
<TextFieldPrimitive.Description
class={cn(labelVariants({ variant: "description" }), local.class)}
{...others}
/>
)
}
type TextFieldErrorMessageProps<T extends ValidComponent = "div"> =
TextFieldPrimitive.TextFieldErrorMessageProps<T> & {
class?: string | undefined
}
const TextFieldErrorMessage = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TextFieldErrorMessageProps<T>>
) => {
const [local, others] = splitProps(props as TextFieldErrorMessageProps, ["class"])
return (
<TextFieldPrimitive.ErrorMessage
class={cn(labelVariants({ variant: "error" }), local.class)}
{...others}
/>
)
}
export {
TextField,
TextFieldInput,
TextFieldTextArea,
TextFieldLabel,
TextFieldDescription,
TextFieldErrorMessage
}

View File

@@ -0,0 +1,145 @@
import type { ComponentProps, ParentComponent } from "solid-js"
import { For, mergeProps, Show, splitProps, type Component, type JSXElement } from "solid-js"
import { cn } from "~/styles/utils"
export type TimelinePropsItem = Omit<
TimelineItemProps,
"isActive" | "isActiveBullet" | "bulletSize" | "lineSize"
> & {
bulletSize?: number
}
export type TimelineProps = {
items: TimelinePropsItem[]
activeItem: number
bulletSize?: number
lineSize?: number
}
/*
No bullet or line is active when activeItem is -1
First bullet is active only if activeItem is 0 or more
First line is active only if activeItem is 1 or more
*/
const Timeline: Component<TimelineProps> = (rawProps) => {
const props = mergeProps({ bulletSize: 16, lineSize: 2 }, rawProps)
return (
<ul
style={{
"padding-left": `${props.bulletSize / 2}px`
}}
>
<For each={props.items}>
{(item, index) => (
<TimelineItem
title={item.title}
description={item.description}
bullet={item.bullet}
isLast={index() === props.items.length - 1}
isActive={props.activeItem === -1 ? false : props.activeItem >= index() + 1}
isActiveBullet={props.activeItem === -1 ? false : props.activeItem >= index()}
bulletSize={props.bulletSize}
lineSize={props.lineSize}
/>
)}
</For>
</ul>
)
}
export type TimelineItemProps = {
title: JSXElement
description?: JSXElement
bullet?: JSXElement
isLast?: boolean
isActive: boolean
isActiveBullet: boolean
class?: string
bulletSize: number
lineSize: number
}
const TimelineItem: Component<TimelineItemProps> = (props) => {
const [local, others] = splitProps(props, [
"class",
"bullet",
"description",
"title",
"isLast",
"isActive",
"isActiveBullet",
"bulletSize",
"lineSize"
])
return (
<li
class={cn(
"relative border-l pb-8 pl-8",
local.isLast && "border-l-transparent pb-0",
local.isActive && !local.isLast && "border-l-primary",
local.class
)}
style={{
"border-left-width": `${local.lineSize}px`
}}
{...others}
>
<TimelineItemBullet
lineSize={local.lineSize}
bulletSize={local.bulletSize}
isActive={local.isActiveBullet}
>
{local.bullet}
</TimelineItemBullet>
<TimelineItemTitle>{local.title}</TimelineItemTitle>
<Show when={local.description}>
<TimelineItemDescription>{local.description}</TimelineItemDescription>
</Show>
</li>
)
}
export type TimelineItemBulletProps = {
children?: JSXElement
isActive?: boolean
bulletSize: number
lineSize: number
}
const TimelineItemBullet: Component<TimelineItemBulletProps> = (props) => {
return (
<div
class={cn(
`absolute top-0 flex items-center justify-center rounded-full border bg-background`,
props.isActive && "border-primary"
)}
style={{
width: `${props.bulletSize}px`,
height: `${props.bulletSize}px`,
left: `${-props.bulletSize / 2 - props.lineSize / 2}px`,
"border-width": `${props.lineSize}px`
}}
aria-hidden="true"
>
{props.children}
</div>
)
}
const TimelineItemTitle: ParentComponent = (props) => {
return <div class="mb-1 text-base font-semibold leading-none">{props.children}</div>
}
const TimelineItemDescription: Component<ComponentProps<"p">> = (props) => {
const [local, others] = splitProps(props, ["class", "children"])
return (
<p class={cn("text-sm text-muted-foreground", local.class)} {...others}>
{local.children}
</p>
)
}
export { Timeline }

View File

@@ -0,0 +1,163 @@
import type { JSX, ValidComponent } from "solid-js"
import { Match, splitProps, Switch } from "solid-js"
import { Portal } from "solid-js/web"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as ToastPrimitive from "@kobalte/core/toast"
import type { VariantProps } from "class-variance-authority"
import { cva } from "class-variance-authority"
import { cn } from "~/styles/utils"
const toastVariants = cva(
"group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--kb-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--kb-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[opened]:animate-in data-[closed]:animate-out data-[swipe=end]:animate-out data-[closed]:fade-out-80 data-[closed]:slide-out-to-right-full data-[opened]:slide-in-from-top-full data-[opened]:sm:slide-in-from-bottom-full",
{
variants: {
variant: {
default: "border bg-background text-foreground",
destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground",
success: "success border-success-foreground bg-success text-success-foreground",
warning: "warning border-warning-foreground bg-warning text-warning-foreground",
error: "error border-error-foreground bg-error text-error-foreground"
}
},
defaultVariants: {
variant: "default"
}
}
)
type ToastVariant = NonNullable<VariantProps<typeof toastVariants>["variant"]>
type ToastListProps<T extends ValidComponent = "ol"> = ToastPrimitive.ToastListProps<T> & {
class?: string | undefined
}
const Toaster = <T extends ValidComponent = "ol">(
props: PolymorphicProps<T, ToastListProps<T>>
) => {
const [local, others] = splitProps(props as ToastListProps, ["class"])
return (
<Portal>
<ToastPrimitive.Region>
<ToastPrimitive.List
class={cn(
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
local.class
)}
{...others}
/>
</ToastPrimitive.Region>
</Portal>
)
}
type ToastRootProps<T extends ValidComponent = "li"> = ToastPrimitive.ToastRootProps<T> &
VariantProps<typeof toastVariants> & { class?: string | undefined }
const Toast = <T extends ValidComponent = "li">(props: PolymorphicProps<T, ToastRootProps<T>>) => {
const [local, others] = splitProps(props as ToastRootProps, ["class", "variant"])
return (
<ToastPrimitive.Root
class={cn(toastVariants({ variant: local.variant }), local.class)}
{...others}
/>
)
}
type ToastCloseButtonProps<T extends ValidComponent = "button"> =
ToastPrimitive.ToastCloseButtonProps<T> & { class?: string | undefined }
const ToastClose = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, ToastCloseButtonProps<T>>
) => {
const [local, others] = splitProps(props as ToastCloseButtonProps, ["class"])
return (
<ToastPrimitive.CloseButton
class={cn(
"absolute right-2 top-2 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity focus:opacity-100 focus:outline-none focus:ring-2 group-hover:opacity-100 group-[.destructive]:text-destructive-foreground group-[.error]:text-error-foreground group-[.success]:text-success-foreground group-[.warning]:text-warning-foreground",
local.class
)}
{...others}
>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="size-4"
>
<path d="M18 6l-12 12" />
<path d="M6 6l12 12" />
</svg>
</ToastPrimitive.CloseButton>
)
}
type ToastTitleProps<T extends ValidComponent = "div"> = ToastPrimitive.ToastTitleProps<T> & {
class?: string | undefined
}
const ToastTitle = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ToastTitleProps<T>>
) => {
const [local, others] = splitProps(props as ToastTitleProps, ["class"])
return <ToastPrimitive.Title class={cn("text-sm font-semibold", local.class)} {...others} />
}
type ToastDescriptionProps<T extends ValidComponent = "div"> =
ToastPrimitive.ToastDescriptionProps<T> & { class?: string | undefined }
const ToastDescription = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ToastDescriptionProps<T>>
) => {
const [local, others] = splitProps(props as ToastDescriptionProps, ["class"])
return <ToastPrimitive.Description class={cn("text-sm opacity-90", local.class)} {...others} />
}
function showToast(props: {
title?: JSX.Element
description?: JSX.Element
variant?: ToastVariant
duration?: number
}) {
ToastPrimitive.toaster.show((data) => (
<Toast toastId={data.toastId} variant={props.variant} duration={props.duration}>
<div class="grid gap-1">
{props.title && <ToastTitle>{props.title}</ToastTitle>}
{props.description && <ToastDescription>{props.description}</ToastDescription>}
</div>
<ToastClose />
</Toast>
))
}
function showToastPromise<T, U>(
promise: Promise<T> | (() => Promise<T>),
options: {
loading?: JSX.Element
success?: (data: T) => JSX.Element
error?: (error: U) => JSX.Element
duration?: number
}
) {
const variant: { [key in ToastPrimitive.ToastPromiseState]: ToastVariant } = {
pending: "default",
fulfilled: "success",
rejected: "error"
}
return ToastPrimitive.toaster.promise<T, U>(promise, (props) => (
<Toast toastId={props.toastId} variant={variant[props.state]} duration={options.duration}>
<Switch>
<Match when={props.state === "pending"}>{options.loading}</Match>
<Match when={props.state === "fulfilled"}>{options.success?.(props.data!)}</Match>
<Match when={props.state === "rejected"}>{options.error?.(props.error!)}</Match>
</Switch>
</Toast>
))
}
export { Toaster, Toast, ToastClose, ToastTitle, ToastDescription, showToast, showToastPromise }

View File

@@ -0,0 +1,75 @@
import type { JSX, ValidComponent } from "solid-js"
import { createContext, splitProps, useContext } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as ToggleGroupPrimitive from "@kobalte/core/toggle-group"
import type { VariantProps } from "class-variance-authority"
import { cn } from "~/styles/utils"
import { toggleVariants } from "~/components/ui/toggle"
const ToggleGroupContext = createContext<VariantProps<typeof toggleVariants>>({
size: "default",
variant: "default"
})
type ToggleGroupRootProps<T extends ValidComponent = "div"> =
ToggleGroupPrimitive.ToggleGroupRootProps<T> &
VariantProps<typeof toggleVariants> & { class?: string | undefined; children?: JSX.Element }
const ToggleGroup = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, ToggleGroupRootProps<T>>
) => {
const [local, others] = splitProps(props as ToggleGroupRootProps, [
"class",
"children",
"size",
"variant"
])
return (
<ToggleGroupPrimitive.Root
class={cn("flex items-center justify-center gap-1", local.class)}
{...others}
>
<ToggleGroupContext.Provider
value={{
get size() {
return local.size
},
get variant() {
return local.variant
}
}}
>
{local.children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
)
}
type ToggleGroupItemProps<T extends ValidComponent = "button"> =
ToggleGroupPrimitive.ToggleGroupItemProps<T> &
VariantProps<typeof toggleVariants> & { class?: string | undefined }
const ToggleGroupItem = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, ToggleGroupItemProps<T>>
) => {
const [local, others] = splitProps(props as ToggleGroupItemProps, ["class", "size", "variant"])
const context = useContext(ToggleGroupContext)
return (
<ToggleGroupPrimitive.Item
class={cn(
toggleVariants({
size: context.size || local.size,
variant: context.variant || local.variant
}),
"hover:bg-muted hover:text-muted-foreground data-[pressed]:bg-accent data-[pressed]:text-accent-foreground",
local.class
)}
{...others}
/>
)
}
export { ToggleGroup, ToggleGroupItem }

View File

@@ -0,0 +1,49 @@
import type { ValidComponent } from "solid-js"
import { splitProps } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as ToggleButtonPrimitive from "@kobalte/core/toggle-button"
import { cva } from "class-variance-authority"
import type { VariantProps } from "class-variance-authority"
import { cn } from "~/styles/utils"
const toggleVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
{
variants: {
variant: {
default: "bg-transparent",
outline: "border border-input bg-transparent shadow-sm"
},
size: {
default: "h-9 px-3",
sm: "h-8 px-2",
lg: "h-10 px-3"
}
},
defaultVariants: {
variant: "default",
size: "default"
}
}
)
type ToggleButtonRootProps<T extends ValidComponent = "button"> =
ToggleButtonPrimitive.ToggleButtonRootProps<T> &
VariantProps<typeof toggleVariants> & { class?: string | undefined }
const Toggle = <T extends ValidComponent = "button">(
props: PolymorphicProps<T, ToggleButtonRootProps<T>>
) => {
const [local, others] = splitProps(props as ToggleButtonRootProps, ["class", "variant", "size"])
return (
<ToggleButtonPrimitive.Root
class={cn(toggleVariants({ variant: local.variant, size: local.size }), local.class)}
{...others}
/>
)
}
export type { ToggleButtonRootProps as ToggleProps }
export { toggleVariants, Toggle }

View File

@@ -0,0 +1,35 @@
import type { ValidComponent } from "solid-js"
import { splitProps, type Component } from "solid-js"
import type { PolymorphicProps } from "@kobalte/core/polymorphic"
import * as TooltipPrimitive from "@kobalte/core/tooltip"
import { cn } from "~/styles/utils"
const TooltipTrigger = TooltipPrimitive.Trigger
const Tooltip: Component<TooltipPrimitive.TooltipRootProps> = (props) => {
return <TooltipPrimitive.Root gutter={4} {...props} />
}
type TooltipContentProps<T extends ValidComponent = "div"> =
TooltipPrimitive.TooltipContentProps<T> & { class?: string | undefined }
const TooltipContent = <T extends ValidComponent = "div">(
props: PolymorphicProps<T, TooltipContentProps<T>>
) => {
const [local, others] = splitProps(props as TooltipContentProps, ["class"])
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
class={cn(
"z-50 origin-[var(--kb-popover-content-transform-origin)] overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
local.class
)}
{...others}
/>
</TooltipPrimitive.Portal>
)
}
export { Tooltip, TooltipTrigger, TooltipContent }

1
apps/webui/src/env.d.ts vendored Normal file
View File

@@ -0,0 +1 @@
/// <reference types="@rsbuild/core/types" />

28
apps/webui/src/main.tsx Normal file
View File

@@ -0,0 +1,28 @@
import { RouterProvider, createRouter } from '@tanstack/solid-router';
import { render } from 'solid-js/web';
import './app.css';
// Import the generated route tree
import { routeTree } from './routeTree.gen';
// Create a new router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
defaultStaleTime: 5000,
scrollRestoration: true,
});
// Register the router instance for type safety
declare module '@tanstack/solid-router' {
interface Register {
router: typeof router;
}
}
// Render the app
const rootElement = document.getElementById('root');
if (rootElement && !rootElement.innerHTML) {
render(() => <RouterProvider router={router} />, rootElement);
}

View File

@@ -0,0 +1,111 @@
/* eslint-disable */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
// Import Routes
import { Route as rootRoute } from './routes/__root'
import { Route as AboutImport } from './routes/about'
import { Route as IndexImport } from './routes/index'
// Create/Update Routes
const AboutRoute = AboutImport.update({
id: '/about',
path: '/about',
getParentRoute: () => rootRoute,
} as any)
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/solid-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/about': {
id: '/about'
path: '/about'
fullPath: '/about'
preLoaderRoute: typeof AboutImport
parentRoute: typeof rootRoute
}
}
}
// Create and export the route tree
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/about': typeof AboutRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/about': typeof AboutRoute
}
export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexRoute
'/about': typeof AboutRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/about'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/about'
id: '__root__' | '/' | '/about'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
AboutRoute: typeof AboutRoute
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
AboutRoute: AboutRoute,
}
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/about"
]
},
"/": {
"filePath": "index.tsx"
},
"/about": {
"filePath": "about.tsx"
}
}
}
ROUTE_MANIFEST_END */

View File

@@ -0,0 +1,34 @@
import { Link, Outlet, createRootRoute } from '@tanstack/solid-router';
import {
NavigationMenu,
NavigationMenuItem,
NavigationMenuLink,
NavigationMenuTrigger,
} from '~/components/ui/navigation-menu';
export const Route = createRootRoute({
component: () => {
return (
<>
<div class="flex space-y-4">
<NavigationMenu orientation="horizontal">
<NavigationMenuItem>
<NavigationMenuTrigger>
<NavigationMenuLink as={Link} to="/">
Home
</NavigationMenuLink>
</NavigationMenuTrigger>
</NavigationMenuItem>
<NavigationMenuItem>
<NavigationMenuLink as={Link} to="/about">
About
</NavigationMenuLink>
</NavigationMenuItem>
</NavigationMenu>
</div>
<hr />
<Outlet />
</>
);
},
});

View File

@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/about')({
component: About,
})
function About() {
return <div class="p-2">Hello from About!</div>
}

View File

@@ -0,0 +1,13 @@
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/')({
component: Index,
})
function Index() {
return (
<div class="p-2">
<h3>Welcome Home!</h3>
</div>
)
}

260
apps/webui/src/style.css Normal file
View File

@@ -0,0 +1,260 @@
/* src/app.css or put it where you have your main .css file */
@import 'tailwindcss';
@plugin 'tailwindcss-animate';
@plugin '@kobalte/tailwindcss';
@custom-variant dark (.dark &,[data-kb-theme="dark"] &);
@theme {
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-info: var(--info);
--color-info-foreground: var(--info-foreground);
--color-success: var(--success);
--color-success-foreground: var(--success-foreground);
--color-warning: var(--warning);
--color-warning-foreground: var(--warning-foreground);
--color-error: var(--error);
--color-error-foreground: var(--error-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--radius: 0.5rem;
--radius-xl: calc(var(--radius) + 4px);
--radius-lg: var(--radius);
--radius-md: calc(var(--radius) - 2px);
--radius-sm: calc(var(--radius) - 4px);
@keyframes accordion-down {
from {
height: 0;
}
to {
height: var(--kb-accordion-content-height);
}
}
@keyframes accordion-up {
from {
height: var(--kb-accordion-content-height);
}
to {
height: 0;
}
}
@keyframes content-show {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes content-hide {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.96);
}
}
@keyframes caret-blink {
0%,
70%,
100% {
opacity: 1;
}
20%,
50% {
opacity: 0;
}
}
--animate-accordion-down: accordion-down 0.2s ease-out;
--animate-accordion-up: accordion-up 0.2s ease-out;
--animate-content-show: content-show 0.2s ease-out;
--animate-content-hide: content-hide 0.2s ease-out;
--animate-caret-blink: caret-blink 1.25s ease-out infinite;
}
@utility container {
margin-inline: auto;
padding-inline: 2rem;
@media (width >= --theme(--breakpoint-sm)) {
max-width: none;
}
@media (width >= 1400px) {
max-width: 1400px;
}
}
@utility step {
counter-increment: step;
&:before {
@apply absolute w-9 h-9 bg-muted rounded-full font-mono font-medium text-center text-base inline-flex items-center justify-center -indent-px border-4 border-background;
@apply ml-[-50px] mt-[-4px];
content: counter(step);
}
}
@layer base {
:root {
--color-background: hsl(0 0% 100%);
--color-foreground: hsl(240 10% 3.9%);
--color-muted: hsl(240 4.8% 95.9%);
--color-muted-foreground: hsl(240 3.8% 46.1%);
--color-popover: hsl(0, 83%, 30%);
--color-popover-foreground: hsl(240 10% 3.9%);
--color-border: hsl(240 5.9% 90%);
--color-input: hsl(240 5.9% 90%);
--color-card: hsl(0 0% 100%);
--color-card-foreground: hsl(240 10% 3.9%);
--color-primary: hsl(240 5.9% 10%);
--color-primary-foreground: hsl(0 0% 98%);
--color-secondary: hsl(240 4.8% 95.9%);
--color-secondary-foreground: hsl(240 5.9% 10%);
--color-accent: hsl(240 4.8% 95.9%);
--color-accent-foreground: hsl(240 5.9% 10%);
--color-destructive: hsl(0 84.2% 60.2%);
--color-destructive-foreground: hsl(0 0% 98%);
--color-info: hsl(204 94% 94%);
--color-info-foreground: hsl(199 89% 48%);
--color-success: hsl(149 80% 90%);
--color-success-foreground: hsl(160 84% 39%);
--color-warning: hsl(48 96% 89%);
--color-warning-foreground: hsl(25 95% 53%);
--color-error: hsl(0 93% 94%);
--color-error-foreground: hsl(0 84% 60%);
--color-ring: hsl(240 5.9% 10%);
}
.dark,
[data-kb-theme="dark"] {
--color-background: hsl(240 10% 3.9%);
--color-foreground: hsl(0 0% 98%);
--color-muted: hsl(240 3.7% 15.9%);
--color-muted-foreground: hsl(240 5% 64.9%);
--color-accent: hsl(240 3.7% 15.9%);
--color-accent-foreground: hsl(0 0% 98%);
--color-popover: hsl(240 10% 3.9%);
--color-popover-foreground: hsl(0 0% 98%);
--color-border: hsl(240 3.7% 15.9%);
--color-input: hsl(240 3.7% 15.9%);
--color-card: hsl(240 10% 3.9%);
--color-card-foreground: hsl(0 0% 98%);
--color-primary: hsl(0 0% 98%);
--color-primary-foreground: hsl(240 5.9% 10%);
--color-secondary: hsl(240 3.7% 15.9%);
--color-secondary-foreground: hsl(0 0% 98%);
--color-destructive: hsl(0 62.8% 30.6%);
--color-destructive-foreground: hsl(0 0% 98%);
--color-info: hsl(226.2 57% 21%);
--color-info-foreground: hsl(199 89% 48%);
--color-success: hsl(165.7 91.3% 9%);
--color-success-foreground: hsl(160 84% 39%);
--color-warning: hsl(26 83.3% 14.1%);
--color-warning-foreground: hsl(25 95% 53%);
--color-error: hsl(0 74.7% 15.5%);
--color-error-foreground: hsl(0 84% 60%);
--color-ring: hsl(240 4.9% 83.9%);
}
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}
@media (max-width: 640px) {
.container {
@apply px-4;
}
}
::-webkit-scrollbar {
width: 16px;
}
::-webkit-scrollbar-thumb {
border-radius: 9999px;
border: 4px solid transparent;
background-clip: content-box;
@apply bg-accent;
}
::-webkit-scrollbar-corner {
display: none;
}
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}

View File

@@ -0,0 +1,7 @@
import type { ClassValue } from 'clsx';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@@ -0,0 +1,98 @@
/**@type {import("tailwindcss").Config} */
module.exports = {
darkMode: ["variant", [".dark &", '[data-kb-theme="dark"] &']],
content: ["./src/**/*.{ts,tsx}"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px"
}
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))"
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))"
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))"
},
info: {
DEFAULT: "hsl(var(--info))",
foreground: "hsl(var(--info-foreground))"
},
success: {
DEFAULT: "hsl(var(--success))",
foreground: "hsl(var(--success-foreground))"
},
warning: {
DEFAULT: "hsl(var(--warning))",
foreground: "hsl(var(--warning-foreground))"
},
error: {
DEFAULT: "hsl(var(--error))",
foreground: "hsl(var(--error-foreground))"
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))"
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))"
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))"
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))"
}
},
borderRadius: {
xl: "calc(var(--radius) + 4px)",
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)"
},
keyframes: {
"accordion-down": {
from: { height: 0 },
to: { height: "var(--kb-accordion-content-height)" }
},
"accordion-up": {
from: { height: "var(--kb-accordion-content-height)" },
to: { height: 0 }
},
"content-show": {
from: { opacity: 0, transform: "scale(0.96)" },
to: { opacity: 1, transform: "scale(1)" }
},
"content-hide": {
from: { opacity: 1, transform: "scale(1)" },
to: { opacity: 0, transform: "scale(0.96)" }
}
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"content-show": "content-show 0.2s ease-out",
"content-hide": "content-hide 0.2s ease-out"
}
}
},
plugins: [require("tailwindcss-animate")]
}

15
apps/webui/tsconfig.json Normal file
View File

@@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",
"composite": true,
"jsx": "preserve",
"jsxImportSource": "solid-js",
"module": "ESNext",
"moduleResolution": "bundler",
"paths": {
"~/*": ["./src/*"]
}
},
"include": ["src"]
}

View File

@@ -0,0 +1,4 @@
{
"routesDirectory": "./src/routes",
"generatedRouteTree": "./src/routeTree.gen.ts"
}

13
apps/webui/ui.config.json Normal file
View File

@@ -0,0 +1,13 @@
{
"$schema": "https://solid-ui.com/schema.json",
"tsx": true,
"tailwind": {
"css": "src/app.css",
"config": "tailwind.config.cjs",
"prefix": ""
},
"aliases": {
"components": "~/components/ui",
"utils": "~/styles/utils"
}
}