refactor: merge playground into webui

This commit is contained in:
2025-03-07 01:50:53 +08:00
parent 383e6340ea
commit 27cdcdef58
100 changed files with 4119 additions and 3757 deletions

View File

@@ -1,24 +0,0 @@
{
"name": "recorder",
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "rsbuild dev",
"build": "rsbuild build",
"preview": "rsbuild preview"
},
"dependencies": {
"@graphiql/react": "^0.28.2",
"@graphiql/toolkit": "^0.11.1",
"graphiql": "^3.8.3",
"graphql-ws": "^6.0.4",
"observable-hooks": "^4.2.4",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
"devDependencies": {
"@rsbuild/plugin-react": "^1.1.1",
"@types/react": "^19.0.7",
"@types/react-dom": "^19.0.3"
}
}

View File

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

View File

@@ -1 +0,0 @@
../../../../assets/favicon.ico

View File

@@ -1,75 +0,0 @@
import { defineConfig } from '@rsbuild/core';
import { pluginReact } from '@rsbuild/plugin-react';
import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack';
export default defineConfig({
plugins: [pluginReact()],
html: {
favicon: './public/assets/favicon.ico',
// tags: [
// {
// tag: 'script',
// attrs: { src: 'https://cdn.tailwindcss.com' },
// },
// ],
},
tools: {
rspack: {
plugins: [TanStackRouterRspack()],
},
},
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
),
},
},
dev: {
client: {
path: '/api/playground/rsbuild-hmr',
},
setupMiddlewares: [
(middlewares) => {
middlewares.unshift((req, res, next) => {
if (process.env.AUTH_TYPE === 'basic') {
res.setHeader('WWW-Authenticate', 'Basic realm="konobangu"');
const authorization =
(req.headers.authorization || '').split(' ')[1] || '';
const [user, password] = Buffer.from(authorization, 'base64')
.toString()
.split(':');
if (
user !== process.env.BASIC_USER ||
password !== process.env.BASIC_PASSWORD
) {
res.statusCode = 401;
res.write('Unauthorized');
res.end();
return;
}
}
next();
});
return middlewares;
},
],
},
server: {
base: '/api/playground/',
host: '0.0.0.0',
port: 5002,
},
});

View File

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

View File

@@ -1 +0,0 @@
@import "tailwindcss";

View File

@@ -1,96 +0,0 @@
import '@abraham/reflection';
import { type Injector, ReflectiveInjector } from '@outposts/injection-js';
import { RouterProvider, createRouter } from '@tanstack/react-router';
import {
OidcSecurityService,
provideAuth,
withCheckAuthResultEvent,
withDefaultFeatures,
} from 'oidc-client-rx';
import { withTanstackRouter } from 'oidc-client-rx/adapters/@tanstack/react-router';
import {
InjectorContextVoidInjector,
InjectorProvider,
} from 'oidc-client-rx/adapters/react';
import React from 'react';
import ReactDOM from 'react-dom/client';
import { buildOidcConfig, isBasicAuth } from './auth/config';
import { useAuth } from './auth/hooks';
import { routeTree } from './routeTree.gen';
import './main.css';
const router = createRouter({
routeTree,
basepath: '/api/playground',
defaultPreload: 'intent',
context: {
isAuthenticated: isBasicAuth,
injector: InjectorContextVoidInjector,
oidcSecurityService: {} as OidcSecurityService,
},
});
// Register things for typesafety
declare module '@tanstack/react-router' {
interface Register {
router: typeof router;
}
}
const injector: Injector = isBasicAuth
? ReflectiveInjector.resolveAndCreate([])
: ReflectiveInjector.resolveAndCreate(
provideAuth(
{
config: buildOidcConfig(),
},
withDefaultFeatures({
router: { enabled: false },
securityStorage: { type: 'local-storage' },
}),
withTanstackRouter(router),
withCheckAuthResultEvent()
)
);
// if needed, check when init
let oidcSecurityService: OidcSecurityService | undefined;
if (!isBasicAuth) {
oidcSecurityService = injector.get(OidcSecurityService);
oidcSecurityService.checkAuth().subscribe();
}
const AppWithBasicAuth = () => {
return <RouterProvider router={router} />;
};
const AppWithOidcAuth = () => {
const { isAuthenticated, oidcSecurityService, injector } = useAuth();
return (
<RouterProvider
router={router}
context={{
isAuthenticated,
oidcSecurityService,
injector,
}}
/>
);
};
const App = isBasicAuth ? AppWithBasicAuth : AppWithOidcAuth;
const rootEl = document.getElementById('root');
if (rootEl) {
rootEl.classList.add('min-h-svh');
const root = ReactDOM.createRoot(rootEl);
root.render(
<React.StrictMode>
<InjectorProvider injector={injector}>
<App />
</InjectorProvider>
</React.StrictMode>
);
}

View File

@@ -1,134 +0,0 @@
/* 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 './web/controller/__root'
import { Route as IndexImport } from './web/controller/index'
import { Route as GraphqlIndexImport } from './web/controller/graphql/index'
import { Route as OidcCallbackImport } from './web/controller/oidc/callback'
// Create/Update Routes
const IndexRoute = IndexImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRoute,
} as any)
const GraphqlIndexRoute = GraphqlIndexImport.update({
id: '/graphql/',
path: '/graphql/',
getParentRoute: () => rootRoute,
} as any)
const OidcCallbackRoute = OidcCallbackImport.update({
id: '/oidc/callback',
path: '/oidc/callback',
getParentRoute: () => rootRoute,
} as any)
// Populate the FileRoutesByPath interface
declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute
}
'/oidc/callback': {
id: '/oidc/callback'
path: '/oidc/callback'
fullPath: '/oidc/callback'
preLoaderRoute: typeof OidcCallbackImport
parentRoute: typeof rootRoute
}
'/graphql/': {
id: '/graphql/'
path: '/graphql'
fullPath: '/graphql'
preLoaderRoute: typeof GraphqlIndexImport
parentRoute: typeof rootRoute
}
}
}
// Create and export the route tree
export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/oidc/callback': typeof OidcCallbackRoute
'/graphql': typeof GraphqlIndexRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/oidc/callback': typeof OidcCallbackRoute
'/graphql': typeof GraphqlIndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRoute
'/': typeof IndexRoute
'/oidc/callback': typeof OidcCallbackRoute
'/graphql/': typeof GraphqlIndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/oidc/callback' | '/graphql'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/oidc/callback' | '/graphql'
id: '__root__' | '/' | '/oidc/callback' | '/graphql/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
OidcCallbackRoute: typeof OidcCallbackRoute
GraphqlIndexRoute: typeof GraphqlIndexRoute
}
const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
OidcCallbackRoute: OidcCallbackRoute,
GraphqlIndexRoute: GraphqlIndexRoute,
}
export const routeTree = rootRoute
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
/* ROUTE_MANIFEST_START
{
"routes": {
"__root__": {
"filePath": "__root.tsx",
"children": [
"/",
"/oidc/callback",
"/graphql/"
]
},
"/": {
"filePath": "index.tsx"
},
"/oidc/callback": {
"filePath": "oidc/callback.tsx"
},
"/graphql/": {
"filePath": "graphql/index.tsx"
}
}
}
ROUTE_MANIFEST_END */

View File

@@ -1,32 +0,0 @@
import type { Injector } from '@outposts/injection-js';
import {
Outlet,
createRootRouteWithContext,
} from '@tanstack/react-router';
import { TanStackRouterDevtools } from '@tanstack/router-devtools';
import type { OidcSecurityService } from 'oidc-client-rx';
export type RouterContext =
| {
isAuthenticated: false;
injector: Injector;
oidcSecurityService: OidcSecurityService;
}
| {
isAuthenticated: true;
injector?: Injector;
oidcSecurityService?: OidcSecurityService;
};
export const Route = createRootRouteWithContext<RouterContext>()({
component: RootComponent,
});
function RootComponent() {
return (
<>
<Outlet />
<TanStackRouterDevtools position="bottom-right" />
</>
);
}

View File

@@ -1,36 +0,0 @@
import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit';
import { createFileRoute } from '@tanstack/react-router';
import GraphiQL from 'graphiql';
import { useMemo } from 'react';
import { firstValueFrom } from 'rxjs';
import { beforeLoadGuard } from '../../../auth/guard';
import { useAuth } from '../../../auth/hooks';
import 'graphiql/graphiql.css';
export const Route = createFileRoute('/graphql/')({
component: RouteComponent,
beforeLoad: beforeLoadGuard,
});
function RouteComponent() {
const { oidcSecurityService } = useAuth();
const fetcher = useMemo(
(): Fetcher => async (props) => {
const accessToken = oidcSecurityService
? await firstValueFrom(oidcSecurityService.getAccessToken())
: undefined;
return createGraphiQLFetcher({
url: '/api/graphql',
headers: accessToken
? {
Authorization: `Bearer ${accessToken}`,
}
: undefined,
})(props);
},
[oidcSecurityService]
);
return <GraphiQL fetcher={fetcher} className="!h-svh" />;
}

View File

@@ -1,9 +0,0 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')({
component: RouteComponent,
})
function RouteComponent() {
return <div>Hello to playground!</div>
}

View File

@@ -1,32 +0,0 @@
import { createFileRoute, redirect } from '@tanstack/react-router';
import { EventTypes } from 'oidc-client-rx';
import { useAuth } from '../../../auth/hooks';
export const Route = createFileRoute('/oidc/callback')({
component: RouteComponent,
beforeLoad: ({ context }) => {
if (!context.oidcSecurityService) {
throw redirect({
to: '/',
});
}
},
});
function RouteComponent() {
const auth = useAuth();
if (!auth.checkAuthResultEvent) {
return <div>Loading...</div>;
}
return (
<div>
OpenID Connect Auth Callback:{' '}
{auth.checkAuthResultEvent?.type ===
EventTypes.CheckingAuthFinishedWithError
? auth.checkAuthResultEvent.value
: 'success'}
</div>
);
}

View File

@@ -1,16 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",
"composite": true,
"jsx": "react-jsx",
"module": "ESNext",
"moduleResolution": "Bundler",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src"]
}

View File

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

View File

@@ -10,7 +10,7 @@
}, },
"dependencies": { "dependencies": {
"@ark-ui/solid": "^4.10.2", "@ark-ui/solid": "^4.10.2",
"@codemirror/language": "^6.10.8", "@codemirror/language": "6.0.0",
"@corvu/drawer": "^0.2.3", "@corvu/drawer": "^0.2.3",
"@corvu/otp-field": "^0.1.4", "@corvu/otp-field": "^0.1.4",
"@corvu/resizable": "^0.2.4", "@corvu/resizable": "^0.2.4",
@@ -26,14 +26,35 @@
"embla-carousel-solid": "^8.5.2", "embla-carousel-solid": "^8.5.2",
"graphiql": "^3.8.3", "graphiql": "^3.8.3",
"lucide-solid": "^0.477.0", "lucide-solid": "^0.477.0",
"react": "^18", "react": "^18.0.0",
"react-dom": "^18", "react-dom": "^18.0.0",
"solid-js": "^1.9.5", "solid-js": "^1.9.5",
"solid-sonner": "^0.2.8", "solid-sonner": "^0.2.8",
"tailwindcss": "^3" "tailwindcss": "^3.4.17",
"tailwind-merge": "^3.0.2",
"tailwindcss-animate": "^1.0.7",
"@tanstack/react-router": "^1.112.0",
"@tanstack/router-devtools": "^1.112.6",
"class-variance-authority": "^0.7.1",
"@abraham/reflection": "^0.12.0",
"@outposts/injection-js": "^2.5.1",
"arktype": "^2.1.2",
"clsx": "^2.1.1",
"oidc-client-rx": "0.1.0-alpha.8",
"rxjs": "^7.8.2"
}, },
"devDependencies": { "devDependencies": {
"@rsbuild/core": "^1.2.14",
"@rsbuild/plugin-babel": "^1.0.4",
"chalk": "^5.4.1",
"commander": "^13.1.0",
"postcss": "^8.5.3",
"@rsbuild/plugin-solid": "^1.0.5", "@rsbuild/plugin-solid": "^1.0.5",
"@tanstack/react-router": "^1.112.0" "@tanstack/react-router": "^1.112.0",
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"@tailwindcss/postcss": "^4.0.9",
"@tanstack/router-devtools": "^1.112.6",
"@tanstack/router-plugin": "^1.112.3"
} }
} }

View File

@@ -5,6 +5,7 @@ import { TanStackRouterRspack } from '@tanstack/router-plugin/rspack';
export default defineConfig({ export default defineConfig({
html: { html: {
title: 'Konobangu',
favicon: './public/assets/favicon.ico', favicon: './public/assets/favicon.ico',
}, },
plugins: [ plugins: [

View File

@@ -1,6 +1,7 @@
import { LogLevel, type OpenIdConfiguration } from 'oidc-client-rx'; import { LogLevel, type OpenIdConfiguration } from 'oidc-client-rx';
export const isBasicAuth = process.env.AUTH_TYPE === 'basic'; export const isBasicAuth = process.env.AUTH_TYPE === 'basic';
export const isOidcAuth = process.env.AUTH_TYPE === 'oidc';
export function buildOidcConfig(): OpenIdConfiguration { export function buildOidcConfig(): OpenIdConfiguration {
const origin = window.location.origin; const origin = window.location.origin;

View File

@@ -1,12 +1,4 @@
import type { Injector } from '@outposts/injection-js'; import { createSignal } from 'solid-js';
import type { OidcSecurityService } from 'oidc-client-rx';
import { type Accessor, createSignal } from 'solid-js';
import { isBasicAuth } from './config'; import { isBasicAuth } from './config';
export const [isAuthenticated, setIsAuthenticated] = createSignal(isBasicAuth); export const [isAuthenticated, setIsAuthenticated] = createSignal(isBasicAuth);
export type RouterContext = {
isAuthenticated: Accessor<boolean>;
injector: Injector;
oidcSecurityService: OidcSecurityService;
};

View File

@@ -1,7 +1,7 @@
import { runInInjectionContext } from '@outposts/injection-js'; import { runInInjectionContext } from '@outposts/injection-js';
import { autoLoginPartialRoutesGuard } from 'oidc-client-rx'; import { autoLoginPartialRoutesGuard } from 'oidc-client-rx';
import { firstValueFrom } from 'rxjs'; import { firstValueFrom } from 'rxjs';
import type { RouterContext } from './context'; import type { RouterContext } from '~/traits/router';
export const beforeLoadGuard = async ({ export const beforeLoadGuard = async ({
context, context,

View File

@@ -5,7 +5,7 @@ import {
} from 'oidc-client-rx/adapters/solid-js'; } from 'oidc-client-rx/adapters/solid-js';
import { NEVER, of } from 'rxjs'; import { NEVER, of } from 'rxjs';
import { createMemo, from } from 'solid-js'; import { createMemo, from } from 'solid-js';
import { isBasicAuth } from './config'; import { isBasicAuth, isOidcAuth } from './config';
const BASIC_AUTH_IS_AUTHENTICATED$ = of({ const BASIC_AUTH_IS_AUTHENTICATED$ = of({
isAuthenticated: true, isAuthenticated: true,
@@ -17,11 +17,10 @@ const BASIC_AUTH_USER_DATA$ = of({
allUserData: [], allUserData: [],
}); });
const useOidcClientExt = isOidcAuth ? useOidcClient : () => ({ oidcSecurityService: undefined, injector: InjectorContextVoidInjector })
export function useAuth() { export function useAuth() {
const { oidcSecurityService, injector } = isBasicAuth const { oidcSecurityService, injector } = useOidcClientExt();
? { oidcSecurityService: undefined, injector: InjectorContextVoidInjector }
: // biome-ignore lint/correctness/useHookAtTopLevel: <explanation>
useOidcClient();
const isAuthenticatedObj = from( const isAuthenticatedObj = from(
oidcSecurityService?.isAuthenticated$ ?? BASIC_AUTH_IS_AUTHENTICATED$ oidcSecurityService?.isAuthenticated$ ?? BASIC_AUTH_IS_AUTHENTICATED$

View File

@@ -1,4 +1,13 @@
import { Outlet } from '@tanstack/solid-router'; import { useMatches } from '@tanstack/solid-router';
import {
type ComponentProps,
type FlowProps,
For,
Show,
createMemo,
splitProps,
} from 'solid-js';
import { Dynamic } from 'solid-js/web';
import { AppSidebar } from '~/components/layout/app-sidebar'; import { AppSidebar } from '~/components/layout/app-sidebar';
import { import {
Breadcrumb, Breadcrumb,
@@ -13,8 +22,55 @@ import {
SidebarProvider, SidebarProvider,
SidebarTrigger, SidebarTrigger,
} from '~/components/ui/sidebar'; } from '~/components/ui/sidebar';
import type { RouteStateDataOption } from '~/traits/router';
import type { RouteBreadcrumbItem } from '~/traits/router';
import { cn } from '~/utils/styles';
import { ProLink } from '../ui/pro-link';
export type AppAsideBreadcrumbItem = RouteBreadcrumbItem;
export interface AppAsideProps extends FlowProps<ComponentProps<'div'>> {
breadcrumb?: AppAsideBreadcrumbItem[];
extractBreadcrumbFromRoutes?: boolean;
}
export function AppAside(props: AppAsideProps) {
const [local, other] = splitProps(props, [
'children',
'class',
'breadcrumb',
'extractBreadcrumbFromRoutes',
]);
const matches = useMatches();
const breadcrumb = createMemo(() => {
if (local.breadcrumb) {
return local.breadcrumb;
}
if (local.extractBreadcrumbFromRoutes) {
return matches()
.map((m, i, arr) => {
const staticData = m.staticData as RouteStateDataOption;
if (staticData.breadcrumb) {
return {
link:
i + 1 >= arr.length
? undefined
: {
to: m.pathname,
},
...staticData.breadcrumb,
} as AppAsideBreadcrumbItem;
}
return undefined;
})
.filter((b): b is AppAsideBreadcrumbItem => !!b);
}
return [];
});
const breadcrumbLength = breadcrumb().length;
export function AppLayout() {
return ( return (
<SidebarProvider> <SidebarProvider>
<AppSidebar /> <AppSidebar />
@@ -22,24 +78,47 @@ export function AppLayout() {
<header class="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12"> <header class="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div class="flex items-center gap-2 px-4"> <div class="flex items-center gap-2 px-4">
<SidebarTrigger class="-ml-1" /> <SidebarTrigger class="-ml-1" />
<Separator orientation="vertical" class="mr-2 h-4" /> <Show when={breadcrumbLength}>
<Breadcrumb> <Separator orientation="vertical" class="mr-2 h-4" />
<BreadcrumbList> <Breadcrumb>
<BreadcrumbItem class="hidden md:block"> <BreadcrumbList>
<BreadcrumbLink href="#"> <For each={breadcrumb()}>
Building Your Application {(item, index) => {
</BreadcrumbLink> const iconEl = (
</BreadcrumbItem> <Show when={!!item.icon}>
<BreadcrumbSeparator class="hidden md:block" /> <Dynamic component={item.icon} class=" size-4" />
<BreadcrumbItem> </Show>
<BreadcrumbLink current>Data Fetching</BreadcrumbLink> );
</BreadcrumbItem>
</BreadcrumbList> const isCurrent = index() + 1 === breadcrumbLength;
</Breadcrumb>
return (
<>
{index() > 0 && (
<BreadcrumbSeparator class="hidden md:block" />
)}
<BreadcrumbItem class="hidden md:block">
<BreadcrumbLink
class="text-[var(--foreground)] hover:text-inherit"
as={item.link ? ProLink : undefined}
current={isCurrent}
{...item?.link}
>
{iconEl}
{item.label}
</BreadcrumbLink>
</BreadcrumbItem>
</>
);
}}
</For>
</BreadcrumbList>
</Breadcrumb>
</Show>
</div> </div>
</header> </header>
<div class="p-4 pt-0"> <div {...other} class={cn('min-h-0 flex-1 p-4 pt-0', local.class)}>
<Outlet /> {local.children}
</div> </div>
</SidebarInset> </SidebarInset>
</SidebarProvider> </SidebarProvider>

View File

@@ -0,0 +1,43 @@
import {
type AnyRoute,
type ParsedLocation,
redirect,
} from '@tanstack/solid-router';
import { ProLink } from '../ui/pro-link';
export function guardRouteIndexAsNotFound(
this: AnyRoute,
{ location }: { location: ParsedLocation<any> }
) {
// biome-ignore lint/performance/useTopLevelRegex: <explanation>
if (location.pathname.replace(/\/+$/, '') === this.id) {
throw redirect({
href: '/404',
replace: true,
reloadDocument: true,
});
}
}
export function AppNotFoundComponent() {
return (
<div class="flex min-h-screen items-center px-4 py-12 sm:px-6 md:px-8 lg:px-12 xl:px-16">
<div class="w-full space-y-6 text-center">
<div class="space-y-3">
<h1 class="font-bold text-4xl tracking-tighter sm:text-5xl">
404 Page Not Found
</h1>
<p class="text-gray-500">
Sorry, we couldn&#x27;t find the page you&#x27;re looking for.
</p>
</div>
<ProLink
to="/"
class="inline-flex h-10 items-center rounded-md border border-gray-200 border-gray-200 bg-white px-8 font-medium text-sm shadow-sm transition-colors hover:bg-gray-100 hover:text-gray-900 dark:border-gray-800 dark:border-gray-800 dark:bg-gray-950 dark:focus-visible:ring-gray-300 dark:hover:bg-gray-800 dark:hover:text-gray-50"
>
Return to website
</ProLink>
</div>
</div>
);
}

View File

@@ -1,6 +1,5 @@
import { import {
BookOpen, BookOpen,
Bot,
Folders, Folders,
Settings2, Settings2,
SquareTerminal, SquareTerminal,
@@ -52,40 +51,16 @@ const navMain = [
}, },
{ {
title: 'Playground', title: 'Playground',
href: '#',
icon: SquareTerminal, icon: SquareTerminal,
isActive: true, link: {
items: [ to: '/playground',
},
children: [
{ {
title: 'History', title: 'GraphQL Api',
href: '#', link: {
}, to: '/playground/graphql-api',
{ },
title: 'Starred',
href: '#',
},
{
title: 'Settings',
href: '#',
},
],
},
{
title: 'Models',
href: '#',
icon: Bot,
items: [
{
title: 'Genesis',
href: '#',
},
{
title: 'Explorer',
href: '#',
},
{
title: 'Quantum',
href: '#',
}, },
], ],
}, },
@@ -99,24 +74,16 @@ const navMain = [
}, },
{ {
title: 'Settings', title: 'Settings',
href: '#', link: {
to: '/settings',
},
icon: Settings2, icon: Settings2,
items: [ children: [
{ {
title: 'General', title: 'Downloader',
href: '#', link: {
}, to: '/settings/downloader',
{ },
title: 'Team',
href: '#',
},
{
title: 'Billing',
href: '#',
},
{
title: 'Limits',
href: '#',
}, },
], ],
}, },

View File

@@ -0,0 +1,12 @@
export function AppSkeleton() {
return (
<>
<div class="grid auto-rows-min gap-4 md:grid-cols-3">
<div class="aspect-video rounded-xl bg-muted/50" />
<div class="aspect-video rounded-xl bg-muted/50" />
<div class="aspect-video rounded-xl bg-muted/50" />
</div>
<div class="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</>
);
}

View File

@@ -1,40 +1,45 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as AccordionPrimitive from "@kobalte/core/accordion" import * as AccordionPrimitive from '@kobalte/core/accordion';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Accordion = AccordionPrimitive.Root const Accordion = AccordionPrimitive.Root;
type AccordionItemProps<T extends ValidComponent = "div"> = type AccordionItemProps<T extends ValidComponent = 'div'> =
AccordionPrimitive.AccordionItemProps<T> & { AccordionPrimitive.AccordionItemProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const AccordionItem = <T extends ValidComponent = "div">( const AccordionItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, AccordionItemProps<T>> props: PolymorphicProps<T, AccordionItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as AccordionItemProps, ["class"]) const [local, others] = splitProps(props as AccordionItemProps, ['class']);
return <AccordionPrimitive.Item class={cn("border-b", local.class)} {...others} /> return (
} <AccordionPrimitive.Item class={cn('border-b', local.class)} {...others} />
);
};
type AccordionTriggerProps<T extends ValidComponent = "button"> = type AccordionTriggerProps<T extends ValidComponent = 'button'> =
AccordionPrimitive.AccordionTriggerProps<T> & { AccordionPrimitive.AccordionTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const AccordionTrigger = <T extends ValidComponent = "button">( const AccordionTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, AccordionTriggerProps<T>> props: PolymorphicProps<T, AccordionTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as AccordionTriggerProps, ["class", "children"]) const [local, others] = splitProps(props as AccordionTriggerProps, [
'class',
'children',
]);
return ( return (
<AccordionPrimitive.Header class="flex"> <AccordionPrimitive.Header class="flex">
<AccordionPrimitive.Trigger <AccordionPrimitive.Trigger
class={cn( class={cn(
"flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-expanded]>svg]:rotate-180", 'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-expanded]>svg]:rotate-180',
local.class local.class
)} )}
{...others} {...others}
@@ -54,30 +59,33 @@ const AccordionTrigger = <T extends ValidComponent = "button">(
</svg> </svg>
</AccordionPrimitive.Trigger> </AccordionPrimitive.Trigger>
</AccordionPrimitive.Header> </AccordionPrimitive.Header>
) );
} };
type AccordionContentProps<T extends ValidComponent = "div"> = type AccordionContentProps<T extends ValidComponent = 'div'> =
AccordionPrimitive.AccordionContentProps<T> & { AccordionPrimitive.AccordionContentProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const AccordionContent = <T extends ValidComponent = "div">( const AccordionContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, AccordionContentProps<T>> props: PolymorphicProps<T, AccordionContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as AccordionContentProps, ["class", "children"]) const [local, others] = splitProps(props as AccordionContentProps, [
'class',
'children',
]);
return ( return (
<AccordionPrimitive.Content <AccordionPrimitive.Content
class={cn( class={cn(
"animate-accordion-up overflow-hidden text-sm transition-all data-[expanded]:animate-accordion-down", 'animate-accordion-up overflow-hidden text-sm transition-all data-[expanded]:animate-accordion-down',
local.class local.class
)} )}
{...others} {...others}
> >
<div class="pb-4 pt-0">{local.children}</div> <div class="pt-0 pb-4">{local.children}</div>
</AccordionPrimitive.Content> </AccordionPrimitive.Content>
) );
} };
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };

View File

@@ -1,57 +1,62 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as AlertDialogPrimitive from "@kobalte/core/alert-dialog" import * as AlertDialogPrimitive from '@kobalte/core/alert-dialog';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const AlertDialog = AlertDialogPrimitive.Root const AlertDialog = AlertDialogPrimitive.Root;
const AlertDialogTrigger = AlertDialogPrimitive.Trigger const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
const AlertDialogPortal = AlertDialogPrimitive.Portal const AlertDialogPortal = AlertDialogPrimitive.Portal;
type AlertDialogOverlayProps<T extends ValidComponent = "div"> = type AlertDialogOverlayProps<T extends ValidComponent = 'div'> =
AlertDialogPrimitive.AlertDialogOverlayProps<T> & { AlertDialogPrimitive.AlertDialogOverlayProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const AlertDialogOverlay = <T extends ValidComponent = "div">( const AlertDialogOverlay = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, AlertDialogOverlayProps<T>> props: PolymorphicProps<T, AlertDialogOverlayProps<T>>
) => { ) => {
const [local, others] = splitProps(props as AlertDialogOverlayProps, ["class"]) const [local, others] = splitProps(props as AlertDialogOverlayProps, [
'class',
]);
return ( return (
<AlertDialogPrimitive.Overlay <AlertDialogPrimitive.Overlay
class={cn( 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", 'data-[closed]:fade-out-0 data-[expanded]:fade-in-0 fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[closed]:animate-out data-[expanded]:animate-in',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type AlertDialogContentProps<T extends ValidComponent = "div"> = type AlertDialogContentProps<T extends ValidComponent = 'div'> =
AlertDialogPrimitive.AlertDialogContentProps<T> & { AlertDialogPrimitive.AlertDialogContentProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const AlertDialogContent = <T extends ValidComponent = "div">( const AlertDialogContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, AlertDialogContentProps<T>> props: PolymorphicProps<T, AlertDialogContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as AlertDialogContentProps, ["class", "children"]) const [local, others] = splitProps(props as AlertDialogContentProps, [
'class',
'children',
]);
return ( return (
<AlertDialogPortal> <AlertDialogPortal>
<AlertDialogOverlay /> <AlertDialogOverlay />
<AlertDialogPrimitive.Content <AlertDialogPrimitive.Content
class={cn( 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", '-translate-x-1/2 -translate-y-1/2 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%] fixed top-1/2 left-1/2 z-50 grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg duration-200 data-[closed]:animate-out data-[expanded]:animate-in sm:rounded-lg md:w-full',
local.class local.class
)} )}
{...others} {...others}
> >
{local.children} {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"> <AlertDialogPrimitive.CloseButton class="absolute top-4 right-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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -69,37 +74,44 @@ const AlertDialogContent = <T extends ValidComponent = "div">(
</AlertDialogPrimitive.CloseButton> </AlertDialogPrimitive.CloseButton>
</AlertDialogPrimitive.Content> </AlertDialogPrimitive.Content>
</AlertDialogPortal> </AlertDialogPortal>
) );
} };
type AlertDialogTitleProps<T extends ValidComponent = "h2"> = type AlertDialogTitleProps<T extends ValidComponent = 'h2'> =
AlertDialogPrimitive.AlertDialogTitleProps<T> & { AlertDialogPrimitive.AlertDialogTitleProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const AlertDialogTitle = <T extends ValidComponent = "h2">( const AlertDialogTitle = <T extends ValidComponent = 'h2'>(
props: PolymorphicProps<T, AlertDialogTitleProps<T>> props: PolymorphicProps<T, AlertDialogTitleProps<T>>
) => { ) => {
const [local, others] = splitProps(props as AlertDialogTitleProps, ["class"]) 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 ( return (
<AlertDialogPrimitive.Description <AlertDialogPrimitive.Title
class={cn("text-sm text-muted-foreground", local.class)} class={cn('font-semibold text-lg', local.class)}
{...others} {...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-muted-foreground text-sm', local.class)}
{...others}
/>
);
};
export { export {
AlertDialog, AlertDialog,
@@ -108,5 +120,5 @@ export {
AlertDialogTrigger, AlertDialogTrigger,
AlertDialogContent, AlertDialogContent,
AlertDialogTitle, AlertDialogTitle,
AlertDialogDescription AlertDialogDescription,
} };

View File

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

View File

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

@@ -1,27 +1,36 @@
import type { Component, JSXElement } from "solid-js" import type { Component, JSXElement } from 'solid-js';
import { createEffect, on, splitProps } from "solid-js" import { createEffect, on, splitProps } from 'solid-js';
import type { VariantProps } from "class-variance-authority" import type { VariantProps } from 'class-variance-authority';
import { cva } from "class-variance-authority" import { cva } from 'class-variance-authority';
import { cn } from "~/styles/utils" import type { BadgeProps } from '~/components/ui/badge';
import type { BadgeProps } from "~/components/ui/badge" import { Badge } from '~/components/ui/badge';
import { Badge } from "~/components/ui/badge" import { cn } from '~/utils/styles';
type DeltaType = "increase" | "moderateIncrease" | "unchanged" | "moderateDecrease" | "decrease" type DeltaType =
| 'increase'
| 'moderateIncrease'
| 'unchanged'
| 'moderateDecrease'
| 'decrease';
const badgeDeltaVariants = cva("", { const badgeDeltaVariants = cva('', {
variants: { variants: {
variant: { variant: {
success: "bg-success text-success-foreground hover:bg-success", success: 'bg-success text-success-foreground hover:bg-success',
warning: "bg-warning text-warning-foreground hover:bg-warning", warning: 'bg-warning text-warning-foreground hover:bg-warning',
error: "bg-error text-error-foreground hover:bg-error" error: 'bg-error text-error-foreground hover:bg-error',
} },
} },
}) });
type DeltaVariant = NonNullable<VariantProps<typeof badgeDeltaVariants>["variant"]> type DeltaVariant = NonNullable<
VariantProps<typeof badgeDeltaVariants>['variant']
>;
const iconMap: { [key in DeltaType]: (props: { class?: string }) => JSXElement } = { const iconMap: {
[key in DeltaType]: (props: { class?: string }) => JSXElement;
} = {
increase: (props) => ( increase: (props) => (
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@@ -99,38 +108,41 @@ const iconMap: { [key in DeltaType]: (props: { class?: string }) => JSXElement }
<path d="M18 13l-6 6" /> <path d="M18 13l-6 6" />
<path d="M6 13l6 6" /> <path d="M6 13l6 6" />
</svg> </svg>
) ),
} };
const variantMap: { [key in DeltaType]: DeltaVariant } = { const variantMap: { [key in DeltaType]: DeltaVariant } = {
increase: "success", increase: 'success',
moderateIncrease: "success", moderateIncrease: 'success',
unchanged: "warning", unchanged: 'warning',
moderateDecrease: "error", moderateDecrease: 'error',
decrease: "error" decrease: 'error',
} };
type BadgeDeltaProps = Omit<BadgeProps, "variant"> & { type BadgeDeltaProps = Omit<BadgeProps, 'variant'> & {
deltaType: DeltaType deltaType: DeltaType;
} };
const BadgeDelta: Component<BadgeDeltaProps> = (props) => { const BadgeDelta: Component<BadgeDeltaProps> = (props) => {
const [local, others] = splitProps(props, ["class", "children", "deltaType"]) const [local, others] = splitProps(props, ['class', 'children', 'deltaType']);
// eslint-disable-next-line solid/reactivity // eslint-disable-next-line solid/reactivity
let Icon = iconMap[local.deltaType] let Icon = iconMap[local.deltaType];
createEffect( createEffect(
on( on(
() => local.deltaType, () => local.deltaType,
() => { () => {
Icon = iconMap[local.deltaType] Icon = iconMap[local.deltaType];
} }
) )
) );
return ( return (
<Badge <Badge
class={cn(badgeDeltaVariants({ variant: variantMap[local.deltaType] }), local.class)} class={cn(
badgeDeltaVariants({ variant: variantMap[local.deltaType] }),
local.class
)}
{...others} {...others}
> >
<span class="flex gap-1"> <span class="flex gap-1">
@@ -138,7 +150,7 @@ const BadgeDelta: Component<BadgeDeltaProps> = (props) => {
{local.children} {local.children}
</span> </span>
</Badge> </Badge>
) );
} };
export { BadgeDelta } export { BadgeDelta };

View File

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

View File

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

View File

@@ -1,61 +1,72 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { Show, splitProps } from "solid-js" import { Show, splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core" import type { PolymorphicProps } from '@kobalte/core';
import * as BreadcrumbPrimitive from "@kobalte/core/breadcrumbs" import * as BreadcrumbPrimitive from '@kobalte/core/breadcrumbs';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Breadcrumb = BreadcrumbPrimitive.Root const Breadcrumb = BreadcrumbPrimitive.Root;
const BreadcrumbList: Component<ComponentProps<"ol">> = (props) => { const BreadcrumbList: Component<ComponentProps<'ol'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<ol <ol
class={cn( class={cn(
"flex flex-wrap items-center gap-1.5 break-words text-sm text-muted-foreground sm:gap-2.5", 'flex flex-wrap items-center gap-1.5 break-words text-muted-foreground text-sm sm:gap-2.5',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const BreadcrumbItem: Component<ComponentProps<"li">> = (props) => { const BreadcrumbItem: Component<ComponentProps<'li'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return <li class={cn("inline-flex items-center gap-1.5", local.class)} {...others} /> return (
} <li
class={cn('inline-flex items-center gap-1.5', local.class)}
{...others}
/>
);
};
type BreadcrumbLinkProps<T extends ValidComponent = "a"> = type BreadcrumbLinkProps<T extends ValidComponent = 'a'> =
BreadcrumbPrimitive.BreadcrumbsLinkProps<T> & { class?: string | undefined } BreadcrumbPrimitive.BreadcrumbsLinkProps<T> & { class?: string | undefined };
const BreadcrumbLink = <T extends ValidComponent = "a">( const BreadcrumbLink = <T extends ValidComponent = 'a'>(
props: PolymorphicProps<T, BreadcrumbLinkProps<T>> props: PolymorphicProps<T, BreadcrumbLinkProps<T>>
) => { ) => {
const [local, others] = splitProps(props as BreadcrumbLinkProps, ["class"]) const [local, others] = splitProps(props as BreadcrumbLinkProps, ['class']);
return ( return (
<BreadcrumbPrimitive.Link <BreadcrumbPrimitive.Link
class={cn( class={cn(
"transition-colors hover:text-foreground data-[current]:font-normal data-[current]:text-foreground", 'transition-colors hover:text-foreground data-[current]:font-normal data-[current]:text-foreground',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type BreadcrumbSeparatorProps<T extends ValidComponent = "span"> = type BreadcrumbSeparatorProps<T extends ValidComponent = 'span'> =
BreadcrumbPrimitive.BreadcrumbsSeparatorProps<T> & { BreadcrumbPrimitive.BreadcrumbsSeparatorProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const BreadcrumbSeparator = <T extends ValidComponent = "span">( const BreadcrumbSeparator = <T extends ValidComponent = 'span'>(
props: PolymorphicProps<T, BreadcrumbSeparatorProps<T>> props: PolymorphicProps<T, BreadcrumbSeparatorProps<T>>
) => { ) => {
const [local, others] = splitProps(props as BreadcrumbSeparatorProps, ["class", "children"]) const [local, others] = splitProps(props as BreadcrumbSeparatorProps, [
'class',
'children',
]);
return ( return (
<BreadcrumbPrimitive.Separator class={cn("[&>svg]:size-3.5", local.class)} {...others}> <BreadcrumbPrimitive.Separator
class={cn('[&>svg]:size-3.5', local.class)}
{...others}
>
<Show <Show
when={local.children} when={local.children}
fallback={ fallback={
@@ -75,13 +86,16 @@ const BreadcrumbSeparator = <T extends ValidComponent = "span">(
{local.children} {local.children}
</Show> </Show>
</BreadcrumbPrimitive.Separator> </BreadcrumbPrimitive.Separator>
) );
} };
const BreadcrumbEllipsis: Component<ComponentProps<"span">> = (props) => { const BreadcrumbEllipsis: Component<ComponentProps<'span'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<span class={cn("flex size-9 items-center justify-center", local.class)} {...others}> <span
class={cn('flex size-9 items-center justify-center', local.class)}
{...others}
>
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -98,8 +112,8 @@ const BreadcrumbEllipsis: Component<ComponentProps<"span">> = (props) => {
</svg> </svg>
<span class="sr-only">More</span> <span class="sr-only">More</span>
</span> </span>
) );
} };
export { export {
Breadcrumb, Breadcrumb,
@@ -107,5 +121,5 @@ export {
BreadcrumbItem, BreadcrumbItem,
BreadcrumbLink, BreadcrumbLink,
BreadcrumbSeparator, BreadcrumbSeparator,
BreadcrumbEllipsis BreadcrumbEllipsis,
} };

View File

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

View File

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

View File

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

@@ -1,4 +1,4 @@
import type { Accessor, Component, ComponentProps, VoidProps } from "solid-js" import type { Accessor, Component, ComponentProps, VoidProps } from 'solid-js';
import { import {
createContext, createContext,
createEffect, createEffect,
@@ -6,118 +6,122 @@ import {
createSignal, createSignal,
mergeProps, mergeProps,
splitProps, splitProps,
useContext useContext,
} from "solid-js" } from 'solid-js';
import type { CreateEmblaCarouselType } from "embla-carousel-solid" import type { CreateEmblaCarouselType } from 'embla-carousel-solid';
import createEmblaCarousel from "embla-carousel-solid" import createEmblaCarousel from 'embla-carousel-solid';
import { cn } from "~/styles/utils" import type { ButtonProps } from '~/components/ui/button';
import type { ButtonProps } from "~/components/ui/button" import { Button } from '~/components/ui/button';
import { Button } from "~/components/ui/button" import { cn } from '~/utils/styles';
export type CarouselApi = CreateEmblaCarouselType[1] export type CarouselApi = CreateEmblaCarouselType[1];
type UseCarouselParameters = Parameters<typeof createEmblaCarousel> type UseCarouselParameters = Parameters<typeof createEmblaCarousel>;
type CarouselOptions = NonNullable<UseCarouselParameters[0]> type CarouselOptions = NonNullable<UseCarouselParameters[0]>;
type CarouselPlugin = NonNullable<UseCarouselParameters[1]> type CarouselPlugin = NonNullable<UseCarouselParameters[1]>;
type CarouselProps = { type CarouselProps = {
opts?: ReturnType<CarouselOptions> opts?: ReturnType<CarouselOptions>;
plugins?: ReturnType<CarouselPlugin> plugins?: ReturnType<CarouselPlugin>;
orientation?: "horizontal" | "vertical" orientation?: 'horizontal' | 'vertical';
setApi?: (api: CarouselApi) => void setApi?: (api: CarouselApi) => void;
} };
type CarouselContextProps = { type CarouselContextProps = {
carouselRef: ReturnType<typeof createEmblaCarousel>[0] carouselRef: ReturnType<typeof createEmblaCarousel>[0];
api: ReturnType<typeof createEmblaCarousel>[1] api: ReturnType<typeof createEmblaCarousel>[1];
scrollPrev: () => void scrollPrev: () => void;
scrollNext: () => void scrollNext: () => void;
canScrollPrev: Accessor<boolean> canScrollPrev: Accessor<boolean>;
canScrollNext: Accessor<boolean> canScrollNext: Accessor<boolean>;
} & CarouselProps } & CarouselProps;
const CarouselContext = createContext<Accessor<CarouselContextProps> | null>(null) const CarouselContext = createContext<Accessor<CarouselContextProps> | null>(
null
);
const useCarousel = () => { const useCarousel = () => {
const context = useContext(CarouselContext) const context = useContext(CarouselContext);
if (!context) { if (!context) {
throw new Error("useCarousel must be used within a <Carousel />") throw new Error('useCarousel must be used within a <Carousel />');
} }
return context() return context();
} };
const Carousel: Component<CarouselProps & ComponentProps<"div">> = (rawProps) => { const Carousel: Component<CarouselProps & ComponentProps<'div'>> = (
const props = mergeProps<(CarouselProps & ComponentProps<"div">)[]>( rawProps
{ orientation: "horizontal" }, ) => {
const props = mergeProps<(CarouselProps & ComponentProps<'div'>)[]>(
{ orientation: 'horizontal' },
rawProps rawProps
) );
const [local, others] = splitProps(props, [ const [local, others] = splitProps(props, [
"orientation", 'orientation',
"opts", 'opts',
"setApi", 'setApi',
"plugins", 'plugins',
"class", 'class',
"children" 'children',
]) ]);
const [carouselRef, api] = createEmblaCarousel( const [carouselRef, api] = createEmblaCarousel(
() => ({ () => ({
...local.opts, ...local.opts,
axis: local.orientation === "horizontal" ? "x" : "y" axis: local.orientation === 'horizontal' ? 'x' : 'y',
}), }),
() => (local.plugins === undefined ? [] : local.plugins) () => (local.plugins === undefined ? [] : local.plugins)
) );
const [canScrollPrev, setCanScrollPrev] = createSignal(false) const [canScrollPrev, setCanScrollPrev] = createSignal(false);
const [canScrollNext, setCanScrollNext] = createSignal(false) const [canScrollNext, setCanScrollNext] = createSignal(false);
const onSelect = (api: NonNullable<ReturnType<CarouselApi>>) => { const onSelect = (api: NonNullable<ReturnType<CarouselApi>>) => {
setCanScrollPrev(api.canScrollPrev()) setCanScrollPrev(api.canScrollPrev());
setCanScrollNext(api.canScrollNext()) setCanScrollNext(api.canScrollNext());
} };
const scrollPrev = () => { const scrollPrev = () => {
api()?.scrollPrev() api()?.scrollPrev();
} };
const scrollNext = () => { const scrollNext = () => {
api()?.scrollNext() api()?.scrollNext();
} };
const handleKeyDown = (event: KeyboardEvent) => { const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === "ArrowLeft") { if (event.key === 'ArrowLeft') {
event.preventDefault() event.preventDefault();
scrollPrev() scrollPrev();
} else if (event.key === "ArrowRight") { } else if (event.key === 'ArrowRight') {
event.preventDefault() event.preventDefault();
scrollNext() scrollNext();
} }
} };
createEffect(() => { createEffect(() => {
if (!api() || !local.setApi) { if (!api() || !local.setApi) {
return return;
} }
local.setApi(api) local.setApi(api);
}) });
createEffect(() => { createEffect(() => {
if (!api()) { if (!api()) {
return return;
} }
onSelect(api()!) onSelect(api()!);
api()!.on("reInit", onSelect) api()!.on('reInit', onSelect);
api()!.on("select", onSelect) api()!.on('select', onSelect);
return () => { return () => {
api()?.off("select", onSelect) api()?.off('select', onSelect);
} };
}) });
const value = createMemo( const value = createMemo(
() => () =>
@@ -125,19 +129,21 @@ const Carousel: Component<CarouselProps & ComponentProps<"div">> = (rawProps) =>
carouselRef, carouselRef,
api, api,
opts: local.opts, opts: local.opts,
orientation: local.orientation || (local.opts?.axis === "y" ? "vertical" : "horizontal"), orientation:
local.orientation ||
(local.opts?.axis === 'y' ? 'vertical' : 'horizontal'),
scrollPrev, scrollPrev,
scrollNext, scrollNext,
canScrollPrev, canScrollPrev,
canScrollNext canScrollNext,
}) satisfies CarouselContextProps }) satisfies CarouselContextProps
) );
return ( return (
<CarouselContext.Provider value={value}> <CarouselContext.Provider value={value}>
<div <div
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
class={cn("relative", local.class)} class={cn('relative', local.class)}
role="region" role="region"
aria-roledescription="carousel" aria-roledescription="carousel"
{...others} {...others}
@@ -145,57 +151,64 @@ const Carousel: Component<CarouselProps & ComponentProps<"div">> = (rawProps) =>
{local.children} {local.children}
</div> </div>
</CarouselContext.Provider> </CarouselContext.Provider>
) );
} };
const CarouselContent: Component<ComponentProps<"div">> = (props) => { const CarouselContent: Component<ComponentProps<'div'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
const { carouselRef, orientation } = useCarousel() const { carouselRef, orientation } = useCarousel();
return ( return (
<div ref={carouselRef} class="overflow-hidden"> <div ref={carouselRef} class="overflow-hidden">
<div <div
class={cn("flex", orientation === "horizontal" ? "-ml-4" : "-mt-4 flex-col", local.class)} class={cn(
'flex',
orientation === 'horizontal' ? '-ml-4' : '-mt-4 flex-col',
local.class
)}
{...others} {...others}
/> />
</div> </div>
) );
} };
const CarouselItem: Component<ComponentProps<"div">> = (props) => { const CarouselItem: Component<ComponentProps<'div'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
const { orientation } = useCarousel() const { orientation } = useCarousel();
return ( return (
<div <div
role="group" role="group"
aria-roledescription="slide" aria-roledescription="slide"
class={cn( class={cn(
"min-w-0 shrink-0 grow-0 basis-full", 'min-w-0 shrink-0 grow-0 basis-full',
orientation === "horizontal" ? "pl-4" : "pt-4", orientation === 'horizontal' ? 'pl-4' : 'pt-4',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type CarouselButtonProps = VoidProps<ButtonProps> type CarouselButtonProps = VoidProps<ButtonProps>;
const CarouselPrevious: Component<CarouselButtonProps> = (rawProps) => { const CarouselPrevious: Component<CarouselButtonProps> = (rawProps) => {
const props = mergeProps<CarouselButtonProps[]>({ variant: "outline", size: "icon" }, rawProps) const props = mergeProps<CarouselButtonProps[]>(
const [local, others] = splitProps(props, ["class", "variant", "size"]) { variant: 'outline', size: 'icon' },
const { orientation, scrollPrev, canScrollPrev } = useCarousel() rawProps
);
const [local, others] = splitProps(props, ['class', 'variant', 'size']);
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return ( return (
<Button <Button
variant={local.variant} variant={local.variant}
size={local.size} size={local.size}
class={cn( class={cn(
"absolute size-8 touch-manipulation rounded-full", 'absolute size-8 touch-manipulation rounded-full',
orientation === "horizontal" orientation === 'horizontal'
? "-left-12 top-1/2 -translate-y-1/2" ? '-left-12 -translate-y-1/2 top-1/2'
: "-top-12 left-1/2 -translate-x-1/2 rotate-90", : '-top-12 -translate-x-1/2 left-1/2 rotate-90',
local.class local.class
)} )}
disabled={!canScrollPrev()} disabled={!canScrollPrev()}
@@ -218,23 +231,26 @@ const CarouselPrevious: Component<CarouselButtonProps> = (rawProps) => {
</svg> </svg>
<span class="sr-only">Previous slide</span> <span class="sr-only">Previous slide</span>
</Button> </Button>
) );
} };
const CarouselNext: Component<CarouselButtonProps> = (rawProps) => { const CarouselNext: Component<CarouselButtonProps> = (rawProps) => {
const props = mergeProps<CarouselButtonProps[]>({ variant: "outline", size: "icon" }, rawProps) const props = mergeProps<CarouselButtonProps[]>(
const [local, others] = splitProps(props, ["class", "variant", "size"]) { variant: 'outline', size: 'icon' },
const { orientation, scrollNext, canScrollNext } = useCarousel() rawProps
);
const [local, others] = splitProps(props, ['class', 'variant', 'size']);
const { orientation, scrollNext, canScrollNext } = useCarousel();
return ( return (
<Button <Button
variant={local.variant} variant={local.variant}
size={local.size} size={local.size}
class={cn( class={cn(
"absolute size-8 touch-manipulation rounded-full", 'absolute size-8 touch-manipulation rounded-full',
orientation === "horizontal" orientation === 'horizontal'
? "-right-12 top-1/2 -translate-y-1/2" ? '-right-12 -translate-y-1/2 top-1/2'
: "-bottom-12 left-1/2 -translate-x-1/2 rotate-90", : '-bottom-12 -translate-x-1/2 left-1/2 rotate-90',
local.class local.class
)} )}
disabled={!canScrollNext()} disabled={!canScrollNext()}
@@ -257,7 +273,13 @@ const CarouselNext: Component<CarouselButtonProps> = (rawProps) => {
</svg> </svg>
<span class="sr-only">Next slide</span> <span class="sr-only">Next slide</span>
</Button> </Button>
) );
} };
export { Carousel, CarouselContent, CarouselItem, CarouselPrevious, CarouselNext } export {
Carousel,
CarouselContent,
CarouselItem,
CarouselPrevious,
CarouselNext,
};

View File

@@ -1,21 +1,21 @@
import type { ValidComponent } from "solid-js" import type { ValidComponent } from 'solid-js';
import { Match, splitProps, Switch } from "solid-js" import { Match, Switch, splitProps } from 'solid-js';
import * as CheckboxPrimitive from "@kobalte/core/checkbox" import * as CheckboxPrimitive from '@kobalte/core/checkbox';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
type CheckboxRootProps<T extends ValidComponent = "div"> = type CheckboxRootProps<T extends ValidComponent = 'div'> =
CheckboxPrimitive.CheckboxRootProps<T> & { class?: string | undefined } CheckboxPrimitive.CheckboxRootProps<T> & { class?: string | undefined };
const Checkbox = <T extends ValidComponent = "div">( const Checkbox = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, CheckboxRootProps<T>> props: PolymorphicProps<T, CheckboxRootProps<T>>
) => { ) => {
const [local, others] = splitProps(props as CheckboxRootProps, ["class"]) const [local, others] = splitProps(props as CheckboxRootProps, ['class']);
return ( return (
<CheckboxPrimitive.Root <CheckboxPrimitive.Root
class={cn("items-top group relative flex space-x-2", local.class)} class={cn('items-top group relative flex space-x-2', local.class)}
{...others} {...others}
> >
<CheckboxPrimitive.Input class="peer" /> <CheckboxPrimitive.Input class="peer" />
@@ -54,7 +54,7 @@ const Checkbox = <T extends ValidComponent = "div">(
</CheckboxPrimitive.Indicator> </CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Control> </CheckboxPrimitive.Control>
</CheckboxPrimitive.Root> </CheckboxPrimitive.Root>
) );
} };
export { Checkbox } export { Checkbox };

View File

@@ -1,43 +1,46 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { Show, splitProps } from "solid-js" import { Show, splitProps } from 'solid-js';
import * as ComboboxPrimitive from "@kobalte/core/combobox" import * as ComboboxPrimitive from '@kobalte/core/combobox';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Combobox = ComboboxPrimitive.Root const Combobox = ComboboxPrimitive.Root;
const ComboboxItemLabel = ComboboxPrimitive.ItemLabel const ComboboxItemLabel = ComboboxPrimitive.ItemLabel;
const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect const ComboboxHiddenSelect = ComboboxPrimitive.HiddenSelect;
type ComboboxItemProps<T extends ValidComponent = "li"> = ComboboxPrimitive.ComboboxItemProps<T> & { type ComboboxItemProps<T extends ValidComponent = 'li'> =
class?: string | undefined ComboboxPrimitive.ComboboxItemProps<T> & {
} class?: string | undefined;
};
const ComboboxItem = <T extends ValidComponent = "li">( const ComboboxItem = <T extends ValidComponent = 'li'>(
props: PolymorphicProps<T, ComboboxItemProps<T>> props: PolymorphicProps<T, ComboboxItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxItemProps, ["class"]) const [local, others] = splitProps(props as ComboboxItemProps, ['class']);
return ( return (
<ComboboxPrimitive.Item <ComboboxPrimitive.Item
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type ComboboxItemIndicatorProps<T extends ValidComponent = "div"> = type ComboboxItemIndicatorProps<T extends ValidComponent = 'div'> =
ComboboxPrimitive.ComboboxItemIndicatorProps<T> & { ComboboxPrimitive.ComboboxItemIndicatorProps<T> & {
children?: JSX.Element children?: JSX.Element;
} };
const ComboboxItemIndicator = <T extends ValidComponent = "div">( const ComboboxItemIndicator = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ComboboxItemIndicatorProps<T>> props: PolymorphicProps<T, ComboboxItemIndicatorProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxItemIndicatorProps, ["children"]) const [local, others] = splitProps(props as ComboboxItemIndicatorProps, [
'children',
]);
return ( return (
<ComboboxPrimitive.ItemIndicator {...others}> <ComboboxPrimitive.ItemIndicator {...others}>
<Show <Show
@@ -60,76 +63,84 @@ const ComboboxItemIndicator = <T extends ValidComponent = "div">(
{(children) => children()} {(children) => children()}
</Show> </Show>
</ComboboxPrimitive.ItemIndicator> </ComboboxPrimitive.ItemIndicator>
) );
} };
type ComboboxSectionProps<T extends ValidComponent = "li"> = type ComboboxSectionProps<T extends ValidComponent = 'li'> =
ComboboxPrimitive.ComboboxSectionProps<T> & { class?: string | undefined } ComboboxPrimitive.ComboboxSectionProps<T> & { class?: string | undefined };
const ComboboxSection = <T extends ValidComponent = "li">( const ComboboxSection = <T extends ValidComponent = 'li'>(
props: PolymorphicProps<T, ComboboxSectionProps<T>> props: PolymorphicProps<T, ComboboxSectionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxSectionProps, ["class"]) const [local, others] = splitProps(props as ComboboxSectionProps, ['class']);
return ( return (
<ComboboxPrimitive.Section <ComboboxPrimitive.Section
class={cn( class={cn(
"overflow-hidden p-1 px-2 py-1.5 text-xs font-medium text-muted-foreground ", 'overflow-hidden p-1 px-2 py-1.5 font-medium text-muted-foreground text-xs ',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type ComboboxControlProps< type ComboboxControlProps<
U, U,
T extends ValidComponent = "div" T extends ValidComponent = 'div',
> = ComboboxPrimitive.ComboboxControlProps<U, T> & { > = ComboboxPrimitive.ComboboxControlProps<U, T> & {
class?: string | undefined class?: string | undefined;
} };
const ComboboxControl = <T, U extends ValidComponent = "div">( const ComboboxControl = <T, U extends ValidComponent = 'div'>(
props: PolymorphicProps<U, ComboboxControlProps<T>> props: PolymorphicProps<U, ComboboxControlProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxControlProps<T>, ["class"]) const [local, others] = splitProps(props as ComboboxControlProps<T>, [
'class',
]);
return ( return (
<ComboboxPrimitive.Control <ComboboxPrimitive.Control
class={cn("flex h-10 items-center rounded-md border px-3", local.class)} class={cn('flex h-10 items-center rounded-md border px-3', local.class)}
{...others} {...others}
/> />
) );
} };
type ComboboxInputProps<T extends ValidComponent = "input"> = type ComboboxInputProps<T extends ValidComponent = 'input'> =
ComboboxPrimitive.ComboboxInputProps<T> & { class?: string | undefined } ComboboxPrimitive.ComboboxInputProps<T> & { class?: string | undefined };
const ComboboxInput = <T extends ValidComponent = "input">( const ComboboxInput = <T extends ValidComponent = 'input'>(
props: PolymorphicProps<T, ComboboxInputProps<T>> props: PolymorphicProps<T, ComboboxInputProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxInputProps, ["class"]) const [local, others] = splitProps(props as ComboboxInputProps, ['class']);
return ( return (
<ComboboxPrimitive.Input <ComboboxPrimitive.Input
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type ComboboxTriggerProps<T extends ValidComponent = "button"> = type ComboboxTriggerProps<T extends ValidComponent = 'button'> =
ComboboxPrimitive.ComboboxTriggerProps<T> & { ComboboxPrimitive.ComboboxTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const ComboboxTrigger = <T extends ValidComponent = "button">( const ComboboxTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, ComboboxTriggerProps<T>> props: PolymorphicProps<T, ComboboxTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxTriggerProps, ["class", "children"]) const [local, others] = splitProps(props as ComboboxTriggerProps, [
'class',
'children',
]);
return ( return (
<ComboboxPrimitive.Trigger class={cn("size-4 opacity-50", local.class)} {...others}> <ComboboxPrimitive.Trigger
class={cn('size-4 opacity-50', local.class)}
{...others}
>
<ComboboxPrimitive.Icon> <ComboboxPrimitive.Icon>
<Show <Show
when={local.children} when={local.children}
@@ -153,21 +164,21 @@ const ComboboxTrigger = <T extends ValidComponent = "button">(
</Show> </Show>
</ComboboxPrimitive.Icon> </ComboboxPrimitive.Icon>
</ComboboxPrimitive.Trigger> </ComboboxPrimitive.Trigger>
) );
} };
type ComboboxContentProps<T extends ValidComponent = "div"> = type ComboboxContentProps<T extends ValidComponent = 'div'> =
ComboboxPrimitive.ComboboxContentProps<T> & { class?: string | undefined } ComboboxPrimitive.ComboboxContentProps<T> & { class?: string | undefined };
const ComboboxContent = <T extends ValidComponent = "div">( const ComboboxContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ComboboxContentProps<T>> props: PolymorphicProps<T, ComboboxContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ComboboxContentProps, ["class"]) const [local, others] = splitProps(props as ComboboxContentProps, ['class']);
return ( return (
<ComboboxPrimitive.Portal> <ComboboxPrimitive.Portal>
<ComboboxPrimitive.Content <ComboboxPrimitive.Content
class={cn( 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", 'fade-in-80 relative z-50 min-w-32 animate-in overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md',
local.class local.class
)} )}
{...others} {...others}
@@ -175,8 +186,8 @@ const ComboboxContent = <T extends ValidComponent = "div">(
<ComboboxPrimitive.Listbox class="m-0 p-1" /> <ComboboxPrimitive.Listbox class="m-0 p-1" />
</ComboboxPrimitive.Content> </ComboboxPrimitive.Content>
</ComboboxPrimitive.Portal> </ComboboxPrimitive.Portal>
) );
} };
export { export {
Combobox, Combobox,
@@ -188,5 +199,5 @@ export {
ComboboxTrigger, ComboboxTrigger,
ComboboxInput, ComboboxInput,
ComboboxHiddenSelect, ComboboxHiddenSelect,
ComboboxContent ComboboxContent,
} };

View File

@@ -1,28 +1,35 @@
import type { Component, ComponentProps, ParentProps, VoidProps } from "solid-js" import type {
import { splitProps } from "solid-js" Component,
ComponentProps,
ParentProps,
VoidProps,
} from 'solid-js';
import { splitProps } from 'solid-js';
import type { DialogRootProps } from "@kobalte/core/dialog" import type { DialogRootProps } from '@kobalte/core/dialog';
import * as CommandPrimitive from "cmdk-solid" import * as CommandPrimitive from 'cmdk-solid';
import { cn } from "~/styles/utils" import { Dialog, DialogContent } from '~/components/ui/dialog';
import { Dialog, DialogContent } from "~/components/ui/dialog" import { cn } from '~/utils/styles';
const Command: Component<ParentProps<CommandPrimitive.CommandRootProps>> = (props) => { const Command: Component<ParentProps<CommandPrimitive.CommandRootProps>> = (
const [local, others] = splitProps(props, ["class"]) props
) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<CommandPrimitive.CommandRoot <CommandPrimitive.CommandRoot
class={cn( class={cn(
"flex size-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground blur-none", 'flex size-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground blur-none',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const CommandDialog: Component<ParentProps<DialogRootProps>> = (props) => { const CommandDialog: Component<ParentProps<DialogRootProps>> = (props) => {
const [local, others] = splitProps(props, ["children"]) const [local, others] = splitProps(props, ['children']);
return ( return (
<Dialog {...others}> <Dialog {...others}>
@@ -32,11 +39,13 @@ const CommandDialog: Component<ParentProps<DialogRootProps>> = (props) => {
</Command> </Command>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
) );
} };
const CommandInput: Component<VoidProps<CommandPrimitive.CommandInputProps>> = (props) => { const CommandInput: Component<VoidProps<CommandPrimitive.CommandInputProps>> = (
const [local, others] = splitProps(props, ["class"]) props
) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<div class="flex items-center border-b px-3" cmdk-input-wrapper=""> <div class="flex items-center border-b px-3" cmdk-input-wrapper="">
@@ -55,82 +64,100 @@ const CommandInput: Component<VoidProps<CommandPrimitive.CommandInputProps>> = (
</svg> </svg>
<CommandPrimitive.CommandInput <CommandPrimitive.CommandInput
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
</div> </div>
) );
} };
const CommandList: Component<ParentProps<CommandPrimitive.CommandListProps>> = (props) => { const CommandList: Component<ParentProps<CommandPrimitive.CommandListProps>> = (
const [local, others] = splitProps(props, ["class"]) props
) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<CommandPrimitive.CommandList <CommandPrimitive.CommandList
class={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", local.class)} class={cn('max-h-[300px] overflow-y-auto overflow-x-hidden', local.class)}
{...others} {...others}
/> />
) );
} };
const CommandEmpty: Component<ParentProps<CommandPrimitive.CommandEmptyProps>> = (props) => { const CommandEmpty: Component<
const [local, others] = splitProps(props, ["class"]) ParentProps<CommandPrimitive.CommandEmptyProps>
> = (props) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<CommandPrimitive.CommandEmpty <CommandPrimitive.CommandEmpty
class={cn("py-6 text-center text-sm", local.class)} class={cn('py-6 text-center text-sm', local.class)}
{...others} {...others}
/> />
) );
} };
const CommandGroup: Component<ParentProps<CommandPrimitive.CommandGroupProps>> = (props) => { const CommandGroup: Component<
const [local, others] = splitProps(props, ["class"]) ParentProps<CommandPrimitive.CommandGroupProps>
> = (props) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<CommandPrimitive.CommandGroup <CommandPrimitive.CommandGroup
class={cn( 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", 'overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group-heading]]:text-xs',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const CommandSeparator: Component<VoidProps<CommandPrimitive.CommandSeparatorProps>> = (props) => { const CommandSeparator: Component<
const [local, others] = splitProps(props, ["class"]) VoidProps<CommandPrimitive.CommandSeparatorProps>
> = (props) => {
const [local, others] = splitProps(props, ['class']);
return <CommandPrimitive.CommandSeparator class={cn("h-px bg-border", local.class)} {...others} /> return (
} <CommandPrimitive.CommandSeparator
class={cn('h-px bg-border', local.class)}
{...others}
/>
);
};
const CommandItem: Component<ParentProps<CommandPrimitive.CommandItemProps>> = (props) => { const CommandItem: Component<ParentProps<CommandPrimitive.CommandItemProps>> = (
const [local, others] = splitProps(props, ["class"]) props
) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<CommandPrimitive.CommandItem <CommandPrimitive.CommandItem
cmdk-item="" cmdk-item=""
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
const CommandShortcut: Component<ComponentProps<"span">> = (props) => { const CommandShortcut: Component<ComponentProps<'span'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<span <span
class={cn("ml-auto text-xs tracking-widest text-muted-foreground", local.class)} class={cn(
'ml-auto text-muted-foreground text-xs tracking-widest',
local.class
)}
{...others} {...others}
/> />
) );
} };
export { export {
Command, Command,
@@ -141,5 +168,5 @@ export {
CommandGroup, CommandGroup,
CommandItem, CommandItem,
CommandShortcut, CommandShortcut,
CommandSeparator CommandSeparator,
} };

View File

@@ -1,99 +1,113 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as ContextMenuPrimitive from "@kobalte/core/context-menu" import * as ContextMenuPrimitive from '@kobalte/core/context-menu';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const ContextMenuTrigger = ContextMenuPrimitive.Trigger const ContextMenuTrigger = ContextMenuPrimitive.Trigger;
const ContextMenuPortal = ContextMenuPrimitive.Portal const ContextMenuPortal = ContextMenuPrimitive.Portal;
const ContextMenuSub = ContextMenuPrimitive.Sub const ContextMenuSub = ContextMenuPrimitive.Sub;
const ContextMenuGroup = ContextMenuPrimitive.Group const ContextMenuGroup = ContextMenuPrimitive.Group;
const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
const ContextMenu: Component<ContextMenuPrimitive.ContextMenuRootProps> = (props) => { const ContextMenu: Component<ContextMenuPrimitive.ContextMenuRootProps> = (
return <ContextMenuPrimitive.Root gutter={4} {...props} /> props
} ) => {
return <ContextMenuPrimitive.Root gutter={4} {...props} />;
};
type ContextMenuContentProps<T extends ValidComponent = "div"> = type ContextMenuContentProps<T extends ValidComponent = 'div'> =
ContextMenuPrimitive.ContextMenuContentProps<T> & { ContextMenuPrimitive.ContextMenuContentProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const ContextMenuContent = <T extends ValidComponent = "div">( const ContextMenuContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ContextMenuContentProps<T>> props: PolymorphicProps<T, ContextMenuContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuContentProps, ["class"]) const [local, others] = splitProps(props as ContextMenuContentProps, [
'class',
]);
return ( return (
<ContextMenuPrimitive.Portal> <ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content <ContextMenuPrimitive.Content
class={cn( 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", 'z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-in overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
local.class local.class
)} )}
{...others} {...others}
/> />
</ContextMenuPrimitive.Portal> </ContextMenuPrimitive.Portal>
) );
} };
type ContextMenuItemProps<T extends ValidComponent = "div"> = type ContextMenuItemProps<T extends ValidComponent = 'div'> =
ContextMenuPrimitive.ContextMenuItemProps<T> & { ContextMenuPrimitive.ContextMenuItemProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const ContextMenuItem = <T extends ValidComponent = "div">( const ContextMenuItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ContextMenuItemProps<T>> props: PolymorphicProps<T, ContextMenuItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuItemProps, ["class"]) const [local, others] = splitProps(props as ContextMenuItemProps, ['class']);
return ( return (
<ContextMenuPrimitive.Item <ContextMenuPrimitive.Item
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
const ContextMenuShortcut: Component<ComponentProps<"span">> = (props) => { const ContextMenuShortcut: Component<ComponentProps<'span'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) 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 ( return (
<ContextMenuPrimitive.Separator <span
class={cn("-mx-1 my-1 h-px bg-muted", local.class)} class={cn('ml-auto text-xs tracking-widest opacity-60', local.class)}
{...others} {...others}
/> />
) );
} };
type ContextMenuSubTriggerProps<T extends ValidComponent = "div"> = 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> & { ContextMenuPrimitive.ContextMenuSubTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const ContextMenuSubTrigger = <T extends ValidComponent = "div">( const ContextMenuSubTrigger = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ContextMenuSubTriggerProps<T>> props: PolymorphicProps<T, ContextMenuSubTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuSubTriggerProps, ["class", "children"]) const [local, others] = splitProps(props as ContextMenuSubTriggerProps, [
'class',
'children',
]);
return ( return (
<ContextMenuPrimitive.SubTrigger <ContextMenuPrimitive.SubTrigger
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
@@ -112,43 +126,48 @@ const ContextMenuSubTrigger = <T extends ValidComponent = "div">(
<path d="M9 6l6 6l-6 6" /> <path d="M9 6l6 6l-6 6" />
</svg> </svg>
</ContextMenuPrimitive.SubTrigger> </ContextMenuPrimitive.SubTrigger>
) );
} };
type ContextMenuSubContentProps<T extends ValidComponent = "div"> = type ContextMenuSubContentProps<T extends ValidComponent = 'div'> =
ContextMenuPrimitive.ContextMenuSubContentProps<T> & { ContextMenuPrimitive.ContextMenuSubContentProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const ContextMenuSubContent = <T extends ValidComponent = "div">( const ContextMenuSubContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ContextMenuSubContentProps<T>> props: PolymorphicProps<T, ContextMenuSubContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuSubContentProps, ["class"]) const [local, others] = splitProps(props as ContextMenuSubContentProps, [
'class',
]);
return ( return (
<ContextMenuPrimitive.SubContent <ContextMenuPrimitive.SubContent
class={cn( 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", 'z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-in overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type ContextMenuCheckboxItemProps<T extends ValidComponent = "div"> = type ContextMenuCheckboxItemProps<T extends ValidComponent = 'div'> =
ContextMenuPrimitive.ContextMenuCheckboxItemProps<T> & { ContextMenuPrimitive.ContextMenuCheckboxItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const ContextMenuCheckboxItem = <T extends ValidComponent = "div">( const ContextMenuCheckboxItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ContextMenuCheckboxItemProps<T>> props: PolymorphicProps<T, ContextMenuCheckboxItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuCheckboxItemProps, ["class", "children"]) const [local, others] = splitProps(props as ContextMenuCheckboxItemProps, [
'class',
'children',
]);
return ( return (
<ContextMenuPrimitive.CheckboxItem <ContextMenuPrimitive.CheckboxItem
class={cn( 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", 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
local.class local.class
)} )}
{...others} {...others}
@@ -171,40 +190,45 @@ const ContextMenuCheckboxItem = <T extends ValidComponent = "div">(
</span> </span>
{local.children} {local.children}
</ContextMenuPrimitive.CheckboxItem> </ContextMenuPrimitive.CheckboxItem>
) );
} };
type ContextMenuGroupLabelProps<T extends ValidComponent = "span"> = type ContextMenuGroupLabelProps<T extends ValidComponent = 'span'> =
ContextMenuPrimitive.ContextMenuGroupLabelProps<T> & { ContextMenuPrimitive.ContextMenuGroupLabelProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const ContextMenuGroupLabel = <T extends ValidComponent = "span">( const ContextMenuGroupLabel = <T extends ValidComponent = 'span'>(
props: PolymorphicProps<T, ContextMenuGroupLabelProps<T>> props: PolymorphicProps<T, ContextMenuGroupLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuGroupLabelProps, ["class"]) const [local, others] = splitProps(props as ContextMenuGroupLabelProps, [
'class',
]);
return ( return (
<ContextMenuPrimitive.GroupLabel <ContextMenuPrimitive.GroupLabel
class={cn("px-2 py-1.5 text-sm font-semibold", local.class)} class={cn('px-2 py-1.5 font-semibold text-sm', local.class)}
{...others} {...others}
/> />
) );
} };
type ContextMenuRadioItemProps<T extends ValidComponent = "div"> = type ContextMenuRadioItemProps<T extends ValidComponent = 'div'> =
ContextMenuPrimitive.ContextMenuRadioItemProps<T> & { ContextMenuPrimitive.ContextMenuRadioItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const ContextMenuRadioItem = <T extends ValidComponent = "div">( const ContextMenuRadioItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ContextMenuRadioItemProps<T>> props: PolymorphicProps<T, ContextMenuRadioItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ContextMenuRadioItemProps, ["class", "children"]) const [local, others] = splitProps(props as ContextMenuRadioItemProps, [
'class',
'children',
]);
return ( return (
<ContextMenuPrimitive.RadioItem <ContextMenuPrimitive.RadioItem
class={cn( 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", 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
local.class local.class
)} )}
{...others} {...others}
@@ -227,8 +251,8 @@ const ContextMenuRadioItem = <T extends ValidComponent = "div">(
</span> </span>
{local.children} {local.children}
</ContextMenuPrimitive.RadioItem> </ContextMenuPrimitive.RadioItem>
) );
} };
export { export {
ContextMenu, ContextMenu,
@@ -245,5 +269,5 @@ export {
ContextMenuGroup, ContextMenuGroup,
ContextMenuGroupLabel, ContextMenuGroupLabel,
ContextMenuRadioGroup, ContextMenuRadioGroup,
ContextMenuRadioItem ContextMenuRadioItem,
} };

View File

@@ -1,53 +1,53 @@
import { children, Show, splitProps } from "solid-js" import { Show, children, splitProps } from 'solid-js';
import { DatePicker as DatePickerPrimitive } from "@ark-ui/solid" import { DatePicker as DatePickerPrimitive } from '@ark-ui/solid';
import { cn } from "~/styles/utils" import { buttonVariants } from '~/components/ui/button';
import { buttonVariants } from "~/components/ui/button" import { cn } from '~/utils/styles';
const DatePicker = DatePickerPrimitive.Root const DatePicker = DatePickerPrimitive.Root;
const DatePickerLabel = DatePickerPrimitive.Label const DatePickerLabel = DatePickerPrimitive.Label;
const DatePickerContext = DatePickerPrimitive.Context const DatePickerContext = DatePickerPrimitive.Context;
const DatePickerTableHead = DatePickerPrimitive.TableHead const DatePickerTableHead = DatePickerPrimitive.TableHead;
const DatePickerTableBody = DatePickerPrimitive.TableBody const DatePickerTableBody = DatePickerPrimitive.TableBody;
const DatePickerYearSelect = DatePickerPrimitive.YearSelect const DatePickerYearSelect = DatePickerPrimitive.YearSelect;
const DatePickerMonthSelect = DatePickerPrimitive.MonthSelect const DatePickerMonthSelect = DatePickerPrimitive.MonthSelect;
const DatePickerPositioner = DatePickerPrimitive.Positioner const DatePickerPositioner = DatePickerPrimitive.Positioner;
const DatePickerControl = (props: DatePickerPrimitive.ControlProps) => { const DatePickerControl = (props: DatePickerPrimitive.ControlProps) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.Control <DatePickerPrimitive.Control
class={cn("inline-flex items-center gap-1", local.class)} class={cn('inline-flex items-center gap-1', local.class)}
{...others} {...others}
/> />
) );
} };
const DatePickerInput = (props: DatePickerPrimitive.InputProps) => { const DatePickerInput = (props: DatePickerPrimitive.InputProps) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.Input <DatePickerPrimitive.Input
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
const DatePickerTrigger = (props: DatePickerPrimitive.TriggerProps) => { const DatePickerTrigger = (props: DatePickerPrimitive.TriggerProps) => {
const [local, others] = splitProps(props, ["class", "children"]) const [local, others] = splitProps(props, ['class', 'children']);
// prevents rendering children twice // prevents rendering children twice
const resolvedChildren = children(() => local.children) const resolvedChildren = children(() => local.children);
const hasChildren = () => resolvedChildren.toArray().length !== 0 const hasChildren = () => resolvedChildren.toArray().length !== 0;
return ( return (
<DatePickerPrimitive.Trigger <DatePickerPrimitive.Trigger
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
@@ -73,54 +73,59 @@ const DatePickerTrigger = (props: DatePickerPrimitive.TriggerProps) => {
</svg> </svg>
</Show> </Show>
</DatePickerPrimitive.Trigger> </DatePickerPrimitive.Trigger>
) );
} };
const DatePickerContent = (props: DatePickerPrimitive.ContentProps) => { const DatePickerContent = (props: DatePickerPrimitive.ContentProps) => {
const [local, others] = splitProps(props, ["class", "children"]) const [local, others] = splitProps(props, ['class', 'children']);
return ( return (
<DatePickerPrimitive.Content <DatePickerPrimitive.Content
class={cn( 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", 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 rounded-md border bg-popover p-3 text-popover-foreground shadow-md outline-none data-[state=closed]:animate-out data-[state=open]:animate-in',
local.class local.class
)} )}
{...others} {...others}
> >
{local.children} {local.children}
</DatePickerPrimitive.Content> </DatePickerPrimitive.Content>
) );
} };
const DatePickerView = (props: DatePickerPrimitive.ViewProps) => { const DatePickerView = (props: DatePickerPrimitive.ViewProps) => {
const [local, others] = splitProps(props, ["class"]) 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 ( return (
<DatePickerPrimitive.ViewControl <DatePickerPrimitive.View
class={cn("flex items-center justify-between gap-4", local.class)} class={cn('space-y-4', local.class)}
{...others} {...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 DatePickerPrevTrigger = (props: DatePickerPrimitive.PrevTriggerProps) => {
const [local, others] = splitProps(props, ["class", "children"]) const [local, others] = splitProps(props, ['class', 'children']);
// prevents rendering children twice // prevents rendering children twice
const resolvedChildren = children(() => local.children) const resolvedChildren = children(() => local.children);
const hasChildren = () => resolvedChildren.toArray().length !== 0 const hasChildren = () => resolvedChildren.toArray().length !== 0;
return ( return (
<DatePickerPrimitive.PrevTrigger <DatePickerPrimitive.PrevTrigger
class={cn( class={cn(
buttonVariants({ buttonVariants({
variant: "outline" variant: 'outline',
}), }),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100", 'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
local.class local.class
)} )}
{...others} {...others}
@@ -141,23 +146,23 @@ const DatePickerPrevTrigger = (props: DatePickerPrimitive.PrevTriggerProps) => {
</svg> </svg>
</Show> </Show>
</DatePickerPrimitive.PrevTrigger> </DatePickerPrimitive.PrevTrigger>
) );
} };
const DatePickerNextTrigger = (props: DatePickerPrimitive.NextTriggerProps) => { const DatePickerNextTrigger = (props: DatePickerPrimitive.NextTriggerProps) => {
const [local, others] = splitProps(props, ["class", "children"]) const [local, others] = splitProps(props, ['class', 'children']);
// prevents rendering children twice // prevents rendering children twice
const resolvedChildren = children(() => local.children) const resolvedChildren = children(() => local.children);
const hasChildren = () => resolvedChildren.toArray().length !== 0 const hasChildren = () => resolvedChildren.toArray().length !== 0;
return ( return (
<DatePickerPrimitive.NextTrigger <DatePickerPrimitive.NextTrigger
class={cn( class={cn(
buttonVariants({ buttonVariants({
variant: "outline" variant: 'outline',
}), }),
"size-7 bg-transparent p-0 opacity-50 hover:opacity-100", 'size-7 bg-transparent p-0 opacity-50 hover:opacity-100',
local.class local.class
)} )}
{...others} {...others}
@@ -178,83 +183,96 @@ const DatePickerNextTrigger = (props: DatePickerPrimitive.NextTriggerProps) => {
</svg> </svg>
</Show> </Show>
</DatePickerPrimitive.NextTrigger> </DatePickerPrimitive.NextTrigger>
) );
} };
const DatePickerViewTrigger = (props: DatePickerPrimitive.ViewTriggerProps) => { const DatePickerViewTrigger = (props: DatePickerPrimitive.ViewTriggerProps) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.ViewTrigger <DatePickerPrimitive.ViewTrigger
class={cn(buttonVariants({ variant: "ghost" }), "h-7", local.class)} class={cn(buttonVariants({ variant: 'ghost' }), 'h-7', local.class)}
{...others} {...others}
/> />
) );
} };
const DatePickerRangeText = (props: DatePickerPrimitive.RangeTextProps) => { const DatePickerRangeText = (props: DatePickerPrimitive.RangeTextProps) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.RangeText class={cn("text-sm font-medium", local.class)} {...others} /> <DatePickerPrimitive.RangeText
) class={cn('font-medium text-sm', local.class)}
} {...others}
/>
);
};
const DatePickerTable = (props: DatePickerPrimitive.TableProps) => { const DatePickerTable = (props: DatePickerPrimitive.TableProps) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.Table <DatePickerPrimitive.Table
class={cn("w-full border-collapse space-y-1", local.class)} class={cn('w-full border-collapse space-y-1', local.class)}
{...others} {...others}
/> />
) );
} };
const DatePickerTableRow = (props: DatePickerPrimitive.TableRowProps) => { const DatePickerTableRow = (props: DatePickerPrimitive.TableRowProps) => {
const [local, others] = splitProps(props, ["class"]) 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 ( return (
<DatePickerPrimitive.TableHeader <DatePickerPrimitive.TableRow
class={cn("w-8 flex-1 text-[0.8rem] font-normal text-muted-foreground", local.class)} class={cn('mt-2 flex w-full', local.class)}
{...others} {...others}
/> />
) );
} };
const DatePickerTableHeader = (props: DatePickerPrimitive.TableHeaderProps) => {
const [local, others] = splitProps(props, ['class']);
return (
<DatePickerPrimitive.TableHeader
class={cn(
'w-8 flex-1 font-normal text-[0.8rem] text-muted-foreground',
local.class
)}
{...others}
/>
);
};
const DatePickerTableCell = (props: DatePickerPrimitive.TableCellProps) => { const DatePickerTableCell = (props: DatePickerPrimitive.TableCellProps) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.TableCell <DatePickerPrimitive.TableCell
class={cn( 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", '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]]:last-of-type:rounded-r-md has-[[data-in-range]]:first-of-type:rounded-l-md',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const DatePickerTableCellTrigger = (props: DatePickerPrimitive.TableCellTriggerProps) => { const DatePickerTableCellTrigger = (
const [local, others] = splitProps(props, ["class"]) props: DatePickerPrimitive.TableCellTriggerProps
) => {
const [local, others] = splitProps(props, ['class']);
return ( return (
<DatePickerPrimitive.TableCellTrigger <DatePickerPrimitive.TableCellTrigger
class={cn( class={cn(
buttonVariants({ variant: "ghost" }), buttonVariants({ variant: 'ghost' }),
"size-8 w-full p-0 font-normal data-[selected]:opacity-100", 'size-8 w-full p-0 font-normal data-[selected]:opacity-100',
"data-[today]:bg-accent data-[today]:text-accent-foreground", 'data-[today]:bg-accent data-[today]:text-accent-foreground',
"[&:is([data-today][data-selected])]:bg-primary [&:is([data-today][data-selected])]:text-primary-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-[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-[disabled]:text-muted-foreground data-[disabled]:opacity-50',
"data-[outside-range]:text-muted-foreground data-[outside-range]: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", '[&: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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
export { export {
DatePicker, DatePicker,
@@ -279,5 +297,5 @@ export {
DatePickerTableCellTrigger, DatePickerTableCellTrigger,
DatePickerYearSelect, DatePickerYearSelect,
DatePickerMonthSelect, DatePickerMonthSelect,
DatePickerPositioner DatePickerPositioner,
} };

View File

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

View File

@@ -1,65 +1,68 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as DialogPrimitive from "@kobalte/core/dialog" import * as DialogPrimitive from '@kobalte/core/dialog';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Dialog = DialogPrimitive.Root const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal: Component<DialogPrimitive.DialogPortalProps> = (props) => { const DialogPortal: Component<DialogPrimitive.DialogPortalProps> = (props) => {
const [, rest] = splitProps(props, ["children"]) const [, rest] = splitProps(props, ['children']);
return ( return (
<DialogPrimitive.Portal {...rest}> <DialogPrimitive.Portal {...rest}>
<div class="fixed inset-0 z-50 flex items-start justify-center sm:items-center"> <div class="fixed inset-0 z-50 flex items-start justify-center sm:items-center">
{props.children} {props.children}
</div> </div>
</DialogPrimitive.Portal> </DialogPrimitive.Portal>
) );
} };
type DialogOverlayProps<T extends ValidComponent = "div"> = type DialogOverlayProps<T extends ValidComponent = 'div'> =
DialogPrimitive.DialogOverlayProps<T> & { class?: string | undefined } DialogPrimitive.DialogOverlayProps<T> & { class?: string | undefined };
const DialogOverlay = <T extends ValidComponent = "div">( const DialogOverlay = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DialogOverlayProps<T>> props: PolymorphicProps<T, DialogOverlayProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DialogOverlayProps, ["class"]) const [, rest] = splitProps(props as DialogOverlayProps, ['class']);
return ( return (
<DialogPrimitive.Overlay <DialogPrimitive.Overlay
class={cn( 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", 'data-[closed]:fade-out-0 data-[expanded]:fade-in-0 fixed inset-0 z-50 bg-background/80 data-[closed]:animate-out data-[expanded]:animate-in',
props.class props.class
)} )}
{...rest} {...rest}
/> />
) );
} };
type DialogContentProps<T extends ValidComponent = "div"> = type DialogContentProps<T extends ValidComponent = 'div'> =
DialogPrimitive.DialogContentProps<T> & { DialogPrimitive.DialogContentProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const DialogContent = <T extends ValidComponent = "div">( const DialogContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DialogContentProps<T>> props: PolymorphicProps<T, DialogContentProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DialogContentProps, ["class", "children"]) const [, rest] = splitProps(props as DialogContentProps, [
'class',
'children',
]);
return ( return (
<DialogPortal> <DialogPortal>
<DialogOverlay /> <DialogOverlay />
<DialogPrimitive.Content <DialogPrimitive.Content
class={cn( 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", '-translate-x-1/2 -translate-y-1/2 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%] fixed top-1/2 left-1/2 z-50 grid max-h-screen w-full max-w-lg gap-4 overflow-y-auto border bg-background p-6 shadow-lg duration-200 data-[closed]:animate-out data-[expanded]:animate-in sm:rounded-lg',
props.class props.class
)} )}
{...rest} {...rest}
> >
{props.children} {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"> <DialogPrimitive.CloseButton class="absolute top-4 right-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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -77,58 +80,71 @@ const DialogContent = <T extends ValidComponent = "div">(
</DialogPrimitive.CloseButton> </DialogPrimitive.CloseButton>
</DialogPrimitive.Content> </DialogPrimitive.Content>
</DialogPortal> </DialogPortal>
) );
} };
const DialogHeader: Component<ComponentProps<"div">> = (props) => { const DialogHeader: Component<ComponentProps<'div'>> = (props) => {
const [, rest] = splitProps(props, ["class"]) 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 ( return (
<div <div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", props.class)} class={cn(
'flex flex-col space-y-1.5 text-center sm:text-left',
props.class
)}
{...rest} {...rest}
/> />
) );
} };
type DialogTitleProps<T extends ValidComponent = "h2"> = DialogPrimitive.DialogTitleProps<T> & { const DialogFooter: Component<ComponentProps<'div'>> = (props) => {
class?: string | undefined 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}
/>
);
};
const DialogTitle = <T extends ValidComponent = "h2">( type DialogTitleProps<T extends ValidComponent = 'h2'> =
DialogPrimitive.DialogTitleProps<T> & {
class?: string | undefined;
};
const DialogTitle = <T extends ValidComponent = 'h2'>(
props: PolymorphicProps<T, DialogTitleProps<T>> props: PolymorphicProps<T, DialogTitleProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DialogTitleProps, ["class"]) const [, rest] = splitProps(props as DialogTitleProps, ['class']);
return ( return (
<DialogPrimitive.Title <DialogPrimitive.Title
class={cn("text-lg font-semibold leading-none tracking-tight", props.class)} class={cn(
'font-semibold text-lg leading-none tracking-tight',
props.class
)}
{...rest} {...rest}
/> />
) );
} };
type DialogDescriptionProps<T extends ValidComponent = "p"> = type DialogDescriptionProps<T extends ValidComponent = 'p'> =
DialogPrimitive.DialogDescriptionProps<T> & { DialogPrimitive.DialogDescriptionProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const DialogDescription = <T extends ValidComponent = "p">( const DialogDescription = <T extends ValidComponent = 'p'>(
props: PolymorphicProps<T, DialogDescriptionProps<T>> props: PolymorphicProps<T, DialogDescriptionProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DialogDescriptionProps, ["class"]) const [, rest] = splitProps(props as DialogDescriptionProps, ['class']);
return ( return (
<DialogPrimitive.Description <DialogPrimitive.Description
class={cn("text-sm text-muted-foreground", props.class)} class={cn('text-muted-foreground text-sm', props.class)}
{...rest} {...rest}
/> />
) );
} };
export { export {
Dialog, Dialog,
@@ -137,5 +153,5 @@ export {
DialogHeader, DialogHeader,
DialogFooter, DialogFooter,
DialogTitle, DialogTitle,
DialogDescription DialogDescription,
} };

View File

@@ -1,61 +1,66 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { import type {
ContentProps, ContentProps,
DescriptionProps, DescriptionProps,
DynamicProps, DynamicProps,
LabelProps, LabelProps,
OverlayProps OverlayProps,
} from "@corvu/drawer" } from '@corvu/drawer';
import DrawerPrimitive from "@corvu/drawer" import DrawerPrimitive from '@corvu/drawer';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Drawer = DrawerPrimitive const Drawer = DrawerPrimitive;
const DrawerTrigger = DrawerPrimitive.Trigger const DrawerTrigger = DrawerPrimitive.Trigger;
const DrawerPortal = DrawerPrimitive.Portal const DrawerPortal = DrawerPrimitive.Portal;
const DrawerClose = DrawerPrimitive.Close const DrawerClose = DrawerPrimitive.Close;
type DrawerOverlayProps<T extends ValidComponent = "div"> = OverlayProps<T> & { class?: string } type DrawerOverlayProps<T extends ValidComponent = 'div'> = OverlayProps<T> & {
class?: string;
};
const DrawerOverlay = <T extends ValidComponent = "div">( const DrawerOverlay = <T extends ValidComponent = 'div'>(
props: DynamicProps<T, DrawerOverlayProps<T>> props: DynamicProps<T, DrawerOverlayProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DrawerOverlayProps, ["class"]) const [, rest] = splitProps(props as DrawerOverlayProps, ['class']);
const drawerContext = DrawerPrimitive.useContext() const drawerContext = DrawerPrimitive.useContext();
return ( return (
<DrawerPrimitive.Overlay <DrawerPrimitive.Overlay
class={cn( class={cn(
"fixed inset-0 z-50 data-[transitioning]:transition-colors data-[transitioning]:duration-300", 'fixed inset-0 z-50 data-[transitioning]:transition-colors data-[transitioning]:duration-300',
props.class props.class
)} )}
style={{ style={{
"background-color": `rgb(0 0 0 / ${0.8 * drawerContext.openPercentage()})` 'background-color': `rgb(0 0 0 / ${0.8 * drawerContext.openPercentage()})`,
}} }}
{...rest} {...rest}
/> />
) );
} };
type DrawerContentProps<T extends ValidComponent = "div"> = ContentProps<T> & { type DrawerContentProps<T extends ValidComponent = 'div'> = ContentProps<T> & {
class?: string class?: string;
children?: JSX.Element children?: JSX.Element;
} };
const DrawerContent = <T extends ValidComponent = "div">( const DrawerContent = <T extends ValidComponent = 'div'>(
props: DynamicProps<T, DrawerContentProps<T>> props: DynamicProps<T, DrawerContentProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DrawerContentProps, ["class", "children"]) const [, rest] = splitProps(props as DrawerContentProps, [
'class',
'children',
]);
return ( return (
<DrawerPortal> <DrawerPortal>
<DrawerOverlay /> <DrawerOverlay />
<DrawerPrimitive.Content <DrawerPrimitive.Content
class={cn( 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", '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 props.class
)} )}
{...rest} {...rest}
@@ -64,48 +69,61 @@ const DrawerContent = <T extends ValidComponent = "div">(
{props.children} {props.children}
</DrawerPrimitive.Content> </DrawerPrimitive.Content>
</DrawerPortal> </DrawerPortal>
) );
} };
const DrawerHeader: Component<ComponentProps<"div">> = (props) => { const DrawerHeader: Component<ComponentProps<'div'>> = (props) => {
const [, rest] = splitProps(props, ["class"]) const [, rest] = splitProps(props, ['class']);
return <div class={cn("grid gap-1.5 p-4 text-center sm:text-left", props.class)} {...rest} /> 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 DrawerFooter: Component<ComponentProps<'div'>> = (props) => {
const [, rest] = splitProps(props, ["class"]) const [, rest] = splitProps(props, ['class']);
return <div class={cn("t-auto flex flex-col gap-2 p-4", props.class)} {...rest} /> 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 } type DrawerTitleProps<T extends ValidComponent = 'div'> = LabelProps<T> & {
class?: string;
};
const DrawerTitle = <T extends ValidComponent = "div">( const DrawerTitle = <T extends ValidComponent = 'div'>(
props: DynamicProps<T, DrawerTitleProps<T>> props: DynamicProps<T, DrawerTitleProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DrawerTitleProps, ["class"]) const [, rest] = splitProps(props as DrawerTitleProps, ['class']);
return ( return (
<DrawerPrimitive.Label <DrawerPrimitive.Label
class={cn("text-lg font-semibold leading-none tracking-tight", props.class)} class={cn(
'font-semibold text-lg leading-none tracking-tight',
props.class
)}
{...rest} {...rest}
/> />
) );
} };
type DrawerDescriptionProps<T extends ValidComponent = "div"> = DescriptionProps<T> & { type DrawerDescriptionProps<T extends ValidComponent = 'div'> =
class?: string DescriptionProps<T> & {
} class?: string;
};
const DrawerDescription = <T extends ValidComponent = "div">( const DrawerDescription = <T extends ValidComponent = 'div'>(
props: DynamicProps<T, DrawerDescriptionProps<T>> props: DynamicProps<T, DrawerDescriptionProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DrawerDescriptionProps, ["class"]) const [, rest] = splitProps(props as DrawerDescriptionProps, ['class']);
return ( return (
<DrawerPrimitive.Description <DrawerPrimitive.Description
class={cn("text-sm text-muted-foreground", props.class)} class={cn('text-muted-foreground text-sm', props.class)}
{...rest} {...rest}
/> />
) );
} };
export { export {
Drawer, Drawer,
@@ -117,5 +135,5 @@ export {
DrawerHeader, DrawerHeader,
DrawerFooter, DrawerFooter,
DrawerTitle, DrawerTitle,
DrawerDescription DrawerDescription,
} };

View File

@@ -1,109 +1,125 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as DropdownMenuPrimitive from "@kobalte/core/dropdown-menu" import * as DropdownMenuPrimitive from '@kobalte/core/dropdown-menu';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
const DropdownMenuPortal = DropdownMenuPrimitive.Portal const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
const DropdownMenuSub = DropdownMenuPrimitive.Sub const DropdownMenuSub = DropdownMenuPrimitive.Sub;
const DropdownMenuGroup = DropdownMenuPrimitive.Group const DropdownMenuGroup = DropdownMenuPrimitive.Group;
const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
const DropdownMenu: Component<DropdownMenuPrimitive.DropdownMenuRootProps> = (props) => { const DropdownMenu: Component<DropdownMenuPrimitive.DropdownMenuRootProps> = (
return <DropdownMenuPrimitive.Root gutter={4} {...props} /> props
} ) => {
return <DropdownMenuPrimitive.Root gutter={4} {...props} />;
};
type DropdownMenuContentProps<T extends ValidComponent = "div"> = type DropdownMenuContentProps<T extends ValidComponent = 'div'> =
DropdownMenuPrimitive.DropdownMenuContentProps<T> & { DropdownMenuPrimitive.DropdownMenuContentProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const DropdownMenuContent = <T extends ValidComponent = "div">( const DropdownMenuContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DropdownMenuContentProps<T>> props: PolymorphicProps<T, DropdownMenuContentProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuContentProps, ["class"]) const [, rest] = splitProps(props as DropdownMenuContentProps, ['class']);
return ( return (
<DropdownMenuPrimitive.Portal> <DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content <DropdownMenuPrimitive.Content
class={cn( 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", '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 props.class
)} )}
{...rest} {...rest}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
) );
} };
type DropdownMenuItemProps<T extends ValidComponent = "div"> = type DropdownMenuItemProps<T extends ValidComponent = 'div'> =
DropdownMenuPrimitive.DropdownMenuItemProps<T> & { DropdownMenuPrimitive.DropdownMenuItemProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const DropdownMenuItem = <T extends ValidComponent = "div">( const DropdownMenuItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DropdownMenuItemProps<T>> props: PolymorphicProps<T, DropdownMenuItemProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuItemProps, ["class"]) const [, rest] = splitProps(props as DropdownMenuItemProps, ['class']);
return ( return (
<DropdownMenuPrimitive.Item <DropdownMenuPrimitive.Item
class={cn( 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", '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 props.class
)} )}
{...rest} {...rest}
/> />
) );
} };
const DropdownMenuShortcut: Component<ComponentProps<"span">> = (props) => { const DropdownMenuShortcut: Component<ComponentProps<'span'>> = (props) => {
const [, rest] = splitProps(props, ["class"]) const [, rest] = splitProps(props, ['class']);
return <span class={cn("ml-auto text-xs tracking-widest opacity-60", props.class)} {...rest} /> return (
} <span
class={cn('ml-auto text-xs tracking-widest opacity-60', props.class)}
{...rest}
/>
);
};
const DropdownMenuLabel: Component<ComponentProps<"div"> & { inset?: boolean }> = (props) => { const DropdownMenuLabel: Component<
const [, rest] = splitProps(props, ["class", "inset"]) ComponentProps<'div'> & { inset?: boolean }
> = (props) => {
const [, rest] = splitProps(props, ['class', 'inset']);
return ( return (
<div <div
class={cn("px-2 py-1.5 text-sm font-semibold", props.inset && "pl-8", props.class)} class={cn(
'px-2 py-1.5 font-semibold text-sm',
props.inset && 'pl-8',
props.class
)}
{...rest} {...rest}
/> />
) );
} };
type DropdownMenuSeparatorProps<T extends ValidComponent = "hr"> = type DropdownMenuSeparatorProps<T extends ValidComponent = 'hr'> =
DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & { DropdownMenuPrimitive.DropdownMenuSeparatorProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const DropdownMenuSeparator = <T extends ValidComponent = "hr">( const DropdownMenuSeparator = <T extends ValidComponent = 'hr'>(
props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>> props: PolymorphicProps<T, DropdownMenuSeparatorProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuSeparatorProps, ["class"]) const [, rest] = splitProps(props as DropdownMenuSeparatorProps, ['class']);
return ( return (
<DropdownMenuPrimitive.Separator <DropdownMenuPrimitive.Separator
class={cn("-mx-1 my-1 h-px bg-muted", props.class)} class={cn('-mx-1 my-1 h-px bg-muted', props.class)}
{...rest} {...rest}
/> />
) );
} };
type DropdownMenuSubTriggerProps<T extends ValidComponent = "div"> = type DropdownMenuSubTriggerProps<T extends ValidComponent = 'div'> =
DropdownMenuPrimitive.DropdownMenuSubTriggerProps<T> & { DropdownMenuPrimitive.DropdownMenuSubTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const DropdownMenuSubTrigger = <T extends ValidComponent = "div">( const DropdownMenuSubTrigger = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DropdownMenuSubTriggerProps<T>> props: PolymorphicProps<T, DropdownMenuSubTriggerProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuSubTriggerProps, ["class", "children"]) const [, rest] = splitProps(props as DropdownMenuSubTriggerProps, [
'class',
'children',
]);
return ( return (
<DropdownMenuPrimitive.SubTrigger <DropdownMenuPrimitive.SubTrigger
class={cn( 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", '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 props.class
)} )}
{...rest} {...rest}
@@ -122,43 +138,46 @@ const DropdownMenuSubTrigger = <T extends ValidComponent = "div">(
<path d="M9 6l6 6l-6 6" /> <path d="M9 6l6 6l-6 6" />
</svg> </svg>
</DropdownMenuPrimitive.SubTrigger> </DropdownMenuPrimitive.SubTrigger>
) );
} };
type DropdownMenuSubContentProps<T extends ValidComponent = "div"> = type DropdownMenuSubContentProps<T extends ValidComponent = 'div'> =
DropdownMenuPrimitive.DropdownMenuSubContentProps<T> & { DropdownMenuPrimitive.DropdownMenuSubContentProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const DropdownMenuSubContent = <T extends ValidComponent = "div">( const DropdownMenuSubContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DropdownMenuSubContentProps<T>> props: PolymorphicProps<T, DropdownMenuSubContentProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuSubContentProps, ["class"]) const [, rest] = splitProps(props as DropdownMenuSubContentProps, ['class']);
return ( return (
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
class={cn( 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", 'z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-in overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
props.class props.class
)} )}
{...rest} {...rest}
/> />
) );
} };
type DropdownMenuCheckboxItemProps<T extends ValidComponent = "div"> = type DropdownMenuCheckboxItemProps<T extends ValidComponent = 'div'> =
DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & { DropdownMenuPrimitive.DropdownMenuCheckboxItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">( const DropdownMenuCheckboxItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>> props: PolymorphicProps<T, DropdownMenuCheckboxItemProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuCheckboxItemProps, ["class", "children"]) const [, rest] = splitProps(props as DropdownMenuCheckboxItemProps, [
'class',
'children',
]);
return ( return (
<DropdownMenuPrimitive.CheckboxItem <DropdownMenuPrimitive.CheckboxItem
class={cn( 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", 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class props.class
)} )}
{...rest} {...rest}
@@ -181,40 +200,43 @@ const DropdownMenuCheckboxItem = <T extends ValidComponent = "div">(
</span> </span>
{props.children} {props.children}
</DropdownMenuPrimitive.CheckboxItem> </DropdownMenuPrimitive.CheckboxItem>
) );
} };
type DropdownMenuGroupLabelProps<T extends ValidComponent = "span"> = type DropdownMenuGroupLabelProps<T extends ValidComponent = 'span'> =
DropdownMenuPrimitive.DropdownMenuGroupLabelProps<T> & { DropdownMenuPrimitive.DropdownMenuGroupLabelProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const DropdownMenuGroupLabel = <T extends ValidComponent = "span">( const DropdownMenuGroupLabel = <T extends ValidComponent = 'span'>(
props: PolymorphicProps<T, DropdownMenuGroupLabelProps<T>> props: PolymorphicProps<T, DropdownMenuGroupLabelProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuGroupLabelProps, ["class"]) const [, rest] = splitProps(props as DropdownMenuGroupLabelProps, ['class']);
return ( return (
<DropdownMenuPrimitive.GroupLabel <DropdownMenuPrimitive.GroupLabel
class={cn("px-2 py-1.5 text-sm font-semibold", props.class)} class={cn('px-2 py-1.5 font-semibold text-sm', props.class)}
{...rest} {...rest}
/> />
) );
} };
type DropdownMenuRadioItemProps<T extends ValidComponent = "div"> = type DropdownMenuRadioItemProps<T extends ValidComponent = 'div'> =
DropdownMenuPrimitive.DropdownMenuRadioItemProps<T> & { DropdownMenuPrimitive.DropdownMenuRadioItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const DropdownMenuRadioItem = <T extends ValidComponent = "div">( const DropdownMenuRadioItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DropdownMenuRadioItemProps<T>> props: PolymorphicProps<T, DropdownMenuRadioItemProps<T>>
) => { ) => {
const [, rest] = splitProps(props as DropdownMenuRadioItemProps, ["class", "children"]) const [, rest] = splitProps(props as DropdownMenuRadioItemProps, [
'class',
'children',
]);
return ( return (
<DropdownMenuPrimitive.RadioItem <DropdownMenuPrimitive.RadioItem
class={cn( 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", 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
props.class props.class
)} )}
{...rest} {...rest}
@@ -237,8 +259,8 @@ const DropdownMenuRadioItem = <T extends ValidComponent = "div">(
</span> </span>
{props.children} {props.children}
</DropdownMenuPrimitive.RadioItem> </DropdownMenuPrimitive.RadioItem>
) );
} };
export { export {
DropdownMenu, DropdownMenu,
@@ -256,5 +278,5 @@ export {
DropdownMenuGroup, DropdownMenuGroup,
DropdownMenuGroupLabel, DropdownMenuGroupLabel,
DropdownMenuRadioGroup, DropdownMenuRadioGroup,
DropdownMenuRadioItem DropdownMenuRadioItem,
} };

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,97 +1,98 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as MenubarPrimitive from "@kobalte/core/menubar" import * as MenubarPrimitive from '@kobalte/core/menubar';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const MenubarGroup = MenubarPrimitive.Group const MenubarGroup = MenubarPrimitive.Group;
const MenubarPortal = MenubarPrimitive.Portal const MenubarPortal = MenubarPrimitive.Portal;
const MenubarSub = MenubarPrimitive.Sub const MenubarSub = MenubarPrimitive.Sub;
const MenubarRadioGroup = MenubarPrimitive.RadioGroup const MenubarRadioGroup = MenubarPrimitive.RadioGroup;
type MenubarRootProps<T extends ValidComponent = "div"> = MenubarPrimitive.MenubarRootProps<T> & { type MenubarRootProps<T extends ValidComponent = 'div'> =
class?: string | undefined MenubarPrimitive.MenubarRootProps<T> & {
} class?: string | undefined;
};
const Menubar = <T extends ValidComponent = "div">( const Menubar = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarRootProps<T>> props: PolymorphicProps<T, MenubarRootProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarRootProps, ["class"]) const [local, others] = splitProps(props as MenubarRootProps, ['class']);
return ( return (
<MenubarPrimitive.Root <MenubarPrimitive.Root
class={cn( class={cn(
"flex h-10 items-center space-x-1 rounded-md border bg-background p-1", 'flex h-10 items-center space-x-1 rounded-md border bg-background p-1',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const MenubarMenu: Component<MenubarPrimitive.MenubarMenuProps> = (props) => { const MenubarMenu: Component<MenubarPrimitive.MenubarMenuProps> = (props) => {
return <MenubarPrimitive.Menu gutter={8} {...props} /> return <MenubarPrimitive.Menu gutter={8} {...props} />;
} };
type MenubarTriggerProps<T extends ValidComponent = "button"> = type MenubarTriggerProps<T extends ValidComponent = 'button'> =
MenubarPrimitive.MenubarTriggerProps<T> & { class?: string | undefined } MenubarPrimitive.MenubarTriggerProps<T> & { class?: string | undefined };
const MenubarTrigger = <T extends ValidComponent = "button">( const MenubarTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, MenubarTriggerProps<T>> props: PolymorphicProps<T, MenubarTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarTriggerProps, ["class"]) const [local, others] = splitProps(props as MenubarTriggerProps, ['class']);
return ( return (
<MenubarPrimitive.Trigger <MenubarPrimitive.Trigger
class={cn( 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", 'flex cursor-default select-none items-center rounded-sm px-3 py-1.5 font-medium text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type MenubarContentProps<T extends ValidComponent = "div"> = type MenubarContentProps<T extends ValidComponent = 'div'> =
MenubarPrimitive.MenubarContentProps<T> & { class?: string | undefined } MenubarPrimitive.MenubarContentProps<T> & { class?: string | undefined };
const MenubarContent = <T extends ValidComponent = "div">( const MenubarContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarContentProps<T>> props: PolymorphicProps<T, MenubarContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarContentProps, ["class"]) const [local, others] = splitProps(props as MenubarContentProps, ['class']);
return ( return (
<MenubarPrimitive.Portal> <MenubarPrimitive.Portal>
<MenubarPrimitive.Content <MenubarPrimitive.Content
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
</MenubarPrimitive.Portal> </MenubarPrimitive.Portal>
) );
} };
type MenubarSubTriggerProps<T extends ValidComponent = "div"> = type MenubarSubTriggerProps<T extends ValidComponent = 'div'> =
MenubarPrimitive.MenubarSubTriggerProps<T> & { MenubarPrimitive.MenubarSubTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
inset?: boolean inset?: boolean;
} };
const MenubarSubTrigger = <T extends ValidComponent = "div">( const MenubarSubTrigger = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarSubTriggerProps<T>> props: PolymorphicProps<T, MenubarSubTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarSubTriggerProps, [ const [local, others] = splitProps(props as MenubarSubTriggerProps, [
"class", 'class',
"children", 'children',
"inset" 'inset',
]) ]);
return ( return (
<MenubarPrimitive.SubTrigger <MenubarPrimitive.SubTrigger
class={cn( 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", '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.inset && 'pl-8',
local.class local.class
)} )}
{...others} {...others}
@@ -110,66 +111,75 @@ const MenubarSubTrigger = <T extends ValidComponent = "div">(
<path d="M9 6l6 6l-6 6" /> <path d="M9 6l6 6l-6 6" />
</svg> </svg>
</MenubarPrimitive.SubTrigger> </MenubarPrimitive.SubTrigger>
) );
} };
type MenubarSubContentProps<T extends ValidComponent = "div"> = type MenubarSubContentProps<T extends ValidComponent = 'div'> =
MenubarPrimitive.MenubarSubContentProps<T> & { MenubarPrimitive.MenubarSubContentProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const MenubarSubContent = <T extends ValidComponent = "div">( const MenubarSubContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarSubContentProps<T>> props: PolymorphicProps<T, MenubarSubContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarSubContentProps, ["class"]) const [local, others] = splitProps(props as MenubarSubContentProps, [
'class',
]);
return ( return (
<MenubarPrimitive.Portal> <MenubarPrimitive.Portal>
<MenubarPrimitive.SubContent <MenubarPrimitive.SubContent
class={cn( 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", 'z-50 min-w-32 origin-[var(--kb-menu-content-transform-origin)] animate-in overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md',
local.class local.class
)} )}
{...others} {...others}
/> />
</MenubarPrimitive.Portal> </MenubarPrimitive.Portal>
) );
} };
type MenubarItemProps<T extends ValidComponent = "div"> = MenubarPrimitive.MenubarItemProps<T> & { type MenubarItemProps<T extends ValidComponent = 'div'> =
class?: string | undefined MenubarPrimitive.MenubarItemProps<T> & {
inset?: boolean class?: string | undefined;
} inset?: boolean;
};
const MenubarItem = <T extends ValidComponent = "div">( const MenubarItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarItemProps<T>> props: PolymorphicProps<T, MenubarItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarItemProps, ["class", "inset"]) const [local, others] = splitProps(props as MenubarItemProps, [
'class',
'inset',
]);
return ( return (
<MenubarPrimitive.Item <MenubarPrimitive.Item
class={cn( 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", '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.inset && 'pl-8',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type MenubarCheckboxItemProps<T extends ValidComponent = "div"> = type MenubarCheckboxItemProps<T extends ValidComponent = 'div'> =
MenubarPrimitive.MenubarCheckboxItemProps<T> & { MenubarPrimitive.MenubarCheckboxItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const MenubarCheckboxItem = <T extends ValidComponent = "div">( const MenubarCheckboxItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarCheckboxItemProps<T>> props: PolymorphicProps<T, MenubarCheckboxItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarCheckboxItemProps, ["class", "children"]) const [local, others] = splitProps(props as MenubarCheckboxItemProps, [
'class',
'children',
]);
return ( return (
<MenubarPrimitive.CheckboxItem <MenubarPrimitive.CheckboxItem
class={cn( 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", 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
local.class local.class
)} )}
{...others} {...others}
@@ -192,23 +202,26 @@ const MenubarCheckboxItem = <T extends ValidComponent = "div">(
</span> </span>
{local.children} {local.children}
</MenubarPrimitive.CheckboxItem> </MenubarPrimitive.CheckboxItem>
) );
} };
type MenubarRadioItemProps<T extends ValidComponent = "div"> = type MenubarRadioItemProps<T extends ValidComponent = 'div'> =
MenubarPrimitive.MenubarRadioItemProps<T> & { MenubarPrimitive.MenubarRadioItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const MenubarRadioItem = <T extends ValidComponent = "div">( const MenubarRadioItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarRadioItemProps<T>> props: PolymorphicProps<T, MenubarRadioItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarRadioItemProps, ["class", "children"]) const [local, others] = splitProps(props as MenubarRadioItemProps, [
'class',
'children',
]);
return ( return (
<MenubarPrimitive.RadioItem <MenubarPrimitive.RadioItem
class={cn( 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", 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
local.class local.class
)} )}
{...others} {...others}
@@ -231,66 +244,86 @@ const MenubarRadioItem = <T extends ValidComponent = "div">(
</span> </span>
{local.children} {local.children}
</MenubarPrimitive.RadioItem> </MenubarPrimitive.RadioItem>
) );
} };
type MenubarItemLabelProps<T extends ValidComponent = "div"> = type MenubarItemLabelProps<T extends ValidComponent = 'div'> =
MenubarPrimitive.MenubarItemLabelProps<T> & { MenubarPrimitive.MenubarItemLabelProps<T> & {
class?: string | undefined class?: string | undefined;
inset?: boolean inset?: boolean;
} };
const MenubarItemLabel = <T extends ValidComponent = "div">( const MenubarItemLabel = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, MenubarItemLabelProps<T>> props: PolymorphicProps<T, MenubarItemLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarItemLabelProps, ["class", "inset"]) const [local, others] = splitProps(props as MenubarItemLabelProps, [
'class',
'inset',
]);
return ( return (
<MenubarPrimitive.ItemLabel <MenubarPrimitive.ItemLabel
class={cn("px-2 py-1.5 text-sm font-semibold", local.inset && "pl-8", local.class)} class={cn(
'px-2 py-1.5 font-semibold text-sm',
local.inset && 'pl-8',
local.class
)}
{...others} {...others}
/> />
) );
} };
type MenubarGroupLabelProps<T extends ValidComponent = "span"> = type MenubarGroupLabelProps<T extends ValidComponent = 'span'> =
MenubarPrimitive.MenubarGroupLabelProps<T> & { MenubarPrimitive.MenubarGroupLabelProps<T> & {
class?: string | undefined class?: string | undefined;
inset?: boolean inset?: boolean;
} };
const MenubarGroupLabel = <T extends ValidComponent = "span">( const MenubarGroupLabel = <T extends ValidComponent = 'span'>(
props: PolymorphicProps<T, MenubarGroupLabelProps<T>> props: PolymorphicProps<T, MenubarGroupLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarGroupLabelProps, ["class", "inset"]) const [local, others] = splitProps(props as MenubarGroupLabelProps, [
'class',
'inset',
]);
return ( return (
<MenubarPrimitive.GroupLabel <MenubarPrimitive.GroupLabel
class={cn("px-2 py-1.5 text-sm font-semibold", local.inset && "pl-8", local.class)} class={cn(
'px-2 py-1.5 font-semibold text-sm',
local.inset && 'pl-8',
local.class
)}
{...others} {...others}
/> />
) );
} };
type MenubarSeparatorProps<T extends ValidComponent = "hr"> = type MenubarSeparatorProps<T extends ValidComponent = 'hr'> =
MenubarPrimitive.MenubarSeparatorProps<T> & { class?: string | undefined } MenubarPrimitive.MenubarSeparatorProps<T> & { class?: string | undefined };
const MenubarSeparator = <T extends ValidComponent = "hr">( const MenubarSeparator = <T extends ValidComponent = 'hr'>(
props: PolymorphicProps<T, MenubarSeparatorProps<T>> props: PolymorphicProps<T, MenubarSeparatorProps<T>>
) => { ) => {
const [local, others] = splitProps(props as MenubarSeparatorProps, ["class"]) const [local, others] = splitProps(props as MenubarSeparatorProps, ['class']);
return ( return (
<MenubarPrimitive.Separator class={cn("-mx-1 my-1 h-px bg-muted", local.class)} {...others} /> <MenubarPrimitive.Separator
) class={cn('-mx-1 my-1 h-px bg-muted', local.class)}
}
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} {...others}
/> />
) );
} };
const MenubarShortcut: Component<ComponentProps<'span'>> = (props) => {
const [local, others] = splitProps(props, ['class']);
return (
<span
class={cn(
'ml-auto text-muted-foreground text-xs tracking-widest',
local.class
)}
{...others}
/>
);
};
export { export {
Menubar, Menubar,
@@ -309,5 +342,5 @@ export {
MenubarSubTrigger, MenubarSubTrigger,
MenubarGroup, MenubarGroup,
MenubarSub, MenubarSub,
MenubarShortcut MenubarShortcut,
} };

View File

@@ -1,28 +1,31 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core" import type { PolymorphicProps } from '@kobalte/core';
import * as NavigationMenuPrimitive from "@kobalte/core/navigation-menu" import * as NavigationMenuPrimitive from '@kobalte/core/navigation-menu';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const NavigationMenuItem = NavigationMenuPrimitive.Menu const NavigationMenuItem = NavigationMenuPrimitive.Menu;
type NavigationMenuProps<T extends ValidComponent = "ul"> = type NavigationMenuProps<T extends ValidComponent = 'ul'> =
NavigationMenuPrimitive.NavigationMenuRootProps<T> & { NavigationMenuPrimitive.NavigationMenuRootProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const NavigationMenu = <T extends ValidComponent = "ul">( const NavigationMenu = <T extends ValidComponent = 'ul'>(
props: PolymorphicProps<T, NavigationMenuProps<T>> props: PolymorphicProps<T, NavigationMenuProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuProps, ["class", "children"]) const [local, others] = splitProps(props as NavigationMenuProps, [
'class',
'children',
]);
return ( return (
<NavigationMenuPrimitive.Root <NavigationMenuPrimitive.Root
gutter={6} gutter={6}
class={cn( class={cn(
"group/menu flex w-max flex-1 list-none items-center justify-center data-[orientation=vertical]:flex-col [&>li]:w-full", 'group/menu flex w-max flex-1 list-none items-center justify-center data-[orientation=vertical]:flex-col [&>li]:w-full',
local.class local.class
)} )}
{...others} {...others}
@@ -30,28 +33,30 @@ const NavigationMenu = <T extends ValidComponent = "ul">(
{local.children} {local.children}
<NavigationMenuViewport /> <NavigationMenuViewport />
</NavigationMenuPrimitive.Root> </NavigationMenuPrimitive.Root>
) );
} };
type NavigationMenuTriggerProps<T extends ValidComponent = "button"> = type NavigationMenuTriggerProps<T extends ValidComponent = 'button'> =
NavigationMenuPrimitive.NavigationMenuTriggerProps<T> & { NavigationMenuPrimitive.NavigationMenuTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const NavigationMenuTrigger = <T extends ValidComponent = "button">( const NavigationMenuTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, NavigationMenuTriggerProps<T>> props: PolymorphicProps<T, NavigationMenuTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuTriggerProps, ["class"]) const [local, others] = splitProps(props as NavigationMenuTriggerProps, [
'class',
]);
return ( return (
<NavigationMenuPrimitive.Trigger <NavigationMenuPrimitive.Trigger
class={cn( 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", 'group/trigger inline-flex h-10 w-full items-center justify-center whitespace-nowrap rounded-md bg-background px-4 py-2 font-medium text-sm 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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
const NavigationMenuIcon = () => { const NavigationMenuIcon = () => {
return ( return (
<NavigationMenuPrimitive.Icon aria-hidden="true"> <NavigationMenuPrimitive.Icon aria-hidden="true">
@@ -63,115 +68,133 @@ const NavigationMenuIcon = () => {
stroke-width="2" stroke-width="2"
stroke-linecap="round" stroke-linecap="round"
stroke-linejoin="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" class="group-data-[orientation=vertical]/menu:-rotate-90 relative top-px ml-1 size-3 transition duration-200 group-data-[orientation=vertical]/menu:group-data-[expanded]/trigger:rotate-90 group-data-[expanded]/trigger:rotate-180"
> >
<path d="M6 9l6 6l6 -6" /> <path d="M6 9l6 6l6 -6" />
</svg> </svg>
</NavigationMenuPrimitive.Icon> </NavigationMenuPrimitive.Icon>
) );
} };
type NavigationMenuViewportProps<T extends ValidComponent = "li"> = type NavigationMenuViewportProps<T extends ValidComponent = 'li'> =
NavigationMenuPrimitive.NavigationMenuViewportProps<T> & { class?: string | undefined } NavigationMenuPrimitive.NavigationMenuViewportProps<T> & {
class?: string | undefined;
};
const NavigationMenuViewport = <T extends ValidComponent = "li">( const NavigationMenuViewport = <T extends ValidComponent = 'li'>(
props: PolymorphicProps<T, NavigationMenuViewportProps<T>> props: PolymorphicProps<T, NavigationMenuViewportProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuViewportProps, ["class"]) const [local, others] = splitProps(props as NavigationMenuViewportProps, [
'class',
]);
return ( return (
<NavigationMenuPrimitive.Viewport <NavigationMenuPrimitive.Viewport
class={cn( class={cn(
// base settings // 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", '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
"animate-content-hide transition-[width,height] duration-200 ease-in data-[expanded]:animate-content-show data-[expanded]:opacity-100 data-[expanded]:ease-out", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type NavigationMenuContentProps<T extends ValidComponent = "ul"> = type NavigationMenuContentProps<T extends ValidComponent = 'ul'> =
NavigationMenuPrimitive.NavigationMenuContentProps<T> & { NavigationMenuPrimitive.NavigationMenuContentProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const NavigationMenuContent = <T extends ValidComponent = "ul">( const NavigationMenuContent = <T extends ValidComponent = 'ul'>(
props: PolymorphicProps<T, NavigationMenuContentProps<T>> props: PolymorphicProps<T, NavigationMenuContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuContentProps, ["class"]) const [local, others] = splitProps(props as NavigationMenuContentProps, [
'class',
]);
return ( return (
<NavigationMenuPrimitive.Portal> <NavigationMenuPrimitive.Portal>
<NavigationMenuPrimitive.Content <NavigationMenuPrimitive.Content
class={cn( class={cn(
// base settings // base settings
"pointer-events-none absolute left-0 top-0 box-border p-4 focus:outline-none data-[expanded]:pointer-events-auto", 'pointer-events-none absolute top-0 left-0 box-border p-4 focus:outline-none data-[expanded]:pointer-events-auto',
// base animation settings // base animation settings
"data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out", 'data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out',
// left to right // 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", '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 // 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", '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 // 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", '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 //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", '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 local.class
)} )}
{...others} {...others}
/> />
</NavigationMenuPrimitive.Portal> </NavigationMenuPrimitive.Portal>
) );
} };
type NavigationMenuLinkProps<T extends ValidComponent = "a"> = type NavigationMenuLinkProps<T extends ValidComponent = 'a'> =
NavigationMenuPrimitive.NavigationMenuItemProps<T> & { class?: string | undefined } NavigationMenuPrimitive.NavigationMenuItemProps<T> & {
class?: string | undefined;
};
const NavigationMenuLink = <T extends ValidComponent = "a">( const NavigationMenuLink = <T extends ValidComponent = 'a'>(
props: PolymorphicProps<T, NavigationMenuLinkProps<T>> props: PolymorphicProps<T, NavigationMenuLinkProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuLinkProps, ["class"]) const [local, others] = splitProps(props as NavigationMenuLinkProps, [
'class',
]);
return ( return (
<NavigationMenuPrimitive.Item <NavigationMenuPrimitive.Item
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type NavigationMenuLabelProps<T extends ValidComponent = "div"> = type NavigationMenuLabelProps<T extends ValidComponent = 'div'> =
NavigationMenuPrimitive.NavigationMenuItemLabelProps<T> & { class?: string | undefined } NavigationMenuPrimitive.NavigationMenuItemLabelProps<T> & {
class?: string | undefined;
};
const NavigationMenuLabel = <T extends ValidComponent = "div">( const NavigationMenuLabel = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, NavigationMenuLabelProps<T>> props: PolymorphicProps<T, NavigationMenuLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuLabelProps, ["class"]) const [local, others] = splitProps(props as NavigationMenuLabelProps, [
'class',
]);
return ( return (
<NavigationMenuPrimitive.ItemLabel <NavigationMenuPrimitive.ItemLabel
class={cn("text-sm font-medium leading-none", local.class)} class={cn('font-medium text-sm leading-none', local.class)}
{...others} {...others}
/> />
) );
} };
type NavigationMenuDescriptionProps<T extends ValidComponent = "div"> = type NavigationMenuDescriptionProps<T extends ValidComponent = 'div'> =
NavigationMenuPrimitive.NavigationMenuItemDescriptionProps<T> & { class?: string | undefined } NavigationMenuPrimitive.NavigationMenuItemDescriptionProps<T> & {
class?: string | undefined;
};
const NavigationMenuDescription = <T extends ValidComponent = "div">( const NavigationMenuDescription = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, NavigationMenuDescriptionProps<T>> props: PolymorphicProps<T, NavigationMenuDescriptionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NavigationMenuDescriptionProps, ["class"]) const [local, others] = splitProps(props as NavigationMenuDescriptionProps, [
'class',
]);
return ( return (
<NavigationMenuPrimitive.ItemDescription <NavigationMenuPrimitive.ItemDescription
class={cn("text-sm leading-snug text-muted-foreground", local.class)} class={cn('text-muted-foreground text-sm leading-snug', local.class)}
{...others} {...others}
/> />
) );
} };
export { export {
NavigationMenu, NavigationMenu,
@@ -182,5 +205,5 @@ export {
NavigationMenuContent, NavigationMenuContent,
NavigationMenuLink, NavigationMenuLink,
NavigationMenuLabel, NavigationMenuLabel,
NavigationMenuDescription NavigationMenuDescription,
} };

View File

@@ -1,83 +1,83 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { Show, splitProps } from "solid-js" import { Show, splitProps } from 'solid-js';
import * as NumberFieldPrimitive from "@kobalte/core/number-field" import * as NumberFieldPrimitive from '@kobalte/core/number-field';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const NumberField = NumberFieldPrimitive.Root const NumberField = NumberFieldPrimitive.Root;
const NumberFieldGroup: Component<ComponentProps<"div">> = (props) => { const NumberFieldGroup: Component<ComponentProps<'div'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return ( return (
<div <div
class={cn( class={cn(
"relative rounded-md focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2", 'relative rounded-md focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type NumberFieldLabelProps<T extends ValidComponent = "label"> = type NumberFieldLabelProps<T extends ValidComponent = 'label'> =
NumberFieldPrimitive.NumberFieldLabelProps<T> & { NumberFieldPrimitive.NumberFieldLabelProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const NumberFieldLabel = <T extends ValidComponent = "label">( const NumberFieldLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, NumberFieldLabelProps<T>> props: PolymorphicProps<T, NumberFieldLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NumberFieldLabelProps, ["class"]) const [local, others] = splitProps(props as NumberFieldLabelProps, ['class']);
return ( return (
<NumberFieldPrimitive.Label <NumberFieldPrimitive.Label
class={cn( class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 'font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type NumberFieldInputProps<T extends ValidComponent = "input"> = type NumberFieldInputProps<T extends ValidComponent = 'input'> =
NumberFieldPrimitive.NumberFieldInputProps<T> & { NumberFieldPrimitive.NumberFieldInputProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const NumberFieldInput = <T extends ValidComponent = "input">( const NumberFieldInput = <T extends ValidComponent = 'input'>(
props: PolymorphicProps<T, NumberFieldInputProps<T>> props: PolymorphicProps<T, NumberFieldInputProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NumberFieldInputProps, ["class"]) const [local, others] = splitProps(props as NumberFieldInputProps, ['class']);
return ( return (
<NumberFieldPrimitive.Input <NumberFieldPrimitive.Input
class={cn( 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", '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:font-medium file:text-sm 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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type NumberFieldIncrementTriggerProps<T extends ValidComponent = "button"> = type NumberFieldIncrementTriggerProps<T extends ValidComponent = 'button'> =
NumberFieldPrimitive.NumberFieldIncrementTriggerProps<T> & { NumberFieldPrimitive.NumberFieldIncrementTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const NumberFieldIncrementTrigger = <T extends ValidComponent = "button">( const NumberFieldIncrementTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, NumberFieldIncrementTriggerProps<T>> props: PolymorphicProps<T, NumberFieldIncrementTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NumberFieldIncrementTriggerProps, [ const [local, others] = splitProps(
"class", props as NumberFieldIncrementTriggerProps,
"children" ['class', 'children']
]) );
return ( return (
<NumberFieldPrimitive.IncrementTrigger <NumberFieldPrimitive.IncrementTrigger
class={cn( class={cn(
"absolute right-1 top-1 inline-flex size-4 items-center justify-center", 'absolute top-1 right-1 inline-flex size-4 items-center justify-center',
local.class local.class
)} )}
{...others} {...others}
@@ -102,26 +102,26 @@ const NumberFieldIncrementTrigger = <T extends ValidComponent = "button">(
{(children) => children()} {(children) => children()}
</Show> </Show>
</NumberFieldPrimitive.IncrementTrigger> </NumberFieldPrimitive.IncrementTrigger>
) );
} };
type NumberFieldDecrementTriggerProps<T extends ValidComponent = "button"> = type NumberFieldDecrementTriggerProps<T extends ValidComponent = 'button'> =
NumberFieldPrimitive.NumberFieldDecrementTriggerProps<T> & { NumberFieldPrimitive.NumberFieldDecrementTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const NumberFieldDecrementTrigger = <T extends ValidComponent = "button">( const NumberFieldDecrementTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, NumberFieldDecrementTriggerProps<T>> props: PolymorphicProps<T, NumberFieldDecrementTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NumberFieldDecrementTriggerProps, [ const [local, others] = splitProps(
"class", props as NumberFieldDecrementTriggerProps,
"children" ['class', 'children']
]) );
return ( return (
<NumberFieldPrimitive.DecrementTrigger <NumberFieldPrimitive.DecrementTrigger
class={cn( class={cn(
"absolute bottom-1 right-1 inline-flex size-4 items-center justify-center", 'absolute right-1 bottom-1 inline-flex size-4 items-center justify-center',
local.class local.class
)} )}
{...others} {...others}
@@ -146,42 +146,46 @@ const NumberFieldDecrementTrigger = <T extends ValidComponent = "button">(
{(children) => children()} {(children) => children()}
</Show> </Show>
</NumberFieldPrimitive.DecrementTrigger> </NumberFieldPrimitive.DecrementTrigger>
) );
} };
type NumberFieldDescriptionProps<T extends ValidComponent = "div"> = type NumberFieldDescriptionProps<T extends ValidComponent = 'div'> =
NumberFieldPrimitive.NumberFieldDescriptionProps<T> & { NumberFieldPrimitive.NumberFieldDescriptionProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const NumberFieldDescription = <T extends ValidComponent = "div">( const NumberFieldDescription = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, NumberFieldDescriptionProps<T>> props: PolymorphicProps<T, NumberFieldDescriptionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NumberFieldDescriptionProps, ["class"]) const [local, others] = splitProps(props as NumberFieldDescriptionProps, [
'class',
]);
return ( return (
<NumberFieldPrimitive.Description <NumberFieldPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)} class={cn('text-muted-foreground text-sm', local.class)}
{...others} {...others}
/> />
) );
} };
type NumberFieldErrorMessageProps<T extends ValidComponent = "div"> = type NumberFieldErrorMessageProps<T extends ValidComponent = 'div'> =
NumberFieldPrimitive.NumberFieldErrorMessageProps<T> & { NumberFieldPrimitive.NumberFieldErrorMessageProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const NumberFieldErrorMessage = <T extends ValidComponent = "div">( const NumberFieldErrorMessage = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, NumberFieldErrorMessageProps<T>> props: PolymorphicProps<T, NumberFieldErrorMessageProps<T>>
) => { ) => {
const [local, others] = splitProps(props as NumberFieldErrorMessageProps, ["class"]) const [local, others] = splitProps(props as NumberFieldErrorMessageProps, [
'class',
]);
return ( return (
<NumberFieldPrimitive.ErrorMessage <NumberFieldPrimitive.ErrorMessage
class={cn("text-sm text-error-foreground", local.class)} class={cn('text-error-foreground text-sm', local.class)}
{...others} {...others}
/> />
) );
} };
export { export {
NumberField, NumberField,
@@ -191,5 +195,5 @@ export {
NumberFieldIncrementTrigger, NumberFieldIncrementTrigger,
NumberFieldDecrementTrigger, NumberFieldDecrementTrigger,
NumberFieldDescription, NumberFieldDescription,
NumberFieldErrorMessage NumberFieldErrorMessage,
} };

View File

@@ -1,55 +1,63 @@
import type { Component, ComponentProps, ValidComponent } from "solid-js" import type { Component, ComponentProps, ValidComponent } from 'solid-js';
import { Show, splitProps } from "solid-js" import { Show, splitProps } from 'solid-js';
import type { DynamicProps, RootProps } from "@corvu/otp-field" import type { DynamicProps, RootProps } from '@corvu/otp-field';
import OtpField from "@corvu/otp-field" import OtpField from '@corvu/otp-field';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
export const REGEXP_ONLY_DIGITS = "^\\d*$" export const REGEXP_ONLY_DIGITS = '^\\d*$';
export const REGEXP_ONLY_CHARS = "^[a-zA-Z]*$" export const REGEXP_ONLY_CHARS = '^[a-zA-Z]*$';
export const REGEXP_ONLY_DIGITS_AND_CHARS = "^[a-zA-Z0-9]*$" export const REGEXP_ONLY_DIGITS_AND_CHARS = '^[a-zA-Z0-9]*$';
type OTPFieldProps<T extends ValidComponent = "div"> = RootProps<T> & { class?: string } type OTPFieldProps<T extends ValidComponent = 'div'> = RootProps<T> & {
class?: string;
};
const OTPField = <T extends ValidComponent = "div">(props: DynamicProps<T, OTPFieldProps<T>>) => { const OTPField = <T extends ValidComponent = 'div'>(
const [local, others] = splitProps(props as OTPFieldProps, ["class"]) props: DynamicProps<T, OTPFieldProps<T>>
) => {
const [local, others] = splitProps(props as OTPFieldProps, ['class']);
return ( return (
<OtpField <OtpField
class={cn( class={cn(
"flex items-center gap-2 disabled:cursor-not-allowed has-[:disabled]:opacity-50", 'flex items-center gap-2 disabled:cursor-not-allowed has-[:disabled]:opacity-50',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const OTPFieldInput = OtpField.Input const OTPFieldInput = OtpField.Input;
const OTPFieldGroup: Component<ComponentProps<"div">> = (props) => { const OTPFieldGroup: Component<ComponentProps<'div'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) const [local, others] = splitProps(props, ['class']);
return <div class={cn("flex items-center", local.class)} {...others} /> return <div class={cn('flex items-center', local.class)} {...others} />;
} };
const OTPFieldSlot: Component<ComponentProps<"div"> & { index: number }> = (props) => { const OTPFieldSlot: Component<ComponentProps<'div'> & { index: number }> = (
const [local, others] = splitProps(props, ["class", "index"]) props
const context = OtpField.useContext() ) => {
const char = () => context.value()[local.index] const [local, others] = splitProps(props, ['class', 'index']);
const showFakeCaret = () => context.value().length === local.index && context.isInserting() const context = OtpField.useContext();
const char = () => context.value()[local.index];
const showFakeCaret = () =>
context.value().length === local.index && context.isInserting();
return ( return (
<div <div
class={cn( 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", 'group relative flex size-10 items-center justify-center border-input border-y border-r text-sm first:rounded-l-md first:border-l last:rounded-r-md',
local.class local.class
)} )}
{...others} {...others}
> >
<div <div
class={cn( class={cn(
"absolute inset-0 z-10 transition-all group-first:rounded-l-md group-last:rounded-r-md", '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" context.activeSlots().includes(local.index) &&
'ring-2 ring-ring ring-offset-background'
)} )}
/> />
{char()} {char()}
@@ -59,10 +67,10 @@ const OTPFieldSlot: Component<ComponentProps<"div"> & { index: number }> = (prop
</div> </div>
</Show> </Show>
</div> </div>
) );
} };
const OTPFieldSeparator: Component<ComponentProps<"div">> = (props) => { const OTPFieldSeparator: Component<ComponentProps<'div'>> = (props) => {
return ( return (
<div {...props}> <div {...props}>
<svg <svg
@@ -78,7 +86,13 @@ const OTPFieldSeparator: Component<ComponentProps<"div">> = (props) => {
<circle cx="12.1" cy="12.1" r="1" /> <circle cx="12.1" cy="12.1" r="1" />
</svg> </svg>
</div> </div>
) );
} };
export { OTPField, OTPFieldInput, OTPFieldGroup, OTPFieldSlot, OTPFieldSeparator } export {
OTPField,
OTPFieldInput,
OTPFieldGroup,
OTPFieldSlot,
OTPFieldSeparator,
};

View File

@@ -1,62 +1,67 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { Show, splitProps } from "solid-js" import { Show, splitProps } from 'solid-js';
import * as PaginationPrimitive from "@kobalte/core/pagination" import * as PaginationPrimitive from '@kobalte/core/pagination';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cn } from "~/styles/utils" import { buttonVariants } from '~/components/ui/button';
import { buttonVariants } from "~/components/ui/button" import { cn } from '~/utils/styles';
const PaginationItems = PaginationPrimitive.Items const PaginationItems = PaginationPrimitive.Items;
type PaginationRootProps<T extends ValidComponent = "nav"> = type PaginationRootProps<T extends ValidComponent = 'nav'> =
PaginationPrimitive.PaginationRootProps<T> & { class?: string | undefined } PaginationPrimitive.PaginationRootProps<T> & { class?: string | undefined };
const Pagination = <T extends ValidComponent = "nav">( const Pagination = <T extends ValidComponent = 'nav'>(
props: PolymorphicProps<T, PaginationRootProps<T>> props: PolymorphicProps<T, PaginationRootProps<T>>
) => { ) => {
const [local, others] = splitProps(props as PaginationRootProps, ["class"]) const [local, others] = splitProps(props as PaginationRootProps, ['class']);
return ( return (
<PaginationPrimitive.Root <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( class={cn(
buttonVariants({ '[&>*]:flex [&>*]:flex-row [&>*]:items-center [&>*]:gap-1',
variant: "ghost"
}),
"size-10 data-[current]:border",
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
type PaginationEllipsisProps<T extends ValidComponent = "div"> = 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> & { PaginationPrimitive.PaginationEllipsisProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const PaginationEllipsis = <T extends ValidComponent = "div">( const PaginationEllipsis = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, PaginationEllipsisProps<T>> props: PolymorphicProps<T, PaginationEllipsisProps<T>>
) => { ) => {
const [local, others] = splitProps(props as PaginationEllipsisProps, ["class"]) const [local, others] = splitProps(props as PaginationEllipsisProps, [
'class',
]);
return ( return (
<PaginationPrimitive.Ellipsis <PaginationPrimitive.Ellipsis
class={cn("flex size-10 items-center justify-center", local.class)} class={cn('flex size-10 items-center justify-center', local.class)}
{...others} {...others}
> >
<svg <svg
@@ -75,26 +80,29 @@ const PaginationEllipsis = <T extends ValidComponent = "div">(
</svg> </svg>
<span class="sr-only">More pages</span> <span class="sr-only">More pages</span>
</PaginationPrimitive.Ellipsis> </PaginationPrimitive.Ellipsis>
) );
} };
type PaginationPreviousProps<T extends ValidComponent = "button"> = type PaginationPreviousProps<T extends ValidComponent = 'button'> =
PaginationPrimitive.PaginationPreviousProps<T> & { PaginationPrimitive.PaginationPreviousProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const PaginationPrevious = <T extends ValidComponent = "button">( const PaginationPrevious = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, PaginationPreviousProps<T>> props: PolymorphicProps<T, PaginationPreviousProps<T>>
) => { ) => {
const [local, others] = splitProps(props as PaginationPreviousProps, ["class", "children"]) const [local, others] = splitProps(props as PaginationPreviousProps, [
'class',
'children',
]);
return ( return (
<PaginationPrimitive.Previous <PaginationPrimitive.Previous
class={cn( class={cn(
buttonVariants({ buttonVariants({
variant: "ghost" variant: 'ghost',
}), }),
"gap-1 pl-2.5", 'gap-1 pl-2.5',
local.class local.class
)} )}
{...others} {...others}
@@ -122,26 +130,29 @@ const PaginationPrevious = <T extends ValidComponent = "button">(
{(children) => children()} {(children) => children()}
</Show> </Show>
</PaginationPrimitive.Previous> </PaginationPrimitive.Previous>
) );
} };
type PaginationNextProps<T extends ValidComponent = "button"> = type PaginationNextProps<T extends ValidComponent = 'button'> =
PaginationPrimitive.PaginationNextProps<T> & { PaginationPrimitive.PaginationNextProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const PaginationNext = <T extends ValidComponent = "button">( const PaginationNext = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, PaginationNextProps<T>> props: PolymorphicProps<T, PaginationNextProps<T>>
) => { ) => {
const [local, others] = splitProps(props as PaginationNextProps, ["class", "children"]) const [local, others] = splitProps(props as PaginationNextProps, [
'class',
'children',
]);
return ( return (
<PaginationPrimitive.Next <PaginationPrimitive.Next
class={cn( class={cn(
buttonVariants({ buttonVariants({
variant: "ghost" variant: 'ghost',
}), }),
"gap-1 pl-2.5", 'gap-1 pl-2.5',
local.class local.class
)} )}
{...others} {...others}
@@ -169,8 +180,8 @@ const PaginationNext = <T extends ValidComponent = "button">(
{(children) => children()} {(children) => children()}
</Show> </Show>
</PaginationPrimitive.Next> </PaginationPrimitive.Next>
) );
} };
export { export {
Pagination, Pagination,
@@ -178,5 +189,5 @@ export {
PaginationItem, PaginationItem,
PaginationEllipsis, PaginationEllipsis,
PaginationPrevious, PaginationPrevious,
PaginationNext PaginationNext,
} };

View File

@@ -1,35 +1,35 @@
import type { Component, ValidComponent } from "solid-js" import type { Component, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import * as PopoverPrimitive from "@kobalte/core/popover" import * as PopoverPrimitive from '@kobalte/core/popover';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const PopoverTrigger = PopoverPrimitive.Trigger const PopoverTrigger = PopoverPrimitive.Trigger;
const Popover: Component<PopoverPrimitive.PopoverRootProps> = (props) => { const Popover: Component<PopoverPrimitive.PopoverRootProps> = (props) => {
return <PopoverPrimitive.Root gutter={4} {...props} /> return <PopoverPrimitive.Root gutter={4} {...props} />;
} };
type PopoverContentProps<T extends ValidComponent = "div"> = type PopoverContentProps<T extends ValidComponent = 'div'> =
PopoverPrimitive.PopoverContentProps<T> & { class?: string | undefined } PopoverPrimitive.PopoverContentProps<T> & { class?: string | undefined };
const PopoverContent = <T extends ValidComponent = "div">( const PopoverContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, PopoverContentProps<T>> props: PolymorphicProps<T, PopoverContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as PopoverContentProps, ["class"]) const [local, others] = splitProps(props as PopoverContentProps, ['class']);
return ( return (
<PopoverPrimitive.Portal> <PopoverPrimitive.Portal>
<PopoverPrimitive.Content <PopoverPrimitive.Content
class={cn( 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", 'data-[closed]:fade-out-0 data-[expanded]:fade-in-0 data-[closed]:zoom-out-95 data-[expanded]:zoom-in-95 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-[closed]:animate-out data-[expanded]:animate-in',
local.class local.class
)} )}
{...others} {...others}
/> />
</PopoverPrimitive.Portal> </PopoverPrimitive.Portal>
) );
} };
export { Popover, PopoverTrigger, PopoverContent } export { Popover, PopoverTrigger, PopoverContent };

View File

@@ -1,5 +1,11 @@
import { type LinkComponent, createLink } from '@tanstack/solid-router'; import { type LinkComponent, createLink } from '@tanstack/solid-router';
import { type Component, type ComponentProps, type JSX, Show } from 'solid-js'; import {
type Component,
type ComponentProps,
type JSX,
Show,
splitProps,
} from 'solid-js';
type BasicLinkProps = JSX.IntrinsicElements['a']; type BasicLinkProps = JSX.IntrinsicElements['a'];
@@ -10,12 +16,13 @@ const BasicLinkComponent: Component<BasicLinkProps> = (props) => (
const CreatedLinkComponent = createLink(BasicLinkComponent); const CreatedLinkComponent = createLink(BasicLinkComponent);
export const ProLink: LinkComponent<typeof BasicLinkComponent> = (props) => { export const ProLink: LinkComponent<typeof BasicLinkComponent> = (props) => {
const [local, other] = splitProps(props, ['href']);
return ( return (
<Show <Show
when={props.href} when={!props.href}
fallback={<CreatedLinkComponent preload={'intent'} {...props} />} fallback={<BasicLinkComponent {...(other as any)} href={local.href} />}
> >
<BasicLinkComponent {...(props as any)} /> <CreatedLinkComponent preload={'intent'} {...(other as typeof props)} />
</Show> </Show>
); );
}; };

View File

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

View File

@@ -1,33 +1,44 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import * as RadioGroupPrimitive from "@kobalte/core/radio-group" import * as RadioGroupPrimitive from '@kobalte/core/radio-group';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
type RadioGroupRootProps<T extends ValidComponent = "div"> = type RadioGroupRootProps<T extends ValidComponent = 'div'> =
RadioGroupPrimitive.RadioGroupRootProps<T> & { class?: string | undefined } RadioGroupPrimitive.RadioGroupRootProps<T> & { class?: string | undefined };
const RadioGroup = <T extends ValidComponent = "div">( const RadioGroup = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, RadioGroupRootProps<T>> props: PolymorphicProps<T, RadioGroupRootProps<T>>
) => { ) => {
const [local, others] = splitProps(props as RadioGroupRootProps, ["class"]) const [local, others] = splitProps(props as RadioGroupRootProps, ['class']);
return <RadioGroupPrimitive.Root class={cn("grid gap-2", local.class)} {...others} /> return (
} <RadioGroupPrimitive.Root
class={cn('grid gap-2', local.class)}
{...others}
/>
);
};
type RadioGroupItemProps<T extends ValidComponent = "div"> = type RadioGroupItemProps<T extends ValidComponent = 'div'> =
RadioGroupPrimitive.RadioGroupItemProps<T> & { RadioGroupPrimitive.RadioGroupItemProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const RadioGroupItem = <T extends ValidComponent = "div">( const RadioGroupItem = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, RadioGroupItemProps<T>> props: PolymorphicProps<T, RadioGroupItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as RadioGroupItemProps, ["class", "children"]) const [local, others] = splitProps(props as RadioGroupItemProps, [
'class',
'children',
]);
return ( return (
<RadioGroupPrimitive.Item class={cn("flex items-center space-x-2", local.class)} {...others}> <RadioGroupPrimitive.Item
class={cn('flex items-center space-x-2', local.class)}
{...others}
>
<RadioGroupPrimitive.ItemInput /> <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.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 "> <RadioGroupPrimitive.ItemIndicator class="flex h-full items-center justify-center ">
@@ -47,27 +58,27 @@ const RadioGroupItem = <T extends ValidComponent = "div">(
</RadioGroupPrimitive.ItemControl> </RadioGroupPrimitive.ItemControl>
{local.children} {local.children}
</RadioGroupPrimitive.Item> </RadioGroupPrimitive.Item>
) );
} };
type RadioGroupLabelProps<T extends ValidComponent = "label"> = type RadioGroupLabelProps<T extends ValidComponent = 'label'> =
RadioGroupPrimitive.RadioGroupLabelProps<T> & { RadioGroupPrimitive.RadioGroupLabelProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const RadioGroupItemLabel = <T extends ValidComponent = "label">( const RadioGroupItemLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, RadioGroupLabelProps<T>> props: PolymorphicProps<T, RadioGroupLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as RadioGroupLabelProps, ["class"]) const [local, others] = splitProps(props as RadioGroupLabelProps, ['class']);
return ( return (
<RadioGroupPrimitive.ItemLabel <RadioGroupPrimitive.ItemLabel
class={cn( class={cn(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 'font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
export { RadioGroup, RadioGroupItem, RadioGroupItemLabel } export { RadioGroup, RadioGroupItem, RadioGroupItemLabel };

View File

@@ -1,38 +1,49 @@
import type { ValidComponent } from "solid-js" import type { ValidComponent } from 'solid-js';
import { Show, splitProps } from "solid-js" import { Show, splitProps } from 'solid-js';
import type { DynamicProps, HandleProps, RootProps } from "@corvu/resizable" import type { DynamicProps, HandleProps, RootProps } from '@corvu/resizable';
import ResizablePrimitive from "@corvu/resizable" import ResizablePrimitive from '@corvu/resizable';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
type ResizableProps<T extends ValidComponent = "div"> = RootProps<T> & { class?: string } type ResizableProps<T extends ValidComponent = 'div'> = RootProps<T> & {
class?: string;
};
const Resizable = <T extends ValidComponent = "div">(props: DynamicProps<T, ResizableProps<T>>) => { const Resizable = <T extends ValidComponent = 'div'>(
const [, rest] = splitProps(props as ResizableProps, ["class"]) props: DynamicProps<T, ResizableProps<T>>
) => {
const [, rest] = splitProps(props as ResizableProps, ['class']);
return ( return (
<ResizablePrimitive <ResizablePrimitive
class={cn("flex size-full data-[orientation=vertical]:flex-col", props.class)} class={cn(
'flex size-full data-[orientation=vertical]:flex-col',
props.class
)}
{...rest} {...rest}
/> />
) );
} };
const ResizablePanel = ResizablePrimitive.Panel const ResizablePanel = ResizablePrimitive.Panel;
type ResizableHandleProps<T extends ValidComponent = "button"> = HandleProps<T> & { type ResizableHandleProps<T extends ValidComponent = 'button'> =
class?: string HandleProps<T> & {
withHandle?: boolean class?: string;
} withHandle?: boolean;
};
const ResizableHandle = <T extends ValidComponent = "button">( const ResizableHandle = <T extends ValidComponent = 'button'>(
props: DynamicProps<T, ResizableHandleProps<T>> props: DynamicProps<T, ResizableHandleProps<T>>
) => { ) => {
const [, rest] = splitProps(props as ResizableHandleProps, ["class", "withHandle"]) const [, rest] = splitProps(props as ResizableHandleProps, [
'class',
'withHandle',
]);
return ( return (
<ResizablePrimitive.Handle <ResizablePrimitive.Handle
class={cn( 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", 'after:-translate-x-1/2 data-[orientation=vertical]:after:-translate-y-1/2 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 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-x-0 [&[data-orientation=vertical]>div]:rotate-90',
props.class props.class
)} )}
{...rest} {...rest}
@@ -59,7 +70,7 @@ const ResizableHandle = <T extends ValidComponent = "button">(
</div> </div>
</Show> </Show>
</ResizablePrimitive.Handle> </ResizablePrimitive.Handle>
) );
} };
export { Resizable, ResizablePanel, ResizableHandle } export { Resizable, ResizablePanel, ResizableHandle };

View File

@@ -1,30 +1,33 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import * as SelectPrimitive from "@kobalte/core/select" import * as SelectPrimitive from '@kobalte/core/select';
import { cva } from "class-variance-authority" import { cva } from 'class-variance-authority';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Select = SelectPrimitive.Root const Select = SelectPrimitive.Root;
const SelectValue = SelectPrimitive.Value const SelectValue = SelectPrimitive.Value;
const SelectHiddenSelect = SelectPrimitive.HiddenSelect const SelectHiddenSelect = SelectPrimitive.HiddenSelect;
type SelectTriggerProps<T extends ValidComponent = "button"> = type SelectTriggerProps<T extends ValidComponent = 'button'> =
SelectPrimitive.SelectTriggerProps<T> & { SelectPrimitive.SelectTriggerProps<T> & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const SelectTrigger = <T extends ValidComponent = "button">( const SelectTrigger = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, SelectTriggerProps<T>> props: PolymorphicProps<T, SelectTriggerProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SelectTriggerProps, ["class", "children"]) const [local, others] = splitProps(props as SelectTriggerProps, [
'class',
'children',
]);
return ( return (
<SelectPrimitive.Trigger <SelectPrimitive.Trigger
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
@@ -45,21 +48,21 @@ const SelectTrigger = <T extends ValidComponent = "button">(
<path d="M16 15l-4 4l-4 -4" /> <path d="M16 15l-4 4l-4 -4" />
</SelectPrimitive.Icon> </SelectPrimitive.Icon>
</SelectPrimitive.Trigger> </SelectPrimitive.Trigger>
) );
} };
type SelectContentProps<T extends ValidComponent = "div"> = type SelectContentProps<T extends ValidComponent = 'div'> =
SelectPrimitive.SelectContentProps<T> & { class?: string | undefined } SelectPrimitive.SelectContentProps<T> & { class?: string | undefined };
const SelectContent = <T extends ValidComponent = "div">( const SelectContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SelectContentProps<T>> props: PolymorphicProps<T, SelectContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SelectContentProps, ["class"]) const [local, others] = splitProps(props as SelectContentProps, ['class']);
return ( return (
<SelectPrimitive.Portal> <SelectPrimitive.Portal>
<SelectPrimitive.Content <SelectPrimitive.Content
class={cn( 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", 'fade-in-80 relative z-50 min-w-32 animate-in overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md',
local.class local.class
)} )}
{...others} {...others}
@@ -67,22 +70,26 @@ const SelectContent = <T extends ValidComponent = "div">(
<SelectPrimitive.Listbox class="m-0 p-1" /> <SelectPrimitive.Listbox class="m-0 p-1" />
</SelectPrimitive.Content> </SelectPrimitive.Content>
</SelectPrimitive.Portal> </SelectPrimitive.Portal>
) );
} };
type SelectItemProps<T extends ValidComponent = "li"> = SelectPrimitive.SelectItemProps<T> & { type SelectItemProps<T extends ValidComponent = 'li'> =
class?: string | undefined SelectPrimitive.SelectItemProps<T> & {
children?: JSX.Element class?: string | undefined;
} children?: JSX.Element;
};
const SelectItem = <T extends ValidComponent = "li">( const SelectItem = <T extends ValidComponent = 'li'>(
props: PolymorphicProps<T, SelectItemProps<T>> props: PolymorphicProps<T, SelectItemProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SelectItemProps, ["class", "children"]) const [local, others] = splitProps(props as SelectItemProps, [
'class',
'children',
]);
return ( return (
<SelectPrimitive.Item <SelectPrimitive.Item
class={cn( 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", 'relative mt-0 flex w-full cursor-default select-none items-center rounded-sm py-1.5 pr-8 pl-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',
local.class local.class
)} )}
{...others} {...others}
@@ -104,69 +111,79 @@ const SelectItem = <T extends ValidComponent = "li">(
</SelectPrimitive.ItemIndicator> </SelectPrimitive.ItemIndicator>
<SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel> <SelectPrimitive.ItemLabel>{local.children}</SelectPrimitive.ItemLabel>
</SelectPrimitive.Item> </SelectPrimitive.Item>
) );
} };
const labelVariants = cva( const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 'font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
{ {
variants: { variants: {
variant: { variant: {
label: "data-[invalid]:text-destructive", label: 'data-[invalid]:text-destructive',
description: "font-normal text-muted-foreground", description: 'font-normal text-muted-foreground',
error: "text-xs text-destructive" error: 'text-destructive text-xs',
} },
}, },
defaultVariants: { defaultVariants: {
variant: "label" variant: 'label',
} },
} }
) );
type SelectLabelProps<T extends ValidComponent = "label"> = SelectPrimitive.SelectLabelProps<T> & { type SelectLabelProps<T extends ValidComponent = 'label'> =
class?: string | undefined SelectPrimitive.SelectLabelProps<T> & {
} class?: string | undefined;
};
const SelectLabel = <T extends ValidComponent = "label">( const SelectLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, SelectLabelProps<T>> props: PolymorphicProps<T, SelectLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SelectLabelProps, ["class"]) const [local, others] = splitProps(props as SelectLabelProps, ['class']);
return <SelectPrimitive.Label class={cn(labelVariants(), local.class)} {...others} /> return (
} <SelectPrimitive.Label
class={cn(labelVariants(), local.class)}
{...others}
/>
);
};
type SelectDescriptionProps<T extends ValidComponent = "div"> = type SelectDescriptionProps<T extends ValidComponent = 'div'> =
SelectPrimitive.SelectDescriptionProps<T> & { SelectPrimitive.SelectDescriptionProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const SelectDescription = <T extends ValidComponent = "div">( const SelectDescription = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SelectDescriptionProps<T>> props: PolymorphicProps<T, SelectDescriptionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SelectDescriptionProps, ["class"]) const [local, others] = splitProps(props as SelectDescriptionProps, [
'class',
]);
return ( return (
<SelectPrimitive.Description <SelectPrimitive.Description
class={cn(labelVariants({ variant: "description" }), local.class)} class={cn(labelVariants({ variant: 'description' }), local.class)}
{...others} {...others}
/> />
) );
} };
type SelectErrorMessageProps<T extends ValidComponent = "div"> = type SelectErrorMessageProps<T extends ValidComponent = 'div'> =
SelectPrimitive.SelectErrorMessageProps<T> & { SelectPrimitive.SelectErrorMessageProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const SelectErrorMessage = <T extends ValidComponent = "div">( const SelectErrorMessage = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SelectErrorMessageProps<T>> props: PolymorphicProps<T, SelectErrorMessageProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SelectErrorMessageProps, ["class"]) const [local, others] = splitProps(props as SelectErrorMessageProps, [
'class',
]);
return ( return (
<SelectPrimitive.ErrorMessage <SelectPrimitive.ErrorMessage
class={cn(labelVariants({ variant: "error" }), local.class)} class={cn(labelVariants({ variant: 'error' }), local.class)}
{...others} {...others}
/> />
) );
} };
export { export {
Select, Select,
@@ -177,5 +194,5 @@ export {
SelectItem, SelectItem,
SelectLabel, SelectLabel,
SelectDescription, SelectDescription,
SelectErrorMessage SelectErrorMessage,
} };

View File

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

View File

@@ -1,84 +1,96 @@
import type { Component, ComponentProps, JSX, ValidComponent } from "solid-js" import type { Component, ComponentProps, JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import * as SheetPrimitive from "@kobalte/core/dialog" import * as SheetPrimitive from '@kobalte/core/dialog';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import { cva, type VariantProps } from "class-variance-authority" import { type VariantProps, cva } from 'class-variance-authority';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Sheet = SheetPrimitive.Root const Sheet = SheetPrimitive.Root;
const SheetTrigger = SheetPrimitive.Trigger const SheetTrigger = SheetPrimitive.Trigger;
const SheetClose = SheetPrimitive.CloseButton const SheetClose = SheetPrimitive.CloseButton;
const portalVariants = cva("fixed inset-0 z-50 flex", { const portalVariants = cva('fixed inset-0 z-50 flex', {
variants: { variants: {
position: { position: {
top: "items-start", top: 'items-start',
bottom: "items-end", bottom: 'items-end',
left: "justify-start", left: 'justify-start',
right: "justify-end" right: 'justify-end',
} },
}, },
defaultVariants: { position: "right" } defaultVariants: { position: 'right' },
}) });
type PortalProps = SheetPrimitive.DialogPortalProps & VariantProps<typeof portalVariants> type PortalProps = SheetPrimitive.DialogPortalProps &
VariantProps<typeof portalVariants>;
const SheetPortal: Component<PortalProps> = (props) => { const SheetPortal: Component<PortalProps> = (props) => {
const [local, others] = splitProps(props, ["position", "children"]) const [local, others] = splitProps(props, ['position', 'children']);
return ( return (
<SheetPrimitive.Portal {...others}> <SheetPrimitive.Portal {...others}>
<div class={portalVariants({ position: local.position })}>{local.children}</div> <div class={portalVariants({ position: local.position })}>
{local.children}
</div>
</SheetPrimitive.Portal> </SheetPrimitive.Portal>
) );
} };
type DialogOverlayProps<T extends ValidComponent = "div"> = SheetPrimitive.DialogOverlayProps<T> & { type DialogOverlayProps<T extends ValidComponent = 'div'> =
class?: string | undefined SheetPrimitive.DialogOverlayProps<T> & {
} class?: string | undefined;
};
const SheetOverlay = <T extends ValidComponent = "div">( const SheetOverlay = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DialogOverlayProps<T>> props: PolymorphicProps<T, DialogOverlayProps<T>>
) => { ) => {
const [local, others] = splitProps(props as DialogOverlayProps, ["class"]) const [local, others] = splitProps(props as DialogOverlayProps, ['class']);
return ( return (
<SheetPrimitive.Overlay <SheetPrimitive.Overlay
class={cn( 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", 'data-[closed=]:fade-out-0 data-[expanded=]:fade-in-0 fixed inset-0 z-50 bg-black/80 data-[closed=]:animate-out data-[expanded=]:animate-in',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
const sheetVariants = cva( 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", 'fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[closed=]:animate-out data-[expanded=]:animate-in data-[closed=]:duration-300 data-[expanded=]:duration-500',
{ {
variants: { variants: {
position: { position: {
top: "inset-x-0 top-0 border-b data-[closed=]:slide-out-to-top data-[expanded=]:slide-in-from-top", top: 'data-[closed=]:slide-out-to-top data-[expanded=]:slide-in-from-top inset-x-0 top-0 border-b',
bottom: bottom:
"inset-x-0 bottom-0 border-t data-[closed=]:slide-out-to-bottom data-[expanded=]:slide-in-from-bottom", 'data-[closed=]:slide-out-to-bottom data-[expanded=]:slide-in-from-bottom inset-x-0 bottom-0 border-t',
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", left: 'data-[closed=]:slide-out-to-left data-[expanded=]:slide-in-from-left inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm',
right: 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" 'data-[closed=]:slide-out-to-right data-[expanded=]:slide-in-from-right inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm',
} },
}, },
defaultVariants: { defaultVariants: {
position: "right" position: 'right',
} },
} }
) );
type DialogContentProps<T extends ValidComponent = "div"> = SheetPrimitive.DialogContentProps<T> & type DialogContentProps<T extends ValidComponent = 'div'> =
VariantProps<typeof sheetVariants> & { class?: string | undefined; children?: JSX.Element } SheetPrimitive.DialogContentProps<T> &
VariantProps<typeof sheetVariants> & {
class?: string | undefined;
children?: JSX.Element;
};
const SheetContent = <T extends ValidComponent = "div">( const SheetContent = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, DialogContentProps<T>> props: PolymorphicProps<T, DialogContentProps<T>>
) => { ) => {
const [local, others] = splitProps(props as DialogContentProps, ["position", "class", "children"]) const [local, others] = splitProps(props as DialogContentProps, [
'position',
'class',
'children',
]);
return ( return (
<SheetPortal position={local.position}> <SheetPortal position={local.position}>
<SheetOverlay /> <SheetOverlay />
@@ -86,12 +98,12 @@ const SheetContent = <T extends ValidComponent = "div">(
class={cn( class={cn(
sheetVariants({ position: local.position }), sheetVariants({ position: local.position }),
local.class, local.class,
"max-h-screen overflow-y-auto" 'max-h-screen overflow-y-auto'
)} )}
{...others} {...others}
> >
{local.children} {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"> <SheetPrimitive.CloseButton class="absolute top-4 right-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 <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@@ -109,56 +121,68 @@ const SheetContent = <T extends ValidComponent = "div">(
</SheetPrimitive.CloseButton> </SheetPrimitive.CloseButton>
</SheetPrimitive.Content> </SheetPrimitive.Content>
</SheetPortal> </SheetPortal>
) );
} };
const SheetHeader: Component<ComponentProps<"div">> = (props) => { const SheetHeader: Component<ComponentProps<'div'>> = (props) => {
const [local, others] = splitProps(props, ["class"]) 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 ( return (
<div <div
class={cn("flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2", local.class)} class={cn(
'flex flex-col space-y-2 text-center sm:text-left',
local.class
)}
{...others} {...others}
/> />
) );
} };
type DialogTitleProps<T extends ValidComponent = "h2"> = SheetPrimitive.DialogTitleProps<T> & { const SheetFooter: Component<ComponentProps<'div'>> = (props) => {
class?: string | undefined 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}
/>
);
};
const SheetTitle = <T extends ValidComponent = "h2">( type DialogTitleProps<T extends ValidComponent = 'h2'> =
SheetPrimitive.DialogTitleProps<T> & {
class?: string | undefined;
};
const SheetTitle = <T extends ValidComponent = 'h2'>(
props: PolymorphicProps<T, DialogTitleProps<T>> props: PolymorphicProps<T, DialogTitleProps<T>>
) => { ) => {
const [local, others] = splitProps(props as DialogTitleProps, ["class"]) const [local, others] = splitProps(props as DialogTitleProps, ['class']);
return ( return (
<SheetPrimitive.Title <SheetPrimitive.Title
class={cn("text-lg font-semibold text-foreground", local.class)} class={cn('font-semibold text-foreground text-lg', local.class)}
{...others} {...others}
/> />
) );
} };
type DialogDescriptionProps<T extends ValidComponent = "p"> = type DialogDescriptionProps<T extends ValidComponent = 'p'> =
SheetPrimitive.DialogDescriptionProps<T> & { class?: string | undefined } SheetPrimitive.DialogDescriptionProps<T> & { class?: string | undefined };
const SheetDescription = <T extends ValidComponent = "p">( const SheetDescription = <T extends ValidComponent = 'p'>(
props: PolymorphicProps<T, DialogDescriptionProps<T>> props: PolymorphicProps<T, DialogDescriptionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as DialogDescriptionProps, ["class"]) const [local, others] = splitProps(props as DialogDescriptionProps, [
'class',
]);
return ( return (
<SheetPrimitive.Description <SheetPrimitive.Description
class={cn("text-sm text-muted-foreground", local.class)} class={cn('text-muted-foreground text-sm', local.class)}
{...others} {...others}
/> />
) );
} };
export { export {
Sheet, Sheet,
@@ -168,5 +192,5 @@ export {
SheetHeader, SheetHeader,
SheetFooter, SheetFooter,
SheetTitle, SheetTitle,
SheetDescription SheetDescription,
} };

View File

@@ -35,7 +35,7 @@ import {
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from '~/components/ui/tooltip'; } from '~/components/ui/tooltip';
import { cn } from '~/styles/utils'; import { cn } from '~/utils/styles';
const MOBILE_BREAKPOINT = 768; const MOBILE_BREAKPOINT = 768;
const SIDEBAR_COOKIE_NAME = 'sidebar:state'; const SIDEBAR_COOKIE_NAME = 'sidebar:state';

View File

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

View File

@@ -1,92 +1,112 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import * as SliderPrimitive from "@kobalte/core/slider" import * as SliderPrimitive from '@kobalte/core/slider';
import { cn } from "~/styles/utils" import { Label } from '~/components/ui/label';
import { Label } from "~/components/ui/label" import { cn } from '~/utils/styles';
type SliderRootProps<T extends ValidComponent = "div"> = SliderPrimitive.SliderRootProps<T> & { type SliderRootProps<T extends ValidComponent = 'div'> =
class?: string | undefined SliderPrimitive.SliderRootProps<T> & {
} class?: string | undefined;
};
const Slider = <T extends ValidComponent = "div">( const Slider = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SliderRootProps<T>> props: PolymorphicProps<T, SliderRootProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SliderRootProps, ["class"]) const [local, others] = splitProps(props as SliderRootProps, ['class']);
return ( return (
<SliderPrimitive.Root <SliderPrimitive.Root
class={cn("relative flex w-full touch-none select-none flex-col items-center", local.class)} class={cn(
'relative flex w-full touch-none select-none flex-col items-center',
local.class
)}
{...others} {...others}
/> />
) );
} };
type SliderTrackProps<T extends ValidComponent = "div"> = SliderPrimitive.SliderTrackProps<T> & { type SliderTrackProps<T extends ValidComponent = 'div'> =
class?: string | undefined SliderPrimitive.SliderTrackProps<T> & {
} class?: string | undefined;
};
const SliderTrack = <T extends ValidComponent = "div">( const SliderTrack = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SliderTrackProps<T>> props: PolymorphicProps<T, SliderTrackProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SliderTrackProps, ["class"]) const [local, others] = splitProps(props as SliderTrackProps, ['class']);
return ( return (
<SliderPrimitive.Track <SliderPrimitive.Track
class={cn("relative h-2 w-full grow rounded-full bg-secondary", local.class)} class={cn(
'relative h-2 w-full grow rounded-full bg-secondary',
local.class
)}
{...others} {...others}
/> />
) );
} };
type SliderFillProps<T extends ValidComponent = "div"> = SliderPrimitive.SliderFillProps<T> & { type SliderFillProps<T extends ValidComponent = 'div'> =
class?: string | undefined SliderPrimitive.SliderFillProps<T> & {
} class?: string | undefined;
};
const SliderFill = <T extends ValidComponent = "div">( const SliderFill = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SliderFillProps<T>> props: PolymorphicProps<T, SliderFillProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SliderFillProps, ["class"]) const [local, others] = splitProps(props as SliderFillProps, ['class']);
return ( return (
<SliderPrimitive.Fill <SliderPrimitive.Fill
class={cn("absolute h-full rounded-full bg-primary", local.class)} class={cn('absolute h-full rounded-full bg-primary', local.class)}
{...others} {...others}
/> />
) );
} };
type SliderThumbProps<T extends ValidComponent = "span"> = SliderPrimitive.SliderThumbProps<T> & { type SliderThumbProps<T extends ValidComponent = 'span'> =
class?: string | undefined SliderPrimitive.SliderThumbProps<T> & {
children?: JSX.Element class?: string | undefined;
} children?: JSX.Element;
};
const SliderThumb = <T extends ValidComponent = "span">( const SliderThumb = <T extends ValidComponent = 'span'>(
props: PolymorphicProps<T, SliderThumbProps<T>> props: PolymorphicProps<T, SliderThumbProps<T>>
) => { ) => {
const [local, others] = splitProps(props as SliderThumbProps, ["class", "children"]) const [local, others] = splitProps(props as SliderThumbProps, [
'class',
'children',
]);
return ( return (
<SliderPrimitive.Thumb <SliderPrimitive.Thumb
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
> >
<SliderPrimitive.Input /> <SliderPrimitive.Input />
</SliderPrimitive.Thumb> </SliderPrimitive.Thumb>
) );
} };
const SliderLabel = <T extends ValidComponent = "label">( const SliderLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, SliderPrimitive.SliderLabelProps<T>> props: PolymorphicProps<T, SliderPrimitive.SliderLabelProps<T>>
) => { ) => {
return <SliderPrimitive.Label as={Label} {...props} /> return <SliderPrimitive.Label as={Label} {...props} />;
} };
const SliderValueLabel = <T extends ValidComponent = "label">( const SliderValueLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, SliderPrimitive.SliderValueLabelProps<T>> props: PolymorphicProps<T, SliderPrimitive.SliderValueLabelProps<T>>
) => { ) => {
return <SliderPrimitive.ValueLabel as={Label} {...props} /> return <SliderPrimitive.ValueLabel as={Label} {...props} />;
} };
export { Slider, SliderTrack, SliderFill, SliderThumb, SliderLabel, SliderValueLabel } export {
Slider,
SliderTrack,
SliderFill,
SliderThumb,
SliderLabel,
SliderValueLabel,
};

View File

@@ -0,0 +1,287 @@
import {
LoaderCircleIcon,
LoaderIcon,
LoaderPinwheelIcon,
type LucideProps,
} from 'lucide-solid';
import { mergeProps, splitProps } from 'solid-js';
import { cn } from '~/utils/styles';
type SpinnerVariantProps = Omit<SpinnerProps, 'variant'>;
const Default = (props: SpinnerVariantProps) => {
const [local, other] = splitProps(props, ['class']);
return <LoaderIcon class={cn('animate-spin', local.class)} {...other} />;
};
const Circle = (props: SpinnerVariantProps) => {
const [local, other] = splitProps(props, ['class']);
return (
<LoaderCircleIcon class={cn('animate-spin', local.class)} {...other} />
);
};
const Pinwheel = (props: SpinnerVariantProps) => {
const [local, other] = splitProps(props, ['class']);
return (
<LoaderPinwheelIcon class={cn('animate-spin', local.class)} {...other} />
);
};
const CircleFilled = (props: SpinnerVariantProps) => {
const [local, _] = splitProps(mergeProps({ size: 24 }, props), [
'class',
'size',
]);
const size = `${local.size}`;
return (
<div class="relative" style={{ width: size, height: size }}>
<div class="absolute inset-0 rotate-180">
<LoaderCircleIcon
class={cn('animate-spin', local.class, 'text-foreground opacity-20')}
size={size}
{...props}
/>
</div>
<LoaderCircleIcon
class={cn('relative animate-spin', local.class)}
size={size}
{...props}
/>
</div>
);
};
const Ellipsis = (props: SpinnerVariantProps) => {
const [{ size = 24 }, other] = splitProps(props, ['size']);
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
{...other}
>
<title>Loading...</title>
<circle cx="4" cy="12" r="2" fill="currentColor">
<animate
id="ellipsis1"
begin="0;ellipsis3.end+0.25s"
attributeName="cy"
calcMode="spline"
dur="0.6s"
values="12;6;12"
keySplines=".33,.66,.66,1;.33,0,.66,.33"
/>
</circle>
<circle cx="12" cy="12" r="2" fill="currentColor">
<animate
begin="ellipsis1.begin+0.1s"
attributeName="cy"
calcMode="spline"
dur="0.6s"
values="12;6;12"
keySplines=".33,.66,.66,1;.33,0,.66,.33"
/>
</circle>
<circle cx="20" cy="12" r="2" fill="currentColor">
<animate
id="ellipsis3"
begin="ellipsis1.begin+0.2s"
attributeName="cy"
calcMode="spline"
dur="0.6s"
values="12;6;12"
keySplines=".33,.66,.66,1;.33,0,.66,.33"
/>
</circle>
</svg>
);
};
const Ring = (props: SpinnerVariantProps) => {
const [{ size = 24 }, other] = splitProps(props, ['size']);
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 44 44"
stroke="currentColor"
{...other}
>
<title>Loading...</title>
<g fill="none" fill-rule="evenodd" stroke-width="2">
<circle cx="22" cy="22" r="1">
<animate
attributeName="r"
begin="0s"
dur="1.8s"
values="1; 20"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.165, 0.84, 0.44, 1"
repeatCount="indefinite"
/>
<animate
attributeName="stroke-opacity"
begin="0s"
dur="1.8s"
values="1; 0"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.3, 0.61, 0.355, 1"
repeatCount="indefinite"
/>
</circle>
<circle cx="22" cy="22" r="1">
<animate
attributeName="r"
begin="-0.9s"
dur="1.8s"
values="1; 20"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.165, 0.84, 0.44, 1"
repeatCount="indefinite"
/>
<animate
attributeName="stroke-opacity"
begin="-0.9s"
dur="1.8s"
values="1; 0"
calcMode="spline"
keyTimes="0; 1"
keySplines="0.3, 0.61, 0.355, 1"
repeatCount="indefinite"
/>
</circle>
</g>
</svg>
);
};
const Bars = ({ size = 24, ...props }: SpinnerVariantProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
{...props}
>
<title>Loading...</title>
<style>{`
.spinner-bar {
animation: spinner-bars-animation .8s linear infinite;
animation-delay: -.8s;
}
.spinner-bars-2 {
animation-delay: -.65s;
}
.spinner-bars-3 {
animation-delay: -0.5s;
}
@keyframes spinner-bars-animation {
0% {
y: 1px;
height: 22px;
}
93.75% {
y: 5px;
height: 14px;
opacity: 0.2;
}
}
`}</style>
<rect
class="spinner-bar"
x="1"
y="1"
width="6"
height="22"
fill="currentColor"
/>
<rect
class="spinner-bar spinner-bars-2"
x="9"
y="1"
width="6"
height="22"
fill="currentColor"
/>
<rect
class="spinner-bar spinner-bars-3"
x="17"
y="1"
width="6"
height="22"
fill="currentColor"
/>
</svg>
);
const Infinite = ({ size = 24, ...props }: SpinnerVariantProps) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid"
{...props}
>
<title>Loading...</title>
<path
fill="none"
stroke="currentColor"
stroke-width="10"
stroke-dasharray="205.271142578125 51.317785644531256"
d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40 C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z"
stroke-linecap="round"
style={{
transform: 'scale(0.8)',
'transform-origin': '50px 50px',
}}
>
<animate
attributeName="stroke-dashoffset"
repeatCount="indefinite"
dur="2s"
keyTimes="0;1"
values="0;256.58892822265625"
/>
</path>
</svg>
);
export type SpinnerProps = LucideProps & {
variant?:
| 'default'
| 'circle'
| 'pinwheel'
| 'circle-filled'
| 'ellipsis'
| 'ring'
| 'bars'
| 'infinite';
};
export const Spinner = ({ variant, ...props }: SpinnerProps) => {
switch (variant) {
case 'circle':
return <Circle {...props} />;
case 'pinwheel':
return <Pinwheel {...props} />;
case 'circle-filled':
return <CircleFilled {...props} />;
case 'ellipsis':
return <Ellipsis {...props} />;
case 'ring':
return <Ring {...props} />;
case 'bars':
return <Bars {...props} />;
case 'infinite':
return <Infinite {...props} />;
default:
return <Default {...props} />;
}
};

View File

@@ -1,35 +1,38 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { splitProps } from "solid-js" import { splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core" import type { PolymorphicProps } from '@kobalte/core';
import * as SwitchPrimitive from "@kobalte/core/switch" import * as SwitchPrimitive from '@kobalte/core/switch';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const Switch = SwitchPrimitive.Root const Switch = SwitchPrimitive.Root;
const SwitchDescription = SwitchPrimitive.Description const SwitchDescription = SwitchPrimitive.Description;
const SwitchErrorMessage = SwitchPrimitive.ErrorMessage const SwitchErrorMessage = SwitchPrimitive.ErrorMessage;
type SwitchControlProps = SwitchPrimitive.SwitchControlProps & { type SwitchControlProps = SwitchPrimitive.SwitchControlProps & {
class?: string | undefined class?: string | undefined;
children?: JSX.Element children?: JSX.Element;
} };
const SwitchControl = <T extends ValidComponent = "input">( const SwitchControl = <T extends ValidComponent = 'input'>(
props: PolymorphicProps<T, SwitchControlProps> props: PolymorphicProps<T, SwitchControlProps>
) => { ) => {
const [local, others] = splitProps(props as SwitchControlProps, ["class", "children"]) const [local, others] = splitProps(props as SwitchControlProps, [
'class',
'children',
]);
return ( return (
<> <>
<SwitchPrimitive.Input <SwitchPrimitive.Input
class={cn( 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", '[&: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 local.class
)} )}
/> />
<SwitchPrimitive.Control <SwitchPrimitive.Control
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
@@ -37,41 +40,52 @@ const SwitchControl = <T extends ValidComponent = "input">(
{local.children} {local.children}
</SwitchPrimitive.Control> </SwitchPrimitive.Control>
</> </>
) );
} };
type SwitchThumbProps = SwitchPrimitive.SwitchThumbProps & { class?: string | undefined } type SwitchThumbProps = SwitchPrimitive.SwitchThumbProps & {
class?: string | undefined;
};
const SwitchThumb = <T extends ValidComponent = "div">( const SwitchThumb = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, SwitchThumbProps> props: PolymorphicProps<T, SwitchThumbProps>
) => { ) => {
const [local, others] = splitProps(props as SwitchThumbProps, ["class"]) const [local, others] = splitProps(props as SwitchThumbProps, ['class']);
return ( return (
<SwitchPrimitive.Thumb <SwitchPrimitive.Thumb
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type SwitchLabelProps = SwitchPrimitive.SwitchLabelProps & { class?: string | undefined } type SwitchLabelProps = SwitchPrimitive.SwitchLabelProps & {
class?: string | undefined;
};
const SwitchLabel = <T extends ValidComponent = "label">( const SwitchLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, SwitchLabelProps> props: PolymorphicProps<T, SwitchLabelProps>
) => { ) => {
const [local, others] = splitProps(props as SwitchLabelProps, ["class"]) const [local, others] = splitProps(props as SwitchLabelProps, ['class']);
return ( return (
<SwitchPrimitive.Label <SwitchPrimitive.Label
class={cn( class={cn(
"text-sm font-medium leading-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70", 'font-medium text-sm leading-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70',
local.class local.class
)} )}
{...others} {...others}
/> />
) );
} };
export { Switch, SwitchControl, SwitchThumb, SwitchLabel, SwitchDescription, SwitchErrorMessage } export {
Switch,
SwitchControl,
SwitchThumb,
SwitchLabel,
SwitchDescription,
SwitchErrorMessage,
};

View File

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

View File

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

View File

@@ -1,146 +1,168 @@
import type { ValidComponent } from "solid-js" import type { ValidComponent } from 'solid-js';
import { mergeProps, splitProps } from "solid-js" import { mergeProps, splitProps } from 'solid-js';
import type { PolymorphicProps } from "@kobalte/core" import type { PolymorphicProps } from '@kobalte/core';
import * as TextFieldPrimitive from "@kobalte/core/text-field" import * as TextFieldPrimitive from '@kobalte/core/text-field';
import { cva } from "class-variance-authority" import { cva } from 'class-variance-authority';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
type TextFieldRootProps<T extends ValidComponent = "div"> = type TextFieldRootProps<T extends ValidComponent = 'div'> =
TextFieldPrimitive.TextFieldRootProps<T> & { TextFieldPrimitive.TextFieldRootProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const TextField = <T extends ValidComponent = "div">( const TextField = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, TextFieldRootProps<T>> props: PolymorphicProps<T, TextFieldRootProps<T>>
) => { ) => {
const [local, others] = splitProps(props as TextFieldRootProps, ["class"]) const [local, others] = splitProps(props as TextFieldRootProps, ['class']);
return <TextFieldPrimitive.Root class={cn("flex flex-col gap-1", local.class)} {...others} /> return (
} <TextFieldPrimitive.Root
class={cn('flex flex-col gap-1', local.class)}
{...others}
/>
);
};
type TextFieldInputProps<T extends ValidComponent = "input"> = type TextFieldInputProps<T extends ValidComponent = 'input'> =
TextFieldPrimitive.TextFieldInputProps<T> & { TextFieldPrimitive.TextFieldInputProps<T> & {
class?: string | undefined class?: string | undefined;
type?: type?:
| "button" | 'button'
| "checkbox" | 'checkbox'
| "color" | 'color'
| "date" | 'date'
| "datetime-local" | 'datetime-local'
| "email" | 'email'
| "file" | 'file'
| "hidden" | 'hidden'
| "image" | 'image'
| "month" | 'month'
| "number" | 'number'
| "password" | 'password'
| "radio" | 'radio'
| "range" | 'range'
| "reset" | 'reset'
| "search" | 'search'
| "submit" | 'submit'
| "tel" | 'tel'
| "text" | 'text'
| "time" | 'time'
| "url" | 'url'
| "week" | 'week';
} };
const TextFieldInput = <T extends ValidComponent = "input">( const TextFieldInput = <T extends ValidComponent = 'input'>(
rawProps: PolymorphicProps<T, TextFieldInputProps<T>> rawProps: PolymorphicProps<T, TextFieldInputProps<T>>
) => { ) => {
const props = mergeProps<TextFieldInputProps<T>[]>({ type: "text" }, rawProps) const props = mergeProps<TextFieldInputProps<T>[]>(
const [local, others] = splitProps(props as TextFieldInputProps, ["type", "class"]) { type: 'text' },
rawProps
);
const [local, others] = splitProps(props as TextFieldInputProps, [
'type',
'class',
]);
return ( return (
<TextFieldPrimitive.Input <TextFieldPrimitive.Input
type={local.type} type={local.type}
class={cn( 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", '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:font-medium file:text-sm 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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
type TextFieldTextAreaProps<T extends ValidComponent = "textarea"> = type TextFieldTextAreaProps<T extends ValidComponent = 'textarea'> =
TextFieldPrimitive.TextFieldTextAreaProps<T> & { class?: string | undefined } TextFieldPrimitive.TextFieldTextAreaProps<T> & { class?: string | undefined };
const TextFieldTextArea = <T extends ValidComponent = "textarea">( const TextFieldTextArea = <T extends ValidComponent = 'textarea'>(
props: PolymorphicProps<T, TextFieldTextAreaProps<T>> props: PolymorphicProps<T, TextFieldTextAreaProps<T>>
) => { ) => {
const [local, others] = splitProps(props as TextFieldTextAreaProps, ["class"]) const [local, others] = splitProps(props as TextFieldTextAreaProps, [
'class',
]);
return ( return (
<TextFieldPrimitive.TextArea <TextFieldPrimitive.TextArea
class={cn( 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", '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 local.class
)} )}
{...others} {...others}
/> />
) );
} };
const labelVariants = cva( const labelVariants = cva(
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 'font-medium text-sm leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
{ {
variants: { variants: {
variant: { variant: {
label: "data-[invalid]:text-destructive", label: 'data-[invalid]:text-destructive',
description: "font-normal text-muted-foreground", description: 'font-normal text-muted-foreground',
error: "text-xs text-destructive" error: 'text-destructive text-xs',
} },
}, },
defaultVariants: { defaultVariants: {
variant: "label" variant: 'label',
} },
} }
) );
type TextFieldLabelProps<T extends ValidComponent = "label"> = type TextFieldLabelProps<T extends ValidComponent = 'label'> =
TextFieldPrimitive.TextFieldLabelProps<T> & { class?: string | undefined } TextFieldPrimitive.TextFieldLabelProps<T> & { class?: string | undefined };
const TextFieldLabel = <T extends ValidComponent = "label">( const TextFieldLabel = <T extends ValidComponent = 'label'>(
props: PolymorphicProps<T, TextFieldLabelProps<T>> props: PolymorphicProps<T, TextFieldLabelProps<T>>
) => { ) => {
const [local, others] = splitProps(props as TextFieldLabelProps, ["class"]) const [local, others] = splitProps(props as TextFieldLabelProps, ['class']);
return <TextFieldPrimitive.Label class={cn(labelVariants(), local.class)} {...others} /> return (
} <TextFieldPrimitive.Label
class={cn(labelVariants(), local.class)}
{...others}
/>
);
};
type TextFieldDescriptionProps<T extends ValidComponent = "div"> = type TextFieldDescriptionProps<T extends ValidComponent = 'div'> =
TextFieldPrimitive.TextFieldDescriptionProps<T> & { TextFieldPrimitive.TextFieldDescriptionProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const TextFieldDescription = <T extends ValidComponent = "div">( const TextFieldDescription = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, TextFieldDescriptionProps<T>> props: PolymorphicProps<T, TextFieldDescriptionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as TextFieldDescriptionProps, ["class"]) const [local, others] = splitProps(props as TextFieldDescriptionProps, [
'class',
]);
return ( return (
<TextFieldPrimitive.Description <TextFieldPrimitive.Description
class={cn(labelVariants({ variant: "description" }), local.class)} class={cn(labelVariants({ variant: 'description' }), local.class)}
{...others} {...others}
/> />
) );
} };
type TextFieldErrorMessageProps<T extends ValidComponent = "div"> = type TextFieldErrorMessageProps<T extends ValidComponent = 'div'> =
TextFieldPrimitive.TextFieldErrorMessageProps<T> & { TextFieldPrimitive.TextFieldErrorMessageProps<T> & {
class?: string | undefined class?: string | undefined;
} };
const TextFieldErrorMessage = <T extends ValidComponent = "div">( const TextFieldErrorMessage = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, TextFieldErrorMessageProps<T>> props: PolymorphicProps<T, TextFieldErrorMessageProps<T>>
) => { ) => {
const [local, others] = splitProps(props as TextFieldErrorMessageProps, ["class"]) const [local, others] = splitProps(props as TextFieldErrorMessageProps, [
'class',
]);
return ( return (
<TextFieldPrimitive.ErrorMessage <TextFieldPrimitive.ErrorMessage
class={cn(labelVariants({ variant: "error" }), local.class)} class={cn(labelVariants({ variant: 'error' }), local.class)}
{...others} {...others}
/> />
) );
} };
export { export {
TextField, TextField,
@@ -148,5 +170,5 @@ export {
TextFieldTextArea, TextFieldTextArea,
TextFieldLabel, TextFieldLabel,
TextFieldDescription, TextFieldDescription,
TextFieldErrorMessage TextFieldErrorMessage,
} };

View File

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

View File

@@ -1,81 +1,90 @@
import type { JSX, ValidComponent } from "solid-js" import type { JSX, ValidComponent } from 'solid-js';
import { Match, splitProps, Switch } from "solid-js" import { Match, Switch, splitProps } from 'solid-js';
import { Portal } from "solid-js/web" import { Portal } from 'solid-js/web';
import type { PolymorphicProps } from "@kobalte/core/polymorphic" import type { PolymorphicProps } from '@kobalte/core/polymorphic';
import * as ToastPrimitive from "@kobalte/core/toast" import * as ToastPrimitive from '@kobalte/core/toast';
import type { VariantProps } from "class-variance-authority" import type { VariantProps } from 'class-variance-authority';
import { cva } from "class-variance-authority" import { cva } from 'class-variance-authority';
import { cn } from "~/styles/utils" import { cn } from '~/utils/styles';
const toastVariants = cva( 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", 'group 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 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-[closed]:animate-out data-[opened]:animate-in data-[swipe=end]:animate-out data-[swipe=move]:transition-none',
{ {
variants: { variants: {
variant: { variant: {
default: "border bg-background text-foreground", default: 'border bg-background text-foreground',
destructive: destructive:
"destructive group border-destructive bg-destructive text-destructive-foreground", 'destructive group border-destructive bg-destructive text-destructive-foreground',
success: "success border-success-foreground bg-success text-success-foreground", success:
warning: "warning border-warning-foreground bg-warning text-warning-foreground", 'success border-success-foreground bg-success text-success-foreground',
error: "error border-error-foreground bg-error text-error-foreground" warning:
} 'warning border-warning-foreground bg-warning text-warning-foreground',
error: 'error border-error-foreground bg-error text-error-foreground',
},
}, },
defaultVariants: { defaultVariants: {
variant: "default" variant: 'default',
} },
} }
) );
type ToastVariant = NonNullable<VariantProps<typeof toastVariants>["variant"]> type ToastVariant = NonNullable<VariantProps<typeof toastVariants>['variant']>;
type ToastListProps<T extends ValidComponent = "ol"> = ToastPrimitive.ToastListProps<T> & { type ToastListProps<T extends ValidComponent = 'ol'> =
class?: string | undefined ToastPrimitive.ToastListProps<T> & {
} class?: string | undefined;
};
const Toaster = <T extends ValidComponent = "ol">( const Toaster = <T extends ValidComponent = 'ol'>(
props: PolymorphicProps<T, ToastListProps<T>> props: PolymorphicProps<T, ToastListProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ToastListProps, ["class"]) const [local, others] = splitProps(props as ToastListProps, ['class']);
return ( return (
<Portal> <Portal>
<ToastPrimitive.Region> <ToastPrimitive.Region>
<ToastPrimitive.List <ToastPrimitive.List
class={cn( 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]", 'fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse gap-2 p-4 sm:top-auto sm:right-0 sm:bottom-0 sm:flex-col md:max-w-[420px]',
local.class local.class
)} )}
{...others} {...others}
/> />
</ToastPrimitive.Region> </ToastPrimitive.Region>
</Portal> </Portal>
) );
} };
type ToastRootProps<T extends ValidComponent = "li"> = ToastPrimitive.ToastRootProps<T> & type ToastRootProps<T extends ValidComponent = 'li'> =
VariantProps<typeof toastVariants> & { class?: string | undefined } ToastPrimitive.ToastRootProps<T> &
VariantProps<typeof toastVariants> & { class?: string | undefined };
const Toast = <T extends ValidComponent = "li">(props: PolymorphicProps<T, ToastRootProps<T>>) => { const Toast = <T extends ValidComponent = 'li'>(
const [local, others] = splitProps(props as ToastRootProps, ["class", "variant"]) props: PolymorphicProps<T, ToastRootProps<T>>
) => {
const [local, others] = splitProps(props as ToastRootProps, [
'class',
'variant',
]);
return ( return (
<ToastPrimitive.Root <ToastPrimitive.Root
class={cn(toastVariants({ variant: local.variant }), local.class)} class={cn(toastVariants({ variant: local.variant }), local.class)}
{...others} {...others}
/> />
) );
} };
type ToastCloseButtonProps<T extends ValidComponent = "button"> = type ToastCloseButtonProps<T extends ValidComponent = 'button'> =
ToastPrimitive.ToastCloseButtonProps<T> & { class?: string | undefined } ToastPrimitive.ToastCloseButtonProps<T> & { class?: string | undefined };
const ToastClose = <T extends ValidComponent = "button">( const ToastClose = <T extends ValidComponent = 'button'>(
props: PolymorphicProps<T, ToastCloseButtonProps<T>> props: PolymorphicProps<T, ToastCloseButtonProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ToastCloseButtonProps, ["class"]) const [local, others] = splitProps(props as ToastCloseButtonProps, ['class']);
return ( return (
<ToastPrimitive.CloseButton <ToastPrimitive.CloseButton
class={cn( 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", 'absolute top-2 right-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 local.class
)} )}
{...others} {...others}
@@ -94,70 +103,103 @@ const ToastClose = <T extends ValidComponent = "button">(
<path d="M6 6l12 12" /> <path d="M6 6l12 12" />
</svg> </svg>
</ToastPrimitive.CloseButton> </ToastPrimitive.CloseButton>
) );
} };
type ToastTitleProps<T extends ValidComponent = "div"> = ToastPrimitive.ToastTitleProps<T> & { type ToastTitleProps<T extends ValidComponent = 'div'> =
class?: string | undefined ToastPrimitive.ToastTitleProps<T> & {
} class?: string | undefined;
};
const ToastTitle = <T extends ValidComponent = "div">( const ToastTitle = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ToastTitleProps<T>> props: PolymorphicProps<T, ToastTitleProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ToastTitleProps, ["class"]) const [local, others] = splitProps(props as ToastTitleProps, ['class']);
return <ToastPrimitive.Title class={cn("text-sm font-semibold", local.class)} {...others} /> return (
} <ToastPrimitive.Title
class={cn('font-semibold text-sm', local.class)}
{...others}
/>
);
};
type ToastDescriptionProps<T extends ValidComponent = "div"> = type ToastDescriptionProps<T extends ValidComponent = 'div'> =
ToastPrimitive.ToastDescriptionProps<T> & { class?: string | undefined } ToastPrimitive.ToastDescriptionProps<T> & { class?: string | undefined };
const ToastDescription = <T extends ValidComponent = "div">( const ToastDescription = <T extends ValidComponent = 'div'>(
props: PolymorphicProps<T, ToastDescriptionProps<T>> props: PolymorphicProps<T, ToastDescriptionProps<T>>
) => { ) => {
const [local, others] = splitProps(props as ToastDescriptionProps, ["class"]) const [local, others] = splitProps(props as ToastDescriptionProps, ['class']);
return <ToastPrimitive.Description class={cn("text-sm opacity-90", local.class)} {...others} /> return (
} <ToastPrimitive.Description
class={cn('text-sm opacity-90', local.class)}
{...others}
/>
);
};
function showToast(props: { function showToast(props: {
title?: JSX.Element title?: JSX.Element;
description?: JSX.Element description?: JSX.Element;
variant?: ToastVariant variant?: ToastVariant;
duration?: number duration?: number;
}) { }) {
ToastPrimitive.toaster.show((data) => ( ToastPrimitive.toaster.show((data) => (
<Toast toastId={data.toastId} variant={props.variant} duration={props.duration}> <Toast
toastId={data.toastId}
variant={props.variant}
duration={props.duration}
>
<div class="grid gap-1"> <div class="grid gap-1">
{props.title && <ToastTitle>{props.title}</ToastTitle>} {props.title && <ToastTitle>{props.title}</ToastTitle>}
{props.description && <ToastDescription>{props.description}</ToastDescription>} {props.description && (
<ToastDescription>{props.description}</ToastDescription>
)}
</div> </div>
<ToastClose /> <ToastClose />
</Toast> </Toast>
)) ));
} }
function showToastPromise<T, U>( function showToastPromise<T, U>(
promise: Promise<T> | (() => Promise<T>), promise: Promise<T> | (() => Promise<T>),
options: { options: {
loading?: JSX.Element loading?: JSX.Element;
success?: (data: T) => JSX.Element success?: (data: T) => JSX.Element;
error?: (error: U) => JSX.Element error?: (error: U) => JSX.Element;
duration?: number duration?: number;
} }
) { ) {
const variant: { [key in ToastPrimitive.ToastPromiseState]: ToastVariant } = { const variant: { [key in ToastPrimitive.ToastPromiseState]: ToastVariant } = {
pending: "default", pending: 'default',
fulfilled: "success", fulfilled: 'success',
rejected: "error" rejected: 'error',
} };
return ToastPrimitive.toaster.promise<T, U>(promise, (props) => ( return ToastPrimitive.toaster.promise<T, U>(promise, (props) => (
<Toast toastId={props.toastId} variant={variant[props.state]} duration={options.duration}> <Toast
toastId={props.toastId}
variant={variant[props.state]}
duration={options.duration}
>
<Switch> <Switch>
<Match when={props.state === "pending"}>{options.loading}</Match> <Match when={props.state === 'pending'}>{options.loading}</Match>
<Match when={props.state === "fulfilled"}>{options.success?.(props.data!)}</Match> <Match when={props.state === 'fulfilled'}>
<Match when={props.state === "rejected"}>{options.error?.(props.error!)}</Match> {options.success?.(props.data!)}
</Match>
<Match when={props.state === 'rejected'}>
{options.error?.(props.error!)}
</Match>
</Switch> </Switch>
</Toast> </Toast>
)) ));
} }
export { Toaster, Toast, ToastClose, ToastTitle, ToastDescription, showToast, showToastPromise } export {
Toaster,
Toast,
ToastClose,
ToastTitle,
ToastDescription,
showToast,
showToastPromise,
};

View File

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

View File

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

View File

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

View File

@@ -18,11 +18,12 @@ import {
InjectorProvider, InjectorProvider,
} from 'oidc-client-rx/adapters/solid-js'; } from 'oidc-client-rx/adapters/solid-js';
import { Dynamic, render } from 'solid-js/web'; import { Dynamic, render } from 'solid-js/web';
import { buildOidcConfig, isBasicAuth } from './auth/config'; import { buildOidcConfig, isBasicAuth, isOidcAuth } from './auth/config';
import { isAuthenticated } from './auth/context'; import { isAuthenticated } from './auth/context';
import { useAuth } from './auth/hooks'; import { useAuth } from './auth/hooks';
import { routeTree } from './routeTree.gen'; import { routeTree } from './routeTree.gen';
import './app.css'; import './app.css';
import { AppNotFoundComponent } from './components/layout/app-not-found';
// Create a new router instance // Create a new router instance
const router = createRouter({ const router = createRouter({
@@ -30,6 +31,8 @@ const router = createRouter({
defaultPreload: 'intent', defaultPreload: 'intent',
defaultStaleTime: 5000, defaultStaleTime: 5000,
scrollRestoration: true, scrollRestoration: true,
defaultNotFoundComponent: AppNotFoundComponent,
notFoundMode: 'root',
context: { context: {
isAuthenticated: isAuthenticated, isAuthenticated: isAuthenticated,
injector: InjectorContextVoidInjector, injector: InjectorContextVoidInjector,
@@ -62,7 +65,7 @@ const injector: Injector = isBasicAuth
// if needed, check when init // if needed, check when init
let oidcSecurityService: OidcSecurityService | undefined; let oidcSecurityService: OidcSecurityService | undefined;
if (!isBasicAuth) { if (isOidcAuth) {
oidcSecurityService = injector.get(OidcSecurityService); oidcSecurityService = injector.get(OidcSecurityService);
oidcSecurityService.checkAuth().subscribe(); oidcSecurityService.checkAuth().subscribe();
} }

View File

@@ -13,15 +13,19 @@
import { Route as rootRoute } from './routes/__root' import { Route as rootRoute } from './routes/__root'
import { Route as AboutImport } from './routes/about' import { Route as AboutImport } from './routes/about'
import { Route as AppImport } from './routes/_app' import { Route as AppImport } from './routes/_app'
import { Route as R404Import } from './routes/404'
import { Route as IndexImport } from './routes/index' import { Route as IndexImport } from './routes/index'
import { Route as AuthSignUpImport } from './routes/auth/sign-up' import { Route as AuthSignUpImport } from './routes/auth/sign-up'
import { Route as AuthSignInImport } from './routes/auth/sign-in' import { Route as AuthSignInImport } from './routes/auth/sign-in'
import { Route as AppExploreImport } from './routes/_app/explore' import { Route as AppExploreImport } from './routes/_app/explore'
import { Route as AppSubscriptionsRouteImport } from './routes/_app/subscriptions/route'
import { Route as AppSettingsRouteImport } from './routes/_app/settings/route'
import { Route as AppPlaygroundRouteImport } from './routes/_app/playground/route'
import { Route as AuthOidcCallbackImport } from './routes/auth/oidc/callback' import { Route as AuthOidcCallbackImport } from './routes/auth/oidc/callback'
import { Route as AppSubscriptionsManageImport } from './routes/_app/subscriptions/manage' import { Route as AppSubscriptionsManageImport } from './routes/_app/subscriptions/manage'
import { Route as AppSubscriptionsCreateImport } from './routes/_app/subscriptions/create' import { Route as AppSubscriptionsCreateImport } from './routes/_app/subscriptions/create'
import { Route as AppPlaygroundGraphqlApiImport } from './routes/_app/playground/graphql-api' import { Route as AppPlaygroundGraphqlApiImport } from './routes/_app/playground/graphql-api'
import { Route as AppSubscriptionsEditSubscriptionIdImport } from './routes/_app/subscriptions/edit/$subscription-id' import { Route as AppSubscriptionsEditSubscriptionIdImport } from './routes/_app/subscriptions/edit.$subscription-id'
// Create/Update Routes // Create/Update Routes
@@ -36,6 +40,12 @@ const AppRoute = AppImport.update({
getParentRoute: () => rootRoute, getParentRoute: () => rootRoute,
} as any) } as any)
const R404Route = R404Import.update({
id: '/404',
path: '/404',
getParentRoute: () => rootRoute,
} as any)
const IndexRoute = IndexImport.update({ const IndexRoute = IndexImport.update({
id: '/', id: '/',
path: '/', path: '/',
@@ -60,6 +70,24 @@ const AppExploreRoute = AppExploreImport.update({
getParentRoute: () => AppRoute, getParentRoute: () => AppRoute,
} as any) } as any)
const AppSubscriptionsRouteRoute = AppSubscriptionsRouteImport.update({
id: '/subscriptions',
path: '/subscriptions',
getParentRoute: () => AppRoute,
} as any)
const AppSettingsRouteRoute = AppSettingsRouteImport.update({
id: '/settings',
path: '/settings',
getParentRoute: () => AppRoute,
} as any)
const AppPlaygroundRouteRoute = AppPlaygroundRouteImport.update({
id: '/playground',
path: '/playground',
getParentRoute: () => AppRoute,
} as any)
const AuthOidcCallbackRoute = AuthOidcCallbackImport.update({ const AuthOidcCallbackRoute = AuthOidcCallbackImport.update({
id: '/auth/oidc/callback', id: '/auth/oidc/callback',
path: '/auth/oidc/callback', path: '/auth/oidc/callback',
@@ -67,28 +95,30 @@ const AuthOidcCallbackRoute = AuthOidcCallbackImport.update({
} as any) } as any)
const AppSubscriptionsManageRoute = AppSubscriptionsManageImport.update({ const AppSubscriptionsManageRoute = AppSubscriptionsManageImport.update({
id: '/subscriptions/manage', id: '/manage',
path: '/subscriptions/manage', path: '/manage',
getParentRoute: () => AppRoute, getParentRoute: () => AppSubscriptionsRouteRoute,
} as any) } as any)
const AppSubscriptionsCreateRoute = AppSubscriptionsCreateImport.update({ const AppSubscriptionsCreateRoute = AppSubscriptionsCreateImport.update({
id: '/subscriptions/create', id: '/create',
path: '/subscriptions/create', path: '/create',
getParentRoute: () => AppRoute, getParentRoute: () => AppSubscriptionsRouteRoute,
} as any) } as any)
const AppPlaygroundGraphqlApiRoute = AppPlaygroundGraphqlApiImport.update({ const AppPlaygroundGraphqlApiRoute = AppPlaygroundGraphqlApiImport.update({
id: '/playground/graphql-api', id: '/graphql-api',
path: '/playground/graphql-api', path: '/graphql-api',
getParentRoute: () => AppRoute, getParentRoute: () => AppPlaygroundRouteRoute,
} as any) } as any).lazy(() =>
import('./routes/_app/playground/graphql-api.lazy').then((d) => d.Route),
)
const AppSubscriptionsEditSubscriptionIdRoute = const AppSubscriptionsEditSubscriptionIdRoute =
AppSubscriptionsEditSubscriptionIdImport.update({ AppSubscriptionsEditSubscriptionIdImport.update({
id: '/subscriptions/edit/$subscription-id', id: '/edit/$subscription-id',
path: '/subscriptions/edit/$subscription-id', path: '/edit/$subscription-id',
getParentRoute: () => AppRoute, getParentRoute: () => AppSubscriptionsRouteRoute,
} as any) } as any)
// Populate the FileRoutesByPath interface // Populate the FileRoutesByPath interface
@@ -102,6 +132,13 @@ declare module '@tanstack/solid-router' {
preLoaderRoute: typeof IndexImport preLoaderRoute: typeof IndexImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
'/404': {
id: '/404'
path: '/404'
fullPath: '/404'
preLoaderRoute: typeof R404Import
parentRoute: typeof rootRoute
}
'/_app': { '/_app': {
id: '/_app' id: '/_app'
path: '' path: ''
@@ -116,6 +153,27 @@ declare module '@tanstack/solid-router' {
preLoaderRoute: typeof AboutImport preLoaderRoute: typeof AboutImport
parentRoute: typeof rootRoute parentRoute: typeof rootRoute
} }
'/_app/playground': {
id: '/_app/playground'
path: '/playground'
fullPath: '/playground'
preLoaderRoute: typeof AppPlaygroundRouteImport
parentRoute: typeof AppImport
}
'/_app/settings': {
id: '/_app/settings'
path: '/settings'
fullPath: '/settings'
preLoaderRoute: typeof AppSettingsRouteImport
parentRoute: typeof AppImport
}
'/_app/subscriptions': {
id: '/_app/subscriptions'
path: '/subscriptions'
fullPath: '/subscriptions'
preLoaderRoute: typeof AppSubscriptionsRouteImport
parentRoute: typeof AppImport
}
'/_app/explore': { '/_app/explore': {
id: '/_app/explore' id: '/_app/explore'
path: '/explore' path: '/explore'
@@ -139,24 +197,24 @@ declare module '@tanstack/solid-router' {
} }
'/_app/playground/graphql-api': { '/_app/playground/graphql-api': {
id: '/_app/playground/graphql-api' id: '/_app/playground/graphql-api'
path: '/playground/graphql-api' path: '/graphql-api'
fullPath: '/playground/graphql-api' fullPath: '/playground/graphql-api'
preLoaderRoute: typeof AppPlaygroundGraphqlApiImport preLoaderRoute: typeof AppPlaygroundGraphqlApiImport
parentRoute: typeof AppImport parentRoute: typeof AppPlaygroundRouteImport
} }
'/_app/subscriptions/create': { '/_app/subscriptions/create': {
id: '/_app/subscriptions/create' id: '/_app/subscriptions/create'
path: '/subscriptions/create' path: '/create'
fullPath: '/subscriptions/create' fullPath: '/subscriptions/create'
preLoaderRoute: typeof AppSubscriptionsCreateImport preLoaderRoute: typeof AppSubscriptionsCreateImport
parentRoute: typeof AppImport parentRoute: typeof AppSubscriptionsRouteImport
} }
'/_app/subscriptions/manage': { '/_app/subscriptions/manage': {
id: '/_app/subscriptions/manage' id: '/_app/subscriptions/manage'
path: '/subscriptions/manage' path: '/manage'
fullPath: '/subscriptions/manage' fullPath: '/subscriptions/manage'
preLoaderRoute: typeof AppSubscriptionsManageImport preLoaderRoute: typeof AppSubscriptionsManageImport
parentRoute: typeof AppImport parentRoute: typeof AppSubscriptionsRouteImport
} }
'/auth/oidc/callback': { '/auth/oidc/callback': {
id: '/auth/oidc/callback' id: '/auth/oidc/callback'
@@ -167,39 +225,69 @@ declare module '@tanstack/solid-router' {
} }
'/_app/subscriptions/edit/$subscription-id': { '/_app/subscriptions/edit/$subscription-id': {
id: '/_app/subscriptions/edit/$subscription-id' id: '/_app/subscriptions/edit/$subscription-id'
path: '/subscriptions/edit/$subscription-id' path: '/edit/$subscription-id'
fullPath: '/subscriptions/edit/$subscription-id' fullPath: '/subscriptions/edit/$subscription-id'
preLoaderRoute: typeof AppSubscriptionsEditSubscriptionIdImport preLoaderRoute: typeof AppSubscriptionsEditSubscriptionIdImport
parentRoute: typeof AppImport parentRoute: typeof AppSubscriptionsRouteImport
} }
} }
} }
// Create and export the route tree // Create and export the route tree
interface AppRouteChildren { interface AppPlaygroundRouteRouteChildren {
AppExploreRoute: typeof AppExploreRoute
AppPlaygroundGraphqlApiRoute: typeof AppPlaygroundGraphqlApiRoute AppPlaygroundGraphqlApiRoute: typeof AppPlaygroundGraphqlApiRoute
}
const AppPlaygroundRouteRouteChildren: AppPlaygroundRouteRouteChildren = {
AppPlaygroundGraphqlApiRoute: AppPlaygroundGraphqlApiRoute,
}
const AppPlaygroundRouteRouteWithChildren =
AppPlaygroundRouteRoute._addFileChildren(AppPlaygroundRouteRouteChildren)
interface AppSubscriptionsRouteRouteChildren {
AppSubscriptionsCreateRoute: typeof AppSubscriptionsCreateRoute AppSubscriptionsCreateRoute: typeof AppSubscriptionsCreateRoute
AppSubscriptionsManageRoute: typeof AppSubscriptionsManageRoute AppSubscriptionsManageRoute: typeof AppSubscriptionsManageRoute
AppSubscriptionsEditSubscriptionIdRoute: typeof AppSubscriptionsEditSubscriptionIdRoute AppSubscriptionsEditSubscriptionIdRoute: typeof AppSubscriptionsEditSubscriptionIdRoute
} }
const AppRouteChildren: AppRouteChildren = { const AppSubscriptionsRouteRouteChildren: AppSubscriptionsRouteRouteChildren = {
AppExploreRoute: AppExploreRoute,
AppPlaygroundGraphqlApiRoute: AppPlaygroundGraphqlApiRoute,
AppSubscriptionsCreateRoute: AppSubscriptionsCreateRoute, AppSubscriptionsCreateRoute: AppSubscriptionsCreateRoute,
AppSubscriptionsManageRoute: AppSubscriptionsManageRoute, AppSubscriptionsManageRoute: AppSubscriptionsManageRoute,
AppSubscriptionsEditSubscriptionIdRoute: AppSubscriptionsEditSubscriptionIdRoute:
AppSubscriptionsEditSubscriptionIdRoute, AppSubscriptionsEditSubscriptionIdRoute,
} }
const AppSubscriptionsRouteRouteWithChildren =
AppSubscriptionsRouteRoute._addFileChildren(
AppSubscriptionsRouteRouteChildren,
)
interface AppRouteChildren {
AppPlaygroundRouteRoute: typeof AppPlaygroundRouteRouteWithChildren
AppSettingsRouteRoute: typeof AppSettingsRouteRoute
AppSubscriptionsRouteRoute: typeof AppSubscriptionsRouteRouteWithChildren
AppExploreRoute: typeof AppExploreRoute
}
const AppRouteChildren: AppRouteChildren = {
AppPlaygroundRouteRoute: AppPlaygroundRouteRouteWithChildren,
AppSettingsRouteRoute: AppSettingsRouteRoute,
AppSubscriptionsRouteRoute: AppSubscriptionsRouteRouteWithChildren,
AppExploreRoute: AppExploreRoute,
}
const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren) const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren)
export interface FileRoutesByFullPath { export interface FileRoutesByFullPath {
'/': typeof IndexRoute '/': typeof IndexRoute
'/404': typeof R404Route
'': typeof AppRouteWithChildren '': typeof AppRouteWithChildren
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/playground': typeof AppPlaygroundRouteRouteWithChildren
'/settings': typeof AppSettingsRouteRoute
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren
'/explore': typeof AppExploreRoute '/explore': typeof AppExploreRoute
'/auth/sign-in': typeof AuthSignInRoute '/auth/sign-in': typeof AuthSignInRoute
'/auth/sign-up': typeof AuthSignUpRoute '/auth/sign-up': typeof AuthSignUpRoute
@@ -212,8 +300,12 @@ export interface FileRoutesByFullPath {
export interface FileRoutesByTo { export interface FileRoutesByTo {
'/': typeof IndexRoute '/': typeof IndexRoute
'/404': typeof R404Route
'': typeof AppRouteWithChildren '': typeof AppRouteWithChildren
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/playground': typeof AppPlaygroundRouteRouteWithChildren
'/settings': typeof AppSettingsRouteRoute
'/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren
'/explore': typeof AppExploreRoute '/explore': typeof AppExploreRoute
'/auth/sign-in': typeof AuthSignInRoute '/auth/sign-in': typeof AuthSignInRoute
'/auth/sign-up': typeof AuthSignUpRoute '/auth/sign-up': typeof AuthSignUpRoute
@@ -227,8 +319,12 @@ export interface FileRoutesByTo {
export interface FileRoutesById { export interface FileRoutesById {
__root__: typeof rootRoute __root__: typeof rootRoute
'/': typeof IndexRoute '/': typeof IndexRoute
'/404': typeof R404Route
'/_app': typeof AppRouteWithChildren '/_app': typeof AppRouteWithChildren
'/about': typeof AboutRoute '/about': typeof AboutRoute
'/_app/playground': typeof AppPlaygroundRouteRouteWithChildren
'/_app/settings': typeof AppSettingsRouteRoute
'/_app/subscriptions': typeof AppSubscriptionsRouteRouteWithChildren
'/_app/explore': typeof AppExploreRoute '/_app/explore': typeof AppExploreRoute
'/auth/sign-in': typeof AuthSignInRoute '/auth/sign-in': typeof AuthSignInRoute
'/auth/sign-up': typeof AuthSignUpRoute '/auth/sign-up': typeof AuthSignUpRoute
@@ -243,8 +339,12 @@ export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: fullPaths:
| '/' | '/'
| '/404'
| '' | ''
| '/about' | '/about'
| '/playground'
| '/settings'
| '/subscriptions'
| '/explore' | '/explore'
| '/auth/sign-in' | '/auth/sign-in'
| '/auth/sign-up' | '/auth/sign-up'
@@ -256,8 +356,12 @@ export interface FileRouteTypes {
fileRoutesByTo: FileRoutesByTo fileRoutesByTo: FileRoutesByTo
to: to:
| '/' | '/'
| '/404'
| '' | ''
| '/about' | '/about'
| '/playground'
| '/settings'
| '/subscriptions'
| '/explore' | '/explore'
| '/auth/sign-in' | '/auth/sign-in'
| '/auth/sign-up' | '/auth/sign-up'
@@ -269,8 +373,12 @@ export interface FileRouteTypes {
id: id:
| '__root__' | '__root__'
| '/' | '/'
| '/404'
| '/_app' | '/_app'
| '/about' | '/about'
| '/_app/playground'
| '/_app/settings'
| '/_app/subscriptions'
| '/_app/explore' | '/_app/explore'
| '/auth/sign-in' | '/auth/sign-in'
| '/auth/sign-up' | '/auth/sign-up'
@@ -284,6 +392,7 @@ export interface FileRouteTypes {
export interface RootRouteChildren { export interface RootRouteChildren {
IndexRoute: typeof IndexRoute IndexRoute: typeof IndexRoute
R404Route: typeof R404Route
AppRoute: typeof AppRouteWithChildren AppRoute: typeof AppRouteWithChildren
AboutRoute: typeof AboutRoute AboutRoute: typeof AboutRoute
AuthSignInRoute: typeof AuthSignInRoute AuthSignInRoute: typeof AuthSignInRoute
@@ -293,6 +402,7 @@ export interface RootRouteChildren {
const rootRouteChildren: RootRouteChildren = { const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute, IndexRoute: IndexRoute,
R404Route: R404Route,
AppRoute: AppRouteWithChildren, AppRoute: AppRouteWithChildren,
AboutRoute: AboutRoute, AboutRoute: AboutRoute,
AuthSignInRoute: AuthSignInRoute, AuthSignInRoute: AuthSignInRoute,
@@ -311,6 +421,7 @@ export const routeTree = rootRoute
"filePath": "__root.tsx", "filePath": "__root.tsx",
"children": [ "children": [
"/", "/",
"/404",
"/_app", "/_app",
"/about", "/about",
"/auth/sign-in", "/auth/sign-in",
@@ -321,19 +432,41 @@ export const routeTree = rootRoute
"/": { "/": {
"filePath": "index.tsx" "filePath": "index.tsx"
}, },
"/404": {
"filePath": "404.tsx"
},
"/_app": { "/_app": {
"filePath": "_app.tsx", "filePath": "_app.tsx",
"children": [ "children": [
"/_app/explore", "/_app/playground",
"/_app/playground/graphql-api", "/_app/settings",
"/_app/subscriptions/create", "/_app/subscriptions",
"/_app/subscriptions/manage", "/_app/explore"
"/_app/subscriptions/edit/$subscription-id"
] ]
}, },
"/about": { "/about": {
"filePath": "about.tsx" "filePath": "about.tsx"
}, },
"/_app/playground": {
"filePath": "_app/playground/route.tsx",
"parent": "/_app",
"children": [
"/_app/playground/graphql-api"
]
},
"/_app/settings": {
"filePath": "_app/settings/route.tsx",
"parent": "/_app"
},
"/_app/subscriptions": {
"filePath": "_app/subscriptions/route.tsx",
"parent": "/_app",
"children": [
"/_app/subscriptions/create",
"/_app/subscriptions/manage",
"/_app/subscriptions/edit/$subscription-id"
]
},
"/_app/explore": { "/_app/explore": {
"filePath": "_app/explore.tsx", "filePath": "_app/explore.tsx",
"parent": "/_app" "parent": "/_app"
@@ -346,22 +479,22 @@ export const routeTree = rootRoute
}, },
"/_app/playground/graphql-api": { "/_app/playground/graphql-api": {
"filePath": "_app/playground/graphql-api.tsx", "filePath": "_app/playground/graphql-api.tsx",
"parent": "/_app" "parent": "/_app/playground"
}, },
"/_app/subscriptions/create": { "/_app/subscriptions/create": {
"filePath": "_app/subscriptions/create.tsx", "filePath": "_app/subscriptions/create.tsx",
"parent": "/_app" "parent": "/_app/subscriptions"
}, },
"/_app/subscriptions/manage": { "/_app/subscriptions/manage": {
"filePath": "_app/subscriptions/manage.tsx", "filePath": "_app/subscriptions/manage.tsx",
"parent": "/_app" "parent": "/_app/subscriptions"
}, },
"/auth/oidc/callback": { "/auth/oidc/callback": {
"filePath": "auth/oidc/callback.tsx" "filePath": "auth/oidc/callback.tsx"
}, },
"/_app/subscriptions/edit/$subscription-id": { "/_app/subscriptions/edit/$subscription-id": {
"filePath": "_app/subscriptions/edit/$subscription-id.tsx", "filePath": "_app/subscriptions/edit.$subscription-id.tsx",
"parent": "/_app" "parent": "/_app/subscriptions"
} }
} }
} }

View File

@@ -0,0 +1,6 @@
import { createFileRoute } from '@tanstack/solid-router';
import { AppNotFoundComponent } from '~/components/layout/app-not-found';
export const Route = createFileRoute('/404')({
component: AppNotFoundComponent,
});

View File

@@ -1,8 +1,12 @@
import { Outlet, createRootRouteWithContext } from '@tanstack/solid-router'; import { Outlet, createRootRouteWithContext } from '@tanstack/solid-router';
import type { RouterContext } from '../auth/context'; import { Home } from 'lucide-solid';
import type { RouteStateDataOption, RouterContext } from '~/traits/router';
export const Route = createRootRouteWithContext<RouterContext>()({ export const Route = createRootRouteWithContext<RouterContext>()({
component: () => { component: Outlet,
return <Outlet />; staticData: {
}, breadcrumb: {
icon: Home,
},
} satisfies RouteStateDataOption,
}); });

View File

@@ -1,8 +1,16 @@
import { createFileRoute } from '@tanstack/solid-router'; import { Outlet, createFileRoute } from '@tanstack/solid-router';
import { beforeLoadGuard } from '~/auth/guard'; import { beforeLoadGuard } from '~/auth/guard';
import { AppLayout } from '~/components/layout/app-layout'; import { AppAside } from '~/components/layout/app-layout';
export const Route = createFileRoute('/_app')({ export const Route = createFileRoute('/_app')({
component: AppLayout, component: AppLayoutRoute,
beforeLoad: beforeLoadGuard, beforeLoad: beforeLoadGuard,
}); });
function AppLayoutRoute() {
return (
<AppAside extractBreadcrumbFromRoutes>
<Outlet />
</AppAside>
);
}

View File

@@ -1,9 +1,15 @@
import { createFileRoute } from '@tanstack/solid-router'; import { createFileRoute } from '@tanstack/solid-router';
import type { RouteStateDataOption } from '~/traits/router';
export const Route = createFileRoute('/_app/explore')({ export const Route = createFileRoute('/_app/explore')({
component: RouteComponent, component: ExploreRouteComponent,
staticData: {
breadcrumb: {
label: 'Explore',
},
} satisfies RouteStateDataOption,
}); });
function RouteComponent() { function ExploreRouteComponent() {
return <div>Hello "/_app/explore"!</div>; return <div>Hello "/_app/explore"!</div>;
} }

View File

@@ -0,0 +1,54 @@
import { type Fetcher, createGraphiQLFetcher } from '@graphiql/toolkit';
import { createLazyFileRoute } from '@tanstack/solid-router';
import GraphiQL from 'graphiql';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { onCleanup, onMount } from 'solid-js';
import { isOidcAuth } from '~/auth/config';
import { useAuth } from '~/auth/hooks';
import 'graphiql/graphiql.css';
import { firstValueFrom } from 'rxjs';
export const Route = createLazyFileRoute('/_app/playground/graphql-api')({
component: PlaygroundGraphQLApiRouteComponent,
});
function PlaygroundGraphQLApiRouteComponent() {
const auth = useAuth();
let containerRef: HTMLDivElement | undefined;
onMount(() => {
if (containerRef) {
const reactRoot = createRoot(containerRef);
if (reactRoot) {
const fetcher: Fetcher = async (props) => {
const accessToken = isOidcAuth
? await firstValueFrom(auth.oidcSecurityService!.getAccessToken())
: undefined;
return createGraphiQLFetcher({
url: '/api/graphql',
headers: accessToken
? {
Authorization: `Bearer ${accessToken}`,
}
: undefined,
})(props);
};
const g = React.createElement(GraphiQL, {
fetcher,
});
reactRoot.render(g);
onCleanup(() => reactRoot.unmount());
}
}
});
return (
<div
data-id="graphiql-playground-container"
class="h-full"
ref={containerRef}
/>
);
}

View File

@@ -1,9 +1,6 @@
import { createFileRoute } from '@tanstack/solid-router' import { createFileRoute } from '@tanstack/solid-router';
import { buildLeafRouteStaticData } from '~/utils/route';
export const Route = createFileRoute('/_app/playground/graphql-api')({ export const Route = createFileRoute('/_app/playground/graphql-api')({
component: RouteComponent, staticData: buildLeafRouteStaticData({ title: 'GraphQL Api' }),
}) });
function RouteComponent() {
return <div>Hello "/_app/playground/group-api"!</div>
}

View File

@@ -0,0 +1,8 @@
import { createFileRoute } from '@tanstack/solid-router';
import { buildVirtualBranchRouteOptions } from '~/utils/route';
export const Route = createFileRoute('/_app/playground')(
buildVirtualBranchRouteOptions({
title: 'Playground',
})
);

View File

@@ -0,0 +1,8 @@
import { createFileRoute } from '@tanstack/solid-router';
import { buildVirtualBranchRouteOptions } from '~/utils/route';
export const Route = createFileRoute('/_app/settings')(
buildVirtualBranchRouteOptions({
title: 'Settings',
})
);

View File

@@ -1,9 +1,13 @@
import { createFileRoute } from '@tanstack/solid-router'; import { createFileRoute } from '@tanstack/solid-router';
import type { RouteStateDataOption } from '~/traits/router';
export const Route = createFileRoute('/_app/subscriptions/create')({ export const Route = createFileRoute('/_app/subscriptions/create')({
component: RouteComponent, component: SubscriptionCreateRouteComponent,
staticData: {
breadcrumb: { label: 'Create' },
} satisfies RouteStateDataOption,
}); });
function RouteComponent() { function SubscriptionCreateRouteComponent() {
return <div>Hello "/subscriptions/create"!</div>; return <div>Hello "/subscriptions/create"!</div>;
} }

View File

@@ -0,0 +1,15 @@
import { createFileRoute } from '@tanstack/solid-router';
import type { RouteStateDataOption } from '~/traits/router';
export const Route = createFileRoute(
'/_app/subscriptions/edit/$subscription-id'
)({
component: RouteComponent,
staticData: {
breadcrumb: { label: 'Edit' },
} satisfies RouteStateDataOption,
});
function RouteComponent() {
return <div>Hello "/subscriptions/edit/$subscription-id"!</div>;
}

View File

@@ -1,9 +0,0 @@
import { createFileRoute } from '@tanstack/solid-router'
export const Route = createFileRoute('/_app/subscriptions/edit/$subscription-id')({
component: RouteComponent,
})
function RouteComponent() {
return <div>Hello "/subscriptions/edit/$subscription-id"!</div>
}

View File

@@ -1,9 +1,13 @@
import { createFileRoute } from '@tanstack/solid-router'; import { createFileRoute } from '@tanstack/solid-router';
import type { RouteStateDataOption } from '~/traits/router';
export const Route = createFileRoute('/_app/subscriptions/manage')({ export const Route = createFileRoute('/_app/subscriptions/manage')({
component: SubscriptionDashboard, component: SubscriptionManage,
staticData: {
breadcrumb: { label: 'Manage' },
} satisfies RouteStateDataOption,
}); });
function SubscriptionDashboard() { function SubscriptionManage() {
return <div>Hello "/subscriptions/manage"!</div>; return <div>Hello "/subscriptions/manage"!</div>;
} }

View File

@@ -0,0 +1,8 @@
import { createFileRoute } from '@tanstack/solid-router';
import { buildVirtualBranchRouteOptions } from '~/utils/route';
export const Route = createFileRoute('/_app/subscriptions')(
buildVirtualBranchRouteOptions({
title: 'Subscriptions',
})
);

View File

@@ -1,57 +1,15 @@
import { createFileRoute } from '@tanstack/solid-router'; import { createFileRoute } from '@tanstack/solid-router';
import { AppSidebar } from '~/components/layout/app-sidebar'; import { AppAside } from '~/components/layout/app-layout';
import { import { AppSkeleton } from '~/components/layout/app-skeleton';
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
} from '~/components/ui/breadcrumb';
import { Separator } from '~/components/ui/separator';
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from '~/components/ui/sidebar';
export const Route = createFileRoute('/')({ export const Route = createFileRoute('/')({
component: Home, component: HomeRouteComponent,
beforeLoad: async () => {},
}); });
function Home() { function HomeRouteComponent() {
return ( return (
<SidebarProvider> <AppAside class="flex flex-1 flex-col gap-4" extractBreadcrumbFromRoutes>
<AppSidebar /> <AppSkeleton />
<SidebarInset> </AppAside>
<header class="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div class="flex items-center gap-2 px-4">
<SidebarTrigger class="-ml-1" />
<Separator orientation="vertical" class="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem class="hidden md:block">
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator class="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbLink current>Data Fetching</BreadcrumbLink>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div class="flex flex-1 flex-col gap-4 p-4 pt-0">
<div class="grid auto-rows-min gap-4 md:grid-cols-3">
<div class="aspect-video rounded-xl bg-muted/50" />
<div class="aspect-video rounded-xl bg-muted/50" />
<div class="aspect-video rounded-xl bg-muted/50" />
</div>
<div class="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min" />
</div>
</SidebarInset>
</SidebarProvider>
); );
} }

View File

@@ -0,0 +1,21 @@
import type { Injector } from '@outposts/injection-js';
import type { LucideIcon } from 'lucide-solid';
import type { OidcSecurityService } from 'oidc-client-rx';
import type { Accessor } from 'solid-js';
import type { ProLinkProps } from '~/components/ui/pro-link';
export type RouterContext = {
isAuthenticated: Accessor<boolean>;
injector: Injector;
oidcSecurityService: OidcSecurityService;
};
export type RouteBreadcrumbItem = {
label?: string;
icon?: LucideIcon;
link?: Omit<ProLinkProps, 'aria-current' | 'current'>;
};
export interface RouteStateDataOption {
breadcrumb?: RouteBreadcrumbItem;
}

View File

@@ -0,0 +1,36 @@
import { FileRoutesByPath, Outlet } from '@tanstack/solid-router';
import { guardRouteIndexAsNotFound } from '~/components/layout/app-not-found';
import type { RouteStateDataOption } from '~/traits/router';
export interface BuildVirtualBranchRouteOptions {
title: string;
}
export function buildVirtualBranchRouteOptions(
options: BuildVirtualBranchRouteOptions
) {
return {
beforeLoad: guardRouteIndexAsNotFound,
staticData: {
breadcrumb: {
label: options.title,
link: undefined,
},
} satisfies RouteStateDataOption,
component: Outlet,
};
}
export interface BuildLeafRouteStaticDataOptions {
title: string;
}
export function buildLeafRouteStaticData(
options: BuildLeafRouteStaticDataOptions
): RouteStateDataOption {
return {
breadcrumb: {
label: options.title,
},
};
}

View File

@@ -60,7 +60,17 @@ module.exports = {
card: { card: {
DEFAULT: "hsl(var(--card))", DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))" foreground: "hsl(var(--card-foreground))"
} },
sidebar: {
DEFAULT: 'hsl(var(--sidebar-background))',
foreground: 'hsl(var(--sidebar-foreground))',
primary: 'hsl(var(--sidebar-primary))',
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
accent: 'hsl(var(--sidebar-accent))',
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
border: 'hsl(var(--sidebar-border))',
ring: 'hsl(var(--sidebar-ring))',
},
}, },
borderRadius: { borderRadius: {
xl: "calc(var(--radius) + 4px)", xl: "calc(var(--radius) + 4px)",

View File

@@ -8,6 +8,6 @@
}, },
"aliases": { "aliases": {
"components": "~/components/ui", "components": "~/components/ui",
"utils": "~/styles/utils" "utils": "~/utils/styles"
} }
} }

View File

@@ -14,8 +14,3 @@ dev-proxy:
dev-recorder: dev-recorder:
bacon recorder bacon recorder
dev-playground:
pnpm run --filter=recorder dev
# play-recorder:
# cargo recorder-playground

View File

@@ -3,21 +3,14 @@
"version": "0.0.0", "version": "0.0.0",
"description": "Kono bangumi?", "description": "Kono bangumi?",
"license": "MIT", "license": "MIT",
"workspaces": [ "workspaces": ["packages/*", "apps/*"],
"packages/*",
"apps/*"
],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/dumtruck/konobangu.git" "url": "https://github.com/dumtruck/konobangu.git"
}, },
"scripts": { "scripts": {
"build": "turbo build",
"dev": "turbo dev",
"lint": "ultracite lint", "lint": "ultracite lint",
"format": "ultracite format", "format": "ultracite format",
"test": "turbo test",
"analyze": "turbo analyze",
"bump-deps": "npx --yes npm-check-updates --deep -u -x react-day-picker && pnpm install", "bump-deps": "npx --yes npm-check-updates --deep -u -x react-day-picker && pnpm install",
"clean": "git clean -xdf node_modules" "clean": "git clean -xdf node_modules"
}, },
@@ -25,34 +18,11 @@
"engines": { "engines": {
"node": ">=22" "node": ">=22"
}, },
"dependencies": {
"@abraham/reflection": "^0.12.0",
"@outposts/injection-js": "^2.5.1",
"@tanstack/react-router": "^1.112.0",
"@tanstack/router-devtools": "^1.112.6",
"arktype": "^2.1.2",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"oidc-client-rx": "0.1.0-alpha.8",
"rxjs": "^7.8.2",
"tailwind-merge": "^3.0.2",
"tailwindcss": "^4.0.9",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": { "devDependencies": {
"@auto-it/all-contributors": "^11.3.0", "@auto-it/all-contributors": "^11.3.0",
"@auto-it/first-time-contributor": "^11.3.0", "@auto-it/first-time-contributor": "^11.3.0",
"@biomejs/biome": "1.9.4", "@biomejs/biome": "1.9.4",
"@rsbuild/core": "^1.2.14",
"@rsbuild/plugin-babel": "^1.0.4",
"@tailwindcss/postcss": "^4.0.9",
"@tanstack/router-devtools": "^1.112.6",
"@tanstack/router-plugin": "^1.112.3",
"@types/jsdom": "^21.1.7",
"@types/node": "^22.13.8", "@types/node": "^22.13.8",
"chalk": "^5.4.1",
"commander": "^13.1.0",
"postcss": "^8.5.3",
"shx": "^0.3.4", "shx": "^0.3.4",
"tsx": "^4.19.2", "tsx": "^4.19.2",
"typescript": "^5.8.2", "typescript": "^5.8.2",

1023
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,9 +4,6 @@
{ {
"path": "./apps/email-playground" "path": "./apps/email-playground"
}, },
{
"path": "./apps/recorder"
},
{ {
"path": "./apps/webui" "path": "./apps/webui"
}, },