@sudobility/building_blocks 0.0.47 → 0.0.49

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.
@@ -19,34 +19,7 @@ export interface AppBreadcrumbsProps extends VariantProps<typeof breadcrumbConta
19
19
  contentClassName?: string;
20
20
  }
21
21
  /**
22
- * AppBreadcrumbs - Breadcrumb navigation with social share and "Talk to Founder" button.
23
- *
24
- * Features:
25
- * - Breadcrumb trail with links
26
- * - Social share buttons on the right
27
- * - Optional "Talk to Founder" meeting button
28
- * - Always renders at max-w-7xl width
29
- * - Dark mode support
30
- *
31
- * @example
32
- * ```tsx
33
- * <AppBreadcrumbs
34
- * items={[
35
- * { label: 'Home', href: '/' },
36
- * { label: 'Products', href: '/products' },
37
- * { label: 'Widget', current: true },
38
- * ]}
39
- * shareConfig={{
40
- * title: 'Check out this widget',
41
- * description: 'Amazing widget for your needs',
42
- * hashtags: ['widget', 'product'],
43
- * }}
44
- * talkToFounder={{
45
- * meetingUrl: 'https://calendly.com/founder/30min',
46
- * buttonText: 'Book a call',
47
- * }}
48
- * />
49
- * ```
22
+ * AppBreadcrumbs - Self-contained breadcrumb navigation with share and "Talk to Founder" button.
50
23
  */
51
24
  export declare const AppBreadcrumbs: React.FC<AppBreadcrumbsProps>;
52
25
  export default AppBreadcrumbs;
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import React, { useState, useRef, useMemo, useCallback, useEffect } from "react";
3
- import { TopbarProvider, Topbar, TopbarLeft, TopbarNavigation, TopbarLogo, Logo, TopbarCenter, TopbarRight, TopbarActions, TopbarMobileContent, BreadcrumbSection, Footer, FooterCompact, FooterCompactLeft, FooterVersion, FooterCopyright, FooterCompactRight, FooterGrid, FooterLinkSection, FooterLink, FooterBottom, FooterBrand, FooterSocialLinks, LayoutProvider, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MasterListItem, Section, MasterDetailLayout } from "@sudobility/components";
3
+ import { TopbarProvider, Topbar, TopbarLeft, TopbarNavigation, TopbarLogo, Logo, TopbarCenter, TopbarRight, TopbarActions, TopbarMobileContent, Footer, FooterCompact, FooterCompactLeft, FooterVersion, FooterCopyright, FooterCompactRight, FooterGrid, FooterLinkSection, FooterLink, FooterBottom, FooterBrand, FooterSocialLinks, LayoutProvider, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MasterListItem, Section, MasterDetailLayout } from "@sudobility/components";
4
4
  import { clsx } from "clsx";
5
5
  import { twMerge } from "tailwind-merge";
6
6
  import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, LanguageIcon, ChevronRightIcon, EnvelopeIcon, DocumentTextIcon, CogIcon, HomeIcon } from "@heroicons/react/24/outline";
@@ -251,7 +251,8 @@ const AppTopBar = ({
251
251
  id: item.id,
252
252
  label: item.label,
253
253
  icon: item.icon,
254
- href: item.href
254
+ href: item.href,
255
+ className: item.className
255
256
  })),
256
257
  [visibleMenuItems]
257
258
  );
@@ -437,7 +438,171 @@ const breadcrumbContainerVariants = cva("border-b", {
437
438
  variant: "default"
438
439
  }
439
440
  });
440
- const TalkToFounderButton = ({ config }) => {
441
+ const createShareUrl = {
442
+ twitter: (url, text, hashtags) => {
443
+ const hashtagStr = hashtags.length > 0 ? `&hashtags=${hashtags.join(",")}` : "";
444
+ return `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}${hashtagStr}`;
445
+ },
446
+ facebook: (url) => {
447
+ return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`;
448
+ },
449
+ linkedin: (url) => {
450
+ return `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`;
451
+ },
452
+ reddit: (url, title) => {
453
+ return `https://reddit.com/submit?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`;
454
+ },
455
+ telegram: (url, text) => {
456
+ return `https://t.me/share/url?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}`;
457
+ },
458
+ email: (url, title, description) => {
459
+ return `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(description + "\n\n" + url)}`;
460
+ }
461
+ };
462
+ const ShareDropdown = ({
463
+ shareConfig
464
+ }) => {
465
+ const [isOpen, setIsOpen] = useState(false);
466
+ const [shareUrl, setShareUrl] = useState("");
467
+ const [isPreparingShare, setIsPreparingShare] = useState(false);
468
+ const [showCopiedFeedback, setShowCopiedFeedback] = useState(false);
469
+ React.useEffect(() => {
470
+ const onBeforeShare = shareConfig.onBeforeShare;
471
+ if (onBeforeShare && !shareUrl) {
472
+ const prepareUrl = async () => {
473
+ setIsPreparingShare(true);
474
+ try {
475
+ const baseUrl = typeof window !== "undefined" ? window.location.href : "";
476
+ const modifiedUrl = await onBeforeShare(baseUrl);
477
+ setShareUrl(modifiedUrl);
478
+ } catch {
479
+ const baseUrl = typeof window !== "undefined" ? window.location.href : "";
480
+ setShareUrl(baseUrl);
481
+ } finally {
482
+ setIsPreparingShare(false);
483
+ }
484
+ };
485
+ prepareUrl();
486
+ }
487
+ }, [shareConfig, shareUrl]);
488
+ const url = shareUrl || (typeof window !== "undefined" ? window.location.href : "");
489
+ const copyToClipboard = async () => {
490
+ try {
491
+ await navigator.clipboard.writeText(url);
492
+ setShowCopiedFeedback(true);
493
+ setTimeout(() => {
494
+ setShowCopiedFeedback(false);
495
+ setIsOpen(false);
496
+ }, 1500);
497
+ } catch {
498
+ }
499
+ };
500
+ const handleSocialShare = (platformUrl) => {
501
+ window.open(
502
+ platformUrl,
503
+ "_blank",
504
+ "noopener,noreferrer,width=600,height=400"
505
+ );
506
+ setIsOpen(false);
507
+ };
508
+ const sharePlatforms = [
509
+ {
510
+ name: "Twitter",
511
+ url: createShareUrl.twitter(url, shareConfig.title, shareConfig.hashtags),
512
+ color: "text-blue-400"
513
+ },
514
+ {
515
+ name: "Facebook",
516
+ url: createShareUrl.facebook(url),
517
+ color: "text-blue-600"
518
+ },
519
+ {
520
+ name: "LinkedIn",
521
+ url: createShareUrl.linkedin(url),
522
+ color: "text-blue-700"
523
+ },
524
+ {
525
+ name: "Reddit",
526
+ url: createShareUrl.reddit(url, shareConfig.title),
527
+ color: "text-orange-600"
528
+ },
529
+ {
530
+ name: "Telegram",
531
+ url: createShareUrl.telegram(url, shareConfig.title),
532
+ color: "text-blue-500"
533
+ },
534
+ {
535
+ name: "Email",
536
+ url: createShareUrl.email(
537
+ url,
538
+ shareConfig.title,
539
+ shareConfig.description
540
+ ),
541
+ color: "text-gray-600"
542
+ }
543
+ ];
544
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
545
+ /* @__PURE__ */ jsx(
546
+ "button",
547
+ {
548
+ onClick: () => setIsOpen(!isOpen),
549
+ disabled: isPreparingShare,
550
+ className: "flex items-center justify-center w-8 h-8 bg-blue-50 hover:bg-blue-100 dark:bg-blue-900/30 dark:hover:bg-blue-900/50 text-blue-600 dark:text-blue-400 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
551
+ title: "Share this page",
552
+ children: isPreparingShare ? /* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ jsx(
553
+ "svg",
554
+ {
555
+ className: "w-4 h-4",
556
+ fill: "none",
557
+ stroke: "currentColor",
558
+ viewBox: "0 0 24 24",
559
+ children: /* @__PURE__ */ jsx(
560
+ "path",
561
+ {
562
+ strokeLinecap: "round",
563
+ strokeLinejoin: "round",
564
+ strokeWidth: 2,
565
+ d: "M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.367 2.684 3 3 0 00-5.367-2.684z"
566
+ }
567
+ )
568
+ }
569
+ )
570
+ }
571
+ ),
572
+ isOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
573
+ /* @__PURE__ */ jsx(
574
+ "div",
575
+ {
576
+ className: "fixed inset-0 z-[999998]",
577
+ onClick: () => setIsOpen(false)
578
+ }
579
+ ),
580
+ /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-10 z-[999999] w-40 bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 py-1", children: [
581
+ sharePlatforms.map((platform) => /* @__PURE__ */ jsx(
582
+ "button",
583
+ {
584
+ onClick: () => handleSocialShare(platform.url),
585
+ className: "w-full flex items-center px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors",
586
+ children: /* @__PURE__ */ jsx("span", { className: `text-sm ${platform.color}`, children: platform.name })
587
+ },
588
+ platform.name
589
+ )),
590
+ /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 my-1" }),
591
+ /* @__PURE__ */ jsx(
592
+ "button",
593
+ {
594
+ onClick: copyToClipboard,
595
+ className: "w-full flex items-center px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors",
596
+ children: /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: showCopiedFeedback ? "Copied!" : "Copy Link" })
597
+ }
598
+ )
599
+ ] })
600
+ ] })
601
+ ] });
602
+ };
603
+ const TalkToFounderButton = ({
604
+ config
605
+ }) => {
441
606
  const IconComponent = config.icon || CalendarDaysIcon;
442
607
  const buttonText = config.buttonText || "Talk to Founder";
443
608
  return /* @__PURE__ */ jsxs(
@@ -447,14 +612,13 @@ const TalkToFounderButton = ({ config }) => {
447
612
  target: "_blank",
448
613
  rel: "noopener noreferrer",
449
614
  className: cn(
450
- "inline-flex items-center gap-2 px-3 py-1.5",
615
+ "inline-flex items-center gap-1.5 px-2.5 h-8",
451
616
  "text-sm font-medium",
452
617
  "text-blue-600 dark:text-blue-400",
453
618
  "hover:text-blue-700 dark:hover:text-blue-300",
454
- "bg-blue-50 dark:bg-blue-900/20",
455
- "hover:bg-blue-100 dark:hover:bg-blue-900/30",
456
- "rounded-full",
457
- "border border-blue-200 dark:border-blue-800",
619
+ "bg-blue-50 dark:bg-blue-900/30",
620
+ "hover:bg-blue-100 dark:hover:bg-blue-900/50",
621
+ "rounded-lg",
458
622
  "transition-colors"
459
623
  ),
460
624
  children: [
@@ -475,9 +639,22 @@ const AppBreadcrumbs = ({
475
639
  if (!items || items.length === 0) {
476
640
  return null;
477
641
  }
478
- return /* @__PURE__ */ jsx("div", { className: cn(breadcrumbContainerVariants({ variant }), className), children: /* @__PURE__ */ jsx("div", { className: cn("max-w-7xl mx-auto px-4 py-3", contentClassName), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
479
- /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx(BreadcrumbSection, { items, shareConfig }) }),
480
- talkToFounder && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 flex-shrink-0", children: /* @__PURE__ */ jsx(TalkToFounderButton, { config: talkToFounder }) })
642
+ return /* @__PURE__ */ jsx("div", { className: cn(breadcrumbContainerVariants({ variant }), className), children: /* @__PURE__ */ jsx("div", { className: cn("max-w-7xl mx-auto px-4 py-2", contentClassName), children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
643
+ /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", children: /* @__PURE__ */ jsx("ol", { className: "flex items-center text-sm space-x-2", children: items.map((item, index) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
644
+ /* @__PURE__ */ jsx("li", { children: item.current || !item.href ? /* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: item.label }) : /* @__PURE__ */ jsx(
645
+ "a",
646
+ {
647
+ href: item.href,
648
+ className: "text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 transition-colors",
649
+ children: item.label
650
+ }
651
+ ) }),
652
+ index < items.length - 1 && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("span", { className: "text-gray-400 dark:text-gray-500", children: "/" }) })
653
+ ] }, index)) }) }),
654
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
655
+ talkToFounder && /* @__PURE__ */ jsx(TalkToFounderButton, { config: talkToFounder }),
656
+ shareConfig && /* @__PURE__ */ jsx(ShareDropdown, { shareConfig })
657
+ ] })
481
658
  ] }) }) });
482
659
  };
483
660
  const DefaultLinkComponent$1 = ({