Compare commits
12 Commits
ae823b755e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 97bda53e9b | |||
| 8105f57be9 | |||
| 15565b7208 | |||
| d1c46cdee1 | |||
| d33b9c5467 | |||
| df0e78d9ba | |||
| cc596e632f | |||
|
|
7923514c5c | ||
|
|
7cd943cced | ||
|
|
0016e2104c | ||
|
|
752ca147ff | ||
|
|
56b57aa8de |
39
.gitea/workflows/publish.yml
Normal file
39
.gitea/workflows/publish.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Publish npm package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: x86
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Check tag matches package version
|
||||||
|
run: |
|
||||||
|
PACKAGE_VERSION="v$(node -p "require('./package.json').version")"
|
||||||
|
TAG_NAME="${GITHUB_REF_NAME:-${GITHUB_REF#refs/tags/}}"
|
||||||
|
test "$PACKAGE_VERSION" = "$TAG_NAME"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: bun run build
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: npm publish --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
11
README.md
11
README.md
@@ -1,12 +1,16 @@
|
|||||||
# create-glstack
|
# create-glstack
|
||||||
|
|
||||||
Scaffold a fullstack application with a Go backend, PostgreSQL database, and your choice of frontend framework, connected via Connect RPC.
|
Scaffold a fullstack application with a Go backend, PostgreSQL database, and your choice of web and mobile frontends, connected via Connect RPC.
|
||||||
|
|
||||||
Supported frontends:
|
Supported web frontends:
|
||||||
|
|
||||||
- [SvelteKit](https://kit.svelte.dev/)
|
- [SvelteKit](https://kit.svelte.dev/)
|
||||||
- [Solid Start](https://start.solidjs.com/)
|
- [Solid Start](https://start.solidjs.com/)
|
||||||
|
|
||||||
|
Supported mobile frontends:
|
||||||
|
|
||||||
|
- [React Native + Expo](https://expo.dev/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -18,7 +22,8 @@ bun create glstack
|
|||||||
A monorepo with the following structure:
|
A monorepo with the following structure:
|
||||||
|
|
||||||
- **`services/api`** -- Go backend using [Cobra](https://github.com/spf13/cobra) for CLI, [pgx](https://github.com/jackc/pgx) for Postgres, and [Connect RPC](https://connectrpc.com/) for the API layer
|
- **`services/api`** -- Go backend using [Cobra](https://github.com/spf13/cobra) for CLI, [pgx](https://github.com/jackc/pgx) for Postgres, and [Connect RPC](https://connectrpc.com/) for the API layer
|
||||||
- **`apps/web`** -- Frontend app (SvelteKit or Solid Start) with TailwindCSS and [TanStack Query](https://tanstack.com/query)
|
- **`apps/web`** -- Web frontend app (SvelteKit or Solid Start)
|
||||||
|
- **`apps/mobile`** -- Optional [React Native + Expo](https://expo.dev/) mobile app sharing the same RPC client
|
||||||
- **`packages/proto`** -- Protobuf service definitions with [Buf](https://buf.build/) for codegen (Go + TypeScript)
|
- **`packages/proto`** -- Protobuf service definitions with [Buf](https://buf.build/) for codegen (Go + TypeScript)
|
||||||
- **`packages/rpc`** -- Generated TypeScript Connect RPC client shared across frontend apps
|
- **`packages/rpc`** -- Generated TypeScript Connect RPC client shared across frontend apps
|
||||||
|
|
||||||
|
|||||||
4
bun.lock
4
bun.lock
@@ -6,7 +6,7 @@
|
|||||||
"name": "glstack",
|
"name": "glstack",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.2.0",
|
"@clack/prompts": "^1.2.0",
|
||||||
"@gregorlohaus/tdir": "^0.1.2",
|
"@gregorlohaus/tdir": "^0.2.0",
|
||||||
"zod": "^4.3.6",
|
"zod": "^4.3.6",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="],
|
"@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="],
|
||||||
|
|
||||||
"@gregorlohaus/tdir": ["@gregorlohaus/tdir@0.1.2", "", { "peerDependencies": { "zod": "^4" } }, "sha512-gEZg1+kwhMQQqEhMdyVBnHBrIWFMlvHt8C6sbPtY/PbbFo02odTBkpclfovIs2p4Awo0Hagzq3B6rpePGD/03w=="],
|
"@gregorlohaus/tdir": ["@gregorlohaus/tdir@0.2.0", "", { "peerDependencies": { "zod": "^4" }, "bin": { "tdir": "dist/cli.js" } }, "sha512-LwISQC7iARezu7SQZJk9TV8Sqp8I/fVMssH18lWL1KD/ETpWp2LwwTzm/j9aWcs+Eo5OBQctbqFTafo9Nfue+A=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
|
|||||||
23
index.ts
23
index.ts
@@ -23,7 +23,17 @@ const project = await p.group(
|
|||||||
message: 'Pick a frontend framework.',
|
message: 'Pick a frontend framework.',
|
||||||
options: [
|
options: [
|
||||||
{ value: "svelte-kit", label: "SvelteKit" },
|
{ value: "svelte-kit", label: "SvelteKit" },
|
||||||
{value: "solid-start", label:"SolidStart"}
|
{ value: "solid-start", label: "SolidStart" },
|
||||||
|
{ value: "none", label: "None" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
,
|
||||||
|
mobile: async () =>
|
||||||
|
await p.select({
|
||||||
|
message: 'Pick a mobile framework.',
|
||||||
|
options: [
|
||||||
|
{ value: "expo", label: "ReactNative + Expo" },
|
||||||
|
{ value: "none", label: "None" }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
,
|
,
|
||||||
@@ -36,6 +46,9 @@ const project = await p.group(
|
|||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
installDeps: async () => {
|
||||||
|
return await p.confirm({message: "Install dependencies?", })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
@@ -52,17 +65,17 @@ const render = createRenderer(z.object({
|
|||||||
name: z.string(),
|
name: z.string(),
|
||||||
goprefix: z.string(),
|
goprefix: z.string(),
|
||||||
frontend: z.string(),
|
frontend: z.string(),
|
||||||
|
mobile: z.string()
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
const destDir = path.join("./", project.name);
|
const destDir = path.join("./", project.name);
|
||||||
render(destDir,{project})
|
render(destDir, { project }, { reverseMap: true })
|
||||||
// TODO: template rendering — copy/generate files into destDir
|
|
||||||
|
|
||||||
const s = p.spinner();
|
const s = p.spinner();
|
||||||
|
if (project.installDeps) {
|
||||||
s.start("Installing dependencies");
|
s.start("Installing dependencies");
|
||||||
await Bun.$`bun install`.cwd(path.join(destDir)).quiet();
|
await Bun.$`bun install`.cwd(path.join(destDir)).quiet();
|
||||||
await Bun.$`bun install`.cwd(path.join(destDir,'packages','rpc')).quiet();
|
|
||||||
await Bun.$`bun install`.cwd(path.join(destDir,'apps','web')).quiet();
|
|
||||||
s.stop("Dependencies installed.");
|
s.stop("Dependencies installed.");
|
||||||
|
}
|
||||||
|
|
||||||
p.outro("You're all set!");
|
p.outro("You're all set!");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "create-glstack",
|
"name": "create-glstack",
|
||||||
"version": "0.0.4",
|
"version": "0.0.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.2.0",
|
"@clack/prompts": "^1.2.0",
|
||||||
"@gregorlohaus/tdir": "^0.1.2",
|
"@gregorlohaus/tdir": "^0.2.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
node_modules
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# SolidStart
|
|
||||||
|
|
||||||
Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com);
|
|
||||||
|
|
||||||
## Creating a project
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# create a new project in the current directory
|
|
||||||
npm init solid@latest
|
|
||||||
|
|
||||||
# create a new project in my-app
|
|
||||||
npm init solid@latest my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
|
||||||
npm run dev -- --open
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
Solid apps are built with _presets_, which optimise your project for deployment to different environments.
|
|
||||||
|
|
||||||
By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add it to the `devDependencies` in `package.json` and specify in your `app.config.js`.
|
|
||||||
|
|
||||||
## This project was created with the [Solid CLI](https://github.com/solidjs-community/solid-cli)
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "example-basic",
|
"name": "example-bare",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
@@ -9,16 +9,9 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@<@var(context.project.name)>/rpc": "workspace:*",
|
"@<@var(context.project.name)>/rpc": "workspace:*",
|
||||||
"@solidjs/meta": "^0.29.4",
|
|
||||||
"@solidjs/router": "^0.15.0",
|
|
||||||
"@solidjs/start": "2.0.0-alpha.2",
|
"@solidjs/start": "2.0.0-alpha.2",
|
||||||
"@solidjs/vite-plugin-nitro-2": "^0.1.0",
|
"@solidjs/vite-plugin-nitro-2": "^0.1.0",
|
||||||
"@tailwindcss/vite": "^4.2.2",
|
|
||||||
"@tanstack/query-db-collection": "^1.0.35",
|
|
||||||
"@tanstack/solid-db": "^0.2.18",
|
|
||||||
"@tanstack/solid-query": "^5.96.2",
|
|
||||||
"solid-js": "^1.9.5",
|
"solid-js": "^1.9.5",
|
||||||
"tailwindcss": "^4.2.2",
|
|
||||||
"vite": "^7.0.0"
|
"vite": "^7.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -1,5 +1,61 @@
|
|||||||
@import "tailwindcss";
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
font-family: Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #335d92;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: 100;
|
||||||
|
line-height: 1.1;
|
||||||
|
margin: 4rem auto;
|
||||||
|
max-width: 14rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
max-width: 14rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
h1 {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.increment {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 1em 2em;
|
||||||
|
color: #335d92;
|
||||||
|
background-color: rgba(68, 107, 158, 0.1);
|
||||||
|
border-radius: 2em;
|
||||||
|
border: 2px solid rgba(68, 107, 158, 0);
|
||||||
|
outline: none;
|
||||||
|
width: 200px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.increment:focus {
|
||||||
|
border: 2px solid #335d92;
|
||||||
|
}
|
||||||
|
|
||||||
|
.increment:active {
|
||||||
|
background-color: rgba(68, 107, 158, 0.2);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
import { MetaProvider, Title } from "@solidjs/meta";
|
import { createSignal, For } from "solid-js";
|
||||||
import { Router } from "@solidjs/router";
|
import { createRouter, Todo } from "@<@var(context.project.name)>/rpc"
|
||||||
import { FileRoutes } from "@solidjs/start/router";
|
import { createEffect } from 'solid-js'
|
||||||
import { Suspense } from "solid-js";
|
|
||||||
import { createCollection } from '@tanstack/solid-db'
|
|
||||||
import { queryCollectionOptions } from '@tanstack/query-db-collection'
|
|
||||||
import type { Todo, ExtractPayload } from '@<@var(context.project.name)>/rpc'
|
|
||||||
import { QueryClient, QueryClientProvider } from "@tanstack/solid-query"
|
|
||||||
import { getRouter } from "./lib/getRouter"
|
|
||||||
import "./app.css";
|
import "./app.css";
|
||||||
import { TodoCollectionProvider } from "./context/todocollection/provider";
|
|
||||||
const queryClient = new QueryClient({
|
|
||||||
|
|
||||||
})
|
|
||||||
const router = getRouter();
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const todosCollection = createCollection(
|
const router = createRouter("http://127.0.0.1:8080")
|
||||||
queryCollectionOptions({
|
const [todos, setTodos] = createSignal<Todo[]>([]);
|
||||||
queryKey: ["todos"],
|
const [todoToCreateTask, setTodoToCreateTask] = createSignal<string>("");
|
||||||
queryFn: async () => {
|
const fetchTodos = () => {
|
||||||
const todos = await router.todos.listTodos({})
|
router.todos.listTodos({}).then((r) => {
|
||||||
return todos.todos as ExtractPayload<Todo>[];
|
setTodos(r.todos)
|
||||||
},
|
|
||||||
queryClient,
|
|
||||||
getKey: (item) => item.id ? item.id : crypto.randomUUID(),
|
|
||||||
onInsert: async ({ transaction }) => {
|
|
||||||
Promise.all(transaction.mutations.map((m) => {
|
|
||||||
router.todos.createTodo({ todo: m.modified })
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
onDelete: async ({ transaction }) => {
|
|
||||||
Promise.all(transaction.mutations.map((m) => {
|
|
||||||
router.todos.deleteTodo({ todo: m.modified })
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
onUpdate: async ({ transaction }) => {
|
|
||||||
Promise.all(transaction.mutations.map((m) => {
|
|
||||||
router.todos.updateTodo({ todo: m.modified })
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
)
|
}
|
||||||
|
createEffect(() => {
|
||||||
|
fetchTodos()
|
||||||
|
})
|
||||||
|
|
||||||
|
const setTodoDone = (id: string, task: string) => {
|
||||||
|
return async (event : Event & { currentTarget: HTMLInputElement }) => {
|
||||||
|
await router.todos.updateTodo({ todo: { id, task, done: event.currentTarget.checked } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTodo = (id: string) => {
|
||||||
|
return async () => {
|
||||||
|
await router.todos.deleteTodo({ todo: { id } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTodo = () => {
|
||||||
|
router.todos.createTodo({ todo: { id: crypto.randomUUID(), task: todoToCreateTask() } }).then((r) => {
|
||||||
|
console.log(r)
|
||||||
|
fetchTodos()
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router
|
<div>
|
||||||
root={props => (
|
<p>Create Todo</p>
|
||||||
<MetaProvider>
|
<input value={todoToCreateTask()} onInput={(e) => setTodoToCreateTask(e.currentTarget.value)} type="text"/>
|
||||||
<QueryClientProvider client={queryClient} >
|
<button onclick={createTodo}> create </button>
|
||||||
<TodoCollectionProvider value={todosCollection}>
|
<For each={todos()}>
|
||||||
<Suspense>{props.children}</Suspense>
|
{(item:Todo) =>
|
||||||
</TodoCollectionProvider>
|
<div>
|
||||||
</QueryClientProvider>
|
{item.task}
|
||||||
</MetaProvider>
|
<input type='checkbox' checked={item.done} onChange={setTodoDone(item.id || "",item.task)}/>
|
||||||
)}
|
<button onclick={deleteTodo(item.id || "")}>delete</button>
|
||||||
>
|
</div>
|
||||||
<FileRoutes/>
|
}
|
||||||
</Router>
|
</For>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
import { ExtractPayload, type Todo } from '@<@var(context.project.name)>/rpc'
|
|
||||||
import { createSignal } from 'solid-js';
|
|
||||||
import { useTodoCollection } from '~/context/todocollection/create';
|
|
||||||
|
|
||||||
export const CreateTodo = () => {
|
|
||||||
const [todoState, setTodoState] = createSignal<ExtractPayload<Todo>>({
|
|
||||||
id: crypto.randomUUID(),
|
|
||||||
task: "",
|
|
||||||
done: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
let inputRef!: HTMLInputElement
|
|
||||||
|
|
||||||
const setTask = () => {
|
|
||||||
const todo = {...todoState()}
|
|
||||||
todo.task = inputRef.value;
|
|
||||||
setTodoState(todo)
|
|
||||||
}
|
|
||||||
|
|
||||||
const todoCollection = useTodoCollection()
|
|
||||||
|
|
||||||
const insertTodo = () => {
|
|
||||||
todoCollection.insert(todoState())
|
|
||||||
setTodoState({
|
|
||||||
id: crypto.randomUUID(),
|
|
||||||
task: "",
|
|
||||||
done: false
|
|
||||||
})
|
|
||||||
inputRef.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<input class="border rounded-md px-3 py-1" ref={inputRef} type='text' onInput={setTask} />
|
|
||||||
<button class="bg-teal-800 text-teal-100 p-1 rounded-md cursor-pointer" onClick={insertTodo}> create </button>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { useLiveQuery } from "@tanstack/solid-db";
|
|
||||||
import { useTodoCollection } from "~/context/todocollection/create";
|
|
||||||
import { Todo } from './Todo'
|
|
||||||
import { For } from 'solid-js'
|
|
||||||
|
|
||||||
export const ListTodos = () => {
|
|
||||||
const todoCollection = useTodoCollection()
|
|
||||||
const data = useLiveQuery((q) =>
|
|
||||||
q.from({todos: todoCollection})
|
|
||||||
)
|
|
||||||
const todos = () => {
|
|
||||||
const items = Array.from(data.state.values());
|
|
||||||
return items.sort((a: any, b: any) => {
|
|
||||||
if (a.done != b.done) return a.done ? -1 : 1
|
|
||||||
if (!a.done && !b.done) {
|
|
||||||
const adate = a.createdAt ? new Date(a.createdAt) : new Date()
|
|
||||||
const bdate = b.createdAt ? new Date(b.createdAt) : new Date()
|
|
||||||
return bdate.getTime() - adate.getTime()
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<For each={todos()} fallback={<div>Loading...</div>}>
|
|
||||||
{(item) => <Todo todo={item} />}
|
|
||||||
</For>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
import { ExtractPayload, type Todo as RpcTodo } from '@<@var(context.project.name)>/rpc'
|
|
||||||
import { createSignal, type JSX } from 'solid-js';
|
|
||||||
import { useTodoCollection } from '~/context/todocollection/create';
|
|
||||||
|
|
||||||
export const Todo = ({ todo }: { todo: ExtractPayload<RpcTodo> }) => {
|
|
||||||
const [todoState, setTodoState] = createSignal(todo)
|
|
||||||
const todoCollection = useTodoCollection()
|
|
||||||
let commitUpdateTimeoutId: NodeJS.Timeout|null = null;
|
|
||||||
const updateTask: JSX.EventHandlerUnion<HTMLInputElement, Event> = (e) => {
|
|
||||||
if (commitUpdateTimeoutId != null) {
|
|
||||||
clearTimeout(commitUpdateTimeoutId)
|
|
||||||
}
|
|
||||||
const todo = { ...todoState() };
|
|
||||||
todo.task = e.currentTarget.value
|
|
||||||
setTodoState(todo)
|
|
||||||
commitUpdateTimeoutId = setTimeout(() => {
|
|
||||||
todoCollection.update(todoState().id, (draft) => {
|
|
||||||
draft.task = todoState().task
|
|
||||||
})
|
|
||||||
},3000)
|
|
||||||
|
|
||||||
}
|
|
||||||
const updateDone: JSX.EventHandlerUnion<HTMLInputElement, Event> = (e) => {
|
|
||||||
const todo = { ...todoState() }
|
|
||||||
todo.done = e.currentTarget.checked
|
|
||||||
setTodoState(todo)
|
|
||||||
todoCollection.update(todoState().id, (draft) => {
|
|
||||||
draft.done = todoState().done
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const del = () => {
|
|
||||||
todoCollection.delete(todoState().id || "")
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div class="flex flex-col items-center border rounded-md p-5 gap-2">
|
|
||||||
<input class="text-center border rounded-md px-3 py-1" oninput={updateTask} type="text" value={todoState().task} />
|
|
||||||
<span> {(new Date(todoState().createdAt || "")).toLocaleString()}</span>
|
|
||||||
<span> {(new Date(todoState().updatesAt || "")).toLocaleString()}</span>
|
|
||||||
<input type="checkbox" onchange={updateDone} checked={todoState().done} />
|
|
||||||
<button class="bg-amber-800 text-amber-100 p-1 rounded-md cursor-pointer" onclick={del} class=""> delete </button>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import { createContext,useContext } from 'solid-js'
|
|
||||||
import { type CollectionImpl, type CollectionLike } from '@tanstack/solid-db'
|
|
||||||
import type { ExtractPayload, Todo } from '@<@var(context.project.name)>/rpc';
|
|
||||||
export type TodoCollection = CollectionImpl<ExtractPayload<Todo>>
|
|
||||||
export const TodoCollectionContext = createContext<TodoCollection>()
|
|
||||||
export const useTodoCollection = () => {
|
|
||||||
const col = useContext(TodoCollectionContext)
|
|
||||||
if (col === undefined) {
|
|
||||||
throw new Error("Todo collection has not been initialized")
|
|
||||||
}
|
|
||||||
return col;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import { JSXElement } from 'solid-js';
|
|
||||||
import { TodoCollection, TodoCollectionContext } from './create'
|
|
||||||
|
|
||||||
export const TodoCollectionProvider = (props : {children:JSXElement, value: TodoCollection}) => {
|
|
||||||
return (
|
|
||||||
<TodoCollectionContext.Provider value={props.value}> {props.children} </TodoCollectionContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
import { createRouter } from "@<@var(context.project.name)>/rpc"
|
|
||||||
|
|
||||||
const router = createRouter("http://127.0.0.1:8080")
|
|
||||||
|
|
||||||
export const getRouter = () => {
|
|
||||||
return router;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { Title } from "@solidjs/meta";
|
|
||||||
import { HttpStatusCode } from "@solidjs/start";
|
|
||||||
|
|
||||||
export default function NotFound() {
|
|
||||||
return (
|
|
||||||
<main>
|
|
||||||
<Title>Not Found</Title>
|
|
||||||
<HttpStatusCode code={404} />
|
|
||||||
<h1>Page Not Found</h1>
|
|
||||||
<p>
|
|
||||||
Visit{" "}
|
|
||||||
<a href="https://start.solidjs.com" target="_blank">
|
|
||||||
start.solidjs.com
|
|
||||||
</a>{" "}
|
|
||||||
to learn how to build SolidStart apps.
|
|
||||||
</p>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { Title } from "@solidjs/meta";
|
|
||||||
import { CreateTodo } from "~/components/CreateTodo";
|
|
||||||
import { ListTodos } from "~/components/ListTodos"
|
|
||||||
export default function Home() {
|
|
||||||
return (
|
|
||||||
<main class="flex flex-col items-center gap-2 py-5">
|
|
||||||
<Title>Todos</Title>
|
|
||||||
<h1 class="text-5xl"> Todos </h1>
|
|
||||||
<CreateTodo/>
|
|
||||||
<ListTodos/>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +1,10 @@
|
|||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
import { nitroV2Plugin as nitro } from "@solidjs/vite-plugin-nitro-2";
|
import { nitroV2Plugin as nitro } from "@solidjs/vite-plugin-nitro-2";
|
||||||
import tailwindcss from '@tailwindcss/vite'
|
|
||||||
import { solidStart } from "@solidjs/start/config";
|
import { solidStart } from "@solidjs/start/config";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [solidStart(),
|
||||||
solidStart(),
|
nitro()
|
||||||
nitro(),
|
|
||||||
tailwindcss()
|
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,6 +21,3 @@ Thumbs.db
|
|||||||
# Vite
|
# Vite
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
# Paraglide
|
|
||||||
src/lib/paraglide
|
|
||||||
project.inlang/cache/
|
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
node_modules
|
|
||||||
|
|
||||||
# Output
|
|
||||||
.output
|
|
||||||
.vercel
|
|
||||||
.netlify
|
|
||||||
.wrangler
|
|
||||||
/.svelte-kit
|
|
||||||
/build
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Env
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
!.env.test
|
|
||||||
|
|
||||||
# Vite
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
# Paraglide
|
|
||||||
src/lib/paraglide
|
|
||||||
project.inlang/cache/
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Package Managers
|
|
||||||
package-lock.json
|
|
||||||
pnpm-lock.yaml
|
|
||||||
yarn.lock
|
|
||||||
bun.lock
|
|
||||||
bun.lockb
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
/static/
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"printWidth": 100,
|
|
||||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.svelte",
|
|
||||||
"options": {
|
|
||||||
"parser": "svelte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tailwindStylesheet": "./src/routes/layout.css"
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode", "bradlc.vscode-tailwindcss"]
|
"recommendations": ["svelte.svelte-vscode"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.css": "tailwindcss"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# sv
|
|
||||||
|
|
||||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
|
||||||
|
|
||||||
## Creating a project
|
|
||||||
|
|
||||||
If you're seeing this, you've probably already done this step. Congrats!
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# create a new project
|
|
||||||
npx sv create my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
To recreate this project with the same configuration:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# recreate this project
|
|
||||||
bun x sv@0.14.0 create --template minimal --types ts --add prettier tailwindcss="plugins:typography" paraglide="languageTags:en, de-De+demo:no" --install bun web
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
|
||||||
npm run dev -- --open
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
To create a production version of your app:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
|
||||||
"tailwind": {
|
|
||||||
"css": "src/routes/layout.css",
|
|
||||||
"baseColor": "mist"
|
|
||||||
},
|
|
||||||
"aliases": {
|
|
||||||
"components": "$lib/components",
|
|
||||||
"utils": "$lib/utils",
|
|
||||||
"ui": "$lib/components/ui",
|
|
||||||
"hooks": "$lib/hooks",
|
|
||||||
"lib": "$lib"
|
|
||||||
},
|
|
||||||
"typescript": true,
|
|
||||||
"registry": "https://shadcn-svelte.com/registry",
|
|
||||||
"style": "lyra",
|
|
||||||
"iconLibrary": "lucide",
|
|
||||||
"menuColor": "default",
|
|
||||||
"menuAccent": "subtle"
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
||||||
"hello_world": "Hello, {name} from de-de!"
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
||||||
"hello_world": "Hello, {name} from en!"
|
|
||||||
}
|
|
||||||
@@ -9,51 +9,18 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"prepare": "svelte-kit sync || echo ''",
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
"lint": "prettier --check .",
|
|
||||||
"format": "prettier --write ."
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fontsource-variable/roboto": "^5.2.10",
|
"@sveltejs/adapter-auto": "^7.0.1",
|
||||||
"@inlang/paraglide-js": "^2.10.0",
|
"@sveltejs/kit": "^2.57.0",
|
||||||
"@internationalized/date": "^3.12.0",
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
"@lucide/svelte": "^0.577.0",
|
"svelte": "^5.55.2",
|
||||||
"@sveltejs/adapter-auto": "^7.0.0",
|
"svelte-check": "^4.4.6",
|
||||||
"@sveltejs/kit": "^2.50.2",
|
"typescript": "^6.0.2",
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
"vite": "^8.0.7"
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
|
||||||
"@tanstack/table-core": "^8.21.3",
|
|
||||||
"bits-ui": "^2.16.3",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"embla-carousel-svelte": "^8.6.0",
|
|
||||||
"formsnap": "^2.0.1",
|
|
||||||
"layerchart": "2.0.0-next.48",
|
|
||||||
"mode-watcher": "^1.1.0",
|
|
||||||
"paneforge": "^1.0.2",
|
|
||||||
"prettier": "^3.8.1",
|
|
||||||
"prettier-plugin-svelte": "^3.4.1",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
||||||
"shadcn-svelte": "^1.2.7",
|
|
||||||
"svelte": "^5.54.0",
|
|
||||||
"svelte-check": "^4.4.2",
|
|
||||||
"svelte-sonner": "^1.1.0",
|
|
||||||
"sveltekit-superforms": "^2.30.0",
|
|
||||||
"tailwind-merge": "^3.5.0",
|
|
||||||
"tailwind-variants": "^3.2.2",
|
|
||||||
"tailwindcss": "^4.1.18",
|
|
||||||
"tw-animate-css": "^1.4.0",
|
|
||||||
"typescript": "^5.9.3",
|
|
||||||
"vaul-svelte": "^1.0.0-next.7",
|
|
||||||
"vite": "^7.3.1"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@bufbuild/protobuf": "^2.11.0",
|
"@<@var(context.project.name)>/rpc": "workspace:*"
|
||||||
"@connectrpc/connect": "^2.1.1",
|
|
||||||
"@connectrpc/connect-web": "^2.1.1",
|
|
||||||
"@<@var(context.project.name)>/rpc": "workspace:*",
|
|
||||||
"@tanstack/query-db-collection": "^1.0.33",
|
|
||||||
"@tanstack/svelte-db": "^0.1.79",
|
|
||||||
"@tanstack/svelte-query": "^6.1.13"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/project-settings",
|
|
||||||
"modules": [
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
|
|
||||||
],
|
|
||||||
"plugin.inlang.messageFormat": {
|
|
||||||
"pathPattern": "./messages/{locale}.json"
|
|
||||||
},
|
|
||||||
"baseLocale": "en",
|
|
||||||
"locales": ["en", "de-de"]
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
<html lang="%paraglide.lang%" dir="%paraglide.dir%">
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
<meta name="text-scale" content="scale" />
|
<meta name="text-scale" content="scale" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import type { Handle } from '@sveltejs/kit';
|
|
||||||
import { getTextDirection } from '$lib/paraglide/runtime';
|
|
||||||
import { paraglideMiddleware } from '$lib/paraglide/server';
|
|
||||||
|
|
||||||
const handleParaglide: Handle = ({ event, resolve }) =>
|
|
||||||
paraglideMiddleware(event.request, ({ request, locale }) => {
|
|
||||||
event.request = request;
|
|
||||||
|
|
||||||
return resolve(event, {
|
|
||||||
transformPageChunk: ({ html }) =>
|
|
||||||
html
|
|
||||||
.replace('%paraglide.lang%', locale)
|
|
||||||
.replace('%paraglide.dir%', getTextDirection(locale))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
export const handle: Handle = handleParaglide;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import type { Reroute } from '@sveltejs/kit';
|
|
||||||
import { deLocalizeUrl } from '$lib/paraglide/runtime';
|
|
||||||
|
|
||||||
export const reroute: Reroute = (request) => deLocalizeUrl(request.url).pathname;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { Todo, ExtractPayload } from "@<@var(context.project.name)>/rpc";
|
|
||||||
import Input from "$lib/components/ui/input/input.svelte";
|
|
||||||
import Button from "$lib/components/ui/button/button.svelte"
|
|
||||||
import * as Field from "$lib/components/ui/field/index"
|
|
||||||
import { getTodoCollection } from "$lib/todocollectionscontext";
|
|
||||||
const todoCollection = getTodoCollection();
|
|
||||||
let todo = $state<ExtractPayload<Todo>>({id: crypto.randomUUID() ,task: "",done:false})
|
|
||||||
let create = () => {
|
|
||||||
todoCollection.insert(todo)
|
|
||||||
todo = { id: crypto.randomUUID(), task: "", done:false }
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex flex-col items-center justify-center">
|
|
||||||
<Field.FieldGroup class="w-50">
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
Todo
|
|
||||||
</Field.Label>
|
|
||||||
<Input bind:value={todo.task}/>
|
|
||||||
</Field.Field>
|
|
||||||
<Button variant="outline" onclick={create} > Create </Button>
|
|
||||||
</Field.FieldGroup>
|
|
||||||
</div>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { getTodoCollection } from '$lib/todocollectionscontext';
|
|
||||||
import { useLiveQuery } from '@tanstack/svelte-db';
|
|
||||||
import { tick } from 'svelte';
|
|
||||||
import { ScrollArea } from '../ui/scroll-area/index';
|
|
||||||
import type { Todo as TodoRpc } from '@<@var(context.project.name)>/rpc';
|
|
||||||
import { browser } from '$app/environment';
|
|
||||||
import Todo from './Todo.svelte';
|
|
||||||
|
|
||||||
let { initialTodos }: { initialTodos: TodoRpc[] } = $props();
|
|
||||||
const todoCollection = getTodoCollection();
|
|
||||||
const data = useLiveQuery((q) => q.from({ todos: todoCollection }));
|
|
||||||
const todos = $derived.by(() => {
|
|
||||||
const res = data.data.toSorted((a, b) => {
|
|
||||||
if (a.done != b.done) {
|
|
||||||
return a.done ? -1 : 1;
|
|
||||||
}
|
|
||||||
if (!a.done && !b.done) {
|
|
||||||
let adate = a.createdAt ? new Date(a.createdAt) : new Date(Date.now());
|
|
||||||
let bdate = b.createdAt ? new Date(b.createdAt) : new Date(Date.now());
|
|
||||||
return bdate.getTime() - adate.getTime();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
let scrollerRef = $state<HTMLElement | null>(null);
|
|
||||||
$effect(() => {
|
|
||||||
todos;
|
|
||||||
tick().then(() => {
|
|
||||||
scrollerRef?.scrollTo({ top: scrollerRef.scrollHeight, behavior: 'smooth' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ScrollArea
|
|
||||||
bind:viewportRef={scrollerRef}
|
|
||||||
id="scroller"
|
|
||||||
class="flex h-[80%] w-full flex-col items-center justify-center lg:w-[30%]"
|
|
||||||
>
|
|
||||||
{#if browser}
|
|
||||||
{#each todos as todo (todo.id)}
|
|
||||||
<Todo {todo} />
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
{#each initialTodos as todo}
|
|
||||||
<Todo {todo} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</ScrollArea>
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { Todo, ExtractPayload } from "@<@var(context.project.name)>/rpc";
|
|
||||||
import { getTodoCollection } from "$lib/todocollectionscontext";
|
|
||||||
import * as Card from "../ui/card/index";
|
|
||||||
import * as Field from "../ui/field/index";
|
|
||||||
import Input from "../ui/input/input.svelte";
|
|
||||||
import Checkbox from "../ui/checkbox/checkbox.svelte";
|
|
||||||
import Button from "../ui/button/button.svelte";
|
|
||||||
import { DeleteIcon } from "@lucide/svelte"
|
|
||||||
let { todo } : { todo:ExtractPayload<Todo> } = $props();
|
|
||||||
let todoState = $state(todo)
|
|
||||||
const todoCollection = getTodoCollection();
|
|
||||||
const update = () => {
|
|
||||||
todoCollection.update(todoState.id,(draft) => {
|
|
||||||
draft.done = todoState.done;
|
|
||||||
draft.task = todoState.task;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const del = () => {
|
|
||||||
if (todoState.id) {
|
|
||||||
todoCollection.delete(todoState.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Card.Card class="p-5" onkeydown={()=>update()}>
|
|
||||||
<Card.CardContent>
|
|
||||||
<Field.FieldSet>
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
task
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent>
|
|
||||||
<Input bind:value={todoState.task}/>
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
done
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent onclick={()=>update()}>
|
|
||||||
<Checkbox bind:checked={todoState.done}/>
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
Created
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent>
|
|
||||||
{(new Date(todoState.createdAt||"now")).toLocaleString()}
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
Updated
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent>
|
|
||||||
{(new Date(todoState.updatesAt||"now")).toLocaleString()}
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<Button variant='destructive' onclick={del}>
|
|
||||||
<DeleteIcon/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Field.FieldSet>
|
|
||||||
</Card.CardContent>
|
|
||||||
</Card.Card>
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn, type WithoutChild } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
data-slot="accordion-content"
|
|
||||||
class="data-open:animate-accordion-down data-closed:animate-accordion-up text-xs overflow-hidden"
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class={cn(
|
|
||||||
"pt-0 pb-2.5 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
</AccordionPrimitive.Content>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AccordionPrimitive.ItemProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Item
|
|
||||||
bind:ref
|
|
||||||
data-slot="accordion-item"
|
|
||||||
class={cn("not-last:border-b", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn, type WithoutChild } from "$lib/utils.js";
|
|
||||||
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
|
||||||
import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
level = 3,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AccordionPrimitive.TriggerProps> & {
|
|
||||||
level?: AccordionPrimitive.HeaderProps["level"];
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Header {level} class="flex">
|
|
||||||
<AccordionPrimitive.Trigger
|
|
||||||
data-slot="accordion-trigger"
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-none py-2.5 text-left text-xs font-medium hover:underline focus-visible:ring-1 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
<ChevronDownIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
|
|
||||||
<ChevronUpIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
|
|
||||||
</AccordionPrimitive.Trigger>
|
|
||||||
</AccordionPrimitive.Header>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
value = $bindable(),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AccordionPrimitive.RootProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Root
|
|
||||||
bind:ref
|
|
||||||
bind:value={value as never}
|
|
||||||
data-slot="accordion"
|
|
||||||
class={cn("cn-accordion flex w-full flex-col", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import Root from "./accordion.svelte";
|
|
||||||
import Content from "./accordion-content.svelte";
|
|
||||||
import Item from "./accordion-item.svelte";
|
|
||||||
import Trigger from "./accordion-trigger.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Content,
|
|
||||||
Item,
|
|
||||||
Trigger,
|
|
||||||
//
|
|
||||||
Root as Accordion,
|
|
||||||
Content as AccordionContent,
|
|
||||||
Item as AccordionItem,
|
|
||||||
Trigger as AccordionTrigger,
|
|
||||||
};
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import {
|
|
||||||
buttonVariants,
|
|
||||||
type ButtonVariant,
|
|
||||||
type ButtonSize,
|
|
||||||
} from "$lib/components/ui/button/index.js";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
variant = "default",
|
|
||||||
size = "default",
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.ActionProps & {
|
|
||||||
variant?: ButtonVariant;
|
|
||||||
size?: ButtonSize;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Action
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-action"
|
|
||||||
class={cn(buttonVariants({ variant, size }), "cn-alert-dialog-action", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import {
|
|
||||||
buttonVariants,
|
|
||||||
type ButtonVariant,
|
|
||||||
type ButtonSize,
|
|
||||||
} from "$lib/components/ui/button/index.js";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
variant = "outline",
|
|
||||||
size = "default",
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.CancelProps & {
|
|
||||||
variant?: ButtonVariant;
|
|
||||||
size?: ButtonSize;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Cancel
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-cancel"
|
|
||||||
class={cn(buttonVariants({ variant, size }), "cn-alert-dialog-cancel", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import AlertDialogPortal from "./alert-dialog-portal.svelte";
|
|
||||||
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
|
||||||
import { cn, type WithoutChild, type WithoutChildrenOrChild } from "$lib/utils.js";
|
|
||||||
import type { ComponentProps } from "svelte";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
size = "default",
|
|
||||||
portalProps,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
|
|
||||||
size?: "default" | "sm";
|
|
||||||
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPortal {...portalProps}>
|
|
||||||
<AlertDialogOverlay />
|
|
||||||
<AlertDialogPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-content"
|
|
||||||
data-size={size}
|
|
||||||
class={cn(
|
|
||||||
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-popover text-popover-foreground ring-foreground/10 gap-4 rounded-none p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
</AlertDialogPortal>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.DescriptionProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Description
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-description"
|
|
||||||
class={cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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="alert-dialog-footer"
|
|
||||||
class={cn(
|
|
||||||
"cn-alert-dialog-footer flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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="alert-dialog-header"
|
|
||||||
class={cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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="alert-dialog-media"
|
|
||||||
class={cn("bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-none sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.OverlayProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Overlay
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-overlay"
|
|
||||||
class={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { ...restProps }: AlertDialogPrimitive.PortalProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Portal {...restProps} />
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.TitleProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Title
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-title"
|
|
||||||
class={cn("text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { open = $bindable(false), ...restProps }: AlertDialogPrimitive.RootProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Root bind:open {...restProps} />
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import Root from "./alert-dialog.svelte";
|
|
||||||
import Portal from "./alert-dialog-portal.svelte";
|
|
||||||
import Trigger from "./alert-dialog-trigger.svelte";
|
|
||||||
import Title from "./alert-dialog-title.svelte";
|
|
||||||
import Action from "./alert-dialog-action.svelte";
|
|
||||||
import Cancel from "./alert-dialog-cancel.svelte";
|
|
||||||
import Footer from "./alert-dialog-footer.svelte";
|
|
||||||
import Header from "./alert-dialog-header.svelte";
|
|
||||||
import Overlay from "./alert-dialog-overlay.svelte";
|
|
||||||
import Content from "./alert-dialog-content.svelte";
|
|
||||||
import Description from "./alert-dialog-description.svelte";
|
|
||||||
import Media from "./alert-dialog-media.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Title,
|
|
||||||
Action,
|
|
||||||
Cancel,
|
|
||||||
Portal,
|
|
||||||
Footer,
|
|
||||||
Header,
|
|
||||||
Trigger,
|
|
||||||
Overlay,
|
|
||||||
Content,
|
|
||||||
Description,
|
|
||||||
Media,
|
|
||||||
//
|
|
||||||
Root as AlertDialog,
|
|
||||||
Title as AlertDialogTitle,
|
|
||||||
Action as AlertDialogAction,
|
|
||||||
Cancel as AlertDialogCancel,
|
|
||||||
Portal as AlertDialogPortal,
|
|
||||||
Footer as AlertDialogFooter,
|
|
||||||
Header as AlertDialogHeader,
|
|
||||||
Trigger as AlertDialogTrigger,
|
|
||||||
Overlay as AlertDialogOverlay,
|
|
||||||
Content as AlertDialogContent,
|
|
||||||
Description as AlertDialogDescription,
|
|
||||||
Media as AlertDialogMedia,
|
|
||||||
};
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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="alert-action"
|
|
||||||
class={cn("absolute top-[calc(--spacing(1.25))] right-[calc(--spacing(1.25))]", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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="alert-description"
|
|
||||||
class={cn(
|
|
||||||
"text-muted-foreground text-xs/relaxed text-balance md:text-pretty [&_p:not(:last-child)]:mb-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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="alert-title"
|
|
||||||
class={cn(
|
|
||||||
"font-medium group-has-[>svg]/alert:col-start-2 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
import { type VariantProps, tv } from "tailwind-variants";
|
|
||||||
|
|
||||||
export const alertVariants = tv({
|
|
||||||
base: "grid gap-0.5 rounded-none border px-2.5 py-2 text-left text-xs has-data-[slot=alert-action]:relative has-data-[slot=alert-action]:pr-18 has-[>svg]:grid-cols-[auto_1fr] has-[>svg]:gap-x-2 *:[svg]:row-span-2 *:[svg]:translate-y-0 *:[svg]:text-current *:[svg:not([class*='size-'])]:size-4 group/alert relative w-full",
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default: "bg-card text-card-foreground",
|
|
||||||
destructive: "text-destructive bg-card *:data-[slot=alert-description]:text-destructive/90 *:[svg]:text-current",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: "default",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type AlertVariant = VariantProps<typeof alertVariants>["variant"];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
variant = "default",
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
||||||
variant?: AlertVariant;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="alert"
|
|
||||||
role="alert"
|
|
||||||
class={cn(alertVariants({ variant }), className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import Root from "./alert.svelte";
|
|
||||||
import Description from "./alert-description.svelte";
|
|
||||||
import Title from "./alert-title.svelte";
|
|
||||||
import Action from "./alert-action.svelte";
|
|
||||||
export { alertVariants, type AlertVariant } from "./alert.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Description,
|
|
||||||
Title,
|
|
||||||
Action,
|
|
||||||
//
|
|
||||||
Root as Alert,
|
|
||||||
Description as AlertDescription,
|
|
||||||
Title as AlertTitle,
|
|
||||||
Action as AlertAction,
|
|
||||||
};
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AspectRatio as AspectRatioPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { ref = $bindable(null), ...restProps }: AspectRatioPrimitive.RootProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AspectRatioPrimitive.Root bind:ref data-slot="aspect-ratio" {...restProps} />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
import Root from "./aspect-ratio.svelte";
|
|
||||||
|
|
||||||
export { Root, Root as AspectRatio };
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<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="avatar-badge"
|
|
||||||
class={cn(
|
|
||||||
"bg-primary text-primary-foreground ring-background absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-blend-color ring-2 select-none",
|
|
||||||
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
|
|
||||||
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
|
|
||||||
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</span>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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(
|
|
||||||
"bg-muted text-muted-foreground rounded-full flex size-full items-center justify-center text-sm group-data-[size=sm]/avatar:text-xs",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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="avatar-group-count"
|
|
||||||
class={cn(
|
|
||||||
"bg-muted text-muted-foreground size-8 rounded-full text-xs group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3 ring-background relative flex shrink-0 items-center justify-center ring-2",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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="avatar-group"
|
|
||||||
class={cn(
|
|
||||||
"cn-avatar-group *:data-[slot=avatar]:ring-background group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<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("rounded-full aspect-square size-full object-cover", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Avatar as AvatarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
loadingStatus = $bindable("loading"),
|
|
||||||
size = "default",
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AvatarPrimitive.RootProps & {
|
|
||||||
size?: "default" | "sm" | "lg";
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AvatarPrimitive.Root
|
|
||||||
bind:ref
|
|
||||||
bind:loadingStatus
|
|
||||||
data-slot="avatar"
|
|
||||||
data-size={size}
|
|
||||||
class={cn(
|
|
||||||
"size-8 rounded-full after:rounded-full data-[size=lg]:size-10 data-[size=sm]:size-6 after:border-border group/avatar relative flex shrink-0 select-none after:absolute after:inset-0 after:border after:mix-blend-darken dark:after:mix-blend-lighten",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
import Root from "./avatar.svelte";
|
|
||||||
import Image from "./avatar-image.svelte";
|
|
||||||
import Fallback from "./avatar-fallback.svelte";
|
|
||||||
import Badge from "./avatar-badge.svelte";
|
|
||||||
import Group from "./avatar-group.svelte";
|
|
||||||
import GroupCount from "./avatar-group-count.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Image,
|
|
||||||
Fallback,
|
|
||||||
Badge,
|
|
||||||
Group,
|
|
||||||
GroupCount,
|
|
||||||
//
|
|
||||||
Root as Avatar,
|
|
||||||
Image as AvatarImage,
|
|
||||||
Fallback as AvatarFallback,
|
|
||||||
Badge as AvatarBadge,
|
|
||||||
Group as AvatarGroup,
|
|
||||||
GroupCount as AvatarGroupCount,
|
|
||||||
};
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
import { type VariantProps, tv } from "tailwind-variants";
|
|
||||||
|
|
||||||
export const badgeVariants = tv({
|
|
||||||
base: "h-5 gap-1 rounded-none border border-transparent px-2 py-0.5 text-xs font-medium transition-all has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&>svg]:size-3! focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive group/badge inline-flex w-fit shrink-0 items-center justify-center overflow-hidden whitespace-nowrap transition-colors focus-visible:ring-[3px] [&>svg]:pointer-events-none",
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
||||||
secondary: "bg-secondary text-secondary-foreground [a]:hover:bg-secondary/80",
|
|
||||||
destructive: "bg-destructive/10 [a]:hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 text-destructive dark:bg-destructive/20",
|
|
||||||
outline: "border-border text-foreground [a]:hover:bg-muted [a]:hover:text-muted-foreground",
|
|
||||||
ghost: "hover:bg-muted hover:text-muted-foreground dark:hover:bg-muted/50",
|
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
href,
|
|
||||||
class: className,
|
|
||||||
variant = "default",
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAnchorAttributes> & {
|
|
||||||
variant?: BadgeVariant;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<svelte:element
|
|
||||||
this={href ? "a" : "span"}
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="badge"
|
|
||||||
{href}
|
|
||||||
class={cn(badgeVariants({ variant }), className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</svelte:element>
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
export { default as Badge } from "./badge.svelte";
|
|
||||||
export { badgeVariants, type BadgeVariant } from "./badge.svelte";
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { cn, type WithElementRef, type WithoutChildren } from "$lib/utils.js";
|
|
||||||
import MoreHorizontalIcon from '@lucide/svelte/icons/more-horizontal';
|
|
||||||
|
|
||||||
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("size-5 [&>svg]:size-4 flex items-center justify-center", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
<MoreHorizontalIcon />
|
|
||||||
<span class="sr-only">More</span>
|
|
||||||
</span>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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("gap-1 inline-flex items-center", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</li>
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<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}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<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("text-muted-foreground gap-1.5 text-xs flex flex-wrap items-center wrap-break-word", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</ol>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<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("text-foreground font-normal", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</span>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
import type { HTMLLiAttributes } from "svelte/elements";
|
|
||||||
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
|
|
||||||
|
|
||||||
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>
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { WithElementRef } from "$lib/utils.js";
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<nav
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="breadcrumb"
|
|
||||||
aria-label="breadcrumb"
|
|
||||||
class={cn("cn-breadcrumb", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</nav>
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
import type { ComponentProps } from "svelte";
|
|
||||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
orientation = "vertical",
|
|
||||||
...restProps
|
|
||||||
}: ComponentProps<typeof Separator> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Separator
|
|
||||||
bind:ref
|
|
||||||
data-slot="button-group-separator"
|
|
||||||
{orientation}
|
|
||||||
class={cn(
|
|
||||||
"bg-input relative self-stretch data-[orientation=horizontal]:mx-px data-[orientation=horizontal]:w-auto data-[orientation=vertical]:my-px data-[orientation=vertical]:h-auto",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import type { Snippet } from "svelte";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
child,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
||||||
child?: Snippet<[{ props: Record<string, unknown> }]>;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
const mergedProps = $derived({
|
|
||||||
...restProps,
|
|
||||||
class: cn("bg-muted gap-2 rounded-none border px-2.5 text-xs font-medium [&_svg:not([class*='size-'])]:size-4 flex items-center [&_svg]:pointer-events-none", className),
|
|
||||||
"data-slot": "button-group-text",
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if child}
|
|
||||||
{@render child({ props: mergedProps })}
|
|
||||||
{:else}
|
|
||||||
<div bind:this={ref} {...mergedProps}>
|
|
||||||
{@render mergedProps.children?.()}
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<script lang="ts" module>
|
|
||||||
import { tv, type VariantProps } from "tailwind-variants";
|
|
||||||
|
|
||||||
export const buttonGroupVariants = tv({
|
|
||||||
base: "rounded-none has-[>[data-slot=button-group]]:gap-2 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-none flex w-fit items-stretch [&>*]:focus-visible:relative [&>*]:focus-visible:z-10 [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1",
|
|
||||||
variants: {
|
|
||||||
orientation: {
|
|
||||||
horizontal:
|
|
||||||
"cn-button-group-orientation-horizontal [&>[data-slot]]:rounded-r-none [&>[data-slot]~[data-slot]]:rounded-l-none [&>[data-slot]~[data-slot]]:border-l-0",
|
|
||||||
vertical:
|
|
||||||
"cn-button-group-orientation-vertical flex-col [&>[data-slot]]:rounded-b-none [&>[data-slot]~[data-slot]]:rounded-t-none [&>[data-slot]~[data-slot]]:border-t-0",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
orientation: "horizontal",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type ButtonGroupOrientation = VariantProps<typeof buttonGroupVariants>["orientation"];
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
orientation = "horizontal",
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
|
||||||
orientation?: ButtonGroupOrientation;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
role="group"
|
|
||||||
data-slot="button-group"
|
|
||||||
data-orientation={orientation}
|
|
||||||
class={cn(buttonGroupVariants({ orientation }), className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import Root, { buttonGroupVariants, type ButtonGroupOrientation } from "./button-group.svelte";
|
|
||||||
import Text from "./button-group-text.svelte";
|
|
||||||
import Separator from "./button-group-separator.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Text,
|
|
||||||
Separator,
|
|
||||||
buttonGroupVariants,
|
|
||||||
type ButtonGroupOrientation,
|
|
||||||
//
|
|
||||||
Root as ButtonGroup,
|
|
||||||
Text as ButtonGroupText,
|
|
||||||
Separator as ButtonGroupSeparator,
|
|
||||||
};
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
<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 const buttonVariants = tv({
|
|
||||||
base: "focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 rounded-none border border-transparent bg-clip-padding text-xs font-medium focus-visible:ring-1 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-1 [&_svg:not([class*='size-'])]:size-4 group/button inline-flex shrink-0 items-center justify-center whitespace-nowrap transition-all outline-none select-none disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0",
|
|
||||||
variants: {
|
|
||||||
variant: {
|
|
||||||
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
||||||
outline: "border-border bg-background hover:bg-muted hover:text-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
||||||
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
||||||
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
||||||
destructive: "bg-destructive/10 hover:bg-destructive/20 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/20 text-destructive focus-visible:border-destructive/40 dark:hover:bg-destructive/30",
|
|
||||||
link: "text-primary underline-offset-4 hover:underline",
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
||||||
xs: "h-6 gap-1 rounded-none px-2 text-xs has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
||||||
sm: "h-7 gap-1 rounded-none px-2.5 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
||||||
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
||||||
icon: "size-8",
|
|
||||||
"icon-xs": "size-6 rounded-none [&_svg:not([class*='size-'])]:size-3",
|
|
||||||
"icon-sm": "size-7 rounded-none",
|
|
||||||
"icon-lg": "size-9",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: "default",
|
|
||||||
size: "default",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
let {
|
|
||||||
class: className,
|
|
||||||
variant = "default",
|
|
||||||
size = "default",
|
|
||||||
ref = $bindable(null),
|
|
||||||
href = undefined,
|
|
||||||
type = "button",
|
|
||||||
disabled,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: ButtonProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if href}
|
|
||||||
<a
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="button"
|
|
||||||
class={cn(buttonVariants({ variant, size }), className)}
|
|
||||||
href={disabled ? undefined : href}
|
|
||||||
aria-disabled={disabled}
|
|
||||||
role={disabled ? "link" : undefined}
|
|
||||||
tabindex={disabled ? -1 : undefined}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</a>
|
|
||||||
{:else}
|
|
||||||
<button
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="button"
|
|
||||||
class={cn(buttonVariants({ variant, size }), className)}
|
|
||||||
{type}
|
|
||||||
{disabled}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import Root, {
|
|
||||||
type ButtonProps,
|
|
||||||
type ButtonSize,
|
|
||||||
type ButtonVariant,
|
|
||||||
buttonVariants,
|
|
||||||
} from "./button.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
type ButtonProps as Props,
|
|
||||||
//
|
|
||||||
Root as Button,
|
|
||||||
buttonVariants,
|
|
||||||
type ButtonProps,
|
|
||||||
type ButtonSize,
|
|
||||||
type ButtonVariant,
|
|
||||||
};
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { ComponentProps } from "svelte";
|
|
||||||
import type Calendar from "./calendar.svelte";
|
|
||||||
import CalendarMonthSelect from "./calendar-month-select.svelte";
|
|
||||||
import CalendarYearSelect from "./calendar-year-select.svelte";
|
|
||||||
import { DateFormatter, getLocalTimeZone, type DateValue } from "@internationalized/date";
|
|
||||||
|
|
||||||
let {
|
|
||||||
captionLayout,
|
|
||||||
months,
|
|
||||||
monthFormat,
|
|
||||||
years,
|
|
||||||
yearFormat,
|
|
||||||
month,
|
|
||||||
locale,
|
|
||||||
placeholder = $bindable(),
|
|
||||||
monthIndex = 0,
|
|
||||||
}: {
|
|
||||||
captionLayout: ComponentProps<typeof Calendar>["captionLayout"];
|
|
||||||
months: ComponentProps<typeof CalendarMonthSelect>["months"];
|
|
||||||
monthFormat: ComponentProps<typeof CalendarMonthSelect>["monthFormat"];
|
|
||||||
years: ComponentProps<typeof CalendarYearSelect>["years"];
|
|
||||||
yearFormat: ComponentProps<typeof CalendarYearSelect>["yearFormat"];
|
|
||||||
month: DateValue;
|
|
||||||
placeholder: DateValue | undefined;
|
|
||||||
locale: string;
|
|
||||||
monthIndex: number;
|
|
||||||
} = $props();
|
|
||||||
|
|
||||||
function formatYear(date: DateValue) {
|
|
||||||
const dateObj = date.toDate(getLocalTimeZone());
|
|
||||||
if (typeof yearFormat === "function") return yearFormat(dateObj.getFullYear());
|
|
||||||
return new DateFormatter(locale, { year: yearFormat }).format(dateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatMonth(date: DateValue) {
|
|
||||||
const dateObj = date.toDate(getLocalTimeZone());
|
|
||||||
if (typeof monthFormat === "function") return monthFormat(dateObj.getMonth() + 1);
|
|
||||||
return new DateFormatter(locale, { month: monthFormat }).format(dateObj);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#snippet MonthSelect()}
|
|
||||||
<CalendarMonthSelect
|
|
||||||
{months}
|
|
||||||
{monthFormat}
|
|
||||||
value={month.month}
|
|
||||||
onchange={(e) => {
|
|
||||||
if (!placeholder) return;
|
|
||||||
const v = Number.parseInt(e.currentTarget.value);
|
|
||||||
const newPlaceholder = placeholder.set({ month: v });
|
|
||||||
placeholder = newPlaceholder.subtract({ months: monthIndex });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
{#snippet YearSelect()}
|
|
||||||
<CalendarYearSelect {years} {yearFormat} value={month.year} />
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
{#if captionLayout === "dropdown"}
|
|
||||||
{@render MonthSelect()}
|
|
||||||
{@render YearSelect()}
|
|
||||||
{:else if captionLayout === "dropdown-months"}
|
|
||||||
{@render MonthSelect()}
|
|
||||||
{#if placeholder}
|
|
||||||
{formatYear(placeholder)}
|
|
||||||
{/if}
|
|
||||||
{:else if captionLayout === "dropdown-years"}
|
|
||||||
{#if placeholder}
|
|
||||||
{formatMonth(placeholder)}
|
|
||||||
{/if}
|
|
||||||
{@render YearSelect()}
|
|
||||||
{:else}
|
|
||||||
{formatMonth(month)} {formatYear(month)}
|
|
||||||
{/if}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.CellProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.Cell
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"relative size-(--cell-size) p-0 text-center text-sm focus-within:z-20 [&:first-child[data-selected]_[data-bits-day]]:rounded-s-(--cell-radius) [&:last-child[data-selected]_[data-bits-day]]:rounded-e-(--cell-radius)",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.DayProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.Day
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"flex size-(--cell-size) flex-col items-center justify-center gap-1 rounded-(--cell-radius) p-0 leading-none font-normal whitespace-nowrap select-none",
|
|
||||||
"[&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)",
|
|
||||||
"not-data-selected:hover:bg-accent/50 not-data-selected:hover:text-accent-foreground",
|
|
||||||
"[&[data-today]:not([data-selected])]:bg-accent [&[data-today]:not([data-selected])]:text-accent-foreground [&[data-today][data-disabled]]:text-muted-foreground",
|
|
||||||
"data-[selected]:bg-primary data-[selected]:text-primary-foreground data-[selected]:hover:text-foreground",
|
|
||||||
// Outside months
|
|
||||||
"[&[data-outside-month]:not([data-selected])]:text-muted-foreground [&[data-outside-month]:not([data-selected])]:hover:text-accent-foreground",
|
|
||||||
// Disabled
|
|
||||||
"data-[disabled]:text-muted-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
||||||
// Unavailable
|
|
||||||
"data-[unavailable]:text-muted-foreground data-[unavailable]:line-through",
|
|
||||||
// focus
|
|
||||||
"focus:border-ring focus:ring-ring/50 focus:relative",
|
|
||||||
// inner spans
|
|
||||||
"[&>span]:text-xs [&>span]:opacity-70",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.GridBodyProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.GridBody bind:ref class={cn(className)} {...restProps} />
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.GridHeadProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.GridHead bind:ref class={cn(className)} {...restProps} />
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.GridRowProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.GridRow bind:ref class={cn("flex", className)} {...restProps} />
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.GridProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.Grid
|
|
||||||
bind:ref
|
|
||||||
class={cn("flex w-full border-collapse flex-col", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.HeadCellProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.HeadCell
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"text-muted-foreground w-(--cell-size) rounded-md text-[0.8rem] font-normal",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.HeaderProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.Header
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"flex h-(--cell-size) w-full items-center justify-center gap-1.5 text-sm font-medium",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.HeadingProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<CalendarPrimitive.Heading
|
|
||||||
bind:ref
|
|
||||||
class={cn("px-(--cell-size) text-sm font-medium", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
|
||||||
import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
value,
|
|
||||||
onchange,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChildrenOrChild<CalendarPrimitive.MonthSelectProps> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<span
|
|
||||||
class={cn(
|
|
||||||
"has-focus:border-ring border-input has-focus:ring-ring/50 relative flex rounded-md border shadow-xs has-focus:ring-[3px]",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<CalendarPrimitive.MonthSelect
|
|
||||||
bind:ref
|
|
||||||
class="bg-background dark:bg-popover dark:text-popover-foreground absolute inset-0 opacity-0"
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#snippet child({ props, monthItems, selectedMonthItem })}
|
|
||||||
<select {...props} {value} {onchange}>
|
|
||||||
{#each monthItems as monthItem (monthItem.value)}
|
|
||||||
<option
|
|
||||||
value={monthItem.value}
|
|
||||||
selected={value !== undefined
|
|
||||||
? monthItem.value === value
|
|
||||||
: monthItem.value === selectedMonthItem.value}
|
|
||||||
>
|
|
||||||
{monthItem.label}
|
|
||||||
</option>
|
|
||||||
{/each}
|
|
||||||
</select>
|
|
||||||
<span
|
|
||||||
class="[&>svg]:text-muted-foreground flex h-(--cell-size) items-center gap-1 rounded-md ps-2 pe-1 text-sm font-medium select-none [&>svg]:size-3.5"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
{monthItems.find((item) => item.value === value)?.label || selectedMonthItem.label}
|
|
||||||
<ChevronDownIcon class="size-4" />
|
|
||||||
</span>
|
|
||||||
{/snippet}
|
|
||||||
</CalendarPrimitive.MonthSelect>
|
|
||||||
</span>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { type WithElementRef, cn } from "$lib/utils.js";
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div {...restProps} bind:this={ref} class={cn("flex w-full flex-col gap-4", className)}>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<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}
|
|
||||||
class={cn("relative flex flex-col gap-4 md:flex-row", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<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<HTMLElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<nav
|
|
||||||
{...restProps}
|
|
||||||
bind:this={ref}
|
|
||||||
class={cn("absolute inset-x-0 top-0 flex w-full items-center justify-between gap-1", className)}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</nav>
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Calendar as CalendarPrimitive } from "bits-ui";
|
|
||||||
import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
|
|
||||||
import { buttonVariants, type ButtonVariant } from "$lib/components/ui/button/index.js";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
variant = "ghost",
|
|
||||||
...restProps
|
|
||||||
}: CalendarPrimitive.NextButtonProps & {
|
|
||||||
variant?: ButtonVariant;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#snippet Fallback()}
|
|
||||||
<ChevronRightIcon class="size-4" />
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
<CalendarPrimitive.NextButton
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
buttonVariants({ variant }),
|
|
||||||
"size-(--cell-size) bg-transparent p-0 select-none disabled:opacity-50 rtl:rotate-180",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{#if children}
|
|
||||||
{@render children?.()}
|
|
||||||
{:else}
|
|
||||||
{@render Fallback()}
|
|
||||||
{/if}
|
|
||||||
</CalendarPrimitive.NextButton>
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user