Skip to content

Commit

Permalink
Implement most of the remaining layout changes
Browse files Browse the repository at this point in the history
Includes the mobile UX optimizations and the tweaks we've made to cut down on wasted space, but does not yet include the change to embed the spotlight tile within the grid.
  • Loading branch information
robintown committed Jul 12, 2024
1 parent a16f235 commit 2440037
Show file tree
Hide file tree
Showing 25 changed files with 761 additions and 497 deletions.
1 change: 0 additions & 1 deletion src/Header.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ limitations under the License.
user-select: none;
flex-shrink: 0;
padding-inline: var(--inline-content-inset);
padding-block-end: var(--cpd-space-4x);
}

.nav {
Expand Down
8 changes: 6 additions & 2 deletions src/grid/CallLayout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ export interface SpotlightTileModel {
export type TileModel = GridTileModel | SpotlightTileModel;

export interface CallLayoutOutputs<Model> {
/**
* Whether the scrolling layer of the layout should appear on top.
*/
scrollingOnTop: boolean;
/**
* The visually fixed (non-scrolling) layer of the layout.
*/
Expand Down Expand Up @@ -121,7 +125,7 @@ export function arrangeTiles(
);
let rows = Math.ceil(tileCount / columns);

let tileWidth = (width - (columns - 1) * gap) / columns;
let tileWidth = (width - (columns + 1) * gap) / columns;
let tileHeight = (minHeight - (rows - 1) * gap) / rows;

// Impose a minimum width and height on the tiles
Expand All @@ -132,7 +136,7 @@ export function arrangeTiles(
// c = (W + g) / (w + g).
columns = Math.floor((width + gap) / (tileMinWidth + gap));
rows = Math.ceil(tileCount / columns);
tileWidth = (width - (columns - 1) * gap) / columns;
tileWidth = (width - (columns + 1) * gap) / columns;
tileHeight = (minHeight - (rows - 1) * gap) / rows;
}
if (tileHeight < tileMinHeight) tileHeight = tileMinHeight;
Expand Down
3 changes: 1 addition & 2 deletions src/grid/GridLayout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ limitations under the License.

.fixed,
.scrolling {
margin-inline: var(--inline-content-inset);
block-size: 100%;
}

Expand All @@ -41,7 +40,7 @@ limitations under the License.
position: absolute;
inline-size: 404px;
block-size: 233px;
inset: -12px;
inset: 0;
}

.fixed > .slot[data-block-alignment="start"] {
Expand Down
2 changes: 2 additions & 0 deletions src/grid/GridLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export const makeGridLayout: CallLayout<GridLayoutModel> = ({
minBounds,
spotlightAlignment,
}) => ({
scrollingOnTop: false,

// The "fixed" (non-scrolling) part of the layout is where the spotlight tile
// lives
fixed: forwardRef(function GridLayoutFixed({ model, Slot }, ref) {
Expand Down
2 changes: 0 additions & 2 deletions src/grid/OneOnOneLayout.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ limitations under the License.
*/

.layer {
margin-inline: var(--inline-content-inset);
block-size: 100%;
display: grid;
place-items: center;
Expand All @@ -36,7 +35,6 @@ limitations under the License.
position: absolute;
inline-size: 404px;
block-size: 233px;
inset: -12px;
}

.slot[data-block-alignment="start"] {
Expand Down
52 changes: 4 additions & 48 deletions src/grid/OneOnOneLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,19 @@ import { useObservableEagerState } from "observable-hooks";
import classNames from "classnames";

import { OneOnOneLayout as OneOnOneLayoutModel } from "../state/CallViewModel";
import {
CallLayout,
GridTileModel,
SpotlightTileModel,
arrangeTiles,
} from "./CallLayout";
import { CallLayout, GridTileModel, arrangeTiles } from "./CallLayout";
import { useReactiveState } from "../useReactiveState";
import styles from "./OneOnOneLayout.module.css";
import { DragCallback } from "./Grid";

export const makeOneOnOneLayout: CallLayout<OneOnOneLayoutModel> = ({
minBounds,
spotlightAlignment,
pipAlignment,
}) => ({
fixed: forwardRef(function OneOnOneLayoutFixed({ model, Slot }, ref) {
const { width, height } = useObservableEagerState(minBounds);
const spotlightAlignmentValue = useObservableEagerState(spotlightAlignment);

const [generation] = useReactiveState<number>(
(prev) => (prev === undefined ? 0 : prev + 1),
[width, height, model.spotlight === undefined, spotlightAlignmentValue],
);

const spotlightTileModel: SpotlightTileModel | undefined = useMemo(
() =>
model.spotlight && {
type: "spotlight",
vms: model.spotlight,
maximised: false,
},
[model.spotlight],
);

const onDragSpotlight: DragCallback = useCallback(
({ xRatio, yRatio }) =>
spotlightAlignment.next({
block: yRatio < 0.5 ? "start" : "end",
inline: xRatio < 0.5 ? "start" : "end",
}),
[],
);
scrollingOnTop: false,

return (
<div ref={ref} data-generation={generation} className={styles.layer}>
{spotlightTileModel && (
<Slot
className={classNames(styles.slot, styles.spotlight)}
id="spotlight"
model={spotlightTileModel}
onDrag={onDragSpotlight}
data-block-alignment={spotlightAlignmentValue.block}
data-inline-alignment={spotlightAlignmentValue.inline}
/>
)}
</div>
);
fixed: forwardRef(function OneOnOneLayoutFixed(_props, ref) {
return <div ref={ref} data-generation={0} />;
}),

scrolling: forwardRef(function OneOnOneLayoutScrolling({ model, Slot }, ref) {
Expand Down
47 changes: 47 additions & 0 deletions src/grid/SpotlightExpandedLayout.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
Copyright 2024 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.layer {
block-size: 100%;
}

.spotlight {
block-size: 100%;
inline-size: 100%;
}

.pip {
position: absolute;
inline-size: 180px;
block-size: 135px;
inset: var(--cpd-space-4x);
}

.pip[data-block-alignment="start"] {
inset-block-end: unset;
}

.pip[data-block-alignment="end"] {
inset-block-start: unset;
}

.pip[data-inline-alignment="start"] {
inset-inline-end: unset;
}

.pip[data-inline-alignment="end"] {
inset-inline-start: unset;
}
99 changes: 99 additions & 0 deletions src/grid/SpotlightExpandedLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
Copyright 2024 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import { forwardRef, useCallback, useMemo } from "react";
import { useObservableEagerState } from "observable-hooks";

import { SpotlightExpandedLayout as SpotlightExpandedLayoutModel } from "../state/CallViewModel";
import { CallLayout, GridTileModel, SpotlightTileModel } from "./CallLayout";
import { DragCallback } from "./Grid";
import styles from "./SpotlightExpandedLayout.module.css";
import { useReactiveState } from "../useReactiveState";

export const makeSpotlightExpandedLayout: CallLayout<
SpotlightExpandedLayoutModel
> = ({ minBounds, pipAlignment }) => ({
scrollingOnTop: true,

fixed: forwardRef(function SpotlightExpandedLayoutFixed(
{ model, Slot },
ref,
) {
const { width, height } = useObservableEagerState(minBounds);

const [generation] = useReactiveState<number>(
(prev) => (prev === undefined ? 0 : prev + 1),
[width, height],
);

const spotlightTileModel: SpotlightTileModel = useMemo(
() => ({ type: "spotlight", vms: model.spotlight, maximised: true }),
[model.spotlight],
);

return (
<div ref={ref} data-generation={generation} className={styles.layer}>
<Slot
className={styles.spotlight}
id="spotlight"
model={spotlightTileModel}
/>
</div>
);
}),

scrolling: forwardRef(function SpotlightExpandedLayoutScrolling(
{ model, Slot },
ref,
) {
const { width, height } = useObservableEagerState(minBounds);
const pipAlignmentValue = useObservableEagerState(pipAlignment);

const [generation] = useReactiveState<number>(
(prev) => (prev === undefined ? 0 : prev + 1),
[width, height, model.pip === undefined, pipAlignmentValue],
);

const pipTileModel: GridTileModel | undefined = useMemo(
() => model.pip && { type: "grid", vm: model.pip },
[model.pip],
);

const onDragPip: DragCallback = useCallback(
({ xRatio, yRatio }) =>
pipAlignment.next({
block: yRatio < 0.5 ? "start" : "end",
inline: xRatio < 0.5 ? "start" : "end",
}),
[],
);

return (
<div ref={ref} data-generation={generation} className={styles.layer}>
{pipTileModel && (
<Slot
className={styles.pip}
id="pip"
model={pipTileModel}
onDrag={onDragPip}
data-block-alignment={pipAlignmentValue.block}
data-inline-alignment={pipAlignmentValue.inline}
/>
)}
</div>
);
}),
});
54 changes: 54 additions & 0 deletions src/grid/SpotlightLandscapeLayout.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
Copyright 2024 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

.layer {
block-size: 100%;
display: grid;
--gap: 20px;
gap: var(--gap);
--grid-slot-width: 180px;
grid-template-columns: 1fr var(--grid-slot-width);
grid-template-rows: minmax(1fr, auto);
padding-inline: var(--gap);
}

.spotlight {
container: spotlight / size;
display: grid;
place-items: center;
}

/* CSS makes us put a condition here, even though all we want to do is
unconditionally select the container so we can use cq units */
@container spotlight (width > 0) {
.spotlight > .slot {
inline-size: min(100cqi, 100cqb * (17 / 9));
block-size: min(100cqb, 100cqi / (4 / 3));
}
}

.grid {
display: flex;
flex-wrap: wrap;
gap: var(--gap);
justify-content: center;
align-content: center;
}

.grid > .slot {
inline-size: 180px;
block-size: 135px;
}
Loading

0 comments on commit 2440037

Please sign in to comment.