@deneb-ui/ui 2.0.36 → 2.0.37
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 +334 -31
- package/dist/EditableGoogleFeedback.d.ts +25 -0
- package/dist/EditableGoogleFeedback.js +125 -0
- package/dist/EditableMap.d.ts +3 -1
- package/dist/EditableMap.js +75 -14
- package/dist/EditableTestimonialSection.d.ts +17 -0
- package/dist/EditableTestimonialSection.js +105 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
<p align="center">
|
|
16
16
|
<strong>Build beautiful, editable storefronts — on mobile, tablet, and desktop.</strong><br />
|
|
17
|
-
The visual-first React component library for Next.js commerce and Fivora live editing.
|
|
17
|
+
The visual-first React component library for Next.js commerce, ARC Engine, and Fivora live visual editing.
|
|
18
18
|
</p>
|
|
19
19
|
|
|
20
20
|
<p align="center">
|
|
@@ -28,18 +28,28 @@
|
|
|
28
28
|
|
|
29
29
|
## Getting Started
|
|
30
30
|
|
|
31
|
-
###
|
|
31
|
+
### Installation
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
npm install @deneb-ui/ui
|
|
35
|
-
#
|
|
36
|
-
|
|
35
|
+
# or
|
|
36
|
+
pnpm add @deneb-ui/ui
|
|
37
|
+
# or
|
|
38
|
+
yarn add @deneb-ui/ui
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
###
|
|
41
|
+
### Quick Start Example
|
|
40
42
|
|
|
41
43
|
```tsx
|
|
42
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
SiteDataProvider,
|
|
46
|
+
Navbar,
|
|
47
|
+
Hero,
|
|
48
|
+
GoogleFeedback,
|
|
49
|
+
TestimonialSection,
|
|
50
|
+
Map,
|
|
51
|
+
Footer
|
|
52
|
+
} from "@deneb-ui/ui";
|
|
43
53
|
import siteData from "@/data/site-data.json";
|
|
44
54
|
|
|
45
55
|
export default function Page() {
|
|
@@ -47,49 +57,342 @@ export default function Page() {
|
|
|
47
57
|
<SiteDataProvider initialSiteData={siteData}>
|
|
48
58
|
<Navbar />
|
|
49
59
|
<Hero />
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
<GoogleFeedback basePath="feedback" />
|
|
61
|
+
<TestimonialSection basePath="testimonials" />
|
|
62
|
+
<div className="w-full h-96 rounded-2xl overflow-hidden shadow-lg">
|
|
63
|
+
<Map mapUrl={siteData.content.contact?.mapUrl} />
|
|
64
|
+
</div>
|
|
53
65
|
<Footer />
|
|
54
66
|
</SiteDataProvider>
|
|
55
67
|
);
|
|
56
68
|
}
|
|
57
69
|
```
|
|
58
70
|
|
|
59
|
-
`SiteDataProvider` automatically injects **ResponsiveBaseStyles** — every component adapts across phone, tablet, and desktop without extra
|
|
71
|
+
`SiteDataProvider` automatically injects **ResponsiveBaseStyles** — every component adapts seamlessly across phone, tablet, and desktop without extra configuration.
|
|
60
72
|
|
|
61
73
|
---
|
|
62
74
|
|
|
63
|
-
##
|
|
75
|
+
## Canonical Component Aliases
|
|
64
76
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
77
|
+
`@deneb-ui/ui` provides both canonical shorthand aliases (shadcn / HeroUI style) and full `Editable*` identifiers:
|
|
78
|
+
|
|
79
|
+
| Canonical Alias | Full Component Identifier | Description |
|
|
80
|
+
| :--- | :--- | :--- |
|
|
81
|
+
| `GoogleFeedback` | `EditableGoogleFeedback` | Official Google review badge, aggregate rating, & review cards |
|
|
82
|
+
| `TestimonialSection` | `EditableTestimonialSection` | Editorial critic testimonials with typography & star ratings |
|
|
83
|
+
| `Testimonials` | `EditableTestimonialSection` | Alias for `EditableTestimonialSection` |
|
|
84
|
+
| `Map` | `EditableMap` | Universal Google Maps embed with automatic URL parsing |
|
|
85
|
+
| `CustomerReviews` | `EditableCustomerReviews` | Customer review summary & rating breakdown |
|
|
86
|
+
| `ProductCard` | `EditableProductCard` | Visual commerce product card with live price & badges |
|
|
87
|
+
| `ProductGrid` | `EditableProductGrid` | Responsive grid with visual editing markers |
|
|
88
|
+
| `Navbar` / `Header` | `EditableNavbar` | Responsive header with mobile drawer & cart trigger |
|
|
89
|
+
| `Hero` | `EditableHeroCentered` | High-converting split / centered storefront hero |
|
|
90
|
+
| `Footer` | `EditableFooter` | Multi-column commerce footer with social links & legal links |
|
|
91
|
+
| `CartDrawer` | `EditableCartDrawer` | Sliding visual cart drawer with item counter |
|
|
92
|
+
| `Accordion` / `FAQ`| `EditableFAQAccordion` | Collapsible FAQ accordion with Fivora list markers |
|
|
93
|
+
| `Button` | `EditableButton` | Visual button with action contract & ripple feedback |
|
|
94
|
+
| `Image` | `EditableImage` | Visual image with aspect ratio preservation & Fivora focus |
|
|
95
|
+
| `Text` | `EditableText` | Inline editable text with instant DOM sync |
|
|
72
96
|
|
|
73
97
|
---
|
|
74
98
|
|
|
75
|
-
##
|
|
99
|
+
## Component Guides & API Reference
|
|
76
100
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
### 1. `GoogleFeedback` (`EditableGoogleFeedback`)
|
|
102
|
+
|
|
103
|
+
The **`GoogleFeedback`** component renders an authentic Google Reviews card experience. It features the official Google multi-colored badge, aggregate rating pill, verified buyer checks, and a responsive grid of customer reviews.
|
|
104
|
+
|
|
105
|
+
#### Key Capabilities
|
|
106
|
+
- **Google Design Fidelity**: Uses Google Material star vectors with authentic warm gold (`#fa7014`) and neutral grey (`#dadce0`).
|
|
107
|
+
- **Live 1–5 Star DOM Sync**: Typing numbers `1` through `5` into the Fivora live inspector immediately recalculates and repaints the star fills in real time via an internal `MutationObserver`.
|
|
108
|
+
- **Zero-Latency Visual Editing**: Built-in `data-preview-field-path` and `data-preview-list-path` attributes compliant with the Fivora Strict Visual Editing Contract.
|
|
109
|
+
- **Auto-Fallbacks**: Provides elegant fallback review data if no `feedbacks` array is passed.
|
|
110
|
+
|
|
111
|
+
#### Props Reference
|
|
112
|
+
|
|
113
|
+
| Prop | Type | Default | Description |
|
|
114
|
+
| :--- | :--- | :--- | :--- |
|
|
115
|
+
| `basePath` | `string` | `'feedback'` | Root key in `site-data.json` (`content[basePath]`) |
|
|
116
|
+
| `badgeIcon` | `string` | Google G SVG | URL or SVG data URI for the review platform badge |
|
|
117
|
+
| `badgeTitle` | `string` | `'Google'` | Review platform label displayed next to badge icon |
|
|
118
|
+
| `badgeRating` | `string \| number` | `'4.9'` | Aggregate rating number shown in the top header |
|
|
119
|
+
| `badgeReviewsCount` | `string \| number` | `'128 reviews'` | Total review count label (e.g., `'128 reviews'`) |
|
|
120
|
+
| `heading` | `string` | `'Loved by Coffee Lovers...'` | Section main heading |
|
|
121
|
+
| `subheading` | `string` | `'Real stories and reviews...'` | Section introductory paragraph |
|
|
122
|
+
| `feedbacks` | `FeedbackItem[]` | `DEFAULT_FEEDBACKS` | Array of customer review items |
|
|
123
|
+
| `maxStars` | `number` | `5` | Maximum number of rating stars to render per card |
|
|
124
|
+
| `className` | `string` | `''` | Custom CSS / Tailwind classes for section wrapper |
|
|
125
|
+
| `cardClassName` | `string` | `''` | Custom CSS / Tailwind classes for review cards |
|
|
126
|
+
|
|
127
|
+
#### `FeedbackItem` Schema
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
export interface FeedbackItem {
|
|
131
|
+
id?: string | number;
|
|
132
|
+
name?: string; // Reviewer full name
|
|
133
|
+
avatar?: string; // Avatar image URL
|
|
134
|
+
rating?: number | string; // Star rating value (1 to 5)
|
|
135
|
+
date?: string; // Relative date (e.g. "2 days ago")
|
|
136
|
+
comment?: string; // Review body text
|
|
137
|
+
verified?: boolean; // Verified customer badge
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### Fivora Data Contract (`site-data.json`)
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
{
|
|
145
|
+
"content": {
|
|
146
|
+
"feedback": {
|
|
147
|
+
"badgeIcon": "https://upload.wikimedia.org/wikipedia/commons/c/c1/Google_%22G%22_logo.svg",
|
|
148
|
+
"badgeTitle": "Google",
|
|
149
|
+
"badgeRating": "4.9",
|
|
150
|
+
"badgeReviewsCount": "128 reviews",
|
|
151
|
+
"heading": "Loved by Coffee Lovers Across the World",
|
|
152
|
+
"subheading": "Real stories and reviews from our community of coffee purists and daily ritualists.",
|
|
153
|
+
"feedbacks": [
|
|
154
|
+
{
|
|
155
|
+
"id": "1",
|
|
156
|
+
"name": "Elena Vance",
|
|
157
|
+
"avatar": "https://images.unsplash.com/photo-1534528741775-53994a69daeb",
|
|
158
|
+
"rating": 5,
|
|
159
|
+
"date": "3 days ago",
|
|
160
|
+
"comment": "The Yirgacheffe pour-over is unmatched. Clean notes of jasmine and bergamot.",
|
|
161
|
+
"verified": true
|
|
162
|
+
}
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
#### Fivora `editorSchema` Section
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"id": "feedback",
|
|
174
|
+
"path": "feedback",
|
|
175
|
+
"type": "object",
|
|
176
|
+
"label": "Google Customer Feedback",
|
|
177
|
+
"pageKey": "home",
|
|
178
|
+
"fields": [
|
|
179
|
+
{ "key": "badgeIcon", "type": "image", "label": "Badge Icon URL" },
|
|
180
|
+
{ "key": "badgeTitle", "type": "text", "label": "Badge Title" },
|
|
181
|
+
{ "key": "badgeRating", "type": "text", "label": "Aggregate Rating" },
|
|
182
|
+
{ "key": "badgeReviewsCount", "type": "text", "label": "Reviews Count" },
|
|
183
|
+
{ "key": "heading", "type": "text", "label": "Section Heading" },
|
|
184
|
+
{ "key": "subheading", "type": "textarea", "label": "Section Subheading" },
|
|
185
|
+
{
|
|
186
|
+
"key": "feedbacks",
|
|
187
|
+
"type": "list",
|
|
188
|
+
"label": "Customer Reviews",
|
|
189
|
+
"itemLabel": "Review",
|
|
190
|
+
"minItems": 1,
|
|
191
|
+
"maxItems": 12,
|
|
192
|
+
"fields": [
|
|
193
|
+
{ "key": "name", "type": "text", "label": "Reviewer Name", "required": true },
|
|
194
|
+
{ "key": "avatar", "type": "image", "label": "Profile Picture" },
|
|
195
|
+
{ "key": "rating", "type": "number", "label": "Star Rating (1-5)" },
|
|
196
|
+
{ "key": "date", "type": "text", "label": "Review Date" },
|
|
197
|
+
{ "key": "comment", "type": "textarea", "label": "Review Comment", "required": true }
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
#### Usage Example
|
|
205
|
+
|
|
206
|
+
```tsx
|
|
207
|
+
import { GoogleFeedback, useSiteData } from "@deneb-ui/ui";
|
|
208
|
+
|
|
209
|
+
export function FeedbackSection() {
|
|
210
|
+
const { siteData } = useSiteData();
|
|
211
|
+
const feedbackData = siteData?.content?.feedback || {};
|
|
212
|
+
|
|
213
|
+
return (
|
|
214
|
+
<section className="py-20 bg-amber-50/40">
|
|
215
|
+
<GoogleFeedback
|
|
216
|
+
basePath="feedback"
|
|
217
|
+
badgeIcon={feedbackData.badgeIcon}
|
|
218
|
+
badgeTitle={feedbackData.badgeTitle}
|
|
219
|
+
badgeRating={feedbackData.badgeRating}
|
|
220
|
+
badgeReviewsCount={feedbackData.badgeReviewsCount}
|
|
221
|
+
heading={feedbackData.heading}
|
|
222
|
+
subheading={feedbackData.subheading}
|
|
223
|
+
feedbacks={feedbackData.feedbacks}
|
|
224
|
+
/>
|
|
225
|
+
</section>
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
### 2. `TestimonialSection` (`EditableTestimonialSection`)
|
|
233
|
+
|
|
234
|
+
The **`TestimonialSection`** component provides an editorial, magazine-grade layout for connoisseur quotes, culinary critics, and press reviews.
|
|
235
|
+
|
|
236
|
+
#### Key Capabilities
|
|
237
|
+
- **Editorial Typography**: Large quotation marks, serif italic styling, and prominent author metadata.
|
|
238
|
+
- **Accreditation Tags**: Includes specialty tags (e.g. *"Michelin Guide"*, *"World Barista Judge"*).
|
|
239
|
+
- **Synchronized Star Ratings**: Dynamic star rating with live inspector sync.
|
|
240
|
+
- **Clean Layout**: Fluid responsive grid adapting from 1 column on mobile to 3 columns on desktop.
|
|
241
|
+
|
|
242
|
+
#### Props Reference
|
|
243
|
+
|
|
244
|
+
| Prop | Type | Default | Description |
|
|
245
|
+
| :--- | :--- | :--- | :--- |
|
|
246
|
+
| `basePath` | `string` | `'testimonials'` | Root key in `site-data.json` (`content[basePath]`) |
|
|
247
|
+
| `badge` | `string` | `'Critic Acclaim'` | Small uppercase pill badge text |
|
|
248
|
+
| `heading` | `string` | `'What Connoisseurs Say'` | Main section title |
|
|
249
|
+
| `subheading` | `string` | `'Unfiltered impressions...'` | Subtitle description text |
|
|
250
|
+
| `testimonials` | `TestimonialSectionItem[]` | `DEFAULT_TESTIMONIALS` | Array of testimonial items |
|
|
251
|
+
| `maxStars` | `number` | `5` | Maximum number of stars per card |
|
|
252
|
+
| `className` | `string` | `''` | Section container CSS classes |
|
|
253
|
+
| `cardClassName` | `string` | `''` | Individual testimonial card CSS classes |
|
|
254
|
+
|
|
255
|
+
#### `TestimonialSectionItem` Schema
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
export interface TestimonialSectionItem {
|
|
259
|
+
id?: string | number;
|
|
260
|
+
quote?: string; // Testimonial quote text
|
|
261
|
+
author?: string; // Author full name
|
|
262
|
+
role?: string; // Profession, publication, or title
|
|
263
|
+
avatar?: string; // Author portrait URL
|
|
264
|
+
tag?: string; // Category or accreditation badge
|
|
265
|
+
rating?: number | string; // Star rating (1 to 5)
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
#### Usage Example
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
import { TestimonialSection, useSiteData } from "@deneb-ui/ui";
|
|
273
|
+
|
|
274
|
+
export function EditorialReviews() {
|
|
275
|
+
const { siteData } = useSiteData();
|
|
276
|
+
const data = siteData?.content?.testimonials || {};
|
|
277
|
+
|
|
278
|
+
return (
|
|
279
|
+
<TestimonialSection
|
|
280
|
+
basePath="testimonials"
|
|
281
|
+
badge={data.badge}
|
|
282
|
+
heading={data.heading}
|
|
283
|
+
subheading={data.subheading}
|
|
284
|
+
testimonials={data.testimonials}
|
|
285
|
+
/>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
---
|
|
291
|
+
|
|
292
|
+
### 3. `Map` (`EditableMap`)
|
|
293
|
+
|
|
294
|
+
The **`Map`** component is an intelligent universal Google Maps embedder. It converts any format of Google Maps URL or search query into a high-performance, responsive iframe embed.
|
|
295
|
+
|
|
296
|
+
#### Universal Link Parser Features
|
|
297
|
+
The underlying `parseGoogleMapsEmbedUrl` engine seamlessly processes:
|
|
298
|
+
1. **Full `<iframe>` Snippets**: Extracts the `src` attribute automatically when developers or merchants paste standard Google Maps embed HTML.
|
|
299
|
+
2. **Standard Embed URLs**: Passes through existing `google.com/maps/embed?...` URLs directly.
|
|
300
|
+
3. **Coordinate URLs**: Parses `@lat,lng` patterns (e.g. `google.com/maps/@6.9271,79.8612,15z`).
|
|
301
|
+
4. **Place URLs**: Extracts place names from `/maps/place/Name` paths.
|
|
302
|
+
5. **Search Query URLs**: Parses `?q=...` parameters.
|
|
303
|
+
6. **Short Links**: Handles `maps.app.goo.gl` links with automatic fallback to merchant address.
|
|
304
|
+
7. **Raw Search Strings**: Directly wraps street addresses or city names into valid Google Maps embed queries.
|
|
305
|
+
|
|
306
|
+
#### Props Reference
|
|
307
|
+
|
|
308
|
+
| Prop | Type | Default | Description |
|
|
309
|
+
| :--- | :--- | :--- | :--- |
|
|
310
|
+
| `mapUrl` | `string` | `undefined` | Google Maps URL, share link, coordinate link, or embed code |
|
|
311
|
+
| `address` | `string` | `'Sri Lanka'` | Fallback physical address when `mapUrl` is empty or unresolved |
|
|
312
|
+
| `defaultLocation` | `string` | `undefined` | Additional location fallback |
|
|
313
|
+
| `data-preview-field-path` | `string` | `undefined` | Fivora visual editing binding path |
|
|
314
|
+
| `className` | `string` | `'w-full h-full border-0'` | CSS classes for the iframe element |
|
|
315
|
+
| `title` | `string` | `'Google Map Location'` | Accessibility title for the iframe |
|
|
316
|
+
|
|
317
|
+
#### Usage Example
|
|
318
|
+
|
|
319
|
+
```tsx
|
|
320
|
+
import { Map } from "@deneb-ui/ui";
|
|
321
|
+
|
|
322
|
+
export function ContactMapSection({ mapUrl, address }: { mapUrl?: string; address?: string }) {
|
|
323
|
+
return (
|
|
324
|
+
<div className="w-full h-96 sm:h-[480px] rounded-3xl overflow-hidden shadow-2xl border border-stone-200">
|
|
325
|
+
<Map
|
|
326
|
+
data-preview-field-path="contact.mapUrl"
|
|
327
|
+
mapUrl={mapUrl}
|
|
328
|
+
address={address}
|
|
329
|
+
className="w-full h-full border-0"
|
|
330
|
+
/>
|
|
331
|
+
</div>
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## ARC Engine & Fivora Platform Architecture
|
|
339
|
+
|
|
340
|
+
`@deneb-ui/ui` is built from the ground up for the **ARC Engine** (`@deneb-ui/cli`) and the **Fivora Visual Commerce Platform**.
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
344
|
+
│ Fivora Admin Portal │
|
|
345
|
+
│ (Inspector / Live Studio) │
|
|
346
|
+
└──────────────────────────────┬──────────────────────────────┘
|
|
347
|
+
│ Live PostMessage Protocol
|
|
348
|
+
▼
|
|
349
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
350
|
+
│ Fivora Preview Focus Bridge │
|
|
351
|
+
│ - Focus Target Identification (data-preview-field-path) │
|
|
352
|
+
│ - Fast DOM Mutation & Star Fill Repainting │
|
|
353
|
+
└──────────────────────────────┬──────────────────────────────┘
|
|
354
|
+
│ Instant DOM / Observer Sync
|
|
355
|
+
▼
|
|
356
|
+
┌─────────────────────────────────────────────────────────────┐
|
|
357
|
+
│ DENEB UI │
|
|
358
|
+
│ - GoogleFeedback (MutationObserver Dual-Layer Rating) │
|
|
359
|
+
│ - TestimonialSection (Editorial Critic Cards) │
|
|
360
|
+
│ - EditableMap (Universal URL Parsing Engine) │
|
|
361
|
+
└─────────────────────────────────────────────────────────────┘
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### Dual-Layer Live Rating Architecture
|
|
365
|
+
|
|
366
|
+
When editing ratings in visual site builders, updating an element's text content usually destroys child SVG icons. DENEB UI solves this through a dual-layer architecture:
|
|
367
|
+
|
|
368
|
+
1. **Inspector Target**: A dedicated `<span data-preview-field-path="...">` renders the numeric string (e.g. `"5"`).
|
|
369
|
+
2. **Visual Star Array**: A sibling `<div data-fivora-stars-row="true">` contains the 5 vector SVG stars with fill colors `#fa7014` (gold) and `#dadce0` (grey).
|
|
370
|
+
3. **`MutationObserver`**: An internal observer listens for inspector text mutations on the numeric span and immediately updates the SVG star fills without remounting or re-rendering errors.
|
|
371
|
+
4. **Focus Bridge Support**: The CLI preview bridge recognizes star rating containers and synchronizes fills directly during live keystrokes.
|
|
372
|
+
|
|
373
|
+
### ARC Engine Automated Scaffolding
|
|
374
|
+
|
|
375
|
+
When running the ARC Engine:
|
|
376
|
+
```bash
|
|
377
|
+
npx @deneb-ui/cli arc init
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
The engine automatically:
|
|
381
|
+
- Identifies `GoogleFeedback`, `TestimonialSection`, and `Map` components.
|
|
382
|
+
- Creates semantic field paths (`feedback.feedbacks`, `testimonials.testimonials`, `contact.mapUrl`).
|
|
383
|
+
- Generates typed `editorSchema` sections with proper numeric, image, and textarea descriptors.
|
|
384
|
+
- Adds non-editable internal IDs to `visualEditing.controlOnlyPaths` to guarantee 100% contract compliance with Fivora platform ingestion.
|
|
84
385
|
|
|
85
386
|
---
|
|
86
387
|
|
|
87
|
-
## Related
|
|
388
|
+
## Related Packages
|
|
88
389
|
|
|
89
390
|
| Package | Purpose |
|
|
90
391
|
| :--- | :--- |
|
|
91
|
-
| [`@deneb-ui/
|
|
92
|
-
| [`@deneb-ui/
|
|
392
|
+
| [`@deneb-ui/ui`](https://www.npmjs.com/package/@deneb-ui/ui) | Visual-First React component framework |
|
|
393
|
+
| [`@deneb-ui/cli`](https://www.npmjs.com/package/@deneb-ui/cli) | ARC Engine compiler, AST transformer & template lab |
|
|
394
|
+
| [`@deneb-ui/core`](https://www.npmjs.com/package/@deneb-ui/core) | Shared design tokens, typography registry & contracts |
|
|
395
|
+
| [`@deneb-ui/create-template`](https://www.npmjs.com/package/@deneb-ui/create-template) | Instant storefront generator |
|
|
93
396
|
|
|
94
397
|
---
|
|
95
398
|
|
|
@@ -97,8 +400,8 @@ export default function Page() {
|
|
|
97
400
|
|
|
98
401
|
Created and maintained by **[Chamika Gayashan](https://github.com/chamikathereal)** and **[Induranga Kawishwara](https://github.com/Induranga-kawishwara)**.
|
|
99
402
|
|
|
100
|
-
Part of the **DENEB UI** ecosystem ·
|
|
403
|
+
Part of the **DENEB UI** ecosystem · In collaboration with **[Fivora](https://fivora.site)**.
|
|
101
404
|
|
|
102
405
|
<p align="center">
|
|
103
|
-
<sub>MIT © DENEB UI</sub>
|
|
406
|
+
<sub>MIT © DENEB UI — The Visual-First React Framework</sub>
|
|
104
407
|
</p>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface FeedbackItem {
|
|
3
|
+
id?: string | number;
|
|
4
|
+
name?: string;
|
|
5
|
+
avatar?: string;
|
|
6
|
+
rating?: number | string;
|
|
7
|
+
date?: string;
|
|
8
|
+
comment?: string;
|
|
9
|
+
verified?: boolean;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
export interface EditableGoogleFeedbackProps extends React.HTMLAttributes<HTMLElement> {
|
|
13
|
+
basePath?: string;
|
|
14
|
+
badgeIcon?: string;
|
|
15
|
+
badgeTitle?: string;
|
|
16
|
+
badgeRating?: string | number;
|
|
17
|
+
badgeReviewsCount?: string | number;
|
|
18
|
+
heading?: string;
|
|
19
|
+
subheading?: string;
|
|
20
|
+
feedbacks?: FeedbackItem[];
|
|
21
|
+
maxStars?: number;
|
|
22
|
+
className?: string;
|
|
23
|
+
cardClassName?: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function EditableGoogleFeedback({ basePath, badgeIcon, badgeTitle, badgeRating, badgeReviewsCount, heading, subheading, feedbacks, maxStars, className, cardClassName, style, ...props }: EditableGoogleFeedbackProps): React.JSX.Element;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EditableGoogleFeedback = EditableGoogleFeedback;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const DEFAULT_GOOGLE_ICON = 'https://upload.wikimedia.org/wikipedia/commons/c/c1/Google_%22G%22_logo.svg';
|
|
7
|
+
const DEFAULT_FEEDBACKS = [
|
|
8
|
+
{
|
|
9
|
+
id: 1,
|
|
10
|
+
name: 'Elena Vance',
|
|
11
|
+
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=300&auto=format&fit=crop',
|
|
12
|
+
rating: 5,
|
|
13
|
+
date: '3 days ago',
|
|
14
|
+
comment: 'The Yirgacheffe pour-over is unmatched. You can taste the jasmine and wild bergamot notes immediately. The atmosphere of the salon is pure serenity.',
|
|
15
|
+
verified: true,
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 2,
|
|
19
|
+
name: 'Julian Thorne',
|
|
20
|
+
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?q=80&w=300&auto=format&fit=crop',
|
|
21
|
+
rating: 5,
|
|
22
|
+
date: '1 week ago',
|
|
23
|
+
comment: 'Hands down the best flat white on the island. The milk texture was like velvet and the single-origin espresso cut through with rich cacao sweetness.',
|
|
24
|
+
verified: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
id: 3,
|
|
28
|
+
name: 'Maya Lin',
|
|
29
|
+
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=300&auto=format&fit=crop',
|
|
30
|
+
rating: 5,
|
|
31
|
+
date: '2 weeks ago',
|
|
32
|
+
comment: 'Ordered 2 bags of the cast-iron roasted beans for my home setup. Shipping was lightning-fast and the roast profile was exceptionally dialed in.',
|
|
33
|
+
verified: true,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 4,
|
|
37
|
+
name: 'David Sterling',
|
|
38
|
+
avatar: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?q=80&w=300&auto=format&fit=crop',
|
|
39
|
+
rating: 5,
|
|
40
|
+
date: '3 weeks ago',
|
|
41
|
+
comment: 'The 9-bar manual lever pull is pure art. Wonderful baristas who genuinely know and love their craft. Liceria & Co. has set a whole new standard.',
|
|
42
|
+
verified: true,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: 5,
|
|
46
|
+
name: 'Sophia Aris',
|
|
47
|
+
avatar: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?q=80&w=300&auto=format&fit=crop',
|
|
48
|
+
rating: 5,
|
|
49
|
+
date: '1 month ago',
|
|
50
|
+
comment: 'Attended the private cupping session on Saturday. Educational, welcoming, and deeply flavorful. Easily our favorite coffee destination in the country.',
|
|
51
|
+
verified: true,
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
function GoogleStarSvg({ filled = true, size = 18 }) {
|
|
55
|
+
return ((0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 24 24", style: {
|
|
56
|
+
width: `${size}px`,
|
|
57
|
+
height: `${size}px`,
|
|
58
|
+
fill: filled ? '#fa7014' : '#dadce0',
|
|
59
|
+
color: filled ? '#fa7014' : '#dadce0',
|
|
60
|
+
flexShrink: 0,
|
|
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
|
+
}
|
|
63
|
+
function FeedbackCard({ item, idx, basePath, maxStars, badgeIcon, cardClassName, }) {
|
|
64
|
+
const currentItem = item || {};
|
|
65
|
+
const rawRating = currentItem.rating != null && currentItem.rating !== '' ? currentItem.rating : 5;
|
|
66
|
+
const initialRating = Math.min(Math.max(Math.round(Number(rawRating) || 5), 1), maxStars);
|
|
67
|
+
const [currentRating, setCurrentRating] = (0, react_1.useState)(initialRating);
|
|
68
|
+
const ratingRef = (0, react_1.useRef)(null);
|
|
69
|
+
// Sync state when props change
|
|
70
|
+
(0, react_1.useEffect)(() => {
|
|
71
|
+
setCurrentRating(initialRating);
|
|
72
|
+
}, [initialRating]);
|
|
73
|
+
// Real-time MutationObserver to sync DOM live edits from Fivora inspector instantly
|
|
74
|
+
(0, react_1.useEffect)(() => {
|
|
75
|
+
const el = ratingRef.current;
|
|
76
|
+
if (!el)
|
|
77
|
+
return;
|
|
78
|
+
const parseValue = () => {
|
|
79
|
+
const text = el.textContent?.trim() ?? '';
|
|
80
|
+
if (!text)
|
|
81
|
+
return;
|
|
82
|
+
const num = Number(text);
|
|
83
|
+
if (!Number.isNaN(num) && Number.isFinite(num)) {
|
|
84
|
+
const clamped = Math.min(Math.max(Math.round(num), 1), maxStars);
|
|
85
|
+
setCurrentRating(clamped);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const observer = new MutationObserver(parseValue);
|
|
89
|
+
observer.observe(el, { characterData: true, childList: true, subtree: true });
|
|
90
|
+
return () => observer.disconnect();
|
|
91
|
+
}, [maxStars]);
|
|
92
|
+
const handleFocusRating = () => {
|
|
93
|
+
if (ratingRef.current) {
|
|
94
|
+
ratingRef.current.focus();
|
|
95
|
+
ratingRef.current.click();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const reviewerName = String(currentItem.name || 'Anonymous');
|
|
99
|
+
const reviewerAvatar = String(currentItem.avatar ||
|
|
100
|
+
'https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=300&auto=format&fit=crop');
|
|
101
|
+
const reviewerComment = String(currentItem.comment || '');
|
|
102
|
+
const reviewerDate = String(currentItem.date || 'Recent review');
|
|
103
|
+
return ((0, jsx_runtime_1.jsx)("div", { "data-preview-item-path": `${basePath}.feedbacks[${idx}]`, 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": `${basePath}.feedbacks[${idx}].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": `${basePath}.feedbacks[${idx}].name`, className: "block text-sm font-black text-[#2b170e]", children: reviewerName }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${basePath}.feedbacks[${idx}].date`, className: "block text-xs text-[#8c7a6e]", children: reviewerDate })] })] }), (0, jsx_runtime_1.jsx)("img", { src: badgeIcon || DEFAULT_GOOGLE_ICON, alt: "Google", className: "w-5 h-5 object-contain opacity-90" })] }), (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": `${basePath}.feedbacks[${idx}].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": `${basePath}.feedbacks[${idx}].comment`, className: "text-sm sm:text-base text-[#3d2114] leading-relaxed italic font-normal pt-1", children: ["\u201C", reviewerComment, "\u201D"] })] }) }));
|
|
104
|
+
}
|
|
105
|
+
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
|
+
const items = (0, react_1.useMemo)(() => {
|
|
107
|
+
const list = Array.isArray(feedbacks) ? feedbacks : [];
|
|
108
|
+
if (list.length === 0) {
|
|
109
|
+
return DEFAULT_FEEDBACKS;
|
|
110
|
+
}
|
|
111
|
+
return list.map((item, idx) => {
|
|
112
|
+
const fallback = DEFAULT_FEEDBACKS[idx % DEFAULT_FEEDBACKS.length] || DEFAULT_FEEDBACKS[0];
|
|
113
|
+
if (!item || typeof item !== 'object') {
|
|
114
|
+
return { ...fallback, id: `fallback-${idx}` };
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
...fallback,
|
|
118
|
+
...item,
|
|
119
|
+
id: item.id || `feedback-${idx}`,
|
|
120
|
+
rating: item.rating !== undefined && item.rating !== null ? item.rating : 5,
|
|
121
|
+
};
|
|
122
|
+
});
|
|
123
|
+
}, [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": `${basePath}.badgeIcon`, className: "w-4 h-4 object-contain" }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${basePath}.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": `${basePath}.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": `${basePath}.badgeReviewsCount`, children: badgeReviewsCount }), ")"] })] }), (0, jsx_runtime_1.jsx)("h2", { "data-preview-field-path": `${basePath}.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": `${basePath}.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": `${basePath}.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": `${basePath}.badgeReviewsCount`, className: "text-xs text-[#786154] font-bold", children: badgeReviewsCount })] })] }) })] }), (0, jsx_runtime_1.jsx)("div", { "data-preview-list-path": `${basePath}.feedbacks`, className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6", children: items.map((item, idx) => ((0, jsx_runtime_1.jsx)(FeedbackCard, { item: item, idx: idx, basePath: basePath, maxStars: maxStars, badgeIcon: badgeIcon, cardClassName: cardClassName }, item.id || `feedback-${idx}`))) })] }) }));
|
|
125
|
+
}
|
package/dist/EditableMap.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export interface EditableMapProps extends React.IframeHTMLAttributes<HTMLIFrameE
|
|
|
3
3
|
id?: string;
|
|
4
4
|
'data-preview-field-path'?: string;
|
|
5
5
|
mapUrl?: string;
|
|
6
|
+
address?: string;
|
|
6
7
|
defaultLocation?: string;
|
|
7
8
|
}
|
|
8
|
-
export declare function
|
|
9
|
+
export declare function parseGoogleMapsEmbedUrl(urlOrLocation: string, addressFallback?: string): string;
|
|
10
|
+
export declare function EditableMap({ id, 'data-preview-field-path': previewFieldPath, mapUrl, address, defaultLocation, className, style, ...props }: EditableMapProps): React.JSX.Element;
|
package/dist/EditableMap.js
CHANGED
|
@@ -1,31 +1,92 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseGoogleMapsEmbedUrl = parseGoogleMapsEmbedUrl;
|
|
3
4
|
exports.EditableMap = EditableMap;
|
|
4
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
6
|
const react_1 = require("react");
|
|
6
|
-
function
|
|
7
|
-
|
|
7
|
+
function parseGoogleMapsEmbedUrl(urlOrLocation, addressFallback) {
|
|
8
|
+
let trimmed = (urlOrLocation || '').trim();
|
|
9
|
+
const fallback = (addressFallback || 'Sri Lanka').trim();
|
|
8
10
|
if (!trimmed) {
|
|
9
|
-
return
|
|
11
|
+
return `https://maps.google.com/maps?q=${encodeURIComponent(fallback)}&output=embed`;
|
|
10
12
|
}
|
|
11
|
-
// If
|
|
13
|
+
// 1. If user pasted the full <iframe> embed tag from Google Maps
|
|
14
|
+
if (trimmed.includes('<iframe') && trimmed.includes('src=')) {
|
|
15
|
+
const srcMatch = trimmed.match(/src=["']([^"']+)["']/i);
|
|
16
|
+
if (srcMatch && srcMatch[1]) {
|
|
17
|
+
trimmed = srcMatch[1].trim();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// 2. Direct embed URL
|
|
12
21
|
if (trimmed.includes('google.com/maps/embed') || trimmed.includes('output=embed')) {
|
|
13
22
|
return trimmed;
|
|
14
23
|
}
|
|
15
|
-
//
|
|
16
|
-
const
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
// 3. Extract coordinates: @lat,lng
|
|
25
|
+
const atMatch = trimmed.match(/@(-?\d+\.\d+),(-?\d+\.\d+)/);
|
|
26
|
+
if (atMatch) {
|
|
27
|
+
return `https://maps.google.com/maps?q=${atMatch[1]},${atMatch[2]}&output=embed`;
|
|
28
|
+
}
|
|
29
|
+
// 4. Extract ll=lat,lng
|
|
30
|
+
const llMatch = trimmed.match(/[?&]ll=(-?\d+\.\d+),(-?\d+\.\d+)/);
|
|
31
|
+
if (llMatch) {
|
|
32
|
+
return `https://maps.google.com/maps?q=${llMatch[1]},${llMatch[2]}&output=embed`;
|
|
33
|
+
}
|
|
34
|
+
// 5. Extract place name from /place/Name or /search/Name
|
|
35
|
+
const placeMatch = trimmed.match(/\/maps\/(?:place|search)\/([^/@?#]+)/i);
|
|
36
|
+
if (placeMatch && placeMatch[1]) {
|
|
37
|
+
const decodedPlace = decodeURIComponent(placeMatch[1].replace(/\+/g, ' '));
|
|
38
|
+
return `https://maps.google.com/maps?q=${encodeURIComponent(decodedPlace)}&output=embed`;
|
|
39
|
+
}
|
|
40
|
+
// 6. Extract query param from Google Maps URLs (e.g. ?q=... or ?daddr=...)
|
|
41
|
+
if (trimmed.includes('google.com/maps') || trimmed.includes('maps.google.com')) {
|
|
42
|
+
const qMatch = trimmed.match(/[?&](?:q|daddr|query)=([^&#]+)/i);
|
|
43
|
+
if (qMatch && qMatch[1]) {
|
|
44
|
+
const decodedQ = decodeURIComponent(qMatch[1].replace(/\+/g, ' '));
|
|
45
|
+
return `https://maps.google.com/maps?q=${encodeURIComponent(decodedQ)}&output=embed`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// 7. If it's still a raw URL (like an unresolved shortlink or http link),
|
|
49
|
+
// NEVER pass a raw URL to Google Maps q= parameter, because Google Maps treats
|
|
50
|
+
// it as a KML file, fails, and displays: "Some custom on-map content could not be displayed."
|
|
51
|
+
// Instead, fall back to the address or fallback location.
|
|
52
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
53
|
+
return `https://maps.google.com/maps?q=${encodeURIComponent(fallback)}&output=embed`;
|
|
21
54
|
}
|
|
22
|
-
//
|
|
55
|
+
// 8. Plain text location (e.g. "Kandy", "Colombo", "Kandy, Sri Lanka")
|
|
23
56
|
return `https://maps.google.com/maps?q=${encodeURIComponent(trimmed)}&output=embed`;
|
|
24
57
|
}
|
|
25
|
-
function EditableMap({ id, 'data-preview-field-path': previewFieldPath, mapUrl, defaultLocation = 'Sri Lanka', className = '', style, ...props }) {
|
|
58
|
+
function EditableMap({ id, 'data-preview-field-path': previewFieldPath, mapUrl, address, defaultLocation = 'Sri Lanka', className = '', style, ...props }) {
|
|
26
59
|
const path = previewFieldPath || id;
|
|
60
|
+
const [resolvedUrl, setResolvedUrl] = (0, react_1.useState)(null);
|
|
61
|
+
(0, react_1.useEffect)(() => {
|
|
62
|
+
let active = true;
|
|
63
|
+
const trimmed = (mapUrl || '').trim();
|
|
64
|
+
setResolvedUrl(null);
|
|
65
|
+
// If it's a shortlink, try to unshorten it with an unshortener API (with 3s timeout)
|
|
66
|
+
if (trimmed.includes('goo.gl') || trimmed.includes('maps.app.goo.gl')) {
|
|
67
|
+
const controller = new AbortController();
|
|
68
|
+
const timer = setTimeout(() => controller.abort(), 3000);
|
|
69
|
+
fetch(`https://unshorten.me/json/${encodeURIComponent(trimmed)}`, { signal: controller.signal })
|
|
70
|
+
.then((res) => res.json())
|
|
71
|
+
.then((data) => {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
if (active && data?.success && data?.resolved_url) {
|
|
74
|
+
setResolvedUrl(data.resolved_url);
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
.catch(() => {
|
|
78
|
+
clearTimeout(timer);
|
|
79
|
+
});
|
|
80
|
+
return () => {
|
|
81
|
+
active = false;
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
controller.abort();
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}, [mapUrl]);
|
|
27
87
|
const embedUrl = (0, react_1.useMemo)(() => {
|
|
28
|
-
|
|
29
|
-
|
|
88
|
+
const target = resolvedUrl || mapUrl || '';
|
|
89
|
+
return parseGoogleMapsEmbedUrl(target, address || defaultLocation);
|
|
90
|
+
}, [mapUrl, resolvedUrl, address, defaultLocation]);
|
|
30
91
|
return ((0, jsx_runtime_1.jsx)("iframe", { "data-preview-field-path": path, src: embedUrl, className: `editable-map ${className}`.trim(), style: { border: 0, ...style }, allowFullScreen: false, loading: "lazy", referrerPolicy: "no-referrer-when-downgrade", ...props }));
|
|
31
92
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { TestimonialItem } from './EditableTestimonialCard';
|
|
3
|
+
export interface TestimonialSectionItem extends TestimonialItem {
|
|
4
|
+
tag?: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface EditableTestimonialSectionProps extends React.HTMLAttributes<HTMLElement> {
|
|
8
|
+
basePath?: string;
|
|
9
|
+
badge?: string;
|
|
10
|
+
heading?: string;
|
|
11
|
+
subheading?: string;
|
|
12
|
+
testimonials?: TestimonialSectionItem[];
|
|
13
|
+
maxStars?: number;
|
|
14
|
+
className?: string;
|
|
15
|
+
cardClassName?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function EditableTestimonialSection({ basePath, badge, heading, subheading, testimonials, maxStars, className, cardClassName, style, ...props }: EditableTestimonialSectionProps): React.JSX.Element;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EditableTestimonialSection = EditableTestimonialSection;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
const DEFAULT_TESTIMONIALS = [
|
|
7
|
+
{
|
|
8
|
+
id: 1,
|
|
9
|
+
quote: 'The thermal profile Liceria achieves on their refurbished drum is revelatory. You get bright floral acidity harmonized with unprecedented chocolate depth.',
|
|
10
|
+
author: 'Chef Antoine Laurent',
|
|
11
|
+
role: 'Michelin Star Restaurateur & Sommelier',
|
|
12
|
+
avatar: 'https://images.unsplash.com/photo-1583394838336-acd977736f90?q=80&w=300&auto=format&fit=crop',
|
|
13
|
+
rating: 5,
|
|
14
|
+
tag: 'Culinary Master',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 2,
|
|
18
|
+
quote: 'Manual spring-lever extraction pulled at 93.5°C with mineralized volcanic water. This is not just coffee; this is pure thermodynamic equilibrium.',
|
|
19
|
+
author: 'Clara Sorensen',
|
|
20
|
+
role: 'World Barista Championship Sensory Judge',
|
|
21
|
+
avatar: 'https://images.unsplash.com/photo-1573496359142-b8d87734a5a2?q=80&w=300&auto=format&fit=crop',
|
|
22
|
+
rating: 5,
|
|
23
|
+
tag: 'Sensory Panel',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 3,
|
|
27
|
+
quote: 'The flagship parlor is our neighborhood sanctuary. Whether for a quiet morning espresso or meeting international bean importers, there is nowhere else quite like it.',
|
|
28
|
+
author: 'Maximilian Sterling',
|
|
29
|
+
role: 'Architecture & Design Critic, Urban Loft',
|
|
30
|
+
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?q=80&w=300&auto=format&fit=crop',
|
|
31
|
+
rating: 5,
|
|
32
|
+
tag: 'Design & Culture',
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
function StarSvg({ filled = true, size = 16 }) {
|
|
36
|
+
return ((0, jsx_runtime_1.jsx)("svg", { viewBox: "0 0 24 24", style: {
|
|
37
|
+
width: `${size}px`,
|
|
38
|
+
height: `${size}px`,
|
|
39
|
+
fill: filled ? '#fa7014' : '#dadce0',
|
|
40
|
+
color: filled ? '#fa7014' : '#dadce0',
|
|
41
|
+
flexShrink: 0,
|
|
42
|
+
}, "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" }) }));
|
|
43
|
+
}
|
|
44
|
+
function TestimonialCard({ item, idx, basePath, maxStars, cardClassName, }) {
|
|
45
|
+
const currentItem = item || {};
|
|
46
|
+
const rawRating = currentItem.rating != null && currentItem.rating !== '' ? currentItem.rating : 5;
|
|
47
|
+
const initialRating = Math.min(Math.max(Math.round(Number(rawRating) || 5), 1), maxStars);
|
|
48
|
+
const [currentRating, setCurrentRating] = (0, react_1.useState)(initialRating);
|
|
49
|
+
const ratingRef = (0, react_1.useRef)(null);
|
|
50
|
+
(0, react_1.useEffect)(() => {
|
|
51
|
+
setCurrentRating(initialRating);
|
|
52
|
+
}, [initialRating]);
|
|
53
|
+
(0, react_1.useEffect)(() => {
|
|
54
|
+
const el = ratingRef.current;
|
|
55
|
+
if (!el)
|
|
56
|
+
return;
|
|
57
|
+
const parseValue = () => {
|
|
58
|
+
const text = el.textContent?.trim() ?? '';
|
|
59
|
+
if (!text)
|
|
60
|
+
return;
|
|
61
|
+
const num = Number(text);
|
|
62
|
+
if (!Number.isNaN(num) && Number.isFinite(num)) {
|
|
63
|
+
const clamped = Math.min(Math.max(Math.round(num), 1), maxStars);
|
|
64
|
+
setCurrentRating(clamped);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
const observer = new MutationObserver(parseValue);
|
|
68
|
+
observer.observe(el, { characterData: true, childList: true, subtree: true });
|
|
69
|
+
return () => observer.disconnect();
|
|
70
|
+
}, [maxStars]);
|
|
71
|
+
const handleFocusRating = () => {
|
|
72
|
+
if (ratingRef.current) {
|
|
73
|
+
ratingRef.current.focus();
|
|
74
|
+
ratingRef.current.click();
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
const quoteText = String(currentItem.quote || '');
|
|
78
|
+
const authorName = String(currentItem.author || 'Anonymous');
|
|
79
|
+
const authorRole = String(currentItem.role || '');
|
|
80
|
+
const authorAvatar = String(currentItem.avatar ||
|
|
81
|
+
'https://images.unsplash.com/photo-1583394838336-acd977736f90?q=80&w=300&auto=format&fit=crop');
|
|
82
|
+
const tagText = String(currentItem.tag || '');
|
|
83
|
+
return ((0, jsx_runtime_1.jsxs)("div", { "data-preview-item-path": `${basePath}.testimonials[${idx}]`, className: `relative p-7 sm:p-8 rounded-3xl bg-[#ffffff] border border-[#3d2114]/12 shadow-md hover:shadow-xl transition-all duration-300 flex flex-col justify-between space-y-6 ${cardClassName || ''}`.trim(), children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "flex items-center justify-between gap-2", children: [Boolean(currentItem.tag) && ((0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${basePath}.testimonials[${idx}].tag`, className: "px-3 py-1 rounded-full bg-[#f5ebe1] text-[#84431b] text-[11px] uppercase tracking-wider font-extrabold border border-[#3d2114]/10", children: tagText })), (0, jsx_runtime_1.jsxs)("div", { className: "inline-flex items-center gap-2 p-1 -mr-1 rounded-lg cursor-pointer transition-all hover:bg-[#fa7014]/10 group ml-auto", 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)(StarSvg, { filled: sIdx < currentRating, size: 16 }, sIdx))) }), (0, jsx_runtime_1.jsx)("span", { ref: ratingRef, "data-preview-field-path": `${basePath}.testimonials[${idx}].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)("blockquote", { "data-preview-field-path": `${basePath}.testimonials[${idx}].quote`, className: "font-serif-italic text-base sm:text-lg text-[#2b170e] leading-relaxed", children: ["\u201C", quoteText, "\u201D"] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "pt-4 border-t border-[#3d2114]/10 flex items-center gap-3.5", children: [(0, jsx_runtime_1.jsx)("img", { src: authorAvatar, alt: authorName, "data-preview-field-path": `${basePath}.testimonials[${idx}].avatar`, className: "w-12 h-12 rounded-full object-cover border border-[#3d2114]/15 shadow-sm" }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("h4", { "data-preview-field-path": `${basePath}.testimonials[${idx}].author`, className: "font-heading font-black text-sm sm:text-base text-[#2b170e]", children: authorName }), (0, jsx_runtime_1.jsx)("p", { "data-preview-field-path": `${basePath}.testimonials[${idx}].role`, className: "text-xs text-[#786154] font-medium leading-snug", children: authorRole })] })] })] }));
|
|
84
|
+
}
|
|
85
|
+
function EditableTestimonialSection({ basePath = 'testimonials', badge = 'WORDS OF ACCLAIM', heading = 'WHAT THE CRITICS ARE SAYING', subheading = 'Reflections from international cuppers, culinary masters, and dedicated regulars.', testimonials = DEFAULT_TESTIMONIALS, maxStars = 5, className = '', cardClassName = '', style, ...props }) {
|
|
86
|
+
const items = (0, react_1.useMemo)(() => {
|
|
87
|
+
const list = Array.isArray(testimonials) ? testimonials : [];
|
|
88
|
+
if (list.length === 0) {
|
|
89
|
+
return DEFAULT_TESTIMONIALS;
|
|
90
|
+
}
|
|
91
|
+
return list.map((item, idx) => {
|
|
92
|
+
const fallback = DEFAULT_TESTIMONIALS[idx % DEFAULT_TESTIMONIALS.length] || DEFAULT_TESTIMONIALS[0];
|
|
93
|
+
if (!item || typeof item !== 'object') {
|
|
94
|
+
return { ...fallback, id: `fallback-${idx}` };
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
...fallback,
|
|
98
|
+
...item,
|
|
99
|
+
id: item.id || `testimonial-${idx}`,
|
|
100
|
+
rating: item.rating !== undefined && item.rating !== null ? item.rating : 5,
|
|
101
|
+
};
|
|
102
|
+
});
|
|
103
|
+
}, [testimonials]);
|
|
104
|
+
return ((0, jsx_runtime_1.jsx)("section", { "data-design-section": "testimonials", className: `editable-testimonial-section w-full py-16 sm:py-24 px-4 sm:px-6 lg:px-10 ${className}`.trim(), style: style, ...props, children: (0, jsx_runtime_1.jsxs)("div", { className: "max-w-[1600px] mx-auto", children: [(0, jsx_runtime_1.jsxs)("div", { className: "text-center max-w-3xl mx-auto space-y-3 mb-12 sm:mb-16", children: [badge && ((0, jsx_runtime_1.jsx)("div", { className: "inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-[#ede0d4] text-[#3d2114] text-xs uppercase tracking-widest font-black border border-[#3d2114]/15", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${basePath}.badge`, children: badge }) })), (0, jsx_runtime_1.jsx)("h2", { "data-preview-field-path": `${basePath}.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": `${basePath}.subheading`, className: "text-xs sm:text-base text-[#5c493f] leading-relaxed", children: subheading })] }), (0, jsx_runtime_1.jsx)("div", { "data-preview-list-path": `${basePath}.testimonials`, className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 sm:gap-8", children: items.map((item, idx) => ((0, jsx_runtime_1.jsx)(TestimonialCard, { item: item, idx: idx, basePath: basePath, maxStars: maxStars, cardClassName: cardClassName }, item.id || `testimonial-${idx}`))) })] }) }));
|
|
105
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,10 +14,12 @@ export * from './EditableProductCard';
|
|
|
14
14
|
export * from './EditableProductGrid';
|
|
15
15
|
export * from './EditableProductDetail';
|
|
16
16
|
export * from './EditableCustomerReviews';
|
|
17
|
+
export * from './EditableGoogleFeedback';
|
|
17
18
|
export * from './EditableServiceCard';
|
|
18
19
|
export * from './EditableCard';
|
|
19
20
|
export * from './EditablePricingCard';
|
|
20
21
|
export * from './EditableTestimonialCard';
|
|
22
|
+
export * from './EditableTestimonialSection';
|
|
21
23
|
export * from './EditableFAQAccordion';
|
|
22
24
|
export * from './EditableContactForm';
|
|
23
25
|
export * from './EditableNavbar';
|
|
@@ -66,9 +68,11 @@ export { EditableProductCard as ProductCard } from './EditableProductCard';
|
|
|
66
68
|
export { EditableProductGrid as ProductGrid } from './EditableProductGrid';
|
|
67
69
|
export { EditableProductDetail as ProductDetail } from './EditableProductDetail';
|
|
68
70
|
export { EditableCustomerReviews as CustomerReviews } from './EditableCustomerReviews';
|
|
71
|
+
export { EditableGoogleFeedback as GoogleFeedback } from './EditableGoogleFeedback';
|
|
69
72
|
export { EditableServiceCard as ServiceCard } from './EditableServiceCard';
|
|
70
73
|
export { EditablePricingCard as PricingCard } from './EditablePricingCard';
|
|
71
74
|
export { EditableTestimonialCard as TestimonialCard } from './EditableTestimonialCard';
|
|
75
|
+
export { EditableTestimonialSection as TestimonialSection, EditableTestimonialSection as Testimonials } from './EditableTestimonialSection';
|
|
72
76
|
export { EditableFAQAccordion as Accordion, EditableFAQAccordion as FAQ } from './EditableFAQAccordion';
|
|
73
77
|
export { EditableContactForm as ContactForm } from './EditableContactForm';
|
|
74
78
|
export { EditableNavbar as Navbar, EditableNavbar as Header } from './EditableNavbar';
|
package/dist/index.js
CHANGED
|
@@ -18,8 +18,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
18
18
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.
|
|
22
|
-
exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.FilterSidebar = exports.CartDrawer = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = exports.Hero = void 0;
|
|
21
|
+
exports.ContactForm = exports.FAQ = exports.Accordion = exports.Testimonials = exports.TestimonialSection = exports.TestimonialCard = exports.PricingCard = exports.ServiceCard = exports.GoogleFeedback = exports.CustomerReviews = exports.ProductDetail = exports.ProductGrid = exports.ProductCard = exports.List = exports.Box = exports.Section = exports.Grid = exports.Map = exports.Image = exports.Quote = exports.Badge = exports.Paragraph = exports.Heading = exports.Text = exports.Dialog = exports.Card = exports.Button = exports.collectStyleTargetsFromHtml = exports.validateStyleTree = exports.styleToCssVariables = exports.patchStyleByPath = exports.patchElementStyle = exports.STYLE_TYPE_ATTRIBUTE = exports.STYLE_TARGET_ATTRIBUTE = exports.DENEB_STYLE_PATCH_MESSAGE = exports.STYLE_PATCH_MESSAGE = exports.formatResponsiveFontSize = exports.resolveInstallableFont = exports.normalizeFontId = exports.lookupFontDefinition = exports.listFontsByCategory = exports.collectFontIdsFromSiteData = exports.buildGoogleFontsStylesheetUrl = exports.DENEB_GOOGLE_FONT_COUNT = exports.DENEB_FONT_REGISTRY = exports.useDenebFonts = exports.DENEB_FONTS_LINK_ID = exports.FontLoader = exports.DenebComponentStyles = exports.ResponsiveBaseStyles = void 0;
|
|
22
|
+
exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.FilterSidebar = exports.CartDrawer = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = exports.Hero = exports.Footer = exports.Header = exports.Navbar = void 0;
|
|
23
23
|
__exportStar(require("./PreviewField"), exports);
|
|
24
24
|
__exportStar(require("./EditableText"), exports);
|
|
25
25
|
__exportStar(require("./EditableImage"), exports);
|
|
@@ -32,10 +32,12 @@ __exportStar(require("./EditableProductCard"), exports);
|
|
|
32
32
|
__exportStar(require("./EditableProductGrid"), exports);
|
|
33
33
|
__exportStar(require("./EditableProductDetail"), exports);
|
|
34
34
|
__exportStar(require("./EditableCustomerReviews"), exports);
|
|
35
|
+
__exportStar(require("./EditableGoogleFeedback"), exports);
|
|
35
36
|
__exportStar(require("./EditableServiceCard"), exports);
|
|
36
37
|
__exportStar(require("./EditableCard"), exports);
|
|
37
38
|
__exportStar(require("./EditablePricingCard"), exports);
|
|
38
39
|
__exportStar(require("./EditableTestimonialCard"), exports);
|
|
40
|
+
__exportStar(require("./EditableTestimonialSection"), exports);
|
|
39
41
|
__exportStar(require("./EditableFAQAccordion"), exports);
|
|
40
42
|
__exportStar(require("./EditableContactForm"), exports);
|
|
41
43
|
__exportStar(require("./EditableNavbar"), exports);
|
|
@@ -127,12 +129,17 @@ var EditableProductDetail_1 = require("./EditableProductDetail");
|
|
|
127
129
|
Object.defineProperty(exports, "ProductDetail", { enumerable: true, get: function () { return EditableProductDetail_1.EditableProductDetail; } });
|
|
128
130
|
var EditableCustomerReviews_1 = require("./EditableCustomerReviews");
|
|
129
131
|
Object.defineProperty(exports, "CustomerReviews", { enumerable: true, get: function () { return EditableCustomerReviews_1.EditableCustomerReviews; } });
|
|
132
|
+
var EditableGoogleFeedback_1 = require("./EditableGoogleFeedback");
|
|
133
|
+
Object.defineProperty(exports, "GoogleFeedback", { enumerable: true, get: function () { return EditableGoogleFeedback_1.EditableGoogleFeedback; } });
|
|
130
134
|
var EditableServiceCard_1 = require("./EditableServiceCard");
|
|
131
135
|
Object.defineProperty(exports, "ServiceCard", { enumerable: true, get: function () { return EditableServiceCard_1.EditableServiceCard; } });
|
|
132
136
|
var EditablePricingCard_1 = require("./EditablePricingCard");
|
|
133
137
|
Object.defineProperty(exports, "PricingCard", { enumerable: true, get: function () { return EditablePricingCard_1.EditablePricingCard; } });
|
|
134
138
|
var EditableTestimonialCard_1 = require("./EditableTestimonialCard");
|
|
135
139
|
Object.defineProperty(exports, "TestimonialCard", { enumerable: true, get: function () { return EditableTestimonialCard_1.EditableTestimonialCard; } });
|
|
140
|
+
var EditableTestimonialSection_1 = require("./EditableTestimonialSection");
|
|
141
|
+
Object.defineProperty(exports, "TestimonialSection", { enumerable: true, get: function () { return EditableTestimonialSection_1.EditableTestimonialSection; } });
|
|
142
|
+
Object.defineProperty(exports, "Testimonials", { enumerable: true, get: function () { return EditableTestimonialSection_1.EditableTestimonialSection; } });
|
|
136
143
|
var EditableFAQAccordion_1 = require("./EditableFAQAccordion");
|
|
137
144
|
Object.defineProperty(exports, "Accordion", { enumerable: true, get: function () { return EditableFAQAccordion_1.EditableFAQAccordion; } });
|
|
138
145
|
Object.defineProperty(exports, "FAQ", { enumerable: true, get: function () { return EditableFAQAccordion_1.EditableFAQAccordion; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.37",
|
|
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.37"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": "^18.0.0 || ^19.0.0",
|