diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b967656..07c8bb0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -84,6 +84,9 @@ importers: '@monaco-editor/react': specifier: ^4.6.0 version: 4.6.0(monaco-editor@0.46.0)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-avatar': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-dialog': specifier: ^1.0.5 version: 1.0.5(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) @@ -93,9 +96,18 @@ importers: '@radix-ui/react-icons': specifier: ^1.3.0 version: 1.3.0(react@18.2.0) + '@radix-ui/react-menubar': + specifier: ^1.0.4 + version: 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) '@radix-ui/react-select': specifier: ^2.0.0 version: 2.0.0(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-separator': + specifier: ^1.0.3 + version: 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-slot': + specifier: ^1.0.2 + version: 1.0.2(@types/react@18.2.21)(react@18.2.0) '@radix-ui/react-tooltip': specifier: ^1.0.7 version: 1.0.7(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) @@ -117,6 +129,9 @@ importers: '@trpc/react-query': specifier: ^10.45.0 version: 10.45.0(@tanstack/react-query@4.33.0)(@trpc/client@10.45.0)(@trpc/server@10.45.0)(react-dom@18.2.0)(react@18.2.0) + ahooks: + specifier: ^3.7.10 + version: 3.7.10(react@18.2.0) antd: specifier: ^5.13.1 version: 5.13.1(react-dom@18.2.0)(react@18.2.0) @@ -156,6 +171,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.17.21 + lucide-react: + specifier: ^0.358.0 + version: 0.358.0(react@18.2.0) millify: specifier: ^6.1.0 version: 6.1.0 @@ -1505,7 +1523,7 @@ packages: '@antv/l7': 2.20.14 '@antv/l7-composite-layers': 0.16.4(@antv/l7@2.20.14) '@antv/l7-draw': 3.1.1(@antv/l7@2.20.14) - ahooks: 3.7.9(react@18.2.0) + ahooks: 3.7.10(react@18.2.0) classnames: 2.5.1 color: 4.2.3 lodash-es: 4.17.21 @@ -7145,6 +7163,30 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: false + /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.0 + '@radix-ui/react-context': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@types/react': 18.2.21 + '@types/react-dom': 18.2.7 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==} peerDependencies: @@ -7395,6 +7437,36 @@ packages: react-remove-scroll: 2.5.5(@types/react@18.2.21)(react@18.2.0) dev: false + /@radix-ui/react-menubar@1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-bHgUo9gayKZfaQcWSSLr++LyS0rgh+MvD89DE4fJ6TkGHvjHgPaBZf44hdka7ogOxIOdj9163J+5xL2Dn4qzzg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.0 + '@radix-ui/primitive': 1.0.1 + '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@radix-ui/react-context': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@radix-ui/react-direction': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@radix-ui/react-id': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@radix-ui/react-menu': 2.0.6(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.21)(react@18.2.0) + '@types/react': 18.2.21 + '@types/react-dom': 18.2.7 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==} peerDependencies: @@ -7559,6 +7631,27 @@ packages: react-remove-scroll: 2.5.5(@types/react@18.2.21)(react@18.2.0) dev: false + /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + dependencies: + '@babel/runtime': 7.24.0 + '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.7)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0) + '@types/react': 18.2.21 + '@types/react-dom': 18.2.7 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + dev: false + /@radix-ui/react-slot@1.0.2(@types/react@18.2.21)(react@18.2.0): resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==} peerDependencies: @@ -10324,8 +10417,8 @@ packages: clean-stack: 2.2.0 indent-string: 4.0.0 - /ahooks@3.7.9(react@18.2.0): - resolution: {integrity: sha512-1nuCnaBe/DvZD2QAZVGLLmu0vDi6jxbiAP3Ghkj6Ocqk9YSXI6ydwo2x5I3lXowZyM8MNJDnHFvIo0vJS1CuFw==} + /ahooks@3.7.10(react@18.2.0): + resolution: {integrity: sha512-/HLYif7sFA/5qSuWKrwvjDbf3bq+sdaMrUWS7XGCDRWdC2FrG/i+u5LZdakMYc6UIgJTMQ7tGiJCV7sdU4kSIw==} engines: {node: '>=8.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -10334,7 +10427,7 @@ packages: dayjs: 1.11.10 intersection-observer: 0.12.2 js-cookie: 2.2.1 - lodash-es: 4.17.21 + lodash: 4.17.21 react: 18.2.0 resize-observer-polyfill: 1.5.1 screenfull: 5.2.0 @@ -17095,6 +17188,14 @@ packages: resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} engines: {node: '>=12'} + /lucide-react@0.358.0(react@18.2.0): + resolution: {integrity: sha512-rBSptRjZTMBm24zsFhR6pK/NgbT18JegZGKcH4+1H3+UigMSRpeoWLtR/fAwMYwYnlJOZB+y8WpeHne9D6X6Kg==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 + dependencies: + react: 18.2.0 + dev: false + /macos-release@3.2.0: resolution: {integrity: sha512-fSErXALFNsnowREYZ49XCdOHF8wOPWuFOGQrAhP7x5J/BqQv+B02cNsTykGpDgRVx43EKg++6ANmTaGTtW+hUA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} diff --git a/src/client/App.tsx b/src/client/App.tsx index 550698f..3f5f2ab 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -16,10 +16,11 @@ import { WebsitePage } from './pages/Website'; import { useGlobalConfig } from './hooks/useConfig'; import { useInjectWebsiteScript } from './hooks/useInjectWebsiteScript'; import { ConfigProvider, theme } from 'antd'; -import clsx from 'clsx'; -import { useSettingsStore } from './store/settings'; +import { useColorSchema } from './store/settings'; import { StatusPage } from './pages/Status'; import { TelemetryPage } from './pages/Telemetry'; +import { LayoutV2 } from './pages/LayoutV2'; +import { isDev } from './utils/env'; export const AppRoutes: React.FC = React.memo(() => { const { info: userInfo } = useUserStore(); @@ -30,7 +31,7 @@ export const AppRoutes: React.FC = React.memo(() => { return ( {userInfo ? ( - }> + : }> } /> } /> } /> @@ -60,17 +61,12 @@ AppRoutes.displayName = 'AppRoutes'; export const App: React.FC = React.memo(() => { const rootRef = useRef(null); - const colorScheme = useSettingsStore((state) => state.colorScheme); + const colorScheme = useColorSchema(); const algorithm = colorScheme === 'dark' ? theme.darkAlgorithm : theme.defaultAlgorithm; return ( -
+
diff --git a/src/client/components.json b/src/client/components.json index 41ea085..cca83e5 100644 --- a/src/client/components.json +++ b/src/client/components.json @@ -6,8 +6,8 @@ "tailwind": { "config": "tailwind.config.ts", "css": "./index.css", - "baseColor": "slate", - "cssVariables": false, + "baseColor": "zinc", + "cssVariables": true, "prefix": "" }, "aliases": { diff --git a/src/client/components/CountryFlag.tsx b/src/client/components/CountryFlag.tsx new file mode 100644 index 0000000..cf445d5 --- /dev/null +++ b/src/client/components/CountryFlag.tsx @@ -0,0 +1,14 @@ +import React from 'react'; + +/** + * image is from discord + */ +export const CountryFlag: React.FC<{ code: string }> = React.memo((props) => { + return ( + + ); +}); +CountryFlag.displayName = 'CountryFlag'; diff --git a/src/client/components/LanguageSelector.tsx b/src/client/components/LanguageSelector.tsx index b05f4b3..f81f4e4 100644 --- a/src/client/components/LanguageSelector.tsx +++ b/src/client/components/LanguageSelector.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { setLanguage, useTranslation } from '@i18next-toolkit/react'; import { Button, Dropdown } from 'antd'; import { LuLanguages } from 'react-icons/lu'; +import { CountryFlag } from './CountryFlag'; export const LanguageSelector: React.FC = React.memo(() => { const { i18n } = useTranslation(); @@ -58,16 +59,3 @@ export const LanguageSelector: React.FC = React.memo(() => { ); }); LanguageSelector.displayName = 'LanguageSelector'; - -/** - * image is from discord - */ -export const CountryFlag: React.FC<{ code: string }> = React.memo((props) => { - return ( - - ); -}); -CountryFlag.displayName = 'CountryFlag'; diff --git a/src/client/components/WorkspaceSwitcher.tsx b/src/client/components/WorkspaceSwitcher.tsx new file mode 100644 index 0000000..d1263c5 --- /dev/null +++ b/src/client/components/WorkspaceSwitcher.tsx @@ -0,0 +1,56 @@ +import React from 'react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { cn } from '@/utils/style'; +import { useUserInfo } from '@/store/user'; +import { RiRocket2Fill } from 'react-icons/ri'; + +interface WorkspaceSwitcherProps { + isCollapsed: boolean; +} +export const WorkspaceSwitcher: React.FC = React.memo( + (props) => { + const userInfo = useUserInfo(); + + if (!userInfo) { + return null; + } + + return ( + + ); + } +); +WorkspaceSwitcher.displayName = 'WorkspaceSwitcher'; diff --git a/src/client/components/ui/avatar.tsx b/src/client/components/ui/avatar.tsx new file mode 100644 index 0000000..f52fd44 --- /dev/null +++ b/src/client/components/ui/avatar.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/utils/style" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/src/client/components/ui/button.tsx b/src/client/components/ui/button.tsx new file mode 100644 index 0000000..c4c225e --- /dev/null +++ b/src/client/components/ui/button.tsx @@ -0,0 +1,58 @@ +import * as React from 'react'; +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; + +import { cn } from '@/utils/style'; + +const buttonVariants = cva( + 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-zinc-950 disabled:pointer-events-none disabled:opacity-50 dark:focus-visible:ring-zinc-300', + { + variants: { + variant: { + default: + 'bg-zinc-900 text-zinc-50 shadow hover:bg-zinc-900/90 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/90', + destructive: + 'bg-red-500 text-zinc-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-zinc-50 dark:hover:bg-red-900/90', + outline: + 'border border-zinc-200 bg-white shadow-sm hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-50', + secondary: + 'bg-zinc-100 text-zinc-900 shadow-sm hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80', + ghost: + 'hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-50', + link: 'text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50', + }, + size: { + default: 'h-9 px-4 py-2', + sm: 'h-8 rounded-md px-3 text-xs', + lg: 'h-10 rounded-md px-8', + icon: 'h-9 w-9', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + } +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : 'button'; + return ( + + ); + } +); +Button.displayName = 'Button'; + +export { Button, buttonVariants }; diff --git a/src/client/components/ui/command.tsx b/src/client/components/ui/command.tsx index cc0314a..91b2295 100644 --- a/src/client/components/ui/command.tsx +++ b/src/client/components/ui/command.tsx @@ -1,10 +1,10 @@ -import * as React from "react" -import { type DialogProps } from "@radix-ui/react-dialog" -import { MagnifyingGlassIcon } from "@radix-ui/react-icons" -import { Command as CommandPrimitive } from "cmdk" +import * as React from 'react'; +import { type DialogProps } from '@radix-ui/react-dialog'; +import { MagnifyingGlassIcon } from '@radix-ui/react-icons'; +import { Command as CommandPrimitive } from 'cmdk'; -import { cn } from "@/utils/style" -import { Dialog, DialogContent } from "@/components/ui/dialog" +import { cn } from '@/utils/style'; +import { Dialog, DialogContent } from '@/components/ui/dialog'; const Command = React.forwardRef< React.ElementRef, @@ -13,13 +13,13 @@ const Command = React.forwardRef< -)) -Command.displayName = CommandPrimitive.displayName +)); +Command.displayName = CommandPrimitive.displayName; interface CommandDialogProps extends DialogProps {} @@ -27,13 +27,13 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => { return ( - + {children} - ) -} + ); +}; const CommandInput = React.forwardRef< React.ElementRef, @@ -44,15 +44,15 @@ const CommandInput = React.forwardRef<
-)) +)); -CommandInput.displayName = CommandPrimitive.Input.displayName +CommandInput.displayName = CommandPrimitive.Input.displayName; const CommandList = React.forwardRef< React.ElementRef, @@ -60,12 +60,12 @@ const CommandList = React.forwardRef< >(({ className, ...props }, ref) => ( -)) +)); -CommandList.displayName = CommandPrimitive.List.displayName +CommandList.displayName = CommandPrimitive.List.displayName; const CommandEmpty = React.forwardRef< React.ElementRef, @@ -76,9 +76,9 @@ const CommandEmpty = React.forwardRef< className="py-6 text-center text-sm" {...props} /> -)) +)); -CommandEmpty.displayName = CommandPrimitive.Empty.displayName +CommandEmpty.displayName = CommandPrimitive.Empty.displayName; const CommandGroup = React.forwardRef< React.ElementRef, @@ -87,14 +87,14 @@ const CommandGroup = React.forwardRef< -)) +)); -CommandGroup.displayName = CommandPrimitive.Group.displayName +CommandGroup.displayName = CommandPrimitive.Group.displayName; const CommandSeparator = React.forwardRef< React.ElementRef, @@ -102,11 +102,11 @@ const CommandSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -CommandSeparator.displayName = CommandPrimitive.Separator.displayName +)); +CommandSeparator.displayName = CommandPrimitive.Separator.displayName; const CommandItem = React.forwardRef< React.ElementRef, @@ -115,14 +115,14 @@ const CommandItem = React.forwardRef< -)) +)); -CommandItem.displayName = CommandPrimitive.Item.displayName +CommandItem.displayName = CommandPrimitive.Item.displayName; const CommandShortcut = ({ className, @@ -131,14 +131,14 @@ const CommandShortcut = ({ return ( - ) -} -CommandShortcut.displayName = "CommandShortcut" + ); +}; +CommandShortcut.displayName = 'CommandShortcut'; export { Command, @@ -150,4 +150,4 @@ export { CommandItem, CommandShortcut, CommandSeparator, -} +}; diff --git a/src/client/components/ui/dialog.tsx b/src/client/components/ui/dialog.tsx index 09e4b66..fbe4f91 100644 --- a/src/client/components/ui/dialog.tsx +++ b/src/client/components/ui/dialog.tsx @@ -1,16 +1,16 @@ -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { Cross2Icon } from "@radix-ui/react-icons" +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { Cross2Icon } from '@radix-ui/react-icons'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; -const Dialog = DialogPrimitive.Root +const Dialog = DialogPrimitive.Root; -const DialogTrigger = DialogPrimitive.Trigger +const DialogTrigger = DialogPrimitive.Trigger; -const DialogPortal = DialogPrimitive.Portal +const DialogPortal = DialogPrimitive.Portal; -const DialogClose = DialogPrimitive.Close +const DialogClose = DialogPrimitive.Close; const DialogOverlay = React.forwardRef< React.ElementRef, @@ -19,13 +19,13 @@ const DialogOverlay = React.forwardRef< -)) -DialogOverlay.displayName = DialogPrimitive.Overlay.displayName +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, @@ -36,20 +36,20 @@ const DialogContent = React.forwardRef< {children} - + Close -)) -DialogContent.displayName = DialogPrimitive.Content.displayName +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; const DialogHeader = ({ className, @@ -57,13 +57,13 @@ const DialogHeader = ({ }: React.HTMLAttributes) => (
-) -DialogHeader.displayName = "DialogHeader" +); +DialogHeader.displayName = 'DialogHeader'; const DialogFooter = ({ className, @@ -71,13 +71,13 @@ const DialogFooter = ({ }: React.HTMLAttributes) => (
-) -DialogFooter.displayName = "DialogFooter" +); +DialogFooter.displayName = 'DialogFooter'; const DialogTitle = React.forwardRef< React.ElementRef, @@ -86,13 +86,13 @@ const DialogTitle = React.forwardRef< -)) -DialogTitle.displayName = DialogPrimitive.Title.displayName +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; const DialogDescription = React.forwardRef< React.ElementRef, @@ -100,11 +100,11 @@ const DialogDescription = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -DialogDescription.displayName = DialogPrimitive.Description.displayName +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; export { Dialog, @@ -117,4 +117,4 @@ export { DialogFooter, DialogTitle, DialogDescription, -} +}; diff --git a/src/client/components/ui/dropdown-menu.tsx b/src/client/components/ui/dropdown-menu.tsx index ec9d707..cedf9a7 100644 --- a/src/client/components/ui/dropdown-menu.tsx +++ b/src/client/components/ui/dropdown-menu.tsx @@ -1,36 +1,36 @@ -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import * as React from 'react'; +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import { CheckIcon, ChevronRightIcon, DotFilledIcon, -} from "@radix-ui/react-icons" +} from '@radix-ui/react-icons'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; -const DropdownMenu = DropdownMenuPrimitive.Root +const DropdownMenu = DropdownMenuPrimitive.Root; -const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; -const DropdownMenuGroup = DropdownMenuPrimitive.Group +const DropdownMenuGroup = DropdownMenuPrimitive.Group; -const DropdownMenuPortal = DropdownMenuPrimitive.Portal +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; -const DropdownMenuSub = DropdownMenuPrimitive.Sub +const DropdownMenuSub = DropdownMenuPrimitive.Sub; -const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; const DropdownMenuSubTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, children, ...props }, ref) => ( -)) +)); DropdownMenuSubTrigger.displayName = - DropdownMenuPrimitive.SubTrigger.displayName + DropdownMenuPrimitive.SubTrigger.displayName; const DropdownMenuSubContent = React.forwardRef< React.ElementRef, @@ -49,14 +49,14 @@ const DropdownMenuSubContent = React.forwardRef< -)) +)); DropdownMenuSubContent.displayName = - DropdownMenuPrimitive.SubContent.displayName + DropdownMenuPrimitive.SubContent.displayName; const DropdownMenuContent = React.forwardRef< React.ElementRef, @@ -67,33 +67,33 @@ const DropdownMenuContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - "z-50 min-w-[8rem] overflow-hidden rounded-md border border-slate-200 bg-white p-1 text-slate-950 shadow-md dark:border-slate-800 dark:bg-slate-950 dark:text-slate-50", - "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", + 'z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-md dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50', + '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', className )} {...props} /> -)) -DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; const DropdownMenuItem = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( -)) -DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; const DropdownMenuCheckboxItem = React.forwardRef< React.ElementRef, @@ -102,7 +102,7 @@ const DropdownMenuCheckboxItem = React.forwardRef< {children} -)) +)); DropdownMenuCheckboxItem.displayName = - DropdownMenuPrimitive.CheckboxItem.displayName + DropdownMenuPrimitive.CheckboxItem.displayName; const DropdownMenuRadioItem = React.forwardRef< React.ElementRef, @@ -126,7 +126,7 @@ const DropdownMenuRadioItem = React.forwardRef< {children} -)) -DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; const DropdownMenuLabel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef & { - inset?: boolean + inset?: boolean; } >(({ className, inset, ...props }, ref) => ( -)) -DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; const DropdownMenuSeparator = React.forwardRef< React.ElementRef, @@ -165,11 +165,11 @@ const DropdownMenuSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; const DropdownMenuShortcut = ({ className, @@ -177,12 +177,12 @@ const DropdownMenuShortcut = ({ }: React.HTMLAttributes) => { return ( - ) -} -DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + ); +}; +DropdownMenuShortcut.displayName = 'DropdownMenuShortcut'; export { DropdownMenu, @@ -200,4 +200,4 @@ export { DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuRadioGroup, -} +}; diff --git a/src/client/components/ui/input.tsx b/src/client/components/ui/input.tsx index 3bee5f1..a359e75 100644 --- a/src/client/components/ui/input.tsx +++ b/src/client/components/ui/input.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; export interface InputProps extends React.InputHTMLAttributes {} @@ -11,15 +11,15 @@ const Input = React.forwardRef( - ) + ); } -) -Input.displayName = "Input" +); +Input.displayName = 'Input'; -export { Input } +export { Input }; diff --git a/src/client/components/ui/menubar.tsx b/src/client/components/ui/menubar.tsx new file mode 100644 index 0000000..0696d07 --- /dev/null +++ b/src/client/components/ui/menubar.tsx @@ -0,0 +1,238 @@ +import * as React from "react" +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons" +import * as MenubarPrimitive from "@radix-ui/react-menubar" + +import { cn } from "@/utils/style" + +const MenubarMenu = MenubarPrimitive.Menu + +const MenubarGroup = MenubarPrimitive.Group + +const MenubarPortal = MenubarPrimitive.Portal + +const MenubarSub = MenubarPrimitive.Sub + +const MenubarRadioGroup = MenubarPrimitive.RadioGroup + +const Menubar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Menubar.displayName = MenubarPrimitive.Root.displayName + +const MenubarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarTrigger.displayName = MenubarPrimitive.Trigger.displayName + +const MenubarSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +MenubarSubTrigger.displayName = MenubarPrimitive.SubTrigger.displayName + +const MenubarSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSubContent.displayName = MenubarPrimitive.SubContent.displayName + +const MenubarContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, align = "start", alignOffset = -4, sideOffset = 8, ...props }, + ref + ) => ( + + + + ) +) +MenubarContent.displayName = MenubarPrimitive.Content.displayName + +const MenubarItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarItem.displayName = MenubarPrimitive.Item.displayName + +const MenubarCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarCheckboxItem.displayName = MenubarPrimitive.CheckboxItem.displayName + +const MenubarRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +MenubarRadioItem.displayName = MenubarPrimitive.RadioItem.displayName + +const MenubarLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean + } +>(({ className, inset, ...props }, ref) => ( + +)) +MenubarLabel.displayName = MenubarPrimitive.Label.displayName + +const MenubarSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +MenubarSeparator.displayName = MenubarPrimitive.Separator.displayName + +const MenubarShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +MenubarShortcut.displayname = "MenubarShortcut" + +export { + Menubar, + MenubarMenu, + MenubarTrigger, + MenubarContent, + MenubarItem, + MenubarSeparator, + MenubarLabel, + MenubarCheckboxItem, + MenubarRadioGroup, + MenubarRadioItem, + MenubarPortal, + MenubarSubContent, + MenubarSubTrigger, + MenubarGroup, + MenubarSub, + MenubarShortcut, +} diff --git a/src/client/components/ui/resizable.tsx b/src/client/components/ui/resizable.tsx index 5c46647..4405ce2 100644 --- a/src/client/components/ui/resizable.tsx +++ b/src/client/components/ui/resizable.tsx @@ -1,7 +1,7 @@ -import { DragHandleDots2Icon } from "@radix-ui/react-icons" -import * as ResizablePrimitive from "react-resizable-panels" +import { DragHandleDots2Icon } from '@radix-ui/react-icons'; +import * as ResizablePrimitive from 'react-resizable-panels'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; const ResizablePanelGroup = ({ className, @@ -9,35 +9,35 @@ const ResizablePanelGroup = ({ }: React.ComponentProps) => ( -) +); -const ResizablePanel = ResizablePrimitive.Panel +const ResizablePanel = ResizablePrimitive.Panel; const ResizableHandle = ({ withHandle, className, ...props }: React.ComponentProps & { - withHandle?: boolean + withHandle?: boolean; }) => ( div]:rotate-90 dark:bg-slate-800 dark:focus-visible:ring-slate-300", + 'relative flex w-px items-center justify-center bg-zinc-200 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-zinc-950 focus-visible:ring-offset-1 data-[panel-group-direction=vertical]:h-px data-[panel-group-direction=vertical]:w-full data-[panel-group-direction=vertical]:after:left-0 data-[panel-group-direction=vertical]:after:h-1 data-[panel-group-direction=vertical]:after:w-full data-[panel-group-direction=vertical]:after:-translate-y-1/2 data-[panel-group-direction=vertical]:after:translate-x-0 [&[data-panel-group-direction=vertical]>div]:rotate-90 dark:bg-zinc-800 dark:focus-visible:ring-zinc-300', className )} {...props} > {withHandle && ( -
+
)} -) +); -export { ResizablePanelGroup, ResizablePanel, ResizableHandle } +export { ResizablePanelGroup, ResizablePanel, ResizableHandle }; diff --git a/src/client/components/ui/select.tsx b/src/client/components/ui/select.tsx index 9a6a578..b207875 100644 --- a/src/client/components/ui/select.tsx +++ b/src/client/components/ui/select.tsx @@ -1,19 +1,19 @@ -import * as React from "react" +import * as React from 'react'; import { CaretSortIcon, CheckIcon, ChevronDownIcon, ChevronUpIcon, -} from "@radix-ui/react-icons" -import * as SelectPrimitive from "@radix-ui/react-select" +} from '@radix-ui/react-icons'; +import * as SelectPrimitive from '@radix-ui/react-select'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; -const Select = SelectPrimitive.Root +const Select = SelectPrimitive.Root; -const SelectGroup = SelectPrimitive.Group +const SelectGroup = SelectPrimitive.Group; -const SelectValue = SelectPrimitive.Value +const SelectValue = SelectPrimitive.Value; const SelectTrigger = React.forwardRef< React.ElementRef, @@ -22,7 +22,7 @@ const SelectTrigger = React.forwardRef< span]:line-clamp-1 dark:border-slate-800 dark:ring-offset-slate-950 dark:placeholder:text-slate-400 dark:focus:ring-slate-300", + 'flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', className )} {...props} @@ -32,8 +32,8 @@ const SelectTrigger = React.forwardRef< -)) -SelectTrigger.displayName = SelectPrimitive.Trigger.displayName +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; const SelectScrollUpButton = React.forwardRef< React.ElementRef, @@ -42,15 +42,15 @@ const SelectScrollUpButton = React.forwardRef< -)) -SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; const SelectScrollDownButton = React.forwardRef< React.ElementRef, @@ -59,28 +59,28 @@ const SelectScrollDownButton = React.forwardRef< -)) +)); SelectScrollDownButton.displayName = - SelectPrimitive.ScrollDownButton.displayName + SelectPrimitive.ScrollDownButton.displayName; const SelectContent = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, children, position = "popper", ...props }, ref) => ( +>(({ className, children, position = 'popper', ...props }, ref) => ( {children} @@ -99,8 +99,8 @@ const SelectContent = React.forwardRef< -)) -SelectContent.displayName = SelectPrimitive.Content.displayName +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; const SelectLabel = React.forwardRef< React.ElementRef, @@ -108,11 +108,11 @@ const SelectLabel = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -SelectLabel.displayName = SelectPrimitive.Label.displayName +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; const SelectItem = React.forwardRef< React.ElementRef, @@ -121,7 +121,7 @@ const SelectItem = React.forwardRef< {children} -)) -SelectItem.displayName = SelectPrimitive.Item.displayName +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; const SelectSeparator = React.forwardRef< React.ElementRef, @@ -142,11 +142,11 @@ const SelectSeparator = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -SelectSeparator.displayName = SelectPrimitive.Separator.displayName +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; export { Select, @@ -159,4 +159,4 @@ export { SelectSeparator, SelectScrollUpButton, SelectScrollDownButton, -} +}; diff --git a/src/client/components/ui/separator.tsx b/src/client/components/ui/separator.tsx new file mode 100644 index 0000000..90824b6 --- /dev/null +++ b/src/client/components/ui/separator.tsx @@ -0,0 +1,29 @@ +import * as React from 'react'; +import * as SeparatorPrimitive from '@radix-ui/react-separator'; + +import { cn } from '@/utils/style'; + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = 'horizontal', decorative = true, ...props }, + ref + ) => ( + + ) +); +Separator.displayName = SeparatorPrimitive.Root.displayName; + +export { Separator }; diff --git a/src/client/components/ui/sonner.tsx b/src/client/components/ui/sonner.tsx index 3b3a2cb..44b7702 100644 --- a/src/client/components/ui/sonner.tsx +++ b/src/client/components/ui/sonner.tsx @@ -1,29 +1,30 @@ -import { useTheme } from "next-themes" -import { Toaster as Sonner } from "sonner" +import { useTheme } from 'next-themes'; +import { Toaster as Sonner } from 'sonner'; -type ToasterProps = React.ComponentProps +type ToasterProps = React.ComponentProps; const Toaster = ({ ...props }: ToasterProps) => { - const { theme = "system" } = useTheme() + const { theme = 'system' } = useTheme(); return ( - ) -} + ); +}; -export { Toaster } +export { Toaster }; diff --git a/src/client/components/ui/table.tsx b/src/client/components/ui/table.tsx index 48f3e11..5dabff1 100644 --- a/src/client/components/ui/table.tsx +++ b/src/client/components/ui/table.tsx @@ -1,6 +1,6 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; const Table = React.forwardRef< HTMLTableElement, @@ -9,20 +9,20 @@ const Table = React.forwardRef<
-)) -Table.displayName = "Table" +)); +Table.displayName = 'Table'; const TableHeader = React.forwardRef< HTMLTableSectionElement, React.HTMLAttributes >(({ className, ...props }, ref) => ( - -)) -TableHeader.displayName = "TableHeader" + +)); +TableHeader.displayName = 'TableHeader'; const TableBody = React.forwardRef< HTMLTableSectionElement, @@ -30,11 +30,11 @@ const TableBody = React.forwardRef< >(({ className, ...props }, ref) => ( -)) -TableBody.displayName = "TableBody" +)); +TableBody.displayName = 'TableBody'; const TableFooter = React.forwardRef< HTMLTableSectionElement, @@ -43,13 +43,13 @@ const TableFooter = React.forwardRef< tr]:last:border-b-0 dark:bg-slate-800/50", + 'border-t bg-zinc-100/50 font-medium [&>tr]:last:border-b-0 dark:bg-zinc-800/50', className )} {...props} /> -)) -TableFooter.displayName = "TableFooter" +)); +TableFooter.displayName = 'TableFooter'; const TableRow = React.forwardRef< HTMLTableRowElement, @@ -58,13 +58,13 @@ const TableRow = React.forwardRef< -)) -TableRow.displayName = "TableRow" +)); +TableRow.displayName = 'TableRow'; const TableHead = React.forwardRef< HTMLTableCellElement, @@ -73,13 +73,13 @@ const TableHead = React.forwardRef<
[role=checkbox]]:translate-y-[2px] dark:text-slate-400", + 'h-10 px-2 text-left align-middle font-medium text-zinc-500 [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px] dark:text-zinc-400', className )} {...props} /> -)) -TableHead.displayName = "TableHead" +)); +TableHead.displayName = 'TableHead'; const TableCell = React.forwardRef< HTMLTableCellElement, @@ -88,13 +88,13 @@ const TableCell = React.forwardRef< [role=checkbox]]:translate-y-[2px]", + 'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]', className )} {...props} /> -)) -TableCell.displayName = "TableCell" +)); +TableCell.displayName = 'TableCell'; const TableCaption = React.forwardRef< HTMLTableCaptionElement, @@ -102,11 +102,11 @@ const TableCaption = React.forwardRef< >(({ className, ...props }, ref) => (
-)) -TableCaption.displayName = "TableCaption" +)); +TableCaption.displayName = 'TableCaption'; export { Table, @@ -117,4 +117,4 @@ export { TableRow, TableCell, TableCaption, -} +}; diff --git a/src/client/components/ui/tooltip.tsx b/src/client/components/ui/tooltip.tsx index 72fe3e7..e01fd96 100644 --- a/src/client/components/ui/tooltip.tsx +++ b/src/client/components/ui/tooltip.tsx @@ -1,13 +1,13 @@ -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" +import * as React from 'react'; +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; -import { cn } from "@/utils/style" +import { cn } from '@/utils/style'; -const TooltipProvider = TooltipPrimitive.Provider +const TooltipProvider = TooltipPrimitive.Provider; -const Tooltip = TooltipPrimitive.Root +const Tooltip = TooltipPrimitive.Root; -const TooltipTrigger = TooltipPrimitive.Trigger +const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipContent = React.forwardRef< React.ElementRef, @@ -17,12 +17,12 @@ const TooltipContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - "z-50 overflow-hidden rounded-md bg-slate-900 px-3 py-1.5 text-xs text-slate-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 dark:bg-slate-50 dark:text-slate-900", + 'z-50 overflow-hidden rounded-md bg-zinc-900 px-3 py-1.5 text-xs text-zinc-50 animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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 dark:bg-zinc-50 dark:text-zinc-900', className )} {...props} /> -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/src/client/index.css b/src/client/index.css index 934e9be..385ab98 100644 --- a/src/client/index.css +++ b/src/client/index.css @@ -66,70 +66,55 @@ a { @tailwind components; @tailwind utilities; +/* https://ui.shadcn.com/themes */ @layer base { :root { --background: 0 0% 100%; - --foreground: 222.2 84% 4.9%; - + --foreground: 240 10% 3.9%; --card: 0 0% 100%; - --card-foreground: 222.2 84% 4.9%; - + --card-foreground: 240 10% 3.9%; --popover: 0 0% 100%; - --popover-foreground: 222.2 84% 4.9%; - - --primary: 222.2 47.4% 11.2%; - --primary-foreground: 210 40% 98%; - - --secondary: 210 40% 96.1%; - --secondary-foreground: 222.2 47.4% 11.2%; - - --muted: 210 40% 96.1%; - --muted-foreground: 215.4 16.3% 46.9%; - - --accent: 210 40% 96.1%; - --accent-foreground: 222.2 47.4% 11.2%; - + --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; --destructive: 0 84.2% 60.2%; - --destructive-foreground: 210 40% 98%; - - --border: 214.3 31.8% 91.4%; - --input: 214.3 31.8% 91.4%; - --ring: 222.2 84% 4.9%; - + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 240 5.9% 10%; --radius: 0.5rem; } .dark { - --background: 222.2 84% 4.9%; - --foreground: 210 40% 98%; - - --card: 222.2 84% 4.9%; - --card-foreground: 210 40% 98%; - - --popover: 222.2 84% 4.9%; - --popover-foreground: 210 40% 98%; - - --primary: 210 40% 98%; - --primary-foreground: 222.2 47.4% 11.2%; - - --secondary: 217.2 32.6% 17.5%; - --secondary-foreground: 210 40% 98%; - - --muted: 217.2 32.6% 17.5%; - --muted-foreground: 215 20.2% 65.1%; - - --accent: 217.2 32.6% 17.5%; - --accent-foreground: 210 40% 98%; - + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + --primary: 0 0% 98%; + --primary-foreground: 240 5.9% 10%; + --secondary: 240 3.7% 15.9%; + --secondary-foreground: 0 0% 98%; + --muted: 240 3.7% 15.9%; + --muted-foreground: 240 5% 64.9%; + --accent: 240 3.7% 15.9%; + --accent-foreground: 0 0% 98%; --destructive: 0 62.8% 30.6%; - --destructive-foreground: 210 40% 98%; - - --border: 217.2 32.6% 17.5%; - --input: 217.2 32.6% 17.5%; - --ring: 212.7 26.8% 83.9%; + --destructive-foreground: 0 0% 98%; + --border: 240 3.7% 15.9%; + --input: 240 3.7% 15.9%; + --ring: 240 4.9% 83.9%; } } + @layer base { * { @apply border-border; diff --git a/src/client/package.json b/src/client/package.json index 1318188..dda6457 100644 --- a/src/client/package.json +++ b/src/client/package.json @@ -22,10 +22,14 @@ "@i18next-toolkit/react": "^1.0.6", "@loadable/component": "^5.16.3", "@monaco-editor/react": "^4.6.0", + "@radix-ui/react-avatar": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-menubar": "^1.0.4", "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-tooltip": "^1.0.7", "@tanstack/react-query": "4.33.0", "@tanstack/react-table": "^8.13.2", @@ -33,6 +37,7 @@ "@tianji/shared": "workspace:^", "@trpc/client": "^10.45.0", "@trpc/react-query": "^10.45.0", + "ahooks": "^3.7.10", "antd": "^5.13.1", "array-move": "^3.0.1", "axios": "^1.5.0", @@ -46,6 +51,7 @@ "filesize": "^10.0.12", "leaflet": "^1.9.4", "lodash-es": "^4.17.21", + "lucide-react": "^0.358.0", "millify": "^6.1.0", "next-themes": "^0.2.1", "pretty-ms": "^9.0.0", diff --git a/src/client/pages/Layout/Nav.tsx b/src/client/pages/Layout/Nav.tsx new file mode 100644 index 0000000..3ea72b9 --- /dev/null +++ b/src/client/pages/Layout/Nav.tsx @@ -0,0 +1,83 @@ +import { buttonVariants } from '@/components/ui/button'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/components/ui/tooltip'; +import { cn } from '@/utils/style'; +import React from 'react'; +import { IconType } from 'react-icons'; + +interface NavProps { + isCollapsed: boolean; + links: { + title: string; + label?: string; + icon: IconType; + variant: 'default' | 'ghost'; + }[]; +} + +export const Nav: React.FC = React.memo(({ links, isCollapsed }) => { + return ( +
+ +
+ ); +}); +Nav.displayName = 'Nav'; diff --git a/src/client/pages/Layout/UserConfig.tsx b/src/client/pages/Layout/UserConfig.tsx new file mode 100644 index 0000000..3cebd48 --- /dev/null +++ b/src/client/pages/Layout/UserConfig.tsx @@ -0,0 +1,103 @@ +import { ColorSchemeSwitcher } from '@/components/ColorSchemeSwitcher'; +import { Avatar, AvatarFallback } from '@/components/ui/avatar'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, +} from '@/components/ui/dropdown-menu'; +import { useUserInfo } from '@/store/user'; +import { languages } from '@/utils/constants'; +import { useTranslation, setLanguage } from '@i18next-toolkit/react'; +import React from 'react'; +import { LuMoreVertical } from 'react-icons/lu'; + +interface UserConfigProps { + isCollapsed: boolean; +} +export const UserConfig: React.FC = React.memo((props) => { + const userInfo = useUserInfo(); + const { i18n } = useTranslation(); + + const avatar = ( + + + {(userInfo?.username ?? '').substring(0, 2).toUpperCase()} + + + ); + + const name =
{userInfo?.username ?? ''}
; + + const more = ( + + ); + + return ( +
+ + {props.isCollapsed ? ( + <> + + {avatar} + + + ) : ( + <> + {avatar} + + {name} + + + {more} + + + )} + + + Profile + Settings + + Language + + + + {languages.map((language) => ( + + {language.label} + + ))} + + + + + + { + e.preventDefault(); + }} + > + + + + +
+ ); +}); +UserConfig.displayName = 'UserConfig'; diff --git a/src/client/pages/LayoutV2.tsx b/src/client/pages/LayoutV2.tsx new file mode 100644 index 0000000..2b86e6e --- /dev/null +++ b/src/client/pages/LayoutV2.tsx @@ -0,0 +1,140 @@ +import * as React from 'react'; +import { + LuAlertCircle, + LuArchive, + LuAreaChart, + LuFile, + LuFilePieChart, + LuInbox, + LuMessagesSquare, + LuMonitorDot, + LuMoreVertical, + LuSend, + LuServer, + LuShoppingCart, + LuTrash2, + LuUsers2, + LuWifi, +} from 'react-icons/lu'; +import { TooltipProvider } from '@/components/ui/tooltip'; +import { + ResizableHandle, + ResizablePanel, + ResizablePanelGroup, +} from '@/components/ui/resizable'; +import { useLocalStorageState } from 'ahooks'; +import { cn } from '@/utils/style'; +import { Separator } from '@/components/ui/separator'; +import { Nav } from './Layout/Nav'; +import { WorkspaceSwitcher } from '@/components/WorkspaceSwitcher'; +import { ColorSchemeSwitcher } from '@/components/ColorSchemeSwitcher'; +import { LanguageSelector } from '@/components/LanguageSelector'; +import { Avatar, AvatarFallback } from '@/components/ui/avatar'; +import { useUserInfo } from '@/store/user'; +import { Button } from '@/components/ui/button'; +import { UserConfig } from './Layout/UserConfig'; + +const defaultLayout: [number, number, number] = [265, 440, 655]; + +export const LayoutV2: React.FC = React.memo(() => { + const [layout = defaultLayout, setLayout] = useLocalStorageState( + 'react-resizable-panels:layout', + { defaultValue: defaultLayout } + ); + const [isCollapsed = false, setIsCollapsed] = useLocalStorageState( + 'react-resizable-panels:collapsed', + { + defaultValue: false, + } + ); + + return ( + + { + setLayout(sizes as typeof defaultLayout); + }} + className="h-full items-stretch" + > + { + setIsCollapsed(true); + }} + onExpand={() => { + setIsCollapsed(false); + }} + className={cn( + 'flex flex-col', + isCollapsed && + 'min-w-[50px] transition-all duration-300 ease-in-out' + )} + > +
+ +
+ +