├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── bun.lockb
├── components.json
├── package.json
├── postcss.config.js
├── src
├── app.css
├── app.d.ts
├── app.html
├── lib
│ ├── components
│ │ ├── theme
│ │ │ └── toggle-mode
│ │ │ │ ├── index.ts
│ │ │ │ └── toggle-mode.svelte
│ │ └── ui
│ │ │ ├── accordion
│ │ │ ├── accordion-content.svelte
│ │ │ ├── accordion-item.svelte
│ │ │ ├── accordion-trigger.svelte
│ │ │ └── index.ts
│ │ │ ├── button
│ │ │ ├── button.svelte
│ │ │ └── index.ts
│ │ │ ├── calendar
│ │ │ ├── calendar-cell.svelte
│ │ │ ├── calendar-day.svelte
│ │ │ ├── calendar-grid-body.svelte
│ │ │ ├── calendar-grid-head.svelte
│ │ │ ├── calendar-grid-row.svelte
│ │ │ ├── calendar-grid.svelte
│ │ │ ├── calendar-head-cell.svelte
│ │ │ ├── calendar-header.svelte
│ │ │ ├── calendar-heading.svelte
│ │ │ ├── calendar-months.svelte
│ │ │ ├── calendar-next-button.svelte
│ │ │ ├── calendar-prev-button.svelte
│ │ │ ├── calendar.svelte
│ │ │ └── index.ts
│ │ │ ├── form
│ │ │ ├── form-button.svelte
│ │ │ ├── form-description.svelte
│ │ │ ├── form-element-field.svelte
│ │ │ ├── form-field-errors.svelte
│ │ │ ├── form-field.svelte
│ │ │ ├── form-fieldset.svelte
│ │ │ ├── form-label.svelte
│ │ │ ├── form-legend.svelte
│ │ │ └── index.ts
│ │ │ ├── input
│ │ │ ├── index.ts
│ │ │ └── input.svelte
│ │ │ ├── label
│ │ │ ├── index.ts
│ │ │ └── label.svelte
│ │ │ ├── popover
│ │ │ ├── index.ts
│ │ │ └── popover-content.svelte
│ │ │ ├── select
│ │ │ ├── index.ts
│ │ │ ├── select-content.svelte
│ │ │ ├── select-group-heading.svelte
│ │ │ ├── select-item.svelte
│ │ │ ├── select-scroll-down-button.svelte
│ │ │ ├── select-scroll-up-button.svelte
│ │ │ ├── select-separator.svelte
│ │ │ └── select-trigger.svelte
│ │ │ ├── separator
│ │ │ ├── index.ts
│ │ │ └── separator.svelte
│ │ │ ├── snippet
│ │ │ ├── index.ts
│ │ │ ├── pre.svelte
│ │ │ └── snippet.svelte
│ │ │ ├── sonner
│ │ │ ├── index.ts
│ │ │ └── sonner.svelte
│ │ │ └── tabs
│ │ │ ├── index.ts
│ │ │ ├── tabs-content.svelte
│ │ │ ├── tabs-list.svelte
│ │ │ └── tabs-trigger.svelte
│ ├── hooks
│ │ └── use-mouse-move.ts
│ ├── index.ts
│ ├── snippets-content.ts
│ ├── snippets
│ │ ├── shadcn-svelte-4
│ │ │ ├── time-picker-input.svelte
│ │ │ ├── time-picker-utils.ts
│ │ │ └── time-picker.svelte
│ │ ├── shadcn-svelte-runes
│ │ │ ├── date-time-picker-form.svelte
│ │ │ ├── date-time-picker.svelte
│ │ │ ├── time-period-select.svelte
│ │ │ ├── time-picker-12h.svelte
│ │ │ ├── time-picker-input.svelte
│ │ │ ├── time-picker-utils.ts
│ │ │ └── time-picker.svelte
│ │ ├── svelte-4
│ │ │ ├── cn.ts
│ │ │ ├── time-picker-input.svelte
│ │ │ ├── time-picker-utils.ts
│ │ │ └── time-picker.svelte
│ │ └── svelte-runes
│ │ │ ├── cn.ts
│ │ │ ├── time-picker-input.svelte
│ │ │ ├── time-picker-utils.ts
│ │ │ └── time-picker.svelte
│ ├── types.ts
│ └── utils.ts
└── routes
│ ├── +layout.svelte
│ ├── +page.server.ts
│ ├── +page.svelte
│ ├── _background.svelte
│ └── _og-image.svelte
├── static
└── favicon.png
├── svelte.config.js
├── tailwind.config.ts
├── tsconfig.json
└── vite.config.ts
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 |
3 | # Output
4 | .output
5 | .vercel
6 | .netlify
7 | .wrangler
8 | /.svelte-kit
9 | /build
10 |
11 | # OS
12 | .DS_Store
13 | Thumbs.db
14 |
15 | # Env
16 | .env
17 | .env.*
18 | !.env.example
19 | !.env.test
20 |
21 | # Vite
22 | vite.config.js.timestamp-*
23 | vite.config.ts.timestamp-*
24 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | engine-strict=true
2 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # Package Managers
2 | package-lock.json
3 | pnpm-lock.yaml
4 | yarn.lock
5 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "useTabs": true,
3 | "singleQuote": true,
4 | "trailingComma": "none",
5 | "printWidth": 100,
6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
7 | "overrides": [
8 | {
9 | "files": "*.svelte",
10 | "options": {
11 | "parser": "svelte"
12 | }
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 Yurii Hulyk
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | > [!TIP]
2 | > Now time picker works with Svelte 4 and Svelte 5 as shadcn or headless!
3 |
4 | # Time Picker Component for Svelte
5 |
6 | This project is a Svelte adaptation of the [TimePickerInput](https://time.openstatus.dev/) component, originally built with React and Shadcn UI. The Svelte version utilizes [svelte-shadcn](https://next.shadcn-svelte.com/docs/installation/sveltekit) components to provide a customizable and accessible time picker for your applications.
7 |
8 | ## Features
9 |
10 | - **Keyboard Navigation**: Supports arrow key navigation for time selection.
11 | - **Date Formatting**: Formats date values for display.
12 | - **Mobile Optimization**: Enhances mobile keyboard interactions for time input.
13 |
14 | ## Demo
15 | - **Docs/Demo**: [link](https://time-picker.nouro.app/)
16 | - **REPL playground**: [link](https://www.sveltelab.dev/zjcmgpaa2tzlylx)
17 |
18 | ## Installation
19 |
20 | **Note**: This component currently works only with SvelteKit projects. Attempting to set up shadcn-svelte on a Svelte project without SvelteKit may result in issues. (But you can use it as headless)
21 |
22 | > [!TIP]
23 | > Time picker can be used without shadcn, but you should a little change code (few lines)
24 |
25 | Before integrating the time picker component, ensure your project includes the following dependencies:
26 |
27 | - [bits-ui](https://www.npmjs.com/package/bits-ui)
28 | - [@internationalized/date](https://www.npmjs.com/package/@internationalized/date)
29 |
30 | Additionally, install the necessary svelte-shadcn components:
31 |
32 | - Input (required)
33 | - Label (optional, for [time-picker.svelte](./src/lib/snippets/time-picker.svelte))
34 | - Select (optional, for period selection)
35 | - Calendar (optional, for date and time selection)
36 | - Popover (optional, for date and time selection)
37 | - Button (optional, for date and time selection)
38 |
39 | ### Steps to Install (shadcn/ui)
40 |
41 | 1. **Initialize shadcn-svelte in your SvelteKit project**:
42 |
43 | ```bash
44 | npx shadcn-svelte@latest init
45 | ```
46 |
47 | Follow the prompts to configure your project. Ensure you have TypeScript and Tailwind CSS set up, as the command assumes a SvelteKit project with these configurations.
48 |
49 | 2. **Add the required components**:
50 |
51 | ```bash
52 | npx shadcn-svelte@latest add input
53 |
54 | # Optional
55 | npx shadcn-svelte@latest add select label calendar popover
56 | ```
57 |
58 | This command will add the specified components to your project.
59 |
60 | 3. **Install additional dependencies**:
61 |
62 | ```bash
63 | npm install @internationalized/date
64 | ```
65 |
66 | Ensure these packages are added to your `package.json` and installed in your project.
67 |
68 | 4. **Copy main snippets**
69 |
70 | Copy [`time-picker-input.svelte`](./src/lib/snippets/shadcn-svelte-runes/time-picker-input.svelte) and [`time-picker-utils.ts`](./src/lib/snippets/shadcn-svelte-runes/time-picker-utils.ts) into your project. (For example into `$lib/components/ui/time-picker`)
71 |
72 | ### Steps to Install (Headless)
73 |
74 | 1. **Install dependencies**:
75 |
76 | ```bash
77 | npm install @internationalized/date
78 | ```
79 |
80 | Ensure these packages are added to your `package.json` and installed in your project.
81 |
82 | 2. **Copy main snippets**
83 |
84 | Copy [`time-picker-input.svelte`](./src/lib/snippets/svelte-runes/time-picker-input.svelte) and [`time-picker-utils.ts`](./src/lib/snippets/svelte-runes/time-picker-utils.ts) into your project. (For example into `$lib/components/ui/time-picker`)
85 |
86 | ## Usage
87 |
88 | After installation, you can [copy snippets](./src/lib/snippets/) and use the time picker components in your Svelte files. (can be without shadcn)
89 |
90 | Customize the component as needed to fit your application's requirements.
91 |
92 | ## Acknowledgments
93 |
94 | This component is inspired by the original [TimePicker](https://time.openstatus.dev/) by OpenStatus and utilizes components from [svelte-shadcn](https://next.shadcn-svelte.com/docs/installation/sveltekit).
95 | Special thanks to the contributors of these projects for their excellent work.
96 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1bye/time-picker-svelte/7016b82b8f42bdc48cdf10ff1164ee31d5024e84/bun.lockb
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://next.shadcn-svelte.com/schema.json",
3 | "style": "default",
4 | "tailwind": {
5 | "config": "tailwind.config.ts",
6 | "css": "src\\app.css",
7 | "baseColor": "slate"
8 | },
9 | "aliases": {
10 | "components": "$lib/components",
11 | "utils": "$lib/utils",
12 | "ui": "$lib/components/ui",
13 | "hooks": "$lib/hooks"
14 | },
15 | "typescript": true,
16 | "registry": "https://next.shadcn-svelte.com/registry"
17 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "time-picker-svelte",
3 | "private": true,
4 | "version": "0.0.1",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite dev",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
11 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
12 | "format": "prettier --write .",
13 | "lint": "prettier --check ."
14 | },
15 | "devDependencies": {
16 | "@sveltejs/adapter-auto": "^3.0.0",
17 | "@sveltejs/adapter-vercel": "^5.5.2",
18 | "@sveltejs/kit": "^2.0.0",
19 | "@sveltejs/vite-plugin-svelte": "^4.0.0",
20 | "autoprefixer": "^10.4.20",
21 | "bits-ui": "^1.0.0-next.74",
22 | "clsx": "^2.1.1",
23 | "formsnap": "^2.0.0",
24 | "lucide-svelte": "^0.469.0",
25 | "mode-watcher": "^0.5.0",
26 | "prettier": "^3.3.2",
27 | "prettier-plugin-svelte": "^3.2.6",
28 | "prettier-plugin-tailwindcss": "^0.6.5",
29 | "svelte": "^5.0.0",
30 | "svelte-check": "^4.0.0",
31 | "tailwind-merge": "^2.6.0",
32 | "tailwind-variants": "^0.3.0",
33 | "tailwindcss": "^3.4.9",
34 | "tailwindcss-animate": "^1.0.7",
35 | "typescript": "^5.0.0",
36 | "vite": "^5.4.11"
37 | },
38 | "dependencies": {
39 | "@internationalized/date": "^3.6.0",
40 | "highlight.js": "^11.11.1",
41 | "highlight.svelte": "^0.1.2",
42 | "svelte-meta-tags": "^4.0.4",
43 | "svelte-sonner": "^0.3.28",
44 | "sveltekit-superforms": "^2.22.1",
45 | "zod": "^3.24.1"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | };
7 |
--------------------------------------------------------------------------------
/src/app.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @import url('https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');
6 |
7 | @layer base {
8 | :root {
9 | --background: 0 0% 100%;
10 | --foreground: 222.2 84% 4.9%;
11 | --muted: 210 40% 96.1%;
12 | --muted-foreground: 215.4 16.3% 46.9%;
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 222.2 84% 4.9%;
15 | --card: 0 0% 100%;
16 | --card-foreground: 222.2 84% 4.9%;
17 | --border: 214.3 31.8% 91.4%;
18 | --input: 214.3 31.8% 91.4%;
19 | --primary: 222.2 47.4% 11.2%;
20 | --primary-foreground: 210 40% 98%;
21 | --secondary: 210 40% 96.1%;
22 | --secondary-foreground: 222.2 47.4% 11.2%;
23 | --accent: 210 40% 96.1%;
24 | --accent-foreground: 222.2 47.4% 11.2%;
25 | --destructive: 0 72.2% 50.6%;
26 | --destructive-foreground: 210 40% 98%;
27 | --ring: 222.2 84% 4.9%;
28 | --radius: 0.5rem;
29 | --sidebar-background: 0 0% 98%;
30 | --sidebar-foreground: 240 5.3% 26.1%;
31 | --sidebar-primary: 240 5.9% 10%;
32 | --sidebar-primary-foreground: 0 0% 98%;
33 | --sidebar-accent: 240 4.8% 95.9%;
34 | --sidebar-accent-foreground: 240 5.9% 10%;
35 | --sidebar-border: 220 13% 91%;
36 | --sidebar-ring: 217.2 91.2% 59.8%;
37 | }
38 |
39 | .dark {
40 | --background: 222.2 84% 4.9%;
41 | --foreground: 210 40% 98%;
42 | --muted: 217.2 32.6% 17.5%;
43 | --muted-foreground: 215 20.2% 65.1%;
44 | --popover: 222.2 84% 4.9%;
45 | --popover-foreground: 210 40% 98%;
46 | --card: 222.2 84% 4.9%;
47 | --card-foreground: 210 40% 98%;
48 | --border: 217.2 32.6% 17.5%;
49 | --input: 217.2 32.6% 17.5%;
50 | --primary: 210 40% 98%;
51 | --primary-foreground: 222.2 47.4% 11.2%;
52 | --secondary: 217.2 32.6% 17.5%;
53 | --secondary-foreground: 210 40% 98%;
54 | --accent: 217.2 32.6% 17.5%;
55 | --accent-foreground: 210 40% 98%;
56 | --destructive: 0 62.8% 30.6%;
57 | --destructive-foreground: 210 40% 98%;
58 | --ring: 212.7 26.8% 83.9%;
59 | --sidebar-background: 240 5.9% 10%;
60 | --sidebar-foreground: 240 4.8% 95.9%;
61 | --sidebar-primary: 224.3 76.3% 48%;
62 | --sidebar-primary-foreground: 0 0% 100%;
63 | --sidebar-accent: 240 3.7% 15.9%;
64 | --sidebar-accent-foreground: 240 4.8% 95.9%;
65 | --sidebar-border: 240 3.7% 15.9%;
66 | --sidebar-ring: 217.2 91.2% 59.8%;
67 | }
68 | }
69 |
70 | @layer base {
71 | * {
72 | @apply border-border;
73 | }
74 | body {
75 | @apply bg-background text-foreground;
76 | }
77 | }
--------------------------------------------------------------------------------
/src/app.d.ts:
--------------------------------------------------------------------------------
1 | // See https://svelte.dev/docs/kit/types#app.d.ts
2 | // for information about these interfaces
3 | declare global {
4 | namespace App {
5 | // interface Error {}
6 | // interface Locals {}
7 | // interface PageData {}
8 | // interface PageState {}
9 | // interface Platform {}
10 | }
11 | }
12 |
13 | export {};
14 |
--------------------------------------------------------------------------------
/src/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | %sveltekit.head%
8 |
9 |
10 | %sveltekit.body%
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/theme/toggle-mode/index.ts:
--------------------------------------------------------------------------------
1 | export { default as ModeToggle } from "./toggle-mode.svelte";
--------------------------------------------------------------------------------
/src/lib/components/theme/toggle-mode/toggle-mode.svelte:
--------------------------------------------------------------------------------
1 |
8 |
9 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/accordion-content.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
21 |
22 | {@render children?.()}
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/accordion-item.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/accordion-trigger.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | svg]:rotate-180",
22 | className
23 | )}
24 | {...restProps}
25 | >
26 | {@render children?.()}
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/src/lib/components/ui/accordion/index.ts:
--------------------------------------------------------------------------------
1 | import { Accordion as AccordionPrimitive } from "bits-ui";
2 | import Content from "./accordion-content.svelte";
3 | import Item from "./accordion-item.svelte";
4 | import Trigger from "./accordion-trigger.svelte";
5 | const Root = AccordionPrimitive.Root;
6 |
7 | export {
8 | Root,
9 | Content,
10 | Item,
11 | Trigger,
12 | //
13 | Root as Accordion,
14 | Content as AccordionContent,
15 | Item as AccordionItem,
16 | Trigger as AccordionTrigger,
17 | };
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/button/button.svelte:
--------------------------------------------------------------------------------
1 |
40 |
41 |
55 |
56 | {#if href}
57 |
63 | {@render children?.()}
64 |
65 | {:else}
66 |
74 | {/if}
75 |
--------------------------------------------------------------------------------
/src/lib/components/ui/button/index.ts:
--------------------------------------------------------------------------------
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 | };
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-cell.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-day.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
31 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid-body.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid-head.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid-row.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-grid.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-head-cell.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-header.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-heading.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-months.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
19 | {@render children?.()}
20 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-next-button.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#snippet Fallback()}
16 |
17 | {/snippet}
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar-prev-button.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 | {#snippet Fallback()}
16 |
17 | {/snippet}
18 |
19 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/calendar.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
20 |
28 | {#snippet children({ months, weekdays })}
29 |
30 |
31 |
32 |
33 |
34 |
35 | {#each months as month}
36 |
37 |
38 |
39 | {#each weekdays as weekday}
40 |
41 | {weekday.slice(0, 2)}
42 |
43 | {/each}
44 |
45 |
46 |
47 | {#each month.weeks as weekDates}
48 |
49 | {#each weekDates as date}
50 |
51 |
52 |
53 | {/each}
54 |
55 | {/each}
56 |
57 |
58 | {/each}
59 |
60 | {/snippet}
61 |
62 |
--------------------------------------------------------------------------------
/src/lib/components/ui/calendar/index.ts:
--------------------------------------------------------------------------------
1 | import Root from "./calendar.svelte";
2 | import Cell from "./calendar-cell.svelte";
3 | import Day from "./calendar-day.svelte";
4 | import Grid from "./calendar-grid.svelte";
5 | import Header from "./calendar-header.svelte";
6 | import Months from "./calendar-months.svelte";
7 | import GridRow from "./calendar-grid-row.svelte";
8 | import Heading from "./calendar-heading.svelte";
9 | import GridBody from "./calendar-grid-body.svelte";
10 | import GridHead from "./calendar-grid-head.svelte";
11 | import HeadCell from "./calendar-head-cell.svelte";
12 | import NextButton from "./calendar-next-button.svelte";
13 | import PrevButton from "./calendar-prev-button.svelte";
14 |
15 | export {
16 | Day,
17 | Cell,
18 | Grid,
19 | Header,
20 | Months,
21 | GridRow,
22 | Heading,
23 | GridBody,
24 | GridHead,
25 | HeadCell,
26 | NextButton,
27 | PrevButton,
28 | //
29 | Root as Calendar,
30 | };
31 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-button.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-description.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-element-field.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
23 |
24 |
25 | {#snippet children({ constraints, errors, tainted, value })}
26 |
27 | {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
28 |
29 | {/snippet}
30 |
31 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-field-errors.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
22 | {#snippet children({ errors, errorProps })}
23 | {#if childrenProp}
24 | {@render childrenProp({ errors, errorProps })}
25 | {:else}
26 | {#each errors as error}
27 | {error}
28 | {/each}
29 | {/if}
30 | {/snippet}
31 |
32 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-field.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
23 |
24 |
25 | {#snippet children({ constraints, errors, tainted, value })}
26 |
27 | {@render childrenProp?.({ constraints, errors, tainted, value: value as T[U] })}
28 |
29 | {/snippet}
30 |
31 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-fieldset.svelte:
--------------------------------------------------------------------------------
1 |
6 |
7 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-label.svelte:
--------------------------------------------------------------------------------
1 |
14 |
15 |
16 | {#snippet child({ props })}
17 |
20 | {/snippet}
21 |
22 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/form-legend.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/form/index.ts:
--------------------------------------------------------------------------------
1 | import * as FormPrimitive from "formsnap";
2 | import Description from "./form-description.svelte";
3 | import Label from "./form-label.svelte";
4 | import FieldErrors from "./form-field-errors.svelte";
5 | import Field from "./form-field.svelte";
6 | import Fieldset from "./form-fieldset.svelte";
7 | import Legend from "./form-legend.svelte";
8 | import ElementField from "./form-element-field.svelte";
9 | import Button from "./form-button.svelte";
10 |
11 | const Control = FormPrimitive.Control;
12 |
13 | export {
14 | Field,
15 | Control,
16 | Label,
17 | Button,
18 | FieldErrors,
19 | Description,
20 | Fieldset,
21 | Legend,
22 | ElementField,
23 | //
24 | Field as FormField,
25 | Control as FormControl,
26 | Description as FormDescription,
27 | Label as FormLabel,
28 | FieldErrors as FormFieldErrors,
29 | Fieldset as FormFieldset,
30 | Legend as FormLegend,
31 | ElementField as FormElementField,
32 | Button as FormButton,
33 | };
34 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input/index.ts:
--------------------------------------------------------------------------------
1 | import Root from "./input.svelte";
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Input,
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/input/input.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/label/index.ts:
--------------------------------------------------------------------------------
1 | import Root from "./label.svelte";
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Label,
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/label/label.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/popover/index.ts:
--------------------------------------------------------------------------------
1 | import { Popover as PopoverPrimitive } from "bits-ui";
2 | import Content from "./popover-content.svelte";
3 | const Root = PopoverPrimitive.Root;
4 | const Trigger = PopoverPrimitive.Trigger;
5 | const Close = PopoverPrimitive.Close;
6 |
7 | export {
8 | Root,
9 | Content,
10 | Trigger,
11 | Close,
12 | //
13 | Root as Popover,
14 | Content as PopoverContent,
15 | Trigger as PopoverTrigger,
16 | Close as PopoverClose,
17 | };
18 |
--------------------------------------------------------------------------------
/src/lib/components/ui/popover/popover-content.svelte:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 |
28 |
29 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/index.ts:
--------------------------------------------------------------------------------
1 | import { Select as SelectPrimitive } from "bits-ui";
2 |
3 | import GroupHeading from "./select-group-heading.svelte";
4 | import Item from "./select-item.svelte";
5 | import Content from "./select-content.svelte";
6 | import Trigger from "./select-trigger.svelte";
7 | import Separator from "./select-separator.svelte";
8 | import ScrollDownButton from "./select-scroll-down-button.svelte";
9 | import ScrollUpButton from "./select-scroll-up-button.svelte";
10 |
11 | const Root = SelectPrimitive.Root;
12 | const Group = SelectPrimitive.Group;
13 |
14 | export {
15 | Root,
16 | Group,
17 | GroupHeading,
18 | Item,
19 | Content,
20 | Trigger,
21 | Separator,
22 | ScrollDownButton,
23 | ScrollUpButton,
24 | //
25 | Root as Select,
26 | Group as SelectGroup,
27 | GroupHeading as SelectGroupHeading,
28 | Item as SelectItem,
29 | Content as SelectContent,
30 | Trigger as SelectTrigger,
31 | Separator as SelectSeparator,
32 | ScrollDownButton as SelectScrollDownButton,
33 | ScrollUpButton as SelectScrollUpButton,
34 | };
35 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-content.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
20 |
29 |
30 |
35 | {@render children?.()}
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-group-heading.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
17 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-item.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
25 | {#snippet children({ selected, highlighted })}
26 |
27 | {#if selected}
28 |
29 | {/if}
30 |
31 | {#if childrenProp}
32 | {@render childrenProp({ selected, highlighted })}
33 | {:else}
34 | {label || value}
35 | {/if}
36 | {/snippet}
37 |
38 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-scroll-down-button.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-scroll-up-button.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-separator.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/lib/components/ui/select/select-trigger.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 | span]:line-clamp-1",
18 | className
19 | )}
20 | {...restProps}
21 | >
22 | {@render children?.()}
23 |
24 |
25 |
--------------------------------------------------------------------------------
/src/lib/components/ui/separator/index.ts:
--------------------------------------------------------------------------------
1 | import Root from "./separator.svelte";
2 |
3 | export {
4 | Root,
5 | //
6 | Root as Separator,
7 | };
8 |
--------------------------------------------------------------------------------
/src/lib/components/ui/separator/separator.svelte:
--------------------------------------------------------------------------------
1 |
12 |
13 |
23 |
--------------------------------------------------------------------------------
/src/lib/components/ui/snippet/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Snippet } from "./snippet.svelte";
--------------------------------------------------------------------------------
/src/lib/components/ui/snippet/pre.svelte:
--------------------------------------------------------------------------------
1 |
38 |
39 |
40 |
53 |
{@render props?.children?.()}
60 |
61 |
--------------------------------------------------------------------------------
/src/lib/components/ui/snippet/snippet.svelte:
--------------------------------------------------------------------------------
1 |
95 |
96 |
97 |
98 | {@html hljs.highlightAuto(snippet.content).value}
99 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sonner/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Toaster } from "./sonner.svelte";
2 |
--------------------------------------------------------------------------------
/src/lib/components/ui/sonner/sonner.svelte:
--------------------------------------------------------------------------------
1 |
7 |
8 |
21 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/index.ts:
--------------------------------------------------------------------------------
1 | import { Tabs as TabsPrimitive } from "bits-ui";
2 | import Content from "./tabs-content.svelte";
3 | import List from "./tabs-list.svelte";
4 | import Trigger from "./tabs-trigger.svelte";
5 |
6 | const Root = TabsPrimitive.Root;
7 |
8 | export {
9 | Root,
10 | Content,
11 | List,
12 | Trigger,
13 | //
14 | Root as Tabs,
15 | Content as TabsContent,
16 | List as TabsList,
17 | Trigger as TabsTrigger,
18 | };
19 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs-content.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs-list.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
20 |
--------------------------------------------------------------------------------
/src/lib/components/ui/tabs/tabs-trigger.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
20 |
--------------------------------------------------------------------------------
/src/lib/hooks/use-mouse-move.ts:
--------------------------------------------------------------------------------
1 | export function useMouseMove() {
2 | function mouseMoveEvent(e: MouseEvent) {
3 | const scale = window.visualViewport?.scale;
4 | // disable mouse movement on viewport zoom - causes page to slow down
5 | if (scale === 1) {
6 | const body = document.body;
7 |
8 | const targetX = e.clientX;
9 | const targetY = e.clientY;
10 |
11 | // the animation requires tranformX and transformY on the HTML Element
12 | body.style.setProperty("--x", `${targetX}px`);
13 | body.style.setProperty("--y", `${targetY}px`);
14 | }
15 | }
16 |
17 | document.addEventListener("mousemove", mouseMoveEvent);
18 | return () => {
19 | document.removeEventListener("mousemove", mouseMoveEvent);
20 | };
21 | }
--------------------------------------------------------------------------------
/src/lib/index.ts:
--------------------------------------------------------------------------------
1 | // place files you want to import through the `$lib` alias in this folder.
2 |
--------------------------------------------------------------------------------
/src/lib/snippets-content.ts:
--------------------------------------------------------------------------------
1 | import type { Snippet } from './types';
2 | import { read } from '$app/server';
3 |
4 | export async function loadAllSnippets(): Promise {
5 | const files = import.meta.glob('./snippets/**', {
6 | query: '?url',
7 | import: 'default',
8 | eager: true
9 | });
10 |
11 | const promises = Object.entries(files).map(async ([_fileName, filePath]) => {
12 | const fileName = _fileName.replace("snippets/", "")
13 | const res = read(filePath as string);
14 |
15 | const [,folder, name] = fileName.split("/");
16 |
17 | return {
18 | folder,
19 | slug: filePath as string,
20 | file: name,
21 | content: await res.text()
22 | };
23 | });
24 |
25 | return await Promise.all(promises);
26 | }
27 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-4/time-picker-input.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
110 |
111 | {
121 | e.preventDefault();
122 | }}
123 | {type}
124 | inputmode="decimal"
125 | onkeydown={handleKeyDown}
126 | {...$$restProps}
127 | />
128 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-4/time-picker-utils.ts:
--------------------------------------------------------------------------------
1 | import { Time } from '@internationalized/date';
2 |
3 | /**
4 | * regular expression to check for valid hour format (01-23)
5 | */
6 | export function isValidHour(value: string) {
7 | return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
8 | }
9 |
10 | /**
11 | * regular expression to check for valid 12 hour format (01-12)
12 | */
13 | export function isValid12Hour(value: string) {
14 | return /^(0[1-9]|1[0-2])$/.test(value);
15 | }
16 |
17 | /**
18 | * regular expression to check for valid minute format (00-59)
19 | */
20 | export function isValidMinuteOrSecond(value: string) {
21 | return /^[0-5][0-9]$/.test(value);
22 | }
23 |
24 | type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
25 |
26 | export function getValidNumber(
27 | value: string,
28 | { max, min = 0, loop = false }: GetValidNumberConfig
29 | ) {
30 | let numericValue = parseInt(value, 10);
31 |
32 | if (!isNaN(numericValue)) {
33 | if (!loop) {
34 | if (numericValue > max) numericValue = max;
35 | if (numericValue < min) numericValue = min;
36 | } else {
37 | if (numericValue > max) numericValue = min;
38 | if (numericValue < min) numericValue = max;
39 | }
40 | return numericValue.toString().padStart(2, "0");
41 | }
42 |
43 | return "00";
44 | }
45 |
46 | export function getValidHour(value: string) {
47 | if (isValidHour(value)) return value;
48 | return getValidNumber(value, { max: 23 });
49 | }
50 |
51 | export function getValid12Hour(value: string) {
52 | if (isValid12Hour(value)) return value;
53 | return getValidNumber(value, { min: 1, max: 12 });
54 | }
55 |
56 | export function getValidMinuteOrSecond(value: string) {
57 | if (isValidMinuteOrSecond(value)) return value;
58 | return getValidNumber(value, { max: 59 });
59 | }
60 |
61 | type GetValidArrowNumberConfig = {
62 | min: number;
63 | max: number;
64 | step: number;
65 | };
66 |
67 | export function getValidArrowNumber(
68 | value: string,
69 | { min, max, step }: GetValidArrowNumberConfig
70 | ) {
71 | let numericValue = parseInt(value, 10);
72 | if (!isNaN(numericValue)) {
73 | numericValue += step;
74 | return getValidNumber(String(numericValue), { min, max, loop: true });
75 | }
76 | return "00";
77 | }
78 |
79 | export function getValidArrowHour(value: string, step: number) {
80 | return getValidArrowNumber(value, { min: 0, max: 23, step });
81 | }
82 |
83 | export function getValidArrow12Hour(value: string, step: number) {
84 | return getValidArrowNumber(value, { min: 1, max: 12, step });
85 | }
86 |
87 | export function getValidArrowMinuteOrSecond(value: string, step: number) {
88 | return getValidArrowNumber(value, { min: 0, max: 59, step });
89 | }
90 |
91 | export function setMinutes(time: Time, value: string) {
92 | const minutes = getValidMinuteOrSecond(value);
93 | return time.set({ minute: parseInt(minutes, 10) });
94 | }
95 |
96 | export function setSeconds(time: Time, value: string) {
97 | const seconds = getValidMinuteOrSecond(value);
98 | return time.set({ second: parseInt(seconds, 10) });
99 | }
100 |
101 | export function setHours(time: Time, value: string) {
102 | const hours = getValidHour(value);
103 | return time.set({ hour: parseInt(hours, 10) });
104 | }
105 |
106 | export function set12Hours(time: Time, value: string, period: Period) {
107 | const hours = parseInt(getValid12Hour(value), 10);
108 | const convertedHours = convert12HourTo24Hour(hours, period);
109 | return time.set({ hour: convertedHours });
110 | }
111 |
112 | export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
113 | export type Period = "AM" | "PM";
114 |
115 | export function setDateByType(
116 | time: Time,
117 | value: string,
118 | type: TimePickerType,
119 | period?: Period
120 | ) {
121 | switch (type) {
122 | case "minutes":
123 | return setMinutes(time, value);
124 | case "seconds":
125 | return setSeconds(time, value);
126 | case "hours":
127 | return setHours(time, value);
128 | case "12hours": {
129 | if (!period) return time;
130 | return set12Hours(time, value, period);
131 | }
132 | default:
133 | return time;
134 | }
135 | }
136 |
137 | export function getDateByType(time: Time, type: TimePickerType) {
138 | switch (type) {
139 | case "minutes":
140 | return getValidMinuteOrSecond(String(time.minute));
141 | case "seconds":
142 | return getValidMinuteOrSecond(String(time.second));
143 | case "hours":
144 | return getValidHour(String(time.hour));
145 | case "12hours":
146 | const hours = display12HourValue(time.hour);
147 | return getValid12Hour(String(hours));
148 | default:
149 | return "00";
150 | }
151 | }
152 |
153 | export function getArrowByType(
154 | value: string,
155 | step: number,
156 | type: TimePickerType
157 | ) {
158 | switch (type) {
159 | case "minutes":
160 | return getValidArrowMinuteOrSecond(value, step);
161 | case "seconds":
162 | return getValidArrowMinuteOrSecond(value, step);
163 | case "hours":
164 | return getValidArrowHour(value, step);
165 | case "12hours":
166 | return getValidArrow12Hour(value, step);
167 | default:
168 | return "00";
169 | }
170 | }
171 |
172 | /**
173 | * handles value change of 12-hour input
174 | * 12:00 PM is 12:00
175 | * 12:00 AM is 00:00
176 | */
177 | export function convert12HourTo24Hour(hour: number, period: Period) {
178 | if (period === "PM") {
179 | if (hour <= 11) {
180 | return hour + 12;
181 | } else {
182 | return hour;
183 | }
184 | } else if (period === "AM") {
185 | if (hour === 12) return 0;
186 | return hour;
187 | }
188 | return hour;
189 | }
190 |
191 | /**
192 | * time is stored in the 24-hour form,
193 | * but needs to be displayed to the user
194 | * in its 12-hour representation
195 | */
196 | export function display12HourValue(hours: number) {
197 | if (hours === 0 || hours === 12) return "12";
198 | if (hours >= 22) return `${hours - 12}`;
199 | if (hours % 12 > 9) return `${hours}`;
200 | return `0${hours % 12}`;
201 | }
202 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-4/time-picker.svelte:
--------------------------------------------------------------------------------
1 |
23 |
24 |
25 |
26 | {#if view === 'labels'}
27 |
28 | {/if}
29 |
30 | minuteRef?.focus()}
36 | />
37 |
38 |
39 | {#if view === 'dotted'}
40 |
:
41 | {/if}
42 |
43 |
44 | {#if view === 'labels'}
45 |
46 | {/if}
47 |
48 | hourRef?.focus()}
54 | onRightFocus={() => secondRef?.focus()}
55 | />
56 |
57 |
58 | {#if view === 'dotted'}
59 |
:
60 | {/if}
61 |
62 |
63 | {#if view === 'labels'}
64 |
65 | {/if}
66 |
67 | minuteRef?.focus()}
73 | />
74 |
75 |
76 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/date-time-picker-form.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
49 |
50 |
52 |
53 | {#snippet children({ props })}
54 | Date of birth
55 | {
58 | if (v) {
59 | $formData.dob = v.toString();
60 | } else {
61 | $formData.dob = '';
62 | }
63 | }}
64 | />
65 | Your date of birth is used to calculator your age
66 |
67 |
68 | {/snippet}
69 |
70 |
71 |
72 | {#if browser}
73 |
74 | {/if}
75 |
76 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/date-time-picker.svelte:
--------------------------------------------------------------------------------
1 |
63 |
64 |
65 |
74 |
75 | {date ? df.format(date.toDate(getLocalTimeZone())) : "Pick a date"}
76 |
77 |
78 |
79 | {
82 | time && setTime(time);
83 | }}
84 | />
85 |
86 |
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/time-period-select.svelte:
--------------------------------------------------------------------------------
1 |
18 |
19 |
72 |
73 |
74 | handleValueChange(value as Period)}
78 | >
79 | {period ?? ''}
84 |
85 | AM
86 | PM
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/time-picker-12h.svelte:
--------------------------------------------------------------------------------
1 |
32 |
33 |
34 |
35 | {#if view === 'labels'}
36 |
37 | {/if}
38 |
39 | minuteRef?.focus()}
46 | />
47 |
48 |
49 | {#if view === 'dotted'}
50 |
:
51 | {/if}
52 |
53 |
54 | {#if view === 'labels'}
55 |
56 | {/if}
57 |
58 | hourRef?.focus()}
64 | onRightFocus={() => secondRef?.focus()}
65 | />
66 |
67 |
68 | {#if view === 'dotted'}
69 |
:
70 | {/if}
71 |
72 |
73 | {#if view === 'labels'}
74 |
75 | {/if}
76 |
77 | minuteRef?.focus()}
83 | onRightFocus={() => periodRef?.focus()}
84 | />
85 |
86 |
87 |
88 | {#if view === 'labels'}
89 |
90 | {/if}
91 |
92 | secondRef?.focus()}
99 | />
100 |
101 |
102 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/time-picker-input.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
114 |
115 | {
125 | e.preventDefault();
126 | onchange?.(e);
127 | }}
128 | {type}
129 | inputmode="decimal"
130 | onkeydown={(e) => {
131 | onkeydown?.(e);
132 | handleKeyDown(e);
133 | }}
134 | {...restProps} />
135 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/time-picker-utils.ts:
--------------------------------------------------------------------------------
1 | import { Time } from '@internationalized/date';
2 |
3 | /**
4 | * regular expression to check for valid hour format (01-23)
5 | */
6 | export function isValidHour(value: string) {
7 | return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
8 | }
9 |
10 | /**
11 | * regular expression to check for valid 12 hour format (01-12)
12 | */
13 | export function isValid12Hour(value: string) {
14 | return /^(0[1-9]|1[0-2])$/.test(value);
15 | }
16 |
17 | /**
18 | * regular expression to check for valid minute format (00-59)
19 | */
20 | export function isValidMinuteOrSecond(value: string) {
21 | return /^[0-5][0-9]$/.test(value);
22 | }
23 |
24 | type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
25 |
26 | export function getValidNumber(
27 | value: string,
28 | { max, min = 0, loop = false }: GetValidNumberConfig
29 | ) {
30 | let numericValue = parseInt(value, 10);
31 |
32 | if (!isNaN(numericValue)) {
33 | if (!loop) {
34 | if (numericValue > max) numericValue = max;
35 | if (numericValue < min) numericValue = min;
36 | } else {
37 | if (numericValue > max) numericValue = min;
38 | if (numericValue < min) numericValue = max;
39 | }
40 | return numericValue.toString().padStart(2, "0");
41 | }
42 |
43 | return "00";
44 | }
45 |
46 | export function getValidHour(value: string) {
47 | if (isValidHour(value)) return value;
48 | return getValidNumber(value, { max: 23 });
49 | }
50 |
51 | export function getValid12Hour(value: string) {
52 | if (isValid12Hour(value)) return value;
53 | return getValidNumber(value, { min: 1, max: 12 });
54 | }
55 |
56 | export function getValidMinuteOrSecond(value: string) {
57 | if (isValidMinuteOrSecond(value)) return value;
58 | return getValidNumber(value, { max: 59 });
59 | }
60 |
61 | type GetValidArrowNumberConfig = {
62 | min: number;
63 | max: number;
64 | step: number;
65 | };
66 |
67 | export function getValidArrowNumber(
68 | value: string,
69 | { min, max, step }: GetValidArrowNumberConfig
70 | ) {
71 | let numericValue = parseInt(value, 10);
72 | if (!isNaN(numericValue)) {
73 | numericValue += step;
74 | return getValidNumber(String(numericValue), { min, max, loop: true });
75 | }
76 | return "00";
77 | }
78 |
79 | export function getValidArrowHour(value: string, step: number) {
80 | return getValidArrowNumber(value, { min: 0, max: 23, step });
81 | }
82 |
83 | export function getValidArrow12Hour(value: string, step: number) {
84 | return getValidArrowNumber(value, { min: 1, max: 12, step });
85 | }
86 |
87 | export function getValidArrowMinuteOrSecond(value: string, step: number) {
88 | return getValidArrowNumber(value, { min: 0, max: 59, step });
89 | }
90 |
91 | export function setMinutes(time: Time, value: string) {
92 | const minutes = getValidMinuteOrSecond(value);
93 | return time.set({ minute: parseInt(minutes, 10) });
94 | }
95 |
96 | export function setSeconds(time: Time, value: string) {
97 | const seconds = getValidMinuteOrSecond(value);
98 | return time.set({ second: parseInt(seconds, 10) });
99 | }
100 |
101 | export function setHours(time: Time, value: string) {
102 | const hours = getValidHour(value);
103 | return time.set({ hour: parseInt(hours, 10) });
104 | }
105 |
106 | export function set12Hours(time: Time, value: string, period: Period) {
107 | const hours = parseInt(getValid12Hour(value), 10);
108 | const convertedHours = convert12HourTo24Hour(hours, period);
109 | return time.set({ hour: convertedHours });
110 | }
111 |
112 | export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
113 | export type Period = "AM" | "PM";
114 |
115 | export function setDateByType(
116 | time: Time,
117 | value: string,
118 | type: TimePickerType,
119 | period?: Period
120 | ) {
121 | switch (type) {
122 | case "minutes":
123 | return setMinutes(time, value);
124 | case "seconds":
125 | return setSeconds(time, value);
126 | case "hours":
127 | return setHours(time, value);
128 | case "12hours": {
129 | if (!period) return time;
130 | return set12Hours(time, value, period);
131 | }
132 | default:
133 | return time;
134 | }
135 | }
136 |
137 | export function getDateByType(time: Time, type: TimePickerType) {
138 | switch (type) {
139 | case "minutes":
140 | return getValidMinuteOrSecond(String(time.minute));
141 | case "seconds":
142 | return getValidMinuteOrSecond(String(time.second));
143 | case "hours":
144 | return getValidHour(String(time.hour));
145 | case "12hours":
146 | const hours = display12HourValue(time.hour);
147 | return getValid12Hour(String(hours));
148 | default:
149 | return "00";
150 | }
151 | }
152 |
153 | export function getArrowByType(
154 | value: string,
155 | step: number,
156 | type: TimePickerType
157 | ) {
158 | switch (type) {
159 | case "minutes":
160 | return getValidArrowMinuteOrSecond(value, step);
161 | case "seconds":
162 | return getValidArrowMinuteOrSecond(value, step);
163 | case "hours":
164 | return getValidArrowHour(value, step);
165 | case "12hours":
166 | return getValidArrow12Hour(value, step);
167 | default:
168 | return "00";
169 | }
170 | }
171 |
172 | /**
173 | * handles value change of 12-hour input
174 | * 12:00 PM is 12:00
175 | * 12:00 AM is 00:00
176 | */
177 | export function convert12HourTo24Hour(hour: number, period: Period) {
178 | if (period === "PM") {
179 | if (hour <= 11) {
180 | return hour + 12;
181 | } else {
182 | return hour;
183 | }
184 | } else if (period === "AM") {
185 | if (hour === 12) return 0;
186 | return hour;
187 | }
188 | return hour;
189 | }
190 |
191 | /**
192 | * time is stored in the 24-hour form,
193 | * but needs to be displayed to the user
194 | * in its 12-hour representation
195 | */
196 | export function display12HourValue(hours: number) {
197 | if (hours === 0 || hours === 12) return "12";
198 | if (hours >= 22) return `${hours - 12}`;
199 | if (hours % 12 > 9) return `${hours}`;
200 | return `0${hours % 12}`;
201 | }
202 |
--------------------------------------------------------------------------------
/src/lib/snippets/shadcn-svelte-runes/time-picker.svelte:
--------------------------------------------------------------------------------
1 |
25 |
26 |
27 |
28 | {#if view === 'labels'}
29 |
30 | {/if}
31 |
32 | minuteRef?.focus()}
38 | />
39 |
40 |
41 | {#if view === 'dotted'}
42 |
:
43 | {/if}
44 |
45 |
46 | {#if view === 'labels'}
47 |
48 | {/if}
49 |
50 | hourRef?.focus()}
56 | onRightFocus={() => secondRef?.focus()}
57 | />
58 |
59 |
60 | {#if view === 'dotted'}
61 |
:
62 | {/if}
63 |
64 |
65 | {#if view === 'labels'}
66 |
67 | {/if}
68 |
69 | minuteRef?.focus()}
75 | />
76 |
77 |
78 |
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-4/cn.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-4/time-picker-input.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
109 |
110 | {
120 | e.preventDefault();
121 | }}
122 | {type}
123 | inputmode="decimal"
124 | onkeydown={handleKeyDown}
125 | {...$$restProps}
126 | />
127 |
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-4/time-picker-utils.ts:
--------------------------------------------------------------------------------
1 | import { Time } from '@internationalized/date';
2 |
3 | /**
4 | * regular expression to check for valid hour format (01-23)
5 | */
6 | export function isValidHour(value: string) {
7 | return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
8 | }
9 |
10 | /**
11 | * regular expression to check for valid 12 hour format (01-12)
12 | */
13 | export function isValid12Hour(value: string) {
14 | return /^(0[1-9]|1[0-2])$/.test(value);
15 | }
16 |
17 | /**
18 | * regular expression to check for valid minute format (00-59)
19 | */
20 | export function isValidMinuteOrSecond(value: string) {
21 | return /^[0-5][0-9]$/.test(value);
22 | }
23 |
24 | type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
25 |
26 | export function getValidNumber(
27 | value: string,
28 | { max, min = 0, loop = false }: GetValidNumberConfig
29 | ) {
30 | let numericValue = parseInt(value, 10);
31 |
32 | if (!isNaN(numericValue)) {
33 | if (!loop) {
34 | if (numericValue > max) numericValue = max;
35 | if (numericValue < min) numericValue = min;
36 | } else {
37 | if (numericValue > max) numericValue = min;
38 | if (numericValue < min) numericValue = max;
39 | }
40 | return numericValue.toString().padStart(2, "0");
41 | }
42 |
43 | return "00";
44 | }
45 |
46 | export function getValidHour(value: string) {
47 | if (isValidHour(value)) return value;
48 | return getValidNumber(value, { max: 23 });
49 | }
50 |
51 | export function getValid12Hour(value: string) {
52 | if (isValid12Hour(value)) return value;
53 | return getValidNumber(value, { min: 1, max: 12 });
54 | }
55 |
56 | export function getValidMinuteOrSecond(value: string) {
57 | if (isValidMinuteOrSecond(value)) return value;
58 | return getValidNumber(value, { max: 59 });
59 | }
60 |
61 | type GetValidArrowNumberConfig = {
62 | min: number;
63 | max: number;
64 | step: number;
65 | };
66 |
67 | export function getValidArrowNumber(
68 | value: string,
69 | { min, max, step }: GetValidArrowNumberConfig
70 | ) {
71 | let numericValue = parseInt(value, 10);
72 | if (!isNaN(numericValue)) {
73 | numericValue += step;
74 | return getValidNumber(String(numericValue), { min, max, loop: true });
75 | }
76 | return "00";
77 | }
78 |
79 | export function getValidArrowHour(value: string, step: number) {
80 | return getValidArrowNumber(value, { min: 0, max: 23, step });
81 | }
82 |
83 | export function getValidArrow12Hour(value: string, step: number) {
84 | return getValidArrowNumber(value, { min: 1, max: 12, step });
85 | }
86 |
87 | export function getValidArrowMinuteOrSecond(value: string, step: number) {
88 | return getValidArrowNumber(value, { min: 0, max: 59, step });
89 | }
90 |
91 | export function setMinutes(time: Time, value: string) {
92 | const minutes = getValidMinuteOrSecond(value);
93 | return time.set({ minute: parseInt(minutes, 10) });
94 | }
95 |
96 | export function setSeconds(time: Time, value: string) {
97 | const seconds = getValidMinuteOrSecond(value);
98 | return time.set({ second: parseInt(seconds, 10) });
99 | }
100 |
101 | export function setHours(time: Time, value: string) {
102 | const hours = getValidHour(value);
103 | return time.set({ hour: parseInt(hours, 10) });
104 | }
105 |
106 | export function set12Hours(time: Time, value: string, period: Period) {
107 | const hours = parseInt(getValid12Hour(value), 10);
108 | const convertedHours = convert12HourTo24Hour(hours, period);
109 | return time.set({ hour: convertedHours });
110 | }
111 |
112 | export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
113 | export type Period = "AM" | "PM";
114 |
115 | export function setDateByType(
116 | time: Time,
117 | value: string,
118 | type: TimePickerType,
119 | period?: Period
120 | ) {
121 | switch (type) {
122 | case "minutes":
123 | return setMinutes(time, value);
124 | case "seconds":
125 | return setSeconds(time, value);
126 | case "hours":
127 | return setHours(time, value);
128 | case "12hours": {
129 | if (!period) return time;
130 | return set12Hours(time, value, period);
131 | }
132 | default:
133 | return time;
134 | }
135 | }
136 |
137 | export function getDateByType(time: Time, type: TimePickerType) {
138 | switch (type) {
139 | case "minutes":
140 | return getValidMinuteOrSecond(String(time.minute));
141 | case "seconds":
142 | return getValidMinuteOrSecond(String(time.second));
143 | case "hours":
144 | return getValidHour(String(time.hour));
145 | case "12hours":
146 | const hours = display12HourValue(time.hour);
147 | return getValid12Hour(String(hours));
148 | default:
149 | return "00";
150 | }
151 | }
152 |
153 | export function getArrowByType(
154 | value: string,
155 | step: number,
156 | type: TimePickerType
157 | ) {
158 | switch (type) {
159 | case "minutes":
160 | return getValidArrowMinuteOrSecond(value, step);
161 | case "seconds":
162 | return getValidArrowMinuteOrSecond(value, step);
163 | case "hours":
164 | return getValidArrowHour(value, step);
165 | case "12hours":
166 | return getValidArrow12Hour(value, step);
167 | default:
168 | return "00";
169 | }
170 | }
171 |
172 | /**
173 | * handles value change of 12-hour input
174 | * 12:00 PM is 12:00
175 | * 12:00 AM is 00:00
176 | */
177 | export function convert12HourTo24Hour(hour: number, period: Period) {
178 | if (period === "PM") {
179 | if (hour <= 11) {
180 | return hour + 12;
181 | } else {
182 | return hour;
183 | }
184 | } else if (period === "AM") {
185 | if (hour === 12) return 0;
186 | return hour;
187 | }
188 | return hour;
189 | }
190 |
191 | /**
192 | * time is stored in the 24-hour form,
193 | * but needs to be displayed to the user
194 | * in its 12-hour representation
195 | */
196 | export function display12HourValue(hours: number) {
197 | if (hours === 0 || hours === 12) return "12";
198 | if (hours >= 22) return `${hours - 12}`;
199 | if (hours % 12 > 9) return `${hours}`;
200 | return `0${hours % 12}`;
201 | }
202 |
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-4/time-picker.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 | {#if view === 'labels'}
26 |
27 | {/if}
28 |
29 | minuteRef?.focus()}
35 | />
36 |
37 |
38 | {#if view === 'dotted'}
39 |
:
40 | {/if}
41 |
42 |
43 | {#if view === 'labels'}
44 |
45 | {/if}
46 |
47 | hourRef?.focus()}
53 | onRightFocus={() => secondRef?.focus()}
54 | />
55 |
56 |
57 | {#if view === 'dotted'}
58 |
:
59 | {/if}
60 |
61 |
62 | {#if view === 'labels'}
63 |
64 | {/if}
65 |
66 | minuteRef?.focus()}
72 | />
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-runes/cn.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from 'clsx';
2 | import { twMerge } from 'tailwind-merge';
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-runes/time-picker-input.svelte:
--------------------------------------------------------------------------------
1 |
19 |
20 |
112 |
113 | {
123 | e.preventDefault();
124 | onchange?.(e);
125 | }}
126 | {type}
127 | inputmode="decimal"
128 | onkeydown={(e) => {
129 | onkeydown?.(e);
130 | handleKeyDown(e);
131 | }}
132 | {...restProps}
133 | />
134 |
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-runes/time-picker-utils.ts:
--------------------------------------------------------------------------------
1 | import { Time } from '@internationalized/date';
2 |
3 | /**
4 | * regular expression to check for valid hour format (01-23)
5 | */
6 | export function isValidHour(value: string) {
7 | return /^(0[0-9]|1[0-9]|2[0-3])$/.test(value);
8 | }
9 |
10 | /**
11 | * regular expression to check for valid 12 hour format (01-12)
12 | */
13 | export function isValid12Hour(value: string) {
14 | return /^(0[1-9]|1[0-2])$/.test(value);
15 | }
16 |
17 | /**
18 | * regular expression to check for valid minute format (00-59)
19 | */
20 | export function isValidMinuteOrSecond(value: string) {
21 | return /^[0-5][0-9]$/.test(value);
22 | }
23 |
24 | type GetValidNumberConfig = { max: number; min?: number; loop?: boolean };
25 |
26 | export function getValidNumber(
27 | value: string,
28 | { max, min = 0, loop = false }: GetValidNumberConfig
29 | ) {
30 | let numericValue = parseInt(value, 10);
31 |
32 | if (!isNaN(numericValue)) {
33 | if (!loop) {
34 | if (numericValue > max) numericValue = max;
35 | if (numericValue < min) numericValue = min;
36 | } else {
37 | if (numericValue > max) numericValue = min;
38 | if (numericValue < min) numericValue = max;
39 | }
40 | return numericValue.toString().padStart(2, "0");
41 | }
42 |
43 | return "00";
44 | }
45 |
46 | export function getValidHour(value: string) {
47 | if (isValidHour(value)) return value;
48 | return getValidNumber(value, { max: 23 });
49 | }
50 |
51 | export function getValid12Hour(value: string) {
52 | if (isValid12Hour(value)) return value;
53 | return getValidNumber(value, { min: 1, max: 12 });
54 | }
55 |
56 | export function getValidMinuteOrSecond(value: string) {
57 | if (isValidMinuteOrSecond(value)) return value;
58 | return getValidNumber(value, { max: 59 });
59 | }
60 |
61 | type GetValidArrowNumberConfig = {
62 | min: number;
63 | max: number;
64 | step: number;
65 | };
66 |
67 | export function getValidArrowNumber(
68 | value: string,
69 | { min, max, step }: GetValidArrowNumberConfig
70 | ) {
71 | let numericValue = parseInt(value, 10);
72 | if (!isNaN(numericValue)) {
73 | numericValue += step;
74 | return getValidNumber(String(numericValue), { min, max, loop: true });
75 | }
76 | return "00";
77 | }
78 |
79 | export function getValidArrowHour(value: string, step: number) {
80 | return getValidArrowNumber(value, { min: 0, max: 23, step });
81 | }
82 |
83 | export function getValidArrow12Hour(value: string, step: number) {
84 | return getValidArrowNumber(value, { min: 1, max: 12, step });
85 | }
86 |
87 | export function getValidArrowMinuteOrSecond(value: string, step: number) {
88 | return getValidArrowNumber(value, { min: 0, max: 59, step });
89 | }
90 |
91 | export function setMinutes(time: Time, value: string) {
92 | const minutes = getValidMinuteOrSecond(value);
93 | return time.set({ minute: parseInt(minutes, 10) });
94 | }
95 |
96 | export function setSeconds(time: Time, value: string) {
97 | const seconds = getValidMinuteOrSecond(value);
98 | return time.set({ second: parseInt(seconds, 10) });
99 | }
100 |
101 | export function setHours(time: Time, value: string) {
102 | const hours = getValidHour(value);
103 | return time.set({ hour: parseInt(hours, 10) });
104 | }
105 |
106 | export function set12Hours(time: Time, value: string, period: Period) {
107 | const hours = parseInt(getValid12Hour(value), 10);
108 | const convertedHours = convert12HourTo24Hour(hours, period);
109 | return time.set({ hour: convertedHours });
110 | }
111 |
112 | export type TimePickerType = "minutes" | "seconds" | "hours" | "12hours";
113 | export type Period = "AM" | "PM";
114 |
115 | export function setDateByType(
116 | time: Time,
117 | value: string,
118 | type: TimePickerType,
119 | period?: Period
120 | ) {
121 | switch (type) {
122 | case "minutes":
123 | return setMinutes(time, value);
124 | case "seconds":
125 | return setSeconds(time, value);
126 | case "hours":
127 | return setHours(time, value);
128 | case "12hours": {
129 | if (!period) return time;
130 | return set12Hours(time, value, period);
131 | }
132 | default:
133 | return time;
134 | }
135 | }
136 |
137 | export function getDateByType(time: Time, type: TimePickerType) {
138 | switch (type) {
139 | case "minutes":
140 | return getValidMinuteOrSecond(String(time.minute));
141 | case "seconds":
142 | return getValidMinuteOrSecond(String(time.second));
143 | case "hours":
144 | return getValidHour(String(time.hour));
145 | case "12hours":
146 | const hours = display12HourValue(time.hour);
147 | return getValid12Hour(String(hours));
148 | default:
149 | return "00";
150 | }
151 | }
152 |
153 | export function getArrowByType(
154 | value: string,
155 | step: number,
156 | type: TimePickerType
157 | ) {
158 | switch (type) {
159 | case "minutes":
160 | return getValidArrowMinuteOrSecond(value, step);
161 | case "seconds":
162 | return getValidArrowMinuteOrSecond(value, step);
163 | case "hours":
164 | return getValidArrowHour(value, step);
165 | case "12hours":
166 | return getValidArrow12Hour(value, step);
167 | default:
168 | return "00";
169 | }
170 | }
171 |
172 | /**
173 | * handles value change of 12-hour input
174 | * 12:00 PM is 12:00
175 | * 12:00 AM is 00:00
176 | */
177 | export function convert12HourTo24Hour(hour: number, period: Period) {
178 | if (period === "PM") {
179 | if (hour <= 11) {
180 | return hour + 12;
181 | } else {
182 | return hour;
183 | }
184 | } else if (period === "AM") {
185 | if (hour === 12) return 0;
186 | return hour;
187 | }
188 | return hour;
189 | }
190 |
191 | /**
192 | * time is stored in the 24-hour form,
193 | * but needs to be displayed to the user
194 | * in its 12-hour representation
195 | */
196 | export function display12HourValue(hours: number) {
197 | if (hours === 0 || hours === 12) return "12";
198 | if (hours >= 22) return `${hours - 12}`;
199 | if (hours % 12 > 9) return `${hours}`;
200 | return `0${hours % 12}`;
201 | }
202 |
--------------------------------------------------------------------------------
/src/lib/snippets/svelte-runes/time-picker.svelte:
--------------------------------------------------------------------------------
1 |
22 |
23 |
24 |
25 | {#if view === 'labels'}
26 |
27 | {/if}
28 |
29 | minuteRef?.focus()}
35 | />
36 |
37 |
38 | {#if view === 'dotted'}
39 |
:
40 | {/if}
41 |
42 |
43 | {#if view === 'labels'}
44 |
45 | {/if}
46 |
47 | hourRef?.focus()}
53 | onRightFocus={() => secondRef?.focus()}
54 | />
55 |
56 |
57 | {#if view === 'dotted'}
58 |
:
59 | {/if}
60 |
61 |
62 | {#if view === 'labels'}
63 |
64 | {/if}
65 |
66 | minuteRef?.focus()}
72 | />
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/lib/types.ts:
--------------------------------------------------------------------------------
1 | export type Snippet = {
2 | folder: string;
3 | slug: string;
4 | file: string;
5 | content: string;
6 | }
--------------------------------------------------------------------------------
/src/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { type ClassValue, clsx } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/src/routes/+layout.svelte:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
41 |
42 |
43 |
44 |
45 | {@render children()}
46 |
47 |
--------------------------------------------------------------------------------
/src/routes/+page.server.ts:
--------------------------------------------------------------------------------
1 | import { loadAllSnippets } from '$lib/snippets-content';
2 |
3 | export async function load() {
4 | return {
5 | snippets: await loadAllSnippets()
6 | };
7 | }
8 |
--------------------------------------------------------------------------------
/src/routes/+page.svelte:
--------------------------------------------------------------------------------
1 |
48 |
49 |
50 |
51 |
54 |
55 |
56 |
64 |
65 |
66 |
69 |
70 |
Time Picker
71 |
72 | A {``}
component built with Svelte and Shadcn UI or Headless.
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
Demo
83 |
84 |
85 |
86 |
87 | -
88 | Listens to
keydown
events
89 |
90 | - Supports arrow navigation
91 | - Formats date values
92 | - Optimizes mobile keyboard
93 |
94 |
95 |
96 |
161 |
162 |
163 |
Snippets
164 |
165 |
166 |
167 | {#each snippetKeys as snippetKey}
168 | {snippetKey}
169 | {/each}
170 |
171 |
172 |
173 | {#if snippets[currentKey]}
174 |
175 | {#each snippets[currentKey] as snippet (snippet.slug)}
176 |
177 |
178 | {snippet.file}
179 |
180 |
181 |
182 |
183 |
184 |
185 | {/each}
186 |
187 | {/if}
188 |
189 |
190 |
207 |
208 |
209 |
--------------------------------------------------------------------------------
/src/routes/_background.svelte:
--------------------------------------------------------------------------------
1 |
15 |
16 |
37 |
38 | {@render children?.()}
39 |
--------------------------------------------------------------------------------
/src/routes/_og-image.svelte:
--------------------------------------------------------------------------------
1 |
9 |
10 |
13 |
14 |
Time Picker
15 |
16 | A {``}
{' '}
17 | component built with Svelte and Shadcn UI.
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/static/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/1bye/time-picker-svelte/7016b82b8f42bdc48cdf10ff1164ee31d5024e84/static/favicon.png
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import adapter from '@sveltejs/adapter-vercel';
2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3 |
4 | /** @type {import('@sveltejs/kit').Config} */
5 | const config = {
6 | // Consult https://svelte.dev/docs/kit/integrations
7 | // for more information about preprocessors
8 | preprocess: vitePreprocess(),
9 |
10 | kit: {
11 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13 | // See https://svelte.dev/docs/kit/adapters for more information about adapters.
14 | adapter: adapter({
15 | runtime: 'nodejs20.x'
16 | }),
17 | alias: {
18 | '$lib/*': './src/lib/*'
19 | }
20 | }
21 | };
22 |
23 | export default config;
24 |
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import { fontFamily } from "tailwindcss/defaultTheme";
2 | import type { Config } from "tailwindcss";
3 | import tailwindcssAnimate from "tailwindcss-animate";
4 |
5 | const config: Config = {
6 | darkMode: ["class"],
7 | content: ["./src/**/*.{html,js,svelte,ts}"],
8 | safelist: ["dark"],
9 | theme: {
10 | backgroundImage: {
11 | "gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
12 | },
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px"
18 | }
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border) / )",
23 | input: "hsl(var(--input) / )",
24 | ring: "hsl(var(--ring) / )",
25 | background: "hsl(var(--background) / )",
26 | foreground: "hsl(var(--foreground) / )",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary) / )",
29 | foreground: "hsl(var(--primary-foreground) / )"
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary) / )",
33 | foreground: "hsl(var(--secondary-foreground) / )"
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive) / )",
37 | foreground: "hsl(var(--destructive-foreground) / )"
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted) / )",
41 | foreground: "hsl(var(--muted-foreground) / )"
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent) / )",
45 | foreground: "hsl(var(--accent-foreground) / )"
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover) / )",
49 | foreground: "hsl(var(--popover-foreground) / )"
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card) / )",
53 | foreground: "hsl(var(--card-foreground) / )"
54 | },
55 | sidebar: {
56 | DEFAULT: "hsl(var(--sidebar-background))",
57 | foreground: "hsl(var(--sidebar-foreground))",
58 | primary: "hsl(var(--sidebar-primary))",
59 | "primary-foreground": "hsl(var(--sidebar-primary-foreground))",
60 | accent: "hsl(var(--sidebar-accent))",
61 | "accent-foreground": "hsl(var(--sidebar-accent-foreground))",
62 | border: "hsl(var(--sidebar-border))",
63 | ring: "hsl(var(--sidebar-ring))",
64 | },
65 | },
66 | borderRadius: {
67 | xl: "calc(var(--radius) + 4px)",
68 | lg: "var(--radius)",
69 | md: "calc(var(--radius) - 2px)",
70 | sm: "calc(var(--radius) - 4px)"
71 | },
72 | fontFamily: {
73 | sans: ["Open Sans", ...fontFamily.sans],
74 | cal: ["Open Sans"],
75 | },
76 | keyframes: {
77 | "accordion-down": {
78 | from: { height: "0" },
79 | to: { height: "var(--bits-accordion-content-height)" },
80 | },
81 | "accordion-up": {
82 | from: { height: "var(--bits-accordion-content-height)" },
83 | to: { height: "0" },
84 | },
85 | "caret-blink": {
86 | "0%,70%,100%": { opacity: "1" },
87 | "20%,50%": { opacity: "0" },
88 | },
89 | },
90 | animation: {
91 | "accordion-down": "accordion-down 0.2s ease-out",
92 | "accordion-up": "accordion-up 0.2s ease-out",
93 | "caret-blink": "caret-blink 1.25s ease-out infinite",
94 | },
95 | },
96 | },
97 | plugins: [tailwindcssAnimate],
98 | };
99 |
100 | export default config;
101 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./.svelte-kit/tsconfig.json",
3 | "compilerOptions": {
4 | "allowJs": true,
5 | "checkJs": true,
6 | "esModuleInterop": true,
7 | "forceConsistentCasingInFileNames": true,
8 | "resolveJsonModule": true,
9 | "skipLibCheck": true,
10 | "sourceMap": true,
11 | "strict": true,
12 | "moduleResolution": "bundler"
13 | }
14 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
15 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
16 | //
17 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
18 | // from the referenced tsconfig.json - TypeScript does not merge them in
19 | }
20 |
--------------------------------------------------------------------------------
/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { sveltekit } from '@sveltejs/kit/vite';
2 | import { defineConfig } from 'vite';
3 |
4 | export default defineConfig({
5 | plugins: [sveltekit()]
6 | });
7 |
--------------------------------------------------------------------------------