feat: refactor components to utilize SelectField for improved UI consistency
Updated various components to replace traditional select implementations with the new SelectField component. This change enhances the user interface by providing a more consistent layout and improved accessibility. Additionally, refactored the dashboard and operations pages to utilize DataGridCard for better organization of content, streamlining the overall user experience.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"pid": 39884,
|
"pid": 43636,
|
||||||
"version": "0.9.9",
|
"version": "0.9.9",
|
||||||
"socketPath": "\\\\.\\pipe\\codegraph-97b92efdcc5351da",
|
"socketPath": "\\\\.\\pipe\\codegraph-97b92efdcc5351da",
|
||||||
"startedAt": 1783489774882
|
"startedAt": 1783570299043
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,15 +11,9 @@ import {
|
|||||||
} from '@evobgp/ui/components/dialog'
|
} from '@evobgp/ui/components/dialog'
|
||||||
import { Input } from '@evobgp/ui/components/input'
|
import { Input } from '@evobgp/ui/components/input'
|
||||||
import { Label } from '@evobgp/ui/components/label'
|
import { Label } from '@evobgp/ui/components/label'
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@evobgp/ui/components/select'
|
|
||||||
|
|
||||||
import { LoadingButton } from '@/components/loading-button'
|
import { LoadingButton } from '@/components/loading-button'
|
||||||
|
import { SelectField } from '@/components/select-field'
|
||||||
import { API_KEY_ROLE_ITEMS } from '@/lib/access/api-key-labels'
|
import { API_KEY_ROLE_ITEMS } from '@/lib/access/api-key-labels'
|
||||||
import { useCreateApiKeyMutation } from '@/queries/api-keys'
|
import { useCreateApiKeyMutation } from '@/queries/api-keys'
|
||||||
import type { ApiKeyCreate, ApiKeyCreated, ApiKeyRole } from '@/types/api'
|
import type { ApiKeyCreate, ApiKeyCreated, ApiKeyRole } from '@/types/api'
|
||||||
@@ -89,25 +83,14 @@ export function ApiKeyCreateDialog({ open, onOpenChange, onCreated }: ApiKeyCrea
|
|||||||
placeholder="CI / оператор UI"
|
placeholder="CI / оператор UI"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<SelectField
|
||||||
<Label htmlFor="key-role">Роль</Label>
|
id="key-role"
|
||||||
<Select
|
label="Роль"
|
||||||
items={[...API_KEY_ROLE_ITEMS]}
|
items={[...API_KEY_ROLE_ITEMS]}
|
||||||
value={role}
|
value={role}
|
||||||
onValueChange={(v) => v && setRole(v as ApiKeyRole)}
|
placeholder="Выберите роль"
|
||||||
>
|
onValueChange={(v) => v && setRole(v as ApiKeyRole)}
|
||||||
<SelectTrigger id="key-role" className="w-full">
|
/>
|
||||||
<SelectValue placeholder="Выберите роль" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{API_KEY_ROLE_ITEMS.map((opt) => (
|
|
||||||
<SelectItem key={opt.value} value={opt.value}>
|
|
||||||
{opt.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<Label htmlFor="key-expires">Истекает (опционально)</Label>
|
<Label htmlFor="key-expires">Истекает (опционально)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ import { Link } from '@tanstack/react-router'
|
|||||||
import { Gauge, Network, Play, Plus, Share2, Tags } from 'lucide-react'
|
import { Gauge, Network, Play, Plus, Share2, Tags } from 'lucide-react'
|
||||||
|
|
||||||
import { Button } from '@evobgp/ui/components/button'
|
import { Button } from '@evobgp/ui/components/button'
|
||||||
import { FrameFooter } from '@/components/reui/frame'
|
import { CardFooter } from '@evobgp/ui/components/card'
|
||||||
|
|
||||||
export function DashboardQuickActions() {
|
export function DashboardQuickActions() {
|
||||||
return (
|
return (
|
||||||
<FrameFooter className="flex flex-wrap gap-2">
|
<CardFooter className="flex flex-wrap gap-2 border-t-0 bg-transparent">
|
||||||
<Button variant="outline" size="sm" type="button" render={<Link to="/modules" />}>
|
<Button variant="outline" size="sm" type="button" render={<Link to="/modules" />}>
|
||||||
<Plus className="size-4" />
|
<Plus className="size-4" />
|
||||||
Создать модуль
|
Создать модуль
|
||||||
@@ -36,6 +36,6 @@ export function DashboardQuickActions() {
|
|||||||
<Gauge className="size-4" />
|
<Gauge className="size-4" />
|
||||||
Мониторинг
|
Мониторинг
|
||||||
</Button>
|
</Button>
|
||||||
</FrameFooter>
|
</CardFooter>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Field } from '@evobgp/ui/components/field'
|
||||||
|
|
||||||
|
import { SelectMenu } from '@/components/select-field'
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ label: 'Select an item', value: 'placeholder' as const },
|
||||||
|
...Array.from({ length: 100 }).map((_, i) => ({
|
||||||
|
label: `Item ${i}`,
|
||||||
|
value: `item-${i}` as const,
|
||||||
|
})),
|
||||||
|
]
|
||||||
|
|
||||||
|
export function Pattern() {
|
||||||
|
return (
|
||||||
|
<Field className="max-w-xs">
|
||||||
|
<SelectMenu items={items} placeholder="Select an item" />
|
||||||
|
</Field>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,14 +1,8 @@
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import { Label } from '@evobgp/ui/components/label'
|
import { Field, FieldLabel } from '@evobgp/ui/components/field'
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@evobgp/ui/components/select'
|
|
||||||
|
|
||||||
|
import { SelectMenu } from '@/components/select-field'
|
||||||
import {
|
import {
|
||||||
NONE_OPTION,
|
NONE_OPTION,
|
||||||
communityOptionLabel,
|
communityOptionLabel,
|
||||||
@@ -49,28 +43,27 @@ export function CommunitySelect({
|
|||||||
|
|
||||||
const selectValue = nullable ? nullableSelectValue(value) : (value ?? '')
|
const selectValue = nullable ? nullableSelectValue(value) : (value ?? '')
|
||||||
|
|
||||||
|
const select = (
|
||||||
|
<SelectMenu
|
||||||
|
id={id}
|
||||||
|
items={items}
|
||||||
|
value={selectValue}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onValueChange={(v) => {
|
||||||
|
if (!v) return
|
||||||
|
onValueChange(nullable ? fromNullableSelect(v) : v)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!label) {
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-1.5">
|
<Field>
|
||||||
{label ? <Label htmlFor={id}>{label}</Label> : null}
|
<FieldLabel htmlFor={id}>{label}</FieldLabel>
|
||||||
<Select
|
{select}
|
||||||
items={items}
|
</Field>
|
||||||
value={selectValue}
|
|
||||||
onValueChange={(v) => {
|
|
||||||
if (!v) return
|
|
||||||
onValueChange(nullable ? fromNullableSelect(v) : v)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<SelectTrigger id={id} className="w-full">
|
|
||||||
<SelectValue placeholder={placeholder} />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{items.map((item) => (
|
|
||||||
<SelectItem key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,8 @@ import {
|
|||||||
} from '@evobgp/ui/components/dialog'
|
} from '@evobgp/ui/components/dialog'
|
||||||
import { Input } from '@evobgp/ui/components/input'
|
import { Input } from '@evobgp/ui/components/input'
|
||||||
import { Label } from '@evobgp/ui/components/label'
|
import { Label } from '@evobgp/ui/components/label'
|
||||||
import {
|
|
||||||
Select,
|
import { SelectField } from '@/components/select-field'
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@evobgp/ui/components/select'
|
|
||||||
import { Button } from '@evobgp/ui/components/button'
|
import { Button } from '@evobgp/ui/components/button'
|
||||||
|
|
||||||
import { LoadingButton } from '@/components/loading-button'
|
import { LoadingButton } from '@/components/loading-button'
|
||||||
@@ -174,25 +169,13 @@ export function ModuleCdnSourceDialog({
|
|||||||
onChange={(e) => setForm((s) => ({ ...s, url: e.target.value }))}
|
onChange={(e) => setForm((s) => ({ ...s, url: e.target.value }))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1.5">
|
<SelectField
|
||||||
<Label htmlFor="cdn-kind">Тип источника</Label>
|
id="cdn-kind"
|
||||||
<Select
|
label="Тип источника"
|
||||||
items={kindItems}
|
items={kindItems}
|
||||||
value={form.source_kind}
|
value={form.source_kind}
|
||||||
onValueChange={(v) => v && setForm((s) => ({ ...s, source_kind: v }))}
|
onValueChange={(v) => v && setForm((s) => ({ ...s, source_kind: v }))}
|
||||||
>
|
/>
|
||||||
<SelectTrigger id="cdn-kind" className="w-full">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{kindItems.map((item) => (
|
|
||||||
<SelectItem key={item.value} value={item.value}>
|
|
||||||
{item.label}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<Label htmlFor="cdn-prefix-path">JSON path (prefix_path)</Label>
|
<Label htmlFor="cdn-prefix-path">JSON path (prefix_path)</Label>
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@@ -4,12 +4,8 @@ import { useDataGrid } from "@/components/reui/data-grid/data-grid"
|
|||||||
import { cn } from "@evobgp/ui/lib/utils"
|
import { cn } from "@evobgp/ui/lib/utils"
|
||||||
import { Button } from "@evobgp/ui/components/button"
|
import { Button } from "@evobgp/ui/components/button"
|
||||||
import {
|
import {
|
||||||
Select,
|
SelectMenu,
|
||||||
SelectContent,
|
} from "@/components/select-field"
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from "@evobgp/ui/components/select"
|
|
||||||
import { Skeleton } from "@evobgp/ui/components/skeleton"
|
import { Skeleton } from "@evobgp/ui/components/skeleton"
|
||||||
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"
|
||||||
|
|
||||||
@@ -152,28 +148,23 @@ function DataGridPagination(props: DataGridPaginationProps): React.JSX.Element {
|
|||||||
<div className="text-muted-foreground text-sm">
|
<div className="text-muted-foreground text-sm">
|
||||||
{mergedProps.rowsPerPageLabel}
|
{mergedProps.rowsPerPageLabel}
|
||||||
</div>
|
</div>
|
||||||
<Select
|
<SelectMenu
|
||||||
items={mergedProps?.sizes?.map((size: number) => ({
|
items={
|
||||||
value: `${size}`,
|
mergedProps?.sizes?.map((size: number) => ({
|
||||||
label: `${size}`,
|
value: `${size}`,
|
||||||
}))}
|
label: `${size}`,
|
||||||
|
})) ?? []
|
||||||
|
}
|
||||||
value={`${pageSize}`}
|
value={`${pageSize}`}
|
||||||
|
triggerClassName="w-14"
|
||||||
|
size="sm"
|
||||||
|
side="top"
|
||||||
|
contentClassName="min-w-18"
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
const newPageSize = Number(value)
|
if (!value) return
|
||||||
table.setPageSize(newPageSize)
|
table.setPageSize(Number(value))
|
||||||
}}
|
}}
|
||||||
>
|
/>
|
||||||
<SelectTrigger className="w-14" size="sm">
|
|
||||||
<SelectValue />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent side="top" className="min-w-18">
|
|
||||||
{mergedProps?.sizes?.map((size: number) => (
|
|
||||||
<SelectItem key={size} value={`${size}`}>
|
|
||||||
{size}
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
import type { ComponentProps, ReactNode } from 'react'
|
||||||
|
|
||||||
|
import { Field, FieldDescription, FieldLabel } from '@evobgp/ui/components/field'
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from '@evobgp/ui/components/select'
|
||||||
|
import { cn } from '@evobgp/ui/lib/utils'
|
||||||
|
|
||||||
|
export type SelectMenuItem<T extends string = string> = {
|
||||||
|
value: T
|
||||||
|
label: ReactNode
|
||||||
|
disabled?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
type SelectRootProps = ComponentProps<typeof Select>
|
||||||
|
|
||||||
|
export interface SelectMenuProps<T extends string = string>
|
||||||
|
extends Omit<SelectRootProps, 'children' | 'onValueChange' | 'value' | 'defaultValue'> {
|
||||||
|
items: ReadonlyArray<SelectMenuItem<T>>
|
||||||
|
value?: T | null
|
||||||
|
defaultValue?: T | null
|
||||||
|
onValueChange?: (value: T | null) => void
|
||||||
|
placeholder?: string
|
||||||
|
id?: string
|
||||||
|
triggerClassName?: string
|
||||||
|
contentClassName?: string
|
||||||
|
size?: 'sm' | 'default'
|
||||||
|
side?: ComponentProps<typeof SelectContent>['side']
|
||||||
|
}
|
||||||
|
|
||||||
|
/** ReUI c-select-4: `items` на Root, опции в `SelectGroup`. */
|
||||||
|
export function SelectMenu<T extends string = string>({
|
||||||
|
items,
|
||||||
|
placeholder,
|
||||||
|
id,
|
||||||
|
triggerClassName,
|
||||||
|
contentClassName,
|
||||||
|
size = 'default',
|
||||||
|
side,
|
||||||
|
value,
|
||||||
|
defaultValue,
|
||||||
|
onValueChange,
|
||||||
|
...selectProps
|
||||||
|
}: SelectMenuProps<T>) {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
items={items}
|
||||||
|
value={value}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
onValueChange={(next) => onValueChange?.(next as T | null)}
|
||||||
|
{...selectProps}
|
||||||
|
>
|
||||||
|
<SelectTrigger id={id} className={cn('w-full', triggerClassName)} size={size}>
|
||||||
|
<SelectValue placeholder={placeholder} />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent side={side} className={contentClassName}>
|
||||||
|
<SelectGroup>
|
||||||
|
{items.map((item) => (
|
||||||
|
<SelectItem key={item.value} value={item.value} disabled={item.disabled}>
|
||||||
|
{item.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SelectFieldProps<T extends string = string> extends SelectMenuProps<T> {
|
||||||
|
label?: ReactNode
|
||||||
|
description?: ReactNode
|
||||||
|
fieldClassName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectField<T extends string = string>({
|
||||||
|
label,
|
||||||
|
description,
|
||||||
|
fieldClassName,
|
||||||
|
id,
|
||||||
|
...menuProps
|
||||||
|
}: SelectFieldProps<T>) {
|
||||||
|
return (
|
||||||
|
<Field className={fieldClassName}>
|
||||||
|
{label ? <FieldLabel htmlFor={id}>{label}</FieldLabel> : null}
|
||||||
|
<SelectMenu id={id} {...menuProps} />
|
||||||
|
{description ? (
|
||||||
|
typeof description === 'string' ? (
|
||||||
|
<FieldDescription className="font-mono text-xs">{description}</FieldDescription>
|
||||||
|
) : (
|
||||||
|
description
|
||||||
|
)
|
||||||
|
) : null}
|
||||||
|
</Field>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -15,20 +15,21 @@ import { useState } from 'react'
|
|||||||
|
|
||||||
import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert'
|
import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert'
|
||||||
import { Button } from '@evobgp/ui/components/button'
|
import { Button } from '@evobgp/ui/components/button'
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from '@evobgp/ui/components/card'
|
||||||
import { Skeleton } from '@evobgp/ui/components/skeleton'
|
import { Skeleton } from '@evobgp/ui/components/skeleton'
|
||||||
|
|
||||||
|
import { DataGridCard } from '@/components/data-grid-shell'
|
||||||
import { DashboardNetworkPanel } from '@/components/dashboard/dashboard-network-panel'
|
import { DashboardNetworkPanel } from '@/components/dashboard/dashboard-network-panel'
|
||||||
import { DashboardQuickActions } from '@/components/dashboard/dashboard-quick-actions'
|
import { DashboardQuickActions } from '@/components/dashboard/dashboard-quick-actions'
|
||||||
import { DashboardRecentJobsGrid } from '@/components/dashboard/dashboard-recent-jobs-grid'
|
import { DashboardRecentJobsGrid } from '@/components/dashboard/dashboard-recent-jobs-grid'
|
||||||
import { DashboardRecentRevisionsGrid } from '@/components/dashboard/dashboard-recent-revisions-grid'
|
import { DashboardRecentRevisionsGrid } from '@/components/dashboard/dashboard-recent-revisions-grid'
|
||||||
import { PageHeader } from '@/components/page-header'
|
import { PageHeader } from '@/components/page-header'
|
||||||
import {
|
|
||||||
Frame,
|
|
||||||
FrameDescription,
|
|
||||||
FrameHeader,
|
|
||||||
FramePanel,
|
|
||||||
FrameTitle,
|
|
||||||
} from '@/components/reui/frame'
|
|
||||||
import { SectionCards, type SectionCardItem } from '@/components/section-cards'
|
import { SectionCards, type SectionCardItem } from '@/components/section-cards'
|
||||||
import { SectionCardsSkeleton } from '@/components/skeletons'
|
import { SectionCardsSkeleton } from '@/components/skeletons'
|
||||||
|
|
||||||
@@ -172,58 +173,52 @@ function DashboardComponent() {
|
|||||||
{initialLoading ? <SectionCardsSkeleton count={5} /> : <SectionCards items={items} className="gap-3" />}
|
{initialLoading ? <SectionCardsSkeleton count={5} /> : <SectionCards items={items} className="gap-3" />}
|
||||||
|
|
||||||
<div className="grid gap-4 lg:grid-cols-3">
|
<div className="grid gap-4 lg:grid-cols-3">
|
||||||
<Frame spacing="sm" className="h-full">
|
<DataGridCard
|
||||||
<FramePanel className="flex h-full flex-col p-0">
|
title="Недавние задачи"
|
||||||
<FrameHeader className="border-b">
|
description="Последние фоновые операции"
|
||||||
<FrameTitle>Недавние задачи</FrameTitle>
|
className="h-full"
|
||||||
<FrameDescription>Последние фоновые операции</FrameDescription>
|
>
|
||||||
</FrameHeader>
|
{activityLoading ? (
|
||||||
{activityLoading ? (
|
<Skeleton className="m-3 h-24 w-auto" />
|
||||||
<Skeleton className="m-3 h-24 w-auto" />
|
) : (
|
||||||
) : (
|
<DashboardRecentJobsGrid jobs={jobs} nameById={nameById} isLoading={refreshing} />
|
||||||
<DashboardRecentJobsGrid jobs={jobs} nameById={nameById} isLoading={refreshing} />
|
)}
|
||||||
)}
|
</DataGridCard>
|
||||||
</FramePanel>
|
|
||||||
</Frame>
|
|
||||||
|
|
||||||
<Frame spacing="sm" className="h-full">
|
<DataGridCard
|
||||||
<FramePanel className="flex h-full flex-col p-0">
|
title="Последние ревизии"
|
||||||
<FrameHeader className="border-b">
|
description="История конфигураций"
|
||||||
<FrameTitle>Последние ревизии</FrameTitle>
|
className="h-full"
|
||||||
<FrameDescription>История конфигураций</FrameDescription>
|
>
|
||||||
</FrameHeader>
|
{activityLoading ? (
|
||||||
{activityLoading ? (
|
<Skeleton className="m-3 h-24 w-auto" />
|
||||||
<Skeleton className="m-3 h-24 w-auto" />
|
) : (
|
||||||
) : (
|
<DashboardRecentRevisionsGrid revisions={revisions} isLoading={refreshing} />
|
||||||
<DashboardRecentRevisionsGrid revisions={revisions} isLoading={refreshing} />
|
)}
|
||||||
)}
|
</DataGridCard>
|
||||||
</FramePanel>
|
|
||||||
</Frame>
|
|
||||||
|
|
||||||
<Frame spacing="sm" className="h-full">
|
<Card className="h-full gap-0">
|
||||||
<FramePanel className="flex h-full flex-col p-0">
|
<CardHeader className="border-b py-3">
|
||||||
<FrameHeader className="border-b">
|
<CardTitle className="text-base">Состояние сети</CardTitle>
|
||||||
<FrameTitle>Состояние сети</FrameTitle>
|
<CardDescription>BGP-сессии и спикеры</CardDescription>
|
||||||
<FrameDescription>BGP-сессии и спикеры</FrameDescription>
|
</CardHeader>
|
||||||
</FrameHeader>
|
<CardContent className="p-0">
|
||||||
{activityLoading ? (
|
{activityLoading ? (
|
||||||
<Skeleton className="m-3 h-24 w-auto" />
|
<Skeleton className="m-3 h-24 w-auto" />
|
||||||
) : (
|
) : (
|
||||||
<DashboardNetworkPanel peers={peers} speakers={speakers} />
|
<DashboardNetworkPanel peers={peers} speakers={speakers} />
|
||||||
)}
|
)}
|
||||||
</FramePanel>
|
</CardContent>
|
||||||
</Frame>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Frame spacing="sm">
|
<Card className="gap-0">
|
||||||
<FramePanel className="p-0">
|
<CardHeader className="border-b py-3">
|
||||||
<FrameHeader className="border-b">
|
<CardTitle className="text-base">Быстрые действия</CardTitle>
|
||||||
<FrameTitle>Быстрые действия</FrameTitle>
|
<CardDescription>Частые переходы к настройке и деплою</CardDescription>
|
||||||
<FrameDescription>Частые переходы к настройке и деплою</FrameDescription>
|
</CardHeader>
|
||||||
</FrameHeader>
|
<DashboardQuickActions />
|
||||||
<DashboardQuickActions />
|
</Card>
|
||||||
</FramePanel>
|
|
||||||
</Frame>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,10 @@ import { useState, useMemo } from 'react'
|
|||||||
import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert'
|
import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert'
|
||||||
import { Button } from '@evobgp/ui/components/button'
|
import { Button } from '@evobgp/ui/components/button'
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@evobgp/ui/components/card'
|
import { Card, CardContent, CardHeader, CardTitle } from '@evobgp/ui/components/card'
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@evobgp/ui/components/select'
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs'
|
||||||
|
|
||||||
import { DataGridCard } from '@/components/data-grid-shell'
|
import { DataGridCard } from '@/components/data-grid-shell'
|
||||||
|
import { SelectMenu } from '@/components/select-field'
|
||||||
import { OperationsJobsGrid } from '@/components/operations/operations-jobs-grid'
|
import { OperationsJobsGrid } from '@/components/operations/operations-jobs-grid'
|
||||||
import { OperationsRevisionsGrid } from '@/components/operations/operations-revisions-grid'
|
import { OperationsRevisionsGrid } from '@/components/operations/operations-revisions-grid'
|
||||||
import { PageHeader } from '@/components/page-header'
|
import { PageHeader } from '@/components/page-header'
|
||||||
@@ -245,33 +239,21 @@ function DiffTab({ revisions }: { revisions: import('@/types/api').RevisionRow[]
|
|||||||
<div className="flex flex-wrap items-end gap-3">
|
<div className="flex flex-wrap items-end gap-3">
|
||||||
<div className="flex w-full max-w-xs flex-col gap-1">
|
<div className="flex w-full max-w-xs flex-col gap-1">
|
||||||
<span className="text-xs text-muted-foreground">Ревизия A</span>
|
<span className="text-xs text-muted-foreground">Ревизия A</span>
|
||||||
<Select items={revisionItems} value={a} onValueChange={(v) => v && setA(v)}>
|
<SelectMenu
|
||||||
<SelectTrigger>
|
items={revisionItems}
|
||||||
<SelectValue placeholder="Выберите" />
|
value={a}
|
||||||
</SelectTrigger>
|
placeholder="Выберите"
|
||||||
<SelectContent>
|
onValueChange={(v) => v && setA(v)}
|
||||||
{revisions.map((r) => (
|
/>
|
||||||
<SelectItem key={r.id} value={r.id}>
|
|
||||||
{r.id.slice(0, 12)}…
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="flex w-full max-w-xs flex-col gap-1">
|
<div className="flex w-full max-w-xs flex-col gap-1">
|
||||||
<span className="text-xs text-muted-foreground">Ревизия B</span>
|
<span className="text-xs text-muted-foreground">Ревизия B</span>
|
||||||
<Select items={revisionItems} value={b} onValueChange={(v) => v && setB(v)}>
|
<SelectMenu
|
||||||
<SelectTrigger>
|
items={revisionItems}
|
||||||
<SelectValue placeholder="Выберите" />
|
value={b}
|
||||||
</SelectTrigger>
|
placeholder="Выберите"
|
||||||
<SelectContent>
|
onValueChange={(v) => v && setB(v)}
|
||||||
{revisions.map((r) => (
|
/>
|
||||||
<SelectItem key={r.id} value={r.id}>
|
|
||||||
{r.id.slice(0, 12)}…
|
|
||||||
</SelectItem>
|
|
||||||
))}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={() => diffQ.refetch()} disabled={!a || !b || diffQ.isFetching}>
|
<Button onClick={() => diffQ.refetch()} disabled={!a || !b || diffQ.isFetching}>
|
||||||
Сравнить
|
Сравнить
|
||||||
|
|||||||
@@ -6,16 +6,10 @@ import { Button } from '@evobgp/ui/components/button'
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@evobgp/ui/components/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@evobgp/ui/components/card'
|
||||||
import { Input } from '@evobgp/ui/components/input'
|
import { Input } from '@evobgp/ui/components/input'
|
||||||
import { Label } from '@evobgp/ui/components/label'
|
import { Label } from '@evobgp/ui/components/label'
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@evobgp/ui/components/select'
|
|
||||||
|
|
||||||
import { PageHeader } from '@/components/page-header'
|
import { PageHeader } from '@/components/page-header'
|
||||||
import { LoadingButton } from '@/components/loading-button'
|
import { LoadingButton } from '@/components/loading-button'
|
||||||
|
import { SelectField } from '@/components/select-field'
|
||||||
import { DEV_API_TOKEN, normalizeApiToken, setToken, TOKEN_STORAGE_KEY } from '@/lib/api-client'
|
import { DEV_API_TOKEN, normalizeApiToken, setToken, TOKEN_STORAGE_KEY } from '@/lib/api-client'
|
||||||
import { authKeys, authSessionQueryOptions } from '@/queries/auth'
|
import { authKeys, authSessionQueryOptions } from '@/queries/auth'
|
||||||
import { toast } from 'sonner'
|
import { toast } from 'sonner'
|
||||||
@@ -141,21 +135,15 @@ function SettingsComponent() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-col gap-2">
|
<CardContent className="flex flex-col gap-2">
|
||||||
<Label htmlFor="theme-select">Тема</Label>
|
<SelectField
|
||||||
<Select
|
id="theme-select"
|
||||||
|
label="Тема"
|
||||||
items={[...THEME_SELECT_ITEMS]}
|
items={[...THEME_SELECT_ITEMS]}
|
||||||
value={theme ?? 'system'}
|
value={theme ?? 'system'}
|
||||||
|
placeholder="Выберите тему"
|
||||||
|
triggerClassName="max-w-xs"
|
||||||
onValueChange={(v) => v && setTheme(v)}
|
onValueChange={(v) => v && setTheme(v)}
|
||||||
>
|
/>
|
||||||
<SelectTrigger id="theme-select" className="w-full max-w-xs">
|
|
||||||
<SelectValue placeholder="Выберите тему" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="light">Светлая</SelectItem>
|
|
||||||
<SelectItem value="dark">Тёмная</SelectItem>
|
|
||||||
<SelectItem value="system">Как в системе</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,15 +8,10 @@ import { Alert, AlertDescription, AlertTitle } from '@evobgp/ui/components/alert
|
|||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@evobgp/ui/components/card'
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@evobgp/ui/components/card'
|
||||||
import { Input } from '@evobgp/ui/components/input'
|
import { Input } from '@evobgp/ui/components/input'
|
||||||
import { Label } from '@evobgp/ui/components/label'
|
import { Label } from '@evobgp/ui/components/label'
|
||||||
import {
|
|
||||||
Select,
|
|
||||||
SelectContent,
|
|
||||||
SelectItem,
|
|
||||||
SelectTrigger,
|
|
||||||
SelectValue,
|
|
||||||
} from '@evobgp/ui/components/select'
|
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs'
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@evobgp/ui/components/tabs'
|
||||||
|
|
||||||
|
import { SelectField } from '@/components/select-field'
|
||||||
|
|
||||||
|
|
||||||
import { SettingsKvGrid } from '@/components/settings/settings-kv-grid'
|
import { SettingsKvGrid } from '@/components/settings/settings-kv-grid'
|
||||||
import { PageHeader } from '@/components/page-header'
|
import { PageHeader } from '@/components/page-header'
|
||||||
@@ -244,31 +239,20 @@ function TenantSettingsComponent() {
|
|||||||
>
|
>
|
||||||
{() => (
|
{() => (
|
||||||
<div className="grid gap-4 md:grid-cols-2">
|
<div className="grid gap-4 md:grid-cols-2">
|
||||||
<div className="flex flex-col gap-1.5">
|
<SelectField
|
||||||
<Label>Авто-очистка включена</Label>
|
label="Авто-очистка включена"
|
||||||
<Select
|
items={[...RUNTIME_LOGS_ENABLED_ITEMS]}
|
||||||
items={[...RUNTIME_LOGS_ENABLED_ITEMS]}
|
value={runtimeLogsForm.runtime_logs_auto_enabled ?? 'false'}
|
||||||
value={runtimeLogsForm.runtime_logs_auto_enabled ?? 'false'}
|
placeholder="Выберите"
|
||||||
onValueChange={(v) =>
|
description="runtime_logs_auto_enabled"
|
||||||
v &&
|
onValueChange={(v) =>
|
||||||
setRuntimeLogsForm((s) => ({
|
v &&
|
||||||
...s,
|
setRuntimeLogsForm((s) => ({
|
||||||
runtime_logs_auto_enabled: v,
|
...s,
|
||||||
}))
|
runtime_logs_auto_enabled: v,
|
||||||
}
|
}))
|
||||||
>
|
}
|
||||||
<SelectTrigger>
|
/>
|
||||||
<SelectValue placeholder="Выберите" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="true">Вкл</SelectItem>
|
|
||||||
<SelectItem value="false">Выкл</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<p className="font-mono text-xs text-muted-foreground">
|
|
||||||
runtime_logs_auto_enabled
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-1.5">
|
<div className="flex flex-col gap-1.5">
|
||||||
<Label htmlFor="runtime_logs_max_file_mb">Макс. размер файла (MB)</Label>
|
<Label htmlFor="runtime_logs_max_file_mb">Макс. размер файла (MB)</Label>
|
||||||
<Input
|
<Input
|
||||||
@@ -302,31 +286,20 @@ function TenantSettingsComponent() {
|
|||||||
runtime_logs_auto_schedule
|
runtime_logs_auto_schedule
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1.5">
|
<SelectField
|
||||||
<Label>Режим очистки</Label>
|
label="Режим очистки"
|
||||||
<Select
|
items={[...RUNTIME_LOGS_MODE_ITEMS]}
|
||||||
items={[...RUNTIME_LOGS_MODE_ITEMS]}
|
value={runtimeLogsForm.runtime_logs_auto_mode ?? ''}
|
||||||
value={runtimeLogsForm.runtime_logs_auto_mode ?? ''}
|
placeholder="Выберите"
|
||||||
onValueChange={(v) =>
|
description="runtime_logs_auto_mode"
|
||||||
v &&
|
onValueChange={(v) =>
|
||||||
setRuntimeLogsForm((s) => ({
|
v &&
|
||||||
...s,
|
setRuntimeLogsForm((s) => ({
|
||||||
runtime_logs_auto_mode: v,
|
...s,
|
||||||
}))
|
runtime_logs_auto_mode: v,
|
||||||
}
|
}))
|
||||||
>
|
}
|
||||||
<SelectTrigger>
|
/>
|
||||||
<SelectValue placeholder="Выберите" />
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
<SelectItem value="truncate">truncate — обнулить</SelectItem>
|
|
||||||
<SelectItem value="delete">delete — удалить файл</SelectItem>
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
<p className="font-mono text-xs text-muted-foreground">
|
|
||||||
runtime_logs_auto_mode
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="md:col-span-2">
|
<div className="md:col-span-2">
|
||||||
<LoadingButton onClick={saveRuntimeLogs} loading={patchMutation.isPending}>
|
<LoadingButton onClick={saveRuntimeLogs} loading={patchMutation.isPending}>
|
||||||
<Save />
|
<Save />
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/components/confirm-dialog.tsx","./src/components/data-grid-shell.tsx","./src/components/empty-state.tsx","./src/components/loading-button.tsx","./src/components/mode-toggle.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/query-state.tsx","./src/components/section-cards.tsx","./src/components/skeletons.tsx","./src/components/status-badge.tsx","./src/components/theme-provider.tsx","./src/components/truncated-text.tsx","./src/components/access/access-api-keys-card.tsx","./src/components/access/access-api-keys-grid.tsx","./src/components/access/api-key-create-dialog.tsx","./src/components/access/api-key-token-dialog.tsx","./src/components/dashboard/dashboard-network-panel.tsx","./src/components/dashboard/dashboard-quick-actions.tsx","./src/components/dashboard/dashboard-recent-jobs-grid.tsx","./src/components/dashboard/dashboard-recent-revisions-grid.tsx","./src/components/directories/directories-communities-grid.tsx","./src/components/directories/directories-doh-grid.tsx","./src/components/firewall/firewall-clients-grid.tsx","./src/components/firewall/firewall-rules-grid.tsx","./src/components/layout/app-shell.tsx","./src/components/modules/community-select.tsx","./src/components/modules/module-as-entry-dialog.tsx","./src/components/modules/module-cdn-source-dialog.tsx","./src/components/modules/module-domain-entry-dialog.tsx","./src/components/modules/module-entries-grid.tsx","./src/components/modules/module-entries-section.tsx","./src/components/modules/module-ip-range-entry-dialog.tsx","./src/components/modules/module-kpi-cards.tsx","./src/components/modules/modules-list-grid.tsx","./src/components/monitoring/monitoring-ready-grid.tsx","./src/components/network/network-peers-grid.tsx","./src/components/network/network-speakers-grid.tsx","./src/components/operations/operations-jobs-grid.tsx","./src/components/operations/operations-revisions-grid.tsx","./src/components/reui/autocomplete.tsx","./src/components/reui/badge.tsx","./src/components/reui/date-selector.tsx","./src/components/reui/filters.tsx","./src/components/reui/frame.tsx","./src/components/reui/number-field.tsx","./src/components/reui/data-grid/data-grid-column-filter.tsx","./src/components/reui/data-grid/data-grid-column-header.tsx","./src/components/reui/data-grid/data-grid-column-visibility.tsx","./src/components/reui/data-grid/data-grid-pagination.tsx","./src/components/reui/data-grid/data-grid-scroll-area.tsx","./src/components/reui/data-grid/data-grid-table-dnd-rows.tsx","./src/components/reui/data-grid/data-grid-table-dnd.tsx","./src/components/reui/data-grid/data-grid-table-virtual.tsx","./src/components/reui/data-grid/data-grid-table.tsx","./src/components/reui/data-grid/data-grid.tsx","./src/components/schedule/schedule-jobs-grid.tsx","./src/components/schedule/schedule-modules-grid.tsx","./src/components/settings/settings-kv-grid.tsx","./src/lib/api-client.ts","./src/lib/data-grid-defaults.ts","./src/lib/queryclient.ts","./src/lib/router.ts","./src/lib/ui-labels.ts","./src/lib/access/api-key-labels.ts","./src/lib/modules/display.ts","./src/lib/modules/helpers.ts","./src/queries/api-keys.ts","./src/queries/auth.ts","./src/queries/directories.ts","./src/queries/firewall.ts","./src/queries/modules.ts","./src/queries/monitoring.ts","./src/queries/network.ts","./src/queries/operations.ts","./src/queries/overview.ts","./src/queries/settings.ts","./src/routes/__root.tsx","./src/routes/_auth.tsx","./src/routes/index.tsx","./src/routes/_auth/access.tsx","./src/routes/_auth/dashboard.tsx","./src/routes/_auth/directories.tsx","./src/routes/_auth/firewall.tsx","./src/routes/_auth/monitoring.tsx","./src/routes/_auth/network.tsx","./src/routes/_auth/operations.tsx","./src/routes/_auth/schedule.tsx","./src/routes/_auth/settings.tsx","./src/routes/_auth/tenant-settings.tsx","./src/routes/_auth/modules/$moduleid.tsx","./src/routes/_auth/modules/index.tsx","./src/routes/_auth/modules/new.tsx","./src/types/api.ts","./vite.config.ts"],"version":"5.9.3"}
|
{"root":["./src/main.tsx","./src/routetree.gen.ts","./src/components/confirm-dialog.tsx","./src/components/data-grid-shell.tsx","./src/components/empty-state.tsx","./src/components/loading-button.tsx","./src/components/mode-toggle.tsx","./src/components/page-header.tsx","./src/components/page-shell.tsx","./src/components/query-state.tsx","./src/components/section-cards.tsx","./src/components/select-field.tsx","./src/components/skeletons.tsx","./src/components/status-badge.tsx","./src/components/theme-provider.tsx","./src/components/truncated-text.tsx","./src/components/access/access-api-keys-card.tsx","./src/components/access/access-api-keys-grid.tsx","./src/components/access/api-key-create-dialog.tsx","./src/components/access/api-key-token-dialog.tsx","./src/components/dashboard/dashboard-network-panel.tsx","./src/components/dashboard/dashboard-quick-actions.tsx","./src/components/dashboard/dashboard-recent-jobs-grid.tsx","./src/components/dashboard/dashboard-recent-revisions-grid.tsx","./src/components/directories/directories-communities-grid.tsx","./src/components/directories/directories-doh-grid.tsx","./src/components/examples/c-select-4.tsx","./src/components/firewall/firewall-clients-grid.tsx","./src/components/firewall/firewall-rules-grid.tsx","./src/components/layout/app-shell.tsx","./src/components/modules/community-select.tsx","./src/components/modules/module-as-entry-dialog.tsx","./src/components/modules/module-cdn-source-dialog.tsx","./src/components/modules/module-domain-entry-dialog.tsx","./src/components/modules/module-entries-grid.tsx","./src/components/modules/module-entries-section.tsx","./src/components/modules/module-ip-range-entry-dialog.tsx","./src/components/modules/module-kpi-cards.tsx","./src/components/modules/modules-list-grid.tsx","./src/components/monitoring/monitoring-ready-grid.tsx","./src/components/network/network-peers-grid.tsx","./src/components/network/network-speakers-grid.tsx","./src/components/operations/operations-jobs-grid.tsx","./src/components/operations/operations-revisions-grid.tsx","./src/components/reui/autocomplete.tsx","./src/components/reui/badge.tsx","./src/components/reui/date-selector.tsx","./src/components/reui/filters.tsx","./src/components/reui/frame.tsx","./src/components/reui/number-field.tsx","./src/components/reui/data-grid/data-grid-column-filter.tsx","./src/components/reui/data-grid/data-grid-column-header.tsx","./src/components/reui/data-grid/data-grid-column-visibility.tsx","./src/components/reui/data-grid/data-grid-pagination.tsx","./src/components/reui/data-grid/data-grid-scroll-area.tsx","./src/components/reui/data-grid/data-grid-table-dnd-rows.tsx","./src/components/reui/data-grid/data-grid-table-dnd.tsx","./src/components/reui/data-grid/data-grid-table-virtual.tsx","./src/components/reui/data-grid/data-grid-table.tsx","./src/components/reui/data-grid/data-grid.tsx","./src/components/schedule/schedule-jobs-grid.tsx","./src/components/schedule/schedule-modules-grid.tsx","./src/components/settings/settings-kv-grid.tsx","./src/lib/api-client.ts","./src/lib/data-grid-defaults.ts","./src/lib/queryclient.ts","./src/lib/router.ts","./src/lib/ui-labels.ts","./src/lib/access/api-key-labels.ts","./src/lib/modules/display.ts","./src/lib/modules/helpers.ts","./src/queries/api-keys.ts","./src/queries/auth.ts","./src/queries/directories.ts","./src/queries/firewall.ts","./src/queries/modules.ts","./src/queries/monitoring.ts","./src/queries/network.ts","./src/queries/operations.ts","./src/queries/overview.ts","./src/queries/settings.ts","./src/routes/__root.tsx","./src/routes/_auth.tsx","./src/routes/index.tsx","./src/routes/_auth/access.tsx","./src/routes/_auth/dashboard.tsx","./src/routes/_auth/directories.tsx","./src/routes/_auth/firewall.tsx","./src/routes/_auth/monitoring.tsx","./src/routes/_auth/network.tsx","./src/routes/_auth/operations.tsx","./src/routes/_auth/schedule.tsx","./src/routes/_auth/settings.tsx","./src/routes/_auth/tenant-settings.tsx","./src/routes/_auth/modules/$moduleid.tsx","./src/routes/_auth/modules/index.tsx","./src/routes/_auth/modules/new.tsx","./src/types/api.ts","./vite.config.ts"],"version":"5.9.3"}
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Select as SelectPrimitive } from "@base-ui/react/select"
|
import { Select as SelectPrimitive } from "@base-ui/react/select"
|
||||||
|
|
||||||
@@ -111,15 +109,11 @@ function SelectLabel({
|
|||||||
function SelectItem({
|
function SelectItem({
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
label,
|
|
||||||
...props
|
...props
|
||||||
}: SelectPrimitive.Item.Props) {
|
}: SelectPrimitive.Item.Props) {
|
||||||
const resolvedLabel = label ?? (typeof children === "string" ? children : undefined)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SelectPrimitive.Item
|
<SelectPrimitive.Item
|
||||||
data-slot="select-item"
|
data-slot="select-item"
|
||||||
label={resolvedLabel}
|
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
"relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
|
||||||
className
|
className
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
|
import { Separator as SeparatorPrimitive } from "@base-ui/react/separator"
|
||||||
|
|
||||||
import { cn } from "@evobgp/ui/lib/utils"
|
import { cn } from "@evobgp/ui/lib/utils"
|
||||||
|
|||||||
Reference in New Issue
Block a user