mirror of
https://github.com/moghtech/komodo.git
synced 2026-09-10 16:01:20 +00:00
choose between config / log for deployment
This commit is contained in:
@@ -24,6 +24,8 @@
|
||||
"@radix-ui/react-switch": "1.0.3",
|
||||
"@radix-ui/react-tabs": "1.0.4",
|
||||
"@radix-ui/react-toast": "1.1.5",
|
||||
"@radix-ui/react-toggle": "^1.0.3",
|
||||
"@radix-ui/react-toggle-group": "^1.0.4",
|
||||
"@tanstack/react-query": "5.35.1",
|
||||
"@tanstack/react-table": "8.16.0",
|
||||
"ansi-to-html": "0.7.2",
|
||||
|
||||
@@ -30,6 +30,7 @@ export const ConfigLayout = <
|
||||
onConfirm,
|
||||
onReset,
|
||||
selector,
|
||||
titleOther,
|
||||
}: {
|
||||
config: Partial<T>;
|
||||
children: ReactNode;
|
||||
@@ -37,33 +38,38 @@ export const ConfigLayout = <
|
||||
onConfirm: () => void;
|
||||
onReset: () => void;
|
||||
selector?: ReactNode;
|
||||
}) => (
|
||||
<Section
|
||||
title="Config"
|
||||
icon={<Settings className="w-4 h-4" />}
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
{selector}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onReset}
|
||||
disabled={disabled || (config ? !Object.keys(config).length : true)}
|
||||
>
|
||||
<History className="w-4 h-4" />
|
||||
</Button>
|
||||
{Object.keys(config).length ? (
|
||||
<ConfirmUpdate
|
||||
content={JSON.stringify(config, null, 2)}
|
||||
onConfirm={onConfirm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Section>
|
||||
);
|
||||
titleOther?: ReactNode;
|
||||
}) => {
|
||||
const titleProps = titleOther
|
||||
? { titleOther }
|
||||
: { title: "Config", icon: <Settings className="w-4 h-4" /> };
|
||||
return (
|
||||
<Section
|
||||
{...titleProps}
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
{selector}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={onReset}
|
||||
disabled={disabled || (config ? !Object.keys(config).length : true)}
|
||||
>
|
||||
<History className="w-4 h-4" />
|
||||
</Button>
|
||||
{Object.keys(config).length ? (
|
||||
<ConfirmUpdate
|
||||
content={JSON.stringify(config, null, 2)}
|
||||
onConfirm={onConfirm}
|
||||
disabled={disabled}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
export const Config = <T,>({
|
||||
config,
|
||||
@@ -73,6 +79,7 @@ export const Config = <T,>({
|
||||
onSave,
|
||||
components,
|
||||
selector,
|
||||
titleOther,
|
||||
}: {
|
||||
config: T;
|
||||
update: Partial<T>;
|
||||
@@ -80,6 +87,7 @@ export const Config = <T,>({
|
||||
set: React.Dispatch<SetStateAction<Partial<T>>>;
|
||||
onSave: () => Promise<void>;
|
||||
selector?: ReactNode;
|
||||
titleOther?: ReactNode;
|
||||
components: Record<
|
||||
string,
|
||||
Record<
|
||||
@@ -96,6 +104,7 @@ export const Config = <T,>({
|
||||
|
||||
return (
|
||||
<ConfigLayout
|
||||
titleOther={titleOther}
|
||||
config={update}
|
||||
disabled={disabled}
|
||||
onConfirm={async () => {
|
||||
|
||||
@@ -37,6 +37,7 @@ export const Layout = () => {
|
||||
interface PageProps {
|
||||
title?: ReactNode;
|
||||
titleRight?: ReactNode;
|
||||
titleOther?: ReactNode;
|
||||
children?: ReactNode;
|
||||
subtitle?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
@@ -45,6 +46,7 @@ interface PageProps {
|
||||
export const Page = ({
|
||||
title,
|
||||
titleRight,
|
||||
titleOther,
|
||||
subtitle,
|
||||
actions,
|
||||
children,
|
||||
@@ -62,26 +64,28 @@ export const Page = ({
|
||||
{actions}
|
||||
</div>
|
||||
)}
|
||||
{titleOther}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
interface SectionProps {
|
||||
title?: ReactNode;
|
||||
children?: ReactNode;
|
||||
icon?: ReactNode;
|
||||
titleOther?: ReactNode;
|
||||
children?: ReactNode;
|
||||
actions?: ReactNode;
|
||||
}
|
||||
|
||||
export const Section = ({ title, icon, actions, children }: SectionProps) => (
|
||||
export const Section = ({ title, icon, titleOther, actions, children }: SectionProps) => (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex items-start justify-between">
|
||||
{(title || icon) && (
|
||||
{(title || icon) ? (
|
||||
<div className="flex items-center gap-2 text-muted-foreground">
|
||||
{icon}
|
||||
<h2 className="text-xl">{title}</h2>
|
||||
{title && <h2 className="text-xl">{title}</h2>}
|
||||
</div>
|
||||
)}
|
||||
) : titleOther}
|
||||
{actions}
|
||||
</div>
|
||||
{children}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useRead, useWrite } from "@lib/hooks";
|
||||
import { Types } from "@monitor/client";
|
||||
import { useState } from "react";
|
||||
import { ReactNode, useState } from "react";
|
||||
import { AccountSelector, ConfigItem } from "@components/config/util";
|
||||
import { ImageConfig } from "./components/image";
|
||||
import { RestartModeSelector } from "./components/restart";
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
import { LabelsConfig, ServerSelector } from "@components/resources/common";
|
||||
import { TextUpdateMenu } from "@components/util";
|
||||
|
||||
export const DeploymentConfig = ({ id }: { id: string }) => {
|
||||
export const DeploymentConfig = ({ id, titleOther }: { id: string; titleOther: ReactNode }) => {
|
||||
const perms = useRead("GetPermissionLevel", {
|
||||
target: { type: "Deployment", id },
|
||||
}).data;
|
||||
@@ -38,6 +38,7 @@ export const DeploymentConfig = ({ id }: { id: string }) => {
|
||||
|
||||
return (
|
||||
<Config
|
||||
titleOther={titleOther}
|
||||
disabled={disabled}
|
||||
config={config}
|
||||
update={update}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { RequiredResourceComponents } from "@types";
|
||||
import { HardDrive, Rocket } from "lucide-react";
|
||||
import { cn } from "@lib/utils";
|
||||
import { useServer } from "../server";
|
||||
import { DeploymentConfig } from "./config";
|
||||
import {
|
||||
DeployContainer,
|
||||
StartOrStopContainer,
|
||||
@@ -24,12 +23,73 @@ import { DeploymentsChart } from "./dashboard";
|
||||
import { NewResource, ResourceLink } from "../common";
|
||||
import { Card, CardHeader } from "@ui/card";
|
||||
import { RunBuild } from "../build/actions";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@ui/tabs";
|
||||
import { DeploymentConfig } from "./config";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { useAtom } from "jotai";
|
||||
|
||||
const configOrLog = atomWithStorage("config-or-log-v1", "Config");
|
||||
|
||||
export const useDeployment = (id?: string) =>
|
||||
useRead("ListDeployments", {}, { refetchInterval: 5000 }).data?.find(
|
||||
(d) => d.id === id
|
||||
);
|
||||
|
||||
const ConfigOrLog = ({ id }: { id: string }) => {
|
||||
const [view, setView] = useAtom(configOrLog);
|
||||
const state = useDeployment(id)?.info.state;
|
||||
const logsDisabled =
|
||||
state === undefined ||
|
||||
state === Types.DockerContainerState.Unknown ||
|
||||
state === Types.DockerContainerState.NotDeployed;
|
||||
return (
|
||||
<Tabs
|
||||
value={logsDisabled ? "Config" : view}
|
||||
onValueChange={setView}
|
||||
className="grid gap-4"
|
||||
>
|
||||
<TabsContent value="Config">
|
||||
<DeploymentConfig
|
||||
id={id}
|
||||
titleOther={
|
||||
<TabsList className="justify-start w-fit">
|
||||
<TabsTrigger value="Config" className="w-[70px]">
|
||||
Config
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="Log"
|
||||
className="w-[70px]"
|
||||
disabled={logsDisabled}
|
||||
>
|
||||
Log
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="Log">
|
||||
<DeploymentLogs
|
||||
id={id}
|
||||
titleOther={
|
||||
<TabsList className="justify-start w-fit">
|
||||
<TabsTrigger value="Config" className="w-[70px]">
|
||||
Config
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="Log"
|
||||
className="w-[70px]"
|
||||
disabled={logsDisabled}
|
||||
>
|
||||
Log
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
export const DeploymentComponents: RequiredResourceComponents = {
|
||||
Dashboard: DeploymentsChart,
|
||||
|
||||
@@ -106,9 +166,9 @@ export const DeploymentComponents: RequiredResourceComponents = {
|
||||
RemoveContainer,
|
||||
},
|
||||
|
||||
Page: { DeploymentLogs },
|
||||
Page: {},
|
||||
|
||||
Config: DeploymentConfig,
|
||||
Config: ConfigOrLog,
|
||||
|
||||
DangerZone: ({ id }) => (
|
||||
<>
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { Section } from "@components/layouts";
|
||||
import { useRead } from "@lib/hooks";
|
||||
import { Types } from "@monitor/client";
|
||||
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@ui/tabs";
|
||||
import { Button } from "@ui/button";
|
||||
import {
|
||||
TerminalSquare,
|
||||
AlertOctagon,
|
||||
RefreshCw,
|
||||
ChevronDown,
|
||||
X,
|
||||
AlertOctagon,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { useDeployment } from ".";
|
||||
import {
|
||||
Select,
|
||||
@@ -23,8 +21,9 @@ import {
|
||||
import { Input } from "@ui/input";
|
||||
import { useToast } from "@ui/use-toast";
|
||||
import { logToHtml } from "@lib/utils";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@ui/toggle-group";
|
||||
|
||||
export const DeploymentLogs = ({ id }: { id: string }) => {
|
||||
export const DeploymentLogs = ({ id, titleOther }: { id: string; titleOther: ReactNode }) => {
|
||||
const state = useDeployment(id)?.info.state;
|
||||
if (
|
||||
state === undefined ||
|
||||
@@ -33,11 +32,12 @@ export const DeploymentLogs = ({ id }: { id: string }) => {
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
return <DeploymentLogsInner id={id} />;
|
||||
return <DeploymentLogsInner id={id} titleOther={titleOther} />;
|
||||
};
|
||||
|
||||
const DeploymentLogsInner = ({ id }: { id: string }) => {
|
||||
const DeploymentLogsInner = ({ id, titleOther }: { id: string; titleOther: ReactNode }) => {
|
||||
const { toast } = useToast();
|
||||
const [stream, setStream] = useState("stdout");
|
||||
const [tail, set] = useState("100");
|
||||
const [terms, setTerms] = useState<string[]>([]);
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -60,88 +60,77 @@ const DeploymentLogsInner = ({ id }: { id: string }) => {
|
||||
|
||||
const { Log, refetch, stderr } = terms.length
|
||||
? SearchLogs(id, terms)
|
||||
: NoSearchLogs(id, tail);
|
||||
: NoSearchLogs(id, tail, stream);
|
||||
|
||||
return (
|
||||
<Tabs defaultValue="stdout">
|
||||
<Section
|
||||
title="Logs"
|
||||
icon={<TerminalSquare className="w-4 h-4" />}
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
{terms.map((term, index) => (
|
||||
<Button
|
||||
key={term}
|
||||
variant="destructive"
|
||||
onClick={() => setTerms(terms.filter((_, i) => i !== index))}
|
||||
className="flex gap-2 items-center py-0 px-2"
|
||||
>
|
||||
{term}
|
||||
<X className="w-4 h-h" />
|
||||
</Button>
|
||||
))}
|
||||
<div className="relative">
|
||||
<Input
|
||||
placeholder="Search Logs"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
onBlur={addTerm}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") addTerm();
|
||||
}}
|
||||
className="w-[300px]"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={clearSearch}
|
||||
className="absolute right-0 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<TabsList>
|
||||
<TabsTrigger value="stdout">stdout</TabsTrigger>
|
||||
<TabsTrigger value="stderr">
|
||||
stderr
|
||||
{stderr && (
|
||||
<AlertOctagon className="w-4 h-4 ml-2 stroke-red-500" />
|
||||
)}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<Button variant="secondary" size="icon" onClick={() => refetch()}>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
<Section
|
||||
titleOther={titleOther}
|
||||
actions={
|
||||
<div className="flex gap-2">
|
||||
{terms.map((term, index) => (
|
||||
<Button
|
||||
key={term}
|
||||
variant="destructive"
|
||||
onClick={() => setTerms(terms.filter((_, i) => i !== index))}
|
||||
className="flex gap-2 items-center py-0 px-2"
|
||||
>
|
||||
{term}
|
||||
<X className="w-4 h-h" />
|
||||
</Button>
|
||||
<TailLengthSelector
|
||||
selected={tail}
|
||||
onSelect={set}
|
||||
disabled={search.length > 0}
|
||||
))}
|
||||
<div className="relative">
|
||||
<Input
|
||||
placeholder="Search Logs"
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
onBlur={addTerm}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") addTerm();
|
||||
}}
|
||||
className="w-[300px]"
|
||||
/>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={clearSearch}
|
||||
className="absolute right-0 top-1/2 -translate-y-1/2"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Log}
|
||||
</Section>
|
||||
</Tabs>
|
||||
<ToggleGroup type="single" value={stream} onValueChange={setStream}>
|
||||
<ToggleGroupItem value="stdout">stdout</ToggleGroupItem>
|
||||
<ToggleGroupItem value="stderr">
|
||||
stderr
|
||||
{stderr && (
|
||||
<AlertOctagon className="w-4 h-4 ml-2 stroke-red-500" />
|
||||
)}
|
||||
</ToggleGroupItem>
|
||||
</ToggleGroup>
|
||||
<Button variant="secondary" size="icon" onClick={() => refetch()}>
|
||||
<RefreshCw className="w-4 h-4" />
|
||||
</Button>
|
||||
<TailLengthSelector
|
||||
selected={tail}
|
||||
onSelect={set}
|
||||
disabled={search.length > 0}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{Log}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const NoSearchLogs = (id: string, tail: string) => {
|
||||
const NoSearchLogs = (id: string, tail: string, stream: string) => {
|
||||
const { data: log, refetch } = useRead(
|
||||
"GetLog",
|
||||
{ deployment: id, tail: Number(tail) },
|
||||
{ refetchInterval: 30000 }
|
||||
);
|
||||
return {
|
||||
Log: (
|
||||
<>
|
||||
{["stdout", "stderr"].map((stream) => (
|
||||
<TabsContent key={stream} className="h-full relative" value={stream}>
|
||||
<Log log={log} stream={stream as "stdout" | "stderr"} />
|
||||
</TabsContent>
|
||||
))}
|
||||
</>
|
||||
),
|
||||
Log: <Log log={log} stream={stream as "stdout" | "stderr"} />,
|
||||
refetch,
|
||||
stderr: !!log?.stderr,
|
||||
};
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
--secondary: 220.1 40.1% 90.2%;
|
||||
--secondary-foreground: 220.1 40.1% 5.2%;
|
||||
|
||||
--muted: 220.1 40.1% 98.2%;
|
||||
--muted-foreground: 220.1 20.1% 40.2%;
|
||||
--muted: 220.1 40.1% 90.2%;
|
||||
--muted-foreground: 220.1 20.1% 20.2%;
|
||||
|
||||
--accent: 220.1 40.1% 90.2%;
|
||||
--accent-foreground: 220.1 40.1% 11.2%;
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import * as React from "react"
|
||||
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
|
||||
import { VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@lib/utils"
|
||||
import { toggleVariants } from "@//ui/toggle"
|
||||
|
||||
const ToggleGroupContext = React.createContext<
|
||||
VariantProps<typeof toggleVariants>
|
||||
>({
|
||||
size: "default",
|
||||
variant: "default",
|
||||
})
|
||||
|
||||
const ToggleGroup = React.forwardRef<
|
||||
React.ElementRef<typeof ToggleGroupPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, variant, size, children, ...props }, ref) => (
|
||||
<ToggleGroupPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn("flex items-center justify-center gap-1", className)}
|
||||
{...props}
|
||||
>
|
||||
<ToggleGroupContext.Provider value={{ variant, size }}>
|
||||
{children}
|
||||
</ToggleGroupContext.Provider>
|
||||
</ToggleGroupPrimitive.Root>
|
||||
))
|
||||
|
||||
ToggleGroup.displayName = ToggleGroupPrimitive.Root.displayName
|
||||
|
||||
const ToggleGroupItem = React.forwardRef<
|
||||
React.ElementRef<typeof ToggleGroupPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof ToggleGroupPrimitive.Item> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, children, variant, size, ...props }, ref) => {
|
||||
const context = React.useContext(ToggleGroupContext)
|
||||
|
||||
return (
|
||||
<ToggleGroupPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
toggleVariants({
|
||||
variant: context.variant || variant,
|
||||
size: context.size || size,
|
||||
}),
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</ToggleGroupPrimitive.Item>
|
||||
)
|
||||
})
|
||||
|
||||
ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName
|
||||
|
||||
export { ToggleGroup, ToggleGroupItem }
|
||||
@@ -0,0 +1,43 @@
|
||||
import * as React from "react"
|
||||
import * as TogglePrimitive from "@radix-ui/react-toggle"
|
||||
import { cva, type VariantProps } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@lib/utils"
|
||||
|
||||
const toggleVariants = cva(
|
||||
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors hover:bg-muted hover:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 data-[state=on]:bg-accent data-[state=on]:text-accent-foreground",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-transparent",
|
||||
outline:
|
||||
"border border-input bg-transparent shadow-sm hover:bg-accent hover:text-accent-foreground",
|
||||
},
|
||||
size: {
|
||||
default: "h-9 px-3",
|
||||
sm: "h-8 px-2",
|
||||
lg: "h-10 px-3",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
size: "default",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const Toggle = React.forwardRef<
|
||||
React.ElementRef<typeof TogglePrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof TogglePrimitive.Root> &
|
||||
VariantProps<typeof toggleVariants>
|
||||
>(({ className, variant, size, ...props }, ref) => (
|
||||
<TogglePrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(toggleVariants({ variant, size, className }))}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
Toggle.displayName = TogglePrimitive.Root.displayName
|
||||
|
||||
export { Toggle, toggleVariants }
|
||||
@@ -889,6 +889,30 @@
|
||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
||||
"@radix-ui/react-visually-hidden" "1.0.3"
|
||||
|
||||
"@radix-ui/react-toggle-group@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz#f5b5c8c477831b013bec3580c55e20a68179d6ec"
|
||||
integrity sha512-Uaj/M/cMyiyT9Bx6fOZO0SAG4Cls0GptBWiBmBxofmDbNVnYYoyRWj/2M/6VCi/7qcXFWnHhRUfdfZFvvkuu8A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.1"
|
||||
"@radix-ui/react-context" "1.0.1"
|
||||
"@radix-ui/react-direction" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-roving-focus" "1.0.4"
|
||||
"@radix-ui/react-toggle" "1.0.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
|
||||
"@radix-ui/react-toggle@1.0.3", "@radix-ui/react-toggle@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle/-/react-toggle-1.0.3.tgz#aecb2945630d1dc5c512997556c57aba894e539e"
|
||||
integrity sha512-Pkqg3+Bc98ftZGsl60CLANXQBBQ4W3mTFS9EJvNxKMZ7magklKV69/id1mlAlOFDDfHvlCms0fx8fA4CMKDJHg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.13.10"
|
||||
"@radix-ui/primitive" "1.0.1"
|
||||
"@radix-ui/react-primitive" "1.0.3"
|
||||
"@radix-ui/react-use-controllable-state" "1.0.1"
|
||||
|
||||
"@radix-ui/react-use-callback-ref@1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.0.1.tgz#f4bb1f27f2023c984e6534317ebc411fc181107a"
|
||||
|
||||
Reference in New Issue
Block a user