Update AGENTS.md. Start fixing old weird errors

This commit is contained in:
gib committed 2026-03-26 12:05:12 -05:00
1 parent 0bc04dbf6b
commit d16f4287ce
96 files changed
+16030 -7017

No files matched your search

+3 -1
View File
@@ -17,8 +17,10 @@ NEXT_PUBLIC_SENTRY_PROJECT_NAME=example
CONVEX_SELF_HOSTED_URL=https://api.convex.example.com # convex-backend:3210
CONVEX_SELF_HOSTED_ADMIN_KEY= # Generate after hosted on docker
# Convex Auth
CONVEX_SITE_URL=https://convex.example.com # convex-backend:3211
CONVEX_SITE_URL=http://localhost:3000 # Always localhost:3000 for local dev; update in Convex Dashboard for production
USESEND_API_KEY=
USESEND_URL=https://usesend.example.com
USESEND_FROM_EMAIL=My App <[email protected]>
AUTH_AUTHENTIK_ID=
AUTH_AUTHENTIK_SECRET=
AUTH_AUTHENTIK_ISSUER=
View File
Whitespace-only changes.
+1255 -464
View File
File diff suppressed because it is too large. Load diff
+192 -95
View File
@@ -32,15 +32,21 @@ A production-ready Turborepo starter with Next.js, Expo, and self-hosted Convex
## Getting Started
This is a self-hosted template. The full setup requires a server (home server or VPS)
to host the Convex backend and dashboard, and a reverse proxy (nginx-proxy-manager is
recommended) to expose them over HTTPS. The Next.js app can run locally in dev mode
once the Convex containers are reachable.
### Prerequisites
- [Bun](https://bun.sh) (v1.2.19+)
- [Bun](https://bun.sh) (v1.2+)
- [Docker](https://www.docker.com/) & Docker Compose (for self-hosted Convex)
- Node.js 22.20.0+ (for compatibility)
- Node.js 22+ (for compatibility)
- A running nginx-proxy-manager instance (or similar reverse proxy) to expose Convex over HTTPS
### Development Setup
---
#### 1. Clone & Install
### Step 1 — Clone & Install
```bash
git clone https://git.gbrown.org/gib/convex-monorepo
@@ -48,88 +54,152 @@ cd convex-monorepo
bun install
```
#### 2. Configure Environment Variables
Create a `.env` file in the project root with the following variables:
If you're using this as a template for a new project, remove the existing remote and
add your own:
```bash
# Convex Backend (Self-Hosted)
CONVEX_SELF_HOSTED_URL=https://api.convex.example.com
CONVEX_SELF_HOSTED_ADMIN_KEY=<generated>
CONVEX_SITE_URL=https://convex.example.com
# Next.js Public
NEXT_PUBLIC_CONVEX_URL=https://api.convex.example.com
NEXT_PUBLIC_SITE_URL=https://example.com
NEXT_PUBLIC_PLAUSIBLE_URL=https://plausible.example.com
NEXT_PUBLIC_SENTRY_DSN=
NEXT_PUBLIC_SENTRY_URL=
NEXT_PUBLIC_SENTRY_ORG=
NEXT_PUBLIC_SENTRY_PROJECT_NAME=
# Server-side
SENTRY_AUTH_TOKEN=
# Auth (will be synced to Convex)
AUTH_AUTHENTIK_ID=
AUTH_AUTHENTIK_SECRET=
AUTH_AUTHENTIK_ISSUER=
USESEND_API_KEY=
git remote remove origin
git remote add origin https://your-git-host.com/your/new-repo.git
```
**For local development:** Use `http://localhost:3210` for Convex URLs.
---
#### 3. Configure Docker Environment
### Step 2 — Configure the Docker Environment
Check and update environment variables in `docker/.env` for your deployment:
The `docker/` directory contains everything needed to run the Convex backend and the
Next.js app in production.
```bash
cd docker/
cp .env.example .env
# Edit .env with your configuration
```
#### 4. Start Self-Hosted Convex
Edit `docker/.env` and fill in your values. The variables below the Next.js app section
control the Convex containers — you'll need to choose:
Spin up the Convex backend and dashboard:
- `INSTANCE_NAME` — a unique name for your Convex instance
- `INSTANCE_SECRET` — a secret string (generate something random)
- `CONVEX_CLOUD_ORIGIN` — the public HTTPS URL for your Convex backend API (e.g. `https://api.convex.example.com`)
- `CONVEX_SITE_ORIGIN` — the public HTTPS URL for Convex Auth HTTP routes (e.g. `https://api.convex.example.com`)
- `NEXT_PUBLIC_DEPLOYMENT_URL` — the URL for the Convex dashboard (e.g. `https://dashboard.convex.example.com`)
---
### Step 3 — Start the Convex Containers
```bash
cd docker/
docker compose up -d convex-backend convex-dashboard
sudo docker compose up -d convex-backend convex-dashboard
```
**Services:**
Wait a moment for `convex-backend` to pass its health check, then verify both
containers are running:
- **Backend:** http://localhost:3210
- **Dashboard:** http://localhost:6791
```bash
sudo docker compose ps
```
#### 5. Generate Auth Keys & Sync Environment Variables
Reverse-proxy the two Convex services through nginx-proxy-manager (or your preferred
proxy) to the URLs you chose in Step 2. Both must be reachable over HTTPS before you
can proceed.
Generate JWT keys for Convex Auth:
---
### Step 4 — Generate the Convex Admin Key
With the backend container running, generate the admin key:
```bash
cd docker/
./generate_convex_admin_key
```
Copy the printed key — you'll need it as `CONVEX_SELF_HOSTED_ADMIN_KEY` in the root
`.env` file.
---
### Step 5 — Configure Root Environment Variables
Create the root `.env` file:
```bash
# From the repo root
cp .env.example .env
```
Fill out all values in `/.env`:
```bash
# Next.js
NODE_ENV=development
SENTRY_AUTH_TOKEN= # From your self-hosted Sentry
NEXT_PUBLIC_SITE_URL=https://example.com
NEXT_PUBLIC_CONVEX_URL=https://api.convex.example.com
NEXT_PUBLIC_PLAUSIBLE_URL=https://plausible.example.com
NEXT_PUBLIC_SENTRY_DSN=
NEXT_PUBLIC_SENTRY_URL=https://sentry.example.com
NEXT_PUBLIC_SENTRY_ORG=sentry
NEXT_PUBLIC_SENTRY_PROJECT_NAME=my-project
# Convex
CONVEX_SELF_HOSTED_URL=https://api.convex.example.com
CONVEX_SELF_HOSTED_ADMIN_KEY= # From Step 4
CONVEX_SITE_URL=http://localhost:3000 # Always localhost:3000 for local dev
# Auth (synced to Convex in Step 6)
USESEND_API_KEY=
USESEND_URL=https://usesend.example.com
USESEND_FROM_EMAIL=My App <[email protected]>
AUTH_AUTHENTIK_ID=
AUTH_AUTHENTIK_SECRET=
AUTH_AUTHENTIK_ISSUER=https://auth.example.com/application/o/my-app/
```
---
### Step 6 — Generate JWT Keys & Sync Environment Variables to Convex
Generate the RS256 JWT keypair needed for Convex Auth:
```bash
cd packages/backend
bun run scripts/generateKeys.mjs
```
Sync environment variables to Convex deployment (via CLI or Dashboard):
This prints `JWT_PRIVATE_KEY` and `JWKS` values. Sync them to your Convex deployment
along with all other backend environment variables:
```bash
cd packages/backend
bun with-env npx convex env set AUTH_AUTHENTIK_ID "your-value"
bun with-env npx convex env set AUTH_AUTHENTIK_SECRET "your-value"
# From packages/backend/
bun with-env npx convex env set JWT_PRIVATE_KEY "your-private-key"
bun with-env npx convex env set JWKS "your-jwks"
bun with-env npx convex env set AUTH_AUTHENTIK_ID "your-client-id"
bun with-env npx convex env set AUTH_AUTHENTIK_SECRET "your-client-secret"
bun with-env npx convex env set AUTH_AUTHENTIK_ISSUER "your-issuer-url"
bun with-env npx convex env set USESEND_API_KEY "your-api-key"
bun with-env npx convex env set CONVEX_SITE_URL "http://localhost:3000"
bun with-env npx convex env set USESEND_URL "https://usesend.example.com"
bun with-env npx convex env set USESEND_FROM_EMAIL "My App <[email protected]>"
```
**Important:** For local development, set `CONVEX_SITE_URL` to `http://localhost:3000`.
**For production auth to work**, you must also update `CONVEX_SITE_URL` in the Convex
Dashboard to your production Next.js URL. Go to
`https://dashboard.convex.example.com` → Settings → Environment Variables and set:
#### 6. Start Development Server
```
CONVEX_SITE_URL = https://example.com
```
The root `.env` value of `http://localhost:3000` is correct for local dev and should
not be changed — only update it in the Dashboard for production.
---
### Step 7 — Start the Development Server
```bash
# From project root
bun dev:next # Next.js app + Convex backend
bun dev:next # Next.js app + Convex backend (most common)
# or
bun dev # All apps (Next.js + Expo + Backend)
```
@@ -137,7 +207,7 @@ bun dev # All apps (Next.js + Expo + Backend)
**App URLs:**
- **Next.js:** http://localhost:3000
- **Convex Dashboard:** http://localhost:6791
- **Convex Dashboard:** https://dashboard.convex.example.com
---
@@ -193,7 +263,7 @@ convex-monorepo/
│
├── packages/
│ ├── backend/ # Convex backend
│ │ ├── convex/ # Convex functions (synced to cloud)
│ │ ├── convex/ # Convex functions (synced to deployment)
│ │ ├── scripts/ # Utilities (generateKeys.mjs)
│ │ └── types/ # Shared types
│ └── ui/ # shadcn/ui components
@@ -223,16 +293,16 @@ convex-monorepo/
- **OAuth:** Authentik SSO integration
- **Password:** Custom password auth with email verification
- **OTP:** Email verification via self-hosted UseSend
- **Session Management:** Secure cookie-based sessions
- **Session Management:** Secure cookie-based sessions (30-day max age)
### Next.js App
- **App Router:** Next.js 16 with React Server Components
- **Data Preloading:** Server-side data fetching with Convex
- **Middleware:** Route protection & authentication
- **Styling:** Tailwind CSS v4 with dark mode
- **Analytics:** Plausible (privacy-focused)
- **Monitoring:** Sentry error tracking
- **Data Preloading:** SSR data fetching with `preloadQuery` + `usePreloadedQuery`
- **Middleware:** Route protection & IP-based security (`src/proxy.ts`)
- **Styling:** Tailwind CSS v4 with dark mode (OKLCH-based theme)
- **Analytics:** Plausible (privacy-focused, proxied through Next.js)
- **Monitoring:** Sentry error tracking & performance
### Backend
@@ -244,87 +314,113 @@ convex-monorepo/
### Developer Experience
- **Monorepo:** Turborepo for efficient builds
- **Monorepo:** Turborepo for efficient builds and caching
- **Type Safety:** Strict TypeScript throughout
- **Code Quality:** ESLint + Prettier with auto-fix
- **Hot Reload:** Fast refresh for all packages
- **Catalog Deps:** Centralized version management
- **Catalog Deps:** Centralized dependency version management
---
## Deployment
### Docker (Recommended)
### Production Deployment (Docker)
Build and deploy with Docker Compose:
Once the Convex containers are running (they only need to be started once), deploying
a new version of the Next.js app is a two-command workflow:
```bash
# SSH onto your server, then:
cd docker/
# Build the new image
sudo docker compose build next-app
# Deploy it
sudo docker compose up -d next-app
```
To start all services from scratch:
```bash
cd docker/
# Start all services
docker compose up -d
# View logs
docker compose logs -f
# Stop services
docker compose down
sudo docker compose up -d convex-backend convex-dashboard
# Wait for backend health check to pass, then:
sudo docker compose up -d next-app
```
**Services:**
- `next-app` - Next.js standalone build
- `convex-backend` - Convex backend (port 3210)
- `convex-dashboard` - Admin dashboard (port 6791)
- `next-app` — Next.js standalone build
- `convex-backend` — Convex backend (port 3210)
- `convex-dashboard` — Admin dashboard (port 6791)
**Network:** Uses `nginx-bridge` network (configurable in `compose.yml`).
**Network:** Uses `nginx-bridge` Docker network (reverse proxy via nginx-proxy-manager).
### Production Checklist
- [ ] Update environment variables in `docker/.env`
- [ ] Generate `CONVEX_SELF_HOSTED_ADMIN_KEY`
- [ ] Configure reverse proxy (Nginx/Traefik)
- [ ] Set up SSL certificates
- [ ] Sync auth environment variables to Convex
- [ ] Configure backup strategy for `docker/data/`
- [ ] Test authentication flow
- [ ] Enable Sentry error tracking
- [ ] Fill out `docker/.env` with your domain names and secrets
- [ ] Start `convex-backend` and `convex-dashboard` containers
- [ ] Generate and set `CONVEX_SELF_HOSTED_ADMIN_KEY` via `./generate_convex_admin_key`
- [ ] Reverse-proxy both Convex services via nginx-proxy-manager with SSL
- [ ] Fill out root `/.env` with all environment variables
- [ ] Generate JWT keys and sync all env vars to Convex (`bun with-env npx convex env set ...`)
- [ ] Update `CONVEX_SITE_URL` in the Convex Dashboard to your production Next.js URL
- [ ] Build and start the `next-app` container
- [ ] Back up `docker/data/` regularly (contains all Convex database data)
---
## Documentation
- **[AGENTS.md](./AGENTS.md)** - Comprehensive guide for AI agents & developers
- **[Convex Docs](https://docs.convex.dev)** - Official Convex documentation
- **[Turborepo Docs](https://turbo.build/repo/docs)** - Turborepo documentation
- **[Next.js Docs](https://nextjs.org/docs)** - Next.js documentation
- **[AGENTS.md](./AGENTS.md)** — Comprehensive guide for AI agents & developers
- **[Convex Docs](https://docs.convex.dev)** — Official Convex documentation
- **[Turborepo Docs](https://turbo.build/repo/docs)** — Turborepo documentation
- **[Next.js Docs](https://nextjs.org/docs)** — Next.js documentation
---
## Troubleshooting
### Backend typecheck shows help message
### Backend typecheck shows TypeScript help message
This is expected behavior. The backend package follows Convex's structure with only `convex/tsconfig.json` (no root tsconfig). See [AGENTS.md](./AGENTS.md) for details.
This is expected behavior. The backend package follows Convex's structure with only
`convex/tsconfig.json` (no root tsconfig). Running `bun typecheck` from the repo root
will show TypeScript's help text for `@gib/backend` — this is not an error.
### Imports from Convex require .js extension
### Imports from Convex require `.js` extension
The project uses ESM (`"type": "module"`), requiring explicit file extensions:
The project uses ESM (`"type": "module"`), which requires explicit file extensions:
```typescript
// ✅ Correct
// ❌ Wrong
import type { Id } from '@gib/backend/convex/_generated/dataModel.js';
// ❌ Wrong — will fail at runtime
import { api } from '@gib/backend/convex/_generated/api';
import { api } from '@gib/backend/convex/_generated/api.js';
```
### Docker containers won't start
1. Check Docker logs: `docker compose logs`
1. Check Docker logs: `sudo docker compose logs`
2. Verify environment variables in `docker/.env`
3. Ensure ports 3210 and 6791 are available
4. Check network configuration (`nginx-bridge`)
3. Ensure the `nginx-bridge` network exists: `sudo docker network create nginx-bridge`
4. Check that the required ports (3210, 6791) are not already in use
### Auth doesn't work in production
Make sure `CONVEX_SITE_URL` is set to your production Next.js URL in the **Convex
Dashboard** (not just in the root `.env` file). The root `.env` should always contain
`http://localhost:3000`; the Dashboard must have your production URL.
### Catalog updates break workspace
After updating dependencies, if you see `sherif` errors on `bun install`:
1. Never use `bun update` inside individual package directories
2. Edit the version in root `package.json` catalog section instead
3. Run `bun install` from the root
4. Verify with `bun lint:ws`
---
@@ -337,6 +433,7 @@ This is a personal monorepo template. Feel free to fork and adapt for your needs
- Single quotes, trailing commas
- 80 character line width
- ESLint + Prettier enforced
- `const fn = () => {}` over `function fn()` (strong preference)
- Import order: Types → React → Next → Third-party → @gib → Local
Run `bun lint:fix` and `bun format:fix` before committing.
+1 -1
View File
@@ -1 +1 @@
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21"],{"key":"22","value":"23"},{"key":"24","value":"25"},{"key":"26","value":"27"},{"key":"28","value":"29"},{"key":"30","value":"31"},{"key":"32","value":"33"},{"key":"34","value":"35"},{"key":"36","value":"37"},{"key":"38","value":"39"},{"key":"40","value":"41"},{"key":"42","value":"43"},{"key":"44","value":"45"},{"key":"46","value":"47"},{"key":"48","value":"49"},{"key":"50","value":"51"},{"key":"52","value":"53"},{"key":"54","value":"55"},{"key":"56","value":"57"},{"key":"58","value":"59"},{"key":"60","value":"61"},{"key":"62","value":"63"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/package.json",{"size":2249,"mtime":1766222924000,"hash":"64","data":"65"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/index.ts",{"size":28,"mtime":1768155639000,"hash":"66","data":"67"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/metro.config.js",{"size":511,"mtime":1768155639000,"hash":"68","data":"69"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/index.tsx",{"size":5019,"mtime":1768372346938,"hash":"70","data":"71"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/turbo.json",{"size":163,"mtime":1766222924000,"hash":"72","data":"73"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/postcss.config.js",{"size":66,"mtime":1768155639000,"hash":"74","data":"75"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eas.json",{"size":567,"mtime":1766222924000,"hash":"76","data":"77"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/.expo-shared/assets.json",{"size":155,"mtime":1766222924000,"hash":"78","data":"79"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/_layout.tsx",{"size":927,"mtime":1768155639000,"hash":"80","data":"81"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/nativewind-env.d.ts",{"size":246,"mtime":1766222924000,"hash":"82","data":"83"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/auth.ts",{"size":398,"mtime":1768155639000,"hash":"84","data":"85"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/post/[id].tsx",{"size":757,"mtime":1768372346967,"hash":"86","data":"87"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/styles.css",{"size":90,"mtime":1768155639000,"hash":"88","data":"89"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/session-store.ts",{"size":272,"mtime":1768155639000,"hash":"90","data":"91"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-dark.png",{"size":19633,"mtime":1766222924000,"hash":"92"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eslint.config.mts",{"size":275,"mtime":1768155639000,"hash":"93","data":"94"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/api.tsx",{"size":1326,"mtime":1768155639000,"hash":"95","data":"96"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/base-url.ts",{"size":880,"mtime":1768155639000,"hash":"97","data":"98"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/app.config.ts",{"size":1333,"mtime":1768155639000,"hash":"99","data":"100"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-light.png",{"size":19133,"mtime":1766222924000,"hash":"101"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/tsconfig.json",{"size":387,"mtime":1766228480000,"hash":"102","data":"103"},"d8763702c14cdc382dcfb84f6f9a068f",{"hashOfOptions":"104"},"11cdbef6afa001cd39bc187041ca6865",{"hashOfOptions":"105"},"dbe97bcde588a81538bbcd6a9befdddd",{"hashOfOptions":"106"},"73c235a66242df70b69394cce29d1ed3",{"hashOfOptions":"107"},"c7d4dcf839dfeaa02e0407adfd5e47a6",{"hashOfOptions":"108"},"b7edffce093c4c84092cc93f3dc208ef",{"hashOfOptions":"109"},"a3c1487f8318513ae7c156acc857fde2",{"hashOfOptions":"110"},"0f7f54c7161b8403d3bc42d91f59cd91",{"hashOfOptions":"111"},"8e407b4b1b0c0bd9c862a00243344be3",{"hashOfOptions":"112"},"d4d589c153ac8b5e7bf0fb130a5b5a7d",{"hashOfOptions":"113"},"cecbed1604a530a7cc099fecddddd76c",{"hashOfOptions":"114"},"ead19d73283f9d8e08b55c896c9fd570",{"hashOfOptions":"115"},"52a1d72379b952dd802f47e1865bd0da",{"hashOfOptions":"116"},"1bc3e15a40c117eecc51294886ea9b38",{"hashOfOptions":"117"},"1e8ac0d261e95efb19d290ffcf70ce36","1c1710ce3de3ce02e8054cc3787c8579",{"hashOfOptions":"118"},"5ff899a601102659dcbd2900e415ce8b",{"hashOfOptions":"119"},"dd2007a211e323deabb3f7fa7d16313f",{"hashOfOptions":"120"},"4f49c6df7733f874fbe72b4e20b3092b",{"hashOfOptions":"121"},"863da15dbd856008b7c24077ca746d91","6937fb7370f1e17491df649888d6ecc9",{"hashOfOptions":"122"},"1820601142","1684748001","3531839294","2748941218","956511134","132171752","3925902565","1550174236","2506462393","526883382","4111358426","2953691686","2383171816","2740949298","3787272667","3740930138","4230803759","3315245788","3164486579"]
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22"],{"key":"23","value":"24"},{"key":"25","value":"26"},{"key":"27","value":"28"},{"key":"29","value":"30"},{"key":"31","value":"32"},{"key":"33","value":"34"},{"key":"35","value":"36"},{"key":"37","value":"38"},{"key":"39","value":"40"},{"key":"41","value":"42"},{"key":"43","value":"44"},{"key":"45","value":"46"},{"key":"47","value":"48"},{"key":"49","value":"50"},{"key":"51","value":"52"},{"key":"53","value":"54"},{"key":"55","value":"56"},{"key":"57","value":"58"},{"key":"59","value":"60"},{"key":"61","value":"62"},{"key":"63","value":"64"},{"key":"65","value":"66"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/index.tsx",{"size":5019,"mtime":1768372346938,"hash":"67","data":"68"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/index.ts",{"size":28,"mtime":1768155639000,"hash":"69","data":"70"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-dark.png",{"size":19633,"mtime":1766222924000,"hash":"71"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/postcss.config.js",{"size":66,"mtime":1768155639000,"hash":"72","data":"73"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/auth.ts",{"size":398,"mtime":1768155639000,"hash":"74","data":"75"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/post/[id].tsx",{"size":757,"mtime":1768372346967,"hash":"76","data":"77"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/api.tsx",{"size":1326,"mtime":1768155639000,"hash":"78","data":"79"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eas.json",{"size":567,"mtime":1766222924000,"hash":"80","data":"81"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/app/_layout.tsx",{"size":927,"mtime":1768155639000,"hash":"82","data":"83"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/styles.css",{"size":90,"mtime":1768155639000,"hash":"84","data":"85"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/.cache/.prettiercache",{"size":4929,"mtime":1774544485533},"/home/gib/Documents/Code/convex-monorepo/apps/expo/assets/icon-light.png",{"size":19133,"mtime":1766222924000,"hash":"86"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/package.json",{"size":2255,"mtime":1774544484676,"hash":"87","data":"88"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/turbo.json",{"size":171,"mtime":1774031879321,"hash":"89","data":"90"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/eslint.config.mts",{"size":275,"mtime":1768155639000,"hash":"91","data":"92"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/tsconfig.json",{"size":387,"mtime":1766228480000,"hash":"93","data":"94"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/metro.config.js",{"size":511,"mtime":1768155639000,"hash":"95","data":"96"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/nativewind-env.d.ts",{"size":246,"mtime":1766222924000,"hash":"97","data":"98"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/base-url.ts",{"size":880,"mtime":1768155639000,"hash":"99","data":"100"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/.expo-shared/assets.json",{"size":155,"mtime":1766222924000,"hash":"101","data":"102"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/src/utils/session-store.ts",{"size":272,"mtime":1768155639000,"hash":"103","data":"104"},"/home/gib/Documents/Code/convex-monorepo/apps/expo/app.config.ts",{"size":1333,"mtime":1768155639000,"hash":"105","data":"106"},"73c235a66242df70b69394cce29d1ed3",{"hashOfOptions":"107"},"11cdbef6afa001cd39bc187041ca6865",{"hashOfOptions":"108"},"1e8ac0d261e95efb19d290ffcf70ce36","b7edffce093c4c84092cc93f3dc208ef",{"hashOfOptions":"109"},"cecbed1604a530a7cc099fecddddd76c",{"hashOfOptions":"110"},"ead19d73283f9d8e08b55c896c9fd570",{"hashOfOptions":"111"},"5ff899a601102659dcbd2900e415ce8b",{"hashOfOptions":"112"},"a3c1487f8318513ae7c156acc857fde2",{"hashOfOptions":"113"},"8e407b4b1b0c0bd9c862a00243344be3",{"hashOfOptions":"114"},"52a1d72379b952dd802f47e1865bd0da",{"hashOfOptions":"115"},"863da15dbd856008b7c24077ca746d91","d8763702c14cdc382dcfb84f6f9a068f",{"hashOfOptions":"116"},"c7d4dcf839dfeaa02e0407adfd5e47a6",{"hashOfOptions":"117"},"1c1710ce3de3ce02e8054cc3787c8579",{"hashOfOptions":"118"},"6937fb7370f1e17491df649888d6ecc9",{"hashOfOptions":"119"},"dbe97bcde588a81538bbcd6a9befdddd",{"hashOfOptions":"120"},"d4d589c153ac8b5e7bf0fb130a5b5a7d",{"hashOfOptions":"121"},"dd2007a211e323deabb3f7fa7d16313f",{"hashOfOptions":"122"},"0f7f54c7161b8403d3bc42d91f59cd91",{"hashOfOptions":"123"},"1bc3e15a40c117eecc51294886ea9b38",{"hashOfOptions":"124"},"4f49c6df7733f874fbe72b4e20b3092b",{"hashOfOptions":"125"},"3000879843","3103968608","384110377","68329755","141502567","3992868763","1050155876","2025343866","4147067111","4228440757","3451484829","4039211292","3318113268","2585374463","45764855","1418614640","2754339483","1950506033","3468872477"]
-1
View File
@@ -65,4 +65,3 @@
},
"prettier": "@gib/prettier-config"
}
+1
View File
@@ -0,0 +1 @@
[{"/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/forgot-password/layout.tsx":"1","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/forgot-password/page.tsx":"2","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/profile/layout.tsx":"3","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/profile/page.tsx":"4","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/sign-in/layout.tsx":"5","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/sign-in/page.tsx":"6","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/global-error.tsx":"7","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/layout.tsx":"8","/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/page.tsx":"9","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/cta.tsx":"10","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/features.tsx":"11","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/hero.tsx":"12","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/index.tsx":"13","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/tech-stack.tsx":"14","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/buttons/gibs-auth.tsx":"15","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/buttons/index.tsx":"16","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/avatar-upload.tsx":"17","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/header.tsx":"18","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/index.tsx":"19","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/reset-password.tsx":"20","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/sign-out.tsx":"21","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/user-info.tsx":"22","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/footer/index.tsx":"23","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/controls/AvatarDropdown.tsx":"24","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/controls/index.tsx":"25","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/index.tsx":"26","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/providers/ConvexClientProvider.tsx":"27","/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/providers/index.tsx":"28","/home/gib/Documents/Code/convex-monorepo/apps/next/src/instrumentation-client.ts":"29","/home/gib/Documents/Code/convex-monorepo/apps/next/src/instrumentation.ts":"30","/home/gib/Documents/Code/convex-monorepo/apps/next/src/lib/metadata.ts":"31","/home/gib/Documents/Code/convex-monorepo/apps/next/src/lib/proxy/ban-sus-ips.ts":"32","/home/gib/Documents/Code/convex-monorepo/apps/next/src/proxy.ts":"33"},{"size":298,"mtime":1768236967188,"results":"34","hashOfConfig":"35"},{"size":10607,"mtime":1768372347281,"results":"36","hashOfConfig":"35"},{"size":276,"mtime":1768236979857,"results":"37","hashOfConfig":"35"},{"size":1515,"mtime":1768372347312,"results":"38","hashOfConfig":"35"},{"size":274,"mtime":1768236849421,"results":"39","hashOfConfig":"35"},{"size":16567,"mtime":1774544486885,"results":"40","hashOfConfig":"35"},{"size":2410,"mtime":1774544486967,"results":"41","hashOfConfig":"35"},{"size":2042,"mtime":1774131581680,"results":"42","hashOfConfig":"35"},{"size":253,"mtime":1768372347406,"results":"43","hashOfConfig":"35"},{"size":1139,"mtime":1774544487103,"results":"44","hashOfConfig":"35"},{"size":2916,"mtime":1774544487137,"results":"45","hashOfConfig":"35"},{"size":4045,"mtime":1774544487187,"results":"46","hashOfConfig":"35"},{"size":141,"mtime":1768325738000,"results":"47","hashOfConfig":"35"},{"size":2697,"mtime":1774544487253,"results":"48","hashOfConfig":"35"},{"size":1081,"mtime":1768372347496,"results":"49","hashOfConfig":"35"},{"size":52,"mtime":1768188510036,"results":"50","hashOfConfig":"35"},{"size":6797,"mtime":1768372347532,"results":"51","hashOfConfig":"35"},{"size":384,"mtime":1774544487414,"results":"52","hashOfConfig":"35"},{"size":230,"mtime":1768238412936,"results":"53","hashOfConfig":"35"},{"size":5681,"mtime":1768372347569,"results":"54","hashOfConfig":"35"},{"size":1234,"mtime":1768372347581,"results":"55","hashOfConfig":"35"},{"size":4786,"mtime":1774544487590,"results":"56","hashOfConfig":"35"},{"size":4010,"mtime":1768372347611,"results":"57","hashOfConfig":"35"},{"size":2539,"mtime":1768372347626,"results":"58","hashOfConfig":"35"},{"size":499,"mtime":1768372347633,"results":"59","hashOfConfig":"35"},{"size":2470,"mtime":1774544487733,"results":"60","hashOfConfig":"35"},{"size":444,"mtime":1774544487759,"rLine truncated
+1 -1
View File
@@ -1 +1 @@
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54"],{"key":"55","value":"56"},{"key":"57","value":"58"},{"key":"59","value":"60"},{"key":"61","value":"62"},{"key":"63","value":"64"},{"key":"65","value":"66"},{"key":"67","value":"68"},{"key":"69","value":"70"},{"key":"71","value":"72"},{"key":"73","value":"74"},{"key":"75","value":"76"},{"key":"77","value":"78"},{"key":"79","value":"80"},{"key":"81","value":"82"},{"key":"83","value":"84"},{"key":"85","value":"86"},{"key":"87","value":"88"},{"key":"89","value":"90"},{"key":"91","value":"92"},{"key":"93","value":"94"},{"key":"95","value":"96"},{"key":"97","value":"98"},{"key":"99","value":"100"},{"key":"101","value":"102"},{"key":"103","value":"104"},{"key":"105","value":"106"},{"key":"107","value":"108"},{"key":"109","value":"110"},{"key":"111","value":"112"},{"key":"113","value":"114"},{"key":"115","value":"116"},{"key":"117","value":"118"},{"key":"119","value":"120"},{"key":"121","value":"122"},{"key":"123","value":"124"},{"key":"125","value":"126"},{"key":"127","value":"128"},{"key":"129","value":"130"},{"key":"131","value":"132"},{"key":"133","value":"134"},{"key":"135","value":"136"},{"key":"137","value":"138"},{"key":"139","value":"140"},{"key":"141","value":"142"},{"key":"143","value":"144"},{"key":"145","value":"146"},{"key":"147","value":"148"},{"key":"149","value":"150"},{"key":"151","value":"152"},{"key":"153","value":"154"},{"key":"155","value":"156"},{"key":"157","value":"158"},{"key":"159","value":"160"},{"key":"161","value":"162"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-logo-black.svg",{"size":3942,"mtime":1717458243000,"hash":"163"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-symbol-color.svg",{"size":948,"mtime":1717458243000,"hash":"164"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/sign-in/page.tsx",{"size":16469,"mtime":1768372347364,"hash":"165","data":"166"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/profile/layout.tsx",{"size":276,"mtime":1768236979857,"hash":"167","data":"168"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/instrumentation-client.ts",{"size":986,"mtime":1768341407000,"hash":"169","data":"170"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/lib/proxy/ban-sus-ips.ts",{"size":4894,"mtime":1768226323845,"hash":"171","data":"172"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-logo-color.svg",{"size":3949,"mtime":1717458243000,"hash":"173"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-symbol-black.svg",{"size":948,"mtime":1717458243000,"hash":"174"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/(auth)/forgot-password/page.tsx",{"size":10607,"mtime":1768372347281,"hash":"175","data":"176"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/hero.tsx",{"size":4282,"mtime":1768372347453,"hash":"177","data":"178"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/tech-stack.tsx",{"size":2781,"mtime":1768372347478,"hash":"179","data":"180"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/avatar-upload.tsx",{"size":6797,"mtime":1768372347532,"hash":"181","data":"182"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/header.tsx",{"size":717,"mtime":1768372347541,"hash":"183","data":"184"},"/home/gib/Documents/Code/convex-monorepo/apps/next/postcss.config.js",{"size":63,"mtime":1766225879000,"hash":"185","data":"186"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/controls/AvatarDropdown.tsx",{"size":2539,"mtime":1768372347626,"hash":"187","data":"188"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/controls/index.tsx",{"size":499,"mtime":1768372347633,"hash":"189","data":"190"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/favicon.png",{"size":17263,"mtime":1717458300000,"hash":"191"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-wordmark-white.svg",{"size":3096,"mtime":1717458243000,"hash":"192"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-wordmark-black.svg",{"size":3110,"mtime":1717458243000,"hash":"193"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/layout.tsx",{"size":1982,"mtime":1768372347399,"hash":"194","data":"195"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/cta.tsx",{"size":1314,"mtime":1768372347426,"hash":"196","data":"197"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/providers/ConvexClientProvider.tsx",{"size":462,"mtime":1768239264017,"hash":"198","data":"199"},"Line truncated
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56"],{"key":"57","value":"58"},{"key":"59","value":"60"},{"key":"61","value":"62"},{"key":"63","value":"64"},{"key":"65","value":"66"},{"key":"67","value":"68"},{"key":"69","value":"70"},{"key":"71","value":"72"},{"key":"73","value":"74"},{"key":"75","value":"76"},{"key":"77","value":"78"},{"key":"79","value":"80"},{"key":"81","value":"82"},{"key":"83","value":"84"},{"key":"85","value":"86"},{"key":"87","value":"88"},{"key":"89","value":"90"},{"key":"91","value":"92"},{"key":"93","value":"94"},{"key":"95","value":"96"},{"key":"97","value":"98"},{"key":"99","value":"100"},{"key":"101","value":"102"},{"key":"103","value":"104"},{"key":"105","value":"106"},{"key":"107","value":"108"},{"key":"109","value":"110"},{"key":"111","value":"112"},{"key":"113","value":"114"},{"key":"115","value":"116"},{"key":"117","value":"118"},{"key":"119","value":"120"},{"key":"121","value":"122"},{"key":"123","value":"124"},{"key":"125","value":"126"},{"key":"127","value":"128"},{"key":"129","value":"130"},{"key":"131","value":"132"},{"key":"133","value":"134"},{"key":"135","value":"136"},{"key":"137","value":"138"},{"key":"139","value":"140"},{"key":"141","value":"142"},{"key":"143","value":"144"},{"key":"145","value":"146"},{"key":"147","value":"148"},{"key":"149","value":"150"},{"key":"151","value":"152"},{"key":"153","value":"154"},{"key":"155","value":"156"},{"key":"157","value":"158"},{"key":"159","value":"160"},{"key":"161","value":"162"},{"key":"163","value":"164"},{"key":"165","value":"166"},{"key":"167","value":"168"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-symbol-black.svg",{"size":948,"mtime":1717458243000,"hash":"169"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/controls/index.tsx",{"size":499,"mtime":1768372347633,"hash":"170","data":"171"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/buttons/gibs-auth.tsx",{"size":1081,"mtime":1768372347496,"hash":"172","data":"173"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-wordmark-black.svg",{"size":3110,"mtime":1717458243000,"hash":"174"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/gitea/gitea.svg",{"size":1844,"mtime":1747415494000,"hash":"175"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/sentry.server.config.ts",{"size":184,"mtime":1774544487955,"hash":"176","data":"177"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/reset-password.tsx",{"size":5681,"mtime":1768372347569,"hash":"178","data":"179"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-symbol-color.svg",{"size":948,"mtime":1717458243000,"hash":"180"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/instrumentation-client.ts",{"size":982,"mtime":1774544487814,"hash":"181","data":"182"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/tech-stack.tsx",{"size":2697,"mtime":1774544487253,"hash":"183","data":"184"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/favicon-light.png",{"size":14406,"mtime":1717458300000,"hash":"185"},"/home/gib/Documents/Code/convex-monorepo/apps/next/.cache/tsbuildinfo.json",{"size":784653,"mtime":1774544485739,"hash":"186","data":"187"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/app/global-error.tsx",{"size":2410,"mtime":1774544486967,"hash":"188","data":"189"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/footer/index.tsx",{"size":4010,"mtime":1768372347611,"hash":"190","data":"191"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/header/controls/AvatarDropdown.tsx",{"size":2539,"mtime":1768372347626,"hash":"192","data":"193"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/cta.tsx",{"size":1139,"mtime":1774544487103,"hash":"194","data":"195"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/profile/sign-out.tsx",{"size":1234,"mtime":1768372347581,"hash":"196","data":"197"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/landing/index.tsx",{"size":141,"mtime":1768325738000,"hash":"198","data":"199"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convex-symbol-white.svg",{"size":942,"mtime":1717458243000,"hash":"200"},"/home/gib/Documents/Code/convex-monorepo/apps/next/src/components/layout/auth/buttons/index.tsx",{"size":52,"mtime":1768188510036,"hash":"201","data":"202"},"/home/gib/Documents/Code/convex-monorepo/apps/next/postcss.config.js",{"size":63,"mtime":1766225879000,"hash":"203","data":"204"},"/home/gib/Documents/Code/convex-monorepo/apps/next/public/misc/convex/convexLine truncated
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -1,5 +1,5 @@
import { createJiti } from 'jiti';
import { withSentryConfig } from '@sentry/nextjs';
import { createJiti } from 'jiti';
import { withPlausibleProxy } from 'next-plausible';
const jiti = createJiti(import.meta.url);
+5 -5
View File
@@ -252,27 +252,27 @@ const SignIn = () => {
<Tabs
defaultValue={flow}
onValueChange={(value) => setFlow(value as 'signIn' | 'signUp')}
className='items-center flex-col'
className='flex-col items-center'
>
<TabsList>
<TabsTrigger
value='signIn'
className='cursor-pointer py-2 px-6 text-2xl font-bold'
className='cursor-pointer px-6 py-2 text-2xl font-bold'
>
Sign In
</TabsTrigger>
<TabsTrigger
value='signUp'
className='cursor-pointer py-2 px-6 text-2xl font-bold'
className='cursor-pointer px-6 py-2 text-2xl font-bold'
>
Sign Up
</TabsTrigger>
</TabsList>
<TabsContent
value='signIn'
className='min-h-[560px] items-center flex flex-row'
className='flex min-h-[560px] flex-row items-center'
>
<Card className='bg-card/50 min-w-xs sm:min-w-sm py-10'>
<Card className='bg-card/50 min-w-xs py-10 sm:min-w-sm'>
<CardContent>
<Form {...signInForm}>
<form
+1 -1
View File
@@ -10,12 +10,12 @@ import { useEffect } from 'react';
import Footer from '@/components/layout/footer';
import Header from '@/components/layout/header';
import { ConvexClientProvider } from '@/components/providers';
import { env } from '@/env';
import { generateMetadata } from '@/lib/metadata';
import * as Sentry from '@sentry/nextjs';
import PlausibleProvider from 'next-plausible';
import { Button, ThemeProvider, Toaster } from '@gib/ui';
import { env } from '@/env';
export const metadata: Metadata = generateMetadata();
+2 -4
View File
@@ -1,5 +1,4 @@
export function CTA() {
return (
export const CTA = () => (
<section className='container mx-auto px-4 py-24'>
<div className='mx-auto max-w-4xl'>
<div className='border-border/40 from-muted/50 to-muted/30 rounded-2xl border bg-linear-to-br p-8 text-center md:p-12'>
@@ -29,5 +28,4 @@ export function CTA() {
</div>
</div>
</section>
);
}
);
@@ -57,8 +57,7 @@ const features = [
},
];
export function Features() {
return (
export const Features = () => (
<section id='features' className='container mx-auto px-4 py-24'>
<div className='mx-auto max-w-6xl'>
{/* Section Header */}
@@ -67,8 +66,8 @@ export function Features() {
Everything You Need to Ship Fast
</h2>
<p className='text-muted-foreground mx-auto max-w-2xl text-lg'>
A complete monorepo template with all the tools and patterns you
need for production-ready applications.
A complete monorepo template with all the tools and patterns you need
for production-ready applications.
</p>
</div>
@@ -88,5 +87,4 @@ export function Features() {
</div>
</div>
</section>
);
}
);
+5 -7
View File
@@ -9,8 +9,7 @@ const kanitSans = Kanit({
weight: ['400', '500', '600', '700'],
});
export function Hero() {
return (
export const Hero = () => (
<section className='container mx-auto px-4 py-24 md:py-32 lg:py-40'>
<div className='mx-auto flex max-w-5xl flex-col items-center gap-8 text-center'>
{/* Badge */}
@@ -31,9 +30,9 @@ export function Hero() {
{/* Description */}
<p className='text-muted-foreground max-w-2xl text-lg md:text-xl'>
A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship
web and mobile apps faster with shared code, type-safe backend, and
complete control over your infrastructure.
A Turborepo starter with Next.js, Expo, and self-hosted Convex. Ship web
and mobile apps faster with shared code, type-safe backend, and complete
control over your infrastructure.
</p>
{/* CTA Buttons */}
@@ -124,5 +123,4 @@ export function Hero() {
</div>
</div>
</section>
);
}
);
@@ -36,8 +36,7 @@ const techStack = [
},
];
export function TechStack() {
return (
export const TechStack = () => (
<section id='tech-stack' className='border-border/40 bg-muted/30 border-t'>
<div className='container mx-auto px-4 py-24'>
<div className='mx-auto max-w-6xl'>
@@ -75,5 +74,4 @@ export function TechStack() {
</div>
</div>
</section>
);
}
);
@@ -1,4 +1,5 @@
'use client';
import { CardDescription, CardHeader, CardTitle } from '@gib/ui';
const ProfileHeader = () => {
@@ -53,7 +53,7 @@ export const UserInfoForm = ({
const userProvider = usePreloadedQuery(preloadedProvider);
const providerMap: Record<string, string> = {
unknown: 'Provider',
authentik: 'Gib\'s Auth',
authentik: "Gib's Auth",
};
const [loading, setLoading] = useState(false);
@@ -150,7 +150,8 @@ export const UserInfoForm = ({
</FormDescription>
) : (
<FormDescription>
Email is managed through your {providerMap[userProvider ?? 'unknown']} account
Email is managed through your{' '}
{providerMap[userProvider ?? 'unknown']} account
</FormDescription>
)}
<FormMessage />
@@ -28,7 +28,7 @@ export default function Header(headerProps: ComponentProps<'header'>) {
alt='Convex Monorepo'
width={50}
height={50}
className='invert dark:invert-0 w-15'
className='w-15 invert dark:invert-0'
/>
<span
className={`mb-3 hidden font-extrabold lg:inline lg:text-5xl ${kanitSans.className}`}
@@ -7,10 +7,8 @@ import { ConvexReactClient } from 'convex/react';
const convex = new ConvexReactClient(env.NEXT_PUBLIC_CONVEX_URL);
export function ConvexClientProvider({ children }: { children: ReactNode }) {
return (
export const ConvexClientProvider = ({ children }: { children: ReactNode }) => (
<ConvexAuthNextjsProvider client={convex}>
{children}
</ConvexAuthNextjsProvider>
);
}
);
+2 -1
View File
@@ -38,7 +38,8 @@ export const env = createEnv({
NEXT_PUBLIC_SENTRY_DSN: process.env.NEXT_PUBLIC_SENTRY_DSN,
NEXT_PUBLIC_SENTRY_URL: process.env.NEXT_PUBLIC_SENTRY_URL,
NEXT_PUBLIC_SENTRY_ORG: process.env.NEXT_PUBLIC_SENTRY_ORG,
NEXT_PUBLIC_SENTRY_PROJECT_NAME: process.env.NEXT_PUBLIC_SENTRY_PROJECT_NAME,
NEXT_PUBLIC_SENTRY_PROJECT_NAME:
process.env.NEXT_PUBLIC_SENTRY_PROJECT_NAME,
},
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
emptyStringAsUndefined: true,
+1 -1
View File
@@ -1,6 +1,6 @@
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from '@sentry/nextjs';
import { env } from '@/env';
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: env.NEXT_PUBLIC_SENTRY_DSN,
+1 -1
View File
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/nextjs';
import { env } from '@/env';
import * as Sentry from '@sentry/nextjs';
Sentry.init({
dsn: env.NEXT_PUBLIC_SENTRY_DSN,
+3
View File
@@ -181,6 +181,7 @@
"@next/eslint-plugin-next": "^16.0.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prefer-arrow-functions": "^3.9.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-turbo": "^2.5.8",
@@ -1951,6 +1952,8 @@
"eslint-plugin-jsx-a11y": ["[email protected]", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="],
"eslint-plugin-prefer-arrow-functions": ["[email protected]", "", { "dependencies": { "@typescript-eslint/types": "^8.19.1", "@typescript-eslint/utils": "^8.19.1" }, "peerDependencies": { "eslint": ">=9.17.0" } }, "sha512-Mr9Ia8i5ohfCMcZBRedXXWdDJIo30gdKdPfRQ5DtO62yzogB5ErVGwQWPz+ycRlQPKpCAlBHpJdp1TCeCtt+YA=="],
"eslint-plugin-react": ["[email protected]", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="],
"eslint-plugin-react-hooks": ["[email protected]", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="],
+1 -1
View File
@@ -18,7 +18,7 @@ ENV NODE_ENV=production
RUN bun run build --filter=@gib/next
# Runner stage
FROM node:20-alpine AS runner
FROM node:22-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
+1 -1
View File
@@ -1 +1 @@
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22"],{"key":"23","value":"24"},{"key":"25","value":"26"},{"key":"27","value":"28"},{"key":"29","value":"30"},{"key":"31","value":"32"},{"key":"33","value":"34"},{"key":"35","value":"36"},{"key":"37","value":"38"},{"key":"39","value":"40"},{"key":"41","value":"42"},{"key":"43","value":"44"},{"key":"45","value":"46"},{"key":"47","value":"48"},{"key":"49","value":"50"},{"key":"51","value":"52"},{"key":"53","value":"54"},{"key":"55","value":"56"},{"key":"57","value":"58"},{"key":"59","value":"60"},{"key":"61","value":"62"},{"key":"63","value":"64"},{"key":"65","value":"66"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/crons.ts",{"size":528,"mtime":1768239263662,"hash":"67","data":"68"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.ts",{"size":4050,"mtime":1768338105000,"hash":"69","data":"70"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/providers/password.ts",{"size":900,"mtime":1768239263701,"hash":"71","data":"72"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/dataModel.d.ts",{"size":1726,"mtime":1768372015506,"hash":"73","data":"74"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.gitignore",{"size":16,"mtime":1766222924000,"hash":"75"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.config.ts",{"size":127,"mtime":1768156477000,"hash":"76","data":"77"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/utils.ts",{"size":635,"mtime":1768155639000,"hash":"78","data":"79"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/providers/usesend.ts",{"size":3115,"mtime":1768164053000,"hash":"80","data":"81"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/tsconfig.json",{"size":732,"mtime":1766222924000,"hash":"82","data":"83"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/package.json",{"size":1300,"mtime":1768239263823,"hash":"84","data":"85"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.js",{"size":3474,"mtime":1768372015599,"hash":"86","data":"87"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.d.ts",{"size":5695,"mtime":1768372015561,"hash":"88","data":"89"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/types/auth.ts",{"size":174,"mtime":1768188156942,"hash":"90","data":"91"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/files.ts",{"size":582,"mtime":1768239263763,"hash":"92","data":"93"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/scripts/generateKeys.mjs",{"size":520,"mtime":1768368324432,"hash":"94","data":"95"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.d.ts",{"size":1521,"mtime":1768372015429,"hash":"96","data":"97"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/types/index.ts",{"size":69,"mtime":1768239263861,"hash":"98","data":"99"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.js",{"size":480,"mtime":1768372015467,"hash":"100","data":"101"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/README.md",{"size":2525,"mtime":1768155639000,"hash":"102","data":"103"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/http.ts",{"size":153,"mtime":1768157491000,"hash":"104","data":"105"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/index.ts",{"size":142,"mtime":1768157708000,"hash":"106","data":"107"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/schema.ts",{"size":1197,"mtime":1768239263796,"hash":"108","data":"109"},"0d76c3867783aec5435b0c716f1bfc65",{"hashOfOptions":"110"},"3d304208139ef0b2775272a5a4a2a970",{"hashOfOptions":"111"},"55bcf7d546da727edf2c1b7e52db6c7f",{"hashOfOptions":"112"},"ce3275ccdd28673d7e9280b33792057c",{"hashOfOptions":"113"},"175b1a771387d8b7e26145c3d04e0475","2d4f4ccfc784af9d668d8c64726c2f6b",{"hashOfOptions":"114"},"6a3198db454396c63e99ee08065726bd",{"hashOfOptions":"115"},"14f5e2d18e21f65282ac744b382f2d5f",{"hashOfOptions":"116"},"cfa98923457caed911ec68b626ef4234",{"hashOfOptions":"117"},"5d96f2526115241c4a37250b0f45ac4d",{"hashOfOptions":"118"},"f42d53bd1ba24bfee1407d2098f3b9ff",{"hashOfOptions":"119"},"6719b019702fc173de16b1bdc8e8ed47",{"hashOfOptions":"120"},"f0f9ef72dc6fef350f3521d3fd8adfc7",{"hashOfOptions":"121"},"5ff3856f1a61b946df899f7c9880b72a",{"hashOfOptions":"122"},"ccf5b9105ad4ff66fce0a7133fa3db4b",{"hashOfOptions":"123"},"7c522302bfb45e9cf5f1c271b48fa247",{"hashOfOptions":"124"},"57577de5aadfa618e91739aea3630342",{"hashOfOptions":"125"},"9d0d3b8d30fb1a1207df0584bf0f4c2e",{"hashOfOptions":"126"},"f5aa8269478f140adb4ccf0470dd1244",{"hashOfOptions":"127"},"4bbb95a66a1e615c30ea7c5098a4a609",{"hashOfOptions":"128"},"8776a23ed6f2ab3681e4ba5b4bcf3d74",{"hashOfOptions":"129"},Line truncated
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23"],{"key":"24","value":"25"},{"key":"26","value":"27"},{"key":"28","value":"29"},{"key":"30","value":"31"},{"key":"32","value":"33"},{"key":"34","value":"35"},{"key":"36","value":"37"},{"key":"38","value":"39"},{"key":"40","value":"41"},{"key":"42","value":"43"},{"key":"44","value":"45"},{"key":"46","value":"47"},{"key":"48","value":"49"},{"key":"50","value":"51"},{"key":"52","value":"53"},{"key":"54","value":"55"},{"key":"56","value":"57"},{"key":"58","value":"59"},{"key":"60","value":"61"},{"key":"62","value":"63"},{"key":"64","value":"65"},{"key":"66","value":"67"},{"key":"68","value":"69"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.gitignore",{"size":16,"mtime":1766222924000,"hash":"70"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/providers/usesend.ts",{"size":3245,"mtime":1774543332725,"hash":"71","data":"72"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/scripts/generateKeys.mjs",{"size":520,"mtime":1768368324432,"hash":"73","data":"74"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/tsconfig.json",{"size":732,"mtime":1766222924000,"hash":"75","data":"76"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/http.ts",{"size":153,"mtime":1768157491000,"hash":"77","data":"78"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/utils.ts",{"size":635,"mtime":1768155639000,"hash":"79","data":"80"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/schema.ts",{"size":1161,"mtime":1774544485430,"hash":"81","data":"82"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.ts",{"size":3770,"mtime":1774537696767,"hash":"83","data":"84"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/files.ts",{"size":582,"mtime":1768239263763,"hash":"85","data":"86"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/index.ts",{"size":142,"mtime":1768157708000,"hash":"87","data":"88"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/types/auth.ts",{"size":174,"mtime":1768188156942,"hash":"89","data":"90"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/crons.ts",{"size":528,"mtime":1768239263662,"hash":"91","data":"92"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/auth.config.ts",{"size":127,"mtime":1768156477000,"hash":"93","data":"94"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.d.ts",{"size":5600,"mtime":1774544485136,"hash":"95","data":"96"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/README.md",{"size":2525,"mtime":1768155639000,"hash":"97","data":"98"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.d.ts",{"size":1632,"mtime":1774544486629,"hash":"99","data":"100"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/dataModel.d.ts",{"size":1737,"mtime":1774544663844,"hash":"101","data":"102"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/server.js",{"size":3696,"mtime":1774544485100,"hash":"103","data":"104"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/types/index.ts",{"size":69,"mtime":1768239263861,"hash":"105","data":"106"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/custom/auth/providers/password.ts",{"size":900,"mtime":1768239263701,"hash":"107","data":"108"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/.cache/.prettiercache",{"size":5478,"mtime":1774544485685},"/home/gib/Documents/Code/convex-monorepo/packages/backend/package.json",{"size":1300,"mtime":1768239263823,"hash":"109","data":"110"},"/home/gib/Documents/Code/convex-monorepo/packages/backend/convex/_generated/api.js",{"size":480,"mtime":1774544485141,"hash":"111","data":"112"},"175b1a771387d8b7e26145c3d04e0475","14f5e2d18e21f65282ac744b382f2d5f",{"hashOfOptions":"113"},"ccf5b9105ad4ff66fce0a7133fa3db4b",{"hashOfOptions":"114"},"cfa98923457caed911ec68b626ef4234",{"hashOfOptions":"115"},"4bbb95a66a1e615c30ea7c5098a4a609",{"hashOfOptions":"116"},"6a3198db454396c63e99ee08065726bd",{"hashOfOptions":"117"},"7e6dcc47831030bfaa3e235d2226cec6",{"hashOfOptions":"118"},"3d304208139ef0b2775272a5a4a2a970",{"hashOfOptions":"119"},"5ff3856f1a61b946df899f7c9880b72a",{"hashOfOptions":"120"},"8776a23ed6f2ab3681e4ba5b4bcf3d74",{"hashOfOptions":"121"},"f0f9ef72dc6fef350f3521d3fd8adfc7",{"hashOfOptions":"122"},"0d76c3867783aec5435b0c716f1bfc65",{"hashOfOptions":"123"},"2d4f4ccfc784af9d668d8c64726c2f6b",{"hashOfOptions":"124"},"6719b019702fc173de16b1bdc8e8ed47",{"hashOfOptions":"125"},"f5aa8269478f140adb4ccf0470dd1244",{"hashOfOptions":"126"},"7c522302bfb45e9cf5f1c271b48fa247",{"hashOfOptions":"127"},"ce3275ccdd28673d7e9280b33792057c",{"hashOfOptions":"128"},"f42d53bd1ba24bfee1407d2098f3b9ff",{"hashOfOptions":"129"},"57577de5aadfa618e91739aeLine truncated
@@ -8,7 +8,7 @@ export default function UseSendProvider(config: EmailUserConfig): EmailConfig {
id: 'usesend',
type: 'email',
name: 'UseSend',
from: 'Convex Monorepo <[email protected]>',
from: process.env.USESEND_FROM_EMAIL ?? '[email protected]',
maxAge: 24 * 60 * 60, // 24 hours
async generateVerificationToken() {
@@ -21,13 +21,14 @@ export default function UseSendProvider(config: EmailUserConfig): EmailConfig {
},
async sendVerificationRequest(params) {
const { identifier: to, provider, url, theme, token } = params;
//const { host } = new URL(url);
const host = 'Convex Monorepo';
const { identifier: to, provider, url, token } = params;
// Derive a display name from the site URL, fallback to 'App'
const siteUrl = process.env.USESEND_FROM_EMAIL ?? '';
const appName = siteUrl.split('@')[1]?.split('.')[0] ?? 'App';
const useSend = new UseSend(
process.env.USESEND_API_KEY!,
'https://usesend.gbrown.org',
process.env.USESEND_URL!,
);
// For password reset, we want to send the code, not the magic link
@@ -38,8 +39,8 @@ export default function UseSendProvider(config: EmailUserConfig): EmailConfig {
from: provider.from!,
to: [to],
subject: isPasswordReset
? `Reset your password - ${host}`
: `Sign in to ${host}`,
? `Reset your password - ${appName}`
: `Sign in to ${appName}`,
text: isPasswordReset
? `Your password reset code is ${token}`
: `Your sign in code is ${token}`,
+3 -5
View File
@@ -18,11 +18,9 @@ const applicationTables = {
phoneVerificationTime: v.optional(v.number()),
isAnonymous: v.optional(v.boolean()),
/* Fields below here are custom & not defined in authTables */
themePreference: v.optional(v.union(
v.literal('light'),
v.literal('dark'),
v.literal('system'),
)),
themePreference: v.optional(
v.union(v.literal('light'), v.literal('dark'), v.literal('system')),
),
})
.index('email', ['email'])
.index('phone', ['phone'])
+1
View File
@@ -0,0 +1 @@
[{"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/accordion.tsx":"1","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/alert-dialog.tsx":"2","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/alert.tsx":"3","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/aspect-ratio.tsx":"4","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/avatar.tsx":"5","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/badge.tsx":"6","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/based-avatar.tsx":"7","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/based-progress.tsx":"8","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/breadcrumb.tsx":"9","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/button-group.tsx":"10","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/button.tsx":"11","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/calendar.tsx":"12","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/card.tsx":"13","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/carousel.tsx":"14","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/chart.tsx":"15","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/checkbox.tsx":"16","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/collapsible.tsx":"17","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/combobox.tsx":"18","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/command.tsx":"19","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/context-menu.tsx":"20","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/dialog.tsx":"21","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/drawer.tsx":"22","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/dropdown-menu.tsx":"23","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/empty.tsx":"24","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/field.tsx":"25","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/form.tsx":"26","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hooks/index.tsx":"27","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hooks/use-mobile.ts":"28","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hooks/use-on-click-outside.tsx":"29","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hover-card.tsx":"30","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/image-crop.tsx":"31","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/index.tsx":"32","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/input-group.tsx":"33","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/input-otp.tsx":"34","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/input.tsx":"35","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/item.tsx":"36","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/kbd.tsx":"37","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/label.tsx":"38","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/menubar.tsx":"39","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/native-select.tsx":"40","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/navigation-menu.tsx":"41","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/pagination.tsx":"42","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/popover.tsx":"43","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/progress.tsx":"44","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/radio-group.tsx":"45","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/resizable.tsx":"46","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/scroll-area.tsx":"47","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/select.tsx":"48","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/separator.tsx":"49","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/sheet.tsx":"50","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/sidebar.tsx":"51","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/skeleton.tsx":"52","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/slider.tsx":"53","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/sonner.tsx":"54","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/spinner.tsx":"55","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/status-message.tsx":"56","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/submit-button.tsx":"57","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/switch.tsx":"58","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/table.tsx":"59","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/tabs.tsx":"60","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/textarea.tsx":"61","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/theme.tsx":"62","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/toggle-group.tsx":"63","/home/gib/Documents/Code/convex-monorepo/packages/ui/src/toggle.tsx":"64","/home/gib/Documents/Code/convex-monorepo/pacLine truncated
+1
View File
@@ -0,0 +1 @@
[["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71"],{"key":"72","value":"73"},{"key":"74","value":"75"},{"key":"76","value":"77"},{"key":"78","value":"79"},{"key":"80","value":"81"},{"key":"82","value":"83"},{"key":"84","value":"85"},{"key":"86","value":"87"},{"key":"88","value":"89"},{"key":"90","value":"91"},{"key":"92","value":"93"},{"key":"94","value":"95"},{"key":"96","value":"97"},{"key":"98","value":"99"},{"key":"100","value":"101"},{"key":"102","value":"103"},{"key":"104","value":"105"},{"key":"106","value":"107"},{"key":"108","value":"109"},{"key":"110","value":"111"},{"key":"112","value":"113"},{"key":"114","value":"115"},{"key":"116","value":"117"},{"key":"118","value":"119"},{"key":"120","value":"121"},{"key":"122","value":"123"},{"key":"124","value":"125"},{"key":"126","value":"127"},{"key":"128","value":"129"},{"key":"130","value":"131"},{"key":"132","value":"133"},{"key":"134","value":"135"},{"key":"136","value":"137"},{"key":"138","value":"139"},{"key":"140","value":"141"},{"key":"142","value":"143"},{"key":"144","value":"145"},{"key":"146","value":"147"},{"key":"148","value":"149"},{"key":"150","value":"151"},{"key":"152","value":"153"},{"key":"154","value":"155"},{"key":"156","value":"157"},{"key":"158","value":"159"},{"key":"160","value":"161"},{"key":"162","value":"163"},{"key":"164","value":"165"},{"key":"166","value":"167"},{"key":"168","value":"169"},{"key":"170","value":"171"},{"key":"172","value":"173"},{"key":"174","value":"175"},{"key":"176","value":"177"},{"key":"178","value":"179"},{"key":"180","value":"181"},{"key":"182","value":"183"},{"key":"184","value":"185"},{"key":"186","value":"187"},{"key":"188","value":"189"},{"key":"190","value":"191"},{"key":"192","value":"193"},{"key":"194","value":"195"},{"key":"196","value":"197"},{"key":"198","value":"199"},{"key":"200","value":"201"},{"key":"202","value":"203"},{"key":"204","value":"205"},{"key":"206","value":"207"},{"key":"208","value":"209"},{"key":"210","value":"211"},{"key":"212","value":"213"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/based-avatar.tsx",{"size":1738,"mtime":1773778628580,"data":"214"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hooks/index.tsx",{"size":104,"mtime":1774538639553,"data":"215"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hover-card.tsx",{"size":1476,"mtime":1774544487157,"data":"216"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/popover.tsx",{"size":2261,"mtime":1774544487690,"data":"217"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/tooltip.tsx",{"size":1771,"mtime":1774544488284,"data":"218"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/aspect-ratio.tsx",{"size":273,"mtime":1774544485059,"data":"219"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/button.tsx",{"size":3380,"mtime":1774544470961,"data":"220"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/toggle-group.tsx",{"size":2238,"mtime":1774544488249,"data":"221"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/hooks/use-mobile.ts",{"size":589,"mtime":1774544470961,"data":"222"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/submit-button.tsx",{"size":1192,"mtime":1773778628581,"data":"223"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/.cache/.eslintcache",{"size":45499,"mtime":1774544652212},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/carousel.tsx",{"size":5758,"mtime":1774544486321,"data":"224"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/textarea.tsx",{"size":736,"mtime":1774544488211,"data":"225"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/.cache/.prettiercache",{"size":13281,"mtime":1774544488300},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/avatar.tsx",{"size":2764,"mtime":1774544485222,"data":"226"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/command.tsx",{"size":4628,"mtime":1774544486715,"data":"227"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/dropdown-menu.tsx",{"size":8511,"mtime":1774544486927,"data":"228"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/empty.tsx",{"size":2265,"mtime":1774544486964,"data":"229"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/item.tsx",{"size":4575,"mtime":1774544487439,"data":"230"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/spinner.tsx",{"size":307,"mtime":1774544488097,"data":"231"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/package.json",{"size":2103,"mtime":1774538890797,"data":"232"},"/home/gib/Documents/Code/convex-monorepo/packages/ui/src/dialog.tsx",{"size":3925,"mtime":1774544486830,"data":"233"},"/home/gib/Documents/Code/convex-monLine truncated
+12 -20
View File
@@ -6,38 +6,33 @@ import { Accordion as AccordionPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Accordion({
const Accordion = ({
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Root>) {
return (
}: React.ComponentProps<typeof AccordionPrimitive.Root>) => (
<AccordionPrimitive.Root
data-slot='accordion'
className={cn('flex w-full flex-col', className)}
{...props}
/>
);
}
);
function AccordionItem({
const AccordionItem = ({
className,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Item>) {
return (
}: React.ComponentProps<typeof AccordionPrimitive.Item>) => (
<AccordionPrimitive.Item
data-slot='accordion-item'
className={cn('not-last:border-b', className)}
{...props}
/>
);
}
);
function AccordionTrigger({
const AccordionTrigger = ({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof AccordionPrimitive.Trigger>) => (
<AccordionPrimitive.Header className='flex'>
<AccordionPrimitive.Trigger
data-slot='accordion-trigger'
@@ -58,15 +53,13 @@ function AccordionTrigger({
/>
</AccordionPrimitive.Trigger>
</AccordionPrimitive.Header>
);
}
);
function AccordionContent({
const AccordionContent = ({
className,
children,
...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) {
return (
}: React.ComponentProps<typeof AccordionPrimitive.Content>) => (
<AccordionPrimitive.Content
data-slot='accordion-content'
className='data-open:animate-accordion-down data-closed:animate-accordion-up overflow-hidden text-sm'
@@ -81,7 +74,6 @@ function AccordionContent({
{children}
</div>
</AccordionPrimitive.Content>
);
}
);
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
+37 -59
View File
@@ -5,33 +5,28 @@ import { AlertDialog as AlertDialogPrimitive } from 'radix-ui';
import { Button, cn } from '@gib/ui';
function AlertDialog({
const AlertDialog = ({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot='alert-dialog' {...props} />;
}
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) => (
<AlertDialogPrimitive.Root data-slot='alert-dialog' {...props} />
);
function AlertDialogTrigger({
const AlertDialogTrigger = ({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) => (
<AlertDialogPrimitive.Trigger data-slot='alert-dialog-trigger' {...props} />
);
}
);
function AlertDialogPortal({
const AlertDialogPortal = ({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return (
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) => (
<AlertDialogPrimitive.Portal data-slot='alert-dialog-portal' {...props} />
);
}
);
function AlertDialogOverlay({
const AlertDialogOverlay = ({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
return (
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) => (
<AlertDialogPrimitive.Overlay
data-slot='alert-dialog-overlay'
className={cn(
@@ -40,17 +35,15 @@ function AlertDialogOverlay({
)}
{...props}
/>
);
}
);
function AlertDialogContent({
const AlertDialogContent = ({
className,
size = 'default',
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
size?: 'default' | 'sm';
}) {
return (
}) => (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
@@ -63,14 +56,12 @@ function AlertDialogContent({
{...props}
/>
</AlertDialogPortal>
);
}
);
function AlertDialogHeader({
const AlertDialogHeader = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='alert-dialog-header'
className={cn(
@@ -79,14 +70,12 @@ function AlertDialogHeader({
)}
{...props}
/>
);
}
);
function AlertDialogFooter({
const AlertDialogFooter = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='alert-dialog-footer'
className={cn(
@@ -95,14 +84,12 @@ function AlertDialogFooter({
)}
{...props}
/>
);
}
);
function AlertDialogMedia({
const AlertDialogMedia = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='alert-dialog-media'
className={cn(
@@ -111,14 +98,12 @@ function AlertDialogMedia({
)}
{...props}
/>
);
}
);
function AlertDialogTitle({
const AlertDialogTitle = ({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) => (
<AlertDialogPrimitive.Title
data-slot='alert-dialog-title'
className={cn(
@@ -127,14 +112,12 @@ function AlertDialogTitle({
)}
{...props}
/>
);
}
);
function AlertDialogDescription({
const AlertDialogDescription = ({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) => (
<AlertDialogPrimitive.Description
data-slot='alert-dialog-description'
className={cn(
@@ -143,17 +126,15 @@ function AlertDialogDescription({
)}
{...props}
/>
);
}
);
function AlertDialogAction({
const AlertDialogAction = ({
className,
variant = 'default',
size = 'default',
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {
return (
Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) => (
<Button variant={variant} size={size} asChild>
<AlertDialogPrimitive.Action
data-slot='alert-dialog-action'
@@ -161,17 +142,15 @@ function AlertDialogAction({
{...props}
/>
</Button>
);
}
);
function AlertDialogCancel({
const AlertDialogCancel = ({
className,
variant = 'outline',
size = 'default',
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) {
return (
Pick<React.ComponentProps<typeof Button>, 'variant' | 'size'>) => (
<Button variant={variant} size={size} asChild>
<AlertDialogPrimitive.Cancel
data-slot='alert-dialog-cancel'
@@ -179,8 +158,7 @@ function AlertDialogCancel({
{...props}
/>
</Button>
);
}
);
export {
AlertDialog,
+10 -18
View File
@@ -20,23 +20,20 @@ const alertVariants = cva(
},
);
function Alert({
const Alert = ({
className,
variant,
...props
}: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) {
return (
}: React.ComponentProps<'div'> & VariantProps<typeof alertVariants>) => (
<div
data-slot='alert'
role='alert'
className={cn(alertVariants({ variant }), className)}
{...props}
/>
);
}
);
function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
return (
const AlertTitle = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='alert-title'
className={cn(
@@ -45,14 +42,12 @@ function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function AlertDescription({
const AlertDescription = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='alert-description'
className={cn(
@@ -61,17 +56,14 @@ function AlertDescription({
)}
{...props}
/>
);
}
);
function AlertAction({ className, ...props }: React.ComponentProps<'div'>) {
return (
const AlertAction = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='alert-action'
className={cn('absolute top-2 right-2', className)}
{...props}
/>
);
}
);
export { Alert, AlertTitle, AlertDescription, AlertAction };
+4 -4
View File
@@ -2,9 +2,9 @@
import { AspectRatio as AspectRatioPrimitive } from 'radix-ui';
function AspectRatio({
const AspectRatio = ({
...props
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) {
return <AspectRatioPrimitive.Root data-slot='aspect-ratio' {...props} />;
}
}: React.ComponentProps<typeof AspectRatioPrimitive.Root>) => (
<AspectRatioPrimitive.Root data-slot='aspect-ratio' {...props} />
);
export { AspectRatio };
+16 -28
View File
@@ -5,14 +5,13 @@ import { Avatar as AvatarPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Avatar({
const Avatar = ({
className,
size = 'default',
...props
}: React.ComponentProps<typeof AvatarPrimitive.Root> & {
size?: 'default' | 'sm' | 'lg';
}) {
return (
}) => (
<AvatarPrimitive.Root
data-slot='avatar'
data-size={size}
@@ -22,27 +21,23 @@ function Avatar({
)}
{...props}
/>
);
}
);
function AvatarImage({
const AvatarImage = ({
className,
...props
}: React.ComponentProps<typeof AvatarPrimitive.Image>) {
return (
}: React.ComponentProps<typeof AvatarPrimitive.Image>) => (
<AvatarPrimitive.Image
data-slot='avatar-image'
className={cn('aspect-square size-full', className)}
{...props}
/>
);
}
);
function AvatarFallback({
const AvatarFallback = ({
className,
...props
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) {
return (
}: React.ComponentProps<typeof AvatarPrimitive.Fallback>) => (
<AvatarPrimitive.Fallback
data-slot='avatar-fallback'
className={cn(
@@ -51,11 +46,9 @@ function AvatarFallback({
)}
{...props}
/>
);
}
);
function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
return (
const AvatarBadge = ({ className, ...props }: React.ComponentProps<'span'>) => (
<span
data-slot='avatar-badge'
className={cn(
@@ -67,11 +60,9 @@ function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
)}
{...props}
/>
);
}
);
function AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
const AvatarGroup = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='avatar-group'
className={cn(
@@ -80,14 +71,12 @@ function AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function AvatarGroupCount({
const AvatarGroupCount = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='avatar-group-count'
className={cn(
@@ -96,8 +85,7 @@ function AvatarGroupCount({
)}
{...props}
/>
);
}
);
export {
Avatar,
+3 -3
View File
@@ -27,13 +27,13 @@ const badgeVariants = cva(
},
);
function Badge({
const Badge = ({
className,
variant = 'default',
asChild = false,
...props
}: React.ComponentProps<'span'> &
VariantProps<typeof badgeVariants> & { asChild?: boolean }) {
VariantProps<typeof badgeVariants> & { asChild?: boolean }) => {
const Comp = asChild ? Slot.Root : 'span';
return (
@@ -44,6 +44,6 @@ function Badge({
{...props}
/>
);
}
};
export { Badge, badgeVariants };
+27 -28
View File
@@ -4,12 +4,14 @@ import { Slot } from 'radix-ui';
import { cn } from '@gib/ui';
function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) {
return <nav aria-label='breadcrumb' data-slot='breadcrumb' {...props} />;
}
const Breadcrumb = ({ ...props }: React.ComponentProps<'nav'>) => (
<nav aria-label='breadcrumb' data-slot='breadcrumb' {...props} />
);
function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
return (
const BreadcrumbList = ({
className,
...props
}: React.ComponentProps<'ol'>) => (
<ol
data-slot='breadcrumb-list'
className={cn(
@@ -18,26 +20,26 @@ function BreadcrumbList({ className, ...props }: React.ComponentProps<'ol'>) {
)}
{...props}
/>
);
}
);
function BreadcrumbItem({ className, ...props }: React.ComponentProps<'li'>) {
return (
const BreadcrumbItem = ({
className,
...props
}: React.ComponentProps<'li'>) => (
<li
data-slot='breadcrumb-item'
className={cn('inline-flex items-center gap-1.5', className)}
{...props}
/>
);
}
);
function BreadcrumbLink({
const BreadcrumbLink = ({
asChild,
className,
...props
}: React.ComponentProps<'a'> & {
asChild?: boolean;
}) {
}) => {
const Comp = asChild ? Slot.Root : 'a';
return (
@@ -47,10 +49,12 @@ function BreadcrumbLink({
{...props}
/>
);
}
};
function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
return (
const BreadcrumbPage = ({
className,
...props
}: React.ComponentProps<'span'>) => (
<span
data-slot='breadcrumb-page'
role='link'
@@ -59,15 +63,13 @@ function BreadcrumbPage({ className, ...props }: React.ComponentProps<'span'>) {
className={cn('text-foreground font-normal', className)}
{...props}
/>
);
}
);
function BreadcrumbSeparator({
const BreadcrumbSeparator = ({
children,
className,
...props
}: React.ComponentProps<'li'>) {
return (
}: React.ComponentProps<'li'>) => (
<li
data-slot='breadcrumb-separator'
role='presentation'
@@ -77,14 +79,12 @@ function BreadcrumbSeparator({
>
{children ?? <ChevronRight />}
</li>
);
}
);
function BreadcrumbEllipsis({
const BreadcrumbEllipsis = ({
className,
...props
}: React.ComponentProps<'span'>) {
return (
}: React.ComponentProps<'span'>) => (
<span
data-slot='breadcrumb-ellipsis'
role='presentation'
@@ -95,8 +95,7 @@ function BreadcrumbEllipsis({
<MoreHorizontal className='size-4' />
<span className='sr-only'>More</span>
</span>
);
}
);
export {
Breadcrumb,
+9 -13
View File
@@ -21,12 +21,11 @@ const buttonGroupVariants = cva(
},
);
function ButtonGroup({
const ButtonGroup = ({
className,
orientation,
...props
}: React.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>) {
return (
}: React.ComponentProps<'div'> & VariantProps<typeof buttonGroupVariants>) => (
<div
role='group'
data-slot='button-group'
@@ -34,16 +33,15 @@ function ButtonGroup({
className={cn(buttonGroupVariants({ orientation }), className)}
{...props}
/>
);
}
);
function ButtonGroupText({
const ButtonGroupText = ({
className,
asChild = false,
...props
}: React.ComponentProps<'div'> & {
asChild?: boolean;
}) {
}) => {
const Comp = asChild ? Slot.Root : 'div';
return (
@@ -55,14 +53,13 @@ function ButtonGroupText({
{...props}
/>
);
}
};
function ButtonGroupSeparator({
const ButtonGroupSeparator = ({
className,
orientation = 'vertical',
...props
}: React.ComponentProps<typeof Separator>) {
return (
}: React.ComponentProps<typeof Separator>) => (
<Separator
data-slot='button-group-separator'
orientation={orientation}
@@ -72,8 +69,7 @@ function ButtonGroupSeparator({
)}
{...props}
/>
);
}
);
export {
ButtonGroup,
+3 -3
View File
@@ -42,7 +42,7 @@ const buttonVariants = cva(
},
);
function Button({
const Button = ({
className,
variant = 'default',
size = 'default',
@@ -51,7 +51,7 @@ function Button({
}: React.ComponentProps<'button'> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
}) {
}) => {
const Comp = asChild ? Slot.Root : 'button';
return (
@@ -63,6 +63,6 @@ function Button({
{...props}
/>
);
}
};
export { Button, buttonVariants };
+6 -6
View File
@@ -11,7 +11,7 @@ import { DayPicker, getDefaultClassNames } from 'react-day-picker';
import { Button, buttonVariants, cn } from '@gib/ui';
function Calendar({
const Calendar = ({
className,
classNames,
showOutsideDays = true,
@@ -23,7 +23,7 @@ function Calendar({
...props
}: React.ComponentProps<typeof DayPicker> & {
buttonVariant?: React.ComponentProps<typeof Button>['variant'];
}) {
}) => {
const defaultClassNames = getDefaultClassNames();
return (
@@ -183,15 +183,15 @@ function Calendar({
{...props}
/>
);
}
};
function CalendarDayButton({
const CalendarDayButton = ({
className,
day,
modifiers,
locale,
...props
}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) {
}: React.ComponentProps<typeof DayButton> & { locale?: Partial<Locale> }) => {
const defaultClassNames = getDefaultClassNames();
const ref = React.useRef<HTMLButtonElement>(null);
@@ -222,6 +222,6 @@ function CalendarDayButton({
{...props}
/>
);
}
};
export { Calendar, CalendarDayButton };
+18 -29
View File
@@ -2,12 +2,11 @@ import type * as React from 'react';
import { cn } from '@gib/ui';
function Card({
const Card = ({
className,
size = 'default',
...props
}: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) {
return (
}: React.ComponentProps<'div'> & { size?: 'default' | 'sm' }) => (
<div
data-slot='card'
data-size={size}
@@ -17,11 +16,9 @@ function Card({
)}
{...props}
/>
);
}
);
function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const CardHeader = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='card-header'
className={cn(
@@ -30,11 +27,9 @@ function CardHeader({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
return (
const CardTitle = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='card-title'
className={cn(
@@ -43,21 +38,20 @@ function CardTitle({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function CardDescription({ className, ...props }: React.ComponentProps<'div'>) {
return (
const CardDescription = ({
className,
...props
}: React.ComponentProps<'div'>) => (
<div
data-slot='card-description'
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
}
);
function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
return (
const CardAction = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='card-action'
className={cn(
@@ -66,21 +60,17 @@ function CardAction({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function CardContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
const CardContent = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='card-content'
className={cn('px-4 group-data-[size=sm]/card:px-3', className)}
{...props}
/>
);
}
);
function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
const CardFooter = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='card-footer'
className={cn(
@@ -89,8 +79,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
export {
Card,
+20 -17
View File
@@ -12,12 +12,12 @@ type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;
type CarouselOptions = UseCarouselParameters[0];
type CarouselPlugin = UseCarouselParameters[1];
type CarouselProps = {
interface CarouselProps {
opts?: CarouselOptions;
plugins?: CarouselPlugin;
orientation?: 'horizontal' | 'vertical';
setApi?: (api: CarouselApi) => void;
};
}
type CarouselContextProps = {
carouselRef: ReturnType<typeof useEmblaCarousel>[0];
@@ -30,7 +30,7 @@ type CarouselContextProps = {
const CarouselContext = React.createContext<CarouselContextProps | null>(null);
function useCarousel() {
const useCarousel = () => {
const context = React.useContext(CarouselContext);
if (!context) {
@@ -38,9 +38,9 @@ function useCarousel() {
}
return context;
}
};
function Carousel({
const Carousel = ({
orientation = 'horizontal',
opts,
setApi,
@@ -48,7 +48,7 @@ function Carousel({
className,
children,
...props
}: React.ComponentProps<'div'> & CarouselProps) {
}: React.ComponentProps<'div'> & CarouselProps) => {
const [carouselRef, api] = useEmblaCarousel(
{
...opts,
@@ -128,9 +128,12 @@ function Carousel({
</div>
</CarouselContext.Provider>
);
}
};
function CarouselContent({ className, ...props }: React.ComponentProps<'div'>) {
const CarouselContent = ({
className,
...props
}: React.ComponentProps<'div'>) => {
const { carouselRef, orientation } = useCarousel();
return (
@@ -149,9 +152,9 @@ function CarouselContent({ className, ...props }: React.ComponentProps<'div'>) {
/>
</div>
);
}
};
function CarouselItem({ className, ...props }: React.ComponentProps<'div'>) {
const CarouselItem = ({ className, ...props }: React.ComponentProps<'div'>) => {
const { orientation } = useCarousel();
return (
@@ -167,14 +170,14 @@ function CarouselItem({ className, ...props }: React.ComponentProps<'div'>) {
{...props}
/>
);
}
};
function CarouselPrevious({
const CarouselPrevious = ({
className,
variant = 'outline',
size = 'icon-sm',
...props
}: React.ComponentProps<typeof Button>) {
}: React.ComponentProps<typeof Button>) => {
const { orientation, scrollPrev, canScrollPrev } = useCarousel();
return (
@@ -197,14 +200,14 @@ function CarouselPrevious({
<span className='sr-only'>Previous slide</span>
</Button>
);
}
};
function CarouselNext({
const CarouselNext = ({
className,
variant = 'outline',
size = 'icon-sm',
...props
}: React.ComponentProps<typeof Button>) {
}: React.ComponentProps<typeof Button>) => {
const { orientation, scrollNext, canScrollNext } = useCarousel();
return (
@@ -227,7 +230,7 @@ function CarouselNext({
<span className='sr-only'>Next slide</span>
</Button>
);
}
};
export {
type CarouselApi,
+23 -24
View File
@@ -8,23 +8,24 @@ import { cn } from '@gib/ui';
// Format: { THEME_NAME: CSS_SELECTOR }
const THEMES = { light: '', dark: '.dark' } as const;
export type ChartConfig = {
[k in string]: {
export type ChartConfig = Record<
string,
{
label?: React.ReactNode;
icon?: React.ComponentType;
} & (
| { color?: string; theme?: never }
| { color?: never; theme: Record<keyof typeof THEMES, string> }
);
};
)
>;
type ChartContextProps = {
interface ChartContextProps {
config: ChartConfig;
};
}
const ChartContext = React.createContext<ChartContextProps | null>(null);
function useChart() {
const useChart = () => {
const context = React.useContext(ChartContext);
if (!context) {
@@ -32,9 +33,9 @@ function useChart() {
}
return context;
}
};
function ChartContainer({
const ChartContainer = ({
id,
className,
children,
@@ -45,7 +46,7 @@ function ChartContainer({
children: React.ComponentProps<
typeof RechartsPrimitive.ResponsiveContainer
>['children'];
}) {
}) => {
const uniqueId = React.useId();
const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`;
@@ -67,7 +68,7 @@ function ChartContainer({
</div>
</ChartContext.Provider>
);
}
};
const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => {
const colorConfig = Object.entries(config).filter(
@@ -104,7 +105,7 @@ ${colorConfig
const ChartTooltip = RechartsPrimitive.Tooltip;
function ChartTooltipContent({
const ChartTooltipContent = ({
active,
payload,
className,
@@ -125,7 +126,7 @@ function ChartTooltipContent({
indicator?: 'line' | 'dot' | 'dashed';
nameKey?: string;
labelKey?: string;
}) {
}) => {
const { config } = useChart();
const tooltipLabel = React.useMemo(() => {
@@ -138,7 +139,7 @@ function ChartTooltipContent({
const itemConfig = getPayloadConfigFromPayload(config, item, key);
const value =
!labelKey && typeof label === 'string'
? config[label as keyof typeof config]?.label || label
? config[label]?.label || label
: itemConfig?.label;
if (labelFormatter) {
@@ -248,11 +249,11 @@ function ChartTooltipContent({
</div>
</div>
);
}
};
const ChartLegend = RechartsPrimitive.Legend;
function ChartLegendContent({
const ChartLegendContent = ({
className,
hideIcon = false,
payload,
@@ -262,7 +263,7 @@ function ChartLegendContent({
Pick<RechartsPrimitive.LegendProps, 'payload' | 'verticalAlign'> & {
hideIcon?: boolean;
nameKey?: string;
}) {
}) => {
const { config } = useChart();
if (!payload?.length) {
@@ -306,13 +307,13 @@ function ChartLegendContent({
})}
</div>
);
}
};
function getPayloadConfigFromPayload(
const getPayloadConfigFromPayload = (
config: ChartConfig,
payload: unknown,
key: string,
) {
) => {
if (typeof payload !== 'object' || payload === null) {
return undefined;
}
@@ -341,10 +342,8 @@ function getPayloadConfigFromPayload(
] as string;
}
return configLabelKey in config
? config[configLabelKey]
: config[key as keyof typeof config];
}
return configLabelKey in config ? config[configLabelKey] : config[key];
};
export {
ChartContainer,
+3 -5
View File
@@ -6,11 +6,10 @@ import { Checkbox as CheckboxPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Checkbox({
const Checkbox = ({
className,
...props
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) {
return (
}: React.ComponentProps<typeof CheckboxPrimitive.Root>) => (
<CheckboxPrimitive.Root
data-slot='checkbox'
className={cn(
@@ -26,7 +25,6 @@ function Checkbox({
<CheckIcon />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
);
}
);
export { Checkbox };
+10 -14
View File
@@ -2,32 +2,28 @@
import { Collapsible as CollapsiblePrimitive } from 'radix-ui';
function Collapsible({
const Collapsible = ({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) {
return <CollapsiblePrimitive.Root data-slot='collapsible' {...props} />;
}
}: React.ComponentProps<typeof CollapsiblePrimitive.Root>) => (
<CollapsiblePrimitive.Root data-slot='collapsible' {...props} />
);
function CollapsibleTrigger({
const CollapsibleTrigger = ({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) {
return (
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleTrigger>) => (
<CollapsiblePrimitive.CollapsibleTrigger
data-slot='collapsible-trigger'
{...props}
/>
);
}
);
function CollapsibleContent({
const CollapsibleContent = ({
...props
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) {
return (
}: React.ComponentProps<typeof CollapsiblePrimitive.CollapsibleContent>) => (
<CollapsiblePrimitive.CollapsibleContent
data-slot='collapsible-content'
{...props}
/>
);
}
);
export { Collapsible, CollapsibleTrigger, CollapsibleContent };
+55 -71
View File
@@ -15,16 +15,15 @@ import {
const Combobox = ComboboxPrimitive.Root;
function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
return <ComboboxPrimitive.Value data-slot='combobox-value' {...props} />;
}
const ComboboxValue = ({ ...props }: ComboboxPrimitive.Value.Props) => (
<ComboboxPrimitive.Value data-slot='combobox-value' {...props} />
);
function ComboboxTrigger({
const ComboboxTrigger = ({
className,
children,
...props
}: ComboboxPrimitive.Trigger.Props) {
return (
}: ComboboxPrimitive.Trigger.Props) => (
<ComboboxPrimitive.Trigger
data-slot='combobox-trigger'
className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
@@ -33,11 +32,12 @@ function ComboboxTrigger({
{children}
<ChevronDownIcon className='text-muted-foreground pointer-events-none size-4' />
</ComboboxPrimitive.Trigger>
);
}
);
function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
return (
const ComboboxClear = ({
className,
...props
}: ComboboxPrimitive.Clear.Props) => (
<ComboboxPrimitive.Clear
data-slot='combobox-clear'
className={cn(className)}
@@ -48,10 +48,9 @@ function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
</InputGroupButton>
}
/>
);
}
);
function ComboboxInput({
const ComboboxInput = ({
className,
children,
disabled = false,
@@ -61,8 +60,7 @@ function ComboboxInput({
}: ComboboxPrimitive.Input.Props & {
showTrigger?: boolean;
showClear?: boolean;
}) {
return (
}) => (
<InputGroup className={cn('w-auto', className)}>
<ComboboxPrimitive.Input
render={<InputGroupInput disabled={disabled} />}
@@ -83,10 +81,9 @@ function ComboboxInput({
</InputGroupAddon>
{children}
</InputGroup>
);
}
);
function ComboboxContent({
const ComboboxContent = ({
className,
side = 'bottom',
sideOffset = 6,
@@ -98,8 +95,7 @@ function ComboboxContent({
Pick<
ComboboxPrimitive.Positioner.Props,
'side' | 'align' | 'sideOffset' | 'alignOffset' | 'anchor'
>) {
return (
>) => (
<ComboboxPrimitive.Portal>
<ComboboxPrimitive.Positioner
side={side}
@@ -120,11 +116,12 @@ function ComboboxContent({
/>
</ComboboxPrimitive.Positioner>
</ComboboxPrimitive.Portal>
);
}
);
function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
return (
const ComboboxList = ({
className,
...props
}: ComboboxPrimitive.List.Props) => (
<ComboboxPrimitive.List
data-slot='combobox-list'
className={cn(
@@ -133,15 +130,13 @@ function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
)}
{...props}
/>
);
}
);
function ComboboxItem({
const ComboboxItem = ({
className,
children,
...props
}: ComboboxPrimitive.Item.Props) {
return (
}: ComboboxPrimitive.Item.Props) => (
<ComboboxPrimitive.Item
data-slot='combobox-item'
className={cn(
@@ -159,40 +154,40 @@ function ComboboxItem({
}
/>
</ComboboxPrimitive.Item>
);
}
);
function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
return (
const ComboboxGroup = ({
className,
...props
}: ComboboxPrimitive.Group.Props) => (
<ComboboxPrimitive.Group
data-slot='combobox-group'
className={cn(className)}
{...props}
/>
);
}
);
function ComboboxLabel({
const ComboboxLabel = ({
className,
...props
}: ComboboxPrimitive.GroupLabel.Props) {
return (
}: ComboboxPrimitive.GroupLabel.Props) => (
<ComboboxPrimitive.GroupLabel
data-slot='combobox-label'
className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)}
{...props}
/>
);
}
);
function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
return (
const ComboboxCollection = ({
...props
}: ComboboxPrimitive.Collection.Props) => (
<ComboboxPrimitive.Collection data-slot='combobox-collection' {...props} />
);
}
);
function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
return (
const ComboboxEmpty = ({
className,
...props
}: ComboboxPrimitive.Empty.Props) => (
<ComboboxPrimitive.Empty
data-slot='combobox-empty'
className={cn(
@@ -201,28 +196,24 @@ function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
)}
{...props}
/>
);
}
);
function ComboboxSeparator({
const ComboboxSeparator = ({
className,
...props
}: ComboboxPrimitive.Separator.Props) {
return (
}: ComboboxPrimitive.Separator.Props) => (
<ComboboxPrimitive.Separator
data-slot='combobox-separator'
className={cn('bg-border -mx-1 my-1 h-px', className)}
{...props}
/>
);
}
);
function ComboboxChips({
const ComboboxChips = ({
className,
...props
}: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &
ComboboxPrimitive.Chips.Props) {
return (
ComboboxPrimitive.Chips.Props) => (
<ComboboxPrimitive.Chips
data-slot='combobox-chips'
className={cn(
@@ -231,18 +222,16 @@ function ComboboxChips({
)}
{...props}
/>
);
}
);
function ComboboxChip({
const ComboboxChip = ({
className,
children,
showRemove = true,
...props
}: ComboboxPrimitive.Chip.Props & {
showRemove?: boolean;
}) {
return (
}) => (
<ComboboxPrimitive.Chip
data-slot='combobox-chip'
className={cn(
@@ -264,25 +253,20 @@ function ComboboxChip({
/>
)}
</ComboboxPrimitive.Chip>
);
}
);
function ComboboxChipsInput({
const ComboboxChipsInput = ({
className,
...props
}: ComboboxPrimitive.Input.Props) {
return (
}: ComboboxPrimitive.Input.Props) => (
<ComboboxPrimitive.Input
data-slot='combobox-chip-input'
className={cn('min-w-16 flex-1 outline-none', className)}
{...props}
/>
);
}
);
function useComboboxAnchor() {
return React.useRef<HTMLDivElement | null>(null);
}
const useComboboxAnchor = () => React.useRef<HTMLDivElement | null>(null);
export {
Combobox,
+27 -45
View File
@@ -15,11 +15,10 @@ import {
InputGroupAddon,
} from '@gib/ui';
function Command({
const Command = ({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive>) {
return (
}: React.ComponentProps<typeof CommandPrimitive>) => (
<CommandPrimitive
data-slot='command'
className={cn(
@@ -28,10 +27,9 @@ function Command({
)}
{...props}
/>
);
}
);
function CommandDialog({
const CommandDialog = ({
title = 'Command Palette',
description = 'Search for a command to run...',
children,
@@ -43,8 +41,7 @@ function CommandDialog({
description?: string;
className?: string;
showCloseButton?: boolean;
}) {
return (
}) => (
<Dialog {...props}>
<DialogHeader className='sr-only'>
<DialogTitle>{title}</DialogTitle>
@@ -60,14 +57,12 @@ function CommandDialog({
{children}
</DialogContent>
</Dialog>
);
}
);
function CommandInput({
const CommandInput = ({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Input>) {
return (
}: React.ComponentProps<typeof CommandPrimitive.Input>) => (
<div data-slot='command-input-wrapper' className='p-1 pb-0'>
<InputGroup className='bg-input/30 border-input/30 h-8! rounded-lg! shadow-none! *:data-[slot=input-group-addon]:pl-2!'>
<CommandPrimitive.Input
@@ -83,14 +78,12 @@ function CommandInput({
</InputGroupAddon>
</InputGroup>
</div>
);
}
);
function CommandList({
const CommandList = ({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.List>) {
return (
}: React.ComponentProps<typeof CommandPrimitive.List>) => (
<CommandPrimitive.List
data-slot='command-list'
className={cn(
@@ -99,27 +92,23 @@ function CommandList({
)}
{...props}
/>
);
}
);
function CommandEmpty({
const CommandEmpty = ({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Empty>) {
return (
}: React.ComponentProps<typeof CommandPrimitive.Empty>) => (
<CommandPrimitive.Empty
data-slot='command-empty'
className={cn('py-6 text-center text-sm', className)}
{...props}
/>
);
}
);
function CommandGroup({
const CommandGroup = ({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Group>) {
return (
}: React.ComponentProps<typeof CommandPrimitive.Group>) => (
<CommandPrimitive.Group
data-slot='command-group'
className={cn(
@@ -128,28 +117,24 @@ function CommandGroup({
)}
{...props}
/>
);
}
);
function CommandSeparator({
const CommandSeparator = ({
className,
...props
}: React.ComponentProps<typeof CommandPrimitive.Separator>) {
return (
}: React.ComponentProps<typeof CommandPrimitive.Separator>) => (
<CommandPrimitive.Separator
data-slot='command-separator'
className={cn('bg-border -mx-1 h-px', className)}
{...props}
/>
);
}
);
function CommandItem({
const CommandItem = ({
className,
children,
...props
}: React.ComponentProps<typeof CommandPrimitive.Item>) {
return (
}: React.ComponentProps<typeof CommandPrimitive.Item>) => (
<CommandPrimitive.Item
data-slot='command-item'
className={cn(
@@ -161,14 +146,12 @@ function CommandItem({
{children}
<CheckIcon className='ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100' />
</CommandPrimitive.Item>
);
}
);
function CommandShortcut({
const CommandShortcut = ({
className,
...props
}: React.ComponentProps<'span'>) {
return (
}: React.ComponentProps<'span'>) => (
<span
data-slot='command-shortcut'
className={cn(
@@ -177,8 +160,7 @@ function CommandShortcut({
)}
{...props}
/>
);
}
);
export {
Command,
+47 -73
View File
@@ -6,65 +6,56 @@ import { ContextMenu as ContextMenuPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function ContextMenu({
const ContextMenu = ({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {
return <ContextMenuPrimitive.Root data-slot='context-menu' {...props} />;
}
}: React.ComponentProps<typeof ContextMenuPrimitive.Root>) => (
<ContextMenuPrimitive.Root data-slot='context-menu' {...props} />
);
function ContextMenuTrigger({
const ContextMenuTrigger = ({
className,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) => (
<ContextMenuPrimitive.Trigger
data-slot='context-menu-trigger'
className={cn('select-none', className)}
{...props}
/>
);
}
);
function ContextMenuGroup({
const ContextMenuGroup = ({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {
return (
}: React.ComponentProps<typeof ContextMenuPrimitive.Group>) => (
<ContextMenuPrimitive.Group data-slot='context-menu-group' {...props} />
);
}
);
function ContextMenuPortal({
const ContextMenuPortal = ({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) {
return (
}: React.ComponentProps<typeof ContextMenuPrimitive.Portal>) => (
<ContextMenuPrimitive.Portal data-slot='context-menu-portal' {...props} />
);
}
);
function ContextMenuSub({
const ContextMenuSub = ({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {
return <ContextMenuPrimitive.Sub data-slot='context-menu-sub' {...props} />;
}
}: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) => (
<ContextMenuPrimitive.Sub data-slot='context-menu-sub' {...props} />
);
function ContextMenuRadioGroup({
const ContextMenuRadioGroup = ({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
return (
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) => (
<ContextMenuPrimitive.RadioGroup
data-slot='context-menu-radio-group'
{...props}
/>
);
}
);
function ContextMenuContent({
const ContextMenuContent = ({
className,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Content> & {
side?: 'top' | 'right' | 'bottom' | 'left';
}) {
return (
}) => (
<ContextMenuPrimitive.Portal>
<ContextMenuPrimitive.Content
data-slot='context-menu-content'
@@ -75,10 +66,9 @@ function ContextMenuContent({
{...props}
/>
</ContextMenuPrimitive.Portal>
);
}
);
function ContextMenuItem({
const ContextMenuItem = ({
className,
inset,
variant = 'default',
@@ -86,8 +76,7 @@ function ContextMenuItem({
}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
inset?: boolean;
variant?: 'default' | 'destructive';
}) {
return (
}) => (
<ContextMenuPrimitive.Item
data-slot='context-menu-item'
data-inset={inset}
@@ -98,18 +87,16 @@ function ContextMenuItem({
)}
{...props}
/>
);
}
);
function ContextMenuSubTrigger({
const ContextMenuSubTrigger = ({
className,
inset,
children,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
inset?: boolean;
}) {
return (
}) => (
<ContextMenuPrimitive.SubTrigger
data-slot='context-menu-sub-trigger'
data-inset={inset}
@@ -122,14 +109,12 @@ function ContextMenuSubTrigger({
{children}
<ChevronRightIcon className='cn-rtl-flip ml-auto' />
</ContextMenuPrimitive.SubTrigger>
);
}
);
function ContextMenuSubContent({
const ContextMenuSubContent = ({
className,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
return (
}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) => (
<ContextMenuPrimitive.SubContent
data-slot='context-menu-sub-content'
className={cn(
@@ -138,10 +123,9 @@ function ContextMenuSubContent({
)}
{...props}
/>
);
}
);
function ContextMenuCheckboxItem({
const ContextMenuCheckboxItem = ({
className,
children,
checked,
@@ -149,8 +133,7 @@ function ContextMenuCheckboxItem({
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem> & {
inset?: boolean;
}) {
return (
}) => (
<ContextMenuPrimitive.CheckboxItem
data-slot='context-menu-checkbox-item'
data-inset={inset}
@@ -168,18 +151,16 @@ function ContextMenuCheckboxItem({
</span>
{children}
</ContextMenuPrimitive.CheckboxItem>
);
}
);
function ContextMenuRadioItem({
const ContextMenuRadioItem = ({
className,
children,
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem> & {
inset?: boolean;
}) {
return (
}) => (
<ContextMenuPrimitive.RadioItem
data-slot='context-menu-radio-item'
data-inset={inset}
@@ -196,17 +177,15 @@ function ContextMenuRadioItem({
</span>
{children}
</ContextMenuPrimitive.RadioItem>
);
}
);
function ContextMenuLabel({
const ContextMenuLabel = ({
className,
inset,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
inset?: boolean;
}) {
return (
}) => (
<ContextMenuPrimitive.Label
data-slot='context-menu-label'
data-inset={inset}
@@ -216,27 +195,23 @@ function ContextMenuLabel({
)}
{...props}
/>
);
}
);
function ContextMenuSeparator({
const ContextMenuSeparator = ({
className,
...props
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {
return (
}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) => (
<ContextMenuPrimitive.Separator
data-slot='context-menu-separator'
className={cn('bg-border -mx-1 my-1 h-px', className)}
{...props}
/>
);
}
);
function ContextMenuShortcut({
const ContextMenuShortcut = ({
className,
...props
}: React.ComponentProps<'span'>) {
return (
}: React.ComponentProps<'span'>) => (
<span
data-slot='context-menu-shortcut'
className={cn(
@@ -245,8 +220,7 @@ function ContextMenuShortcut({
)}
{...props}
/>
);
}
);
export {
ContextMenu,
+33 -45
View File
@@ -6,35 +6,34 @@ import { Dialog as DialogPrimitive } from 'radix-ui';
import { Button, cn } from '@gib/ui';
function Dialog({
const Dialog = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
return <DialogPrimitive.Root data-slot='dialog' {...props} />;
}
}: React.ComponentProps<typeof DialogPrimitive.Root>) => (
<DialogPrimitive.Root data-slot='dialog' {...props} />
);
function DialogTrigger({
const DialogTrigger = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
return <DialogPrimitive.Trigger data-slot='dialog-trigger' {...props} />;
}
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) => (
<DialogPrimitive.Trigger data-slot='dialog-trigger' {...props} />
);
function DialogPortal({
const DialogPortal = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
return <DialogPrimitive.Portal data-slot='dialog-portal' {...props} />;
}
}: React.ComponentProps<typeof DialogPrimitive.Portal>) => (
<DialogPrimitive.Portal data-slot='dialog-portal' {...props} />
);
function DialogClose({
const DialogClose = ({
...props
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
return <DialogPrimitive.Close data-slot='dialog-close' {...props} />;
}
}: React.ComponentProps<typeof DialogPrimitive.Close>) => (
<DialogPrimitive.Close data-slot='dialog-close' {...props} />
);
function DialogOverlay({
const DialogOverlay = ({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
return (
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) => (
<DialogPrimitive.Overlay
data-slot='dialog-overlay'
className={cn(
@@ -43,18 +42,16 @@ function DialogOverlay({
)}
{...props}
/>
);
}
);
function DialogContent({
const DialogContent = ({
className,
children,
showCloseButton = true,
...props
}: React.ComponentProps<typeof DialogPrimitive.Content> & {
showCloseButton?: boolean;
}) {
return (
}) => (
<DialogPortal>
<DialogOverlay />
<DialogPrimitive.Content
@@ -80,28 +77,24 @@ function DialogContent({
)}
</DialogPrimitive.Content>
</DialogPortal>
);
}
);
function DialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const DialogHeader = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='dialog-header'
className={cn('flex flex-col gap-2', className)}
{...props}
/>
);
}
);
function DialogFooter({
const DialogFooter = ({
className,
showCloseButton = false,
children,
...props
}: React.ComponentProps<'div'> & {
showCloseButton?: boolean;
}) {
return (
}) => (
<div
data-slot='dialog-footer'
className={cn(
@@ -117,27 +110,23 @@ function DialogFooter({
</DialogPrimitive.Close>
)}
</div>
);
}
);
function DialogTitle({
const DialogTitle = ({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
return (
}: React.ComponentProps<typeof DialogPrimitive.Title>) => (
<DialogPrimitive.Title
data-slot='dialog-title'
className={cn('text-base leading-none font-medium', className)}
{...props}
/>
);
}
);
function DialogDescription({
const DialogDescription = ({
className,
...props
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
return (
}: React.ComponentProps<typeof DialogPrimitive.Description>) => (
<DialogPrimitive.Description
data-slot='dialog-description'
className={cn(
@@ -146,8 +135,7 @@ function DialogDescription({
)}
{...props}
/>
);
}
);
export {
Dialog,
+32 -44
View File
@@ -5,35 +5,34 @@ import { Drawer as DrawerPrimitive } from 'vaul';
import { cn } from '@gib/ui';
function Drawer({
const Drawer = ({
...props
}: React.ComponentProps<typeof DrawerPrimitive.Root>) {
return <DrawerPrimitive.Root data-slot='drawer' {...props} />;
}
}: React.ComponentProps<typeof DrawerPrimitive.Root>) => (
<DrawerPrimitive.Root data-slot='drawer' {...props} />
);
function DrawerTrigger({
const DrawerTrigger = ({
...props
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) {
return <DrawerPrimitive.Trigger data-slot='drawer-trigger' {...props} />;
}
}: React.ComponentProps<typeof DrawerPrimitive.Trigger>) => (
<DrawerPrimitive.Trigger data-slot='drawer-trigger' {...props} />
);
function DrawerPortal({
const DrawerPortal = ({
...props
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) {
return <DrawerPrimitive.Portal data-slot='drawer-portal' {...props} />;
}
}: React.ComponentProps<typeof DrawerPrimitive.Portal>) => (
<DrawerPrimitive.Portal data-slot='drawer-portal' {...props} />
);
function DrawerClose({
const DrawerClose = ({
...props
}: React.ComponentProps<typeof DrawerPrimitive.Close>) {
return <DrawerPrimitive.Close data-slot='drawer-close' {...props} />;
}
}: React.ComponentProps<typeof DrawerPrimitive.Close>) => (
<DrawerPrimitive.Close data-slot='drawer-close' {...props} />
);
function DrawerOverlay({
const DrawerOverlay = ({
className,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) {
return (
}: React.ComponentProps<typeof DrawerPrimitive.Overlay>) => (
<DrawerPrimitive.Overlay
data-slot='drawer-overlay'
className={cn(
@@ -42,15 +41,13 @@ function DrawerOverlay({
)}
{...props}
/>
);
}
);
function DrawerContent({
const DrawerContent = ({
className,
children,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Content>) {
return (
}: React.ComponentProps<typeof DrawerPrimitive.Content>) => (
<DrawerPortal data-slot='drawer-portal'>
<DrawerOverlay />
<DrawerPrimitive.Content
@@ -65,11 +62,9 @@ function DrawerContent({
{children}
</DrawerPrimitive.Content>
</DrawerPortal>
);
}
);
function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const DrawerHeader = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='drawer-header'
className={cn(
@@ -78,44 +73,37 @@ function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
const DrawerFooter = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='drawer-footer'
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
{...props}
/>
);
}
);
function DrawerTitle({
const DrawerTitle = ({
className,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Title>) {
return (
}: React.ComponentProps<typeof DrawerPrimitive.Title>) => (
<DrawerPrimitive.Title
data-slot='drawer-title'
className={cn('text-foreground text-base font-medium', className)}
{...props}
/>
);
}
);
function DrawerDescription({
const DrawerDescription = ({
className,
...props
}: React.ComponentProps<typeof DrawerPrimitive.Description>) {
return (
}: React.ComponentProps<typeof DrawerPrimitive.Description>) => (
<DrawerPrimitive.Description
data-slot='drawer-description'
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
}
);
export {
Drawer,
+48 -77
View File
@@ -6,38 +6,30 @@ import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function DropdownMenu({
const DropdownMenu = ({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot='dropdown-menu' {...props} />;
}
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) => (
<DropdownMenuPrimitive.Root data-slot='dropdown-menu' {...props} />
);
function DropdownMenuPortal({
const DropdownMenuPortal = ({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) => (
<DropdownMenuPrimitive.Portal data-slot='dropdown-menu-portal' {...props} />
);
}
);
function DropdownMenuTrigger({
const DropdownMenuTrigger = ({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot='dropdown-menu-trigger'
{...props}
/>
);
}
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) => (
<DropdownMenuPrimitive.Trigger data-slot='dropdown-menu-trigger' {...props} />
);
function DropdownMenuContent({
const DropdownMenuContent = ({
className,
align = 'start',
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) => (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
data-slot='dropdown-menu-content'
@@ -50,18 +42,15 @@ function DropdownMenuContent({
{...props}
/>
</DropdownMenuPrimitive.Portal>
);
}
);
function DropdownMenuGroup({
const DropdownMenuGroup = ({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) => (
<DropdownMenuPrimitive.Group data-slot='dropdown-menu-group' {...props} />
);
}
);
function DropdownMenuItem({
const DropdownMenuItem = ({
className,
inset,
variant = 'default',
@@ -69,8 +58,7 @@ function DropdownMenuItem({
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean;
variant?: 'default' | 'destructive';
}) {
return (
}) => (
<DropdownMenuPrimitive.Item
data-slot='dropdown-menu-item'
data-inset={inset}
@@ -81,10 +69,9 @@ function DropdownMenuItem({
)}
{...props}
/>
);
}
);
function DropdownMenuCheckboxItem({
const DropdownMenuCheckboxItem = ({
className,
children,
checked,
@@ -92,8 +79,7 @@ function DropdownMenuCheckboxItem({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem> & {
inset?: boolean;
}) {
return (
}) => (
<DropdownMenuPrimitive.CheckboxItem
data-slot='dropdown-menu-checkbox-item'
data-inset={inset}
@@ -114,29 +100,25 @@ function DropdownMenuCheckboxItem({
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
);
}
);
function DropdownMenuRadioGroup({
const DropdownMenuRadioGroup = ({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) => (
<DropdownMenuPrimitive.RadioGroup
data-slot='dropdown-menu-radio-group'
{...props}
/>
);
}
);
function DropdownMenuRadioItem({
const DropdownMenuRadioItem = ({
className,
children,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem> & {
inset?: boolean;
}) {
return (
}) => (
<DropdownMenuPrimitive.RadioItem
data-slot='dropdown-menu-radio-item'
data-inset={inset}
@@ -156,17 +138,15 @@ function DropdownMenuRadioItem({
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
);
}
);
function DropdownMenuLabel({
const DropdownMenuLabel = ({
className,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean;
}) {
return (
}) => (
<DropdownMenuPrimitive.Label
data-slot='dropdown-menu-label'
data-inset={inset}
@@ -176,27 +156,23 @@ function DropdownMenuLabel({
)}
{...props}
/>
);
}
);
function DropdownMenuSeparator({
const DropdownMenuSeparator = ({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return (
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) => (
<DropdownMenuPrimitive.Separator
data-slot='dropdown-menu-separator'
className={cn('bg-border -mx-1 my-1 h-px', className)}
{...props}
/>
);
}
);
function DropdownMenuShortcut({
const DropdownMenuShortcut = ({
className,
...props
}: React.ComponentProps<'span'>) {
return (
}: React.ComponentProps<'span'>) => (
<span
data-slot='dropdown-menu-shortcut'
className={cn(
@@ -205,24 +181,22 @@ function DropdownMenuShortcut({
)}
{...props}
/>
);
}
);
function DropdownMenuSub({
const DropdownMenuSub = ({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot='dropdown-menu-sub' {...props} />;
}
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) => (
<DropdownMenuPrimitive.Sub data-slot='dropdown-menu-sub' {...props} />
);
function DropdownMenuSubTrigger({
const DropdownMenuSubTrigger = ({
className,
inset,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean;
}) {
return (
}) => (
<DropdownMenuPrimitive.SubTrigger
data-slot='dropdown-menu-sub-trigger'
data-inset={inset}
@@ -235,14 +209,12 @@ function DropdownMenuSubTrigger({
{children}
<ChevronRightIcon className='cn-rtl-flip ml-auto' />
</DropdownMenuPrimitive.SubTrigger>
);
}
);
function DropdownMenuSubContent({
const DropdownMenuSubContent = ({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return (
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) => (
<DropdownMenuPrimitive.SubContent
data-slot='dropdown-menu-sub-content'
className={cn(
@@ -251,8 +223,7 @@ function DropdownMenuSubContent({
)}
{...props}
/>
);
}
);
export {
DropdownMenu,
+16 -25
View File
@@ -3,8 +3,7 @@ import { cva } from 'class-variance-authority';
import { cn } from '@gib/ui';
function Empty({ className, ...props }: React.ComponentProps<'div'>) {
return (
const Empty = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='empty'
className={cn(
@@ -13,18 +12,15 @@ function Empty({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function EmptyHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const EmptyHeader = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='empty-header'
className={cn('flex max-w-sm flex-col items-center gap-2', className)}
{...props}
/>
);
}
);
const emptyMediaVariants = cva(
'mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0',
@@ -41,33 +37,31 @@ const emptyMediaVariants = cva(
},
);
function EmptyMedia({
const EmptyMedia = ({
className,
variant = 'default',
...props
}: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {
return (
}: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) => (
<div
data-slot='empty-icon'
data-variant={variant}
className={cn(emptyMediaVariants({ variant, className }))}
{...props}
/>
);
}
);
function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
return (
const EmptyTitle = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='empty-title'
className={cn('text-sm font-medium tracking-tight', className)}
{...props}
/>
);
}
);
function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
return (
const EmptyDescription = ({
className,
...props
}: React.ComponentProps<'p'>) => (
<div
data-slot='empty-description'
className={cn(
@@ -76,11 +70,9 @@ function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
)}
{...props}
/>
);
}
);
function EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
const EmptyContent = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='empty-content'
className={cn(
@@ -89,8 +81,7 @@ function EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
export {
Empty,
+30 -48
View File
@@ -6,11 +6,10 @@ import { cva } from 'class-variance-authority';
import { cn, Label, Separator } from '@gib/ui';
export function FieldSet({
export const FieldSet = ({
className,
...props
}: React.ComponentProps<'fieldset'>) {
return (
}: React.ComponentProps<'fieldset'>) => (
<fieldset
data-slot='field-set'
className={cn(
@@ -20,15 +19,13 @@ export function FieldSet({
)}
{...props}
/>
);
}
);
export function FieldLegend({
export const FieldLegend = ({
className,
variant = 'legend',
...props
}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
return (
}: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) => (
<legend
data-slot='field-legend'
data-variant={variant}
@@ -40,14 +37,12 @@ export function FieldLegend({
)}
{...props}
/>
);
}
);
export function FieldGroup({
export const FieldGroup = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='field-group'
className={cn(
@@ -56,8 +51,7 @@ export function FieldGroup({
)}
{...props}
/>
);
}
);
const fieldVariants = cva(
'group/field data-[invalid=true]:text-destructive flex w-full gap-3',
@@ -83,12 +77,11 @@ const fieldVariants = cva(
},
);
export function Field({
export const Field = ({
className,
orientation = 'vertical',
...props
}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
return (
}: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) => (
<div
role='group'
data-slot='field'
@@ -96,14 +89,12 @@ export function Field({
className={cn(fieldVariants({ orientation }), className)}
{...props}
/>
);
}
);
export function FieldContent({
export const FieldContent = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='field-content'
className={cn(
@@ -112,14 +103,12 @@ export function FieldContent({
)}
{...props}
/>
);
}
);
export function FieldLabel({
export const FieldLabel = ({
className,
...props
}: React.ComponentProps<typeof Label>) {
return (
}: React.ComponentProps<typeof Label>) => (
<Label
data-slot='field-label'
className={cn(
@@ -130,14 +119,12 @@ export function FieldLabel({
)}
{...props}
/>
);
}
);
export function FieldTitle({
export const FieldTitle = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='field-label'
className={cn(
@@ -146,14 +133,12 @@ export function FieldTitle({
)}
{...props}
/>
);
}
);
export function FieldDescription({
export const FieldDescription = ({
className,
...props
}: React.ComponentProps<'p'>) {
return (
}: React.ComponentProps<'p'>) => (
<p
data-slot='field-description'
className={cn(
@@ -164,17 +149,15 @@ export function FieldDescription({
)}
{...props}
/>
);
}
);
export function FieldSeparator({
export const FieldSeparator = ({
children,
className,
...props
}: React.ComponentProps<'div'> & {
children?: React.ReactNode;
}) {
return (
}) => (
<div
data-slot='field-separator'
data-content={!!children}
@@ -194,17 +177,16 @@ export function FieldSeparator({
</span>
)}
</div>
);
}
);
export function FieldError({
export const FieldError = ({
className,
children,
errors: maybeErrors,
...props
}: React.ComponentProps<'div'> & {
errors?: ({ message?: string } | undefined)[];
}) {
}) => {
const content = useMemo(() => {
if (children) {
return children;
@@ -244,4 +226,4 @@ export function FieldError({
{content}
</div>
);
}
};
+19 -16
View File
@@ -15,12 +15,12 @@ import { cn, Label } from '@gib/ui';
const Form = FormProvider;
type FormFieldContextValue<
interface FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
> = {
> {
name: TName;
};
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue,
@@ -62,15 +62,15 @@ const useFormField = () => {
};
};
type FormItemContextValue = {
interface FormItemContextValue {
id: string;
};
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue,
);
function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
const FormItem = ({ className, ...props }: React.ComponentProps<'div'>) => {
const id = React.useId();
return (
@@ -82,12 +82,12 @@ function FormItem({ className, ...props }: React.ComponentProps<'div'>) {
/>
</FormItemContext.Provider>
);
}
};
function FormLabel({
const FormLabel = ({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
}: React.ComponentProps<typeof LabelPrimitive.Root>) => {
const { error, formItemId } = useFormField();
return (
@@ -99,9 +99,9 @@ function FormLabel({
{...props}
/>
);
}
};
function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
const FormControl = ({ ...props }: React.ComponentProps<typeof Slot>) => {
const { error, formItemId, formDescriptionId, formMessageId } =
useFormField();
@@ -118,9 +118,12 @@ function FormControl({ ...props }: React.ComponentProps<typeof Slot>) {
{...props}
/>
);
}
};
function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
const FormDescription = ({
className,
...props
}: React.ComponentProps<'p'>) => {
const { formDescriptionId } = useFormField();
return (
@@ -131,9 +134,9 @@ function FormDescription({ className, ...props }: React.ComponentProps<'p'>) {
{...props}
/>
);
}
};
function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
const FormMessage = ({ className, ...props }: React.ComponentProps<'p'>) => {
const { error, formMessageId } = useFormField();
const body = error ? String(error?.message ?? '') : props.children;
@@ -151,7 +154,7 @@ function FormMessage({ className, ...props }: React.ComponentProps<'p'>) {
{body}
</p>
);
}
};
export {
useFormField,
+2 -2
View File
@@ -2,7 +2,7 @@ import * as React from 'react';
const MOBILE_BREAKPOINT = 768;
export function useIsMobile() {
export const useIsMobile = () => {
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
undefined,
);
@@ -18,4 +18,4 @@ export function useIsMobile() {
}, []);
return !!isMobile;
}
};
@@ -9,12 +9,12 @@ type EventType =
| 'focusin'
| 'focusout';
export function useOnClickOutside<T extends HTMLElement = HTMLElement>(
export const useOnClickOutside = <T,>(
ref: React.RefObject<T | null> | React.RefObject<T | null>[],
handler: (event: MouseEvent | TouchEvent | FocusEvent) => void,
eventType: EventType = 'mousedown',
eventListenerOptions: AddEventListenerOptions = {},
): void {
): void => {
const savedHandler = React.useRef(handler);
React.useLayoutEffect(() => {
@@ -55,6 +55,6 @@ export function useOnClickOutside<T extends HTMLElement = HTMLElement>(
);
};
}, [ref, eventType, eventListenerOptions]);
}
};
export type { EventType };
+10 -14
View File
@@ -5,27 +5,24 @@ import { HoverCard as HoverCardPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function HoverCard({
const HoverCard = ({
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) {
return <HoverCardPrimitive.Root data-slot='hover-card' {...props} />;
}
}: React.ComponentProps<typeof HoverCardPrimitive.Root>) => (
<HoverCardPrimitive.Root data-slot='hover-card' {...props} />
);
function HoverCardTrigger({
const HoverCardTrigger = ({
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof HoverCardPrimitive.Trigger>) => (
<HoverCardPrimitive.Trigger data-slot='hover-card-trigger' {...props} />
);
}
);
function HoverCardContent({
const HoverCardContent = ({
className,
align = 'center',
sideOffset = 4,
...props
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) {
return (
}: React.ComponentProps<typeof HoverCardPrimitive.Content>) => (
<HoverCardPrimitive.Portal data-slot='hover-card-portal'>
<HoverCardPrimitive.Content
data-slot='hover-card-content'
@@ -38,7 +35,6 @@ function HoverCardContent({
{...props}
/>
</HoverCardPrimitive.Portal>
);
}
);
export { HoverCard, HoverCardTrigger, HoverCardContent };
+2 -2
View File
@@ -378,7 +378,7 @@ export const Cropper = ({
</ImageCrop>
);
export function Demo() {
export const Demo = () => {
const [file, setFile] = useState<File | null>(null);
const [croppedImage, setCroppedImage] = useState<string | null>(null);
@@ -432,4 +432,4 @@ export function Demo() {
</div>
</div>
);
}
};
+20 -28
View File
@@ -6,8 +6,7 @@ import { cva } from 'class-variance-authority';
import { Button, cn, Input, Textarea } from '@gib/ui';
function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
const InputGroup = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='input-group'
role='group'
@@ -17,8 +16,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
const inputGroupAddonVariants = cva(
"text-muted-foreground flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
@@ -41,12 +39,12 @@ const inputGroupAddonVariants = cva(
},
);
function InputGroupAddon({
const InputGroupAddon = ({
className,
align = 'inline-start',
...props
}: React.ComponentProps<'div'> & VariantProps<typeof inputGroupAddonVariants>) {
return (
}: React.ComponentProps<'div'> &
VariantProps<typeof inputGroupAddonVariants>) => (
<div
role='group'
data-slot='input-group-addon'
@@ -60,8 +58,7 @@ function InputGroupAddon({
}}
{...props}
/>
);
}
);
const inputGroupButtonVariants = cva(
'flex items-center gap-2 text-sm shadow-none',
@@ -81,15 +78,14 @@ const inputGroupButtonVariants = cva(
},
);
function InputGroupButton({
const InputGroupButton = ({
className,
type = 'button',
variant = 'ghost',
size = 'xs',
...props
}: Omit<React.ComponentProps<typeof Button>, 'size'> &
VariantProps<typeof inputGroupButtonVariants>) {
return (
VariantProps<typeof inputGroupButtonVariants>) => (
<Button
type={type}
data-size={size}
@@ -97,11 +93,12 @@ function InputGroupButton({
className={cn(inputGroupButtonVariants({ size }), className)}
{...props}
/>
);
}
);
function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
return (
const InputGroupText = ({
className,
...props
}: React.ComponentProps<'span'>) => (
<span
className={cn(
"text-muted-foreground flex items-center gap-2 text-sm [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
@@ -109,14 +106,12 @@ function InputGroupText({ className, ...props }: React.ComponentProps<'span'>) {
)}
{...props}
/>
);
}
);
function InputGroupInput({
const InputGroupInput = ({
className,
...props
}: React.ComponentProps<'input'>) {
return (
}: React.ComponentProps<'input'>) => (
<Input
data-slot='input-group-control'
className={cn(
@@ -125,14 +120,12 @@ function InputGroupInput({
)}
{...props}
/>
);
}
);
function InputGroupTextarea({
const InputGroupTextarea = ({
className,
...props
}: React.ComponentProps<'textarea'>) {
return (
}: React.ComponentProps<'textarea'>) => (
<Textarea
data-slot='input-group-control'
className={cn(
@@ -141,8 +134,7 @@ function InputGroupTextarea({
)}
{...props}
/>
);
}
);
export {
InputGroup,
+13 -16
View File
@@ -6,14 +6,13 @@ import { MinusIcon } from 'lucide-react';
import { cn } from '@gib/ui';
function InputOTP({
const InputOTP = ({
className,
containerClassName,
...props
}: React.ComponentProps<typeof OTPInput> & {
containerClassName?: string;
}) {
return (
}) => (
<OTPInput
data-slot='input-otp'
containerClassName={cn(
@@ -24,11 +23,12 @@ function InputOTP({
className={cn('disabled:cursor-not-allowed', className)}
{...props}
/>
);
}
);
function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
const InputOTPGroup = ({
className,
...props
}: React.ComponentProps<'div'>) => (
<div
data-slot='input-otp-group'
className={cn(
@@ -37,16 +37,15 @@ function InputOTPGroup({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function InputOTPSlot({
const InputOTPSlot = ({
index,
className,
...props
}: React.ComponentProps<'div'> & {
index: number;
}) {
}) => {
const inputOTPContext = React.useContext(OTPInputContext);
const { char, hasFakeCaret, isActive } = inputOTPContext?.slots[index] ?? {};
@@ -68,10 +67,9 @@ function InputOTPSlot({
)}
</div>
);
}
};
function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
return (
const InputOTPSeparator = ({ ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='input-otp-separator'
className="flex items-center [&_svg:not([class*='size-'])]:size-4"
@@ -80,7 +78,6 @@ function InputOTPSeparator({ ...props }: React.ComponentProps<'div'>) {
>
<MinusIcon />
</div>
);
}
);
export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
+6 -4
View File
@@ -2,8 +2,11 @@ import type * as React from 'react';
import { cn } from '@gib/ui';
function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
return (
const Input = ({
className,
type,
...props
}: React.ComponentProps<'input'>) => (
<input
type={type}
data-slot='input'
@@ -13,7 +16,6 @@ function Input({ className, type, ...props }: React.ComponentProps<'input'>) {
)}
{...props}
/>
);
}
);
export { Input };
+26 -41
View File
@@ -5,8 +5,7 @@ import { Slot } from 'radix-ui';
import { cn, Separator } from '@gib/ui';
function ItemGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
const ItemGroup = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
role='list'
data-slot='item-group'
@@ -16,22 +15,19 @@ function ItemGroup({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function ItemSeparator({
const ItemSeparator = ({
className,
...props
}: React.ComponentProps<typeof Separator>) {
return (
}: React.ComponentProps<typeof Separator>) => (
<Separator
data-slot='item-separator'
orientation='horizontal'
className={cn('my-2', className)}
{...props}
/>
);
}
);
const itemVariants = cva(
'[a]:hover:bg-muted group/item focus-visible:border-ring focus-visible:ring-ring/50 flex w-full flex-wrap items-center rounded-lg border text-sm transition-colors duration-100 outline-none focus-visible:ring-[3px] [a]:transition-colors',
@@ -55,14 +51,14 @@ const itemVariants = cva(
},
);
function Item({
const Item = ({
className,
variant = 'default',
size = 'default',
asChild = false,
...props
}: React.ComponentProps<'div'> &
VariantProps<typeof itemVariants> & { asChild?: boolean }) {
VariantProps<typeof itemVariants> & { asChild?: boolean }) => {
const Comp = asChild ? Slot.Root : 'div';
return (
<Comp
@@ -73,7 +69,7 @@ function Item({
{...props}
/>
);
}
};
const itemMediaVariants = cva(
'flex shrink-0 items-center justify-center gap-2 group-has-data-[slot=item-description]/item:translate-y-0.5 group-has-data-[slot=item-description]/item:self-start [&_svg]:pointer-events-none',
@@ -92,23 +88,20 @@ const itemMediaVariants = cva(
},
);
function ItemMedia({
const ItemMedia = ({
className,
variant = 'default',
...props
}: React.ComponentProps<'div'> & VariantProps<typeof itemMediaVariants>) {
return (
}: React.ComponentProps<'div'> & VariantProps<typeof itemMediaVariants>) => (
<div
data-slot='item-media'
data-variant={variant}
className={cn(itemMediaVariants({ variant, className }))}
{...props}
/>
);
}
);
function ItemContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
const ItemContent = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='item-content'
className={cn(
@@ -117,11 +110,9 @@ function ItemContent({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {
return (
const ItemTitle = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='item-title'
className={cn(
@@ -130,11 +121,12 @@ function ItemTitle({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function ItemDescription({ className, ...props }: React.ComponentProps<'p'>) {
return (
const ItemDescription = ({
className,
...props
}: React.ComponentProps<'p'>) => (
<p
data-slot='item-description'
className={cn(
@@ -143,21 +135,17 @@ function ItemDescription({ className, ...props }: React.ComponentProps<'p'>) {
)}
{...props}
/>
);
}
);
function ItemActions({ className, ...props }: React.ComponentProps<'div'>) {
return (
const ItemActions = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='item-actions'
className={cn('flex items-center gap-2', className)}
{...props}
/>
);
}
);
function ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const ItemHeader = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='item-header'
className={cn(
@@ -166,11 +154,9 @@ function ItemHeader({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
const ItemFooter = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='item-footer'
className={cn(
@@ -179,8 +165,7 @@ function ItemFooter({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
export {
Item,
+4 -8
View File
@@ -1,7 +1,6 @@
import { cn } from '@gib/ui';
function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
return (
const Kbd = ({ className, ...props }: React.ComponentProps<'kbd'>) => (
<kbd
data-slot='kbd'
className={cn(
@@ -10,17 +9,14 @@ function Kbd({ className, ...props }: React.ComponentProps<'kbd'>) {
)}
{...props}
/>
);
}
);
function KbdGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
const KbdGroup = ({ className, ...props }: React.ComponentProps<'div'>) => (
<kbd
data-slot='kbd-group'
className={cn('inline-flex items-center gap-1', className)}
{...props}
/>
);
}
);
export { Kbd, KbdGroup };
+3 -5
View File
@@ -5,11 +5,10 @@ import { Label as LabelPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Label({
const Label = ({
className,
...props
}: React.ComponentProps<typeof LabelPrimitive.Root>) {
return (
}: React.ComponentProps<typeof LabelPrimitive.Root>) => (
<LabelPrimitive.Root
data-slot='label'
className={cn(
@@ -18,6 +17,5 @@ function Label({
)}
{...props}
/>
);
}
);
export { Label };
+52 -76
View File
@@ -6,11 +6,10 @@ import { Menubar as MenubarPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Menubar({
const Menubar = ({
className,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Root>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.Root>) => (
<MenubarPrimitive.Root
data-slot='menubar'
className={cn(
@@ -19,40 +18,36 @@ function Menubar({
)}
{...props}
/>
);
}
);
function MenubarMenu({
const MenubarMenu = ({
...props
}: React.ComponentProps<typeof MenubarPrimitive.Menu>) {
return <MenubarPrimitive.Menu data-slot='menubar-menu' {...props} />;
}
}: React.ComponentProps<typeof MenubarPrimitive.Menu>) => (
<MenubarPrimitive.Menu data-slot='menubar-menu' {...props} />
);
function MenubarGroup({
const MenubarGroup = ({
...props
}: React.ComponentProps<typeof MenubarPrimitive.Group>) {
return <MenubarPrimitive.Group data-slot='menubar-group' {...props} />;
}
}: React.ComponentProps<typeof MenubarPrimitive.Group>) => (
<MenubarPrimitive.Group data-slot='menubar-group' {...props} />
);
function MenubarPortal({
const MenubarPortal = ({
...props
}: React.ComponentProps<typeof MenubarPrimitive.Portal>) {
return <MenubarPrimitive.Portal data-slot='menubar-portal' {...props} />;
}
}: React.ComponentProps<typeof MenubarPrimitive.Portal>) => (
<MenubarPrimitive.Portal data-slot='menubar-portal' {...props} />
);
function MenubarRadioGroup({
const MenubarRadioGroup = ({
...props
}: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.RadioGroup>) => (
<MenubarPrimitive.RadioGroup data-slot='menubar-radio-group' {...props} />
);
}
);
function MenubarTrigger({
const MenubarTrigger = ({
className,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.Trigger>) => (
<MenubarPrimitive.Trigger
data-slot='menubar-trigger'
className={cn(
@@ -61,17 +56,15 @@ function MenubarTrigger({
)}
{...props}
/>
);
}
);
function MenubarContent({
const MenubarContent = ({
className,
align = 'start',
alignOffset = -4,
sideOffset = 8,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Content>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.Content>) => (
<MenubarPortal>
<MenubarPrimitive.Content
data-slot='menubar-content'
@@ -85,10 +78,9 @@ function MenubarContent({
{...props}
/>
</MenubarPortal>
);
}
);
function MenubarItem({
const MenubarItem = ({
className,
inset,
variant = 'default',
@@ -96,8 +88,7 @@ function MenubarItem({
}: React.ComponentProps<typeof MenubarPrimitive.Item> & {
inset?: boolean;
variant?: 'default' | 'destructive';
}) {
return (
}) => (
<MenubarPrimitive.Item
data-slot='menubar-item'
data-inset={inset}
@@ -108,16 +99,14 @@ function MenubarItem({
)}
{...props}
/>
);
}
);
function MenubarCheckboxItem({
const MenubarCheckboxItem = ({
className,
children,
checked,
...props
}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.CheckboxItem>) => (
<MenubarPrimitive.CheckboxItem
data-slot='menubar-checkbox-item'
className={cn(
@@ -134,15 +123,13 @@ function MenubarCheckboxItem({
</span>
{children}
</MenubarPrimitive.CheckboxItem>
);
}
);
function MenubarRadioItem({
const MenubarRadioItem = ({
className,
children,
...props
}: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.RadioItem>) => (
<MenubarPrimitive.RadioItem
data-slot='menubar-radio-item'
className={cn(
@@ -158,17 +145,15 @@ function MenubarRadioItem({
</span>
{children}
</MenubarPrimitive.RadioItem>
);
}
);
function MenubarLabel({
const MenubarLabel = ({
className,
inset,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Label> & {
inset?: boolean;
}) {
return (
}) => (
<MenubarPrimitive.Label
data-slot='menubar-label'
data-inset={inset}
@@ -178,27 +163,23 @@ function MenubarLabel({
)}
{...props}
/>
);
}
);
function MenubarSeparator({
const MenubarSeparator = ({
className,
...props
}: React.ComponentProps<typeof MenubarPrimitive.Separator>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.Separator>) => (
<MenubarPrimitive.Separator
data-slot='menubar-separator'
className={cn('bg-border -mx-1 my-1 h-px', className)}
{...props}
/>
);
}
);
function MenubarShortcut({
const MenubarShortcut = ({
className,
...props
}: React.ComponentProps<'span'>) {
return (
}: React.ComponentProps<'span'>) => (
<span
data-slot='menubar-shortcut'
className={cn(
@@ -207,24 +188,22 @@ function MenubarShortcut({
)}
{...props}
/>
);
}
);
function MenubarSub({
const MenubarSub = ({
...props
}: React.ComponentProps<typeof MenubarPrimitive.Sub>) {
return <MenubarPrimitive.Sub data-slot='menubar-sub' {...props} />;
}
}: React.ComponentProps<typeof MenubarPrimitive.Sub>) => (
<MenubarPrimitive.Sub data-slot='menubar-sub' {...props} />
);
function MenubarSubTrigger({
const MenubarSubTrigger = ({
className,
inset,
children,
...props
}: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
inset?: boolean;
}) {
return (
}) => (
<MenubarPrimitive.SubTrigger
data-slot='menubar-sub-trigger'
data-inset={inset}
@@ -237,14 +216,12 @@ function MenubarSubTrigger({
{children}
<ChevronRightIcon className='ml-auto h-4 w-4' />
</MenubarPrimitive.SubTrigger>
);
}
);
function MenubarSubContent({
const MenubarSubContent = ({
className,
...props
}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) {
return (
}: React.ComponentProps<typeof MenubarPrimitive.SubContent>) => (
<MenubarPrimitive.SubContent
data-slot='menubar-sub-content'
className={cn(
@@ -253,8 +230,7 @@ function MenubarSubContent({
)}
{...props}
/>
);
}
);
export {
Menubar,
+11 -13
View File
@@ -3,12 +3,13 @@ import { ChevronDownIcon } from 'lucide-react';
import { cn } from '@gib/ui';
function NativeSelect({
const NativeSelect = ({
className,
size = 'default',
...props
}: Omit<React.ComponentProps<'select'>, 'size'> & { size?: 'sm' | 'default' }) {
return (
}: Omit<React.ComponentProps<'select'>, 'size'> & {
size?: 'sm' | 'default';
}) => (
<div
className='group/native-select relative w-fit has-[select:disabled]:opacity-50'
data-slot='native-select-wrapper'
@@ -30,24 +31,21 @@ function NativeSelect({
data-slot='native-select-icon'
/>
</div>
);
}
);
function NativeSelectOption({ ...props }: React.ComponentProps<'option'>) {
return <option data-slot='native-select-option' {...props} />;
}
const NativeSelectOption = ({ ...props }: React.ComponentProps<'option'>) => (
<option data-slot='native-select-option' {...props} />
);
function NativeSelectOptGroup({
const NativeSelectOptGroup = ({
className,
...props
}: React.ComponentProps<'optgroup'>) {
return (
}: React.ComponentProps<'optgroup'>) => (
<optgroup
data-slot='native-select-optgroup'
className={cn(className)}
{...props}
/>
);
}
);
export { NativeSelect, NativeSelectOptGroup, NativeSelectOption };
+25 -43
View File
@@ -5,15 +5,14 @@ import { NavigationMenu as NavigationMenuPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function NavigationMenu({
const NavigationMenu = ({
className,
children,
viewport = true,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Root> & {
viewport?: boolean;
}) {
return (
}) => (
<NavigationMenuPrimitive.Root
data-slot='navigation-menu'
data-viewport={viewport}
@@ -26,14 +25,12 @@ function NavigationMenu({
{children}
{viewport && <NavigationMenuViewport />}
</NavigationMenuPrimitive.Root>
);
}
);
function NavigationMenuList({
const NavigationMenuList = ({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.List>) => (
<NavigationMenuPrimitive.List
data-slot='navigation-menu-list'
className={cn(
@@ -42,32 +39,28 @@ function NavigationMenuList({
)}
{...props}
/>
);
}
);
function NavigationMenuItem({
const NavigationMenuItem = ({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.Item>) => (
<NavigationMenuPrimitive.Item
data-slot='navigation-menu-item'
className={cn('relative', className)}
{...props}
/>
);
}
);
const navigationMenuTriggerStyle = cva(
'group bg-background hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 data-[state=open]:bg-accent/50 data-[state=open]:text-accent-foreground data-[state=open]:hover:bg-accent data-[state=open]:focus:bg-accent inline-flex h-9 w-max items-center justify-center rounded-md px-4 py-2 text-sm font-medium transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50',
);
function NavigationMenuTrigger({
const NavigationMenuTrigger = ({
className,
children,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.Trigger>) => (
<NavigationMenuPrimitive.Trigger
data-slot='navigation-menu-trigger'
className={cn(navigationMenuTriggerStyle(), 'group', className)}
@@ -79,14 +72,12 @@ function NavigationMenuTrigger({
aria-hidden='true'
/>
</NavigationMenuPrimitive.Trigger>
);
}
);
function NavigationMenuContent({
const NavigationMenuContent = ({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.Content>) => (
<NavigationMenuPrimitive.Content
data-slot='navigation-menu-content'
className={cn(
@@ -96,18 +87,14 @@ function NavigationMenuContent({
)}
{...props}
/>
);
}
);
function NavigationMenuViewport({
const NavigationMenuViewport = ({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.Viewport>) => (
<div
className={cn(
'absolute top-full left-0 isolate z-50 flex justify-center',
)}
className={cn('absolute top-full left-0 isolate z-50 flex justify-center')}
>
<NavigationMenuPrimitive.Viewport
data-slot='navigation-menu-viewport'
@@ -118,14 +105,12 @@ function NavigationMenuViewport({
{...props}
/>
</div>
);
}
);
function NavigationMenuLink({
const NavigationMenuLink = ({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.Link>) => (
<NavigationMenuPrimitive.Link
data-slot='navigation-menu-link'
className={cn(
@@ -134,14 +119,12 @@ function NavigationMenuLink({
)}
{...props}
/>
);
}
);
function NavigationMenuIndicator({
const NavigationMenuIndicator = ({
className,
...props
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
return (
}: React.ComponentProps<typeof NavigationMenuPrimitive.Indicator>) => (
<NavigationMenuPrimitive.Indicator
data-slot='navigation-menu-indicator'
className={cn(
@@ -152,8 +135,7 @@ function NavigationMenuIndicator({
>
<div className='bg-border relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm shadow-md' />
</NavigationMenuPrimitive.Indicator>
);
}
);
export {
NavigationMenu,
+20 -32
View File
@@ -7,8 +7,7 @@ import {
import { Button, cn } from '@gib/ui';
function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
return (
const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (
<nav
role='navigation'
aria-label='pagination'
@@ -16,38 +15,34 @@ function Pagination({ className, ...props }: React.ComponentProps<'nav'>) {
className={cn('mx-auto flex w-full justify-center', className)}
{...props}
/>
);
}
);
function PaginationContent({
const PaginationContent = ({
className,
...props
}: React.ComponentProps<'ul'>) {
return (
}: React.ComponentProps<'ul'>) => (
<ul
data-slot='pagination-content'
className={cn('flex items-center gap-0.5', className)}
{...props}
/>
);
}
);
function PaginationItem({ ...props }: React.ComponentProps<'li'>) {
return <li data-slot='pagination-item' {...props} />;
}
const PaginationItem = ({ ...props }: React.ComponentProps<'li'>) => (
<li data-slot='pagination-item' {...props} />
);
type PaginationLinkProps = {
isActive?: boolean;
} & Pick<React.ComponentProps<typeof Button>, 'size'> &
React.ComponentProps<'a'>;
function PaginationLink({
const PaginationLink = ({
className,
isActive,
size = 'icon',
...props
}: PaginationLinkProps) {
return (
}: PaginationLinkProps) => (
<Button
asChild
variant={isActive ? 'outline' : 'ghost'}
@@ -61,15 +56,13 @@ function PaginationLink({
{...props}
/>
</Button>
);
}
);
function PaginationPrevious({
const PaginationPrevious = ({
className,
text = 'Previous',
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) => (
<PaginationLink
aria-label='Go to previous page'
size='default'
@@ -79,15 +72,13 @@ function PaginationPrevious({
<ChevronLeftIcon data-icon='inline-start' className='cn-rtl-flip' />
<span className='hidden sm:block'>{text}</span>
</PaginationLink>
);
}
);
function PaginationNext({
const PaginationNext = ({
className,
text = 'Next',
...props
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
return (
}: React.ComponentProps<typeof PaginationLink> & { text?: string }) => (
<PaginationLink
aria-label='Go to next page'
size='default'
@@ -97,14 +88,12 @@ function PaginationNext({
<span className='hidden sm:block'>{text}</span>
<ChevronRightIcon data-icon='inline-end' className='cn-rtl-flip' />
</PaginationLink>
);
}
);
function PaginationEllipsis({
const PaginationEllipsis = ({
className,
...props
}: React.ComponentProps<'span'>) {
return (
}: React.ComponentProps<'span'>) => (
<span
aria-hidden
data-slot='pagination-ellipsis'
@@ -117,8 +106,7 @@ function PaginationEllipsis({
<MoreHorizontalIcon />
<span className='sr-only'>More pages</span>
</span>
);
}
);
export {
Pagination,
+25 -30
View File
@@ -5,25 +5,24 @@ import { Popover as PopoverPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Popover({
const Popover = ({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Root>) {
return <PopoverPrimitive.Root data-slot='popover' {...props} />;
}
}: React.ComponentProps<typeof PopoverPrimitive.Root>) => (
<PopoverPrimitive.Root data-slot='popover' {...props} />
);
function PopoverTrigger({
const PopoverTrigger = ({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
return <PopoverPrimitive.Trigger data-slot='popover-trigger' {...props} />;
}
}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) => (
<PopoverPrimitive.Trigger data-slot='popover-trigger' {...props} />
);
function PopoverContent({
const PopoverContent = ({
className,
align = 'center',
sideOffset = 4,
...props
}: React.ComponentProps<typeof PopoverPrimitive.Content>) {
return (
}: React.ComponentProps<typeof PopoverPrimitive.Content>) => (
<PopoverPrimitive.Portal>
<PopoverPrimitive.Content
data-slot='popover-content'
@@ -36,47 +35,43 @@ function PopoverContent({
{...props}
/>
</PopoverPrimitive.Portal>
);
}
);
function PopoverAnchor({
const PopoverAnchor = ({
...props
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
return <PopoverPrimitive.Anchor data-slot='popover-anchor' {...props} />;
}
}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) => (
<PopoverPrimitive.Anchor data-slot='popover-anchor' {...props} />
);
function PopoverHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const PopoverHeader = ({
className,
...props
}: React.ComponentProps<'div'>) => (
<div
data-slot='popover-header'
className={cn('flex flex-col gap-1 text-sm', className)}
{...props}
/>
);
}
);
function PopoverTitle({ className, ...props }: React.ComponentProps<'h2'>) {
return (
const PopoverTitle = ({ className, ...props }: React.ComponentProps<'h2'>) => (
<div
data-slot='popover-title'
className={cn('font-medium', className)}
{...props}
/>
);
}
);
function PopoverDescription({
const PopoverDescription = ({
className,
...props
}: React.ComponentProps<'p'>) {
return (
}: React.ComponentProps<'p'>) => (
<p
data-slot='popover-description'
className={cn('text-muted-foreground', className)}
{...props}
/>
);
}
);
export {
Popover,
+3 -5
View File
@@ -5,12 +5,11 @@ import { Progress as ProgressPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Progress({
const Progress = ({
className,
value,
...props
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
return (
}: React.ComponentProps<typeof ProgressPrimitive.Root>) => (
<ProgressPrimitive.Root
data-slot='progress'
className={cn(
@@ -25,7 +24,6 @@ function Progress({
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
/>
</ProgressPrimitive.Root>
);
}
);
export { Progress };
+6 -10
View File
@@ -6,24 +6,21 @@ import { RadioGroup as RadioGroupPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function RadioGroup({
const RadioGroup = ({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) {
return (
}: React.ComponentProps<typeof RadioGroupPrimitive.Root>) => (
<RadioGroupPrimitive.Root
data-slot='radio-group'
className={cn('grid gap-3', className)}
{...props}
/>
);
}
);
function RadioGroupItem({
const RadioGroupItem = ({
className,
...props
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) {
return (
}: React.ComponentProps<typeof RadioGroupPrimitive.Item>) => (
<RadioGroupPrimitive.Item
data-slot='radio-group-item'
className={cn(
@@ -39,7 +36,6 @@ function RadioGroupItem({
<CircleIcon className='fill-primary absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2' />
</RadioGroupPrimitive.Indicator>
</RadioGroupPrimitive.Item>
);
}
);
export { RadioGroup, RadioGroupItem };
+9 -13
View File
@@ -5,11 +5,10 @@ import * as ResizablePrimitive from 'react-resizable-panels';
import { cn } from '@gib/ui';
function ResizablePanelGroup({
const ResizablePanelGroup = ({
className,
...props
}: ResizablePrimitive.GroupProps) {
return (
}: ResizablePrimitive.GroupProps) => (
<ResizablePrimitive.Group
data-slot='resizable-panel-group'
className={cn(
@@ -18,21 +17,19 @@ function ResizablePanelGroup({
)}
{...props}
/>
);
}
);
function ResizablePanel({ ...props }: ResizablePrimitive.PanelProps) {
return <ResizablePrimitive.Panel data-slot='resizable-panel' {...props} />;
}
const ResizablePanel = ({ ...props }: ResizablePrimitive.PanelProps) => (
<ResizablePrimitive.Panel data-slot='resizable-panel' {...props} />
);
function ResizableHandle({
const ResizableHandle = ({
withHandle,
className,
...props
}: ResizablePrimitive.SeparatorProps & {
withHandle?: boolean;
}) {
return (
}) => (
<ResizablePrimitive.Separator
data-slot='resizable-handle'
className={cn(
@@ -47,7 +44,6 @@ function ResizableHandle({
</div>
)}
</ResizablePrimitive.Separator>
);
}
);
export { ResizableHandle, ResizablePanel, ResizablePanelGroup };
+6 -10
View File
@@ -5,12 +5,11 @@ import { ScrollArea as ScrollAreaPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function ScrollArea({
const ScrollArea = ({
className,
children,
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) {
return (
}: React.ComponentProps<typeof ScrollAreaPrimitive.Root>) => (
<ScrollAreaPrimitive.Root
data-slot='scroll-area'
className={cn('relative', className)}
@@ -25,15 +24,13 @@ function ScrollArea({
<ScrollBar />
<ScrollAreaPrimitive.Corner />
</ScrollAreaPrimitive.Root>
);
}
);
function ScrollBar({
const ScrollBar = ({
className,
orientation = 'vertical',
...props
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) {
return (
}: React.ComponentProps<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>) => (
<ScrollAreaPrimitive.ScrollAreaScrollbar
data-slot='scroll-area-scrollbar'
data-orientation={orientation}
@@ -49,7 +46,6 @@ function ScrollBar({
className='bg-border relative flex-1 rounded-full'
/>
</ScrollAreaPrimitive.ScrollAreaScrollbar>
);
}
);
export { ScrollArea, ScrollBar };
+33 -47
View File
@@ -6,33 +6,32 @@ import { Select as SelectPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Select({
const Select = ({
...props
}: React.ComponentProps<typeof SelectPrimitive.Root>) {
return <SelectPrimitive.Root data-slot='select' {...props} />;
}
}: React.ComponentProps<typeof SelectPrimitive.Root>) => (
<SelectPrimitive.Root data-slot='select' {...props} />
);
function SelectGroup({
const SelectGroup = ({
...props
}: React.ComponentProps<typeof SelectPrimitive.Group>) {
return <SelectPrimitive.Group data-slot='select-group' {...props} />;
}
}: React.ComponentProps<typeof SelectPrimitive.Group>) => (
<SelectPrimitive.Group data-slot='select-group' {...props} />
);
function SelectValue({
const SelectValue = ({
...props
}: React.ComponentProps<typeof SelectPrimitive.Value>) {
return <SelectPrimitive.Value data-slot='select-value' {...props} />;
}
}: React.ComponentProps<typeof SelectPrimitive.Value>) => (
<SelectPrimitive.Value data-slot='select-value' {...props} />
);
function SelectTrigger({
const SelectTrigger = ({
className,
size = 'default',
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {
size?: 'sm' | 'default';
}) {
return (
}) => (
<SelectPrimitive.Trigger
data-slot='select-trigger'
data-size={size}
@@ -47,17 +46,15 @@ function SelectTrigger({
<ChevronDownIcon className='size-4 opacity-50' />
</SelectPrimitive.Icon>
</SelectPrimitive.Trigger>
);
}
);
function SelectContent({
const SelectContent = ({
className,
children,
position = 'item-aligned',
align = 'center',
...props
}: React.ComponentProps<typeof SelectPrimitive.Content>) {
return (
}: React.ComponentProps<typeof SelectPrimitive.Content>) => (
<SelectPrimitive.Portal>
<SelectPrimitive.Content
data-slot='select-content'
@@ -84,28 +81,24 @@ function SelectContent({
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPrimitive.Portal>
);
}
);
function SelectLabel({
const SelectLabel = ({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Label>) {
return (
}: React.ComponentProps<typeof SelectPrimitive.Label>) => (
<SelectPrimitive.Label
data-slot='select-label'
className={cn('text-muted-foreground px-2 py-1.5 text-xs', className)}
{...props}
/>
);
}
);
function SelectItem({
const SelectItem = ({
className,
children,
...props
}: React.ComponentProps<typeof SelectPrimitive.Item>) {
return (
}: React.ComponentProps<typeof SelectPrimitive.Item>) => (
<SelectPrimitive.Item
data-slot='select-item'
className={cn(
@@ -124,27 +117,23 @@ function SelectItem({
</span>
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
</SelectPrimitive.Item>
);
}
);
function SelectSeparator({
const SelectSeparator = ({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.Separator>) {
return (
}: React.ComponentProps<typeof SelectPrimitive.Separator>) => (
<SelectPrimitive.Separator
data-slot='select-separator'
className={cn('bg-border pointer-events-none -mx-1 my-1 h-px', className)}
{...props}
/>
);
}
);
function SelectScrollUpButton({
const SelectScrollUpButton = ({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {
return (
}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) => (
<SelectPrimitive.ScrollUpButton
data-slot='select-scroll-up-button'
className={cn(
@@ -155,14 +144,12 @@ function SelectScrollUpButton({
>
<ChevronUpIcon className='size-4' />
</SelectPrimitive.ScrollUpButton>
);
}
);
function SelectScrollDownButton({
const SelectScrollDownButton = ({
className,
...props
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {
return (
}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) => (
<SelectPrimitive.ScrollDownButton
data-slot='select-scroll-down-button'
className={cn(
@@ -173,8 +160,7 @@ function SelectScrollDownButton({
>
<ChevronDownIcon className='size-4' />
</SelectPrimitive.ScrollDownButton>
);
}
);
export {
Select,
+3 -5
View File
@@ -5,13 +5,12 @@ import * as SeparatorPrimitive from '@radix-ui/react-separator';
import { cn } from '@gib/ui';
function Separator({
const Separator = ({
className,
orientation = 'horizontal',
decorative = true,
...props
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
return (
}: React.ComponentProps<typeof SeparatorPrimitive.Root>) => (
<SeparatorPrimitive.Root
data-slot='separator'
decorative={decorative}
@@ -22,7 +21,6 @@ function Separator({
)}
{...props}
/>
);
}
);
export { Separator };
+34 -44
View File
@@ -6,33 +6,34 @@ import { Dialog as SheetPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Sheet({ ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {
return <SheetPrimitive.Root data-slot='sheet' {...props} />;
}
function SheetTrigger({
const Sheet = ({
...props
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {
return <SheetPrimitive.Trigger data-slot='sheet-trigger' {...props} />;
}
}: React.ComponentProps<typeof SheetPrimitive.Root>) => (
<SheetPrimitive.Root data-slot='sheet' {...props} />
);
function SheetClose({
const SheetTrigger = ({
...props
}: React.ComponentProps<typeof SheetPrimitive.Close>) {
return <SheetPrimitive.Close data-slot='sheet-close' {...props} />;
}
}: React.ComponentProps<typeof SheetPrimitive.Trigger>) => (
<SheetPrimitive.Trigger data-slot='sheet-trigger' {...props} />
);
function SheetPortal({
const SheetClose = ({
...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) {
return <SheetPrimitive.Portal data-slot='sheet-portal' {...props} />;
}
}: React.ComponentProps<typeof SheetPrimitive.Close>) => (
<SheetPrimitive.Close data-slot='sheet-close' {...props} />
);
function SheetOverlay({
const SheetPortal = ({
...props
}: React.ComponentProps<typeof SheetPrimitive.Portal>) => (
<SheetPrimitive.Portal data-slot='sheet-portal' {...props} />
);
const SheetOverlay = ({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) {
return (
}: React.ComponentProps<typeof SheetPrimitive.Overlay>) => (
<SheetPrimitive.Overlay
data-slot='sheet-overlay'
className={cn(
@@ -41,10 +42,9 @@ function SheetOverlay({
)}
{...props}
/>
);
}
);
function SheetContent({
const SheetContent = ({
className,
children,
side = 'right',
@@ -53,8 +53,7 @@ function SheetContent({
}: React.ComponentProps<typeof SheetPrimitive.Content> & {
side?: 'top' | 'right' | 'bottom' | 'left';
showCloseButton?: boolean;
}) {
return (
}) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
@@ -82,54 +81,45 @@ function SheetContent({
)}
</SheetPrimitive.Content>
</SheetPortal>
);
}
);
function SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const SheetHeader = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='sheet-header'
className={cn('flex flex-col gap-1.5 p-4', className)}
{...props}
/>
);
}
);
function SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
const SheetFooter = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='sheet-footer'
className={cn('mt-auto flex flex-col gap-2 p-4', className)}
{...props}
/>
);
}
);
function SheetTitle({
const SheetTitle = ({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Title>) {
return (
}: React.ComponentProps<typeof SheetPrimitive.Title>) => (
<SheetPrimitive.Title
data-slot='sheet-title'
className={cn('text-foreground font-semibold', className)}
{...props}
/>
);
}
);
function SheetDescription({
const SheetDescription = ({
className,
...props
}: React.ComponentProps<typeof SheetPrimitive.Description>) {
return (
}: React.ComponentProps<typeof SheetPrimitive.Description>) => (
<SheetPrimitive.Description
data-slot='sheet-description'
className={cn('text-muted-foreground text-sm', className)}
{...props}
/>
);
}
);
export {
Sheet,
+85 -90
View File
@@ -31,7 +31,7 @@ const SIDEBAR_WIDTH_MOBILE = '18rem';
const SIDEBAR_WIDTH_ICON = '3rem';
const SIDEBAR_KEYBOARD_SHORTCUT = 'b';
type SidebarContextProps = {
interface SidebarContextProps {
state: 'expanded' | 'collapsed';
open: boolean;
setOpen: (open: boolean) => void;
@@ -39,20 +39,20 @@ type SidebarContextProps = {
setOpenMobile: (open: boolean) => void;
isMobile: boolean;
toggleSidebar: () => void;
};
}
const SidebarContext = React.createContext<SidebarContextProps | null>(null);
function useSidebar() {
const useSidebar = () => {
const context = React.useContext(SidebarContext);
if (!context) {
throw new Error('useSidebar must be used within a SidebarProvider.');
}
return context;
}
};
function SidebarProvider({
const SidebarProvider = ({
defaultOpen = true,
open: openProp,
onOpenChange: setOpenProp,
@@ -64,7 +64,7 @@ function SidebarProvider({
defaultOpen?: boolean;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}) {
}) => {
const isMobile = useIsMobile();
const [openMobile, setOpenMobile] = React.useState(false);
@@ -148,9 +148,9 @@ function SidebarProvider({
</TooltipProvider>
</SidebarContext.Provider>
);
}
};
function Sidebar({
const Sidebar = ({
side = 'left',
variant = 'sidebar',
collapsible = 'offcanvas',
@@ -161,7 +161,7 @@ function Sidebar({
side?: 'left' | 'right';
variant?: 'sidebar' | 'floating' | 'inset';
collapsible?: 'offcanvas' | 'icon' | 'none';
}) {
}) => {
const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
if (collapsible === 'none') {
@@ -250,13 +250,13 @@ function Sidebar({
</div>
</div>
);
}
};
function SidebarTrigger({
const SidebarTrigger = ({
className,
onClick,
...props
}: React.ComponentProps<typeof Button>) {
}: React.ComponentProps<typeof Button>) => {
const { toggleSidebar } = useSidebar();
return (
@@ -276,9 +276,12 @@ function SidebarTrigger({
<span className='sr-only'>Toggle Sidebar</span>
</Button>
);
}
};
function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {
const SidebarRail = ({
className,
...props
}: React.ComponentProps<'button'>) => {
const { toggleSidebar } = useSidebar();
return (
@@ -301,10 +304,12 @@ function SidebarRail({ className, ...props }: React.ComponentProps<'button'>) {
{...props}
/>
);
}
};
function SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {
return (
const SidebarInset = ({
className,
...props
}: React.ComponentProps<'main'>) => (
<main
data-slot='sidebar-inset'
className={cn(
@@ -314,61 +319,60 @@ function SidebarInset({ className, ...props }: React.ComponentProps<'main'>) {
)}
{...props}
/>
);
}
);
function SidebarInput({
const SidebarInput = ({
className,
...props
}: React.ComponentProps<typeof Input>) {
return (
}: React.ComponentProps<typeof Input>) => (
<Input
data-slot='sidebar-input'
data-sidebar='input'
className={cn('bg-background h-8 w-full shadow-none', className)}
{...props}
/>
);
}
);
function SidebarHeader({ className, ...props }: React.ComponentProps<'div'>) {
return (
const SidebarHeader = ({
className,
...props
}: React.ComponentProps<'div'>) => (
<div
data-slot='sidebar-header'
data-sidebar='header'
className={cn('flex flex-col gap-2 p-2', className)}
{...props}
/>
);
}
);
function SidebarFooter({ className, ...props }: React.ComponentProps<'div'>) {
return (
const SidebarFooter = ({
className,
...props
}: React.ComponentProps<'div'>) => (
<div
data-slot='sidebar-footer'
data-sidebar='footer'
className={cn('flex flex-col gap-2 p-2', className)}
{...props}
/>
);
}
);
function SidebarSeparator({
const SidebarSeparator = ({
className,
...props
}: React.ComponentProps<typeof Separator>) {
return (
}: React.ComponentProps<typeof Separator>) => (
<Separator
data-slot='sidebar-separator'
data-sidebar='separator'
className={cn('bg-sidebar-border mx-2 w-auto', className)}
{...props}
/>
);
}
);
function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {
return (
const SidebarContent = ({
className,
...props
}: React.ComponentProps<'div'>) => (
<div
data-slot='sidebar-content'
data-sidebar='content'
@@ -378,25 +382,22 @@ function SidebarContent({ className, ...props }: React.ComponentProps<'div'>) {
)}
{...props}
/>
);
}
);
function SidebarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
const SidebarGroup = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='sidebar-group'
data-sidebar='group'
className={cn('relative flex w-full min-w-0 flex-col p-2', className)}
{...props}
/>
);
}
);
function SidebarGroupLabel({
const SidebarGroupLabel = ({
className,
asChild = false,
...props
}: React.ComponentProps<'div'> & { asChild?: boolean }) {
}: React.ComponentProps<'div'> & { asChild?: boolean }) => {
const Comp = asChild ? Slot.Root : 'div';
return (
@@ -411,13 +412,13 @@ function SidebarGroupLabel({
{...props}
/>
);
}
};
function SidebarGroupAction({
const SidebarGroupAction = ({
className,
asChild = false,
...props
}: React.ComponentProps<'button'> & { asChild?: boolean }) {
}: React.ComponentProps<'button'> & { asChild?: boolean }) => {
const Comp = asChild ? Slot.Root : 'button';
return (
@@ -434,43 +435,40 @@ function SidebarGroupAction({
{...props}
/>
);
}
};
function SidebarGroupContent({
const SidebarGroupContent = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='sidebar-group-content'
data-sidebar='group-content'
className={cn('w-full text-sm', className)}
{...props}
/>
);
}
);
function SidebarMenu({ className, ...props }: React.ComponentProps<'ul'>) {
return (
const SidebarMenu = ({ className, ...props }: React.ComponentProps<'ul'>) => (
<ul
data-slot='sidebar-menu'
data-sidebar='menu'
className={cn('flex w-full min-w-0 flex-col gap-1', className)}
{...props}
/>
);
}
);
function SidebarMenuItem({ className, ...props }: React.ComponentProps<'li'>) {
return (
const SidebarMenuItem = ({
className,
...props
}: React.ComponentProps<'li'>) => (
<li
data-slot='sidebar-menu-item'
data-sidebar='menu-item'
className={cn('group/menu-item relative', className)}
{...props}
/>
);
}
);
const sidebarMenuButtonVariants = cva(
'peer/menu-button ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:font-medium [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',
@@ -494,7 +492,7 @@ const sidebarMenuButtonVariants = cva(
},
);
function SidebarMenuButton({
const SidebarMenuButton = ({
asChild = false,
isActive = false,
variant = 'default',
@@ -506,7 +504,7 @@ function SidebarMenuButton({
asChild?: boolean;
isActive?: boolean;
tooltip?: string | React.ComponentProps<typeof TooltipContent>;
} & VariantProps<typeof sidebarMenuButtonVariants>) {
} & VariantProps<typeof sidebarMenuButtonVariants>) => {
const Comp = asChild ? Slot.Root : 'button';
const { isMobile, state } = useSidebar();
@@ -542,9 +540,9 @@ function SidebarMenuButton({
/>
</Tooltip>
);
}
};
function SidebarMenuAction({
const SidebarMenuAction = ({
className,
asChild = false,
showOnHover = false,
@@ -552,7 +550,7 @@ function SidebarMenuAction({
}: React.ComponentProps<'button'> & {
asChild?: boolean;
showOnHover?: boolean;
}) {
}) => {
const Comp = asChild ? Slot.Root : 'button';
return (
@@ -574,13 +572,12 @@ function SidebarMenuAction({
{...props}
/>
);
}
};
function SidebarMenuBadge({
const SidebarMenuBadge = ({
className,
...props
}: React.ComponentProps<'div'>) {
return (
}: React.ComponentProps<'div'>) => (
<div
data-slot='sidebar-menu-badge'
data-sidebar='menu-badge'
@@ -595,16 +592,15 @@ function SidebarMenuBadge({
)}
{...props}
/>
);
}
);
function SidebarMenuSkeleton({
const SidebarMenuSkeleton = ({
className,
showIcon = false,
...props
}: React.ComponentProps<'div'> & {
showIcon?: boolean;
}) {
}) => {
// Random width between 50 to 90%.
const width = React.useMemo(() => {
return `${Math.floor(Math.random() * 40) + 50}%`;
@@ -634,10 +630,12 @@ function SidebarMenuSkeleton({
/>
</div>
);
}
};
function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
return (
const SidebarMenuSub = ({
className,
...props
}: React.ComponentProps<'ul'>) => (
<ul
data-slot='sidebar-menu-sub'
data-sidebar='menu-sub'
@@ -648,24 +646,21 @@ function SidebarMenuSub({ className, ...props }: React.ComponentProps<'ul'>) {
)}
{...props}
/>
);
}
);
function SidebarMenuSubItem({
const SidebarMenuSubItem = ({
className,
...props
}: React.ComponentProps<'li'>) {
return (
}: React.ComponentProps<'li'>) => (
<li
data-slot='sidebar-menu-sub-item'
data-sidebar='menu-sub-item'
className={cn('group/menu-sub-item relative', className)}
{...props}
/>
);
}
);
function SidebarMenuSubButton({
const SidebarMenuSubButton = ({
asChild = false,
size = 'md',
isActive = false,
@@ -675,7 +670,7 @@ function SidebarMenuSubButton({
asChild?: boolean;
size?: 'sm' | 'md';
isActive?: boolean;
}) {
}) => {
const Comp = asChild ? Slot.Root : 'a';
return (
@@ -695,7 +690,7 @@ function SidebarMenuSubButton({
{...props}
/>
);
}
};
export {
Sidebar,
+2 -4
View File
@@ -1,13 +1,11 @@
import { cn } from '@gib/ui';
function Skeleton({ className, ...props }: React.ComponentProps<'div'>) {
return (
const Skeleton = ({ className, ...props }: React.ComponentProps<'div'>) => (
<div
data-slot='skeleton'
className={cn('bg-accent animate-pulse rounded-md', className)}
{...props}
/>
);
}
);
export { Skeleton };
+3 -3
View File
@@ -5,14 +5,14 @@ import { Slider as SliderPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Slider({
const Slider = ({
className,
defaultValue,
value,
min = 0,
max = 100,
...props
}: React.ComponentProps<typeof SliderPrimitive.Root>) {
}: React.ComponentProps<typeof SliderPrimitive.Root>) => {
const _values = React.useMemo(
() =>
Array.isArray(value)
@@ -58,6 +58,6 @@ function Slider({
))}
</SliderPrimitive.Root>
);
}
};
export { Slider };
+2 -4
View File
@@ -2,15 +2,13 @@ import { Loader2Icon } from 'lucide-react';
import { cn } from '@gib/ui';
function Spinner({ className, ...props }: React.ComponentProps<'svg'>) {
return (
const Spinner = ({ className, ...props }: React.ComponentProps<'svg'>) => (
<Loader2Icon
role='status'
aria-label='Loading'
className={cn('size-4 animate-spin', className)}
{...props}
/>
);
}
);
export { Spinner };
+3 -5
View File
@@ -5,14 +5,13 @@ import { Switch as SwitchPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Switch({
const Switch = ({
className,
size = 'default',
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root> & {
size?: 'sm' | 'default';
}) {
return (
}) => (
<SwitchPrimitive.Root
data-slot='switch'
data-size={size}
@@ -27,7 +26,6 @@ function Switch({
className='bg-background dark:data-unchecked:bg-foreground dark:data-checked:bg-primary-foreground pointer-events-none block rounded-full ring-0 transition-transform group-data-[size=default]/switch:size-4 group-data-[size=sm]/switch:size-3 group-data-[size=default]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=sm]/switch:data-checked:translate-x-[calc(100%-2px)] group-data-[size=default]/switch:data-unchecked:translate-x-0 group-data-[size=sm]/switch:data-unchecked:translate-x-0'
/>
</SwitchPrimitive.Root>
);
}
);
export { Switch };
+24 -37
View File
@@ -4,43 +4,39 @@ import type * as React from 'react';
import { cn } from '@gib/ui';
function Table({ className, ...props }: React.ComponentProps<'table'>) {
return (
<div
data-slot='table-container'
className='relative w-full overflow-x-auto'
>
const Table = ({ className, ...props }: React.ComponentProps<'table'>) => (
<div data-slot='table-container' className='relative w-full overflow-x-auto'>
<table
data-slot='table'
className={cn('w-full caption-bottom text-sm', className)}
{...props}
/>
</div>
);
}
);
function TableHeader({ className, ...props }: React.ComponentProps<'thead'>) {
return (
const TableHeader = ({
className,
...props
}: React.ComponentProps<'thead'>) => (
<thead
data-slot='table-header'
className={cn('[&_tr]:border-b', className)}
{...props}
/>
);
}
);
function TableBody({ className, ...props }: React.ComponentProps<'tbody'>) {
return (
const TableBody = ({ className, ...props }: React.ComponentProps<'tbody'>) => (
<tbody
data-slot='table-body'
className={cn('[&_tr:last-child]:border-0', className)}
{...props}
/>
);
}
);
function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
return (
const TableFooter = ({
className,
...props
}: React.ComponentProps<'tfoot'>) => (
<tfoot
data-slot='table-footer'
className={cn(
@@ -49,11 +45,9 @@ function TableFooter({ className, ...props }: React.ComponentProps<'tfoot'>) {
)}
{...props}
/>
);
}
);
function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
return (
const TableRow = ({ className, ...props }: React.ComponentProps<'tr'>) => (
<tr
data-slot='table-row'
className={cn(
@@ -62,11 +56,9 @@ function TableRow({ className, ...props }: React.ComponentProps<'tr'>) {
)}
{...props}
/>
);
}
);
function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
return (
const TableHead = ({ className, ...props }: React.ComponentProps<'th'>) => (
<th
data-slot='table-head'
className={cn(
@@ -75,11 +67,9 @@ function TableHead({ className, ...props }: React.ComponentProps<'th'>) {
)}
{...props}
/>
);
}
);
function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
return (
const TableCell = ({ className, ...props }: React.ComponentProps<'td'>) => (
<td
data-slot='table-cell'
className={cn(
@@ -88,21 +78,18 @@ function TableCell({ className, ...props }: React.ComponentProps<'td'>) {
)}
{...props}
/>
);
}
);
function TableCaption({
const TableCaption = ({
className,
...props
}: React.ComponentProps<'caption'>) {
return (
}: React.ComponentProps<'caption'>) => (
<caption
data-slot='table-caption'
className={cn('text-muted-foreground mt-4 text-sm', className)}
{...props}
/>
);
}
);
export {
Table,
+13 -24
View File
@@ -7,23 +7,18 @@ import { Tabs as TabsPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function Tabs({
const Tabs = ({
className,
orientation = 'horizontal',
...props
}: React.ComponentProps<typeof TabsPrimitive.Root>) {
return (
}: React.ComponentProps<typeof TabsPrimitive.Root>) => (
<TabsPrimitive.Root
data-slot='tabs'
data-orientation={orientation}
className={cn(
'group/tabs flex gap-2 data-horizontal:flex-col',
className,
)}
className={cn('group/tabs flex gap-2 data-horizontal:flex-col', className)}
{...props}
/>
);
}
);
const tabsListVariants = cva(
'group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center rounded-lg p-[3px] group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none',
@@ -40,27 +35,24 @@ const tabsListVariants = cva(
},
);
function TabsList({
const TabsList = ({
className,
variant = 'default',
...props
}: React.ComponentProps<typeof TabsPrimitive.List> &
VariantProps<typeof tabsListVariants>) {
return (
VariantProps<typeof tabsListVariants>) => (
<TabsPrimitive.List
data-slot='tabs-list'
data-variant={variant}
className={cn(tabsListVariants({ variant }), className)}
{...props}
/>
);
}
);
function TabsTrigger({
const TabsTrigger = ({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
return (
}: React.ComponentProps<typeof TabsPrimitive.Trigger>) => (
<TabsPrimitive.Trigger
data-slot='tabs-trigger'
className={cn(
@@ -72,20 +64,17 @@ function TabsTrigger({
)}
{...props}
/>
);
}
);
function TabsContent({
const TabsContent = ({
className,
...props
}: React.ComponentProps<typeof TabsPrimitive.Content>) {
return (
}: React.ComponentProps<typeof TabsPrimitive.Content>) => (
<TabsPrimitive.Content
data-slot='tabs-content'
className={cn('flex-1 text-sm outline-none', className)}
{...props}
/>
);
}
);
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants };
+5 -4
View File
@@ -2,8 +2,10 @@ import * as React from 'react';
import { cn } from '@gib/ui';
function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
return (
const Textarea = ({
className,
...props
}: React.ComponentProps<'textarea'>) => (
<textarea
data-slot='textarea'
className={cn(
@@ -12,7 +14,6 @@ function Textarea({ className, ...props }: React.ComponentProps<'textarea'>) {
)}
{...props}
/>
);
}
);
export { Textarea };
+8 -10
View File
@@ -1,16 +1,16 @@
'use client';
import type { ComponentProps } from 'react';
import { ThemeProvider as NextThemesProvider, useTheme } from 'next-themes';
import { Moon, Sun } from 'lucide-react';
import { Button, cn } from '@gib/ui';
import { ThemeProvider as NextThemesProvider, useTheme } from 'next-themes';
import { Button, cn } from '@gib/ui';
const ThemeProvider = ({
children,
...props
}: ComponentProps<typeof NextThemesProvider>) => {
return <NextThemesProvider {...props}>{children}</NextThemesProvider>
return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
};
interface ThemeToggleProps {
@@ -22,23 +22,21 @@ const ThemeToggle = ({ size = 1, buttonProps }: ThemeToggleProps) => {
const { setTheme, resolvedTheme } = useTheme();
return (
<Button
variant="outline"
size="icon"
variant='outline'
size='icon'
onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
{...buttonProps}
className={cn('cursor-pointer', buttonProps?.className)}
>
<Sun
style={{ height: `${size}rem`, width: `${size}rem` }}
className='scale-100 rotate-0 transition-all
dark:scale-0 dark:-rotate-90'
className='scale-100 rotate-0 transition-all dark:scale-0 dark:-rotate-90'
/>
<Moon
style={{ height: `${size}rem`, width: `${size}rem` }}
className='absolute scale-0 rotate-90 transition-all
dark:scale-100 dark:rotate-0'
className='absolute scale-0 rotate-90 transition-all dark:scale-100 dark:rotate-0'
/>
<span className="sr-only">Toggle theme</span>
<span className='sr-only'>Toggle theme</span>
</Button>
);
};
+6 -8
View File
@@ -16,7 +16,7 @@ const ToggleGroupContext = React.createContext<
spacing: 0,
});
function ToggleGroup({
const ToggleGroup = ({
className,
variant,
size,
@@ -26,8 +26,7 @@ function ToggleGroup({
}: React.ComponentProps<typeof ToggleGroupPrimitive.Root> &
VariantProps<typeof toggleVariants> & {
spacing?: number;
}) {
return (
}) => (
<ToggleGroupPrimitive.Root
data-slot='toggle-group'
data-variant={variant}
@@ -44,17 +43,16 @@ function ToggleGroup({
{children}
</ToggleGroupContext.Provider>
</ToggleGroupPrimitive.Root>
);
}
);
function ToggleGroupItem({
const ToggleGroupItem = ({
className,
children,
variant,
size,
...props
}: React.ComponentProps<typeof ToggleGroupPrimitive.Item> &
VariantProps<typeof toggleVariants>) {
VariantProps<typeof toggleVariants>) => {
const context = React.useContext(ToggleGroupContext);
return (
@@ -77,6 +75,6 @@ function ToggleGroupItem({
{children}
</ToggleGroupPrimitive.Item>
);
}
};
export { ToggleGroup, ToggleGroupItem };
+3 -5
View File
@@ -29,20 +29,18 @@ const toggleVariants = cva(
},
);
function Toggle({
const Toggle = ({
className,
variant,
size,
...props
}: React.ComponentProps<typeof TogglePrimitive.Root> &
VariantProps<typeof toggleVariants>) {
return (
VariantProps<typeof toggleVariants>) => (
<TogglePrimitive.Root
data-slot='toggle'
className={cn(toggleVariants({ variant, size, className }))}
{...props}
/>
);
}
);
export { Toggle, toggleVariants };
+14 -18
View File
@@ -5,38 +5,35 @@ import { Tooltip as TooltipPrimitive } from 'radix-ui';
import { cn } from '@gib/ui';
function TooltipProvider({
const TooltipProvider = ({
delayDuration = 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return (
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) => (
<TooltipPrimitive.Provider
data-slot='tooltip-provider'
delayDuration={delayDuration}
{...props}
/>
);
}
);
function Tooltip({
const Tooltip = ({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root data-slot='tooltip' {...props} />;
}
}: React.ComponentProps<typeof TooltipPrimitive.Root>) => (
<TooltipPrimitive.Root data-slot='tooltip' {...props} />
);
function TooltipTrigger({
const TooltipTrigger = ({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot='tooltip-trigger' {...props} />;
}
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) => (
<TooltipPrimitive.Trigger data-slot='tooltip-trigger' {...props} />
);
function TooltipContent({
const TooltipContent = ({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
}: React.ComponentProps<typeof TooltipPrimitive.Content>) => (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot='tooltip-content'
@@ -51,7 +48,6 @@ function TooltipContent({
<TooltipPrimitive.Arrow className='bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]' />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
);
}
);
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
+1 -1
View File
@@ -1 +1 @@
[["1","2","3","4","5"],{"key":"6","value":"7"},{"key":"8","value":"9"},{"key":"10","value":"11"},{"key":"12","value":"13"},{"key":"14","value":"15"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/package.json",{"size":979,"mtime":1768166330000,"hash":"16","data":"17"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/nextjs.ts",{"size":440,"mtime":1768155639000,"hash":"18","data":"19"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/react.ts",{"size":592,"mtime":1768155639000,"hash":"20","data":"21"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/base.ts",{"size":2508,"mtime":1768320541000,"hash":"22","data":"23"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"24","data":"25"},"a5326aca75246da261fd2e354257b45a",{"hashOfOptions":"26"},"25c52c46972131dcc296288599ff108d",{"hashOfOptions":"27"},"2292935ede6baf909f6a0c61486e15da",{"hashOfOptions":"28"},"6a779439826cf31b5561a21273d134a9",{"hashOfOptions":"29"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"30"},"1249441784","4234667885","3945480854","3486090744","681484145"]
[["1","2","3","4","5","6"],{"key":"7","value":"8"},{"key":"9","value":"10"},{"key":"11","value":"12"},{"key":"13","value":"14"},{"key":"15","value":"16"},{"key":"17","value":"18"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/base.ts",{"size":2963,"mtime":1774544629518,"hash":"19","data":"20"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/nextjs.ts",{"size":440,"mtime":1768155639000,"hash":"21","data":"22"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/.cache/.prettiercache",{"size":1278,"mtime":1774544484642},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/package.json",{"size":1033,"mtime":1774544387169,"hash":"23","data":"24"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/react.ts",{"size":592,"mtime":1768155639000,"hash":"25","data":"26"},"/home/gib/Documents/Code/convex-monorepo/tools/eslint/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"27","data":"28"},"6a779439826cf31b5561a21273d134a9",{"hashOfOptions":"29"},"25c52c46972131dcc296288599ff108d",{"hashOfOptions":"30"},"a5326aca75246da261fd2e354257b45a",{"hashOfOptions":"31"},"2292935ede6baf909f6a0c61486e15da",{"hashOfOptions":"32"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"33"},"1686097143","2347540204","302976953","3406150487","1582266352"]
+17 -5
View File
@@ -2,10 +2,15 @@ import * as path from 'node:path';
import { includeIgnoreFile } from '@eslint/compat';
import eslint from '@eslint/js';
import importPlugin from 'eslint-plugin-import';
// eslint-plugin-prefer-arrow-functions doesn't ship flat config types — cast needed
import preferArrowFunctions from 'eslint-plugin-prefer-arrow-functions';
import turboPlugin from 'eslint-plugin-turbo';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const preferArrowPlugin = preferArrowFunctions as any;
/**
* All packages that leverage t3-env should use this rule
*/
@@ -45,6 +50,7 @@ export const baseConfig = defineConfig(
plugins: {
import: importPlugin,
turbo: turboPlugin,
'prefer-arrow-functions': preferArrowPlugin,
},
extends: [
eslint.configs.recommended,
@@ -58,10 +64,6 @@ export const baseConfig = defineConfig(
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
'@typescript-eslint/consistent-type-imports': [
'warn',
{ prefer: 'type-imports', fixStyle: 'inline-type-imports' },
],
'@typescript-eslint/no-misused-promises': [
2,
{ checksVoidReturn: { attributes: false } },
@@ -73,7 +75,17 @@ export const baseConfig = defineConfig(
},
],
'@typescript-eslint/no-non-null-assertion': 'error',
'import/consistent-type-specifier-style': ['error', 'prefer-top-level'],
'@typescript-eslint/consistent-type-definitions': 'off',
'prefer-arrow-functions/prefer-arrow-functions': [
'warn',
{
allowNamedFunctions: false,
classPropertiesAllowed: false,
disallowPrototype: false,
returnStyle: 'unchanged',
singleReturnOnly: false,
},
],
},
},
{
+1
View File
@@ -18,6 +18,7 @@
"@next/eslint-plugin-next": "^16.0.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prefer-arrow-functions": "^3.9.1",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-turbo": "^2.5.8",
+1 -1
View File
@@ -1 +1 @@
[["1","2","3"],{"key":"4","value":"5"},{"key":"6","value":"7"},{"key":"8","value":"9"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"10","data":"11"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/package.json",{"size":607,"mtime":1766222924000,"hash":"12","data":"13"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/index.js",{"size":1194,"mtime":1768372320442,"hash":"14","data":"15"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"16"},"11b634ce56ac720ac9a2860d77fbd2cc",{"hashOfOptions":"17"},"ecbaa91166a940dfcec8117059f52402",{"hashOfOptions":"18"},"67859251","550572982","1674623979"]
[["1","2","3","4"],{"key":"5","value":"6"},{"key":"7","value":"8"},{"key":"9","value":"10"},{"key":"11","value":"12"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/.cache/.prettiercache",{"size":832,"mtime":1774544484482},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/index.js",{"size":1194,"mtime":1768372320442,"hash":"13","data":"14"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/package.json",{"size":607,"mtime":1774032385569,"hash":"15","data":"16"},"/home/gib/Documents/Code/convex-monorepo/tools/prettier/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"17","data":"18"},"ecbaa91166a940dfcec8117059f52402",{"hashOfOptions":"19"},"11b634ce56ac720ac9a2860d77fbd2cc",{"hashOfOptions":"20"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"21"},"1828250668","802511607","4250532914"]
+1
View File
@@ -0,0 +1 @@
[{"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/postcss-config.js":"1"},{"size":70,"mtime":1768155639000,"results":"2","hashOfConfig":"3"},{"filePath":"4","messages":"5","suppressedMessages":"6","errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"z9il2c","/home/gib/Documents/Code/convex-monorepo/tools/tailwind/postcss-config.js",[],[]]
+1 -1
View File
@@ -1 +1 @@
[["1","2","3","4","5"],{"key":"6","value":"7"},{"key":"8","value":"9"},{"key":"10","value":"11"},{"key":"12","value":"13"},{"key":"14","value":"15"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/package.json",{"size":851,"mtime":1766222924000,"hash":"16","data":"17"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/postcss-config.js",{"size":70,"mtime":1768155639000,"hash":"18","data":"19"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/eslint.config.ts",{"size":143,"mtime":1768155639000,"hash":"20","data":"21"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/theme.css",{"size":7273,"mtime":1768320378000,"hash":"22","data":"23"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"24","data":"25"},"0d22e47f57739db9de04c6f8420d6fb5",{"hashOfOptions":"26"},"9a944fbda06979be39571bd9bd00b0d9",{"hashOfOptions":"27"},"b8fec960cb32340eea62ca1485093e68",{"hashOfOptions":"28"},"e40c2569ef375a9c828c80c0f9ce1bf2",{"hashOfOptions":"29"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"30"},"34296497","1053475182","4101125807","919761249","2958815992"]
[["1","2","3","4","5","6","7"],{"key":"8","value":"9"},{"key":"10","value":"11"},{"key":"12","value":"13"},{"key":"14","value":"15"},{"key":"16","value":"17"},{"key":"18","value":"19"},{"key":"20","value":"21"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/.cache/.eslintcache",{"size":396,"mtime":1774544641255},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/.cache/.prettiercache",{"size":1450,"mtime":1774544484418},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/package.json",{"size":851,"mtime":1774032407411,"hash":"22","data":"23"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/tsconfig.json",{"size":94,"mtime":1766222924000,"hash":"24","data":"25"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/eslint.config.ts",{"size":143,"mtime":1768155639000,"hash":"26","data":"27"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/theme.css",{"size":7273,"mtime":1768320378000,"hash":"28","data":"29"},"/home/gib/Documents/Code/convex-monorepo/tools/tailwind/postcss-config.js",{"size":70,"mtime":1768155639000,"hash":"30","data":"31"},"0d22e47f57739db9de04c6f8420d6fb5",{"hashOfOptions":"32"},"b3c77d33a30318d89c9c2cafcbe00bbe",{"hashOfOptions":"33"},"b8fec960cb32340eea62ca1485093e68",{"hashOfOptions":"34"},"e40c2569ef375a9c828c80c0f9ce1bf2",{"hashOfOptions":"35"},"9a944fbda06979be39571bd9bd00b0d9",{"hashOfOptions":"36"},"286235122","2846522359","1288936688","2683656544","1694755693"]
+3 -3
View File
@@ -15,13 +15,13 @@
"CONVEX_SELF_HOSTED_ADMIN_KEY",
"CONVEX_SITE_URL",
"USESEND_API_KEY",
"USESEND_URL",
"USESEND_FROM_EMAIL",
"AUTH_AUTHENTIK_ID",
"AUTH_AUTHENTIK_SECRET",
"AUTH_AUTHENTIK_ISSUER"
],
"globalPassThroughEnv": [
"NODE_ENV"
],
"globalPassThroughEnv": ["NODE_ENV"],
"ui": "tui",
"tasks": {
"topo": {
+35 -37
View File
@@ -1,5 +1,5 @@
import { execSync } from "node:child_process";
import type { PlopTypes } from "@turbo/gen";
import { execSync } from 'node:child_process';
import type { PlopTypes } from '@turbo/gen';
interface PackageJson {
name: string;
@@ -9,58 +9,58 @@ interface PackageJson {
}
export default function generator(plop: PlopTypes.NodePlopAPI): void {
plop.setGenerator("init", {
description: "Generate a new package for the Acme Monorepo",
plop.setGenerator('init', {
description: 'Generate a new package for the Monorepo',
prompts: [
{
type: "input",
name: "name",
type: 'input',
name: 'name',
message:
"What is the name of the package? (You can skip the `@gib/` prefix)",
'What is the name of the package? (You can skip the `@gib/` prefix)',
},
{
type: "input",
name: "deps",
type: 'input',
name: 'deps',
message:
"Enter a space separated list of dependencies you would like to install",
'Enter a space separated list of dependencies you would like to install',
},
],
actions: [
(answers) => {
if ("name" in answers && typeof answers.name === "string") {
if (answers.name.startsWith("@gib/")) {
answers.name = answers.name.replace("@gib/", "");
if ('name' in answers && typeof answers.name === 'string') {
if (answers.name.startsWith('@gib/')) {
answers.name = answers.name.replace('@gib/', '');
}
}
return "Config sanitized";
return 'Config sanitized';
},
{
type: "add",
path: "packages/{{ name }}/eslint.config.ts",
templateFile: "templates/eslint.config.ts.hbs",
type: 'add',
path: 'packages/{{ name }}/eslint.config.ts',
templateFile: 'templates/eslint.config.ts.hbs',
},
{
type: "add",
path: "packages/{{ name }}/package.json",
templateFile: "templates/package.json.hbs",
type: 'add',
path: 'packages/{{ name }}/package.json',
templateFile: 'templates/package.json.hbs',
},
{
type: "add",
path: "packages/{{ name }}/tsconfig.json",
templateFile: "templates/tsconfig.json.hbs",
type: 'add',
path: 'packages/{{ name }}/tsconfig.json',
templateFile: 'templates/tsconfig.json.hbs',
},
{
type: "add",
path: "packages/{{ name }}/src/index.ts",
type: 'add',
path: 'packages/{{ name }}/src/index.ts',
template: "export const name = '{{ name }}';",
},
{
type: "modify",
path: "packages/{{ name }}/package.json",
type: 'modify',
path: 'packages/{{ name }}/package.json',
async transform(content, answers) {
if ("deps" in answers && typeof answers.deps === "string") {
if ('deps' in answers && typeof answers.deps === 'string') {
const pkg = JSON.parse(content) as PackageJson;
for (const dep of answers.deps.split(" ").filter(Boolean)) {
for (const dep of answers.deps.split(' ').filter(Boolean)) {
const version = await fetch(
`https://registry.npmjs.org/-/package/${dep}/dist-tags`,
)
@@ -78,17 +78,15 @@ export default function generator(plop: PlopTypes.NodePlopAPI): void {
/**
* Install deps and format everything
*/
if ("name" in answers && typeof answers.name === "string") {
// execSync("pnpm dlx sherif@latest --fix", {
// stdio: "inherit",
// });
execSync("pnpm i", { stdio: "inherit" });
if ('name' in answers && typeof answers.name === 'string') {
execSync('bun install', { stdio: 'inherit' });
execSync(
`pnpm prettier --write packages/${answers.name}/** --list-different`,
`bun prettier --write packages/${answers.name}/** --list-different`,
{ stdio: 'inherit' },
);
return "Package scaffolded";
return 'Package scaffolded';
}
return "Package not scaffolded";
return 'Package not scaffolded';
},
],
});