@dub/ui 0.0.8 → 0.0.9

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.
Files changed (134) hide show
  1. package/README.md +11 -0
  2. package/dist/accordion.d.mts +9 -0
  3. package/dist/accordion.mjs +1 -1
  4. package/dist/avatar.d.mts +15 -0
  5. package/dist/avatar.mjs +1 -1
  6. package/dist/background.d.mts +5 -0
  7. package/dist/background.mjs +1 -1
  8. package/dist/badge.d.mts +12 -0
  9. package/dist/badge.mjs +1 -1
  10. package/dist/button.d.mts +13 -0
  11. package/dist/button.mjs +1 -1
  12. package/dist/chunk-3Y5WGFFC.mjs +2 -0
  13. package/dist/chunk-4Y22ZONT.mjs +2 -0
  14. package/dist/chunk-B7YPSVHM.mjs +2 -0
  15. package/dist/chunk-BCILXFZH.mjs +2 -0
  16. package/dist/chunk-BZY4AURF.mjs +2 -0
  17. package/dist/chunk-HZBYDQAF.mjs +2 -0
  18. package/dist/chunk-IMRGY4OW.mjs +2 -0
  19. package/dist/chunk-JF4XLNZS.mjs +2 -0
  20. package/dist/chunk-OIIHWZYP.mjs +2 -0
  21. package/dist/chunk-S32PKYSI.mjs +8 -0
  22. package/dist/chunk-XG3NPZVV.mjs +2 -0
  23. package/dist/copy-button.d.mts +8 -0
  24. package/dist/copy-button.mjs +1 -1
  25. package/dist/footer.d.mts +5 -0
  26. package/dist/footer.mjs +1 -1
  27. package/dist/form.d.mts +14 -0
  28. package/dist/form.mjs +1 -1
  29. package/dist/icon-menu.d.mts +10 -0
  30. package/dist/icon-menu.mjs +1 -1
  31. package/dist/icons/copy.d.mts +7 -0
  32. package/dist/icons/copy.mjs +1 -1
  33. package/dist/icons/expanding-arrow.d.mts +7 -0
  34. package/dist/icons/expanding-arrow.mjs +1 -1
  35. package/dist/icons/facebook.d.mts +8 -0
  36. package/dist/icons/facebook.mjs +1 -1
  37. package/dist/icons/github.d.mts +7 -0
  38. package/dist/icons/github.mjs +1 -1
  39. package/dist/icons/google.d.mts +7 -0
  40. package/dist/icons/google.mjs +1 -1
  41. package/dist/icons/index.d.mts +17 -0
  42. package/dist/icons/index.mjs +1 -1
  43. package/dist/icons/linkedin.d.mts +8 -0
  44. package/dist/icons/linkedin.mjs +1 -1
  45. package/dist/icons/loading-circle.d.mts +7 -0
  46. package/dist/icons/loading-circle.mjs +1 -1
  47. package/dist/icons/loading-dots.d.mts +5 -0
  48. package/dist/icons/loading-dots.mjs +1 -1
  49. package/dist/icons/loading-spinner.d.mts +7 -0
  50. package/dist/icons/loading-spinner.mjs +1 -1
  51. package/dist/icons/logo.d.mts +7 -0
  52. package/dist/icons/logo.mjs +1 -1
  53. package/dist/icons/logotype.d.mts +7 -0
  54. package/dist/icons/logotype.mjs +1 -1
  55. package/dist/icons/magic.d.mts +7 -0
  56. package/dist/icons/magic.mjs +1 -1
  57. package/dist/icons/photo.d.mts +7 -0
  58. package/dist/icons/photo.mjs +1 -1
  59. package/dist/icons/tick.d.mts +7 -0
  60. package/dist/icons/tick.mjs +1 -1
  61. package/dist/icons/twitter.d.mts +7 -0
  62. package/dist/icons/twitter.mjs +1 -1
  63. package/dist/icons/unsplash.d.mts +7 -0
  64. package/dist/icons/unsplash.mjs +1 -1
  65. package/dist/index.d.mts +69 -0
  66. package/dist/index.mjs +1 -1
  67. package/dist/link-preview.d.mts +7 -0
  68. package/dist/link-preview.mjs +1 -1
  69. package/dist/max-width-wrapper.d.mts +9 -0
  70. package/dist/max-width-wrapper.mjs +1 -1
  71. package/dist/modal.d.mts +14 -0
  72. package/dist/modal.mjs +1 -1
  73. package/dist/nav-mobile.d.mts +5 -0
  74. package/dist/nav-mobile.mjs +1 -1
  75. package/dist/nav.d.mts +9 -0
  76. package/dist/nav.mjs +1 -1
  77. package/dist/popover.d.mts +13 -0
  78. package/dist/popover.mjs +1 -1
  79. package/dist/switch.d.mts +14 -0
  80. package/dist/switch.mjs +1 -1
  81. package/dist/tab-select.d.mts +9 -0
  82. package/dist/tab-select.mjs +1 -1
  83. package/dist/tooltip.d.mts +32 -0
  84. package/dist/tooltip.mjs +1 -1
  85. package/package.json +13 -14
  86. package/.turbo/turbo-build.log +0 -123
  87. package/postcss.config.js +0 -9
  88. package/src/accordion.tsx +0 -60
  89. package/src/avatar.tsx +0 -47
  90. package/src/background.tsx +0 -71
  91. package/src/badge.tsx +0 -33
  92. package/src/button.tsx +0 -60
  93. package/src/content.ts +0 -34
  94. package/src/copy-button.tsx +0 -39
  95. package/src/footer.tsx +0 -204
  96. package/src/form.tsx +0 -77
  97. package/src/hooks/index.ts +0 -5
  98. package/src/hooks/use-current-anchor.ts +0 -65
  99. package/src/hooks/use-intersection-observer.ts +0 -41
  100. package/src/hooks/use-local-storage.ts +0 -24
  101. package/src/hooks/use-media-query.ts +0 -46
  102. package/src/hooks/use-scroll.ts +0 -21
  103. package/src/icon-menu.tsx +0 -15
  104. package/src/icons/copy.tsx +0 -18
  105. package/src/icons/expanding-arrow.tsx +0 -39
  106. package/src/icons/facebook.tsx +0 -23
  107. package/src/icons/github.tsx +0 -14
  108. package/src/icons/google.tsx +0 -12
  109. package/src/icons/index.tsx +0 -23
  110. package/src/icons/linkedin.tsx +0 -22
  111. package/src/icons/loading-circle.tsx +0 -25
  112. package/src/icons/loading-dots.tsx +0 -21
  113. package/src/icons/loading-spinner.tsx +0 -34
  114. package/src/icons/logo.tsx +0 -29
  115. package/src/icons/logotype.tsx +0 -51
  116. package/src/icons/magic.tsx +0 -30
  117. package/src/icons/photo.tsx +0 -20
  118. package/src/icons/tick.tsx +0 -18
  119. package/src/icons/twitter.tsx +0 -31
  120. package/src/icons/unsplash.tsx +0 -17
  121. package/src/index.tsx +0 -35
  122. package/src/link-preview.tsx +0 -111
  123. package/src/max-width-wrapper.tsx +0 -21
  124. package/src/modal.tsx +0 -102
  125. package/src/nav-mobile.tsx +0 -108
  126. package/src/nav.tsx +0 -205
  127. package/src/popover.tsx +0 -61
  128. package/src/styles.css +0 -3
  129. package/src/switch.tsx +0 -60
  130. package/src/tab-select.tsx +0 -27
  131. package/src/tooltip.tsx +0 -184
  132. package/tailwind.config.ts +0 -9
  133. package/tsconfig.json +0 -5
  134. package/tsup.config.ts +0 -15
package/src/badge.tsx DELETED
@@ -1,33 +0,0 @@
1
- import { cn } from "@dub/utils";
2
- import { cva, type VariantProps } from "class-variance-authority";
3
-
4
- const badgeVariants = cva(
5
- "max-w-fit rounded-full border px-2 py-px text-xs font-medium capitalize",
6
- {
7
- variants: {
8
- variant: {
9
- default: "border-gray-400 text-gray-500",
10
- violet: "border-violet-600 bg-violet-600 text-white",
11
- blue: "border-blue-500 bg-blue-500 text-white",
12
- black: "border-black bg-black text-white",
13
- gray: "border-gray-400 bg-gray-400 text-white",
14
- neutral: "border-gray-400 text-gray-500",
15
- },
16
- },
17
- defaultVariants: {
18
- variant: "neutral",
19
- },
20
- },
21
- );
22
-
23
- interface BadgeProps
24
- extends React.HTMLAttributes<HTMLSpanElement>,
25
- VariantProps<typeof badgeVariants> {}
26
-
27
- function Badge({ className, variant, ...props }: BadgeProps) {
28
- return (
29
- <span className={cn(badgeVariants({ variant }), className)} {...props} />
30
- );
31
- }
32
-
33
- export { Badge, badgeVariants };
package/src/button.tsx DELETED
@@ -1,60 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "@dub/utils";
4
- import { ReactNode } from "react";
5
- import { LoadingSpinner } from "./icons";
6
- import { Tooltip } from "./tooltip";
7
-
8
- interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
9
- text: string;
10
- variant?: "primary" | "secondary" | "success" | "danger";
11
- loading?: boolean;
12
- icon?: ReactNode;
13
- disabledTooltip?: string | ReactNode;
14
- }
15
-
16
- export function Button({
17
- text,
18
- variant = "primary",
19
- loading,
20
- icon,
21
- disabledTooltip,
22
- ...props
23
- }: ButtonProps) {
24
- if (disabledTooltip) {
25
- return (
26
- <Tooltip content={disabledTooltip} fullWidth>
27
- <div className="flex h-10 w-full cursor-not-allowed items-center justify-center rounded-md border border-gray-200 bg-gray-100 px-4 text-sm text-gray-400 transition-all focus:outline-none">
28
- <p>{text}</p>
29
- </div>
30
- </Tooltip>
31
- );
32
- }
33
- return (
34
- <button
35
- // if onClick is passed, it's a "button" type, otherwise it's being used in a form, hence "submit"
36
- type={props.onClick ? "button" : "submit"}
37
- className={cn(
38
- "flex h-10 w-full items-center justify-center space-x-2 rounded-md border px-4 text-sm transition-all focus:outline-none",
39
- props.disabled || loading
40
- ? "cursor-not-allowed border-gray-200 bg-gray-100 text-gray-400"
41
- : {
42
- "border-black bg-black text-white hover:bg-white hover:text-black":
43
- variant === "primary",
44
- "border-gray-200 bg-white text-gray-500 hover:border-black hover:text-black":
45
- variant === "secondary",
46
- "border-blue-500 bg-blue-500 text-white hover:bg-white hover:text-blue-500":
47
- variant === "success",
48
- "border-red-500 bg-red-500 text-white hover:bg-white hover:text-red-500":
49
- variant === "danger",
50
- },
51
- props.className,
52
- )}
53
- disabled={props.disabled || loading}
54
- {...props}
55
- >
56
- {loading ? <LoadingSpinner /> : icon ? icon : null}
57
- <p>{text}</p>
58
- </button>
59
- );
60
- }
package/src/content.ts DELETED
@@ -1,34 +0,0 @@
1
- import { Airplay, BarChart, Link2, QrCode, Users } from "lucide-react";
2
-
3
- export const FEATURES_LIST = [
4
- {
5
- title: "Powerful Analytics For The Modern Marketer",
6
- shortTitle: "Advanced Analytics",
7
- icon: BarChart,
8
- slug: "analytics",
9
- },
10
- {
11
- title: "Branded Links That Stand Out",
12
- shortTitle: "Branded Links",
13
- icon: Airplay,
14
- slug: "branded-links",
15
- },
16
- {
17
- title: "Free QR Code Generator",
18
- shortTitle: "QR Codes",
19
- icon: QrCode,
20
- slug: "qr-codes",
21
- },
22
- {
23
- title: "Personalize Your Short Links",
24
- shortTitle: "Personalization",
25
- icon: Link2,
26
- slug: "personalization",
27
- },
28
- {
29
- title: "Collaborate With Your Team",
30
- shortTitle: "Team Collaboration",
31
- icon: Users,
32
- slug: "collaboration",
33
- },
34
- ];
@@ -1,39 +0,0 @@
1
- "use client";
2
-
3
- import { cn } from "@dub/utils";
4
- import { useState } from "react";
5
- import { toast } from "sonner";
6
- import { Copy, Tick } from "./icons";
7
-
8
- export function CopyButton({
9
- value,
10
- className,
11
- }: {
12
- value: string;
13
- className?: string;
14
- }) {
15
- const [copied, setCopied] = useState(false);
16
- return (
17
- <button
18
- onClick={(e) => {
19
- e.stopPropagation();
20
- setCopied(true);
21
- navigator.clipboard.writeText(value).then(() => {
22
- toast.success("Copied to clipboard!");
23
- });
24
- setTimeout(() => setCopied(false), 3000);
25
- }}
26
- className={cn(
27
- "group rounded-full bg-gray-100 p-1.5 transition-all duration-75 hover:scale-105 hover:bg-blue-100 active:scale-95",
28
- className,
29
- )}
30
- >
31
- <span className="sr-only">Copy</span>
32
- {copied ? (
33
- <Tick className="text-gray-700 transition-all group-hover:text-blue-800" />
34
- ) : (
35
- <Copy className="text-gray-700 transition-all group-hover:text-blue-800" />
36
- )}
37
- </button>
38
- );
39
- }
package/src/footer.tsx DELETED
@@ -1,204 +0,0 @@
1
- "use client";
2
-
3
- import { ALL_TOOLS } from "@dub/utils";
4
- import va from "@vercel/analytics";
5
- import Link from "next/link";
6
- import { useParams } from "next/navigation";
7
- import { FEATURES_LIST } from "./content";
8
- import { Github, LinkedIn, LogoType, Twitter } from "./icons";
9
- import { MaxWidthWrapper } from "./max-width-wrapper";
10
-
11
- const navigation = {
12
- features: FEATURES_LIST.map(({ shortTitle, slug }) => ({
13
- name: shortTitle,
14
- href: `/features/${slug}`,
15
- })),
16
- product: [
17
- { name: "Blog", href: "/blog" },
18
- { name: "Changelog", href: "/changelog" },
19
- { name: "Customer Stories", href: "/customers" },
20
- { name: "Help Center", href: "/help" },
21
- { name: "Pricing", href: "/pricing" },
22
- ],
23
- tools: ALL_TOOLS.map(({ name, slug }) => ({
24
- name,
25
- href: `/tools/${slug}`,
26
- })),
27
- legal: [
28
- { name: "Privacy", href: "/privacy" },
29
- { name: "Terms", href: "/terms" },
30
- { name: "Abuse", href: "/abuse" },
31
- ],
32
- };
33
-
34
- export function Footer() {
35
- const { domain = "dub.co" } = useParams() as { domain: string };
36
-
37
- const createHref = (href: string) =>
38
- domain === "dub.co" ? href : `https://dub.co${href}`;
39
-
40
- return (
41
- <footer className="z-10 border-t border-gray-200 bg-white/50 py-8 backdrop-blur-lg">
42
- <MaxWidthWrapper className="pt-10">
43
- <div className="xl:grid xl:grid-cols-5 xl:gap-8">
44
- <div className="space-y-8 xl:col-span-2">
45
- <Link
46
- href={createHref("/")}
47
- {...(domain !== "dub.co" && {
48
- onClick: () => {
49
- va.track("Referred from custom domain", {
50
- domain,
51
- medium: `footer item (logo)`,
52
- });
53
- },
54
- })}
55
- >
56
- <span className="sr-only">Dub.co Logo</span>
57
- <LogoType className="h-7 text-gray-600" />
58
- </Link>
59
- <p className="max-w-xs text-sm text-gray-500">
60
- Giving modern marketing teams superpowers with short links that
61
- stand out.
62
- </p>
63
- <div className="flex items-center space-x-2">
64
- <a
65
- href="https://twitter.com/dubdotco"
66
- target="_blank"
67
- rel="noreferrer"
68
- className="group rounded-md p-2 transition-colors hover:bg-gray-100 active:bg-gray-200"
69
- >
70
- <span className="sr-only">Twitter</span>
71
- <Twitter className="h-5 w-5 text-gray-600" />
72
- </a>
73
- <div className="h-8 border-l border-gray-200" />
74
- <a
75
- href="https://github.com/steven-tey/dub"
76
- target="_blank"
77
- rel="noreferrer"
78
- className="rounded-md p-2 transition-colors hover:bg-gray-100 active:bg-gray-200"
79
- >
80
- <span className="sr-only">Github</span>
81
- <Github className="h-5 w-5 text-gray-600" />
82
- </a>
83
- <div className="h-8 border-l border-gray-200" />
84
- <a
85
- href="https://www.linkedin.com/company/dubhq/"
86
- target="_blank"
87
- rel="noreferrer"
88
- className="rounded-md p-2 transition-colors hover:bg-gray-100 active:bg-gray-200"
89
- >
90
- <span className="sr-only">LinkedIn</span>
91
- <LinkedIn className="h-5 w-5" fill="#52525B" />
92
- </a>
93
- </div>
94
- </div>
95
- <div className="mt-16 grid grid-cols-2 gap-8 xl:col-span-3 xl:mt-0">
96
- <div className="md:grid md:grid-cols-2 md:gap-8">
97
- <div>
98
- <h3 className="text-sm font-semibold text-gray-900">
99
- Features
100
- </h3>
101
- <ul role="list" className="mt-4 space-y-4">
102
- {navigation.features.map((item) => (
103
- <li key={item.name}>
104
- <Link
105
- href={createHref(item.href)}
106
- {...(domain !== "dub.co" && {
107
- onClick: () => {
108
- va.track("Referred from custom domain", {
109
- domain,
110
- medium: `footer item (${item.name})`,
111
- });
112
- },
113
- })}
114
- className="text-sm text-gray-500 hover:text-gray-900"
115
- >
116
- {item.name}
117
- </Link>
118
- </li>
119
- ))}
120
- </ul>
121
- </div>
122
- <div className="mt-10 md:mt-0">
123
- <h3 className="text-sm font-semibold text-gray-600">Product</h3>
124
- <ul role="list" className="mt-4 space-y-4">
125
- {navigation.product.map((item) => (
126
- <li key={item.name}>
127
- <Link
128
- href={createHref(item.href)}
129
- {...(domain !== "dub.co" && {
130
- onClick: () => {
131
- va.track("Referred from custom domain", {
132
- domain,
133
- medium: `footer item (${item.name})`,
134
- });
135
- },
136
- })}
137
- className="text-sm text-gray-500 hover:text-gray-900"
138
- >
139
- {item.name}
140
- </Link>
141
- </li>
142
- ))}
143
- </ul>
144
- </div>
145
- </div>
146
- <div className="md:grid md:grid-cols-2 md:gap-8">
147
- <div>
148
- <h3 className="text-sm font-semibold text-gray-600">Tools</h3>
149
- <ul role="list" className="mt-4 space-y-4">
150
- {navigation.tools.map((item) => (
151
- <li key={item.name}>
152
- <Link
153
- href={createHref(item.href)}
154
- {...(domain !== "dub.co" && {
155
- onClick: () => {
156
- va.track("Referred from custom domain", {
157
- domain,
158
- medium: `footer item (${item.name})`,
159
- });
160
- },
161
- })}
162
- className="text-sm text-gray-500 hover:text-gray-900"
163
- >
164
- {item.name}
165
- </Link>
166
- </li>
167
- ))}
168
- </ul>
169
- </div>
170
- <div className="mt-10 md:mt-0">
171
- <h3 className="text-sm font-semibold text-gray-600">Legal</h3>
172
- <ul role="list" className="mt-4 space-y-4">
173
- {navigation.legal.map((item) => (
174
- <li key={item.name}>
175
- <Link
176
- href={createHref(item.href)}
177
- {...(domain !== "dub.co" && {
178
- onClick: () => {
179
- va.track("Referred from custom domain", {
180
- domain,
181
- medium: `footer item (${item.name})`,
182
- });
183
- },
184
- })}
185
- className="text-sm text-gray-500 hover:text-gray-900"
186
- >
187
- {item.name}
188
- </Link>
189
- </li>
190
- ))}
191
- </ul>
192
- </div>
193
- </div>
194
- </div>
195
- </div>
196
- <div className="mt-16 border-t border-gray-900/10 pt-8 sm:mt-20 lg:mt-24">
197
- <p className="text-sm leading-5 text-gray-500">
198
- © {new Date().getFullYear()} Dub.co
199
- </p>
200
- </div>
201
- </MaxWidthWrapper>
202
- </footer>
203
- );
204
- }
package/src/form.tsx DELETED
@@ -1,77 +0,0 @@
1
- import { cn } from "@dub/utils";
2
- import { InputHTMLAttributes, ReactNode, useMemo, useState } from "react";
3
- import { Button } from "./button";
4
-
5
- export function Form({
6
- title,
7
- description,
8
- inputData,
9
- helpText,
10
- buttonText = "Save Changes",
11
- disabledTooltip,
12
- handleSubmit,
13
- }: {
14
- title: string;
15
- description: string;
16
- inputData: InputHTMLAttributes<HTMLInputElement>;
17
- helpText?: string;
18
- buttonText?: string;
19
- disabledTooltip?: string | ReactNode;
20
- handleSubmit: (data: any) => Promise<any>;
21
- }) {
22
- const [value, setValue] = useState(inputData.defaultValue);
23
- const [saving, setSaving] = useState(false);
24
- const saveDisabled = useMemo(() => {
25
- return saving || !value || value === inputData.defaultValue;
26
- }, [saving, value, inputData.defaultValue]);
27
-
28
- return (
29
- <form
30
- onSubmit={async (e) => {
31
- e.preventDefault();
32
- setSaving(true);
33
- await handleSubmit({
34
- [inputData.name as string]: value,
35
- });
36
- setSaving(false);
37
- }}
38
- className="rounded-lg border border-gray-200 bg-white"
39
- >
40
- <div className="relative flex flex-col space-y-6 p-5 sm:p-10">
41
- <div className="flex flex-col space-y-3">
42
- <h2 className="text-xl font-medium">{title}</h2>
43
- <p className="text-sm text-gray-500">{description}</p>
44
- </div>
45
- {typeof inputData.defaultValue === "string" ? (
46
- <input
47
- {...inputData}
48
- type="text"
49
- required
50
- disabled={disabledTooltip ? true : false}
51
- onChange={(e) => setValue(e.target.value)}
52
- className={cn(
53
- "w-full max-w-md rounded-md border border-gray-300 text-gray-900 placeholder-gray-300 focus:border-gray-500 focus:outline-none focus:ring-gray-500 sm:text-sm",
54
- {
55
- "cursor-not-allowed bg-gray-100 text-gray-400": disabledTooltip,
56
- },
57
- )}
58
- />
59
- ) : (
60
- <div className="h-[2.35rem] w-full max-w-md animate-pulse rounded-md bg-gray-200" />
61
- )}
62
- </div>
63
-
64
- <div className="flex items-center justify-between rounded-b-lg border-t border-gray-200 bg-gray-50 p-3 sm:px-10">
65
- <p className="text-sm text-gray-500">{helpText}</p>
66
- <div>
67
- <Button
68
- text={buttonText}
69
- loading={saving}
70
- disabled={saveDisabled}
71
- disabledTooltip={disabledTooltip}
72
- />
73
- </div>
74
- </div>
75
- </form>
76
- );
77
- }
@@ -1,5 +0,0 @@
1
- export { default as useCurrentAnchor } from "./use-current-anchor";
2
- export { default as useIntersectionObserver } from "./use-intersection-observer";
3
- export { default as useLocalStorage } from "./use-local-storage";
4
- export { default as useMediaQuery } from "./use-media-query";
5
- export { default as useScroll } from "./use-scroll";
@@ -1,65 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export default function useCurrentAnchor() {
4
- const [currentAnchor, setCurrentAnchor] = useState<string | null>(null);
5
-
6
- useEffect(() => {
7
- const mdxContainer: HTMLElement | null = document.querySelector(
8
- "[data-mdx-container]",
9
- );
10
- if (!mdxContainer) return;
11
-
12
- const offsetTop = mdxContainer.offsetTop - 1;
13
-
14
- const observer = new IntersectionObserver(
15
- (entries) => {
16
- let currentEntry = entries[0];
17
- if (!currentEntry) return;
18
-
19
- const offsetBottom =
20
- (currentEntry.rootBounds?.height || 0) * 0.3 + offsetTop;
21
-
22
- for (let i = 1; i < entries.length; i++) {
23
- const entry = entries[i];
24
- if (!entry) break;
25
-
26
- if (
27
- entry.boundingClientRect.top <
28
- currentEntry.boundingClientRect.top ||
29
- currentEntry.boundingClientRect.bottom < offsetTop
30
- ) {
31
- currentEntry = entry;
32
- }
33
- }
34
-
35
- let target: Element | undefined = currentEntry.target;
36
-
37
- // if the target is too high up, we need to find the next sibling
38
- while (target && target.getBoundingClientRect().bottom < offsetTop) {
39
- target = siblings.get(target)?.next;
40
- }
41
-
42
- // if the target is too low, we need to find the previous sibling
43
- while (target && target.getBoundingClientRect().top > offsetBottom) {
44
- target = siblings.get(target)?.prev;
45
- }
46
- if (target) setCurrentAnchor(target.getAttribute("href"));
47
- },
48
- {
49
- threshold: 1,
50
- rootMargin: `-${offsetTop}px 0px 0px 0px`,
51
- },
52
- );
53
-
54
- const siblings = new Map();
55
-
56
- const anchors = mdxContainer?.querySelectorAll("[data-mdx-heading]");
57
- anchors.forEach((anchor) => observer.observe(anchor));
58
-
59
- return () => {
60
- observer.disconnect();
61
- };
62
- }, []);
63
-
64
- return currentAnchor?.replace("#", "");
65
- }
@@ -1,41 +0,0 @@
1
- import { RefObject, useEffect, useState } from "react";
2
-
3
- interface Args extends IntersectionObserverInit {
4
- freezeOnceVisible?: boolean;
5
- }
6
-
7
- export default function useIntersectionObserver(
8
- elementRef: RefObject<Element>,
9
- {
10
- threshold = 0,
11
- root = null,
12
- rootMargin = "0%",
13
- freezeOnceVisible = false,
14
- }: Args,
15
- ): IntersectionObserverEntry | undefined {
16
- const [entry, setEntry] = useState<IntersectionObserverEntry>();
17
-
18
- const frozen = entry?.isIntersecting && freezeOnceVisible;
19
-
20
- const updateEntry = ([entry]: IntersectionObserverEntry[]): void => {
21
- setEntry(entry);
22
- };
23
-
24
- useEffect(() => {
25
- const node = elementRef?.current; // DOM Ref
26
- const hasIOSupport = !!window.IntersectionObserver;
27
-
28
- if (!hasIOSupport || frozen || !node) return;
29
-
30
- const observerParams = { threshold, root, rootMargin };
31
- const observer = new IntersectionObserver(updateEntry, observerParams);
32
-
33
- observer.observe(node);
34
-
35
- return () => observer.disconnect();
36
-
37
- // eslint-disable-next-line react-hooks/exhaustive-deps
38
- }, [elementRef, JSON.stringify(threshold), root, rootMargin, frozen]);
39
-
40
- return entry;
41
- }
@@ -1,24 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export default function useLocalStorage<T>(
4
- key: string,
5
- initialValue: T,
6
- ): [T, (value: T) => void] {
7
- const [storedValue, setStoredValue] = useState(initialValue);
8
-
9
- useEffect(() => {
10
- // Retrieve from localStorage
11
- const item = window.localStorage.getItem(key);
12
- if (item) {
13
- setStoredValue(JSON.parse(item));
14
- }
15
- }, [key]);
16
-
17
- const setValue = (value: T) => {
18
- // Save state
19
- setStoredValue(value);
20
- // Save to localStorage
21
- window.localStorage.setItem(key, JSON.stringify(value));
22
- };
23
- return [storedValue, setValue];
24
- }
@@ -1,46 +0,0 @@
1
- import { useEffect, useState } from "react";
2
-
3
- export default function useMediaQuery() {
4
- const [device, setDevice] = useState<"mobile" | "tablet" | "desktop" | null>(
5
- null,
6
- );
7
- const [dimensions, setDimensions] = useState<{
8
- width: number;
9
- height: number;
10
- } | null>(null);
11
-
12
- useEffect(() => {
13
- const checkDevice = () => {
14
- if (window.matchMedia("(max-width: 640px)").matches) {
15
- setDevice("mobile");
16
- } else if (
17
- window.matchMedia("(min-width: 641px) and (max-width: 1024px)").matches
18
- ) {
19
- setDevice("tablet");
20
- } else {
21
- setDevice("desktop");
22
- }
23
- setDimensions({ width: window.innerWidth, height: window.innerHeight });
24
- };
25
-
26
- // Initial detection
27
- checkDevice();
28
-
29
- // Listener for windows resize
30
- window.addEventListener("resize", checkDevice);
31
-
32
- // Cleanup listener
33
- return () => {
34
- window.removeEventListener("resize", checkDevice);
35
- };
36
- }, []);
37
-
38
- return {
39
- device,
40
- width: dimensions?.width,
41
- height: dimensions?.height,
42
- isMobile: device === "mobile",
43
- isTablet: device === "tablet",
44
- isDesktop: device === "desktop",
45
- };
46
- }
@@ -1,21 +0,0 @@
1
- import { useCallback, useEffect, useState } from "react";
2
-
3
- export default function useScroll(threshold: number) {
4
- const [scrolled, setScrolled] = useState(false);
5
-
6
- const onScroll = useCallback(() => {
7
- setScrolled(window.scrollY > threshold);
8
- }, [threshold]);
9
-
10
- useEffect(() => {
11
- window.addEventListener("scroll", onScroll);
12
- return () => window.removeEventListener("scroll", onScroll);
13
- }, [onScroll]);
14
-
15
- // also check on first load
16
- useEffect(() => {
17
- onScroll();
18
- }, [onScroll]);
19
-
20
- return scrolled;
21
- }
package/src/icon-menu.tsx DELETED
@@ -1,15 +0,0 @@
1
- import { ReactNode } from "react";
2
-
3
- interface MenuIconProps {
4
- icon: ReactNode;
5
- text: string;
6
- }
7
-
8
- export function IconMenu({ icon, text }: MenuIconProps) {
9
- return (
10
- <div className="flex items-center justify-start space-x-2">
11
- {icon}
12
- <p className="text-sm">{text}</p>
13
- </div>
14
- );
15
- }