@jsenv/navi 0.29.425 → 0.29.427
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.
- package/dist/dev/jsenv_navi.js +600 -198
- package/dist/dev/jsenv_navi.js.map +22 -20
- package/dist/dev/jsenv_navi_side_effects.js +1 -5
- package/dist/dev/jsenv_navi_side_effects.js.map +2 -2
- package/dist/jsenv_navi.js +597 -195
- package/dist/jsenv_navi.js.map +22 -20
- package/dist/jsenv_navi_side_effects.js +1 -5
- package/dist/jsenv_navi_side_effects.js.map +2 -2
- package/docs/AI_INSTRUCTIONS.md +9 -5
- package/docs/control_object.md +76 -2
- package/docs/css_architecture.md +1 -1
- package/docs/navigation.md +4 -1
- package/docs/popup_open.md +6 -3
- package/docs/scroll.md +53 -0
- package/package.json +2 -2
|
@@ -471,14 +471,10 @@ const css = /* css */ `@layer navi {
|
|
|
471
471
|
--navi-placeholder-font-style: normal;
|
|
472
472
|
}
|
|
473
473
|
|
|
474
|
-
.navi_popover, .navi_dialog {
|
|
474
|
+
.navi_popover, .navi_dialog, .navi_callout {
|
|
475
475
|
--navi-color-primary: var(--navi-popup-color);
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
.navi_callout {
|
|
479
|
-
--navi-color-primary: CanvasText;
|
|
480
|
-
}
|
|
481
|
-
|
|
482
478
|
:root, .navi_popover, .navi_dialog, .navi_callout {
|
|
483
479
|
--navi-color-secondary: color-mix(in srgb,
|
|
484
480
|
currentColor var(--navi-color-secondary-mix),
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"const installImportMetaCssBuild = (importMeta) => {\n const IMPORT_META_CSS_BUILD = \"jsenv_import_meta_css_build\";\n\n if (importMeta.css === IMPORT_META_CSS_BUILD) {\n return;\n }\n\n const stylesheetMap = new Map();\n const adopt = (url, value) => {\n const stylesheet = new CSSStyleSheet({ baseUrl: importMeta.url });\n stylesheet.replaceSync(value);\n stylesheetMap.set(url, stylesheet);\n document.adoptedStyleSheets.push(stylesheet);\n };\n const update = (url, value) => {\n stylesheetMap.get(url).replaceSync(value);\n };\n const remove = (url) => {\n const stylesheet = stylesheetMap.get(url);\n document.adoptedStyleSheets = document.adoptedStyleSheets.filter(\n (s) => s !== stylesheet,\n );\n stylesheetMap.delete(url);\n };\n\n const currentCssSourceMap = new Map();\n Object.defineProperty(importMeta, \"css\", {\n configurable: true,\n get() {\n return IMPORT_META_CSS_BUILD;\n },\n set([value, url]) {\n if (value === undefined) {\n if (stylesheetMap.has(url)) {\n remove(url);\n currentCssSourceMap.delete(url);\n }\n return;\n }\n if (!stylesheetMap.has(url)) {\n adopt(url, value);\n currentCssSourceMap.set(url, value);\n } else if (currentCssSourceMap.get(url) !== value) {\n update(url, value);\n currentCssSourceMap.set(url, value);\n }\n },\n });\n};\n\nexport { installImportMetaCssBuild };\n",
|
|
12
12
|
"import {\n getVirtualKeyboardOverlayHeight,\n subscribeVisualViewportResizeSettled,\n subscribeWindowResizeSettled,\n} from \"@jsenv/dom\";\nimport { computed, signal } from \"@preact/signals\";\n\nexport const windowWidthSignal = signal(window.innerWidth);\nexport const windowHeightSignal = signal(window.innerHeight);\n\n// Debounced (not a raw \"resize\" listener) — see window_size.js's own\n// module comment: mobile fires a transient \"resize\" when the browser's own\n// UI chrome (address bar, etc.) briefly shows/hides, and this needs to settle\n// on the same tick as Popover/Dialog's own repositioning, or one flickers a\n// moment out of sync with the others.\nsubscribeWindowResizeSettled(() => {\n windowWidthSignal.value = window.innerWidth;\n windowHeightSignal.value = window.innerHeight;\n});\n\n// Visual viewport dimensions — update when the virtual keyboard opens/closes or\n// when the browser UI (address bar) shows/hides.\n// When visualViewport is not available, derived from window signals so they\n// stay live without any extra listeners.\nconst vv = window.visualViewport;\nexport const visualViewportWidthSignal = vv\n ? signal(vv.width)\n : computed(() => windowWidthSignal.value);\nexport const visualViewportHeightSignal = vv\n ? signal(vv.height)\n : computed(() => windowHeightSignal.value);\n\nif (vv) {\n const update = () => {\n visualViewportWidthSignal.value = vv.width;\n visualViewportHeightSignal.value = vv.height;\n };\n // The two directions are not equally trustworthy, and treating them alike is\n // what makes one of the two bugs unavoidable.\n //\n // SMALLER is believed at once. Something now covers the screen, and what is\n // sized against these numbers — the dialog/popover ceilings, through\n // --navi-vvh (navi_css_vars.js) — has to answer the smaller screen in the\n // same frame the placement does. The placement reads the viewport live (see\n // getVisibleViewportRect in @jsenv/dom), so a ceiling arriving a debounce\n // later means a box sized for a screen that is gone, placed in the one that\n // replaced it.\n //\n // BIGGER waits for the resize to settle, because growing back is the reading\n // a mobile browser lies about: going straight from one field to the next\n // fires a blur/focus pair that briefly reports the full height again, with\n // the keyboard never having left. Believed, it flicks every popup back to\n // full height and down again between two taps — the \"two inputs\" case in\n // Dialog's own demo. Nothing is lost by waiting: a keyboard that really left\n // stays gone, and the settled event lands 100ms later.\n vv.addEventListener(\"resize\", () => {\n if (\n vv.width < visualViewportWidthSignal.peek() ||\n vv.height < visualViewportHeightSignal.peek()\n ) {\n update();\n }\n });\n subscribeVisualViewportResizeSettled(update);\n vv.addEventListener(\"scroll\", update);\n}\n\n// The app's own screen: the visual viewport minus the bands --navi-app-inset-*\n// describes (see safe_area.js, and navi_css_vars.js, which derives\n// --navi-app-width/--navi-app-height from the same bands in CSS). Anything\n// escaping normal flow is sized and placed against this rather than the\n// viewport: an app that simulates a handheld screen keeps that width even for\n// what paints on top of it.\n//\n// The bands are read back from CSS rather than worked out again here: a JS\n// copy of the formula is the one that goes stale, and it only knows the\n// centered bands --navi-app-max-width/height produce, not the uneven ones an\n// app writes directly. They are registered as lengths (safe_area.js), so they\n// compute to pixels whatever unit they were declared in. Read on the spot\n// rather than cached in a signal: the callers are a popup resolving its margin\n// and placing itself, which already read far more of the DOM than this, and\n// nothing then has to be invalidated when the value changes.\nconst readAppInsets = () => {\n const computedStyle = getComputedStyle(document.documentElement);\n return {\n top: readAppInset(computedStyle, \"top\"),\n right: readAppInset(computedStyle, \"right\"),\n bottom: readAppInset(computedStyle, \"bottom\"),\n left: readAppInset(computedStyle, \"left\"),\n };\n};\nconst readAppInset = (computedStyle, side) => {\n const value = parseFloat(\n computedStyle.getPropertyValue(`--navi-app-inset-${side}`),\n );\n // A browser that cannot register a custom property hands back the calc()\n // it was written as: JS then answers for the whole viewport.\n return Number.isFinite(value) ? value : 0;\n};\nexport const getAppWidth = () => {\n const { left, right } = readAppInsets();\n return visualViewportWidthSignal.value - left - right;\n};\n// Handed to @jsenv/dom (setPlacementViewportInsets, wired in navi_css_vars.js)\n// so placement keeps to the same rectangle the CSS size caps describe. The\n// keyboard is taken back off the bottom band: --navi-app-inset-bottom counts\n// it, and the placement viewport already subtracts it (see\n// getVisibleViewportRect in @jsenv/dom's visible_rect.js), so it would count\n// twice.\nexport const getAppInsets = () => {\n const insets = readAppInsets();\n insets.bottom -= getVirtualKeyboardOverlayHeight();\n return insets;\n};\n// The keyboard stays in: this is --navi-app-height, which the ceilings are\n// sized against, read in JS.\nexport const getAppHeight = () => {\n const { top, bottom } = readAppInsets();\n const height = visualViewportHeightSignal.value - top - bottom;\n if (height < 0) {\n return 0;\n }\n return height;\n};\n\n// Whether the primary input is a finger rather than a mouse. A pointer type is\n// not a size: a narrow desktop window is still a mouse, and a large tablet is\n// still a finger — so anything sized for the on-screen keyboard must key off\n// this, never off windowWidthSignal. Thumb reach needs both, which is what\n// smallTouchScreenSignal below answers.\nconst coarsePointerQuery = window.matchMedia\n ? window.matchMedia(\"(pointer: coarse)\")\n : null;\nexport const coarsePointerSignal = signal(\n coarsePointerQuery ? coarsePointerQuery.matches : false,\n);\nif (coarsePointerQuery) {\n coarsePointerQuery.addEventListener(\"change\", () => {\n coarsePointerSignal.value = coarsePointerQuery.matches;\n });\n}\n\n// Whether the screen is one a bottom sheet actually suits: a finger *and* a\n// screen whose bottom edge stays where the thumb already is. Touch alone is not\n// enough — a tall touch screen (a tablet, a kiosk panel) docks a sheet a whole\n// screen away from where the finger just tapped, which is worse than the\n// centered box it replaced.\n//\n// A phone is recognized by its SHAPE, not by a box of maximum dimensions: what\n// makes the bottom edge reachable is holding a narrow slab, and phones keep\n// growing along their long side (20:9, 21:9) while staying just as narrow. So\n// each orientation is answered on the short side plus the elongation:\n// - upright: narrow enough to be held in one hand, and taller than it is wide.\n// Its height is deliberately unbounded — a very tall narrow screen is the\n// case a bottom sheet is most for, not the case to exclude.\n// - on its side: short enough that the bottom edge is a thumb away whatever the\n// width, and wider than it is tall.\n// A tablet fails both: it is too wide upright, and too tall on its side — the\n// smallest one already starts around 740 CSS px on its short side, and the\n// bound below leaves that gap deliberately wide rather than cutting close to\n// the largest phone.\n//\n// Read off window, not visualViewport: the virtual keyboard shrinks the visual\n// viewport while the user types, and a dialog must not undock mid-interaction\n// because a keyboard opened under it.\nconst HANDHELD_MAX_SHORT_SIDE = 600;\n// Enough elongation to tell a slab from a square-ish panel; a phone is well\n// past it (1.7 and up) in either orientation.\nconst HANDHELD_MIN_RATIO = 1.2;\nexport const smallTouchScreenSignal = computed(() => {\n if (!coarsePointerSignal.value) {\n return false;\n }\n const width = windowWidthSignal.value;\n const height = windowHeightSignal.value;\n if (width <= HANDHELD_MAX_SHORT_SIDE) {\n return height >= width * HANDHELD_MIN_RATIO;\n }\n if (height <= HANDHELD_MAX_SHORT_SIDE) {\n return width >= height * HANDHELD_MIN_RATIO;\n }\n return false;\n});\n",
|
|
13
13
|
"/**\n * The part of the window an app actually has, in two levels.\n *\n * Two, and not one, for a reason worth stating up front: a fixed bar is one of\n * the things that reduce the free region, so it cannot ALSO be placed against\n * that region — it would push itself off the edge it is pinned to. What is\n * anchored and what is anchored-inside are two different rectangles.\n *\n * 1. `--navi-app-inset-{top,right,bottom,left}` — from the window's edges to\n * the app's own rectangle. Whatever is pinned to an edge (a fixed bar, a\n * side panel, a popup aimed at a corner) is pinned to THAT, so an app\n * pretending to be a 600px handheld inside a 1500px window stays one\n * rectangle instead of a column with its furniture spread across the glass.\n *\n * 2. `--navi-safe-area-inset-{top,right,bottom,left}` — from the window's edges\n * to the band left free INSIDE that rectangle. Whatever flows, scrolls, or\n * gets painted keeps to it.\n *\n * Level 2 gathers what takes an edge: the device's own notch\n * (`env(safe-area-inset-*)`, which is the browser's version of this very idea)\n * and the fixed bars (fixed_bar_space.js, the one slot per edge — something\n * else covering an edge is published by being drawn as a FixedBar). That is\n * the point of naming it at all — a component that must stay clear of what\n * covers the screen reads ONE set of numbers, and never has to learn what is\n * covering it.\n *\n * `max()` between the notch and the bars rather than a sum: a bar pinned to an\n * edge already reaches under the notch and counts it in its own size (see\n * fixed_bar.jsx), so adding both would reserve it twice.\n *\n * JS placement answers to the level-1 rectangle too: getAppInsets\n * (layout/responsive.js) reads these same bands back off the computed style,\n * and hands them to pickPositionRelativeTo via setPlacementViewportInsets (see\n * navi_css_vars.js).\n */\n\nconst SAFE_AREA_CSS = /* css */ `\n /* Declared as lengths so that they COMPUTE to one: an unregistered custom\n property keeps the calc() it was written as, and the sums below are then\n strings no one can read back. Reading them off the computed style only\n works for a registered property, and both levels are read there: level 1\n by popup placement, which must keep to the same rectangle as the CSS\n whether the bands are centered or written by the app (getAppInsets in\n layout/responsive.js); level 2 by a route transition, to keep the band the\n page being left had (nav/transition_window.js). */\n @property --navi-app-inset-top {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-app-inset-right {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-app-inset-bottom {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-app-inset-left {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-top {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-right {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-bottom {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n @property --navi-safe-area-inset-left {\n syntax: \"<length>\";\n inherits: true;\n initial-value: 0px;\n }\n\n @layer navi {\n /* Layered whole, rules included: the two rules below are offers, not\n structure. [data-navi-safe-area] is an attribute the app puts on its own\n scroller, so the app's own padding on that element has to win over what\n navi suggests for it. */\n :root {\n /* The room each kind of furniture takes, declared here at zero and\n written by whoever takes it. A slot rather than a value: the sum below\n has to be readable whether or not the app ever mounts a fixed bar. */\n --navi-fixed-bar-space-top: 0px;\n --navi-fixed-bar-space-right: 0px;\n --navi-fixed-bar-space-bottom: 0px;\n --navi-fixed-bar-space-left: 0px;\n\n /* What the on-screen keyboard covers — and ONLY where it overlays the\n content rather than shrinking the viewport, which an app asks for\n with enableVirtualKeyboardOverlay() (layout/virtual_keyboard.js).\n Zero otherwise, and always on Firefox/Safari, which have no such API:\n the keyboard shrinks the visual viewport instead, which --navi-vvh\n already tracks. Reading\n env() rather than a JS-written value keeps it live: the keyboard\n slides in over several frames and this follows it without a\n listener. */\n --navi-keyboard-inset-bottom: env(keyboard-inset-height, 0px);\n /* What Chrome paints right above the keyboard without counting it in\n the keyboard's geometry: the autofill/suggestion strip, which neither\n env(keyboard-inset-height) nor geometrychange reports (see\n window_size.js in @jsenv/dom). 49–64px measured on Android 10 /\n Chrome 153; 0 while no keyboard is up.\n\n Kept out of --navi-keyboard-inset-bottom: the strip is not always\n there, and whatever sits against the keyboard (a fixed bar, a popup)\n would float above it with a gap when it is missing. Only scrolling\n takes it, where an excess costs nothing: the room to scroll into, and\n the scroll-padding-bottom a scroll lands against — navi's reveal of\n the focused field (layout/virtual_keyboard.js), and Chrome's own,\n which keeps the caret flush on scroll-padding-bottom as a textarea\n grows under typing. */\n --navi-keyboard-strip-allowance: min(\n var(--navi-keyboard-inset-bottom),\n 64px\n );\n\n /* Level 1. Centered bands, so that declaring one ceiling\n (--navi-app-max-width) is all an app has to do to be a narrow screen in\n a wide window; an app that wants them uneven writes these directly. */\n --navi-app-inset-top: max(\n 0px,\n (var(--navi-vvh) - var(--navi-app-max-height, var(--navi-vvh))) / 2\n );\n /* The keyboard on top of the band, and on this edge only: it eats into\n the app's own rectangle exactly like a viewport that shrank, which is\n what makes both paths end up at the same --navi-app-height (and so at\n the same dialog/popover ceiling). Not part of the centering, hence\n added here rather than folded into --navi-app-inset-top: a keyboard\n takes the bottom, it doesn't re-center anything. */\n --navi-app-inset-bottom: calc(\n var(--navi-app-inset-top) + var(--navi-keyboard-inset-bottom)\n );\n --navi-app-inset-left: max(\n 0px,\n (var(--navi-vvw) - var(--navi-app-max-width, var(--navi-vvw))) / 2\n );\n --navi-app-inset-right: var(--navi-app-inset-left);\n\n /* Level 2. */\n --navi-safe-area-inset-top: calc(\n var(--navi-app-inset-top) +\n max(env(safe-area-inset-top), var(--navi-fixed-bar-space-top))\n );\n --navi-safe-area-inset-right: calc(\n var(--navi-app-inset-right) +\n max(env(safe-area-inset-right), var(--navi-fixed-bar-space-right))\n );\n --navi-safe-area-inset-bottom: calc(\n var(--navi-app-inset-bottom) +\n max(env(safe-area-inset-bottom), var(--navi-fixed-bar-space-bottom))\n );\n --navi-safe-area-inset-left: calc(\n var(--navi-app-inset-left) +\n max(env(safe-area-inset-left), var(--navi-fixed-bar-space-left))\n );\n\n /* The document is the scrollport in the common case, and something the\n browser scrolls to — an anchor, a focused field, a restored position —\n landing under a bar is never what anyone wants. */\n scroll-padding-top: var(--navi-safe-area-inset-top);\n scroll-padding-right: var(--navi-safe-area-inset-right);\n scroll-padding-bottom: calc(\n var(--navi-safe-area-inset-bottom) +\n var(--navi-keyboard-strip-allowance)\n );\n scroll-padding-left: var(--navi-safe-area-inset-left);\n }\n\n /* Put this on whatever scrolls under the furniture.\n\n There are TWO rooms to give back, and forgetting the second one is the\n classic bug:\n\n - padding, so the end of the content can be scrolled out from under it.\n Without it the last screenful stays covered, unreachable.\n - scroll-padding, so anything the browser scrolls TO lands in front of it\n rather than under. The padding above does not help here: it moves the\n content, not the place the browser scrolls the target to.\n\n Marked by the app rather than picked by navi: which element scrolls is\n the app's business, and an app with more than one would have to fight a\n component that chose for it. An app is free to read the variables itself\n instead. */\n [data-navi-safe-area] {\n padding-top: var(--navi-safe-area-inset-top);\n padding-right: var(--navi-safe-area-inset-right);\n padding-bottom: calc(\n var(--navi-safe-area-inset-bottom) +\n var(--navi-keyboard-strip-allowance)\n );\n padding-left: var(--navi-safe-area-inset-left);\n\n scroll-padding-top: var(--navi-safe-area-inset-top);\n scroll-padding-right: var(--navi-safe-area-inset-right);\n scroll-padding-bottom: calc(\n var(--navi-safe-area-inset-bottom) +\n var(--navi-keyboard-strip-allowance)\n );\n scroll-padding-left: var(--navi-safe-area-inset-left);\n }\n\n /* The keyboard's room, for a page that marked nothing: the document is\n then what scrolls under the keyboard, and a field near its end needs as\n much room below it as the keyboard and its strip are tall to be brought\n out from under them (layout/virtual_keyboard.js). Unlike the bars, it\n is given without the app marking anything: a viewport that shrinks\n always leaves the browser that room, and enabling the overlay must not\n take it away. A marked element already gives that room in its\n padding-bottom, hence the :has().\n\n A pseudo-element rather than a padding on :root, which the common reset\n zeroing html's padding would take away. */\n :root:not(:has([data-navi-safe-area]))::after {\n display: block;\n height: calc(\n var(--navi-keyboard-inset-bottom) + var(--navi-keyboard-strip-allowance)\n );\n content: \"\";\n }\n }\n`;\nimport.meta.css = SAFE_AREA_CSS;\n",
|
|
14
|
-
"/**\n * Regroup CSS vars that makes sense to share across all navi components.\n */\nimport { setPlacementViewportInsets } from \"@jsenv/dom\";\nimport { effect } from \"@preact/signals\";\n\nimport {\n getAppInsets,\n visualViewportHeightSignal,\n visualViewportWidthSignal,\n} from \"./layout/responsive.js\";\nimport \"./layout/safe_area.js\";\n\nconst button = document.createElement(\"button\");\nbutton.style.display = \"none\";\ndocument.body.appendChild(button);\nconst computedStyle = getComputedStyle(button);\nconst controlDefaultFontFamily = computedStyle.fontFamily;\nconst controlDefaultFontSize = computedStyle.fontSize;\ndocument.body.removeChild(button);\n\nconst css = /* css */ `\n @layer navi {\n :root {\n /* Overridden at runtime with precise VisualViewport pixel values so that dvw/dvh \n (which don't track the virtual keyboard dimensions) are never used in practice on supported browsers. */\n --navi-vvw: 100dvw;\n --navi-vvh: 100dvh;\n\n /* What navi treats as \"the screen\" when it sizes something that escapes\n normal flow (a dialog in the top layer, a popover, anything built on\n them): the app's own rectangle, which is the visual viewport minus the\n bands an app asks for. An app that never spans the whole window says so\n ONCE, without ever naming a component:\n\n :root {\n --navi-app-max-width: 600px;\n }\n\n Typically an app simulating a handheld screen: a column centered in a\n wide window with bands on the sides. A dialog is in the top layer, so\n it answers to the viewport, not to that column — left alone it would\n paint 1500px of modal over a 600px app. Declaring the ceiling here is\n what makes the app's own width apply everywhere, including on top.\n\n It stays a ceiling and nothing else: on a screen narrower than the app\n it never binds, so popups keep shrinking with the phone, and the gap\n each popup keeps with the edges (marginWithContainer) is subtracted\n from it too. A single popup that genuinely needs more can still\n raise its own maxWidth/maxHeight prop.\n\n Read from the insets rather than as a min() of its own so that the\n width and the placement come from ONE description of where the app is\n (see layout/safe_area.js). Placement follows the same rectangle: see\n setPlacementViewportInsets at the bottom of this file. */\n --navi-app-width: calc(\n var(--navi-vvw) - var(--navi-app-inset-left) - var(\n --navi-app-inset-right\n )\n );\n --navi-app-height: calc(\n var(--navi-vvh) - var(--navi-app-inset-top) - var(\n --navi-app-inset-bottom\n )\n );\n\n --navi-focus-outline-width: 2px;\n --navi-focus-outline-color: light-dark(#4476ff, #3b82f6);\n --navi-loader-color: light-dark(#355fcc, #3b82f6);\n /* The grey rectangle a mobile browser flashes under a finger: we draw\n what a press does ourselves (background, ring, chevron), and the\n browser's rectangle is a square painted over rounded corners.\n Declared here alone because the property is inherited: :root covers\n every element, in and out of navi, so nothing has to remember it.\n Set the token to a color to get the flash back, on the whole app or\n on one subtree. */\n -webkit-tap-highlight-color: var(--navi-tap-highlight-color);\n --navi-tap-highlight-color: transparent;\n\n /* A face of their own for the controls, and nothing else on the page.\n Undeclared here on purpose: controls write in inherit, so what a\n container says reaches the button, the field and the picker inside it,\n and a value on :root would resolve here and wall that off again. Set\n it to give the controls their own face, on the whole app or on one\n subtree:\n\n :root {\n --navi-control-font-family: system-ui;\n }\n */\n --navi-control-font-size: ${controlDefaultFontSize};\n --navi-control-border-radius: 2px;\n /* A checkbox is only ~1em wide: applying a control radius meant for\n buttons/inputs (say 8px) would turn it into a circle and make it read\n as a radio. So it follows the control radius but capped at a quarter\n of its own size. Override this token alone to opt out of the cap. */\n --navi-checkbox-border-radius: min(\n var(--navi-control-border-radius),\n 0.25em\n );\n /* The color a control uses to say \"this one is on\": a checked checkbox,\n a checked radio, an enabled switch. Kept apart from --navi-accent-color\n (the brand color for CTA and selection) because a control that is on\n must stay readable as a control, which usually means a brighter, more\n saturated color than a brand accent.\n The light value matches the browser's own accent so a navi control and\n a native one can sit side by side without reading as two blues. */\n --navi-control-accent-color: light-dark(rgb(24, 117, 255), #3b82f6);\n --navi-control-border-width: 1px;\n --navi-control-border-color: light-dark(#767676, #8e8e93);\n --navi-control-padding-x-default: 2px;\n --navi-control-padding-y-default: 1px;\n /* Global padding defaults — override these to change all button paddings. */\n /* Use --button-padding, --button-padding-x, --button-padding-y for per-button overrides. */\n --navi-button-padding-x-default: 6px;\n --navi-button-padding-y-default: 1px;\n /* Read by Button, and by whatever is drawn as one (a picker under\n variant=\"button\"), so the two keep the same surface. */\n --navi-button-background-color: light-dark(#f3f4f6, #2d3748);\n\n /* What a callout says about its message, as a color — and what draws\n as one outside the callout too (the icon a picker in callout mode\n presses, see CalloutStatusIcon). */\n --navi-callout-success-color: #4caf50;\n --navi-callout-info-color: #2196f3;\n --navi-callout-warning-color: #ff9800;\n --navi-callout-error-color: #f44336;\n /* No status: the message is an aside, said without an icon and framed\n like a control rather than like a warning. */\n --navi-callout-neutral-color: var(--navi-control-border-color);\n /* For list item we need slightly more padding to be able to see radio/checkbox outline */\n --navi-list-item-padding-x-default: 4px;\n --navi-list-item-padding-y-default: 1px;\n /* default */\n --navi-picker-padding-x-default: var(--navi-control-padding-x-default);\n --navi-picker-padding-y-default: var(--navi-control-padding-y-default);\n\n --navi-popup-border-radius: 8px;\n --navi-popup-border-color: light-dark(#d0d0d0, #3b3b3b);\n --navi-popup-box-shadow:\n 0 4px 8px rgba(0, 0, 0, 0.08), 0 12px 40px rgba(0, 0, 0, 0.22);\n /* A popup's paper is the same paper as everything else's: derived from\n the surface token so one override themes fields, layouts and popups\n together. Still its own token, so popups alone can be re-papered. */\n --navi-popup-background-color: var(--navi-surface-color);\n /* The ink written on that paper. A popup declares both on itself (see\n dialog.jsx / popover.jsx): it is a new surface, so it does not write\n in whatever the container it was declared in writes in. */\n --navi-popup-color: var(--navi-surface-text-color);\n /* A backdrop's paint is a colour AND a filter, one pair per kind of\n backdrop: how much the page behind must stop competing for the eye is\n a different question from what an outside click does, so a backdrop\n that closes on that click can blur too. Only \"capture\" blurs by\n default. */\n --navi-backdrop-close-background: rgba(0, 0, 0, 0.08);\n --navi-backdrop-close-backdrop-filter: none;\n /* backdropVariant=\"discrete\": the popup still catches every outside\n click, it just stops announcing that it did. For an affordance one\n reaches past rather than through — the dim is there to mark the layer,\n not to push the page behind it away. */\n --navi-backdrop-discrete-background: rgba(0, 0, 0, 0.02);\n --navi-backdrop-discrete-backdrop-filter: none;\n /* \"capture\" means the rest of the page is fully non-interactive —\n blurred, not just dimmed, so it reads as clearly secondary and\n pulls visual focus onto the popover's own content. */\n --navi-backdrop-capture-background: rgb(255 255 255 / 0.08);\n --navi-backdrop-capture-backdrop-filter: blur(30px) saturate(180%);\n /* animation=\"lifting\", and backdropVariant=\"lift\" asking for the same\n wall without the movement: the popup is what the anchor became, so the page\n it came out of is not a context to keep readable — it is the thing the\n movement leaves. A card, a plan, a picture lifted to be looked at wants\n nothing legible competing with it, and a document still crisp behind a\n box that is still travelling reads as two movements at once. Dark in\n both schemes on purpose: what withdraws here is the page itself, the\n way it does around a photo, not a surface tinted to match a theme. */\n --navi-backdrop-lift-background: rgb(6 10 20 / 88%);\n --navi-backdrop-lift-backdrop-filter: blur(4px);\n\n /* Link colors. They live here rather than only on .navi_link because a\n var declared on the element itself always beats the same var inherited\n from an ancestor: a page setting --link-color-pressed on :root would\n never reach a link. These :root tokens are the theme-level surface;\n --link-color-* stays the per-link (or per-subtree) override. */\n --navi-link-color: rgb(0, 0, 238);\n /* --navi-link-color-visited is intentionally left undefined: by default a\n visited link is derived from whatever --link-color ended up being on\n the link itself (see link.jsx), which a :root token cannot see. Set it\n here from an app to pin one visited color for every link. */\n --navi-link-color-pressed: red;\n --navi-link-current-indicator-color: rgb(205, 52, 37);\n\n --navi-selection-border-color: #0078d4;\n --navi-selection-background-color: #eaf1fd;\n /* Accent color — used for call-to-action buttons and selected list items.\n Override this single variable to apply a consistent brand color across\n all components that need to stand out. */\n --navi-accent-color: rgb(3, 30, 60);\n /* The surface a control's content sits on — the \"paper\" behind the text.\n Fields use it whenever they need a solid background (their default\n background, a transparent field being edited, …). */\n --navi-surface-color: light-dark(#ffffff, #1c1c1e);\n /* What is written on that paper. The browser's own text color rather\n than a literal, so it follows color-scheme the way the paper does. */\n --navi-surface-text-color: CanvasText;\n /* The plane the app's own frame is painted on — a top bar, a side menu, a\n toolbar: everything that surrounds a screen rather than being in it.\n A step away from the paper, because a bar painted in the paper's own\n color on a page of that color is not a bar, it is a line. Derived from\n the paper rather than written as a literal so it follows the theme by\n construction: a step toward the dark on a light page, toward the light\n on a dark one. Reached as background=\"chrome\" on a Box. */\n --navi-chrome-color: color-mix(\n in srgb,\n var(--navi-surface-color) 94%,\n var(--navi-color-dark)\n );\n /* The line that separates two regions of one surface — a scrolling area's\n header from what scrolls under it, for instance. Not a border: the\n separation belongs to the layout, not to the box that draws it. */\n --navi-separator-color-default: #d1d9e0;\n --navi-color-white: white;\n --navi-color-dark: rgb(55, 60, 69);\n\n --navi-info-color-light: #eaf6fc;\n --navi-info-color: #376cc2;\n --navi-success-color-light: #ecf9ef;\n --navi-success-color: #50c464;\n --navi-warning-color-light: #fdf6e3;\n --navi-warning-color: #f19c05;\n --navi-error-color-light: #fcebed;\n --navi-error-color: #eb364b;\n\n --navi-xxs: 0.125em; /* = 2px at 16px base */\n --navi-xs: 0.25em; /* = 4px at 16px base */\n --navi-s: 0.5em; /* = 8px at 16px base */\n --navi-m: 1em; /* = 16px at 16px base (base font size) */\n --navi-l: 1.5em; /* = 24px at 16px base */\n --navi-xl: 2em; /* = 32px at 16px base */\n --navi-xxl: 3em; /* 48px at 16px base */\n\n --navi-typo-xxs: 0.625rem; /* 10px at 16px base */\n --navi-typo-xs: 0.75rem; /* 12px at 16px base */\n --navi-typo-s: 0.875rem; /* 14px at 16px base */\n --navi-typo-m: 1rem; /* 16px at 16px base (base font size) */\n --navi-typo-l: 1.125rem; /* 18px at 16px base */\n --navi-typo-xl: 1.25rem; /* 20px at 16px base */\n --navi-typo-xxl: 1.5rem; /* 24px at 16px base */\n\n /* The line every text is written on, controls included. A number\n rather than \"normal\", because of the emoji: its box is taller than a\n letter's, so under \"normal\" the one line carrying one stands taller\n than the lines around it, and under a tighter line its top is cut.\n 1.25 is where neither happens — 1 clips the glyph, 1.5 spaces the rows\n out more than reading them asks for — which also makes it the floor\n for an app that displays what people typed.\n The document is written on it; the components that come with a line of\n their own from the browser (Button, Input, Textarea, Select) are handed\n it by name. One number everywhere is what keeps a value on the same\n line whether it is typed in a field or drawn in the page.\n See docs/typography.md. */\n --navi-line-height: 1.25;\n /* The face everything is written in, controls included — they inherit\n it rather than carrying one of their own, so an app names its typeface\n once, wherever it wants it to start: on the document, on a section, on\n a popup opened with font=\"…\". The browser's own control face rather\n than the document's default, so that a page saying nothing gets its\n text and its buttons in one face instead of two.\n Layered like the line above, so the app's own rule wins. */\n font-family: ${controlDefaultFontFamily};\n /* Layered: the document's line is what navi writes on when the app says\n nothing, so the app's own body rule wins. */\n line-height: var(--navi-line-height);\n /* The same line for a control, snapped to the pixel. The browser lays a\n line out at its exact height but paints the glyph on a pixel row: at\n the default control size (13.333px) the line is 16.666px, and the\n two-thirds that do not fit go entirely under the glyph, which then\n sits a pixel above the middle of its field. A whole number of pixels\n has no remainder to put anywhere. Written with em so it resolves on\n the control that uses it, at that control's own size. */\n --navi-control-line-height: round(\n calc(var(--navi-line-height) * 1em),\n 1px\n );\n\n /* Color keywords.\n primary: the ink of the paper, at full strength. An absolute\n rather than currentColor: it is what brings a run back\n to plain text inside a muted one (a value in a secondary\n label). Themed through --navi-surface-text-color.\n secondary: supporting text, captions, less important labels\n emphasis: reinforce meaning, make content stand out more\n discrete: unobtrusive elements that shouldn't compete for attention\n hint: barely-there color, watermarks, ghost placeholders\n The last four mix currentColor toward transparent or black, so they\n follow whatever ink a container writes in: a dark card sets color and\n nothing else. The share of ink in each is the theme's knob, a -mix\n token read wherever the formulas are declared — here and on each\n surface that re-declares them (rules below) — so a ratio set on :root\n reaches the page and its popups alike. Set on a container it reaches\n none of that container's own text, a var() resolving where its custom\n property is declared; a container that wants one keyword otherwise\n for ITS paper (a white at 88% where 80% of white reads too faint on a\n colored resin) pins the keyword itself, and that value stops at the\n next surface. */\n --navi-color-secondary-mix: 80%;\n --navi-color-emphasis-mix: 50%;\n --navi-color-discrete-mix: 60%;\n --navi-color-hint-mix: 25%;\n --navi-color-primary: var(--navi-surface-text-color);\n\n /* What a control shows while it holds nothing: the ::placeholder of an\n input, the empty value slot of a picker. One place for the whole app:\n\n :root {\n --navi-placeholder-color: #8a94a6;\n --navi-placeholder-font-style: italic;\n }\n */\n --navi-placeholder-color: var(--navi-color-discrete);\n --navi-placeholder-font-style: normal;\n }\n\n /* A surface is a new paper: it writes in an ink of its own, and the\n keywords below are computed against that ink rather than the\n container's. A popup writes in --navi-popup-color. */\n .navi_popover,\n .navi_dialog {\n --navi-color-primary: var(--navi-popup-color);\n }\n /* A callout writes in the UA's own ink: callout.js sets color: revert on\n a [popover] element, which the UA styles CanvasText. */\n .navi_callout {\n --navi-color-primary: CanvasText;\n }\n\n /* The keywords derived from the ink, declared on every paper — :root, and\n each surface that is a new one — so what a container pinned for its own\n paper stops at the surface. The -mix ratios (see :root) are read where\n this is declared, and each paper resolves currentColor as its own. On\n the element itself, so a surface beats the container's value whatever\n the layer; an app that wants a surface to keep its container's ink says\n so on the surface, unlayered, and wins in turn. */\n :root,\n .navi_popover,\n .navi_dialog,\n .navi_callout {\n --navi-color-secondary: color-mix(\n in srgb,\n currentColor var(--navi-color-secondary-mix),\n transparent\n );\n --navi-color-emphasis: color-mix(\n in srgb,\n currentColor var(--navi-color-emphasis-mix),\n black\n );\n --navi-color-discrete: color-mix(\n in srgb,\n currentColor var(--navi-color-discrete-mix),\n transparent\n );\n --navi-color-hint: color-mix(\n in srgb,\n currentColor var(--navi-color-hint-mix),\n transparent\n );\n }\n }\n\n /* Hidden appearance */\n input[navi-visually-hidden],\n button[navi-visually-hidden],\n div[navi-visually-hidden] {\n position: absolute;\n top: 0;\n left: 0;\n z-index: -1;\n /* Important to take full size so that scrollIntoView work as expected */\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n white-space: nowrap;\n border: 0;\n clip-path: inset(50%);\n appearance: none;\n overflow: hidden;\n\n &[navi-debug] {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n clip-path: none;\n appearance: auto;\n }\n }\n`;\nimport.meta.css = css;\n\neffect(() => {\n document.documentElement.style.setProperty(\n \"--navi-vvw\",\n `${visualViewportWidthSignal.value}px`,\n );\n document.documentElement.style.setProperty(\n \"--navi-vvh\",\n `${visualViewportHeightSignal.value}px`,\n );\n});\n\n// Placement follows the same rectangle the size caps above describe: the JS\n// that positions popups (pickPositionRelativeTo in @jsenv/dom) computes\n// against the visual viewport narrowed by the app's own bands, so a dialog\n// centers on the app column and a side panel docks flush against the app's\n// edge rather than the window's. Here rather than in each component: it is\n// one decision about the whole window, and this is the module that makes\n// those.\nsetPlacementViewportInsets(getAppInsets);\n"
|
|
14
|
+
"/**\n * Regroup CSS vars that makes sense to share across all navi components.\n */\nimport { setPlacementViewportInsets } from \"@jsenv/dom\";\nimport { effect } from \"@preact/signals\";\n\nimport {\n getAppInsets,\n visualViewportHeightSignal,\n visualViewportWidthSignal,\n} from \"./layout/responsive.js\";\nimport \"./layout/safe_area.js\";\n\nconst button = document.createElement(\"button\");\nbutton.style.display = \"none\";\ndocument.body.appendChild(button);\nconst computedStyle = getComputedStyle(button);\nconst controlDefaultFontFamily = computedStyle.fontFamily;\nconst controlDefaultFontSize = computedStyle.fontSize;\ndocument.body.removeChild(button);\n\nconst css = /* css */ `\n @layer navi {\n :root {\n /* Overridden at runtime with precise VisualViewport pixel values so that dvw/dvh \n (which don't track the virtual keyboard dimensions) are never used in practice on supported browsers. */\n --navi-vvw: 100dvw;\n --navi-vvh: 100dvh;\n\n /* What navi treats as \"the screen\" when it sizes something that escapes\n normal flow (a dialog in the top layer, a popover, anything built on\n them): the app's own rectangle, which is the visual viewport minus the\n bands an app asks for. An app that never spans the whole window says so\n ONCE, without ever naming a component:\n\n :root {\n --navi-app-max-width: 600px;\n }\n\n Typically an app simulating a handheld screen: a column centered in a\n wide window with bands on the sides. A dialog is in the top layer, so\n it answers to the viewport, not to that column — left alone it would\n paint 1500px of modal over a 600px app. Declaring the ceiling here is\n what makes the app's own width apply everywhere, including on top.\n\n It stays a ceiling and nothing else: on a screen narrower than the app\n it never binds, so popups keep shrinking with the phone, and the gap\n each popup keeps with the edges (marginWithContainer) is subtracted\n from it too. A single popup that genuinely needs more can still\n raise its own maxWidth/maxHeight prop.\n\n Read from the insets rather than as a min() of its own so that the\n width and the placement come from ONE description of where the app is\n (see layout/safe_area.js). Placement follows the same rectangle: see\n setPlacementViewportInsets at the bottom of this file. */\n --navi-app-width: calc(\n var(--navi-vvw) - var(--navi-app-inset-left) - var(\n --navi-app-inset-right\n )\n );\n --navi-app-height: calc(\n var(--navi-vvh) - var(--navi-app-inset-top) - var(\n --navi-app-inset-bottom\n )\n );\n\n --navi-focus-outline-width: 2px;\n --navi-focus-outline-color: light-dark(#4476ff, #3b82f6);\n --navi-loader-color: light-dark(#355fcc, #3b82f6);\n /* The grey rectangle a mobile browser flashes under a finger: we draw\n what a press does ourselves (background, ring, chevron), and the\n browser's rectangle is a square painted over rounded corners.\n Declared here alone because the property is inherited: :root covers\n every element, in and out of navi, so nothing has to remember it.\n Set the token to a color to get the flash back, on the whole app or\n on one subtree. */\n -webkit-tap-highlight-color: var(--navi-tap-highlight-color);\n --navi-tap-highlight-color: transparent;\n\n /* A face of their own for the controls, and nothing else on the page.\n Undeclared here on purpose: controls write in inherit, so what a\n container says reaches the button, the field and the picker inside it,\n and a value on :root would resolve here and wall that off again. Set\n it to give the controls their own face, on the whole app or on one\n subtree:\n\n :root {\n --navi-control-font-family: system-ui;\n }\n */\n --navi-control-font-size: ${controlDefaultFontSize};\n --navi-control-border-radius: 2px;\n /* A checkbox is only ~1em wide: applying a control radius meant for\n buttons/inputs (say 8px) would turn it into a circle and make it read\n as a radio. So it follows the control radius but capped at a quarter\n of its own size. Override this token alone to opt out of the cap. */\n --navi-checkbox-border-radius: min(\n var(--navi-control-border-radius),\n 0.25em\n );\n /* The color a control uses to say \"this one is on\": a checked checkbox,\n a checked radio, an enabled switch. Kept apart from --navi-accent-color\n (the brand color for CTA and selection) because a control that is on\n must stay readable as a control, which usually means a brighter, more\n saturated color than a brand accent.\n The light value matches the browser's own accent so a navi control and\n a native one can sit side by side without reading as two blues. */\n --navi-control-accent-color: light-dark(rgb(24, 117, 255), #3b82f6);\n --navi-control-border-width: 1px;\n --navi-control-border-color: light-dark(#767676, #8e8e93);\n --navi-control-padding-x-default: 2px;\n --navi-control-padding-y-default: 1px;\n /* Global padding defaults — override these to change all button paddings. */\n /* Use --button-padding, --button-padding-x, --button-padding-y for per-button overrides. */\n --navi-button-padding-x-default: 6px;\n --navi-button-padding-y-default: 1px;\n /* Read by Button, and by whatever is drawn as one (a picker under\n variant=\"button\"), so the two keep the same surface. */\n --navi-button-background-color: light-dark(#f3f4f6, #2d3748);\n\n /* What a callout says about its message, as a color — and what draws\n as one outside the callout too (the icon a picker in callout mode\n presses, see CalloutStatusIcon). */\n --navi-callout-success-color: #4caf50;\n --navi-callout-info-color: #2196f3;\n --navi-callout-warning-color: #ff9800;\n --navi-callout-error-color: #f44336;\n /* No status: the message is an aside, said without an icon and framed\n like a control rather than like a warning. */\n --navi-callout-neutral-color: var(--navi-control-border-color);\n /* For list item we need slightly more padding to be able to see radio/checkbox outline */\n --navi-list-item-padding-x-default: 4px;\n --navi-list-item-padding-y-default: 1px;\n /* default */\n --navi-picker-padding-x-default: var(--navi-control-padding-x-default);\n --navi-picker-padding-y-default: var(--navi-control-padding-y-default);\n\n --navi-popup-border-radius: 8px;\n --navi-popup-border-color: light-dark(#d0d0d0, #3b3b3b);\n --navi-popup-box-shadow:\n 0 4px 8px rgba(0, 0, 0, 0.08), 0 12px 40px rgba(0, 0, 0, 0.22);\n /* A popup's paper is the same paper as everything else's: derived from\n the surface token so one override themes fields, layouts and popups\n together. Still its own token, so popups alone can be re-papered. */\n --navi-popup-background-color: var(--navi-surface-color);\n /* The ink written on that paper. A popup declares both on itself (see\n dialog.jsx / popover.jsx): it is a new surface, so it does not write\n in whatever the container it was declared in writes in. */\n --navi-popup-color: var(--navi-surface-text-color);\n /* A backdrop's paint is a colour AND a filter, one pair per kind of\n backdrop: how much the page behind must stop competing for the eye is\n a different question from what an outside click does, so a backdrop\n that closes on that click can blur too. Only \"capture\" blurs by\n default. */\n --navi-backdrop-close-background: rgba(0, 0, 0, 0.08);\n --navi-backdrop-close-backdrop-filter: none;\n /* backdropVariant=\"discrete\": the popup still catches every outside\n click, it just stops announcing that it did. For an affordance one\n reaches past rather than through — the dim is there to mark the layer,\n not to push the page behind it away. */\n --navi-backdrop-discrete-background: rgba(0, 0, 0, 0.02);\n --navi-backdrop-discrete-backdrop-filter: none;\n /* \"capture\" means the rest of the page is fully non-interactive —\n blurred, not just dimmed, so it reads as clearly secondary and\n pulls visual focus onto the popover's own content. */\n --navi-backdrop-capture-background: rgb(255 255 255 / 0.08);\n --navi-backdrop-capture-backdrop-filter: blur(30px) saturate(180%);\n /* animation=\"lifting\", and backdropVariant=\"lift\" asking for the same\n wall without the movement: the popup is what the anchor became, so the page\n it came out of is not a context to keep readable — it is the thing the\n movement leaves. A card, a plan, a picture lifted to be looked at wants\n nothing legible competing with it, and a document still crisp behind a\n box that is still travelling reads as two movements at once. Dark in\n both schemes on purpose: what withdraws here is the page itself, the\n way it does around a photo, not a surface tinted to match a theme. */\n --navi-backdrop-lift-background: rgb(6 10 20 / 88%);\n --navi-backdrop-lift-backdrop-filter: blur(4px);\n\n /* Link colors. They live here rather than only on .navi_link because a\n var declared on the element itself always beats the same var inherited\n from an ancestor: a page setting --link-color-pressed on :root would\n never reach a link. These :root tokens are the theme-level surface;\n --link-color-* stays the per-link (or per-subtree) override. */\n --navi-link-color: rgb(0, 0, 238);\n /* --navi-link-color-visited is intentionally left undefined: by default a\n visited link is derived from whatever --link-color ended up being on\n the link itself (see link.jsx), which a :root token cannot see. Set it\n here from an app to pin one visited color for every link. */\n --navi-link-color-pressed: red;\n --navi-link-current-indicator-color: rgb(205, 52, 37);\n\n --navi-selection-border-color: #0078d4;\n --navi-selection-background-color: #eaf1fd;\n /* Accent color — used for call-to-action buttons and selected list items.\n Override this single variable to apply a consistent brand color across\n all components that need to stand out. */\n --navi-accent-color: rgb(3, 30, 60);\n /* The surface a control's content sits on — the \"paper\" behind the text.\n Fields use it whenever they need a solid background (their default\n background, a transparent field being edited, …). */\n --navi-surface-color: light-dark(#ffffff, #1c1c1e);\n /* What is written on that paper. The browser's own text color rather\n than a literal, so it follows color-scheme the way the paper does. */\n --navi-surface-text-color: CanvasText;\n /* The plane the app's own frame is painted on — a top bar, a side menu, a\n toolbar: everything that surrounds a screen rather than being in it.\n A step away from the paper, because a bar painted in the paper's own\n color on a page of that color is not a bar, it is a line. Derived from\n the paper rather than written as a literal so it follows the theme by\n construction: a step toward the dark on a light page, toward the light\n on a dark one. Reached as background=\"chrome\" on a Box. */\n --navi-chrome-color: color-mix(\n in srgb,\n var(--navi-surface-color) 94%,\n var(--navi-color-dark)\n );\n /* The line that separates two regions of one surface — a scrolling area's\n header from what scrolls under it, for instance. Not a border: the\n separation belongs to the layout, not to the box that draws it. */\n --navi-separator-color-default: #d1d9e0;\n --navi-color-white: white;\n --navi-color-dark: rgb(55, 60, 69);\n\n --navi-info-color-light: #eaf6fc;\n --navi-info-color: #376cc2;\n --navi-success-color-light: #ecf9ef;\n --navi-success-color: #50c464;\n --navi-warning-color-light: #fdf6e3;\n --navi-warning-color: #f19c05;\n --navi-error-color-light: #fcebed;\n --navi-error-color: #eb364b;\n\n --navi-xxs: 0.125em; /* = 2px at 16px base */\n --navi-xs: 0.25em; /* = 4px at 16px base */\n --navi-s: 0.5em; /* = 8px at 16px base */\n --navi-m: 1em; /* = 16px at 16px base (base font size) */\n --navi-l: 1.5em; /* = 24px at 16px base */\n --navi-xl: 2em; /* = 32px at 16px base */\n --navi-xxl: 3em; /* 48px at 16px base */\n\n --navi-typo-xxs: 0.625rem; /* 10px at 16px base */\n --navi-typo-xs: 0.75rem; /* 12px at 16px base */\n --navi-typo-s: 0.875rem; /* 14px at 16px base */\n --navi-typo-m: 1rem; /* 16px at 16px base (base font size) */\n --navi-typo-l: 1.125rem; /* 18px at 16px base */\n --navi-typo-xl: 1.25rem; /* 20px at 16px base */\n --navi-typo-xxl: 1.5rem; /* 24px at 16px base */\n\n /* The line every text is written on, controls included. A number\n rather than \"normal\", because of the emoji: its box is taller than a\n letter's, so under \"normal\" the one line carrying one stands taller\n than the lines around it, and under a tighter line its top is cut.\n 1.25 is where neither happens — 1 clips the glyph, 1.5 spaces the rows\n out more than reading them asks for — which also makes it the floor\n for an app that displays what people typed.\n The document is written on it; the components that come with a line of\n their own from the browser (Button, Input, Textarea, Select) are handed\n it by name. One number everywhere is what keeps a value on the same\n line whether it is typed in a field or drawn in the page.\n See docs/typography.md. */\n --navi-line-height: 1.25;\n /* The face everything is written in, controls included — they inherit\n it rather than carrying one of their own, so an app names its typeface\n once, wherever it wants it to start: on the document, on a section, on\n a popup opened with font=\"…\". The browser's own control face rather\n than the document's default, so that a page saying nothing gets its\n text and its buttons in one face instead of two.\n Layered like the line above, so the app's own rule wins. */\n font-family: ${controlDefaultFontFamily};\n /* Layered: the document's line is what navi writes on when the app says\n nothing, so the app's own body rule wins. */\n line-height: var(--navi-line-height);\n /* The same line for a control, snapped to the pixel. The browser lays a\n line out at its exact height but paints the glyph on a pixel row: at\n the default control size (13.333px) the line is 16.666px, and the\n two-thirds that do not fit go entirely under the glyph, which then\n sits a pixel above the middle of its field. A whole number of pixels\n has no remainder to put anywhere. Written with em so it resolves on\n the control that uses it, at that control's own size. */\n --navi-control-line-height: round(\n calc(var(--navi-line-height) * 1em),\n 1px\n );\n\n /* Color keywords.\n primary: the ink of the paper, at full strength. An absolute\n rather than currentColor: it is what brings a run back\n to plain text inside a muted one (a value in a secondary\n label). Themed through --navi-surface-text-color.\n secondary: supporting text, captions, less important labels\n emphasis: reinforce meaning, make content stand out more\n discrete: unobtrusive elements that shouldn't compete for attention\n hint: barely-there color, watermarks, ghost placeholders\n The last four mix currentColor toward transparent or black, so they\n follow whatever ink a container writes in: a dark card sets color and\n nothing else. The share of ink in each is the theme's knob, a -mix\n token read wherever the formulas are declared — here and on each\n surface that re-declares them (rules below) — so a ratio set on :root\n reaches the page and its popups alike. Set on a container it reaches\n none of that container's own text, a var() resolving where its custom\n property is declared; a container that wants one keyword otherwise\n for ITS paper (a white at 88% where 80% of white reads too faint on a\n colored resin) pins the keyword itself, and that value stops at the\n next surface. */\n --navi-color-secondary-mix: 80%;\n --navi-color-emphasis-mix: 50%;\n --navi-color-discrete-mix: 60%;\n --navi-color-hint-mix: 25%;\n --navi-color-primary: var(--navi-surface-text-color);\n\n /* What a control shows while it holds nothing: the ::placeholder of an\n input, the empty value slot of a picker. One place for the whole app:\n\n :root {\n --navi-placeholder-color: #8a94a6;\n --navi-placeholder-font-style: italic;\n }\n */\n --navi-placeholder-color: var(--navi-color-discrete);\n --navi-placeholder-font-style: normal;\n }\n\n /* A surface is a new paper: it writes in an ink of its own, and the\n keywords below are computed against that ink rather than the\n container's. A popup and a callout write in --navi-popup-color. */\n .navi_popover,\n .navi_dialog,\n .navi_callout {\n --navi-color-primary: var(--navi-popup-color);\n }\n\n /* The keywords derived from the ink, declared on every paper — :root, and\n each surface that is a new one — so what a container pinned for its own\n paper stops at the surface. The -mix ratios (see :root) are read where\n this is declared, and each paper resolves currentColor as its own. On\n the element itself, so a surface beats the container's value whatever\n the layer; an app that wants a surface to keep its container's ink says\n so on the surface, unlayered, and wins in turn. */\n :root,\n .navi_popover,\n .navi_dialog,\n .navi_callout {\n --navi-color-secondary: color-mix(\n in srgb,\n currentColor var(--navi-color-secondary-mix),\n transparent\n );\n --navi-color-emphasis: color-mix(\n in srgb,\n currentColor var(--navi-color-emphasis-mix),\n black\n );\n --navi-color-discrete: color-mix(\n in srgb,\n currentColor var(--navi-color-discrete-mix),\n transparent\n );\n --navi-color-hint: color-mix(\n in srgb,\n currentColor var(--navi-color-hint-mix),\n transparent\n );\n }\n }\n\n /* Hidden appearance */\n input[navi-visually-hidden],\n button[navi-visually-hidden],\n div[navi-visually-hidden] {\n position: absolute;\n top: 0;\n left: 0;\n z-index: -1;\n /* Important to take full size so that scrollIntoView work as expected */\n width: 100%;\n height: 100%;\n margin: 0;\n padding: 0;\n white-space: nowrap;\n border: 0;\n clip-path: inset(50%);\n appearance: none;\n overflow: hidden;\n\n &[navi-debug] {\n position: static;\n width: auto;\n height: auto;\n margin: 0;\n clip-path: none;\n appearance: auto;\n }\n }\n`;\nimport.meta.css = css;\n\neffect(() => {\n document.documentElement.style.setProperty(\n \"--navi-vvw\",\n `${visualViewportWidthSignal.value}px`,\n );\n document.documentElement.style.setProperty(\n \"--navi-vvh\",\n `${visualViewportHeightSignal.value}px`,\n );\n});\n\n// Placement follows the same rectangle the size caps above describe: the JS\n// that positions popups (pickPositionRelativeTo in @jsenv/dom) computes\n// against the visual viewport narrowed by the app's own bands, so a dialog\n// centers on the app column and a side panel docks flush against the app's\n// edge rather than the window's. Here rather than in each component: it is\n// one decision about the whole window, and this is the module that makes\n// those.\nsetPlacementViewportInsets(getAppInsets);\n"
|
|
15
15
|
],
|
|
16
16
|
"names": [],
|
|
17
|
-
"mappings": "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
|
|
17
|
+
"mappings": "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"
|
|
18
18
|
}
|
package/docs/AI_INSTRUCTIONS.md
CHANGED
|
@@ -116,8 +116,11 @@ start when unsure which export solves a problem.
|
|
|
116
116
|
keeps the shape of the question. Before wiring a value with `value` +
|
|
117
117
|
`uiAction`.
|
|
118
118
|
- `control_object.md` — one value made of several controls: `ControlGroup` versus
|
|
119
|
-
`Form`, naming, `<Picker type="object">`, a settings sheet
|
|
120
|
-
|
|
119
|
+
`Form`, naming, `<Picker type="object">`, a settings sheet, controls that are
|
|
120
|
+
not there (lazy slides, popups, deferred boxes, items outside the render
|
|
121
|
+
window: the group keeps their keys, shared state is written through it).
|
|
122
|
+
Before putting anything in a picker popup, and before lazy-mounting anything
|
|
123
|
+
that holds named controls.
|
|
121
124
|
- `group.md` — `<Group>`: several controls reading as one framed object. Before
|
|
122
125
|
negative margins or `border-radius: 0` by hand.
|
|
123
126
|
- `form_changed.md` — a form sends nothing when nothing changed; what "changed"
|
|
@@ -179,9 +182,10 @@ start when unsure which export solves a problem.
|
|
|
179
182
|
- `scroll.md` — where scrolling happens: `header`/`body`/`footer`, a `List`'s
|
|
180
183
|
`scroller`, where a list opens (a position handed back whole, `visibleCount`
|
|
181
184
|
included), a popup that scrolls, many rows (`<List.Items>`, the render window
|
|
182
|
-
and its first picture, `findText`),
|
|
183
|
-
|
|
184
|
-
|
|
185
|
+
and its first picture, `findText`), many sections (`<Box mount="after-paint">`
|
|
186
|
+
and what it leaves out of reach), hover while scrolling. Before writing CSS to
|
|
187
|
+
make something scroll, before rendering a collection as `<List.Item>`
|
|
188
|
+
children, and before deferring part of a page.
|
|
185
189
|
- `z_index.md` — DOM order first; a `z-index` without `isolation: isolate`
|
|
186
190
|
competes with the page; navi's bands. Before writing a `z-index`.
|
|
187
191
|
- `typography.md` — text is a component; `maxLines` is the one truncation; one
|
package/docs/control_object.md
CHANGED
|
@@ -12,6 +12,7 @@ whose value is an object needs in its popup.
|
|
|
12
12
|
- [Naming, and what a nameless group does](#naming-and-what-a-nameless-group-does)
|
|
13
13
|
- [A picker whose value is an object](#a-picker-whose-value-is-an-object)
|
|
14
14
|
- [A group holds what it was given](#a-group-holds-what-it-was-given)
|
|
15
|
+
- [A control that is not there](#a-control-that-is-not-there)
|
|
15
16
|
- [One line, one key](#one-line-one-key)
|
|
16
17
|
- [A settings sheet](#a-settings-sheet)
|
|
17
18
|
- [`Group` is not `ControlGroup`](#group-is-not-controlgroup)
|
|
@@ -142,7 +143,9 @@ not while the children are not all there yet:
|
|
|
142
143
|
|
|
143
144
|
- a group **told** a value holds it whole, before any child has registered to
|
|
144
145
|
show it: items still loading, a popup built at open, a row scrolled out of a
|
|
145
|
-
virtualized list do not make the value smaller
|
|
146
|
+
virtualized list do not make the value smaller, nor does a child acting while
|
|
147
|
+
others are away (see
|
|
148
|
+
[A control that is not there](#a-control-that-is-not-there));
|
|
146
149
|
- **a child mounting or unmounting is not somebody answering.** While children
|
|
147
150
|
arrive, their aggregate is a partial reading; the group takes it for its value
|
|
148
151
|
only once it derived that value itself (nobody handed it one), or once a child
|
|
@@ -209,6 +212,76 @@ const moveTo = (from, to) => {
|
|
|
209
212
|
};
|
|
210
213
|
```
|
|
211
214
|
|
|
215
|
+
## A control that is not there
|
|
216
|
+
|
|
217
|
+
The answer lives in the group — and in the signal bound to it, or to the
|
|
218
|
+
picker around it — and the controls are views of it. A view can be missing
|
|
219
|
+
while the answer still matters: a slide not built yet
|
|
220
|
+
(`SlideContainer mount="near"`), a popup or an `Expandable` not opened yet, a
|
|
221
|
+
`<Box mount="after-paint">` before its first frame, an item outside a list's
|
|
222
|
+
render window, a page parked while a route action reruns. None of that makes
|
|
223
|
+
the answer smaller:
|
|
224
|
+
|
|
225
|
+
- **a key stays until a control of that name, there, says otherwise.** The
|
|
226
|
+
group keeps what it holds for a control that is not there, the way a
|
|
227
|
+
selectable list keeps the selection of items it does not draw. A control that
|
|
228
|
+
arrives is placed from it; one that leaves takes nothing with it;
|
|
229
|
+
- **a value put ON the group reaches every key**, whether its control is there
|
|
230
|
+
or not: the group's `signal` or `value`, a picker filling its popup, a cancel
|
|
231
|
+
putting back what the picker held at open, a `--navi-update` aimed at the
|
|
232
|
+
group. It replaces the whole object, which is also how a key is dropped on
|
|
233
|
+
purpose;
|
|
234
|
+
- **a control's own `signal` reaches that control only.** Written while the
|
|
235
|
+
control is not there, nobody is listening: the group never hears of it, and a
|
|
236
|
+
cancel has nothing to take back. The answer is ONE signal, on the group or on
|
|
237
|
+
the picker holding it — never one per control, assembled by hand;
|
|
238
|
+
- **only a control that is there checks itself.** `required`, a pattern, a range
|
|
239
|
+
are verified by the control, so a field not built yet is not validated;
|
|
240
|
+
- **a field hidden on purpose keeps its key too**: the group cannot tell
|
|
241
|
+
"removed" from "not built yet". Empty it before hiding it, or put the value
|
|
242
|
+
without it on the group.
|
|
243
|
+
|
|
244
|
+
So something shared by controls that are not all there — seven days of which
|
|
245
|
+
one is on screen, a copy of one day onto the others — is written as one value,
|
|
246
|
+
through the group:
|
|
247
|
+
|
|
248
|
+
```jsx
|
|
249
|
+
// the week, keyed by day: what the tabs, the summary and the save read
|
|
250
|
+
const weekSignal = useSignal({ 1: [], 2: [18, 19], 3: [], … });
|
|
251
|
+
|
|
252
|
+
<Picker type="object" signal={weekSignal} action={saveWeek} ui={<WeekSummary />}>
|
|
253
|
+
<ControlGroup id="week">
|
|
254
|
+
<SlideContainer mount="near" signal={daySignal}>
|
|
255
|
+
{DAYS.map((day) => (
|
|
256
|
+
<Slide key={day} area={day}>
|
|
257
|
+
<List selectable multiple name={day}>…</List>
|
|
258
|
+
</Slide>
|
|
259
|
+
))}
|
|
260
|
+
</SlideContainer>
|
|
261
|
+
{/* every day, the ones not built included — and a cancel takes it back */}
|
|
262
|
+
<Button
|
|
263
|
+
command="--navi-update"
|
|
264
|
+
commandFor="week"
|
|
265
|
+
value={sameEveryDay(weekSignal.value[daySignal.value])}
|
|
266
|
+
>
|
|
267
|
+
Same hours every day
|
|
268
|
+
</Button>
|
|
269
|
+
</ControlGroup>
|
|
270
|
+
</Picker>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
```jsx
|
|
274
|
+
// ✗ one signal per day, written by hand: the days not built never hear it,
|
|
275
|
+
// and Escape leaves them changed
|
|
276
|
+
for (const day of DAYS) {
|
|
277
|
+
daySignals[day].value = [...shownHours];
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
The picker's signal follows the popup gesture by gesture, so the button reads
|
|
282
|
+
the day shown from it; the dots on the tabs read it too, for days whose slide
|
|
283
|
+
was never built.
|
|
284
|
+
|
|
212
285
|
## One line, one key
|
|
213
286
|
|
|
214
287
|
A row that opens a popup is one control, so what it answers arrives under its
|
|
@@ -274,7 +347,8 @@ one being tried.
|
|
|
274
347
|
|
|
275
348
|
**Every field answers**, including those on another tab: they stay mounted and
|
|
276
349
|
come back in the object, and whoever receives it reads `origin` to know which
|
|
277
|
-
count.
|
|
350
|
+
count. Under `mount="near"` a tab not built yet answers with what the group
|
|
351
|
+
holds for it ([A control that is not there](#a-control-that-is-not-there)). Aggregating the sheet to a single value of its own
|
|
278
352
|
(`aggregateChildStates`) throws away what makes the answer readable, starting
|
|
279
353
|
with the tab. **A button inside is not a field**: the one asking for a position
|
|
280
354
|
acts on the press, and what it answers goes into a control that IS one (a
|
package/docs/css_architecture.md
CHANGED
|
@@ -355,7 +355,7 @@ the layer:
|
|
|
355
355
|
|
|
356
356
|
| taken back | how |
|
|
357
357
|
| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
358
|
-
| the ink (`color`) | `--navi-popup-color
|
|
358
|
+
| the ink (`color`) | `--navi-popup-color`, on a popup and on a callout |
|
|
359
359
|
| text properties that belong to the opener (alignment, transform, shadow, spacing, wrapping) | reset in `@layer navi`, so an app that wants one of them back says so on the surface |
|
|
360
360
|
| the five color keywords `--navi-color-primary/secondary/emphasis/discrete/hint` | re-declared on each surface, so a `:root` value of one stops there |
|
|
361
361
|
|
package/docs/navigation.md
CHANGED
|
@@ -565,7 +565,10 @@ of the document's scrollport — it holds an offset even without a scrollbar, wh
|
|
|
565
565
|
`overflow: clip` holds none — so it is the first thing to look for when a row
|
|
566
566
|
does not give positions back. And a page whose height depends on something still
|
|
567
567
|
loading is not tall enough when its position is put back: only the page knows
|
|
568
|
-
when it is whole.
|
|
568
|
+
when it is whole. What navi itself builds after the first frame
|
|
569
|
+
(`<Box mount="after-paint">`, see
|
|
570
|
+
[scroll.md](./scroll.md#many-sections-box-mountafter-paint)) is not such a
|
|
571
|
+
page: the part the position shows is built before it is written.
|
|
569
572
|
|
|
570
573
|
## Creating something, then editing it
|
|
571
574
|
|
package/docs/popup_open.md
CHANGED
|
@@ -895,9 +895,12 @@ opening — so the `mount` prop answers both at once:
|
|
|
895
895
|
| `"while-opened"` | not mounted | not mounted |
|
|
896
896
|
|
|
897
897
|
`"always"` is for content something depends on before any opening: a value read
|
|
898
|
-
off it (the default of a picker told no value whose value is read), fields a
|
|
899
|
-
|
|
900
|
-
|
|
898
|
+
off it (the default of a picker told no value whose value is read), fields a
|
|
899
|
+
surrounding form must validate or read their own `defaultValue` from, a size
|
|
900
|
+
measured from outside. What such a form was handed it keeps for fields not built
|
|
901
|
+
yet, and hands them once they are
|
|
902
|
+
([control_object.md](./control_object.md#a-control-that-is-not-there)).
|
|
903
|
+
`"idle"` is `"always"` minus the cost on the render that draws the page.
|
|
901
904
|
|
|
902
905
|
Whatever the value but `"while-opened"`, intent on what opens the popup builds
|
|
903
906
|
the content ahead of the click, `onOpen` or not: a pointer over its anchor, or
|
package/docs/scroll.md
CHANGED
|
@@ -307,6 +307,11 @@ A run whose rows all fit the window is just rows: nothing is virtualized, and
|
|
|
307
307
|
it costs what the same rows would as children. There is no reason to hold back
|
|
308
308
|
from it for a list that might grow.
|
|
309
309
|
|
|
310
|
+
What an item outside the window holds is not lost with its row: a selection
|
|
311
|
+
keeps the items it does not draw (`--navi-select` reaches them by id), and a
|
|
312
|
+
group keeps the keys of named controls that are not there
|
|
313
|
+
([control_object.md](./control_object.md#a-control-that-is-not-there)).
|
|
314
|
+
|
|
310
315
|
### What the list knows, and what it guesses
|
|
311
316
|
|
|
312
317
|
A virtualized list has one aim: draw as few items as it can, and never show a
|
|
@@ -433,6 +438,54 @@ on the run when the collection changes as a whole (never to refresh it, see
|
|
|
433
438
|
searched), `src/control/demos/integration/1_list_loaded_by_scroll_demo.html` (a
|
|
434
439
|
run reading a slice at a time).
|
|
435
440
|
|
|
441
|
+
## Many sections: `<Box mount="after-paint">`
|
|
442
|
+
|
|
443
|
+
The render window's twin for a page that is not a list — a profile, a settings
|
|
444
|
+
screen, sections stacked in a column. The browser paints nothing until the
|
|
445
|
+
render that builds the page has ended, and the sections below the first screen
|
|
446
|
+
weigh in it as much as those on it.
|
|
447
|
+
|
|
448
|
+
```jsx
|
|
449
|
+
<Box flex="y" spacing="l">
|
|
450
|
+
<ProfileHeader user={user} />
|
|
451
|
+
<UpcomingGames user={user} />
|
|
452
|
+
<Box flex="y" spacing="l" mount="after-paint">
|
|
453
|
+
<Searches user={user} />
|
|
454
|
+
<Badges user={user} />
|
|
455
|
+
<LinkedAccounts />
|
|
456
|
+
</Box>
|
|
457
|
+
</Box>
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
The box is empty in the render that mounts it, and built right after the first
|
|
461
|
+
frame that shows the page (the first frame of its route transition, when there
|
|
462
|
+
is one), then kept: a later render of the page renders it as usual.
|
|
463
|
+
|
|
464
|
+
Which sections lie below the screen is not left to the app's guess, which a
|
|
465
|
+
taller phone or an empty first section breaks. navi looks at each box before
|
|
466
|
+
that frame is painted, and one the frame would show — on the screen, or above
|
|
467
|
+
it — is built before the paint all the same: marking too much costs a second
|
|
468
|
+
render in that frame, never a section popping in. A page put back where it was
|
|
469
|
+
left (see
|
|
470
|
+
[navigation.md](./navigation.md#where-a-navigation-lands-the-scroll)) has the
|
|
471
|
+
boxes that offset shows built before the offset is written, so the offset is not
|
|
472
|
+
clamped by a document still missing its bottom.
|
|
473
|
+
|
|
474
|
+
What navi cannot do is make the content exist before it is built. So the box is
|
|
475
|
+
only for content nothing reaches during that first frame:
|
|
476
|
+
|
|
477
|
+
- **Named controls** inside are not there until built: what a group holds for
|
|
478
|
+
them waits, a value written into their own `signal` reaches nothing, and
|
|
479
|
+
nothing inside checks itself (see
|
|
480
|
+
[control_object.md](./control_object.md#a-control-that-is-not-there)).
|
|
481
|
+
- **Ids**: a `commandFor`, a popup `anchor` or a `triggerNaviCommand` aimed
|
|
482
|
+
inside finds nothing, and neither does a focus put back there. A `#fragment`
|
|
483
|
+
aimed inside lands a frame late, once the element is there; a popup opened
|
|
484
|
+
from the address opens once built, since it reads its signal then.
|
|
485
|
+
- **Size**: the box has no height until it is built (a `minHeight` reserves
|
|
486
|
+
one). Anything measuring it in that frame — a sticky offset, a size read from
|
|
487
|
+
outside — sees it empty.
|
|
488
|
+
|
|
436
489
|
## Hover while scrolling
|
|
437
490
|
|
|
438
491
|
A scroll moves the content under a pointer that does not move, and the browser
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/navi",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.427",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Library of components including navigation to create frontend applications",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"preact": "^11.0.0-rc.2"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@jsenv/dom": "0.17.
|
|
37
|
+
"@jsenv/dom": "0.17.89",
|
|
38
38
|
"@jsenv/humanize": "1.8.6",
|
|
39
39
|
"@jsenv/validity": "0.4.5"
|
|
40
40
|
},
|