rune-lab 0.0.1
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/LICENSE +21 -0
- package/README.md +64 -0
- package/dist/components/UIShowcase.svelte +364 -0
- package/dist/components/UIShowcase.svelte.d.ts +3 -0
- package/dist/components/layout/Footer.svelte +115 -0
- package/dist/components/layout/Footer.svelte.d.ts +18 -0
- package/dist/components/layout/ThemeSelector.svelte +78 -0
- package/dist/components/layout/ThemeSelector.svelte.d.ts +3 -0
- package/dist/components/layout/URLDisplay.svelte +85 -0
- package/dist/components/layout/URLDisplay.svelte.d.ts +3 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +25 -0
- package/dist/stores/api.svelte.d.ts +18 -0
- package/dist/stores/api.svelte.js +49 -0
- package/dist/stores/app.svelte.d.ts +15 -0
- package/dist/stores/app.svelte.js +24 -0
- package/dist/stores/theme.svelte.d.ts +12 -0
- package/dist/stores/theme.svelte.js +48 -0
- package/dist/theme/index.d.ts +4 -0
- package/dist/theme/index.js +4 -0
- package/dist/theme/static.d.ts +146 -0
- package/dist/theme/static.js +48 -0
- package/dist/tools/format.d.ts +20 -0
- package/dist/tools/format.js +25 -0
- package/dist/tools/pdf.d.ts +1 -0
- package/dist/tools/pdf.js +320 -0
- package/package.json +74 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) [2025] [Yrrrrrf]
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
<img src="./resources/img/rune.png" alt="Rune-Lab Icon" width="128" height="128" description="Some glowing runes">
|
|
3
|
+
<div align="center">Rune Lab</div>
|
|
4
|
+
</h1>
|
|
5
|
+
|
|
6
|
+
<!-- [](https://jsr.io/@yrrrrrf/rune-lab) -->
|
|
7
|
+
[](https://github.com/Yrrrrrf/rune-lab)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Rune Lab is a SvelteKit component library **designed for the SvelteHack 2024**. It provides a collection of reusable UI components and utilities built with modern web technologies. The library emphasizes type safety, performance, and developer experience.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- **Svelte 5 Ready**: Built with the latest Svelte features
|
|
16
|
+
- **TypeScript Support**: Full type safety and IDE integration
|
|
17
|
+
- **Tailwind CSS**: Utility-first styling with custom components
|
|
18
|
+
- **Zero Dependencies**: Lightweight and efficient
|
|
19
|
+
- **Hot Reload**: Development-friendly with watch mode
|
|
20
|
+
- **JSR Distribution**: Modern package distribution
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
jsr add @rune-lab
|
|
26
|
+
# jsr add @yrrrrrf/rune-lab
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Components
|
|
32
|
+
|
|
33
|
+
TODO
|
|
34
|
+
TODO
|
|
35
|
+
TODO
|
|
36
|
+
TODO
|
|
37
|
+
TODO
|
|
38
|
+
|
|
39
|
+
## Development
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
bun install # Install dependencies
|
|
43
|
+
bun watch # Start development watch mode
|
|
44
|
+
bun run build # Build library
|
|
45
|
+
bun test # Run tests
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Project Structure
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
rune-lab/
|
|
52
|
+
├── src/
|
|
53
|
+
│ └── lib/
|
|
54
|
+
│ ├── components/
|
|
55
|
+
│ ├── utils/
|
|
56
|
+
│ └── index.ts
|
|
57
|
+
├── tests/
|
|
58
|
+
├── watch.ts
|
|
59
|
+
└── package.json
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT License - [LICENSE](LICENSE)
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ThemeSelector from "./layout/ThemeSelector.svelte";
|
|
3
|
+
|
|
4
|
+
// State management with Svelte 5 runes
|
|
5
|
+
let activeTab = $state(0);
|
|
6
|
+
let counter = $state(0);
|
|
7
|
+
let modalOpen = $state(false);
|
|
8
|
+
let drawerOpen = $state(false);
|
|
9
|
+
let loading = $state(false);
|
|
10
|
+
let progress = $state(0);
|
|
11
|
+
let rating = $state(3);
|
|
12
|
+
let selected = $state("Option 1");
|
|
13
|
+
let steps = $state(1);
|
|
14
|
+
let toast = $state("");
|
|
15
|
+
|
|
16
|
+
// Color variants for demonstration
|
|
17
|
+
const colors = ['primary', 'secondary', 'accent', 'info', 'success', 'warning', 'error'];
|
|
18
|
+
const sizes = ['xs', 'sm', 'md', 'lg'];
|
|
19
|
+
|
|
20
|
+
// Mock data for examples
|
|
21
|
+
const stats = [
|
|
22
|
+
{ title: "Downloads", value: "31K", desc: "Jan 1st - Feb 1st" },
|
|
23
|
+
{ title: "New Users", value: "4,200", desc: "↗︎ 400 (22%)" },
|
|
24
|
+
{ title: "New Registers", value: "1,200", desc: "↘︎ 90 (14%)" }
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
// Simulate loading
|
|
28
|
+
function simulateLoading() {
|
|
29
|
+
loading = true;
|
|
30
|
+
setTimeout(() => loading = false, 2000);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Show toast message
|
|
34
|
+
function showToast(message: string) {
|
|
35
|
+
toast = message;
|
|
36
|
+
setTimeout(() => toast = "", 3000);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Progress bar simulation
|
|
40
|
+
$effect(() => {
|
|
41
|
+
const interval = setInterval(() => {
|
|
42
|
+
if (progress < 100) {
|
|
43
|
+
progress += 1;
|
|
44
|
+
} else {
|
|
45
|
+
progress = 0;
|
|
46
|
+
}
|
|
47
|
+
}, 100);
|
|
48
|
+
|
|
49
|
+
return () => clearInterval(interval);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const tabs = [
|
|
53
|
+
{ title: 'Basic UI', icon: '🎨' },
|
|
54
|
+
{ title: 'Components', icon: '🧩' },
|
|
55
|
+
{ title: 'Layout', icon: '📐' },
|
|
56
|
+
{ title: 'Data Display', icon: '📊' },
|
|
57
|
+
{ title: 'Feedback', icon: '💫' }
|
|
58
|
+
];
|
|
59
|
+
</script>
|
|
60
|
+
|
|
61
|
+
<ThemeSelector />
|
|
62
|
+
|
|
63
|
+
<div class="min-h-screen bg-base-200 p-4">
|
|
64
|
+
<!-- Header with Theme Demo -->
|
|
65
|
+
<div class="text-center mb-8 hero bg-base-100 rounded-box p-8">
|
|
66
|
+
<div class="hero-content text-center">
|
|
67
|
+
<div>
|
|
68
|
+
<h1 class="text-5xl font-bold mb-4">DaisyUI Showcase</h1>
|
|
69
|
+
<p class="text-xl opacity-75 mb-6">A comprehensive demonstration of DaisyUI components with Svelte 5</p>
|
|
70
|
+
<div class="flex justify-center gap-2">
|
|
71
|
+
<button class="btn btn-primary" onclick={() => modalOpen = true}>Open Modal</button>
|
|
72
|
+
<button class="btn btn-secondary" onclick={() => drawerOpen = true}>Open Drawer</button>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<!-- Navigation Tabs -->
|
|
79
|
+
<div class="tabs tabs-boxed justify-center mb-8">
|
|
80
|
+
{#each tabs as tab, i}
|
|
81
|
+
<button
|
|
82
|
+
class="tab {activeTab === i ? 'tab-active' : ''}"
|
|
83
|
+
onclick={() => activeTab = i}
|
|
84
|
+
>
|
|
85
|
+
<span class="mr-2">{tab.icon}</span>
|
|
86
|
+
{tab.title}
|
|
87
|
+
</button>
|
|
88
|
+
{/each}
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<!-- Main Content -->
|
|
92
|
+
<div class="container mx-auto">
|
|
93
|
+
{#if activeTab === 0}
|
|
94
|
+
<!-- Basic UI Elements -->
|
|
95
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
96
|
+
<!-- Buttons Showcase -->
|
|
97
|
+
<div class="card bg-base-100 shadow-xl">
|
|
98
|
+
<div class="card-body">
|
|
99
|
+
<h2 class="card-title">Buttons</h2>
|
|
100
|
+
<div class="flex flex-wrap gap-2">
|
|
101
|
+
{#each colors as color}
|
|
102
|
+
<button class="btn btn-{color}">{color}</button>
|
|
103
|
+
{/each}
|
|
104
|
+
</div>
|
|
105
|
+
<div class="divider">Sizes</div>
|
|
106
|
+
<div class="flex flex-wrap gap-2 items-center">
|
|
107
|
+
{#each sizes as size}
|
|
108
|
+
<button class="btn btn-{size}">{size}</button>
|
|
109
|
+
{/each}
|
|
110
|
+
</div>
|
|
111
|
+
<div class="divider">States</div>
|
|
112
|
+
<div class="flex flex-wrap gap-2">
|
|
113
|
+
<button class="btn btn-primary loading">Loading</button>
|
|
114
|
+
<button class="btn btn-disabled">Disabled</button>
|
|
115
|
+
<button class="btn btn-outline">Outline</button>
|
|
116
|
+
<button class="btn btn-link">Link</button>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
<!-- Form Elements -->
|
|
122
|
+
<div class="card bg-base-100 shadow-xl">
|
|
123
|
+
<div class="card-body">
|
|
124
|
+
<h2 class="card-title">Form Elements</h2>
|
|
125
|
+
<div class="form-control gap-4">
|
|
126
|
+
<label class="input-group">
|
|
127
|
+
<span>Email</span>
|
|
128
|
+
<input type="text" placeholder="info@site.com" class="input input-bordered" />
|
|
129
|
+
</label>
|
|
130
|
+
|
|
131
|
+
<select class="select select-bordered w-full" bind:value={selected}>
|
|
132
|
+
<option>Option 1</option>
|
|
133
|
+
<option>Option 2</option>
|
|
134
|
+
<option>Option 3</option>
|
|
135
|
+
</select>
|
|
136
|
+
|
|
137
|
+
<div class="flex gap-4">
|
|
138
|
+
<label class="label cursor-pointer">
|
|
139
|
+
<span class="label-text mr-2">Toggle</span>
|
|
140
|
+
<input type="checkbox" class="toggle toggle-primary" />
|
|
141
|
+
</label>
|
|
142
|
+
|
|
143
|
+
<label class="label cursor-pointer">
|
|
144
|
+
<span class="label-text mr-2">Radio</span>
|
|
145
|
+
<input type="radio" name="radio-10" class="radio radio-primary" />
|
|
146
|
+
</label>
|
|
147
|
+
</div>
|
|
148
|
+
|
|
149
|
+
<input type="range" min="0" max="100" class="range range-primary" />
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
{:else if activeTab === 1}
|
|
156
|
+
<!-- Advanced Components -->
|
|
157
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
158
|
+
<!-- Dropdown and Menu -->
|
|
159
|
+
<div class="card bg-base-100 shadow-xl">
|
|
160
|
+
<div class="card-body">
|
|
161
|
+
<h2 class="card-title">Dropdowns & Menus</h2>
|
|
162
|
+
<div class="flex flex-wrap gap-4">
|
|
163
|
+
<div class="dropdown">
|
|
164
|
+
<button class="btn m-1">Dropdown</button>
|
|
165
|
+
<ul class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52">
|
|
166
|
+
<li><button class="w-full text-left">Item 1</button></li>
|
|
167
|
+
<li><button class="w-full text-left">Item 2</button></li>
|
|
168
|
+
</ul>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div class="menu bg-base-200 rounded-box">
|
|
172
|
+
<li><button class="w-full text-left">Menu Item 1</button></li>
|
|
173
|
+
<li><button class="w-full text-left">Menu Item 2</button></li>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<!-- Steps -->
|
|
180
|
+
<div class="card bg-base-100 shadow-xl">
|
|
181
|
+
<div class="card-body">
|
|
182
|
+
<h2 class="card-title">Steps</h2>
|
|
183
|
+
<ul class="steps steps-vertical lg:steps-horizontal w-full">
|
|
184
|
+
<li class="step step-primary">Register</li>
|
|
185
|
+
<li class="step" class:step-primary={steps >= 2}>Choose plan</li>
|
|
186
|
+
<li class="step" class:step-primary={steps >= 3}>Purchase</li>
|
|
187
|
+
<li class="step" class:step-primary={steps === 4}>Receive Product</li>
|
|
188
|
+
</ul>
|
|
189
|
+
<div class="flex justify-center mt-4">
|
|
190
|
+
<button class="btn btn-primary" onclick={() => steps = steps < 4 ? steps + 1 : 1}>
|
|
191
|
+
Next Step
|
|
192
|
+
</button>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
{:else if activeTab === 2}
|
|
199
|
+
<!-- Layout Elements -->
|
|
200
|
+
<div class="grid grid-cols-1 gap-6">
|
|
201
|
+
<!-- Hero Section -->
|
|
202
|
+
<div class="card bg-base-100 shadow-xl">
|
|
203
|
+
<div class="card-body">
|
|
204
|
+
<h2 class="card-title">Hero Section</h2>
|
|
205
|
+
<div class="hero bg-base-200 rounded-box">
|
|
206
|
+
<div class="hero-content text-center">
|
|
207
|
+
<div class="max-w-md">
|
|
208
|
+
<h1 class="text-5xl font-bold">Hello there</h1>
|
|
209
|
+
<p class="py-6">This is a sample hero section with a button below.</p>
|
|
210
|
+
<button class="btn btn-primary">Get Started</button>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
|
|
217
|
+
<!-- Grid Layout -->
|
|
218
|
+
<div class="card bg-base-100 shadow-xl">
|
|
219
|
+
<div class="card-body">
|
|
220
|
+
<h2 class="card-title">Grid Layout</h2>
|
|
221
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
222
|
+
{#each Array(3) as _, i}
|
|
223
|
+
<div class="bg-primary text-primary-content p-4 rounded-box text-center">
|
|
224
|
+
Grid Item {i + 1}
|
|
225
|
+
</div>
|
|
226
|
+
{/each}
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
{:else if activeTab === 3}
|
|
233
|
+
<!-- Data Display -->
|
|
234
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
235
|
+
<!-- Stats -->
|
|
236
|
+
<div class="card bg-base-100 shadow-xl">
|
|
237
|
+
<div class="card-body">
|
|
238
|
+
<h2 class="card-title">Statistics</h2>
|
|
239
|
+
<div class="stats shadow">
|
|
240
|
+
{#each stats as stat}
|
|
241
|
+
<div class="stat">
|
|
242
|
+
<div class="stat-title">{stat.title}</div>
|
|
243
|
+
<div class="stat-value">{stat.value}</div>
|
|
244
|
+
<div class="stat-desc">{stat.desc}</div>
|
|
245
|
+
</div>
|
|
246
|
+
{/each}
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<!-- Table -->
|
|
252
|
+
<div class="card bg-base-100 shadow-xl">
|
|
253
|
+
<div class="card-body">
|
|
254
|
+
<h2 class="card-title">Table</h2>
|
|
255
|
+
<div class="overflow-x-auto">
|
|
256
|
+
<table class="table">
|
|
257
|
+
<thead>
|
|
258
|
+
<tr>
|
|
259
|
+
<th>Name</th>
|
|
260
|
+
<th>Job</th>
|
|
261
|
+
<th>Action</th>
|
|
262
|
+
</tr>
|
|
263
|
+
</thead>
|
|
264
|
+
<tbody>
|
|
265
|
+
<tr>
|
|
266
|
+
<td>Cy Ganderton</td>
|
|
267
|
+
<td>Quality Control Specialist</td>
|
|
268
|
+
<td><button class="btn btn-xs">Details</button></td>
|
|
269
|
+
</tr>
|
|
270
|
+
<tr>
|
|
271
|
+
<td>Hart Hagerty</td>
|
|
272
|
+
<td>Desktop Support Technician</td>
|
|
273
|
+
<td><button class="btn btn-xs">Details</button></td>
|
|
274
|
+
</tr>
|
|
275
|
+
</tbody>
|
|
276
|
+
</table>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
|
|
282
|
+
{:else if activeTab === 4}
|
|
283
|
+
<!-- Feedback Elements -->
|
|
284
|
+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
285
|
+
<!-- Alerts -->
|
|
286
|
+
<div class="card bg-base-100 shadow-xl">
|
|
287
|
+
<div class="card-body">
|
|
288
|
+
<h2 class="card-title">Alerts</h2>
|
|
289
|
+
<div class="flex flex-col gap-2">
|
|
290
|
+
{#each colors as color}
|
|
291
|
+
<div class="alert alert-{color}">
|
|
292
|
+
<span>This is a {color} alert</span>
|
|
293
|
+
</div>
|
|
294
|
+
{/each}
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
<!-- Progress & Loading -->
|
|
300
|
+
<div class="card bg-base-100 shadow-xl">
|
|
301
|
+
<div class="card-body">
|
|
302
|
+
<h2 class="card-title">Progress & Loading</h2>
|
|
303
|
+
<progress class="progress w-full" value={progress} max="100"></progress>
|
|
304
|
+
<div class="flex gap-2 mt-4">
|
|
305
|
+
<button class="btn" onclick={simulateLoading}>
|
|
306
|
+
{loading ? 'Loading...' : 'Simulate Loading'}
|
|
307
|
+
</button>
|
|
308
|
+
{#if loading}
|
|
309
|
+
<span class="loading loading-spinner loading-lg"></span>
|
|
310
|
+
{/if}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</div>
|
|
315
|
+
{/if}
|
|
316
|
+
</div>
|
|
317
|
+
|
|
318
|
+
<!-- Modal -->
|
|
319
|
+
{#if modalOpen}
|
|
320
|
+
<div class="modal modal-open">
|
|
321
|
+
<div class="modal-box">
|
|
322
|
+
<h3 class="font-bold text-lg">Modal Title</h3>
|
|
323
|
+
<p class="py-4">This is a sample modal dialog using DaisyUI.</p>
|
|
324
|
+
<div class="modal-action">
|
|
325
|
+
<button class="btn" onclick={() => modalOpen = false}>Close</button>
|
|
326
|
+
</div>
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
{/if}
|
|
330
|
+
|
|
331
|
+
<!-- Drawer -->
|
|
332
|
+
{#if drawerOpen}
|
|
333
|
+
<div class="drawer drawer-end">
|
|
334
|
+
<input id="my-drawer" type="checkbox" class="drawer-toggle" checked />
|
|
335
|
+
<div class="drawer-side">
|
|
336
|
+
<button
|
|
337
|
+
class="drawer-overlay"
|
|
338
|
+
onclick={() => drawerOpen = false}
|
|
339
|
+
onkeydown={(e) => e.key === 'Escape' && (drawerOpen = false)}
|
|
340
|
+
></button>
|
|
341
|
+
<ul class="menu p-4 w-80 min-h-full bg-base-200 text-base-content">
|
|
342
|
+
<li><button class="w-full text-left">Sidebar Item 1</button></li>
|
|
343
|
+
<li><button class="w-full text-left">Sidebar Item 2</button></li>
|
|
344
|
+
</ul>
|
|
345
|
+
</div>
|
|
346
|
+
</div>
|
|
347
|
+
{/if}
|
|
348
|
+
|
|
349
|
+
<!-- Toast -->
|
|
350
|
+
{#if toast}
|
|
351
|
+
<div class="toast toast-end">
|
|
352
|
+
<div class="alert alert-info">
|
|
353
|
+
<span>{toast}</span>
|
|
354
|
+
</div>
|
|
355
|
+
</div>
|
|
356
|
+
{/if}
|
|
357
|
+
|
|
358
|
+
<!-- Footer -->
|
|
359
|
+
<footer class="footer footer-center p-4 bg-base-300 text-base-content mt-8">
|
|
360
|
+
<div>
|
|
361
|
+
<p>Made with 💝 using Svelte 5 and DaisyUI</p>
|
|
362
|
+
</div>
|
|
363
|
+
</footer>
|
|
364
|
+
</div>
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<!-- src/lib/components/layout/Footer.svelte -->
|
|
2
|
+
<script lang="ts">
|
|
3
|
+
import { appData } from '../../stores/app.svelte.js';
|
|
4
|
+
|
|
5
|
+
// Define footer section types
|
|
6
|
+
interface FooterLink {
|
|
7
|
+
text: string;
|
|
8
|
+
href: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface FooterSection {
|
|
12
|
+
title: string;
|
|
13
|
+
links: FooterLink[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Footer sections configuration
|
|
17
|
+
const footerSections: FooterSection[] = [
|
|
18
|
+
{
|
|
19
|
+
title: "Product",
|
|
20
|
+
links: [
|
|
21
|
+
{ text: "Features", href: "/features" },
|
|
22
|
+
{ text: "Docs", href: "/docs" },
|
|
23
|
+
{ text: "API", href: "/api" },
|
|
24
|
+
{ text: "Pricing", href: "/pricing" }
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: "Company",
|
|
29
|
+
links: [
|
|
30
|
+
{ text: "About", href: "/about" },
|
|
31
|
+
{ text: "Blog", href: "/blog" },
|
|
32
|
+
{ text: "Careers", href: "/careers" },
|
|
33
|
+
{ text: "Contact", href: "/contact" }
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
title: "Resources",
|
|
38
|
+
links: [
|
|
39
|
+
{ text: "Community", href: "/community" },
|
|
40
|
+
{ text: "Help Center", href: "/help" },
|
|
41
|
+
{ text: "Status", href: "/status" },
|
|
42
|
+
{ text: "Terms", href: "/terms" }
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Current year for copyright
|
|
48
|
+
const currentYear = new Date().getFullYear();
|
|
49
|
+
</script>
|
|
50
|
+
|
|
51
|
+
<footer class="footer p-10 bg-base-200 text-base-content">
|
|
52
|
+
<!-- Logo and company info -->
|
|
53
|
+
<aside class="flex flex-col items-start gap-4">
|
|
54
|
+
<img
|
|
55
|
+
src="/favicon.png"
|
|
56
|
+
alt="{appData.name} Logo"
|
|
57
|
+
class="h-12 w-12 rounded-lg hover:animate-pulse"
|
|
58
|
+
/>
|
|
59
|
+
<div>
|
|
60
|
+
<h2 class="text-lg font-bold">{appData.name}</h2>
|
|
61
|
+
<p class="text-sm opacity-75">
|
|
62
|
+
Building better software<br/>
|
|
63
|
+
since {currentYear}
|
|
64
|
+
</p>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<!-- Social media links -->
|
|
68
|
+
<div class="flex gap-4">
|
|
69
|
+
<a
|
|
70
|
+
href="https://github.com/Yrrrrrf"
|
|
71
|
+
aria-label="Visit my GitHub profile"
|
|
72
|
+
class="btn btn-circle btn-ghost btn-sm hover:text-primary"
|
|
73
|
+
>
|
|
74
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
75
|
+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
|
|
76
|
+
</svg>
|
|
77
|
+
</a>
|
|
78
|
+
<a
|
|
79
|
+
href="https://twitter.com"
|
|
80
|
+
aria-label="Visit my Twitter profile"
|
|
81
|
+
class="btn btn-circle btn-ghost btn-sm hover:text-primary"
|
|
82
|
+
>
|
|
83
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
84
|
+
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
|
|
85
|
+
</svg>
|
|
86
|
+
</a>
|
|
87
|
+
<a
|
|
88
|
+
href="https://linkedin.com"
|
|
89
|
+
aria-label="Visit my LinkedIn profile"
|
|
90
|
+
class="btn btn-circle btn-ghost btn-sm hover:text-primary"
|
|
91
|
+
>
|
|
92
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" viewBox="0 0 24 24">
|
|
93
|
+
<path d="M19 0h-14c-2.761 0-5 2.239-5 5v14c0 2.761 2.239 5 5 5h14c2.762 0 5-2.239 5-5v-14c0-2.761-2.238-5-5-5zm-11 19h-3v-11h3v11zm-1.5-12.268c-.966 0-1.75-.79-1.75-1.764s.784-1.764 1.75-1.764 1.75.79 1.75 1.764-.783 1.764-1.75 1.764zm13.5 12.268h-3v-5.604c0-3.368-4-3.113-4 0v5.604h-3v-11h3v1.765c1.396-2.586 7-2.777 7 2.476v6.759z"/>
|
|
94
|
+
</svg>
|
|
95
|
+
</a>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<!-- Navigation sections -->
|
|
99
|
+
{#each footerSections as section}
|
|
100
|
+
<nav>
|
|
101
|
+
<h6 class="footer-title">{section.title}</h6>
|
|
102
|
+
{#each section.links as link}
|
|
103
|
+
<a href={link.href} class="link link-hover">{link.text}</a>
|
|
104
|
+
{/each}
|
|
105
|
+
</nav>
|
|
106
|
+
{/each}
|
|
107
|
+
</footer>
|
|
108
|
+
|
|
109
|
+
<!-- Copyright footer -->
|
|
110
|
+
<footer class="footer footer-center p-4 bg-base-300 text-base-content">
|
|
111
|
+
<aside class="flex flex-col items-center gap-2">
|
|
112
|
+
<p>Copyright © {currentYear} {appData.name} - All rights reserved</p>
|
|
113
|
+
<p class="text-sm opacity-75">Version {appData.version}</p>
|
|
114
|
+
</aside>
|
|
115
|
+
</footer>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
2
|
+
new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
|
|
3
|
+
$$bindings?: Bindings;
|
|
4
|
+
} & Exports;
|
|
5
|
+
(internal: unknown, props: {
|
|
6
|
+
$$events?: Events;
|
|
7
|
+
$$slots?: Slots;
|
|
8
|
+
}): Exports & {
|
|
9
|
+
$set?: any;
|
|
10
|
+
$on?: any;
|
|
11
|
+
};
|
|
12
|
+
z_$$bindings?: Bindings;
|
|
13
|
+
}
|
|
14
|
+
declare const Footer: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
|
|
15
|
+
[evt: string]: CustomEvent<any>;
|
|
16
|
+
}, {}, {}, string>;
|
|
17
|
+
type Footer = InstanceType<typeof Footer>;
|
|
18
|
+
export default Footer;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { themeStore } from '../../stores/theme.svelte.js';
|
|
3
|
+
|
|
4
|
+
// Local state using runes
|
|
5
|
+
let showDropdown = $state(false);
|
|
6
|
+
|
|
7
|
+
// Initialize theme on mount
|
|
8
|
+
themeStore.init();
|
|
9
|
+
|
|
10
|
+
// Get available themes
|
|
11
|
+
const themes = themeStore.getAvailableThemes();
|
|
12
|
+
|
|
13
|
+
function handleThemeSelect(themeValue: string) {
|
|
14
|
+
themeStore.setTheme(themeValue);
|
|
15
|
+
showDropdown = false;
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<div class="theme-selector">
|
|
20
|
+
<button
|
|
21
|
+
class="btn"
|
|
22
|
+
onclick={() => showDropdown = !showDropdown}
|
|
23
|
+
>
|
|
24
|
+
<span>{themeStore.themeConfig.icon}</span>
|
|
25
|
+
</button>
|
|
26
|
+
|
|
27
|
+
{#if showDropdown}
|
|
28
|
+
<ul class="theme-menu">
|
|
29
|
+
{#each themes as theme}
|
|
30
|
+
<li>
|
|
31
|
+
<button
|
|
32
|
+
class="theme-option"
|
|
33
|
+
class:active={themeStore.currentTheme === theme.value}
|
|
34
|
+
onclick={() => handleThemeSelect(theme.value)}
|
|
35
|
+
>
|
|
36
|
+
<span>{theme.icon}</span>
|
|
37
|
+
<span>{theme.name}</span>
|
|
38
|
+
</button>
|
|
39
|
+
</li>
|
|
40
|
+
{/each}
|
|
41
|
+
</ul>
|
|
42
|
+
{/if}
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<style>
|
|
46
|
+
.theme-selector {
|
|
47
|
+
position: relative;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.theme-menu {
|
|
51
|
+
position: absolute;
|
|
52
|
+
right: 0;
|
|
53
|
+
top: 100%;
|
|
54
|
+
margin-top: 0.5rem;
|
|
55
|
+
background: var(--background);
|
|
56
|
+
border: 1px solid var(--border);
|
|
57
|
+
border-radius: 0.5rem;
|
|
58
|
+
padding: 0.5rem;
|
|
59
|
+
min-width: 150px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.theme-option {
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 0.5rem;
|
|
66
|
+
padding: 0.5rem;
|
|
67
|
+
width: 100%;
|
|
68
|
+
text-align: left;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
.theme-option:hover {
|
|
72
|
+
background: var(--hover);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.active {
|
|
76
|
+
background: var(--active);
|
|
77
|
+
}
|
|
78
|
+
</style>
|