Skip to content

Commit 18312f5

Browse files
committed
feat: env & mode toggle
1 parent af00111 commit 18312f5

File tree

12 files changed

+560
-8
lines changed

12 files changed

+560
-8
lines changed

.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# NEXT_PUBLIC_GA_ID=""
2+
# NEXT_PUBLIC_GOOGLE_ADSENSE_ACCOUNT=""

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ yarn-error.log*
3232

3333
# env files (can opt-in for committing if needed)
3434
.env*
35+
!.env.example
3536

3637
# vercel
3738
.vercel

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# sourcemap-online
2+
3+
## 0.1.1
4+
5+
### Patch Changes
6+
7+
- env & mode toggle

app/layout.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Geist, Geist_Mono } from "next/font/google";
33
import { GoogleAnalytics } from "@next/third-parties/google";
44
import { ThemeProvider } from "@/components/theme-provider";
55
import { Toaster } from "@/components/ui/sonner";
6+
import { env } from "@/lib/env";
67
import "./globals.css";
78

89
const geistSans = Geist({
@@ -87,12 +88,16 @@ export default function RootLayout({
8788
{children}
8889
</ThemeProvider>
8990
<Toaster />
90-
<GoogleAnalytics gaId="G-L61TTVLT2K" />
91-
<script
92-
async
93-
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-9339016402558827"
94-
crossOrigin="anonymous"
95-
></script>
91+
{env.NEXT_PUBLIC_GA_ID !== undefined && (
92+
<GoogleAnalytics gaId={env.NEXT_PUBLIC_GA_ID} />
93+
)}
94+
{env.NEXT_PUBLIC_GOOGLE_ADSENSE_ACCOUNT !== undefined && (
95+
<script
96+
async
97+
src={`https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${env.NEXT_PUBLIC_GOOGLE_ADSENSE_ACCOUNT}`}
98+
crossOrigin="anonymous"
99+
></script>
100+
)}
96101
</body>
97102
</html>
98103
);

app/original-position-for/layout.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type { Metadata } from "next";
2+
import { Button } from "@/components/ui/button";
23
import { Nav } from "@/features/original-position-for/nav";
4+
import { repository } from "@/package.json";
5+
import { ModeToggle } from "@/components/mode-toggle";
6+
import { Github } from "@/components/icons";
37

48
export const metadata: Metadata = {
59
title: "Create Next App",
@@ -14,8 +18,20 @@ export default function RootLayout({
1418
return (
1519
<div className="min-h-screen bg-background">
1620
<header className="border-b bg-card">
17-
<div className="container mx-auto p-4">
21+
<div className="container mx-auto p-4 flex justify-between items-center">
1822
<h1 className="text-2xl font-bold">Source Map Parser</h1>
23+
<div className="flex items-center gap-2">
24+
<ModeToggle />
25+
<a
26+
href={repository.url.slice(0, -".git".length)}
27+
target="_blank"
28+
aria-label="GitHub Repository"
29+
>
30+
<Button variant="outline" size="icon">
31+
<Github strokeWidth={0} fill="currentColor" />
32+
</Button>
33+
</a>
34+
</div>
1935
</div>
2036
</header>
2137
<main className="container mx-auto p-4">

components/icons/github.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createLucideIcon, type IconNode } from "lucide-react";
2+
3+
const __iconNode: IconNode = [
4+
[
5+
"path",
6+
{
7+
d: "M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12",
8+
key: "github",
9+
},
10+
],
11+
];
12+
13+
export const Github = createLucideIcon("github", __iconNode);

components/icons/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from "./github";

components/mode-toggle.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"use client";
2+
3+
import { Moon, Sun } from "lucide-react";
4+
import { useTheme } from "next-themes";
5+
import { Button } from "@/components/ui/button";
6+
import {
7+
DropdownMenu,
8+
DropdownMenuContent,
9+
DropdownMenuItem,
10+
DropdownMenuTrigger,
11+
} from "@/components/ui/dropdown-menu";
12+
13+
export function ModeToggle() {
14+
const { setTheme } = useTheme();
15+
16+
return (
17+
<DropdownMenu>
18+
<DropdownMenuTrigger asChild>
19+
<Button variant="outline" size="icon">
20+
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
21+
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
22+
<span className="sr-only">Toggle theme</span>
23+
</Button>
24+
</DropdownMenuTrigger>
25+
<DropdownMenuContent align="end">
26+
<DropdownMenuItem onClick={() => setTheme("light")}>
27+
Light
28+
</DropdownMenuItem>
29+
<DropdownMenuItem onClick={() => setTheme("dark")}>
30+
Dark
31+
</DropdownMenuItem>
32+
<DropdownMenuItem onClick={() => setTheme("system")}>
33+
System
34+
</DropdownMenuItem>
35+
</DropdownMenuContent>
36+
</DropdownMenu>
37+
);
38+
}

components/ui/dropdown-menu.tsx

Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
"use client"
2+
3+
import * as React from "react"
4+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
5+
import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react"
6+
7+
import { cn } from "@/lib/utils"
8+
9+
function DropdownMenu({
10+
...props
11+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
12+
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
13+
}
14+
15+
function DropdownMenuPortal({
16+
...props
17+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
18+
return (
19+
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
20+
)
21+
}
22+
23+
function DropdownMenuTrigger({
24+
...props
25+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
26+
return (
27+
<DropdownMenuPrimitive.Trigger
28+
data-slot="dropdown-menu-trigger"
29+
{...props}
30+
/>
31+
)
32+
}
33+
34+
function DropdownMenuContent({
35+
className,
36+
sideOffset = 4,
37+
...props
38+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
39+
return (
40+
<DropdownMenuPrimitive.Portal>
41+
<DropdownMenuPrimitive.Content
42+
data-slot="dropdown-menu-content"
43+
sideOffset={sideOffset}
44+
className={cn(
45+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
46+
className
47+
)}
48+
{...props}
49+
/>
50+
</DropdownMenuPrimitive.Portal>
51+
)
52+
}
53+
54+
function DropdownMenuGroup({
55+
...props
56+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
57+
return (
58+
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
59+
)
60+
}
61+
62+
function DropdownMenuItem({
63+
className,
64+
inset,
65+
variant = "default",
66+
...props
67+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
68+
inset?: boolean
69+
variant?: "default" | "destructive"
70+
}) {
71+
return (
72+
<DropdownMenuPrimitive.Item
73+
data-slot="dropdown-menu-item"
74+
data-inset={inset}
75+
data-variant={variant}
76+
className={cn(
77+
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
78+
className
79+
)}
80+
{...props}
81+
/>
82+
)
83+
}
84+
85+
function DropdownMenuCheckboxItem({
86+
className,
87+
children,
88+
checked,
89+
...props
90+
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
91+
return (
92+
<DropdownMenuPrimitive.CheckboxItem
93+
data-slot="dropdown-menu-checkbox-item"
94+
className={cn(
95+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
96+
className
97+
)}
98+
checked={checked}
99+
{...props}
100+
>
101+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
102+
<DropdownMenuPrimitive.ItemIndicator>
103+
<CheckIcon className="size-4" />
104+
</DropdownMenuPrimitive.ItemIndicator>
105+
</span>
106+
{children}
107+
</DropdownMenuPrimitive.CheckboxItem>
108+
)
109+
}
110+
111+
function DropdownMenuRadioGroup({
112+
...props
113+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
114+
return (
115+
<DropdownMenuPrimitive.RadioGroup
116+
data-slot="dropdown-menu-radio-group"
117+
{...props}
118+
/>
119+
)
120+
}
121+
122+
function DropdownMenuRadioItem({
123+
className,
124+
children,
125+
...props
126+
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
127+
return (
128+
<DropdownMenuPrimitive.RadioItem
129+
data-slot="dropdown-menu-radio-item"
130+
className={cn(
131+
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
132+
className
133+
)}
134+
{...props}
135+
>
136+
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
137+
<DropdownMenuPrimitive.ItemIndicator>
138+
<CircleIcon className="size-2 fill-current" />
139+
</DropdownMenuPrimitive.ItemIndicator>
140+
</span>
141+
{children}
142+
</DropdownMenuPrimitive.RadioItem>
143+
)
144+
}
145+
146+
function DropdownMenuLabel({
147+
className,
148+
inset,
149+
...props
150+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
151+
inset?: boolean
152+
}) {
153+
return (
154+
<DropdownMenuPrimitive.Label
155+
data-slot="dropdown-menu-label"
156+
data-inset={inset}
157+
className={cn(
158+
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
159+
className
160+
)}
161+
{...props}
162+
/>
163+
)
164+
}
165+
166+
function DropdownMenuSeparator({
167+
className,
168+
...props
169+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
170+
return (
171+
<DropdownMenuPrimitive.Separator
172+
data-slot="dropdown-menu-separator"
173+
className={cn("bg-border -mx-1 my-1 h-px", className)}
174+
{...props}
175+
/>
176+
)
177+
}
178+
179+
function DropdownMenuShortcut({
180+
className,
181+
...props
182+
}: React.ComponentProps<"span">) {
183+
return (
184+
<span
185+
data-slot="dropdown-menu-shortcut"
186+
className={cn(
187+
"text-muted-foreground ml-auto text-xs tracking-widest",
188+
className
189+
)}
190+
{...props}
191+
/>
192+
)
193+
}
194+
195+
function DropdownMenuSub({
196+
...props
197+
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
198+
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
199+
}
200+
201+
function DropdownMenuSubTrigger({
202+
className,
203+
inset,
204+
children,
205+
...props
206+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
207+
inset?: boolean
208+
}) {
209+
return (
210+
<DropdownMenuPrimitive.SubTrigger
211+
data-slot="dropdown-menu-sub-trigger"
212+
data-inset={inset}
213+
className={cn(
214+
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8",
215+
className
216+
)}
217+
{...props}
218+
>
219+
{children}
220+
<ChevronRightIcon className="ml-auto size-4" />
221+
</DropdownMenuPrimitive.SubTrigger>
222+
)
223+
}
224+
225+
function DropdownMenuSubContent({
226+
className,
227+
...props
228+
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
229+
return (
230+
<DropdownMenuPrimitive.SubContent
231+
data-slot="dropdown-menu-sub-content"
232+
className={cn(
233+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
234+
className
235+
)}
236+
{...props}
237+
/>
238+
)
239+
}
240+
241+
export {
242+
DropdownMenu,
243+
DropdownMenuPortal,
244+
DropdownMenuTrigger,
245+
DropdownMenuContent,
246+
DropdownMenuGroup,
247+
DropdownMenuLabel,
248+
DropdownMenuItem,
249+
DropdownMenuCheckboxItem,
250+
DropdownMenuRadioGroup,
251+
DropdownMenuRadioItem,
252+
DropdownMenuSeparator,
253+
DropdownMenuShortcut,
254+
DropdownMenuSub,
255+
DropdownMenuSubTrigger,
256+
DropdownMenuSubContent,
257+
}

0 commit comments

Comments
 (0)