mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-08 20:09:19 +00:00
Let page background shine through the footer
By visually separating it from other page content with an additional gradient background only when necessary.
This commit is contained in:
@@ -375,25 +375,11 @@ export class TestHelpers {
|
|||||||
frame: FrameLocator,
|
frame: FrameLocator,
|
||||||
count: number,
|
count: number,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// XXX we need to be better at our HTML markup and accessibility, it would make
|
await expect(
|
||||||
// this kind of stuff way easier to test if we could look out for aria attributes.
|
frame
|
||||||
await expect
|
.getByRole("img")
|
||||||
.poll(
|
.and(frame.locator("video"))
|
||||||
async () => {
|
.filter({ visible: true }),
|
||||||
return await frame
|
).toHaveCount(count, { timeout: 10000 });
|
||||||
.locator("video")
|
|
||||||
.evaluateAll(
|
|
||||||
(videos: Element[]) =>
|
|
||||||
videos.filter(
|
|
||||||
(v: Element) =>
|
|
||||||
window.getComputedStyle(v).display === "block",
|
|
||||||
).length,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
{
|
|
||||||
timeout: 10000,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.toBe(count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,6 @@ Please see LICENSE in the repository root for full details.
|
|||||||
padding-right: calc(env(safe-area-inset-right) + var(--cpd-space-6x));
|
padding-right: calc(env(safe-area-inset-right) + var(--cpd-space-6x));
|
||||||
padding-block: var(--cpd-space-10x)
|
padding-block: var(--cpd-space-10x)
|
||||||
calc(env(safe-area-inset-bottom) + var(--cpd-space-10x));
|
calc(env(safe-area-inset-bottom) + var(--cpd-space-10x));
|
||||||
background: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
rgba(0, 0, 0, 0) 0%,
|
|
||||||
var(--cpd-color-bg-canvas-default) 100%
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer.hidden {
|
.footer.hidden {
|
||||||
|
|||||||
@@ -112,11 +112,17 @@ export interface FooterState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FooterProps {
|
export interface FooterProps {
|
||||||
|
className?: string;
|
||||||
ref?: Ref<HTMLDivElement>;
|
ref?: Ref<HTMLDivElement>;
|
||||||
children?: JSX.Element | JSX.Element[] | false;
|
children?: JSX.Element | JSX.Element[] | false;
|
||||||
vm: ViewModel<FooterSnapshot>;
|
vm: ViewModel<FooterSnapshot>;
|
||||||
}
|
}
|
||||||
export const CallFooter: FC<FooterProps> = ({ ref, children, vm }) => {
|
export const CallFooter: FC<FooterProps> = ({
|
||||||
|
className,
|
||||||
|
ref,
|
||||||
|
children,
|
||||||
|
vm,
|
||||||
|
}) => {
|
||||||
const asOverlay = useBehavior(vm.asOverlay$);
|
const asOverlay = useBehavior(vm.asOverlay$);
|
||||||
const showFooter = useBehavior(vm.showFooter$);
|
const showFooter = useBehavior(vm.showFooter$);
|
||||||
const hideControls = useBehavior(vm.hideControls$);
|
const hideControls = useBehavior(vm.hideControls$);
|
||||||
@@ -292,7 +298,7 @@ export const CallFooter: FC<FooterProps> = ({ ref, children, vm }) => {
|
|||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data-testid="footer-container"
|
data-testid="footer-container"
|
||||||
className={classNames(styles.footer, {
|
className={classNames(className, styles.footer, {
|
||||||
[styles.overlay]: asOverlay,
|
[styles.overlay]: asOverlay,
|
||||||
[styles.hidden]: !showFooter,
|
[styles.hidden]: !showFooter,
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -14,6 +14,23 @@ Please see LICENSE in the repository root for full details.
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Normally the footer uses a transparent background to allow our expressive
|
||||||
|
page gradients to shine through. However, we sometimes need to visually separate
|
||||||
|
it from the content underneath. If the call layout is overflowing, or if the
|
||||||
|
spotlight tile is maximised and displaying video, apply a gradient background. */
|
||||||
|
.overflowing > .footer,
|
||||||
|
.fixedGrid:has(
|
||||||
|
> .tile[data-maximised="true"]
|
||||||
|
.spotlightItem[data-bg-style="transparent"][data-video-enabled="true"][aria-hidden="false"]
|
||||||
|
)
|
||||||
|
~ .footer {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
var(--cpd-color-bg-canvas-default) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
position: sticky;
|
position: sticky;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@@ -82,19 +99,18 @@ Please see LICENSE in the repository root for full details.
|
|||||||
/* Disable pointer events so the overlay doesn't block interaction with
|
/* Disable pointer events so the overlay doesn't block interaction with
|
||||||
elements behind it */
|
elements behind it */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
|
||||||
|
|
||||||
.fixedGrid > :not(:first-child),
|
> :not(:first-child) {
|
||||||
.scrollingGrid > :not(:first-child) {
|
pointer-events: initial;
|
||||||
pointer-events: initial;
|
}
|
||||||
|
|
||||||
|
.tile {
|
||||||
|
position: absolute;
|
||||||
|
inset-block-start: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile {
|
.tile {
|
||||||
position: absolute;
|
|
||||||
inset-block-start: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tile.maximised {
|
|
||||||
position: relative;
|
position: relative;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
const reconnecting = useBehavior(vm.reconnecting$);
|
const reconnecting = useBehavior(vm.reconnecting$);
|
||||||
const layout = useBehavior(vm.layout$);
|
const layout = useBehavior(vm.layout$);
|
||||||
const edgeToEdge = useBehavior(vm.edgeToEdge$);
|
const edgeToEdge = useBehavior(vm.edgeToEdge$);
|
||||||
|
const overflowing = useBehavior(vm.overflowing$);
|
||||||
const showNameTags = useBehavior(vm.showNameTags$);
|
const showNameTags = useBehavior(vm.showNameTags$);
|
||||||
const showHeader = useBehavior(vm.showHeader$);
|
const showHeader = useBehavior(vm.showHeader$);
|
||||||
const settingsOpen = useBehavior(vm.settingsOpen$);
|
const settingsOpen = useBehavior(vm.settingsOpen$);
|
||||||
@@ -468,6 +469,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
showRingingStatus={showRingingStatus}
|
showRingingStatus={showRingingStatus}
|
||||||
focusable={!contentObscured}
|
focusable={!contentObscured}
|
||||||
className={classNames(className, styles.tile)}
|
className={classNames(className, styles.tile)}
|
||||||
|
itemClassName={styles.spotlightItem}
|
||||||
style={style}
|
style={style}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -492,7 +494,9 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
if (layout.type === "pip") {
|
if (layout.type === "pip") {
|
||||||
return (
|
return (
|
||||||
<SpotlightTile
|
<SpotlightTile
|
||||||
className={classNames(styles.tile, styles.maximised)}
|
className={styles.tile}
|
||||||
|
itemClassName={styles.spotlightItem}
|
||||||
|
data-maximised
|
||||||
vm={layout.spotlight}
|
vm={layout.spotlight}
|
||||||
expanded
|
expanded
|
||||||
onToggleExpanded={null}
|
onToggleExpanded={null}
|
||||||
@@ -584,7 +588,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
|
|
||||||
// Only hide the settings button if we have an AppBar header and we are showing the header
|
// Only hide the settings button if we have an AppBar header and we are showing the header
|
||||||
const footer = footerVm !== null && (
|
const footer = footerVm !== null && (
|
||||||
<CallFooter ref={footerRef} vm={footerVm} />
|
<CallFooter className={styles.footer} ref={footerRef} vm={footerVm} />
|
||||||
);
|
);
|
||||||
const allConnections = useBehavior(vm.allConnections$);
|
const allConnections = useBehavior(vm.allConnections$);
|
||||||
|
|
||||||
@@ -593,7 +597,9 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
// and the footer is also viewable by moving focus into it, so this is fine.
|
// and the footer is also viewable by moving focus into it, so this is fine.
|
||||||
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
// eslint-disable-next-line jsx-a11y/no-static-element-interactions
|
||||||
<div
|
<div
|
||||||
className={styles.inRoom}
|
className={classNames(styles.inRoom, {
|
||||||
|
[styles.overflowing]: overflowing,
|
||||||
|
})}
|
||||||
ref={containerRef}
|
ref={containerRef}
|
||||||
onPointerUp={onViewPointerUp}
|
onPointerUp={onViewPointerUp}
|
||||||
onPointerMove={onPointerMove}
|
onPointerMove={onPointerMove}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ exports[`InCallView > rendering > renders 1`] = `
|
|||||||
class="container"
|
class="container"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="footer"
|
class="footer footer"
|
||||||
data-testid="footer-container"
|
data-testid="footer-container"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ export type WindowMode = "normal" | "narrow" | "flat" | "pip";
|
|||||||
|
|
||||||
interface LayoutScanState {
|
interface LayoutScanState {
|
||||||
layout: Layout | null;
|
layout: Layout | null;
|
||||||
|
overflowing: boolean;
|
||||||
tiles: TileStore;
|
tiles: TileStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -360,6 +361,10 @@ export interface CallViewModel {
|
|||||||
* and header as overlays.
|
* and header as overlays.
|
||||||
*/
|
*/
|
||||||
edgeToEdge$: Behavior<boolean>;
|
edgeToEdge$: Behavior<boolean>;
|
||||||
|
/**
|
||||||
|
* Whether the call layout is overflowing the interface (causing it to scroll).
|
||||||
|
*/
|
||||||
|
overflowing$: Behavior<boolean>;
|
||||||
|
|
||||||
settingsOpen$: Behavior<boolean>;
|
settingsOpen$: Behavior<boolean>;
|
||||||
setSettingsOpen$: Behavior<(open: boolean) => void>;
|
setSettingsOpen$: Behavior<(open: boolean) => void>;
|
||||||
@@ -1467,7 +1472,7 @@ export function createCallViewModel$(
|
|||||||
|
|
||||||
// There is a cyclical dependency here: the layout algorithms want to know
|
// There is a cyclical dependency here: the layout algorithms want to know
|
||||||
// which tiles are on screen, but to know which tiles are on screen we have to
|
// which tiles are on screen, but to know which tiles are on screen we have to
|
||||||
// first render a layout. To deal with this we assume initially that no tiles
|
// first render a layout. To deal with this we assume initially that all tiles
|
||||||
// are visible, and loop the data back into the layouts with a Subject.
|
// are visible, and loop the data back into the layouts with a Subject.
|
||||||
const visibleTiles$ = new Subject<number>();
|
const visibleTiles$ = new Subject<number>();
|
||||||
const setVisibleTiles = (value: number): void => visibleTiles$.next(value);
|
const setVisibleTiles = (value: number): void => visibleTiles$.next(value);
|
||||||
@@ -1475,7 +1480,7 @@ export function createCallViewModel$(
|
|||||||
const layoutInternals$ = scope.behavior<LayoutScanState & { layout: Layout }>(
|
const layoutInternals$ = scope.behavior<LayoutScanState & { layout: Layout }>(
|
||||||
combineLatest([
|
combineLatest([
|
||||||
layoutMedia$,
|
layoutMedia$,
|
||||||
visibleTiles$.pipe(startWith(0), distinctUntilChanged()),
|
visibleTiles$.pipe(startWith(Infinity), distinctUntilChanged()),
|
||||||
]).pipe(
|
]).pipe(
|
||||||
scan<
|
scan<
|
||||||
[LayoutMedia, number],
|
[LayoutMedia, number],
|
||||||
@@ -1486,6 +1491,7 @@ export function createCallViewModel$(
|
|||||||
let layout: Layout;
|
let layout: Layout;
|
||||||
let newTiles: TileStore;
|
let newTiles: TileStore;
|
||||||
let pip: GridTileViewModel | undefined;
|
let pip: GridTileViewModel | undefined;
|
||||||
|
let overflowing = false;
|
||||||
switch (media.type) {
|
switch (media.type) {
|
||||||
case "grid":
|
case "grid":
|
||||||
case "spotlight-landscape":
|
case "spotlight-landscape":
|
||||||
@@ -1497,6 +1503,7 @@ export function createCallViewModel$(
|
|||||||
setVisibleTiles,
|
setVisibleTiles,
|
||||||
prevTiles,
|
prevTiles,
|
||||||
);
|
);
|
||||||
|
overflowing = newTiles.gridTiles.length > visibleTiles;
|
||||||
break;
|
break;
|
||||||
case "spotlight-expanded":
|
case "spotlight-expanded":
|
||||||
[layout, newTiles] = spotlightExpandedLayout(
|
[layout, newTiles] = spotlightExpandedLayout(
|
||||||
@@ -1531,9 +1538,9 @@ export function createCallViewModel$(
|
|||||||
tile.setShowOutline(tile === pip);
|
tile.setShowOutline(tile === pip);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { layout, tiles: newTiles };
|
return { layout, overflowing, tiles: newTiles };
|
||||||
},
|
},
|
||||||
{ layout: null, tiles: TileStore.empty() },
|
{ layout: null, overflowing: false, tiles: TileStore.empty() },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -1545,6 +1552,10 @@ export function createCallViewModel$(
|
|||||||
layoutInternals$.pipe(map(({ layout }) => layout)),
|
layoutInternals$.pipe(map(({ layout }) => layout)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const overflowing$ = scope.behavior<boolean>(
|
||||||
|
layoutInternals$.pipe(map(({ overflowing }) => overflowing)),
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current generation of the tile store, exposed for debugging purposes.
|
* The current generation of the tile store, exposed for debugging purposes.
|
||||||
*/
|
*/
|
||||||
@@ -1786,6 +1797,7 @@ export function createCallViewModel$(
|
|||||||
settingsOpen$: settingsOpen$,
|
settingsOpen$: settingsOpen$,
|
||||||
setSettingsOpen$: setSettingsOpen$,
|
setSettingsOpen$: setSettingsOpen$,
|
||||||
edgeToEdge$,
|
edgeToEdge$,
|
||||||
|
overflowing$,
|
||||||
earpieceMode$: earpieceMode$,
|
earpieceMode$: earpieceMode$,
|
||||||
audioOutputSwitcher$: audioOutputSwitcher$,
|
audioOutputSwitcher$: audioOutputSwitcher$,
|
||||||
reconnecting$: localMembership.reconnecting$,
|
reconnecting$: localMembership.reconnecting$,
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ Please see LICENSE in the repository root for full details.
|
|||||||
transform: translate(0);
|
transform: translate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media[data-video-enabled="false"] video {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.media.mirror video {
|
.media.mirror video {
|
||||||
transform: scaleX(-1);
|
transform: scaleX(-1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,30 +129,4 @@ describe("MediaView", () => {
|
|||||||
).toBe(0);
|
).toBe(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("videoEnabled", () => {
|
|
||||||
test("just video is visible", () => {
|
|
||||||
render(
|
|
||||||
<TooltipProvider>
|
|
||||||
<MediaView {...baseProps} videoEnabled={true} />
|
|
||||||
</TooltipProvider>,
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId("video")).toBeVisible();
|
|
||||||
expect(screen.queryAllByRole("img", { name: "some name" }).length).toBe(
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("just avatar is visible", () => {
|
|
||||||
render(
|
|
||||||
<TooltipProvider>
|
|
||||||
<MediaView {...baseProps} videoEnabled={false} />
|
|
||||||
</TooltipProvider>,
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
screen.getByRole("img", { name: "@alice:example.com" }),
|
|
||||||
).toBeVisible();
|
|
||||||
expect(screen.getByTestId("video")).not.toBeVisible();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ export const MediaView: FC<Props> = ({
|
|||||||
style={style}
|
style={style}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
data-testid="videoTile"
|
data-testid="videoTile"
|
||||||
|
data-video-enabled={video && videoEnabled}
|
||||||
data-video-fit={videoFit}
|
data-video-fit={videoFit}
|
||||||
data-bg-style={bgStyle}
|
data-bg-style={bgStyle}
|
||||||
{...props}
|
{...props}
|
||||||
@@ -152,7 +153,6 @@ export const MediaView: FC<Props> = ({
|
|||||||
// There's no reason for this to be focusable
|
// There's no reason for this to be focusable
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
disablePictureInPicture
|
disablePictureInPicture
|
||||||
style={{ display: video && videoEnabled ? "block" : "none" }}
|
|
||||||
data-testid="video"
|
data-testid="video"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
scroll-behavior: smooth; */
|
scroll-behavior: smooth; */
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile.maximised .contents {
|
.tile[data-maximised="true"] .contents {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
--media-view-fg-inset: 10px;
|
--media-view-fg-inset: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maximised .item {
|
.tile[data-maximised="true"] .item {
|
||||||
/* Ensure that foreground elements lie within the safe area */
|
/* Ensure that foreground elements lie within the safe area */
|
||||||
--media-view-fg-inset: calc(var(--call-view-safe-area-inset-top, 0px) + 10px)
|
--media-view-fg-inset: calc(var(--call-view-safe-area-inset-top, 0px) + 10px)
|
||||||
calc(env(safe-area-inset-right) + 10px)
|
calc(env(safe-area-inset-right) + 10px)
|
||||||
@@ -191,7 +191,7 @@ Please see LICENSE in the repository root for full details.
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.maximised .indicators {
|
.tile[data-maximised="true"] .indicators {
|
||||||
inset-block-end: calc(-1 * var(--cpd-space-4x) - 2px);
|
inset-block-end: calc(-1 * var(--cpd-space-4x) - 2px);
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -254,6 +254,7 @@ interface SpotlightItemProps {
|
|||||||
* Whether this item should act as a scroll snapping point.
|
* Whether this item should act as a scroll snapping point.
|
||||||
*/
|
*/
|
||||||
snap: boolean;
|
snap: boolean;
|
||||||
|
className?: string;
|
||||||
"aria-hidden"?: boolean;
|
"aria-hidden"?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,6 +269,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
|||||||
focusable,
|
focusable,
|
||||||
intersectionObserver$,
|
intersectionObserver$,
|
||||||
snap,
|
snap,
|
||||||
|
className,
|
||||||
"aria-hidden": ariaHidden,
|
"aria-hidden": ariaHidden,
|
||||||
}) => {
|
}) => {
|
||||||
const ourRef = useRef<HTMLDivElement | null>(null);
|
const ourRef = useRef<HTMLDivElement | null>(null);
|
||||||
@@ -294,7 +296,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
|||||||
const baseProps: SpotlightItemBaseProps & RefAttributes<HTMLDivElement> = {
|
const baseProps: SpotlightItemBaseProps & RefAttributes<HTMLDivElement> = {
|
||||||
ref,
|
ref,
|
||||||
"data-id": vm.id,
|
"data-id": vm.id,
|
||||||
className: classNames(styles.item, { [styles.snap]: snap }),
|
className: classNames(className, styles.item, { [styles.snap]: snap }),
|
||||||
targetWidth,
|
targetWidth,
|
||||||
targetHeight,
|
targetHeight,
|
||||||
userId: vm.userId,
|
userId: vm.userId,
|
||||||
@@ -398,6 +400,10 @@ interface Props {
|
|||||||
showRingingStatus: boolean;
|
showRingingStatus: boolean;
|
||||||
focusable: boolean;
|
focusable: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
/**
|
||||||
|
* CSS class of the individual spotlight items.
|
||||||
|
*/
|
||||||
|
itemClassName?: string;
|
||||||
style?: ComponentProps<typeof animated.div>["style"];
|
style?: ComponentProps<typeof animated.div>["style"];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,6 +419,7 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
showRingingStatus,
|
showRingingStatus,
|
||||||
focusable = true,
|
focusable = true,
|
||||||
className,
|
className,
|
||||||
|
itemClassName,
|
||||||
style,
|
style,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -500,9 +507,8 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
return (
|
return (
|
||||||
<animated.div
|
<animated.div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={classNames(className, styles.tile, {
|
className={classNames(className, styles.tile)}
|
||||||
[styles.maximised]: maximised,
|
data-maximised={maximised}
|
||||||
})}
|
|
||||||
style={style}
|
style={style}
|
||||||
>
|
>
|
||||||
{canGoBack && (
|
{canGoBack && (
|
||||||
@@ -532,6 +538,7 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
// remove all scroll snap points except for just the one media
|
// remove all scroll snap points except for just the one media
|
||||||
// that we want to bring into view
|
// that we want to bring into view
|
||||||
snap={scrollToId === null || scrollToId === vm.id}
|
snap={scrollToId === null || scrollToId === vm.id}
|
||||||
|
className={itemClassName}
|
||||||
aria-hidden={(scrollToId ?? visibleId) !== vm.id}
|
aria-hidden={(scrollToId ?? visibleId) !== vm.id}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|||||||
Reference in New Issue
Block a user