@deneb-ui/ui 2.0.118 → 2.0.120

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.
@@ -153,9 +153,9 @@ function EditableFooter({ defaultLinks = DEFAULT_FOOTER_PAGES, links, basePath =
153
153
  return ((0, jsx_runtime_1.jsxs)("footer", { style: {
154
154
  position: 'relative',
155
155
  width: '100%',
156
- backgroundColor: '#020617', // slate-950
157
- color: '#f8fafc',
158
- borderTop: '1px solid rgba(255, 255, 255, 0.08)',
156
+ backgroundColor: 'var(--footer-bg, var(--page-background, #020617))',
157
+ color: 'var(--color-text, #f8fafc)',
158
+ borderTop: '1px solid var(--color-border, rgba(255, 255, 255, 0.08))',
159
159
  padding: '4.5rem 1.5rem 2rem',
160
160
  ...style,
161
161
  }, className: `deneb-footer relative w-full border-t border-slate-800/80 bg-slate-950 text-slate-100 ${className}`.trim(), ...props, children: [(0, jsx_runtime_1.jsx)("div", { style: {
@@ -9,6 +9,17 @@ export interface EditableNavbarProps extends React.HTMLAttributes<HTMLElement> {
9
9
  sticky?: boolean;
10
10
  activeRoute?: string;
11
11
  basePath?: string;
12
+ showSearch?: boolean;
13
+ searchFieldPath?: string;
14
+ searchLabel?: string;
15
+ onSearchClick?: () => void;
16
+ searchUrl?: string;
17
+ showCart?: boolean;
18
+ cartCount?: number;
19
+ onCartClick?: () => void;
20
+ showWishlist?: boolean;
21
+ wishlistCount?: number;
22
+ onWishlistClick?: () => void;
12
23
  }
13
24
  /**
14
25
  * Next-Level DENEB UI Navbar / Header
@@ -23,6 +34,6 @@ export interface EditableNavbarProps extends React.HTMLAttributes<HTMLElement> {
23
34
  *
24
35
  * Created by Chamika Gayashan & Induranga Kawishwara
25
36
  */
26
- export declare function EditableNavbar({ defaultLinks, sticky, activeRoute, basePath, className, style, ...props }: EditableNavbarProps): React.JSX.Element;
37
+ export declare function EditableNavbar({ defaultLinks, sticky, activeRoute, basePath, showSearch, searchFieldPath, searchLabel: customSearchLabel, onSearchClick, searchUrl, showCart, cartCount, onCartClick, showWishlist, wishlistCount, onWishlistClick, className, style, ...props }: EditableNavbarProps): React.JSX.Element;
27
38
  export declare const Navbar: typeof EditableNavbar;
28
39
  export declare const Header: typeof EditableNavbar;
@@ -25,7 +25,7 @@ const DEFAULT_PAGES = [
25
25
  *
26
26
  * Created by Chamika Gayashan & Induranga Kawishwara
27
27
  */
28
- function EditableNavbar({ defaultLinks = DEFAULT_PAGES, sticky = true, activeRoute, basePath = '', className = '', style, ...props }) {
28
+ function EditableNavbar({ defaultLinks = DEFAULT_PAGES, sticky = true, activeRoute, basePath = "", showSearch = true, searchFieldPath = "common.header.searchFramesLabel", searchLabel: customSearchLabel, onSearchClick, searchUrl = "/shop", showCart = false, cartCount, onCartClick, showWishlist = false, wishlistCount, onWishlistClick, className = "", style, ...props }) {
29
29
  const siteData = (0, SiteDataProvider_1.useSiteData)();
30
30
  const [mobileMenuOpen, setMobileMenuOpen] = (0, react_1.useState)(false);
31
31
  const content = (siteData?.content || {});
@@ -35,6 +35,11 @@ function EditableNavbar({ defaultLinks = DEFAULT_PAGES, sticky = true, activeRou
35
35
  const logoUrl = String(common?.logoUrl || shop?.logoUrl || '');
36
36
  const headerCtaLabel = String(common?.headerCtaLabel || 'Get in Touch');
37
37
  const navLabels = (common?.navLabels || {});
38
+ const searchLabel = String(customSearchLabel ||
39
+ common?.header?.searchFramesLabel ||
40
+ common?.searchFramesLabel ||
41
+ common?.searchPlaceholder ||
42
+ "Search frames");
38
43
  const requiredPages = Array.isArray(siteData?.requirements?.requiredPages)
39
44
  ? siteData.requirements.requiredPages
40
45
  : siteData?.template?.structure?.pages || ['home', 'contact'];
@@ -98,28 +103,46 @@ function EditableNavbar({ defaultLinks = DEFAULT_PAGES, sticky = true, activeRou
98
103
  fontWeight: 700,
99
104
  letterSpacing: '-0.025em',
100
105
  color: 'var(--color-text, #0f172a)',
101
- }, className: "deneb-site-title text-xl font-bold tracking-tight text-slate-900 dark:text-white", children: siteName })] }), (0, jsx_runtime_1.jsx)("nav", { style: {
106
+ }, className: "deneb-site-title text-xl font-bold tracking-tight text-slate-900 dark:text-white", children: siteName })] }), (0, jsx_runtime_1.jsxs)("nav", { style: {
102
107
  alignItems: 'center',
103
108
  gap: '0.375rem',
104
- }, className: "deneb-desktop-nav flex items-center gap-1.5", children: activeLinks.map((link) => {
105
- const label = navLabels[link.id] || link.label;
106
- const isActive = activeRoute === link.route;
107
- return ((0, jsx_runtime_1.jsx)("span", { "data-target-page": link.id, children: (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => navigate(link.route), style: {
108
- display: 'inline-flex',
109
- alignItems: 'center',
110
- padding: '0.45rem 0.875rem',
111
- borderRadius: '9999px',
112
- fontSize: '0.9rem',
113
- fontWeight: isActive ? 600 : 500,
114
- color: isActive ? 'var(--color-primary, #0f172a)' : 'var(--color-text-muted, #475569)',
115
- backgroundColor: isActive ? 'var(--color-secondary, #f1f5f9)' : 'transparent',
116
- border: 0,
117
- cursor: 'pointer',
118
- transition: 'all 0.15s ease',
119
- }, className: `deneb-nav-item px-3.5 py-1.5 rounded-full text-sm font-medium transition-all border-0 cursor-pointer ${isActive
120
- ? 'bg-slate-100 text-slate-900 font-semibold dark:bg-slate-800 dark:text-white'
121
- : 'bg-transparent text-slate-600 hover:text-slate-900 hover:bg-slate-100/70 dark:text-slate-400 dark:hover:text-white dark:hover:bg-slate-800/70'}`, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `common.navLabels.${link.id}`, children: label }) }) }, link.id));
122
- }) }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: '0.75rem' }, className: "flex items-center gap-3", children: [hasContact ? ((0, jsx_runtime_1.jsx)("span", { "data-target-page": "contact", children: (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => navigate('/contact'), style: {
109
+ }, className: "deneb-desktop-nav flex items-center gap-1.5", children: [showSearch && ((0, jsx_runtime_1.jsx)("div", { style: { marginBottom: "0.5rem" }, children: (0, jsx_runtime_1.jsxs)("button", { type: "button", onClick: () => {
110
+ setMobileMenuOpen(false);
111
+ if (onSearchClick)
112
+ onSearchClick();
113
+ else
114
+ navigate(searchUrl);
115
+ }, "data-preview-static": "header-search-toggle", style: {
116
+ display: "flex",
117
+ width: "100%",
118
+ alignItems: "center",
119
+ gap: "0.75rem",
120
+ padding: "0.625rem 0.875rem",
121
+ borderRadius: "8px",
122
+ border: "1px solid var(--color-border, #e2e8f0)",
123
+ backgroundColor: "transparent",
124
+ color: "var(--color-text-muted, #64748b)",
125
+ cursor: "pointer",
126
+ fontSize: "0.9rem",
127
+ }, className: "w-full flex items-center gap-2.5 px-3.5 py-2.5 rounded-lg border border-slate-200 text-slate-500 dark:border-slate-800 dark:text-slate-400", children: [(0, jsx_runtime_1.jsxs)("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", "data-preview-static": "decorative-icon", children: [(0, jsx_runtime_1.jsx)("circle", { cx: "11", cy: "11", r: "8" }), (0, jsx_runtime_1.jsx)("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })] }), (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": searchFieldPath, children: searchLabel })] }) })), activeLinks.map((link) => {
128
+ const label = navLabels[link.id] || link.label;
129
+ const isActive = activeRoute === link.route;
130
+ return ((0, jsx_runtime_1.jsx)("span", { "data-target-page": link.id, children: (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => navigate(link.route), style: {
131
+ display: 'inline-flex',
132
+ alignItems: 'center',
133
+ padding: '0.45rem 0.875rem',
134
+ borderRadius: '9999px',
135
+ fontSize: '0.9rem',
136
+ fontWeight: isActive ? 600 : 500,
137
+ color: isActive ? 'var(--color-primary, #0f172a)' : 'var(--color-text-muted, #475569)',
138
+ backgroundColor: isActive ? 'var(--color-secondary, #f1f5f9)' : 'transparent',
139
+ border: 0,
140
+ cursor: 'pointer',
141
+ transition: 'all 0.15s ease',
142
+ }, className: `deneb-nav-item px-3.5 py-1.5 rounded-full text-sm font-medium transition-all border-0 cursor-pointer ${isActive
143
+ ? 'bg-slate-100 text-slate-900 font-semibold dark:bg-slate-800 dark:text-white'
144
+ : 'bg-transparent text-slate-600 hover:text-slate-900 hover:bg-slate-100/70 dark:text-slate-400 dark:hover:text-white dark:hover:bg-slate-800/70'}`, children: (0, jsx_runtime_1.jsx)("span", { "data-preview-field-path": `common.navLabels.${link.id}`, children: label }) }) }, link.id));
145
+ })] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: '0.75rem' }, className: "flex items-center gap-3", children: [hasContact ? ((0, jsx_runtime_1.jsx)("span", { "data-target-page": "contact", children: (0, jsx_runtime_1.jsx)("button", { type: "button", onClick: () => navigate('/contact'), style: {
123
146
  display: 'inline-flex',
124
147
  alignItems: 'center',
125
148
  justifyContent: 'center',
@@ -82,35 +82,16 @@ function isDarkColor(color) {
82
82
  function syncThemeToDocument(theme) {
83
83
  if (typeof document === "undefined" || !isRecord(theme))
84
84
  return;
85
+ const vars = (0, core_1.generateThemeVariables)(theme);
85
86
  const rootStyle = document.documentElement.style;
86
- if (typeof theme.primaryColor === "string") {
87
- rootStyle.setProperty("--brand-primary", theme.primaryColor);
88
- rootStyle.setProperty("--brand-color", theme.primaryColor);
89
- rootStyle.setProperty("--color-primary", theme.primaryColor);
90
- const isPrimaryDark = isDarkColor(theme.primaryColor);
91
- const autoBtnText = isPrimaryDark ? "#ffffff" : "#0f172a";
92
- rootStyle.setProperty("--button-bg", String(theme.buttonBackgroundColor || theme.primaryColor));
93
- rootStyle.setProperty("--button-text", String(theme.buttonTextColor || autoBtnText));
94
- }
95
- if (typeof theme.secondaryColor === "string") {
96
- rootStyle.setProperty("--brand-secondary", theme.secondaryColor);
97
- }
98
- if (typeof theme.accentColor === "string") {
99
- rootStyle.setProperty("--brand-accent", theme.accentColor);
100
- rootStyle.setProperty("--color-accent", theme.accentColor);
101
- }
102
- if (typeof theme.backgroundColor === "string") {
103
- const isDark = isDarkColor(theme.backgroundColor);
104
- document.documentElement.classList.toggle("dark", isDark);
105
- document.documentElement.setAttribute("data-theme", isDark ? "dark" : "light");
106
- rootStyle.colorScheme = isDark ? "dark" : "light";
107
- rootStyle.setProperty("--page-background", theme.backgroundColor);
108
- rootStyle.setProperty("--card-bg", isDark ? "#111a2e" : "#ffffff");
109
- rootStyle.setProperty("--product-card-bg", isDark ? "#111a2e" : "#ffffff");
110
- rootStyle.setProperty("--color-surface", isDark ? "rgba(255,255,255,0.05)" : "#ffffff");
111
- rootStyle.setProperty("--color-secondary", isDark ? "rgba(255,255,255,0.08)" : "#f1f5f9");
112
- rootStyle.setProperty("--button-secondary-bg", isDark ? "rgba(255,255,255,0.08)" : "#f1f5f9");
113
- rootStyle.setProperty("--button-secondary-text", isDark ? "#f8fafc" : "#0f172a");
87
+ const isDark = isDarkColor(theme.backgroundColor);
88
+ document.documentElement.classList.toggle("dark", isDark);
89
+ document.documentElement.setAttribute("data-theme", isDark ? "dark" : "light");
90
+ rootStyle.colorScheme = isDark ? "dark" : "light";
91
+ for (const [key, val] of Object.entries(vars)) {
92
+ if (key.startsWith("--")) {
93
+ rootStyle.setProperty(key, val);
94
+ }
114
95
  }
115
96
  }
116
97
  function mergeSiteData(current, incoming, depth = 0, seen = new WeakSet()) {
@@ -631,8 +612,21 @@ function SiteDataProvider({ children, initialSiteData, fallbackSiteData, liveCat
631
612
  }
632
613
  }
633
614
  }, [siteData]);
615
+ const resolvedTheme = (0, react_1.useMemo)(() => {
616
+ const raw = siteData;
617
+ return raw?.theme ||
618
+ raw?.template?.structure?.theme ||
619
+ initialSiteData?.theme ||
620
+ initialSiteData?.template?.structure?.theme ||
621
+ undefined;
622
+ }, [siteData, initialSiteData]);
623
+ (0, react_1.useEffect)(() => {
624
+ if (resolvedTheme) {
625
+ syncThemeToDocument(resolvedTheme);
626
+ }
627
+ }, [resolvedTheme]);
634
628
  const value = (0, react_1.useMemo)(() => siteData, [siteData]);
635
- 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] }));
629
+ return ((0, jsx_runtime_1.jsxs)(exports.SiteDataContext.Provider, { value: value, children: [(0, jsx_runtime_1.jsx)(ThemeStyles_1.ThemeStyles, { theme: resolvedTheme }), (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] }));
636
630
  }
637
631
  function useSiteData() {
638
632
  const ctx = (0, react_1.useContext)(exports.SiteDataContext);
@@ -136,77 +136,10 @@ function getCategoryTheme(category, overrides) {
136
136
  * is automatically converted to a CSS variable (e.g. --card-bg: #111).
137
137
  */
138
138
  function getThemeCssProperties(theme) {
139
- const customVars = {};
140
- if (theme) {
141
- for (const [key, val] of Object.entries(theme)) {
142
- if (key !== 'dark' && key !== 'light' && (typeof val === 'string' || typeof val === 'number')) {
143
- const cssVarName = `--${key.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`;
144
- customVars[cssVarName] = String(val);
145
- }
146
- }
147
- }
148
- const isDark = (0, core_1.isDarkColor)(theme?.backgroundColor);
149
- const bgColor = theme?.backgroundColor || (isDark ? '#090d1a' : '#ffffff');
150
- const textColor = theme?.textColor || (isDark ? '#f8fafc' : '#0f172a');
151
- const mutedColor = theme?.mutedTextColor || (isDark ? 'rgba(248, 250, 252, 0.7)' : '#64748b');
152
- const borderColor = isDark ? 'rgba(255, 255, 255, 0.1)' : '#e2e8f0';
153
- const cardBg = isDark ? '#111a2e' : '#ffffff';
154
- const cardBorder = isDark ? 'rgba(255, 255, 255, 0.09)' : '#e2e8f0';
155
- const primaryColor = theme?.primaryColor || '#2563eb';
156
- const buttonBg = String(theme?.buttonBackgroundColor || primaryColor || '#2563eb');
157
- const autoButtonText = (0, core_1.getAutoContrastTextColor)(buttonBg);
158
- const buttonText = String(theme?.buttonTextColor || autoButtonText);
159
- const buttonSecondaryBg = isDark
160
- ? 'rgba(255, 255, 255, 0.08)'
161
- : (theme?.secondaryColor && !(0, core_1.isDarkColor)(theme.secondaryColor) ? theme.secondaryColor : '#f1f5f9');
162
- const buttonSecondaryText = isDark ? '#f8fafc' : '#0f172a';
139
+ const vars = (0, core_1.generateThemeVariables)(theme);
163
140
  return {
164
- '--brand-color': primaryColor,
165
- '--brand-secondary': theme?.secondaryColor || (isDark ? '#1e293b' : '#0f172a'),
166
- '--brand-accent': theme?.accentColor || '#14b8a6',
167
- '--page-background': bgColor,
168
- '--page-text': textColor,
169
- '--heading-color': theme?.headingColor || (isDark ? '#ffffff' : theme?.secondaryColor || '#0f172a'),
170
- '--muted-text': mutedColor,
171
- '--link-color': theme?.linkColor || primaryColor,
172
- '--hero-min-height': theme?.heroMinHeight || '72vh',
173
- '--section-padding': theme?.sectionPadding || '5rem',
174
- '--base-size': theme?.baseSize || '16px',
175
- '--heading-font': theme?.headingFont || 'Inter, sans-serif',
176
- '--body-font': theme?.bodyFont || 'Inter, sans-serif',
177
- '--border-radius': theme?.borderRadius || '8px',
178
- '--content-align': theme?.align || 'left',
179
- // Canonical color system tokens for light & dark mode harmony
180
- '--color-primary': primaryColor,
181
- '--color-secondary': buttonSecondaryBg,
182
- '--color-accent': theme?.accentColor || '#14b8a6',
183
- '--color-text': textColor,
184
- '--color-text-muted': mutedColor,
185
- '--color-border': borderColor,
186
- '--color-surface': isDark ? 'rgba(255,255,255,0.05)' : '#ffffff',
187
- '--card-bg': cardBg,
188
- '--card-border': cardBorder,
189
- '--product-card-bg': cardBg,
190
- '--product-card-border': cardBorder,
191
- '--tag-bg': isDark ? 'rgba(255, 255, 255, 0.08)' : 'rgba(241, 245, 249, 0.9)',
192
- '--tag-color': mutedColor,
193
- '--card-shadow': isDark ? '0 10px 25px -5px rgba(0, 0, 0, 0.4)' : '0 10px 25px -5px rgba(0, 0, 0, 0.05)',
194
- '--header-bg': isDark ? 'rgba(15, 23, 42, 0.85)' : 'rgba(255, 255, 255, 0.82)',
195
- '--input-bg': isDark ? '#1e293b' : '#ffffff',
196
- '--input-border': isDark ? 'rgba(255, 255, 255, 0.15)' : '#cbd5e1',
197
- '--input-color': textColor,
198
- // Dedicated Button Design Tokens
199
- '--button-bg': buttonBg,
200
- '--button-text': buttonText,
201
- '--button-primary-bg': buttonBg,
202
- '--button-primary-text': buttonText,
203
- '--button-secondary-bg': buttonSecondaryBg,
204
- '--button-secondary-text': buttonSecondaryText,
205
- '--button-outline-border': isDark ? 'rgba(255, 255, 255, 0.22)' : 'currentColor',
206
- '--button-outline-text': isDark ? '#f8fafc' : textColor,
207
- '--button-ghost-text': isDark ? '#f8fafc' : textColor,
141
+ ...vars,
208
142
  fontFamily: theme?.bodyFont || 'Inter, sans-serif',
209
- ...customVars,
210
143
  };
211
144
  }
212
145
  /**
@@ -223,6 +156,10 @@ function ThemeStyles({ theme, enableDualMode = true, }) {
223
156
  :root {
224
157
  ${baseLines}
225
158
  }
159
+ html, body {
160
+ background-color: var(--page-background, var(--background, #ffffff));
161
+ color: var(--page-text, var(--color-text, #0f172a));
162
+ }
226
163
  `;
227
164
  if (enableDualMode) {
228
165
  const isDarkBase = (0, core_1.isDarkColor)(theme?.backgroundColor);
@@ -243,6 +180,10 @@ ${baseLines}
243
180
  css += `
244
181
  .light, [data-theme="light"] {
245
182
  ${lightLines}
183
+ }
184
+ .light body, [data-theme="light"] body {
185
+ background-color: var(--page-background, var(--background, #ffffff));
186
+ color: var(--page-text, var(--color-text, #0f172a));
246
187
  }
247
188
  `;
248
189
  }
@@ -263,6 +204,10 @@ ${lightLines}
263
204
  css += `
264
205
  .dark, [data-theme="dark"] {
265
206
  ${darkLines}
207
+ }
208
+ .dark body, [data-theme="dark"] body {
209
+ background-color: var(--page-background, var(--background, #090d1a));
210
+ color: var(--page-text, var(--color-text, #f8fafc));
266
211
  }
267
212
  `;
268
213
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deneb-ui/ui",
3
- "version": "2.0.118",
3
+ "version": "2.0.120",
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.118"
53
+ "@deneb-ui/core": "^2.0.120"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "react": "^18.0.0 || ^19.0.0",