cite-ui 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Readme.md ADDED
@@ -0,0 +1,162 @@
1
+ # cite-ui
2
+
3
+ A reusable React component library built for FrameStudio client projects. Prop-driven, Tailwind CSS styled, and published on npm.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install cite-ui
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ After installing, add cite-ui to your Tailwind `content` array so styles are picked up correctly:
14
+
15
+ ```js
16
+ // tailwind.config.js
17
+ export default {
18
+ content: [
19
+ "./index.html",
20
+ "./src/**/*.{js,jsx}",
21
+ "./node_modules/cite-ui/src/**/*.{js,jsx}"
22
+ ],
23
+ }
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Components
29
+
30
+ ### Button
31
+
32
+ A flexible button component with three variants.
33
+
34
+ **Props**
35
+
36
+ | Prop | Type | Default | Description |
37
+ |------|------|---------|-------------|
38
+ | `variant` | `"primary"` \| `"secondary"` \| `"whatsapp"` | `"primary"` | Button style |
39
+ | `label` | `string` | — | Button text |
40
+ | `onClick` | `function` | — | Click handler |
41
+ | `className` | `string` | `""` | Extra Tailwind classes |
42
+
43
+ **Usage**
44
+
45
+ ```jsx
46
+ import { Button } from 'cite-ui'
47
+
48
+ <Button variant="primary" label="Get Started" onClick={() => {}} />
49
+ <Button variant="secondary" label="Learn More" onClick={() => {}} />
50
+ <Button variant="whatsapp" label="Chat with us" onClick={() => window.open('https://wa.me/254712345678')} />
51
+ ```
52
+
53
+ ---
54
+
55
+ ### Navbar
56
+
57
+ A responsive sticky navbar with mobile hamburger menu.
58
+
59
+ **Props**
60
+
61
+ | Prop | Type | Default | Description |
62
+ |------|------|---------|-------------|
63
+ | `logo` | `string` | — | Brand name or logo text |
64
+ | `links` | `{ label: string, href: string }[]` | `[]` | Navigation links |
65
+ | `ctaLabel` | `string` | — | CTA button text (optional) |
66
+ | `ctaHref` | `string` | — | CTA button link (optional) |
67
+
68
+ **Usage**
69
+
70
+ ```jsx
71
+ import { Navbar } from 'cite-ui'
72
+
73
+ const links = [
74
+ { label: 'Home', href: '#' },
75
+ { label: 'About', href: '#' },
76
+ { label: 'Services', href: '#' },
77
+ ]
78
+
79
+ <Navbar
80
+ logo="My Business"
81
+ links={links}
82
+ ctaLabel="Get Started"
83
+ ctaHref="#contact"
84
+ />
85
+ ```
86
+
87
+ ---
88
+
89
+ ### Gallery
90
+
91
+ A responsive image grid with lightbox on click.
92
+
93
+ **Props**
94
+
95
+ | Prop | Type | Default | Description |
96
+ |------|------|---------|-------------|
97
+ | `images` | `{ src: string, alt: string }[]` | `[]` | Array of image objects |
98
+ | `columns` | `number` | `3` | Number of columns on desktop (1–6) |
99
+ | `gap` | `number` | `4` | Gap between images (Tailwind spacing scale) |
100
+
101
+ **Usage**
102
+
103
+ ```jsx
104
+ import { Gallery } from 'cite-ui'
105
+
106
+ const images = [
107
+ { src: '/images/photo1.jpg', alt: 'Photo 1' },
108
+ { src: '/images/photo2.jpg', alt: 'Photo 2' },
109
+ { src: '/images/photo3.jpg', alt: 'Photo 3' },
110
+ ]
111
+
112
+ <Gallery images={images} columns={3} gap={4} />
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Full Example
118
+
119
+ ```jsx
120
+ import { Navbar, Button, Gallery } from 'cite-ui'
121
+
122
+ const links = [
123
+ { label: 'Home', href: '#' },
124
+ { label: 'About', href: '#' },
125
+ { label: 'Services', href: '#' },
126
+ ]
127
+
128
+ const images = [
129
+ { src: '/images/photo1.jpg', alt: 'Photo 1' },
130
+ { src: '/images/photo2.jpg', alt: 'Photo 2' },
131
+ { src: '/images/photo3.jpg', alt: 'Photo 3' },
132
+ ]
133
+
134
+ export default function App() {
135
+ return (
136
+ <div>
137
+ <Navbar logo="FrameStudio" links={links} ctaLabel="Get Started" ctaHref="#" />
138
+ <div className="p-8 space-y-6">
139
+ <Button variant="whatsapp" label="Chat with us" onClick={() => window.open('https://wa.me/254712345678')} />
140
+ <Gallery images={images} columns={3} />
141
+ </div>
142
+ </div>
143
+ )
144
+ }
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Peer Dependencies
150
+
151
+ cite-ui requires React 18+ and Tailwind CSS v3 to be installed in your project.
152
+
153
+ ```bash
154
+ npm install react react-dom
155
+ npm install -D tailwindcss
156
+ ```
157
+
158
+ ---
159
+
160
+ ## License
161
+
162
+ MIT
@@ -1,136 +1,198 @@
1
- import { jsxs as s, jsx as e, Fragment as h } from "react/jsx-runtime";
2
- import { useState as d } from "react";
3
- const b = ({ logo: a, links: n = [], ctaLabel: l, ctaHref: t }) => {
4
- const [c, o] = d(!1);
5
- return /* @__PURE__ */ s("nav", { className: "sticky top-0 z-50 bg-white border-b border-gray-200", children: [
6
- /* @__PURE__ */ e("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ s("div", { className: "flex items-center justify-between h-16", children: [
7
- /* @__PURE__ */ e("div", { className: "text-xl font-bold tracking-tight", children: a }),
8
- /* @__PURE__ */ e("div", { className: "hidden md:flex items-center gap-8", children: n.map((r, i) => /* @__PURE__ */ e(
9
- "a",
10
- {
11
- href: r.href,
12
- className: "text-gray-600 hover:text-black transition-colors",
13
- children: r.label
14
- },
15
- i
16
- )) }),
17
- /* @__PURE__ */ s("div", { className: "flex items-center gap-4", children: [
18
- l && t && /* @__PURE__ */ e(
19
- "a",
20
- {
21
- href: t,
22
- className: "hidden md:inline-block bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",
23
- children: l
24
- }
25
- ),
26
- /* @__PURE__ */ e(
27
- "button",
28
- {
29
- onClick: () => o((r) => !r),
30
- className: "md:hidden p-2 rounded-md text-gray-600 hover:text-black",
31
- "aria-label": "Toggle menu",
32
- children: /* @__PURE__ */ e(
33
- "svg",
1
+ import { jsxs as l, jsx as e, Fragment as m } from "react/jsx-runtime";
2
+ import { useState as g } from "react";
3
+ const w = ({
4
+ logo: o,
5
+ links: s = [],
6
+ ctaLabel: r,
7
+ ctaHref: t,
8
+ variant: n = "default",
9
+ cartCount: a = 0,
10
+ onCartClick: d
11
+ }) => {
12
+ const [p, x] = g(!1), c = n === "glass";
13
+ return /* @__PURE__ */ l(
14
+ "nav",
15
+ {
16
+ className: `sticky top-0 z-50 ${c ? "" : "bg-white border-b border-gray-200"}`,
17
+ style: c ? {
18
+ background: "rgba(255,255,255,0.7)",
19
+ backdropFilter: "blur(12px)",
20
+ WebkitBackdropFilter: "blur(12px)",
21
+ borderBottom: "1px solid rgba(255,255,255,0.3)"
22
+ } : {},
23
+ children: [
24
+ /* @__PURE__ */ e("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ l("div", { className: "flex items-center justify-between h-16 relative", children: [
25
+ /* @__PURE__ */ e("div", { className: "flex-shrink-0 text-xl font-bold tracking-tight", children: o }),
26
+ /* @__PURE__ */ e(
27
+ "div",
28
+ {
29
+ className: `items-center gap-8 ${c ? "absolute left-1/2 -translate-x-1/2 hidden md:flex" : "hidden md:flex"}`,
30
+ children: s.map((i, h) => /* @__PURE__ */ e(
31
+ "a",
32
+ {
33
+ href: i.href,
34
+ className: "text-gray-600 hover:text-black transition-colors",
35
+ children: i.label
36
+ },
37
+ h
38
+ ))
39
+ }
40
+ ),
41
+ /* @__PURE__ */ l("div", { className: "flex items-center gap-4", children: [
42
+ c ? /* @__PURE__ */ l(
43
+ "button",
34
44
  {
35
- className: "w-6 h-6",
36
- fill: "none",
37
- stroke: "currentColor",
38
- viewBox: "0 0 24 24",
39
- children: c ? /* @__PURE__ */ e(
40
- "path",
41
- {
42
- strokeLinecap: "round",
43
- strokeLinejoin: "round",
44
- strokeWidth: 2,
45
- d: "M6 18L18 6M6 6l12 12"
46
- }
47
- ) : /* @__PURE__ */ e(
48
- "path",
45
+ onClick: d,
46
+ className: "relative p-2 text-gray-600 hover:text-black transition-colors",
47
+ children: [
48
+ /* @__PURE__ */ e(
49
+ "svg",
50
+ {
51
+ className: "w-6 h-6",
52
+ fill: "none",
53
+ stroke: "currentColor",
54
+ viewBox: "0 0 24 24",
55
+ children: /* @__PURE__ */ e(
56
+ "path",
57
+ {
58
+ strokeLinecap: "round",
59
+ strokeLinejoin: "round",
60
+ strokeWidth: 2,
61
+ d: "M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 100 4 2 2 0 000-4z"
62
+ }
63
+ )
64
+ }
65
+ ),
66
+ a > 0 && /* @__PURE__ */ e("span", { className: "absolute -top-1 -right-1 bg-red-500 text-white text-xs w-5 h-5 rounded-full flex items-center justify-center font-medium", children: a })
67
+ ]
68
+ }
69
+ ) : r && t && /* @__PURE__ */ e(
70
+ "a",
71
+ {
72
+ href: t,
73
+ className: "hidden md:inline-block bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",
74
+ children: r
75
+ }
76
+ ),
77
+ /* @__PURE__ */ e(
78
+ "button",
79
+ {
80
+ onClick: () => x((i) => !i),
81
+ className: "md:hidden p-2 rounded-md text-gray-600 hover:text-black transition-colors",
82
+ "aria-label": "Toggle menu",
83
+ children: /* @__PURE__ */ e(
84
+ "svg",
49
85
  {
50
- strokeLinecap: "round",
51
- strokeLinejoin: "round",
52
- strokeWidth: 2,
53
- d: "M4 6h16M4 12h16M4 18h16"
86
+ className: "w-6 h-6",
87
+ fill: "none",
88
+ stroke: "currentColor",
89
+ viewBox: "0 0 24 24",
90
+ children: p ? /* @__PURE__ */ e(
91
+ "path",
92
+ {
93
+ strokeLinecap: "round",
94
+ strokeLinejoin: "round",
95
+ strokeWidth: 2,
96
+ d: "M6 18L18 6M6 6l12 12"
97
+ }
98
+ ) : /* @__PURE__ */ e(
99
+ "path",
100
+ {
101
+ strokeLinecap: "round",
102
+ strokeLinejoin: "round",
103
+ strokeWidth: 2,
104
+ d: "M4 6h16M4 12h16M4 18h16"
105
+ }
106
+ )
54
107
  }
55
108
  )
56
109
  }
57
110
  )
111
+ ] })
112
+ ] }) }),
113
+ p && /* @__PURE__ */ l(
114
+ "div",
115
+ {
116
+ className: "md:hidden border-t px-4 pb-4 pt-2 space-y-3",
117
+ style: {
118
+ borderColor: c ? "rgba(255,255,255,0.3)" : "#e5e7eb"
119
+ },
120
+ children: [
121
+ s.map((i, h) => /* @__PURE__ */ e(
122
+ "a",
123
+ {
124
+ href: i.href,
125
+ className: "block text-gray-600 hover:text-black transition-colors",
126
+ children: i.label
127
+ },
128
+ h
129
+ )),
130
+ !c && r && t && /* @__PURE__ */ e(
131
+ "a",
132
+ {
133
+ href: t,
134
+ className: "block text-center bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",
135
+ children: r
136
+ }
137
+ )
138
+ ]
58
139
  }
59
140
  )
60
- ] })
61
- ] }) }),
62
- c && /* @__PURE__ */ s("div", { className: "md:hidden border-t border-gray-200 px-4 pb-4 pt-2 space-y-3", children: [
63
- n.map((r, i) => /* @__PURE__ */ e(
64
- "a",
65
- {
66
- href: r.href,
67
- className: "block text-gray-600 hover:text-black transition-colors",
68
- children: r.label
69
- },
70
- i
71
- )),
72
- l && t && /* @__PURE__ */ e(
73
- "a",
74
- {
75
- href: t,
76
- className: "block text-center bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",
77
- children: l
78
- }
79
- )
80
- ] })
81
- ] });
82
- }, m = {
141
+ ]
142
+ }
143
+ );
144
+ }, b = {
83
145
  primary: "bg-black text-white rounded-lg px-6 py-3",
84
146
  secondary: "bg-transparent border border-black text-black rounded-lg px-6 py-3",
85
147
  whatsapp: "bg-[#25D366] text-white rounded-lg px-6 py-3"
86
- }, u = ({ variant: a = "primary", label: n, onClick: l, className: t = "" }) => /* @__PURE__ */ s(
148
+ }, v = ({ variant: o = "primary", label: s, onClick: r, className: t = "" }) => /* @__PURE__ */ l(
87
149
  "button",
88
150
  {
89
- onClick: l,
90
- className: `${m[a]} ${t}`,
151
+ onClick: r,
152
+ className: `${b[o]} ${t}`,
91
153
  children: [
92
- a === "whatsapp" && /* @__PURE__ */ e("span", { children: "💬 " }),
93
- n
154
+ o === "whatsapp" && /* @__PURE__ */ e("span", { children: "💬 " }),
155
+ s
94
156
  ]
95
157
  }
96
- ), g = {
158
+ ), u = {
97
159
  1: "lg:grid-cols-1",
98
160
  2: "lg:grid-cols-2",
99
161
  3: "lg:grid-cols-3",
100
162
  4: "lg:grid-cols-4",
101
163
  5: "lg:grid-cols-5",
102
164
  6: "lg:grid-cols-6"
103
- }, k = ({ images: a = [], columns: n = 3, gap: l = 4 }) => {
104
- const [t, c] = d(null);
105
- return /* @__PURE__ */ s(h, { children: [
165
+ }, N = ({ images: o = [], columns: s = 3, gap: r = 4 }) => {
166
+ const [t, n] = g(null);
167
+ return /* @__PURE__ */ l(m, { children: [
106
168
  /* @__PURE__ */ e(
107
169
  "div",
108
170
  {
109
- className: `grid grid-cols-1 sm:grid-cols-2 ${g[n] || ""}`,
110
- style: { gap: `${l * 0.25}rem` },
111
- children: a.map((o, r) => /* @__PURE__ */ e(
171
+ className: `grid grid-cols-1 sm:grid-cols-2 ${u[s] || ""}`,
172
+ style: { gap: `${r * 0.25}rem` },
173
+ children: o.map((a, d) => /* @__PURE__ */ e(
112
174
  "img",
113
175
  {
114
- src: o.src,
115
- alt: o.alt,
176
+ src: a.src,
177
+ alt: a.alt,
116
178
  className: "w-full h-64 object-cover rounded-lg cursor-pointer",
117
- onClick: () => c(o)
179
+ onClick: () => n(a)
118
180
  },
119
- r
181
+ d
120
182
  ))
121
183
  }
122
184
  ),
123
- t && /* @__PURE__ */ s(
185
+ t && /* @__PURE__ */ l(
124
186
  "div",
125
187
  {
126
188
  className: "fixed inset-0 z-50 bg-black/80 flex items-center justify-center",
127
- onClick: () => c(null),
189
+ onClick: () => n(null),
128
190
  children: [
129
191
  /* @__PURE__ */ e(
130
192
  "button",
131
193
  {
132
194
  className: "absolute top-4 right-4 text-white text-4xl leading-none hover:opacity-70",
133
- onClick: () => c(null),
195
+ onClick: () => n(null),
134
196
  children: "×"
135
197
  }
136
198
  ),
@@ -140,16 +202,69 @@ const b = ({ logo: a, links: n = [], ctaLabel: l, ctaHref: t }) => {
140
202
  src: t.src,
141
203
  alt: t.alt,
142
204
  className: "max-w-[90vw] max-h-[90vh] object-contain rounded-lg",
143
- onClick: (o) => o.stopPropagation()
205
+ onClick: (a) => a.stopPropagation()
144
206
  }
145
207
  )
146
208
  ]
147
209
  }
148
210
  )
149
211
  ] });
212
+ }, C = ({
213
+ phoneNumber: o,
214
+ message: s = "Hello, I would like to inquire about your services.",
215
+ bottom: r = "24px",
216
+ right: t = "24px"
217
+ }) => {
218
+ const n = `https://wa.me/${o}?text=${encodeURIComponent(s)}`;
219
+ return /* @__PURE__ */ l(m, { children: [
220
+ /* @__PURE__ */ e("style", { children: `
221
+ @keyframes wa-pulse {
222
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(37, 211, 102, 0.5); }
223
+ 50% { box-shadow: 0 0 0 12px rgba(37, 211, 102, 0); }
224
+ }
225
+ .wa-btn {
226
+ animation: wa-pulse 2s infinite;
227
+ }
228
+ ` }),
229
+ /* @__PURE__ */ e(
230
+ "a",
231
+ {
232
+ href: n,
233
+ target: "_blank",
234
+ rel: "noopener noreferrer",
235
+ className: "wa-btn",
236
+ style: {
237
+ position: "fixed",
238
+ bottom: r,
239
+ right: t,
240
+ width: 56,
241
+ height: 56,
242
+ borderRadius: "50%",
243
+ backgroundColor: "#25D366",
244
+ display: "flex",
245
+ alignItems: "center",
246
+ justifyContent: "center",
247
+ zIndex: 9999,
248
+ cursor: "pointer",
249
+ textDecoration: "none"
250
+ },
251
+ children: /* @__PURE__ */ e(
252
+ "svg",
253
+ {
254
+ viewBox: "0 0 24 24",
255
+ fill: "white",
256
+ width: 28,
257
+ height: 28,
258
+ children: /* @__PURE__ */ e("path", { d: "M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" })
259
+ }
260
+ )
261
+ }
262
+ )
263
+ ] });
150
264
  };
151
265
  export {
152
- u as Button,
153
- k as Gallery,
154
- b as Navbar
266
+ v as Button,
267
+ N as Gallery,
268
+ w as Navbar,
269
+ C as WhatsAppFloat
155
270
  };
@@ -1 +1,9 @@
1
- (function(r,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],e):(r=typeof globalThis<"u"?globalThis:r||self,e(r.CiteUI={},r.ReactJsxRuntime,r.React))})(this,function(r,e,i){"use strict";const h=({logo:o,links:c=[],ctaLabel:s,ctaHref:t})=>{const[d,a]=i.useState(!1);return e.jsxs("nav",{className:"sticky top-0 z-50 bg-white border-b border-gray-200",children:[e.jsx("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",children:e.jsxs("div",{className:"flex items-center justify-between h-16",children:[e.jsx("div",{className:"text-xl font-bold tracking-tight",children:o}),e.jsx("div",{className:"hidden md:flex items-center gap-8",children:c.map((l,n)=>e.jsx("a",{href:l.href,className:"text-gray-600 hover:text-black transition-colors",children:l.label},n))}),e.jsxs("div",{className:"flex items-center gap-4",children:[s&&t&&e.jsx("a",{href:t,className:"hidden md:inline-block bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",children:s}),e.jsx("button",{onClick:()=>a(l=>!l),className:"md:hidden p-2 rounded-md text-gray-600 hover:text-black","aria-label":"Toggle menu",children:e.jsx("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:d?e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"}):e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 6h16M4 12h16M4 18h16"})})})]})]})}),d&&e.jsxs("div",{className:"md:hidden border-t border-gray-200 px-4 pb-4 pt-2 space-y-3",children:[c.map((l,n)=>e.jsx("a",{href:l.href,className:"block text-gray-600 hover:text-black transition-colors",children:l.label},n)),s&&t&&e.jsx("a",{href:t,className:"block text-center bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",children:s})]})]})},g={primary:"bg-black text-white rounded-lg px-6 py-3",secondary:"bg-transparent border border-black text-black rounded-lg px-6 py-3",whatsapp:"bg-[#25D366] text-white rounded-lg px-6 py-3"},p=({variant:o="primary",label:c,onClick:s,className:t=""})=>e.jsxs("button",{onClick:s,className:`${g[o]} ${t}`,children:[o==="whatsapp"&&e.jsx("span",{children:"💬 "}),c]}),x={1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6"},b=({images:o=[],columns:c=3,gap:s=4})=>{const[t,d]=i.useState(null);return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:`grid grid-cols-1 sm:grid-cols-2 ${x[c]||""}`,style:{gap:`${s*.25}rem`},children:o.map((a,l)=>e.jsx("img",{src:a.src,alt:a.alt,className:"w-full h-64 object-cover rounded-lg cursor-pointer",onClick:()=>d(a)},l))}),t&&e.jsxs("div",{className:"fixed inset-0 z-50 bg-black/80 flex items-center justify-center",onClick:()=>d(null),children:[e.jsx("button",{className:"absolute top-4 right-4 text-white text-4xl leading-none hover:opacity-70",onClick:()=>d(null),children:"×"}),e.jsx("img",{src:t.src,alt:t.alt,className:"max-w-[90vw] max-h-[90vh] object-contain rounded-lg",onClick:a=>a.stopPropagation()})]})]})};r.Button=p,r.Gallery=b,r.Navbar=h,Object.defineProperty(r,Symbol.toStringTag,{value:"Module"})});
1
+ (function(t,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],e):(t=typeof globalThis<"u"?globalThis:t||self,e(t.CiteUI={},t.ReactJsxRuntime,t.React))})(this,function(t,e,p){"use strict";const b=({logo:s,links:o=[],ctaLabel:l,ctaHref:r,variant:n="default",cartCount:a=0,onCartClick:i})=>{const[g,y]=p.useState(!1),d=n==="glass",v=d?{background:"rgba(255,255,255,0.7)",backdropFilter:"blur(12px)",WebkitBackdropFilter:"blur(12px)",borderBottom:"1px solid rgba(255,255,255,0.3)"}:{};return e.jsxs("nav",{className:`sticky top-0 z-50 ${d?"":"bg-white border-b border-gray-200"}`,style:v,children:[e.jsx("div",{className:"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8",children:e.jsxs("div",{className:"flex items-center justify-between h-16 relative",children:[e.jsx("div",{className:"flex-shrink-0 text-xl font-bold tracking-tight",children:s}),e.jsx("div",{className:`items-center gap-8 ${d?"absolute left-1/2 -translate-x-1/2 hidden md:flex":"hidden md:flex"}`,children:o.map((c,h)=>e.jsx("a",{href:c.href,className:"text-gray-600 hover:text-black transition-colors",children:c.label},h))}),e.jsxs("div",{className:"flex items-center gap-4",children:[d?e.jsxs("button",{onClick:i,className:"relative p-2 text-gray-600 hover:text-black transition-colors",children:[e.jsx("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 100 4 2 2 0 000-4z"})}),a>0&&e.jsx("span",{className:"absolute -top-1 -right-1 bg-red-500 text-white text-xs w-5 h-5 rounded-full flex items-center justify-center font-medium",children:a})]}):l&&r&&e.jsx("a",{href:r,className:"hidden md:inline-block bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",children:l}),e.jsx("button",{onClick:()=>y(c=>!c),className:"md:hidden p-2 rounded-md text-gray-600 hover:text-black transition-colors","aria-label":"Toggle menu",children:e.jsx("svg",{className:"w-6 h-6",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:g?e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"}):e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M4 6h16M4 12h16M4 18h16"})})})]})]})}),g&&e.jsxs("div",{className:"md:hidden border-t px-4 pb-4 pt-2 space-y-3",style:{borderColor:d?"rgba(255,255,255,0.3)":"#e5e7eb"},children:[o.map((c,h)=>e.jsx("a",{href:c.href,className:"block text-gray-600 hover:text-black transition-colors",children:c.label},h)),!d&&l&&r&&e.jsx("a",{href:r,className:"block text-center bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors",children:l})]})]})},x={primary:"bg-black text-white rounded-lg px-6 py-3",secondary:"bg-transparent border border-black text-black rounded-lg px-6 py-3",whatsapp:"bg-[#25D366] text-white rounded-lg px-6 py-3"},f=({variant:s="primary",label:o,onClick:l,className:r=""})=>e.jsxs("button",{onClick:l,className:`${x[s]} ${r}`,children:[s==="whatsapp"&&e.jsx("span",{children:"💬 "}),o]}),m={1:"lg:grid-cols-1",2:"lg:grid-cols-2",3:"lg:grid-cols-3",4:"lg:grid-cols-4",5:"lg:grid-cols-5",6:"lg:grid-cols-6"},u=({images:s=[],columns:o=3,gap:l=4})=>{const[r,n]=p.useState(null);return e.jsxs(e.Fragment,{children:[e.jsx("div",{className:`grid grid-cols-1 sm:grid-cols-2 ${m[o]||""}`,style:{gap:`${l*.25}rem`},children:s.map((a,i)=>e.jsx("img",{src:a.src,alt:a.alt,className:"w-full h-64 object-cover rounded-lg cursor-pointer",onClick:()=>n(a)},i))}),r&&e.jsxs("div",{className:"fixed inset-0 z-50 bg-black/80 flex items-center justify-center",onClick:()=>n(null),children:[e.jsx("button",{className:"absolute top-4 right-4 text-white text-4xl leading-none hover:opacity-70",onClick:()=>n(null),children:"×"}),e.jsx("img",{src:r.src,alt:r.alt,className:"max-w-[90vw] max-h-[90vh] object-contain rounded-lg",onClick:a=>a.stopPropagation()})]})]})},k=({phoneNumber:s,message:o="Hello, I would like to inquire about your services.",bottom:l="24px",right:r="24px"})=>{const n=`https://wa.me/${s}?text=${encodeURIComponent(o)}`;return e.jsxs(e.Fragment,{children:[e.jsx("style",{children:`
2
+ @keyframes wa-pulse {
3
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(37, 211, 102, 0.5); }
4
+ 50% { box-shadow: 0 0 0 12px rgba(37, 211, 102, 0); }
5
+ }
6
+ .wa-btn {
7
+ animation: wa-pulse 2s infinite;
8
+ }
9
+ `}),e.jsx("a",{href:n,target:"_blank",rel:"noopener noreferrer",className:"wa-btn",style:{position:"fixed",bottom:l,right:r,width:56,height:56,borderRadius:"50%",backgroundColor:"#25D366",display:"flex",alignItems:"center",justifyContent:"center",zIndex:9999,cursor:"pointer",textDecoration:"none"},children:e.jsx("svg",{viewBox:"0 0 24 24",fill:"white",width:28,height:28,children:e.jsx("path",{d:"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"})})})]})};t.Button=f,t.Gallery=u,t.Navbar=b,t.WhatsAppFloat=k,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cite-ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "type": "module",
5
5
  "main": "dist/cite-ui.umd.js",
6
6
  "module": "dist/cite-ui.es.js",
@@ -1,15 +1,42 @@
1
1
  import { useState } from 'react'
2
2
 
3
- const Navbar = ({ logo, links = [], ctaLabel, ctaHref }) => {
3
+ const Navbar = ({
4
+ logo,
5
+ links = [],
6
+ ctaLabel,
7
+ ctaHref,
8
+ variant = 'default',
9
+ cartCount = 0,
10
+ onCartClick,
11
+ }) => {
4
12
  const [open, setOpen] = useState(false)
13
+ const isGlass = variant === 'glass'
14
+
15
+ const glassStyles = isGlass
16
+ ? {
17
+ background: 'rgba(255,255,255,0.7)',
18
+ backdropFilter: 'blur(12px)',
19
+ WebkitBackdropFilter: 'blur(12px)',
20
+ borderBottom: '1px solid rgba(255,255,255,0.3)',
21
+ }
22
+ : {}
5
23
 
6
24
  return (
7
- <nav className="sticky top-0 z-50 bg-white border-b border-gray-200">
25
+ <nav
26
+ className={`sticky top-0 z-50 ${isGlass ? '' : 'bg-white border-b border-gray-200'}`}
27
+ style={glassStyles}
28
+ >
8
29
  <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
9
- <div className="flex items-center justify-between h-16">
10
- <div className="text-xl font-bold tracking-tight">{logo}</div>
30
+ <div className="flex items-center justify-between h-16 relative">
31
+ <div className="flex-shrink-0 text-xl font-bold tracking-tight">{logo}</div>
11
32
 
12
- <div className="hidden md:flex items-center gap-8">
33
+ <div
34
+ className={`items-center gap-8 ${
35
+ isGlass
36
+ ? 'absolute left-1/2 -translate-x-1/2 hidden md:flex'
37
+ : 'hidden md:flex'
38
+ }`}
39
+ >
13
40
  {links.map((link, i) => (
14
41
  <a
15
42
  key={i}
@@ -22,18 +49,45 @@ const Navbar = ({ logo, links = [], ctaLabel, ctaHref }) => {
22
49
  </div>
23
50
 
24
51
  <div className="flex items-center gap-4">
25
- {ctaLabel && ctaHref && (
26
- <a
27
- href={ctaHref}
28
- className="hidden md:inline-block bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors"
52
+ {isGlass ? (
53
+ <button
54
+ onClick={onCartClick}
55
+ className="relative p-2 text-gray-600 hover:text-black transition-colors"
29
56
  >
30
- {ctaLabel}
31
- </a>
57
+ <svg
58
+ className="w-6 h-6"
59
+ fill="none"
60
+ stroke="currentColor"
61
+ viewBox="0 0 24 24"
62
+ >
63
+ <path
64
+ strokeLinecap="round"
65
+ strokeLinejoin="round"
66
+ strokeWidth={2}
67
+ d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 100 4 2 2 0 000-4z"
68
+ />
69
+ </svg>
70
+ {cartCount > 0 && (
71
+ <span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs w-5 h-5 rounded-full flex items-center justify-center font-medium">
72
+ {cartCount}
73
+ </span>
74
+ )}
75
+ </button>
76
+ ) : (
77
+ ctaLabel &&
78
+ ctaHref && (
79
+ <a
80
+ href={ctaHref}
81
+ className="hidden md:inline-block bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors"
82
+ >
83
+ {ctaLabel}
84
+ </a>
85
+ )
32
86
  )}
33
87
 
34
88
  <button
35
89
  onClick={() => setOpen((v) => !v)}
36
- className="md:hidden p-2 rounded-md text-gray-600 hover:text-black"
90
+ className="md:hidden p-2 rounded-md text-gray-600 hover:text-black transition-colors"
37
91
  aria-label="Toggle menu"
38
92
  >
39
93
  <svg
@@ -64,7 +118,12 @@ const Navbar = ({ logo, links = [], ctaLabel, ctaHref }) => {
64
118
  </div>
65
119
 
66
120
  {open && (
67
- <div className="md:hidden border-t border-gray-200 px-4 pb-4 pt-2 space-y-3">
121
+ <div
122
+ className="md:hidden border-t px-4 pb-4 pt-2 space-y-3"
123
+ style={{
124
+ borderColor: isGlass ? 'rgba(255,255,255,0.3)' : '#e5e7eb',
125
+ }}
126
+ >
68
127
  {links.map((link, i) => (
69
128
  <a
70
129
  key={i}
@@ -74,7 +133,7 @@ const Navbar = ({ logo, links = [], ctaLabel, ctaHref }) => {
74
133
  {link.label}
75
134
  </a>
76
135
  ))}
77
- {ctaLabel && ctaHref && (
136
+ {!isGlass && ctaLabel && ctaHref && (
78
137
  <a
79
138
  href={ctaHref}
80
139
  className="block text-center bg-black text-white rounded-lg px-5 py-2 text-sm font-medium hover:bg-gray-800 transition-colors"
@@ -0,0 +1,54 @@
1
+ const WhatsAppFloat = ({
2
+ phoneNumber,
3
+ message = 'Hello, I would like to inquire about your services.',
4
+ bottom = '24px',
5
+ right = '24px',
6
+ }) => {
7
+ const href = `https://wa.me/${phoneNumber}?text=${encodeURIComponent(message)}`
8
+
9
+ return (
10
+ <>
11
+ <style>{`
12
+ @keyframes wa-pulse {
13
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(37, 211, 102, 0.5); }
14
+ 50% { box-shadow: 0 0 0 12px rgba(37, 211, 102, 0); }
15
+ }
16
+ .wa-btn {
17
+ animation: wa-pulse 2s infinite;
18
+ }
19
+ `}</style>
20
+ <a
21
+ href={href}
22
+ target="_blank"
23
+ rel="noopener noreferrer"
24
+ className="wa-btn"
25
+ style={{
26
+ position: 'fixed',
27
+ bottom,
28
+ right,
29
+ width: 56,
30
+ height: 56,
31
+ borderRadius: '50%',
32
+ backgroundColor: '#25D366',
33
+ display: 'flex',
34
+ alignItems: 'center',
35
+ justifyContent: 'center',
36
+ zIndex: 9999,
37
+ cursor: 'pointer',
38
+ textDecoration: 'none',
39
+ }}
40
+ >
41
+ <svg
42
+ viewBox="0 0 24 24"
43
+ fill="white"
44
+ width={28}
45
+ height={28}
46
+ >
47
+ <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z" />
48
+ </svg>
49
+ </a>
50
+ </>
51
+ )
52
+ }
53
+
54
+ export default WhatsAppFloat
package/src/index.jsx CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as Navbar } from './components/Navbar'
2
2
  export { default as Button } from './components/Button'
3
3
  export { default as Gallery } from './components/Gallery'
4
+ export { default as WhatsAppFloat } from './components/WhatsAppFloat'
package/src/main.jsx ADDED
@@ -0,0 +1,112 @@
1
+ import React from "react";
2
+ import ReactDOM from "react-dom/client";
3
+ import "./styles.css";
4
+ import { Navbar, Button, Gallery, WhatsAppFloat } from "./index";
5
+
6
+ const links = [
7
+ { label: "Home", href: "#" },
8
+ { label: "About", href: "#" },
9
+ { label: "Services", href: "#" },
10
+ ];
11
+
12
+ const images = [
13
+ { src: "https://picsum.photos/seed/a/400/300", alt: "Image 1" },
14
+ { src: "https://picsum.photos/seed/b/400/300", alt: "Image 2" },
15
+ { src: "https://picsum.photos/seed/c/400/300", alt: "Image 3" },
16
+ { src: "https://picsum.photos/seed/d/400/300", alt: "Image 4" },
17
+ { src: "https://picsum.photos/seed/e/400/300", alt: "Image 5" },
18
+ { src: "https://picsum.photos/seed/f/400/300", alt: "Image 6" },
19
+ ];
20
+
21
+ const Section = ({ title, description, children }) => (
22
+ <div className="px-8 py-16 border-b border-gray-200">
23
+ <div className="max-w-5xl mx-auto">
24
+ <p className="mb-1 text-xs font-semibold tracking-widest text-gray-400 uppercase">
25
+ Component
26
+ </p>
27
+ <h2 className="mb-1 text-2xl font-bold text-gray-900">{title}</h2>
28
+ <p className="mb-10 text-gray-500">{description}</p>
29
+ <div className="p-8 border border-gray-200 bg-gray-50 rounded-xl">
30
+ {children}
31
+ </div>
32
+ </div>
33
+ </div>
34
+ );
35
+
36
+ ReactDOM.createRoot(document.getElementById("root")).render(
37
+ <div className="min-h-screen bg-white">
38
+ {/* Header */}
39
+ <div className="px-8 py-12 text-center border-b border-gray-200">
40
+ <h1 className="text-4xl font-bold tracking-tight text-gray-900">
41
+ cite-ui
42
+ </h1>
43
+ <p className="mt-2 text-gray-500">
44
+ FrameStudio component library — v0.1.2
45
+ </p>
46
+ </div>
47
+
48
+ {/* Navbar */}
49
+ <Section
50
+ title="Navbar"
51
+ description="Two variants — default and glass. Glass includes centered links and cart icon."
52
+ >
53
+ <div className="mb-6 overflow-hidden border border-gray-200 rounded-lg">
54
+ <Navbar
55
+ logo="FrameStudio"
56
+ links={links}
57
+ ctaLabel="Get Started"
58
+ ctaHref="#"
59
+ />
60
+ </div>
61
+
62
+ <div
63
+ className="relative overflow-hidden border border-gray-200 rounded-lg"
64
+ style={{
65
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
66
+ }}
67
+ >
68
+ <Navbar
69
+ variant="glass"
70
+ logo="FrameStudio"
71
+ links={links}
72
+ cartCount={3}
73
+ onCartClick={() => alert("Cart clicked")}
74
+ />
75
+ </div>
76
+ </Section>
77
+
78
+ {/* Button */}
79
+ <Section
80
+ title="Button"
81
+ description="Three variants — primary, secondary, and WhatsApp — all prop-driven."
82
+ >
83
+ <div className="flex flex-wrap gap-4">
84
+ <Button variant="primary" label="Primary" onClick={() => {}} />
85
+ <Button variant="secondary" label="Secondary" onClick={() => {}} />
86
+ <Button variant="whatsapp" label="WhatsApp" onClick={() => {}} />
87
+ </div>
88
+ </Section>
89
+
90
+ {/* Gallery */}
91
+ <Section
92
+ title="Gallery"
93
+ description="Responsive image grid with lightbox on click. Control columns and gap via props."
94
+ >
95
+ <Gallery images={images} columns={3} gap={4} />
96
+ </Section>
97
+
98
+ {/* WhatsAppFloat */}
99
+ <Section
100
+ title="WhatsApp Float"
101
+ description="Fixed floating WhatsApp button with pulse animation. Opens a pre-filled chat."
102
+ >
103
+ <div className="relative h-32 bg-gray-100 rounded-lg">
104
+ <p className="pt-6 text-sm text-center text-gray-400">
105
+ Floating button preview — visible bottom right of screen
106
+ </p>
107
+ </div>
108
+ </Section>
109
+
110
+ <WhatsAppFloat phoneNumber="254712345678" />
111
+ </div>,
112
+ );