Skip to content

Commit

Permalink
support for landscape
Browse files Browse the repository at this point in the history
  • Loading branch information
Shane Osbourne committed Aug 6, 2024
1 parent a7434f4 commit d369580
Show file tree
Hide file tree
Showing 51 changed files with 395 additions and 190 deletions.
1 change: 0 additions & 1 deletion packages/special-pages/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,6 @@ for (const buildJob of buildJobs) {
format: 'iife',
// external: ['../assets/img/*'],
sourcemap: NODE_ENV === 'development',
// publicPath: '/js',
loader: {
'.js': 'jsx',
'.module.css': 'local-css',
Expand Down
158 changes: 99 additions & 59 deletions packages/special-pages/pages/duckplayer/app/components/App.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { h, Fragment } from "preact";
import cn from "classnames";
import styles from "./App.module.css";
import { Background } from "./Background.jsx";
import { InfoBar, InfoBarContainer } from "./InfoBar.jsx";
Expand All @@ -17,8 +18,10 @@ import cog from "../img/cog.data.svg";
import { BottomNavBar, FloatingBar, TopBar } from "./FloatingBar.jsx";
import { Wordmark } from "./Wordmark.jsx";
import { SwitchProvider } from "../providers/SwitchProvider.jsx";
import { useOrientation } from "../providers/OrientationProvider.jsx";
import { createAppFeaturesFrom } from "../features/app.js";
import { useTypedTranslation } from "../types.js";
import { HideInFocusMode } from "./FocusMode.jsx";


/**
Expand All @@ -27,6 +30,7 @@ import { useTypedTranslation } from "../types.js";
*/
export function App({ embed }) {
const layout = useLayout();
const orientation = useOrientation();
const settings = useSettings();
const features = createAppFeaturesFrom(settings)
return (
Expand All @@ -35,7 +39,7 @@ export function App({ embed }) {
{features.focusMode()}
<main class={styles.app}>
{layout === 'desktop' && <DesktopLayout embed={embed} />}
{layout === 'mobile' && <MobileLayout embed={embed} />}
{layout === 'mobile' && <MobileLayout embed={embed} orientation={orientation} />}
</main>
</>
)
Expand All @@ -61,98 +65,134 @@ function DesktopLayout({embed}) {
)
}

function HideInFocusMode({children, style="fade"}) {
return (
<div class={styles.hideInFocus} data-style={style}>{children}</div>
)
}

/**
* @param {object} props
* @param {ReturnType<useOrientation>} props.orientation
* @param {import("../embed-settings.js").EmbedSettings|null} props.embed
*/
function MobileLayout({embed}) {
function MobileLayout({orientation, embed}) {
const platformName = usePlatformName();
const insetPlayer = orientation === "portrait";
const classes = cn({
[styles.portrait]: orientation === "portrait",
[styles.landscape]: orientation === "landscape"
});
return (
<div class={styles.mobile}>
<div class={styles.header}>
<HideInFocusMode>
<MobileHeader />
</HideInFocusMode>
</div>
<div class={styles.main}>
<MobilePlayer embed={embed} />
</div>
<div class={styles.footer}>
<HideInFocusMode>
<MobileFooter embed={embed} />
</HideInFocusMode>
<div class={classes}>
{orientation === "portrait" && (
<div class={styles.header}>
<HideInFocusMode>
<TopBar>
<Wordmark />
</TopBar>
</HideInFocusMode>
</div>
)}
<div class={styles.wrapper}>
<div class={styles.main}>
<PlayerContainer inset={insetPlayer}>
<PlayerInternal inset={insetPlayer}>
{embed === null && <PlayerError layout={'mobile'} kind={'invalid-id'}/>}
{embed !== null && <Player src={embed.toEmbedUrl()} layout={'mobile'}/>}
{orientation === "portrait" && (
<SwitchProvider>
<SwitchBarMobile platformName={platformName} />
</SwitchProvider>
)}
</PlayerInternal>
</PlayerContainer>
</div>
{orientation === "landscape" && <LandscapeControls embed={embed} platformName={platformName}/>}
{orientation === "portrait" && <PortraitControls embed={embed} />}
</div>
</div>
)
}

function MobileHeader() {
/**
* How the controls are rendered in Portrait mode.
* @param {object} props
* @param {import("../embed-settings.js").EmbedSettings|null} props.embed
*/
function PortraitControls({embed}) {
return (
<TopBar>
<Wordmark />
</TopBar>
<div className={styles.controls}>
<HideInFocusMode>
<BottomNavBar>
<FloatingBar inset>
<MobileFooter embed={embed}/>
</FloatingBar>
</BottomNavBar>
</HideInFocusMode>
</div>
)
}

/**
* How the controls are rendered in Landscape mode
* @param {object} props
* @param {import("../embed-settings.js").EmbedSettings|null} props.embed
* @param {ImportMeta['platform']} props.platformName - The name of the platform.
*/
function MobilePlayer({embed}) {
const platformName = usePlatformName();
function LandscapeControls({embed, platformName}) {
return (
<PlayerContainer inset>
<PlayerInternal inset>
{embed === null && <PlayerError layout={'mobile'} kind={'invalid-id'} />}
{embed !== null && <Player src={embed.toEmbedUrl()} layout={'mobile'} />}
<div className={styles.rhs}>
<div className={styles.header}>
<HideInFocusMode>
<TopBar>
<Wordmark />
</TopBar>
</HideInFocusMode>
</div>
<div className={styles.controls}>
<HideInFocusMode>
<FloatingBar>
<MobileFooter embed={embed}/>
</FloatingBar>
</HideInFocusMode>
</div>
<div className={styles.switch}>
<SwitchProvider>
<SwitchBarMobile platformName={platformName} />
<SwitchBarMobile platformName={platformName}/>
</SwitchProvider>
</PlayerInternal>
</PlayerContainer>
</div>
</div>
)
}

/**
* @param {object} props
* @param {import("../embed-settings.js").EmbedSettings|null} props.embed
*/
function MobileFooter({ embed }) {
function MobileFooter({embed}) {
const openSettings = useOpenSettingsHandler();
const openInfo = useOpenInfoHandler();
const openOnYoutube = useOpenOnYoutubeHandler();
const { t } = useTypedTranslation();
const {t} = useTypedTranslation();
return (
<BottomNavBar>
<FloatingBar>
<Button
icon={true}
buttonProps={{
"aria-label": t('openInfoButton'),
onClick: openInfo
}}
><Icon src={info}/></Button>
<Button
icon={true}
<>
<Button
icon={true}
buttonProps={{
"aria-label": t('openInfoButton'),
onClick: openInfo
}}
><Icon src={info}/></Button>
<Button
icon={true}
buttonProps={{
"aria-label": t('openSettingsButton'),
onClick: openSettings
}}
><Icon src={cog}/></Button>
<Button fill={true}
buttonProps={{
"aria-label": t('openSettingsButton'),
onClick: openSettings
onClick: () => {
if (embed) openOnYoutube(embed)
}
}}
><Icon src={cog}/></Button>
<Button fill={true}
buttonProps={{
onClick: () => {
if (embed) openOnYoutube(embed)
}
}}
>{t('watchOnYoutube')}</Button>
</FloatingBar>
</BottomNavBar>
>{t('watchOnYoutube')}</Button>
</>
)
}

Expand Down
104 changes: 70 additions & 34 deletions packages/special-pages/pages/duckplayer/app/components/App.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,80 @@
}
}

body[data-layout="mobile"] {
--video-width: 100%;
}

:root [data-layout="desktop"] {
--frame-height: min(
calc(var(--video-width) * calc(9 / 16)),
80vh
)
}

:root [data-layout="mobile"] {
--frame-height: min(
calc(100vw * calc(9 / 16)),
calc(100vh - 250px)
)
:root [data-layout="mobile"][data-orientation="portrait"] {
--video-width: calc(100vw - 32px)
}
:root [data-layout="mobile"][data-orientation="landscape"] {
--video-width: calc(calc(100vw - 32px) * 0.6) /* 60% of the container */
}
@media screen and (max-width: 700px) {
:root [data-layout="mobile"][data-orientation="landscape"] {
--video-width: calc(calc(100vw - 32px) * 0.5) /* 60% of the container */
}
}

:root [data-layout="mobile"][data-player="enabled"] {
:root [data-layout="mobile"] {
--frame-height: min(
calc(100vw * calc(9 / 16)),
calc(100vh - 182px)
calc(var(--video-width) * calc(9 / 16)),
calc(100vh - 32px)
)
}

.app {
max-width: 3840px;
margin: 0 auto;
position: relative;
z-index: 1;
height: 100%;
width: 100%;
max-width: 3840px;
color: rgba(255, 255, 255, 0.85);
}

.mobile {
.portrait {
height: 100%;
display: grid;
align-self: center;
grid-template-areas:
'header'
'main';
grid-template-rows: max-content 1fr;
}

.landscape {
height: 100%;
display: grid;
align-self: center;
align-items: center;
align-content: center;
}

.wrapper {}

.portrait .wrapper {
grid-area: main;
display: grid;
grid-template-areas:
'main'
'footer';
grid-template-rows: max-content 1fr max-content;
'controls';
grid-template-rows: auto max-content;
}

.landscape .wrapper {
display: grid;
grid-template-columns: 60% 1fr;
grid-column-gap: 8px;
background: rgba(0, 0, 0, 0.3);
border-radius: 16px;
padding: 8px;
@media screen and (max-width: 700px) {
grid-template-columns: 50% 1fr;
}
}

.desktop {
Expand All @@ -70,36 +100,42 @@ body[data-layout="mobile"] {
justify-content: center;
}

.rhs {
display: grid;
height: 100%;
grid-template-areas: 'header' 'controls' 'switch';
grid-template-rows: max-content max-content auto;
grid-template-columns: 1fr;
grid-row-gap: 12px;
}

/* When the RHS has a checked checkbox, we can center the other content */
.rhs:has([data-state=completed] [aria-checked="true"]) {
align-content: center;
}

.header {
grid-area: header;
padding-top: 48px;
@media screen and (max-height: 500px) {
padding-top: 32px;
}
}

.main {
grid-area: main;
align-self: center;
}

.footer {
grid-area: footer;
.controls {
grid-area: controls
}

[data-focus-mode="on"] .hideInFocus[data-style="fade"] {
opacity: 0;
visibility: hidden;
.switch {
grid-area: switch
}

[data-focus-mode="on"] .hideInFocus[data-style="slide"] {
opacity: 0;
visibility: hidden;
transform: translateY(-100%);
.landscape .header {
padding-top: 8px;
}

.hideInFocus {
opacity: 1;
visibility: visible;
top: 0;
transition: all .3s;
.landscape .switch {
align-self: end;
}
Loading

0 comments on commit d369580

Please sign in to comment.