Files
Genarrative/packages/image-canvas-react/src/CanvasChrome.tsx
T
menghao 03a4410272
Project CI / Repository checks (push) Successful in 1m5s
Project CI / Frontend tests (push) Successful in 3m20s
Project CI / Backend tests (push) Successful in 4m1s
Project CI / Native shell tests (push) Failing after 11m56s
game agent无限画布开发 (#136)
主要工作是共享同一套“画布内核与通用 UI 源码”,网站和 Tauri 只分别实现宿主适配层。

---------

Co-authored-by: 段舒康 <kdletters@qq.com>
Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/136
Co-authored-by: menghao <mh18530625731@163.com>
Co-committed-by: menghao <mh18530625731@163.com>
2026-08-12 10:54:16 +08:00

153 lines
3.5 KiB
TypeScript

import {
type ButtonHTMLAttributes,
forwardRef,
type HTMLAttributes,
type ReactNode,
} from 'react';
function joinClassNames(...classNames: Array<string | undefined | false>) {
return classNames.filter(Boolean).join(' ');
}
export type CanvasChromeButtonProps = Omit<
ButtonHTMLAttributes<HTMLButtonElement>,
'aria-expanded' | 'aria-label' | 'aria-pressed' | 'children'
> & {
label: string;
icon: ReactNode;
children?: ReactNode;
pressed?: boolean;
expanded?: boolean;
};
/**
* 画布宿主共享的动作按钮,只承接 chrome、可访问性与原生按钮状态。
*/
export const CanvasChromeButton = forwardRef<
HTMLButtonElement,
CanvasChromeButtonProps
>(function CanvasChromeButton(
{
label,
icon,
children,
pressed,
expanded,
title = label,
type = 'button',
className,
...buttonProps
},
ref,
) {
const hasVisibleLabel =
children !== undefined && children !== null && children !== false;
return (
<button
{...buttonProps}
ref={ref}
type={type}
className={joinClassNames(
'genarrative-image-canvas__chrome-button',
hasVisibleLabel &&
'genarrative-image-canvas__chrome-button--with-label',
className,
)}
aria-label={label}
aria-pressed={pressed}
aria-expanded={expanded}
title={title}
>
<span className="genarrative-image-canvas__chrome-button-icon">
{icon}
</span>
{hasVisibleLabel ? (
<span className="genarrative-image-canvas__chrome-button-label">
{children}
</span>
) : null}
</button>
);
});
export type CanvasToolbarProps = Omit<
HTMLAttributes<HTMLDivElement>,
'aria-label' | 'children' | 'role'
> & {
label: string;
children: ReactNode;
surface?: 'plain' | 'floating';
};
/** 画布宿主共享的横向工具栏容器。 */
export const CanvasToolbar = forwardRef<HTMLDivElement, CanvasToolbarProps>(
function CanvasToolbar(
{ label, children, surface = 'plain', className, ...toolbarProps },
ref,
) {
return (
<div
{...toolbarProps}
ref={ref}
role="toolbar"
aria-label={label}
aria-orientation="horizontal"
className={joinClassNames(
'genarrative-image-canvas__toolbar',
`genarrative-image-canvas__toolbar--${surface}`,
className,
)}
>
{children}
</div>
);
},
);
export type CanvasToolbarGroupProps = HTMLAttributes<HTMLSpanElement> & {
children: ReactNode;
};
/** 将同类画布动作保持为不可拆散的横向分组。 */
export const CanvasToolbarGroup = forwardRef<
HTMLSpanElement,
CanvasToolbarGroupProps
>(function CanvasToolbarGroup({ children, className, ...groupProps }, ref) {
return (
<span
{...groupProps}
ref={ref}
className={joinClassNames(
'genarrative-image-canvas__toolbar-group',
className,
)}
>
{children}
</span>
);
});
export type CanvasToolbarDividerProps = Omit<
HTMLAttributes<HTMLSpanElement>,
'aria-hidden' | 'children'
>;
/** 纯视觉工具分隔符,不进入无障碍树。 */
export const CanvasToolbarDivider = forwardRef<
HTMLSpanElement,
CanvasToolbarDividerProps
>(function CanvasToolbarDivider({ className, ...dividerProps }, ref) {
return (
<span
{...dividerProps}
ref={ref}
aria-hidden="true"
className={joinClassNames(
'genarrative-image-canvas__toolbar-divider',
className,
)}
/>
);
});