Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions src/common/YoutubeVideoEmbed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: MIT
*/
import { styled } from "styled-system/jsx";
import { AspectRatio } from "styled-system/jsx";

export interface YoutubeVideo {
alt: string;
Expand All @@ -18,20 +18,20 @@ interface YoutubeVideoProps {
const YoutubeVideoEmbed = ({ alt, youtubeId }: YoutubeVideoProps) => {
return (
<figure>
<styled.iframe
aspectRatio="16 / 9"
width="100%"
// Avoid youtube cookie. rel=0 should limit related videos to our channel.
// Once we have translated videos we can try e.g. cc_lang_pref=fr
// but we'll need to check our codes match theirs.
title={alt}
src={`https://www.youtube-nocookie.com/embed/${
youtubeId ? encodeURIComponent(youtubeId.trim()) : ""
}?rel=0&cc_load_policy=1`}
allow="encrypted-media"
frameBorder="0"
allowFullScreen
/>
<AspectRatio ratio={16 / 9}>
<iframe
// Avoid youtube cookie. rel=0 should limit related videos to our channel.
// Once we have translated videos we can try e.g. cc_lang_pref=fr
// but we'll need to check our codes match theirs.
title={alt}
src={`https://www.youtube-nocookie.com/embed/${
youtubeId ? encodeURIComponent(youtubeId.trim()) : ""
}?rel=0&cc_load_policy=1`}
allow="encrypted-media"
frameBorder="0"
allowFullScreen
/>
</AspectRatio>
</figure>
);
};
Expand Down
20 changes: 20 additions & 0 deletions src/common/documentation-styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,26 @@
*/
import { css } from "styled-system/css";

// Follows the pattern used by the AspectRatio component which uses a
// percentage-padding spacer rather than the aspect-ratio property, which is
// not supported in Safari 14. Only apply when --aspect-ratio-padding is set or
// the wrapper collapses to zero height.
export const runtimeAspectRatioClass = css({
position: "relative",
_before: {
content: '""',
display: "block",
height: 0,
paddingBottom: "var(--aspect-ratio-padding)",
},
"& > *": {
position: "absolute",
inset: 0,
width: "100%",
height: "100%",
},
});

// A class rather than a style object: Panda can only statically extract
// styles written literally at the definition site, not objects spread into
// a css prop elsewhere.
Expand Down
10 changes: 8 additions & 2 deletions src/common/imageUrlBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@ export const imageUrlBuilder = unconfiguredImageUrlBuilder()
.dpr(window.devicePixelRatio ?? 1)
.quality(defaultQuality);

export const getAspectRatio = (imageRef: string): string | undefined => {
/**
* Percentage padding-bottom matching the image's aspect ratio, read from the
* dimensions embedded in a Sanity image reference. Used to reserve an image's
* height before it loads via a padding spacer rather than the aspect-ratio
* property, which Safari 14 does not support.
*/
export const getAspectRatioPadding = (imageRef: string): string | undefined => {
const dimensionsArr = imageRef.match(/\d+x\d+/g);
if (!dimensionsArr) {
return undefined;
}
const dimensions = dimensionsArr.join().split("x");
const [width, height] = dimensions.map((n: string) => Number(n));
return (width / height).toString();
return `${(height / width) * 100}%`;
};
2 changes: 1 addition & 1 deletion src/documentation/common/DocumentationContent.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ describe("DocumentationContent", () => {
];
const view = render(<DocumentationContent blocks={content} />);
expect(view.container.innerHTML).toMatchInlineSnapshot(
`"<div class="d_flex flex-d_column gap_3 mt_3"><img src="https://cdn.sanity.io/images/project/dataset/9fccaf51a164fedc98662188593de19bfb9be8ad-435x512.png?w=300&amp;q=80&amp;fit=max&amp;auto=format" alt="micro:bit showing X axis going across the front, Y axis going down and up, Z axis going back to front" class="max-w_100% w_300px bdr_lg bd_solid_1px bd-c_gray.300"></div>"`
`"<div class="d_flex flex-d_column gap_3 mt_3"><div class="w_300px max-w_100% pos_relative before:content_&quot;&quot; before:d_block before:h_0 before:pb_var(--aspect-ratio-padding) [&amp;_>_*]:pos_absolute [&amp;_>_*]:inset_0 [&amp;_>_*]:w_100% [&amp;_>_*]:h_100%" style="--aspect-ratio-padding: 117.70114942528735%;"><img src="https://cdn.sanity.io/images/project/dataset/9fccaf51a164fedc98662188593de19bfb9be8ad-435x512.png?w=300&amp;q=80&amp;fit=max&amp;auto=format" alt="micro:bit showing X axis going across the front, Y axis going down and up, Z axis going back to front" class="max-w_100% bdr_lg bd_solid_1px bd-c_gray.300"></div></div>"`
);
});
});
40 changes: 25 additions & 15 deletions src/documentation/common/DocumentationContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import BlockContent from "@sanity/block-content-to-react";
import React, { ReactNode, useContext, useMemo } from "react";
import { RiExternalLinkLine } from "react-icons/ri";
import { Box, Stack } from "styled-system/jsx";
import { getAspectRatio, imageUrlBuilder } from "../../common/imageUrlBuilder";
import { runtimeAspectRatioClass } from "../../common/documentation-styles";
import {
getAspectRatioPadding,
imageUrlBuilder,
} from "../../common/imageUrlBuilder";
import { PortableText, SimpleImage } from "../../common/sanity";
import { useRouterState } from "../../router-hooks";
import {
Expand Down Expand Up @@ -200,23 +204,29 @@ const serializers = {
<ContextualCodeEmbed code={main} />
),
simpleImage: (props: SerializerNodeProps<SimpleImage>) => {
const padding = getAspectRatioPadding(props.node.asset._ref);
return (
<ImageWithFallback
src={imageUrlBuilder
.image(props.node.asset)
.width(300)
.fit("max")
.url()}
ignoreFallback={navigator.onLine}
fallback={<OfflineImageFallback width={300} />}
alt={props.node.alt}
<Box
width="300px"
borderRadius="lg"
border="solid 1px"
borderColor="gray.300"
maxWidth="100%"
className={padding ? runtimeAspectRatioClass : undefined}
// Runtime value derived from the image reference.
style={{ aspectRatio: getAspectRatio(props.node.asset._ref) }}
/>
style={{ "--aspect-ratio-padding": padding } as React.CSSProperties}
>
<ImageWithFallback
src={imageUrlBuilder
.image(props.node.asset)
.width(300)
.fit("max")
.url()}
ignoreFallback={navigator.onLine}
fallback={<OfflineImageFallback width={300} />}
alt={props.node.alt}
borderRadius="lg"
border="solid 1px"
borderColor="gray.300"
/>
</Box>
);
},
},
Expand Down
52 changes: 34 additions & 18 deletions src/documentation/ideas/IdeasDocumentation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,19 @@
* SPDX-License-Identifier: MIT
*/
import { Link, Text } from "@microbit/ui";
import { ReactNode, useCallback, useRef } from "react";
import { CSSProperties, ReactNode, useCallback, useRef } from "react";
import { FormattedMessage, useIntl } from "react-intl";
import { Grid, Stack } from "styled-system/jsx";
import { Box, Grid, Stack } from "styled-system/jsx";
import AreaHeading from "../../common/AreaHeading";
import { docStylesClass } from "../../common/documentation-styles";
import {
docStylesClass,
runtimeAspectRatioClass,
} from "../../common/documentation-styles";
import HeadedScrollablePanel from "../../common/HeadedScrollablePanel";
import { getAspectRatio, imageUrlBuilder } from "../../common/imageUrlBuilder";
import {
getAspectRatioPadding,
imageUrlBuilder,
} from "../../common/imageUrlBuilder";
import { useResizeObserverContentRect } from "../../common/use-resize-observer";
import { Anchor, useRouterTabSlug } from "../../router-hooks";
import { useAnimationDirection } from "../common/documentation-animation-hooks";
Expand Down Expand Up @@ -75,6 +81,10 @@ const ActiveLevel = ({
const numCols =
!contentWidth || contentWidth > 1100 ? 3 : contentWidth > 550 ? 2 : 1;
if (activeIdea) {
// Runtime value derived from the image reference.
const imagePadding = activeIdea.image
? getAspectRatioPadding(activeIdea.image.asset._ref)
: undefined;
return (
<HeadedScrollablePanel
key={activeIdea.slug.current}
Expand All @@ -99,21 +109,27 @@ const ActiveLevel = ({
className={docStylesClass}
>
{activeIdea.image && (
<ImageWithFallback
src={imageUrlBuilder
.image(activeIdea.image.asset)
.fit("max")
.url()}
ignoreFallback={navigator.onLine}
fallback={<OfflineImageFallback width={600} />}
alt=""
borderTopRadius="lg"
<Box
width="600px"
// Runtime value derived from the image reference.
style={{
aspectRatio: getAspectRatio(activeIdea.image.asset._ref),
}}
/>
maxWidth="100%"
className={imagePadding ? runtimeAspectRatioClass : undefined}
style={
{
"--aspect-ratio-padding": imagePadding,
} as CSSProperties
}
>
<ImageWithFallback
src={imageUrlBuilder
.image(activeIdea.image.asset)
.fit("max")
.url()}
ignoreFallback={navigator.onLine}
fallback={<OfflineImageFallback width={600} />}
alt=""
borderTopRadius="lg"
/>
</Box>
)}

<DocumentationContextProvider
Expand Down
26 changes: 13 additions & 13 deletions src/simulator/Simulator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { usePrevious } from "@microbit/ui";
import { useEffect, useMemo, useRef, useState } from "react";
import { IntlShape, useIntl } from "react-intl";
import { token } from "styled-system/tokens";
import { Box, Flex, styled, VStack } from "styled-system/jsx";
import { AspectRatio, Box, Flex, VStack } from "styled-system/jsx";
import HideSplitViewButton from "../common/SplitView/HideSplitViewButton";
import { topBarHeight } from "../deployment/misc";
import { DeviceContextProvider } from "../device/device-hooks";
Expand Down Expand Up @@ -122,18 +122,18 @@ const Simulator = ({
// Runtime value from the layout.
style={{ minWidth: minWidth }}
>
<styled.iframe
aspectRatio="191.27 / 155.77"
width="100%"
ref={ref}
src={url}
title={simulatorTitle}
name={simulatorTitle}
frameBorder="no"
scrolling="no"
allow="autoplay;microphone"
sandbox="allow-scripts allow-same-origin"
/>
<AspectRatio ratio={191.27 / 155.77}>
<iframe
ref={ref}
src={url}
title={simulatorTitle}
name={simulatorTitle}
frameBorder="no"
scrolling="no"
allow="autoplay;microphone"
sandbox="allow-scripts allow-same-origin"
/>
</AspectRatio>
<SimulatorActionBar
aria-label={intl.formatMessage({ id: "simulator-actions" })}
css={{ overflow: "hidden" }}
Expand Down
22 changes: 14 additions & 8 deletions src/workbench/AboutDialog/AboutDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,15 @@ import {
import { ReactNode, useCallback, useState } from "react";
import { RiFileCopy2Line, RiGithubFill } from "react-icons/ri";
import { FormattedMessage, useIntl } from "react-intl";
import { Box, Flex, Grid, HStack, styled, VStack } from "styled-system/jsx";
import {
AspectRatio,
Box,
Flex,
Grid,
HStack,
styled,
VStack,
} from "styled-system/jsx";
import ExpandCollapseIcon from "../../common/ExpandCollapseIcon";
import { useDeployment } from "../../deployment";
import { microPythonConfig } from "../../micropython/micropython";
Expand Down Expand Up @@ -125,18 +133,17 @@ const AboutDialog = ({ isOpen, onClose, finalFocusRef }: AboutDialogProps) => {
</Text>
<Grid columns={{ base: 1, md: 2 }} gap="5" width="100%">
<Box>
<Box
<AspectRatio
ml="auto"
mr="auto"
maxWidth={{ base: "303px", md: "unset" }}
css={{ aspectRatio: "690 / 562" }}
ratio={690 / 562}
>
<Image
src={microbitHeartImage}
alt={intl.formatMessage({ id: "microbit-hearts-alt" })}
css={{ width: "100%", height: "100%", objectFit: "contain" }}
/>
</Box>
</AspectRatio>
</Box>
<VStack alignItems="center" justifyContent="center" gap="4">
<styled.table css={{ fontSize: "sm" }}>
Expand Down Expand Up @@ -256,13 +263,12 @@ const MicroPythonSection = () => {
const intl = useIntl();
return (
<VStack gap="4" mt="8" pl="5" pr="5">
<Box width="100%" css={{ aspectRatio: "1035 / 423" }}>
<AspectRatio width="100%" ratio={1035 / 423}>
<Image
src={comicImage}
alt={intl.formatMessage({ id: "about-comic" })}
css={{ width: "100%", height: "100%", objectFit: "contain" }}
/>
</Box>
</AspectRatio>
<Grid columns={{ base: 1, lg: 2 }} gap="4" textAlign="center">
<Text fontSize="md">
<FormattedMessage
Expand Down