收口前端平台组件库能力

新增 PlatformUiKit 通用弹窗、按钮、状态、空态、媒体、表单和标签等公共组件
迁移结果页、创作工作台、认证入口、RPG 暗色面板和运行态弹窗的重复 UI chrome
补充组件测试、页面回归测试、技术文档和 Hermes 共享决策记录
This commit is contained in:
2026-06-10 10:24:18 +08:00
parent a4ee6ff698
commit 1ad25e30f8
226 changed files with 23364 additions and 7825 deletions

View File

@@ -6,11 +6,15 @@ import {
useState,
} from 'react';
export type SquareImageCropRect = {
x: number;
y: number;
size: number;
};
import { PlatformActionButton } from './PlatformActionButton';
import { PlatformModalCloseButton } from './PlatformModalCloseButton';
import { PlatformStatusMessage } from './PlatformStatusMessage';
import {
clampNumber,
clampSquareImageCropRect,
getSquareCropSizeBounds,
type SquareImageCropRect,
} from './squareImageCropModel';
export type SquareImageCropModalLabels = {
title: string;
@@ -98,44 +102,6 @@ const SQUARE_CROP_RESIZE_HANDLES: Array<{
},
];
function clampNumber(value: number, min: number, max: number) {
return Math.max(min, Math.min(max, value));
}
function getSquareCropSizeBounds(imageSize: { width: number; height: number }) {
const maxSize = Math.max(1, Math.min(imageSize.width, imageSize.height));
const minSize = Math.min(maxSize, Math.max(48, maxSize * 0.18));
return { minSize, maxSize };
}
export function buildCenteredSquareImageCropRect(imageSize: {
width: number;
height: number;
}): SquareImageCropRect {
const size = Math.max(1, Math.min(imageSize.width, imageSize.height));
return {
x: Math.max(0, (imageSize.width - size) / 2),
y: Math.max(0, (imageSize.height - size) / 2),
size,
};
}
export function clampSquareImageCropRect(
imageSize: { width: number; height: number },
crop: SquareImageCropRect,
): SquareImageCropRect {
const { minSize, maxSize } = getSquareCropSizeBounds(imageSize);
const size = clampNumber(crop.size, minSize, maxSize);
return {
x: clampNumber(crop.x, 0, Math.max(0, imageSize.width - size)),
y: clampNumber(crop.y, 0, Math.max(0, imageSize.height - size)),
size,
};
}
function buildSquareCropPreviewStyle(
crop: SquareImageCropRect,
imageSize: { width: number; height: number },
@@ -354,14 +320,12 @@ export function SquareImageCropModal({
<div id={titleId} className="text-base font-black">
{labels.title}
</div>
<button
type="button"
aria-label={labels.close}
<PlatformModalCloseButton
label={labels.close}
variant="profileCompact"
onClick={onClose}
className="platform-profile-icon-button flex h-8 w-8 items-center justify-center rounded-full"
>
×
</button>
icon="×"
/>
</div>
<div className="px-5 py-5">
<div
@@ -416,26 +380,24 @@ export function SquareImageCropModal({
</div>
</div>
{error ? (
<div className="mt-4 rounded-2xl border border-[var(--platform-button-danger-border)] bg-[var(--platform-button-danger-fill)] px-3 py-2 text-sm text-[var(--platform-button-danger-text)]">
<PlatformStatusMessage
tone="error"
surface="profile"
className="mt-4 rounded-2xl"
>
{error}
</div>
</PlatformStatusMessage>
) : null}
<div className="mt-5 grid grid-cols-2 gap-3">
<button
type="button"
onClick={onClose}
className="platform-button platform-button--secondary justify-center"
>
<PlatformActionButton tone="secondary" onClick={onClose}>
{labels.cancel}
</button>
<button
type="button"
</PlatformActionButton>
<PlatformActionButton
onClick={onSubmit}
disabled={isSaving}
className="platform-button platform-button--primary justify-center disabled:cursor-not-allowed disabled:opacity-60"
>
{isSaving ? labels.saving : labels.submit}
</button>
</PlatformActionButton>
</div>
</div>
</div>