diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 23375ff6..6872596f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,21 +1,24 @@ name: CI on: - pull_request: - branches: [main] + pull_request: + branches: [main] jobs: - lint: - runs-on: ubuntu-latest + lint: + runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v7 - - - name: Setup pnpm - uses: pnpm/action-setup@v6 + steps: + - uses: actions/checkout@v7 - - name: Install Dependencies - run: pnpm install + - name: Setup pnpm + uses: pnpm/action-setup@v6 - - name: Lint - run: pnpm run lint \ No newline at end of file + - name: Install Dependencies + run: pnpm install + + - name: Check formatting + run: pnpm run fmt + + - name: Lint + run: pnpm run lint diff --git a/eslint.config.mjs b/eslint.config.mjs index a47add79..d0a5b7d4 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,30 +1,23 @@ -import globals from "globals"; -import pluginJs from "@eslint/js"; -import tseslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; -import reactCompiler from 'eslint-plugin-react-compiler' +import react from '@playcanvas/eslint-config/react'; +import typescript from '@playcanvas/eslint-config/typescript'; +import reactCompiler from 'eslint-plugin-react-compiler'; +import globals from 'globals'; /** @type {import('eslint').Linter.Config[]} */ export default [ - {files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"]}, - {languageOptions: { globals: {...globals.browser, ...globals.node} }}, - pluginJs.configs.recommended, - ...tseslint.configs.recommended, - pluginReact.configs.flat.recommended, - { - plugins: { - 'react-compiler': reactCompiler, - }, - settings: { - react: { - version: "detect", // Automatically detect React version - } - }, - rules: { - 'react/prop-types': 'off', - "react/jsx-uses-react": "off", - "react/react-in-jsx-scope": "off", - "react-compiler/react-compiler": "warn" - }, - } -]; \ No newline at end of file + ...typescript, + ...react, + { languageOptions: { globals: { ...globals.browser, ...globals.node } } }, + { + plugins: { + 'react-compiler': reactCompiler + }, + rules: { + 'import-x/no-unresolved': ['error', { ignore: ['^@playcanvas/react(?:/|$)'] }], + 'react/prop-types': 'off', + 'react/jsx-uses-react': 'off', + 'react/react-in-jsx-scope': 'off', + 'react-compiler/react-compiler': 'warn' + } + } +]; diff --git a/package.json b/package.json index 86d9bc8b..0518cad6 100644 --- a/package.json +++ b/package.json @@ -18,25 +18,27 @@ "build:docs": "pnpm --filter docs run build", "build": "pnpm run build:lib && pnpm run build:blocks && pnpm run build:docs", "start": "pnpm --filter docs run start", + "fmt": "prettier --check packages eslint.config.mjs prettier.config.mjs", + "fmt:fix": "prettier --write packages eslint.config.mjs prettier.config.mjs", "lint": "pnpm -r exec eslint .", + "lint:fix": "pnpm -r exec eslint . --fix", "test": "pnpm -r run test" }, "devDependencies": { "@changesets/cli": "2.31.0", - "@eslint/js": "9.39.4", + "@playcanvas/eslint-config": "3.0.0-beta.8", "@testing-library/dom": "10.4.1", "@types/react": "19.2.17", "@types/react-dom": "19.2.3", "@vitejs/plugin-react": "5.2.0", "eslint": "9.39.4", "eslint-plugin-import": "2.32.0", - "eslint-plugin-react": "7.37.5", "eslint-plugin-react-compiler": "19.0.0-beta-ebf51a3-20250411", "globals": "16.5.0", "pkg-pr-new": "0.0.75", + "prettier": "3.9.6", "type-fest": "5.7.0", "typescript": "5.9.3", - "typescript-eslint": "8.60.1", "vitest": "4.1.8" } } diff --git a/packages/blocks/CHANGELOG.md b/packages/blocks/CHANGELOG.md index 94e3d5c6..db2cdc5d 100644 --- a/packages/blocks/CHANGELOG.md +++ b/packages/blocks/CHANGELOG.md @@ -7,28 +7,28 @@ - 4f25e4e: Fix stale homepage URLs in package metadata: `@playcanvas/react` now points at the canonical docs path and `@playcanvas/blocks` at its GitHub README instead of the retired Vercel site. - Updated dependencies [4f25e4e] - Updated dependencies [f88e26e] - - @playcanvas/react@0.11.5 + - @playcanvas/react@0.11.5 ## 0.3.8 ### Patch Changes - Updated dependencies [8a44703] - - @playcanvas/react@0.11.4 + - @playcanvas/react@0.11.4 ## 0.3.7 ### Patch Changes - Updated dependencies [c1106f7] - - @playcanvas/react@0.11.3 + - @playcanvas/react@0.11.3 ## 0.3.6 ### Patch Changes - Updated dependencies [e564a0e] - - @playcanvas/react@0.11.2 + - @playcanvas/react@0.11.2 ## 0.3.5 @@ -36,7 +36,7 @@ - 4f0986c: Moved PlayCanvas from dependencies to peerDependencies (^2.11.8) to align with @playcanvas/react and avoid version conflicts - Updated dependencies [4f0986c] - - @playcanvas/react@0.11.1 + - @playcanvas/react@0.11.1 ## 0.3.4 @@ -46,14 +46,14 @@ - Updated dependencies [86f01de] - Updated dependencies [19a8f7f] - Updated dependencies [e800b2e] - - @playcanvas/react@0.11.0 + - @playcanvas/react@0.11.0 ## 0.3.3 ### Patch Changes - Updated dependencies [61b1f1d] - - @playcanvas/react@0.10.3 + - @playcanvas/react@0.10.3 ## 0.3.2 @@ -61,7 +61,7 @@ - Updated dependencies [deab2a7] - Updated dependencies [f147fa5] - - @playcanvas/react@0.10.2 + - @playcanvas/react@0.10.2 ## 0.3.1 @@ -69,7 +69,7 @@ - Updated dependencies [39c8647] - Updated dependencies [98dc81c] - - @playcanvas/react@0.10.1 + - @playcanvas/react@0.10.1 ## 0.3.0 @@ -84,7 +84,7 @@ - Updated dependencies [3edf166] - Updated dependencies [fa859ed] - Updated dependencies [3082203] - - @playcanvas/react@0.10.0 + - @playcanvas/react@0.10.0 ## 0.2.1 @@ -92,7 +92,7 @@ - f6bf216: Updated camera api (removed resetZoom) - Updated dependencies [6372f9c] - - @playcanvas/react@0.9.1 + - @playcanvas/react@0.9.1 ## 0.2.0 @@ -104,7 +104,7 @@ - 3f6a73f: Removes redundant Quaternion methods - Updated dependencies [118c922] - - @playcanvas/react@0.9.0 + - @playcanvas/react@0.9.0 ## 0.1.3 @@ -115,7 +115,7 @@ - Updated dependencies [f5f3001] - Updated dependencies [91fac3a] - Updated dependencies [6c3f3c9] - - @playcanvas/react@0.8.0 + - @playcanvas/react@0.8.0 ## 0.1.2 @@ -127,7 +127,7 @@ - Updated dependencies [28998cb] - Updated dependencies [0b08685] - Updated dependencies [62d7178] - - @playcanvas/react@0.7.0 + - @playcanvas/react@0.7.0 ## 0.1.1 @@ -142,7 +142,7 @@ - Updated dependencies [637d196] - Updated dependencies [842abe2] - Updated dependencies [85229d7] - - @playcanvas/react@0.6.0 + - @playcanvas/react@0.6.0 ## 0.1.0 @@ -156,4 +156,4 @@ - 8348361: Updated @playcanvas/blocks to use 2.8.0 of the engine - Updated dependencies [4917998] - Updated dependencies [894ca08] - - @playcanvas/react@0.5.0 + - @playcanvas/react@0.5.0 diff --git a/packages/blocks/README.md b/packages/blocks/README.md index 6823ef35..170a0777 100644 --- a/packages/blocks/README.md +++ b/packages/blocks/README.md @@ -15,8 +15,7 @@ Follow this guide to [Install Tailwind v4](https://tailwindcss.com/docs/installa And add the following top your css ```css -@import "@playcanvas/blocks" -@source "../../node_modules/@playcanvas/blocks"; +@import '@playcanvas/blocks' @source '../../node_modules/@playcanvas/blocks'; ``` ...and you're done! @@ -30,28 +29,28 @@ A responsive and composable component for displaying **Gaussian splats**. image Includes built-in camera controls, loading states, and optional UI slots for overlays or actions. -Supports large assets with lazy loading and automatic framing. Use it in modals, pages, or full-screen scenes. +Supports large assets with lazy loading and automatic framing. Use it in modals, pages, or full-screen scenes. Supports compressed gaussian splats. ```jsx -import { Viewer } from "@playcanvas/blocks" +import { Viewer } from '@playcanvas/blocks'; export function SplatViewer() { return ( - - -
- - -
-
- - - -
-
-
- ) + + +
+ + +
+
+ + + +
+
+
+ ); } ``` diff --git a/packages/blocks/eslint.config.mjs b/packages/blocks/eslint.config.mjs index 497972c5..b94008c3 100644 --- a/packages/blocks/eslint.config.mjs +++ b/packages/blocks/eslint.config.mjs @@ -1,31 +1,33 @@ -import baseConfig from '../../eslint.config.mjs'; import importPlugin from 'eslint-plugin-import'; +import baseConfig from '../../eslint.config.mjs'; + /** @type {import('eslint').Linter.Config[]} */ export default [ - ...baseConfig, - { - plugins: { - 'import': importPlugin, - }, - settings: { - 'import/resolver': { - node: { - extensions: ['.js', '.ts', '.json'] + ...baseConfig, + { + plugins: { + import: importPlugin + }, + settings: { + 'import/resolver': { + node: { + extensions: ['.js', '.ts', '.json'] + } + } + }, + rules: { + 'import/extensions': [ + 'error', + 'ignorePackages', + { + js: 'always', + ts: 'always' + } + ] } - } }, - rules: { - 'import/extensions': ['error', 'ignorePackages', { - js: 'always', - ts: 'always', - }] + { + ignores: ['dist/', 'node_modules/'] } - }, - { - ignores: [ - "dist/", - "node_modules/", - ], - }, -]; \ No newline at end of file +]; diff --git a/packages/blocks/package.json b/packages/blocks/package.json index 8457647c..e410773e 100644 --- a/packages/blocks/package.json +++ b/packages/blocks/package.json @@ -1,80 +1,80 @@ { - "name": "@playcanvas/blocks", - "description": "High level abstract 3D primitives for React", - "homepage": "https://github.com/playcanvas/react/tree/main/packages/blocks#readme", - "version": "0.3.9", - "license": "MIT", - "type": "module", - "sideEffects": false, - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "exports": { - ".": { - "import": "./dist/index.js", - "types": "./dist/index.d.ts", - "style": "./dist/index.css" + "name": "@playcanvas/blocks", + "version": "0.3.9", + "description": "High level abstract 3D primitives for React", + "keywords": [ + "shadcn", + "primitives", + "registry", + "playcanvas", + "react", + "gaussian splatting", + "splatting", + "3d", + "3d primitives", + "3d blocks", + "3d components" + ], + "homepage": "https://github.com/playcanvas/react/tree/main/packages/blocks#readme", + "bugs": { + "url": "https://github.com/playcanvas/react/issues" }, - "./index.css": { - "default": "./dist/index.css" + "repository": { + "type": "git", + "url": "https://github.com/playcanvas/react", + "directory": "packages/blocks" + }, + "license": "MIT", + "sideEffects": false, + "type": "module", + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts", + "style": "./dist/index.css" + }, + "./index.css": { + "default": "./dist/index.css" + } + }, + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsc && tsc-alias && cp src/index.css dist/" + }, + "dependencies": { + "@playcanvas/react": "workspace:*", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slider": "^1.3.5", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-switch": "^1.2.5", + "@radix-ui/react-tabs": "^1.1.12", + "@radix-ui/react-toggle": "^1.1.9", + "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.7", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.511.0 || ^0.514.0 || ^0.515.0 || ^0.523.0 || ^0.525.0 || ^0.539.0 || ^0.542.0 || ^0.543.0 || ^0.544.0 || ^0.552.0 || ^0.553.0 || ^0.577.0", + "tailwind-merge": "^3.3.0", + "tw-animate-css": "^1.3.0", + "vaul": "^1.1.2" + }, + "devDependencies": { + "@types/react": "19.2.17", + "@types/react-dom": "19.2.3", + "tsc-alias": "1.8.17" + }, + "peerDependencies": { + "playcanvas": "^2.11.8", + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "publishConfig": { + "access": "public" } - }, - "scripts": { - "build": "tsc && tsc-alias && cp src/index.css dist/" - }, - "repository": { - "type": "git", - "url": "https://github.com/playcanvas/react", - "directory": "packages/blocks" - }, - "keywords": [ - "shadcn", - "primitives", - "registry", - "playcanvas", - "react", - "gaussian splatting", - "splatting", - "3d", - "3d primitives", - "3d blocks", - "3d components" - ], - "bugs": { - "url": "https://github.com/playcanvas/react/issues" - }, - "dependencies": { - "@playcanvas/react": "workspace:*", - "@radix-ui/react-dialog": "^1.1.14", - "@radix-ui/react-dropdown-menu": "^2.1.15", - "@radix-ui/react-separator": "^1.1.7", - "@radix-ui/react-slider": "^1.3.5", - "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-switch": "^1.2.5", - "@radix-ui/react-tabs": "^1.1.12", - "@radix-ui/react-toggle": "^1.1.9", - "@radix-ui/react-toggle-group": "^1.1.10", - "@radix-ui/react-tooltip": "^1.2.7", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "lucide-react": "^0.511.0 || ^0.514.0 || ^0.515.0 || ^0.523.0 || ^0.525.0 || ^0.539.0 || ^0.542.0 || ^0.543.0 || ^0.544.0 || ^0.552.0 || ^0.553.0 || ^0.577.0", - "tailwind-merge": "^3.3.0", - "tw-animate-css": "^1.3.0", - "vaul": "^1.1.2" - }, - "devDependencies": { - "@types/react": "19.2.17", - "@types/react-dom": "19.2.3", - "tsc-alias": "1.8.17" - }, - "peerDependencies": { - "playcanvas": "^2.11.8", - "react": "^19.1.0", - "react-dom": "^19.1.0" - }, - "publishConfig": { - "access": "public" - } } diff --git a/packages/blocks/src/components/ui/badge.tsx b/packages/blocks/src/components/ui/badge.tsx index 2e8991fc..c5d65827 100644 --- a/packages/blocks/src/components/ui/badge.tsx +++ b/packages/blocks/src/components/ui/badge.tsx @@ -1,46 +1,37 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from '@radix-ui/react-slot'; +import { cva } from 'class-variance-authority'; +import type { VariantProps } from 'class-variance-authority'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; const badgeVariants = cva( - "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", - { - variants: { - variant: { - default: - "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", - secondary: - "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", - destructive: - "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", - outline: - "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", - }, - }, - defaultVariants: { - variant: "default", - }, - } -) + 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + { + variants: { + variant: { + default: 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', + secondary: 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', + destructive: + 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground' + } + }, + defaultVariants: { + variant: 'default' + } + } +); function Badge({ - className, - variant, - asChild = false, - ...props -}: React.ComponentProps<"span"> & - VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot : "span" + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<'span'> & VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : 'span'; - return ( - - ) + return ; } -export { Badge, badgeVariants } +export { Badge, badgeVariants }; diff --git a/packages/blocks/src/components/ui/button.tsx b/packages/blocks/src/components/ui/button.tsx index 34e9d3fd..13d2c48a 100644 --- a/packages/blocks/src/components/ui/button.tsx +++ b/packages/blocks/src/components/ui/button.tsx @@ -1,59 +1,51 @@ -import * as React from "react" -import { Slot } from "@radix-ui/react-slot" -import { cva, type VariantProps } from "class-variance-authority" +import { Slot } from '@radix-ui/react-slot'; +import { cva } from 'class-variance-authority'; +import type { VariantProps } from 'class-variance-authority'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; const buttonVariants = cva( - "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", - { - variants: { - variant: { - default: - "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", - destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", - outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", - secondary: - "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", - ghost: - "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", - link: "text-primary underline-offset-4 hover:underline", - }, - size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 rounded-md px-6 has-[>svg]:px-4", - icon: "size-9", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90', + destructive: + 'bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline' + }, + size: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } + } +); function Button({ - className, - variant, - size, - asChild = false, - ...props -}: React.ComponentProps<"button"> & - VariantProps & { - asChild?: boolean - }) { - const Comp = asChild ? Slot : "button" + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<'button'> & + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? Slot : 'button'; - return ( - - ) + return ; } -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/packages/blocks/src/components/ui/card.tsx b/packages/blocks/src/components/ui/card.tsx index b37eaef3..7adc9c80 100644 --- a/packages/blocks/src/components/ui/card.tsx +++ b/packages/blocks/src/components/ui/card.tsx @@ -1,92 +1,59 @@ -import * as React from "react" +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function Card({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function Card({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); } -function CardHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function CardHeader({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); } -function CardTitle({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function CardTitle({ className, ...props }: React.ComponentProps<'div'>) { + return
; } -function CardDescription({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function CardDescription({ className, ...props }: React.ComponentProps<'div'>) { + return
; } -function CardAction({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function CardAction({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); } -function CardContent({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function CardContent({ className, ...props }: React.ComponentProps<'div'>) { + return
; } -function CardFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function CardFooter({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); } -export { - Card, - CardHeader, - CardFooter, - CardTitle, - CardAction, - CardDescription, - CardContent, -} +export { Card, CardHeader, CardFooter, CardTitle, CardAction, CardDescription, CardContent }; diff --git a/packages/blocks/src/components/ui/dialog.tsx b/packages/blocks/src/components/ui/dialog.tsx index 958429ac..3123d3be 100644 --- a/packages/blocks/src/components/ui/dialog.tsx +++ b/packages/blocks/src/components/ui/dialog.tsx @@ -1,135 +1,111 @@ -"use client" +'use client'; -import * as React from "react" -import * as DialogPrimitive from "@radix-ui/react-dialog" -import { XIcon } from "lucide-react" +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { XIcon } from 'lucide-react'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function Dialog({ - ...props -}: React.ComponentProps) { - return +function Dialog({ ...props }: React.ComponentProps) { + return ; } -function DialogTrigger({ - ...props -}: React.ComponentProps) { - return +function DialogTrigger({ ...props }: React.ComponentProps) { + return ; } -function DialogPortal({ - ...props -}: React.ComponentProps) { - return +function DialogPortal({ ...props }: React.ComponentProps) { + return ; } -function DialogClose({ - ...props -}: React.ComponentProps) { - return +function DialogClose({ ...props }: React.ComponentProps) { + return ; } -function DialogOverlay({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DialogOverlay({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function DialogContent({ - className, - children, - ...props -}: React.ComponentProps) { - return ( - - - - {children} - - - Close - - - - ) +function DialogContent({ className, children, ...props }: React.ComponentProps) { + return ( + + + + {children} + + + Close + + + + ); } -function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); } -function DialogFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function DialogFooter({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); } -function DialogTitle({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DialogTitle({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function DialogDescription({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DialogDescription({ className, ...props }: React.ComponentProps) { + return ( + + ); } export { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogOverlay, - DialogPortal, - DialogTitle, - DialogTrigger, -} + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger +}; diff --git a/packages/blocks/src/components/ui/drawer.tsx b/packages/blocks/src/components/ui/drawer.tsx index 1ccba87a..212f722d 100644 --- a/packages/blocks/src/components/ui/drawer.tsx +++ b/packages/blocks/src/components/ui/drawer.tsx @@ -1,132 +1,99 @@ -"use client" +'use client'; -import * as React from "react" -import { Drawer as DrawerPrimitive } from "vaul" +import * as React from 'react'; +import { Drawer as DrawerPrimitive } from 'vaul'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function Drawer({ - ...props -}: React.ComponentProps) { - return +function Drawer({ ...props }: React.ComponentProps) { + return ; } -function DrawerTrigger({ - ...props -}: React.ComponentProps) { - return +function DrawerTrigger({ ...props }: React.ComponentProps) { + return ; } -function DrawerPortal({ - ...props -}: React.ComponentProps) { - return +function DrawerPortal({ ...props }: React.ComponentProps) { + return ; } -function DrawerClose({ - ...props -}: React.ComponentProps) { - return +function DrawerClose({ ...props }: React.ComponentProps) { + return ; } -function DrawerOverlay({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DrawerOverlay({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function DrawerContent({ - className, - children, - ...props -}: React.ComponentProps) { - return ( - - - -
- {children} - - - ) +function DrawerContent({ className, children, ...props }: React.ComponentProps) { + return ( + + + +
+ {children} + + + ); } -function DrawerHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) { + return
; } -function DrawerFooter({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ) +function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) { + return
; } -function DrawerTitle({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DrawerTitle({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function DrawerDescription({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DrawerDescription({ className, ...props }: React.ComponentProps) { + return ( + + ); } export { - Drawer, - DrawerPortal, - DrawerOverlay, - DrawerTrigger, - DrawerClose, - DrawerContent, - DrawerHeader, - DrawerFooter, - DrawerTitle, - DrawerDescription, -} + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription +}; diff --git a/packages/blocks/src/components/ui/dropdown-menu.tsx b/packages/blocks/src/components/ui/dropdown-menu.tsx index 5bc973fe..066f6148 100644 --- a/packages/blocks/src/components/ui/dropdown-menu.tsx +++ b/packages/blocks/src/components/ui/dropdown-menu.tsx @@ -1,256 +1,219 @@ -"use client" +'use client'; -import * as React from "react" -import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" -import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" -import { cn } from "@lib/utils" +import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; +import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react'; +import * as React from 'react'; -function DropdownMenu({ - ...props -}: React.ComponentProps) { - return +import { cn } from '@lib/utils'; + +function DropdownMenu({ ...props }: React.ComponentProps) { + return ; } -function DropdownMenuPortal({ - ...props -}: React.ComponentProps) { - return ( - - ) +function DropdownMenuPortal({ ...props }: React.ComponentProps) { + return ; } -function DropdownMenuTrigger({ - ...props -}: React.ComponentProps) { - return ( - - ) +function DropdownMenuTrigger({ ...props }: React.ComponentProps) { + return ; } function DropdownMenuContent({ - className, - sideOffset = 4, - ...props + className, + sideOffset = 4, + ...props }: React.ComponentProps) { - return ( - - - - ) + return ( + + + + ); } -function DropdownMenuGroup({ - ...props -}: React.ComponentProps) { - return ( - - ) +function DropdownMenuGroup({ ...props }: React.ComponentProps) { + return ; } function DropdownMenuItem({ - className, - inset, - variant = "default", - ...props + className, + inset, + variant = 'default', + ...props }: React.ComponentProps & { - inset?: boolean - variant?: "default" | "destructive" + inset?: boolean; + variant?: 'default' | 'destructive'; }) { - return ( - - ) + return ( + + ); } function DropdownMenuCheckboxItem({ - className, - children, - checked, - ...props + className, + children, + checked, + ...props }: React.ComponentProps) { - return ( - - - - - - - {children} - - ) -} - -function DropdownMenuRadioGroup({ - ...props -}: React.ComponentProps) { - return ( - - ) + return ( + + + + + + + {children} + + ); +} + +function DropdownMenuRadioGroup({ ...props }: React.ComponentProps) { + return ; } function DropdownMenuRadioItem({ - className, - children, - ...props + className, + children, + ...props }: React.ComponentProps) { - return ( - - - - - - - {children} - - ) + return ( + + + + + + + {children} + + ); } function DropdownMenuLabel({ - className, - inset, - ...props + className, + inset, + ...props }: React.ComponentProps & { - inset?: boolean + inset?: boolean; }) { - return ( - - ) + return ( + + ); } -function DropdownMenuSeparator({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function DropdownMenuSeparator({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function DropdownMenuShortcut({ - className, - ...props -}: React.ComponentProps<"span">) { - return ( - - ) +function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) { + return ( + + ); } -function DropdownMenuSub({ - ...props -}: React.ComponentProps) { - return +function DropdownMenuSub({ ...props }: React.ComponentProps) { + return ; } function DropdownMenuSubTrigger({ - className, - inset, - children, - ...props + className, + inset, + children, + ...props }: React.ComponentProps & { - inset?: boolean + inset?: boolean; }) { - return ( - - {children} - - - ) + return ( + + {children} + + + ); } function DropdownMenuSubContent({ - className, - ...props + className, + ...props }: React.ComponentProps) { - return ( - - ) + return ( + + ); } export { - DropdownMenu, - DropdownMenuPortal, - DropdownMenuTrigger, - DropdownMenuContent, - DropdownMenuGroup, - DropdownMenuLabel, - DropdownMenuItem, - DropdownMenuCheckboxItem, - DropdownMenuRadioGroup, - DropdownMenuRadioItem, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuSub, - DropdownMenuSubTrigger, - DropdownMenuSubContent, -} + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent +}; diff --git a/packages/blocks/src/components/ui/separator.tsx b/packages/blocks/src/components/ui/separator.tsx index 50c6ea15..97d61d0a 100644 --- a/packages/blocks/src/components/ui/separator.tsx +++ b/packages/blocks/src/components/ui/separator.tsx @@ -1,28 +1,28 @@ -"use client" +'use client'; -import * as React from "react" -import * as SeparatorPrimitive from "@radix-ui/react-separator" +import * as SeparatorPrimitive from '@radix-ui/react-separator'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; function Separator({ - className, - orientation = "horizontal", - decorative = true, - ...props + className, + orientation = 'horizontal', + decorative = true, + ...props }: React.ComponentProps) { - return ( - - ) + return ( + + ); } -export { Separator } +export { Separator }; diff --git a/packages/blocks/src/components/ui/slider.tsx b/packages/blocks/src/components/ui/slider.tsx index a60a8d84..c156959f 100644 --- a/packages/blocks/src/components/ui/slider.tsx +++ b/packages/blocks/src/components/ui/slider.tsx @@ -1,63 +1,58 @@ -"use client" +'use client'; -import * as React from "react" -import * as SliderPrimitive from "@radix-ui/react-slider" +import * as SliderPrimitive from '@radix-ui/react-slider'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; function Slider({ - className, - defaultValue, - value, - min = 0, - max = 100, - ...props + className, + defaultValue, + value, + min = 0, + max = 100, + ...props }: React.ComponentProps) { - const _values = React.useMemo( - () => - Array.isArray(value) - ? value - : Array.isArray(defaultValue) - ? defaultValue - : [min, max], - [value, defaultValue, min, max] - ) + const _values = React.useMemo( + () => (Array.isArray(value) ? value : Array.isArray(defaultValue) ? defaultValue : [min, max]), + [value, defaultValue, min, max] + ); - return ( - - - - - {Array.from({ length: _values.length }, (_, index) => ( - - ))} - - ) + return ( + + + + + {Array.from({ length: _values.length }, (_, index) => ( + + ))} + + ); } -export { Slider } +export { Slider }; diff --git a/packages/blocks/src/components/ui/switch.tsx b/packages/blocks/src/components/ui/switch.tsx index 12fdabc8..2398b65b 100644 --- a/packages/blocks/src/components/ui/switch.tsx +++ b/packages/blocks/src/components/ui/switch.tsx @@ -1,31 +1,28 @@ -"use client" +'use client'; -import * as React from "react" -import * as SwitchPrimitive from "@radix-ui/react-switch" +import * as SwitchPrimitive from '@radix-ui/react-switch'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function Switch({ - className, - ...props -}: React.ComponentProps) { - return ( - - - - ) +function Switch({ className, ...props }: React.ComponentProps) { + return ( + + + + ); } -export { Switch } +export { Switch }; diff --git a/packages/blocks/src/components/ui/table.tsx b/packages/blocks/src/components/ui/table.tsx index 408c5e8f..edb7ef20 100644 --- a/packages/blocks/src/components/ui/table.tsx +++ b/packages/blocks/src/components/ui/table.tsx @@ -1,116 +1,75 @@ -"use client" +'use client'; -import * as React from "react" +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function Table({ className, ...props }: React.ComponentProps<"table">) { - return ( -
- - - ) +function Table({ className, ...props }: React.ComponentProps<'table'>) { + return ( +
+
+ + ); } -function TableHeader({ className, ...props }: React.ComponentProps<"thead">) { - return ( - - ) +function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) { + return ; } -function TableBody({ className, ...props }: React.ComponentProps<"tbody">) { - return ( - - ) +function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) { + return ; } -function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) { - return ( - tr]:last:border-b-0", - className - )} - {...props} - /> - ) +function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) { + return ( + tr]:last:border-b-0', className)} + {...props} + /> + ); } -function TableRow({ className, ...props }: React.ComponentProps<"tr">) { - return ( - - ) +function TableRow({ className, ...props }: React.ComponentProps<'tr'>) { + return ( + + ); } -function TableHead({ className, ...props }: React.ComponentProps<"th">) { - return ( -
[role=checkbox]]:translate-y-[2px]", - className - )} - {...props} - /> - ) +function TableHead({ className, ...props }: React.ComponentProps<'th'>) { + return ( + [role=checkbox]]:translate-y-[2px]', + className + )} + {...props} + /> + ); } -function TableCell({ className, ...props }: React.ComponentProps<"td">) { - return ( - [role=checkbox]]:translate-y-[2px]", - className - )} - {...props} - /> - ) +function TableCell({ className, ...props }: React.ComponentProps<'td'>) { + return ( + [role=checkbox]]:translate-y-[2px]', + className + )} + {...props} + /> + ); } -function TableCaption({ - className, - ...props -}: React.ComponentProps<"caption">) { - return ( -
- ) +function TableCaption({ className, ...props }: React.ComponentProps<'caption'>) { + return ( + + ); } -export { - Table, - TableHeader, - TableBody, - TableFooter, - TableHead, - TableRow, - TableCell, - TableCaption, -} +export { Table, TableHeader, TableBody, TableFooter, TableHead, TableRow, TableCell, TableCaption }; diff --git a/packages/blocks/src/components/ui/tabs.tsx b/packages/blocks/src/components/ui/tabs.tsx index 76bebf20..f2dcc9e1 100644 --- a/packages/blocks/src/components/ui/tabs.tsx +++ b/packages/blocks/src/components/ui/tabs.tsx @@ -1,66 +1,44 @@ -"use client" +'use client'; -import * as React from "react" -import * as TabsPrimitive from "@radix-ui/react-tabs" +import * as TabsPrimitive from '@radix-ui/react-tabs'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function Tabs({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function Tabs({ className, ...props }: React.ComponentProps) { + return ; } -function TabsList({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function TabsList({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function TabsTrigger({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function TabsTrigger({ className, ...props }: React.ComponentProps) { + return ( + + ); } -function TabsContent({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) +function TabsContent({ className, ...props }: React.ComponentProps) { + return ( + + ); } -export { Tabs, TabsList, TabsTrigger, TabsContent } +export { Tabs, TabsList, TabsTrigger, TabsContent }; diff --git a/packages/blocks/src/components/ui/toggle-group.tsx b/packages/blocks/src/components/ui/toggle-group.tsx index 05893856..ecd4bcc1 100644 --- a/packages/blocks/src/components/ui/toggle-group.tsx +++ b/packages/blocks/src/components/ui/toggle-group.tsx @@ -1,73 +1,67 @@ -"use client" +'use client'; -import * as React from "react" -import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group" -import { type VariantProps } from "class-variance-authority" +import * as ToggleGroupPrimitive from '@radix-ui/react-toggle-group'; +import type { VariantProps } from 'class-variance-authority'; +import * as React from 'react'; -import { cn } from "@lib/utils" -import { toggleVariants } from "@components/ui/toggle" +import { toggleVariants } from '@components/ui/toggle'; +import { cn } from '@lib/utils'; -const ToggleGroupContext = React.createContext< - VariantProps ->({ - size: "default", - variant: "default", -}) +const ToggleGroupContext = React.createContext>({ + size: 'default', + variant: 'default' +}); function ToggleGroup({ - className, - variant, - size, - children, - ...props -}: React.ComponentProps & - VariantProps) { - return ( - - - {children} - - - ) + className, + variant, + size, + children, + ...props +}: React.ComponentProps & VariantProps) { + return ( + + {children} + + ); } function ToggleGroupItem({ - className, - children, - variant, - size, - ...props -}: React.ComponentProps & - VariantProps) { - const context = React.useContext(ToggleGroupContext) + className, + children, + variant, + size, + ...props +}: React.ComponentProps & VariantProps) { + const context = React.useContext(ToggleGroupContext); - return ( - - {children} - - ) + return ( + + {children} + + ); } -export { ToggleGroup, ToggleGroupItem } +export { ToggleGroup, ToggleGroupItem }; diff --git a/packages/blocks/src/components/ui/toggle.tsx b/packages/blocks/src/components/ui/toggle.tsx index 316c973a..9873f8ba 100644 --- a/packages/blocks/src/components/ui/toggle.tsx +++ b/packages/blocks/src/components/ui/toggle.tsx @@ -1,47 +1,46 @@ -"use client" +'use client'; -import * as React from "react" -import * as TogglePrimitive from "@radix-ui/react-toggle" -import { cva, type VariantProps } from "class-variance-authority" +import * as TogglePrimitive from '@radix-ui/react-toggle'; +import { cva } from 'class-variance-authority'; +import type { VariantProps } from 'class-variance-authority'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; const toggleVariants = cva( - "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", - { - variants: { - variant: { - default: "bg-transparent", - outline: - "border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground", - }, - size: { - default: "h-9 px-2 min-w-9", - sm: "h-8 px-1.5 min-w-8", - lg: "h-10 px-2.5 min-w-10", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } -) + "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium hover:bg-muted hover:text-muted-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0 focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] outline-none transition-[color,box-shadow] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive whitespace-nowrap", + { + variants: { + variant: { + default: 'bg-transparent', + outline: 'border border-input bg-transparent shadow-xs hover:bg-accent hover:text-accent-foreground' + }, + size: { + default: 'h-9 px-2 min-w-9', + sm: 'h-8 px-1.5 min-w-8', + lg: 'h-10 px-2.5 min-w-10' + } + }, + defaultVariants: { + variant: 'default', + size: 'default' + } + } +); function Toggle({ - className, - variant, - size, - ...props -}: React.ComponentProps & - VariantProps) { - return ( - - ) + className, + variant, + size, + ...props +}: React.ComponentProps & VariantProps) { + return ( + + ); } -export { Toggle, toggleVariants } +export { Toggle, toggleVariants }; diff --git a/packages/blocks/src/components/ui/tooltip.tsx b/packages/blocks/src/components/ui/tooltip.tsx index c0622b18..7ab25841 100644 --- a/packages/blocks/src/components/ui/tooltip.tsx +++ b/packages/blocks/src/components/ui/tooltip.tsx @@ -1,60 +1,47 @@ -"use client" +'use client'; -import * as React from "react" -import * as TooltipPrimitive from "@radix-ui/react-tooltip" +import * as TooltipPrimitive from '@radix-ui/react-tooltip'; +import * as React from 'react'; -import { cn } from "@lib/utils" +import { cn } from '@lib/utils'; -function TooltipProvider({ - delayDuration = 0, - ...props -}: React.ComponentProps) { - return ( - - ) +function TooltipProvider({ delayDuration = 0, ...props }: React.ComponentProps) { + return ; } -function Tooltip({ - ...props -}: React.ComponentProps) { - return ( - - - - ) +function Tooltip({ ...props }: React.ComponentProps) { + return ( + + + + ); } -function TooltipTrigger({ - ...props -}: React.ComponentProps) { - return +function TooltipTrigger({ ...props }: React.ComponentProps) { + return ; } function TooltipContent({ - className, - sideOffset = 0, - children, - ...props + className, + sideOffset = 0, + children, + ...props }: React.ComponentProps) { - return ( - - - {children} - - - ) + return ( + + + {children} + + + ); } -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/packages/blocks/src/index.css b/packages/blocks/src/index.css index 84e62a7c..4c704648 100644 --- a/packages/blocks/src/index.css +++ b/packages/blocks/src/index.css @@ -1,5 +1,5 @@ -@import "tailwindcss"; -@import "tw-animate-css"; +@import 'tailwindcss'; +@import 'tw-animate-css'; @custom-variant dark (&:is(.dark *)); @@ -74,9 +74,9 @@ --sidebar-accent-foreground: oklch(0.21 0.006 285.885); --sidebar-border: oklch(0.92 0.004 286.32); --sidebar-ring: oklch(0.705 0.015 286.067); - } - - .dark { +} + +.dark { --background: oklch(0.141 0.005 285.823); --foreground: oklch(0.985 0 0); --card: oklch(0.21 0.006 285.885); @@ -108,11 +108,10 @@ --sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-border: oklch(1 0 0 / 10%); --sidebar-ring: oklch(0.552 0.016 285.938); - } - +} @layer base { - * { - @apply border-border outline-ring/50; - } + * { + @apply border-border outline-ring/50; + } } diff --git a/packages/blocks/src/index.ts b/packages/blocks/src/index.ts index 58d3f5c0..68dde325 100644 --- a/packages/blocks/src/index.ts +++ b/packages/blocks/src/index.ts @@ -1,4 +1,4 @@ // Splat Viewer -import * as Viewer from "./splat-viewer/index.ts" -import { useAssetViewer } from "./splat-viewer/splat-viewer-context.ts"; +import * as Viewer from './splat-viewer/index.ts'; +import { useAssetViewer } from './splat-viewer/splat-viewer-context.tsx'; export { Viewer, useAssetViewer }; diff --git a/packages/blocks/src/lib/utils.ts b/packages/blocks/src/lib/utils.ts index bd0c391d..66bf229d 100644 --- a/packages/blocks/src/lib/utils.ts +++ b/packages/blocks/src/lib/utils.ts @@ -1,6 +1,7 @@ -import { clsx, type ClassValue } from "clsx" -import { twMerge } from "tailwind-merge" +import { clsx } from 'clsx'; +import type { ClassValue } from 'clsx'; +import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/packages/blocks/src/splat-viewer/camera-mode-toggle.tsx b/packages/blocks/src/splat-viewer/camera-mode-toggle.tsx index 9bbd45c4..cbe7a445 100644 --- a/packages/blocks/src/splat-viewer/camera-mode-toggle.tsx +++ b/packages/blocks/src/splat-viewer/camera-mode-toggle.tsx @@ -1,32 +1,30 @@ -"use client" +'use client'; -import { Move3D, Rotate3D } from "lucide-react"; -import { useAssetViewer } from "./splat-viewer-context.tsx"; -import { Button } from "@components/ui/button"; -import { Tooltip, TooltipContent, TooltipTrigger } from "@components/ui/tooltip"; +import { Move3D, Rotate3D } from 'lucide-react'; -function CameraModeToggle() { +import { Button } from '@components/ui/button'; +import { Tooltip, TooltipContent, TooltipTrigger } from '@components/ui/tooltip'; + +import { useAssetViewer } from './splat-viewer-context.tsx'; +function CameraModeToggle() { const { mode, setMode } = useAssetViewer(); - - return ( - - - - - {mode === 'orbit' ? "Fly Camera" : "Orbit Camera"} - - ) - } -export { CameraModeToggle }; \ No newline at end of file + return ( + + + + + {mode === 'orbit' ? 'Fly Camera' : 'Orbit Camera'} + + ); +} + +export { CameraModeToggle }; diff --git a/packages/blocks/src/splat-viewer/controls.tsx b/packages/blocks/src/splat-viewer/controls.tsx index 17d0b00e..fefdca8a 100644 --- a/packages/blocks/src/splat-viewer/controls.tsx +++ b/packages/blocks/src/splat-viewer/controls.tsx @@ -1,40 +1,40 @@ +'use client'; -"use client" +import { cn } from '@lib/utils'; -import { useAssetViewer, useTimeline } from "./splat-viewer-context.tsx"; -import { cn } from "@lib/utils"; +import { useAssetViewer, useTimeline } from './splat-viewer-context.tsx'; type ControlsProps = { /** * The className of the controls */ - className?: string, + className?: string; /** * When enabled, the controls will be hidden when the user is not interacting with the asset. * @defaultValue false */ - autoHide?: boolean, + autoHide?: boolean; /** * The children of the controls */ - children: React.ReactNode, -} + children: React.ReactNode; +}; export function Controls({ className, autoHide = false, children }: ControlsProps) { - const { isInteracting } = useAssetViewer(); - const { isPlaying } = useTimeline(); + const { isInteracting } = useAssetViewer(); + const { isPlaying } = useTimeline(); - return ( -
- {children} -
- ); -} \ No newline at end of file + return ( +
+ {children} +
+ ); +} diff --git a/packages/blocks/src/splat-viewer/download-button.tsx b/packages/blocks/src/splat-viewer/download-button.tsx index 499dc944..29b6f7ae 100644 --- a/packages/blocks/src/splat-viewer/download-button.tsx +++ b/packages/blocks/src/splat-viewer/download-button.tsx @@ -1,36 +1,36 @@ -"use client"; +'use client'; -import { DownloadIcon } from "lucide-react"; -import { Button } from "@components/ui/button"; -import { useAssetViewer } from "./splat-viewer-context.tsx"; -import { TooltipTrigger, TooltipContent, Tooltip } from "@components/ui/tooltip"; +import { DownloadIcon } from 'lucide-react'; + +import { Button } from '@components/ui/button'; +import { TooltipTrigger, TooltipContent, Tooltip } from '@components/ui/tooltip'; + +import { useAssetViewer } from './splat-viewer-context.tsx'; type DownloadButtonProps = { - variant?: "default" | "outline" | "ghost" | "link"; -} + variant?: 'default' | 'outline' | 'ghost' | 'link'; +}; -function DownloadButton({ variant = "ghost" }: DownloadButtonProps) { - const { src, triggerDownload } = useAssetViewer(); // assume src is a URL string +function DownloadButton({ variant = 'ghost' }: DownloadButtonProps) { + const { src, triggerDownload } = useAssetViewer(); // assume src is a URL string - return ( - - - - - - Download - - - ); + return ( + + + + + Download + + ); } -export { DownloadButton }; \ No newline at end of file +export { DownloadButton }; diff --git a/packages/blocks/src/splat-viewer/full-screen-button.tsx b/packages/blocks/src/splat-viewer/full-screen-button.tsx index 8ce89571..bfc3a95b 100644 --- a/packages/blocks/src/splat-viewer/full-screen-button.tsx +++ b/packages/blocks/src/splat-viewer/full-screen-button.tsx @@ -1,31 +1,38 @@ -"use client"; +'use client'; -import { MaximizeIcon, MinimizeIcon } from "lucide-react"; -import { Button } from "@components/ui/button"; -import { useAssetViewer } from "./splat-viewer-context.tsx"; -import { TooltipTrigger, TooltipContent, Tooltip } from "@components/ui/tooltip"; +import { MaximizeIcon, MinimizeIcon } from 'lucide-react'; + +import { Button } from '@components/ui/button'; +import { TooltipTrigger, TooltipContent, Tooltip } from '@components/ui/tooltip'; + +import { useAssetViewer } from './splat-viewer-context.tsx'; const FullScreenToggleIcon = ({ isFullscreen }: { isFullscreen: boolean }) => { return isFullscreen ? : ; -} +}; type FullScreenButtonProps = { - variant?: "default" | "outline" | "ghost" | "link"; -} + variant?: 'default' | 'outline' | 'ghost' | 'link'; +}; -function FullScreenButton({ variant = "ghost" }: FullScreenButtonProps) { +function FullScreenButton({ variant = 'ghost' }: FullScreenButtonProps) { const { isFullscreen, toggleFullscreen } = useAssetViewer(); - return ( - - - - - {isFullscreen ? "Exit Fullscreen" : "Enter Fullscreen"} - - ); + return ( + + + + + {isFullscreen ? 'Exit Fullscreen' : 'Enter Fullscreen'} + + ); } -export { FullScreenButton }; \ No newline at end of file +export { FullScreenButton }; diff --git a/packages/blocks/src/splat-viewer/help-button.tsx b/packages/blocks/src/splat-viewer/help-button.tsx index 9c05cff1..a4916be9 100644 --- a/packages/blocks/src/splat-viewer/help-button.tsx +++ b/packages/blocks/src/splat-viewer/help-button.tsx @@ -1,17 +1,22 @@ -"use client" +'use client'; -import { Button } from "@components/ui/button" -import { useAssetViewer } from "./splat-viewer-context.tsx" +import { Button } from '@components/ui/button'; -function HelpButton() { +import { useAssetViewer } from './splat-viewer-context.tsx'; - const { setOverlay } = useAssetViewer() +function HelpButton() { + const { setOverlay } = useAssetViewer(); - return ( - - ) + return ( + + ); } -export { HelpButton }; \ No newline at end of file +export { HelpButton }; diff --git a/packages/blocks/src/splat-viewer/help-dialog.tsx b/packages/blocks/src/splat-viewer/help-dialog.tsx index e3c6a994..a2ffea67 100644 --- a/packages/blocks/src/splat-viewer/help-dialog.tsx +++ b/packages/blocks/src/splat-viewer/help-dialog.tsx @@ -1,165 +1,207 @@ -import * as React from "react" -import { useAssetViewer } from "./splat-viewer-context.ts" -import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@components/ui/dialog" -import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle } from "@components/ui/drawer" -import { Table, TableHeader, TableBody, TableCell, TableRow, TableHead } from "@components/ui/table" -import { Badge } from "@components/ui/badge" -import { TabsContent, TabsList, TabsTrigger } from "@components/ui/tabs" -import { Tabs } from "@components/ui/tabs" -import { CameraModeToggle } from "./menu-button.tsx" -import { Card } from "@components/ui/card" +import * as React from 'react'; + +import { Badge } from '@components/ui/badge'; +import { Card } from '@components/ui/card'; +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@components/ui/dialog'; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle +} from '@components/ui/drawer'; +import { Table, TableHeader, TableBody, TableCell, TableRow, TableHead } from '@components/ui/table'; +import { TabsContent, TabsList, TabsTrigger, Tabs } from '@components/ui/tabs'; + +import { CameraModeToggle } from './menu-button.tsx'; +import { useAssetViewer } from './splat-viewer-context.tsx'; export function useMediaQuery(query: string) { - const [value, setValue] = React.useState(false) + const [value, setValue] = React.useState(false); - React.useEffect(() => { - function onChange(event: MediaQueryListEvent) { - setValue(event.matches) - } + React.useEffect(() => { + function onChange(event: MediaQueryListEvent) { + setValue(event.matches); + } - const result = matchMedia(query) - result.addEventListener("change", onChange) - setValue(result.matches) + const result = matchMedia(query); + result.addEventListener('change', onChange); + // eslint-disable-next-line react-hooks/set-state-in-effect -- initialize from the subscribed media query + setValue(result.matches); - return () => result.removeEventListener("change", onChange) - }, [query]) + return () => result.removeEventListener('change', onChange); + }, [query]); - return value + return value; } - + function CameraHelpTable() { - const { mode } = useAssetViewer() + const { mode } = useAssetViewer(); - return ( - <> - - { mode === "orbit" ? ( - - R - ], - ]} /> - ) : ( - - W - S - ], - ["Left / Right",
- A - D -
], - ["Up / Down",
- Q - E -
], - ["Reset View",
- R -
], - ]} /> - )} -
- - { mode === "orbit" ? () : ( - - )} - - - ) + return ( + <> + + {mode === 'orbit' ? ( + + R + + ] + ]} + /> + ) : ( + + W + S + + ], + [ + 'Left / Right', +
+ A + D +
+ ], + [ + 'Up / Down', +
+ Q + E +
+ ], + [ + 'Reset View', +
+ R +
+ ] + ]} + /> + )} +
+ + {mode === 'orbit' ? ( + + ) : ( + + )} + + + ); } - + function ControlSection({ controls }: { controls: [string, string | React.ReactNode][] }) { - return ( - - - - Input - Action - - - - {controls.map(([label, key]) => ( - - {label} - {key} - - ))} - -
- ) + return ( + + + + Input + Action + + + + {controls.map(([label, key]) => ( + + {label} + {key} + + ))} + +
+ ); } - + export function HelpDialog() { - const { overlay, setOverlay } = useAssetViewer() - const isDesktop = useMediaQuery("(min-width: 768px)") + const { overlay, setOverlay } = useAssetViewer(); + const isDesktop = useMediaQuery('(min-width: 768px)'); + + if (isDesktop) { + return ( + setOverlay(null)}> + + + Camera Controls + Use these controls to navigate. + + + + + + + + Keyboard + + + Touch + + + + + + + ); + } - if (isDesktop) { return ( - setOverlay(null)}> - - - Camera Controls - Use these controls to navigate. - - - - - - - Keyboard - Touch - - - - - - ) - } - - return ( - setOverlay(null)} - shouldScaleBackground={true} - setBackgroundColorOnScale={true} - > - -
- - Controls - Use these controls to navigate. - - - - -
- - - Keyboard - Touch - - - -
- - - - -
-
-
- ) -} \ No newline at end of file + setOverlay(null)} + shouldScaleBackground={true} + setBackgroundColorOnScale={true} + > + +
+ + Controls + Use these controls to navigate. + + + + +
+ + + + Keyboard + + + Touch + + + + +
+ + + +
+
+
+ ); +} diff --git a/packages/blocks/src/splat-viewer/hooks/use-render-on-camera-change.tsx b/packages/blocks/src/splat-viewer/hooks/use-render-on-camera-change.tsx index 3175dbae..303875a8 100644 --- a/packages/blocks/src/splat-viewer/hooks/use-render-on-camera-change.tsx +++ b/packages/blocks/src/splat-viewer/hooks/use-render-on-camera-change.tsx @@ -1,12 +1,12 @@ -import { useApp, useAppEvent } from "@playcanvas/react/hooks"; -import { Entity as PcEntity } from "playcanvas"; -import { useEffect, useRef } from "react"; +import { useApp, useAppEvent } from '@playcanvas/react/hooks'; +import type { Entity as PcEntity } from 'playcanvas'; +import { useEffect, useRef } from 'react'; const nearlyEquals = (a: Float32Array, b: Float32Array, epsilon = 1e-4): boolean => { - for (let i = 0; i < a.length; i++) { - if (Math.abs(a[i] - b[i]) >= epsilon) return false; - } - return true; + for (let i = 0; i < a.length; i++) { + if (Math.abs(a[i] - b[i]) >= epsilon) return false; + } + return true; }; /** @@ -17,59 +17,61 @@ const nearlyEquals = (a: Float32Array, b: Float32Array, epsilon = 1e-4): boolean * @returns {void} This hook does not return a value but updates the rendering state of the application. */ export const useRenderOnCameraChange = (entity: PcEntity | null) => { - const app = useApp(); - const prevWorld = useRef(new Float32Array(16)); - const prevProj = useRef(new Float32Array(16)); - const isVisible = useRef(true); + const app = useApp(); + const prevWorld = useRef(new Float32Array(16)); + const prevProj = useRef(new Float32Array(16)); + const isVisible = useRef(true); - /** - * This hook ensures that rendering only happens when the canvas is visible in the viewport. - * This takes precedence over the camera change detection. Eg if the camera is animating, but - * the canvas is not visible, dont render. - */ + /** + * This hook ensures that rendering only happens when the canvas is visible in the viewport. + * This takes precedence over the camera change detection. Eg if the camera is animating, but + * the canvas is not visible, dont render. + */ - useEffect(() => { - const canvas = app.graphicsDevice.canvas; - if (!canvas) return; + useEffect(() => { + const canvas = app.graphicsDevice.canvas; + if (!canvas) return; - const observer = new IntersectionObserver( - ([entry]) => { - isVisible.current = entry.isIntersecting; - if (!isVisible.current) { - app.renderNextFrame = false; - } - }, - { threshold: 0.1 } - ); + const observer = new IntersectionObserver( + ([entry]) => { + isVisible.current = entry.isIntersecting; + if (!isVisible.current) { + app.renderNextFrame = false; + } + }, + { threshold: 0.1 } + ); - observer.observe(canvas); - return () => observer.disconnect(); - }, [app]); + observer.observe(canvas); + return () => observer.disconnect(); + }, [app]); - /** - * This hook ensures that rendering only happens when the camera has moved or its projection has changed. - * However if the canvas is not visible on the page it will take precedence. - * Don't render if the canvas is not visible regardless of any animations. - */ - useAppEvent('update', () => { - if (!entity || !isVisible.current) return; - const world = entity.getWorldTransform().data; - const proj = entity.camera?.projectionMatrix?.data; + /** + * This hook ensures that rendering only happens when the camera has moved or its projection has changed. + * However if the canvas is not visible on the page it will take precedence. + * Don't render if the canvas is not visible regardless of any animations. + */ + /* eslint-disable react-hooks/immutability -- the engine update callback intentionally controls rendering */ + useAppEvent('update', () => { + if (!entity || !isVisible.current) return; + const world = entity.getWorldTransform().data; + const proj = entity.camera?.projectionMatrix?.data; - if (!proj) { - app.renderNextFrame = true; - return; - } + if (!proj) { + app.renderNextFrame = true; + return; + } - const changed = !nearlyEquals(world, prevWorld.current) || !nearlyEquals(proj, prevProj.current); - - if (!app.renderNextFrame) { - app.renderNextFrame = changed; - } + const changed = !nearlyEquals(world, prevWorld.current) || !nearlyEquals(proj, prevProj.current); - if (app.renderNextFrame) { - prevWorld.current.set(world); - prevProj.current.set(proj); - } - }); -}; \ No newline at end of file + if (!app.renderNextFrame) { + app.renderNextFrame = changed; + } + + if (app.renderNextFrame) { + prevWorld.current.set(world); + prevProj.current.set(proj); + } + }); + /* eslint-enable react-hooks/immutability */ +}; diff --git a/packages/blocks/src/splat-viewer/hooks/use-subscribe.ts b/packages/blocks/src/splat-viewer/hooks/use-subscribe.ts index 23309e27..b9e3e325 100644 --- a/packages/blocks/src/splat-viewer/hooks/use-subscribe.ts +++ b/packages/blocks/src/splat-viewer/hooks/use-subscribe.ts @@ -9,20 +9,20 @@ export type NotifyFn = (value: T) => void; * const { subscribe, notify } = useSubscribe(); * subscribe((value) => console.log(value)); * notify(1); - * + * * @returns `subscribe` and `notify` functions. */ -export function useSubscribe() : [SubscribeFn, NotifyFn] { - const subscribers = useRef(new Set<(value: T) => void>()); +export function useSubscribe(): [SubscribeFn, NotifyFn] { + const subscribers = useRef(new Set<(value: T) => void>()); - const subscribe: SubscribeFn = useCallback((fn: (value: T) => void) => { - subscribers.current.add(fn); - return () => subscribers.current.delete(fn); - }, []); + const subscribe: SubscribeFn = useCallback((fn: (value: T) => void) => { + subscribers.current.add(fn); + return () => subscribers.current.delete(fn); + }, []); - const notify: NotifyFn = useCallback((value: T) => { - subscribers.current.forEach((fn) => fn(value)); - }, []); + const notify: NotifyFn = useCallback((value: T) => { + subscribers.current.forEach((fn) => fn(value)); + }, []); - return [subscribe, notify]; -} \ No newline at end of file + return [subscribe, notify]; +} diff --git a/packages/blocks/src/splat-viewer/index.ts b/packages/blocks/src/splat-viewer/index.ts index 8d02287a..5199bcf3 100644 --- a/packages/blocks/src/splat-viewer/index.ts +++ b/packages/blocks/src/splat-viewer/index.ts @@ -1,19 +1,19 @@ -import { FullScreenButton } from "./full-screen-button.tsx" -import { DownloadButton } from "./download-button.tsx" -import { MenuButton } from "./menu-button.tsx" -import { Controls } from "./controls.tsx" -import { CameraModeToggle } from "./camera-mode-toggle.tsx" -import { HelpButton } from "./help-button.tsx" -import { SplatViewer } from "./splat-viewer.tsx" -import { Progress } from "./progress-indicator.tsx" +import { CameraModeToggle } from './camera-mode-toggle.tsx'; +import { Controls } from './controls.tsx'; +import { DownloadButton } from './download-button.tsx'; +import { FullScreenButton } from './full-screen-button.tsx'; +import { HelpButton } from './help-button.tsx'; +import { MenuButton } from './menu-button.tsx'; +import { Progress } from './progress-indicator.tsx'; +import { SplatViewer } from './splat-viewer.tsx'; export { - FullScreenButton, - DownloadButton, - MenuButton, - Controls, - CameraModeToggle, - HelpButton, - SplatViewer as Splat, - Progress, -}; \ No newline at end of file + FullScreenButton, + DownloadButton, + MenuButton, + Controls, + CameraModeToggle, + HelpButton, + SplatViewer as Splat, + Progress +}; diff --git a/packages/blocks/src/splat-viewer/menu-button.tsx b/packages/blocks/src/splat-viewer/menu-button.tsx index 4b246161..749af669 100644 --- a/packages/blocks/src/splat-viewer/menu-button.tsx +++ b/packages/blocks/src/splat-viewer/menu-button.tsx @@ -1,17 +1,47 @@ -"use client"; +'use client'; -import { Button } from "@components/ui/button"; -import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuTrigger } from "@components/ui/dropdown-menu"; -import { useMediaQuery } from "./help-dialog.tsx"; -import { EllipsisVerticalIcon, DownloadIcon, MinimizeIcon, MaximizeIcon, HelpCircleIcon, Rotate3dIcon, Move3DIcon, RotateCcwIcon } from "lucide-react"; -import { Drawer, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerClose } from "@components/ui/drawer"; -import { useAssetViewer } from "./splat-viewer-context.ts"; -import { ToggleGroupItem } from "@components/ui/toggle-group"; -import { ToggleGroup } from "@components/ui/toggle-group"; -import { Card } from "@components/ui/card"; -import { Switch } from "@components/ui/switch"; -import { Separator } from "@components/ui/separator"; -import { CameraMode } from "./splat-viewer.tsx"; +import { + EllipsisVerticalIcon, + DownloadIcon, + MinimizeIcon, + MaximizeIcon, + HelpCircleIcon, + Rotate3dIcon, + Move3DIcon, + RotateCcwIcon +} from 'lucide-react'; + +import { Button } from '@components/ui/button'; +import { Card } from '@components/ui/card'; +import { + Drawer, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerClose +} from '@components/ui/drawer'; +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger +} from '@components/ui/dropdown-menu'; +import { Separator } from '@components/ui/separator'; +import { Switch } from '@components/ui/switch'; +import { ToggleGroupItem, ToggleGroup } from '@components/ui/toggle-group'; + +import { useMediaQuery } from './help-dialog.tsx'; +import { useAssetViewer } from './splat-viewer-context.tsx'; +import type { CameraMode } from './splat-viewer.tsx'; function MenuItemsDesktop({ mode, setMode, @@ -19,71 +49,68 @@ function MenuItemsDesktop({ triggerDownload, toggleFullscreen, resetCamera - }: ReturnType) { +}: ReturnType) { return ( - <> - - Settings - - setOverlay("help")}> - Help - ⇧F - - triggerDownload()}> - Download - ⇧⌘D - - toggleFullscreen()}> - Fullscreen - ⇧⌘F - - resetCamera()}> - Reset View - R - - - Auto Rotate - ⇧⌘R - - - - - - Controls - setMode(value as CameraMode)}> - - Orbit - ⌘O - - - Fly - ⌘F - - - - - ) - } + <> + + Settings + + setOverlay('help')}> + Help + ⇧F + + triggerDownload()}> + Download + ⇧⌘D + + toggleFullscreen()}> + Fullscreen + ⇧⌘F + + resetCamera()}> + Reset View + R + + + Auto Rotate + ⇧⌘R + + + + + + Controls + setMode(value as CameraMode)}> + + Orbit + ⌘O + + + Fly + ⌘F + + + + + ); +} - export function CameraModeToggle() { +export function CameraModeToggle() { const { mode, setMode } = useAssetViewer(); const safeSetMode = (mode: string) => { - if (mode === "orbit" || mode === "fly") { + if (mode === 'orbit' || mode === 'fly') { setMode(mode); } - } + }; return (
Camera mode
- { mode === "orbit" && Great for inspecting an object. } - { mode === "fly" && Ideal for navigating larger scenes. } + {mode === 'orbit' && Great for inspecting an object.} + {mode === 'fly' && Ideal for navigating larger scenes.}
@@ -95,10 +122,10 @@ function MenuItemsDesktop({
- ) - } + ); +} - function MenuItemsMobile({ +function MenuItemsMobile({ setOverlay, triggerDownload, toggleFullscreen, @@ -106,117 +133,140 @@ function MenuItemsDesktop({ autoRotate, setAutoRotate, resetCamera - }: ReturnType) { +}: ReturnType) { return ( -
- - - - +
+ + + - {/* Auto rotate */} - -
-
- Auto rotate - - { autoRotate - ? Automatically rotate the camera around the object. - : Disable automatic rotation. - } - + {/* Auto rotate */} + +
+
+ Auto rotate + + {autoRotate ? ( + Automatically rotate the camera around the object. + ) : ( + Disable automatic rotation. + )} + +
+ { + setAutoRotate(!autoRotate); + setOverlay(null); + }} + />
- { - setAutoRotate(!autoRotate); - setOverlay(null); - }}/> -
- + + + - - -
-

Settings

- - - - +
+

Settings

+ + + + +
-
- ) - } + ); +} function MenuButton() { + const isDesktop = useMediaQuery('(min-width: 768px)'); + const props = useAssetViewer(); + const { overlay, setOverlay } = props; - const isDesktop = useMediaQuery("(min-width: 768px)"); - const props = useAssetViewer(); - const {overlay, setOverlay} = props - if (isDesktop) { return ( - ) + ); } return ( - <> - - setOverlay(null)} shouldScaleBackground={true} setBackgroundColorOnScale={true}> - -
- - Settings - Viewer options and controls. - - - - - -
-
-
- - ) + <> + + setOverlay(null)} + shouldScaleBackground={true} + setBackgroundColorOnScale={true} + > + +
+ + Settings + Viewer options and controls. + + + + + +
+
+
+ + ); } -export { MenuButton } \ No newline at end of file +export { MenuButton }; diff --git a/packages/blocks/src/splat-viewer/progress-indicator.tsx b/packages/blocks/src/splat-viewer/progress-indicator.tsx index d60e875e..be0de29c 100644 --- a/packages/blocks/src/splat-viewer/progress-indicator.tsx +++ b/packages/blocks/src/splat-viewer/progress-indicator.tsx @@ -1,51 +1,53 @@ -"use client"; +'use client'; -import { useEffect, useRef, useState } from "react"; -import { useAssetViewer } from "./splat-viewer-context.ts"; -import { cn } from "@lib/utils"; +import { useEffect, useRef, useState } from 'react'; + +import { cn } from '@lib/utils'; + +import { useAssetViewer } from './splat-viewer-context.tsx'; type ProgressProps = { - variant?: "top" | "bottom"; - className?: string; - style?: React.CSSProperties; + variant?: 'top' | 'bottom'; + className?: string; + style?: React.CSSProperties; }; -export function Progress({ variant = "top", className, style }: ProgressProps) { - const { subscribe } = useAssetViewer(); - const ref = useRef(null); - const [visible, setVisible] = useState(true); - const timeoutRef = useRef | null>(null); - - useEffect(() => { - const unsubscribe = subscribe((progress) => { - if (!ref.current) return; - ref.current.style.width = `${progress * 100}%`; - - if (progress >= 1) { - timeoutRef.current = setTimeout(() => setVisible(false), 500); - } else { - setVisible(true); - } - }); - - return () => { - unsubscribe(); - if (timeoutRef.current) clearTimeout(timeoutRef.current); - }; - }, [subscribe]); - - if (!visible) return null; - - return ( -
- ); -} \ No newline at end of file +export function Progress({ variant = 'top', className, style }: ProgressProps) { + const { subscribe } = useAssetViewer(); + const ref = useRef(null); + const [visible, setVisible] = useState(true); + const timeoutRef = useRef | null>(null); + + useEffect(() => { + const unsubscribe = subscribe((progress) => { + if (!ref.current) return; + ref.current.style.width = `${progress * 100}%`; + + if (progress >= 1) { + timeoutRef.current = setTimeout(() => setVisible(false), 500); + } else { + setVisible(true); + } + }); + + return () => { + unsubscribe(); + if (timeoutRef.current) clearTimeout(timeoutRef.current); + }; + }, [subscribe]); + + if (!visible) return null; + + return ( +
+ ); +} diff --git a/packages/blocks/src/splat-viewer/smart-camera.tsx b/packages/blocks/src/splat-viewer/smart-camera.tsx index 6f04fd44..3f33e678 100644 --- a/packages/blocks/src/splat-viewer/smart-camera.tsx +++ b/packages/blocks/src/splat-viewer/smart-camera.tsx @@ -1,46 +1,45 @@ -"use client"; - -import { useEffect, useRef, useState } from "react"; -import { Entity } from "@playcanvas/react"; -import { Camera, Script } from "@playcanvas/react/components"; -import { useTimeline, useAssetViewer } from "./splat-viewer-context.ts"; -import { Vec3, Entity as PcEntity } from "playcanvas"; - -import { AnimationTrack, AnimCamera, createRotationTrack } from "./utils/animation.ts"; // assumed -import { computeStartingPose, Pose, PoseType } from "./utils/pose.ts"; -import { useApp, useParent } from "@playcanvas/react/hooks"; -import { PostEffectsSettings, StaticPostEffects } from "./utils/effects.ts"; -import { paris, neutral, noir } from "./utils/style.ts"; - -// @ts-expect-error There is no type definition for the camera-controls script -import { CameraControls } from "playcanvas/scripts/esm/camera-controls.mjs"; -import { useRenderOnCameraChange } from "./hooks/use-render-on-camera-change.ts"; +'use client'; + +import { Entity } from '@playcanvas/react'; +import { Camera, Script } from '@playcanvas/react/components'; +import { useApp, useParent } from '@playcanvas/react/hooks'; +import type { Entity as PcEntity } from 'playcanvas'; +import { Vec3 } from 'playcanvas'; +// @ts-expect-error there is no type definition for the camera-controls script +import { CameraControls } from 'playcanvas/scripts/esm/camera-controls.mjs'; +import { useEffect, useRef, useState } from 'react'; + +import { useRenderOnCameraChange } from './hooks/use-render-on-camera-change.tsx'; +import { useTimeline, useAssetViewer } from './splat-viewer-context.tsx'; +import type { AnimationTrack } from './utils/animation.ts'; +import { AnimCamera, createRotationTrack } from './utils/animation.ts'; // assumed +import type { PostEffectsSettings } from './utils/effects.tsx'; +import { StaticPostEffects } from './utils/effects.tsx'; +import type { PoseType } from './utils/pose.ts'; +import { computeStartingPose, Pose } from './utils/pose.ts'; +import { paris, neutral, noir } from './utils/style.ts'; const variants = new Map([ - ['paris', paris], - ['neutral', neutral], - ['noir', noir] + ['paris', paris], + ['neutral', neutral], + ['noir', noir] ]); -const length = (a: [number, number, number], b: [number, number, number]) => Math.sqrt( - Math.pow(a[0] - b[0], 2) + - Math.pow(a[1] - b[1], 2) + - Math.pow(a[2] - b[2], 2) -) +const length = (a: [number, number, number], b: [number, number, number]) => + Math.sqrt(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2) + Math.pow(a[2] - b[2], 2)); type CameraControlsProps = { /* The focus point of the camera */ - focus?: [number, number, number] - enablePan: boolean, - enableFly: boolean, - enableOrbit: boolean, - enabled?: boolean, - distance?: number, - animate?: boolean, -} + focus?: [number, number, number]; + enablePan: boolean; + enableFly: boolean; + enableOrbit: boolean; + enabled?: boolean; + distance?: number; + animate?: boolean; +}; function CameraController({ focus = [0, 0, 0], distance = 0, animate = false, ...props }: CameraControlsProps) { - const entity = useParent(); useEffect(() => { @@ -53,161 +52,164 @@ function CameraController({ focus = [0, 0, 0], distance = 0, animate = false, .. const controls = (entity.script?.cameraControls || entity.script?._CameraControls) as CameraControls; if (controls) { controls.focus(new Vec3().fromArray(focus), false); - } }, [focus, distance, animate]); - return (<> -