@sudobility/building_blocks 0.0.46 → 0.0.48

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";
@@ -437,7 +437,171 @@ const breadcrumbContainerVariants = cva("border-b", {
437
437
  variant: "default"
438
438
  }
439
439
  });
440
- const TalkToFounderButton = ({ config }) => {
440
+ const createShareUrl = {
441
+ twitter: (url, text, hashtags) => {
442
+ const hashtagStr = hashtags.length > 0 ? `&hashtags=${hashtags.join(",")}` : "";
443
+ return `https://twitter.com/intent/tweet?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}${hashtagStr}`;
444
+ },
445
+ facebook: (url) => {
446
+ return `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(url)}`;
447
+ },
448
+ linkedin: (url) => {
449
+ return `https://www.linkedin.com/sharing/share-offsite/?url=${encodeURIComponent(url)}`;
450
+ },
451
+ reddit: (url, title) => {
452
+ return `https://reddit.com/submit?url=${encodeURIComponent(url)}&title=${encodeURIComponent(title)}`;
453
+ },
454
+ telegram: (url, text) => {
455
+ return `https://t.me/share/url?url=${encodeURIComponent(url)}&text=${encodeURIComponent(text)}`;
456
+ },
457
+ email: (url, title, description) => {
458
+ return `mailto:?subject=${encodeURIComponent(title)}&body=${encodeURIComponent(description + "\n\n" + url)}`;
459
+ }
460
+ };
461
+ const ShareDropdown = ({
462
+ shareConfig
463
+ }) => {
464
+ const [isOpen, setIsOpen] = useState(false);
465
+ const [shareUrl, setShareUrl] = useState("");
466
+ const [isPreparingShare, setIsPreparingShare] = useState(false);
467
+ const [showCopiedFeedback, setShowCopiedFeedback] = useState(false);
468
+ React.useEffect(() => {
469
+ const onBeforeShare = shareConfig.onBeforeShare;
470
+ if (onBeforeShare && !shareUrl) {
471
+ const prepareUrl = async () => {
472
+ setIsPreparingShare(true);
473
+ try {
474
+ const baseUrl = typeof window !== "undefined" ? window.location.href : "";
475
+ const modifiedUrl = await onBeforeShare(baseUrl);
476
+ setShareUrl(modifiedUrl);
477
+ } catch {
478
+ const baseUrl = typeof window !== "undefined" ? window.location.href : "";
479
+ setShareUrl(baseUrl);
480
+ } finally {
481
+ setIsPreparingShare(false);
482
+ }
483
+ };
484
+ prepareUrl();
485
+ }
486
+ }, [shareConfig, shareUrl]);
487
+ const url = shareUrl || (typeof window !== "undefined" ? window.location.href : "");
488
+ const copyToClipboard = async () => {
489
+ try {
490
+ await navigator.clipboard.writeText(url);
491
+ setShowCopiedFeedback(true);
492
+ setTimeout(() => {
493
+ setShowCopiedFeedback(false);
494
+ setIsOpen(false);
495
+ }, 1500);
496
+ } catch {
497
+ }
498
+ };
499
+ const handleSocialShare = (platformUrl) => {
500
+ window.open(
501
+ platformUrl,
502
+ "_blank",
503
+ "noopener,noreferrer,width=600,height=400"
504
+ );
505
+ setIsOpen(false);
506
+ };
507
+ const sharePlatforms = [
508
+ {
509
+ name: "Twitter",
510
+ url: createShareUrl.twitter(url, shareConfig.title, shareConfig.hashtags),
511
+ color: "text-blue-400"
512
+ },
513
+ {
514
+ name: "Facebook",
515
+ url: createShareUrl.facebook(url),
516
+ color: "text-blue-600"
517
+ },
518
+ {
519
+ name: "LinkedIn",
520
+ url: createShareUrl.linkedin(url),
521
+ color: "text-blue-700"
522
+ },
523
+ {
524
+ name: "Reddit",
525
+ url: createShareUrl.reddit(url, shareConfig.title),
526
+ color: "text-orange-600"
527
+ },
528
+ {
529
+ name: "Telegram",
530
+ url: createShareUrl.telegram(url, shareConfig.title),
531
+ color: "text-blue-500"
532
+ },
533
+ {
534
+ name: "Email",
535
+ url: createShareUrl.email(
536
+ url,
537
+ shareConfig.title,
538
+ shareConfig.description
539
+ ),
540
+ color: "text-gray-600"
541
+ }
542
+ ];
543
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
544
+ /* @__PURE__ */ jsx(
545
+ "button",
546
+ {
547
+ onClick: () => setIsOpen(!isOpen),
548
+ disabled: isPreparingShare,
549
+ 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",
550
+ title: "Share this page",
551
+ children: isPreparingShare ? /* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ jsx(
552
+ "svg",
553
+ {
554
+ className: "w-4 h-4",
555
+ fill: "none",
556
+ stroke: "currentColor",
557
+ viewBox: "0 0 24 24",
558
+ children: /* @__PURE__ */ jsx(
559
+ "path",
560
+ {
561
+ strokeLinecap: "round",
562
+ strokeLinejoin: "round",
563
+ strokeWidth: 2,
564
+ 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"
565
+ }
566
+ )
567
+ }
568
+ )
569
+ }
570
+ ),
571
+ isOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
572
+ /* @__PURE__ */ jsx(
573
+ "div",
574
+ {
575
+ className: "fixed inset-0 z-[999998]",
576
+ onClick: () => setIsOpen(false)
577
+ }
578
+ ),
579
+ /* @__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: [
580
+ sharePlatforms.map((platform) => /* @__PURE__ */ jsx(
581
+ "button",
582
+ {
583
+ onClick: () => handleSocialShare(platform.url),
584
+ className: "w-full flex items-center px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors",
585
+ children: /* @__PURE__ */ jsx("span", { className: `text-sm ${platform.color}`, children: platform.name })
586
+ },
587
+ platform.name
588
+ )),
589
+ /* @__PURE__ */ jsx("div", { className: "border-t border-gray-200 dark:border-gray-700 my-1" }),
590
+ /* @__PURE__ */ jsx(
591
+ "button",
592
+ {
593
+ onClick: copyToClipboard,
594
+ className: "w-full flex items-center px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors",
595
+ children: /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: showCopiedFeedback ? "Copied!" : "Copy Link" })
596
+ }
597
+ )
598
+ ] })
599
+ ] })
600
+ ] });
601
+ };
602
+ const TalkToFounderButton = ({
603
+ config
604
+ }) => {
441
605
  const IconComponent = config.icon || CalendarDaysIcon;
442
606
  const buttonText = config.buttonText || "Talk to Founder";
443
607
  return /* @__PURE__ */ jsxs(
@@ -447,14 +611,13 @@ const TalkToFounderButton = ({ config }) => {
447
611
  target: "_blank",
448
612
  rel: "noopener noreferrer",
449
613
  className: cn(
450
- "inline-flex items-center gap-2 px-3 py-1.5",
614
+ "inline-flex items-center gap-1.5 px-2.5 h-8",
451
615
  "text-sm font-medium",
452
616
  "text-blue-600 dark:text-blue-400",
453
617
  "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",
618
+ "bg-blue-50 dark:bg-blue-900/30",
619
+ "hover:bg-blue-100 dark:hover:bg-blue-900/50",
620
+ "rounded-lg",
458
621
  "transition-colors"
459
622
  ),
460
623
  children: [
@@ -475,9 +638,22 @@ const AppBreadcrumbs = ({
475
638
  if (!items || items.length === 0) {
476
639
  return null;
477
640
  }
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 }) })
641
+ 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: [
642
+ /* @__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: [
643
+ /* @__PURE__ */ jsx("li", { children: item.current || !item.href ? /* @__PURE__ */ jsx("span", { className: "text-gray-700 dark:text-gray-300", children: item.label }) : /* @__PURE__ */ jsx(
644
+ "a",
645
+ {
646
+ href: item.href,
647
+ className: "text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 transition-colors",
648
+ children: item.label
649
+ }
650
+ ) }),
651
+ index < items.length - 1 && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx("span", { className: "text-gray-400 dark:text-gray-500", children: "/" }) })
652
+ ] }, index)) }) }),
653
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
654
+ talkToFounder && /* @__PURE__ */ jsx(TalkToFounderButton, { config: talkToFounder }),
655
+ shareConfig && /* @__PURE__ */ jsx(ShareDropdown, { shareConfig })
656
+ ] })
481
657
  ] }) }) });
482
658
  };
483
659
  const DefaultLinkComponent$1 = ({