urpanels-ui-pack 0.0.2

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.
Files changed (62) hide show
  1. package/README.md +37 -0
  2. package/dist/Button/Button.svelte +76 -0
  3. package/dist/Button/Button.svelte.d.ts +13 -0
  4. package/dist/Button/index.d.ts +1 -0
  5. package/dist/Button/index.js +1 -0
  6. package/dist/InfoCard/InfoCard.svelte +227 -0
  7. package/dist/InfoCard/InfoCard.svelte.d.ts +4 -0
  8. package/dist/InfoCard/InfoCard.types.d.ts +56 -0
  9. package/dist/InfoCard/InfoCard.types.js +1 -0
  10. package/dist/InfoCard/index.d.ts +2 -0
  11. package/dist/InfoCard/index.js +1 -0
  12. package/dist/LoadingSpinner/LoadingSpinner.svelte +3 -0
  13. package/dist/LoadingSpinner/LoadingSpinner.svelte.d.ts +26 -0
  14. package/dist/LoadingSpinner/index.d.ts +1 -0
  15. package/dist/LoadingSpinner/index.js +1 -0
  16. package/dist/Modal/Modal.svelte +122 -0
  17. package/dist/Modal/Modal.svelte.d.ts +39 -0
  18. package/dist/Modal/index.d.ts +1 -0
  19. package/dist/Modal/index.js +1 -0
  20. package/dist/PageLayout/ActionButton.svelte +26 -0
  21. package/dist/PageLayout/ActionButton.svelte.d.ts +8 -0
  22. package/dist/PageLayout/PageContent.svelte +19 -0
  23. package/dist/PageLayout/PageContent.svelte.d.ts +8 -0
  24. package/dist/PageLayout/PageHeader.svelte +29 -0
  25. package/dist/PageLayout/PageHeader.svelte.d.ts +9 -0
  26. package/dist/PageLayout/SearchBar.svelte +25 -0
  27. package/dist/PageLayout/SearchBar.svelte.d.ts +8 -0
  28. package/dist/PageLayout/ViewToggle.svelte +53 -0
  29. package/dist/PageLayout/ViewToggle.svelte.d.ts +8 -0
  30. package/dist/PageLayout/index.d.ts +5 -0
  31. package/dist/PageLayout/index.js +5 -0
  32. package/dist/Pagination/Pagination.svelte +96 -0
  33. package/dist/Pagination/Pagination.svelte.d.ts +4 -0
  34. package/dist/Pagination/Pagination.types.d.ts +11 -0
  35. package/dist/Pagination/Pagination.types.js +1 -0
  36. package/dist/Pagination/index.d.ts +2 -0
  37. package/dist/Pagination/index.js +1 -0
  38. package/dist/RichTextEditor/RichTextEditor.svelte +575 -0
  39. package/dist/RichTextEditor/RichTextEditor.svelte.d.ts +10 -0
  40. package/dist/RichTextEditor/index.d.ts +1 -0
  41. package/dist/RichTextEditor/index.js +1 -0
  42. package/dist/index.d.ts +5 -0
  43. package/dist/index.js +12 -0
  44. package/dist/sections/ArticlesGrid/ArticlesGrid.svelte +201 -0
  45. package/dist/sections/ArticlesGrid/ArticlesGrid.svelte.d.ts +31 -0
  46. package/dist/sections/ArticlesGrid/README.md +229 -0
  47. package/dist/sections/ArticlesGrid/index.d.ts +2 -0
  48. package/dist/sections/ArticlesGrid/index.js +1 -0
  49. package/dist/sections/FeaturedGalleryGrid/FeaturedGalleryGrid.svelte +118 -0
  50. package/dist/sections/FeaturedGalleryGrid/FeaturedGalleryGrid.svelte.d.ts +21 -0
  51. package/dist/sections/FeaturedGalleryGrid/README.md +270 -0
  52. package/dist/sections/FeaturedGalleryGrid/index.d.ts +2 -0
  53. package/dist/sections/FeaturedGalleryGrid/index.js +1 -0
  54. package/dist/sections/HeroCarousel/HeroCarousel.svelte +318 -0
  55. package/dist/sections/HeroCarousel/HeroCarousel.svelte.d.ts +23 -0
  56. package/dist/sections/HeroCarousel/index.d.ts +1 -0
  57. package/dist/sections/HeroCarousel/index.js +1 -0
  58. package/dist/sections/index.d.ts +5 -0
  59. package/dist/sections/index.js +3 -0
  60. package/dist/utils/translations.svelte.d.ts +37 -0
  61. package/dist/utils/translations.svelte.js +67 -0
  62. package/package.json +47 -0
package/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # urpanels-ui-pack
2
+
3
+ Svelte 5 tabanlı urPanels projeleri için tekrar kullanılabilir UI bileşenleri. İçerik şu an PageLayout parçaları, Pagination, LoadingSpinner, InfoCard ve çeviri helper içeriyor.
4
+
5
+ ## Kurulum
6
+
7
+ ```bash
8
+ npm install urpanels-ui-pack
9
+ ```
10
+
11
+ Peer dependency olarak Svelte 5 gerekir.
12
+
13
+ ## Kullanım
14
+
15
+ ```svelte
16
+ <script lang="ts">
17
+ import { PageHeader, PageContent, SearchBar, ViewToggle, ActionButton, Pagination, LoadingSpinner, InfoCard } from 'urpanels-ui-pack';
18
+ </script>
19
+ ```
20
+
21
+ ## Geliştirme
22
+
23
+ - Tip kontrolü: `npm run check`
24
+ - Paketleme: `npm run build` (svelte-package ile `dist` altına paketler ve tipleri üretir)
25
+
26
+ ## İçerik
27
+
28
+ - PageLayout: PageHeader, PageContent, SearchBar, ViewToggle, ActionButton
29
+ - Pagination
30
+ - LoadingSpinner
31
+ - InfoCard (+ tipleri)
32
+ - utils/translations.svelte.ts (global window üstünden çeviri helper)
33
+
34
+ ## Notlar
35
+
36
+ - Yayın öncesi `npm run build` `dist` altında bundle ve `.d.ts` üretir.
37
+ - `package.json` `exports` alanı Svelte + ESM + CJS girişlerini ve tipleri işaret eder.
@@ -0,0 +1,76 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Button Component - UI'dan bağımsız, loading ve disabled state yöneten buton
4
+ *
5
+ * @example
6
+ * <Button
7
+ * loading={isLoading}
8
+ * disabled={isDisabled}
9
+ * onclick={handleClick}
10
+ * class="bg-blue-500 text-white px-4 py-2 rounded"
11
+ * >
12
+ * Kaydet
13
+ * </Button>
14
+ */
15
+
16
+ let {
17
+ onclick = undefined,
18
+ disabled = false,
19
+ loading = false,
20
+ type = 'button',
21
+ class: className = '',
22
+ loadingText = undefined,
23
+ children,
24
+ ...rest
25
+ }: {
26
+ onclick?: (e: MouseEvent) => void | Promise<void>;
27
+ disabled?: boolean;
28
+ loading?: boolean;
29
+ type?: 'button' | 'submit' | 'reset';
30
+ class?: string;
31
+ loadingText?: string;
32
+ children?: any;
33
+ [key: string]: any;
34
+ } = $props();
35
+
36
+ // Loading durumunda otomatik disable
37
+ const isDisabled = $derived(disabled || loading);
38
+ </script>
39
+
40
+ <button
41
+ {type}
42
+ disabled={isDisabled}
43
+ onclick={onclick}
44
+ class={className}
45
+ {...rest}
46
+ >
47
+ {#if loading}
48
+ <span class="flex items-center justify-center gap-2">
49
+ <!-- Spinner SVG -->
50
+ <svg
51
+ class="animate-spin h-4 w-4"
52
+ xmlns="http://www.w3.org/2000/svg"
53
+ fill="none"
54
+ viewBox="0 0 24 24"
55
+ >
56
+ <circle
57
+ class="opacity-25"
58
+ cx="12"
59
+ cy="12"
60
+ r="10"
61
+ stroke="currentColor"
62
+ stroke-width="4"
63
+ ></circle>
64
+ <path
65
+ class="opacity-75"
66
+ fill="currentColor"
67
+ d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
68
+ ></path>
69
+ </svg>
70
+ <!-- Loading metni veya normal children -->
71
+ <span>{loadingText || ''}{#if !loadingText}{@render children()}{/if}</span>
72
+ </span>
73
+ {:else}
74
+ {@render children()}
75
+ {/if}
76
+ </button>
@@ -0,0 +1,13 @@
1
+ type $$ComponentProps = {
2
+ onclick?: (e: MouseEvent) => void | Promise<void>;
3
+ disabled?: boolean;
4
+ loading?: boolean;
5
+ type?: 'button' | 'submit' | 'reset';
6
+ class?: string;
7
+ loadingText?: string;
8
+ children?: any;
9
+ [key: string]: any;
10
+ };
11
+ declare const Button: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type Button = ReturnType<typeof Button>;
13
+ export default Button;
@@ -0,0 +1 @@
1
+ export { default as Button } from './Button.svelte';
@@ -0,0 +1 @@
1
+ export { default as Button } from './Button.svelte';
@@ -0,0 +1,227 @@
1
+ <script lang="ts">
2
+ import type { InfoCardProps } from './InfoCard.types';
3
+ import { t } from '../utils/translations.svelte';
4
+
5
+ let {
6
+ title,
7
+ id,
8
+ variant = 'compact',
9
+ actionLayout = 'icons',
10
+ hover = false,
11
+ className = '',
12
+ subtitle,
13
+ subtitleIcon,
14
+ linkedStatus,
15
+ fields = [],
16
+ badges = [],
17
+ relatedItems,
18
+ note,
19
+ onView,
20
+ onEdit,
21
+ onDelete
22
+ }: InfoCardProps = $props();
23
+
24
+ const isCompact = $derived(variant === 'compact');
25
+ const hasActions = $derived(onView || onEdit || onDelete);
26
+
27
+ function truncateText(text: string, maxLength: number): string {
28
+ if (!text) return '';
29
+ return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
30
+ }
31
+
32
+ function getBadgeColorClasses(color: string): string {
33
+ const colorMap: Record<string, string> = {
34
+ blue: 'bg-blue-100 text-blue-800 border-blue-200',
35
+ purple: 'bg-purple-100 text-purple-800 border-purple-200',
36
+ orange: 'bg-orange-100 text-orange-800 border-orange-200',
37
+ red: 'bg-red-100 text-red-800 border-red-200',
38
+ green: 'bg-green-100 text-green-800 border-green-200',
39
+ gray: 'bg-gray-100 text-gray-800 border-gray-200'
40
+ } as const;
41
+ return colorMap[color] || colorMap.gray;
42
+ }
43
+ </script>
44
+
45
+ <div
46
+ class="bg-white rounded-lg shadow {isCompact ? 'p-3' : 'p-4'} flex flex-col gap-2 {hover ? 'hover:shadow-md transition-shadow' : ''} {className} relative"
47
+ >
48
+ {#if linkedStatus?.isLinked}
49
+ <div class="absolute -top-1 -right-1 bg-indigo-500 text-white rounded-full w-5 h-5 flex items-center justify-center shadow-sm ring-2 ring-white" title="Kullanıcı hesabına bağlı">
50
+ <svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
51
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1" />
52
+ </svg>
53
+ </div>
54
+ {/if}
55
+
56
+ <div class="flex items-start justify-between gap-2">
57
+ <div class="flex-1 min-w-0">
58
+ <div class="flex items-center gap-2">
59
+ <h3 class="font-bold {isCompact ? 'text-sm' : 'text-lg'} truncate">
60
+ {title}
61
+ </h3>
62
+ {#if linkedStatus}
63
+ {#if !linkedStatus.isLinked && linkedStatus.hasEmail && linkedStatus.onLink}
64
+ <button
65
+ onclick={linkedStatus.onLink}
66
+ class="text-orange-600 {isCompact ? 'text-[10px] px-1.5 py-0.5' : 'text-xs px-2 py-1'} bg-orange-50 hover:bg-orange-100 rounded border border-orange-200 transition-colors"
67
+ >
68
+ {linkedStatus.linkText || t('common.link')}
69
+ </button>
70
+ {/if}
71
+ {/if}
72
+ </div>
73
+
74
+ {#if subtitle}
75
+ <div class="{isCompact ? 'text-[10px]' : 'text-xs'} text-gray-600 mt-0.5 truncate">
76
+ {#if subtitleIcon}<span class="mr-1">{subtitleIcon}</span>{/if}
77
+ {subtitle}
78
+ </div>
79
+ {/if}
80
+ </div>
81
+
82
+ {#if actionLayout === 'icons' && hasActions}
83
+ <div class="flex items-center gap-1 flex-shrink-0">
84
+ {#if onView}
85
+ <button
86
+ onclick={onView}
87
+ class="p-1.5 rounded hover:bg-green-50 text-green-600 transition-colors"
88
+ aria-label={t('common.view')}
89
+ >
90
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
91
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
92
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
93
+ </svg>
94
+ </button>
95
+ {/if}
96
+ {#if onEdit}
97
+ <button
98
+ onclick={onEdit}
99
+ class="p-1.5 rounded hover:bg-blue-50 text-blue-600 transition-colors"
100
+ aria-label={t('common.edit')}
101
+ >
102
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
103
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
104
+ </svg>
105
+ </button>
106
+ {/if}
107
+ {#if onDelete}
108
+ <button
109
+ onclick={onDelete}
110
+ class="p-1.5 rounded hover:bg-red-50 text-red-600 transition-colors"
111
+ aria-label={t('common.delete')}
112
+ >
113
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
114
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
115
+ </svg>
116
+ </button>
117
+ {/if}
118
+ </div>
119
+ {/if}
120
+ </div>
121
+
122
+ {#if fields && fields.length > 0}
123
+ <div class="grid grid-cols-2 gap-2 {isCompact ? 'text-xs' : 'text-sm'}">
124
+ {#each fields as field}
125
+ {#if field.condition !== false}
126
+ <div class="{field.span === 2 ? 'col-span-2' : ''} truncate">
127
+ <span class="font-semibold text-gray-700">{field.label}:</span>
128
+ {#if field.icon}<span class="mx-1">{field.icon}</span>{/if}
129
+ <span class="text-gray-900">{field.value || '-'}</span>
130
+ </div>
131
+ {/if}
132
+ {/each}
133
+ </div>
134
+ {/if}
135
+
136
+ {#if badges && badges.length > 0}
137
+ {#each badges as badgeGroup}
138
+ {#if badgeGroup.items && badgeGroup.items.length > 0}
139
+ {#snippet badgeSection()}
140
+ {@const displayItems = badgeGroup.maxDisplay ? badgeGroup.items.slice(0, badgeGroup.maxDisplay) : badgeGroup.items}
141
+ {@const remaining = badgeGroup.maxDisplay && badgeGroup.items.length > badgeGroup.maxDisplay ? badgeGroup.items.length - badgeGroup.maxDisplay : 0}
142
+
143
+ <div class="mt-1">
144
+ {#if badgeGroup.label}
145
+ <div class="{isCompact ? 'text-[10px]' : 'text-xs'} font-semibold text-gray-600 mb-1">
146
+ {#if badgeGroup.icon}<span class="mr-1">{badgeGroup.icon}</span>{/if}
147
+ {badgeGroup.label}
148
+ </div>
149
+ {/if}
150
+ <div class="flex flex-wrap gap-1">
151
+ {#each displayItems as item}
152
+ <span class="{isCompact ? 'text-[10px] px-1.5 py-0.5' : 'text-xs px-2 py-1'} rounded border {getBadgeColorClasses(badgeGroup.color)}">
153
+ {item}
154
+ </span>
155
+ {/each}
156
+
157
+ {#if remaining > 0}
158
+ <span class="{isCompact ? 'text-[10px] px-1.5 py-0.5' : 'text-xs px-2 py-1'} rounded border {getBadgeColorClasses(badgeGroup.color)}">
159
+ +{remaining}
160
+ </span>
161
+ {/if}
162
+ </div>
163
+ </div>
164
+ {/snippet}
165
+
166
+ {@render badgeSection()}
167
+ {/if}
168
+ {/each}
169
+ {/if}
170
+
171
+ {#if relatedItems && relatedItems.items && relatedItems.items.length > 0}
172
+ <div class="mt-2 border border-gray-200 rounded p-2 bg-gray-50">
173
+ <div class="{isCompact ? 'text-xs' : 'text-sm'} font-semibold text-gray-700 mb-1.5">
174
+ {relatedItems.title}
175
+ </div>
176
+ <div class="space-y-1">
177
+ {#each relatedItems.items as item}
178
+ <div class="{isCompact ? 'text-[10px]' : 'text-xs'} flex items-center justify-between">
179
+ <span class="text-gray-900">{item.primary}</span>
180
+ <div class="flex items-center gap-1">
181
+ {#if item.secondary}
182
+ <span class="text-gray-600">{item.secondary}</span>
183
+ {/if}
184
+ {#if item.badge}
185
+ <span class="px-1.5 py-0.5 bg-blue-100 text-blue-800 rounded text-[10px]">
186
+ {item.badge}
187
+ </span>
188
+ {/if}
189
+ </div>
190
+ </div>
191
+ {/each}
192
+ </div>
193
+ </div>
194
+ {:else if relatedItems && relatedItems.emptyText}
195
+ <div class="mt-2 {isCompact ? 'text-xs' : 'text-sm'} text-gray-500 italic">
196
+ {relatedItems.emptyText}
197
+ </div>
198
+ {/if}
199
+
200
+ {#if note && note.text}
201
+ <div class="mt-1 {isCompact ? 'text-[10px]' : 'text-xs'} text-gray-600">
202
+ {#if note.icon}<span class="mr-1">{note.icon}</span>{/if}
203
+ {note.maxLength ? truncateText(note.text, note.maxLength) : note.text}
204
+ </div>
205
+ {/if}
206
+
207
+ {#if actionLayout === 'buttons' && hasActions}
208
+ <div class="mt-3 flex justify-end gap-2 pt-2 border-t border-gray-100">
209
+ {#if onEdit}
210
+ <button
211
+ onclick={onEdit}
212
+ class="{isCompact ? 'px-2 py-1 text-xs' : 'px-3 py-1.5 text-sm'} bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors font-medium"
213
+ >
214
+ {t('common.edit')}
215
+ </button>
216
+ {/if}
217
+ {#if onDelete}
218
+ <button
219
+ onclick={onDelete}
220
+ class="{isCompact ? 'px-2 py-1 text-xs' : 'px-3 py-1.5 text-sm'} bg-red-500 text-white rounded hover:bg-red-600 transition-colors font-medium"
221
+ >
222
+ {t('common.delete')}
223
+ </button>
224
+ {/if}
225
+ </div>
226
+ {/if}
227
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { InfoCardProps } from './InfoCard.types';
2
+ declare const InfoCard: import("svelte").Component<InfoCardProps, {}, "">;
3
+ type InfoCard = ReturnType<typeof InfoCard>;
4
+ export default InfoCard;
@@ -0,0 +1,56 @@
1
+ export type InfoCardVariant = 'default' | 'compact';
2
+ export type ActionLayout = 'icons' | 'buttons';
3
+ export type BadgeColor = 'blue' | 'purple' | 'orange' | 'red' | 'green' | 'gray';
4
+ export interface InfoCardField {
5
+ label: string;
6
+ value: string | number;
7
+ icon?: string;
8
+ condition?: boolean;
9
+ span?: 1 | 2;
10
+ }
11
+ export interface InfoCardBadge {
12
+ items: string[];
13
+ color: BadgeColor;
14
+ icon?: string;
15
+ maxDisplay?: number;
16
+ label?: string;
17
+ }
18
+ export interface InfoCardRelatedItem {
19
+ primary: string;
20
+ secondary?: string;
21
+ badge?: string;
22
+ }
23
+ export interface InfoCardRelatedSection {
24
+ title: string;
25
+ items: InfoCardRelatedItem[];
26
+ emptyText?: string;
27
+ }
28
+ export interface InfoCardLinkedStatus {
29
+ isLinked: boolean;
30
+ hasEmail: boolean;
31
+ onLink?: () => void;
32
+ linkText?: string;
33
+ }
34
+ export interface InfoCardNote {
35
+ text: string;
36
+ icon?: string;
37
+ maxLength?: number;
38
+ }
39
+ export interface InfoCardProps {
40
+ title: string;
41
+ id: string;
42
+ variant?: InfoCardVariant;
43
+ actionLayout?: ActionLayout;
44
+ hover?: boolean;
45
+ className?: string;
46
+ subtitle?: string;
47
+ subtitleIcon?: string;
48
+ linkedStatus?: InfoCardLinkedStatus;
49
+ fields?: InfoCardField[];
50
+ badges?: InfoCardBadge[];
51
+ relatedItems?: InfoCardRelatedSection;
52
+ note?: InfoCardNote;
53
+ onView?: () => void;
54
+ onEdit?: () => void;
55
+ onDelete?: () => void;
56
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ export { default as InfoCard } from './InfoCard.svelte';
2
+ export type * from './InfoCard.types';
@@ -0,0 +1 @@
1
+ export { default as InfoCard } from './InfoCard.svelte';
@@ -0,0 +1,3 @@
1
+ <div class="flex items-center justify-center py-12">
2
+ <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
3
+ </div>
@@ -0,0 +1,26 @@
1
+ export default LoadingSpinner;
2
+ type LoadingSpinner = SvelteComponent<{
3
+ [x: string]: never;
4
+ }, {
5
+ [evt: string]: CustomEvent<any>;
6
+ }, {}> & {
7
+ $$bindings?: string;
8
+ };
9
+ declare const LoadingSpinner: $$__sveltets_2_IsomorphicComponent<{
10
+ [x: string]: never;
11
+ }, {
12
+ [evt: string]: CustomEvent<any>;
13
+ }, {}, {}, string>;
14
+ 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> {
15
+ new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
16
+ $$bindings?: Bindings;
17
+ } & Exports;
18
+ (internal: unknown, props: {
19
+ $$events?: Events;
20
+ $$slots?: Slots;
21
+ }): Exports & {
22
+ $set?: any;
23
+ $on?: any;
24
+ };
25
+ z_$$bindings?: Bindings;
26
+ }
@@ -0,0 +1 @@
1
+ export { default as LoadingSpinner } from './LoadingSpinner.svelte';
@@ -0,0 +1 @@
1
+ export { default as LoadingSpinner } from './LoadingSpinner.svelte';
@@ -0,0 +1,122 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ interface Props {
5
+ open: boolean;
6
+ title: string;
7
+ description?: string;
8
+ icon?: string;
9
+ color?: 'blue' | 'green' | 'amber' | 'red' | 'purple' | 'gray' | 'emerald' | 'teal';
10
+ size?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | 'full';
11
+ showClose?: boolean;
12
+ onClose?: () => void;
13
+ children?: Snippet;
14
+ actions?: Snippet;
15
+ }
16
+
17
+ let {
18
+ open,
19
+ title,
20
+ description = '',
21
+ icon = '',
22
+ color = 'blue',
23
+ size = 'md',
24
+ showClose = true,
25
+ onClose,
26
+ children,
27
+ actions
28
+ }: Props = $props();
29
+
30
+ // Modal açıkken body scroll'u kapat
31
+ $effect(() => {
32
+ if (open) {
33
+ document.body.style.overflow = 'hidden';
34
+ } else {
35
+ document.body.style.overflow = '';
36
+ }
37
+
38
+ // Cleanup - modal unmount olduğunda
39
+ return () => {
40
+ document.body.style.overflow = '';
41
+ };
42
+ });
43
+
44
+ const panelSizes = {
45
+ sm: 'max-w-md',
46
+ md: 'max-w-2xl',
47
+ lg: 'max-w-3xl',
48
+ xl: 'max-w-4xl',
49
+ '2xl': 'max-w-5xl',
50
+ '3xl': 'max-w-6xl',
51
+ full: 'max-w-[95vw]'
52
+ } as const;
53
+
54
+ // Gradient header renkleri (kurs template gibi)
55
+ const gradientColors: Record<string, string> = {
56
+ blue: 'from-blue-600 to-indigo-600',
57
+ green: 'from-green-600 to-emerald-600',
58
+ emerald: 'from-emerald-600 to-teal-600',
59
+ teal: 'from-teal-600 to-cyan-600',
60
+ amber: 'from-amber-500 to-orange-500',
61
+ red: 'from-red-600 to-rose-600',
62
+ purple: 'from-purple-600 to-indigo-600',
63
+ gray: 'from-gray-600 to-slate-600'
64
+ };
65
+ </script>
66
+
67
+ {#if open}
68
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
69
+ <!-- svelte-ignore a11y_interactive_supports_focus -->
70
+ <div
71
+ class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm"
72
+ role="dialog"
73
+ aria-modal="true"
74
+ onclick={(e) => e.target === e.currentTarget && onClose?.()}
75
+ >
76
+ <div
77
+ class={`w-full ${panelSizes[size]} mx-4 bg-white rounded-2xl shadow-2xl overflow-hidden flex flex-col max-h-[90vh]`}
78
+ >
79
+ <!-- Gradient Header (kurs template gibi) -->
80
+ <div class={`bg-gradient-to-r ${gradientColors[color]} px-6 py-5 flex items-center justify-between shrink-0`}>
81
+ <div class="flex items-center gap-3">
82
+ {#if icon}
83
+ <div class="bg-white/20 backdrop-blur-sm p-2.5 rounded-xl">
84
+ <span class="text-xl text-white">{icon}</span>
85
+ </div>
86
+ {/if}
87
+ <div>
88
+ <h2 class="text-xl font-bold text-white">{title}</h2>
89
+ {#if description}
90
+ <p class="text-sm text-white/80 mt-0.5">{description}</p>
91
+ {/if}
92
+ </div>
93
+ </div>
94
+ {#if showClose}
95
+ <button
96
+ class="text-white/80 hover:text-white hover:bg-white/20 p-2 rounded-lg transition-all"
97
+ aria-label="Kapat"
98
+ onclick={() => onClose?.()}
99
+ >
100
+ <svg class="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
101
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
102
+ </svg>
103
+ </button>
104
+ {/if}
105
+ </div>
106
+
107
+ <!-- Scrollable Content -->
108
+ <div class="flex-1 overflow-y-auto p-6">
109
+ {#if children}
110
+ {@render children()}
111
+ {/if}
112
+ </div>
113
+
114
+ <!-- Footer Actions -->
115
+ {#if actions}
116
+ <div class="px-6 py-4 bg-gray-50 border-t border-gray-100 shrink-0">
117
+ {@render actions()}
118
+ </div>
119
+ {/if}
120
+ </div>
121
+ </div>
122
+ {/if}
@@ -0,0 +1,39 @@
1
+ import type { Snippet } from 'svelte';
2
+ interface Props {
3
+ open: boolean;
4
+ title: string;
5
+ description?: string;
6
+ icon?: string;
7
+ color?: 'blue' | 'green' | 'amber' | 'red' | 'purple' | 'gray';
8
+ size?: 'sm' | 'md' | 'lg';
9
+ showClose?: boolean;
10
+ onClose?: () => void;
11
+ actions?: Snippet;
12
+ }
13
+ 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> {
14
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
15
+ $$bindings?: Bindings;
16
+ } & Exports;
17
+ (internal: unknown, props: Props & {
18
+ $$events?: Events;
19
+ $$slots?: Slots;
20
+ }): Exports & {
21
+ $set?: any;
22
+ $on?: any;
23
+ };
24
+ z_$$bindings?: Bindings;
25
+ }
26
+ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
27
+ default: any;
28
+ } ? Props extends Record<string, never> ? any : {
29
+ children?: any;
30
+ } : {});
31
+ declare const Modal: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<Props, {
32
+ default: {};
33
+ }>, {
34
+ [evt: string]: CustomEvent<any>;
35
+ }, {
36
+ default: {};
37
+ }, {}, "">;
38
+ type Modal = InstanceType<typeof Modal>;
39
+ export default Modal;
@@ -0,0 +1 @@
1
+ export { default as Modal } from './Modal.svelte';
@@ -0,0 +1 @@
1
+ export { default as Modal } from './Modal.svelte';
@@ -0,0 +1,26 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ label: string;
4
+ onclick: () => void;
5
+ color?: 'green' | 'blue' | 'red' | 'gray';
6
+ }
7
+
8
+ let { label, onclick, color = 'green' }: Props = $props();
9
+
10
+ const colorClasses = {
11
+ green: 'bg-green-500 hover:bg-green-600',
12
+ blue: 'bg-blue-500 hover:bg-blue-600',
13
+ red: 'bg-red-500 hover:bg-red-600',
14
+ gray: 'bg-gray-500 hover:bg-gray-600'
15
+ } as const;
16
+ </script>
17
+
18
+ <button
19
+ class="p-2 md:px-4 md:py-2 {colorClasses[color]} text-white rounded-lg transition-colors flex items-center justify-center gap-2"
20
+ {onclick}
21
+ >
22
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
23
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
24
+ </svg>
25
+ <span class="hidden md:inline text-sm font-semibold">{label}</span>
26
+ </button>
@@ -0,0 +1,8 @@
1
+ interface Props {
2
+ label: string;
3
+ onclick: () => void;
4
+ color?: 'green' | 'blue' | 'red' | 'gray';
5
+ }
6
+ declare const ActionButton: import("svelte").Component<Props, {}, "">;
7
+ type ActionButton = ReturnType<typeof ActionButton>;
8
+ export default ActionButton;