@deneb-ui/ui 2.0.32 → 2.0.33
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/EditableText.js +1 -1
- package/dist/PreviewField.d.ts +11 -0
- package/dist/PreviewField.js +13 -0
- package/dist/SiteDataProvider.js +5 -1
- package/dist/business/HeritageCollage.d.ts +24 -0
- package/dist/business/HeritageCollage.js +51 -0
- package/dist/business/index.d.ts +1 -0
- package/dist/business/index.js +1 -0
- package/dist/core/ListActionCta.d.ts +19 -0
- package/dist/core/ListActionCta.js +20 -0
- package/dist/core/SplitFieldAction.d.ts +19 -0
- package/dist/core/SplitFieldAction.js +66 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/location/LocationCard.js +1 -1
- package/dist/location/LocationLink.js +1 -1
- package/dist/location/MapLink.d.ts +9 -1
- package/dist/location/MapLink.js +12 -6
- package/dist/utils/urls.d.ts +11 -1
- package/dist/utils/urls.js +27 -5
- package/package.json +2 -2
package/dist/EditableText.js
CHANGED
|
@@ -151,7 +151,7 @@ function EditableText({ id, 'data-preview-field-path': previewFieldPath, default
|
|
|
151
151
|
...styleVars,
|
|
152
152
|
...style,
|
|
153
153
|
};
|
|
154
|
-
return ((0, jsx_runtime_1.jsx)(Component, { "data-preview-field-path": path, "data-preview-style-target": path, "data-preview-style-type": "text", "data-preview-placeholder": isPlaceholderActive ? 'true' : undefined, className: `deneb-text ${className}`.trim(), style: typographyStyle, ...props, children: displayContent }));
|
|
154
|
+
return ((0, jsx_runtime_1.jsx)(Component, { "data-preview-field-path": path, "data-preview-style-target": path, "data-preview-style-type": "text", "data-preview-placeholder": isPlaceholderActive ? 'true' : undefined, className: `deneb-text ${className}`.trim(), style: typographyStyle, suppressHydrationWarning: true, ...props, children: displayContent }));
|
|
155
155
|
}
|
|
156
156
|
function EditableHeading({ level = 2, as, ...props }) {
|
|
157
157
|
const variant = `h${level}`;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export type PreviewFieldProps<T extends React.ElementType = 'span'> = {
|
|
3
|
+
as?: T;
|
|
4
|
+
path: string;
|
|
5
|
+
children?: React.ReactNode;
|
|
6
|
+
} & Omit<React.ComponentPropsWithoutRef<T>, 'as' | 'children'>;
|
|
7
|
+
/**
|
|
8
|
+
* Leaf node for Fivora visual editing. Uses suppressHydrationWarning because the
|
|
9
|
+
* preview focus bridge may annotate the DOM before React hydrates.
|
|
10
|
+
*/
|
|
11
|
+
export declare function PreviewField<T extends React.ElementType = 'span'>({ as, path, children, ...rest }: PreviewFieldProps<T>): React.JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.PreviewField = PreviewField;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* Leaf node for Fivora visual editing. Uses suppressHydrationWarning because the
|
|
8
|
+
* preview focus bridge may annotate the DOM before React hydrates.
|
|
9
|
+
*/
|
|
10
|
+
function PreviewField({ as, path, children, ...rest }) {
|
|
11
|
+
const Component = (as || 'span');
|
|
12
|
+
return ((0, jsx_runtime_1.jsx)(Component, { "data-preview-field-path": path, suppressHydrationWarning: true, ...rest, children: children }));
|
|
13
|
+
}
|
package/dist/SiteDataProvider.js
CHANGED
|
@@ -271,8 +271,12 @@ function SiteDataProvider({ children, initialSiteData, fallbackSiteData, }) {
|
|
|
271
271
|
window.parent.postMessage({ type: exports.LEGACY_PREVIEW_READY_MESSAGE, pathname: window.location.pathname }, parentOrigin ?? '*');
|
|
272
272
|
return () => window.removeEventListener('message', onMessage);
|
|
273
273
|
}, []);
|
|
274
|
+
const [hydrated, setHydrated] = (0, react_1.useState)(false);
|
|
275
|
+
(0, react_1.useEffect)(() => {
|
|
276
|
+
setHydrated(true);
|
|
277
|
+
}, []);
|
|
274
278
|
const value = (0, react_1.useMemo)(() => siteData, [siteData]);
|
|
275
|
-
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] }));
|
|
279
|
+
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, {}), hydrated ? (children) : ((0, jsx_runtime_1.jsx)("div", { className: "min-h-screen", style: { background: 'var(--page-bg, #f9f5ef)' }, suppressHydrationWarning: true, "aria-hidden": true }))] }));
|
|
276
280
|
}
|
|
277
281
|
function useSiteData() {
|
|
278
282
|
return (0, react_1.useContext)(exports.SiteDataContext);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface CollageImageItem {
|
|
3
|
+
id?: string;
|
|
4
|
+
image?: string | null;
|
|
5
|
+
caption?: string | null;
|
|
6
|
+
}
|
|
7
|
+
export interface HeritageCollageProps {
|
|
8
|
+
collageHeading?: React.ReactNode;
|
|
9
|
+
collageDescription?: React.ReactNode;
|
|
10
|
+
historyHeading?: React.ReactNode;
|
|
11
|
+
history?: React.ReactNode;
|
|
12
|
+
images?: CollageImageItem[];
|
|
13
|
+
collageHeadingPath?: string;
|
|
14
|
+
collageDescriptionPath?: string;
|
|
15
|
+
historyHeadingPath?: string;
|
|
16
|
+
historyPath?: string;
|
|
17
|
+
listPath?: string;
|
|
18
|
+
className?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Heritage collage + history block for About pages.
|
|
22
|
+
* List images use standard Fivora list/item/field path attributes.
|
|
23
|
+
*/
|
|
24
|
+
export declare function HeritageCollage({ collageHeading, collageDescription, historyHeading, history, images, collageHeadingPath, collageDescriptionPath, historyHeadingPath, historyPath, listPath, className, }: HeritageCollageProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.HeritageCollage = HeritageCollage;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const urls_1 = require("../utils/urls");
|
|
7
|
+
const COLLAGE_LAYOUT = [
|
|
8
|
+
'row-span-2 min-h-[220px]',
|
|
9
|
+
'min-h-[140px]',
|
|
10
|
+
'min-h-[140px]',
|
|
11
|
+
'col-span-2 min-h-[180px]',
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* Heritage collage + history block for About pages.
|
|
15
|
+
* List images use standard Fivora list/item/field path attributes.
|
|
16
|
+
*/
|
|
17
|
+
function HeritageCollage({ collageHeading, collageDescription, historyHeading, history, images = [], collageHeadingPath, collageDescriptionPath, historyHeadingPath, historyPath, listPath = 'about.collageImages', className = '', }) {
|
|
18
|
+
const hasContent = images.length > 0 ||
|
|
19
|
+
(typeof collageDescription === 'string' ? collageDescription.trim() : collageDescription) ||
|
|
20
|
+
(typeof history === 'string' ? history.trim() : history);
|
|
21
|
+
if (!hasContent)
|
|
22
|
+
return null;
|
|
23
|
+
return ((0, jsx_runtime_1.jsx)("section", { className: `deneb-heritage-collage ${className}`.trim(), "data-design-section": "heritage-collage", style: { marginTop: '3rem', paddingTop: '2.5rem', borderTop: '1px solid var(--color-border, #e2e8f0)' }, children: (0, jsx_runtime_1.jsxs)("div", { style: {
|
|
24
|
+
display: 'grid',
|
|
25
|
+
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
|
|
26
|
+
gap: '2rem',
|
|
27
|
+
alignItems: 'start',
|
|
28
|
+
}, children: [images.length > 0 ? ((0, jsx_runtime_1.jsx)("div", { "data-preview-list-path": listPath, style: {
|
|
29
|
+
display: 'grid',
|
|
30
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
31
|
+
gap: '0.75rem',
|
|
32
|
+
gridAutoRows: 'minmax(120px, auto)',
|
|
33
|
+
}, children: images.map((item, idx) => ((0, jsx_runtime_1.jsxs)("figure", { "data-preview-item-path": `${listPath}[${idx}]`, className: COLLAGE_LAYOUT[idx] ?? COLLAGE_LAYOUT[1], style: {
|
|
34
|
+
position: 'relative',
|
|
35
|
+
overflow: 'hidden',
|
|
36
|
+
borderRadius: '1rem',
|
|
37
|
+
border: '1px solid var(--color-border, #e2e8f0)',
|
|
38
|
+
margin: 0,
|
|
39
|
+
minHeight: '140px',
|
|
40
|
+
background: 'var(--color-secondary, #f1f5f9)',
|
|
41
|
+
}, children: [(0, jsx_runtime_1.jsx)("img", { src: (0, urls_1.withBasePath)(item.image || '/placeholder.svg'), alt: "", "data-preview-field-path": `${listPath}[${idx}].image`, style: { width: '100%', height: '100%', objectFit: 'cover', display: 'block' } }), item.caption ? ((0, jsx_runtime_1.jsx)("figcaption", { style: {
|
|
42
|
+
position: 'absolute',
|
|
43
|
+
insetInline: 0,
|
|
44
|
+
bottom: 0,
|
|
45
|
+
padding: '0.75rem 1rem',
|
|
46
|
+
background: 'linear-gradient(transparent, rgba(15,23,42,0.85))',
|
|
47
|
+
color: '#fff',
|
|
48
|
+
fontSize: '0.8125rem',
|
|
49
|
+
fontWeight: 600,
|
|
50
|
+
}, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `${listPath}[${idx}].caption`, children: item.caption }) })) : null] }, item.id || `collage-${idx}`))) })) : null, (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [collageHeading ? ((0, jsx_runtime_1.jsx)("h2", { ...(collageHeadingPath ? { 'data-preview-field-path': collageHeadingPath } : {}), style: { fontSize: '1.5rem', fontWeight: 700, margin: 0 }, children: collageHeading })) : null, collageDescription ? ((0, jsx_runtime_1.jsx)("p", { ...(collageDescriptionPath ? { 'data-preview-field-path': collageDescriptionPath } : {}), style: { margin: 0, lineHeight: 1.6, color: 'var(--color-muted, #64748b)' }, children: collageDescription })) : null, historyHeading ? ((0, jsx_runtime_1.jsx)("h3", { ...(historyHeadingPath ? { 'data-preview-field-path': historyHeadingPath } : {}), style: { fontSize: '1.125rem', fontWeight: 700, margin: '0.5rem 0 0' }, children: historyHeading })) : null, history ? ((0, jsx_runtime_1.jsx)("p", { ...(historyPath ? { 'data-preview-field-path': historyPath } : {}), style: { margin: 0, lineHeight: 1.7 }, children: history })) : null] })] }) }));
|
|
51
|
+
}
|
package/dist/business/index.d.ts
CHANGED
package/dist/business/index.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface ListActionCtaProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
3
|
+
/** List path without index, e.g. `seasonal.preOrderCta`. */
|
|
4
|
+
listPath: string;
|
|
5
|
+
itemIndex?: number;
|
|
6
|
+
/** When true, stamps list/item paths and editable label + hidden URL (use on first card only). */
|
|
7
|
+
editable?: boolean;
|
|
8
|
+
buttonLabel: string;
|
|
9
|
+
buttonUrl: string;
|
|
10
|
+
urlMode?: 'auto' | 'external';
|
|
11
|
+
/** Optional icons or extra content rendered before the label span. */
|
|
12
|
+
leading?: React.ReactNode;
|
|
13
|
+
trailing?: React.ReactNode;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Shared list CTA for grids (Pre-Order, Add to Tray, etc.).
|
|
17
|
+
* Edit via the first card's button; other cards mirror the same label/URL statically.
|
|
18
|
+
*/
|
|
19
|
+
export declare function ListActionCta({ listPath, itemIndex, editable, buttonLabel, buttonUrl, urlMode, leading, trailing, className, ...rest }: ListActionCtaProps): React.JSX.Element;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.ListActionCta = ListActionCta;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const urls_1 = require("../utils/urls");
|
|
7
|
+
/**
|
|
8
|
+
* Shared list CTA for grids (Pre-Order, Add to Tray, etc.).
|
|
9
|
+
* Edit via the first card's button; other cards mirror the same label/URL statically.
|
|
10
|
+
*/
|
|
11
|
+
function ListActionCta({ listPath, itemIndex = 0, editable = true, buttonLabel, buttonUrl, urlMode = 'auto', leading, trailing, className, ...rest }) {
|
|
12
|
+
const href = (0, urls_1.resolveActionUrl)(buttonUrl, urlMode);
|
|
13
|
+
const labelPath = `${listPath}[${itemIndex}].buttonLabel`;
|
|
14
|
+
const urlPath = `${listPath}[${itemIndex}].buttonUrl`;
|
|
15
|
+
const opensNewTab = Boolean(href && /^https?:\/\//i.test(href));
|
|
16
|
+
const link = ((0, jsx_runtime_1.jsxs)("a", { href: href || '#', target: opensNewTab ? '_blank' : undefined, rel: opensNewTab ? 'noopener noreferrer' : undefined, className: className, "data-preview-static": editable ? undefined : 'list-action-cta', ...rest, children: [leading, editable ? ((0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": labelPath, children: buttonLabel })) : ((0, jsx_runtime_1.jsx)("span", { "data-preview-static": "list-action-cta-label", children: buttonLabel })), trailing] }));
|
|
17
|
+
if (!editable)
|
|
18
|
+
return link;
|
|
19
|
+
return ((0, jsx_runtime_1.jsx)("div", { "data-preview-list-path": listPath, children: (0, jsx_runtime_1.jsxs)("div", { "data-preview-item-path": `${listPath}[${itemIndex}]`, children: [link, (0, jsx_runtime_1.jsx)("span", { hidden: true, "aria-hidden": "true", "data-preview-field-path": urlPath, children: buttonUrl })] }) }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface SplitFieldActionProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
|
|
3
|
+
/** Visible button / link label (inspector reads textContent). */
|
|
4
|
+
label: React.ReactNode;
|
|
5
|
+
/** Raw URL stored in site data (hidden from the visible label). */
|
|
6
|
+
urlValue: string;
|
|
7
|
+
labelFieldPath: string;
|
|
8
|
+
urlFieldPath: string;
|
|
9
|
+
/** When `auto`, bare phone numbers become WhatsApp links. */
|
|
10
|
+
urlMode?: 'auto' | 'external';
|
|
11
|
+
icon?: React.ReactNode;
|
|
12
|
+
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link';
|
|
13
|
+
size?: 'sm' | 'md' | 'lg';
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fivora-safe action link: editable label on the anchor, URL on a hidden sibling span.
|
|
17
|
+
* Never put the URL on the same node as the label — the inspector overwrites paired fields.
|
|
18
|
+
*/
|
|
19
|
+
export declare function SplitFieldAction({ label, urlValue, labelFieldPath, urlFieldPath, urlMode, href, icon, variant, size, className, style, children, ...rest }: SplitFieldActionProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.SplitFieldAction = SplitFieldAction;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
const urls_1 = require("../utils/urls");
|
|
7
|
+
/**
|
|
8
|
+
* Fivora-safe action link: editable label on the anchor, URL on a hidden sibling span.
|
|
9
|
+
* Never put the URL on the same node as the label — the inspector overwrites paired fields.
|
|
10
|
+
*/
|
|
11
|
+
function SplitFieldAction({ label, urlValue, labelFieldPath, urlFieldPath, urlMode = 'auto', href, icon, variant = 'primary', size = 'md', className = '', style, children, ...rest }) {
|
|
12
|
+
const resolvedHref = href || (0, urls_1.resolveActionUrl)(urlValue, urlMode);
|
|
13
|
+
if (!resolvedHref && !label && !children)
|
|
14
|
+
return null;
|
|
15
|
+
const baseStyles = {
|
|
16
|
+
display: 'inline-flex',
|
|
17
|
+
alignItems: 'center',
|
|
18
|
+
justifyContent: 'center',
|
|
19
|
+
gap: '0.5rem',
|
|
20
|
+
fontWeight: 500,
|
|
21
|
+
textDecoration: 'none',
|
|
22
|
+
transition: 'all 0.2s ease',
|
|
23
|
+
cursor: 'pointer',
|
|
24
|
+
borderRadius: '0.5rem',
|
|
25
|
+
...style,
|
|
26
|
+
};
|
|
27
|
+
const sizeStyles = {
|
|
28
|
+
sm: { fontSize: '0.875rem', padding: '0.375rem 0.75rem' },
|
|
29
|
+
md: { fontSize: '0.95rem', padding: '0.5rem 1rem' },
|
|
30
|
+
lg: { fontSize: '1.05rem', padding: '0.75rem 1.5rem' },
|
|
31
|
+
};
|
|
32
|
+
const variantStyles = {
|
|
33
|
+
primary: {
|
|
34
|
+
backgroundColor: 'var(--color-primary, #0f172a)',
|
|
35
|
+
color: '#ffffff',
|
|
36
|
+
},
|
|
37
|
+
secondary: {
|
|
38
|
+
backgroundColor: 'var(--color-secondary, #f1f5f9)',
|
|
39
|
+
color: 'var(--color-text, #0f172a)',
|
|
40
|
+
},
|
|
41
|
+
outline: {
|
|
42
|
+
backgroundColor: 'transparent',
|
|
43
|
+
border: '1px solid var(--color-border, #e2e8f0)',
|
|
44
|
+
color: 'var(--color-text, #0f172a)',
|
|
45
|
+
},
|
|
46
|
+
ghost: {
|
|
47
|
+
backgroundColor: 'transparent',
|
|
48
|
+
color: 'var(--color-text, #0f172a)',
|
|
49
|
+
},
|
|
50
|
+
link: {
|
|
51
|
+
backgroundColor: 'transparent',
|
|
52
|
+
color: 'var(--color-primary, #2563eb)',
|
|
53
|
+
padding: 0,
|
|
54
|
+
textDecoration: 'underline',
|
|
55
|
+
borderRadius: 0,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const mergedStyles = {
|
|
59
|
+
...baseStyles,
|
|
60
|
+
...(variant !== 'link' ? sizeStyles[size] : {}),
|
|
61
|
+
...variantStyles[variant],
|
|
62
|
+
};
|
|
63
|
+
const finalHref = resolvedHref || '#';
|
|
64
|
+
const opensNewTab = finalHref !== '#' && /^https?:\/\//i.test(finalHref);
|
|
65
|
+
return ((0, jsx_runtime_1.jsxs)("span", { className: "deneb-split-field-action", style: { display: 'contents' }, children: [(0, jsx_runtime_1.jsxs)("a", { href: finalHref, "data-preview-static": "split-field-action", target: opensNewTab ? '_blank' : undefined, rel: opensNewTab ? 'noopener noreferrer' : undefined, className: `deneb-split-field-action-link ${className}`.trim(), style: mergedStyles, ...rest, children: [icon, children, (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": labelFieldPath, children: label })] }), (0, jsx_runtime_1.jsx)("span", { hidden: true, "aria-hidden": "true", "data-preview-field-path": urlFieldPath, children: urlValue })] }));
|
|
66
|
+
}
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -15,3 +15,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
__exportStar(require("./DenebAction"), exports);
|
|
18
|
+
__exportStar(require("./SplitFieldAction"), exports);
|
|
19
|
+
__exportStar(require("./ListActionCta"), exports);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* DENEB UI — The Visual-First React Framework for High-Converting Storefronts
|
|
3
3
|
* Created by Chamika Gayashan & Induranga Kawishwara
|
|
4
4
|
*/
|
|
5
|
+
export * from './PreviewField';
|
|
5
6
|
export * from './EditableText';
|
|
6
7
|
export * from './EditableImage';
|
|
7
8
|
export * from './EditableList';
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.Hero = exports.Footer = exports.Header = exports.Navbar = exports.ContactForm = exports.FAQ = exports.Accordion = exports.TestimonialCard = exports.PricingCard = exports.ServiceCard = exports.CustomerReviews = exports.ProductDetail = exports.ProductGrid = exports.ProductCard = exports.List = exports.Box = exports.Section = exports.Grid = 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
22
|
exports.DENEB_AUTHOR = exports.DENEB_FRAMEWORK_VERSION = exports.DENEB_FRAMEWORK_NAME = exports.FilterSidebar = exports.CartDrawer = exports.CategoryPills = exports.AnnouncementBar = exports.HeroSplit = void 0;
|
|
23
|
+
__exportStar(require("./PreviewField"), exports);
|
|
23
24
|
__exportStar(require("./EditableText"), exports);
|
|
24
25
|
__exportStar(require("./EditableImage"), exports);
|
|
25
26
|
__exportStar(require("./EditableList"), exports);
|
|
@@ -9,7 +9,7 @@ const EditableText_1 = require("../EditableText");
|
|
|
9
9
|
/**
|
|
10
10
|
* Premium storefront LocationCard displaying address, pin icon, and Google Maps directions button.
|
|
11
11
|
*/
|
|
12
|
-
function LocationCard({ title = 'Our Location', titleFieldPath, address, city, country, postalCode, mapUrl, fieldPath, addressFieldPath = 'common.business.location.address', mapUrlFieldPath = 'common.business.location.
|
|
12
|
+
function LocationCard({ title = 'Our Location', titleFieldPath, address, city, country, postalCode, mapUrl, fieldPath, addressFieldPath = 'common.business.location.address', mapUrlFieldPath = 'common.business.location.addressUrl', directionsLabel = 'Get Directions →', className = '', style, }) {
|
|
13
13
|
if (!address && !city && !country && !mapUrl) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
@@ -8,7 +8,7 @@ const MapLink_1 = require("./MapLink");
|
|
|
8
8
|
/**
|
|
9
9
|
* Combines physical address with an interactive "Get Directions" action.
|
|
10
10
|
*/
|
|
11
|
-
function LocationLink({ address, city, country, mapUrl, fieldPath, addressFieldPath = 'common.business.location.address', mapUrlFieldPath = 'common.business.location.
|
|
11
|
+
function LocationLink({ address, city, country, mapUrl, fieldPath, addressFieldPath = 'common.business.location.address', mapUrlFieldPath = 'common.business.location.addressUrl', directionsLabel = 'Get Directions', showAddress = true, className = '', style, }) {
|
|
12
12
|
if (!address && !city && !country && !mapUrl) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
@@ -5,9 +5,17 @@ export interface MapLinkProps extends React.AnchorHTMLAttributes<HTMLAnchorEleme
|
|
|
5
5
|
city?: string | null;
|
|
6
6
|
country?: string | null;
|
|
7
7
|
label?: React.ReactNode;
|
|
8
|
+
/** Visible label field path, e.g. `contact.directionsLabel`. */
|
|
9
|
+
labelFieldPath?: string;
|
|
10
|
+
/** Hidden URL field path, e.g. `contact.directionsUrl`. */
|
|
11
|
+
urlFieldPath?: string;
|
|
12
|
+
/** @deprecated Use `urlFieldPath` instead. */
|
|
8
13
|
fieldPath?: string;
|
|
9
14
|
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'link';
|
|
10
15
|
size?: 'sm' | 'md' | 'lg';
|
|
11
16
|
icon?: React.ReactNode;
|
|
12
17
|
}
|
|
13
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Directions CTA with Fivora-safe split fields: label on the button, URL on a hidden sibling.
|
|
20
|
+
*/
|
|
21
|
+
export declare function MapLink({ mapUrl, address, city, country, label, labelFieldPath, urlFieldPath, fieldPath, variant, size, icon, className, style, ...rest }: MapLinkProps): React.JSX.Element | null;
|
package/dist/location/MapLink.js
CHANGED
|
@@ -4,11 +4,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
4
4
|
exports.MapLink = MapLink;
|
|
5
5
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
6
|
const urls_1 = require("../utils/urls");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Directions CTA with Fivora-safe split fields: label on the button, URL on a hidden sibling.
|
|
9
|
+
*/
|
|
10
|
+
function MapLink({ mapUrl, address, city, country, label = 'Get Directions', labelFieldPath, urlFieldPath, fieldPath, variant = 'outline', size = 'md', icon, className = '', style, ...rest }) {
|
|
11
|
+
const resolvedUrlFieldPath = urlFieldPath || fieldPath || 'contact.directionsUrl';
|
|
12
|
+
const resolvedLabelFieldPath = labelFieldPath || resolvedUrlFieldPath.replace(/Url$/, 'Label');
|
|
13
|
+
const resolvedHref = (0, urls_1.createMapUrl)({ mapUrl, addressUrl: mapUrl, address, city, country });
|
|
14
|
+
const urlText = (mapUrl && mapUrl.trim()) || resolvedHref;
|
|
15
|
+
const finalHref = resolvedHref || '#';
|
|
16
|
+
if (!finalHref && !label)
|
|
12
17
|
return null;
|
|
13
18
|
const defaultIcon = ((0, jsx_runtime_1.jsxs)("svg", { "data-preview-static": "map-link-icon", width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [(0, jsx_runtime_1.jsx)("path", { d: "M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0Z" }), (0, jsx_runtime_1.jsx)("circle", { cx: "12", cy: "10", r: "3" })] }));
|
|
14
19
|
const baseStyles = {
|
|
@@ -59,5 +64,6 @@ function MapLink({ mapUrl, address, city, country, label = 'Get Directions', fie
|
|
|
59
64
|
...(variant !== 'link' ? sizeStyles[size] : {}),
|
|
60
65
|
...variantStyles[variant],
|
|
61
66
|
};
|
|
62
|
-
|
|
67
|
+
const opensNewTab = finalHref !== '#';
|
|
68
|
+
return ((0, jsx_runtime_1.jsxs)("span", { className: "deneb-map-link-group", style: { display: 'contents' }, children: [(0, jsx_runtime_1.jsxs)("a", { href: finalHref, "data-preview-static": "map-link", 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 })] }), (0, jsx_runtime_1.jsx)("span", { hidden: true, "aria-hidden": "true", "data-preview-field-path": resolvedUrlFieldPath, children: urlText })] }));
|
|
63
69
|
}
|
package/dist/utils/urls.d.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export interface MapLocationInput {
|
|
6
6
|
mapUrl?: string | null;
|
|
7
|
+
/** Fivora-paired sibling of `address` (`addressUrl` shares the `address` inspector stem). */
|
|
8
|
+
addressUrl?: string | null;
|
|
7
9
|
address?: string | null;
|
|
8
10
|
city?: string | null;
|
|
9
11
|
country?: string | null;
|
|
@@ -26,6 +28,11 @@ export declare function createEmailUrl(email?: string | null, subject?: string |
|
|
|
26
28
|
* Prefers an explicit mapUrl, otherwise constructs a query using address, city, and country.
|
|
27
29
|
*/
|
|
28
30
|
export declare function createMapUrl(location?: MapLocationInput | string | null): string;
|
|
31
|
+
/**
|
|
32
|
+
* Resolves a merchant-entered action URL for CTAs (WhatsApp, tel, mailto, or external http(s)).
|
|
33
|
+
* Phone-like values without a scheme become WhatsApp links when `mode` is `auto`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveActionUrl(raw?: string | null, mode?: 'auto' | 'external'): string;
|
|
29
36
|
/**
|
|
30
37
|
* Validates if a string is a safe, valid external link (prevents script injection).
|
|
31
38
|
*/
|
|
@@ -37,4 +44,7 @@ export declare function withBasePath(value?: string | null): string;
|
|
|
37
44
|
/**
|
|
38
45
|
* Resolves standard page route from pageKey.
|
|
39
46
|
*/
|
|
40
|
-
export declare function
|
|
47
|
+
export declare function resolvePageRoute(pageKey: string, pages?: Array<{
|
|
48
|
+
id: string;
|
|
49
|
+
route: string;
|
|
50
|
+
}>): string;
|
package/dist/utils/urls.js
CHANGED
|
@@ -8,9 +8,10 @@ exports.createWhatsAppUrl = createWhatsAppUrl;
|
|
|
8
8
|
exports.createPhoneUrl = createPhoneUrl;
|
|
9
9
|
exports.createEmailUrl = createEmailUrl;
|
|
10
10
|
exports.createMapUrl = createMapUrl;
|
|
11
|
+
exports.resolveActionUrl = resolveActionUrl;
|
|
11
12
|
exports.isSafeExternalLink = isSafeExternalLink;
|
|
12
13
|
exports.withBasePath = withBasePath;
|
|
13
|
-
exports.
|
|
14
|
+
exports.resolvePageRoute = resolvePageRoute;
|
|
14
15
|
/**
|
|
15
16
|
* Creates a valid WhatsApp click-to-chat URL.
|
|
16
17
|
* Automatically cleans spaces, dashes, and special characters from the phone number.
|
|
@@ -65,8 +66,9 @@ function createMapUrl(location) {
|
|
|
65
66
|
}
|
|
66
67
|
return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(location)}`;
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
const explicitUrl = (location.addressUrl || location.mapUrl || '').trim();
|
|
70
|
+
if (explicitUrl) {
|
|
71
|
+
return explicitUrl;
|
|
70
72
|
}
|
|
71
73
|
const queryParts = [location.address, location.city, location.country]
|
|
72
74
|
.filter((part) => Boolean(part && part.trim()))
|
|
@@ -76,6 +78,25 @@ function createMapUrl(location) {
|
|
|
76
78
|
}
|
|
77
79
|
return '';
|
|
78
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Resolves a merchant-entered action URL for CTAs (WhatsApp, tel, mailto, or external http(s)).
|
|
83
|
+
* Phone-like values without a scheme become WhatsApp links when `mode` is `auto`.
|
|
84
|
+
*/
|
|
85
|
+
function resolveActionUrl(raw, mode = 'auto') {
|
|
86
|
+
const value = raw?.trim() ?? '';
|
|
87
|
+
if (!value)
|
|
88
|
+
return '';
|
|
89
|
+
if (mode === 'external') {
|
|
90
|
+
return isSafeExternalLink(value) ? value : '';
|
|
91
|
+
}
|
|
92
|
+
if (/^https?:\/\//i.test(value) || /^tel:/i.test(value) || /^mailto:/i.test(value)) {
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
if (/^\+?[\d\s().-]+$/.test(value)) {
|
|
96
|
+
return createWhatsAppUrl(value);
|
|
97
|
+
}
|
|
98
|
+
return isSafeExternalLink(value) ? value : '';
|
|
99
|
+
}
|
|
79
100
|
/**
|
|
80
101
|
* Validates if a string is a safe, valid external link (prevents script injection).
|
|
81
102
|
*/
|
|
@@ -105,6 +126,7 @@ function withBasePath(value) {
|
|
|
105
126
|
/**
|
|
106
127
|
* Resolves standard page route from pageKey.
|
|
107
128
|
*/
|
|
108
|
-
function
|
|
109
|
-
|
|
129
|
+
function resolvePageRoute(pageKey, pages) {
|
|
130
|
+
const match = pages?.find((page) => page.id === pageKey);
|
|
131
|
+
return match?.route ?? `/${pageKey.replace(/_/g, '/')}`;
|
|
110
132
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.33",
|
|
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.33"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": "^18.0.0 || ^19.0.0",
|