@deneb-ui/ui 2.0.70 → 2.0.72
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/EditableBeforeAfterSlider.d.ts +16 -0
- package/dist/EditableBeforeAfterSlider.js +137 -0
- package/dist/EditableBookingModal.d.ts +29 -0
- package/dist/EditableBookingModal.js +198 -0
- package/dist/EditableTestimonialCarousel.d.ts +22 -0
- package/dist/EditableTestimonialCarousel.js +185 -0
- package/dist/PlatformProductDetail.js +1 -1
- package/dist/SiteDataProvider.d.ts +3 -2
- package/dist/SiteDataProvider.js +14 -3
- package/dist/ThemeStyles.d.ts +10 -1
- package/dist/ThemeStyles.js +50 -6
- package/dist/ThemeToggle.d.ts +11 -0
- package/dist/ThemeToggle.js +51 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +11 -1
- package/dist/location/MapLink.js +1 -1
- package/package.json +2 -2
- package/platform-contract.json +4 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface EditableBeforeAfterSliderProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
3
|
+
beforeImageId?: string;
|
|
4
|
+
afterImageId?: string;
|
|
5
|
+
defaultBeforeImage?: string;
|
|
6
|
+
defaultAfterImage?: string;
|
|
7
|
+
beforeLabelId?: string;
|
|
8
|
+
defaultBeforeLabel?: string;
|
|
9
|
+
afterLabelId?: string;
|
|
10
|
+
defaultAfterLabel?: string;
|
|
11
|
+
initialPosition?: number;
|
|
12
|
+
aspectRatio?: string;
|
|
13
|
+
handleColor?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function EditableBeforeAfterSlider({ beforeImageId, afterImageId, defaultBeforeImage, defaultAfterImage, beforeLabelId, defaultBeforeLabel, afterLabelId, defaultAfterLabel, initialPosition, aspectRatio, handleColor, className, style, ...props }: EditableBeforeAfterSliderProps): React.JSX.Element;
|
|
16
|
+
export declare const BeforeAfterSlider: typeof EditableBeforeAfterSlider;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.BeforeAfterSlider = void 0;
|
|
5
|
+
exports.EditableBeforeAfterSlider = EditableBeforeAfterSlider;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const EditableImage_1 = require("./EditableImage");
|
|
9
|
+
const EditableText_1 = require("./EditableText");
|
|
10
|
+
function EditableBeforeAfterSlider({ beforeImageId = 'home.showcase.beforeImage', afterImageId = 'home.showcase.afterImage', defaultBeforeImage = 'https://images.unsplash.com/photo-1560066984-138dadb4c035?auto=format&fit=crop&w=1200&q=80', defaultAfterImage = 'https://images.unsplash.com/photo-1522337360788-8b13dee7a37e?auto=format&fit=crop&w=1200&q=80', beforeLabelId = 'home.showcase.beforeLabel', defaultBeforeLabel = 'Before', afterLabelId = 'home.showcase.afterLabel', defaultAfterLabel = 'After', initialPosition = 50, aspectRatio = '16 / 9', handleColor = '#ffffff', className = '', style, ...props }) {
|
|
11
|
+
const [sliderPos, setSliderPos] = (0, react_1.useState)(initialPosition);
|
|
12
|
+
const [isDragging, setIsDragging] = (0, react_1.useState)(false);
|
|
13
|
+
const containerRef = (0, react_1.useRef)(null);
|
|
14
|
+
const updatePosition = (0, react_1.useCallback)((clientX) => {
|
|
15
|
+
if (!containerRef.current)
|
|
16
|
+
return;
|
|
17
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
18
|
+
const x = clientX - rect.left;
|
|
19
|
+
const percentage = Math.max(0, Math.min(100, (x / rect.width) * 100));
|
|
20
|
+
setSliderPos(percentage);
|
|
21
|
+
}, []);
|
|
22
|
+
const handleTouchMove = (0, react_1.useCallback)((e) => {
|
|
23
|
+
if (!isDragging || !e.touches[0])
|
|
24
|
+
return;
|
|
25
|
+
updatePosition(e.touches[0].clientX);
|
|
26
|
+
}, [isDragging, updatePosition]);
|
|
27
|
+
const handleMouseMove = (0, react_1.useCallback)((e) => {
|
|
28
|
+
if (!isDragging)
|
|
29
|
+
return;
|
|
30
|
+
updatePosition(e.clientX);
|
|
31
|
+
}, [isDragging, updatePosition]);
|
|
32
|
+
const handleMouseUp = (0, react_1.useCallback)(() => {
|
|
33
|
+
setIsDragging(false);
|
|
34
|
+
}, []);
|
|
35
|
+
(0, react_1.useEffect)(() => {
|
|
36
|
+
if (isDragging) {
|
|
37
|
+
window.addEventListener('mousemove', handleMouseMove);
|
|
38
|
+
window.addEventListener('mouseup', handleMouseUp);
|
|
39
|
+
window.addEventListener('touchmove', handleTouchMove);
|
|
40
|
+
window.addEventListener('touchend', handleMouseUp);
|
|
41
|
+
}
|
|
42
|
+
return () => {
|
|
43
|
+
window.removeEventListener('mousemove', handleMouseMove);
|
|
44
|
+
window.removeEventListener('mouseup', handleMouseUp);
|
|
45
|
+
window.removeEventListener('touchmove', handleTouchMove);
|
|
46
|
+
window.removeEventListener('touchend', handleMouseUp);
|
|
47
|
+
};
|
|
48
|
+
}, [isDragging, handleMouseMove, handleMouseUp, handleTouchMove]);
|
|
49
|
+
const handleKeyDown = (e) => {
|
|
50
|
+
if (e.key === 'ArrowLeft') {
|
|
51
|
+
setSliderPos((prev) => Math.max(0, prev - 5));
|
|
52
|
+
}
|
|
53
|
+
else if (e.key === 'ArrowRight') {
|
|
54
|
+
setSliderPos((prev) => Math.min(100, prev + 5));
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
return ((0, jsx_runtime_1.jsxs)("div", { ref: containerRef, tabIndex: 0, onKeyDown: handleKeyDown, className: `editable-before-after-slider relative overflow-hidden select-none cursor-ew-resize rounded-2xl shadow-xl ${className}`.trim(), style: {
|
|
58
|
+
position: 'relative',
|
|
59
|
+
width: '100%',
|
|
60
|
+
aspectRatio,
|
|
61
|
+
userSelect: 'none',
|
|
62
|
+
overflow: 'hidden',
|
|
63
|
+
outline: 'none',
|
|
64
|
+
...style,
|
|
65
|
+
}, onMouseDown: (e) => {
|
|
66
|
+
setIsDragging(true);
|
|
67
|
+
updatePosition(e.clientX);
|
|
68
|
+
}, onTouchStart: (e) => {
|
|
69
|
+
if (e.touches[0]) {
|
|
70
|
+
setIsDragging(true);
|
|
71
|
+
updatePosition(e.touches[0].clientX);
|
|
72
|
+
}
|
|
73
|
+
}, ...props, children: [(0, jsx_runtime_1.jsxs)("div", { style: { position: 'absolute', inset: 0, width: '100%', height: '100%' }, children: [(0, jsx_runtime_1.jsx)(EditableImage_1.EditableImage, { id: afterImageId, src: defaultAfterImage, alt: "After transformation", style: { width: '100%', height: '100%', objectFit: 'cover' } }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
74
|
+
position: 'absolute',
|
|
75
|
+
bottom: '1rem',
|
|
76
|
+
right: '1rem',
|
|
77
|
+
backgroundColor: 'rgba(0, 0, 0, 0.65)',
|
|
78
|
+
backdropFilter: 'blur(8px)',
|
|
79
|
+
color: '#ffffff',
|
|
80
|
+
padding: '0.25rem 0.75rem',
|
|
81
|
+
borderRadius: '9999px',
|
|
82
|
+
fontSize: '0.75rem',
|
|
83
|
+
fontWeight: 600,
|
|
84
|
+
textTransform: 'uppercase',
|
|
85
|
+
letterSpacing: '0.05em',
|
|
86
|
+
pointerEvents: 'auto',
|
|
87
|
+
}, children: (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: afterLabelId, defaultValue: defaultAfterLabel, as: "span" }) })] }), (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
88
|
+
position: 'absolute',
|
|
89
|
+
top: 0,
|
|
90
|
+
left: 0,
|
|
91
|
+
bottom: 0,
|
|
92
|
+
width: `${sliderPos}%`,
|
|
93
|
+
overflow: 'hidden',
|
|
94
|
+
borderRight: `2px solid ${handleColor}`,
|
|
95
|
+
}, children: [(0, jsx_runtime_1.jsx)("div", { style: {
|
|
96
|
+
position: 'absolute',
|
|
97
|
+
top: 0,
|
|
98
|
+
left: 0,
|
|
99
|
+
width: containerRef.current ? `${containerRef.current.clientWidth}px` : '100%',
|
|
100
|
+
height: '100%',
|
|
101
|
+
}, children: (0, jsx_runtime_1.jsx)(EditableImage_1.EditableImage, { id: beforeImageId, src: defaultBeforeImage, alt: "Before transformation", style: { width: '100%', height: '100%', objectFit: 'cover' } }) }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
102
|
+
position: 'absolute',
|
|
103
|
+
bottom: '1rem',
|
|
104
|
+
left: '1rem',
|
|
105
|
+
backgroundColor: 'rgba(0, 0, 0, 0.65)',
|
|
106
|
+
backdropFilter: 'blur(8px)',
|
|
107
|
+
color: '#ffffff',
|
|
108
|
+
padding: '0.25rem 0.75rem',
|
|
109
|
+
borderRadius: '9999px',
|
|
110
|
+
fontSize: '0.75rem',
|
|
111
|
+
fontWeight: 600,
|
|
112
|
+
textTransform: 'uppercase',
|
|
113
|
+
letterSpacing: '0.05em',
|
|
114
|
+
pointerEvents: 'auto',
|
|
115
|
+
}, children: (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: beforeLabelId, defaultValue: defaultBeforeLabel, as: "span" }) })] }), (0, jsx_runtime_1.jsx)("div", { style: {
|
|
116
|
+
position: 'absolute',
|
|
117
|
+
top: 0,
|
|
118
|
+
bottom: 0,
|
|
119
|
+
left: `${sliderPos}%`,
|
|
120
|
+
transform: 'translateX(-50%)',
|
|
121
|
+
display: 'flex',
|
|
122
|
+
alignItems: 'center',
|
|
123
|
+
justifyContent: 'center',
|
|
124
|
+
pointerEvents: 'none',
|
|
125
|
+
}, children: (0, jsx_runtime_1.jsx)("div", { style: {
|
|
126
|
+
width: '40px',
|
|
127
|
+
height: '40px',
|
|
128
|
+
backgroundColor: handleColor,
|
|
129
|
+
borderRadius: '9999px',
|
|
130
|
+
boxShadow: '0 4px 14px 0 rgba(0, 0, 0, 0.35)',
|
|
131
|
+
display: 'flex',
|
|
132
|
+
alignItems: 'center',
|
|
133
|
+
justifyContent: 'center',
|
|
134
|
+
cursor: 'ew-resize',
|
|
135
|
+
}, children: (0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "#1e293b", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", "data-preview-static": "slider-arrows", children: [(0, jsx_runtime_1.jsx)("polyline", { points: "15 18 9 12 15 6" }), (0, jsx_runtime_1.jsx)("polyline", { points: "9 18 3 12 9 6" })] }) }) })] }));
|
|
136
|
+
}
|
|
137
|
+
exports.BeforeAfterSlider = EditableBeforeAfterSlider;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ServiceOption {
|
|
3
|
+
id: string | number;
|
|
4
|
+
name: string;
|
|
5
|
+
duration?: string;
|
|
6
|
+
price?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface EditableBookingModalProps {
|
|
9
|
+
isOpen: boolean;
|
|
10
|
+
onClose: () => void;
|
|
11
|
+
titleId?: string;
|
|
12
|
+
defaultTitle?: string;
|
|
13
|
+
subtitleId?: string;
|
|
14
|
+
defaultSubtitle?: string;
|
|
15
|
+
buttonLabelId?: string;
|
|
16
|
+
defaultButtonLabel?: string;
|
|
17
|
+
services?: ServiceOption[];
|
|
18
|
+
whatsappNumber?: string;
|
|
19
|
+
onSubmitBooking?: (details: {
|
|
20
|
+
service: string;
|
|
21
|
+
date: string;
|
|
22
|
+
time: string;
|
|
23
|
+
name: string;
|
|
24
|
+
phone: string;
|
|
25
|
+
notes?: string;
|
|
26
|
+
}) => void;
|
|
27
|
+
}
|
|
28
|
+
export declare function EditableBookingModal({ isOpen, onClose, titleId, defaultTitle, subtitleId, defaultSubtitle, buttonLabelId, defaultButtonLabel, services, whatsappNumber, onSubmitBooking, }: EditableBookingModalProps): React.JSX.Element | null;
|
|
29
|
+
export declare const BookingModal: typeof EditableBookingModal;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.BookingModal = void 0;
|
|
5
|
+
exports.EditableBookingModal = EditableBookingModal;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const EditableText_1 = require("./EditableText");
|
|
9
|
+
const SiteDataProvider_1 = require("./SiteDataProvider");
|
|
10
|
+
function EditableBookingModal({ isOpen, onClose, titleId = 'common.booking.title', defaultTitle = 'Book Your Appointment', subtitleId = 'common.booking.subtitle', defaultSubtitle = 'Select your desired service, date, and time. We will confirm instantly via WhatsApp.', buttonLabelId = 'common.booking.buttonLabel', defaultButtonLabel = 'Confirm & Book via WhatsApp', services = [
|
|
11
|
+
{ id: '1', name: 'Signature Haircut & Style', duration: '45 mins', price: '$65' },
|
|
12
|
+
{ id: '2', name: 'Bespoke Balayage & Gloss', duration: '120 mins', price: '$180' },
|
|
13
|
+
{ id: '3', name: 'Scalp Detox & Deep Conditioning', duration: '40 mins', price: '$55' },
|
|
14
|
+
{ id: '4', name: 'Executive Beard Sculpt & Hot Towel', duration: '30 mins', price: '$40' },
|
|
15
|
+
], whatsappNumber, onSubmitBooking, }) {
|
|
16
|
+
const siteData = (0, SiteDataProvider_1.useSiteData)();
|
|
17
|
+
const rawBusiness = siteData?.content?.common?.business;
|
|
18
|
+
const targetWhatsapp = whatsappNumber || rawBusiness?.whatsapp || rawBusiness?.phone || '1234567890';
|
|
19
|
+
const [selectedService, setSelectedService] = (0, react_1.useState)(services[0]?.name || '');
|
|
20
|
+
const [selectedDate, setSelectedDate] = (0, react_1.useState)('');
|
|
21
|
+
const [selectedTime, setSelectedTime] = (0, react_1.useState)('10:00 AM');
|
|
22
|
+
const [customerName, setCustomerName] = (0, react_1.useState)('');
|
|
23
|
+
const [customerPhone, setCustomerPhone] = (0, react_1.useState)('');
|
|
24
|
+
const [customerNotes, setCustomerNotes] = (0, react_1.useState)('');
|
|
25
|
+
const timeSlots = [
|
|
26
|
+
'09:00 AM',
|
|
27
|
+
'10:00 AM',
|
|
28
|
+
'11:30 AM',
|
|
29
|
+
'01:00 PM',
|
|
30
|
+
'02:30 PM',
|
|
31
|
+
'04:00 PM',
|
|
32
|
+
'05:30 PM',
|
|
33
|
+
];
|
|
34
|
+
const handleKeyDown = (0, react_1.useCallback)((e) => {
|
|
35
|
+
if (e.key === 'Escape' && isOpen) {
|
|
36
|
+
onClose();
|
|
37
|
+
}
|
|
38
|
+
}, [isOpen, onClose]);
|
|
39
|
+
(0, react_1.useEffect)(() => {
|
|
40
|
+
if (isOpen) {
|
|
41
|
+
document.body.style.overflow = 'hidden';
|
|
42
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
document.body.style.overflow = '';
|
|
46
|
+
}
|
|
47
|
+
return () => {
|
|
48
|
+
document.body.style.overflow = '';
|
|
49
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
50
|
+
};
|
|
51
|
+
}, [isOpen, handleKeyDown]);
|
|
52
|
+
if (!isOpen)
|
|
53
|
+
return null;
|
|
54
|
+
const handleSubmit = (e) => {
|
|
55
|
+
e.preventDefault();
|
|
56
|
+
if (onSubmitBooking) {
|
|
57
|
+
onSubmitBooking({
|
|
58
|
+
service: selectedService,
|
|
59
|
+
date: selectedDate,
|
|
60
|
+
time: selectedTime,
|
|
61
|
+
name: customerName,
|
|
62
|
+
phone: customerPhone,
|
|
63
|
+
notes: customerNotes,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const cleanNumber = String(targetWhatsapp).replace(/[^0-9]/g, '');
|
|
67
|
+
const message = encodeURIComponent(`Hello! I would like to book an appointment:\n\n` +
|
|
68
|
+
`• Service: ${selectedService}\n` +
|
|
69
|
+
`• Date: ${selectedDate || 'Upcoming Available Date'}\n` +
|
|
70
|
+
`• Time: ${selectedTime}\n` +
|
|
71
|
+
`• Name: ${customerName}\n` +
|
|
72
|
+
`• Phone: ${customerPhone}\n` +
|
|
73
|
+
(customerNotes ? `• Notes: ${customerNotes}\n` : ''));
|
|
74
|
+
window.open(`https://wa.me/${cleanNumber}?text=${message}`, '_blank', 'noopener,noreferrer');
|
|
75
|
+
onClose();
|
|
76
|
+
};
|
|
77
|
+
return ((0, jsx_runtime_1.jsx)("div", { role: "dialog", "aria-modal": "true", "aria-labelledby": "booking-modal-title", style: {
|
|
78
|
+
position: 'fixed',
|
|
79
|
+
inset: 0,
|
|
80
|
+
zIndex: 9999,
|
|
81
|
+
display: 'flex',
|
|
82
|
+
alignItems: 'center',
|
|
83
|
+
justifyContent: 'center',
|
|
84
|
+
padding: '1rem',
|
|
85
|
+
backgroundColor: 'rgba(0, 0, 0, 0.7)',
|
|
86
|
+
backdropFilter: 'blur(6px)',
|
|
87
|
+
}, onClick: (e) => {
|
|
88
|
+
if (e.target === e.currentTarget)
|
|
89
|
+
onClose();
|
|
90
|
+
}, children: (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
91
|
+
backgroundColor: 'var(--color-surface, #ffffff)',
|
|
92
|
+
color: 'var(--color-text, #1e293b)',
|
|
93
|
+
borderRadius: '24px',
|
|
94
|
+
width: '100%',
|
|
95
|
+
maxWidth: '560px',
|
|
96
|
+
maxHeight: '90vh',
|
|
97
|
+
overflowY: 'auto',
|
|
98
|
+
boxShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
|
|
99
|
+
padding: '2rem',
|
|
100
|
+
position: 'relative',
|
|
101
|
+
border: '1px solid var(--color-border, #e2e8f0)',
|
|
102
|
+
}, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: onClose, "aria-label": "Close booking modal", "data-preview-static": "modal-close-button", style: {
|
|
103
|
+
position: 'absolute',
|
|
104
|
+
top: '1.25rem',
|
|
105
|
+
right: '1.25rem',
|
|
106
|
+
background: 'none',
|
|
107
|
+
border: 'none',
|
|
108
|
+
cursor: 'pointer',
|
|
109
|
+
padding: '0.5rem',
|
|
110
|
+
color: '#64748b',
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
justifyContent: 'center',
|
|
114
|
+
borderRadius: '9999px',
|
|
115
|
+
}, children: (0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [(0, jsx_runtime_1.jsx)("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), (0, jsx_runtime_1.jsx)("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }) }), (0, jsx_runtime_1.jsxs)("div", { style: { marginBottom: '1.5rem', paddingRight: '2rem' }, children: [(0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: titleId, defaultValue: defaultTitle, as: "h2", style: {
|
|
116
|
+
fontSize: '1.5rem',
|
|
117
|
+
fontWeight: 700,
|
|
118
|
+
letterSpacing: '-0.025em',
|
|
119
|
+
marginBottom: '0.5rem',
|
|
120
|
+
color: 'var(--color-text, #0f172a)',
|
|
121
|
+
} }), (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: subtitleId, defaultValue: defaultSubtitle, as: "p", style: {
|
|
122
|
+
fontSize: '0.875rem',
|
|
123
|
+
color: '#64748b',
|
|
124
|
+
lineHeight: 1.5,
|
|
125
|
+
} })] }), (0, jsx_runtime_1.jsxs)("form", { onSubmit: handleSubmit, style: { display: 'flex', flexDirection: 'column', gap: '1.25rem' }, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { "data-preview-static": "service-label", style: { display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }, children: "Select Service" }), (0, jsx_runtime_1.jsx)("select", { value: selectedService, onChange: (e) => setSelectedService(e.target.value), style: {
|
|
126
|
+
width: '100%',
|
|
127
|
+
padding: '0.75rem 1rem',
|
|
128
|
+
borderRadius: '12px',
|
|
129
|
+
border: '1px solid #cbd5e1',
|
|
130
|
+
fontSize: '0.95rem',
|
|
131
|
+
backgroundColor: '#f8fafc',
|
|
132
|
+
color: '#0f172a',
|
|
133
|
+
outline: 'none',
|
|
134
|
+
}, children: services.map((svc) => ((0, jsx_runtime_1.jsxs)("option", { value: svc.name, children: [svc.name, " ", svc.duration ? `(${svc.duration})` : '', " ", svc.price ? `— ${svc.price}` : ''] }, svc.id))) })] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { "data-preview-static": "date-label", style: { display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }, children: "Date" }), (0, jsx_runtime_1.jsx)("input", { type: "date", required: true, value: selectedDate, onChange: (e) => setSelectedDate(e.target.value), style: {
|
|
135
|
+
width: '100%',
|
|
136
|
+
padding: '0.75rem 1rem',
|
|
137
|
+
borderRadius: '12px',
|
|
138
|
+
border: '1px solid #cbd5e1',
|
|
139
|
+
fontSize: '0.95rem',
|
|
140
|
+
backgroundColor: '#f8fafc',
|
|
141
|
+
color: '#0f172a',
|
|
142
|
+
outline: 'none',
|
|
143
|
+
} })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { "data-preview-static": "time-label", style: { display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }, children: "Preferred Time" }), (0, jsx_runtime_1.jsx)("select", { value: selectedTime, onChange: (e) => setSelectedTime(e.target.value), style: {
|
|
144
|
+
width: '100%',
|
|
145
|
+
padding: '0.75rem 1rem',
|
|
146
|
+
borderRadius: '12px',
|
|
147
|
+
border: '1px solid #cbd5e1',
|
|
148
|
+
fontSize: '0.95rem',
|
|
149
|
+
backgroundColor: '#f8fafc',
|
|
150
|
+
color: '#0f172a',
|
|
151
|
+
outline: 'none',
|
|
152
|
+
}, children: timeSlots.map((time) => ((0, jsx_runtime_1.jsx)("option", { value: time, children: time }, time))) })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '1rem' }, children: [(0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { "data-preview-static": "name-label", style: { display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }, children: "Full Name" }), (0, jsx_runtime_1.jsx)("input", { type: "text", required: true, placeholder: "Jane Doe", value: customerName, onChange: (e) => setCustomerName(e.target.value), style: {
|
|
153
|
+
width: '100%',
|
|
154
|
+
padding: '0.75rem 1rem',
|
|
155
|
+
borderRadius: '12px',
|
|
156
|
+
border: '1px solid #cbd5e1',
|
|
157
|
+
fontSize: '0.95rem',
|
|
158
|
+
backgroundColor: '#f8fafc',
|
|
159
|
+
color: '#0f172a',
|
|
160
|
+
outline: 'none',
|
|
161
|
+
} })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { "data-preview-static": "phone-label", style: { display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }, children: "Phone / WhatsApp" }), (0, jsx_runtime_1.jsx)("input", { type: "tel", required: true, placeholder: "+1 (555) 000-0000", value: customerPhone, onChange: (e) => setCustomerPhone(e.target.value), style: {
|
|
162
|
+
width: '100%',
|
|
163
|
+
padding: '0.75rem 1rem',
|
|
164
|
+
borderRadius: '12px',
|
|
165
|
+
border: '1px solid #cbd5e1',
|
|
166
|
+
fontSize: '0.95rem',
|
|
167
|
+
backgroundColor: '#f8fafc',
|
|
168
|
+
color: '#0f172a',
|
|
169
|
+
outline: 'none',
|
|
170
|
+
} })] })] }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("label", { "data-preview-static": "notes-label", style: { display: 'block', fontSize: '0.875rem', fontWeight: 600, marginBottom: '0.5rem' }, children: "Special Notes / Requests (Optional)" }), (0, jsx_runtime_1.jsx)("textarea", { rows: 2, placeholder: "Any hair preferences, color history, or styling goals...", value: customerNotes, onChange: (e) => setCustomerNotes(e.target.value), style: {
|
|
171
|
+
width: '100%',
|
|
172
|
+
padding: '0.75rem 1rem',
|
|
173
|
+
borderRadius: '12px',
|
|
174
|
+
border: '1px solid #cbd5e1',
|
|
175
|
+
fontSize: '0.95rem',
|
|
176
|
+
backgroundColor: '#f8fafc',
|
|
177
|
+
color: '#0f172a',
|
|
178
|
+
outline: 'none',
|
|
179
|
+
resize: 'vertical',
|
|
180
|
+
} })] }), (0, jsx_runtime_1.jsxs)("button", { type: "submit", style: {
|
|
181
|
+
marginTop: '0.5rem',
|
|
182
|
+
padding: '0.95rem 1.5rem',
|
|
183
|
+
borderRadius: '14px',
|
|
184
|
+
backgroundColor: '#16a34a',
|
|
185
|
+
color: '#ffffff',
|
|
186
|
+
fontWeight: 600,
|
|
187
|
+
fontSize: '1rem',
|
|
188
|
+
border: 'none',
|
|
189
|
+
cursor: 'pointer',
|
|
190
|
+
display: 'flex',
|
|
191
|
+
alignItems: 'center',
|
|
192
|
+
justifyContent: 'center',
|
|
193
|
+
gap: '0.625rem',
|
|
194
|
+
transition: 'background-color 0.2s ease, transform 0.1s ease',
|
|
195
|
+
boxShadow: '0 10px 15px -3px rgba(22, 163, 74, 0.3)',
|
|
196
|
+
}, children: [(0, jsx_runtime_1.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", "data-preview-static": "whatsapp-icon", children: (0, jsx_runtime_1.jsx)("path", { d: "M12.031 6.172c-3.181 0-5.767 2.586-5.768 5.766-.001 1.298.38 2.27 1.019 3.287l-.582 2.128 2.182-.573c.978.58 1.911.928 3.145.929 3.178 0 5.767-2.587 5.768-5.766.001-3.187-2.575-5.771-5.764-5.771zm3.392 8.244c-.144.405-.837.774-1.17.824-.299.045-.677.063-1.092-.069-.252-.08-.575-.187-.988-.365-1.739-.751-2.874-2.502-2.961-2.617-.087-.116-.708-.94-.708-1.793s.448-1.273.607-1.446c.159-.173.346-.217.462-.217l.332.006c.106.005.249-.04.39.298.144.347.491 1.2.534 1.287.043.087.072.188.014.304-.058.116-.087.188-.173.289l-.26.304c-.087.086-.177.18-.076.354.101.174.449.741.964 1.201.662.591 1.221.774 1.394.86.173.086.275.071.376-.043.101-.116.433-.506.549-.68.116-.173.231-.145.39-.086s1.011.477 1.184.564.289.13.332.202c.045.072.045.419-.099.824z" }) }), (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: buttonLabelId, defaultValue: defaultButtonLabel, as: "span" })] })] })] }) }));
|
|
197
|
+
}
|
|
198
|
+
exports.BookingModal = EditableBookingModal;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface CarouselTestimonialItem {
|
|
3
|
+
id?: string | number;
|
|
4
|
+
quote: string;
|
|
5
|
+
author: string;
|
|
6
|
+
role?: string;
|
|
7
|
+
company?: string;
|
|
8
|
+
avatar?: string;
|
|
9
|
+
rating?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface EditableTestimonialCarouselProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
12
|
+
listPath?: string;
|
|
13
|
+
items?: CarouselTestimonialItem[];
|
|
14
|
+
autoplay?: boolean;
|
|
15
|
+
autoplayInterval?: number;
|
|
16
|
+
titleId?: string;
|
|
17
|
+
defaultTitle?: string;
|
|
18
|
+
subtitleId?: string;
|
|
19
|
+
defaultSubtitle?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function EditableTestimonialCarousel({ listPath, items, autoplay, autoplayInterval, titleId, defaultTitle, subtitleId, defaultSubtitle, className, style, ...props }: EditableTestimonialCarouselProps): React.JSX.Element;
|
|
22
|
+
export declare const TestimonialCarousel: typeof EditableTestimonialCarousel;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.TestimonialCarousel = void 0;
|
|
5
|
+
exports.EditableTestimonialCarousel = EditableTestimonialCarousel;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
const react_1 = require("react");
|
|
8
|
+
const EditableText_1 = require("./EditableText");
|
|
9
|
+
const EditableImage_1 = require("./EditableImage");
|
|
10
|
+
const SiteDataProvider_1 = require("./SiteDataProvider");
|
|
11
|
+
function EditableTestimonialCarousel({ listPath = 'home.testimonials', items, autoplay = true, autoplayInterval = 5000, titleId = 'home.testimonialsSection.title', defaultTitle = 'Loved by Over 5,000+ Happy Clients', subtitleId = 'home.testimonialsSection.subtitle', defaultSubtitle = 'Real experiences, authentic transformations, and 5-star artistry.', className = '', style, ...props }) {
|
|
12
|
+
const siteData = (0, SiteDataProvider_1.useSiteData)();
|
|
13
|
+
const defaultItems = [
|
|
14
|
+
{
|
|
15
|
+
id: '1',
|
|
16
|
+
quote: 'The balayage and haircut completely transformed my look. The stylists here take genuine time to understand your face shape and hair texture. Absolutely premier experience!',
|
|
17
|
+
author: 'Sophia Kensington',
|
|
18
|
+
role: 'Fashion Director & Verified Client',
|
|
19
|
+
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=400&q=80',
|
|
20
|
+
rating: 5,
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: '2',
|
|
24
|
+
quote: 'Flawless precision fade and hot towel treatment. This is not just a haircut, it is true modern atelier craftsmanship. I have been coming here every two weeks for two years.',
|
|
25
|
+
author: 'Marcus Vance',
|
|
26
|
+
role: 'Creative Producer',
|
|
27
|
+
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?auto=format&fit=crop&w=400&q=80',
|
|
28
|
+
rating: 5,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: '3',
|
|
32
|
+
quote: 'Brought my daughter here for her first styling experience. The patience, warmth, and luxury care they showed made it unforgettable. Cannot recommend enough!',
|
|
33
|
+
author: 'Elena Rostova',
|
|
34
|
+
role: 'Architect & Mother',
|
|
35
|
+
avatar: 'https://images.unsplash.com/photo-1544005313-94ddf0286df2?auto=format&fit=crop&w=400&q=80',
|
|
36
|
+
rating: 5,
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
// Resolve collection from siteData content if present
|
|
40
|
+
let resolvedList = defaultItems;
|
|
41
|
+
if (items && items.length > 0) {
|
|
42
|
+
resolvedList = items;
|
|
43
|
+
}
|
|
44
|
+
else if (siteData?.content) {
|
|
45
|
+
const parts = listPath.split('.');
|
|
46
|
+
let curr = siteData.content;
|
|
47
|
+
for (const part of parts) {
|
|
48
|
+
if (curr && typeof curr === 'object') {
|
|
49
|
+
curr = curr[part];
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
curr = null;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(curr) && curr.length > 0) {
|
|
57
|
+
resolvedList = curr;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const [activeIndex, setActiveIndex] = (0, react_1.useState)(0);
|
|
61
|
+
const [isPaused, setIsPaused] = (0, react_1.useState)(false);
|
|
62
|
+
const touchStartX = (0, react_1.useRef)(0);
|
|
63
|
+
const touchEndX = (0, react_1.useRef)(0);
|
|
64
|
+
const nextSlide = (0, react_1.useCallback)(() => {
|
|
65
|
+
setActiveIndex((prev) => (prev + 1) % resolvedList.length);
|
|
66
|
+
}, [resolvedList.length]);
|
|
67
|
+
const prevSlide = (0, react_1.useCallback)(() => {
|
|
68
|
+
setActiveIndex((prev) => (prev - 1 + resolvedList.length) % resolvedList.length);
|
|
69
|
+
}, [resolvedList.length]);
|
|
70
|
+
(0, react_1.useEffect)(() => {
|
|
71
|
+
if (!autoplay || isPaused || resolvedList.length <= 1)
|
|
72
|
+
return;
|
|
73
|
+
const interval = setInterval(nextSlide, autoplayInterval);
|
|
74
|
+
return () => clearInterval(interval);
|
|
75
|
+
}, [autoplay, isPaused, autoplayInterval, nextSlide, resolvedList.length]);
|
|
76
|
+
const handleTouchStart = (e) => {
|
|
77
|
+
touchStartX.current = e.targetTouches[0].clientX;
|
|
78
|
+
};
|
|
79
|
+
const handleTouchMove = (e) => {
|
|
80
|
+
touchEndX.current = e.targetTouches[0].clientX;
|
|
81
|
+
};
|
|
82
|
+
const handleTouchEnd = () => {
|
|
83
|
+
if (touchStartX.current - touchEndX.current > 60) {
|
|
84
|
+
nextSlide();
|
|
85
|
+
}
|
|
86
|
+
if (touchStartX.current - touchEndX.current < -60) {
|
|
87
|
+
prevSlide();
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const currentItem = resolvedList[activeIndex] || resolvedList[0];
|
|
91
|
+
return ((0, jsx_runtime_1.jsxs)("div", { className: `editable-testimonial-carousel relative py-12 px-4 max-w-5xl mx-auto ${className}`.trim(), onMouseEnter: () => setIsPaused(true), onMouseLeave: () => setIsPaused(false), onTouchStart: handleTouchStart, onTouchMove: handleTouchMove, onTouchEnd: handleTouchEnd, style: {
|
|
92
|
+
position: 'relative',
|
|
93
|
+
width: '100%',
|
|
94
|
+
maxWidth: '56rem',
|
|
95
|
+
margin: '0 auto',
|
|
96
|
+
padding: '3rem 1rem',
|
|
97
|
+
...style,
|
|
98
|
+
}, ...props, children: [(0, jsx_runtime_1.jsxs)("div", { style: { textAlign: 'center', marginBottom: '2.5rem' }, children: [(0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: titleId, defaultValue: defaultTitle, as: "h2", style: {
|
|
99
|
+
fontSize: '2rem',
|
|
100
|
+
fontWeight: 700,
|
|
101
|
+
letterSpacing: '-0.03em',
|
|
102
|
+
marginBottom: '0.75rem',
|
|
103
|
+
color: 'var(--color-text, #0f172a)',
|
|
104
|
+
} }), (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: subtitleId, defaultValue: defaultSubtitle, as: "p", style: {
|
|
105
|
+
fontSize: '1rem',
|
|
106
|
+
color: '#64748b',
|
|
107
|
+
maxWidth: '36rem',
|
|
108
|
+
margin: '0 auto',
|
|
109
|
+
lineHeight: 1.6,
|
|
110
|
+
} })] }), (0, jsx_runtime_1.jsxs)("div", { "data-preview-list-path": listPath, style: {
|
|
111
|
+
position: 'relative',
|
|
112
|
+
backgroundColor: 'var(--color-surface, #ffffff)',
|
|
113
|
+
borderRadius: '24px',
|
|
114
|
+
padding: '3rem 2.5rem',
|
|
115
|
+
boxShadow: '0 20px 40px -15px rgba(0, 0, 0, 0.07)',
|
|
116
|
+
border: '1px solid var(--color-border, #f1f5f9)',
|
|
117
|
+
minHeight: '280px',
|
|
118
|
+
display: 'flex',
|
|
119
|
+
flexDirection: 'column',
|
|
120
|
+
justifyContent: 'space-between',
|
|
121
|
+
}, children: [(0, jsx_runtime_1.jsxs)("div", { "data-preview-item-path": `${listPath}[${activeIndex}]`, children: [(0, jsx_runtime_1.jsx)("div", { style: { display: 'flex', gap: '4px', marginBottom: '1.5rem' }, children: [...Array(5)].map((_, i) => ((0, jsx_runtime_1.jsx)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: i < (currentItem?.rating ?? 5) ? '#f59e0b' : '#e2e8f0', "data-preview-static": "carousel-star", children: (0, jsx_runtime_1.jsx)("path", { d: "M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" }) }, i))) }), (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: `${listPath}[${activeIndex}].quote`, defaultValue: currentItem?.quote, as: "p", style: {
|
|
122
|
+
fontSize: '1.25rem',
|
|
123
|
+
lineHeight: 1.7,
|
|
124
|
+
fontWeight: 400,
|
|
125
|
+
fontStyle: 'italic',
|
|
126
|
+
color: 'var(--color-text, #1e293b)',
|
|
127
|
+
marginBottom: '2rem',
|
|
128
|
+
} }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: '1rem' }, children: [(0, jsx_runtime_1.jsx)(EditableImage_1.EditableImage, { id: `${listPath}[${activeIndex}].avatar`, src: currentItem?.avatar || 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?auto=format&fit=crop&w=400&q=80', alt: currentItem?.author || 'Client', style: {
|
|
129
|
+
width: '52px',
|
|
130
|
+
height: '52px',
|
|
131
|
+
borderRadius: '9999px',
|
|
132
|
+
objectFit: 'cover',
|
|
133
|
+
border: '2px solid #e2e8f0',
|
|
134
|
+
} }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: `${listPath}[${activeIndex}].author`, defaultValue: currentItem?.author, as: "h4", style: {
|
|
135
|
+
fontSize: '1rem',
|
|
136
|
+
fontWeight: 700,
|
|
137
|
+
color: 'var(--color-text, #0f172a)',
|
|
138
|
+
margin: 0,
|
|
139
|
+
} }), (0, jsx_runtime_1.jsx)(EditableText_1.EditableText, { id: `${listPath}[${activeIndex}].role`, defaultValue: currentItem?.role || currentItem?.company || 'Verified Client', as: "p", style: {
|
|
140
|
+
fontSize: '0.85rem',
|
|
141
|
+
color: '#64748b',
|
|
142
|
+
margin: '0.2rem 0 0 0',
|
|
143
|
+
} })] })] })] }), (0, jsx_runtime_1.jsxs)("div", { "data-preview-static": "carousel-controls", style: {
|
|
144
|
+
display: 'flex',
|
|
145
|
+
alignItems: 'center',
|
|
146
|
+
justifyContent: 'space-between',
|
|
147
|
+
marginTop: '2rem',
|
|
148
|
+
paddingTop: '1.5rem',
|
|
149
|
+
borderTop: '1px solid #f1f5f9',
|
|
150
|
+
}, children: [(0, jsx_runtime_1.jsx)("div", { style: { display: 'flex', gap: '8px' }, children: resolvedList.map((item, idx) => ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": `Go to slide ${idx + 1}`, onClick: () => setActiveIndex(idx), style: {
|
|
151
|
+
width: idx === activeIndex ? '28px' : '8px',
|
|
152
|
+
height: '8px',
|
|
153
|
+
borderRadius: '9999px',
|
|
154
|
+
backgroundColor: idx === activeIndex ? 'var(--color-primary, #0f172a)' : '#cbd5e1',
|
|
155
|
+
border: 'none',
|
|
156
|
+
cursor: 'pointer',
|
|
157
|
+
transition: 'all 0.3s ease',
|
|
158
|
+
padding: 0,
|
|
159
|
+
} }, item.id || item.author || idx))) }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', gap: '8px' }, children: [(0, jsx_runtime_1.jsx)("button", { type: "button", onClick: prevSlide, "aria-label": "Previous testimonial", style: {
|
|
160
|
+
width: '40px',
|
|
161
|
+
height: '40px',
|
|
162
|
+
borderRadius: '9999px',
|
|
163
|
+
border: '1px solid #e2e8f0',
|
|
164
|
+
backgroundColor: '#ffffff',
|
|
165
|
+
display: 'flex',
|
|
166
|
+
alignItems: 'center',
|
|
167
|
+
justifyContent: 'center',
|
|
168
|
+
cursor: 'pointer',
|
|
169
|
+
color: '#334155',
|
|
170
|
+
transition: 'background-color 0.2s ease',
|
|
171
|
+
}, children: (0, jsx_runtime_1.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: (0, jsx_runtime_1.jsx)("polyline", { points: "15 18 9 12 15 6" }) }) }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: nextSlide, "aria-label": "Next testimonial", style: {
|
|
172
|
+
width: '40px',
|
|
173
|
+
height: '40px',
|
|
174
|
+
borderRadius: '9999px',
|
|
175
|
+
border: '1px solid #e2e8f0',
|
|
176
|
+
backgroundColor: '#ffffff',
|
|
177
|
+
display: 'flex',
|
|
178
|
+
alignItems: 'center',
|
|
179
|
+
justifyContent: 'center',
|
|
180
|
+
cursor: 'pointer',
|
|
181
|
+
color: '#334155',
|
|
182
|
+
transition: 'background-color 0.2s ease',
|
|
183
|
+
}, children: (0, jsx_runtime_1.jsx)("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: (0, jsx_runtime_1.jsx)("polyline", { points: "9 18 15 12 9 6" }) }) })] })] })] })] }));
|
|
184
|
+
}
|
|
185
|
+
exports.TestimonialCarousel = EditableTestimonialCarousel;
|
|
@@ -301,7 +301,7 @@ function PlatformProductDetail({ productId, queryParam = DEFAULT_QUERY_PARAM, pr
|
|
|
301
301
|
retryDelays,
|
|
302
302
|
});
|
|
303
303
|
if (result.status === 'loading') {
|
|
304
|
-
return loadingFallback ?? ((0, jsx_runtime_1.jsx)("div", { role: "status", className: "mx-auto flex min-h-[40vh] max-w-7xl items-center justify-center px-4 py-16 text-sm text-slate-400", children: "Loading product\u2026" }));
|
|
304
|
+
return loadingFallback ?? ((0, jsx_runtime_1.jsx)("div", { role: "status", className: "mx-auto flex min-h-[40vh] max-w-7xl items-center justify-center px-4 py-16 text-sm text-slate-400", children: (0, jsx_runtime_1.jsx)("span", { "data-preview-static": "product-detail-loading", children: "Loading product\u2026" }) }));
|
|
305
305
|
}
|
|
306
306
|
if (result.status === 'error') {
|
|
307
307
|
return errorFallback ?? ((0, jsx_runtime_1.jsxs)("div", { className: "mx-auto flex min-h-[40vh] max-w-xl flex-col items-center justify-center gap-4 px-4 py-16 text-center", children: [(0, jsx_runtime_1.jsx)("h1", { className: "text-3xl font-bold text-slate-100", children: "Unable to load product" }), (0, jsx_runtime_1.jsx)("p", { className: "text-sm text-slate-400", children: "Please check your connection and try again." }), (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: result.retry, className: "rounded-xl bg-white px-5 py-2.5 text-sm font-bold text-slate-950", children: "Try again" })] }));
|
|
@@ -57,7 +57,8 @@ export type SiteData = {
|
|
|
57
57
|
styles?: GenericRecord | null;
|
|
58
58
|
[key: string]: unknown;
|
|
59
59
|
};
|
|
60
|
-
|
|
60
|
+
declare const UNINITIALIZED_SITE_DATA: unique symbol;
|
|
61
|
+
export declare const SiteDataContext: React.Context<SiteData | typeof UNINITIALIZED_SITE_DATA>;
|
|
61
62
|
export declare function isRecord(value: unknown): value is GenericRecord;
|
|
62
63
|
export declare function isDarkColor(color?: unknown): boolean;
|
|
63
64
|
export declare function syncThemeToDocument(theme: unknown): void;
|
|
@@ -99,7 +100,7 @@ export declare function useFieldStyle(path?: string): GenericRecord | null;
|
|
|
99
100
|
*/
|
|
100
101
|
export declare const DenebDataProvider: typeof SiteDataProvider;
|
|
101
102
|
export declare const useDenebData: typeof useSiteData;
|
|
102
|
-
export declare const DenebDataContext: React.Context<SiteData>;
|
|
103
|
+
export declare const DenebDataContext: React.Context<SiteData | typeof UNINITIALIZED_SITE_DATA>;
|
|
103
104
|
export type DenebData = SiteData;
|
|
104
105
|
/**
|
|
105
106
|
* Hook to retrieve products cleanly from SiteData, supporting both
|
package/dist/SiteDataProvider.js
CHANGED
|
@@ -28,6 +28,7 @@ const core_1 = require("@deneb-ui/core");
|
|
|
28
28
|
const DenebComponentStyles_1 = require("./DenebComponentStyles");
|
|
29
29
|
const FontLoader_1 = require("./fonts/FontLoader");
|
|
30
30
|
const ResponsiveBaseStyles_1 = require("./ResponsiveBaseStyles");
|
|
31
|
+
const ThemeStyles_1 = require("./ThemeStyles");
|
|
31
32
|
exports.DENEB_PREVIEW_DATA_MESSAGE = 'DENEB_PREVIEW_SITE_DATA';
|
|
32
33
|
exports.PREVIEW_DATA_MESSAGE = 'FIVORA_PREVIEW_SITE_DATA';
|
|
33
34
|
const PREVIOUS_PREVIEW_PREFIX = `${['MARKET', 'PLACE'].join('')}_PREVIEW_`;
|
|
@@ -50,7 +51,8 @@ const SITE_DATA_CACHE_KEY = '__FIVORA_PREVIEW_SITE_DATA_CACHE__';
|
|
|
50
51
|
const LEGACY_SITE_DATA_CACHE_KEY = previousPreviewStorageKey('SITE_DATA_CACHE');
|
|
51
52
|
const SITE_DATA_GLOBAL_KEY = '__FIVORA_PREVIEW_SITE_DATA__';
|
|
52
53
|
const LEGACY_SITE_DATA_GLOBAL_KEY = previousPreviewStorageKey('SITE_DATA');
|
|
53
|
-
|
|
54
|
+
const UNINITIALIZED_SITE_DATA = Symbol('DENEB_UNINITIALIZED_SITE_DATA');
|
|
55
|
+
exports.SiteDataContext = (0, react_1.createContext)(UNINITIALIZED_SITE_DATA);
|
|
54
56
|
function isRecord(value) {
|
|
55
57
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
56
58
|
}
|
|
@@ -464,10 +466,19 @@ function SiteDataProvider({ children, initialSiteData, fallbackSiteData, liveCat
|
|
|
464
466
|
return () => window.removeEventListener('message', onMessage);
|
|
465
467
|
}, []);
|
|
466
468
|
const value = (0, react_1.useMemo)(() => siteData, [siteData]);
|
|
467
|
-
return ((0, jsx_runtime_1.jsxs)(exports.SiteDataContext.Provider, { value: value, children: [(0, jsx_runtime_1.jsx)(FontLoader_1.FontLoader, {}), (0, jsx_runtime_1.jsx)(ResponsiveBaseStyles_1.ResponsiveBaseStyles, {}), (0, jsx_runtime_1.jsx)(DenebComponentStyles_1.DenebComponentStyles, {}), children] }));
|
|
469
|
+
return ((0, jsx_runtime_1.jsxs)(exports.SiteDataContext.Provider, { value: value, children: [(0, jsx_runtime_1.jsx)(ThemeStyles_1.ThemeStyles, { theme: value?.theme }), (0, jsx_runtime_1.jsx)(FontLoader_1.FontLoader, {}), (0, jsx_runtime_1.jsx)(ResponsiveBaseStyles_1.ResponsiveBaseStyles, {}), (0, jsx_runtime_1.jsx)(DenebComponentStyles_1.DenebComponentStyles, {}), children] }));
|
|
468
470
|
}
|
|
469
471
|
function useSiteData() {
|
|
470
|
-
|
|
472
|
+
const ctx = (0, react_1.useContext)(exports.SiteDataContext);
|
|
473
|
+
if (ctx === UNINITIALIZED_SITE_DATA) {
|
|
474
|
+
if (typeof window !== 'undefined' && process.env.NODE_ENV !== 'production') {
|
|
475
|
+
console.warn('[Deneb UI] useSiteData() was called outside of <SiteDataProvider>. ' +
|
|
476
|
+
'Ensure your root layout.tsx or _app.tsx wraps the tree with: ' +
|
|
477
|
+
'<SiteDataProvider initialSiteData={initialSiteData}>. Falling back to empty data.');
|
|
478
|
+
}
|
|
479
|
+
return {};
|
|
480
|
+
}
|
|
481
|
+
return ctx;
|
|
471
482
|
}
|
|
472
483
|
function contentObject(value) {
|
|
473
484
|
return isRecord(value) ? value : {};
|
package/dist/ThemeStyles.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface TemplateTheme {
|
|
|
17
17
|
align?: 'left' | 'center' | 'right';
|
|
18
18
|
buttonBackgroundColor?: string;
|
|
19
19
|
buttonTextColor?: string;
|
|
20
|
+
dark?: Partial<TemplateTheme>;
|
|
21
|
+
light?: Partial<TemplateTheme>;
|
|
20
22
|
[key: string]: unknown;
|
|
21
23
|
}
|
|
22
24
|
/**
|
|
@@ -41,8 +43,15 @@ export interface ThemeStylesProps {
|
|
|
41
43
|
defaultAccent?: string;
|
|
42
44
|
defaultBg?: string;
|
|
43
45
|
defaultText?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Automatically generate opposite mode selectors (.dark / .light or [data-theme="..."])
|
|
48
|
+
* so templates with theme switchers transition without writing manual CSS.
|
|
49
|
+
* Default: true.
|
|
50
|
+
*/
|
|
51
|
+
enableDualMode?: boolean;
|
|
44
52
|
}
|
|
45
53
|
/**
|
|
46
54
|
* Automatically injects standard and custom fivora theme variables into the document.
|
|
55
|
+
* Supports light-only, dark-only, and dual-mode (light & dark toggle) templates.
|
|
47
56
|
*/
|
|
48
|
-
export declare function ThemeStyles({ theme, defaultPrimary, defaultSecondary, defaultAccent, defaultBg, defaultText, }: ThemeStylesProps): React.JSX.Element;
|
|
57
|
+
export declare function ThemeStyles({ theme, defaultPrimary, defaultSecondary, defaultAccent, defaultBg, defaultText, enableDualMode, }: ThemeStylesProps): React.JSX.Element;
|
package/dist/ThemeStyles.js
CHANGED
|
@@ -139,14 +139,14 @@ function getThemeCssProperties(theme) {
|
|
|
139
139
|
const customVars = {};
|
|
140
140
|
if (theme) {
|
|
141
141
|
for (const [key, val] of Object.entries(theme)) {
|
|
142
|
-
if (typeof val === 'string' || typeof val === 'number') {
|
|
142
|
+
if (key !== 'dark' && key !== 'light' && (typeof val === 'string' || typeof val === 'number')) {
|
|
143
143
|
const cssVarName = `--${key.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`;
|
|
144
144
|
customVars[cssVarName] = String(val);
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
const isDark = (0, core_1.isDarkColor)(theme?.backgroundColor);
|
|
149
|
-
const bgColor = theme?.backgroundColor || (isDark ? '#
|
|
149
|
+
const bgColor = theme?.backgroundColor || (isDark ? '#090d1a' : '#ffffff');
|
|
150
150
|
const textColor = theme?.textColor || (isDark ? '#f8fafc' : '#0f172a');
|
|
151
151
|
const mutedColor = theme?.mutedTextColor || (isDark ? 'rgba(248, 250, 252, 0.7)' : '#64748b');
|
|
152
152
|
const borderColor = isDark ? 'rgba(255, 255, 255, 0.1)' : '#e2e8f0';
|
|
@@ -211,17 +211,61 @@ function getThemeCssProperties(theme) {
|
|
|
211
211
|
}
|
|
212
212
|
/**
|
|
213
213
|
* Automatically injects standard and custom fivora theme variables into the document.
|
|
214
|
+
* Supports light-only, dark-only, and dual-mode (light & dark toggle) templates.
|
|
214
215
|
*/
|
|
215
|
-
function ThemeStyles({ theme, defaultPrimary = '#2563eb', defaultSecondary = '#0f172a', defaultAccent = '#14b8a6', defaultBg = '#ffffff', defaultText = '#0f172a', }) {
|
|
216
|
+
function ThemeStyles({ theme, defaultPrimary = '#2563eb', defaultSecondary = '#0f172a', defaultAccent = '#14b8a6', defaultBg = '#ffffff', defaultText = '#0f172a', enableDualMode = true, }) {
|
|
216
217
|
const styleProps = getThemeCssProperties(theme);
|
|
217
|
-
const
|
|
218
|
+
const baseLines = Object.entries(styleProps)
|
|
218
219
|
.filter(([key]) => key.startsWith('--'))
|
|
219
220
|
.map(([key, value]) => ` ${key}: ${value};`)
|
|
220
221
|
.join('\n');
|
|
221
|
-
|
|
222
|
+
let css = `
|
|
222
223
|
:root {
|
|
223
|
-
${
|
|
224
|
+
${baseLines}
|
|
224
225
|
}
|
|
225
226
|
`;
|
|
227
|
+
if (enableDualMode) {
|
|
228
|
+
const isDarkBase = (0, core_1.isDarkColor)(theme?.backgroundColor);
|
|
229
|
+
if (isDarkBase) {
|
|
230
|
+
// Base theme is dark. Generate light mode rules for .light or [data-theme="light"]
|
|
231
|
+
const lightTheme = {
|
|
232
|
+
...theme,
|
|
233
|
+
backgroundColor: '#ffffff',
|
|
234
|
+
textColor: '#0f172a',
|
|
235
|
+
mutedTextColor: '#64748b',
|
|
236
|
+
...(theme?.light || {}),
|
|
237
|
+
};
|
|
238
|
+
const lightProps = getThemeCssProperties(lightTheme);
|
|
239
|
+
const lightLines = Object.entries(lightProps)
|
|
240
|
+
.filter(([key]) => key.startsWith('--'))
|
|
241
|
+
.map(([key, value]) => ` ${key}: ${value};`)
|
|
242
|
+
.join('\n');
|
|
243
|
+
css += `
|
|
244
|
+
.light, [data-theme="light"] {
|
|
245
|
+
${lightLines}
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
}
|
|
249
|
+
else {
|
|
250
|
+
// Base theme is light. Generate dark mode rules for .dark or [data-theme="dark"]
|
|
251
|
+
const darkTheme = {
|
|
252
|
+
...theme,
|
|
253
|
+
backgroundColor: '#090d1a',
|
|
254
|
+
textColor: '#f8fafc',
|
|
255
|
+
mutedTextColor: 'rgba(248, 250, 252, 0.7)',
|
|
256
|
+
...(theme?.dark || {}),
|
|
257
|
+
};
|
|
258
|
+
const darkProps = getThemeCssProperties(darkTheme);
|
|
259
|
+
const darkLines = Object.entries(darkProps)
|
|
260
|
+
.filter(([key]) => key.startsWith('--'))
|
|
261
|
+
.map(([key, value]) => ` ${key}: ${value};`)
|
|
262
|
+
.join('\n');
|
|
263
|
+
css += `
|
|
264
|
+
.dark, [data-theme="dark"] {
|
|
265
|
+
${darkLines}
|
|
266
|
+
}
|
|
267
|
+
`;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
226
270
|
return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(ResponsiveBaseStyles_1.ResponsiveBaseStyles, {}), (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: css } })] }));
|
|
227
271
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ThemeToggleProps {
|
|
3
|
+
className?: string;
|
|
4
|
+
showLabel?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Built-in ThemeToggle Component for DENEB Storefront Templates.
|
|
8
|
+
* Toggles the `.dark` class on the root <html> element and persists preference to localStorage.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ThemeToggle({ className, showLabel }: ThemeToggleProps): React.JSX.Element;
|
|
11
|
+
export default ThemeToggle;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ThemeToggle = ThemeToggle;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
/**
|
|
8
|
+
* Built-in ThemeToggle Component for DENEB Storefront Templates.
|
|
9
|
+
* Toggles the `.dark` class on the root <html> element and persists preference to localStorage.
|
|
10
|
+
*/
|
|
11
|
+
function ThemeToggle({ className = '', showLabel = false }) {
|
|
12
|
+
const [isDark, setIsDark] = (0, react_1.useState)(false);
|
|
13
|
+
const [mounted, setMounted] = (0, react_1.useState)(false);
|
|
14
|
+
(0, react_1.useEffect)(() => {
|
|
15
|
+
setMounted(true);
|
|
16
|
+
const hasDarkClass = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
|
|
17
|
+
const storedTheme = typeof window !== 'undefined' ? localStorage.getItem('deneb-theme') : null;
|
|
18
|
+
const prefersDark = typeof window !== 'undefined' && typeof window.matchMedia === 'function' ? window.matchMedia('(prefers-color-scheme: dark)').matches : false;
|
|
19
|
+
const activeDark = storedTheme ? storedTheme === 'dark' : hasDarkClass || prefersDark;
|
|
20
|
+
setIsDark(activeDark);
|
|
21
|
+
if (activeDark) {
|
|
22
|
+
if (typeof document !== 'undefined')
|
|
23
|
+
document.documentElement.classList.add('dark');
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
if (typeof document !== 'undefined')
|
|
27
|
+
document.documentElement.classList.remove('dark');
|
|
28
|
+
}
|
|
29
|
+
}, []);
|
|
30
|
+
const toggleTheme = () => {
|
|
31
|
+
const nextDark = !isDark;
|
|
32
|
+
setIsDark(nextDark);
|
|
33
|
+
if (nextDark) {
|
|
34
|
+
if (typeof document !== 'undefined')
|
|
35
|
+
document.documentElement.classList.add('dark');
|
|
36
|
+
if (typeof window !== 'undefined')
|
|
37
|
+
localStorage.setItem('deneb-theme', 'dark');
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
if (typeof document !== 'undefined')
|
|
41
|
+
document.documentElement.classList.remove('dark');
|
|
42
|
+
if (typeof window !== 'undefined')
|
|
43
|
+
localStorage.setItem('deneb-theme', 'light');
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
if (!mounted) {
|
|
47
|
+
return ((0, jsx_runtime_1.jsx)("button", { type: "button", "aria-label": "Toggle theme", "data-preview-static": "theme-toggle", className: `w-9 h-9 rounded-lg border border-[var(--border-color,#e2e8f0)] bg-[var(--card-bg,#ffffff)] opacity-0 transition-opacity ${className}` }));
|
|
48
|
+
}
|
|
49
|
+
return ((0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: toggleTheme, "data-preview-static": "theme-toggle", "aria-label": isDark ? 'Switch to light mode' : 'Switch to dark mode', title: isDark ? 'Switch to light mode' : 'Switch to dark mode', className: `inline-flex items-center justify-center gap-2 h-9 px-2.5 rounded-lg border border-[var(--border-color,rgba(0,0,0,0.1))] bg-[var(--card-bg,#ffffff)] text-[var(--text-color,#0f172a)] hover:border-[var(--color-primary,#016a7e)] hover:text-[var(--color-primary,#016a7e)] shadow-sm transition-all duration-200 cursor-pointer ${className}`, children: [isDark ? ((0, jsx_runtime_1.jsx)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "w-4 h-4 text-amber-400", children: (0, jsx_runtime_1.jsx)("path", { d: "M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" }) })) : ((0, jsx_runtime_1.jsxs)("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className: "w-4 h-4 text-amber-500", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "12", r: "4" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 2v2" }), (0, jsx_runtime_1.jsx)("path", { d: "M12 20v2" }), (0, jsx_runtime_1.jsx)("path", { d: "m4.93 4.93 1.41 1.41" }), (0, jsx_runtime_1.jsx)("path", { d: "m17.66 17.66 1.41 1.41" }), (0, jsx_runtime_1.jsx)("path", { d: "M2 12h2" }), (0, jsx_runtime_1.jsx)("path", { d: "M20 12h2" }), (0, jsx_runtime_1.jsx)("path", { d: "m6.34 17.66-1.41 1.41" }), (0, jsx_runtime_1.jsx)("path", { d: "m19.07 4.93-1.41 1.41" })] })), showLabel && ((0, jsx_runtime_1.jsx)("span", { className: "text-xs font-medium", children: isDark ? 'Dark' : 'Light' }))] }));
|
|
50
|
+
}
|
|
51
|
+
exports.default = ThemeToggle;
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export * from './EditableCard';
|
|
|
22
22
|
export * from './EditablePricingCard';
|
|
23
23
|
export * from './EditableTestimonialCard';
|
|
24
24
|
export * from './EditableTestimonialSection';
|
|
25
|
+
export * from './EditableTestimonialCarousel';
|
|
26
|
+
export * from './EditableBeforeAfterSlider';
|
|
27
|
+
export * from './EditableBookingModal';
|
|
25
28
|
export * from './EditableFAQAccordion';
|
|
26
29
|
export * from './EditableContactForm';
|
|
27
30
|
export * from './EditableNavbar';
|
|
@@ -39,6 +42,7 @@ export * from './EditableCartDrawer';
|
|
|
39
42
|
export * from './EditableFilterSidebar';
|
|
40
43
|
export * from './SiteDataProvider';
|
|
41
44
|
export * from './ThemeStyles';
|
|
45
|
+
export * from './ThemeToggle';
|
|
42
46
|
export { ResponsiveBaseStyles } from './ResponsiveBaseStyles';
|
|
43
47
|
export { DenebComponentStyles } from './DenebComponentStyles';
|
|
44
48
|
export { FontLoader, DENEB_FONTS_LINK_ID } from './fonts/FontLoader';
|
|
@@ -86,6 +90,9 @@ export { EditableAnnouncementBar as AnnouncementBar } from './EditableAnnounceme
|
|
|
86
90
|
export { EditableCategoryPills as CategoryPills } from './EditableCategoryPills';
|
|
87
91
|
export { EditableCartDrawer as CartDrawer } from './EditableCartDrawer';
|
|
88
92
|
export { EditableFilterSidebar as FilterSidebar } from './EditableFilterSidebar';
|
|
93
|
+
export { EditableBeforeAfterSlider as BeforeAfterSlider } from './EditableBeforeAfterSlider';
|
|
94
|
+
export { EditableBookingModal as BookingModal } from './EditableBookingModal';
|
|
95
|
+
export { EditableTestimonialCarousel as TestimonialCarousel } from './EditableTestimonialCarousel';
|
|
89
96
|
export { PlatformAdditionalPages, AdditionalPagesNav } from './PlatformAdditionalPages';
|
|
90
97
|
/**
|
|
91
98
|
* DENEB UI Framework Metadata
|
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.FAQ = exports.Accordion = exports.Testimonials = exports.TestimonialSection = exports.TestimonialCard = exports.PricingCard = exports.ServiceCard = exports.GoogleFeedback = exports.CustomerReviews = exports.ProductDetail = exports.ProductShowcase = 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.generateThemeVariables = exports.mergeVisualCustomizationIntoTheme = exports.customizationToTheme = exports.parseVisualCustomization = exports.normalizeHexColor = exports.toHexColor = exports.getAutoContrastTextColor = exports.isDarkColor = exports.FONT_PAIRINGS = exports.GLOBAL_FONT_OPTIONS = exports.THEME_PALETTES = exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.AdditionalPagesNav = exports.PlatformAdditionalPages = exports.FilterSidebar = exports.CartDrawer = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = exports.Hero = exports.Footer = exports.Header = exports.Navbar = exports.ContactForm = void 0;
|
|
22
|
+
exports.generateThemeVariables = exports.mergeVisualCustomizationIntoTheme = exports.customizationToTheme = exports.parseVisualCustomization = exports.normalizeHexColor = exports.toHexColor = exports.getAutoContrastTextColor = exports.isDarkColor = exports.FONT_PAIRINGS = exports.GLOBAL_FONT_OPTIONS = exports.THEME_PALETTES = exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.AdditionalPagesNav = exports.PlatformAdditionalPages = exports.TestimonialCarousel = exports.BookingModal = exports.BeforeAfterSlider = exports.FilterSidebar = exports.CartDrawer = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = exports.Hero = exports.Footer = exports.Header = exports.Navbar = exports.ContactForm = void 0;
|
|
23
23
|
__exportStar(require("./PreviewField"), exports);
|
|
24
24
|
__exportStar(require("./EditableText"), exports);
|
|
25
25
|
__exportStar(require("./EditableImage"), exports);
|
|
@@ -40,6 +40,9 @@ __exportStar(require("./EditableCard"), exports);
|
|
|
40
40
|
__exportStar(require("./EditablePricingCard"), exports);
|
|
41
41
|
__exportStar(require("./EditableTestimonialCard"), exports);
|
|
42
42
|
__exportStar(require("./EditableTestimonialSection"), exports);
|
|
43
|
+
__exportStar(require("./EditableTestimonialCarousel"), exports);
|
|
44
|
+
__exportStar(require("./EditableBeforeAfterSlider"), exports);
|
|
45
|
+
__exportStar(require("./EditableBookingModal"), exports);
|
|
43
46
|
__exportStar(require("./EditableFAQAccordion"), exports);
|
|
44
47
|
__exportStar(require("./EditableContactForm"), exports);
|
|
45
48
|
__exportStar(require("./EditableNavbar"), exports);
|
|
@@ -57,6 +60,7 @@ __exportStar(require("./EditableCartDrawer"), exports);
|
|
|
57
60
|
__exportStar(require("./EditableFilterSidebar"), exports);
|
|
58
61
|
__exportStar(require("./SiteDataProvider"), exports);
|
|
59
62
|
__exportStar(require("./ThemeStyles"), exports);
|
|
63
|
+
__exportStar(require("./ThemeToggle"), exports);
|
|
60
64
|
var ResponsiveBaseStyles_1 = require("./ResponsiveBaseStyles");
|
|
61
65
|
Object.defineProperty(exports, "ResponsiveBaseStyles", { enumerable: true, get: function () { return ResponsiveBaseStyles_1.ResponsiveBaseStyles; } });
|
|
62
66
|
var DenebComponentStyles_1 = require("./DenebComponentStyles");
|
|
@@ -166,6 +170,12 @@ var EditableCartDrawer_1 = require("./EditableCartDrawer");
|
|
|
166
170
|
Object.defineProperty(exports, "CartDrawer", { enumerable: true, get: function () { return EditableCartDrawer_1.EditableCartDrawer; } });
|
|
167
171
|
var EditableFilterSidebar_1 = require("./EditableFilterSidebar");
|
|
168
172
|
Object.defineProperty(exports, "FilterSidebar", { enumerable: true, get: function () { return EditableFilterSidebar_1.EditableFilterSidebar; } });
|
|
173
|
+
var EditableBeforeAfterSlider_1 = require("./EditableBeforeAfterSlider");
|
|
174
|
+
Object.defineProperty(exports, "BeforeAfterSlider", { enumerable: true, get: function () { return EditableBeforeAfterSlider_1.EditableBeforeAfterSlider; } });
|
|
175
|
+
var EditableBookingModal_1 = require("./EditableBookingModal");
|
|
176
|
+
Object.defineProperty(exports, "BookingModal", { enumerable: true, get: function () { return EditableBookingModal_1.EditableBookingModal; } });
|
|
177
|
+
var EditableTestimonialCarousel_1 = require("./EditableTestimonialCarousel");
|
|
178
|
+
Object.defineProperty(exports, "TestimonialCarousel", { enumerable: true, get: function () { return EditableTestimonialCarousel_1.EditableTestimonialCarousel; } });
|
|
169
179
|
var PlatformAdditionalPages_1 = require("./PlatformAdditionalPages");
|
|
170
180
|
Object.defineProperty(exports, "PlatformAdditionalPages", { enumerable: true, get: function () { return PlatformAdditionalPages_1.PlatformAdditionalPages; } });
|
|
171
181
|
Object.defineProperty(exports, "AdditionalPagesNav", { enumerable: true, get: function () { return PlatformAdditionalPages_1.AdditionalPagesNav; } });
|
package/dist/location/MapLink.js
CHANGED
|
@@ -66,5 +66,5 @@ function MapLink({ name, mapUrl, addressUrl, url, address, city, state, country,
|
|
|
66
66
|
...variantStyles[variant],
|
|
67
67
|
};
|
|
68
68
|
const opensNewTab = finalHref !== '#';
|
|
69
|
-
return ((0, jsx_runtime_1.jsxs)("
|
|
69
|
+
return ((0, jsx_runtime_1.jsxs)("a", { href: finalHref, "data-preview-field-path": resolvedUrlFieldPath, target: opensNewTab ? '_blank' : undefined, rel: opensNewTab ? 'noopener noreferrer' : undefined, className: `deneb-map-link ${className}`.trim(), style: mergedStyles, ...rest, children: [icon !== undefined ? icon : defaultIcon, (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": resolvedLabelFieldPath, children: label })] }));
|
|
70
70
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.72",
|
|
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",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
],
|
|
51
51
|
"license": "MIT",
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@deneb-ui/core": "^2.0.
|
|
53
|
+
"@deneb-ui/core": "^2.0.72"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"react": "^18.0.0 || ^19.0.0",
|
package/platform-contract.json
CHANGED
|
@@ -26,7 +26,10 @@
|
|
|
26
26
|
"products[*].measurement",
|
|
27
27
|
"products[*].unit",
|
|
28
28
|
"testimonials[*].id",
|
|
29
|
-
"categories[*].slug"
|
|
29
|
+
"categories[*].slug",
|
|
30
|
+
"services[*].id",
|
|
31
|
+
"products[*].currency",
|
|
32
|
+
"about.collageImages[*].id"
|
|
30
33
|
],
|
|
31
34
|
"platformSiteDataKeys": [
|
|
32
35
|
"__fivoraIntake",
|