[功能] 更新组件库hero3.0.1

This commit is contained in:
ryan
2026-03-27 13:34:35 +08:00
parent 6b1d2e8af9
commit f14ba66a11
5 changed files with 725 additions and 1754 deletions
+6 -5
View File
@@ -1,7 +1,8 @@
@import '../styles/tokens.css'; @import '../styles/tokens.css';
@import 'tailwindcss'; @import 'tailwindcss';
@import "tw-animate-css"; @import '@heroui/styles';
@import "shadcn/tailwind.css"; @import "tw-animate-css";
@import "shadcn/tailwind.css";
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
@@ -169,4 +170,4 @@ button {
html { html {
@apply font-sans; @apply font-sans;
} }
} }
@@ -16,7 +16,6 @@ import { deleteOrigin, getOrigin } from '@/features/origins/api/origins';
import { OriginEditorModal } from '@/features/origins/components/origin-editor-modal'; import { OriginEditorModal } from '@/features/origins/components/origin-editor-modal';
import { import {
DangerButton, DangerButton,
PrimaryButton,
SecondaryButton, SecondaryButton,
} from '@/features/shared/components/resource-primitives'; } from '@/features/shared/components/resource-primitives';
import { formatDateTime } from '@/lib/utils/date'; import { formatDateTime } from '@/lib/utils/date';
@@ -1,6 +1,6 @@
'use client'; 'use client';
import { Select, SelectItem, SelectSection } from '@heroui/select'; import { ListBox, Select } from '@heroui/react';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useEffect, useMemo, useRef, useState } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react';
@@ -58,6 +58,8 @@ import {
import { cn } from '@/lib/utils/cn'; import { cn } from '@/lib/utils/cn';
import { formatDateTime } from '@/lib/utils/date'; import { formatDateTime } from '@/lib/utils/date';
const SelectItem = ListBox.Item;
const customHeaderSchema = z.object({ const customHeaderSchema = z.object({
key: z.string(), key: z.string(),
value: z.string(), value: z.string(),
@@ -278,17 +280,15 @@ type SectionIconProps = {
const INPUT_CLASS_NAME = 'h-10 rounded-xl px-3 py-2 text-sm'; const INPUT_CLASS_NAME = 'h-10 rounded-xl px-3 py-2 text-sm';
const PANEL_CLASS_NAME = const PANEL_CLASS_NAME =
'rounded-2xl border border-[var(--border-default)] bg-[color:color-mix(in_srgb,var(--surface-elevated)_82%,white_18%)] p-4 shadow-[var(--shadow-soft)]'; 'rounded-2xl border border-[var(--border-default)] bg-[color:color-mix(in_srgb,var(--surface-elevated)_82%,white_18%)] p-4 shadow-[var(--shadow-soft)]';
const HERO_SELECT_CLASS_NAMES = { const HERO_SELECT_TRIGGER_CLASS_NAME = cn(
base: 'w-full', INPUT_CLASS_NAME,
trigger: cn( 'flex min-h-10 w-full items-center justify-between rounded-xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-3 shadow-none transition-colors data-[hovered=true]:bg-[var(--surface-elevated)] data-[focus-visible=true]:border-[var(--border-strong)] data-[open=true]:border-[var(--border-strong)]',
INPUT_CLASS_NAME, );
'min-h-10 rounded-xl border border-[var(--border-default)] bg-[var(--surface-elevated)] px-3 shadow-none data-[hover=true]:bg-[var(--surface-elevated)] data-[open=true]:border-[var(--border-strong)]', const HERO_SELECT_VALUE_CLASS_NAME =
), 'truncate text-left text-sm text-[var(--foreground-primary)]';
value: 'text-sm text-[var(--foreground-primary)]', const HERO_SELECT_POPOVER_CLASS_NAME =
popoverContent: 'rounded-2xl border border-[var(--border-default)] bg-[var(--surface-panel)] shadow-[var(--shadow-soft)]';
'rounded-2xl border border-[var(--border-default)] bg-[var(--surface-panel)] shadow-[var(--shadow-soft)]', const HERO_SELECT_LISTBOX_CLASS_NAME = 'p-1';
listboxWrapper: 'p-1',
} as const;
const defaultValues: ProxyRouteFormValues = { const defaultValues: ProxyRouteFormValues = {
managed_domain_id: '', managed_domain_id: '',
@@ -1074,10 +1074,6 @@ function HeroTextArea(
); );
} }
function getSelectedKeys(value: string) {
return value ? new Set([value]) : new Set<string>();
}
function HeroSelectField({ function HeroSelectField({
ariaLabel, ariaLabel,
value, value,
@@ -1089,29 +1085,29 @@ function HeroSelectField({
value: string; value: string;
disabled?: boolean; disabled?: boolean;
onChange: (value: string) => void; onChange: (value: string) => void;
children: React.ComponentProps<typeof Select>['children']; children: React.ReactNode;
}) { }) {
return ( return (
<Select <Select
aria-label={ariaLabel} aria-label={ariaLabel}
variant="bordered" variant="primary"
radius="lg" selectedKey={value || null}
disallowEmptySelection
selectedKeys={getSelectedKeys(value)}
isDisabled={disabled} isDisabled={disabled}
classNames={HERO_SELECT_CLASS_NAMES} className="w-full"
onSelectionChange={(keys) => { onSelectionChange={(key) => {
if (keys === 'all') { if (typeof key !== 'string') {
return; return;
} }
onChange(key);
const [nextValue] = Array.from(keys);
if (typeof nextValue === 'string') {
onChange(nextValue);
}
}} }}
> >
{children} <Select.Trigger className={HERO_SELECT_TRIGGER_CLASS_NAME}>
<Select.Value className={HERO_SELECT_VALUE_CLASS_NAME} />
<Select.Indicator />
</Select.Trigger>
<Select.Popover className={HERO_SELECT_POPOVER_CLASS_NAME}>
<ListBox className={HERO_SELECT_LISTBOX_CLASS_NAME}>{children}</ListBox>
</Select.Popover>
</Select> </Select>
); );
} }
@@ -1199,10 +1195,6 @@ export function ProxyRoutesPage() {
control: form.control, control: form.control,
name: 'origin_rows', name: 'origin_rows',
}); });
const watchedOriginURI = useWatch({
control: form.control,
name: 'origin_uri',
});
const watchedEnabled = useWatch({ control: form.control, name: 'enabled' }); const watchedEnabled = useWatch({ control: form.control, name: 'enabled' });
const watchedEnableHttps = useWatch({ const watchedEnableHttps = useWatch({
control: form.control, control: form.control,
@@ -1885,28 +1877,35 @@ export function ProxyRoutesPage() {
} }
> >
{matchResult?.candidate ? ( {matchResult?.candidate ? (
<SelectSection title="自动匹配"> <>
<SelectItem key={String(matchResult.candidate.certificate_id)}> <SelectItem key="__auto_match_label__" isDisabled textValue="自动匹配">
自动匹配
</SelectItem>
<SelectItem
key={String(matchResult.candidate.certificate_id)}
textValue={matchResult.candidate.certificate_name}
>
{matchResult.candidate.certificate_name} {matchResult.candidate.certificate_name}
</SelectItem> </SelectItem>
</SelectSection> </>
) : null} ) : null}
<SelectSection title="全部证书"> <SelectItem key="__all_certificates_label__" isDisabled textValue="全部证书">
{certificates 全部证书
.filter( </SelectItem>
(certificate) => {certificates
String(certificate.id) !== .filter(
String(matchResult?.candidate?.certificate_id ?? ''), (certificate) =>
) String(certificate.id) !==
.map((certificate) => ( String(matchResult?.candidate?.certificate_id ?? ''),
<SelectItem )
key={String(certificate.id)} .map((certificate) => (
textValue={buildCertificateLabel(certificate)} <SelectItem
> key={String(certificate.id)}
{buildCertificateLabel(certificate)} textValue={buildCertificateLabel(certificate)}
</SelectItem> >
))} {buildCertificateLabel(certificate)}
</SelectSection> </SelectItem>
))}
</HeroSelectField> </HeroSelectField>
</ResourceField> </ResourceField>
+2 -3
View File
@@ -18,9 +18,8 @@
"format:check": "prettier --check ." "format:check": "prettier --check ."
}, },
"dependencies": { "dependencies": {
"@heroui/input": "^2.4.33", "@heroui/react": "^3.0.1",
"@heroui/react": "^2.8.10", "@heroui/styles": "^3.0.1",
"@heroui/select": "^2.4.33",
"@hookform/resolvers": "^4.1.3", "@hookform/resolvers": "^4.1.3",
"@tanstack/react-query": "^5.66.8", "@tanstack/react-query": "^5.66.8",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
+665 -1692
View File
File diff suppressed because it is too large Load Diff