feat: init

This commit is contained in:
hh committed 2026-09-22 02:24:49 +02:00
commit 610fa90bec
147 files changed
+4979

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
node_modules
static/r
.DS_Store
+8
View File
@@ -0,0 +1,8 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}
+23
View File
@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2026 h
Copyright (c) 2026 cladd-ui (design tokens, surface system and component styling
ported from https://github.com/cladd-ui/cladd)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+72
View File
@@ -0,0 +1,72 @@
# claddcn-svelte
The [cladd.io](https://cladd.io) skin for shadcn-svelte.
## Install into a project
```bash
# in the root of a shadcn-svelte project
npx git+https://git.kotikot.com/lib/claddcn-svelte.git init --strip-vars
# or
bunx git+https://git.kotikot.com/lib/claddcn-svelte.git init --strip-vars
```
`init`:
1. copies `claddcn.css` next to your Tailwind entry and `@import`s it;
2. `--strip-vars` removes your old `:root` / `.dark` / `@theme inline` token blocks
(claddcn defines every shadcn token, and blocks after the import would win);
3. overwrites `$lib/components/ui/*` with the cladd versions and backs up the old
files to `.claddcn-backup/<timestamp>/`;
4. extends `cn()` so tailwind-merge understands `text-cladd-xs`, `h-cladd-md`, etc.;
5. installs `@fontsource-variable/geist` and `geist-mono`, using bun, pnpm, yarn or npm.
Flags: `--css <file>` (if components.json points at the wrong CSS), `--only button,card`, `--no-install`, `--dry`.
As a shadcn-svelte registry: `npx shadcn-svelte registry build` → serve `static/r`
→ `npx shadcn-svelte add https://…/r/button.json`.
## What you get
| shadcn-svelte | cladd equivalent |
| ----------------------------- | -------------------------------------------------------- |
| `Button` `default` | brand-tinted gradient surface (the “Get started” button) |
| `outline` / `secondary` | neutral raised gradient, with or without outline |
| `ghost` | transparent, rises to the next surface level on hover |
| `destructive` | red-tinted surface |
| `fill` (new) | solid accent gradient fill |
| `color="green"` (new) | any of the 11 cladd accents on Button / Badge |
| `rounded` (new) | pill shape |
| `Input`, `Textarea` | recessed “cut” well, focus ring floating 4px off |
| `Checkbox` | round thumb, brand fill when checked |
| `Switch` | cladd switch with the × → ✓ indicator |
| `Tabs` | toolbar pill with raised active segment |
| `Select`, menus | 24px-radius popover, pill rows, spring scale-in |
| `Dialog`, `Sheet` | 24px panels, spring pop-in, floating sheet |
| `Tooltip` | small raised surface, no arrow |
| `Surface`, `SurfaceCut` (new) | the cladd primitives, auto-nesting depth |
### Utility classes for hand-written markup
`cladd-surface` (+ `cladd-gradient`, `cladd-fill`, `cladd-gradient-fill`, `cladd-outline`),
`cladd-cut`, `cladd-ghost`, `cladd-hoverable`, `cladd-clickable`, `cladd-focusable`,
`cladd-segment` (active via `aria-current="page"` / `data-state="active"`),
`cladd-section-title`, `cladd-color-{neutral,brand,red,pink,purple,blue,cyan,lime,green,yellow,orange}`,
`cladd-level-{1..5}`.
Tailwind tokens: `bg-cladd-surface`, `bg-cladd-surface-next`, `text-cladd-fg-soft`,
`text-cladd-primary`, `h-cladd-md`, `rounded-cladd-md`, `rounded-cladd-panel`,
`shadow-cladd-outline`, `text-cladd-xs`, …
Nested `.cladd-surface` elements step one level deeper automatically (1–5). cladd does
this with React context. Here it comes from selector depth, so it also works in plain markup.
## Requirements
Tailwind CSS v4, bits-ui v2, tailwind-merge v3, and browsers with relative color
syntax (`oklch(from …)`): Chrome 119+, Safari 18+, Firefox 128+.
## License
MIT. Design tokens, surface math and component styling are ported from
[cladd](https://github.com/cladd-ui/cladd) (MIT, © 2026 cladd-ui).
+211
View File
@@ -0,0 +1,211 @@
#!/usr/bin/env node
// claddcn — puts the cladd.io look onto an existing shadcn-svelte project.
//
// npx claddcn init [--cwd <dir>] [--css <file>] [--only button,card] [--strip-vars] [--no-install] [--dry]
//
// What `init` does, in order:
// 1. copies claddcn.css next to the Tailwind entry named in components.json
// and imports it right after the other top-level @imports;
// 2. with --strip-vars, removes the old :root / .dark / @theme inline token
// blocks from that file (claddcn ships its own, and later ones would win);
// 3. overwrites ui components with the cladd versions (same names, same API),
// backing up whatever it replaces into .claddcn-backup/<timestamp>/;
// 4. teaches `cn()` the cladd scale so tailwind-merge doesn't drop classes;
// 5. adds the Geist / Geist Mono fonts with the project's package manager.
import { execSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const REGISTRY_UI = path.join(ROOT, 'registry', 'ui');
const THEME_CSS = path.join(ROOT, 'src', 'claddcn.css');
const UTILS_TS = path.join(ROOT, 'registry', 'lib', 'utils.ts');
const args = process.argv.slice(2);
const command = args[0];
const flag = (name) => args.includes(`--${name}`);
const option = (name) => {
const i = args.indexOf(`--${name}`);
return i >= 0 ? args[i + 1] : undefined;
};
const cwd = path.resolve(option('cwd') ?? process.cwd());
const dry = flag('dry');
const log = (msg) => console.log(`claddcn ${msg}`);
function write(file, content) {
if (dry) return log(`[dry] write ${path.relative(cwd, file)}`);
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, content);
}
function copyDir(src, dest) {
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
const from = path.join(src, entry.name);
const to = path.join(dest, entry.name);
if (entry.isDirectory()) copyDir(from, to);
else write(to, fs.readFileSync(from));
}
}
function resolveAlias(alias, config) {
const lib = config.aliases?.lib ?? '$lib';
if (alias.startsWith('$lib')) return path.join(cwd, 'src', 'lib', alias.slice('$lib'.length));
if (alias.startsWith(lib)) return path.join(cwd, 'src', 'lib', alias.slice(lib.length));
return path.join(cwd, alias);
}
// Removes a top-level block that starts with `selector {` by brace matching.
function stripBlock(css, selector) {
const re = new RegExp(`(^|\\n)${selector.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*\\{`);
const m = re.exec(css);
if (!m) return css;
let depth = 0;
let i = m.index + m[0].length - 1;
for (; i < css.length; i++) {
if (css[i] === '{') depth++;
else if (css[i] === '}' && --depth === 0) break;
}
return css.slice(0, m.index) + css.slice(i + 1);
}
// Tailwind entries under src/, i.e. CSS files that `@import 'tailwindcss'`.
function findTailwindEntries(dir = path.join(cwd, 'src'), found = []) {
if (!fs.existsSync(dir)) return found;
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const file = path.join(dir, entry.name);
if (entry.isDirectory()) findTailwindEntries(file, found);
else if (
entry.name.endsWith('.css') &&
entry.name !== 'claddcn.css' &&
/^@import ['"]tailwindcss['"]/m.test(fs.readFileSync(file, 'utf8'))
)
found.push(file);
}
return found;
}
// --css wins; then components.json; if that path is stale, the single Tailwind
// entry under src/ (or the one that already imports claddcn).
function resolveCss(config) {
if (option('css')) {
const file = path.join(cwd, option('css'));
if (fs.existsSync(file)) return file;
console.error(`claddcn ${option('css')} not found`);
process.exit(1);
}
const configured = path.join(cwd, config.tailwind.css);
if (fs.existsSync(configured)) return configured;
const entries = findTailwindEntries();
const ours = entries.filter((f) => fs.readFileSync(f, 'utf8').includes('claddcn.css'));
const pick = entries.length === 1 ? entries[0] : ours.length === 1 ? ours[0] : null;
if (pick) {
log(`components.json points at ${config.tailwind.css}, using ${path.relative(cwd, pick)}`);
return pick;
}
console.error(`claddcn ${config.tailwind.css} not found. Pick the Tailwind entry with --css:`);
for (const f of entries) console.error(` --css "${path.relative(cwd, f)}"`);
process.exit(1);
}
function patchCss(cssFile) {
let css = fs.readFileSync(cssFile, 'utf8');
if (!css.includes('claddcn.css')) {
const imports = [...css.matchAll(/^@import[^;]+;\s*$/gm)];
const at = imports.length ? imports.at(-1).index + imports.at(-1)[0].length : 0;
css = `${css.slice(0, at)}\n@import './claddcn.css';${css.slice(at)}`;
log(`import added to ${path.relative(cwd, cssFile)}`);
}
if (flag('strip-vars')) {
for (const selector of [':root', '.dark', '@theme inline']) css = stripBlock(css, selector);
log('old :root / .dark / @theme inline blocks removed');
} else if (/(^|\n):root\s*\{[^}]*--background/.test(css)) {
log('warning: this file still defines shadcn tokens after the import and will override');
log(' claddcn colors. Re-run with --strip-vars or delete those blocks by hand.');
}
write(cssFile, css.replace(/\n{3,}/g, '\n\n'));
}
function patchUtils(utilsFile) {
if (!fs.existsSync(utilsFile)) return write(utilsFile, fs.readFileSync(UTILS_TS));
const src = fs.readFileSync(utilsFile, 'utf8');
if (src.includes('extendTailwindMerge')) return log('cn() already extended, left as is');
const ours = fs.readFileSync(UTILS_TS, 'utf8');
const block = ours.slice(0, ours.indexOf('export function cn'));
const next = src.replace(
/import \{ clsx, type ClassValue \} from 'clsx';\s*\nimport \{ twMerge \} from 'tailwind-merge';\s*\n/,
block
);
if (next === src) {
log(
`warning: couldn't patch ${path.relative(cwd, utilsFile)} — merge registry/lib/utils.ts by hand`
);
return;
}
write(utilsFile, next);
log('cn() now knows the cladd scale');
}
function install() {
const has = (f) => fs.existsSync(path.join(cwd, f));
const pkg = '@fontsource-variable/geist @fontsource-variable/geist-mono';
const cmd =
has('bun.lock') || has('bun.lockb')
? `bun add -d ${pkg}`
: has('pnpm-lock.yaml')
? `pnpm add -D ${pkg}`
: has('yarn.lock')
? `yarn add -D ${pkg}`
: `npm i -D ${pkg}`;
if (flag('no-install') || dry) return log(`skip install, run: ${cmd}`);
log(cmd);
execSync(cmd, { cwd, stdio: 'inherit' });
}
function init() {
const configFile = path.join(cwd, 'components.json');
if (!fs.existsSync(configFile)) {
console.error(`claddcn no components.json in ${cwd} — is this a shadcn-svelte project?`);
process.exit(1);
}
const config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
const cssFile = resolveCss(config);
const uiDir = resolveAlias(config.aliases?.ui ?? '$lib/components/ui', config);
const utilsFile = `${resolveAlias(config.aliases?.utils ?? '$lib/utils', config)}.ts`;
const only = option('only')?.split(',');
const available = fs.readdirSync(REGISTRY_UI);
const components = only ?? available;
const missing = components.filter((c) => !available.includes(c));
if (missing.length) log(`unknown components skipped: ${missing.join(', ')}`);
write(path.join(path.dirname(cssFile), 'claddcn.css'), fs.readFileSync(THEME_CSS));
patchCss(cssFile);
const stamp = new Date().toISOString().replace(/[:.]/g, '-');
const backup = path.join(cwd, '.claddcn-backup', stamp);
for (const name of components.filter((c) => available.includes(c))) {
const target = path.join(uiDir, name);
if (fs.existsSync(target) && !dry) {
fs.mkdirSync(backup, { recursive: true });
fs.cpSync(target, path.join(backup, name), { recursive: true });
}
copyDir(path.join(REGISTRY_UI, name), target);
}
log(`${components.length} components written to ${path.relative(cwd, uiDir)}`);
if (fs.existsSync(backup)) log(`previous versions saved in ${path.relative(cwd, backup)}`);
patchUtils(utilsFile);
install();
log('done — the cladd look is in. Dark mode follows the `.dark` class (mode-watcher).');
}
if (command === 'init') init();
else {
console.log(
`usage: claddcn init [--cwd <dir>] [--css <file>] [--only a,b] [--strip-vars] [--no-install] [--dry]`
);
process.exit(command ? 1 : 0);
}
+29
View File
@@ -0,0 +1,29 @@
{
"name": "claddcn-svelte",
"version": "0.1.0",
"description": "The cladd.io skin for shadcn-svelte",
"license": "MIT",
"type": "module",
"bin": {
"claddcn": "./bin/claddcn.mjs"
},
"files": [
"bin",
"src",
"registry",
"registry.json"
],
"exports": {
"./css": "./src/claddcn.css"
},
"scripts": {
"registry:build": "shadcn-svelte registry build"
},
"peerDependencies": {
"@fontsource-variable/geist": "^5",
"@fontsource-variable/geist-mono": "^5",
"bits-ui": "^2",
"tailwind-merge": "^3",
"tailwindcss": "^4"
}
}
+801
View File
@@ -0,0 +1,801 @@
{
"$schema": "https://shadcn-svelte.com/schema/registry.json",
"name": "claddcn",
"homepage": "https://cladd.io",
"items": [
{
"name": "claddcn-theme",
"type": "registry:style",
"title": "claddcn theme",
"description": "cladd.io tokens, surfaces and shadcn variable mapping",
"dependencies": ["@fontsource-variable/geist", "@fontsource-variable/geist-mono"],
"files": [
{
"path": "src/claddcn.css",
"type": "registry:file",
"target": "src/routes/claddcn.css"
}
]
},
{
"name": "utils",
"type": "registry:lib",
"title": "cn() with cladd scale",
"dependencies": ["clsx", "tailwind-merge"],
"files": [
{
"path": "registry/lib/utils.ts",
"type": "registry:lib"
}
]
},
{
"name": "avatar",
"type": "registry:ui",
"title": "avatar",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/avatar/avatar-fallback.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/avatar/avatar-image.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/avatar/avatar.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/avatar/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "badge",
"type": "registry:ui",
"title": "badge",
"dependencies": [],
"registryDependencies": ["utils", "button"],
"files": [
{
"path": "registry/ui/badge/badge.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/badge/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "breadcrumb",
"type": "registry:ui",
"title": "breadcrumb",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/breadcrumb/breadcrumb-ellipsis.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/breadcrumb-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/breadcrumb-link.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/breadcrumb-list.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/breadcrumb-page.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/breadcrumb-separator.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/breadcrumb.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/breadcrumb/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "button",
"type": "registry:ui",
"title": "button",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/button/button.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/button/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "card",
"type": "registry:ui",
"title": "card",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/card/card-action.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/card-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/card-description.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/card-footer.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/card-header.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/card-title.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/card.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/card/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "checkbox",
"type": "registry:ui",
"title": "checkbox",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/checkbox/checkbox.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/checkbox/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "collapsible",
"type": "registry:ui",
"title": "collapsible",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/collapsible/collapsible-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/collapsible/collapsible-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/collapsible/collapsible.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/collapsible/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "context-menu",
"type": "registry:ui",
"title": "context-menu",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/context-menu/context-menu-checkbox-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-group-heading.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-group.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-label.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-portal.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-radio-group.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-radio-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-separator.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-shortcut.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-sub-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-sub-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-sub.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/context-menu.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/context-menu/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "dialog",
"type": "registry:ui",
"title": "dialog",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/dialog/dialog-close.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-description.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-footer.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-header.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-overlay.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-portal.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-title.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/dialog.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dialog/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "dropdown-menu",
"type": "registry:ui",
"title": "dropdown-menu",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-group-heading.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-group.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-label.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-portal.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-radio-group.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-radio-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-separator.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-shortcut.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-sub-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-sub.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/dropdown-menu.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/dropdown-menu/index.ts",
"type": "registry:ui"
}
]
},
{
"name": "input",
"type": "registry:ui",
"title": "input",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/input/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/input/input.svelte",
"type": "registry:ui"
}
]
},
{
"name": "label",
"type": "registry:ui",
"title": "label",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/label/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/label/label.svelte",
"type": "registry:ui"
}
]
},
{
"name": "progress",
"type": "registry:ui",
"title": "progress",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/progress/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/progress/progress.svelte",
"type": "registry:ui"
}
]
},
{
"name": "select",
"type": "registry:ui",
"title": "select",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/select/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-group-heading.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-group.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-item.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-label.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-portal.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-scroll-down-button.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-scroll-up-button.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-separator.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/select/select.svelte",
"type": "registry:ui"
}
]
},
{
"name": "separator",
"type": "registry:ui",
"title": "separator",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/separator/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/separator/separator.svelte",
"type": "registry:ui"
}
]
},
{
"name": "sheet",
"type": "registry:ui",
"title": "sheet",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/sheet/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-close.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-description.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-footer.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-header.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-overlay.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-portal.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-title.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/sheet/sheet.svelte",
"type": "registry:ui"
}
]
},
{
"name": "slider",
"type": "registry:ui",
"title": "slider",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/slider/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/slider/slider.svelte",
"type": "registry:ui"
}
]
},
{
"name": "sonner",
"type": "registry:ui",
"title": "sonner",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/sonner/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/sonner/sonner.svelte",
"type": "registry:ui"
}
]
},
{
"name": "surface",
"type": "registry:ui",
"title": "surface",
"dependencies": [],
"registryDependencies": ["utils", "button"],
"files": [
{
"path": "registry/ui/surface/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/surface/surface-cut.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/surface/surface.svelte",
"type": "registry:ui"
}
]
},
{
"name": "switch",
"type": "registry:ui",
"title": "switch",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/switch/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/switch/switch.svelte",
"type": "registry:ui"
}
]
},
{
"name": "table",
"type": "registry:ui",
"title": "table",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/table/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-body.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-caption.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-cell.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-footer.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-head.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-header.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table-row.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/table/table.svelte",
"type": "registry:ui"
}
]
},
{
"name": "tabs",
"type": "registry:ui",
"title": "tabs",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/tabs/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/tabs/tabs-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tabs/tabs-list.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tabs/tabs-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tabs/tabs.svelte",
"type": "registry:ui"
}
]
},
{
"name": "textarea",
"type": "registry:ui",
"title": "textarea",
"dependencies": [],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/textarea/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/textarea/textarea.svelte",
"type": "registry:ui"
}
]
},
{
"name": "tooltip",
"type": "registry:ui",
"title": "tooltip",
"dependencies": ["bits-ui"],
"registryDependencies": ["utils"],
"files": [
{
"path": "registry/ui/tooltip/index.ts",
"type": "registry:ui"
},
{
"path": "registry/ui/tooltip/tooltip-content.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tooltip/tooltip-portal.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tooltip/tooltip-provider.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tooltip/tooltip-trigger.svelte",
"type": "registry:ui"
},
{
"path": "registry/ui/tooltip/tooltip.svelte",
"type": "registry:ui"
}
]
}
]
}
+60
View File
@@ -0,0 +1,60 @@
import { clsx, type ClassValue } from 'clsx';
import { extendTailwindMerge } from 'tailwind-merge';
const cladd = (...names: string[]) => names.map((n) => `cladd-${n}`);
// Teach tailwind-merge the cladd scale, otherwise `text-cladd-xs` (size) and
// `text-cladd-primary` (color) look like the same group and one gets dropped.
const twMerge = extendTailwindMerge({
extend: {
theme: {
text: cladd('md', 'sm', 'xs', '2xs', '3xs'),
radius: cladd(
'3xs',
'2xs',
'xs',
'sm',
'md',
'lg',
'xl',
'2xl',
'wrap-md',
'panel',
'popover',
'dialog',
'toast',
'tooltip'
),
spacing: cladd(
'3xs',
'2xs',
'xs',
'sm',
'md',
'lg',
'xl',
'2xl',
'thumb-xs',
'thumb-sm',
'thumb-md',
'nested-sm',
'nested-md',
'nested-lg'
),
shadow: cladd('outline', 'outline-fill', 'cut-outline', 'popover')
}
}
});
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, 'child'> : T;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, 'children'> : T;
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & {
ref?: U | null;
};
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: AvatarPrimitive.FallbackProps = $props();
</script>
<AvatarPrimitive.Fallback
bind:ref
data-slot="avatar-fallback"
class={cn(
'flex size-full items-center justify-center rounded-full text-cladd-xs font-semibold text-cladd-fg-soft uppercase',
className
)}
{...restProps}
/>
+17
View File
@@ -0,0 +1,17 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: AvatarPrimitive.ImageProps = $props();
</script>
<AvatarPrimitive.Image
bind:ref
data-slot="avatar-image"
class={cn('aspect-square size-full', className)}
{...restProps}
/>
+22
View File
@@ -0,0 +1,22 @@
<script lang="ts">
import { Avatar as AvatarPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
loadingStatus = $bindable('loading'),
class: className,
...restProps
}: AvatarPrimitive.RootProps = $props();
</script>
<AvatarPrimitive.Root
bind:ref
bind:loadingStatus
data-slot="avatar"
class={cn(
'cladd-surface cladd-gradient cladd-outline relative flex size-cladd-lg shrink-0 overflow-hidden rounded-full',
className
)}
{...restProps}
/>
+13
View File
@@ -0,0 +1,13 @@
import Root from './avatar.svelte';
import Image from './avatar-image.svelte';
import Fallback from './avatar-fallback.svelte';
export {
Root,
Image,
Fallback,
//
Root as Avatar,
Image as AvatarImage,
Fallback as AvatarFallback
};
+57
View File
@@ -0,0 +1,57 @@
<script lang="ts" module>
import { type VariantProps, tv } from 'tailwind-variants';
export const badgeVariants = tv({
base: 'cladd-surface inline-flex h-cladd-nested-md w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-cladd-sm px-2 text-cladd-xs leading-none font-semibold whitespace-nowrap select-none has-[>svg:first-child]:ps-1.5 has-[>svg:last-child]:pe-1.5 [&>svg]:pointer-events-none [&>svg]:size-3',
variants: {
variant: {
default: 'cladd-gradient cladd-outline cladd-color-brand text-cladd-primary',
secondary: 'cladd-gradient cladd-outline text-cladd-fg',
destructive: 'cladd-gradient cladd-outline cladd-color-red text-cladd-primary',
success: 'cladd-gradient cladd-outline cladd-color-green text-cladd-primary',
warning: 'cladd-gradient cladd-outline cladd-color-yellow text-cladd-primary',
outline: 'cladd-ghost shadow-cladd-outline text-cladd-fg',
fill: 'cladd-gradient-fill cladd-outline cladd-color-brand'
}
},
defaultVariants: {
variant: 'default'
}
});
export type BadgeVariant = VariantProps<typeof badgeVariants>['variant'];
</script>
<script lang="ts">
import type { HTMLAnchorAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
import { withColor, type CladdColor } from '../button/button.svelte';
let {
ref = $bindable(null),
href,
class: className,
variant = 'default',
color,
children,
...restProps
}: WithElementRef<HTMLAnchorAttributes> & {
variant?: BadgeVariant;
color?: CladdColor;
} = $props();
</script>
<svelte:element
this={href ? 'a' : 'span'}
bind:this={ref}
data-slot="badge"
{href}
class={cn(
withColor(badgeVariants({ variant }), color),
href && 'cladd-hoverable cladd-focusable',
className
)}
{...restProps}
>
{@render children?.()}
</svelte:element>
+2
View File
@@ -0,0 +1,2 @@
export { default as Badge } from './badge.svelte';
export { badgeVariants, type BadgeVariant } from './badge.svelte';
@@ -0,0 +1,23 @@
<script lang="ts">
import EllipsisIcon from '@lucide/svelte/icons/ellipsis';
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef, type WithoutChildren } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildren<WithElementRef<HTMLAttributes<HTMLSpanElement>>> = $props();
</script>
<span
bind:this={ref}
data-slot="breadcrumb-ellipsis"
role="presentation"
aria-hidden="true"
class={cn('flex size-9 items-center justify-center', className)}
{...restProps}
>
<EllipsisIcon class="size-4" />
<span class="sr-only">More</span>
</span>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLLiAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li
bind:this={ref}
data-slot="breadcrumb-item"
class={cn('inline-flex items-center gap-1.5', className)}
{...restProps}
>
{@render children?.()}
</li>
@@ -0,0 +1,31 @@
<script lang="ts">
import type { HTMLAnchorAttributes } from 'svelte/elements';
import type { Snippet } from 'svelte';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
href = undefined,
child,
children,
...restProps
}: WithElementRef<HTMLAnchorAttributes> & {
child?: Snippet<[{ props: HTMLAnchorAttributes }]>;
} = $props();
const attrs = $derived({
'data-slot': 'breadcrumb-link',
class: cn('hover:text-foreground transition-colors', className),
href,
...restProps
});
</script>
{#if child}
{@render child({ props: attrs })}
{:else}
<a bind:this={ref} {...attrs}>
{@render children?.()}
</a>
{/if}
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLOlAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLOlAttributes> = $props();
</script>
<ol
bind:this={ref}
data-slot="breadcrumb-list"
class={cn(
'flex flex-wrap items-center gap-1.5 text-cladd-xs font-medium break-words text-cladd-fg-soft sm:gap-2.5',
className
)}
{...restProps}
>
{@render children?.()}
</ol>
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
data-slot="breadcrumb-page"
role="link"
aria-disabled="true"
aria-current="page"
class={cn('font-medium text-cladd-fg', className)}
{...restProps}
>
{@render children?.()}
</span>
@@ -0,0 +1,27 @@
<script lang="ts">
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLLiAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLLiAttributes> = $props();
</script>
<li
bind:this={ref}
data-slot="breadcrumb-separator"
role="presentation"
aria-hidden="true"
class={cn('[&>svg]:size-3.5', className)}
{...restProps}
>
{#if children}
{@render children?.()}
{:else}
<ChevronRightIcon />
{/if}
</li>
+21
View File
@@ -0,0 +1,21 @@
<script lang="ts">
import type { WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
</script>
<nav
bind:this={ref}
data-slot="breadcrumb"
class={className}
aria-label="хлебные крошки"
{...restProps}
>
{@render children?.()}
</nav>
+25
View File
@@ -0,0 +1,25 @@
import Root from './breadcrumb.svelte';
import Ellipsis from './breadcrumb-ellipsis.svelte';
import Item from './breadcrumb-item.svelte';
import Separator from './breadcrumb-separator.svelte';
import Link from './breadcrumb-link.svelte';
import List from './breadcrumb-list.svelte';
import Page from './breadcrumb-page.svelte';
export {
Root,
Ellipsis,
Item,
Separator,
Link,
List,
Page,
//
Root as Breadcrumb,
Ellipsis as BreadcrumbEllipsis,
Item as BreadcrumbItem,
Separator as BreadcrumbSeparator,
Link as BreadcrumbLink,
List as BreadcrumbList,
Page as BreadcrumbPage
};
+113
View File
@@ -0,0 +1,113 @@
<script lang="ts" module>
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from 'svelte/elements';
import { type VariantProps, tv } from 'tailwind-variants';
export type CladdColor =
| 'neutral'
| 'brand'
| 'red'
| 'pink'
| 'purple'
| 'blue'
| 'cyan'
| 'lime'
| 'green'
| 'yellow'
| 'orange';
export const buttonVariants = tv({
base: "cladd-clickable cladd-focusable relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 font-semibold whitespace-nowrap select-none disabled:pointer-events-none disabled:*:opacity-40 aria-disabled:pointer-events-none aria-disabled:*:opacity-40 aria-invalid:outline-destructive [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
variants: {
variant: {
default:
'cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-color-brand text-cladd-primary',
destructive:
'cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-color-red text-cladd-primary',
outline: 'cladd-surface cladd-gradient cladd-outline cladd-hoverable text-cladd-fg',
secondary: 'cladd-surface cladd-gradient cladd-hoverable text-cladd-fg',
ghost: 'cladd-ghost cladd-hoverable text-cladd-fg',
fill: 'cladd-surface cladd-gradient-fill cladd-outline cladd-hoverable',
link: 'text-cladd-primary cladd-color-brand underline-offset-4 hover:underline'
},
size: {
default: 'h-cladd-md rounded-cladd-md px-2.5 text-cladd-xs',
xs: 'h-cladd-xs gap-1.5 rounded-cladd-xs px-2 text-cladd-xs [&_svg:not([class*=size-])]:size-3',
sm: 'h-cladd-sm gap-1.5 rounded-cladd-sm px-2.5 text-cladd-xs',
lg: 'h-cladd-lg rounded-cladd-lg px-3 text-cladd-xs',
xl: 'h-cladd-xl rounded-cladd-xl px-3.5 text-cladd-sm',
icon: 'size-cladd-md rounded-cladd-md',
'icon-xs': 'size-cladd-xs rounded-cladd-xs [&_svg:not([class*=size-])]:size-3',
'icon-sm': 'size-cladd-sm rounded-cladd-sm',
'icon-lg': 'size-cladd-lg rounded-cladd-lg'
},
rounded: {
true: 'rounded-full',
false: ''
}
},
compoundVariants: [{ variant: 'link', class: 'h-auto px-0 rounded-none' }],
defaultVariants: {
variant: 'default',
size: 'default',
rounded: false
}
});
/** Swaps the variant's accent for an explicit one: `<Button color="green">`. */
export function withColor(classes: string, color?: CladdColor) {
if (!color) return classes;
return `${classes.replace(/\bcladd-color-\w+/g, '')} cladd-color-${color}`;
}
export type ButtonVariant = VariantProps<typeof buttonVariants>['variant'];
export type ButtonSize = VariantProps<typeof buttonVariants>['size'];
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
WithElementRef<HTMLAnchorAttributes> & {
variant?: ButtonVariant;
size?: ButtonSize;
rounded?: boolean;
color?: CladdColor;
};
</script>
<script lang="ts">
let {
class: className,
variant = 'default',
size = 'default',
rounded = false,
color,
ref = $bindable(null),
href = undefined,
type = 'button',
disabled,
children,
...restProps
}: ButtonProps = $props();
const classes = $derived(
cn(withColor(buttonVariants({ variant, size, rounded }), color), className)
);
</script>
<!-- Content sits in its own box so the press can shrink it while the surface stays put. -->
{#if href}
<a
bind:this={ref}
data-slot="button"
class={classes}
href={disabled ? undefined : href}
aria-disabled={disabled}
role={disabled ? 'link' : undefined}
tabindex={disabled ? -1 : undefined}
{...restProps}
>
<span data-slot="button-content" class="cladd-button-content">{@render children?.()}</span>
</a>
{:else}
<button bind:this={ref} data-slot="button" class={classes} {type} {disabled} {...restProps}>
<span data-slot="button-content" class="cladd-button-content">{@render children?.()}</span>
</button>
{/if}
+21
View File
@@ -0,0 +1,21 @@
import Root, {
type ButtonProps,
type ButtonSize,
type ButtonVariant,
type CladdColor,
buttonVariants,
withColor
} from './button.svelte';
export {
Root,
type ButtonProps as Props,
//
Root as Button,
buttonVariants,
withColor,
type CladdColor,
type ButtonProps,
type ButtonSize,
type ButtonVariant
};
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-action"
class={cn('col-start-2 row-span-2 row-start-1 self-start justify-self-end', className)}
{...restProps}
>
{@render children?.()}
</div>
+15
View File
@@ -0,0 +1,15 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div bind:this={ref} data-slot="card-content" class={cn('px-4', className)} {...restProps}>
{@render children?.()}
</div>
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
</script>
<p
bind:this={ref}
data-slot="card-description"
class={cn('text-cladd-xs leading-relaxed text-cladd-fg-soft', className)}
{...restProps}
>
{@render children?.()}
</p>
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-footer"
class={cn('flex items-center gap-2 px-4 [.border-t]:pt-4', className)}
{...restProps}
>
{@render children?.()}
</div>
+23
View File
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-header"
class={cn(
'@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1 px-4 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-4',
className
)}
{...restProps}
>
{@render children?.()}
</div>
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card-title"
class={cn('text-cladd-sm leading-tight font-semibold', className)}
{...restProps}
>
{@render children?.()}
</div>
+23
View File
@@ -0,0 +1,23 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="card"
class={cn(
'cladd-surface cladd-outline flex flex-col gap-4 rounded-cladd-panel py-4 text-cladd-fg *:[form]:flex *:[form]:flex-col *:[form]:gap-4',
className
)}
{...restProps}
>
{@render children?.()}
</div>
+25
View File
@@ -0,0 +1,25 @@
import Root from './card.svelte';
import Content from './card-content.svelte';
import Description from './card-description.svelte';
import Footer from './card-footer.svelte';
import Header from './card-header.svelte';
import Title from './card-title.svelte';
import Action from './card-action.svelte';
export {
Root,
Content,
Description,
Footer,
Header,
Title,
Action,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction
};
+46
View File
@@ -0,0 +1,46 @@
<script lang="ts">
import { Checkbox as CheckboxPrimitive } from 'bits-ui';
import CheckIcon from '@lucide/svelte/icons/check';
import MinusIcon from '@lucide/svelte/icons/minus';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
...restProps
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
</script>
<CheckboxPrimitive.Root
bind:ref
data-slot="checkbox"
class={cn(
'peer group/checkbox cladd-surface cladd-gradient cladd-outline cladd-hoverable cladd-clickable cladd-focusable relative flex size-cladd-thumb-sm shrink-0 items-center justify-center rounded-full text-cladd-fg-soft disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:outline-destructive',
'cladd-fill-checked',
(checked || indeterminate) && 'cladd-color-brand',
className
)}
bind:checked
bind:indeterminate
{...restProps}
>
{#snippet children({ checked, indeterminate })}
<span
data-slot="checkbox-indicator"
class={cn(
'flex items-center justify-center transition-[scale,color] duration-200',
checked || indeterminate
? 'text-cladd-on-primary group-active/checkbox:scale-90'
: 'scale-75 group-active/checkbox:scale-65'
)}
>
{#if indeterminate}
<MinusIcon class="size-3" strokeWidth={3} />
{:else}
<CheckIcon class="size-3" strokeWidth={3} />
{/if}
</span>
{/snippet}
</CheckboxPrimitive.Root>
+6
View File
@@ -0,0 +1,6 @@
import Root from './checkbox.svelte';
export {
Root,
//
Root as Checkbox
};
@@ -0,0 +1,7 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.ContentProps = $props();
</script>
<CollapsiblePrimitive.Content bind:ref data-slot="collapsible-content" {...restProps} />
@@ -0,0 +1,7 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: CollapsiblePrimitive.TriggerProps = $props();
</script>
<CollapsiblePrimitive.Trigger bind:ref data-slot="collapsible-trigger" {...restProps} />
@@ -0,0 +1,11 @@
<script lang="ts">
import { Collapsible as CollapsiblePrimitive } from 'bits-ui';
let {
ref = $bindable(null),
open = $bindable(false),
...restProps
}: CollapsiblePrimitive.RootProps = $props();
</script>
<CollapsiblePrimitive.Root bind:ref bind:open data-slot="collapsible" {...restProps} />
+13
View File
@@ -0,0 +1,13 @@
import Root from './collapsible.svelte';
import Trigger from './collapsible-trigger.svelte';
import Content from './collapsible-content.svelte';
export {
Root,
Content,
Trigger,
//
Root as Collapsible,
Content as CollapsibleContent,
Trigger as CollapsibleTrigger
};
@@ -0,0 +1,38 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import CheckIcon from '@lucide/svelte/icons/check';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { Snippet } from 'svelte';
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
children: childrenProp,
...restProps
}: WithoutChildrenOrChild<ContextMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script>
<ContextMenuPrimitive.CheckboxItem
bind:ref
bind:checked
bind:indeterminate
data-slot="context-menu-checkbox-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full py-1 ps-9 pe-3 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft",
className
)}
{...restProps}
>
{#snippet children({ checked })}
<span class="pointer-events-none absolute start-3 flex size-4 items-center justify-center">
{#if checked}
<CheckIcon class="size-4" />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
</ContextMenuPrimitive.CheckboxItem>
@@ -0,0 +1,28 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
import ContextMenuPortal from './context-menu-portal.svelte';
import type { ComponentProps } from 'svelte';
import type { WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
portalProps,
class: className,
...restProps
}: ContextMenuPrimitive.ContentProps & {
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof ContextMenuPortal>>;
} = $props();
</script>
<ContextMenuPortal {...portalProps}>
<ContextMenuPrimitive.Content
bind:ref
data-slot="context-menu-content"
class={cn(
'cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 max-h-(--bits-context-menu-content-available-height) min-w-[10rem] origin-(--bits-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none',
className
)}
{...restProps}
/>
</ContextMenuPortal>
@@ -0,0 +1,21 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: ContextMenuPrimitive.GroupHeadingProps & {
inset?: boolean;
} = $props();
</script>
<ContextMenuPrimitive.GroupHeading
bind:ref
data-slot="context-menu-group-heading"
data-inset={inset}
class={cn('cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9', className)}
{...restProps}
/>
@@ -0,0 +1,7 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: ContextMenuPrimitive.GroupProps = $props();
</script>
<ContextMenuPrimitive.Group bind:ref data-slot="context-menu-group" {...restProps} />
@@ -0,0 +1,27 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
variant = 'default',
...restProps
}: ContextMenuPrimitive.ItemProps & {
inset?: boolean;
variant?: 'default' | 'destructive';
} = $props();
</script>
<ContextMenuPrimitive.Item
bind:ref
data-slot="context-menu-item"
data-inset={inset}
data-variant={variant}
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full px-3 py-1 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 data-[inset]:ps-8 data-[inset]:ps-9 data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/12 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft data-[variant=destructive]:*:[svg]:!text-destructive",
className
)}
{...restProps}
/>
@@ -0,0 +1,24 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
</script>
<div
bind:this={ref}
data-slot="context-menu-label"
data-inset={inset}
class={cn('cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,7 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
let { ...restProps }: ContextMenuPrimitive.PortalProps = $props();
</script>
<ContextMenuPrimitive.Portal {...restProps} />
@@ -0,0 +1,16 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
value = $bindable(''),
...restProps
}: ContextMenuPrimitive.RadioGroupProps = $props();
</script>
<ContextMenuPrimitive.RadioGroup
bind:ref
bind:value
data-slot="context-menu-radio-group"
{...restProps}
/>
@@ -0,0 +1,31 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import CircleIcon from '@lucide/svelte/icons/circle';
import { cn, type WithoutChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<ContextMenuPrimitive.RadioItemProps> = $props();
</script>
<ContextMenuPrimitive.RadioItem
bind:ref
data-slot="context-menu-radio-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full py-1 ps-9 pe-3 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft",
className
)}
{...restProps}
>
{#snippet children({ checked })}
<span class="pointer-events-none absolute start-3 flex size-4 items-center justify-center">
{#if checked}
<CircleIcon class="size-2 fill-current" />
{/if}
</span>
{@render childrenProp?.({ checked })}
{/snippet}
</ContextMenuPrimitive.RadioItem>
@@ -0,0 +1,17 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: ContextMenuPrimitive.SeparatorProps = $props();
</script>
<ContextMenuPrimitive.Separator
bind:ref
data-slot="context-menu-separator"
class={cn('-mx-2 my-2 h-px bg-cladd-bg-outline', className)}
{...restProps}
/>
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
data-slot="context-menu-shortcut"
class={cn('ms-auto font-mono text-cladd-2xs tracking-widest text-cladd-fg-softer', className)}
{...restProps}
>
{@render children?.()}
</span>
@@ -0,0 +1,20 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: ContextMenuPrimitive.SubContentProps = $props();
</script>
<ContextMenuPrimitive.SubContent
bind:ref
data-slot="context-menu-sub-content"
class={cn(
'cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 min-w-[10rem] origin-(--bits-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none',
className
)}
{...restProps}
/>
@@ -0,0 +1,29 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { cn, type WithoutChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithoutChild<ContextMenuPrimitive.SubTriggerProps> & {
inset?: boolean;
} = $props();
</script>
<ContextMenuPrimitive.SubTrigger
bind:ref
data-slot="context-menu-sub-trigger"
data-inset={inset}
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full px-3 py-1 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 data-[inset]:ps-8 data-[inset]:ps-9 data-[state=open]:bg-cladd-surface-next [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronRightIcon class="ms-auto" />
</ContextMenuPrimitive.SubTrigger>
@@ -0,0 +1,7 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: ContextMenuPrimitive.SubProps = $props();
</script>
<ContextMenuPrimitive.Sub bind:open {...restProps} />
@@ -0,0 +1,7 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: ContextMenuPrimitive.TriggerProps = $props();
</script>
<ContextMenuPrimitive.Trigger bind:ref data-slot="context-menu-trigger" {...restProps} />
@@ -0,0 +1,7 @@
<script lang="ts">
import { ContextMenu as ContextMenuPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: ContextMenuPrimitive.RootProps = $props();
</script>
<ContextMenuPrimitive.Root bind:open {...restProps} />
+52
View File
@@ -0,0 +1,52 @@
import Root from './context-menu.svelte';
import Sub from './context-menu-sub.svelte';
import Portal from './context-menu-portal.svelte';
import Trigger from './context-menu-trigger.svelte';
import Group from './context-menu-group.svelte';
import RadioGroup from './context-menu-radio-group.svelte';
import Item from './context-menu-item.svelte';
import GroupHeading from './context-menu-group-heading.svelte';
import Content from './context-menu-content.svelte';
import Shortcut from './context-menu-shortcut.svelte';
import RadioItem from './context-menu-radio-item.svelte';
import Separator from './context-menu-separator.svelte';
import SubContent from './context-menu-sub-content.svelte';
import SubTrigger from './context-menu-sub-trigger.svelte';
import CheckboxItem from './context-menu-checkbox-item.svelte';
import Label from './context-menu-label.svelte';
export {
Root,
Sub,
Portal,
Item,
GroupHeading,
Label,
Group,
Trigger,
Content,
Shortcut,
Separator,
RadioItem,
SubContent,
SubTrigger,
RadioGroup,
CheckboxItem,
//
Root as ContextMenu,
Sub as ContextMenuSub,
Portal as ContextMenuPortal,
Item as ContextMenuItem,
GroupHeading as ContextMenuGroupHeading,
Group as ContextMenuGroup,
Content as ContextMenuContent,
Trigger as ContextMenuTrigger,
Shortcut as ContextMenuShortcut,
RadioItem as ContextMenuRadioItem,
Separator as ContextMenuSeparator,
RadioGroup as ContextMenuRadioGroup,
SubContent as ContextMenuSubContent,
SubTrigger as ContextMenuSubTrigger,
CheckboxItem as ContextMenuCheckboxItem,
Label as ContextMenuLabel
};
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DialogPrimitive.CloseProps = $props();
</script>
<DialogPrimitive.Close bind:ref data-slot="dialog-close" {...restProps} />
+45
View File
@@ -0,0 +1,45 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import DialogPortal from './dialog-portal.svelte';
import XIcon from '@lucide/svelte/icons/x';
import type { Snippet } from 'svelte';
import * as Dialog from './index.js';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
class: className,
portalProps,
children,
showCloseButton = true,
...restProps
}: WithoutChildrenOrChild<DialogPrimitive.ContentProps> & {
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof DialogPortal>>;
children: Snippet;
showCloseButton?: boolean;
} = $props();
</script>
<DialogPortal {...portalProps}>
<Dialog.Overlay />
<DialogPrimitive.Content
bind:ref
data-slot="dialog-content"
class={cn(
'cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-dialog fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-cladd-dialog p-4 text-cladd-fg shadow-cladd-popover outline-none sm:max-w-lg',
className
)}
{...restProps}
>
{@render children?.()}
{#if showCloseButton}
<DialogPrimitive.Close
class="cladd-ghost cladd-hoverable cladd-clickable cladd-focusable absolute end-3 top-3 flex size-cladd-md items-center justify-center rounded-full text-cladd-fg-soft disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
>
<XIcon />
<span class="sr-only">Close</span>
</DialogPrimitive.Close>
{/if}
</DialogPrimitive.Content>
</DialogPortal>
@@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.DescriptionProps = $props();
</script>
<DialogPrimitive.Description
bind:ref
data-slot="dialog-description"
class={cn('text-cladd-sm leading-relaxed text-cladd-fg-soft', className)}
{...restProps}
/>
+23
View File
@@ -0,0 +1,23 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="dialog-footer"
class={cn(
'mt-2 flex flex-col-reverse gap-2 sm:flex-row sm:flex-wrap sm:items-center sm:justify-end',
className
)}
{...restProps}
>
{@render children?.()}
</div>
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
</script>
<div
bind:this={ref}
data-slot="dialog-header"
class={cn('flex flex-col gap-1.5 text-start', className)}
{...restProps}
>
{@render children?.()}
</div>
+17
View File
@@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.OverlayProps = $props();
</script>
<DialogPrimitive.Overlay
bind:ref
data-slot="dialog-overlay"
class={cn('cladd-backdrop cladd-anim-fade fixed inset-0 z-50', className)}
{...restProps}
/>
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
let { ...restProps }: DialogPrimitive.PortalProps = $props();
</script>
<DialogPrimitive.Portal {...restProps} />
+17
View File
@@ -0,0 +1,17 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DialogPrimitive.TitleProps = $props();
</script>
<DialogPrimitive.Title
bind:ref
data-slot="dialog-title"
class={cn('pe-8 text-cladd-md leading-tight font-semibold', className)}
{...restProps}
/>
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DialogPrimitive.TriggerProps = $props();
</script>
<DialogPrimitive.Trigger bind:ref data-slot="dialog-trigger" {...restProps} />
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { Dialog as DialogPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: DialogPrimitive.RootProps = $props();
</script>
<DialogPrimitive.Root bind:open {...restProps} />
+34
View File
@@ -0,0 +1,34 @@
import Root from './dialog.svelte';
import Portal from './dialog-portal.svelte';
import Title from './dialog-title.svelte';
import Footer from './dialog-footer.svelte';
import Header from './dialog-header.svelte';
import Overlay from './dialog-overlay.svelte';
import Content from './dialog-content.svelte';
import Description from './dialog-description.svelte';
import Trigger from './dialog-trigger.svelte';
import Close from './dialog-close.svelte';
export {
Root,
Title,
Portal,
Footer,
Header,
Trigger,
Overlay,
Content,
Description,
Close,
//
Root as Dialog,
Title as DialogTitle,
Portal as DialogPortal,
Footer as DialogFooter,
Header as DialogHeader,
Trigger as DialogTrigger,
Overlay as DialogOverlay,
Content as DialogContent,
Description as DialogDescription,
Close as DialogClose
};
@@ -0,0 +1,16 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
value = $bindable([]),
...restProps
}: DropdownMenuPrimitive.CheckboxGroupProps = $props();
</script>
<DropdownMenuPrimitive.CheckboxGroup
bind:ref
bind:value
data-slot="dropdown-menu-checkbox-group"
{...restProps}
/>
@@ -0,0 +1,41 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import CheckIcon from '@lucide/svelte/icons/check';
import MinusIcon from '@lucide/svelte/icons/minus';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import type { Snippet } from 'svelte';
let {
ref = $bindable(null),
checked = $bindable(false),
indeterminate = $bindable(false),
class: className,
children: childrenProp,
...restProps
}: WithoutChildrenOrChild<DropdownMenuPrimitive.CheckboxItemProps> & {
children?: Snippet;
} = $props();
</script>
<DropdownMenuPrimitive.CheckboxItem
bind:ref
bind:checked
bind:indeterminate
data-slot="dropdown-menu-checkbox-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full py-1 ps-9 pe-3 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft",
className
)}
{...restProps}
>
{#snippet children({ checked, indeterminate })}
<span class="pointer-events-none absolute start-3 flex size-4 items-center justify-center">
{#if indeterminate}
<MinusIcon class="size-4" />
{:else}
<CheckIcon class={cn('size-4', !checked && 'text-transparent')} />
{/if}
</span>
{@render childrenProp?.()}
{/snippet}
</DropdownMenuPrimitive.CheckboxItem>
@@ -0,0 +1,29 @@
<script lang="ts">
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
import DropdownMenuPortal from './dropdown-menu-portal.svelte';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
sideOffset = 4,
portalProps,
class: className,
...restProps
}: DropdownMenuPrimitive.ContentProps & {
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof DropdownMenuPortal>>;
} = $props();
</script>
<DropdownMenuPortal {...portalProps}>
<DropdownMenuPrimitive.Content
bind:ref
data-slot="dropdown-menu-content"
{sideOffset}
class={cn(
'cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 max-h-(--bits-dropdown-menu-content-available-height) min-w-[10rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none',
className
)}
{...restProps}
/>
</DropdownMenuPortal>
@@ -0,0 +1,22 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
class: className,
inset,
...restProps
}: ComponentProps<typeof DropdownMenuPrimitive.GroupHeading> & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.GroupHeading
bind:ref
data-slot="dropdown-menu-group-heading"
data-inset={inset}
class={cn('cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9', className)}
{...restProps}
/>
@@ -0,0 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.GroupProps = $props();
</script>
<DropdownMenuPrimitive.Group bind:ref data-slot="dropdown-menu-group" {...restProps} />
@@ -0,0 +1,28 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
class: className,
inset,
variant = 'default',
...restProps
}: DropdownMenuPrimitive.ItemProps & {
inset?: boolean;
variant?: 'default' | 'destructive';
asChild?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.Item
bind:ref
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full px-3 py-1 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 data-[inset]:ps-8 data-[inset]:ps-9 data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/12 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft data-[variant=destructive]:*:[svg]:!text-destructive",
className
)}
{...restProps}
/>
@@ -0,0 +1,24 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
inset?: boolean;
} = $props();
</script>
<div
bind:this={ref}
data-slot="dropdown-menu-label"
data-inset={inset}
class={cn('cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-8 data-[inset]:ps-9', className)}
{...restProps}
>
{@render children?.()}
</div>
@@ -0,0 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ...restProps }: DropdownMenuPrimitive.PortalProps = $props();
</script>
<DropdownMenuPrimitive.Portal {...restProps} />
@@ -0,0 +1,16 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let {
ref = $bindable(null),
value = $bindable(),
...restProps
}: DropdownMenuPrimitive.RadioGroupProps = $props();
</script>
<DropdownMenuPrimitive.RadioGroup
bind:ref
bind:value
data-slot="dropdown-menu-radio-group"
{...restProps}
/>
@@ -0,0 +1,31 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import CircleIcon from '@lucide/svelte/icons/circle';
import { cn, type WithoutChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children: childrenProp,
...restProps
}: WithoutChild<DropdownMenuPrimitive.RadioItemProps> = $props();
</script>
<DropdownMenuPrimitive.RadioItem
bind:ref
data-slot="dropdown-menu-radio-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full py-1 ps-9 pe-3 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft",
className
)}
{...restProps}
>
{#snippet children({ checked })}
<span class="pointer-events-none absolute start-3 flex size-4 items-center justify-center">
{#if checked}
<CircleIcon class="size-2 fill-current" />
{/if}
</span>
{@render childrenProp?.({ checked })}
{/snippet}
</DropdownMenuPrimitive.RadioItem>
@@ -0,0 +1,17 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SeparatorProps = $props();
</script>
<DropdownMenuPrimitive.Separator
bind:ref
data-slot="dropdown-menu-separator"
class={cn('-mx-2 my-2 h-px bg-cladd-bg-outline', className)}
{...restProps}
/>
@@ -0,0 +1,20 @@
<script lang="ts">
import type { HTMLAttributes } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLSpanElement>> = $props();
</script>
<span
bind:this={ref}
data-slot="dropdown-menu-shortcut"
class={cn('ms-auto font-mono text-cladd-2xs tracking-widest text-cladd-fg-softer', className)}
{...restProps}
>
{@render children?.()}
</span>
@@ -0,0 +1,20 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: DropdownMenuPrimitive.SubContentProps = $props();
</script>
<DropdownMenuPrimitive.SubContent
bind:ref
data-slot="dropdown-menu-sub-content"
class={cn(
'cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 min-w-[10rem] origin-(--bits-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-cladd-popover p-2 text-cladd-fg shadow-cladd-popover outline-none',
className
)}
{...restProps}
/>
@@ -0,0 +1,29 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
inset,
children,
...restProps
}: DropdownMenuPrimitive.SubTriggerProps & {
inset?: boolean;
} = $props();
</script>
<DropdownMenuPrimitive.SubTrigger
bind:ref
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full px-3 py-1 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 data-[inset]:ps-8 data-[inset]:ps-9 data-[state=open]:bg-cladd-surface-next [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft",
className
)}
{...restProps}
>
{@render children?.()}
<ChevronRightIcon class="ms-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
@@ -0,0 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: DropdownMenuPrimitive.SubProps = $props();
</script>
<DropdownMenuPrimitive.Sub bind:open {...restProps} />
@@ -0,0 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: DropdownMenuPrimitive.TriggerProps = $props();
</script>
<DropdownMenuPrimitive.Trigger bind:ref data-slot="dropdown-menu-trigger" {...restProps} />
@@ -0,0 +1,7 @@
<script lang="ts">
import { DropdownMenu as DropdownMenuPrimitive } from 'bits-ui';
let { open = $bindable(false), ...restProps }: DropdownMenuPrimitive.RootProps = $props();
</script>
<DropdownMenuPrimitive.Root bind:open {...restProps} />
+54
View File
@@ -0,0 +1,54 @@
import Root from './dropdown-menu.svelte';
import Sub from './dropdown-menu-sub.svelte';
import CheckboxGroup from './dropdown-menu-checkbox-group.svelte';
import CheckboxItem from './dropdown-menu-checkbox-item.svelte';
import Content from './dropdown-menu-content.svelte';
import Group from './dropdown-menu-group.svelte';
import Item from './dropdown-menu-item.svelte';
import Label from './dropdown-menu-label.svelte';
import RadioGroup from './dropdown-menu-radio-group.svelte';
import RadioItem from './dropdown-menu-radio-item.svelte';
import Separator from './dropdown-menu-separator.svelte';
import Shortcut from './dropdown-menu-shortcut.svelte';
import Trigger from './dropdown-menu-trigger.svelte';
import SubContent from './dropdown-menu-sub-content.svelte';
import SubTrigger from './dropdown-menu-sub-trigger.svelte';
import GroupHeading from './dropdown-menu-group-heading.svelte';
import Portal from './dropdown-menu-portal.svelte';
export {
CheckboxGroup,
CheckboxItem,
Content,
Portal,
Root as DropdownMenu,
CheckboxGroup as DropdownMenuCheckboxGroup,
CheckboxItem as DropdownMenuCheckboxItem,
Content as DropdownMenuContent,
Portal as DropdownMenuPortal,
Group as DropdownMenuGroup,
Item as DropdownMenuItem,
Label as DropdownMenuLabel,
RadioGroup as DropdownMenuRadioGroup,
RadioItem as DropdownMenuRadioItem,
Separator as DropdownMenuSeparator,
Shortcut as DropdownMenuShortcut,
Sub as DropdownMenuSub,
SubContent as DropdownMenuSubContent,
SubTrigger as DropdownMenuSubTrigger,
Trigger as DropdownMenuTrigger,
GroupHeading as DropdownMenuGroupHeading,
Group,
GroupHeading,
Item,
Label,
RadioGroup,
RadioItem,
Root,
Separator,
Shortcut,
Sub,
SubContent,
SubTrigger,
Trigger
};
+7
View File
@@ -0,0 +1,7 @@
import Root from './input.svelte';
export {
Root,
//
Root as Input
};
+48
View File
@@ -0,0 +1,48 @@
<script lang="ts">
import type { HTMLInputAttributes, HTMLInputTypeAttribute } from 'svelte/elements';
import { cn, type WithElementRef } from '$lib/utils.js';
type InputType = Exclude<HTMLInputTypeAttribute, 'file'>;
type Props = WithElementRef<
Omit<HTMLInputAttributes, 'type'> &
({ type: 'file'; files?: FileList } | { type?: InputType; files?: undefined })
>;
let {
ref = $bindable(null),
value = $bindable(),
type,
files = $bindable(),
class: className,
'data-slot': dataSlot = 'input',
...restProps
}: Props = $props();
</script>
{#if type === 'file'}
<input
bind:this={ref}
data-slot={dataSlot}
class={cn(
'cladd-cut cladd-hoverable cladd-focusable-input flex h-cladd-md w-full min-w-0 rounded-cladd-md px-2.5 pt-1.5 text-cladd-xs font-medium text-cladd-fg selection:bg-brand/30 file:me-2 file:font-semibold file:text-cladd-fg placeholder:text-cladd-fg-softer disabled:cursor-not-allowed disabled:opacity-50',
className
)}
type="file"
bind:files
bind:value
{...restProps}
/>
{:else}
<input
bind:this={ref}
data-slot={dataSlot}
class={cn(
'cladd-cut cladd-hoverable cladd-focusable-input flex h-cladd-md w-full min-w-0 rounded-cladd-md px-2.5 py-1 text-cladd-xs font-medium text-cladd-fg selection:bg-brand/30 placeholder:text-cladd-fg-softer disabled:cursor-not-allowed disabled:opacity-50',
className
)}
{type}
bind:value
{...restProps}
/>
{/if}
+3
View File
@@ -0,0 +1,3 @@
import Label from './label.svelte';
export { Label };
+22
View File
@@ -0,0 +1,22 @@
<script lang="ts">
import { Label as LabelPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: { ref?: any; class?: string; children?: any; [key: string]: any } = $props();
</script>
<LabelPrimitive.Root
bind:ref
class={cn(
'text-cladd-xs leading-none font-medium text-cladd-fg-soft peer-disabled:cursor-not-allowed peer-disabled:opacity-70',
className
)}
{...restProps}
>
{@render children?.()}
</LabelPrimitive.Root>
+7
View File
@@ -0,0 +1,7 @@
import Root from './progress.svelte';
export {
Root,
//
Root as Progress
};
+27
View File
@@ -0,0 +1,27 @@
<script lang="ts">
import { Progress as ProgressPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
max = 100,
value,
...restProps
}: WithoutChildrenOrChild<ProgressPrimitive.RootProps> = $props();
</script>
<ProgressPrimitive.Root
bind:ref
data-slot="progress"
class={cn('cladd-cut relative h-2 w-full overflow-hidden rounded-full', className)}
{value}
{max}
{...restProps}
>
<div
data-slot="progress-indicator"
class="cladd-surface cladd-gradient-fill cladd-color-brand h-full w-full flex-1 rounded-full transition-all duration-300 ease-out"
style="transform: translateX(-{100 - (100 * (value ?? 0)) / (max ?? 1)}%)"
></div>
</ProgressPrimitive.Root>
+37
View File
@@ -0,0 +1,37 @@
import Root from './select.svelte';
import Group from './select-group.svelte';
import Label from './select-label.svelte';
import Item from './select-item.svelte';
import Content from './select-content.svelte';
import Trigger from './select-trigger.svelte';
import Separator from './select-separator.svelte';
import ScrollDownButton from './select-scroll-down-button.svelte';
import ScrollUpButton from './select-scroll-up-button.svelte';
import GroupHeading from './select-group-heading.svelte';
import Portal from './select-portal.svelte';
export {
Root,
Group,
Label,
Item,
Content,
Trigger,
Separator,
ScrollDownButton,
ScrollUpButton,
GroupHeading,
Portal,
//
Root as Select,
Group as SelectGroup,
Label as SelectLabel,
Item as SelectItem,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator,
ScrollDownButton as SelectScrollDownButton,
ScrollUpButton as SelectScrollUpButton,
GroupHeading as SelectGroupHeading,
Portal as SelectPortal
};
+45
View File
@@ -0,0 +1,45 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
import SelectPortal from './select-portal.svelte';
import SelectScrollUpButton from './select-scroll-up-button.svelte';
import SelectScrollDownButton from './select-scroll-down-button.svelte';
import { cn, type WithoutChild } from '$lib/utils.js';
import type { ComponentProps } from 'svelte';
import type { WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
sideOffset = 4,
portalProps,
children,
preventScroll = true,
...restProps
}: WithoutChild<SelectPrimitive.ContentProps> & {
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof SelectPortal>>;
} = $props();
</script>
<SelectPortal {...portalProps}>
<SelectPrimitive.Content
bind:ref
{sideOffset}
{preventScroll}
data-slot="select-content"
class={cn(
'cladd-surface cladd-gradient cladd-outline cladd-level-1 cladd-anim-popover z-50 max-h-(--bits-select-content-available-height) min-w-[10rem] origin-(--bits-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-cladd-popover text-cladd-fg shadow-cladd-popover outline-none',
className
)}
{...restProps}
>
<SelectScrollUpButton />
<SelectPrimitive.Viewport
class={cn(
'h-(--bits-select-anchor-height) w-full min-w-(--bits-select-anchor-width) scroll-my-2 p-2'
)}
>
{@render children?.()}
</SelectPrimitive.Viewport>
<SelectScrollDownButton />
</SelectPrimitive.Content>
</SelectPortal>
@@ -0,0 +1,21 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
import { cn } from '$lib/utils.js';
import type { ComponentProps } from 'svelte';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: ComponentProps<typeof SelectPrimitive.GroupHeading> = $props();
</script>
<SelectPrimitive.GroupHeading
bind:ref
data-slot="select-group-heading"
class={cn('cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-9', className)}
{...restProps}
>
{@render children?.()}
</SelectPrimitive.GroupHeading>
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
let { ref = $bindable(null), ...restProps }: SelectPrimitive.GroupProps = $props();
</script>
<SelectPrimitive.Group bind:ref data-slot="select-group" {...restProps} />
+38
View File
@@ -0,0 +1,38 @@
<script lang="ts">
import CheckIcon from '@lucide/svelte/icons/check';
import { Select as SelectPrimitive } from 'bits-ui';
import { cn, type WithoutChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
value,
label,
children: childrenProp,
...restProps
}: WithoutChild<SelectPrimitive.ItemProps> = $props();
</script>
<SelectPrimitive.Item
bind:ref
{value}
data-slot="select-item"
class={cn(
"relative flex min-h-cladd-lg cursor-default items-center gap-2 rounded-full py-1 ps-3 pe-9 text-cladd-xs font-medium outline-hidden transition-[background-color,box-shadow] duration-200 select-none data-highlighted:bg-cladd-surface-next data-[disabled]:pointer-events-none data-[disabled]:opacity-40 data-[selected]:bg-cladd-surface-next data-[selected]:shadow-cladd-outline [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-cladd-fg-soft *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className
)}
{...restProps}
>
{#snippet children({ selected, highlighted })}
<span class="absolute end-3 flex size-4 items-center justify-center">
{#if selected}
<CheckIcon class="size-4 text-cladd-fg" />
{/if}
</span>
{#if childrenProp}
{@render childrenProp({ selected, highlighted })}
{:else}
{label || value}
{/if}
{/snippet}
</SelectPrimitive.Item>
+20
View File
@@ -0,0 +1,20 @@
<script lang="ts">
import { cn, type WithElementRef } from '$lib/utils.js';
import type { HTMLAttributes } from 'svelte/elements';
let {
ref = $bindable(null),
class: className,
children,
...restProps
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {} = $props();
</script>
<div
bind:this={ref}
data-slot="select-label"
class={cn('cladd-section-title px-3 pt-2 pb-1.5 data-[inset]:ps-9', className)}
{...restProps}
>
{@render children?.()}
</div>
+7
View File
@@ -0,0 +1,7 @@
<script lang="ts">
import { Select as SelectPrimitive } from 'bits-ui';
let { ...restProps }: SelectPrimitive.PortalProps = $props();
</script>
<SelectPrimitive.Portal {...restProps} />
@@ -0,0 +1,20 @@
<script lang="ts">
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
import { Select as SelectPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildrenOrChild<SelectPrimitive.ScrollDownButtonProps> = $props();
</script>
<SelectPrimitive.ScrollDownButton
bind:ref
data-slot="select-scroll-down-button"
class={cn('flex cursor-default items-center justify-center py-1 text-cladd-fg-softer', className)}
{...restProps}
>
<ChevronDownIcon class="size-4" />
</SelectPrimitive.ScrollDownButton>
@@ -0,0 +1,20 @@
<script lang="ts">
import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
import { Select as SelectPrimitive } from 'bits-ui';
import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: WithoutChildrenOrChild<SelectPrimitive.ScrollUpButtonProps> = $props();
</script>
<SelectPrimitive.ScrollUpButton
bind:ref
data-slot="select-scroll-up-button"
class={cn('flex cursor-default items-center justify-center py-1 text-cladd-fg-softer', className)}
{...restProps}
>
<ChevronUpIcon class="size-4" />
</SelectPrimitive.ScrollUpButton>
@@ -0,0 +1,18 @@
<script lang="ts">
import type { Separator as SeparatorPrimitive } from 'bits-ui';
import { Separator } from '$lib/components/ui/separator/index.js';
import { cn } from '$lib/utils.js';
let {
ref = $bindable(null),
class: className,
...restProps
}: SeparatorPrimitive.RootProps = $props();
</script>
<Separator
bind:ref
data-slot="select-separator"
class={cn('pointer-events-none -mx-2 my-2 h-px bg-cladd-bg-outline', className)}
{...restProps}
/>
Loaded 100 of 147 files, more files were not shown because too many files have changed in this diff. Show more