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.
- package/README.md +37 -0
- package/dist/Button/Button.svelte +76 -0
- package/dist/Button/Button.svelte.d.ts +13 -0
- package/dist/Button/index.d.ts +1 -0
- package/dist/Button/index.js +1 -0
- package/dist/InfoCard/InfoCard.svelte +227 -0
- package/dist/InfoCard/InfoCard.svelte.d.ts +4 -0
- package/dist/InfoCard/InfoCard.types.d.ts +56 -0
- package/dist/InfoCard/InfoCard.types.js +1 -0
- package/dist/InfoCard/index.d.ts +2 -0
- package/dist/InfoCard/index.js +1 -0
- package/dist/LoadingSpinner/LoadingSpinner.svelte +3 -0
- package/dist/LoadingSpinner/LoadingSpinner.svelte.d.ts +26 -0
- package/dist/LoadingSpinner/index.d.ts +1 -0
- package/dist/LoadingSpinner/index.js +1 -0
- package/dist/Modal/Modal.svelte +122 -0
- package/dist/Modal/Modal.svelte.d.ts +39 -0
- package/dist/Modal/index.d.ts +1 -0
- package/dist/Modal/index.js +1 -0
- package/dist/PageLayout/ActionButton.svelte +26 -0
- package/dist/PageLayout/ActionButton.svelte.d.ts +8 -0
- package/dist/PageLayout/PageContent.svelte +19 -0
- package/dist/PageLayout/PageContent.svelte.d.ts +8 -0
- package/dist/PageLayout/PageHeader.svelte +29 -0
- package/dist/PageLayout/PageHeader.svelte.d.ts +9 -0
- package/dist/PageLayout/SearchBar.svelte +25 -0
- package/dist/PageLayout/SearchBar.svelte.d.ts +8 -0
- package/dist/PageLayout/ViewToggle.svelte +53 -0
- package/dist/PageLayout/ViewToggle.svelte.d.ts +8 -0
- package/dist/PageLayout/index.d.ts +5 -0
- package/dist/PageLayout/index.js +5 -0
- package/dist/Pagination/Pagination.svelte +96 -0
- package/dist/Pagination/Pagination.svelte.d.ts +4 -0
- package/dist/Pagination/Pagination.types.d.ts +11 -0
- package/dist/Pagination/Pagination.types.js +1 -0
- package/dist/Pagination/index.d.ts +2 -0
- package/dist/Pagination/index.js +1 -0
- package/dist/RichTextEditor/RichTextEditor.svelte +575 -0
- package/dist/RichTextEditor/RichTextEditor.svelte.d.ts +10 -0
- package/dist/RichTextEditor/index.d.ts +1 -0
- package/dist/RichTextEditor/index.js +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +12 -0
- package/dist/sections/ArticlesGrid/ArticlesGrid.svelte +201 -0
- package/dist/sections/ArticlesGrid/ArticlesGrid.svelte.d.ts +31 -0
- package/dist/sections/ArticlesGrid/README.md +229 -0
- package/dist/sections/ArticlesGrid/index.d.ts +2 -0
- package/dist/sections/ArticlesGrid/index.js +1 -0
- package/dist/sections/FeaturedGalleryGrid/FeaturedGalleryGrid.svelte +118 -0
- package/dist/sections/FeaturedGalleryGrid/FeaturedGalleryGrid.svelte.d.ts +21 -0
- package/dist/sections/FeaturedGalleryGrid/README.md +270 -0
- package/dist/sections/FeaturedGalleryGrid/index.d.ts +2 -0
- package/dist/sections/FeaturedGalleryGrid/index.js +1 -0
- package/dist/sections/HeroCarousel/HeroCarousel.svelte +318 -0
- package/dist/sections/HeroCarousel/HeroCarousel.svelte.d.ts +23 -0
- package/dist/sections/HeroCarousel/index.d.ts +1 -0
- package/dist/sections/HeroCarousel/index.js +1 -0
- package/dist/sections/index.d.ts +5 -0
- package/dist/sections/index.js +3 -0
- package/dist/utils/translations.svelte.d.ts +37 -0
- package/dist/utils/translations.svelte.js +67 -0
- package/package.json +47 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* ArticlesGrid Component - Framework-agnostic article cards grid
|
|
4
|
+
* Extracted from av-talha-web for reusability
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface Article {
|
|
8
|
+
slug: string;
|
|
9
|
+
title: string;
|
|
10
|
+
date?: string;
|
|
11
|
+
featuredImage?: string;
|
|
12
|
+
images?: string[];
|
|
13
|
+
summary?: string;
|
|
14
|
+
header?: string;
|
|
15
|
+
content?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
articles: Article[];
|
|
20
|
+
translations: {
|
|
21
|
+
articlesLabel: string;
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
readMore: string;
|
|
25
|
+
viewAll: string;
|
|
26
|
+
};
|
|
27
|
+
locale?: string;
|
|
28
|
+
onArticleClick?: (slug: string) => void;
|
|
29
|
+
onViewAllClick?: () => void;
|
|
30
|
+
imageResolver?: (path: string) => string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let {
|
|
34
|
+
articles = [],
|
|
35
|
+
translations,
|
|
36
|
+
locale = 'tr',
|
|
37
|
+
onArticleClick,
|
|
38
|
+
onViewAllClick,
|
|
39
|
+
imageResolver = (path: string) => path
|
|
40
|
+
}: Props = $props();
|
|
41
|
+
|
|
42
|
+
const formatDate = (dateStr: string, locale: string): string => {
|
|
43
|
+
const date = new Date(dateStr);
|
|
44
|
+
if (locale === 'tr') {
|
|
45
|
+
return date.toLocaleDateString('tr-TR', {
|
|
46
|
+
year: 'numeric',
|
|
47
|
+
month: 'long',
|
|
48
|
+
day: 'numeric'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
return date.toLocaleDateString('en-US', {
|
|
52
|
+
year: 'numeric',
|
|
53
|
+
month: 'long',
|
|
54
|
+
day: 'numeric'
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const handleArticleClick = (e: MouseEvent, slug: string) => {
|
|
59
|
+
if (onArticleClick) {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
onArticleClick(slug);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleViewAllClick = (e: MouseEvent) => {
|
|
66
|
+
if (onViewAllClick) {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
onViewAllClick();
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const getArticleImage = (article: Article): string | null => {
|
|
73
|
+
return article.featuredImage || (article.images && article.images[0]) || null;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const getArticleExcerpt = (article: Article): string => {
|
|
77
|
+
const text = article.summary || article.header || article.content || '';
|
|
78
|
+
return text.replace(/<[^>]*>/g, '').substring(0, 150);
|
|
79
|
+
};
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<section class="py-20 bg-gray-50">
|
|
83
|
+
<div class="container mx-auto px-4">
|
|
84
|
+
<div class="text-center mb-16">
|
|
85
|
+
<span class="text-primary-600 font-semibold text-sm uppercase tracking-wider mb-2 block">
|
|
86
|
+
{translations.articlesLabel}
|
|
87
|
+
</span>
|
|
88
|
+
<h2 class="text-4xl md:text-5xl font-bold text-gray-900 mb-4">
|
|
89
|
+
{translations.title}
|
|
90
|
+
</h2>
|
|
91
|
+
<p class="text-xl text-gray-600 max-w-2xl mx-auto">
|
|
92
|
+
{translations.description}
|
|
93
|
+
</p>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-7xl mx-auto">
|
|
97
|
+
{#each articles as article}
|
|
98
|
+
<article class="group bg-white rounded-2xl shadow-lg hover:shadow-2xl transition-all duration-300 overflow-hidden transform hover:-translate-y-2">
|
|
99
|
+
{#if getArticleImage(article)}
|
|
100
|
+
<a
|
|
101
|
+
href="#"
|
|
102
|
+
onclick={(e) => handleArticleClick(e, article.slug)}
|
|
103
|
+
class="block relative overflow-hidden aspect-[16/10]"
|
|
104
|
+
>
|
|
105
|
+
<div class="absolute inset-0 bg-gradient-to-t from-black/50 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300 z-10"></div>
|
|
106
|
+
<img
|
|
107
|
+
src={imageResolver(getArticleImage(article)!)}
|
|
108
|
+
alt={article.title}
|
|
109
|
+
class="w-full h-full object-cover transform group-hover:scale-110 transition-transform duration-500"
|
|
110
|
+
/>
|
|
111
|
+
</a>
|
|
112
|
+
{:else}
|
|
113
|
+
<div class="aspect-[16/10] bg-gradient-to-br from-primary-100 to-primary-50 flex items-center justify-center">
|
|
114
|
+
<svg class="w-16 h-16 text-primary-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
115
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" />
|
|
116
|
+
</svg>
|
|
117
|
+
</div>
|
|
118
|
+
{/if}
|
|
119
|
+
|
|
120
|
+
<div class="p-8">
|
|
121
|
+
{#if article.date}
|
|
122
|
+
<div class="flex items-center text-sm text-gray-500 mb-4">
|
|
123
|
+
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
124
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
|
|
125
|
+
</svg>
|
|
126
|
+
{formatDate(article.date, locale)}
|
|
127
|
+
</div>
|
|
128
|
+
{/if}
|
|
129
|
+
|
|
130
|
+
<h3 class="text-2xl font-bold text-gray-900 mb-4 line-clamp-2 group-hover:text-primary-600 transition-colors">
|
|
131
|
+
<a href="#" onclick={(e) => handleArticleClick(e, article.slug)}>
|
|
132
|
+
{article.title}
|
|
133
|
+
</a>
|
|
134
|
+
</h3>
|
|
135
|
+
|
|
136
|
+
{#if article.summary || article.header || article.content}
|
|
137
|
+
<p class="text-gray-600 mb-6 line-clamp-3 leading-relaxed">
|
|
138
|
+
{getArticleExcerpt(article)}...
|
|
139
|
+
</p>
|
|
140
|
+
{/if}
|
|
141
|
+
|
|
142
|
+
<a
|
|
143
|
+
href="#"
|
|
144
|
+
onclick={(e) => handleArticleClick(e, article.slug)}
|
|
145
|
+
class="inline-flex items-center text-primary-600 hover:text-primary-700 font-semibold group/link"
|
|
146
|
+
>
|
|
147
|
+
<span>{translations.readMore}</span>
|
|
148
|
+
<svg class="w-5 h-5 ml-2 transform group-hover/link:translate-x-1 transition-transform" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
149
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
|
150
|
+
</svg>
|
|
151
|
+
</a>
|
|
152
|
+
</div>
|
|
153
|
+
</article>
|
|
154
|
+
{/each}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="text-center mt-16">
|
|
158
|
+
<a
|
|
159
|
+
href="#"
|
|
160
|
+
onclick={handleViewAllClick}
|
|
161
|
+
class="inline-flex items-center px-8 py-4 bg-gradient-to-r from-[#d6bb6d] via-[#c9a858] to-[#b89544] text-white font-bold text-lg rounded-xl hover:from-[#e0c97d] hover:via-[#d4b868] hover:to-[#c9a858] transform hover:scale-105 transition-all shadow-xl hover:shadow-2xl"
|
|
162
|
+
>
|
|
163
|
+
<span>{translations.viewAll}</span>
|
|
164
|
+
<svg class="w-6 h-6 ml-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
165
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8l4 4m0 0l-4 4m4-4H3" />
|
|
166
|
+
</svg>
|
|
167
|
+
</a>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
</section>
|
|
171
|
+
|
|
172
|
+
<style>
|
|
173
|
+
/* Scoped Tailwind-like styles - framework agnostic */
|
|
174
|
+
section :global(.container) {
|
|
175
|
+
max-width: 1280px;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Line clamp utility classes */
|
|
179
|
+
:global(.line-clamp-2) {
|
|
180
|
+
display: -webkit-box;
|
|
181
|
+
-webkit-line-clamp: 2;
|
|
182
|
+
-webkit-box-orient: vertical;
|
|
183
|
+
overflow: hidden;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
:global(.line-clamp-3) {
|
|
187
|
+
display: -webkit-box;
|
|
188
|
+
-webkit-line-clamp: 3;
|
|
189
|
+
-webkit-box-orient: vertical;
|
|
190
|
+
overflow: hidden;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* Primary color fallbacks if not defined in parent */
|
|
194
|
+
section {
|
|
195
|
+
--primary-50: #fef5e7;
|
|
196
|
+
--primary-100: #fce8c4;
|
|
197
|
+
--primary-300: #f5c76e;
|
|
198
|
+
--primary-600: #d6bb6d;
|
|
199
|
+
--primary-700: #c9a858;
|
|
200
|
+
}
|
|
201
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ArticlesGrid Component - Framework-agnostic article cards grid
|
|
3
|
+
* Extracted from av-talha-web for reusability
|
|
4
|
+
*/
|
|
5
|
+
export interface Article {
|
|
6
|
+
slug: string;
|
|
7
|
+
title: string;
|
|
8
|
+
date?: string;
|
|
9
|
+
featuredImage?: string;
|
|
10
|
+
images?: string[];
|
|
11
|
+
summary?: string;
|
|
12
|
+
header?: string;
|
|
13
|
+
content?: string;
|
|
14
|
+
}
|
|
15
|
+
interface Props {
|
|
16
|
+
articles: Article[];
|
|
17
|
+
translations: {
|
|
18
|
+
articlesLabel: string;
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
readMore: string;
|
|
22
|
+
viewAll: string;
|
|
23
|
+
};
|
|
24
|
+
locale?: string;
|
|
25
|
+
onArticleClick?: (slug: string) => void;
|
|
26
|
+
onViewAllClick?: () => void;
|
|
27
|
+
imageResolver?: (path: string) => string;
|
|
28
|
+
}
|
|
29
|
+
declare const ArticlesGrid: import("svelte").Component<Props, {}, "">;
|
|
30
|
+
type ArticlesGrid = ReturnType<typeof ArticlesGrid>;
|
|
31
|
+
export default ArticlesGrid;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# ArticlesGrid Component
|
|
2
|
+
|
|
3
|
+
Framework-agnostic articles grid component extracted from av-talha-web project.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ Responsive 3-column grid (mobile: 1 column)
|
|
8
|
+
- ✅ Article cards with image, date, title, excerpt
|
|
9
|
+
- ✅ Hover animations & transitions
|
|
10
|
+
- ✅ Framework-agnostic event handlers
|
|
11
|
+
- ✅ Customizable translations
|
|
12
|
+
- ✅ Optional image resolver
|
|
13
|
+
- ✅ Fallback placeholder for articles without images
|
|
14
|
+
- ✅ View all button with custom click handler
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```svelte
|
|
19
|
+
<script>
|
|
20
|
+
import { ArticlesGrid } from '@urga-panel/urPanels-ui-pack/sections';
|
|
21
|
+
|
|
22
|
+
const articles = [
|
|
23
|
+
{
|
|
24
|
+
slug: 'article-1',
|
|
25
|
+
title: 'Article Title',
|
|
26
|
+
date: '2024-01-15',
|
|
27
|
+
featuredImage: '/images/article1.jpg',
|
|
28
|
+
summary: 'Article summary text...',
|
|
29
|
+
},
|
|
30
|
+
// ... more articles
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const translations = {
|
|
34
|
+
articlesLabel: 'ARTICLES',
|
|
35
|
+
title: 'Latest Articles',
|
|
36
|
+
description: 'Read our latest legal insights and updates',
|
|
37
|
+
readMore: 'Read More',
|
|
38
|
+
viewAll: 'View All Articles'
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function handleArticleClick(slug: string) {
|
|
42
|
+
// Navigate to article detail page
|
|
43
|
+
window.location.href = `/articles/${slug}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function handleViewAllClick() {
|
|
47
|
+
// Navigate to articles list page
|
|
48
|
+
window.location.href = '/articles';
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<ArticlesGrid
|
|
53
|
+
{articles}
|
|
54
|
+
{translations}
|
|
55
|
+
locale="en"
|
|
56
|
+
onArticleClick={handleArticleClick}
|
|
57
|
+
onViewAllClick={handleViewAllClick}
|
|
58
|
+
/>
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Props
|
|
62
|
+
|
|
63
|
+
### `articles` (required)
|
|
64
|
+
Type: `Article[]`
|
|
65
|
+
|
|
66
|
+
Array of article objects:
|
|
67
|
+
```typescript
|
|
68
|
+
interface Article {
|
|
69
|
+
slug: string; // Unique identifier
|
|
70
|
+
title: string; // Article title
|
|
71
|
+
date?: string; // ISO date string (e.g., '2024-01-15')
|
|
72
|
+
featuredImage?: string; // Primary image URL
|
|
73
|
+
images?: string[]; // Fallback images array
|
|
74
|
+
summary?: string; // Article summary/excerpt
|
|
75
|
+
header?: string; // Fallback excerpt source
|
|
76
|
+
content?: string; // Fallback excerpt source (HTML stripped)
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### `translations` (required)
|
|
81
|
+
Type: `object`
|
|
82
|
+
|
|
83
|
+
Localized text content:
|
|
84
|
+
```typescript
|
|
85
|
+
{
|
|
86
|
+
articlesLabel: string; // Small label above title
|
|
87
|
+
title: string; // Main section heading
|
|
88
|
+
description: string; // Subtitle/description
|
|
89
|
+
readMore: string; // "Read More" link text
|
|
90
|
+
viewAll: string; // "View All" button text
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `locale` (optional)
|
|
95
|
+
Type: `string`
|
|
96
|
+
Default: `'tr'`
|
|
97
|
+
|
|
98
|
+
Locale for date formatting (`'tr'` or `'en'`).
|
|
99
|
+
|
|
100
|
+
### `onArticleClick` (optional)
|
|
101
|
+
Type: `(slug: string) => void`
|
|
102
|
+
|
|
103
|
+
Click handler for article cards and "Read More" links.
|
|
104
|
+
|
|
105
|
+
### `onViewAllClick` (optional)
|
|
106
|
+
Type: `() => void`
|
|
107
|
+
|
|
108
|
+
Click handler for "View All Articles" button.
|
|
109
|
+
|
|
110
|
+
### `imageResolver` (optional)
|
|
111
|
+
Type: `(path: string) => string`
|
|
112
|
+
Default: `(path) => path`
|
|
113
|
+
|
|
114
|
+
Function to resolve/transform image paths (e.g., CDN URLs).
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
### With SvelteKit
|
|
119
|
+
|
|
120
|
+
```svelte
|
|
121
|
+
<script lang="ts">
|
|
122
|
+
import { ArticlesGrid } from '@urga-panel/urPanels-ui-pack/sections';
|
|
123
|
+
import { goto } from '$app/navigation';
|
|
124
|
+
import { localizedPath, locale, t } from '$lib/i18n';
|
|
125
|
+
|
|
126
|
+
export let data;
|
|
127
|
+
|
|
128
|
+
const translations = {
|
|
129
|
+
articlesLabel: $t('nav.articles'),
|
|
130
|
+
title: $t('home.articles.title'),
|
|
131
|
+
description: $t('home.articles.description'),
|
|
132
|
+
readMore: $t('articles.readMore'),
|
|
133
|
+
viewAll: $t('home.articles.viewAll')
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
function handleArticleClick(slug: string) {
|
|
137
|
+
goto(localizedPath(`/articles/${slug}`, $locale));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function handleViewAllClick() {
|
|
141
|
+
goto(localizedPath('/articles', $locale));
|
|
142
|
+
}
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<ArticlesGrid
|
|
146
|
+
articles={data.featuredBlogs}
|
|
147
|
+
{translations}
|
|
148
|
+
locale={$locale}
|
|
149
|
+
onArticleClick={handleArticleClick}
|
|
150
|
+
onViewAllClick={handleViewAllClick}
|
|
151
|
+
/>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### With Custom Image Resolver
|
|
155
|
+
|
|
156
|
+
```svelte
|
|
157
|
+
<script>
|
|
158
|
+
const imageResolver = (path) => {
|
|
159
|
+
// Add CDN prefix
|
|
160
|
+
if (path.startsWith('/')) {
|
|
161
|
+
return `https://cdn.example.com${path}`;
|
|
162
|
+
}
|
|
163
|
+
return path;
|
|
164
|
+
};
|
|
165
|
+
</script>
|
|
166
|
+
|
|
167
|
+
<ArticlesGrid
|
|
168
|
+
{articles}
|
|
169
|
+
{translations}
|
|
170
|
+
{imageResolver}
|
|
171
|
+
/>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Turkish Locale
|
|
175
|
+
|
|
176
|
+
```svelte
|
|
177
|
+
<script>
|
|
178
|
+
const translationsTr = {
|
|
179
|
+
articlesLabel: 'MAKALELER',
|
|
180
|
+
title: 'Son Makaleler',
|
|
181
|
+
description: 'En güncel hukuki içeriklerimizi okuyun',
|
|
182
|
+
readMore: 'Devamını Oku',
|
|
183
|
+
viewAll: 'Tüm Makaleleri Görüntüle'
|
|
184
|
+
};
|
|
185
|
+
</script>
|
|
186
|
+
|
|
187
|
+
<ArticlesGrid
|
|
188
|
+
{articles}
|
|
189
|
+
translations={translationsTr}
|
|
190
|
+
locale="tr"
|
|
191
|
+
/>
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Styling
|
|
195
|
+
|
|
196
|
+
Component uses Tailwind-like utility classes. Includes scoped CSS with fallback primary color variables:
|
|
197
|
+
|
|
198
|
+
```css
|
|
199
|
+
--primary-50: #fef5e7;
|
|
200
|
+
--primary-100: #fce8c4;
|
|
201
|
+
--primary-300: #f5c76e;
|
|
202
|
+
--primary-600: #d6bb6d;
|
|
203
|
+
--primary-700: #c9a858;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Override by defining these CSS variables in your parent component or global styles.
|
|
207
|
+
|
|
208
|
+
## Layout
|
|
209
|
+
|
|
210
|
+
- **Grid**: 1 column (mobile), 3 columns (md+)
|
|
211
|
+
- **Card Aspect Ratio**: 16:10 for images
|
|
212
|
+
- **Max Width**: 1280px (7xl container)
|
|
213
|
+
- **Spacing**: py-20 section, gap-8 grid
|
|
214
|
+
|
|
215
|
+
## Accessibility
|
|
216
|
+
|
|
217
|
+
- Semantic `<article>` elements
|
|
218
|
+
- Proper heading hierarchy
|
|
219
|
+
- Alt text on images
|
|
220
|
+
- SVG icons with proper viewBox
|
|
221
|
+
- Keyboard-friendly links
|
|
222
|
+
|
|
223
|
+
## Browser Support
|
|
224
|
+
|
|
225
|
+
Works in all modern browsers with CSS Grid and Flexbox support.
|
|
226
|
+
|
|
227
|
+
## Version
|
|
228
|
+
|
|
229
|
+
Component extracted from av-talha-web (January 2025).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ArticlesGrid } from './ArticlesGrid.svelte';
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* FeaturedGalleryGrid Component - Framework-agnostic gallery grid
|
|
4
|
+
* Extracted from turk-cin-dernek-website
|
|
5
|
+
* Layout: Large featured image on left, 3 smaller images stacked on right
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface GalleryItem {
|
|
9
|
+
image: string;
|
|
10
|
+
alt: string;
|
|
11
|
+
text?: string;
|
|
12
|
+
href?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface Props {
|
|
16
|
+
featuredItem: GalleryItem;
|
|
17
|
+
smallItems: GalleryItem[];
|
|
18
|
+
onItemClick?: (href: string) => void;
|
|
19
|
+
imageResolver?: (path: string) => string;
|
|
20
|
+
placeholderImage?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let {
|
|
24
|
+
featuredItem,
|
|
25
|
+
smallItems = [],
|
|
26
|
+
onItemClick,
|
|
27
|
+
imageResolver = (path: string) => path,
|
|
28
|
+
placeholderImage = 'https://placehold.co/800x500'
|
|
29
|
+
}: Props = $props();
|
|
30
|
+
|
|
31
|
+
const handleItemClick = (e: MouseEvent, href?: string) => {
|
|
32
|
+
if (onItemClick && href) {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
onItemClick(href);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const resolveImage = (image?: string): string => {
|
|
39
|
+
if (!image || !image.trim()) {
|
|
40
|
+
return placeholderImage;
|
|
41
|
+
}
|
|
42
|
+
return imageResolver(image);
|
|
43
|
+
};
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<div class="container mx-auto">
|
|
47
|
+
<div class="mb-12 grid grid-cols-1 gap-y-4 md:grid-cols-3 md:gap-4">
|
|
48
|
+
<!-- Sol Büyük Resim (Featured) -->
|
|
49
|
+
<a
|
|
50
|
+
class="group relative col-span-2 block cursor-pointer overflow-hidden rounded-lg shadow-md max-h-[600px]"
|
|
51
|
+
href={featuredItem.href ?? '/'}
|
|
52
|
+
onclick={(e) => handleItemClick(e, featuredItem.href)}
|
|
53
|
+
>
|
|
54
|
+
<img
|
|
55
|
+
src={resolveImage(featuredItem.image)}
|
|
56
|
+
alt={featuredItem.alt}
|
|
57
|
+
class="h-auto w-full object-cover transition-transform duration-300 group-hover:scale-[1.01]"
|
|
58
|
+
/>
|
|
59
|
+
<div
|
|
60
|
+
class="absolute inset-0 bg-gradient-to-t from-black/50 via-black/20 to-transparent"
|
|
61
|
+
></div>
|
|
62
|
+
{#if featuredItem.text}
|
|
63
|
+
<div class="pointer-events-none absolute inset-x-0 bottom-0 p-4 text-white md:p-6">
|
|
64
|
+
<h3 class="text-lg font-semibold drop-shadow md:text-2xl">
|
|
65
|
+
{featuredItem.text}
|
|
66
|
+
</h3>
|
|
67
|
+
</div>
|
|
68
|
+
{/if}
|
|
69
|
+
</a>
|
|
70
|
+
|
|
71
|
+
<!-- Sağdaki 3 Küçük Resim -->
|
|
72
|
+
<div class="flex flex-col justify-between gap-4 max-h-[600px]">
|
|
73
|
+
{#each smallItems.slice(0, 3) as item}
|
|
74
|
+
<a
|
|
75
|
+
class="group relative block max-h-[200px] cursor-pointer overflow-hidden rounded-lg shadow-md"
|
|
76
|
+
href={item.href ?? '/'}
|
|
77
|
+
onclick={(e) => handleItemClick(e, item.href)}
|
|
78
|
+
>
|
|
79
|
+
<img
|
|
80
|
+
src={resolveImage(item.image)}
|
|
81
|
+
alt={item.alt}
|
|
82
|
+
class="h-auto w-full object-cover transition-transform duration-300 group-hover:scale-105"
|
|
83
|
+
/>
|
|
84
|
+
<div
|
|
85
|
+
class="absolute inset-0 bg-gradient-to-t from-black/50 via-black/20 to-transparent"
|
|
86
|
+
></div>
|
|
87
|
+
{#if item.text}
|
|
88
|
+
<div class="pointer-events-none absolute inset-x-0 bottom-0 p-3 text-white md:p-4">
|
|
89
|
+
<h4 class="text-base font-semibold drop-shadow md:text-lg">{item.text}</h4>
|
|
90
|
+
</div>
|
|
91
|
+
{/if}
|
|
92
|
+
</a>
|
|
93
|
+
{/each}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
|
|
98
|
+
<style>
|
|
99
|
+
/* Container max-width */
|
|
100
|
+
.container {
|
|
101
|
+
max-width: 1280px;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Ensure proper aspect ratio and object-fit */
|
|
105
|
+
img {
|
|
106
|
+
display: block;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* Drop shadow utility for text */
|
|
110
|
+
.drop-shadow {
|
|
111
|
+
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Pointer events none utility */
|
|
115
|
+
.pointer-events-none {
|
|
116
|
+
pointer-events: none;
|
|
117
|
+
}
|
|
118
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FeaturedGalleryGrid Component - Framework-agnostic gallery grid
|
|
3
|
+
* Extracted from turk-cin-dernek-website
|
|
4
|
+
* Layout: Large featured image on left, 3 smaller images stacked on right
|
|
5
|
+
*/
|
|
6
|
+
export interface GalleryItem {
|
|
7
|
+
image: string;
|
|
8
|
+
alt: string;
|
|
9
|
+
text?: string;
|
|
10
|
+
href?: string;
|
|
11
|
+
}
|
|
12
|
+
interface Props {
|
|
13
|
+
featuredItem: GalleryItem;
|
|
14
|
+
smallItems: GalleryItem[];
|
|
15
|
+
onItemClick?: (href: string) => void;
|
|
16
|
+
imageResolver?: (path: string) => string;
|
|
17
|
+
placeholderImage?: string;
|
|
18
|
+
}
|
|
19
|
+
declare const FeaturedGalleryGrid: import("svelte").Component<Props, {}, "">;
|
|
20
|
+
type FeaturedGalleryGrid = ReturnType<typeof FeaturedGalleryGrid>;
|
|
21
|
+
export default FeaturedGalleryGrid;
|