67 lines
1.8 KiB
TypeScript
Executable File
67 lines
1.8 KiB
TypeScript
Executable File
import { Card, Space, Tag, Typography } from 'antd';
|
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
import { resolveWidgetFeatures } from './registry/widget-features';
|
|
import type { WidgetHandle, WidgetShellProps } from './types/widget';
|
|
|
|
const { Title } = Typography;
|
|
|
|
export const WidgetShell = forwardRef<WidgetHandle, WidgetShellProps>(function WidgetShell(
|
|
{ id, title, features = [], featureSlot, cardWrapper = true, children },
|
|
ref,
|
|
) {
|
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
const resolvedFeatures = resolveWidgetFeatures(features);
|
|
const hasHeader = Boolean(title) || resolvedFeatures.length > 0 || Boolean(featureSlot);
|
|
|
|
useImperativeHandle(
|
|
ref,
|
|
() => ({
|
|
focus: () => {
|
|
wrapperRef.current?.focus();
|
|
},
|
|
scrollIntoView: (options) => {
|
|
wrapperRef.current?.scrollIntoView(options);
|
|
},
|
|
getId: () => id,
|
|
getFeatures: () => features,
|
|
}),
|
|
[features, id],
|
|
);
|
|
|
|
const content = (
|
|
<Space direction="vertical" size={16} className="widget-shell__stack">
|
|
{hasHeader ? (
|
|
<div className="widget-shell__header">
|
|
<Title level={4} className="widget-shell__title">
|
|
{title}
|
|
</Title>
|
|
<Space size={[8, 8]} wrap>
|
|
{resolvedFeatures.map((feature) => (
|
|
<Tag key={feature.key} color="blue">
|
|
{feature.label}
|
|
</Tag>
|
|
))}
|
|
{featureSlot}
|
|
</Space>
|
|
</div>
|
|
) : null}
|
|
|
|
<div className="widget-shell__content">{children}</div>
|
|
</Space>
|
|
);
|
|
|
|
if (!cardWrapper) {
|
|
return (
|
|
<div ref={wrapperRef} className="widget-shell widget-shell--plain" tabIndex={-1}>
|
|
{content}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Card ref={wrapperRef} className="widget-shell" tabIndex={-1}>
|
|
{content}
|
|
</Card>
|
|
);
|
|
});
|