@goldlabelapps/theme 3.2.0 → 3.5.6
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 +2 -2
- package/dist/components/RenderMarkdown.d.ts +7 -0
- package/dist/components/RenderMarkdown.js +123 -0
- package/dist/components/download/DownloadBanner.js +1 -8
- package/dist/components/footer/Footer.js +6 -3
- package/dist/components/footer/FooterWrapper.d.ts +7 -0
- package/dist/components/footer/FooterWrapper.js +15 -0
- package/dist/components/footer/NXContactColumn.d.ts +13 -0
- package/dist/components/footer/NXContactColumn.js +32 -0
- package/dist/components/footer/NXLiveApps.d.ts +22 -0
- package/dist/components/footer/NXLiveApps.js +87 -0
- package/dist/components/footer/NXSocialColumn.d.ts +14 -0
- package/dist/components/footer/NXSocialColumn.js +17 -0
- package/dist/components/footer/NXTagsColumn.d.ts +14 -0
- package/dist/components/footer/NXTagsColumn.js +31 -0
- package/dist/components/footer/index.d.ts +6 -0
- package/dist/components/footer/index.js +6 -0
- package/dist/components/hero/HeroSection.js +1 -1
- package/dist/components/hero/VideoModal.js +1 -2
- package/dist/components/index.d.ts +11 -3
- package/dist/components/index.js +11 -3
- package/dist/components/layouts/GuideBreadcrumbs.d.ts +14 -0
- package/dist/components/layouts/GuideBreadcrumbs.js +15 -0
- package/dist/components/layouts/MarkdownContent.js +6 -0
- package/dist/components/layouts/Standard.d.ts +10 -0
- package/dist/components/layouts/Standard.js +7 -0
- package/dist/components/navigation/DropdownMenu.js +1 -1
- package/dist/components/navigation/Header.js +3 -6
- package/dist/components/navigation/LayoutActions.d.ts +7 -0
- package/dist/components/navigation/LayoutActions.js +12 -0
- package/dist/components/navigation/LogoContextMenu.d.ts +0 -1
- package/dist/components/navigation/LogoContextMenu.js +1 -2
- package/dist/components/navigation/MarkdownMenu.d.ts +9 -0
- package/dist/components/navigation/MarkdownMenu.js +12 -0
- package/dist/components/navigation/MobileMenu.js +16 -7
- package/dist/components/navigation/ShareMenu.d.ts +1 -1
- package/dist/components/navigation/ShareMenu.js +1 -2
- package/dist/components/navigation/Waiting.d.ts +10 -0
- package/dist/components/navigation/Waiting.js +45 -0
- package/dist/components/search/HeroSearchBar.d.ts +16 -0
- package/dist/components/search/HeroSearchBar.js +38 -0
- package/dist/components/search/SearchDialog.d.ts +11 -0
- package/dist/components/search/SearchDialog.js +69 -0
- package/dist/components/search/SearchIconButton.d.ts +10 -0
- package/dist/components/search/SearchIconButton.js +10 -0
- package/dist/components/search/SearchResultsList.d.ts +13 -0
- package/dist/components/search/SearchResultsList.js +18 -0
- package/dist/components/search/index.d.ts +5 -0
- package/dist/components/search/index.js +5 -0
- package/dist/components/search/useSearch.d.ts +54 -0
- package/dist/components/search/useSearch.js +106 -0
- package/dist/components/shortcodes/PageLinkShortcode.d.ts +15 -0
- package/dist/components/shortcodes/PageLinkShortcode.js +51 -0
- package/dist/components/showcase/BlogSection.js +2 -2
- package/dist/components/showcase/FeatureExplorer.js +2 -2
- package/dist/components/showcase/FeaturedCard.d.ts +13 -0
- package/dist/components/showcase/FeaturedCard.js +6 -0
- package/dist/components/showcase/SolutionsSection.js +2 -2
- package/dist/components/showcase/StatementSection.js +1 -1
- package/dist/components/showcase/VideoSection.js +2 -2
- package/dist/components/ui/Bash.d.ts +16 -0
- package/dist/components/ui/Bash.js +30 -0
- package/dist/components/ui/Button.js +1 -1
- package/dist/components/ui/Card.d.ts +2 -1
- package/dist/components/ui/Card.js +29 -7
- package/dist/components/ui/ThemeToggle.d.ts +5 -1
- package/dist/components/ui/ThemeToggle.js +32 -2
- package/dist/components/video/VideoLayout.js +2 -2
- package/dist/context/ConfigContext.js +1 -1
- package/dist/context/ThemeContext.d.ts +4 -0
- package/dist/context/ThemeContext.js +23 -18
- package/dist/data/og-images.json +530 -0
- package/dist/design-system/components/DesignSystemProvider/DesignSystemProvider.js +4 -9
- package/dist/design-system/components/buttons/Button/Button.js +6 -3
- package/dist/design-system/components/buttons/IconButton/IconButton.js +6 -3
- package/dist/design-system/components/feedback/Alert/Alert.js +45 -7
- package/dist/design-system/components/feedback/Card/Card.d.ts +1 -1
- package/dist/design-system/components/feedback/Card/Card.js +15 -9
- package/dist/design-system/components/headings/Heading/Heading.js +2 -1
- package/dist/design-system/components/icons/SVGIcons/BlokeyIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/BlokeyIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/ChromeIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/ChromeIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/DesktopIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/DesktopIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/EdgeIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/EdgeIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/FallmanagerIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/FirefoxIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/FirefoxIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/FlickrIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/FlickrIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/GatsbyIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/GoldenticketIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/GoldenticketIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/GoldlabelOutlined.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/GraphqlIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/GraphqlIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/IphoneIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/IphoneIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/LinuxIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/LinuxIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/MacIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/MacIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/MacromediaIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/MacromediaIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/OliverIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/OpenAIIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/PingpongballIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/SafariIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/SafariIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/WindowsIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/WindowsIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/WordpressIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/WordpressIcon.js +1 -1
- package/dist/design-system/components/icons/SVGIcons/XboxIcon.d.ts +2 -1
- package/dist/design-system/components/icons/SVGIcons/XboxIcon.js +1 -1
- package/dist/design-system/components/images/FeaturedImage/FeaturedImage.d.ts +1 -1
- package/dist/design-system/components/images/FeaturedImage/FeaturedImage.js +73 -25
- package/dist/design-system/components/images/FeaturedImage/types.d.ts +1 -1
- package/dist/design-system/components/layout/Footer/Footer.js +1 -1
- package/dist/design-system/components/layout/index.d.ts +1 -0
- package/dist/design-system/components/layout/index.js +1 -0
- package/dist/design-system/components/navigation/SiteNav/SiteNav.js +1 -1
- package/dist/design-system/components/nxStyle.d.ts +39 -0
- package/dist/design-system/components/nxStyle.js +40 -14
- package/dist/design-system/types.d.ts +1 -0
- package/dist/design-system/types.js +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +2 -0
- package/dist/styles/globals.css +150 -3
- package/dist/styles/site-layout.css +1 -1
- package/dist/styles/theme.js +106 -7
- package/dist/styles/tokens.css +65 -0
- package/dist/types/index.d.ts +220 -0
- package/package.json +5 -2
- package/dist/components/guide/GuideLayout.d.ts +0 -8
- package/dist/components/guide/GuideLayout.js +0 -12
- package/dist/components/guide/MarkdownContent.js +0 -6
- /package/dist/components/{guide → layouts}/MarkdownContent.d.ts +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
5
|
+
import { Home, ChevronRight } from "lucide-react";
|
|
6
|
+
import { cn } from "../../lib/utils";
|
|
7
|
+
export function GuideBreadcrumbs({ items, homeHref = "/", homeLabel = "Home", className, }) {
|
|
8
|
+
if (!items)
|
|
9
|
+
return null;
|
|
10
|
+
return (_jsxs("nav", { "aria-label": "Breadcrumb", className: cn("flex items-center gap-2 text-xs sm:text-sm text-slate-500 overflow-x-auto no-scrollbar whitespace-nowrap py-1", className), children: [_jsxs(Link, { href: homeHref, className: "hover:text-[#0f172a] transition-colors flex items-center gap-1 font-medium shrink-0", children: [_jsx(Home, { className: "h-3.5 w-3.5" }), _jsx("span", { children: homeLabel })] }), items.map((item, idx) => {
|
|
11
|
+
const isLastItem = item.isLast ?? idx === items.length - 1;
|
|
12
|
+
return (_jsxs(React.Fragment, { children: [_jsx(ChevronRight, { className: "h-3.5 w-3.5 text-slate-400 shrink-0" }), isLastItem ? (_jsx("span", { className: "font-semibold text-slate-900 truncate shrink-0", children: item.label })) : (_jsx(Link, { href: item.href, className: "hover:text-[#0f172a] transition-colors text-slate-600 font-medium shrink-0", children: item.label }))] }, idx));
|
|
13
|
+
})] }));
|
|
14
|
+
}
|
|
15
|
+
export { GuideBreadcrumbs as GuideBreadcrumb };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
export function MarkdownContent({ content }) {
|
|
4
|
+
// Simple markdown renderer for headers, tables, code blocks, and lists
|
|
5
|
+
return (_jsx("div", { className: "prose prose-slate dark:prose-invert max-w-none text-slate-800 dark:text-slate-100 leading-relaxed font-sans", children: _jsx("div", { dangerouslySetInnerHTML: { __html: content } }) }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { GuideMeta } from "../../types";
|
|
3
|
+
export interface StandardProps {
|
|
4
|
+
guides: GuideMeta[];
|
|
5
|
+
currentGuide?: GuideMeta;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export type StandardLayoutProps = StandardProps;
|
|
9
|
+
export declare function Standard({ guides, currentGuide, children, }: StandardProps): React.JSX.Element;
|
|
10
|
+
export { Standard as StandardLayout };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { MarkdownMenu } from "../navigation/MarkdownMenu";
|
|
4
|
+
export function Standard({ guides, currentGuide, children, }) {
|
|
5
|
+
return (_jsx("div", { className: "min-h-screen pt-28 pb-20 bg-white", children: _jsx("div", { className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8", children: _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-8", children: [_jsx("aside", { className: "lg:col-span-3 space-y-4", children: _jsx(MarkdownMenu, { guides: guides, currentGuide: currentGuide }) }), _jsx("main", { className: "lg:col-span-9", children: children })] }) }) }));
|
|
6
|
+
}
|
|
7
|
+
export { Standard as StandardLayout };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import React from "react";
|
|
4
|
-
import Link from "
|
|
4
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
5
5
|
import { usePathname } from "next/navigation";
|
|
6
6
|
import { ChevronDown, Layers, Terminal, Code2, Cpu, Globe, Layout, Sparkles, Building2, BookOpen, Newspaper, History, LifeBuoy, ShieldCheck, Zap, Play, Video, ExternalLink, Download, Settings, } from "lucide-react";
|
|
7
7
|
import { cn } from "../../lib/utils";
|
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect } from "react";
|
|
4
|
-
import Link from "
|
|
5
|
-
import { LayoutDashboard, ExternalLink } from "lucide-react";
|
|
4
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
6
5
|
import { useSiteConfig } from "../../context/ConfigContext";
|
|
7
6
|
import { LogoContextMenu } from "./LogoContextMenu";
|
|
8
7
|
import { DropdownMenu } from "./DropdownMenu";
|
|
9
|
-
import {
|
|
10
|
-
import { ShareMenu } from "./ShareMenu";
|
|
11
|
-
import { Button } from "../ui/Button";
|
|
8
|
+
import { LayoutActions } from "./LayoutActions";
|
|
12
9
|
import { cn } from "../../lib/utils";
|
|
13
10
|
export function Header({ className }) {
|
|
14
11
|
const siteConfig = useSiteConfig();
|
|
@@ -28,5 +25,5 @@ export function Header({ className }) {
|
|
|
28
25
|
return _jsx(DropdownMenu, { item: item }, idx);
|
|
29
26
|
}
|
|
30
27
|
return (_jsx(Link, { href: item.href || "#", target: item.external ? "_blank" : undefined, rel: item.external ? "noreferrer" : undefined, className: "px-3.5 py-1.5 text-sm font-medium text-slate-600 hover:text-[#012867] hover:bg-slate-100/80 rounded-full transition-colors", children: item.label }, idx));
|
|
31
|
-
}) })] }),
|
|
28
|
+
}) })] }), _jsx(LayoutActions, { mobileMenuOpen: mobileMenuOpen, setMobileMenuOpen: setMobileMenuOpen })] }) }));
|
|
32
29
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface LayoutActionsProps {
|
|
3
|
+
mobileMenuOpen: boolean;
|
|
4
|
+
setMobileMenuOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function LayoutActions({ mobileMenuOpen, setMobileMenuOpen, className, }: LayoutActionsProps): React.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { LayoutDashboard, ExternalLink } from "lucide-react";
|
|
4
|
+
import { useSiteConfig } from "../../context/ConfigContext";
|
|
5
|
+
import { ShareMenu } from "./ShareMenu";
|
|
6
|
+
import { MobileMenu } from "./MobileMenu";
|
|
7
|
+
import { Button } from "../ui/Button";
|
|
8
|
+
import { cn } from "../../lib/utils";
|
|
9
|
+
export function LayoutActions({ mobileMenuOpen, setMobileMenuOpen, className, }) {
|
|
10
|
+
const siteConfig = useSiteConfig();
|
|
11
|
+
return (_jsxs("div", { className: cn("flex items-center gap-2.5 sm:gap-3", className), children: [_jsx(ShareMenu, {}), siteConfig.navigation.remoteControlBadge?.enabled && (_jsxs("a", { href: siteConfig.navigation.remoteControlBadge.href, target: "_blank", rel: "noopener noreferrer", title: siteConfig.navigation.remoteControlBadge.tooltip, className: "hidden sm:inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold text-slate-700 bg-slate-100/80 hover:bg-[#012867]/[0.08] hover:text-[#012867] border border-slate-200/80 transition-colors", children: [_jsx(LayoutDashboard, { className: "h-3.5 w-3.5 text-slate-500" }), _jsx("span", { children: siteConfig.navigation.remoteControlBadge.label }), _jsx(ExternalLink, { className: "h-3 w-3 text-slate-400" })] })), _jsx(Button, { href: siteConfig.navigation.primaryCta.href, variant: "primary", size: "sm", icon: siteConfig.navigation.primaryCta.icon, iconPosition: "right", className: "shadow-sm shadow-[#012867]/20 font-bold bg-[#012867] hover:bg-[#011f52] text-white", children: _jsx("span", { children: siteConfig.navigation.primaryCta.label }) }), _jsx(MobileMenu, { isOpen: mobileMenuOpen, onToggle: () => setMobileMenuOpen((prev) => !prev), onClose: () => setMobileMenuOpen(false) })] }));
|
|
12
|
+
}
|
|
@@ -2,6 +2,5 @@ import React from "react";
|
|
|
2
2
|
export declare function BrandLogo({ className }: {
|
|
3
3
|
className?: string;
|
|
4
4
|
}): React.JSX.Element;
|
|
5
|
-
export declare const EchoPayLogo: typeof BrandLogo;
|
|
6
5
|
export declare const NxLogo: typeof BrandLogo;
|
|
7
6
|
export declare function LogoContextMenu(): React.JSX.Element;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useRef } from "react";
|
|
4
|
-
import Link from "
|
|
4
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
5
5
|
import Image from "next/image";
|
|
6
6
|
import { Copy, Check, FileText } from "lucide-react";
|
|
7
7
|
import { useSiteConfig } from "../../context/ConfigContext";
|
|
@@ -9,7 +9,6 @@ export function BrandLogo({ className = "h-8 w-auto" }) {
|
|
|
9
9
|
const siteConfig = useSiteConfig();
|
|
10
10
|
return (_jsxs("div", { className: `inline-flex items-center gap-2.5 font-bold tracking-tight select-none ${className}`, children: [_jsx(Image, { src: siteConfig.brand.logoSrc || "/svg/favicon_light.svg", alt: siteConfig.brand.name, width: 34, height: 34, className: "h-8 w-8 shrink-0 object-contain", priority: true }), _jsx("span", { className: "font-extrabold text-lg sm:text-xl tracking-tight text-[#012867] inline-flex items-center", children: _jsx("span", { children: siteConfig.brand.name }) })] }));
|
|
11
11
|
}
|
|
12
|
-
export const EchoPayLogo = BrandLogo;
|
|
13
12
|
export const NxLogo = BrandLogo;
|
|
14
13
|
export function LogoContextMenu() {
|
|
15
14
|
const siteConfig = useSiteConfig();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { GuideMeta } from "../../types";
|
|
3
|
+
export interface MarkdownMenuProps {
|
|
4
|
+
guides: GuideMeta[];
|
|
5
|
+
currentGuide?: GuideMeta;
|
|
6
|
+
title?: string;
|
|
7
|
+
basePath?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function MarkdownMenu({ guides, currentGuide, title, basePath, }: MarkdownMenuProps): React.JSX.Element;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
4
|
+
import { BookOpen } from "lucide-react";
|
|
5
|
+
export function MarkdownMenu({ guides, currentGuide, title = "How To Guides", basePath = "/how-to", }) {
|
|
6
|
+
return (_jsxs("div", { className: "p-4 rounded-2xl bg-slate-50 border border-red-500", children: [_jsxs("div", { className: "flex items-center gap-2 mb-4 text-[#012867] font-bold text-sm", children: [_jsx(BookOpen, { className: "h-4 w-4" }), _jsx("span", { children: title })] }), _jsx("ul", { className: "space-y-1", children: guides.map((g) => {
|
|
7
|
+
const isActive = currentGuide?.slug === g.slug;
|
|
8
|
+
return (_jsx("li", { children: _jsx(Link, { href: `${basePath}${g.slug === "/" ? "" : g.slug}`, className: `block px-3 py-2 rounded-xl text-xs font-semibold transition-colors ${isActive
|
|
9
|
+
? "bg-slate-200 text-[#012867]"
|
|
10
|
+
: "text-slate-700 hover:bg-slate-200/70"}`, children: g.title }) }, g.slug));
|
|
11
|
+
}) })] }));
|
|
12
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useSyncExternalStore } from "react";
|
|
4
4
|
import { createPortal } from "react-dom";
|
|
5
|
-
import Link from "
|
|
5
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
6
6
|
import { Menu, X, ChevronDown, ExternalLink } from "lucide-react";
|
|
7
7
|
import { useSiteConfig } from "../../context/ConfigContext";
|
|
8
8
|
import { Button } from "../ui/Button";
|
|
@@ -31,13 +31,22 @@ export function MobileMenu({ isOpen: controlledIsOpen, onToggle, onClose, } = {}
|
|
|
31
31
|
}
|
|
32
32
|
};
|
|
33
33
|
useEffect(() => {
|
|
34
|
-
if (isOpen) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return () => {
|
|
38
|
-
document.body.style.overflow = originalOverflow;
|
|
39
|
-
};
|
|
34
|
+
if (!isOpen) {
|
|
35
|
+
setExpandedSections({});
|
|
36
|
+
return;
|
|
40
37
|
}
|
|
38
|
+
const originalOverflow = document.body.style.overflow;
|
|
39
|
+
document.body.style.overflow = "hidden";
|
|
40
|
+
const handleKeyDown = (e) => {
|
|
41
|
+
if (e.key === "Escape") {
|
|
42
|
+
handleClose();
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
46
|
+
return () => {
|
|
47
|
+
document.body.style.overflow = originalOverflow;
|
|
48
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
49
|
+
};
|
|
41
50
|
}, [isOpen]);
|
|
42
51
|
const toggleSection = (label) => {
|
|
43
52
|
setExpandedSections((prev) => ({
|
|
@@ -4,4 +4,4 @@ export interface ShareMenuProps {
|
|
|
4
4
|
title?: string;
|
|
5
5
|
description?: string;
|
|
6
6
|
}
|
|
7
|
-
export declare function ShareMenu({ url, title, description }: ShareMenuProps): React.JSX.Element;
|
|
7
|
+
export declare function ShareMenu({ url, title, description: _description }: ShareMenuProps): React.JSX.Element;
|
|
@@ -11,7 +11,7 @@ const emptySubscribe = (callback) => {
|
|
|
11
11
|
window.addEventListener("popstate", callback);
|
|
12
12
|
return () => window.removeEventListener("popstate", callback);
|
|
13
13
|
};
|
|
14
|
-
export function ShareMenu({ url, title, description }) {
|
|
14
|
+
export function ShareMenu({ url, title, description: _description }) {
|
|
15
15
|
const siteConfig = useSiteConfig();
|
|
16
16
|
const [isOpen, setIsOpen] = useState(false);
|
|
17
17
|
const [copied, setCopied] = useState(false);
|
|
@@ -68,7 +68,6 @@ export function ShareMenu({ url, title, description }) {
|
|
|
68
68
|
}
|
|
69
69
|
};
|
|
70
70
|
const shareTitle = title || siteConfig.metadata.title;
|
|
71
|
-
const shareDescription = description || siteConfig.metadata.description;
|
|
72
71
|
return (_jsxs("div", { className: "relative inline-flex items-center", ref: menuRef, children: [_jsxs("button", { type: "button", onClick: () => setIsOpen((prev) => !prev), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": `Share ${siteConfig.brand.name}`, className: cn("inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold transition-all duration-200 cursor-pointer border", isOpen
|
|
73
72
|
? "bg-[#012867] text-white border-[#012867] shadow-sm"
|
|
74
73
|
: "text-slate-700 bg-slate-100/80 hover:bg-[#012867]/[0.08] hover:text-[#012867] border-slate-200/80"), children: [_jsx(Share2, { className: "h-3.5 w-3.5" }), _jsx("span", { children: "Share" })] }), isOpen && (_jsxs("div", { role: "dialog", "aria-label": "Share options", className: "absolute right-0 top-full mt-2 z-50 w-64 rounded-2xl bg-white border border-slate-200 p-2 shadow-2xl backdrop-blur-2xl animate-in fade-in zoom-in-95 duration-150", children: [_jsx("div", { className: "px-2.5 py-1.5 border-b border-slate-100 mb-1", children: _jsx("p", { className: "text-[11px] font-bold text-slate-500 uppercase tracking-wider", children: "Share this page" }) }), _jsxs("div", { className: "space-y-1", children: [_jsx(FacebookShareButton, { url: currentUrl, title: shareTitle, className: "w-full text-left !flex", onClick: () => setIsOpen(false), children: _jsx("div", { className: "flex items-center gap-3 p-2 rounded-xl hover:bg-slate-100/80 transition-colors w-full cursor-pointer group", children: _jsx("span", { className: "text-xs font-medium text-slate-700 group-hover:text-slate-900", children: "Facebook" }) }) }), _jsx(TwitterShareButton, { url: currentUrl, title: shareTitle, className: "w-full text-left !flex", onClick: () => setIsOpen(false), children: _jsx("div", { className: "flex items-center gap-3 p-2 rounded-xl hover:bg-slate-100/80 transition-colors w-full cursor-pointer group", children: _jsx("span", { className: "text-xs font-medium text-slate-700 group-hover:text-slate-900", children: "Twitter / X" }) }) }), _jsx(WhatsappShareButton, { url: currentUrl, title: shareTitle, className: "w-full text-left !flex", onClick: () => setIsOpen(false), children: _jsx("div", { className: "flex items-center gap-3 p-2 rounded-xl hover:bg-slate-100/80 transition-colors w-full cursor-pointer group", children: _jsx("span", { className: "text-xs font-medium text-slate-700 group-hover:text-slate-900", children: "WhatsApp" }) }) }), _jsx("div", { className: "pt-1 border-t border-slate-100", children: _jsxs("button", { type: "button", onClick: handleCopy, className: "flex items-center justify-between w-full p-2 rounded-xl hover:bg-[#012867]/[0.06] hover:text-[#012867] transition-colors cursor-pointer text-left group", children: [_jsx("div", { className: "flex items-center gap-3", children: _jsx("span", { className: "text-xs font-medium text-slate-700 group-hover:text-[#012867]", children: copied ? "Link Copied!" : "Copy Link" }) }), copied ? (_jsx(Check, { className: "h-4 w-4 text-emerald-600" })) : (_jsx(Copy, { className: "h-4 w-4 text-slate-400 group-hover:text-[#012867]" }))] }) })] })] }))] }));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface WaitingProps {
|
|
3
|
+
/** Optional container class name override */
|
|
4
|
+
className?: string;
|
|
5
|
+
/** Safety timeout in ms before auto-dismissing (default 4000ms). Pass 0 to disable. */
|
|
6
|
+
timeout?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function WaitingContent({ className, timeout }: WaitingProps): React.JSX.Element | null;
|
|
9
|
+
export declare function Waiting(props: WaitingProps): React.JSX.Element;
|
|
10
|
+
export default Waiting;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Suspense, useEffect, useState } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { Flash } from "@goldlabelapps/flash";
|
|
6
|
+
import { useSlice } from "@goldlabelapps/uberedux";
|
|
7
|
+
export function WaitingContent({ className, timeout = 4000 }) {
|
|
8
|
+
const [mounted, setMounted] = useState(false);
|
|
9
|
+
const [timedOut, setTimedOut] = useState(false);
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
setMounted(true);
|
|
12
|
+
}, []);
|
|
13
|
+
let isWaiting = false;
|
|
14
|
+
try {
|
|
15
|
+
const slice = useSlice();
|
|
16
|
+
isWaiting = Boolean(slice?.waiting || slice?.loadingOverlay || slice?.navigation?.isNavigating);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
isWaiting = false;
|
|
20
|
+
}
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
if (!isWaiting) {
|
|
23
|
+
setTimedOut(false);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (timeout > 0) {
|
|
27
|
+
const timer = setTimeout(() => {
|
|
28
|
+
setTimedOut(true);
|
|
29
|
+
}, timeout);
|
|
30
|
+
return () => clearTimeout(timer);
|
|
31
|
+
}
|
|
32
|
+
}, [isWaiting, timeout]);
|
|
33
|
+
if (!isWaiting || timedOut)
|
|
34
|
+
return null;
|
|
35
|
+
const content = (_jsx("div", { role: "dialog", "aria-modal": "true", "aria-label": "Loading page", onClick: () => setTimedOut(true), style: { zIndex: 999999 }, className: className ||
|
|
36
|
+
"fixed inset-0 z-[999999] flex items-center justify-center bg-white/80 dark:bg-slate-950/80 backdrop-blur-sm transition-opacity duration-200 pointer-events-auto", children: _jsx(Flash, { movie: "NXLoading", width: 320, height: 320, color: "transparent", loop: true, autoPlay: true }) }));
|
|
37
|
+
if (mounted && typeof document !== "undefined") {
|
|
38
|
+
return createPortal(content, document.body);
|
|
39
|
+
}
|
|
40
|
+
return content;
|
|
41
|
+
}
|
|
42
|
+
export function Waiting(props) {
|
|
43
|
+
return (_jsx(Suspense, { fallback: null, children: _jsx(WaitingContent, { ...props }) }));
|
|
44
|
+
}
|
|
45
|
+
export default Waiting;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { UseSearchOptions } from "./useSearch";
|
|
3
|
+
export interface HeroSearchBarProps {
|
|
4
|
+
guides?: any[];
|
|
5
|
+
searchOptions?: UseSearchOptions;
|
|
6
|
+
founderName?: string;
|
|
7
|
+
founderTitle?: string;
|
|
8
|
+
founderTagline?: string;
|
|
9
|
+
tags?: Array<{
|
|
10
|
+
name: string;
|
|
11
|
+
slug: string;
|
|
12
|
+
}>;
|
|
13
|
+
antigravityGuide?: any;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function HeroSearchBar({ guides, searchOptions, founderName: _founderName, founderTitle, founderTagline: _founderTagline, tags, antigravityGuide, className, }: HeroSearchBarProps): React.JSX.Element;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { Search, X } from "lucide-react";
|
|
5
|
+
import { CleverText } from "@goldlabelapps/flash";
|
|
6
|
+
import { clsx } from "clsx";
|
|
7
|
+
import { twMerge } from "tailwind-merge";
|
|
8
|
+
import { ParticleCanvas } from "../hero/ParticleCanvas";
|
|
9
|
+
import { useSearch } from "./useSearch";
|
|
10
|
+
import { SearchResultsList } from "./SearchResultsList";
|
|
11
|
+
import { FeaturedCard } from "../showcase/FeaturedCard";
|
|
12
|
+
function cn(...inputs) {
|
|
13
|
+
return twMerge(clsx(inputs));
|
|
14
|
+
}
|
|
15
|
+
export function HeroSearchBar({ guides = [], searchOptions, founderName: _founderName = "Wei Zang", founderTitle = "Creator & Author", founderTagline: _founderTagline = "Building next-generation digital products & multi-platform architectures.", tags = [], antigravityGuide, className, }) {
|
|
16
|
+
const mergedSearchOptions = {
|
|
17
|
+
initialGuides: guides,
|
|
18
|
+
...searchOptions,
|
|
19
|
+
};
|
|
20
|
+
const { query, setQuery, searchResults, hasResults } = useSearch(mergedSearchOptions);
|
|
21
|
+
const searchContainerRef = React.useRef(null);
|
|
22
|
+
const handleTagClick = (label) => {
|
|
23
|
+
setQuery(label);
|
|
24
|
+
if (searchContainerRef.current?.scrollIntoView) {
|
|
25
|
+
searchContainerRef.current.scrollIntoView({
|
|
26
|
+
behavior: "smooth",
|
|
27
|
+
block: "start",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
return (_jsxs("section", { className: cn("relative z-30 flex flex-col items-center justify-center pt-0 pb-6 w-full", className), children: [_jsx("div", { className: "pointer-events-none absolute inset-0 overflow-hidden z-0", children: _jsx(ParticleCanvas, {}) }), _jsxs("div", { className: "relative z-20 w-full max-w-4xl mx-auto px-4 flex flex-col items-center text-center", children: [_jsx("h1", { className: "text-3xl sm:text-5xl md:text-6xl font-extrabold tracking-tight text-slate-900 dark:text-white max-w-3xl leading-tight mb-1", children: _jsx("span", { className: "text-transparent bg-clip-text bg-gradient-to-r from-[#85580C] via-[#9E6E17] to-[#784E07] dark:from-[#F1D57A] dark:via-[#C09F52] dark:to-[#E6CA65] font-bold", children: _jsx(CleverText, { text: founderTitle, speed: 40, style: { fontFamily: "inherit", display: "inline" } }) }) }), antigravityGuide && (_jsx(FeaturedCard, { categoryTag: "Career / AI / Antigravity Overview", badge: "Featured", title: antigravityGuide.title || "Building Next.js Apps with Agentic AI & Google Antigravity", description: antigravityGuide.description || "Software development has transformed from manually typing boilerplate code into steering high-autonomy agentic systems. Discover our workflow for shipping Next.js apps with automated monorepos and static site pipelines.", href: antigravityGuide.slug || "/career/ai/antigravity", readMoreLabel: "Read more" })), _jsxs("div", { ref: searchContainerRef, className: "w-full max-w-[340px] mt-1 mb-2 sm:mb-3 relative z-20 scroll-mt-24 sm:scroll-mt-28", children: [_jsxs("div", { className: "relative flex items-center shadow-xl rounded-2xl overflow-hidden bg-white/90 dark:bg-slate-900/90 backdrop-blur-md border-2 border-indigo-500/30 focus-within:border-indigo-600 focus-within:ring-4 focus-within:ring-indigo-500/20 transition-all", children: [_jsx("div", { className: "pl-4 text-indigo-500 dark:text-indigo-400", children: _jsx(Search, { className: "w-5 h-5" }) }), _jsx("input", { type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: "Search experience...", className: "w-full py-3 px-3 text-sm sm:text-base bg-transparent text-slate-900 dark:text-white placeholder-slate-400 dark:placeholder-slate-500 focus:outline-none" }), query && (_jsx("button", { onClick: () => setQuery(""), className: "pr-4 text-slate-400 hover:text-slate-600 dark:hover:text-white transition-colors", "aria-label": "Clear search query", children: _jsx(X, { className: "w-4 h-4" }) }))] }), !query && tags.length > 0 && (_jsxs("div", { className: "mt-3 flex flex-wrap justify-center gap-2", children: [_jsx("span", { className: "text-xs text-slate-400 dark:text-slate-500 self-center mr-1 font-medium", children: "Try:" }), tags
|
|
32
|
+
.filter((t) => t.name !== "FastAPI" && t.name !== "Supabase" && t.name !== "Firebase")
|
|
33
|
+
.slice(0, 6)
|
|
34
|
+
.map((tag) => {
|
|
35
|
+
const label = tag.slug === "flash" ? "Flash" : tag.name;
|
|
36
|
+
return (_jsx("button", { onClick: () => handleTagClick(label), className: "px-3 py-1 text-xs font-medium rounded-full bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-700 hover:border-slate-300 dark:hover:border-slate-600 transition-all shadow-sm cursor-pointer", children: label }, tag.slug));
|
|
37
|
+
})] })), query.trim() && (_jsx("div", { className: "absolute top-full left-0 right-0 mt-4 p-4 bg-white dark:bg-slate-900 rounded-2xl shadow-2xl border border-slate-200 dark:border-slate-800 text-left max-h-[70vh] overflow-y-auto z-50", children: _jsx(SearchResultsList, { query: query, searchResults: searchResults, hasResults: hasResults, onSelectResult: () => setQuery(""), allTags: tags }) }))] })] })] }));
|
|
38
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { UseSearchOptions } from "./useSearch";
|
|
3
|
+
export interface SearchDialogProps {
|
|
4
|
+
isOpen?: boolean;
|
|
5
|
+
onClose?: () => void;
|
|
6
|
+
searchOptions?: UseSearchOptions;
|
|
7
|
+
title?: string;
|
|
8
|
+
placeholder?: string;
|
|
9
|
+
renderTrigger?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function SearchDialog({ isOpen: externalIsOpen, onClose: externalOnClose, searchOptions, title, placeholder, renderTrigger, }: SearchDialogProps): React.JSX.Element;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect, useRef, useSyncExternalStore } from "react";
|
|
4
|
+
import { Search, X } from "lucide-react";
|
|
5
|
+
import { Dialog, DialogContent, IconButton } from "@mui/material";
|
|
6
|
+
import { useTheme } from "../../context/ThemeContext";
|
|
7
|
+
import { useSearch } from "./useSearch";
|
|
8
|
+
import { SearchResultsList } from "./SearchResultsList";
|
|
9
|
+
import { cn } from "../../lib/utils";
|
|
10
|
+
const emptySubscribe = () => () => { };
|
|
11
|
+
const getClientSnapshot = () => true;
|
|
12
|
+
const getServerSnapshot = () => false;
|
|
13
|
+
export function SearchDialog({ isOpen: externalIsOpen, onClose: externalOnClose, searchOptions, title = "Search Experience & Documentation", placeholder = "Search site (e.g. Next.js, Flash, Agentic AI, React)...", renderTrigger, }) {
|
|
14
|
+
const [internalIsOpen, setInternalIsOpen] = useState(false);
|
|
15
|
+
const isControlled = externalIsOpen !== undefined;
|
|
16
|
+
const isOpen = isControlled ? externalIsOpen : internalIsOpen;
|
|
17
|
+
const mounted = useSyncExternalStore(emptySubscribe, getClientSnapshot, getServerSnapshot);
|
|
18
|
+
const { theme } = useTheme();
|
|
19
|
+
const isDark = mounted && theme === "dark";
|
|
20
|
+
const inputRef = useRef(null);
|
|
21
|
+
const { query, setQuery, searchResults, hasResults } = useSearch(searchOptions);
|
|
22
|
+
// Keyboard shortcut (Cmd+K / Ctrl+K) to open search dialog
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const handleKeyDown = (e) => {
|
|
25
|
+
if ((e.metaKey || e.ctrlKey) && e.key === "k") {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
if (isControlled) {
|
|
28
|
+
if (isOpen && externalOnClose) {
|
|
29
|
+
externalOnClose();
|
|
30
|
+
}
|
|
31
|
+
else if (!isOpen && externalOnClose) {
|
|
32
|
+
// allow opening via keydown if parent supports it
|
|
33
|
+
externalOnClose();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
setInternalIsOpen((prev) => !prev);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
42
|
+
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
43
|
+
}, [isControlled, isOpen, externalOnClose]);
|
|
44
|
+
const handleClose = () => {
|
|
45
|
+
if (isControlled) {
|
|
46
|
+
externalOnClose?.();
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
setInternalIsOpen(false);
|
|
50
|
+
}
|
|
51
|
+
setQuery("");
|
|
52
|
+
};
|
|
53
|
+
const handleSelectResult = () => {
|
|
54
|
+
handleClose();
|
|
55
|
+
};
|
|
56
|
+
const shouldRenderTrigger = renderTrigger !== undefined ? renderTrigger : !isControlled;
|
|
57
|
+
return (_jsxs(_Fragment, { children: [shouldRenderTrigger && (_jsx("button", { type: "button", onClick: () => (isControlled ? externalOnClose?.() : setInternalIsOpen(true)), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": "Search", title: "Search (Cmd+K)", className: cn("inline-flex items-center justify-center h-9 w-9 rounded-full text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer", isOpen && "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white"), children: _jsx(Search, { className: "h-4 w-4", "aria-hidden": "true" }) })), _jsxs(Dialog, { open: isOpen, onClose: handleClose, "aria-labelledby": "search-dialog-title", maxWidth: "md", fullWidth: true, PaperProps: {
|
|
58
|
+
sx: {
|
|
59
|
+
borderRadius: "20px",
|
|
60
|
+
p: 1.5,
|
|
61
|
+
maxWidth: "680px",
|
|
62
|
+
maxHeight: "85vh",
|
|
63
|
+
backgroundColor: isDark ? "#0f172a" : "#ffffff",
|
|
64
|
+
color: isDark ? "#f8fafc" : "#0f172a",
|
|
65
|
+
backgroundImage: "none",
|
|
66
|
+
border: isDark ? "1px solid #334155" : "1px solid #e2e8f0",
|
|
67
|
+
},
|
|
68
|
+
}, children: [_jsxs("div", { className: "flex items-center justify-between px-2 pt-1 pb-3 border-b border-slate-200 dark:border-slate-800", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-bold text-slate-700 dark:text-slate-200", id: "search-dialog-title", children: [_jsx(Search, { className: "w-4 h-4 text-indigo-500" }), _jsx("span", { children: title })] }), _jsx(IconButton, { onClick: handleClose, size: "small", "aria-label": "Close search", sx: { color: isDark ? "#94a3b8" : "#64748b" }, children: _jsx(X, { className: "w-4 h-4" }) })] }), _jsxs(DialogContent, { sx: { p: 2 }, children: [_jsxs("div", { className: "relative flex items-center shadow-sm rounded-xl overflow-hidden bg-slate-50 dark:bg-slate-800/80 border border-slate-200 dark:border-slate-700 focus-within:border-indigo-500 transition-all", children: [_jsx("div", { className: "pl-3.5 text-slate-400", children: _jsx(Search, { className: "w-5 h-5" }) }), _jsx("input", { ref: inputRef, type: "text", value: query, onChange: (e) => setQuery(e.target.value), placeholder: placeholder, autoFocus: true, className: "w-full py-3 px-3 text-base bg-transparent text-slate-900 dark:text-white placeholder-slate-400 focus:outline-none" }), query && (_jsx("button", { onClick: () => setQuery(""), className: "pr-3.5 text-slate-400 hover:text-slate-600 dark:hover:text-white", "aria-label": "Clear search query", children: _jsx(X, { className: "w-4 h-4" }) }))] }), query.trim() && (_jsx("div", { className: "mt-4 max-h-[55vh] overflow-y-auto pr-1", children: _jsx(SearchResultsList, { query: query, searchResults: searchResults, hasResults: hasResults, onSelectResult: handleSelectResult }) }))] })] })] }));
|
|
69
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SearchDialogProps } from "./SearchDialog";
|
|
3
|
+
export interface SearchIconButtonProps extends Omit<SearchDialogProps, "isOpen" | "onClose"> {
|
|
4
|
+
className?: string;
|
|
5
|
+
iconClassName?: string;
|
|
6
|
+
ariaLabel?: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
buttonClassName?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function SearchIconButton({ className, iconClassName, ariaLabel, title, buttonClassName, ...dialogProps }: SearchIconButtonProps): React.JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Search } from "lucide-react";
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
import { SearchDialog } from "./SearchDialog";
|
|
7
|
+
export function SearchIconButton({ className, iconClassName = "h-4 w-4", ariaLabel = "Search", title = "Search (Cmd+K)", buttonClassName, ...dialogProps }) {
|
|
8
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
9
|
+
return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", onClick: () => setIsOpen(true), "aria-expanded": isOpen, "aria-haspopup": "dialog", "aria-label": ariaLabel, title: title, className: cn("inline-flex items-center justify-center h-9 w-9 rounded-full text-slate-600 dark:text-slate-300 hover:text-slate-900 dark:hover:text-white hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors cursor-pointer", isOpen && "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white", className, buttonClassName), children: _jsx(Search, { className: iconClassName, "aria-hidden": "true" }) }), _jsx(SearchDialog, { ...dialogProps, isOpen: isOpen, onClose: () => setIsOpen(false), renderTrigger: false })] }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { SearchResultsData } from "./useSearch";
|
|
3
|
+
export interface SearchResultsListProps {
|
|
4
|
+
query: string;
|
|
5
|
+
searchResults: SearchResultsData;
|
|
6
|
+
hasResults: boolean;
|
|
7
|
+
onSelectResult?: () => void;
|
|
8
|
+
allTags?: Array<{
|
|
9
|
+
name: string;
|
|
10
|
+
slug: string;
|
|
11
|
+
}>;
|
|
12
|
+
}
|
|
13
|
+
export declare function SearchResultsList({ query, searchResults, hasResults, onSelectResult, allTags, }: SearchResultsListProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { AppRouterLink as Link } from "@goldlabelapps/uberedux";
|
|
4
|
+
import { ArrowRight, Tag, Briefcase, BookOpen, Video, Layers } from "lucide-react";
|
|
5
|
+
import { HighlightText, getContentSnippet } from "./useSearch";
|
|
6
|
+
export function SearchResultsList({ query, searchResults, hasResults, onSelectResult, allTags = [], }) {
|
|
7
|
+
if (!query.trim())
|
|
8
|
+
return null;
|
|
9
|
+
if (!hasResults) {
|
|
10
|
+
return (_jsxs("div", { className: "p-6 text-center text-slate-500 dark:text-slate-400", children: ["No exact matches for \"", query, "\". Try searching for ", _jsx("i", { children: "Next.js" }), ", ", _jsx("i", { children: "React" }), ", ", _jsx("i", { children: "Flash" }), ", or ", _jsx("i", { children: "Agentic AI" }), "."] }));
|
|
11
|
+
}
|
|
12
|
+
return (_jsxs("div", { className: "space-y-6", children: [searchResults.guides.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(BookOpen, { className: "w-3.5 h-3.5" }), "(", searchResults.guides.length, ") Results"] }), _jsx("div", { className: "space-y-2", children: searchResults.guides.map((guide) => (_jsxs(Link, { href: guide.cleanSlug === "index" ? "/" : `/${guide.cleanSlug}`, onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsxs("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-2", children: [_jsx("span", { children: _jsx(HighlightText, { text: guide.title, query: query }) }), guide.category && (_jsx("span", { className: "text-[10px] px-2 py-0.5 rounded-full bg-indigo-100 dark:bg-indigo-900/60 text-indigo-700 dark:text-indigo-300 font-medium", children: guide.category }))] }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-1 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(guide.description || "", query), query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-3" })] }, guide.cleanSlug))) })] })), searchResults.features.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Layers, { className: "w-3.5 h-3.5" }), "Features (", searchResults.features.length, ")"] }), _jsx("div", { className: "space-y-2", children: searchResults.features.map((feature, idx) => (_jsxs(Link, { href: "/features", onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsxs("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-2", children: [_jsx("span", { children: _jsx(HighlightText, { text: feature.title, query: query }) }), feature.tag && (_jsx("span", { className: "text-[10px] px-2 py-0.5 rounded-full bg-purple-100 dark:bg-purple-900/60 text-purple-700 dark:text-purple-300 font-medium", children: feature.tag }))] }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-1 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(feature.description || "", query), query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-3" })] }, feature.title || idx))) })] })), searchResults.videos.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Video, { className: "w-3.5 h-3.5" }), "Videos (", searchResults.videos.length, ")"] }), _jsx("div", { className: "space-y-2", children: searchResults.videos.map((video, idx) => (_jsxs(Link, { href: "/demos", onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsx("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-2", children: _jsx("span", { children: _jsx(HighlightText, { text: video.title, query: query }) }) }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-1 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(video.tagline || video.description || "", query), query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-3" })] }, video.title || idx))) })] })), searchResults.tags.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Tag, { className: "w-3.5 h-3.5" }), "Skills & Topics (", searchResults.tags.length, ")"] }), _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2", children: searchResults.tags.map((tag) => (_jsxs(Link, { href: `/tag/${tag.slug}`, onClick: onSelectResult, className: "p-3 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all flex items-center justify-between group cursor-pointer", children: [_jsxs("div", { children: [_jsx("div", { className: "font-semibold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400", children: _jsx(HighlightText, { text: tag.name, query: query }) }), _jsx("p", { className: "text-xs text-slate-500 dark:text-slate-400 mt-0.5 line-clamp-1", children: _jsx(HighlightText, { text: tag.description, query: query }) })] }), _jsx(ArrowRight, { className: "w-4 h-4 text-slate-400 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 group-hover:translate-x-0.5 transition-all flex-shrink-0 ml-2" })] }, tag.slug))) })] })), searchResults.roles.length > 0 && (_jsxs("div", { children: [_jsxs("div", { className: "text-xs font-bold uppercase tracking-wider text-indigo-600 dark:text-indigo-400 mb-2 flex items-center gap-1.5", children: [_jsx(Briefcase, { className: "w-3.5 h-3.5" }), "Experience (", searchResults.roles.length, ")"] }), _jsx("div", { className: "space-y-2", children: searchResults.roles.map((role) => {
|
|
13
|
+
const availableTags = allTags.length > 0 ? allTags : searchResults.tags;
|
|
14
|
+
const matchingTag = availableTags.find((t) => role.skills?.some((s) => s.toLowerCase() === t.name.toLowerCase()));
|
|
15
|
+
const targetUrl = matchingTag ? `/tag/${matchingTag.slug}` : `/tag/${role.eraId || "experience"}`;
|
|
16
|
+
return (_jsxs(Link, { href: targetUrl, onClick: onSelectResult, className: "block p-3.5 rounded-xl bg-slate-50 dark:bg-slate-800/60 hover:bg-indigo-50 dark:hover:bg-indigo-950/40 border border-slate-100 dark:border-slate-800 transition-all group cursor-pointer", children: [_jsxs("div", { className: "flex justify-between items-start", children: [_jsxs("span", { className: "font-bold text-slate-900 dark:text-white text-sm group-hover:text-indigo-600 dark:group-hover:text-indigo-400 flex items-center gap-1.5", children: [_jsx("span", { children: _jsx(HighlightText, { text: role.title, query: query }) }), _jsx("span", { className: "text-slate-400 font-normal", children: "@" }), _jsx("span", { className: "text-slate-700 dark:text-slate-300", children: _jsx(HighlightText, { text: role.company, query: query }) })] }), role.period && (_jsx("span", { className: "text-[11px] text-slate-400 font-mono", children: role.period }))] }), role.summary && (_jsx("p", { className: "text-xs text-slate-600 dark:text-slate-300 mt-1.5 line-clamp-2", children: _jsx(HighlightText, { text: getContentSnippet(role.summary, query, 140), query: query }) }))] }, role.id));
|
|
17
|
+
}) })] }))] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
export interface SearchResultsData {
|
|
3
|
+
guides: any[];
|
|
4
|
+
videos: any[];
|
|
5
|
+
features: any[];
|
|
6
|
+
tags: any[];
|
|
7
|
+
roles: any[];
|
|
8
|
+
}
|
|
9
|
+
export interface UseSearchOptions {
|
|
10
|
+
initialGuides?: any[];
|
|
11
|
+
profileData?: {
|
|
12
|
+
tags?: Array<{
|
|
13
|
+
name: string;
|
|
14
|
+
slug: string;
|
|
15
|
+
description: string;
|
|
16
|
+
}>;
|
|
17
|
+
roles?: Array<{
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
company: string;
|
|
21
|
+
period?: string;
|
|
22
|
+
eraId?: string;
|
|
23
|
+
summary?: string;
|
|
24
|
+
skills: string[];
|
|
25
|
+
}>;
|
|
26
|
+
};
|
|
27
|
+
videosConfig?: {
|
|
28
|
+
items?: Array<{
|
|
29
|
+
title: string;
|
|
30
|
+
tagline: string;
|
|
31
|
+
description: string;
|
|
32
|
+
keyFeatures?: string[];
|
|
33
|
+
}>;
|
|
34
|
+
};
|
|
35
|
+
featuresConfig?: {
|
|
36
|
+
items?: Array<{
|
|
37
|
+
title: string;
|
|
38
|
+
tabLabel?: string;
|
|
39
|
+
tag?: string;
|
|
40
|
+
description: string;
|
|
41
|
+
}>;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export declare function useSearch(initialGuidesOrOptions?: any[] | UseSearchOptions, optionsParam?: UseSearchOptions): {
|
|
45
|
+
query: string;
|
|
46
|
+
setQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
47
|
+
searchResults: SearchResultsData;
|
|
48
|
+
hasResults: boolean;
|
|
49
|
+
};
|
|
50
|
+
export declare function HighlightText({ text, query }: {
|
|
51
|
+
text: string;
|
|
52
|
+
query: string;
|
|
53
|
+
}): React.JSX.Element;
|
|
54
|
+
export declare function getContentSnippet(content: string, query: string, maxLength?: number): string;
|