@autumnsgrove/groveengine 0.8.5 → 0.9.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/dist/components/WispPanel.svelte +0 -1
- package/dist/components/admin/GutterManager.svelte +213 -101
- package/dist/components/admin/MarkdownEditor.svelte +6 -3
- package/dist/components/custom/ContentWithGutter.svelte +7 -13
- package/dist/components/custom/GutterItem.svelte +8 -2
- package/dist/components/quota/UpgradePrompt.svelte +1 -0
- package/dist/config/domain-blocklist.d.ts +59 -0
- package/dist/config/domain-blocklist.js +731 -0
- package/dist/config/index.d.ts +3 -1
- package/dist/config/index.js +2 -1
- package/dist/config/offensive-blocklist.d.ts +44 -0
- package/dist/config/offensive-blocklist.js +751 -0
- package/dist/config/terrarium.d.ts +109 -0
- package/dist/config/terrarium.js +125 -0
- package/dist/styles/tokens.css +90 -0
- package/dist/types/dom-to-image-more.d.ts +39 -0
- package/dist/ui/components/chrome/Footer.svelte +137 -0
- package/dist/ui/components/chrome/Footer.svelte.d.ts +11 -0
- package/dist/ui/components/chrome/FooterMinimal.svelte +75 -0
- package/dist/ui/components/chrome/FooterMinimal.svelte.d.ts +10 -0
- package/dist/ui/components/chrome/Header.svelte +113 -0
- package/dist/ui/components/chrome/Header.svelte.d.ts +11 -0
- package/dist/ui/components/chrome/HeaderMinimal.svelte +68 -0
- package/dist/ui/components/chrome/HeaderMinimal.svelte.d.ts +9 -0
- package/dist/ui/components/chrome/MobileMenu.svelte +145 -0
- package/dist/ui/components/chrome/MobileMenu.svelte.d.ts +9 -0
- package/dist/ui/components/chrome/ThemeToggle.svelte +34 -0
- package/dist/ui/components/chrome/ThemeToggle.svelte.d.ts +3 -0
- package/dist/ui/components/chrome/defaults.d.ts +6 -0
- package/dist/ui/components/chrome/defaults.js +65 -0
- package/dist/ui/components/chrome/index.d.ts +13 -0
- package/dist/ui/components/chrome/index.js +14 -0
- package/dist/ui/components/chrome/types.d.ts +19 -0
- package/dist/ui/components/chrome/types.js +8 -0
- package/dist/ui/components/content/RoadmapPreview.svelte +2 -1
- package/dist/ui/components/forms/ContentSearch.svelte +406 -0
- package/dist/ui/components/forms/ContentSearch.svelte.d.ts +71 -0
- package/dist/ui/components/forms/SearchInput.svelte +0 -1
- package/dist/ui/components/forms/filterUtils.d.ts +138 -0
- package/dist/ui/components/forms/filterUtils.js +240 -0
- package/dist/ui/components/forms/index.d.ts +2 -0
- package/dist/ui/components/forms/index.js +5 -1
- package/dist/ui/components/gallery/ImageGallery.svelte +17 -3
- package/dist/ui/components/gallery/Lightbox.svelte +11 -3
- package/dist/ui/components/gallery/ZoomableImage.svelte +13 -2
- package/dist/ui/components/icons/index.d.ts +2 -1
- package/dist/ui/components/icons/index.js +14 -3
- package/dist/ui/components/icons/lucide.d.ts +213 -0
- package/dist/ui/components/icons/lucide.js +224 -0
- package/dist/ui/components/terrarium/AssetPalette.svelte +207 -0
- package/dist/ui/components/terrarium/AssetPalette.svelte.d.ts +7 -0
- package/dist/ui/components/terrarium/Canvas.svelte +231 -0
- package/dist/ui/components/terrarium/Canvas.svelte.d.ts +14 -0
- package/dist/ui/components/terrarium/ExportDialog.svelte +307 -0
- package/dist/ui/components/terrarium/ExportDialog.svelte.d.ts +18 -0
- package/dist/ui/components/terrarium/PaletteItem.svelte +169 -0
- package/dist/ui/components/terrarium/PaletteItem.svelte.d.ts +9 -0
- package/dist/ui/components/terrarium/PlacedAsset.svelte +222 -0
- package/dist/ui/components/terrarium/PlacedAsset.svelte.d.ts +11 -0
- package/dist/ui/components/terrarium/Terrarium.svelte +266 -0
- package/dist/ui/components/terrarium/Terrarium.svelte.d.ts +3 -0
- package/dist/ui/components/terrarium/Toolbar.svelte +299 -0
- package/dist/ui/components/terrarium/Toolbar.svelte.d.ts +24 -0
- package/dist/ui/components/terrarium/index.d.ts +31 -0
- package/dist/ui/components/terrarium/index.js +33 -0
- package/dist/ui/components/terrarium/terrariumState.svelte.d.ts +45 -0
- package/dist/ui/components/terrarium/terrariumState.svelte.js +291 -0
- package/dist/ui/components/terrarium/types.d.ts +139 -0
- package/dist/ui/components/terrarium/types.js +43 -0
- package/dist/ui/components/terrarium/utils/export.d.ts +48 -0
- package/dist/ui/components/terrarium/utils/export.js +148 -0
- package/dist/ui/components/typography/index.d.ts +0 -10
- package/dist/ui/components/typography/index.js +1 -12
- package/dist/ui/components/ui/CollapsibleSection.svelte +12 -0
- package/dist/ui/components/ui/GlassConfirmDialog.svelte +9 -0
- package/dist/ui/components/ui/GlassOverlay.svelte +2 -1
- package/dist/ui/components/ui/Input.svelte +9 -1
- package/dist/ui/components/ui/Input.svelte.d.ts +2 -0
- package/dist/ui/components/ui/Textarea.svelte +9 -1
- package/dist/ui/components/ui/Textarea.svelte.d.ts +2 -0
- package/dist/ui/stores/index.d.ts +6 -0
- package/dist/ui/stores/index.js +6 -0
- package/dist/ui/stores/season.d.ts +14 -0
- package/dist/ui/stores/season.js +65 -0
- package/dist/ui/tokens/fonts.d.ts +1 -1
- package/dist/ui/tokens/fonts.js +0 -126
- package/package.json +46 -22
- package/static/fonts/alagard.ttf +0 -0
- package/LICENSE +0 -378
- package/dist/ui/components/typography/BodoniModa.svelte +0 -17
- package/dist/ui/components/typography/BodoniModa.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Cormorant.svelte +0 -17
- package/dist/ui/components/typography/Cormorant.svelte.d.ts +0 -10
- package/dist/ui/components/typography/EBGaramond.svelte +0 -17
- package/dist/ui/components/typography/EBGaramond.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Fraunces.svelte +0 -17
- package/dist/ui/components/typography/Fraunces.svelte.d.ts +0 -10
- package/dist/ui/components/typography/InstrumentSans.svelte +0 -17
- package/dist/ui/components/typography/InstrumentSans.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Lora.svelte +0 -17
- package/dist/ui/components/typography/Lora.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Luciole.svelte +0 -17
- package/dist/ui/components/typography/Luciole.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Manrope.svelte +0 -17
- package/dist/ui/components/typography/Manrope.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Merriweather.svelte +0 -17
- package/dist/ui/components/typography/Merriweather.svelte.d.ts +0 -10
- package/dist/ui/components/typography/Nunito.svelte +0 -17
- package/dist/ui/components/typography/Nunito.svelte.d.ts +0 -10
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common filter utilities for use with ContentSearch component
|
|
3
|
+
* Part of the Grove UI design system
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```typescript
|
|
7
|
+
* import { createTextFilter, createMultiFieldFilter } from '@autumnsgrove/groveengine';
|
|
8
|
+
*
|
|
9
|
+
* const filterPost = createTextFilter(['title', 'description']);
|
|
10
|
+
* <ContentSearch items={posts} filterFn={filterPost} />
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Normalize text for searching (lowercase, trim)
|
|
15
|
+
*/
|
|
16
|
+
export function normalizeSearchText(text) {
|
|
17
|
+
return text.toLowerCase().trim();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if normalized text includes a normalized query
|
|
21
|
+
* Note: Both inputs should already be normalized via normalizeSearchText()
|
|
22
|
+
*
|
|
23
|
+
* @param normalizedText - Already normalized text to search in
|
|
24
|
+
* @param normalizedQuery - Already normalized query to search for
|
|
25
|
+
*/
|
|
26
|
+
export function includesNormalized(normalizedText, normalizedQuery) {
|
|
27
|
+
return normalizedText.includes(normalizedQuery);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if text includes a search query (case-insensitive)
|
|
31
|
+
* This is a convenience function that normalizes both inputs
|
|
32
|
+
*
|
|
33
|
+
* @param text - Text to search in (will be normalized)
|
|
34
|
+
* @param query - Query to search for (will be normalized)
|
|
35
|
+
*/
|
|
36
|
+
export function textIncludes(text, query) {
|
|
37
|
+
return normalizeSearchText(text).includes(normalizeSearchText(query));
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a filter function that searches multiple text fields
|
|
41
|
+
*
|
|
42
|
+
* @param fields - Array of field names to search
|
|
43
|
+
* @returns Filter function for use with ContentSearch
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const filterPost = createTextFilter(['title', 'description', 'author']);
|
|
48
|
+
* <ContentSearch items={posts} filterFn={filterPost} />
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function createTextFilter(fields) {
|
|
52
|
+
return (item, query) => {
|
|
53
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
54
|
+
return fields.some(field => {
|
|
55
|
+
const value = item[field];
|
|
56
|
+
if (typeof value === 'string') {
|
|
57
|
+
// Use includesNormalized to avoid double normalization
|
|
58
|
+
return includesNormalized(normalizeSearchText(value), normalizedQuery);
|
|
59
|
+
}
|
|
60
|
+
return false;
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a filter function that searches text fields and array fields (like tags)
|
|
66
|
+
*
|
|
67
|
+
* @param textFields - Array of text field names to search
|
|
68
|
+
* @param arrayFields - Array of array field names to search (e.g., tags)
|
|
69
|
+
* @returns Filter function for use with ContentSearch
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const filterPost = createMultiFieldFilter(['title', 'description'], ['tags', 'categories']);
|
|
74
|
+
* <ContentSearch items={posts} filterFn={filterPost} />
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function createMultiFieldFilter(textFields, arrayFields = []) {
|
|
78
|
+
return (item, query) => {
|
|
79
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
80
|
+
// Check text fields
|
|
81
|
+
const matchesText = textFields.some(field => {
|
|
82
|
+
const value = item[field];
|
|
83
|
+
if (typeof value === 'string') {
|
|
84
|
+
// Use includesNormalized to avoid double normalization
|
|
85
|
+
return includesNormalized(normalizeSearchText(value), normalizedQuery);
|
|
86
|
+
}
|
|
87
|
+
return false;
|
|
88
|
+
});
|
|
89
|
+
if (matchesText)
|
|
90
|
+
return true;
|
|
91
|
+
// Check array fields
|
|
92
|
+
const matchesArray = arrayFields.some(field => {
|
|
93
|
+
const value = item[field];
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
return value.some(element => {
|
|
96
|
+
if (typeof element === 'string') {
|
|
97
|
+
// Use includesNormalized to avoid double normalization
|
|
98
|
+
return includesNormalized(normalizeSearchText(element), normalizedQuery);
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
});
|
|
105
|
+
return matchesArray;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Pre-compute lowercase versions of searchable fields for performance
|
|
110
|
+
*
|
|
111
|
+
* **When to use this optimization:**
|
|
112
|
+
* - **Large datasets (100+ items)**: Repeated toLowerCase() calls during filtering become expensive
|
|
113
|
+
* - **Frequent searches**: Users actively searching/filtering the same dataset multiple times
|
|
114
|
+
* - **Multiple searchable fields**: More fields = more normalization = more performance gain
|
|
115
|
+
* - **Real-time filtering**: When debouncing isn't enough and you need instant results
|
|
116
|
+
*
|
|
117
|
+
* **When NOT to use:**
|
|
118
|
+
* - Small datasets (<50 items): Minimal performance benefit, adds memory overhead
|
|
119
|
+
* - Static lists that don't change: Consider precomputing at build time instead
|
|
120
|
+
* - Infrequent searches: The setup cost may outweigh the benefit
|
|
121
|
+
*
|
|
122
|
+
* @param items - Array of items to optimize
|
|
123
|
+
* @param fields - Fields to pre-compute lowercase versions of
|
|
124
|
+
* @returns Array of items with lowercase fields added (suffixed with 'Lower')
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* // In your Svelte component with large dataset (100+ posts)
|
|
129
|
+
* let postsWithLowercase = $derived.by(() => {
|
|
130
|
+
* return precomputeLowercaseFields(posts, ['title', 'description', 'tags']);
|
|
131
|
+
* });
|
|
132
|
+
* // Creates: { ...post, titleLower: '...', descriptionLower: '...', tagsLower: [...] }
|
|
133
|
+
*
|
|
134
|
+
* function filterPost(post, query) {
|
|
135
|
+
* const q = query.toLowerCase();
|
|
136
|
+
* return post.titleLower.includes(q) ||
|
|
137
|
+
* post.descriptionLower.includes(q) ||
|
|
138
|
+
* post.tagsLower.some(tag => tag.includes(q));
|
|
139
|
+
* }
|
|
140
|
+
*
|
|
141
|
+
* <ContentSearch items={postsWithLowercase} filterFn={filterPost} />
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export function precomputeLowercaseFields(items, fields) {
|
|
145
|
+
return items.map(item => {
|
|
146
|
+
const computed = { ...item };
|
|
147
|
+
fields.forEach(field => {
|
|
148
|
+
const value = item[field];
|
|
149
|
+
if (typeof value === 'string') {
|
|
150
|
+
computed[`${String(field)}Lower`] = value.toLowerCase();
|
|
151
|
+
}
|
|
152
|
+
else if (Array.isArray(value)) {
|
|
153
|
+
computed[`${String(field)}Lower`] = value.map(v => typeof v === 'string' ? v.toLowerCase() : v);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return computed;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Create a fuzzy filter that matches partial words
|
|
161
|
+
*
|
|
162
|
+
* @param fields - Array of field names to search
|
|
163
|
+
* @param minMatchLength - Minimum length of query before fuzzy matching (default: 2)
|
|
164
|
+
* @returns Filter function for use with ContentSearch
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* const filterPost = createFuzzyFilter(['title', 'description']);
|
|
169
|
+
* // Matches: "jav" in "JavaScript", "scr" in "TypeScript"
|
|
170
|
+
* ```
|
|
171
|
+
*/
|
|
172
|
+
export function createFuzzyFilter(fields, minMatchLength = 2) {
|
|
173
|
+
return (item, query) => {
|
|
174
|
+
if (query.length < minMatchLength)
|
|
175
|
+
return true;
|
|
176
|
+
const normalizedQuery = normalizeSearchText(query);
|
|
177
|
+
return fields.some(field => {
|
|
178
|
+
const value = item[field];
|
|
179
|
+
if (typeof value === 'string') {
|
|
180
|
+
const normalizedValue = normalizeSearchText(value);
|
|
181
|
+
// Split into words and check if any word starts with the query
|
|
182
|
+
const words = normalizedValue.split(/\s+/);
|
|
183
|
+
return words.some(word => word.startsWith(normalizedQuery));
|
|
184
|
+
}
|
|
185
|
+
return false;
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Combine multiple filter functions with AND logic
|
|
191
|
+
*
|
|
192
|
+
* @param filters - Array of filter functions to combine
|
|
193
|
+
* @returns Combined filter function
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```typescript
|
|
197
|
+
* const hasTag = (item, tag) => item.tags.includes(tag);
|
|
198
|
+
* const matchesText = createTextFilter(['title']);
|
|
199
|
+
* const combinedFilter = combineFilters([matchesText, (item) => hasTag(item, 'featured')]);
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export function combineFilters(filters) {
|
|
203
|
+
return (item, query) => {
|
|
204
|
+
return filters.every(filter => filter(item, query));
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create a date range filter
|
|
209
|
+
*
|
|
210
|
+
* @param dateField - Field name containing the date
|
|
211
|
+
* @param startDate - Start of date range (optional)
|
|
212
|
+
* @param endDate - End of date range (optional)
|
|
213
|
+
* @returns Filter function for use with ContentSearch
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* ```typescript
|
|
217
|
+
* const recentPostsFilter = createDateFilter('publishedAt', new Date('2024-01-01'));
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
export function createDateFilter(dateField, startDate, endDate) {
|
|
221
|
+
// Date filters don't use the query string, but must match the filterFn signature
|
|
222
|
+
return (item, _query) => {
|
|
223
|
+
const dateValue = item[dateField];
|
|
224
|
+
if (!dateValue)
|
|
225
|
+
return false;
|
|
226
|
+
// Validate dateValue is a valid date type before constructing Date
|
|
227
|
+
if (typeof dateValue !== 'string' && typeof dateValue !== 'number' && !(dateValue instanceof Date)) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
const itemDate = new Date(dateValue);
|
|
231
|
+
// Check if the date is valid (invalid dates return NaN for getTime())
|
|
232
|
+
if (isNaN(itemDate.getTime()))
|
|
233
|
+
return false;
|
|
234
|
+
if (startDate && itemDate < startDate)
|
|
235
|
+
return false;
|
|
236
|
+
if (endDate && itemDate > endDate)
|
|
237
|
+
return false;
|
|
238
|
+
return true;
|
|
239
|
+
};
|
|
240
|
+
}
|
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { default as SearchInput } from './SearchInput.svelte';
|
|
2
|
+
export { default as ContentSearch } from './ContentSearch.svelte';
|
|
3
|
+
export { normalizeSearchText, includesNormalized, textIncludes, createTextFilter, createMultiFieldFilter, precomputeLowercaseFields, createFuzzyFilter, combineFilters, createDateFilter } from './filterUtils.js';
|
|
2
4
|
export declare const FORMS_VERSION = "0.2.0";
|
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
// This module exports specialized form input components
|
|
4
4
|
//
|
|
5
5
|
// Usage:
|
|
6
|
-
// import { SearchInput } from '@groveengine/ui/forms';
|
|
6
|
+
// import { SearchInput, ContentSearch } from '@groveengine/ui/forms';
|
|
7
|
+
// import { createTextFilter, createMultiFieldFilter } from '@groveengine/ui/forms';
|
|
7
8
|
export { default as SearchInput } from './SearchInput.svelte';
|
|
9
|
+
export { default as ContentSearch } from './ContentSearch.svelte';
|
|
10
|
+
// Filter utilities for ContentSearch
|
|
11
|
+
export { normalizeSearchText, includesNormalized, textIncludes, createTextFilter, createMultiFieldFilter, precomputeLowercaseFields, createFuzzyFilter, combineFilters, createDateFilter } from './filterUtils.js';
|
|
8
12
|
export const FORMS_VERSION = '0.2.0';
|
|
@@ -263,10 +263,15 @@
|
|
|
263
263
|
|
|
264
264
|
<!-- Lightbox modal -->
|
|
265
265
|
{#if lightboxOpen}
|
|
266
|
-
<!-- svelte-ignore
|
|
266
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
267
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
267
268
|
<div
|
|
268
269
|
class="lightbox-backdrop"
|
|
269
270
|
onclick={(/** @type {MouseEvent} */ e) => e.target === e.currentTarget && closeLightbox()}
|
|
271
|
+
onkeydown={(/** @type {KeyboardEvent} */ e) => {
|
|
272
|
+
if (e.key === 'Escape') closeLightbox();
|
|
273
|
+
if (e.key === 'Enter' || e.key === ' ') closeLightbox();
|
|
274
|
+
}}
|
|
270
275
|
role="dialog"
|
|
271
276
|
aria-modal="true"
|
|
272
277
|
aria-label="Image viewer"
|
|
@@ -279,8 +284,17 @@
|
|
|
279
284
|
</svg>
|
|
280
285
|
</button>
|
|
281
286
|
|
|
282
|
-
<!-- svelte-ignore
|
|
283
|
-
|
|
287
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
288
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
289
|
+
<div
|
|
290
|
+
class="lightbox-content"
|
|
291
|
+
onclick={(/** @type {MouseEvent} */ e) => e.target === e.currentTarget && closeLightbox()}
|
|
292
|
+
onkeydown={(/** @type {KeyboardEvent} */ e) => {
|
|
293
|
+
if (e.key === 'Escape') closeLightbox();
|
|
294
|
+
if (e.key === 'Enter' || e.key === ' ') closeLightbox();
|
|
295
|
+
}}
|
|
296
|
+
role="presentation"
|
|
297
|
+
>
|
|
284
298
|
<ZoomableImage
|
|
285
299
|
src={currentImage.url}
|
|
286
300
|
alt={currentImage.alt || `Image ${currentIndex + 1}`}
|
|
@@ -24,10 +24,12 @@
|
|
|
24
24
|
<svelte:window onkeydown={handleKeydown} />
|
|
25
25
|
|
|
26
26
|
{#if isOpen}
|
|
27
|
-
<!-- svelte-ignore
|
|
27
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
28
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
28
29
|
<div
|
|
29
30
|
class="lightbox-backdrop"
|
|
30
31
|
onclick={handleBackdropClick}
|
|
32
|
+
onkeydown={handleKeydown}
|
|
31
33
|
role="dialog"
|
|
32
34
|
aria-modal="true"
|
|
33
35
|
aria-label="Image viewer"
|
|
@@ -39,8 +41,14 @@
|
|
|
39
41
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
40
42
|
</svg>
|
|
41
43
|
</button>
|
|
42
|
-
<!-- svelte-ignore
|
|
43
|
-
|
|
44
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
45
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
46
|
+
<div
|
|
47
|
+
class="lightbox-content"
|
|
48
|
+
onclick={handleBackdropClick}
|
|
49
|
+
onkeydown={handleKeydown}
|
|
50
|
+
role="presentation"
|
|
51
|
+
>
|
|
44
52
|
<ZoomableImage {src} {alt} isActive={isOpen} class="lightbox-image" />
|
|
45
53
|
</div>
|
|
46
54
|
<LightboxCaption {caption} />
|
|
@@ -124,11 +124,20 @@
|
|
|
124
124
|
}
|
|
125
125
|
cycleZoom();
|
|
126
126
|
}
|
|
127
|
+
|
|
128
|
+
// Keyboard handler for accessibility
|
|
129
|
+
function handleKeydown(/** @type {KeyboardEvent} */ event) {
|
|
130
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
131
|
+
event.preventDefault();
|
|
132
|
+
cycleZoom();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
127
135
|
</script>
|
|
128
136
|
|
|
129
137
|
<svelte:window onmousemove={handleMouseMove} onmouseup={handleMouseUp} />
|
|
130
138
|
|
|
131
|
-
<!-- svelte-ignore
|
|
139
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
140
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_to_interactive_role -->
|
|
132
141
|
<img
|
|
133
142
|
{src}
|
|
134
143
|
{alt}
|
|
@@ -141,8 +150,10 @@
|
|
|
141
150
|
ontouchmove={handleTouchMove}
|
|
142
151
|
ontouchend={handleTouchEnd}
|
|
143
152
|
onclick={handleClick}
|
|
144
|
-
|
|
153
|
+
onkeydown={handleKeydown}
|
|
145
154
|
tabindex="0"
|
|
155
|
+
role="button"
|
|
156
|
+
aria-label="Click to zoom image"
|
|
146
157
|
/>
|
|
147
158
|
|
|
148
159
|
<style>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
export { default as Icons } from './Icons.svelte';
|
|
2
2
|
export { default as IconLegend } from './IconLegend.svelte';
|
|
3
|
-
export
|
|
3
|
+
export { navIcons, stateIcons, pricingIcons, featureIcons, growthIcons, phaseIcons, actionIcons, metricsIcons, allIcons, type IconKey, getIcon, getIconFromAll, Check, CheckCircle, X, ArrowRight, ArrowLeft, MapPin, Sprout, Trees, TreeDeciduous, Crown, Flower2, Leaf, Heart, Home, Menu, Settings, ExternalLink, ChevronDown, LogIn, Github, Mail, HardDrive, Palette, Shield, Download, Rss, Eye, MessageCircle, Loader2, AlertTriangle, HelpCircle, Lock, Sparkles, Clock, TrendingUp, Users, Activity, } from './lucide';
|
|
4
|
+
export declare const ICONS_VERSION = "0.3.0";
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
// GroveUI - Icon Components
|
|
2
2
|
//
|
|
3
|
-
// This module exports SVG icon components
|
|
3
|
+
// This module exports SVG icon components and Lucide icon registries
|
|
4
|
+
// used across the Grove platform.
|
|
4
5
|
//
|
|
5
6
|
// Usage:
|
|
6
|
-
// import { Icons, IconLegend } from '@groveengine/ui/icons';
|
|
7
|
+
// import { Icons, IconLegend } from '@autumnsgrove/groveengine/ui/icons';
|
|
8
|
+
// import { stateIcons, pricingIcons, Check } from '@autumnsgrove/groveengine/ui/icons';
|
|
9
|
+
// Custom SVG components
|
|
7
10
|
export { default as Icons } from './Icons.svelte';
|
|
8
11
|
export { default as IconLegend } from './IconLegend.svelte';
|
|
9
|
-
|
|
12
|
+
// Lucide icon registries and utilities
|
|
13
|
+
export {
|
|
14
|
+
// Icon maps (semantic groupings)
|
|
15
|
+
navIcons, stateIcons, pricingIcons, featureIcons, growthIcons, phaseIcons, actionIcons, metricsIcons, allIcons,
|
|
16
|
+
// Utilities
|
|
17
|
+
getIcon, getIconFromAll,
|
|
18
|
+
// Direct icon exports (commonly used)
|
|
19
|
+
Check, CheckCircle, X, ArrowRight, ArrowLeft, MapPin, Sprout, Trees, TreeDeciduous, Crown, Flower2, Leaf, Heart, Home, Menu, Settings, ExternalLink, ChevronDown, LogIn, Github, Mail, HardDrive, Palette, Shield, Download, Rss, Eye, MessageCircle, Loader2, AlertTriangle, HelpCircle, Lock, Sparkles, Clock, TrendingUp, Users, Activity, } from './lucide';
|
|
20
|
+
export const ICONS_VERSION = '0.3.0';
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Lucide icon registry for Grove Platform.
|
|
3
|
+
* Single source of truth for commonly used icons across all Grove apps.
|
|
4
|
+
*
|
|
5
|
+
* DO: Import icons from '@autumnsgrove/groveengine/ui/icons'
|
|
6
|
+
* DON'T: Import directly from 'lucide-svelte' in app components
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```svelte
|
|
10
|
+
* import { stateIcons, navIcons } from '@autumnsgrove/groveengine/ui/icons';
|
|
11
|
+
*
|
|
12
|
+
* <svelte:component this={stateIcons.check} class="w-5 h-5" />
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
import { Home, Info, Telescope, MapPin, CircleDollarSign, BookOpen, Trees, PenLine, ArrowRight, ArrowLeft, ChevronRight, ChevronLeft, ChevronDown, ExternalLink, LogIn, Mail, HardDrive, Palette, ShieldCheck, Shield, Cloud, SearchCode, Archive, Upload, MessagesSquare, MessageCircle, FileText, Tag, Rss, Eye, Github, Layers, Sprout, Heart, Leaf, Flower2, TreeDeciduous, Crown, Check, CheckCircle, X, Loader2, AlertTriangle, HelpCircle, Circle, Lock, Gem, Sparkles, Star, Moon, Sun, Compass, Megaphone, Lightbulb, Download, Settings, Menu, Clock, TrendingUp, TrendingDown, Activity, Users, ShieldUser, BarChart3, Globe, CalendarDays, LifeBuoy } from 'lucide-svelte';
|
|
16
|
+
/** Icons for main navigation items */
|
|
17
|
+
export declare const navIcons: {
|
|
18
|
+
readonly home: typeof Home;
|
|
19
|
+
readonly about: typeof Info;
|
|
20
|
+
readonly vision: typeof Telescope;
|
|
21
|
+
readonly roadmap: typeof MapPin;
|
|
22
|
+
readonly pricing: typeof CircleDollarSign;
|
|
23
|
+
readonly knowledge: typeof BookOpen;
|
|
24
|
+
readonly forest: typeof Trees;
|
|
25
|
+
readonly blog: typeof PenLine;
|
|
26
|
+
readonly arrow: typeof ArrowRight;
|
|
27
|
+
readonly arrowLeft: typeof ArrowLeft;
|
|
28
|
+
readonly chevron: typeof ChevronRight;
|
|
29
|
+
readonly chevronLeft: typeof ChevronLeft;
|
|
30
|
+
readonly chevronDown: typeof ChevronDown;
|
|
31
|
+
readonly external: typeof ExternalLink;
|
|
32
|
+
readonly login: typeof LogIn;
|
|
33
|
+
readonly github: typeof Github;
|
|
34
|
+
};
|
|
35
|
+
/** Icons for states: success, error, loading, etc. */
|
|
36
|
+
export declare const stateIcons: {
|
|
37
|
+
readonly check: typeof Check;
|
|
38
|
+
readonly checkcircle: typeof CheckCircle;
|
|
39
|
+
readonly x: typeof X;
|
|
40
|
+
readonly loader: typeof Loader2;
|
|
41
|
+
readonly warning: typeof AlertTriangle;
|
|
42
|
+
readonly help: typeof HelpCircle;
|
|
43
|
+
readonly info: typeof Info;
|
|
44
|
+
readonly circle: typeof Circle;
|
|
45
|
+
readonly lock: typeof Lock;
|
|
46
|
+
};
|
|
47
|
+
/** Icons for pricing tiers and feature comparison */
|
|
48
|
+
export declare const pricingIcons: {
|
|
49
|
+
readonly sprout: typeof Sprout;
|
|
50
|
+
readonly treedeciduous: typeof TreeDeciduous;
|
|
51
|
+
readonly trees: typeof Trees;
|
|
52
|
+
readonly crown: typeof Crown;
|
|
53
|
+
readonly penline: typeof PenLine;
|
|
54
|
+
readonly filetext: typeof FileText;
|
|
55
|
+
readonly harddrive: typeof HardDrive;
|
|
56
|
+
readonly palette: typeof Palette;
|
|
57
|
+
readonly flower2: typeof Flower2;
|
|
58
|
+
readonly messagecircle: typeof MessageCircle;
|
|
59
|
+
readonly globe: typeof Globe;
|
|
60
|
+
readonly searchcode: typeof SearchCode;
|
|
61
|
+
readonly mail: typeof Mail;
|
|
62
|
+
readonly lifebuoy: typeof LifeBuoy;
|
|
63
|
+
readonly calendardays: typeof CalendarDays;
|
|
64
|
+
readonly clock: typeof Clock;
|
|
65
|
+
readonly check: typeof Check;
|
|
66
|
+
};
|
|
67
|
+
/** Icons for features, tools, and content types */
|
|
68
|
+
export declare const featureIcons: {
|
|
69
|
+
readonly mail: typeof Mail;
|
|
70
|
+
readonly harddrive: typeof HardDrive;
|
|
71
|
+
readonly palette: typeof Palette;
|
|
72
|
+
readonly shieldcheck: typeof ShieldCheck;
|
|
73
|
+
readonly shield: typeof Shield;
|
|
74
|
+
readonly cloud: typeof Cloud;
|
|
75
|
+
readonly searchcode: typeof SearchCode;
|
|
76
|
+
readonly archive: typeof Archive;
|
|
77
|
+
readonly upload: typeof Upload;
|
|
78
|
+
readonly messagessquare: typeof MessagesSquare;
|
|
79
|
+
readonly externallink: typeof ExternalLink;
|
|
80
|
+
readonly filetext: typeof FileText;
|
|
81
|
+
readonly tag: typeof Tag;
|
|
82
|
+
readonly rss: typeof Rss;
|
|
83
|
+
readonly eye: typeof Eye;
|
|
84
|
+
readonly download: typeof Download;
|
|
85
|
+
readonly layers: typeof Layers;
|
|
86
|
+
};
|
|
87
|
+
/** Icons representing growth and nature (Grove themed) */
|
|
88
|
+
export declare const growthIcons: {
|
|
89
|
+
readonly sprout: typeof Sprout;
|
|
90
|
+
readonly heart: typeof Heart;
|
|
91
|
+
readonly leaf: typeof Leaf;
|
|
92
|
+
readonly flower2: typeof Flower2;
|
|
93
|
+
readonly trees: typeof Trees;
|
|
94
|
+
readonly treedeciduous: typeof TreeDeciduous;
|
|
95
|
+
};
|
|
96
|
+
/** Icons for phases, refinement, and mystical/future content */
|
|
97
|
+
export declare const phaseIcons: {
|
|
98
|
+
readonly gem: typeof Gem;
|
|
99
|
+
readonly sparkles: typeof Sparkles;
|
|
100
|
+
readonly star: typeof Star;
|
|
101
|
+
readonly moon: typeof Moon;
|
|
102
|
+
readonly sun: typeof Sun;
|
|
103
|
+
readonly sprout: typeof Sprout;
|
|
104
|
+
};
|
|
105
|
+
/** Icons for user actions and processes */
|
|
106
|
+
export declare const actionIcons: {
|
|
107
|
+
readonly compass: typeof Compass;
|
|
108
|
+
readonly megaphone: typeof Megaphone;
|
|
109
|
+
readonly lightbulb: typeof Lightbulb;
|
|
110
|
+
readonly download: typeof Download;
|
|
111
|
+
readonly settings: typeof Settings;
|
|
112
|
+
readonly menu: typeof Menu;
|
|
113
|
+
readonly trend: typeof TrendingUp;
|
|
114
|
+
readonly trenddown: typeof TrendingDown;
|
|
115
|
+
readonly arrow: typeof ArrowRight;
|
|
116
|
+
};
|
|
117
|
+
/** Icons for analytics and metrics display */
|
|
118
|
+
export declare const metricsIcons: {
|
|
119
|
+
readonly clock: typeof Clock;
|
|
120
|
+
readonly trending: typeof TrendingUp;
|
|
121
|
+
readonly trenddown: typeof TrendingDown;
|
|
122
|
+
readonly activity: typeof Activity;
|
|
123
|
+
readonly users: typeof Users;
|
|
124
|
+
readonly shield: typeof ShieldUser;
|
|
125
|
+
readonly barchart: typeof BarChart3;
|
|
126
|
+
};
|
|
127
|
+
/** All icons in one map (use specific maps above when possible) */
|
|
128
|
+
export declare const allIcons: {
|
|
129
|
+
readonly clock: typeof Clock;
|
|
130
|
+
readonly trending: typeof TrendingUp;
|
|
131
|
+
readonly trenddown: typeof TrendingDown;
|
|
132
|
+
readonly activity: typeof Activity;
|
|
133
|
+
readonly users: typeof Users;
|
|
134
|
+
readonly shield: typeof ShieldUser;
|
|
135
|
+
readonly barchart: typeof BarChart3;
|
|
136
|
+
readonly compass: typeof Compass;
|
|
137
|
+
readonly megaphone: typeof Megaphone;
|
|
138
|
+
readonly lightbulb: typeof Lightbulb;
|
|
139
|
+
readonly download: typeof Download;
|
|
140
|
+
readonly settings: typeof Settings;
|
|
141
|
+
readonly menu: typeof Menu;
|
|
142
|
+
readonly trend: typeof TrendingUp;
|
|
143
|
+
readonly arrow: typeof ArrowRight;
|
|
144
|
+
readonly gem: typeof Gem;
|
|
145
|
+
readonly sparkles: typeof Sparkles;
|
|
146
|
+
readonly star: typeof Star;
|
|
147
|
+
readonly moon: typeof Moon;
|
|
148
|
+
readonly sun: typeof Sun;
|
|
149
|
+
readonly sprout: typeof Sprout;
|
|
150
|
+
readonly heart: typeof Heart;
|
|
151
|
+
readonly leaf: typeof Leaf;
|
|
152
|
+
readonly flower2: typeof Flower2;
|
|
153
|
+
readonly trees: typeof Trees;
|
|
154
|
+
readonly treedeciduous: typeof TreeDeciduous;
|
|
155
|
+
readonly mail: typeof Mail;
|
|
156
|
+
readonly harddrive: typeof HardDrive;
|
|
157
|
+
readonly palette: typeof Palette;
|
|
158
|
+
readonly shieldcheck: typeof ShieldCheck;
|
|
159
|
+
readonly cloud: typeof Cloud;
|
|
160
|
+
readonly searchcode: typeof SearchCode;
|
|
161
|
+
readonly archive: typeof Archive;
|
|
162
|
+
readonly upload: typeof Upload;
|
|
163
|
+
readonly messagessquare: typeof MessagesSquare;
|
|
164
|
+
readonly externallink: typeof ExternalLink;
|
|
165
|
+
readonly filetext: typeof FileText;
|
|
166
|
+
readonly tag: typeof Tag;
|
|
167
|
+
readonly rss: typeof Rss;
|
|
168
|
+
readonly eye: typeof Eye;
|
|
169
|
+
readonly layers: typeof Layers;
|
|
170
|
+
readonly check: typeof Check;
|
|
171
|
+
readonly checkcircle: typeof CheckCircle;
|
|
172
|
+
readonly x: typeof X;
|
|
173
|
+
readonly loader: typeof Loader2;
|
|
174
|
+
readonly warning: typeof AlertTriangle;
|
|
175
|
+
readonly help: typeof HelpCircle;
|
|
176
|
+
readonly info: typeof Info;
|
|
177
|
+
readonly circle: typeof Circle;
|
|
178
|
+
readonly lock: typeof Lock;
|
|
179
|
+
readonly home: typeof Home;
|
|
180
|
+
readonly about: typeof Info;
|
|
181
|
+
readonly vision: typeof Telescope;
|
|
182
|
+
readonly roadmap: typeof MapPin;
|
|
183
|
+
readonly pricing: typeof CircleDollarSign;
|
|
184
|
+
readonly knowledge: typeof BookOpen;
|
|
185
|
+
readonly forest: typeof Trees;
|
|
186
|
+
readonly blog: typeof PenLine;
|
|
187
|
+
readonly arrowLeft: typeof ArrowLeft;
|
|
188
|
+
readonly chevron: typeof ChevronRight;
|
|
189
|
+
readonly chevronLeft: typeof ChevronLeft;
|
|
190
|
+
readonly chevronDown: typeof ChevronDown;
|
|
191
|
+
readonly external: typeof ExternalLink;
|
|
192
|
+
readonly login: typeof LogIn;
|
|
193
|
+
readonly github: typeof Github;
|
|
194
|
+
};
|
|
195
|
+
/** Type for any icon key in the registry */
|
|
196
|
+
export type IconKey = keyof typeof allIcons;
|
|
197
|
+
/**
|
|
198
|
+
* Get an icon from a specific map by key
|
|
199
|
+
* @example
|
|
200
|
+
* ```ts
|
|
201
|
+
* const icon = getIcon(stateIcons, 'check');
|
|
202
|
+
* ```
|
|
203
|
+
*/
|
|
204
|
+
export declare function getIcon<T extends Record<string, any>>(map: T, key: keyof T | string): any;
|
|
205
|
+
/**
|
|
206
|
+
* Get an icon from the unified map
|
|
207
|
+
* @example
|
|
208
|
+
* ```ts
|
|
209
|
+
* const icon = getIconFromAll('check');
|
|
210
|
+
* ```
|
|
211
|
+
*/
|
|
212
|
+
export declare function getIconFromAll(key: string): any;
|
|
213
|
+
export { Check, CheckCircle, X, ArrowRight, ArrowLeft, MapPin, Sprout, Trees, TreeDeciduous, Crown, Flower2, Leaf, Heart, Home, Menu, Settings, ExternalLink, ChevronDown, LogIn, Github, Mail, HardDrive, Palette, Shield, Download, Rss, Eye, MessageCircle, Layers, Loader2, AlertTriangle, HelpCircle, Lock, Sparkles, Clock, TrendingUp, Users, Activity, };
|