Support app.relay.emoji_confetti on jumbo emoji clicks with canvas physics, per-emoji particle profiles, and a fixed overlay that does not affect scroll. Also add a Vite playground for live component previews and theme hover preview.
107 lines
3.9 KiB
TypeScript
107 lines
3.9 KiB
TypeScript
import { ReactNode, useEffect, useMemo, useState } from 'react';
|
|
import { Provider as JotaiProvider } from 'jotai';
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import {
|
|
Box,
|
|
OverlayContainerProvider,
|
|
PopOutContainerProvider,
|
|
TooltipContainerProvider,
|
|
configClass,
|
|
varsClass,
|
|
} from 'folds';
|
|
import { MatrixClientProvider } from '@cinny/app/hooks/useMatrixClient';
|
|
import { RoomProvider, IsDirectRoomProvider } from '@cinny/app/hooks/useRoom';
|
|
import { SpecVersionsProvider } from '@cinny/app/hooks/useSpecVersions';
|
|
import { DarkTheme, ThemeContextProvider } from '@cinny/app/hooks/useTheme';
|
|
import {
|
|
PowerLevelsContextProvider,
|
|
type IPowerLevels,
|
|
} from '@cinny/app/hooks/usePowerLevels';
|
|
import { ContainerColor } from '@cinny/app/styles/ContainerColor.css';
|
|
import type { MatrixClient, Room } from 'matrix-js-sdk';
|
|
import { MOCK_POWER_LEVELS, createMockRoom, mocks } from './matrix';
|
|
|
|
type Props = {
|
|
children: ReactNode;
|
|
client?: MatrixClient;
|
|
room?: Room;
|
|
isDirect?: boolean;
|
|
powerLevels?: IPowerLevels;
|
|
};
|
|
|
|
const SPEC_VERSIONS = {
|
|
versions: ['v1.11', 'v1.12'],
|
|
unstable_features: {
|
|
'org.matrix.msc3916.stable': true,
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Wraps a preview in the providers Cinny UI pieces usually expect.
|
|
* Overlay/popout/tooltip portals stay inside the stage (not the full window).
|
|
*/
|
|
export function PlaygroundProviders({
|
|
children,
|
|
client,
|
|
room,
|
|
isDirect = false,
|
|
powerLevels = MOCK_POWER_LEVELS as IPowerLevels,
|
|
}: Props) {
|
|
const [portal, setPortal] = useState<HTMLDivElement | null>(null);
|
|
const queryClient = useMemo(() => new QueryClient(), []);
|
|
const resolvedClient = client ?? mocks.client;
|
|
// Always provide a room with `.client` set — RoomInput/useStateEvent require it.
|
|
const resolvedRoom = useMemo(() => {
|
|
if (room) return room;
|
|
return createMockRoom({ client: resolvedClient });
|
|
}, [room, resolvedClient]);
|
|
|
|
useEffect(() => {
|
|
document.body.classList.add(configClass, varsClass, ...DarkTheme.classNames);
|
|
}, []);
|
|
|
|
return (
|
|
<div className="preview-frame">
|
|
<ThemeContextProvider value={DarkTheme}>
|
|
<TooltipContainerProvider value={portal ?? undefined}>
|
|
<PopOutContainerProvider value={portal ?? undefined}>
|
|
<OverlayContainerProvider value={portal ?? undefined}>
|
|
<QueryClientProvider client={queryClient}>
|
|
<JotaiProvider>
|
|
<MemoryRouter>
|
|
<SpecVersionsProvider value={SPEC_VERSIONS}>
|
|
<MatrixClientProvider value={resolvedClient}>
|
|
<RoomProvider value={resolvedRoom}>
|
|
<IsDirectRoomProvider value={isDirect}>
|
|
<PowerLevelsContextProvider value={powerLevels}>
|
|
<Box
|
|
className={ContainerColor({ variant: 'Surface' })}
|
|
style={{
|
|
width: '100%',
|
|
minHeight: '100%',
|
|
height: '100%',
|
|
padding: 16,
|
|
borderRadius: 8,
|
|
}}
|
|
>
|
|
{children}
|
|
</Box>
|
|
</PowerLevelsContextProvider>
|
|
</IsDirectRoomProvider>
|
|
</RoomProvider>
|
|
</MatrixClientProvider>
|
|
</SpecVersionsProvider>
|
|
</MemoryRouter>
|
|
</JotaiProvider>
|
|
</QueryClientProvider>
|
|
</OverlayContainerProvider>
|
|
</PopOutContainerProvider>
|
|
</TooltipContainerProvider>
|
|
</ThemeContextProvider>
|
|
{/* Host for folds Overlay/Dialog portals — contained by .preview-frame */}
|
|
<div className="preview-portal" ref={setPortal} />
|
|
</div>
|
|
);
|
|
}
|