Add a fullscreen button that uses the element request Fullscreen browser api (#3447)
* Add a fullscreen button that uses the element request Fullscreen browser api Signed-off-by: Timo K <toger5@hotmail.de> * use body instead of root node Signed-off-by: Timo K <toger5@hotmail.de> --------- Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
6
src/icons/FullScreenMaximise.svg
Normal file
6
src/icons/FullScreenMaximise.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M4 14C4.55228 14 5 14.4477 5 15V19H9C9.55228 19 10 19.4477 10 20C10 20.5523 9.55228 21 9 21H3V15C3 14.4477 3.44772 14 4 14Z"/>
|
||||||
|
<path d="M20 14C20.5523 14 21 14.4477 21 15V21H15C14.4477 21 14 20.5523 14 20C14 19.4477 14.4477 19 15 19H19V15C19 14.4477 19.4477 14 20 14Z" />
|
||||||
|
<path d="M9 3C9.55228 3 10 3.44772 10 4C10 4.55228 9.55228 5 9 5H5V9C5 9.55228 4.55228 10 4 10C3.44772 10 3 9.55228 3 9V3H9Z" />
|
||||||
|
<path d="M21 9C21 9.55228 20.5523 10 20 10C19.4477 10 19 9.55228 19 9V5H15C14.4477 5 14 4.55228 14 4C14 3.44772 14.4477 3 15 3H21V9Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 658 B |
6
src/icons/FullScreenMinimise.svg
Normal file
6
src/icons/FullScreenMinimise.svg
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10 20C10 20.5523 9.55228 21 9 21C8.44772 21 8 20.5523 8 20V16H4C3.44772 16 3 15.5523 3 15C3 14.4477 3.44772 14 4 14H10V20Z" />
|
||||||
|
<path d="M20 14C20.5523 14 21 14.4477 21 15C21 15.5523 20.5523 16 20 16H16V20C16 20.5523 15.5523 21 15 21C14.4477 21 14 20.5523 14 20V14H20Z" />
|
||||||
|
<path d="M9 3C9.55228 3 10 3.44772 10 4V10H4C3.44772 10 3 9.55228 3 9C3 8.44772 3.44772 8 4 8H8V4C8 3.44772 8.44772 3 9 3Z" />
|
||||||
|
<path d="M15 3C15.5523 3 16 3.44772 16 4V8H20C20.5523 8 21 8.44772 21 9C21 9.55228 20.5523 10 20 10H14V4C14 3.44772 14.4477 3 15 3Z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 656 B |
@@ -88,40 +88,48 @@ Please see LICENSE in the repository root for full details.
|
|||||||
padding: var(--cpd-space-2x);
|
padding: var(--cpd-space-2x);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: var(--cpd-radius-pill-effect);
|
border-radius: var(--cpd-radius-pill-effect);
|
||||||
background: var(--cpd-color-alpha-gray-1400);
|
background: rgba(from var(--cpd-color-gray-100) r g b / 0.6);
|
||||||
box-shadow: var(--small-drop-shadow);
|
box-shadow: var(--small-drop-shadow);
|
||||||
transition:
|
transition:
|
||||||
opacity 0.15s,
|
opacity 0.15s,
|
||||||
background-color 0.1s;
|
background-color 0.1s;
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
--inset: 6px;
|
--inset: 6px;
|
||||||
inset-block-end: var(--inset);
|
inset-block-end: var(--inset);
|
||||||
inset-inline-end: var(--inset);
|
inset-inline-end: var(--inset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bottomRightButtons {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--cpd-space-2x);
|
||||||
|
position: absolute;
|
||||||
|
inset-block-end: var(--cpd-space-1x);
|
||||||
|
inset-inline-end: var(--cpd-space-1x);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.expand > svg {
|
.expand > svg {
|
||||||
display: block;
|
display: block;
|
||||||
color: var(--cpd-color-icon-on-solid-primary);
|
color: var(--cpd-color-icon-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (hover) {
|
@media (hover) {
|
||||||
.expand:hover {
|
.expand:hover {
|
||||||
background: var(--cpd-color-bg-action-primary-hovered);
|
background: var(--cpd-color-gray-400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.expand:active {
|
.expand:active {
|
||||||
background: var(--cpd-color-bg-action-primary-pressed);
|
background: var(--cpd-color-gray-100);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (hover) {
|
@media (hover) {
|
||||||
.tile:hover > button {
|
.tile:hover > div > button {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tile:has(:focus-visible) > button {
|
.tile:has(:focus-visible) > div > button {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ import classNames from "classnames";
|
|||||||
import { type TrackReferenceOrPlaceholder } from "@livekit/components-core";
|
import { type TrackReferenceOrPlaceholder } from "@livekit/components-core";
|
||||||
import { type RoomMember } from "matrix-js-sdk";
|
import { type RoomMember } from "matrix-js-sdk";
|
||||||
|
|
||||||
|
import FullScreenMaximiseIcon from "../icons/FullScreenMaximise.svg?react";
|
||||||
|
import FullScreenMinimiseIcon from "../icons/FullScreenMinimise.svg?react";
|
||||||
import { MediaView } from "./MediaView";
|
import { MediaView } from "./MediaView";
|
||||||
import styles from "./SpotlightTile.module.css";
|
import styles from "./SpotlightTile.module.css";
|
||||||
import {
|
import {
|
||||||
@@ -210,6 +212,26 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
const canGoBack = visibleIndex > 0;
|
const canGoBack = visibleIndex > 0;
|
||||||
const canGoToNext = visibleIndex !== -1 && visibleIndex < media.length - 1;
|
const canGoToNext = visibleIndex !== -1 && visibleIndex < media.length - 1;
|
||||||
|
|
||||||
|
const isFullscreen = useCallback((): boolean => {
|
||||||
|
const rootElement = document.body;
|
||||||
|
if (rootElement && document.fullscreenElement) return true;
|
||||||
|
return false;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const FullScreenIcon = isFullscreen()
|
||||||
|
? FullScreenMinimiseIcon
|
||||||
|
: FullScreenMaximiseIcon;
|
||||||
|
|
||||||
|
const onToggleFullscreen = useCallback(() => {
|
||||||
|
const rootElement = document.body;
|
||||||
|
if (!rootElement) return;
|
||||||
|
if (isFullscreen()) {
|
||||||
|
void document?.exitFullscreen();
|
||||||
|
} else {
|
||||||
|
void rootElement.requestFullscreen();
|
||||||
|
}
|
||||||
|
}, [isFullscreen]);
|
||||||
|
|
||||||
// To keep track of which item is visible, we need an intersection observer
|
// To keep track of which item is visible, we need an intersection observer
|
||||||
// hooked up to the root element and the items. Because the items will run
|
// hooked up to the root element and the items. Because the items will run
|
||||||
// their effects before their parent does, we need to do this dance with an
|
// their effects before their parent does, we need to do this dance with an
|
||||||
@@ -292,17 +314,28 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
{onToggleExpanded && (
|
<div className={styles.bottomRightButtons}>
|
||||||
<button
|
<button
|
||||||
className={classNames(styles.expand)}
|
className={classNames(styles.expand)}
|
||||||
aria-label={
|
aria-label={"maximise"}
|
||||||
expanded ? t("video_tile.collapse") : t("video_tile.expand")
|
onClick={onToggleFullscreen}
|
||||||
}
|
|
||||||
onClick={onToggleExpanded}
|
|
||||||
>
|
>
|
||||||
<ToggleExpandIcon aria-hidden width={20} height={20} />
|
<FullScreenIcon aria-hidden width={20} height={20} />
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
|
{onToggleExpanded && (
|
||||||
|
<button
|
||||||
|
className={classNames(styles.expand)}
|
||||||
|
aria-label={
|
||||||
|
expanded ? t("video_tile.collapse") : t("video_tile.expand")
|
||||||
|
}
|
||||||
|
onClick={onToggleExpanded}
|
||||||
|
>
|
||||||
|
<ToggleExpandIcon aria-hidden width={20} height={20} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{canGoToNext && (
|
{canGoToNext && (
|
||||||
<button
|
<button
|
||||||
className={classNames(styles.advance, styles.next)}
|
className={classNames(styles.advance, styles.next)}
|
||||||
|
|||||||
Reference in New Issue
Block a user