Skip to content

Commit 981909b

Browse files
committed
work
1 parent fb928d5 commit 981909b

2 files changed

Lines changed: 56 additions & 61 deletions

File tree

packages/frontend/navi/src/control/control_swap/control_swap.jsx

Lines changed: 44 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77
* Four boxes on a row that is never wide enough for both controls — a phone
88
* answering "what do I search IN" then "what am I looking for". The two end
99
* caps are fixed: same place, same size, in both states, and either of them
10-
* hands the floor to the other side. That is the whole reason the caps are
11-
* OUTSIDE
12-
* the controls rather than drawn inside them (a picker's façade yields a zone
13-
* with `ownTarget`, a field has `Input.UI.LeftSlot`): an icon that lives
14-
* inside its control while open and becomes a pill once closed is a switch
15-
* that moves when you flip it, and the finger that opened the search has to
16-
* travel to close it again. Out here, the same pixel opens and closes.
10+
* hands the floor to the other side. That is the whole reason the caps sit
11+
* OUTSIDE the controls rather than being drawn inside them (a picker's façade
12+
* yields a zone with `ownTarget`, a field has `Input.UI.LeftSlot`): an icon
13+
* that lives inside its control while open and becomes a pill once closed is
14+
* a switch that moves when you flip it: the finger that opened the search has
15+
* to travel to close it again. Out here, the same pixel does both.
1716
*
1817
* Nothing is unmounted. Two elements that never coexist have nothing to
1918
* interpolate between, so both sides stay mounted at all times and only their
@@ -28,11 +27,14 @@
2827
* mid-swap, and a collapsed control is not a zero-width column of stacked
2928
* words silently setting the row's height.
3029
*
31-
* The square end cap cannot be had from CSS: on a flex item whose height comes
32-
* from `align-self: stretch`, `aspect-ratio: 1` does not read that height (the
33-
* main size is resolved from content first, measured the same in chromium,
34-
* webkit and firefox). The row's height is whatever the controls in it happen
35-
* to be, so a cap is measured and given its own height as a width.
30+
* The row has ONE height, and everything in it is that tall: the caps because
31+
* they are squares of it, the two controls because they are stretched to it.
32+
* Nothing is measured — a height read off the controls could only come back
33+
* through a resize observer, and a cap made square with `aspect-ratio` never
34+
* reads it anyway (on a flex item stretched to its line, the main size is
35+
* resolved from content first). It is a length, `--navi-control-swap-size`,
36+
* defaulting to the height of a navi control at its default padding; a row of
37+
* roomier controls says so once, there.
3638
*/
3739

3840
import { elementIsFocusable, findAfter } from "@jsenv/dom";
@@ -47,14 +49,25 @@ import { Button } from "../input/button.jsx";
4749

4850
const css = /* css */ `
4951
.navi_control_swap {
52+
/* One navi control tall, and a caller with roomier controls than that
53+
overrides the length rather than every box that has to match it. */
54+
--x-control-swap-size: var(
55+
--navi-control-swap-size,
56+
calc(
57+
var(--navi-control-line-height) + 2 *
58+
var(--navi-control-padding-y-default) + 2 *
59+
var(--navi-control-border-width)
60+
)
61+
);
62+
63+
height: var(--x-control-swap-size);
5064
align-items: stretch;
5165
5266
> .navi_control_swap_cap {
5367
position: relative;
54-
/* Its own height, measured (see the top comment). Unset until the first
55-
measurement, where the cap is simply as wide as its icon. */
56-
width: var(--navi-control-swap-cap-size);
57-
flex: 0 0 auto;
68+
/* Square on the row: the same length gives the width, the height comes
69+
from the stretch. */
70+
flex: 0 0 var(--x-control-swap-size);
5871
align-items: center;
5972
align-self: stretch;
6073
justify-content: center;
@@ -96,6 +109,14 @@ const css = /* css */ `
96109
flex: 0 0 auto;
97110
flex-direction: column;
98111
justify-content: center;
112+
113+
/* A control is as wide as its content on its own (a navi one is
114+
literally width: fit-content), and here the one holding the floor
115+
has the whole middle to fill — so it is told to, rather than left
116+
to stretch, which an explicit width would win against anyway. */
117+
> * {
118+
width: 100%;
119+
}
99120
}
100121
101122
&[data-collapsed] {
@@ -153,6 +174,10 @@ const css = /* css */ `
153174
* @param animation - On by default: the two slots trade their share of the
154175
* middle over `--navi-control-swap-animation-duration` (0.22s). `false`
155176
* swaps them in one frame; `prefers-reduced-motion` does too.
177+
*
178+
* The row is one length tall — `--navi-control-swap-size`, the height of a navi
179+
* control at its default padding — and the caps are squares of it. Controls
180+
* with a padding of their own need that length said once, here.
156181
*/
157182
export const ControlSwap = (props) => {
158183
import.meta.css = css;
@@ -246,8 +271,6 @@ export const ControlSwap = (props) => {
246271
});
247272
}, [activeSide.name]);
248273

249-
useCapSizeEffect(capRefs);
250-
251274
// Both caps do the same thing, and it is the reason they sit outside the
252275
// controls: whichever one the finger lands on, the floor goes to the other
253276
// side. The same pixel opens the search and closes it.
@@ -336,7 +359,9 @@ const ControlSwapCap = ({ ref, side, slotId, active, onPress }) => {
336359
aria-controls={slotId}
337360
onClick={onPress}
338361
>
339-
<Icon fillLine>{icon}</Icon>
362+
<Icon width="60%" square>
363+
{icon}
364+
</Icon>
340365
{badge ? (
341366
<span className="navi_control_swap_badge" aria-hidden="true">
342367
{badge === true ? null : badge}
@@ -388,44 +413,3 @@ const findElementToFocus = (slot) => {
388413
}
389414
return findAfter(slot, elementIsFocusable, { root: slot });
390415
};
391-
392-
// A cap is square on the row's height, and the row's height is whatever the
393-
// controls in it happen to be — so it is watched rather than computed. Written
394-
// on the cap itself: what it reads back is what it just measured, and a change
395-
// too small to see is dropped so the observer cannot chase itself (the width it
396-
// sets takes room away from the controls, which could in principle change the
397-
// height it reads).
398-
const CAP_SIZE_EPSILON = 0.5;
399-
const useCapSizeEffect = (capRefs) => {
400-
useLayoutEffect(() => {
401-
const sizeWritten = new WeakMap();
402-
const syncCapSize = (capElement) => {
403-
const { height } = capElement.getBoundingClientRect();
404-
const previousHeight = sizeWritten.get(capElement);
405-
if (
406-
previousHeight !== undefined &&
407-
Math.abs(previousHeight - height) < CAP_SIZE_EPSILON
408-
) {
409-
return;
410-
}
411-
sizeWritten.set(capElement, height);
412-
capElement.style.setProperty(
413-
"--navi-control-swap-cap-size",
414-
`${height}px`,
415-
);
416-
};
417-
const resizeObserver = new ResizeObserver((entries) => {
418-
for (const entry of entries) {
419-
syncCapSize(entry.target);
420-
}
421-
});
422-
for (const capRef of capRefs) {
423-
const capElement = capRef.current;
424-
syncCapSize(capElement);
425-
resizeObserver.observe(capElement);
426-
}
427-
return () => {
428-
resizeObserver.disconnect();
429-
};
430-
}, []);
431-
};

packages/frontend/navi/src/control/demos/39_control_swap_demo.html

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ <h2 style="margin: 0 0 4px">Sommaire</h2>
294294

295295
const WidthDemo = () => (
296296
<div class="demo-section">
297-
<Heading id="width">Largeur</Heading>
297+
<Heading id="width">Largeur et hauteur</Heading>
298298
<Box flex="y" spacing="m">
299299
<Variant caption="320px">
300300
<div class="phone">
@@ -320,6 +320,17 @@ <h2 style="margin: 0 0 4px">Sommaire</h2>
320320
</ControlSwap>
321321
</div>
322322
</Variant>
323+
<Variant caption="--navi-control-swap-size: 40px">
324+
<div class="phone">
325+
<ControlSwap
326+
spacing="xs"
327+
style={{ "--navi-control-swap-size": "40px" }}
328+
>
329+
{whereSide()}
330+
{whatSide()}
331+
</ControlSwap>
332+
</div>
333+
</Variant>
323334
</Box>
324335
</div>
325336
);

0 commit comments

Comments
 (0)