@podosoft/podokit 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/dist/add.d.ts +40 -0
- package/dist/add.js +114 -0
- package/dist/create.d.ts +2 -1
- package/dist/create.js +3 -2
- package/dist/index.js +39 -1
- package/dist/prompt.d.ts +0 -1
- package/dist/prompt.js +6 -7
- package/dist/templates/fullstack-nest-svelte/README.md +20 -8
- package/dist/templates/fullstack-nest-svelte/apps/api/package.json +14 -2
- package/dist/templates/fullstack-nest-svelte/apps/api/src/app.module.ts +11 -1
- package/dist/templates/fullstack-nest-svelte/apps/api/src/config/env.validation.ts +20 -15
- package/dist/templates/fullstack-nest-svelte/apps/api/src/database/data-source.ts +19 -0
- package/dist/templates/fullstack-nest-svelte/apps/api/src/health/health.controller.ts +15 -5
- package/dist/templates/fullstack-nest-svelte/apps/api/src/main.ts +8 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/components.json +1 -1
- package/dist/templates/fullstack-nest-svelte/apps/web/package.json +9 -2
- package/dist/templates/fullstack-nest-svelte/apps/web/src/app.css +72 -8
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/button/button.svelte +82 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/button/index.ts +17 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card-action.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card-content.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card-description.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card-header.svelte +23 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card-title.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/card.svelte +22 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/card/index.ts +25 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/checkbox/checkbox.svelte +39 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/checkbox/index.ts +6 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/input/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/input/input.svelte +48 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/label/index.ts +7 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/components/ui/label/label.svelte +20 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/lib/utils.ts +11 -0
- package/dist/templates/fullstack-nest-svelte/apps/web/src/routes/+page.svelte +19 -12
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.controller.ts +31 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.module.ts +22 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/auth.service.ts +44 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/dto/login.dto.ts +12 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/dto/register.dto.ts +13 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/jwt-auth.guard.ts +5 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/jwt.strategy.ts +23 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/auth/user.entity.ts +16 -0
- package/dist/templates/modules/auth-jwt/files/apps/api/src/migrations/1720200000000-InitUsers.ts +23 -0
- package/dist/templates/modules/auth-jwt/module.manifest.json +31 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/demo.processor.ts +12 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/dto/create-job.dto.ts +9 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/jobs.controller.ts +29 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/jobs.module.ts +15 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/queue.ts +8 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/jobs/worker.module.ts +20 -0
- package/dist/templates/modules/bullmq/files/apps/api/src/main-worker.ts +14 -0
- package/dist/templates/modules/bullmq/files/infra/docker/worker.compose.example.yml +18 -0
- package/dist/templates/modules/bullmq/files/infra/k3s/worker-deployment.yaml +22 -0
- package/dist/templates/modules/bullmq/module.manifest.json +28 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/files.controller.ts +29 -0
- package/dist/templates/modules/file-upload/files/apps/api/src/files/files.module.ts +9 -0
- package/dist/templates/modules/file-upload/module.manifest.json +19 -0
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/dto/start-job.dto.ts +11 -0
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/job-progress.module.ts +13 -0
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.bridge.ts +19 -0
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.controller.ts +17 -0
- package/dist/templates/modules/job-progress/files/apps/api/src/progress/progress.processor.ts +25 -0
- package/dist/templates/modules/job-progress/module.manifest.json +23 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/dto/put-object.dto.ts +8 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.config.ts +31 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.controller.ts +29 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.module.ts +11 -0
- package/dist/templates/modules/object-storage-s3/files/apps/api/src/storage/storage.service.ts +37 -0
- package/dist/templates/modules/object-storage-s3/files/infra/docker/minio.compose.yml +33 -0
- package/dist/templates/modules/object-storage-s3/module.manifest.json +31 -0
- package/dist/templates/modules/redis/files/apps/api/src/redis/cache.controller.ts +21 -0
- package/dist/templates/modules/redis/files/apps/api/src/redis/dto/set-cache.dto.ts +14 -0
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis.module.ts +12 -0
- package/dist/templates/modules/redis/files/apps/api/src/redis/redis.service.ts +43 -0
- package/dist/templates/modules/redis/module.manifest.json +21 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/dto/publish-event.dto.ts +8 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/events.controller.ts +25 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/events.module.ts +12 -0
- package/dist/templates/modules/sse/files/apps/api/src/events/events.service.ts +17 -0
- package/dist/templates/modules/sse/module.manifest.json +16 -0
- package/dist/templates/todo/README.md +40 -0
- package/dist/templates/todo/apps/api/Dockerfile +22 -0
- package/dist/templates/todo/apps/api/nest-cli.json +5 -0
- package/dist/templates/todo/apps/api/package.json +44 -0
- package/dist/templates/todo/apps/api/src/app.module.ts +19 -0
- package/dist/templates/todo/apps/api/src/common/all-exceptions.filter.ts +43 -0
- package/dist/templates/todo/apps/api/src/common/app-exception.ts +12 -0
- package/dist/templates/todo/apps/api/src/config/env.validation.ts +23 -0
- package/dist/templates/todo/apps/api/src/database/data-source.ts +19 -0
- package/dist/templates/todo/apps/api/src/health/health.controller.ts +23 -0
- package/dist/templates/todo/apps/api/src/health/health.module.ts +7 -0
- package/dist/templates/todo/apps/api/src/main.ts +29 -0
- package/dist/templates/todo/apps/api/src/migrations/1720100000000-InitTodos.ts +22 -0
- package/dist/templates/todo/apps/api/src/todos/dto/create-todo.dto.ts +10 -0
- package/dist/templates/todo/apps/api/src/todos/dto/update-todo.dto.ts +10 -0
- package/dist/templates/todo/apps/api/src/todos/todo.entity.ts +16 -0
- package/dist/templates/todo/apps/api/src/todos/todos.controller.ts +38 -0
- package/dist/templates/todo/apps/api/src/todos/todos.module.ts +12 -0
- package/dist/templates/todo/apps/api/src/todos/todos.service.ts +41 -0
- package/dist/templates/todo/apps/api/test/health.e2e-spec.ts +23 -0
- package/dist/templates/todo/apps/api/tsconfig.json +21 -0
- package/dist/templates/todo/apps/web/Dockerfile +22 -0
- package/dist/templates/todo/apps/web/components.json +15 -0
- package/dist/templates/todo/apps/web/package.json +35 -0
- package/dist/templates/todo/apps/web/src/app.css +81 -0
- package/dist/templates/todo/apps/web/src/app.d.ts +11 -0
- package/dist/templates/todo/apps/web/src/app.html +11 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/button/button.svelte +82 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/button/index.ts +17 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card-action.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card-content.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card-description.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card-header.svelte +23 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card-title.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/card.svelte +22 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/card/index.ts +25 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/checkbox/checkbox.svelte +39 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/checkbox/index.ts +6 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/input/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/input/input.svelte +48 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/label/index.ts +7 -0
- package/dist/templates/todo/apps/web/src/lib/components/ui/label/label.svelte +20 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/README.md +7 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/en.ts +10 -0
- package/dist/templates/todo/apps/web/src/lib/i18n/ko.ts +8 -0
- package/dist/templates/todo/apps/web/src/lib/server/backend-proxy.ts +16 -0
- package/dist/templates/todo/apps/web/src/lib/utils.ts +11 -0
- package/dist/templates/todo/apps/web/src/routes/+layout.svelte +9 -0
- package/dist/templates/todo/apps/web/src/routes/+page.svelte +95 -0
- package/dist/templates/todo/apps/web/src/routes/api/health/+server.ts +12 -0
- package/dist/templates/todo/apps/web/src/routes/api/todos/+server.ts +24 -0
- package/dist/templates/todo/apps/web/src/routes/api/todos/[id]/+server.ts +24 -0
- package/dist/templates/todo/apps/web/static/.gitkeep +0 -0
- package/dist/templates/todo/apps/web/svelte.config.js +15 -0
- package/dist/templates/todo/apps/web/tsconfig.json +9 -0
- package/dist/templates/todo/apps/web/vite.config.ts +7 -0
- package/dist/templates/todo/dot-env.example +16 -0
- package/dist/templates/todo/dot-gitignore +9 -0
- package/dist/templates/todo/infra/docker/docker-compose.yml +29 -0
- package/dist/templates/todo/infra/k3s/api-deployment.yaml +24 -0
- package/dist/templates/todo/infra/k3s/configmap.yaml +10 -0
- package/dist/templates/todo/infra/k3s/ingress.yaml +18 -0
- package/dist/templates/todo/infra/k3s/namespace.yaml +4 -0
- package/dist/templates/todo/infra/k3s/secret.example.yaml +10 -0
- package/dist/templates/todo/infra/k3s/services.yaml +21 -0
- package/dist/templates/todo/infra/k3s/web-deployment.yaml +20 -0
- package/dist/templates/todo/package.json +13 -0
- package/dist/templates.d.ts +10 -0
- package/dist/templates.js +33 -0
- package/package.json +2 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Body, Controller, Delete, Get, HttpCode, Param, Patch, Post } from "@nestjs/common";
|
|
2
|
+
import { ApiTags } from "@nestjs/swagger";
|
|
3
|
+
import { TodosService } from "./todos.service";
|
|
4
|
+
import { CreateTodoDto } from "./dto/create-todo.dto";
|
|
5
|
+
import { UpdateTodoDto } from "./dto/update-todo.dto";
|
|
6
|
+
import { Todo } from "./todo.entity";
|
|
7
|
+
|
|
8
|
+
@ApiTags("todos")
|
|
9
|
+
@Controller("todos")
|
|
10
|
+
export class TodosController {
|
|
11
|
+
constructor(private readonly todos: TodosService) {}
|
|
12
|
+
|
|
13
|
+
@Get()
|
|
14
|
+
findAll(): Promise<Todo[]> {
|
|
15
|
+
return this.todos.findAll();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@Get(":id")
|
|
19
|
+
findOne(@Param("id") id: string): Promise<Todo> {
|
|
20
|
+
return this.todos.findOne(id);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Post()
|
|
24
|
+
create(@Body() dto: CreateTodoDto): Promise<Todo> {
|
|
25
|
+
return this.todos.create(dto);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Patch(":id")
|
|
29
|
+
update(@Param("id") id: string, @Body() dto: UpdateTodoDto): Promise<Todo> {
|
|
30
|
+
return this.todos.update(id, dto);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@Delete(":id")
|
|
34
|
+
@HttpCode(204)
|
|
35
|
+
remove(@Param("id") id: string): Promise<void> {
|
|
36
|
+
return this.todos.remove(id);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Module } from "@nestjs/common";
|
|
2
|
+
import { TypeOrmModule } from "@nestjs/typeorm";
|
|
3
|
+
import { Todo } from "./todo.entity";
|
|
4
|
+
import { TodosController } from "./todos.controller";
|
|
5
|
+
import { TodosService } from "./todos.service";
|
|
6
|
+
|
|
7
|
+
@Module({
|
|
8
|
+
imports: [TypeOrmModule.forFeature([Todo])],
|
|
9
|
+
controllers: [TodosController],
|
|
10
|
+
providers: [TodosService],
|
|
11
|
+
})
|
|
12
|
+
export class TodosModule {}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Injectable, NotFoundException } from "@nestjs/common";
|
|
2
|
+
import { InjectRepository } from "@nestjs/typeorm";
|
|
3
|
+
import { Repository } from "typeorm";
|
|
4
|
+
import { Todo } from "./todo.entity";
|
|
5
|
+
import { CreateTodoDto } from "./dto/create-todo.dto";
|
|
6
|
+
import { UpdateTodoDto } from "./dto/update-todo.dto";
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class TodosService {
|
|
10
|
+
constructor(
|
|
11
|
+
@InjectRepository(Todo)
|
|
12
|
+
private readonly todos: Repository<Todo>,
|
|
13
|
+
) {}
|
|
14
|
+
|
|
15
|
+
findAll(): Promise<Todo[]> {
|
|
16
|
+
return this.todos.find({ order: { createdAt: "DESC" } });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async findOne(id: string): Promise<Todo> {
|
|
20
|
+
const todo = await this.todos.findOne({ where: { id } });
|
|
21
|
+
if (!todo) {
|
|
22
|
+
throw new NotFoundException(`Todo ${id} not found`);
|
|
23
|
+
}
|
|
24
|
+
return todo;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
create(dto: CreateTodoDto): Promise<Todo> {
|
|
28
|
+
return this.todos.save(this.todos.create(dto));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async update(id: string, dto: UpdateTodoDto): Promise<Todo> {
|
|
32
|
+
const todo = await this.findOne(id);
|
|
33
|
+
Object.assign(todo, dto);
|
|
34
|
+
return this.todos.save(todo);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async remove(id: string): Promise<void> {
|
|
38
|
+
const todo = await this.findOne(id);
|
|
39
|
+
await this.todos.remove(todo);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Test } from "@nestjs/testing";
|
|
2
|
+
import type { INestApplication } from "@nestjs/common";
|
|
3
|
+
import request from "supertest";
|
|
4
|
+
import { HealthModule } from "../src/health/health.module";
|
|
5
|
+
|
|
6
|
+
describe("Health (e2e)", () => {
|
|
7
|
+
let app: INestApplication;
|
|
8
|
+
|
|
9
|
+
beforeAll(async () => {
|
|
10
|
+
const moduleRef = await Test.createTestingModule({ imports: [HealthModule] }).compile();
|
|
11
|
+
app = moduleRef.createNestApplication();
|
|
12
|
+
await app.init();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
afterAll(async () => {
|
|
16
|
+
await app.close();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("GET /health returns ok", async () => {
|
|
20
|
+
const res = await request(app.getHttpServer()).get("/health").expect(200);
|
|
21
|
+
expect(res.body.status).toBe("ok");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"module": "CommonJS",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"moduleResolution": "Node",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"declaration": false,
|
|
9
|
+
"emitDecoratorMetadata": true,
|
|
10
|
+
"experimentalDecorators": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noUnusedLocals": true,
|
|
13
|
+
"noUnusedParameters": true,
|
|
14
|
+
"noImplicitReturns": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"forceConsistentCasingInFileNames": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*.ts"],
|
|
20
|
+
"exclude": ["node_modules", "dist", "test"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# syntax=docker/dockerfile:1
|
|
2
|
+
FROM node:20-alpine AS deps
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
COPY package.json ./
|
|
5
|
+
RUN npm install --no-audit --no-fund
|
|
6
|
+
|
|
7
|
+
FROM node:20-alpine AS build
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
COPY --from=deps /app/node_modules ./node_modules
|
|
10
|
+
COPY . .
|
|
11
|
+
RUN npm run build
|
|
12
|
+
|
|
13
|
+
FROM node:20-alpine AS runtime
|
|
14
|
+
WORKDIR /app
|
|
15
|
+
ENV NODE_ENV=production
|
|
16
|
+
RUN addgroup -S app && adduser -S app -G app
|
|
17
|
+
COPY --from=build /app/build ./build
|
|
18
|
+
COPY --from=build /app/node_modules ./node_modules
|
|
19
|
+
COPY package.json ./
|
|
20
|
+
USER app
|
|
21
|
+
EXPOSE 3000
|
|
22
|
+
CMD ["node", "build"]
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://shadcn-svelte.com/schema.json",
|
|
3
|
+
"style": "nova",
|
|
4
|
+
"tailwind": {
|
|
5
|
+
"css": "src/app.css",
|
|
6
|
+
"baseColor": "zinc"
|
|
7
|
+
},
|
|
8
|
+
"aliases": {
|
|
9
|
+
"components": "$lib/components",
|
|
10
|
+
"ui": "$lib/components/ui",
|
|
11
|
+
"utils": "$lib/utils"
|
|
12
|
+
},
|
|
13
|
+
"typescript": true,
|
|
14
|
+
"registry": "https://shadcn-svelte.com/registry"
|
|
15
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}-web",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite dev --port 5173",
|
|
8
|
+
"build": "vite build",
|
|
9
|
+
"preview": "vite preview",
|
|
10
|
+
"lint": "svelte-check --tsconfig ./tsconfig.json",
|
|
11
|
+
"test": "echo \"add Playwright tests here\" && exit 0"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"mode-watcher": "^0.5.0",
|
|
15
|
+
"bits-ui": "^2.18.1",
|
|
16
|
+
"clsx": "^2.1.1",
|
|
17
|
+
"tailwind-merge": "^3.6.0",
|
|
18
|
+
"tailwind-variants": "^3.2.2",
|
|
19
|
+
"@lucide/svelte": "^1.23.0",
|
|
20
|
+
"@internationalized/date": "^3.12.2"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@sveltejs/adapter-node": "^5.2.0",
|
|
24
|
+
"@sveltejs/kit": "^2.8.0",
|
|
25
|
+
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
|
26
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
27
|
+
"svelte": "^5.1.0",
|
|
28
|
+
"svelte-check": "^4.0.0",
|
|
29
|
+
"tailwindcss": "^4.0.0",
|
|
30
|
+
"typesafe-i18n": "^5.26.2",
|
|
31
|
+
"typescript": "^5.6.3",
|
|
32
|
+
"vite": "^5.4.0",
|
|
33
|
+
"tw-animate-css": "^1.4.0"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
@import "tw-animate-css";
|
|
3
|
+
|
|
4
|
+
@custom-variant dark (&:is(.dark *));
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
--radius: 0.625rem;
|
|
8
|
+
--background: oklch(1 0 0);
|
|
9
|
+
--foreground: oklch(0.141 0.005 285.823);
|
|
10
|
+
--card: oklch(1 0 0);
|
|
11
|
+
--card-foreground: oklch(0.141 0.005 285.823);
|
|
12
|
+
--popover: oklch(1 0 0);
|
|
13
|
+
--popover-foreground: oklch(0.141 0.005 285.823);
|
|
14
|
+
--primary: oklch(0.21 0.006 285.885);
|
|
15
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
16
|
+
--secondary: oklch(0.967 0.001 286.375);
|
|
17
|
+
--secondary-foreground: oklch(0.21 0.006 285.885);
|
|
18
|
+
--muted: oklch(0.967 0.001 286.375);
|
|
19
|
+
--muted-foreground: oklch(0.552 0.016 285.938);
|
|
20
|
+
--accent: oklch(0.967 0.001 286.375);
|
|
21
|
+
--accent-foreground: oklch(0.21 0.006 285.885);
|
|
22
|
+
--destructive: oklch(0.577 0.245 27.325);
|
|
23
|
+
--border: oklch(0.92 0.004 286.32);
|
|
24
|
+
--input: oklch(0.92 0.004 286.32);
|
|
25
|
+
--ring: oklch(0.705 0.015 286.067);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.dark {
|
|
29
|
+
--background: oklch(0.141 0.005 285.823);
|
|
30
|
+
--foreground: oklch(0.985 0 0);
|
|
31
|
+
--card: oklch(0.21 0.006 285.885);
|
|
32
|
+
--card-foreground: oklch(0.985 0 0);
|
|
33
|
+
--popover: oklch(0.21 0.006 285.885);
|
|
34
|
+
--popover-foreground: oklch(0.985 0 0);
|
|
35
|
+
--primary: oklch(0.92 0.004 286.32);
|
|
36
|
+
--primary-foreground: oklch(0.21 0.006 285.885);
|
|
37
|
+
--secondary: oklch(0.274 0.006 286.033);
|
|
38
|
+
--secondary-foreground: oklch(0.985 0 0);
|
|
39
|
+
--muted: oklch(0.274 0.006 286.033);
|
|
40
|
+
--muted-foreground: oklch(0.705 0.015 286.067);
|
|
41
|
+
--accent: oklch(0.274 0.006 286.033);
|
|
42
|
+
--accent-foreground: oklch(0.985 0 0);
|
|
43
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
44
|
+
--border: oklch(1 0 0 / 10%);
|
|
45
|
+
--input: oklch(1 0 0 / 15%);
|
|
46
|
+
--ring: oklch(0.552 0.016 285.938);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@theme inline {
|
|
50
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
51
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
52
|
+
--radius-lg: var(--radius);
|
|
53
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
54
|
+
--color-background: var(--background);
|
|
55
|
+
--color-foreground: var(--foreground);
|
|
56
|
+
--color-card: var(--card);
|
|
57
|
+
--color-card-foreground: var(--card-foreground);
|
|
58
|
+
--color-popover: var(--popover);
|
|
59
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
60
|
+
--color-primary: var(--primary);
|
|
61
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
62
|
+
--color-secondary: var(--secondary);
|
|
63
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
64
|
+
--color-muted: var(--muted);
|
|
65
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
66
|
+
--color-accent: var(--accent);
|
|
67
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
68
|
+
--color-destructive: var(--destructive);
|
|
69
|
+
--color-border: var(--border);
|
|
70
|
+
--color-input: var(--input);
|
|
71
|
+
--color-ring: var(--ring);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@layer base {
|
|
75
|
+
* {
|
|
76
|
+
@apply border-border outline-ring/50;
|
|
77
|
+
}
|
|
78
|
+
body {
|
|
79
|
+
@apply bg-background text-foreground;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="h-full">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
%sveltekit.head%
|
|
7
|
+
</head>
|
|
8
|
+
<body data-sveltekit-preload-data="hover" class="h-full">
|
|
9
|
+
<div style="display: contents">%sveltekit.body%</div>
|
|
10
|
+
</body>
|
|
11
|
+
</html>
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAnchorAttributes, HTMLButtonAttributes } from "svelte/elements";
|
|
4
|
+
import { type VariantProps, tv } from "tailwind-variants";
|
|
5
|
+
|
|
6
|
+
export const buttonVariants = tv({
|
|
7
|
+
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-lg border border-transparent bg-clip-padding text-sm font-medium focus-visible:ring-3 active:not-aria-[haspopup]:translate-y-px aria-invalid:ring-3 [&_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",
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
|
|
11
|
+
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",
|
|
12
|
+
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
|
|
13
|
+
ghost: "hover:bg-muted hover:text-foreground dark:hover:bg-muted/50 aria-expanded:bg-muted aria-expanded:text-foreground",
|
|
14
|
+
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",
|
|
15
|
+
link: "text-primary underline-offset-4 hover:underline",
|
|
16
|
+
},
|
|
17
|
+
size: {
|
|
18
|
+
default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
19
|
+
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
|
20
|
+
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
|
21
|
+
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
22
|
+
icon: "size-8",
|
|
23
|
+
"icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
|
|
24
|
+
"icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
|
|
25
|
+
"icon-lg": "size-9",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
defaultVariants: {
|
|
29
|
+
variant: "default",
|
|
30
|
+
size: "default",
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export type ButtonVariant = VariantProps<typeof buttonVariants>["variant"];
|
|
35
|
+
export type ButtonSize = VariantProps<typeof buttonVariants>["size"];
|
|
36
|
+
|
|
37
|
+
export type ButtonProps = WithElementRef<HTMLButtonAttributes> &
|
|
38
|
+
WithElementRef<HTMLAnchorAttributes> & {
|
|
39
|
+
variant?: ButtonVariant;
|
|
40
|
+
size?: ButtonSize;
|
|
41
|
+
};
|
|
42
|
+
</script>
|
|
43
|
+
|
|
44
|
+
<script lang="ts">
|
|
45
|
+
let {
|
|
46
|
+
class: className,
|
|
47
|
+
variant = "default",
|
|
48
|
+
size = "default",
|
|
49
|
+
ref = $bindable(null),
|
|
50
|
+
href = undefined,
|
|
51
|
+
type = "button",
|
|
52
|
+
disabled,
|
|
53
|
+
children,
|
|
54
|
+
...restProps
|
|
55
|
+
}: ButtonProps = $props();
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
{#if href}
|
|
59
|
+
<a
|
|
60
|
+
bind:this={ref}
|
|
61
|
+
data-slot="button"
|
|
62
|
+
class={cn(buttonVariants({ variant, size }), className)}
|
|
63
|
+
href={disabled ? undefined : href}
|
|
64
|
+
aria-disabled={disabled}
|
|
65
|
+
role={disabled ? "link" : undefined}
|
|
66
|
+
tabindex={disabled ? -1 : undefined}
|
|
67
|
+
{...restProps}
|
|
68
|
+
>
|
|
69
|
+
{@render children?.()}
|
|
70
|
+
</a>
|
|
71
|
+
{:else}
|
|
72
|
+
<button
|
|
73
|
+
bind:this={ref}
|
|
74
|
+
data-slot="button"
|
|
75
|
+
class={cn(buttonVariants({ variant, size }), className)}
|
|
76
|
+
{type}
|
|
77
|
+
{disabled}
|
|
78
|
+
{...restProps}
|
|
79
|
+
>
|
|
80
|
+
{@render children?.()}
|
|
81
|
+
</button>
|
|
82
|
+
{/if}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import Root, {
|
|
2
|
+
type ButtonProps,
|
|
3
|
+
type ButtonSize,
|
|
4
|
+
type ButtonVariant,
|
|
5
|
+
buttonVariants,
|
|
6
|
+
} from "./button.svelte";
|
|
7
|
+
|
|
8
|
+
export {
|
|
9
|
+
Root,
|
|
10
|
+
type ButtonProps as Props,
|
|
11
|
+
//
|
|
12
|
+
Root as Button,
|
|
13
|
+
buttonVariants,
|
|
14
|
+
type ButtonProps,
|
|
15
|
+
type ButtonSize,
|
|
16
|
+
type ButtonVariant,
|
|
17
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-action"
|
|
16
|
+
class={cn(
|
|
17
|
+
"cn-card-action col-start-2 row-span-2 row-start-1 self-start justify-self-end",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-content"
|
|
16
|
+
class={cn("px-4 group-data-[size=sm]/card:px-3", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<p
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-description"
|
|
16
|
+
class={cn("text-muted-foreground text-sm", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</p>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-footer"
|
|
16
|
+
class={cn("bg-muted/50 rounded-b-xl border-t p-4 group-data-[size=sm]/card:p-3 flex items-center", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-header"
|
|
16
|
+
class={cn(
|
|
17
|
+
"gap-1 rounded-t-xl px-4 group-data-[size=sm]/card:px-3 [.border-b]:pb-4 group-data-[size=sm]/card:[.border-b]:pb-3 group/card-header @container/card-header grid auto-rows-min items-start has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto]",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-title"
|
|
16
|
+
class={cn("text-base leading-snug font-medium group-data-[size=sm]/card:text-sm", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
size = "default",
|
|
10
|
+
...restProps
|
|
11
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & { size?: "default" | "sm" } = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div
|
|
15
|
+
bind:this={ref}
|
|
16
|
+
data-slot="card"
|
|
17
|
+
data-size={size}
|
|
18
|
+
class={cn("ring-foreground/10 bg-card text-card-foreground gap-4 overflow-hidden rounded-xl py-4 text-sm ring-1 has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:gap-3 data-[size=sm]:py-3 data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl group/card flex flex-col", className)}
|
|
19
|
+
{...restProps}
|
|
20
|
+
>
|
|
21
|
+
{@render children?.()}
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Root from "./card.svelte";
|
|
2
|
+
import Content from "./card-content.svelte";
|
|
3
|
+
import Description from "./card-description.svelte";
|
|
4
|
+
import Footer from "./card-footer.svelte";
|
|
5
|
+
import Header from "./card-header.svelte";
|
|
6
|
+
import Title from "./card-title.svelte";
|
|
7
|
+
import Action from "./card-action.svelte";
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
Root,
|
|
11
|
+
Content,
|
|
12
|
+
Description,
|
|
13
|
+
Footer,
|
|
14
|
+
Header,
|
|
15
|
+
Title,
|
|
16
|
+
Action,
|
|
17
|
+
//
|
|
18
|
+
Root as Card,
|
|
19
|
+
Content as CardContent,
|
|
20
|
+
Description as CardDescription,
|
|
21
|
+
Footer as CardFooter,
|
|
22
|
+
Header as CardHeader,
|
|
23
|
+
Title as CardTitle,
|
|
24
|
+
Action as CardAction,
|
|
25
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
|
3
|
+
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
|
4
|
+
import CheckIcon from '@lucide/svelte/icons/check';
|
|
5
|
+
import MinusIcon from '@lucide/svelte/icons/minus';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
ref = $bindable(null),
|
|
9
|
+
checked = $bindable(false),
|
|
10
|
+
indeterminate = $bindable(false),
|
|
11
|
+
class: className,
|
|
12
|
+
...restProps
|
|
13
|
+
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<CheckboxPrimitive.Root
|
|
17
|
+
bind:ref
|
|
18
|
+
data-slot="checkbox"
|
|
19
|
+
class={cn(
|
|
20
|
+
"border-input dark:bg-input/30 data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-checked:border-primary aria-invalid:aria-checked:border-primary aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 flex size-4 items-center justify-center rounded-[4px] border transition-colors group-has-disabled/field:opacity-50 focus-visible:ring-3 aria-invalid:ring-3 peer relative shrink-0 outline-none after:absolute after:-inset-x-3 after:-inset-y-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
21
|
+
className
|
|
22
|
+
)}
|
|
23
|
+
bind:checked
|
|
24
|
+
bind:indeterminate
|
|
25
|
+
{...restProps}
|
|
26
|
+
>
|
|
27
|
+
{#snippet children({ checked, indeterminate })}
|
|
28
|
+
<div
|
|
29
|
+
data-slot="checkbox-indicator"
|
|
30
|
+
class="[&>svg]:size-3.5 grid place-content-center text-current transition-none"
|
|
31
|
+
>
|
|
32
|
+
{#if checked}
|
|
33
|
+
<CheckIcon />
|
|
34
|
+
{:else if indeterminate}
|
|
35
|
+
<MinusIcon />
|
|
36
|
+
{/if}
|
|
37
|
+
</div>
|
|
38
|
+
{/snippet}
|
|
39
|
+
</CheckboxPrimitive.Root>
|