@flopay/ui 0.5.0 → 0.6.1

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 CHANGED
@@ -75,7 +75,42 @@ The primitives render the shared component classes, so they work in plain-CSS
75
75
  apps (which import `components.css`) and in Tailwind apps (where you can extend
76
76
  them via `className` + `cn()`).
77
77
 
78
- ### 4. Annotation layer
78
+ ### 4. Marketing chrome (nav + footer)
79
+
80
+ ```tsx
81
+ import logo from "@flopay/ui/logo.png";
82
+ import { FlopayFooter, Nav } from "@flopay/ui/marketing";
83
+
84
+ <Nav
85
+ logo={{ src: logo, label: "FloPay" }}
86
+ links={[
87
+ { href: "/pricing", label: "Pricing" },
88
+ { href: "/contact", label: "Contact", cta: true },
89
+ ]}
90
+ />;
91
+
92
+ <FlopayFooter logo={logo} />; // on flopay.com itself
93
+ <FlopayFooter logo={logo} baseUrl="https://flopay.com" />; // any other portal
94
+ ```
95
+
96
+ `Nav` links stay per-app (the demo points at its own routes), but the chrome is
97
+ shared: mark `cta: true` on the call to action and every *other* item gets a
98
+ `nav-link-item` class, which `components.css` hides below 768px — so narrow
99
+ viewports collapse to logo + CTA everywhere. Apps that ship their own component
100
+ CSS instead of importing `components.css` re-declare that one rule to opt in.
101
+
102
+ `FlopayFooter` is the whole footer, **content included** — the sitemap, tagline
103
+ and copyright live in `flopay-footer.tsx`, so adding a footer link is one change
104
+ here plus a version bump, and every portal picks it up. Portals hosted off
105
+ `flopay.com` pass `baseUrl` to make the marketing routes absolute; `demoUrl` and
106
+ `contactUrl` cover the two entries a portal usually wants to re-point at itself.
107
+
108
+ Need a different sitemap (a partner or internal portal)? Use the lower-level
109
+ `Footer` with your own `columns`/`legal`, or start from `flopayFooterContent()`
110
+ and edit the result. `MarketingLink` picks `next/link` vs. a new-tab anchor per
111
+ href, so column lists can mix internal routes, absolute URLs and `mailto:`.
112
+
113
+ ### 5. Annotation layer
79
114
 
80
115
  Hand-drawn marker notes and arrows drawn over a UI, for guided demos and
81
116
  product tours. Opt-in, because the arrow geometry is a few KB most pages don't
@@ -30,9 +30,11 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/marketing/index.ts
31
31
  var marketing_exports = {};
32
32
  __export(marketing_exports, {
33
+ FlopayFooter: () => FlopayFooter,
33
34
  Footer: () => Footer,
34
35
  MarketingLink: () => MarketingLink,
35
- Nav: () => Nav
36
+ Nav: () => Nav,
37
+ flopayFooterContent: () => flopayFooterContent
36
38
  });
37
39
  module.exports = __toCommonJS(marketing_exports);
38
40
 
@@ -78,7 +80,7 @@ function Footer({ brand, columns = [], legal = [], copyright, className }) {
78
80
  ] }),
79
81
  columns.map((column, index) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "footer-column", children: [
80
82
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { children: column.title }),
81
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: column.links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MarketingLink, { href: link.href, external: link.external, children: link.label }) }, link.href)) })
83
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("ul", { children: column.links.map((link, linkIndex) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MarketingLink, { href: link.href, external: link.external, children: link.label }) }, linkIndex)) })
82
84
  ] }, index))
83
85
  ] }),
84
86
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "footer-bottom", children: [
@@ -88,19 +90,102 @@ function Footer({ brand, columns = [], legal = [], copyright, className }) {
88
90
  ] });
89
91
  }
90
92
 
93
+ // src/marketing/flopay-footer.tsx
94
+ var import_jsx_runtime3 = require("react/jsx-runtime");
95
+ var TAGLINE = "Payment Orchestration That Keeps Revenue Flowing";
96
+ var COMPANY = "FloPay LLC";
97
+ var DOCS_URL = "https://docs.flopay.com/docs";
98
+ var API_URL = "https://api.flopay.com/docs";
99
+ var DEMO_URL = "https://demo.flopay.com";
100
+ function normalizeBase(baseUrl) {
101
+ return baseUrl.replace(/\/+$/, "");
102
+ }
103
+ function flopayFooterContent({
104
+ baseUrl = "",
105
+ demoUrl = DEMO_URL,
106
+ contactUrl
107
+ } = {}) {
108
+ const base = normalizeBase(baseUrl);
109
+ const route = (path) => `${base}${path}`;
110
+ const pending = base || "#";
111
+ return {
112
+ tagline: TAGLINE,
113
+ columns: [
114
+ {
115
+ title: "Product",
116
+ links: [
117
+ { href: route("/vault"), label: "Card Vault" },
118
+ { href: route("/chargeback-prevention"), label: "Chargeback Prevention" },
119
+ { href: pending, label: "Agent-Assisted Commerce" },
120
+ { href: pending, label: "The Agent Vault" },
121
+ { href: pending, label: "Agentic Discovery Network" },
122
+ { href: pending, label: "Smart Agentic Orchestration" }
123
+ ]
124
+ },
125
+ {
126
+ title: "Development",
127
+ links: [
128
+ { href: DOCS_URL, label: "Documentation" },
129
+ { href: pending, label: "MCP Server" },
130
+ { href: API_URL, label: "API Reference" },
131
+ { href: demoUrl, label: "Demo Playground" }
132
+ ]
133
+ },
134
+ {
135
+ title: "Company",
136
+ links: [
137
+ { href: route("/mission"), label: "Mission" },
138
+ { href: route("/values"), label: "Values" },
139
+ { href: route("/pricing"), label: "Pricing" },
140
+ { href: route("/blog"), label: "Blog" },
141
+ { href: contactUrl ?? route("/contact"), label: "Contact" }
142
+ ]
143
+ }
144
+ ],
145
+ legal: [
146
+ { href: route("/legal/privacy"), label: "Privacy" },
147
+ { href: route("/legal/terms"), label: "Terms" },
148
+ { href: route("/legal/cookies"), label: "Cookies" }
149
+ ],
150
+ copyright: `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${COMPANY}. All rights reserved.`
151
+ };
152
+ }
153
+ function FlopayFooter({ logo, brand, className, ...options }) {
154
+ const { tagline, columns, legal, copyright } = flopayFooterContent(options);
155
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
156
+ Footer,
157
+ {
158
+ className,
159
+ brand: {
160
+ src: logo,
161
+ alt: "FloPay",
162
+ label: "FloPay",
163
+ href: normalizeBase(options.baseUrl ?? "") || "/",
164
+ size: 20,
165
+ mark: true,
166
+ tagline,
167
+ ...brand
168
+ },
169
+ columns,
170
+ legal,
171
+ copyright
172
+ }
173
+ );
174
+ }
175
+
91
176
  // src/marketing/nav.tsx
92
177
  var import_image2 = __toESM(require("next/image"), 1);
93
- var import_jsx_runtime3 = require("react/jsx-runtime");
178
+ var import_jsx_runtime4 = require("react/jsx-runtime");
94
179
  function Nav({ logo, links = [], children, className }) {
95
180
  const size = logo.size ?? 28;
96
- const image = /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_image2.default, { src: logo.src, alt: logo.alt ?? "", width: size, height: size, priority: true });
97
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("nav", { className: cn("nav", className), children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "nav-inner", children: [
98
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(MarketingLink, { href: logo.href ?? "/", className: "nav-logo", children: [
99
- logo.mark ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "nav-logo-mark", style: { width: size, height: size }, children: image }) : image,
181
+ const image = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_image2.default, { src: logo.src, alt: logo.alt ?? "", width: size, height: size, priority: true });
182
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("nav", { className: cn("nav", className), children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "nav-inner", children: [
183
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(MarketingLink, { href: logo.href ?? "/", className: "nav-logo", children: [
184
+ logo.mark ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "nav-logo-mark", style: { width: size, height: size }, children: image }) : image,
100
185
  logo.label
101
186
  ] }),
102
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("ul", { className: "nav-links", children: [
103
- links.map((link) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
187
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("ul", { className: "nav-links", children: [
188
+ links.map((link, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { className: link.cta ? void 0 : "nav-link-item", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
104
189
  MarketingLink,
105
190
  {
106
191
  href: link.href,
@@ -108,15 +193,17 @@ function Nav({ logo, links = [], children, className }) {
108
193
  className: link.cta ? "nav-cta" : void 0,
109
194
  children: link.label
110
195
  }
111
- ) }, link.href)),
112
- children ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("li", { children }) : null
196
+ ) }, index)),
197
+ children ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("li", { children }) : null
113
198
  ] })
114
199
  ] }) });
115
200
  }
116
201
  // Annotate the CommonJS export names for ESM import in node:
117
202
  0 && (module.exports = {
203
+ FlopayFooter,
118
204
  Footer,
119
205
  MarketingLink,
120
- Nav
206
+ Nav,
207
+ flopayFooterContent
121
208
  });
122
209
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/marketing/index.ts","../../src/marketing/footer.tsx","../../src/cn.ts","../../src/marketing/link.tsx","../../src/marketing/nav.tsx"],"sourcesContent":["export type { FooterBrand, FooterColumn, FooterLink, FooterProps } from './footer';\nexport { Footer } from './footer';\nexport type { MarketingLinkProps } from './link';\nexport { MarketingLink } from './link';\nexport type { NavLink, NavLogo, NavProps } from './nav';\nexport { Nav } from './nav';\n","import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface FooterLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n}\n\nexport interface FooterColumn {\n title: ReactNode;\n links: FooterLink[];\n}\n\nexport interface FooterBrand {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n size?: number;\n mark?: boolean;\n tagline?: ReactNode;\n}\n\nexport interface FooterProps {\n brand: FooterBrand;\n columns?: FooterColumn[];\n legal?: FooterLink[];\n copyright?: ReactNode;\n className?: string;\n}\n\n/** Shared site footer for marketing surfaces (landing, demo). */\nexport function Footer({ brand, columns = [], legal = [], copyright, className }: FooterProps) {\n const size = brand.size ?? 20;\n const image = <Image src={brand.src} alt={brand.alt ?? ''} width={size} height={size} />;\n\n return (\n <footer className={cn('footer', className)}>\n <div className=\"footer-inner\">\n <div className=\"footer-brand\">\n <MarketingLink href={brand.href ?? '/'} className=\"nav-logo\">\n {brand.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {brand.label}\n </MarketingLink>\n {brand.tagline ? <p>{brand.tagline}</p> : null}\n </div>\n {columns.map((column, index) => (\n <div className=\"footer-column\" key={index}>\n <h4>{column.title}</h4>\n <ul>\n {column.links.map((link) => (\n <li key={link.href}>\n <MarketingLink href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n <div className=\"footer-bottom\">\n {copyright ? <p>{copyright}</p> : <span />}\n {legal.length > 0 ? (\n <div className=\"footer-legal\">\n {legal.map((link) => (\n <MarketingLink key={link.href} href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n ))}\n </div>\n ) : null}\n </div>\n </footer>\n );\n}\n","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge class names, resolving conflicting Tailwind utilities (last wins).\n * Mirrors the helper used across the FloPay frontends so consumers can extend\n * any primitive's classes safely.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import Link from 'next/link';\nimport type { ReactNode } from 'react';\n\nexport interface MarketingLinkProps {\n href: string;\n /** Force new-tab rendering. Defaults to true for absolute http(s) URLs. */\n external?: boolean;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * Picks the right element for a marketing link:\n * - internal route (\"/...\", \"#...\") → next/link\n * - protocol link (mailto:/tel:) → plain anchor\n * - absolute URL (or `external`) → new-tab anchor\n */\nexport function MarketingLink({ href, external, className, children }: MarketingLinkProps) {\n const isAbsolute = /^https?:\\/\\//.test(href) || href.startsWith('//');\n const isProtocol = !isAbsolute && /^[a-z][a-z0-9+.-]*:/i.test(href);\n const openExternally = external ?? isAbsolute;\n\n if (openExternally) {\n return (\n <a href={href} className={className} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n }\n if (isProtocol) {\n return (\n <a href={href} className={className}>\n {children}\n </a>\n );\n }\n return (\n <Link href={href} className={className}>\n {children}\n </Link>\n );\n}\n","import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface NavLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n /** Render as the prominent call-to-action button. */\n cta?: boolean;\n}\n\nexport interface NavLogo {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n /** Logo image edge size in px (default 28). */\n size?: number;\n /** Wrap the image in the rounded brand mark. */\n mark?: boolean;\n}\n\nexport interface NavProps {\n logo: NavLogo;\n links?: NavLink[];\n /** Extra item appended to the link list (e.g. an interactive control). */\n children?: ReactNode;\n className?: string;\n}\n\n/** Shared fixed top navigation for marketing surfaces (landing, demo). */\nexport function Nav({ logo, links = [], children, className }: NavProps) {\n const size = logo.size ?? 28;\n const image = <Image src={logo.src} alt={logo.alt ?? ''} width={size} height={size} priority />;\n\n return (\n <nav className={cn('nav', className)}>\n <div className=\"nav-inner\">\n <MarketingLink href={logo.href ?? '/'} className=\"nav-logo\">\n {logo.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {logo.label}\n </MarketingLink>\n <ul className=\"nav-links\">\n {links.map((link) => (\n <li key={link.href}>\n <MarketingLink\n href={link.href}\n external={link.external}\n className={link.cta ? 'nav-cta' : undefined}\n >\n {link.label}\n </MarketingLink>\n </li>\n ))}\n {children ? <li>{children}</li> : null}\n </ul>\n </div>\n </nav>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAuC;;;ACAvC,kBAAsC;AACtC,4BAAwB;AAOjB,SAAS,MAAM,QAAsB;AAC1C,aAAO,mCAAQ,kBAAK,MAAM,CAAC;AAC7B;;;ACVA,kBAAiB;AAwBX;AAPC,SAAS,cAAc,EAAE,MAAM,UAAU,WAAW,SAAS,GAAuB;AACzF,QAAM,aAAa,eAAe,KAAK,IAAI,KAAK,KAAK,WAAW,IAAI;AACpE,QAAM,aAAa,CAAC,cAAc,uBAAuB,KAAK,IAAI;AAClE,QAAM,iBAAiB,YAAY;AAEnC,MAAI,gBAAgB;AAClB,WACE,4CAAC,OAAE,MAAY,WAAsB,QAAO,UAAS,KAAI,uBACtD,UACH;AAAA,EAEJ;AACA,MAAI,YAAY;AACd,WACE,4CAAC,OAAE,MAAY,WACZ,UACH;AAAA,EAEJ;AACA,SACE,4CAAC,YAAAA,SAAA,EAAK,MAAY,WACf,UACH;AAEJ;;;AFHgB,IAAAC,sBAAA;AAFT,SAAS,OAAO,EAAE,OAAO,UAAU,CAAC,GAAG,QAAQ,CAAC,GAAG,WAAW,UAAU,GAAgB;AAC7F,QAAM,OAAO,MAAM,QAAQ;AAC3B,QAAM,QAAQ,6CAAC,aAAAC,SAAA,EAAM,KAAK,MAAM,KAAK,KAAK,MAAM,OAAO,IAAI,OAAO,MAAM,QAAQ,MAAM;AAEtF,SACE,8CAAC,YAAO,WAAW,GAAG,UAAU,SAAS,GACvC;AAAA,kDAAC,SAAI,WAAU,gBACb;AAAA,oDAAC,SAAI,WAAU,gBACb;AAAA,sDAAC,iBAAc,MAAM,MAAM,QAAQ,KAAK,WAAU,YAC/C;AAAA,gBAAM,OACL,6CAAC,UAAK,WAAU,iBAAgB,OAAO,EAAE,OAAO,MAAM,QAAQ,KAAK,GAChE,iBACH,IAEA;AAAA,UAED,MAAM;AAAA,WACT;AAAA,QACC,MAAM,UAAU,6CAAC,OAAG,gBAAM,SAAQ,IAAO;AAAA,SAC5C;AAAA,MACC,QAAQ,IAAI,CAAC,QAAQ,UACpB,8CAAC,SAAI,WAAU,iBACb;AAAA,qDAAC,QAAI,iBAAO,OAAM;AAAA,QAClB,6CAAC,QACE,iBAAO,MAAM,IAAI,CAAC,SACjB,6CAAC,QACC,uDAAC,iBAAc,MAAM,KAAK,MAAM,UAAU,KAAK,UAC5C,eAAK,OACR,KAHO,KAAK,IAId,CACD,GACH;AAAA,WAVkC,KAWpC,CACD;AAAA,OACH;AAAA,IACA,8CAAC,SAAI,WAAU,iBACZ;AAAA,kBAAY,6CAAC,OAAG,qBAAU,IAAO,6CAAC,UAAK;AAAA,MACvC,MAAM,SAAS,IACd,6CAAC,SAAI,WAAU,gBACZ,gBAAM,IAAI,CAAC,SACV,6CAAC,iBAA8B,MAAM,KAAK,MAAM,UAAU,KAAK,UAC5D,eAAK,SADY,KAAK,IAEzB,CACD,GACH,IACE;AAAA,OACN;AAAA,KACF;AAEJ;;;AGrFA,IAAAC,gBAAuC;AAoCvB,IAAAC,sBAAA;AAFT,SAAS,IAAI,EAAE,MAAM,QAAQ,CAAC,GAAG,UAAU,UAAU,GAAa;AACvE,QAAM,OAAO,KAAK,QAAQ;AAC1B,QAAM,QAAQ,6CAAC,cAAAC,SAAA,EAAM,KAAK,KAAK,KAAK,KAAK,KAAK,OAAO,IAAI,OAAO,MAAM,QAAQ,MAAM,UAAQ,MAAC;AAE7F,SACE,6CAAC,SAAI,WAAW,GAAG,OAAO,SAAS,GACjC,wDAAC,SAAI,WAAU,aACb;AAAA,kDAAC,iBAAc,MAAM,KAAK,QAAQ,KAAK,WAAU,YAC9C;AAAA,WAAK,OACJ,6CAAC,UAAK,WAAU,iBAAgB,OAAO,EAAE,OAAO,MAAM,QAAQ,KAAK,GAChE,iBACH,IAEA;AAAA,MAED,KAAK;AAAA,OACR;AAAA,IACA,8CAAC,QAAG,WAAU,aACX;AAAA,YAAM,IAAI,CAAC,SACV,6CAAC,QACC;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,KAAK;AAAA,UACX,UAAU,KAAK;AAAA,UACf,WAAW,KAAK,MAAM,YAAY;AAAA,UAEjC,eAAK;AAAA;AAAA,MACR,KAPO,KAAK,IAQd,CACD;AAAA,MACA,WAAW,6CAAC,QAAI,UAAS,IAAQ;AAAA,OACpC;AAAA,KACF,GACF;AAEJ;","names":["Link","import_jsx_runtime","Image","import_image","import_jsx_runtime","Image"]}
1
+ {"version":3,"sources":["../../src/marketing/index.ts","../../src/marketing/footer.tsx","../../src/cn.ts","../../src/marketing/link.tsx","../../src/marketing/flopay-footer.tsx","../../src/marketing/nav.tsx"],"sourcesContent":["export type {\n FlopayFooterContent,\n FlopayFooterOptions,\n FlopayFooterProps,\n} from './flopay-footer';\nexport { FlopayFooter, flopayFooterContent } from './flopay-footer';\nexport type { FooterBrand, FooterColumn, FooterLink, FooterProps } from './footer';\nexport { Footer } from './footer';\nexport type { MarketingLinkProps } from './link';\nexport { MarketingLink } from './link';\nexport type { NavLink, NavLogo, NavProps } from './nav';\nexport { Nav } from './nav';\n","import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface FooterLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n}\n\nexport interface FooterColumn {\n title: ReactNode;\n links: FooterLink[];\n}\n\nexport interface FooterBrand {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n size?: number;\n mark?: boolean;\n tagline?: ReactNode;\n}\n\nexport interface FooterProps {\n brand: FooterBrand;\n columns?: FooterColumn[];\n legal?: FooterLink[];\n copyright?: ReactNode;\n className?: string;\n}\n\n/** Shared site footer for marketing surfaces (landing, demo). */\nexport function Footer({ brand, columns = [], legal = [], copyright, className }: FooterProps) {\n const size = brand.size ?? 20;\n const image = <Image src={brand.src} alt={brand.alt ?? ''} width={size} height={size} />;\n\n return (\n <footer className={cn('footer', className)}>\n <div className=\"footer-inner\">\n <div className=\"footer-brand\">\n <MarketingLink href={brand.href ?? '/'} className=\"nav-logo\">\n {brand.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {brand.label}\n </MarketingLink>\n {brand.tagline ? <p>{brand.tagline}</p> : null}\n </div>\n {columns.map((column, index) => (\n <div className=\"footer-column\" key={index}>\n <h4>{column.title}</h4>\n <ul>\n {/* Keyed by index: placeholder links legitimately share an href. */}\n {column.links.map((link, linkIndex) => (\n <li key={linkIndex}>\n <MarketingLink href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n <div className=\"footer-bottom\">\n {copyright ? <p>{copyright}</p> : <span />}\n {legal.length > 0 ? (\n <div className=\"footer-legal\">\n {legal.map((link) => (\n <MarketingLink key={link.href} href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n ))}\n </div>\n ) : null}\n </div>\n </footer>\n );\n}\n","import { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * Merge class names, resolving conflicting Tailwind utilities (last wins).\n * Mirrors the helper used across the FloPay frontends so consumers can extend\n * any primitive's classes safely.\n */\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","import Link from 'next/link';\nimport type { ReactNode } from 'react';\n\nexport interface MarketingLinkProps {\n href: string;\n /** Force new-tab rendering. Defaults to true for absolute http(s) URLs. */\n external?: boolean;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * Picks the right element for a marketing link:\n * - internal route (\"/...\", \"#...\") → next/link\n * - protocol link (mailto:/tel:) → plain anchor\n * - absolute URL (or `external`) → new-tab anchor\n */\nexport function MarketingLink({ href, external, className, children }: MarketingLinkProps) {\n const isAbsolute = /^https?:\\/\\//.test(href) || href.startsWith('//');\n const isProtocol = !isAbsolute && /^[a-z][a-z0-9+.-]*:/i.test(href);\n const openExternally = external ?? isAbsolute;\n\n if (openExternally) {\n return (\n <a href={href} className={className} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n }\n if (isProtocol) {\n return (\n <a href={href} className={className}>\n {children}\n </a>\n );\n }\n return (\n <Link href={href} className={className}>\n {children}\n </Link>\n );\n}\n","import type { ImageProps } from 'next/image';\nimport type { FooterBrand, FooterColumn, FooterLink } from './footer';\nimport { Footer } from './footer';\n\nexport interface FlopayFooterOptions {\n /**\n * Origin for marketing-site routes, e.g. `https://flopay.com`. Leave unset on\n * the marketing site itself so its own routes stay relative.\n */\n baseUrl?: string;\n /** Where \"Demo Playground\" points. Defaults to the hosted playground. */\n demoUrl?: string;\n /** Where \"Contact\" points. Defaults to `<baseUrl>/contact`. */\n contactUrl?: string;\n}\n\nexport interface FlopayFooterContent {\n tagline: string;\n columns: FooterColumn[];\n legal: FooterLink[];\n copyright: string;\n}\n\nconst TAGLINE = 'Payment Orchestration That Keeps Revenue Flowing';\nconst COMPANY = 'FloPay LLC';\nconst DOCS_URL = 'https://docs.flopay.com/docs';\nconst API_URL = 'https://api.flopay.com/docs';\nconst DEMO_URL = 'https://demo.flopay.com';\n\n/**\n * Trim trailing slashes so `${base}${path}` can't double up: a caller passing\n * `https://flopay.com/` should still get `https://flopay.com/vault`, and the\n * placeholder href shouldn't keep a dangling slash either.\n */\nfunction normalizeBase(baseUrl: string) {\n return baseUrl.replace(/\\/+$/, '');\n}\n\n/**\n * The FloPay footer sitemap — one source of truth so every portal shows the\n * same columns, labels and order.\n *\n * Products without a page yet point at the marketing home page (`#` when the\n * caller *is* the marketing site, where that would be a self-link).\n */\nexport function flopayFooterContent({\n baseUrl = '',\n demoUrl = DEMO_URL,\n contactUrl,\n}: FlopayFooterOptions = {}): FlopayFooterContent {\n const base = normalizeBase(baseUrl);\n const route = (path: string) => `${base}${path}`;\n const pending = base || '#';\n\n return {\n tagline: TAGLINE,\n columns: [\n {\n title: 'Product',\n links: [\n { href: route('/vault'), label: 'Card Vault' },\n { href: route('/chargeback-prevention'), label: 'Chargeback Prevention' },\n { href: pending, label: 'Agent-Assisted Commerce' },\n { href: pending, label: 'The Agent Vault' },\n { href: pending, label: 'Agentic Discovery Network' },\n { href: pending, label: 'Smart Agentic Orchestration' },\n ],\n },\n {\n title: 'Development',\n links: [\n { href: DOCS_URL, label: 'Documentation' },\n { href: pending, label: 'MCP Server' },\n { href: API_URL, label: 'API Reference' },\n { href: demoUrl, label: 'Demo Playground' },\n ],\n },\n {\n title: 'Company',\n links: [\n { href: route('/mission'), label: 'Mission' },\n { href: route('/values'), label: 'Values' },\n { href: route('/pricing'), label: 'Pricing' },\n { href: route('/blog'), label: 'Blog' },\n { href: contactUrl ?? route('/contact'), label: 'Contact' },\n ],\n },\n ],\n legal: [\n { href: route('/legal/privacy'), label: 'Privacy' },\n { href: route('/legal/terms'), label: 'Terms' },\n { href: route('/legal/cookies'), label: 'Cookies' },\n ],\n copyright: `© ${new Date().getFullYear()} ${COMPANY}. All rights reserved.`,\n };\n}\n\nexport interface FlopayFooterProps extends FlopayFooterOptions {\n /** The shared logo: `import logo from '@flopay/ui/logo.png'`. */\n logo: ImageProps['src'];\n /** Override parts of the default brand block (label, tagline, size, …). */\n brand?: Partial<Omit<FooterBrand, 'src'>>;\n className?: string;\n}\n\n/**\n * The FloPay footer, content included — drop it into any portal and the\n * sitemap, tagline and copyright come from this package.\n *\n * ```tsx\n * import logo from '@flopay/ui/logo.png';\n * <FlopayFooter logo={logo} /> // on flopay.com\n * <FlopayFooter logo={logo} baseUrl=\"https://flopay.com\" /> // anywhere else\n * ```\n */\nexport function FlopayFooter({ logo, brand, className, ...options }: FlopayFooterProps) {\n const { tagline, columns, legal, copyright } = flopayFooterContent(options);\n\n return (\n <Footer\n className={className}\n brand={{\n src: logo,\n alt: 'FloPay',\n label: 'FloPay',\n href: normalizeBase(options.baseUrl ?? '') || '/',\n size: 20,\n mark: true,\n tagline,\n ...brand,\n }}\n columns={columns}\n legal={legal}\n copyright={copyright}\n />\n );\n}\n","import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface NavLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n /** Render as the prominent call-to-action button. */\n cta?: boolean;\n}\n\nexport interface NavLogo {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n /** Logo image edge size in px (default 28). */\n size?: number;\n /** Wrap the image in the rounded brand mark. */\n mark?: boolean;\n}\n\nexport interface NavProps {\n logo: NavLogo;\n links?: NavLink[];\n /** Extra item appended to the link list (e.g. an interactive control). */\n children?: ReactNode;\n className?: string;\n}\n\n/** Shared fixed top navigation for marketing surfaces (landing, demo). */\nexport function Nav({ logo, links = [], children, className }: NavProps) {\n const size = logo.size ?? 28;\n const image = <Image src={logo.src} alt={logo.alt ?? ''} width={size} height={size} priority />;\n\n return (\n <nav className={cn('nav', className)}>\n <div className=\"nav-inner\">\n <MarketingLink href={logo.href ?? '/'} className=\"nav-logo\">\n {logo.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {logo.label}\n </MarketingLink>\n <ul className=\"nav-links\">\n {/* Keyed by index: placeholder links legitimately share an href.\n `nav-link-item` is what the narrow-viewport rule hides, so the\n call to action survives on mobile while the rest collapse. */}\n {links.map((link, index) => (\n <li key={index} className={link.cta ? undefined : 'nav-link-item'}>\n <MarketingLink\n href={link.href}\n external={link.external}\n className={link.cta ? 'nav-cta' : undefined}\n >\n {link.label}\n </MarketingLink>\n </li>\n ))}\n {children ? <li>{children}</li> : null}\n </ul>\n </div>\n </nav>\n );\n}\n"],"mappings":"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","names":["Link","import_jsx_runtime","Image","import_jsx_runtime","import_image","import_jsx_runtime","Image"]}
@@ -31,6 +31,50 @@ interface FooterProps {
31
31
  /** Shared site footer for marketing surfaces (landing, demo). */
32
32
  declare function Footer({ brand, columns, legal, copyright, className }: FooterProps): react.JSX.Element;
33
33
 
34
+ interface FlopayFooterOptions {
35
+ /**
36
+ * Origin for marketing-site routes, e.g. `https://flopay.com`. Leave unset on
37
+ * the marketing site itself so its own routes stay relative.
38
+ */
39
+ baseUrl?: string;
40
+ /** Where "Demo Playground" points. Defaults to the hosted playground. */
41
+ demoUrl?: string;
42
+ /** Where "Contact" points. Defaults to `<baseUrl>/contact`. */
43
+ contactUrl?: string;
44
+ }
45
+ interface FlopayFooterContent {
46
+ tagline: string;
47
+ columns: FooterColumn[];
48
+ legal: FooterLink[];
49
+ copyright: string;
50
+ }
51
+ /**
52
+ * The FloPay footer sitemap — one source of truth so every portal shows the
53
+ * same columns, labels and order.
54
+ *
55
+ * Products without a page yet point at the marketing home page (`#` when the
56
+ * caller *is* the marketing site, where that would be a self-link).
57
+ */
58
+ declare function flopayFooterContent({ baseUrl, demoUrl, contactUrl, }?: FlopayFooterOptions): FlopayFooterContent;
59
+ interface FlopayFooterProps extends FlopayFooterOptions {
60
+ /** The shared logo: `import logo from '@flopay/ui/logo.png'`. */
61
+ logo: ImageProps['src'];
62
+ /** Override parts of the default brand block (label, tagline, size, …). */
63
+ brand?: Partial<Omit<FooterBrand, 'src'>>;
64
+ className?: string;
65
+ }
66
+ /**
67
+ * The FloPay footer, content included — drop it into any portal and the
68
+ * sitemap, tagline and copyright come from this package.
69
+ *
70
+ * ```tsx
71
+ * import logo from '@flopay/ui/logo.png';
72
+ * <FlopayFooter logo={logo} /> // on flopay.com
73
+ * <FlopayFooter logo={logo} baseUrl="https://flopay.com" /> // anywhere else
74
+ * ```
75
+ */
76
+ declare function FlopayFooter({ logo, brand, className, ...options }: FlopayFooterProps): react.JSX.Element;
77
+
34
78
  interface MarketingLinkProps {
35
79
  href: string;
36
80
  /** Force new-tab rendering. Defaults to true for absolute http(s) URLs. */
@@ -74,4 +118,4 @@ interface NavProps {
74
118
  /** Shared fixed top navigation for marketing surfaces (landing, demo). */
75
119
  declare function Nav({ logo, links, children, className }: NavProps): react.JSX.Element;
76
120
 
77
- export { Footer, type FooterBrand, type FooterColumn, type FooterLink, type FooterProps, MarketingLink, type MarketingLinkProps, Nav, type NavLink, type NavLogo, type NavProps };
121
+ export { FlopayFooter, type FlopayFooterContent, type FlopayFooterOptions, type FlopayFooterProps, Footer, type FooterBrand, type FooterColumn, type FooterLink, type FooterProps, MarketingLink, type MarketingLinkProps, Nav, type NavLink, type NavLogo, type NavProps, flopayFooterContent };
@@ -31,6 +31,50 @@ interface FooterProps {
31
31
  /** Shared site footer for marketing surfaces (landing, demo). */
32
32
  declare function Footer({ brand, columns, legal, copyright, className }: FooterProps): react.JSX.Element;
33
33
 
34
+ interface FlopayFooterOptions {
35
+ /**
36
+ * Origin for marketing-site routes, e.g. `https://flopay.com`. Leave unset on
37
+ * the marketing site itself so its own routes stay relative.
38
+ */
39
+ baseUrl?: string;
40
+ /** Where "Demo Playground" points. Defaults to the hosted playground. */
41
+ demoUrl?: string;
42
+ /** Where "Contact" points. Defaults to `<baseUrl>/contact`. */
43
+ contactUrl?: string;
44
+ }
45
+ interface FlopayFooterContent {
46
+ tagline: string;
47
+ columns: FooterColumn[];
48
+ legal: FooterLink[];
49
+ copyright: string;
50
+ }
51
+ /**
52
+ * The FloPay footer sitemap — one source of truth so every portal shows the
53
+ * same columns, labels and order.
54
+ *
55
+ * Products without a page yet point at the marketing home page (`#` when the
56
+ * caller *is* the marketing site, where that would be a self-link).
57
+ */
58
+ declare function flopayFooterContent({ baseUrl, demoUrl, contactUrl, }?: FlopayFooterOptions): FlopayFooterContent;
59
+ interface FlopayFooterProps extends FlopayFooterOptions {
60
+ /** The shared logo: `import logo from '@flopay/ui/logo.png'`. */
61
+ logo: ImageProps['src'];
62
+ /** Override parts of the default brand block (label, tagline, size, …). */
63
+ brand?: Partial<Omit<FooterBrand, 'src'>>;
64
+ className?: string;
65
+ }
66
+ /**
67
+ * The FloPay footer, content included — drop it into any portal and the
68
+ * sitemap, tagline and copyright come from this package.
69
+ *
70
+ * ```tsx
71
+ * import logo from '@flopay/ui/logo.png';
72
+ * <FlopayFooter logo={logo} /> // on flopay.com
73
+ * <FlopayFooter logo={logo} baseUrl="https://flopay.com" /> // anywhere else
74
+ * ```
75
+ */
76
+ declare function FlopayFooter({ logo, brand, className, ...options }: FlopayFooterProps): react.JSX.Element;
77
+
34
78
  interface MarketingLinkProps {
35
79
  href: string;
36
80
  /** Force new-tab rendering. Defaults to true for absolute http(s) URLs. */
@@ -74,4 +118,4 @@ interface NavProps {
74
118
  /** Shared fixed top navigation for marketing surfaces (landing, demo). */
75
119
  declare function Nav({ logo, links, children, className }: NavProps): react.JSX.Element;
76
120
 
77
- export { Footer, type FooterBrand, type FooterColumn, type FooterLink, type FooterProps, MarketingLink, type MarketingLinkProps, Nav, type NavLink, type NavLogo, type NavProps };
121
+ export { FlopayFooter, type FlopayFooterContent, type FlopayFooterOptions, type FlopayFooterProps, Footer, type FooterBrand, type FooterColumn, type FooterLink, type FooterProps, MarketingLink, type MarketingLinkProps, Nav, type NavLink, type NavLogo, type NavProps, flopayFooterContent };
@@ -37,7 +37,7 @@ function Footer({ brand, columns = [], legal = [], copyright, className }) {
37
37
  ] }),
38
38
  columns.map((column, index) => /* @__PURE__ */ jsxs("div", { className: "footer-column", children: [
39
39
  /* @__PURE__ */ jsx2("h4", { children: column.title }),
40
- /* @__PURE__ */ jsx2("ul", { children: column.links.map((link) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(MarketingLink, { href: link.href, external: link.external, children: link.label }) }, link.href)) })
40
+ /* @__PURE__ */ jsx2("ul", { children: column.links.map((link, linkIndex) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(MarketingLink, { href: link.href, external: link.external, children: link.label }) }, linkIndex)) })
41
41
  ] }, index))
42
42
  ] }),
43
43
  /* @__PURE__ */ jsxs("div", { className: "footer-bottom", children: [
@@ -47,19 +47,102 @@ function Footer({ brand, columns = [], legal = [], copyright, className }) {
47
47
  ] });
48
48
  }
49
49
 
50
+ // src/marketing/flopay-footer.tsx
51
+ import { jsx as jsx3 } from "react/jsx-runtime";
52
+ var TAGLINE = "Payment Orchestration That Keeps Revenue Flowing";
53
+ var COMPANY = "FloPay LLC";
54
+ var DOCS_URL = "https://docs.flopay.com/docs";
55
+ var API_URL = "https://api.flopay.com/docs";
56
+ var DEMO_URL = "https://demo.flopay.com";
57
+ function normalizeBase(baseUrl) {
58
+ return baseUrl.replace(/\/+$/, "");
59
+ }
60
+ function flopayFooterContent({
61
+ baseUrl = "",
62
+ demoUrl = DEMO_URL,
63
+ contactUrl
64
+ } = {}) {
65
+ const base = normalizeBase(baseUrl);
66
+ const route = (path) => `${base}${path}`;
67
+ const pending = base || "#";
68
+ return {
69
+ tagline: TAGLINE,
70
+ columns: [
71
+ {
72
+ title: "Product",
73
+ links: [
74
+ { href: route("/vault"), label: "Card Vault" },
75
+ { href: route("/chargeback-prevention"), label: "Chargeback Prevention" },
76
+ { href: pending, label: "Agent-Assisted Commerce" },
77
+ { href: pending, label: "The Agent Vault" },
78
+ { href: pending, label: "Agentic Discovery Network" },
79
+ { href: pending, label: "Smart Agentic Orchestration" }
80
+ ]
81
+ },
82
+ {
83
+ title: "Development",
84
+ links: [
85
+ { href: DOCS_URL, label: "Documentation" },
86
+ { href: pending, label: "MCP Server" },
87
+ { href: API_URL, label: "API Reference" },
88
+ { href: demoUrl, label: "Demo Playground" }
89
+ ]
90
+ },
91
+ {
92
+ title: "Company",
93
+ links: [
94
+ { href: route("/mission"), label: "Mission" },
95
+ { href: route("/values"), label: "Values" },
96
+ { href: route("/pricing"), label: "Pricing" },
97
+ { href: route("/blog"), label: "Blog" },
98
+ { href: contactUrl ?? route("/contact"), label: "Contact" }
99
+ ]
100
+ }
101
+ ],
102
+ legal: [
103
+ { href: route("/legal/privacy"), label: "Privacy" },
104
+ { href: route("/legal/terms"), label: "Terms" },
105
+ { href: route("/legal/cookies"), label: "Cookies" }
106
+ ],
107
+ copyright: `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} ${COMPANY}. All rights reserved.`
108
+ };
109
+ }
110
+ function FlopayFooter({ logo, brand, className, ...options }) {
111
+ const { tagline, columns, legal, copyright } = flopayFooterContent(options);
112
+ return /* @__PURE__ */ jsx3(
113
+ Footer,
114
+ {
115
+ className,
116
+ brand: {
117
+ src: logo,
118
+ alt: "FloPay",
119
+ label: "FloPay",
120
+ href: normalizeBase(options.baseUrl ?? "") || "/",
121
+ size: 20,
122
+ mark: true,
123
+ tagline,
124
+ ...brand
125
+ },
126
+ columns,
127
+ legal,
128
+ copyright
129
+ }
130
+ );
131
+ }
132
+
50
133
  // src/marketing/nav.tsx
51
134
  import Image2 from "next/image";
52
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
135
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
53
136
  function Nav({ logo, links = [], children, className }) {
54
137
  const size = logo.size ?? 28;
55
- const image = /* @__PURE__ */ jsx3(Image2, { src: logo.src, alt: logo.alt ?? "", width: size, height: size, priority: true });
56
- return /* @__PURE__ */ jsx3("nav", { className: cn("nav", className), children: /* @__PURE__ */ jsxs2("div", { className: "nav-inner", children: [
138
+ const image = /* @__PURE__ */ jsx4(Image2, { src: logo.src, alt: logo.alt ?? "", width: size, height: size, priority: true });
139
+ return /* @__PURE__ */ jsx4("nav", { className: cn("nav", className), children: /* @__PURE__ */ jsxs2("div", { className: "nav-inner", children: [
57
140
  /* @__PURE__ */ jsxs2(MarketingLink, { href: logo.href ?? "/", className: "nav-logo", children: [
58
- logo.mark ? /* @__PURE__ */ jsx3("span", { className: "nav-logo-mark", style: { width: size, height: size }, children: image }) : image,
141
+ logo.mark ? /* @__PURE__ */ jsx4("span", { className: "nav-logo-mark", style: { width: size, height: size }, children: image }) : image,
59
142
  logo.label
60
143
  ] }),
61
144
  /* @__PURE__ */ jsxs2("ul", { className: "nav-links", children: [
62
- links.map((link) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(
145
+ links.map((link, index) => /* @__PURE__ */ jsx4("li", { className: link.cta ? void 0 : "nav-link-item", children: /* @__PURE__ */ jsx4(
63
146
  MarketingLink,
64
147
  {
65
148
  href: link.href,
@@ -67,14 +150,16 @@ function Nav({ logo, links = [], children, className }) {
67
150
  className: link.cta ? "nav-cta" : void 0,
68
151
  children: link.label
69
152
  }
70
- ) }, link.href)),
71
- children ? /* @__PURE__ */ jsx3("li", { children }) : null
153
+ ) }, index)),
154
+ children ? /* @__PURE__ */ jsx4("li", { children }) : null
72
155
  ] })
73
156
  ] }) });
74
157
  }
75
158
  export {
159
+ FlopayFooter,
76
160
  Footer,
77
161
  MarketingLink,
78
- Nav
162
+ Nav,
163
+ flopayFooterContent
79
164
  };
80
165
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/marketing/footer.tsx","../../src/marketing/link.tsx","../../src/marketing/nav.tsx"],"sourcesContent":["import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface FooterLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n}\n\nexport interface FooterColumn {\n title: ReactNode;\n links: FooterLink[];\n}\n\nexport interface FooterBrand {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n size?: number;\n mark?: boolean;\n tagline?: ReactNode;\n}\n\nexport interface FooterProps {\n brand: FooterBrand;\n columns?: FooterColumn[];\n legal?: FooterLink[];\n copyright?: ReactNode;\n className?: string;\n}\n\n/** Shared site footer for marketing surfaces (landing, demo). */\nexport function Footer({ brand, columns = [], legal = [], copyright, className }: FooterProps) {\n const size = brand.size ?? 20;\n const image = <Image src={brand.src} alt={brand.alt ?? ''} width={size} height={size} />;\n\n return (\n <footer className={cn('footer', className)}>\n <div className=\"footer-inner\">\n <div className=\"footer-brand\">\n <MarketingLink href={brand.href ?? '/'} className=\"nav-logo\">\n {brand.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {brand.label}\n </MarketingLink>\n {brand.tagline ? <p>{brand.tagline}</p> : null}\n </div>\n {columns.map((column, index) => (\n <div className=\"footer-column\" key={index}>\n <h4>{column.title}</h4>\n <ul>\n {column.links.map((link) => (\n <li key={link.href}>\n <MarketingLink href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n <div className=\"footer-bottom\">\n {copyright ? <p>{copyright}</p> : <span />}\n {legal.length > 0 ? (\n <div className=\"footer-legal\">\n {legal.map((link) => (\n <MarketingLink key={link.href} href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n ))}\n </div>\n ) : null}\n </div>\n </footer>\n );\n}\n","import Link from 'next/link';\nimport type { ReactNode } from 'react';\n\nexport interface MarketingLinkProps {\n href: string;\n /** Force new-tab rendering. Defaults to true for absolute http(s) URLs. */\n external?: boolean;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * Picks the right element for a marketing link:\n * - internal route (\"/...\", \"#...\") → next/link\n * - protocol link (mailto:/tel:) → plain anchor\n * - absolute URL (or `external`) → new-tab anchor\n */\nexport function MarketingLink({ href, external, className, children }: MarketingLinkProps) {\n const isAbsolute = /^https?:\\/\\//.test(href) || href.startsWith('//');\n const isProtocol = !isAbsolute && /^[a-z][a-z0-9+.-]*:/i.test(href);\n const openExternally = external ?? isAbsolute;\n\n if (openExternally) {\n return (\n <a href={href} className={className} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n }\n if (isProtocol) {\n return (\n <a href={href} className={className}>\n {children}\n </a>\n );\n }\n return (\n <Link href={href} className={className}>\n {children}\n </Link>\n );\n}\n","import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface NavLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n /** Render as the prominent call-to-action button. */\n cta?: boolean;\n}\n\nexport interface NavLogo {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n /** Logo image edge size in px (default 28). */\n size?: number;\n /** Wrap the image in the rounded brand mark. */\n mark?: boolean;\n}\n\nexport interface NavProps {\n logo: NavLogo;\n links?: NavLink[];\n /** Extra item appended to the link list (e.g. an interactive control). */\n children?: ReactNode;\n className?: string;\n}\n\n/** Shared fixed top navigation for marketing surfaces (landing, demo). */\nexport function Nav({ logo, links = [], children, className }: NavProps) {\n const size = logo.size ?? 28;\n const image = <Image src={logo.src} alt={logo.alt ?? ''} width={size} height={size} priority />;\n\n return (\n <nav className={cn('nav', className)}>\n <div className=\"nav-inner\">\n <MarketingLink href={logo.href ?? '/'} className=\"nav-logo\">\n {logo.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {logo.label}\n </MarketingLink>\n <ul className=\"nav-links\">\n {links.map((link) => (\n <li key={link.href}>\n <MarketingLink\n href={link.href}\n external={link.external}\n className={link.cta ? 'nav-cta' : undefined}\n >\n {link.label}\n </MarketingLink>\n </li>\n ))}\n {children ? <li>{children}</li> : null}\n </ul>\n </div>\n </nav>\n );\n}\n"],"mappings":"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","names":["jsx","Image","jsx","jsxs","Image"]}
1
+ {"version":3,"sources":["../../src/marketing/footer.tsx","../../src/marketing/link.tsx","../../src/marketing/flopay-footer.tsx","../../src/marketing/nav.tsx"],"sourcesContent":["import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface FooterLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n}\n\nexport interface FooterColumn {\n title: ReactNode;\n links: FooterLink[];\n}\n\nexport interface FooterBrand {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n size?: number;\n mark?: boolean;\n tagline?: ReactNode;\n}\n\nexport interface FooterProps {\n brand: FooterBrand;\n columns?: FooterColumn[];\n legal?: FooterLink[];\n copyright?: ReactNode;\n className?: string;\n}\n\n/** Shared site footer for marketing surfaces (landing, demo). */\nexport function Footer({ brand, columns = [], legal = [], copyright, className }: FooterProps) {\n const size = brand.size ?? 20;\n const image = <Image src={brand.src} alt={brand.alt ?? ''} width={size} height={size} />;\n\n return (\n <footer className={cn('footer', className)}>\n <div className=\"footer-inner\">\n <div className=\"footer-brand\">\n <MarketingLink href={brand.href ?? '/'} className=\"nav-logo\">\n {brand.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {brand.label}\n </MarketingLink>\n {brand.tagline ? <p>{brand.tagline}</p> : null}\n </div>\n {columns.map((column, index) => (\n <div className=\"footer-column\" key={index}>\n <h4>{column.title}</h4>\n <ul>\n {/* Keyed by index: placeholder links legitimately share an href. */}\n {column.links.map((link, linkIndex) => (\n <li key={linkIndex}>\n <MarketingLink href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n </li>\n ))}\n </ul>\n </div>\n ))}\n </div>\n <div className=\"footer-bottom\">\n {copyright ? <p>{copyright}</p> : <span />}\n {legal.length > 0 ? (\n <div className=\"footer-legal\">\n {legal.map((link) => (\n <MarketingLink key={link.href} href={link.href} external={link.external}>\n {link.label}\n </MarketingLink>\n ))}\n </div>\n ) : null}\n </div>\n </footer>\n );\n}\n","import Link from 'next/link';\nimport type { ReactNode } from 'react';\n\nexport interface MarketingLinkProps {\n href: string;\n /** Force new-tab rendering. Defaults to true for absolute http(s) URLs. */\n external?: boolean;\n className?: string;\n children: ReactNode;\n}\n\n/**\n * Picks the right element for a marketing link:\n * - internal route (\"/...\", \"#...\") → next/link\n * - protocol link (mailto:/tel:) → plain anchor\n * - absolute URL (or `external`) → new-tab anchor\n */\nexport function MarketingLink({ href, external, className, children }: MarketingLinkProps) {\n const isAbsolute = /^https?:\\/\\//.test(href) || href.startsWith('//');\n const isProtocol = !isAbsolute && /^[a-z][a-z0-9+.-]*:/i.test(href);\n const openExternally = external ?? isAbsolute;\n\n if (openExternally) {\n return (\n <a href={href} className={className} target=\"_blank\" rel=\"noopener noreferrer\">\n {children}\n </a>\n );\n }\n if (isProtocol) {\n return (\n <a href={href} className={className}>\n {children}\n </a>\n );\n }\n return (\n <Link href={href} className={className}>\n {children}\n </Link>\n );\n}\n","import type { ImageProps } from 'next/image';\nimport type { FooterBrand, FooterColumn, FooterLink } from './footer';\nimport { Footer } from './footer';\n\nexport interface FlopayFooterOptions {\n /**\n * Origin for marketing-site routes, e.g. `https://flopay.com`. Leave unset on\n * the marketing site itself so its own routes stay relative.\n */\n baseUrl?: string;\n /** Where \"Demo Playground\" points. Defaults to the hosted playground. */\n demoUrl?: string;\n /** Where \"Contact\" points. Defaults to `<baseUrl>/contact`. */\n contactUrl?: string;\n}\n\nexport interface FlopayFooterContent {\n tagline: string;\n columns: FooterColumn[];\n legal: FooterLink[];\n copyright: string;\n}\n\nconst TAGLINE = 'Payment Orchestration That Keeps Revenue Flowing';\nconst COMPANY = 'FloPay LLC';\nconst DOCS_URL = 'https://docs.flopay.com/docs';\nconst API_URL = 'https://api.flopay.com/docs';\nconst DEMO_URL = 'https://demo.flopay.com';\n\n/**\n * Trim trailing slashes so `${base}${path}` can't double up: a caller passing\n * `https://flopay.com/` should still get `https://flopay.com/vault`, and the\n * placeholder href shouldn't keep a dangling slash either.\n */\nfunction normalizeBase(baseUrl: string) {\n return baseUrl.replace(/\\/+$/, '');\n}\n\n/**\n * The FloPay footer sitemap — one source of truth so every portal shows the\n * same columns, labels and order.\n *\n * Products without a page yet point at the marketing home page (`#` when the\n * caller *is* the marketing site, where that would be a self-link).\n */\nexport function flopayFooterContent({\n baseUrl = '',\n demoUrl = DEMO_URL,\n contactUrl,\n}: FlopayFooterOptions = {}): FlopayFooterContent {\n const base = normalizeBase(baseUrl);\n const route = (path: string) => `${base}${path}`;\n const pending = base || '#';\n\n return {\n tagline: TAGLINE,\n columns: [\n {\n title: 'Product',\n links: [\n { href: route('/vault'), label: 'Card Vault' },\n { href: route('/chargeback-prevention'), label: 'Chargeback Prevention' },\n { href: pending, label: 'Agent-Assisted Commerce' },\n { href: pending, label: 'The Agent Vault' },\n { href: pending, label: 'Agentic Discovery Network' },\n { href: pending, label: 'Smart Agentic Orchestration' },\n ],\n },\n {\n title: 'Development',\n links: [\n { href: DOCS_URL, label: 'Documentation' },\n { href: pending, label: 'MCP Server' },\n { href: API_URL, label: 'API Reference' },\n { href: demoUrl, label: 'Demo Playground' },\n ],\n },\n {\n title: 'Company',\n links: [\n { href: route('/mission'), label: 'Mission' },\n { href: route('/values'), label: 'Values' },\n { href: route('/pricing'), label: 'Pricing' },\n { href: route('/blog'), label: 'Blog' },\n { href: contactUrl ?? route('/contact'), label: 'Contact' },\n ],\n },\n ],\n legal: [\n { href: route('/legal/privacy'), label: 'Privacy' },\n { href: route('/legal/terms'), label: 'Terms' },\n { href: route('/legal/cookies'), label: 'Cookies' },\n ],\n copyright: `© ${new Date().getFullYear()} ${COMPANY}. All rights reserved.`,\n };\n}\n\nexport interface FlopayFooterProps extends FlopayFooterOptions {\n /** The shared logo: `import logo from '@flopay/ui/logo.png'`. */\n logo: ImageProps['src'];\n /** Override parts of the default brand block (label, tagline, size, …). */\n brand?: Partial<Omit<FooterBrand, 'src'>>;\n className?: string;\n}\n\n/**\n * The FloPay footer, content included — drop it into any portal and the\n * sitemap, tagline and copyright come from this package.\n *\n * ```tsx\n * import logo from '@flopay/ui/logo.png';\n * <FlopayFooter logo={logo} /> // on flopay.com\n * <FlopayFooter logo={logo} baseUrl=\"https://flopay.com\" /> // anywhere else\n * ```\n */\nexport function FlopayFooter({ logo, brand, className, ...options }: FlopayFooterProps) {\n const { tagline, columns, legal, copyright } = flopayFooterContent(options);\n\n return (\n <Footer\n className={className}\n brand={{\n src: logo,\n alt: 'FloPay',\n label: 'FloPay',\n href: normalizeBase(options.baseUrl ?? '') || '/',\n size: 20,\n mark: true,\n tagline,\n ...brand,\n }}\n columns={columns}\n legal={legal}\n copyright={copyright}\n />\n );\n}\n","import Image, { type ImageProps } from 'next/image';\nimport type { ReactNode } from 'react';\nimport { cn } from '../cn';\nimport { MarketingLink } from './link';\n\nexport interface NavLink {\n href: string;\n label: ReactNode;\n external?: boolean;\n /** Render as the prominent call-to-action button. */\n cta?: boolean;\n}\n\nexport interface NavLogo {\n /** A URL string or a statically-imported image (e.g. `@flopay/ui/logo.png`). */\n src: ImageProps['src'];\n alt?: string;\n label?: ReactNode;\n href?: string;\n /** Logo image edge size in px (default 28). */\n size?: number;\n /** Wrap the image in the rounded brand mark. */\n mark?: boolean;\n}\n\nexport interface NavProps {\n logo: NavLogo;\n links?: NavLink[];\n /** Extra item appended to the link list (e.g. an interactive control). */\n children?: ReactNode;\n className?: string;\n}\n\n/** Shared fixed top navigation for marketing surfaces (landing, demo). */\nexport function Nav({ logo, links = [], children, className }: NavProps) {\n const size = logo.size ?? 28;\n const image = <Image src={logo.src} alt={logo.alt ?? ''} width={size} height={size} priority />;\n\n return (\n <nav className={cn('nav', className)}>\n <div className=\"nav-inner\">\n <MarketingLink href={logo.href ?? '/'} className=\"nav-logo\">\n {logo.mark ? (\n <span className=\"nav-logo-mark\" style={{ width: size, height: size }}>\n {image}\n </span>\n ) : (\n image\n )}\n {logo.label}\n </MarketingLink>\n <ul className=\"nav-links\">\n {/* Keyed by index: placeholder links legitimately share an href.\n `nav-link-item` is what the narrow-viewport rule hides, so the\n call to action survives on mobile while the rest collapse. */}\n {links.map((link, index) => (\n <li key={index} className={link.cta ? undefined : 'nav-link-item'}>\n <MarketingLink\n href={link.href}\n external={link.external}\n className={link.cta ? 'nav-cta' : undefined}\n >\n {link.label}\n </MarketingLink>\n </li>\n ))}\n {children ? <li>{children}</li> : null}\n </ul>\n </div>\n </nav>\n );\n}\n"],"mappings":"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","names":["jsx","jsx","Image","jsx","jsxs","Image"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flopay/ui",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "packageManager": "pnpm@11.17.0",
6
6
  "description": "FloPay shared design system — design tokens, a Tailwind v4 preset, and React primitives shared across landing, demo, docs, and dashboard.",
@@ -368,6 +368,12 @@
368
368
  .nav-inner {
369
369
  padding: 0 16px;
370
370
  }
371
+ /* Collapse the nav to logo + call to action; `Nav` tags every non-CTA
372
+ item with `nav-link-item`. Apps that don't import this file (they keep
373
+ their own component CSS) can re-declare just this rule to opt in. */
374
+ .nav-links .nav-link-item {
375
+ display: none;
376
+ }
371
377
  .section {
372
378
  padding: 80px 16px;
373
379
  }