@jsenv/navi 0.29.52 → 0.29.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -194,6 +194,120 @@ const smallTouchScreenSignal = computed(() => {
194
194
  return false;
195
195
  });
196
196
 
197
+ /**
198
+ * The part of the window an app actually has, in two levels.
199
+ *
200
+ * Two, and not one, for a reason worth stating up front: a fixed bar is one of
201
+ * the things that reduce the free region, so it cannot ALSO be placed against
202
+ * that region — it would push itself off the edge it is pinned to. What is
203
+ * anchored and what is anchored-inside are two different rectangles.
204
+ *
205
+ * 1. `--navi-app-inset-{top,right,bottom,left}` — from the window's edges to
206
+ * the app's own rectangle. Whatever is pinned to an edge (a fixed bar, a
207
+ * side panel, a popup aimed at a corner) is pinned to THAT, so an app
208
+ * pretending to be a 600px handheld inside a 1500px window stays one
209
+ * rectangle instead of a column with its furniture spread across the glass.
210
+ *
211
+ * 2. `--navi-safe-area-inset-{top,right,bottom,left}` — from the window's edges
212
+ * to the band left free INSIDE that rectangle. Whatever flows, scrolls, or
213
+ * gets painted keeps to it.
214
+ *
215
+ * Level 2 is a sum, and the contract for taking part in it is only "publish
216
+ * what you take on one edge": the device's own notch (`env(safe-area-inset-*)`,
217
+ * which is the browser's version of this very idea), the fixed bars
218
+ * (fixed_bar_space.js), and anything an app adds. That is the point of naming
219
+ * it at all — a component that must stay clear of what covers the screen reads
220
+ * ONE set of numbers, and never has to learn what is covering it.
221
+ *
222
+ * `max()` between the notch and the bars rather than a sum: a bar pinned to an
223
+ * edge already reaches under the notch and counts it in its own size (see
224
+ * fixed_bar.jsx), so adding both would reserve it twice.
225
+ *
226
+ * Sizes only, not placement, for the time being — see "Current limitations" in
227
+ * docs/css_architecture.md.
228
+ */
229
+
230
+ const SAFE_AREA_CSS = /* css */ `
231
+ @layer navi {
232
+ :root {
233
+ /* The room each kind of furniture takes, declared here at zero and
234
+ written by whoever takes it. A slot rather than a value: the sum below
235
+ has to be readable whether or not the app ever mounts a fixed bar. */
236
+ --navi-fixed-bar-space-top: 0px;
237
+ --navi-fixed-bar-space-right: 0px;
238
+ --navi-fixed-bar-space-bottom: 0px;
239
+ --navi-fixed-bar-space-left: 0px;
240
+
241
+ /* Level 1. Centered bands, so that declaring one ceiling
242
+ (--navi-app-max-width) is all an app has to do to be a narrow screen in
243
+ a wide window; an app that wants them uneven writes these directly. */
244
+ --navi-app-inset-top: max(
245
+ 0px,
246
+ (var(--navi-vvh) - var(--navi-app-max-height, var(--navi-vvh))) / 2
247
+ );
248
+ --navi-app-inset-bottom: var(--navi-app-inset-top);
249
+ --navi-app-inset-left: max(
250
+ 0px,
251
+ (var(--navi-vvw) - var(--navi-app-max-width, var(--navi-vvw))) / 2
252
+ );
253
+ --navi-app-inset-right: var(--navi-app-inset-left);
254
+
255
+ /* Level 2. */
256
+ --navi-safe-area-inset-top: calc(
257
+ var(--navi-app-inset-top) +
258
+ max(env(safe-area-inset-top), var(--navi-fixed-bar-space-top))
259
+ );
260
+ --navi-safe-area-inset-right: calc(
261
+ var(--navi-app-inset-right) +
262
+ max(env(safe-area-inset-right), var(--navi-fixed-bar-space-right))
263
+ );
264
+ --navi-safe-area-inset-bottom: calc(
265
+ var(--navi-app-inset-bottom) +
266
+ max(env(safe-area-inset-bottom), var(--navi-fixed-bar-space-bottom))
267
+ );
268
+ --navi-safe-area-inset-left: calc(
269
+ var(--navi-app-inset-left) +
270
+ max(env(safe-area-inset-left), var(--navi-fixed-bar-space-left))
271
+ );
272
+
273
+ /* The document is the scrollport in the common case, and something the
274
+ browser scrolls to — an anchor, a focused field, a restored position —
275
+ landing under a bar is never what anyone wants. */
276
+ scroll-padding-top: var(--navi-safe-area-inset-top);
277
+ scroll-padding-right: var(--navi-safe-area-inset-right);
278
+ scroll-padding-bottom: var(--navi-safe-area-inset-bottom);
279
+ scroll-padding-left: var(--navi-safe-area-inset-left);
280
+ }
281
+
282
+ /* Put this on whatever scrolls under the furniture.
283
+
284
+ There are TWO rooms to give back, and forgetting the second one is the
285
+ classic bug:
286
+
287
+ - padding, so the end of the content can be scrolled out from under it.
288
+ Without it the last screenful stays covered, unreachable.
289
+ - scroll-padding, so anything the browser scrolls TO lands in front of it
290
+ rather than under. The padding above does not help here: it moves the
291
+ content, not the place the browser scrolls the target to.
292
+
293
+ Marked by the app rather than picked by navi: which element scrolls is
294
+ the app's business, and an app with more than one would have to fight a
295
+ component that chose for it. An app is free to read the variables itself
296
+ instead. */
297
+ [data-navi-safe-area] {
298
+ padding-top: var(--navi-safe-area-inset-top);
299
+ padding-right: var(--navi-safe-area-inset-right);
300
+ padding-bottom: var(--navi-safe-area-inset-bottom);
301
+ padding-left: var(--navi-safe-area-inset-left);
302
+
303
+ scroll-padding-top: var(--navi-safe-area-inset-top);
304
+ scroll-padding-right: var(--navi-safe-area-inset-right);
305
+ scroll-padding-bottom: var(--navi-safe-area-inset-bottom);
306
+ scroll-padding-left: var(--navi-safe-area-inset-left);
307
+ }
308
+ }
309
+ `;
310
+
197
311
  installImportMetaCssBuild(import.meta);/**
198
312
  * Regroup CSS vars that makes sense to share across all navi components.
199
313
  */
@@ -214,8 +328,9 @@ const css = /* css */`
214
328
 
215
329
  /* What navi treats as "the screen" when it sizes something that escapes
216
330
  normal flow (a dialog in the top layer, a popover, anything built on
217
- them). The visual viewport by default but an app that never spans
218
- the whole window says so here, ONCE, without ever naming a component:
331
+ them): the app's own rectangle, which is the visual viewport minus the
332
+ bands an app asks for. An app that never spans the whole window says so
333
+ ONCE, without ever naming a component:
219
334
 
220
335
  :root {
221
336
  --navi-app-max-width: 600px;
@@ -235,17 +350,19 @@ const css = /* css */`
235
350
  from it as before. A single popup that genuinely needs more can still
236
351
  raise its own maxWidth/maxHeight prop.
237
352
 
238
- Sizes only, not placement: what is anchored to an edge (a SidePanel, a
239
- fixed bar, a "bottom-start" positionArea) still sits against the
240
- window's edge, not the app column's. See "Current limitations" in
241
- docs/css_architecture.md. */
242
- --navi-app-width: min(
243
- var(--navi-vvw),
244
- var(--navi-app-max-width, var(--navi-vvw))
353
+ Read from the insets rather than as a min() of its own so that the
354
+ width and the placement come from ONE description of where the app is
355
+ (see layout/safe_area.js). Placement does not follow yet everywhere
356
+ see "Current limitations" in docs/css_architecture.md. */
357
+ --navi-app-width: calc(
358
+ var(--navi-vvw) - var(--navi-app-inset-left) - var(
359
+ --navi-app-inset-right
360
+ )
245
361
  );
246
- --navi-app-height: min(
247
- var(--navi-vvh),
248
- var(--navi-app-max-height, var(--navi-vvh))
362
+ --navi-app-height: calc(
363
+ var(--navi-vvh) - var(--navi-app-inset-top) - var(
364
+ --navi-app-inset-bottom
365
+ )
249
366
  );
250
367
 
251
368
  --navi-focus-outline-width: 2px;
@@ -385,6 +502,8 @@ const css = /* css */`
385
502
  }
386
503
  }
387
504
 
505
+ ${SAFE_AREA_CSS}
506
+
388
507
  /* Hidden appearance */
389
508
  input[navi-visually-hidden],
390
509
  button[navi-visually-hidden],