refactor(ui): install the shadcn field primitive (#38126)

* refactor(ui): install the shadcn field primitive

`components/shared/form/field.tsx` was the upstream base-vega `field` source
living outside `components/ui/`. It exported the same ten symbols as upstream,
so `npx shadcn add` could never update it and it had already drifted: its
`FieldLabel` was missing the hover and focus-visible ring utilities upstream
now ships for labels that wrap a nested field.

Install the primitive and point the 77 importers at it. The copy is deleted
rather than kept as a wrapper because it added nothing beyond `forwardRef`,
which React 19 makes unnecessary since `ref` arrives as an ordinary prop.

`field.test.tsx` moves next to the primitive with no edits to its contents,
and its nineteen tests, ref assertions included, pass against the generated
file. That is the evidence the swap is behaviour-preserving.

Two nested-field call sites pick up the upstream hover and focus-visible
styling that the stale copy had been missing.

(cherry picked from commit 947f7fa674c83bfc57f43ad8bfc89c894da947a2)

* test(ui): cover the nested-field interaction cues FieldLabel had lost

The stale copy of `field` was missing the hover, focus-visible and disabled
selectors upstream applies to a label that wraps a nested field, so installing
the primitive restored them with nothing asserting they stay.

Assert the class contract rather than the rendered effect. jsdom evaluates
neither `:has()` nor `:focus-visible`, and Tailwind is not compiled under
vitest, so a test that clicked or tabbed would pass on an element with no
styling at all. Checking the utilities are present is the assertion that
actually fails when they go missing, which is the way they were lost before.

Verified by stripping the four selectors from the primitive: both tests fail,
and both pass once it is restored.

(cherry picked from commit 5a5dbf64270d9d1285dbc4a7af76bb3d927778a8)
This commit is contained in:
yuneng-jiang 2026-08-24 14:30:32 -07:00 committed by GitHub
parent 0f596a5145
commit 6147b3ce6e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
81 changed files with 199 additions and 169 deletions

View file

@ -2018,11 +2018,6 @@
"count": 1
}
},
"src/components/shared/form/field.tsx": {
"local/filename-pascal-case": {
"count": 1
}
},
"src/components/shared/numerical_input.tsx": {
"local/filename-pascal-case": {
"count": 1
@ -2198,6 +2193,11 @@
"count": 1
}
},
"src/components/ui/field.tsx": {
"local/filename-pascal-case": {
"count": 1
}
},
"src/components/ui/hover-card.tsx": {
"local/filename-pascal-case": {
"count": 1

View file

@ -7,7 +7,7 @@ import { z } from "zod/v4";
import { useAgents } from "@/app/(dashboard)/hooks/agents/useAgents";
import { useMCPServers } from "@/app/(dashboard)/hooks/mcpServers/useMCPServers";
import { ModelSelect } from "@/components/ModelSelect/ModelSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

View file

@ -9,7 +9,7 @@ import { useAgents } from "@/app/(dashboard)/hooks/agents/useAgents";
import { useMCPServers } from "@/app/(dashboard)/hooks/mcpServers/useMCPServers";
import { ModelSelect } from "@/components/ModelSelect/ModelSelect";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";

View file

@ -29,7 +29,7 @@ import {
} from "@/components/Settings/AdminSettings/SSOSettings/Modals/BaseSSOSettingsForm";
import UIAccessControlForm from "@/components/UIAccessControlForm";
import { z } from "zod/v4";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { useZodForm } from "@/lib/forms/useZodForm";

View file

@ -26,7 +26,7 @@ import {
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Input } from "@/components/ui/input";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldError, FieldGroup, FieldLabel } from "@/components/ui/field";
export interface AgentSkillFormValue {
id?: string;

View file

@ -15,7 +15,7 @@ import { Separator } from "@/components/ui/separator";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { SearchSelect } from "@/components/shared/SearchSelect";
import {
createAgentCall,

View file

@ -6,7 +6,7 @@ import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { Field, FieldGroup, FieldTitle } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldTitle } from "@/components/ui/field";
import { AGENT_FORM_CONFIG, SKILL_FIELD_CONFIG } from "./agent_config";
import CostConfigFields, { COST_FIELD_NAMES } from "./cost_config_fields";
import {

View file

@ -8,7 +8,7 @@ import { Separator } from "@/components/ui/separator";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { TooltipProvider } from "@/components/ui/tooltip";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { toast } from "@/lib/toast";
import { ArrowLeft } from "lucide-react";
import { getAgentInfo, patchAgentCall, getAgentCreateMetadata, AgentCreateInfo } from "@/components/networking";

View file

@ -2,7 +2,7 @@ import React from "react";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { AgentCreateInfo, AgentCredentialFieldMetadata } from "@/components/networking";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { AGENT_FORM_CONFIG } from "./agent_config";

View file

@ -4,7 +4,7 @@ import { z } from "zod/v4";
import { useCreateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
import { applyBudgetPrecision } from "./budgetPrecision";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";

View file

@ -5,7 +5,7 @@ import { useUpdateBudget } from "@/app/(dashboard)/hooks/budgets/useBudgets";
import { budgetItem } from "@/app/(dashboard)/hooks/budgets/useBudgets";
import { applyBudgetPrecision } from "./budgetPrecision";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";

View file

@ -6,7 +6,7 @@ import { z } from "zod/v4";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { createGuardrailCall, getGuardrailsList } from "@/components/networking";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -2,7 +2,7 @@ import React from "react";
import { CircleHelp } from "lucide-react";
import { Providers, provider_map } from "@/components/provider_info_helpers";
import { Logo } from "@/components/molecules/logo/Logo";
import { Field, FieldLabel, FieldTitle } from "@/components/shared/form/field";
import { Field, FieldLabel, FieldTitle } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import {
Combobox,

View file

@ -3,7 +3,7 @@ import { CircleHelp } from "lucide-react";
import { Logo } from "@/components/molecules/logo/Logo";
import { Providers, provider_map } from "@/components/provider_info_helpers";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import {
Combobox,

View file

@ -3,7 +3,7 @@
import { CircleHelp } from "lucide-react";
import React, { useId } from "react";
import { useController, type Control, type ControllerRenderProps, type RegisterOptions } from "react-hook-form";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";

View file

@ -30,7 +30,7 @@ import TeamDropdown from "@/components/common_components/team_dropdown";
import { useRegisterGuardrail } from "@/app/(dashboard)/hooks/guardrails/useRegisterGuardrail";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { isProxyAdminRole } from "@/utils/roles";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

View file

@ -25,7 +25,7 @@ import {
} from "./guardrail_info_helpers";
import { Logo } from "@/components/molecules/logo/Logo";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import {
Combobox,

View file

@ -1,7 +1,7 @@
import React, { useEffect, useId, useState } from "react";
import { getMajorAirlines } from "@/components/networking";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Switch } from "@/components/ui/switch";

View file

@ -14,7 +14,7 @@ import React, { useCallback, useEffect, useLayoutEffect, useState } from "react"
import { useForm } from "react-hook-form";
import { toast } from "@/lib/toast";
import { Logo } from "@/components/molecules/logo/Logo";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

View file

@ -9,7 +9,7 @@ import { getGuardrailProviderSpecificParams } from "@/components/networking";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { PasswordInput } from "@/components/shared/PasswordInput";
import NumericalInput from "@/components/shared/numerical_input";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Slider } from "@/components/ui/slider";

View file

@ -3,7 +3,7 @@
import { Plus, X } from "lucide-react";
import React from "react";
import { useController } from "react-hook-form";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import {
Combobox,

View file

@ -16,7 +16,7 @@ import {
type MountedFormValues,
} from "@/components/common_components/MountedFormField";
import { requiredRule } from "@/components/common_components/formRules";
import { Field, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldLabel } from "@/components/ui/field";
import { invertedSwitchControl, switchControl, tagsControl, textControl } from "./mcpFieldRules";
import { listControl } from "./mcpFormStore";

View file

@ -14,7 +14,7 @@ import {
getProxyBaseUrl,
} from "@/components/networking";
import { MCPToolset, MCPToolsetTool } from "@/components/mcp_tools/types";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -7,7 +7,7 @@ import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField, type FormFieldControlProps } from "@/components/shared/form/FormField";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import type { InputSchemaProperty } from "@/components/mcp_tools/types";

View file

@ -5,7 +5,7 @@ import { z } from "zod/v4";
import { MCPServer, MCPUserEnvVarsStatus, MCPUserEnvVarSpec } from "@/components/mcp_tools/types";
import { getMCPUserEnvVars, storeMCPUserEnvVars } from "@/components/networking";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";

View file

@ -5,7 +5,7 @@ import React, { useEffect, useState } from "react";
import { z } from "zod/v4";
import type { MemoryRow } from "@/components/networking";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";

View file

@ -5,7 +5,7 @@ import { Policy } from "@/components/policies/types";
import { teamListCall, keyListCall, modelAvailableCall, estimateAttachmentImpactCall } from "@/components/networking";
import { toast } from "@/lib/toast";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { FieldGroup, FieldLabel, FieldTitle } from "@/components/shared/form/field";
import { FieldGroup, FieldLabel, FieldTitle } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";

View file

@ -7,7 +7,7 @@ import { toast } from "@/lib/toast";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { SearchSelect } from "@/components/shared/SearchSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Badge } from "@/components/ui/badge";
import { StatusBadge } from "@/components/shared/table_cells/status_badge";

View file

@ -4,7 +4,7 @@ import { CircleAlert, Inbox } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { resolvePoliciesCall, teamListCall, keyListCall, modelAvailableCall } from "@/components/networking";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";

View file

@ -14,7 +14,7 @@ import { getGuardrailsList } from "@/components/networking";
import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filter/TagsInput";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { SearchSelect } from "@/components/shared/SearchSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";

View file

@ -3,7 +3,7 @@ import { Upload as UploadIcon, X } from "lucide-react";
import { z } from "zod/v4";
import { convertPromptFileToJson, createPromptCall } from "@/components/networking";
import { toast } from "@/lib/toast";
import { Field, FieldDescription, FieldGroup, FieldSeparator, FieldTitle } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldGroup, FieldSeparator, FieldTitle } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -8,7 +8,7 @@ import { Logo } from "@/components/molecules/logo/Logo";
import { toast } from "@/lib/toast";
import { createSearchTool, fetchAvailableSearchProviders } from "@/components/networking";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import {

View file

@ -11,7 +11,7 @@ import {
updateSearchTool,
} from "@/components/networking";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -3,7 +3,7 @@ import { CircleHelp } from "lucide-react";
import { z } from "zod/v4";
import { toast } from "@/lib/toast";
import { registerClaudeCodePlugin } from "@/components/networking";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";

View file

@ -4,7 +4,7 @@ import { ChevronRight, CircleHelp } from "lucide-react";
import React from "react";
import { z } from "zod/v4";
import BudgetDurationDropdown from "@/components/common_components/budget_duration_dropdown";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { MultiSelect } from "@/components/shared/MultiSelect";

View file

@ -10,7 +10,7 @@ import { Tag, TagUpdateRequest } from "@/components/tag_management/types";
import { toast } from "@/lib/toast";
import NumericalInput from "@/components/shared/numerical_input";
import BudgetDurationDropdown from "@/components/common_components/budget_duration_dropdown";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { Badge } from "@/components/ui/badge";

View file

@ -8,7 +8,7 @@ import { useInfiniteTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
import { ModelSelect, MODEL_SENTINEL_OPTIONS } from "@/components/ModelSelect/ModelSelect";
import { toast } from "@/lib/toast";
import { PaginatedSearchSelect } from "@/components/shared/PaginatedSearchSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import type { SearchSelectOption } from "@/components/shared/SearchSelect";
import { Button } from "@/components/ui/button";

View file

@ -10,7 +10,7 @@ import MCPServerSelector from "@/components/mcp_server_management/MCPServerSelec
import MCPToolPermissions from "@/components/mcp_server_management/MCPToolPermissions";
import type { ObjectPermission } from "@/components/object_permission_types";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";

View file

@ -18,7 +18,7 @@ import {
Member,
} from "@/components/networking";
import { SimpleTooltip } from "@/components/ui/tooltip";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import {
Combobox,
ComboboxContent,

View file

@ -14,7 +14,7 @@ import {
VectorStoreFieldConfig,
} from "@/components/vector_store_providers";
import { Logo } from "@/components/molecules/logo/Logo";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";

View file

@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
import { CircleHelp, Info } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models";
import { Field, FieldError, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import {
Combobox,
ComboboxContent,

View file

@ -15,7 +15,7 @@ import {
import { Logo } from "@/components/molecules/logo/Logo";
import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import {

View file

@ -13,7 +13,7 @@ import { getVectorStoreProviderLogoAndName } from "@/components/vector_store_pro
import { Logo } from "@/components/molecules/logo/Logo";
import VectorStoreTester from "./VectorStoreTester";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";

View file

@ -6,7 +6,7 @@ import LoadingScreen from "@/components/common_components/LoadingScreen";
import { exchangeLoginCode, getProxyBaseUrl, switchToWorkerUrl } from "@/components/networking";
import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";

View file

@ -3,7 +3,7 @@ import { CircleAlert, Info } from "lucide-react";
import { z } from "zod/v4";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Field, FieldLabel, FieldGroup } from "@/components/shared/form/field";
import { Field, FieldLabel, FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button, buttonVariants } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";

View file

@ -3,7 +3,7 @@ import { z } from "zod/v4";
import { useCloudZeroCreate } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { TooltipProvider } from "@/components/ui/tooltip";

View file

@ -3,7 +3,7 @@ import { z } from "zod/v4";
import { useCloudZeroUpdateSettings } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { TooltipProvider } from "@/components/ui/tooltip";

View file

@ -1,7 +1,7 @@
import { useQueryClient } from "@tanstack/react-query";
import { useOrganizations } from "@/app/(dashboard)/hooks/organizations/useOrganizations";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Checkbox } from "@/components/ui/checkbox";

View file

@ -6,7 +6,7 @@ import { CircleAlert, CirclePlus, Copy, Info, KeyRound, Link } from "lucide-reac
import { parseErrorMessage } from "./shared/errorUtils";
import { toast } from "@/lib/toast";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardTitle } from "@/components/ui/card";

View file

@ -4,7 +4,7 @@ import { getSSOSettings, updateSSOSettings } from "./networking";
import { toast } from "@/lib/toast";
import { parseErrorMessage } from "./shared/errorUtils";
import { Button } from "@/components/ui/button";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import {
GroupClaimField,
MappingToggleField,

View file

@ -6,7 +6,7 @@ import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { toast } from "@/lib/toast";
import React, { useMemo } from "react";
import { z } from "zod/v4";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Button } from "@/components/ui/button";

View file

@ -12,7 +12,7 @@ import {
} from "@/app/(dashboard)/hooks/storeRequestInSpendLogs/useStoreRequestInSpendLogs";
import { toast } from "@/lib/toast";
import { parseErrorMessage } from "@/components/shared/errorUtils";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -9,7 +9,7 @@ import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/s
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { SearchSelect } from "@/components/shared/SearchSelect";
import { Button } from "@/components/ui/button";

View file

@ -5,7 +5,7 @@ import { Button } from "@/components/ui/button";
import { Eye, EyeOff, Pencil, Plus, Trash2 } from "lucide-react";
import { getConfigFieldSetting, updateConfigFieldSetting } from "@/components/networking";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Input } from "@/components/ui/input";

View file

@ -5,7 +5,7 @@ import { FormProvider, useFormContext, useWatch, type UseFormReturn } from "reac
import { z } from "zod/v4";
import { ssoProviderLogoMap, ssoProviderDisplayNames } from "../constants";
import { Logo } from "@/components/molecules/logo/Logo";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Checkbox } from "@/components/ui/checkbox";

View file

@ -9,7 +9,7 @@ import { Input as UIInput } from "@/components/ui/input";
import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { TooltipProvider } from "@/components/ui/tooltip";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { SearchSelect } from "@/components/shared/SearchSelect";
import { labelWithDocsHint, labelWithHint } from "@/components/shared/form/LabelWithHint";

View file

@ -3,7 +3,7 @@ import React, { useEffect, useState } from "react";
import { useWatch } from "react-hook-form";
import { z } from "zod/v4";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -4,7 +4,7 @@ import { useTags } from "@/app/(dashboard)/hooks/tags/useTags";
import { all_admin_roles, isUserTeamAdminForAnyTeam } from "@/utils/roles";
import { modelCreationScope } from "@/utils/modelPermissions";
import { Switch } from "@/components/ui/switch";
import { Field, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldLabel } from "@/components/ui/field";
import { Card, CardContent } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { SearchSelect, type SearchSelectOption } from "@/components/shared/SearchSelect";

View file

@ -3,7 +3,7 @@ import { useQuery } from "@tanstack/react-query";
import { useWatch } from "react-hook-form";
import { ChevronDown, ChevronRight, CircleHelp } from "lucide-react";
import { z } from "zod/v4";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";

View file

@ -5,7 +5,7 @@ import { getGlobalLitellmHeaderName } from "@/components/networking";
import { toast } from "@/lib/toast";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -9,7 +9,7 @@ import {
type UseFormGetValues,
} from "react-hook-form";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
export type MountedFormValues = Record<string, unknown>;

View file

@ -4,7 +4,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import GuardrailSelector from "../guardrails/GuardrailSelector";
import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filter/TagsInput";
import { Field, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldLabel } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";

View file

@ -5,7 +5,7 @@ import { useDebouncedCallback } from "@tanstack/react-pacer/debouncer";
import { useForm } from "react-hook-form";
import { userFilterUICall } from "@/components/networking";
import { DEBOUNCE_WAIT_MS } from "@/utils/debounceConstants";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import {

View file

@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from "react";
import { z } from "zod/v4";
import { toast } from "@/lib/toast";
import { CircleHelp } from "lucide-react";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -1,5 +1,5 @@
import { SearchSelect } from "@/components/shared/SearchSelect";
import { Field, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldLabel } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupText } from "@/components/ui/input-group";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

View file

@ -1,7 +1,7 @@
import React, { forwardRef, useImperativeHandle, useMemo } from "react";
import { CircleHelp } from "lucide-react";
import { useForm, type Resolver } from "react-hook-form";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";

View file

@ -1,6 +1,6 @@
import React from "react";
import { z } from "zod/v4";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -4,7 +4,7 @@ import { ConfigType, useProxyConfig } from "@/app/(dashboard)/hooks/proxyConfig/
import { StoreModelInDBParams, useStoreModelInDB } from "@/app/(dashboard)/hooks/storeModelInDB/useStoreModelInDB";
import { toast } from "@/lib/toast";
import { parseErrorMessage } from "@/components/shared/errorUtils";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Switch } from "@/components/ui/switch";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";

View file

@ -10,7 +10,7 @@ import { z } from "zod/v4";
import { KeyResponse } from "../key_team_helpers/key_list";
import { toast } from "@/lib/toast";
import { regenerateKeyCall } from "../networking";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Input } from "@/components/ui/input";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";

View file

@ -11,7 +11,7 @@ import { useQueryClient } from "@tanstack/react-query";
import { Button } from "@/components/ui/button";
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
import { Input } from "@/components/ui/input";
import { Field, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldLabel } from "@/components/ui/field";
import { Badge } from "@/components/ui/badge";
import {
Combobox,

View file

@ -7,7 +7,7 @@ import { organizationKeys } from "@/app/(dashboard)/hooks/organizations/useOrgan
import { ModelSelect } from "@/components/ModelSelect/ModelSelect";
import MCPServerSelector from "@/components/mcp_server_management/MCPServerSelector";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";

View file

@ -8,7 +8,7 @@ import { ModelSelect } from "@/components/ModelSelect/ModelSelect";
import MCPServerSelector from "@/components/mcp_server_management/MCPServerSelector";
import { toast } from "@/lib/toast";
import type { Organization } from "@/components/networking";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";

View file

@ -3,7 +3,7 @@
import React, { useEffect, useMemo } from "react";
import { useWatch } from "react-hook-form";
import { z } from "zod/v4";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import {
Combobox,

View file

@ -1,7 +1,7 @@
import React, { useEffect, useState } from "react";
import { Controller, FormProvider, useForm, useFormContext } from "react-hook-form";
import { Field, FieldError, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldError, FieldLabel } from "@/components/ui/field";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {

View file

@ -9,7 +9,7 @@ import {
type FieldValues,
} from "react-hook-form";
import { Field, FieldDescription, FieldError, FieldLabel } from "./field";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
export type FormFieldControlProps<
TFieldValues extends FieldValues,

View file

@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from "react";
import { z } from "zod/v4";
import NumericalInput from "../shared/numerical_input";
import BudgetDurationDropdown from "../common_components/budget_duration_dropdown";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { MultiSelect } from "@/components/shared/MultiSelect";
import { Button } from "@/components/ui/button";

View file

@ -31,7 +31,7 @@ import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { SimpleTooltip, TooltipProvider } from "@/components/ui/tooltip";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldDescription, FieldGroup, FieldLabel } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { labelWithDocsHint, labelWithHint } from "@/components/shared/form/LabelWithHint";
import { MultiSelect } from "@/components/shared/MultiSelect";

View file

@ -10,7 +10,7 @@ import { Switch } from "@/components/ui/switch";
import { Textarea } from "@/components/ui/textarea";
import { TooltipProvider } from "@/components/ui/tooltip";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { Field, FieldGroup, FieldLabel } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import React, { useEffect, useRef, useState } from "react";
import { hasCapability } from "../../utils/capabilities";

View file

@ -123,3 +123,38 @@ describe("field primitives forward refs to their DOM node", () => {
expect(ref.current).toBeInstanceOf(HTMLDivElement);
});
});
describe("FieldLabel wrapping a nested Field", () => {
const NESTED_FIELD_CUES = [
"has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50",
"has-[>[data-slot=field]]:has-[:focus-visible]:border-ring",
"has-[>[data-slot=field]]:has-[:focus-visible]:ring-3",
"has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50",
];
it("carries the hover, focus-visible and disabled cues the card layout depends on", () => {
render(
<FieldLabel data-testid="card-label">
<Field>
<input />
</Field>
</FieldLabel>,
);
expect(screen.getByTestId("card-label")).toHaveClass(...NESTED_FIELD_CUES);
});
it("keeps the cues addressable when a caller passes its own className", () => {
render(
<FieldLabel data-testid="card-label" className="mt-2">
<Field>
<input />
</Field>
</FieldLabel>,
);
const label = screen.getByTestId("card-label");
expect(label).toHaveClass(...NESTED_FIELD_CUES);
expect(label).toHaveClass("mt-2");
});
});

View file

@ -1,17 +1,15 @@
"use client";
import * as React from "react";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { useMemo } from "react";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/cva.config";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
const FieldSet = React.forwardRef<HTMLFieldSetElement, React.ComponentPropsWithoutRef<"fieldset">>(
({ className, ...props }, ref) => (
function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
return (
<fieldset
ref={ref}
data-slot="field-set"
className={cn(
"flex flex-col gap-6 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
@ -19,28 +17,27 @@ const FieldSet = React.forwardRef<HTMLFieldSetElement, React.ComponentPropsWitho
)}
{...props}
/>
),
);
FieldSet.displayName = "FieldSet";
);
}
const FieldLegend = React.forwardRef<
HTMLLegendElement,
React.ComponentPropsWithoutRef<"legend"> & { variant?: "legend" | "label" }
>(({ className, variant = "legend", ...props }, ref) => (
<legend
ref={ref}
data-slot="field-legend"
data-variant={variant}
className={cn("mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base", className)}
{...props}
/>
));
FieldLegend.displayName = "FieldLegend";
function FieldLegend({
className,
variant = "legend",
...props
}: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
return (
<legend
data-slot="field-legend"
data-variant={variant}
className={cn("mb-3 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base", className)}
{...props}
/>
);
}
const FieldGroup = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
({ className, ...props }, ref) => (
function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
ref={ref}
data-slot="field-group"
className={cn(
"group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4",
@ -48,9 +45,8 @@ const FieldGroup = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutR
)}
{...props}
/>
),
);
FieldGroup.displayName = "FieldGroup";
);
}
const fieldVariants = cva("group/field flex w-full gap-3 data-[invalid=true]:text-destructive", {
variants: {
@ -67,53 +63,49 @@ const fieldVariants = cva("group/field flex w-full gap-3 data-[invalid=true]:tex
},
});
const Field = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<"div"> & VariantProps<typeof fieldVariants>
>(({ className, orientation = "vertical", ...props }, ref) => (
<div
ref={ref}
role="group"
data-slot="field"
data-orientation={orientation}
className={cn(fieldVariants({ orientation }), className)}
{...props}
/>
));
Field.displayName = "Field";
const FieldContent = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
({ className, ...props }, ref) => (
function Field({
className,
orientation = "vertical",
...props
}: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
return (
<div
role="group"
data-slot="field"
data-orientation={orientation}
className={cn(fieldVariants({ orientation }), className)}
{...props}
/>
);
}
function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
ref={ref}
data-slot="field-content"
className={cn("group/field-content flex flex-1 flex-col gap-1 leading-snug", className)}
{...props}
/>
),
);
FieldContent.displayName = "FieldContent";
);
}
const FieldLabel = React.forwardRef<HTMLLabelElement, React.ComponentPropsWithoutRef<typeof Label>>(
({ className, ...props }, ref) => (
function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
return (
<Label
ref={ref}
data-slot="field-label"
className={cn(
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border *:data-[slot=field]:p-3 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
"group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50 has-data-checked:border-primary/30 has-data-checked:bg-primary/5 has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border has-[>[data-slot=field]]:not-has-[:disabled,[data-disabled]]:hover:bg-muted/50 has-[>[data-slot=field]]:has-[:focus-visible]:border-ring has-[>[data-slot=field]]:has-[:focus-visible]:ring-3 has-[>[data-slot=field]]:has-[:focus-visible]:ring-ring/50 *:data-[slot=field]:p-3 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10",
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
className,
)}
{...props}
/>
),
);
FieldLabel.displayName = "FieldLabel";
);
}
const FieldTitle = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
({ className, ...props }, ref) => (
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
ref={ref}
data-slot="field-label"
className={cn(
"flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50",
@ -121,14 +113,12 @@ const FieldTitle = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutR
)}
{...props}
/>
),
);
FieldTitle.displayName = "FieldTitle";
);
}
const FieldDescription = React.forwardRef<HTMLParagraphElement, React.ComponentPropsWithoutRef<"p">>(
({ className, ...props }, ref) => (
function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
<p
ref={ref}
data-slot="field-description"
className={cn(
"text-left text-sm leading-normal font-normal text-muted-foreground group-has-data-horizontal/field:text-balance [[data-variant=legend]+&]:-mt-1.5",
@ -138,14 +128,18 @@ const FieldDescription = React.forwardRef<HTMLParagraphElement, React.ComponentP
)}
{...props}
/>
),
);
FieldDescription.displayName = "FieldDescription";
);
}
const FieldSeparator = React.forwardRef<HTMLDivElement, React.ComponentPropsWithoutRef<"div">>(
({ children, className, ...props }, ref) => (
function FieldSeparator({
children,
className,
...props
}: React.ComponentProps<"div"> & {
children?: React.ReactNode;
}) {
return (
<div
ref={ref}
data-slot="field-separator"
data-content={!!children}
className={cn("relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2", className)}
@ -161,15 +155,18 @@ const FieldSeparator = React.forwardRef<HTMLDivElement, React.ComponentPropsWith
</span>
)}
</div>
),
);
FieldSeparator.displayName = "FieldSeparator";
);
}
const FieldError = React.forwardRef<
HTMLDivElement,
React.ComponentPropsWithoutRef<"div"> & { errors?: Array<{ message?: string } | undefined> }
>(({ className, children, errors, ...props }, ref) => {
const content = React.useMemo(() => {
function FieldError({
className,
children,
errors,
...props
}: React.ComponentProps<"div"> & {
errors?: Array<{ message?: string } | undefined>;
}) {
const content = useMemo(() => {
if (children) {
return children;
}
@ -180,7 +177,7 @@ const FieldError = React.forwardRef<
const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
if (uniqueErrors.length === 1) {
if (uniqueErrors?.length == 1) {
return uniqueErrors[0]?.message;
}
@ -197,7 +194,6 @@ const FieldError = React.forwardRef<
return (
<div
ref={ref}
role="alert"
data-slot="field-error"
className={cn("text-sm font-normal text-destructive", className)}
@ -206,18 +202,17 @@ const FieldError = React.forwardRef<
{content}
</div>
);
});
FieldError.displayName = "FieldError";
}
export {
Field,
FieldContent,
FieldLabel,
FieldDescription,
FieldError,
FieldGroup,
FieldLabel,
FieldLegend,
FieldSeparator,
FieldSet,
FieldContent,
FieldTitle,
};

View file

@ -3,7 +3,7 @@ import { useState } from "react";
import { z } from "zod/v4";
import { modelPatchUpdateCall } from "./networking";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FieldGroup } from "@/components/ui/field";
import { FormField } from "@/components/shared/form/FormField";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";