@groundbrick/svelte-ui 0.1.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/README.md +125 -0
- package/dist/components/Alert.svelte +335 -0
- package/dist/components/Alert.svelte.d.ts +24 -0
- package/dist/components/AutocompleteInput.svelte +356 -0
- package/dist/components/AutocompleteInput.svelte.d.ts +72 -0
- package/dist/components/Badge.svelte +185 -0
- package/dist/components/Badge.svelte.d.ts +20 -0
- package/dist/components/Button.svelte +415 -0
- package/dist/components/Button.svelte.d.ts +34 -0
- package/dist/components/Card.svelte +181 -0
- package/dist/components/Card.svelte.d.ts +24 -0
- package/dist/components/CardBody.svelte +78 -0
- package/dist/components/CardBody.svelte.d.ts +12 -0
- package/dist/components/CardFooter.svelte +81 -0
- package/dist/components/CardFooter.svelte.d.ts +14 -0
- package/dist/components/CardHeader.svelte +186 -0
- package/dist/components/CardHeader.svelte.d.ts +21 -0
- package/dist/components/Col.svelte +172 -0
- package/dist/components/Col.svelte.d.ts +26 -0
- package/dist/components/Container.svelte +118 -0
- package/dist/components/Container.svelte.d.ts +14 -0
- package/dist/components/Drawer.svelte +233 -0
- package/dist/components/Drawer.svelte.d.ts +13 -0
- package/dist/components/Dropdown.svelte +190 -0
- package/dist/components/Dropdown.svelte.d.ts +26 -0
- package/dist/components/DropdownItem.svelte +103 -0
- package/dist/components/DropdownItem.svelte.d.ts +22 -0
- package/dist/components/DurationInput.svelte +170 -0
- package/dist/components/DurationInput.svelte.d.ts +27 -0
- package/dist/components/EditableTable.svelte +647 -0
- package/dist/components/EditableTable.svelte.d.ts +74 -0
- package/dist/components/EmptyState.svelte +192 -0
- package/dist/components/EmptyState.svelte.d.ts +22 -0
- package/dist/components/FormField.svelte +260 -0
- package/dist/components/FormField.svelte.d.ts +68 -0
- package/dist/components/GridView.svelte +1022 -0
- package/dist/components/GridView.svelte.d.ts +38 -0
- package/dist/components/GridView.types.d.ts +28 -0
- package/dist/components/GridView.types.js +1 -0
- package/dist/components/LoadingSpinner.svelte +253 -0
- package/dist/components/LoadingSpinner.svelte.d.ts +17 -0
- package/dist/components/Modal.svelte +473 -0
- package/dist/components/Modal.svelte.d.ts +42 -0
- package/dist/components/PhoneInput.svelte +406 -0
- package/dist/components/PhoneInput.svelte.d.ts +31 -0
- package/dist/components/PhotoUpload.svelte +529 -0
- package/dist/components/PhotoUpload.svelte.d.ts +46 -0
- package/dist/components/Row.svelte +153 -0
- package/dist/components/Row.svelte.d.ts +18 -0
- package/dist/icons/PawPrintIcon.svelte +41 -0
- package/dist/icons/PawPrintIcon.svelte.d.ts +14 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.js +49 -0
- package/dist/styles/forms.css +182 -0
- package/dist/styles/tokens.css +243 -0
- package/dist/utils/duration.d.ts +20 -0
- package/dist/utils/duration.js +40 -0
- package/dist/utils/scrollLock.d.ts +7 -0
- package/dist/utils/scrollLock.js +26 -0
- package/package.json +66 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface ContainerProps {
|
|
3
|
+
/** Tamanho máximo do container */
|
|
4
|
+
size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'full' | 'fluid';
|
|
5
|
+
/** Padding vertical */
|
|
6
|
+
py?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
|
|
7
|
+
/** Conteúdo do container */
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
/** Classes CSS adicionais */
|
|
10
|
+
class?: string;
|
|
11
|
+
}
|
|
12
|
+
declare const Container: import("svelte").Component<ContainerProps, {}, "">;
|
|
13
|
+
type Container = ReturnType<typeof Container>;
|
|
14
|
+
export default Container;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { onDestroy } from "svelte";
|
|
3
|
+
import { lockBodyScroll, unlockBodyScroll } from "../utils/scrollLock.js";
|
|
4
|
+
interface Props {
|
|
5
|
+
open: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
title?: string;
|
|
8
|
+
customTitle?: import('svelte').Snippet;
|
|
9
|
+
customHeader?: import('svelte').Snippet;
|
|
10
|
+
showHeader?: boolean;
|
|
11
|
+
size?: "default" | "large" | "xlarge";
|
|
12
|
+
children?: import('svelte').Snippet;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let {
|
|
16
|
+
open = false,
|
|
17
|
+
onClose,
|
|
18
|
+
title,
|
|
19
|
+
customTitle,
|
|
20
|
+
customHeader,
|
|
21
|
+
showHeader = true,
|
|
22
|
+
size = "large",
|
|
23
|
+
children,
|
|
24
|
+
}: Props = $props();
|
|
25
|
+
|
|
26
|
+
let hasScrollLock = false;
|
|
27
|
+
|
|
28
|
+
function acquireScrollLock() {
|
|
29
|
+
if (hasScrollLock) return;
|
|
30
|
+
lockBodyScroll();
|
|
31
|
+
hasScrollLock = true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function releaseScrollLock() {
|
|
35
|
+
if (!hasScrollLock) return;
|
|
36
|
+
unlockBodyScroll();
|
|
37
|
+
hasScrollLock = false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle ESC key
|
|
41
|
+
function handleKeyDown(event: KeyboardEvent) {
|
|
42
|
+
if (event.key === "Escape" && open) {
|
|
43
|
+
onClose();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Prevent body scroll when drawer is open
|
|
48
|
+
$effect(() => {
|
|
49
|
+
if (open) {
|
|
50
|
+
acquireScrollLock();
|
|
51
|
+
} else {
|
|
52
|
+
releaseScrollLock();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return () => {
|
|
56
|
+
releaseScrollLock();
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
onDestroy(() => {
|
|
61
|
+
releaseScrollLock();
|
|
62
|
+
});
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<svelte:window on:keydown={handleKeyDown} />
|
|
66
|
+
|
|
67
|
+
{#if open}
|
|
68
|
+
<!-- Backdrop -->
|
|
69
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
70
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
71
|
+
<div class="drawer-backdrop" onclick={onClose}></div>
|
|
72
|
+
|
|
73
|
+
<!-- Drawer -->
|
|
74
|
+
<div class="drawer drawer-{size}">
|
|
75
|
+
{#if showHeader}
|
|
76
|
+
{#if customHeader}
|
|
77
|
+
{@render customHeader()}
|
|
78
|
+
{:else}
|
|
79
|
+
<div class="drawer-header">
|
|
80
|
+
{#if customTitle}
|
|
81
|
+
<div class="drawer-title-custom">
|
|
82
|
+
{@render customTitle()}
|
|
83
|
+
</div>
|
|
84
|
+
{:else if title}
|
|
85
|
+
<h2 class="drawer-title">{title}</h2>
|
|
86
|
+
{/if}
|
|
87
|
+
<button class="drawer-close" onclick={onClose} aria-label="Fechar">
|
|
88
|
+
<i class="bi bi-x-lg"></i>
|
|
89
|
+
</button>
|
|
90
|
+
</div>
|
|
91
|
+
{/if}
|
|
92
|
+
{/if}
|
|
93
|
+
|
|
94
|
+
<!-- Content -->
|
|
95
|
+
<div class="drawer-content">
|
|
96
|
+
{@render children?.()}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
{/if}
|
|
100
|
+
|
|
101
|
+
<style>
|
|
102
|
+
/* Backdrop */
|
|
103
|
+
.drawer-backdrop {
|
|
104
|
+
position: fixed;
|
|
105
|
+
top: 0;
|
|
106
|
+
left: 0;
|
|
107
|
+
right: 0;
|
|
108
|
+
bottom: 0;
|
|
109
|
+
background: rgba(0, 0, 0, 0.5);
|
|
110
|
+
backdrop-filter: blur(4px);
|
|
111
|
+
z-index: 1040;
|
|
112
|
+
animation: fadeIn var(--transition-base);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@keyframes fadeIn {
|
|
116
|
+
from {
|
|
117
|
+
opacity: 0;
|
|
118
|
+
}
|
|
119
|
+
to {
|
|
120
|
+
opacity: 1;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Drawer Container */
|
|
125
|
+
.drawer {
|
|
126
|
+
position: fixed;
|
|
127
|
+
top: 0;
|
|
128
|
+
right: 0;
|
|
129
|
+
bottom: 0;
|
|
130
|
+
background: var(--color-white);
|
|
131
|
+
box-shadow: var(--shadow-2xl);
|
|
132
|
+
z-index: 1050;
|
|
133
|
+
display: flex;
|
|
134
|
+
flex-direction: column;
|
|
135
|
+
animation: slideInRight var(--transition-base);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@keyframes slideInRight {
|
|
139
|
+
from {
|
|
140
|
+
transform: translateX(100%);
|
|
141
|
+
}
|
|
142
|
+
to {
|
|
143
|
+
transform: translateX(0);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Drawer Sizes */
|
|
148
|
+
.drawer-default {
|
|
149
|
+
width: min(500px, 90vw);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.drawer-large {
|
|
153
|
+
width: min(700px, 90vw);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.drawer-xlarge {
|
|
157
|
+
width: min(900px, 95vw);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* Drawer Header */
|
|
161
|
+
.drawer-header {
|
|
162
|
+
display: flex;
|
|
163
|
+
align-items: center;
|
|
164
|
+
justify-content: space-between;
|
|
165
|
+
padding: var(--spacing-xl) var(--spacing-xl) var(--spacing-lg);
|
|
166
|
+
border-bottom: var(--border-width-thin) solid var(--border-color-light);
|
|
167
|
+
flex-shrink: 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.drawer-header-custom {
|
|
171
|
+
position: absolute;
|
|
172
|
+
width: 100%;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
.drawer-title {
|
|
176
|
+
margin: 0;
|
|
177
|
+
font-size: var(--font-size-2xl);
|
|
178
|
+
font-weight: var(--font-weight-bold);
|
|
179
|
+
color: var(--color-gray-900);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.drawer-title-custom {
|
|
183
|
+
flex: 1;
|
|
184
|
+
min-width: 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.drawer-close {
|
|
188
|
+
display: flex;
|
|
189
|
+
align-items: center;
|
|
190
|
+
justify-content: center;
|
|
191
|
+
width: 40px;
|
|
192
|
+
height: 40px;
|
|
193
|
+
border: none;
|
|
194
|
+
background: var(--color-gray-100);
|
|
195
|
+
color: var(--color-gray-600);
|
|
196
|
+
border-radius: var(--radius-full);
|
|
197
|
+
cursor: pointer;
|
|
198
|
+
transition: all var(--transition-fast);
|
|
199
|
+
flex-shrink: 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.drawer-close:hover {
|
|
203
|
+
background: var(--color-gray-200);
|
|
204
|
+
color: var(--color-gray-900);
|
|
205
|
+
transform: rotate(90deg);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.drawer-close i {
|
|
209
|
+
font-size: var(--font-size-lg);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Drawer Content */
|
|
213
|
+
.drawer-content {
|
|
214
|
+
flex: 1;
|
|
215
|
+
overflow-y: auto;
|
|
216
|
+
overflow-x: hidden;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Mobile Responsive */
|
|
220
|
+
@media (max-width: 768px) {
|
|
221
|
+
.drawer {
|
|
222
|
+
width: 100vw !important;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.drawer-header {
|
|
226
|
+
padding: var(--spacing-lg) var(--spacing-md);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.drawer-title {
|
|
230
|
+
font-size: var(--font-size-xl);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
</style>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onClose: () => void;
|
|
4
|
+
title?: string;
|
|
5
|
+
customTitle?: import('svelte').Snippet;
|
|
6
|
+
customHeader?: import('svelte').Snippet;
|
|
7
|
+
showHeader?: boolean;
|
|
8
|
+
size?: "default" | "large" | "xlarge";
|
|
9
|
+
children?: import('svelte').Snippet;
|
|
10
|
+
}
|
|
11
|
+
declare const Drawer: import("svelte").Component<Props, {}, "">;
|
|
12
|
+
type Drawer = ReturnType<typeof Drawer>;
|
|
13
|
+
export default Drawer;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface DropdownProps {
|
|
5
|
+
/** Conteúdo do botão trigger */
|
|
6
|
+
trigger?: Snippet;
|
|
7
|
+
/** Conteúdo do menu dropdown */
|
|
8
|
+
children?: Snippet;
|
|
9
|
+
/** Variante do botão trigger */
|
|
10
|
+
variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark' | 'outline-primary' | 'outline-secondary' | 'link' | 'ghost';
|
|
11
|
+
/** Tamanho do botão trigger */
|
|
12
|
+
size?: 'sm' | 'md' | 'lg';
|
|
13
|
+
/** Alinhamento do menu */
|
|
14
|
+
align?: 'start' | 'end';
|
|
15
|
+
/** Direção do dropdown */
|
|
16
|
+
direction?: 'down' | 'up' | 'start' | 'end';
|
|
17
|
+
/** Classes CSS adicionais para o wrapper */
|
|
18
|
+
class?: string;
|
|
19
|
+
/** Classes CSS adicionais para o botão */
|
|
20
|
+
buttonClass?: string;
|
|
21
|
+
/** ARIA label para acessibilidade */
|
|
22
|
+
ariaLabel?: string;
|
|
23
|
+
/** Desabilitar dropdown */
|
|
24
|
+
disabled?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
trigger,
|
|
29
|
+
children,
|
|
30
|
+
variant = 'link',
|
|
31
|
+
size = 'sm',
|
|
32
|
+
align = 'end',
|
|
33
|
+
direction = 'down',
|
|
34
|
+
class: additionalClasses = '',
|
|
35
|
+
buttonClass = '',
|
|
36
|
+
ariaLabel = 'Menu de ações',
|
|
37
|
+
disabled = false
|
|
38
|
+
}: DropdownProps = $props();
|
|
39
|
+
|
|
40
|
+
// Mapear variantes para classes Bootstrap
|
|
41
|
+
const variantClasses: Record<string, string> = {
|
|
42
|
+
primary: 'btn-primary',
|
|
43
|
+
secondary: 'btn-secondary',
|
|
44
|
+
success: 'btn-success',
|
|
45
|
+
danger: 'btn-danger',
|
|
46
|
+
warning: 'btn-warning',
|
|
47
|
+
info: 'btn-info',
|
|
48
|
+
light: 'btn-light',
|
|
49
|
+
dark: 'btn-dark',
|
|
50
|
+
'outline-primary': 'btn-outline-primary',
|
|
51
|
+
'outline-secondary': 'btn-outline-secondary',
|
|
52
|
+
link: 'btn-link',
|
|
53
|
+
ghost: 'btn-ghost'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const sizeClasses: Record<string, string> = {
|
|
57
|
+
sm: 'btn-sm',
|
|
58
|
+
md: '',
|
|
59
|
+
lg: 'btn-lg'
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const directionClasses: Record<string, string> = {
|
|
63
|
+
down: 'dropdown',
|
|
64
|
+
up: 'dropup',
|
|
65
|
+
start: 'dropstart',
|
|
66
|
+
end: 'dropend'
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Construir classe do botão
|
|
70
|
+
const btnClass = [
|
|
71
|
+
'btn',
|
|
72
|
+
variantClasses[variant],
|
|
73
|
+
sizeClasses[size],
|
|
74
|
+
'dropdown-toggle',
|
|
75
|
+
buttonClass
|
|
76
|
+
].filter(Boolean).join(' ');
|
|
77
|
+
|
|
78
|
+
// Construir classe do menu
|
|
79
|
+
const menuClass = [
|
|
80
|
+
'dropdown-menu',
|
|
81
|
+
align === 'end' ? 'dropdown-menu-end' : ''
|
|
82
|
+
].filter(Boolean).join(' ');
|
|
83
|
+
|
|
84
|
+
// Construir classe do wrapper
|
|
85
|
+
const wrapperClass = [
|
|
86
|
+
directionClasses[direction],
|
|
87
|
+
additionalClasses
|
|
88
|
+
].filter(Boolean).join(' ');
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<div class={wrapperClass}>
|
|
92
|
+
<button
|
|
93
|
+
class={btnClass}
|
|
94
|
+
type="button"
|
|
95
|
+
data-bs-toggle="dropdown"
|
|
96
|
+
aria-expanded="false"
|
|
97
|
+
aria-label={ariaLabel}
|
|
98
|
+
disabled={disabled}
|
|
99
|
+
>
|
|
100
|
+
{#if trigger}
|
|
101
|
+
{@render trigger()}
|
|
102
|
+
{:else}
|
|
103
|
+
<i class="bi bi-three-dots-vertical"></i>
|
|
104
|
+
{/if}
|
|
105
|
+
</button>
|
|
106
|
+
<ul class={menuClass}>
|
|
107
|
+
{#if children}
|
|
108
|
+
{@render children()}
|
|
109
|
+
{/if}
|
|
110
|
+
</ul>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<style>
|
|
114
|
+
/* ============================================
|
|
115
|
+
DROPDOWN - AgendaPet Design System
|
|
116
|
+
============================================ */
|
|
117
|
+
|
|
118
|
+
/* Variante Ghost */
|
|
119
|
+
.btn-ghost {
|
|
120
|
+
background: transparent;
|
|
121
|
+
border: none;
|
|
122
|
+
color: var(--color-text);
|
|
123
|
+
padding: 0.5rem 0.75rem;
|
|
124
|
+
border-radius: var(--radius-md);
|
|
125
|
+
transition: all var(--transition-fast);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.btn-ghost:hover:not(:disabled) {
|
|
129
|
+
background: var(--color-bg-surface-2);
|
|
130
|
+
color: var(--color-text);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.btn-ghost:active:not(:disabled) {
|
|
134
|
+
background: var(--color-gray-200);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/* Remove a seta do dropdown para o botão link */
|
|
138
|
+
.btn-link.dropdown-toggle::after {
|
|
139
|
+
display: none;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Ghost dropdown toggle */
|
|
143
|
+
.btn-ghost.dropdown-toggle::after {
|
|
144
|
+
display: none;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Dropdown menu styling */
|
|
148
|
+
:global(.dropdown-menu) {
|
|
149
|
+
border: 1px solid var(--color-border-subtle);
|
|
150
|
+
border-radius: var(--radius-lg);
|
|
151
|
+
box-shadow: var(--shadow-lg);
|
|
152
|
+
padding: 0.375rem;
|
|
153
|
+
min-width: 10rem;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
:global(.dropdown-item) {
|
|
157
|
+
padding: 0.5rem 0.75rem;
|
|
158
|
+
border-radius: var(--radius-md);
|
|
159
|
+
color: var(--color-text);
|
|
160
|
+
font-size: var(--font-size-sm);
|
|
161
|
+
transition: all var(--transition-fast);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
:global(.dropdown-item:hover),
|
|
165
|
+
:global(.dropdown-item:focus) {
|
|
166
|
+
background-color: var(--color-bg-surface-2);
|
|
167
|
+
color: var(--color-text);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
:global(.dropdown-item.active),
|
|
171
|
+
:global(.dropdown-item:active) {
|
|
172
|
+
background-color: var(--color-primary);
|
|
173
|
+
color: white;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
:global(.dropdown-item.text-danger) {
|
|
177
|
+
color: var(--color-danger);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
:global(.dropdown-item.text-danger:hover),
|
|
181
|
+
:global(.dropdown-item.text-danger:focus) {
|
|
182
|
+
background-color: var(--color-danger-bg);
|
|
183
|
+
color: var(--color-danger);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
:global(.dropdown-divider) {
|
|
187
|
+
margin: 0.375rem 0;
|
|
188
|
+
border-color: var(--color-border-subtle);
|
|
189
|
+
}
|
|
190
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface DropdownProps {
|
|
3
|
+
/** Conteúdo do botão trigger */
|
|
4
|
+
trigger?: Snippet;
|
|
5
|
+
/** Conteúdo do menu dropdown */
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
/** Variante do botão trigger */
|
|
8
|
+
variant?: 'primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark' | 'outline-primary' | 'outline-secondary' | 'link' | 'ghost';
|
|
9
|
+
/** Tamanho do botão trigger */
|
|
10
|
+
size?: 'sm' | 'md' | 'lg';
|
|
11
|
+
/** Alinhamento do menu */
|
|
12
|
+
align?: 'start' | 'end';
|
|
13
|
+
/** Direção do dropdown */
|
|
14
|
+
direction?: 'down' | 'up' | 'start' | 'end';
|
|
15
|
+
/** Classes CSS adicionais para o wrapper */
|
|
16
|
+
class?: string;
|
|
17
|
+
/** Classes CSS adicionais para o botão */
|
|
18
|
+
buttonClass?: string;
|
|
19
|
+
/** ARIA label para acessibilidade */
|
|
20
|
+
ariaLabel?: string;
|
|
21
|
+
/** Desabilitar dropdown */
|
|
22
|
+
disabled?: boolean;
|
|
23
|
+
}
|
|
24
|
+
declare const Dropdown: import("svelte").Component<DropdownProps, {}, "">;
|
|
25
|
+
type Dropdown = ReturnType<typeof Dropdown>;
|
|
26
|
+
export default Dropdown;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from "svelte";
|
|
3
|
+
|
|
4
|
+
interface DropdownItemProps {
|
|
5
|
+
/** Item content */
|
|
6
|
+
children?: Snippet;
|
|
7
|
+
/** Left icon */
|
|
8
|
+
icon?: Snippet;
|
|
9
|
+
/** Navigation URL (renders an anchor) */
|
|
10
|
+
href?: string;
|
|
11
|
+
/** Click callback */
|
|
12
|
+
onclick?: (event: MouseEvent) => void;
|
|
13
|
+
/** Color variant */
|
|
14
|
+
variant?: 'default' | 'danger' | 'success' | 'warning' | 'primary';
|
|
15
|
+
/** Disabled item */
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
/** Additional CSS classes */
|
|
18
|
+
class?: string;
|
|
19
|
+
/** Item type */
|
|
20
|
+
type?: 'item' | 'divider' | 'header';
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
children,
|
|
25
|
+
icon,
|
|
26
|
+
href,
|
|
27
|
+
onclick,
|
|
28
|
+
variant = 'default',
|
|
29
|
+
disabled = false,
|
|
30
|
+
class: additionalClasses = '',
|
|
31
|
+
type = 'item'
|
|
32
|
+
}: DropdownItemProps = $props();
|
|
33
|
+
|
|
34
|
+
// Variant classes
|
|
35
|
+
const variantClasses: Record<string, string> = {
|
|
36
|
+
default: '',
|
|
37
|
+
danger: 'text-danger',
|
|
38
|
+
success: 'text-success',
|
|
39
|
+
warning: 'text-warning',
|
|
40
|
+
primary: 'text-primary'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const itemClass = [
|
|
44
|
+
'dropdown-item',
|
|
45
|
+
variantClasses[variant],
|
|
46
|
+
additionalClasses
|
|
47
|
+
].filter(Boolean).join(' ');
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
{#if type === 'divider'}
|
|
51
|
+
<li><hr class="dropdown-divider" /></li>
|
|
52
|
+
{:else if type === 'header'}
|
|
53
|
+
<li><h6 class="dropdown-header">{#if children}{@render children()}{/if}</h6></li>
|
|
54
|
+
{:else if href}
|
|
55
|
+
<li>
|
|
56
|
+
<a
|
|
57
|
+
{href}
|
|
58
|
+
class={itemClass}
|
|
59
|
+
class:disabled={disabled}
|
|
60
|
+
aria-disabled={disabled}
|
|
61
|
+
>
|
|
62
|
+
{#if icon}
|
|
63
|
+
<span class="dropdown-icon">
|
|
64
|
+
{@render icon()}
|
|
65
|
+
</span>
|
|
66
|
+
{/if}
|
|
67
|
+
{#if children}
|
|
68
|
+
{@render children()}
|
|
69
|
+
{/if}
|
|
70
|
+
</a>
|
|
71
|
+
</li>
|
|
72
|
+
{:else}
|
|
73
|
+
<li>
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
class={itemClass}
|
|
77
|
+
onclick={onclick}
|
|
78
|
+
disabled={disabled}
|
|
79
|
+
>
|
|
80
|
+
{#if icon}
|
|
81
|
+
<span class="dropdown-icon">
|
|
82
|
+
{@render icon()}
|
|
83
|
+
</span>
|
|
84
|
+
{/if}
|
|
85
|
+
{#if children}
|
|
86
|
+
{@render children()}
|
|
87
|
+
{/if}
|
|
88
|
+
</button>
|
|
89
|
+
</li>
|
|
90
|
+
{/if}
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
.dropdown-icon {
|
|
94
|
+
display: inline-flex;
|
|
95
|
+
align-items: center;
|
|
96
|
+
margin-right: 0.5rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.dropdown-item.disabled {
|
|
100
|
+
pointer-events: none;
|
|
101
|
+
opacity: 0.5;
|
|
102
|
+
}
|
|
103
|
+
</style>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface DropdownItemProps {
|
|
3
|
+
/** Item content */
|
|
4
|
+
children?: Snippet;
|
|
5
|
+
/** Left icon */
|
|
6
|
+
icon?: Snippet;
|
|
7
|
+
/** Navigation URL (renders an anchor) */
|
|
8
|
+
href?: string;
|
|
9
|
+
/** Click callback */
|
|
10
|
+
onclick?: (event: MouseEvent) => void;
|
|
11
|
+
/** Color variant */
|
|
12
|
+
variant?: 'default' | 'danger' | 'success' | 'warning' | 'primary';
|
|
13
|
+
/** Disabled item */
|
|
14
|
+
disabled?: boolean;
|
|
15
|
+
/** Additional CSS classes */
|
|
16
|
+
class?: string;
|
|
17
|
+
/** Item type */
|
|
18
|
+
type?: 'item' | 'divider' | 'header';
|
|
19
|
+
}
|
|
20
|
+
declare const DropdownItem: import("svelte").Component<DropdownItemProps, {}, "">;
|
|
21
|
+
type DropdownItem = ReturnType<typeof DropdownItem>;
|
|
22
|
+
export default DropdownItem;
|