@lukoweb/apitogo 0.1.86 → 0.1.88
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/dist/cli/cli.js +10755 -1987
- package/dist/declarations/cli/platform/apply/index.d.ts +3 -0
- package/dist/declarations/cli/platform/client/deploy-server.d.ts +16 -0
- package/dist/declarations/cli/platform/client/platform-api.d.ts +397 -0
- package/dist/declarations/cli/platform/config.d.ts +20 -0
- package/dist/declarations/cli/platform/errors.d.ts +16 -0
- package/dist/declarations/cli/platform/helpers/backend-packaging.d.ts +11 -0
- package/dist/declarations/cli/platform/helpers/deploy-artifact-hash.d.ts +14 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-backend-job.d.ts +25 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-backend.d.ts +11 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-landing.d.ts +99 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-manifest.d.ts +167 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-plan-sync.d.ts +20 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-secrets.d.ts +61 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-settings-kinds.d.ts +8 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-site-api-url.d.ts +12 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-site-validation.d.ts +22 -0
- package/dist/declarations/cli/platform/helpers/dev-portal-urls.d.ts +17 -0
- package/dist/declarations/cli/platform/helpers/fetch-retry.d.ts +17 -0
- package/dist/declarations/cli/platform/helpers/free-projects-quota.d.ts +14 -0
- package/dist/declarations/cli/platform/helpers/gateway-plan-sync.d.ts +26 -0
- package/dist/declarations/cli/platform/helpers/gateway-readiness.d.ts +11 -0
- package/dist/declarations/cli/platform/helpers/job-status.d.ts +12 -0
- package/dist/declarations/cli/platform/helpers/merge-dev-portal-settings.d.ts +26 -0
- package/dist/declarations/cli/platform/helpers/npm-build.d.ts +34 -0
- package/dist/declarations/cli/platform/helpers/openapi-gateway-servers.d.ts +23 -0
- package/dist/declarations/cli/platform/helpers/openapi.d.ts +24 -0
- package/dist/declarations/cli/platform/helpers/package-files.d.ts +1 -0
- package/dist/declarations/cli/platform/helpers/paid-gateway-platform-compat.d.ts +22 -0
- package/dist/declarations/cli/platform/helpers/platform-plan-catalog.d.ts +27 -0
- package/dist/declarations/cli/platform/helpers/platform-plan.d.ts +17 -0
- package/dist/declarations/cli/platform/helpers/poll.d.ts +21 -0
- package/dist/declarations/cli/platform/helpers/progress.d.ts +45 -0
- package/dist/declarations/cli/platform/helpers/project-name.d.ts +8 -0
- package/dist/declarations/cli/platform/helpers/public-routes.d.ts +4 -0
- package/dist/declarations/cli/platform/helpers/publish-ledger.d.ts +30 -0
- package/dist/declarations/cli/platform/helpers/publish-preflight.d.ts +60 -0
- package/dist/declarations/cli/platform/helpers/status.d.ts +7 -0
- package/dist/declarations/cli/platform/helpers/stripe-webhook.d.ts +3 -0
- package/dist/declarations/cli/platform/helpers/tarball.d.ts +11 -0
- package/dist/declarations/cli/platform/helpers/validate-dev-portal-readiness.d.ts +48 -0
- package/dist/declarations/cli/platform/helpers/wait-for-gateway-actions.d.ts +9 -0
- package/dist/declarations/cli/platform/helpers/warm-pool.d.ts +35 -0
- package/dist/declarations/cli/platform/helpers/zip.d.ts +6 -0
- package/dist/declarations/cli/platform/tools/backend.d.ts +45 -0
- package/dist/declarations/cli/platform/tools/dev-portal-bootstrap-shared.d.ts +25 -0
- package/dist/declarations/cli/platform/tools/dev-portal-publish.d.ts +45 -0
- package/dist/declarations/cli/platform/tools/dev-portal-site-build.d.ts +12 -0
- package/dist/declarations/cli/platform/tools/dev-portal.d.ts +84 -0
- package/dist/declarations/cli/platform/tools/gateway.d.ts +140 -0
- package/dist/declarations/cli/platform/tools/infrastructure.d.ts +47 -0
- package/dist/declarations/cli/platform/tools/lifecycle.d.ts +40 -0
- package/dist/declarations/cli/platform/tools/platform-plan.d.ts +43 -0
- package/dist/declarations/cli/platform/tools/projects.d.ts +27 -0
- package/dist/declarations/cli/platform/tools/subscription.d.ts +13 -0
- package/dist/declarations/config/landing-manifest.d.ts +5 -0
- package/dist/declarations/config/local-manifest.d.ts +5 -0
- package/dist/declarations/config/validators/ModulesSchema.d.ts +21 -0
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +5 -0
- package/dist/flat-config.d.ts +2 -0
- package/docs/cli/commands.md +14 -9
- package/docs/cli/go-live.md +2 -2
- package/docs/cli/setup.md +1 -1
- package/docs/guides/environment-variables.md +1 -1
- package/package.json +14 -2
- package/src/app/apitogoBrandTheme.ts +30 -29
- package/src/app/defaultTheme.css +56 -56
- package/src/config/validators/ModulesSchema.ts +2 -0
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +45 -18
- package/src/lib/components/Header.tsx +8 -5
- package/src/lib/components/HeaderAuthActions.tsx +5 -7
- package/src/lib/components/Layout.tsx +13 -4
- package/src/lib/components/Main.tsx +3 -2
- package/src/lib/components/MobileTopNavigation.tsx +2 -5
- package/src/lib/components/ThemeSwitch.tsx +41 -25
|
@@ -94,6 +94,7 @@ export const LandingApiShowcaseItemSchema = z.object({
|
|
|
94
94
|
icon: z.string().optional(),
|
|
95
95
|
latency: z.string().optional(),
|
|
96
96
|
summary: z.string().optional(),
|
|
97
|
+
method: z.string().optional(),
|
|
97
98
|
});
|
|
98
99
|
|
|
99
100
|
export const LandingApiShowcaseSchema = z
|
|
@@ -104,6 +105,7 @@ export const LandingApiShowcaseSchema = z
|
|
|
104
105
|
browseAllHref: z.string().optional(),
|
|
105
106
|
browseAllLabel: z.string().optional(),
|
|
106
107
|
autoFromOpenApi: z.boolean().optional(),
|
|
108
|
+
layout: z.enum(["grid", "list"]).optional(),
|
|
107
109
|
items: z.array(LandingApiShowcaseItemSchema).optional(),
|
|
108
110
|
})
|
|
109
111
|
.optional();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Spinner } from "@lukoweb/apitogo/components";
|
|
2
|
+
import { Badge } from "@lukoweb/apitogo/ui/Badge.js";
|
|
2
3
|
import { Button } from "@lukoweb/apitogo/ui/Button.js";
|
|
3
4
|
import {
|
|
4
5
|
Card,
|
|
@@ -9,7 +10,7 @@ import {
|
|
|
9
10
|
} from "@lukoweb/apitogo/ui/Card.js";
|
|
10
11
|
import { Input } from "@lukoweb/apitogo/ui/Input.js";
|
|
11
12
|
import { Label } from "@lukoweb/apitogo/ui/Label.js";
|
|
12
|
-
import { useCallback, useEffect, useState } from "react";
|
|
13
|
+
import { useCallback, useEffect, useState, type ReactNode } from "react";
|
|
13
14
|
import { Link, useSearchParams } from "react-router";
|
|
14
15
|
import type { DevPortalAuthConfigResponse } from "./dev-portal-constants.js";
|
|
15
16
|
import {
|
|
@@ -31,20 +32,31 @@ import {
|
|
|
31
32
|
type AuthPageShellProps = {
|
|
32
33
|
title: string;
|
|
33
34
|
description?: string;
|
|
34
|
-
|
|
35
|
+
badge?: ReactNode;
|
|
36
|
+
children: ReactNode;
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
function AuthPageShell({
|
|
39
|
+
function AuthPageShell({
|
|
40
|
+
title,
|
|
41
|
+
description,
|
|
42
|
+
badge,
|
|
43
|
+
children,
|
|
44
|
+
}: AuthPageShellProps) {
|
|
38
45
|
return (
|
|
39
|
-
<div className="flex items-center justify-center
|
|
40
|
-
<Card className="max-w-md
|
|
41
|
-
<CardHeader>
|
|
42
|
-
<
|
|
46
|
+
<div className="flex items-center justify-center bg-muted px-5 py-10">
|
|
47
|
+
<Card className="w-full max-w-md gap-0 bg-card py-8">
|
|
48
|
+
<CardHeader className="gap-0 px-8 pb-0">
|
|
49
|
+
{badge ? <div className="mb-3.5">{badge}</div> : null}
|
|
50
|
+
<CardTitle className="text-2xl font-bold tracking-tight">
|
|
51
|
+
{title}
|
|
52
|
+
</CardTitle>
|
|
43
53
|
{description ? (
|
|
44
|
-
<CardDescription>
|
|
54
|
+
<CardDescription className="mt-1 mb-[22px] text-sm">
|
|
55
|
+
{description}
|
|
56
|
+
</CardDescription>
|
|
45
57
|
) : null}
|
|
46
58
|
</CardHeader>
|
|
47
|
-
<CardContent className="space-y-4">{children}</CardContent>
|
|
59
|
+
<CardContent className="space-y-4 px-8">{children}</CardContent>
|
|
48
60
|
</Card>
|
|
49
61
|
</div>
|
|
50
62
|
);
|
|
@@ -349,7 +361,7 @@ export function DevPortalRegisterPage({
|
|
|
349
361
|
if (!baseUrl) {
|
|
350
362
|
return (
|
|
351
363
|
<AuthPageShell
|
|
352
|
-
title="Create account"
|
|
364
|
+
title="Create your account"
|
|
353
365
|
description="Dev portal API is not configured."
|
|
354
366
|
>
|
|
355
367
|
<p className="text-sm text-muted-foreground">
|
|
@@ -361,16 +373,17 @@ export function DevPortalRegisterPage({
|
|
|
361
373
|
|
|
362
374
|
return (
|
|
363
375
|
<AuthPageShell
|
|
364
|
-
title="Create account"
|
|
365
|
-
description="Register for API access."
|
|
376
|
+
title="Create your account"
|
|
366
377
|
>
|
|
367
|
-
<form className="
|
|
378
|
+
<form className="flex flex-col gap-4" onSubmit={onSubmit}>
|
|
368
379
|
<div className="space-y-2">
|
|
369
|
-
<Label htmlFor="displayName">
|
|
380
|
+
<Label htmlFor="displayName">Full name</Label>
|
|
370
381
|
<Input
|
|
371
382
|
id="displayName"
|
|
383
|
+
placeholder="Priya Shah"
|
|
372
384
|
value={displayName}
|
|
373
385
|
onChange={(event) => setDisplayName(event.target.value)}
|
|
386
|
+
className="h-10"
|
|
374
387
|
/>
|
|
375
388
|
</div>
|
|
376
389
|
<div className="space-y-2">
|
|
@@ -379,9 +392,11 @@ export function DevPortalRegisterPage({
|
|
|
379
392
|
id="email"
|
|
380
393
|
type="email"
|
|
381
394
|
autoComplete="email"
|
|
395
|
+
placeholder="you@company.com"
|
|
382
396
|
value={email}
|
|
383
397
|
onChange={(event) => setEmail(event.target.value)}
|
|
384
398
|
required
|
|
399
|
+
className="h-10"
|
|
385
400
|
/>
|
|
386
401
|
</div>
|
|
387
402
|
<div className="space-y-2">
|
|
@@ -390,24 +405,36 @@ export function DevPortalRegisterPage({
|
|
|
390
405
|
id="password"
|
|
391
406
|
type="password"
|
|
392
407
|
autoComplete="new-password"
|
|
408
|
+
placeholder="••••••••"
|
|
393
409
|
value={password}
|
|
394
410
|
onChange={(event) => setPassword(event.target.value)}
|
|
395
411
|
required
|
|
412
|
+
className="h-10"
|
|
396
413
|
/>
|
|
414
|
+
<p className="text-xs text-muted-foreground">At least 8 characters</p>
|
|
397
415
|
</div>
|
|
398
416
|
<AuthError message={error} />
|
|
399
417
|
{message ? (
|
|
400
418
|
<p className="text-sm text-muted-foreground">{message}</p>
|
|
401
419
|
) : null}
|
|
402
|
-
<Button
|
|
420
|
+
<Button
|
|
421
|
+
type="submit"
|
|
422
|
+
size="lg"
|
|
423
|
+
className="h-12 w-full text-base"
|
|
424
|
+
disabled={pending}
|
|
425
|
+
>
|
|
403
426
|
{pending ? <Spinner /> : "Create account"}
|
|
404
427
|
</Button>
|
|
405
|
-
<p className="text-
|
|
428
|
+
<p className="text-xs leading-relaxed text-muted-foreground">
|
|
429
|
+
By creating an account you agree to our Terms and Privacy Policy.
|
|
430
|
+
</p>
|
|
431
|
+
<p className="text-sm text-muted-foreground">
|
|
432
|
+
Already have an account?{" "}
|
|
406
433
|
<Link
|
|
407
|
-
className="text-primary hover:underline"
|
|
434
|
+
className="font-semibold text-primary no-underline hover:underline"
|
|
408
435
|
to={`/login?redirect=${encodeURIComponent(returnUrl)}`}
|
|
409
436
|
>
|
|
410
|
-
|
|
437
|
+
Log in
|
|
411
438
|
</Link>
|
|
412
439
|
</p>
|
|
413
440
|
</form>
|
|
@@ -148,6 +148,7 @@ export const Header = memo(function HeaderInner() {
|
|
|
148
148
|
const authPlacement = header?.placements?.auth ?? "end";
|
|
149
149
|
const authPosition =
|
|
150
150
|
authPlacement === "navigation" ? navPosition : authPlacement;
|
|
151
|
+
const hasMarketingHeaderNav = (header?.navigation?.length ?? 0) > 0;
|
|
151
152
|
|
|
152
153
|
const logoLightSrc = site?.logo
|
|
153
154
|
? /https?:\/\//.test(site.logo.src.light)
|
|
@@ -206,11 +207,13 @@ export const Header = memo(function HeaderInner() {
|
|
|
206
207
|
</div>
|
|
207
208
|
</Link>
|
|
208
209
|
<Slot.Target name="head-navigation-start" />
|
|
209
|
-
|
|
210
|
-
<
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
210
|
+
{!hasMarketingHeaderNav ? (
|
|
211
|
+
<div className="hidden lg:flex min-w-0 items-center ml-2">
|
|
212
|
+
<Slot.Target name="top-navigation-before" />
|
|
213
|
+
<TopNavigation />
|
|
214
|
+
<Slot.Target name="top-navigation-after" />
|
|
215
|
+
</div>
|
|
216
|
+
) : null}
|
|
214
217
|
{searchPosition === "start" && (
|
|
215
218
|
<Search className="hidden lg:flex" />
|
|
216
219
|
)}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Button } from "@lukoweb/apitogo/ui/Button.js";
|
|
2
|
-
import { ArrowRightIcon } from "lucide-react";
|
|
3
2
|
import { useAuth } from "../authentication/hook.js";
|
|
4
3
|
import { cn } from "../util/cn.js";
|
|
5
4
|
|
|
@@ -15,19 +14,18 @@ export const HeaderAuthActions = ({ className }: { className?: string }) => {
|
|
|
15
14
|
<div className={cn("hidden lg:flex items-center gap-2.5", className)}>
|
|
16
15
|
<Button
|
|
17
16
|
variant="ghost"
|
|
18
|
-
size="
|
|
17
|
+
size="sm"
|
|
19
18
|
className="font-semibold text-foreground hover:text-primary px-3"
|
|
20
19
|
onClick={() => void auth.login()}
|
|
21
20
|
>
|
|
22
|
-
|
|
21
|
+
Log in
|
|
23
22
|
</Button>
|
|
24
23
|
<Button
|
|
25
|
-
size="
|
|
26
|
-
className="
|
|
24
|
+
size="sm"
|
|
25
|
+
className="rounded-[9px] px-4 font-semibold shadow-[0_4px_14px_-4px] shadow-primary/30"
|
|
27
26
|
onClick={() => void auth.signup()}
|
|
28
27
|
>
|
|
29
|
-
|
|
30
|
-
<ArrowRightIcon size={15} strokeWidth={2} />
|
|
28
|
+
Get started
|
|
31
29
|
</Button>
|
|
32
30
|
</div>
|
|
33
31
|
);
|
|
@@ -4,7 +4,10 @@ import { Outlet } from "react-router";
|
|
|
4
4
|
import { cn } from "../util/cn.js";
|
|
5
5
|
import { useScrollToAnchor } from "../util/useScrollToAnchor.js";
|
|
6
6
|
import { useScrollToTop } from "../util/useScrollToTop.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
useCurrentNavigation,
|
|
9
|
+
useZudoku,
|
|
10
|
+
} from "./context/ZudokuContext.js";
|
|
8
11
|
import { Footer } from "./Footer.js";
|
|
9
12
|
import { Header } from "./Header.js";
|
|
10
13
|
import { Main } from "./Main.js";
|
|
@@ -19,6 +22,8 @@ const LoadingFallback = () => (
|
|
|
19
22
|
|
|
20
23
|
export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
21
24
|
const { authentication } = useZudoku();
|
|
25
|
+
const { navigation } = useCurrentNavigation();
|
|
26
|
+
const hasSideNav = navigation.length > 0;
|
|
22
27
|
|
|
23
28
|
useScrollToAnchor();
|
|
24
29
|
useScrollToTop();
|
|
@@ -38,9 +43,13 @@ export const Layout = ({ children }: { children?: ReactNode }) => {
|
|
|
38
43
|
|
|
39
44
|
<div
|
|
40
45
|
className={cn(
|
|
41
|
-
"grid
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
"grid w-full",
|
|
47
|
+
// Docs shell: cap width + reserve side-nav column.
|
|
48
|
+
// Marketing pages (no side nav): full-bleed so section backgrounds
|
|
49
|
+
// and compare/FAQ bands can span the viewport.
|
|
50
|
+
hasSideNav
|
|
51
|
+
? "max-w-screen-2xl lg:mx-auto grid-rows-[0_min-content_1fr] lg:grid-rows-[min-content_1fr] grid-cols-1 lg:grid-cols-[var(--side-nav-width)_1fr]"
|
|
52
|
+
: "grid-cols-1 grid-rows-1",
|
|
44
53
|
)}
|
|
45
54
|
>
|
|
46
55
|
<Suspense fallback={<LoadingFallback />}>
|
|
@@ -37,8 +37,9 @@ export const Main = ({ children }: PropsWithChildren) => {
|
|
|
37
37
|
<main
|
|
38
38
|
data-pagefind-body
|
|
39
39
|
className={cn(
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Docs: gutter so content clears the side nav.
|
|
41
|
+
// Marketing: no shell padding — pages own section px / full-bleed bands.
|
|
42
|
+
hasNavigation ? "px-4 lg:pe-8 lg:px-8" : "col-span-full px-0",
|
|
42
43
|
isNavigating && "animate-pulse",
|
|
43
44
|
)}
|
|
44
45
|
>
|
|
@@ -3,7 +3,6 @@ import { Separator } from "@lukoweb/apitogo/ui/Separator.js";
|
|
|
3
3
|
import { VisuallyHidden } from "@radix-ui/react-visually-hidden";
|
|
4
4
|
import { deepEqual } from "fast-equals";
|
|
5
5
|
import {
|
|
6
|
-
ArrowRightIcon,
|
|
7
6
|
ChevronDownIcon,
|
|
8
7
|
LogOutIcon,
|
|
9
8
|
MenuIcon,
|
|
@@ -258,17 +257,15 @@ export const MobileTopNavigation = () => {
|
|
|
258
257
|
void login();
|
|
259
258
|
}}
|
|
260
259
|
>
|
|
261
|
-
|
|
260
|
+
Log in
|
|
262
261
|
</Button>
|
|
263
262
|
<Button
|
|
264
|
-
className="gap-1.5"
|
|
265
263
|
onClick={() => {
|
|
266
264
|
setDrawerOpen(false);
|
|
267
265
|
void signup();
|
|
268
266
|
}}
|
|
269
267
|
>
|
|
270
|
-
|
|
271
|
-
<ArrowRightIcon size={15} strokeWidth={2} />
|
|
268
|
+
Get started
|
|
272
269
|
</Button>
|
|
273
270
|
</div>
|
|
274
271
|
))}
|
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
import { MoonIcon, SunIcon } from "lucide-react";
|
|
1
|
+
import { MoonIcon, MonitorIcon, SunIcon } from "lucide-react";
|
|
2
2
|
import { useTheme } from "next-themes";
|
|
3
3
|
import { focusRing } from "../ui/util.js";
|
|
4
4
|
import { cn } from "../util/cn.js";
|
|
5
5
|
import { useIsClient } from "./ClientOnly.js";
|
|
6
6
|
|
|
7
|
+
const COMPACT_OPTIONS = [
|
|
8
|
+
{ value: "light", label: "Light", Icon: SunIcon },
|
|
9
|
+
{ value: "dark", label: "Dark", Icon: MoonIcon },
|
|
10
|
+
{ value: "system", label: "System", Icon: MonitorIcon },
|
|
11
|
+
] as const;
|
|
12
|
+
|
|
7
13
|
export const ThemeSwitch = ({
|
|
8
14
|
variant = "default",
|
|
9
15
|
}: {
|
|
10
16
|
variant?: "default" | "compact";
|
|
11
17
|
}) => {
|
|
12
|
-
const { resolvedTheme, setTheme } = useTheme();
|
|
18
|
+
const { theme, resolvedTheme, setTheme } = useTheme();
|
|
13
19
|
const isClient = useIsClient();
|
|
14
20
|
|
|
15
|
-
const
|
|
21
|
+
const activeTheme = isClient ? resolvedTheme : undefined;
|
|
22
|
+
const selectedMode = isClient ? (theme ?? "system") : "system";
|
|
16
23
|
|
|
17
24
|
const handleThemeToggle = () => {
|
|
18
25
|
setTheme(resolvedTheme === "dark" ? "light" : "dark");
|
|
@@ -20,27 +27,36 @@ export const ThemeSwitch = ({
|
|
|
20
27
|
|
|
21
28
|
if (variant === "compact") {
|
|
22
29
|
return (
|
|
23
|
-
<
|
|
24
|
-
|
|
30
|
+
<div
|
|
31
|
+
role="group"
|
|
32
|
+
aria-label="Theme"
|
|
25
33
|
className={cn(
|
|
26
|
-
"
|
|
34
|
+
"inline-flex h-8 items-center gap-0.5 rounded-[9px] border border-border bg-muted/40 p-0.5",
|
|
27
35
|
focusRing,
|
|
28
36
|
)}
|
|
29
|
-
onClick={handleThemeToggle}
|
|
30
|
-
aria-label={
|
|
31
|
-
!isClient
|
|
32
|
-
? "Toggle theme"
|
|
33
|
-
: theme === "dark"
|
|
34
|
-
? "Switch to light mode"
|
|
35
|
-
: "Switch to dark mode"
|
|
36
|
-
}
|
|
37
37
|
>
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
{COMPACT_OPTIONS.map(({ value, label, Icon }) => {
|
|
39
|
+
const selected = selectedMode === value;
|
|
40
|
+
return (
|
|
41
|
+
<button
|
|
42
|
+
key={value}
|
|
43
|
+
type="button"
|
|
44
|
+
className={cn(
|
|
45
|
+
"grid size-7 place-items-center rounded-[7px] text-muted-foreground transition-colors",
|
|
46
|
+
selected
|
|
47
|
+
? "bg-card text-foreground shadow-xs"
|
|
48
|
+
: "hover:text-foreground",
|
|
49
|
+
)}
|
|
50
|
+
onClick={() => setTheme(value)}
|
|
51
|
+
aria-label={label}
|
|
52
|
+
aria-pressed={selected}
|
|
53
|
+
title={label}
|
|
54
|
+
>
|
|
55
|
+
<Icon size={14} strokeWidth={1.8} />
|
|
56
|
+
</button>
|
|
57
|
+
);
|
|
58
|
+
})}
|
|
59
|
+
</div>
|
|
44
60
|
);
|
|
45
61
|
}
|
|
46
62
|
|
|
@@ -52,7 +68,7 @@ export const ThemeSwitch = ({
|
|
|
52
68
|
aria-label={
|
|
53
69
|
!isClient
|
|
54
70
|
? "Toggle theme"
|
|
55
|
-
:
|
|
71
|
+
: activeTheme === "dark"
|
|
56
72
|
? "Switch to light mode"
|
|
57
73
|
: "Switch to dark mode"
|
|
58
74
|
}
|
|
@@ -60,8 +76,8 @@ export const ThemeSwitch = ({
|
|
|
60
76
|
<div
|
|
61
77
|
className={cn(
|
|
62
78
|
"rounded-full p-0.5 border border-transparent [&>svg>circle]:transition-colors [&>svg>path]:transition-transform transition-all [&>svg>path]:duration-200 [&>svg>circle]:duration-500 [&>svg>circle]:fill-transparent",
|
|
63
|
-
|
|
64
|
-
|
|
79
|
+
activeTheme === "light" && "border-border bg-muted",
|
|
80
|
+
activeTheme === "dark" &&
|
|
65
81
|
"group-hover:[&>svg>path]:scale-110 group-hover:[&>svg>path]:-translate-x-px group-hover:[&>svg>path]:-translate-y-px group-hover:rotate-15",
|
|
66
82
|
)}
|
|
67
83
|
>
|
|
@@ -70,9 +86,9 @@ export const ThemeSwitch = ({
|
|
|
70
86
|
<div
|
|
71
87
|
className={cn(
|
|
72
88
|
"rounded-full p-0.5 border border-transparent transition-transform transform-gpu duration-500",
|
|
73
|
-
|
|
89
|
+
activeTheme === "dark" &&
|
|
74
90
|
"border-border bg-muted [&>svg>path]:fill-white [&>svg>path]:stroke-transparent",
|
|
75
|
-
|
|
91
|
+
activeTheme === "light" &&
|
|
76
92
|
"group-hover:rotate-[-10deg] [&>svg>path]:stroke-currentColor",
|
|
77
93
|
)}
|
|
78
94
|
>
|