@deneb-ui/ui 2.0.47 → 2.0.49
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 +94 -2
- package/dist/EditableGoogleFeedback.js +17 -3
- package/dist/EditableProductGrid.d.ts +5 -4
- package/dist/EditableProductGrid.js +15 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -156,8 +156,12 @@ export default function Page() {
|
|
|
156
156
|
### 7. Runtime State & Theming Primitives
|
|
157
157
|
| Component / Hook | Module Path | Purpose |
|
|
158
158
|
| :--- | :--- | :--- |
|
|
159
|
-
| `SiteDataProvider` | `SiteDataProvider` | Real-time
|
|
160
|
-
| `
|
|
159
|
+
| `SiteDataProvider` / `DenebDataProvider` | `SiteDataProvider` | Real-time backend catalog fetcher & `postMessage` preview state synchronizer |
|
|
160
|
+
| `useProducts` | `SiteDataProvider` | React hook to access live synchronized products list (`content.products`) |
|
|
161
|
+
| `useServices` | `SiteDataProvider` | React hook to access live synchronized services list (`content.services`) |
|
|
162
|
+
| `useSiteCatalog` | `SiteDataProvider` | Returns `{ products, services, project, siteInstance, api }` in one call |
|
|
163
|
+
| `useSiteApi` | `SiteDataProvider` | Returns official Fivora backend endpoints (`catalogUrl`, `contactUrl`, `analyticsUrl`) |
|
|
164
|
+
| `useSiteData` / `useDenebData` | `SiteDataProvider` | React hook to access full live synchronized `siteData` |
|
|
161
165
|
| `ThemeStyles` | `ThemeStyles` | Dynamic CSS variable injector for colors, fonts, and radii |
|
|
162
166
|
| `ResponsiveBaseStyles`| `ResponsiveBaseStyles` | Universal fluid typography & responsive baseline styles |
|
|
163
167
|
| `DenebComponentStyles`| `DenebComponentStyles` | Scoped component style definitions |
|
|
@@ -165,6 +169,94 @@ export default function Page() {
|
|
|
165
169
|
| `useDenebFonts` | `fonts/useDenebFonts` | Hook for checking active font definitions |
|
|
166
170
|
| `CartProvider` / `useCart`| `cart/useCart` | Global cart state management with persistent storage |
|
|
167
171
|
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Data Fetching & Backend Integration Hooks
|
|
175
|
+
|
|
176
|
+
### 1. `useProducts(fallback?: ProductItem[])`
|
|
177
|
+
Retrieves the live products list from the site data. Automatically resolves top-level `content.products`, nested `content.home.products`, and live backend rehydration updates from `api.catalogUrl`.
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
import { useProducts, ProductGrid } from '@deneb-ui/ui';
|
|
181
|
+
|
|
182
|
+
export default function CatalogSection() {
|
|
183
|
+
const products = useProducts();
|
|
184
|
+
|
|
185
|
+
return (
|
|
186
|
+
<ProductGrid
|
|
187
|
+
title="Featured Collection"
|
|
188
|
+
subtitle="Handpicked Styles"
|
|
189
|
+
products={products}
|
|
190
|
+
categories={['All', 'Accessories', 'Apparel']}
|
|
191
|
+
cardVariant="modern-glass" // 'modern-glass' | 'classic' | 'minimal' | 'horizontal'
|
|
192
|
+
columns={{ mobile: 1, tablet: 2, desktop: 3 }}
|
|
193
|
+
/>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### 2. `useSiteApi()`
|
|
199
|
+
Returns the official backend API configuration object (`SiteDataApiConfig`) defined in `site-data.json`. Use this hook whenever you need to connect custom forms or direct API calls to the backend:
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
import { useSiteApi } from '@deneb-ui/ui';
|
|
203
|
+
|
|
204
|
+
export default function ApiDemo() {
|
|
205
|
+
const api = useSiteApi();
|
|
206
|
+
|
|
207
|
+
// api?.baseUrl -> "https://api.fivora.site"
|
|
208
|
+
// api?.catalogUrl -> "https://api.fivora.site/site-catalog/:slug/live-data"
|
|
209
|
+
// api?.contactUrl -> "https://api.fivora.site/site-contact"
|
|
210
|
+
// api?.analyticsUrl -> "https://api.fivora.site/site-analytics/page-view"
|
|
211
|
+
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 3. `useSiteCatalog()`
|
|
217
|
+
Convenience hook that bundles catalog metadata and live status together:
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import { useSiteCatalog } from '@deneb-ui/ui';
|
|
221
|
+
|
|
222
|
+
export default function CatalogOverview() {
|
|
223
|
+
const { products, services, project, siteInstance, api } = useSiteCatalog();
|
|
224
|
+
|
|
225
|
+
return (
|
|
226
|
+
<div>
|
|
227
|
+
<h3>{project?.title}</h3>
|
|
228
|
+
<p>Active live products: {products.length}</p>
|
|
229
|
+
<p>Active live services: {services.length}</p>
|
|
230
|
+
</div>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 4. `useServices(fallback?: ServiceItem[])`
|
|
236
|
+
Retrieves the live services list from `content.services` or `content.home.services`:
|
|
237
|
+
|
|
238
|
+
```tsx
|
|
239
|
+
import { useServices, ServiceCard } from '@deneb-ui/ui';
|
|
240
|
+
|
|
241
|
+
export default function ServicesList() {
|
|
242
|
+
const services = useServices();
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
246
|
+
{services.map((service, index) => (
|
|
247
|
+
<ServiceCard
|
|
248
|
+
key={service.id || index}
|
|
249
|
+
itemPath={`services[${index}]`}
|
|
250
|
+
service={service}
|
|
251
|
+
/>
|
|
252
|
+
))}
|
|
253
|
+
</div>
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
168
260
|
## Component Guides & API Reference
|
|
169
261
|
|
|
170
262
|
### 1. `GoogleFeedback` (`EditableGoogleFeedback`)
|
|
@@ -60,7 +60,7 @@ function GoogleStarSvg({ filled = true, size = 18 }) {
|
|
|
60
60
|
flexShrink: 0,
|
|
61
61
|
}, "aria-hidden": "true", children: (0, jsx_runtime_1.jsx)("path", { d: "M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" }) }));
|
|
62
62
|
}
|
|
63
|
-
function FeedbackCard({ item, index, basePath, maxStars, badgeIcon, cardClassName, }) {
|
|
63
|
+
function FeedbackCard({ item, index, basePath, maxStars, badgeIcon, badgeTitle, cardClassName, }) {
|
|
64
64
|
const currentItem = item || {};
|
|
65
65
|
const rawRating = currentItem.rating != null && currentItem.rating !== '' ? currentItem.rating : 5;
|
|
66
66
|
const initialRating = Math.min(Math.max(Math.round(Number(rawRating) || 5), 1), maxStars);
|
|
@@ -100,7 +100,21 @@ function FeedbackCard({ item, index, basePath, maxStars, badgeIcon, cardClassNam
|
|
|
100
100
|
'https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=300&auto=format&fit=crop');
|
|
101
101
|
const reviewerComment = String(currentItem.comment || '');
|
|
102
102
|
const reviewerDate = String(currentItem.date || 'Recent review');
|
|
103
|
-
return ((0, jsx_runtime_1.jsx)("div", { "data-preview-item-path": `feedback.feedbacks[${index}]`, className: `p-6 sm:p-7 rounded-3xl bg-[#ffffff] border border-[#3d2114]/12 shadow-sm hover:shadow-md transition-all duration-300 flex flex-col justify-between space-y-4 ${cardClassName || ''}`.trim(), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("img", { src: reviewerAvatar, alt: reviewerName, "data-preview-field-path": `feedback.feedbacks[${index}].avatar`, className: "w-11 h-11 rounded-full object-cover border border-[#3d2114]/15 shadow-sm" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.feedbacks[${index}].name`, className: "block text-sm font-black text-[#2b170e]", children: reviewerName }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.feedbacks[${index}].date`, className: "block text-xs text-[#8c7a6e]", children: reviewerDate })] })] }), (
|
|
103
|
+
return ((0, jsx_runtime_1.jsx)("div", { "data-preview-item-path": `feedback.feedbacks[${index}]`, className: `p-6 sm:p-7 rounded-3xl bg-[#ffffff] border border-[#3d2114]/12 shadow-sm hover:shadow-md transition-all duration-300 flex flex-col justify-between space-y-4 ${cardClassName || ''}`.trim(), children: (0, jsx_runtime_1.jsxs)("div", { className: "space-y-3.5", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-3", children: [(0, jsx_runtime_1.jsx)("img", { src: reviewerAvatar, alt: reviewerName, "data-preview-field-path": `feedback.feedbacks[${index}].avatar`, className: "w-11 h-11 rounded-full object-cover border border-[#3d2114]/15 shadow-sm" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.feedbacks[${index}].name`, className: "block text-sm font-black text-[#2b170e]", children: reviewerName }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.feedbacks[${index}].date`, className: "block text-xs text-[#8c7a6e]", children: reviewerDate })] })] }), (() => {
|
|
104
|
+
const itemCustomIcon = currentItem?.badgeIcon || currentItem?.platformIcon;
|
|
105
|
+
const finalCardIcon = typeof itemCustomIcon === 'string' && itemCustomIcon.trim() ? itemCustomIcon : (badgeIcon || DEFAULT_GOOGLE_ICON);
|
|
106
|
+
const cardIconFieldPath = typeof itemCustomIcon === 'string' && itemCustomIcon.trim()
|
|
107
|
+
? `${basePath}.feedbacks[${index}].badgeIcon`
|
|
108
|
+
: `${basePath}.badgeIcon`;
|
|
109
|
+
return ((0, jsx_runtime_1.jsx)("img", { src: finalCardIcon, alt: badgeTitle || 'Review Platform', "data-preview-field-path": cardIconFieldPath, className: "w-5 h-5 object-contain opacity-90 shrink-0 cursor-pointer", style: {
|
|
110
|
+
width: '20px',
|
|
111
|
+
height: '20px',
|
|
112
|
+
minWidth: '20px',
|
|
113
|
+
minHeight: '20px',
|
|
114
|
+
maxWidth: '20px',
|
|
115
|
+
maxHeight: '20px',
|
|
116
|
+
}, title: "Click to upload/change review platform icon (Google, Twitter, Trustpilot, etc.)" }));
|
|
117
|
+
})()] }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-2 pt-1", children: [(0, jsx_runtime_1.jsxs)("div", { className: "inline-flex items-center gap-2 p-1 -ml-1 rounded-lg cursor-pointer transition-all hover:bg-[#fa7014]/10 group", onClick: handleFocusRating, title: `Rating: ${currentRating} of ${maxStars} (Click to edit)`, children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-[2px]", "data-fivora-stars-row": "true", children: Array.from({ length: maxStars }).map((_, sIdx) => ((0, jsx_runtime_1.jsx)(GoogleStarSvg, { filled: sIdx < currentRating, size: 18 }, sIdx))) }), (0, jsx_runtime_1.jsx)("span", { ref: ratingRef, "data-preview-field-path": `feedback.feedbacks[${index}].rating`, "data-fivora-rating-text": "true", className: "text-xs font-black text-[#fa7014] bg-[#fa7014]/10 px-1.5 py-0.5 rounded-md border border-[#fa7014]/20 group-hover:bg-[#fa7014]/20 transition-colors tabular-nums", title: "Star count (1-5)", children: currentRating })] }), (0, jsx_runtime_1.jsxs)("div", { className: "inline-flex items-center gap-1 text-[11px] font-bold text-[#1a73e8] bg-[#e8f0fe] px-2.5 py-1 rounded-full border border-[#1a73e8]/20", children: [(0, jsx_runtime_1.jsx)("svg", { className: "w-3 h-3 fill-current", viewBox: "0 0 24 24", children: (0, jsx_runtime_1.jsx)("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" }) }), (0, jsx_runtime_1.jsx)("span", { children: "Verified" })] })] }), (0, jsx_runtime_1.jsxs)("p", { "data-preview-field-path": `feedback.feedbacks[${index}].comment`, className: "text-sm sm:text-base text-[#3d2114] leading-relaxed italic font-normal pt-1", children: ["\u201C", reviewerComment, "\u201D"] })] }) }));
|
|
104
118
|
}
|
|
105
119
|
function EditableGoogleFeedback({ basePath = 'feedback', badgeIcon = DEFAULT_GOOGLE_ICON, badgeTitle = 'Google Reviews', badgeRating = '4.9', badgeReviewsCount = '340+ Verified Reviews', heading = 'PRAISED BY COFFEE LOVERS', subheading = 'Real experiences from patrons who visit our roastery parlor and brew our micro-lots.', feedbacks = DEFAULT_FEEDBACKS, maxStars = 5, className = '', cardClassName = '', style, ...props }) {
|
|
106
120
|
const items = (0, react_1.useMemo)(() => {
|
|
@@ -121,5 +135,5 @@ function EditableGoogleFeedback({ basePath = 'feedback', badgeIcon = DEFAULT_GOO
|
|
|
121
135
|
};
|
|
122
136
|
});
|
|
123
137
|
}, [feedbacks]);
|
|
124
|
-
return ((0, jsx_runtime_1.jsx)("section", { "data-design-section": "google-feedback", className: `editable-google-feedback w-full ${className}`.trim(), style: style, ...props, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-[1600px] mx-auto px-4 sm:px-6 lg:px-10", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col md:flex-row md:items-end justify-between gap-6 mb-10 sm:mb-14", children: [(0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl space-y-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "inline-flex items-center gap-2.5 px-3.5 py-1.5 rounded-full bg-[#ede0d4]/80 border border-[#3d2114]/15 shadow-sm", children: [(0, jsx_runtime_1.jsx)("img", { src: badgeIcon || DEFAULT_GOOGLE_ICON, alt: "Google", "data-preview-field-path": `feedback.badgeIcon`, className: "w-4 h-4 object-contain" }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeTitle`, className: "text-xs uppercase tracking-widest font-black text-[#3d2114]", children: badgeTitle }), (0, jsx_runtime_1.jsx)("span", { className: "w-1 h-1 rounded-full bg-[#84431b]" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1.5", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeRating`, className: "text-xs font-black text-[#84431b]", children: badgeRating }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-[2px]", children: Array.from({ length: 5 }).map((_, sIdx) => ((0, jsx_runtime_1.jsx)(GoogleStarSvg, { filled: true, size: 14 }, sIdx))) })] }), (0, jsx_runtime_1.jsxs)("span", { className: "text-[11px] text-[#786154] font-medium hidden sm:inline", children: ["(", (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeReviewsCount`, children: badgeReviewsCount }), ")"] })] }), (0, jsx_runtime_1.jsx)("h2", { "data-preview-field-path": `feedback.heading`, className: "font-heading text-2xl xs:text-3xl sm:text-4xl lg:text-5xl font-black text-[#2b170e] tracking-tight", children: heading }), (0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `feedback.subheading`, className: "text-xs sm:text-base text-[#5c493f] leading-relaxed", children: subheading })] }), (0, jsx_runtime_1.jsx)("div", { className: "hidden md:flex flex-col items-end text-right", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeRating`, className: "font-heading text-4xl font-black text-[#84431b]", children: badgeRating }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0.5", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-[2px]", children: Array.from({ length: 5 }).map((_, sIdx) => ((0, jsx_runtime_1.jsx)(GoogleStarSvg, { filled: true, size: 16 }, sIdx))) }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeReviewsCount`, className: "text-xs text-[#786154] font-bold", children: badgeReviewsCount })] })] }) })] }), (0, jsx_runtime_1.jsx)("div", { "data-preview-list-path": `feedback.feedbacks`, className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: items.map((item, index) => ((0, jsx_runtime_1.jsx)(FeedbackCard, { item: item, index: index, basePath: basePath, maxStars: maxStars, badgeIcon: badgeIcon, cardClassName: cardClassName }, item.id || `feedback-${index}`))) })] }) }));
|
|
138
|
+
return ((0, jsx_runtime_1.jsx)("section", { "data-design-section": "google-feedback", className: `editable-google-feedback w-full ${className}`.trim(), style: style, ...props, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-[1600px] mx-auto px-4 sm:px-6 lg:px-10", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col md:flex-row md:items-end justify-between gap-6 mb-10 sm:mb-14", children: [(0, jsx_runtime_1.jsxs)("div", { className: "max-w-2xl space-y-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "inline-flex items-center gap-2.5 px-3.5 py-1.5 rounded-full bg-[#ede0d4]/80 border border-[#3d2114]/15 shadow-sm", children: [(0, jsx_runtime_1.jsx)("img", { src: badgeIcon || DEFAULT_GOOGLE_ICON, alt: "Google", "data-preview-field-path": `feedback.badgeIcon`, className: "w-4 h-4 object-contain" }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeTitle`, className: "text-xs uppercase tracking-widest font-black text-[#3d2114]", children: badgeTitle }), (0, jsx_runtime_1.jsx)("span", { className: "w-1 h-1 rounded-full bg-[#84431b]" }), (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-1.5", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeRating`, className: "text-xs font-black text-[#84431b]", children: badgeRating }), (0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-[2px]", children: Array.from({ length: 5 }).map((_, sIdx) => ((0, jsx_runtime_1.jsx)(GoogleStarSvg, { filled: true, size: 14 }, sIdx))) })] }), (0, jsx_runtime_1.jsxs)("span", { className: "text-[11px] text-[#786154] font-medium hidden sm:inline", children: ["(", (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeReviewsCount`, children: badgeReviewsCount }), ")"] })] }), (0, jsx_runtime_1.jsx)("h2", { "data-preview-field-path": `feedback.heading`, className: "font-heading text-2xl xs:text-3xl sm:text-4xl lg:text-5xl font-black text-[#2b170e] tracking-tight", children: heading }), (0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `feedback.subheading`, className: "text-xs sm:text-base text-[#5c493f] leading-relaxed", children: subheading })] }), (0, jsx_runtime_1.jsx)("div", { className: "hidden md:flex flex-col items-end text-right", children: (0, jsx_runtime_1.jsxs)("div", { className: "flex items-center gap-2", children: [(0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeRating`, className: "font-heading text-4xl font-black text-[#84431b]", children: badgeRating }), (0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col items-start gap-0.5", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center gap-[2px]", children: Array.from({ length: 5 }).map((_, sIdx) => ((0, jsx_runtime_1.jsx)(GoogleStarSvg, { filled: true, size: 16 }, sIdx))) }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `feedback.badgeReviewsCount`, className: "text-xs text-[#786154] font-bold", children: badgeReviewsCount })] })] }) })] }), (0, jsx_runtime_1.jsx)("div", { "data-preview-list-path": `feedback.feedbacks`, className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: items.map((item, index) => ((0, jsx_runtime_1.jsx)(FeedbackCard, { item: item, index: index, basePath: basePath, maxStars: maxStars, badgeIcon: badgeIcon, badgeTitle: badgeTitle, cardClassName: cardClassName }, item.id || `feedback-${index}`))) })] }) }));
|
|
125
139
|
}
|
|
@@ -19,11 +19,12 @@ export interface EditableProductGridProps extends React.HTMLAttributes<HTMLEleme
|
|
|
19
19
|
*/
|
|
20
20
|
subtitle?: string;
|
|
21
21
|
/**
|
|
22
|
-
* Array of product items.
|
|
22
|
+
* Array of product items. If omitted or empty, automatically pulls live products using useProducts().
|
|
23
23
|
*/
|
|
24
|
-
products
|
|
24
|
+
products?: ProductItem[];
|
|
25
25
|
/**
|
|
26
26
|
* Optional category filter tabs (e.g. ['All', 'Sneakers', 'Performance', 'Lifestyle']).
|
|
27
|
+
* If omitted, automatically derived from product categories.
|
|
27
28
|
*/
|
|
28
29
|
categories?: string[];
|
|
29
30
|
/**
|
|
@@ -52,9 +53,9 @@ export interface EditableProductGridProps extends React.HTMLAttributes<HTMLEleme
|
|
|
52
53
|
/**
|
|
53
54
|
* EditableProductGrid is an elite, fully responsive e-commerce showcase grid
|
|
54
55
|
* with interactive category filtering, live Fivora visual editing synchronization,
|
|
55
|
-
* and built-in Quick View modal triggers.
|
|
56
|
+
* automatic useProducts() fallback rehydration, and built-in Quick View modal triggers.
|
|
56
57
|
*
|
|
57
58
|
* Created by Chamika Gayashan & Induranga Kawishwara
|
|
58
59
|
*/
|
|
59
|
-
export declare function EditableProductGrid({ sectionPath, listPath, title, subtitle, products, categories, cardVariant, columns, onQuickView, cardPrefix, className, style, ...props }: EditableProductGridProps): React.JSX.Element;
|
|
60
|
+
export declare function EditableProductGrid({ sectionPath, listPath, title, subtitle, products: userProducts, categories, cardVariant, columns, onQuickView, cardPrefix, className, style, ...props }: EditableProductGridProps): React.JSX.Element;
|
|
60
61
|
export declare const ProductGrid: typeof EditableProductGrid;
|
|
@@ -5,14 +5,26 @@ exports.EditableProductGrid = EditableProductGrid;
|
|
|
5
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
6
|
const react_1 = require("react");
|
|
7
7
|
const EditableProductCard_1 = require("./EditableProductCard");
|
|
8
|
+
const SiteDataProvider_1 = require("./SiteDataProvider");
|
|
8
9
|
/**
|
|
9
10
|
* EditableProductGrid is an elite, fully responsive e-commerce showcase grid
|
|
10
11
|
* with interactive category filtering, live Fivora visual editing synchronization,
|
|
11
|
-
* and built-in Quick View modal triggers.
|
|
12
|
+
* automatic useProducts() fallback rehydration, and built-in Quick View modal triggers.
|
|
12
13
|
*
|
|
13
14
|
* Created by Chamika Gayashan & Induranga Kawishwara
|
|
14
15
|
*/
|
|
15
|
-
function EditableProductGrid({ sectionPath = 'home', listPath = 'products', title = 'Featured Collection', subtitle = 'Just Dropped', products
|
|
16
|
+
function EditableProductGrid({ sectionPath = 'home', listPath = 'products', title = 'Featured Collection', subtitle = 'Just Dropped', products: userProducts, categories = ['All'], cardVariant = 'modern-glass', columns = { mobile: 1, tablet: 2, desktop: 4 }, onQuickView, cardPrefix = 'product', className = '', style, ...props }) {
|
|
17
|
+
const liveProducts = (0, SiteDataProvider_1.useProducts)();
|
|
18
|
+
const products = userProducts && userProducts.length > 0 ? userProducts : liveProducts;
|
|
19
|
+
const resolvedCategories = (0, react_1.useMemo)(() => {
|
|
20
|
+
if (categories && categories.length > 1) {
|
|
21
|
+
return categories;
|
|
22
|
+
}
|
|
23
|
+
const distinct = Array.from(new Set(products
|
|
24
|
+
.map((p) => p.category)
|
|
25
|
+
.filter((cat) => typeof cat === 'string' && cat.trim().length > 0)));
|
|
26
|
+
return distinct.length > 0 ? ['All', ...distinct] : categories;
|
|
27
|
+
}, [categories, products]);
|
|
16
28
|
const [activeCategory, setActiveCategory] = (0, react_1.useState)(categories[0] || 'All');
|
|
17
29
|
const filteredProducts = (0, react_1.useMemo)(() => {
|
|
18
30
|
if (!activeCategory || activeCategory.toLowerCase() === 'all') {
|
|
@@ -33,7 +45,7 @@ function EditableProductGrid({ sectionPath = 'home', listPath = 'products', titl
|
|
|
33
45
|
const dClass = d === 3 ? 'lg:grid-cols-3' : d === 2 ? 'lg:grid-cols-2' : 'lg:grid-cols-4';
|
|
34
46
|
return `${mClass} ${tClass} ${dClass}`;
|
|
35
47
|
}, [columns]);
|
|
36
|
-
return ((0, jsx_runtime_1.jsxs)("section", { "data-preview-page-key": sectionPath, className: `editable-product-grid max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 ${className}`.trim(), style: style, ...props, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col md:flex-row md:items-end justify-between mb-10 gap-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [subtitle && ((0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.gridSubtitle`, className: "text-xs font-black tracking-widest uppercase text-lime-400 block mb-2", children: subtitle })), (0, jsx_runtime_1.jsx)("h2", { "data-preview-field-path": `${sectionPath}.gridTitle`, className: "text-3xl sm:text-4xl font-extrabold tracking-tight text-white leading-tight", children: title })] }),
|
|
48
|
+
return ((0, jsx_runtime_1.jsxs)("section", { "data-preview-page-key": sectionPath, className: `editable-product-grid max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 ${className}`.trim(), style: style, ...props, children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col md:flex-row md:items-end justify-between mb-10 gap-6", children: [(0, jsx_runtime_1.jsxs)("div", { children: [subtitle && ((0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${sectionPath}.gridSubtitle`, className: "text-xs font-black tracking-widest uppercase text-lime-400 block mb-2", children: subtitle })), (0, jsx_runtime_1.jsx)("h2", { "data-preview-field-path": `${sectionPath}.gridTitle`, className: "text-3xl sm:text-4xl font-extrabold tracking-tight text-white leading-tight", children: title })] }), resolvedCategories.length > 1 && ((0, jsx_runtime_1.jsx)("div", { className: "flex flex-wrap items-center gap-2", children: resolvedCategories.map((cat) => {
|
|
37
49
|
const isActive = activeCategory.toLowerCase() === cat.toLowerCase();
|
|
38
50
|
return ((0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => setActiveCategory(cat), className: `px-4 py-2 rounded-xl text-xs font-bold transition-all duration-200 ${isActive
|
|
39
51
|
? 'bg-white text-slate-950 shadow-md scale-102 font-extrabold'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.49",
|
|
4
4
|
"description": "Visual-first React component library for editable commerce storefronts. Built for Next.js and Fivora.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
],
|
|
50
50
|
"license": "MIT",
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@deneb-ui/core": "^2.0.
|
|
52
|
+
"@deneb-ui/core": "^2.0.49"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": "^18.0.0 || ^19.0.0",
|