@zveltio/components 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +39 -0
- package/admin/ColumnList/ColumnList.svelte +68 -0
- package/admin/ColumnList/component.json +13 -0
- package/admin/MetadataSettings/MetadataSettings.svelte +62 -0
- package/admin/MetadataSettings/component.json +9 -0
- package/admin/RelationshipManager/RelationshipManager.svelte +86 -0
- package/admin/RelationshipManager/component.json +10 -0
- package/admin/RoleManager/RoleManager.svelte +167 -0
- package/admin/RoleManager/component.json +17 -0
- package/admin/TableDesigner/TableDesigner.svelte +37 -0
- package/admin/TableDesigner/component.json +14 -0
- package/ai/AIFeedback/AIFeedback.svelte +43 -0
- package/ai/AIInsightWidget/AIInsightWidget.svelte +56 -0
- package/ai/AIQueryBuilder/AIQueryBuilder.svelte +91 -0
- package/ai/Omnisearch/Omnisearch.svelte +112 -0
- package/ai/VoiceSearch/VoiceSearch.svelte +73 -0
- package/attachments/AttachmentManager/AttachmentManager.svelte +175 -0
- package/attachments/AttachmentManager/component.json +21 -0
- package/charts/SimpleBarChart/SimpleBarChart.svelte +28 -0
- package/charts/SimpleBarChart/component.json +14 -0
- package/common/AddressInput/AddressInput.svelte +88 -0
- package/common/AddressInput/component.json +12 -0
- package/common/Alert/Alert.svelte +20 -0
- package/common/Alert/component.json +15 -0
- package/common/Button/Button.svelte +28 -0
- package/common/Button/component.json +16 -0
- package/common/Card/Card.svelte +25 -0
- package/common/Card/component.json +13 -0
- package/common/DynamicDataTable/DynamicDataTable.svelte +84 -0
- package/common/DynamicDataTable/component.json +14 -0
- package/common/Input/Input.svelte +21 -0
- package/common/Input/component.json +16 -0
- package/common/Loading/Loading.svelte +12 -0
- package/common/Loading/component.json +12 -0
- package/common/Modal/Modal.svelte +31 -0
- package/common/Modal/component.json +14 -0
- package/common/Pagination/Pagination.svelte +40 -0
- package/common/Pagination/component.json +1 -0
- package/common/PermissionGuard/PermissionGuard.svelte +53 -0
- package/common/PermissionGuard/component.json +21 -0
- package/common/SearchableSelect/SearchableSelect.svelte +136 -0
- package/common/SearchableSelect/component.json +17 -0
- package/common/StatusBadge/StatusBadge.svelte +22 -0
- package/common/StatusBadge/component.json +1 -0
- package/dashboard/RecentActivity/RecentActivity.svelte +70 -0
- package/dashboard/RecentActivity/component.json +1 -0
- package/forms/FormField/FormField.svelte +39 -0
- package/forms/FormField/component.json +17 -0
- package/navigation/NavGroup/NavGroup.svelte +40 -0
- package/navigation/NavGroup/component.json +1 -0
- package/navigation/NavLink/NavLink.svelte +18 -0
- package/navigation/NavLink/component.json +15 -0
- package/navigation/SmartNavbar/SmartNavbar.svelte +184 -0
- package/navigation/SmartNavbar/component.json +20 -0
- package/package.json +53 -0
- package/pages/Settings/Settings.svelte +154 -0
- package/pages/Settings/component.json +11 -0
- package/src/lib/index.ts +53 -0
- package/views/ListView/ListView.svelte +19 -0
- package/views/ListView/component.json +13 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
key,
|
|
4
|
+
label,
|
|
5
|
+
type = 'text',
|
|
6
|
+
value = $bindable(),
|
|
7
|
+
options = [],
|
|
8
|
+
error = '',
|
|
9
|
+
onLookupSearch = null
|
|
10
|
+
}: {
|
|
11
|
+
key: string;
|
|
12
|
+
label: string;
|
|
13
|
+
type?: string;
|
|
14
|
+
value?: any;
|
|
15
|
+
options?: Array<{value: any; label: string}>;
|
|
16
|
+
error?: string;
|
|
17
|
+
onLookupSearch?: ((query: string) => Promise<any[]>) | null;
|
|
18
|
+
} = $props();
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<div class="form-control">
|
|
22
|
+
<label class="label"><span class="label-text">{label}</span></label>
|
|
23
|
+
|
|
24
|
+
{#if type === 'text' || type === 'email' || type === 'number'}
|
|
25
|
+
<input {type} bind:value class="input input-bordered {error ? 'input-error' : ''}" />
|
|
26
|
+
{:else if type === 'textarea'}
|
|
27
|
+
<textarea bind:value class="textarea textarea-bordered {error ? 'input-error' : ''}"></textarea>
|
|
28
|
+
{:else if type === 'select'}
|
|
29
|
+
<select bind:value class="select select-bordered {error ? 'input-error' : ''}">
|
|
30
|
+
{#each options as opt}
|
|
31
|
+
<option value={opt.value}>{opt.label}</option>
|
|
32
|
+
{/each}
|
|
33
|
+
</select>
|
|
34
|
+
{:else if type === 'boolean'}
|
|
35
|
+
<input type="checkbox" bind:checked={value} class="checkbox" />
|
|
36
|
+
{/if}
|
|
37
|
+
|
|
38
|
+
{#if error}<label class="label"><span class="label-text-alt text-error">{error}</span></label>{/if}
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "FormField",
|
|
3
|
+
"category": "forms",
|
|
4
|
+
"description": "FormField - Svelte 5 component",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"packages": [],
|
|
7
|
+
"components": []
|
|
8
|
+
},
|
|
9
|
+
"props": {
|
|
10
|
+
"key": "string",
|
|
11
|
+
"label": "string",
|
|
12
|
+
"type": "string",
|
|
13
|
+
"value": "$bindable<any>",
|
|
14
|
+
"options": "Array",
|
|
15
|
+
"error": "string"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
let {
|
|
5
|
+
children,
|
|
6
|
+
label,
|
|
7
|
+
icon = null,
|
|
8
|
+
collapsed = $bindable(false)
|
|
9
|
+
}: {
|
|
10
|
+
children: Snippet;
|
|
11
|
+
label: string;
|
|
12
|
+
icon?: Snippet | null;
|
|
13
|
+
collapsed?: boolean;
|
|
14
|
+
} = $props();
|
|
15
|
+
</script>
|
|
16
|
+
|
|
17
|
+
<div class="nav-group">
|
|
18
|
+
<button
|
|
19
|
+
class="flex items-center gap-2 w-full px-3 py-2 text-sm font-semibold opacity-70 hover:opacity-100"
|
|
20
|
+
onclick={() => collapsed = !collapsed}
|
|
21
|
+
>
|
|
22
|
+
{#if icon}{@render icon()}{/if}
|
|
23
|
+
<span class="flex-1 text-left">{label}</span>
|
|
24
|
+
<svg
|
|
25
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
26
|
+
class="h-4 w-4 transition-transform {collapsed ? '' : 'rotate-90'}"
|
|
27
|
+
fill="none"
|
|
28
|
+
viewBox="0 0 24 24"
|
|
29
|
+
stroke="currentColor"
|
|
30
|
+
>
|
|
31
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
|
|
32
|
+
</svg>
|
|
33
|
+
</button>
|
|
34
|
+
|
|
35
|
+
{#if !collapsed}
|
|
36
|
+
<div class="ml-4 space-y-1">
|
|
37
|
+
{@render children()}
|
|
38
|
+
</div>
|
|
39
|
+
{/if}
|
|
40
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"NavGroup","category":"navigation","dependencies":{"packages":[],"components":[]},"props":{}}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
href,
|
|
4
|
+
label,
|
|
5
|
+
icon = null,
|
|
6
|
+
active = false
|
|
7
|
+
}: {
|
|
8
|
+
href: string;
|
|
9
|
+
label: string;
|
|
10
|
+
icon?: any;
|
|
11
|
+
active?: boolean;
|
|
12
|
+
} = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<a {href} class="btn btn-ghost {active ? 'btn-active' : ''}">
|
|
16
|
+
{#if icon}<svelte:component this={icon} size={16} />{/if}
|
|
17
|
+
<span>{label}</span>
|
|
18
|
+
</a>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "NavLink",
|
|
3
|
+
"category": "navigation",
|
|
4
|
+
"description": "NavLink - Svelte 5 component",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"packages": [],
|
|
7
|
+
"components": []
|
|
8
|
+
},
|
|
9
|
+
"props": {
|
|
10
|
+
"href": "string",
|
|
11
|
+
"label": "string",
|
|
12
|
+
"icon": "Component",
|
|
13
|
+
"active": "boolean"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SmartNavbar - Intelligent navbar with dynamic positioning
|
|
4
|
+
* Positions: top, bottom, left, right
|
|
5
|
+
* Includes: user menu, theme switcher, logo, nav items
|
|
6
|
+
*/
|
|
7
|
+
import type { Snippet } from 'svelte';
|
|
8
|
+
|
|
9
|
+
interface NavItem {
|
|
10
|
+
label: string;
|
|
11
|
+
href: string;
|
|
12
|
+
icon?: Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface User {
|
|
16
|
+
name?: string;
|
|
17
|
+
email?: string;
|
|
18
|
+
avatar?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let {
|
|
22
|
+
position = 'top',
|
|
23
|
+
user = null,
|
|
24
|
+
navItems = [],
|
|
25
|
+
logo = null,
|
|
26
|
+
currentTheme = 'light',
|
|
27
|
+
onThemeToggle,
|
|
28
|
+
onLogout = null,
|
|
29
|
+
onPositionChange = null,
|
|
30
|
+
isDraggable = false
|
|
31
|
+
}: {
|
|
32
|
+
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
33
|
+
user?: User | null;
|
|
34
|
+
navItems?: NavItem[];
|
|
35
|
+
logo?: Snippet | null;
|
|
36
|
+
currentTheme?: string;
|
|
37
|
+
onThemeToggle: (theme: string) => void;
|
|
38
|
+
onLogout?: (() => void) | null;
|
|
39
|
+
onPositionChange?: ((position: string) => Promise<void>) | null;
|
|
40
|
+
isDraggable?: boolean;
|
|
41
|
+
} = $props();
|
|
42
|
+
|
|
43
|
+
let dragging = $state(false);
|
|
44
|
+
let showPositionMenu = $state(false);
|
|
45
|
+
|
|
46
|
+
const isHorizontal = $derived(position === 'top' || position === 'bottom');
|
|
47
|
+
const isVertical = $derived(position === 'left' || position === 'right');
|
|
48
|
+
|
|
49
|
+
const positionClasses = {
|
|
50
|
+
top: 'top-0 left-0 right-0 flex-row border-b',
|
|
51
|
+
bottom: 'bottom-0 left-0 right-0 flex-row border-t',
|
|
52
|
+
left: 'top-0 bottom-0 left-0 flex-col border-r w-64',
|
|
53
|
+
right: 'top-0 bottom-0 right-0 flex-col border-l w-64'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
async function changePosition(newPos: 'top' | 'bottom' | 'left' | 'right') {
|
|
57
|
+
if (onPositionChange) {
|
|
58
|
+
await onPositionChange(newPos);
|
|
59
|
+
}
|
|
60
|
+
showPositionMenu = false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function handleThemeToggle() {
|
|
64
|
+
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
|
65
|
+
onThemeToggle(newTheme);
|
|
66
|
+
}
|
|
67
|
+
</script>
|
|
68
|
+
|
|
69
|
+
<nav
|
|
70
|
+
class="navbar bg-base-100 border-base-200 fixed z-50 {positionClasses[position]} {dragging ? 'opacity-50' : ''}"
|
|
71
|
+
class:draggable={isDraggable}
|
|
72
|
+
>
|
|
73
|
+
<!-- Logo/Brand -->
|
|
74
|
+
<div class="flex-none {isVertical ? 'px-4 py-3 border-b border-base-200 w-full' : ''}">
|
|
75
|
+
{#if logo}
|
|
76
|
+
{@render logo()}
|
|
77
|
+
{:else}
|
|
78
|
+
<a href="/" class="btn btn-ghost text-xl font-bold">App</a>
|
|
79
|
+
{/if}
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Nav Items -->
|
|
83
|
+
{#if navItems.length > 0}
|
|
84
|
+
<div class="flex-1 {isVertical ? 'flex-col p-2 space-y-1' : 'px-4'}">
|
|
85
|
+
{#each navItems as item}
|
|
86
|
+
<a
|
|
87
|
+
href={item.href}
|
|
88
|
+
class="btn btn-ghost {isVertical ? 'w-full justify-start' : 'btn-sm'}"
|
|
89
|
+
>
|
|
90
|
+
{#if item.icon}
|
|
91
|
+
<span class="w-5 h-5">{@render item.icon()}</span>
|
|
92
|
+
{/if}
|
|
93
|
+
{item.label}
|
|
94
|
+
</a>
|
|
95
|
+
{/each}
|
|
96
|
+
</div>
|
|
97
|
+
{/if}
|
|
98
|
+
|
|
99
|
+
<!-- Actions (Theme, Position, User) -->
|
|
100
|
+
<div class="flex-none {isVertical ? 'p-2 border-t border-base-200 space-y-2 w-full' : 'gap-2'}">
|
|
101
|
+
<!-- Theme Toggle -->
|
|
102
|
+
<button
|
|
103
|
+
class="btn btn-ghost btn-circle btn-sm"
|
|
104
|
+
onclick={handleThemeToggle}
|
|
105
|
+
title="Toggle theme"
|
|
106
|
+
>
|
|
107
|
+
{#if currentTheme === 'light'}
|
|
108
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
109
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
|
|
110
|
+
</svg>
|
|
111
|
+
{:else}
|
|
112
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
113
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
|
|
114
|
+
</svg>
|
|
115
|
+
{/if}
|
|
116
|
+
</button>
|
|
117
|
+
|
|
118
|
+
<!-- Position Menu (if draggable) -->
|
|
119
|
+
{#if isDraggable && onPositionChange}
|
|
120
|
+
<div class="dropdown {isVertical ? 'dropdown-right' : 'dropdown-end'}">
|
|
121
|
+
<button
|
|
122
|
+
tabindex="0"
|
|
123
|
+
class="btn btn-ghost btn-circle btn-sm"
|
|
124
|
+
title="Change position"
|
|
125
|
+
>
|
|
126
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
127
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
|
128
|
+
</svg>
|
|
129
|
+
</button>
|
|
130
|
+
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-40">
|
|
131
|
+
<li><button onclick={() => changePosition('top')}>Top</button></li>
|
|
132
|
+
<li><button onclick={() => changePosition('bottom')}>Bottom</button></li>
|
|
133
|
+
<li><button onclick={() => changePosition('left')}>Left</button></li>
|
|
134
|
+
<li><button onclick={() => changePosition('right')}>Right</button></li>
|
|
135
|
+
</ul>
|
|
136
|
+
</div>
|
|
137
|
+
{/if}
|
|
138
|
+
|
|
139
|
+
<!-- User Menu -->
|
|
140
|
+
{#if user}
|
|
141
|
+
<div class="dropdown {isVertical ? 'dropdown-right' : 'dropdown-end'}">
|
|
142
|
+
<button tabindex="0" class="btn btn-ghost btn-circle avatar placeholder">
|
|
143
|
+
{#if user.avatar}
|
|
144
|
+
<div class="w-10 rounded-full">
|
|
145
|
+
<img src={user.avatar} alt={user.name} />
|
|
146
|
+
</div>
|
|
147
|
+
{:else}
|
|
148
|
+
<div class="w-10 rounded-full bg-neutral text-neutral-content">
|
|
149
|
+
<span class="text-sm">{user.name?.substring(0, 2).toUpperCase() || 'U'}</span>
|
|
150
|
+
</div>
|
|
151
|
+
{/if}
|
|
152
|
+
</button>
|
|
153
|
+
<ul tabindex="0" class="dropdown-content menu p-2 shadow bg-base-100 rounded-box w-52">
|
|
154
|
+
<li class="menu-title">
|
|
155
|
+
<span>{user.name || user.email}</span>
|
|
156
|
+
</li>
|
|
157
|
+
<li><a href="/private/profile">Profile</a></li>
|
|
158
|
+
<li><a href="/private/settings">Settings</a></li>
|
|
159
|
+
<li class="divider"></li>
|
|
160
|
+
{#if onLogout}
|
|
161
|
+
<li><button onclick={onLogout}>Logout</button></li>
|
|
162
|
+
{/if}
|
|
163
|
+
</ul>
|
|
164
|
+
</div>
|
|
165
|
+
{/if}
|
|
166
|
+
</div>
|
|
167
|
+
</nav>
|
|
168
|
+
|
|
169
|
+
<!-- Spacer to prevent content overlap -->
|
|
170
|
+
{#if position === 'top'}
|
|
171
|
+
<div class="h-16"></div>
|
|
172
|
+
{:else if position === 'bottom'}
|
|
173
|
+
<div class="h-16"></div>
|
|
174
|
+
{:else if position === 'left'}
|
|
175
|
+
<div class="w-64 flex-shrink-0"></div>
|
|
176
|
+
{:else if position === 'right'}
|
|
177
|
+
<div class="w-64 flex-shrink-0"></div>
|
|
178
|
+
{/if}
|
|
179
|
+
|
|
180
|
+
<style>
|
|
181
|
+
.draggable {
|
|
182
|
+
cursor: move;
|
|
183
|
+
}
|
|
184
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "SmartNavbar",
|
|
3
|
+
"category": "navigation",
|
|
4
|
+
"description": "Intelligent navbar with dynamic positioning (top/bottom/left/right), user menu, and theme switcher",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"packages": [],
|
|
7
|
+
"components": []
|
|
8
|
+
},
|
|
9
|
+
"props": {
|
|
10
|
+
"position": "'top'|'bottom'|'left'|'right' - Navbar position",
|
|
11
|
+
"user": "{name?, email?, avatar?} - Current user",
|
|
12
|
+
"navItems": "Array<{label, href, icon?}> - Navigation items",
|
|
13
|
+
"logo": "Snippet - Custom logo",
|
|
14
|
+
"currentTheme": "string - Current theme",
|
|
15
|
+
"onThemeToggle": "(theme: string) => void - Theme change callback",
|
|
16
|
+
"onLogout": "(() => void) | null - Logout callback",
|
|
17
|
+
"onPositionChange": "((position: string) => Promise<void>) | null - Position change callback",
|
|
18
|
+
"isDraggable": "boolean - Enable position menu"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zveltio/components",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official Zveltio UI component library — Svelte 5 + DaisyUI/Tailwind",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"svelte": "./src/lib/index.ts",
|
|
7
|
+
"types": "./src/lib/index.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"svelte": "./src/lib/index.ts",
|
|
11
|
+
"types": "./src/lib/index.ts",
|
|
12
|
+
"import": "./src/lib/index.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src",
|
|
17
|
+
"admin",
|
|
18
|
+
"ai",
|
|
19
|
+
"attachments",
|
|
20
|
+
"charts",
|
|
21
|
+
"common",
|
|
22
|
+
"dashboard",
|
|
23
|
+
"forms",
|
|
24
|
+
"navigation",
|
|
25
|
+
"pages",
|
|
26
|
+
"views"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"check": "svelte-check --tsconfig ./tsconfig.json"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"svelte": "^5.0.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"svelte": "^5.0.0",
|
|
36
|
+
"svelte-check": "^4.0.0",
|
|
37
|
+
"typescript": "^5.4.0"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"svelte",
|
|
41
|
+
"svelte5",
|
|
42
|
+
"zveltio",
|
|
43
|
+
"components",
|
|
44
|
+
"ui",
|
|
45
|
+
"daisy",
|
|
46
|
+
"tailwind"
|
|
47
|
+
],
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "https://github.com/liviumarianiordache/zveltio-components.git"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* SettingsPage - User preferences management
|
|
4
|
+
* Handles: theme, navbar position, display preferences
|
|
5
|
+
*/
|
|
6
|
+
interface Preferences {
|
|
7
|
+
theme?: string;
|
|
8
|
+
navbar_position?: 'top' | 'bottom' | 'left' | 'right';
|
|
9
|
+
nav_collapsed?: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let {
|
|
13
|
+
preferences = $bindable<Preferences>({}),
|
|
14
|
+
onSave,
|
|
15
|
+
saving = false
|
|
16
|
+
}: {
|
|
17
|
+
preferences?: Preferences;
|
|
18
|
+
onSave: (prefs: Preferences) => Promise<void>;
|
|
19
|
+
saving?: boolean;
|
|
20
|
+
} = $props();
|
|
21
|
+
|
|
22
|
+
let localPrefs = $state<Preferences>({ ...preferences });
|
|
23
|
+
let hasChanges = $derived(
|
|
24
|
+
JSON.stringify(localPrefs) !== JSON.stringify(preferences)
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
async function handleSave() {
|
|
28
|
+
await onSave(localPrefs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function handleReset() {
|
|
32
|
+
localPrefs = { ...preferences };
|
|
33
|
+
}
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<div class="max-w-4xl mx-auto space-y-6">
|
|
37
|
+
<div>
|
|
38
|
+
<h1 class="text-3xl font-bold">Settings</h1>
|
|
39
|
+
<p class="text-sm opacity-60 mt-1">Manage your account preferences</p>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<!-- Appearance Section -->
|
|
43
|
+
<div class="card bg-base-100 border border-base-200">
|
|
44
|
+
<div class="card-body">
|
|
45
|
+
<h2 class="card-title">Appearance</h2>
|
|
46
|
+
|
|
47
|
+
<div class="form-control">
|
|
48
|
+
<label class="label">
|
|
49
|
+
<span class="label-text">Theme</span>
|
|
50
|
+
</label>
|
|
51
|
+
<select bind:value={localPrefs.theme} class="select select-bordered">
|
|
52
|
+
<option value="light">Light</option>
|
|
53
|
+
<option value="dark">Dark</option>
|
|
54
|
+
<option value="auto">Auto (System)</option>
|
|
55
|
+
</select>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="form-control mt-4">
|
|
59
|
+
<label class="label">
|
|
60
|
+
<span class="label-text">Navbar Position</span>
|
|
61
|
+
</label>
|
|
62
|
+
<div class="grid grid-cols-2 gap-3">
|
|
63
|
+
{#each ['top', 'bottom', 'left', 'right'] as pos}
|
|
64
|
+
<label class="cursor-pointer">
|
|
65
|
+
<input
|
|
66
|
+
type="radio"
|
|
67
|
+
bind:group={localPrefs.navbar_position}
|
|
68
|
+
value={pos}
|
|
69
|
+
class="radio radio-primary"
|
|
70
|
+
/>
|
|
71
|
+
<span class="ml-2 capitalize">{pos}</span>
|
|
72
|
+
</label>
|
|
73
|
+
{/each}
|
|
74
|
+
</div>
|
|
75
|
+
<label class="label">
|
|
76
|
+
<span class="label-text-alt">Changes apply immediately after saving</span>
|
|
77
|
+
</label>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
<!-- Navigation Section -->
|
|
83
|
+
<div class="card bg-base-100 border border-base-200">
|
|
84
|
+
<div class="card-body">
|
|
85
|
+
<h2 class="card-title">Navigation</h2>
|
|
86
|
+
|
|
87
|
+
<div class="form-control">
|
|
88
|
+
<label class="label cursor-pointer justify-start gap-3">
|
|
89
|
+
<input
|
|
90
|
+
type="checkbox"
|
|
91
|
+
bind:checked={localPrefs.nav_collapsed}
|
|
92
|
+
class="toggle toggle-primary"
|
|
93
|
+
/>
|
|
94
|
+
<span class="label-text">Collapse sidebar by default</span>
|
|
95
|
+
</label>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
|
|
100
|
+
<!-- Preview Section -->
|
|
101
|
+
<div class="card bg-base-100 border border-base-200">
|
|
102
|
+
<div class="card-body">
|
|
103
|
+
<h2 class="card-title">Preview</h2>
|
|
104
|
+
<div class="relative h-40 bg-base-200 rounded-lg overflow-hidden">
|
|
105
|
+
<!-- Navbar Preview -->
|
|
106
|
+
{#if localPrefs.navbar_position === 'top'}
|
|
107
|
+
<div class="absolute top-0 left-0 right-0 h-12 bg-primary flex items-center justify-center text-primary-content text-xs">
|
|
108
|
+
Navbar (Top)
|
|
109
|
+
</div>
|
|
110
|
+
{:else if localPrefs.navbar_position === 'bottom'}
|
|
111
|
+
<div class="absolute bottom-0 left-0 right-0 h-12 bg-primary flex items-center justify-center text-primary-content text-xs">
|
|
112
|
+
Navbar (Bottom)
|
|
113
|
+
</div>
|
|
114
|
+
{:else if localPrefs.navbar_position === 'left'}
|
|
115
|
+
<div class="absolute top-0 bottom-0 left-0 w-16 bg-primary flex items-center justify-center text-primary-content text-xs writing-vertical">
|
|
116
|
+
<span class="rotate-90">Navbar</span>
|
|
117
|
+
</div>
|
|
118
|
+
{:else if localPrefs.navbar_position === 'right'}
|
|
119
|
+
<div class="absolute top-0 bottom-0 right-0 w-16 bg-primary flex items-center justify-center text-primary-content text-xs">
|
|
120
|
+
<span class="rotate-90">Navbar</span>
|
|
121
|
+
</div>
|
|
122
|
+
{/if}
|
|
123
|
+
|
|
124
|
+
<!-- Content Area -->
|
|
125
|
+
<div class="absolute inset-0 flex items-center justify-center text-xs opacity-50">
|
|
126
|
+
Content Area
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
<!-- Actions -->
|
|
133
|
+
<div class="flex justify-end gap-3 sticky bottom-4 bg-base-100 p-4 rounded-lg shadow-lg border border-base-200">
|
|
134
|
+
<button
|
|
135
|
+
class="btn btn-ghost"
|
|
136
|
+
onclick={handleReset}
|
|
137
|
+
disabled={!hasChanges || saving}
|
|
138
|
+
>
|
|
139
|
+
Reset
|
|
140
|
+
</button>
|
|
141
|
+
<button
|
|
142
|
+
class="btn btn-primary"
|
|
143
|
+
onclick={handleSave}
|
|
144
|
+
disabled={!hasChanges || saving}
|
|
145
|
+
>
|
|
146
|
+
{#if saving}
|
|
147
|
+
<span class="loading loading-spinner loading-sm"></span>
|
|
148
|
+
Saving...
|
|
149
|
+
{:else}
|
|
150
|
+
Save Changes
|
|
151
|
+
{/if}
|
|
152
|
+
</button>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "Settings",
|
|
3
|
+
"category": "pages",
|
|
4
|
+
"description": "User preferences management page (theme, navbar position, navigation)",
|
|
5
|
+
"dependencies": {"packages": [], "components": []},
|
|
6
|
+
"props": {
|
|
7
|
+
"preferences": "$bindable<{theme?, navbar_position?, nav_collapsed?}>",
|
|
8
|
+
"onSave": "(prefs: Preferences) => Promise<void>",
|
|
9
|
+
"saving": "boolean"
|
|
10
|
+
}
|
|
11
|
+
}
|
package/src/lib/index.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// @zveltio/components — Official Zveltio UI component library
|
|
2
|
+
// Svelte 5 + DaisyUI/Tailwind
|
|
3
|
+
|
|
4
|
+
// ── Admin ─────────────────────────────────────────────────────────────────
|
|
5
|
+
export { default as ColumnList } from '../../admin/ColumnList/ColumnList.svelte';
|
|
6
|
+
export { default as MetadataSettings } from '../../admin/MetadataSettings/MetadataSettings.svelte';
|
|
7
|
+
export { default as RelationshipManager } from '../../admin/RelationshipManager/RelationshipManager.svelte';
|
|
8
|
+
export { default as RoleManager } from '../../admin/RoleManager/RoleManager.svelte';
|
|
9
|
+
export { default as TableDesigner } from '../../admin/TableDesigner/TableDesigner.svelte';
|
|
10
|
+
|
|
11
|
+
// ── AI ────────────────────────────────────────────────────────────────────
|
|
12
|
+
export { default as AIFeedback } from '../../ai/AIFeedback/AIFeedback.svelte';
|
|
13
|
+
export { default as AIInsightWidget } from '../../ai/AIInsightWidget/AIInsightWidget.svelte';
|
|
14
|
+
export { default as AIQueryBuilder } from '../../ai/AIQueryBuilder/AIQueryBuilder.svelte';
|
|
15
|
+
export { default as Omnisearch } from '../../ai/Omnisearch/Omnisearch.svelte';
|
|
16
|
+
export { default as VoiceSearch } from '../../ai/VoiceSearch/VoiceSearch.svelte';
|
|
17
|
+
|
|
18
|
+
// ── Attachments ───────────────────────────────────────────────────────────
|
|
19
|
+
export { default as AttachmentManager } from '../../attachments/AttachmentManager/AttachmentManager.svelte';
|
|
20
|
+
|
|
21
|
+
// ── Charts ────────────────────────────────────────────────────────────────
|
|
22
|
+
export { default as SimpleBarChart } from '../../charts/SimpleBarChart/SimpleBarChart.svelte';
|
|
23
|
+
|
|
24
|
+
// ── Common ────────────────────────────────────────────────────────────────
|
|
25
|
+
export { default as AddressInput } from '../../common/AddressInput/AddressInput.svelte';
|
|
26
|
+
export { default as Alert } from '../../common/Alert/Alert.svelte';
|
|
27
|
+
export { default as Button } from '../../common/Button/Button.svelte';
|
|
28
|
+
export { default as Card } from '../../common/Card/Card.svelte';
|
|
29
|
+
export { default as DynamicDataTable } from '../../common/DynamicDataTable/DynamicDataTable.svelte';
|
|
30
|
+
export { default as Input } from '../../common/Input/Input.svelte';
|
|
31
|
+
export { default as Loading } from '../../common/Loading/Loading.svelte';
|
|
32
|
+
export { default as Modal } from '../../common/Modal/Modal.svelte';
|
|
33
|
+
export { default as Pagination } from '../../common/Pagination/Pagination.svelte';
|
|
34
|
+
export { default as PermissionGuard } from '../../common/PermissionGuard/PermissionGuard.svelte';
|
|
35
|
+
export { default as SearchableSelect } from '../../common/SearchableSelect/SearchableSelect.svelte';
|
|
36
|
+
export { default as StatusBadge } from '../../common/StatusBadge/StatusBadge.svelte';
|
|
37
|
+
|
|
38
|
+
// ── Dashboard ─────────────────────────────────────────────────────────────
|
|
39
|
+
export { default as RecentActivity } from '../../dashboard/RecentActivity/RecentActivity.svelte';
|
|
40
|
+
|
|
41
|
+
// ── Forms ─────────────────────────────────────────────────────────────────
|
|
42
|
+
export { default as FormField } from '../../forms/FormField/FormField.svelte';
|
|
43
|
+
|
|
44
|
+
// ── Navigation ────────────────────────────────────────────────────────────
|
|
45
|
+
export { default as NavGroup } from '../../navigation/NavGroup/NavGroup.svelte';
|
|
46
|
+
export { default as NavLink } from '../../navigation/NavLink/NavLink.svelte';
|
|
47
|
+
export { default as SmartNavbar } from '../../navigation/SmartNavbar/SmartNavbar.svelte';
|
|
48
|
+
|
|
49
|
+
// ── Pages ─────────────────────────────────────────────────────────────────
|
|
50
|
+
export { default as Settings } from '../../pages/Settings/Settings.svelte';
|
|
51
|
+
|
|
52
|
+
// ── Views ─────────────────────────────────────────────────────────────────
|
|
53
|
+
export { default as ListView } from '../../views/ListView/ListView.svelte';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
items = [],
|
|
4
|
+
onRowClick = null
|
|
5
|
+
}: {
|
|
6
|
+
items: any[];
|
|
7
|
+
onRowClick?: ((item: any) => void) | null;
|
|
8
|
+
} = $props();
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div class="space-y-2">
|
|
12
|
+
{#each items as item}
|
|
13
|
+
<div class="card bg-base-100 shadow cursor-pointer hover:shadow-lg transition" onclick={() => onRowClick?.(item)}>
|
|
14
|
+
<div class="card-body p-4">
|
|
15
|
+
<pre class="text-xs">{JSON.stringify(item, null, 2)}</pre>
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
{/each}
|
|
19
|
+
</div>
|