@graview/primitives 0.1.65 → 0.1.66

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.
Files changed (128) hide show
  1. package/dist/app-bar.d.ts +88 -15
  2. package/dist/app-bar.d.ts.map +1 -1
  3. package/dist/app-bar.js +176 -130
  4. package/dist/app-bar.js.map +1 -1
  5. package/dist/app-menu.d.ts +9 -0
  6. package/dist/app-menu.d.ts.map +1 -0
  7. package/dist/app-menu.js +60 -0
  8. package/dist/app-menu.js.map +1 -0
  9. package/dist/bar-activity.d.ts +5 -9
  10. package/dist/bar-activity.d.ts.map +1 -1
  11. package/dist/bar-activity.js +24 -7
  12. package/dist/bar-activity.js.map +1 -1
  13. package/dist/bar-panes.d.ts +5 -5
  14. package/dist/bar-panes.d.ts.map +1 -1
  15. package/dist/bar-panes.js +9 -5
  16. package/dist/bar-panes.js.map +1 -1
  17. package/dist/bar-suggested.d.ts +9 -1
  18. package/dist/bar-suggested.d.ts.map +1 -1
  19. package/dist/bar-suggested.js +11 -17
  20. package/dist/bar-suggested.js.map +1 -1
  21. package/dist/better-bar.d.ts +65 -0
  22. package/dist/better-bar.d.ts.map +1 -0
  23. package/dist/better-bar.js +252 -0
  24. package/dist/better-bar.js.map +1 -0
  25. package/dist/better-panel.d.ts +50 -0
  26. package/dist/better-panel.d.ts.map +1 -0
  27. package/dist/better-panel.js +151 -0
  28. package/dist/better-panel.js.map +1 -0
  29. package/dist/editable.d.ts +8 -1
  30. package/dist/editable.d.ts.map +1 -1
  31. package/dist/editable.js +140 -16
  32. package/dist/editable.js.map +1 -1
  33. package/dist/first-look-door.d.ts +2 -2
  34. package/dist/first-look-door.js +2 -2
  35. package/dist/first-look.d.ts.map +1 -1
  36. package/dist/first-look.js +9 -7
  37. package/dist/first-look.js.map +1 -1
  38. package/dist/frame.d.ts +2 -2
  39. package/dist/frame.d.ts.map +1 -1
  40. package/dist/frame.js +1 -1
  41. package/dist/frame.js.map +1 -1
  42. package/dist/inbox-pane.d.ts +31 -0
  43. package/dist/inbox-pane.d.ts.map +1 -0
  44. package/dist/inbox-pane.js +89 -0
  45. package/dist/inbox-pane.js.map +1 -0
  46. package/dist/inbox.d.ts +17 -0
  47. package/dist/inbox.d.ts.map +1 -0
  48. package/dist/inbox.js +84 -0
  49. package/dist/inbox.js.map +1 -0
  50. package/dist/index.d.ts +4 -3
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/index.js +3 -2
  53. package/dist/index.js.map +1 -1
  54. package/dist/menu-suggested.d.ts +54 -0
  55. package/dist/menu-suggested.d.ts.map +1 -0
  56. package/dist/menu-suggested.js +140 -0
  57. package/dist/menu-suggested.js.map +1 -0
  58. package/dist/menu-told-door.js +1 -1
  59. package/dist/menu-told-door.js.map +1 -1
  60. package/dist/menu-told.d.ts +7 -0
  61. package/dist/menu-told.d.ts.map +1 -1
  62. package/dist/menu-told.js +24 -0
  63. package/dist/menu-told.js.map +1 -1
  64. package/dist/notify-sheet.js +2 -2
  65. package/dist/notify-sheet.js.map +1 -1
  66. package/dist/page-better.d.ts +13 -0
  67. package/dist/page-better.d.ts.map +1 -0
  68. package/dist/page-better.js +714 -0
  69. package/dist/page-better.js.map +1 -0
  70. package/dist/page-parts.d.ts +41 -0
  71. package/dist/page-parts.d.ts.map +1 -0
  72. package/dist/page-parts.js +100 -0
  73. package/dist/page-parts.js.map +1 -0
  74. package/dist/pages.d.ts +2 -3
  75. package/dist/pages.d.ts.map +1 -1
  76. package/dist/pages.js +2 -3
  77. package/dist/pages.js.map +1 -1
  78. package/dist/profile.d.ts +1 -10
  79. package/dist/profile.d.ts.map +1 -1
  80. package/dist/profile.js +2 -3
  81. package/dist/profile.js.map +1 -1
  82. package/dist/scene-bar.d.ts +1 -1
  83. package/dist/scene-bar.d.ts.map +1 -1
  84. package/dist/scene-bar.js +4 -7
  85. package/dist/scene-bar.js.map +1 -1
  86. package/dist/section-better.d.ts +47 -9
  87. package/dist/section-better.d.ts.map +1 -1
  88. package/dist/section-better.js +239 -121
  89. package/dist/section-better.js.map +1 -1
  90. package/dist/section-door.d.ts +7 -0
  91. package/dist/section-door.d.ts.map +1 -1
  92. package/dist/section-door.js +39 -5
  93. package/dist/section-door.js.map +1 -1
  94. package/dist/section-menu.d.ts +23 -9
  95. package/dist/section-menu.d.ts.map +1 -1
  96. package/dist/section-menu.js +176 -23
  97. package/dist/section-menu.js.map +1 -1
  98. package/dist/section-words.d.ts +8 -0
  99. package/dist/section-words.d.ts.map +1 -1
  100. package/dist/section-words.js +25 -0
  101. package/dist/section-words.js.map +1 -1
  102. package/dist/shell.d.ts +5 -1
  103. package/dist/shell.d.ts.map +1 -1
  104. package/dist/shell.js +4 -2
  105. package/dist/shell.js.map +1 -1
  106. package/dist/suggested.d.ts +21 -10
  107. package/dist/suggested.d.ts.map +1 -1
  108. package/dist/suggested.js +43 -64
  109. package/dist/suggested.js.map +1 -1
  110. package/dist/told-ui.d.ts +1 -1
  111. package/dist/told-ui.d.ts.map +1 -1
  112. package/dist/told-ui.js +1 -1
  113. package/dist/told-ui.js.map +1 -1
  114. package/dist/when-words.d.ts +21 -0
  115. package/dist/when-words.d.ts.map +1 -0
  116. package/dist/when-words.js +43 -0
  117. package/dist/when-words.js.map +1 -0
  118. package/dist/workbench/activity.d.ts +44 -0
  119. package/dist/workbench/activity.d.ts.map +1 -1
  120. package/dist/workbench/activity.js +74 -59
  121. package/dist/workbench/activity.js.map +1 -1
  122. package/dist/workbench/inspector.d.ts.map +1 -1
  123. package/dist/workbench/inspector.js +170 -31
  124. package/dist/workbench/inspector.js.map +1 -1
  125. package/dist/workbench/standing.d.ts.map +1 -1
  126. package/dist/workbench/standing.js +1 -1
  127. package/dist/workbench/standing.js.map +1 -1
  128. package/package.json +6 -6
package/dist/app-bar.js CHANGED
@@ -2,12 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { allLabel, arrangedEntries, hueFor, isInlineSvg, kindPath, MOST_STANDING, offeredPlaces, onePerPicture, OVERVIEW_SLUG, placeLabel, placePath, pluralLabel, primaryOf, samePicture, sharesItsName, supportingKinds } from "@graview/core";
3
3
  import { POPOVER_STYLE, useGraviewIfAny, usePopover } from "@graview/react/provider";
4
4
  import { useQuickAskDrawn } from "./quick-ask-desk.js";
5
- import { createContext, Suspense, useCallback, useContext, useEffect, useLayoutEffect, useRef, useState } from "react";
5
+ import { createContext, Suspense, useCallback, useContext, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
6
6
  import { AppMark } from "./app-title.js";
7
7
  import { useMarkup } from "./markup.js";
8
8
  import { barPanes } from "./bar-panes-door.js";
9
9
  /* The places' foot, Hidden and Recently deleted (Roadmap f-place-menu-views): fetched with the panes, and the tidying with it, once there is something to list and the list is open. */
10
10
  const TidyFoot = barPanes.part((panes, props) => _jsx(panes.TidyFoot, { ...props }), { quiet: true });
11
+ /* The app menu's rows (Roadmap f-app-bar-calm): fetched with the panes when it is first reached for. */
12
+ const AppMenuRows = barPanes.part((panes, props) => _jsx(panes.AppMenuPane, { ...props }), { what: "The app menu" });
11
13
  /* Who else is here (FR-155): drawn once somebody is, so a reader alone never fetches it. */
12
14
  const Here = barPanes.part((panes, props) => _jsx(panes.Here, { ...props }), { quiet: true });
13
15
  /**
@@ -134,8 +136,10 @@ export const useBarFind = () => useContext(BarFindContext);
134
136
  export const TOOL = 30;
135
137
  /** The bar's one row, its rule under it included, on a desk and a phone alike (FR-138). */
136
138
  export const BAR_HEIGHT = 48;
137
- /** Below this width of its own the bar is a phone's: the place is the page's first line, and Find a magnifier that opens the box over the row. */
139
+ /** Below this width of its own the bar is a phone's: the place is the page's first line. */
138
140
  export const BAR_PHONE = 640;
141
+ /** Below this width of its own Find is the magnifier, which opens the box over the row (Roadmap f-app-bar-calm): sooner than a phone's. */
142
+ export const BAR_FIND_FOLDS = 900;
139
143
  /** The room between two places standing on the row (FR-145). */
140
144
  export const PLACE_GAP = 4;
141
145
  /** The fewest places that stand on the row: fewer, and the one control says the same in less room. */
@@ -180,17 +184,15 @@ export function placesThatStand(input) {
180
184
  * short, Find gives first (a small box that says its shortcut, drawn wide
181
185
  * while it is used), then the place down to 9em of its name (whole in its
182
186
  * title), then the app's name, which wraps between its words onto a second
183
- * line and never inside one; the switch keeps its size, and draws its marks
184
- * alone when its words do not fit (`roomForWords`). Whether the places stand
185
- * and the words fit is weighed with the name as it would stand on one line
187
+ * line and never inside one; the app menu's chevron keeps its size. Whether
188
+ * the places stand is weighed with the name as it would stand on one line
186
189
  * (`nameGiven`), never as it is drawn now: the name drawn on two lines because
187
190
  * the places stood would fold them, and the name back on one would stand
188
191
  * them again, frame after frame.
189
192
  *
190
193
  * Where the places stand on the row (FR-145), Find gives first — down to
191
194
  * its least — then the places fold into "More", then into the one control,
192
- * which gives as above. The places are weighed with the switch's words
193
- * kept: the words are worth more than a third place on the row.
195
+ * which gives as above.
194
196
  *
195
197
  * The order is the shrink factors: Find 600, the place 100, the app 1. A
196
198
  * factor under 1 would not do — the free space is scaled by the factors
@@ -204,23 +206,31 @@ export function placesThatStand(input) {
204
206
  const BAR_CSS = `
205
207
  .graview-bar{display:block;margin:0;padding:0;container:graview-bar/inline-size;flex:0 0 auto;position:relative;background:var(--graview-bar);border-bottom:1px solid var(--graview-edge);color:var(--graview-ink);font-family:var(--graview-font-body,system-ui)}
206
208
  .graview-bar-row{display:flex;flex-wrap:nowrap;align-items:center;gap:16px;height:${BAR_HEIGHT - 1}px;margin:0;padding:0 16px}
207
- .graview-bar-home,.graview-bar-face,.graview-bar-place,.graview-bar-item,.graview-bar-at{display:inline-flex;align-items:center;box-sizing:border-box;min-width:0;margin:0;border:0;background:none;box-shadow:none;font:inherit;letter-spacing:normal;text-transform:none;text-decoration:none;color:var(--graview-ink);cursor:pointer;text-align:left}
208
- .graview-bar-app{display:flex;flex:0 1 auto;max-width:50%}
209
+ .graview-bar-home,.graview-bar-place,.graview-bar-item,.graview-bar-at{display:inline-flex;align-items:center;box-sizing:border-box;min-width:0;margin:0;border:0;background:none;box-shadow:none;font:inherit;letter-spacing:normal;text-transform:none;text-decoration:none;color:var(--graview-ink);cursor:pointer;text-align:left}
210
+ .graview-bar-app{display:flex;align-items:center;gap:2px;flex:0 1 auto;max-width:50%}
209
211
  .graview-bar-app::after{content:"";flex:0 100000 auto;width:2px;min-width:0}
210
212
  .graview-bar-name{margin:0;display:flex;font-family:var(--graview-font-display,var(--graview-font-body,system-ui));font-size:.9375rem;font-weight:600;line-height:1.25}
211
213
  .graview-bar-home{gap:8px;min-width:auto;min-height:30px;padding:0;letter-spacing:-.005em}
212
214
  .graview-bar-app-name{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden;text-overflow:ellipsis;overflow-wrap:normal;word-break:normal}
213
- .graview-bar-faces{display:inline-flex;flex:0 0 auto;box-sizing:border-box;height:30px;margin:0;padding:2px;gap:2px;border:1px solid var(--graview-edge);border-radius:8px}
214
- .graview-bar-face{gap:6px;padding:0 10px;border-radius:6px;font-size:.8125rem;white-space:nowrap;color:var(--graview-ink-muted)}
215
- .graview-bar-face span{min-width:0;max-width:11em;overflow:hidden;text-overflow:ellipsis}
216
- .graview-bar-face[aria-pressed=true]{color:var(--graview-ink);font-weight:600;background:color-mix(in srgb,var(--graview-accent) 16%,transparent)}
217
- [data-switch-drawn=icons] .graview-bar-face{padding:0 8px}
218
- .graview-bar-said,[data-switch-drawn=icons] .graview-bar-face span{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
215
+ .graview-bar-said{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}
216
+ .graview-bar-menu-open{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;box-sizing:border-box;width:24px;height:30px;margin:0;padding:0;border:1px solid transparent;border-radius:6px;background:none;box-shadow:none;color:var(--graview-ink-muted);cursor:pointer}
217
+ .graview-bar-menu-open:is(:hover,[aria-expanded=true]){border-color:var(--graview-edge);color:var(--graview-ink)}
218
+ .graview-bar-tool{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:6px;flex:0 0 auto;box-sizing:border-box;min-width:${TOOL}px;height:${TOOL}px;margin:0;padding:0;border-radius:8px;border:1px solid transparent;background:none;box-shadow:none;font:inherit;font-size:.875rem;font-weight:500;letter-spacing:normal;text-transform:none;white-space:nowrap;color:var(--graview-ink-muted);cursor:pointer;text-decoration:none}
219
+ .graview-bar-tool:is(:hover,[aria-expanded=true]){border-color:var(--graview-edge);color:var(--graview-ink)}
220
+ .graview-bar-tool[data-running]{color:var(--graview-accent)}
221
+ .graview-bar-badge{position:absolute;top:-3px;right:-5px;box-sizing:border-box;min-width:16px;height:16px;padding:0 4px;border-radius:8px;background:var(--graview-accent);color:var(--graview-accent-ink);font-size:.625rem;font-weight:700;line-height:16px;text-align:center;font-variant-numeric:tabular-nums;box-shadow:0 0 0 2px var(--graview-bar);pointer-events:none}
222
+ .graview-bar-badge[data-dot]{min-width:8px;width:8px;height:8px;padding:0;top:2px;right:2px}
223
+ .graview-bar-badge[data-tone=warn]{background:var(--graview-warn)}
224
+ .graview-bar-share{gap:6px;padding:0 10px}
225
+ .graview-bar-share-pane{display:grid;gap:10px;width:min(300px,calc(100vw - 24px));padding:14px;border-radius:10px;border:1px solid var(--graview-edge);background:var(--graview-float);box-shadow:var(--graview-lift-high);color:var(--graview-ink);font-size:.875rem;line-height:1.45}
226
+ .graview-bar-share-pane[hidden]{display:none}
227
+ .graview-bar-share-pane p{margin:0}
228
+ .graview-bar-share-pane a,.graview-bar-share-pane button{justify-self:start;display:inline-flex;align-items:center;min-height:28px;padding:0;border:0;background:none;box-shadow:none;font:inherit;font-weight:600;color:var(--graview-accent);text-decoration:none;cursor:pointer}
219
229
  .graview-bar-mid{display:flex;flex:1 100 0;min-width:0}
220
230
  .graview-bar-mid[data-place]{min-width:9em;flex-basis:auto}
221
231
  .graview-bar-place-at{position:relative;display:inline-flex;min-width:0;max-width:100%}
222
232
  .graview-bar-place{gap:6px;height:30px;max-width:100%;padding:0 8px;border:1px solid transparent;border-radius:8px;font-size:.875rem;font-weight:600}
223
- .graview-bar-face:hover,.graview-bar-place:hover{border-color:var(--graview-edge);color:var(--graview-ink)}
233
+ .graview-bar-place:hover{border-color:var(--graview-edge);color:var(--graview-ink)}
224
234
  .graview-bar-place-words{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
225
235
  .graview-bar-mid[data-places=standing]{flex:0 0 auto}
226
236
  .graview-bar-places{display:flex;align-items:center;gap:${PLACE_GAP}px;min-width:0;margin:0;padding:0}
@@ -247,10 +257,11 @@ const BAR_CSS = `
247
257
  .graview-bar-host{display:flex;align-items:center;gap:2px;flex:0 0 auto}
248
258
  .graview-bar-host-action{display:inline-flex;align-items:center;gap:6px;box-sizing:border-box;min-width:${TOOL}px;height:${TOOL}px;margin:0;padding:0 10px;border-radius:8px;border:1px solid transparent;background:none;box-shadow:none;font:inherit;font-size:.875rem;font-weight:500;letter-spacing:normal;text-transform:none;white-space:nowrap;color:var(--graview-ink-muted);cursor:pointer}
249
259
  .graview-bar-host-action:is(:hover,[aria-expanded=true]){border-color:var(--graview-edge);color:var(--graview-ink)}
250
- .graview-bar-host-action[data-folded]{padding:0}
260
+ .graview-bar-host-action:is([data-folded],[data-icon-only]){padding:0;justify-content:center}
251
261
  .graview-bar-host-mark{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:16px;height:16px;font-size:.9375rem;line-height:1}
252
262
  .graview-bar-host-mark svg{width:16px;height:16px}
253
263
  .graview-bar-list .graview-bar-host-action{width:100%;justify-content:flex-start;white-space:normal;overflow-wrap:anywhere}
264
+ .graview-bar-host-action[data-icon-only]>.graview-bar-host-words{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
254
265
  .graview-bar-find{position:relative;display:flex;min-width:7.5rem;flex:0 600 9rem;--graview-bar-find-end:3.25em}
255
266
  .graview-bar-find-slot{display:flex;flex:1;min-width:0}
256
267
  .graview-bar-find-keys{position:absolute;right:10px;line-height:30px;pointer-events:none;font-size:.75rem;color:var(--graview-ink-faint)}
@@ -260,16 +271,21 @@ const BAR_CSS = `
260
271
  .graview-bar-line .graview-bar-place{height:auto;min-height:32px}
261
272
  .graview-bar-line .graview-bar-place-words{white-space:normal}
262
273
  @media (pointer:coarse){.graview-bar-find-keys{display:none}}
274
+ @container graview-bar (max-width: ${BAR_FIND_FOLDS - 1}px){
275
+ .graview-bar-find{display:none}
276
+ .graview-bar-find-open{display:inline-flex}
277
+ .graview-bar-tools>.graview-bar-find-open{margin-left:auto}
278
+ .graview-bar[data-finding] :is(.graview-bar-app,.graview-bar-mid,.graview-bar-find-open){display:none}
279
+ .graview-bar[data-finding] .graview-bar-find{display:flex;flex:1 1 auto;min-width:0;margin-left:0}
280
+ }
263
281
  @container graview-bar (max-width: ${BAR_PHONE - 1}px){
264
282
  .graview-bar-row{gap:10px;padding:0 12px}
265
- .graview-bar-app,.graview-bar-home{max-width:none;flex:1 1 auto;min-width:28px}
283
+ .graview-bar-app{max-width:none;flex:1 1 auto;min-width:28px}
284
+ .graview-bar-home{min-width:28px}
266
285
  .graview-bar-name,.graview-bar-app-name{min-width:0}
267
- .graview-bar-mid,.graview-bar-find{display:none}
268
- .graview-bar-find-open{display:inline-flex}
269
- .graview-bar-own{--graview-activity-mark:inline}
270
- .graview-bar-own .graview-activity-word{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
271
- .graview-bar[data-finding] :is(.graview-bar-app,.graview-bar-faces,.graview-bar-find-open){display:none}
272
- .graview-bar[data-finding] .graview-bar-find{display:flex;flex:1 1 auto;min-width:0}
286
+ .graview-bar-mid{display:none}
287
+ .graview-bar-share{padding:0}
288
+ .graview-bar-share>.graview-bar-share-words{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
273
289
  }
274
290
  @container graview-bar (max-width: 300px){
275
291
  .graview-bar-row{gap:4px;padding:0 10px}
@@ -295,22 +311,23 @@ export const toolStyle = {
295
311
  };
296
312
  /**
297
313
  * THE BAR. The app at the left — its mark, its name as the page's heading,
298
- * the way home — then the switch between the scene and the pages (FR-137),
299
- * then, on Pages, the place you are on (FR-138), and the tools at the
300
- * right: the Find a face puts in it, then `tools` (the standing and the
301
- * person). One row of one height on a desk and a phone; on a phone the
314
+ * the way home — and the app menu beside it, which holds the scene and the
315
+ * pages (FR-137, Roadmap f-app-bar-calm), then, on Pages, the place you are
316
+ * on (FR-138), and the tools at the right: the Find a face puts in it, the
317
+ * inbox, a host's buttons, the people and a host's Share, then `tools` (the
318
+ * standing and the person). One row of one height on a desk and a phone; on a phone the
302
319
  * place is the page's first line, under the bar.
303
320
  */
304
- export function AppBar({ brand, name, heading = 1, home, faces, places, current, reach, tools, find = true, description, findBox, onFind, switch: switchForm = "words", scenePlaces, skips, named = true, actions, }) {
321
+ export function AppBar({ brand, name, heading = 1, home, faces, places, current, reach, tools, find = true, description, findBox, onFind, scenePlaces, skips, named = true, actions, appMenu = NO_ROWS, scheme, share, }) {
305
322
  const bar = useRef(null);
306
323
  // Anybody else here, by the provider's presence (FR-155); none where no provider is.
307
324
  const company = useGraviewIfAny()?.who.size;
308
325
  const [slot, setSlot] = useState(null);
309
326
  const [own, setOwn] = useState(null);
310
327
  const [compact, setCompact] = useState(false);
328
+ // Whether Find is the magnifier (below `BAR_FIND_FOLDS`), which opens the box over the row.
329
+ const [folded, setFolded] = useState(false);
311
330
  const [finding, setFinding] = useState(false);
312
- // Whether the switch's words fit beside everything else the row holds (`roomForWords`).
313
- const [roomy, setRoomy] = useState(true);
314
331
  // Which places stand on the row, by their place in the list ("0,1,2"); null, the one control (FR-145).
315
332
  const [standing, setStanding] = useState(null);
316
333
  // Whether the host's buttons stand on the row (FR-227): where the bar has their width over a phone's; folded where it does not.
@@ -327,21 +344,16 @@ export function AppBar({ brand, name, heading = 1, home, faces, places, current,
327
344
  const shown = from && { ...from, places: offeredPlaces(from.places), here: from.places.find((place) => place.key === from.current)?.label };
328
345
  const list = shown && shown.places.length > 0 ? shown : null;
329
346
  const currentAt = list ? list.places.findIndex((place) => place.key === list.current) : -1;
330
- const wordsAsked = Boolean(faces) && switchForm === "words";
331
347
  // Which places may stand, as one word for the weighing to depend on ("110010…").
332
348
  const ranks = list ? list.places.map((place) => (place.primary === false ? "0" : "1")).join("") : "";
333
349
  const weigh = useCallback(() => {
334
350
  const element = bar.current;
335
351
  if (!element)
336
352
  return;
337
- const stand = standsIn(element, currentAt, wordsAsked, ranks);
353
+ const stand = standsIn(element, currentAt, ranks);
338
354
  if (stand !== undefined)
339
355
  setStanding(stand);
340
- // Standing places were weighed with the switch's words kept.
341
- const room = stand ? true : roomForWords(element);
342
- if (room !== null)
343
- setRoomy(room);
344
- }, [currentAt, wordsAsked, ranks]);
356
+ }, [currentAt, ranks]);
345
357
  useLayoutEffect(() => {
346
358
  const element = bar.current;
347
359
  if (!element)
@@ -349,8 +361,10 @@ export function AppBar({ brand, name, heading = 1, home, faces, places, current,
349
361
  const read = () => {
350
362
  const width = element.getBoundingClientRect().width;
351
363
  setCompact(width < BAR_PHONE);
364
+ setFolded(width < BAR_FIND_FOLDS);
352
365
  // The host's buttons as they would stand, off their ruler: they stand where the bar has their width over a phone's.
353
366
  const host = element.querySelector("[data-graview-bar-host-ruler]");
367
+ // Two of the bar's tools drawn as marks alone stand at every width, as the bar's own do.
354
368
  setHostStands(width >= BAR_PHONE + (host?.getBoundingClientRect().width ?? 0));
355
369
  // What hangs from the bar as wide as it (a phone's Find sheet) is told the bar's width, not the screen's.
356
370
  element.style.setProperty("--graview-bar-width", `${Math.round(width)}px`);
@@ -384,8 +398,8 @@ export function AppBar({ brand, name, heading = 1, home, faces, places, current,
384
398
  const told = useRef(onFind);
385
399
  told.current = onFind;
386
400
  useLayoutEffect(() => {
387
- told.current?.(find && slot ? { slot, compact, own } : null);
388
- }, [find, slot, compact, own]);
401
+ told.current?.(find && slot ? { slot, compact: folded, own } : null);
402
+ }, [find, slot, folded, own]);
389
403
  useLayoutEffect(() => () => told.current?.(null), []);
390
404
  const openFind = useCallback(() => {
391
405
  setFinding(true);
@@ -404,6 +418,50 @@ export function AppBar({ brand, name, heading = 1, home, faces, places, current,
404
418
  escaping.current = false;
405
419
  requestAnimationFrame(() => opener.current?.focus());
406
420
  }, []);
421
+ /*
422
+ * HEARD ON THE ELEMENT ITSELF, not through React: the face's box is a
423
+ * portal from the face drawn under the bar, so its keys and its blur
424
+ * bubble through the face's tree and never reach a handler written here.
425
+ * Put away when folded (`BAR_FIND_FOLDS`) as the keyboard leaves it with
426
+ * nothing typed, or by Escape once its words are gone.
427
+ */
428
+ const findAt = useRef(null);
429
+ useEffect(() => {
430
+ const at = findAt.current;
431
+ if (!at || !folded)
432
+ return;
433
+ const typed = () => Boolean(at.querySelector("input")?.value);
434
+ const left = (event) => {
435
+ const next = event.relatedTarget;
436
+ if (next instanceof Node && at.contains(next))
437
+ return;
438
+ if (!typed())
439
+ putAway();
440
+ };
441
+ const marked = (event) => {
442
+ if (event.key === "Escape")
443
+ escaping.current = true;
444
+ };
445
+ const escaped = (event) => {
446
+ if (event.key !== "Escape")
447
+ return;
448
+ // The scene's Find takes Escape for its words first; an empty box is put away. Weighed once the face's own handler (React's, at its root, after this one) has had the key.
449
+ queueMicrotask(() => {
450
+ if (!event.defaultPrevented || !typed())
451
+ putAway();
452
+ else
453
+ escaping.current = false;
454
+ });
455
+ };
456
+ at.addEventListener("focusout", left);
457
+ at.addEventListener("keydown", marked, true);
458
+ at.addEventListener("keydown", escaped);
459
+ return () => {
460
+ at.removeEventListener("focusout", left);
461
+ at.removeEventListener("keydown", marked, true);
462
+ at.removeEventListener("keydown", escaped);
463
+ };
464
+ }, [folded, putAway]);
407
465
  const Heading = heading === false ? "span" : `h${heading}`;
408
466
  const Home = (home.href !== undefined ? "a" : "button");
409
467
  const press = (go) => (event) => {
@@ -423,83 +481,83 @@ export function AppBar({ brand, name, heading = 1, home, faces, places, current,
423
481
  const placeControl = list ? _jsx(Places, { places: list.places, current: list.current, here: list.here, reach: list.reach, scene: scene, stands: stands }) : null;
424
482
  const said = list ? list.places.map((place) => place.label).join("\n") : "";
425
483
  // What the row holds changed — the name, the places, the face, the brand — so the places and the words may fit now, or no longer.
426
- useLayoutEffect(weigh, [weigh, name, brand, said, list?.current, onPages, compact, switchForm, find]);
427
- // The marks alone on a phone's bar, when asked, or when the words do not fit.
428
- const drawn = switchForm === "icons" || !roomy || compact ? "icons" : "words";
429
- return (_jsxs(_Fragment, { children: [_jsxs("header", { ref: bar, className: "graview-bar", "data-testid": "app-bar", "data-graview-app-bar": "", ...(finding && compact ? { "data-finding": "" } : {}), children: [_jsx("style", { children: BAR_CSS }), skips, _jsxs("div", { className: "graview-bar-row", children: [named ? _jsx("div", { className: "graview-bar-app", children: _jsx(Heading, { className: "graview-bar-name", children: _jsxs(Home, { ...(home.href !== undefined ? { href: home.href, onClick: press(home.go) } : { type: "button", onClick: home.go }), "data-testid": "app-home", ...(home.current ? { "aria-current": "page" } : {}), title: description ? `${name} — ${description}` : `${name} — home`, className: "graview-bar-home", children: [_jsx(AppMark, { brand: brand, size: 20 }), _jsx("span", { className: "graview-bar-app-name", "data-testid": "app-name", children: name })] }) }) }) : null, faces ? _jsx(FaceSwitch, { faces: faces, form: switchForm, drawn: drawn }) : null, _jsx("div", { className: "graview-bar-mid", ...(stands ? { "data-places": "standing" } : !compact && placeControl ? { "data-place": "" } : {}), children: compact ? null : placeControl }), !compact && list && list.places.length > 1 ? (_jsxs("span", { className: "graview-bar-ruler", "aria-hidden": "true", "data-graview-bar-ruler": "", children: [list.places.map((place) => (_jsx("span", { className: "graview-bar-at", children: place.label }, place.key))), _jsxs("span", { className: "graview-bar-at", children: [MORE, _jsx(Chevron, {})] })] })) : null, actions && actions.length > 0 ? (_jsx("span", { className: "graview-bar-ruler", "aria-hidden": "true", "data-graview-bar-host-ruler": "", children: _jsx("span", { className: "graview-bar-host", children: actions.map((action) => (_jsxs("span", { className: "graview-bar-host-action", children: [action.icon ? _jsx(HostMark, { icon: action.icon }) : null, _jsx("span", { children: action.label })] }, action.label))) }) })) : null, named ? (_jsx("span", { className: "graview-bar-ruler", "aria-hidden": "true", children: _jsx("span", { className: "graview-bar-name", children: name }) })) : null, _jsxs("div", { className: "graview-bar-tools", children: [find ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "graview-bar-find-open", ref: opener, "data-testid": "app-find-open", "aria-label": "Find", title: "Find", onClick: openFind, style: { ...toolStyle, display: undefined }, children: _jsx(FindMark, {}) }), _jsxs("div", { className: "graview-bar-find", "data-testid": "app-find", onBlur: (event) => {
430
- // Put away on a phone when the keyboard leaves it with nothing typed.
431
- const next = event.relatedTarget;
432
- if (next instanceof Node && event.currentTarget.contains(next))
433
- return;
434
- if (!event.currentTarget.querySelector("input")?.value)
435
- putAway();
436
- }, onKeyDownCapture: (event) => {
437
- if (event.key === "Escape" && compact)
438
- escaping.current = true;
439
- }, onKeyDown: (event) => {
440
- if (event.key !== "Escape" || !compact)
441
- return;
442
- // The scene's Find takes Escape for its words first; an empty box is put away.
443
- if (!event.defaultPrevented || !event.currentTarget.querySelector("input")?.value)
444
- putAway();
445
- else
446
- escaping.current = false;
447
- }, children: [_jsx("div", { ref: setSlot, className: "graview-bar-find-slot", children: findBox }), _jsx(FindKeys, {})] })] })) : null, _jsx("div", { ref: setOwn, className: "graview-bar-own", "data-graview-bar-own": "" }), company ? (_jsx(Suspense, { fallback: null, children: _jsx(Here, { scene: !onPages, compact: compact }) })) : null, _jsx(TheirChanges, {}), actions && actions.length > 0 ? _jsx(HostButtons, { actions: actions, folded: compact || !hostStands }) : null, tools] })] })] }), compact && placeControl ? (_jsx("div", { className: "graview-bar-line", "data-graview-place-line": "", "data-testid": "app-place-line", children: placeControl })) : null] }));
484
+ useLayoutEffect(weigh, [weigh, name, brand, said, list?.current, onPages, compact, folded, find]);
485
+ // The app menu: the scene and the pages, the host's rows, the scheme — drawn where it holds anything.
486
+ const menuHolds = Boolean(faces) || appMenu.length > 0 || Boolean(scheme);
487
+ return (_jsxs(_Fragment, { children: [_jsxs("header", { ref: bar, className: "graview-bar", "data-testid": "app-bar", "data-graview-app-bar": "", ...(finding && folded ? { "data-finding": "" } : {}), children: [_jsx("style", { children: BAR_CSS }), skips, _jsxs("div", { className: "graview-bar-row", children: [named ? _jsxs("div", { className: "graview-bar-app", children: [_jsx(Heading, { className: "graview-bar-name", children: _jsxs(Home, { ...(home.href !== undefined ? { href: home.href, onClick: press(home.go) } : { type: "button", onClick: home.go }), "data-testid": "app-home", ...(home.current ? { "aria-current": "page" } : {}), title: description ? `${name} — ${description}` : `${name} — home`, className: "graview-bar-home", children: [_jsx(AppMark, { brand: brand, size: 20 }), _jsx("span", { className: "graview-bar-app-name", "data-testid": "app-name", children: name })] }) }), menuHolds ? _jsx(AppMenu, { name: name, faces: faces, rows: appMenu, scheme: scheme }) : null] }) : menuHolds ? _jsx(AppMenu, { name: name, faces: faces, rows: appMenu, scheme: scheme }) : null, _jsx("div", { className: "graview-bar-mid", ...(stands ? { "data-places": "standing" } : !compact && placeControl ? { "data-place": "" } : {}), children: compact ? null : placeControl }), !compact && list && list.places.length > 1 ? (_jsxs("span", { className: "graview-bar-ruler", "aria-hidden": "true", "data-graview-bar-ruler": "", children: [list.places.map((place) => (_jsx("span", { className: "graview-bar-at", children: place.label }, place.key))), _jsxs("span", { className: "graview-bar-at", children: [MORE, _jsx(Chevron, {})] })] })) : null, actions && actions.length > 0 ? (_jsx("span", { className: "graview-bar-ruler", "aria-hidden": "true", "data-graview-bar-host-ruler": "", children: _jsx("span", { className: "graview-bar-host", children: actions.map((action) => (_jsxs("span", { className: "graview-bar-host-action", ...(action.iconOnly && action.icon ? { "data-icon-only": "" } : {}), children: [action.icon ? _jsx(HostMark, { icon: action.icon }) : null, _jsx("span", { className: "graview-bar-host-words", children: action.label })] }, action.label))) }) })) : null, named ? (_jsx("span", { className: "graview-bar-ruler", "aria-hidden": "true", children: _jsx("span", { className: "graview-bar-name", children: name }) })) : null, _jsxs("div", { className: "graview-bar-tools", children: [find ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "graview-bar-find-open", ref: opener, "data-testid": "app-find-open", "aria-label": "Find", title: "Find", onClick: openFind, style: { ...toolStyle, display: undefined }, children: _jsx(FindMark, {}) }), _jsxs("div", { ref: findAt, className: "graview-bar-find", "data-testid": "app-find", children: [_jsx("div", { ref: setSlot, className: "graview-bar-find-slot", children: findBox }), _jsx(FindKeys, {})] })] })) : null, _jsx("div", { ref: setOwn, className: "graview-bar-own", "data-graview-bar-own": "" }), actions && actions.length > 0 ? _jsx(HostButtons, { actions: actions, folded: !(actions.length <= 2 && actions.every((action) => action.iconOnly && action.icon)) && (compact || !hostStands) }) : null, company ? (_jsx(Suspense, { fallback: null, children: _jsx(Here, { scene: !onPages, compact: compact }) })) : null, share ? _jsx(ShareSlot, { share: share }) : null, _jsx(TheirChanges, {}), tools] })] })] }), compact && placeControl ? (_jsx("div", { className: "graview-bar-line", "data-graview-place-line": "", "data-testid": "app-place-line", children: placeControl })) : null] }));
448
488
  }
489
+ const NO_ROWS = [];
449
490
  /**
450
- * THE SWITCH (FR-137): the scene and the pages, side by side, an icon and a
451
- * word each — the words the accessible names alone on a phone — the one
452
- * drawn pressed. Choosing the scene draws it under the bar; choosing the
453
- * pages goes back to the page you were on.
491
+ * THE APP MENU'S CONTROL (Roadmap f-app-bar-calm): a chevron beside the
492
+ * app's name — the name stays the way home, the page's heading — that opens
493
+ * the menu: the scene and the pages, a host's rows, the scheme. Its rows
494
+ * are fetched with the bar's panes when a hand first reaches for it.
454
495
  */
455
- function FaceSwitch({ faces, form, drawn }) {
456
- const face = (which) => {
457
- const one = faces[which];
458
- return (_jsxs("button", { type: "button", className: "graview-bar-face", "data-testid": `app-face-${which}`, "aria-pressed": one.current, title: one.label, ...(one.reach && !one.current ? { onPointerEnter: one.reach, onPointerDown: one.reach, onFocus: one.reach } : {}), onClick: () => {
459
- if (!one.current)
460
- one.go();
461
- }, children: [which === "scene" ? _jsx(SceneMark, {}) : _jsx(PagesMark, {}), _jsx("span", { children: one.label })] }));
462
- };
463
- return (_jsxs("div", { className: "graview-bar-faces", role: "group", "aria-label": `${faces.scene.label} or ${faces.pages.label}`, "data-testid": "app-faces", "data-switch": form, "data-switch-drawn": drawn, children: [face("scene"), face("pages")] }));
496
+ /**
497
+ * GOING TO THE OTHER FACE WITHOUT A BUTTON ON THE ROW (Roadmap
498
+ * f-app-bar-calm, f-make-any-page-better). The scene and the pages are rows
499
+ * of the app menu, drawn only while it is open, so a part that must switch
500
+ * faces — Better UI asked in the scene, opened on its page — cannot press
501
+ * them. It dispatches this on the app menu's control
502
+ * (`[data-testid="app-menu-open"]`, which says `data-pages` where the bar
503
+ * holds both faces), with the face as its detail, and the bar goes there as
504
+ * the menu's row would.
505
+ */
506
+ export const FACE_EVENT = "graview:go-to-face";
507
+ function AppMenu({ name, faces, rows, scheme }) {
508
+ const popover = usePopover("app-menu", { align: "start" });
509
+ const reach = () => barPanes.prefetch();
510
+ /* A part asked to go to the other face (`FACE_EVENT`): gone to as the menu's row would, without opening the menu. */
511
+ const opener = useRef(null);
512
+ const held = useRef(faces);
513
+ held.current = faces;
514
+ useEffect(() => {
515
+ const element = opener.current;
516
+ if (!element)
517
+ return;
518
+ const go = (event) => {
519
+ const one = held.current?.[event.detail];
520
+ if (!one || one.current)
521
+ return;
522
+ one.reach?.();
523
+ one.go();
524
+ };
525
+ element.addEventListener(FACE_EVENT, go);
526
+ return () => element.removeEventListener(FACE_EVENT, go);
527
+ }, []);
528
+ return (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "graview-bar-menu-open", "data-testid": "app-menu-open", ...popover.trigger, ref: (element) => {
529
+ popover.trigger.ref(element);
530
+ opener.current = element;
531
+ }, onClick: popover.toggle, onPointerEnter: reach, onFocus: reach, "aria-label": `${name} — app menu`, title: "App menu", ...(faces ? { "data-scene": faces.scene.label, "data-pages": faces.pages.label } : {}), children: _jsx(Chevron, {}) }), _jsx("div", { ...popover.pane, "data-testid": "app-menu", hidden: !popover.open, className: "graview-bar-list graview-app-menu", style: POPOVER_STYLE, "aria-label": `${name} — app menu`, role: "group", children: popover.open ? (_jsx(Suspense, { fallback: null, children: _jsx(AppMenuRows, { faces: faces, rows: rows, scheme: scheme, close: () => popover.setOpen(false) }) })) : null })] }));
532
+ }
533
+ /** Share's marks: a lock, a globe, two people. */
534
+ function ShareMark({ mark = "lock" }) {
535
+ if (mark === "lock")
536
+ return (_jsxs(Mark, { size: 14, children: [_jsx("rect", { x: "2.75", y: "6.25", width: "8.5", height: "6", rx: "1.25", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("path", { d: "M4.75 6.25 V4.5 a2.25 2.25 0 0 1 4.5 0 V6.25", fill: "none", stroke: "currentColor", strokeWidth: "1.3" })] }));
537
+ if (mark === "globe")
538
+ return (_jsxs(Mark, { size: 14, children: [_jsx("circle", { cx: "7", cy: "7", r: "5.25", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("path", { d: "M1.75 7 H12.25 M7 1.75 C4.75 4.5 4.75 9.5 7 12.25 M7 1.75 C9.25 4.5 9.25 9.5 7 12.25", fill: "none", stroke: "currentColor", strokeWidth: "1.1" })] }));
539
+ if (mark === "people")
540
+ return (_jsxs(Mark, { size: 14, children: [_jsx("circle", { cx: "5", cy: "5", r: "2", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("path", { d: "M1.5 12 a3.5 3.5 0 0 1 7 0", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("circle", { cx: "10", cy: "5.5", r: "1.6", fill: "none", stroke: "currentColor", strokeWidth: "1.2" }), _jsx("path", { d: "M9.5 9 a3 3 0 0 1 3.5 3", fill: "none", stroke: "currentColor", strokeWidth: "1.2" })] }));
541
+ return _jsx(HostMark, { icon: mark });
464
542
  }
465
543
  /**
466
- * WHETHER THE SWITCH'S WORDS FIT (the switch left to say its words where
467
- * there is room). The row is weighed as it is drawn: the room the middle
468
- * has past what the place wants (its words, up to 14em; none on the
469
- * scene), less what Find has given up and what the app's name has given
470
- * (`nameGiven`); none when the name is wider on one line than its room. The
471
- * words fit when that room holds them — the words
472
- * drawn already, or their width to come when the marks are drawn alone —
473
- * so the switch never flips back and forth at one width. Not weighed while
474
- * Find is in use (it is drawn wide then), on a phone's bar (the marks
475
- * alone), or where nothing is laid out (null).
544
+ * A HOST'S SHARE (Roadmap f-app-bar-calm, f-bar-status-item): its mark and
545
+ * its word, the sentence its description — on hover and to a screen reader —
546
+ * and, pressed, the sentence again with the link to where it changes. One
547
+ * of the bar's family of popovers.
476
548
  */
477
- function roomForWords(header) {
478
- const faces = header.querySelector(".graview-bar-faces");
479
- const mid = header.querySelector(".graview-bar-mid");
480
- const find = header.querySelector(".graview-bar-find");
481
- if (!faces || !mid || find?.matches(":focus-within"))
482
- return null;
483
- const row = header.getBoundingClientRect().width;
484
- if (row < BAR_PHONE)
485
- return null;
486
- // The words to come, beside each mark: the word, the gap before it, and the button's wider padding.
487
- const words = wordsOf(faces);
488
- const place = mid.querySelector(".graview-bar-place-words");
489
- const em = place ? parseFloat(getComputedStyle(place).fontSize) || 14 : 0;
490
- // The place's words, its chevron, its gap, padding and frame: up to 14em of them.
491
- const wanted = place ? Math.min(place.scrollWidth + 34, 14 * em) : 0;
492
- const given = nameGiven(header);
493
- // The app's name on two lines whatever stands beside it has given its room already: no room for the words.
494
- if (given === null)
495
- return false;
496
- let spare = mid.getBoundingClientRect().width - wanted - given - overrun(header);
497
- if (find && find.getBoundingClientRect().width > 0) {
498
- const basis = parseFloat(getComputedStyle(find).flexBasis);
499
- if (isFinite(basis))
500
- spare -= Math.max(0, basis - find.getBoundingClientRect().width);
501
- }
502
- return faces.getAttribute("data-switch-drawn") === "icons" ? spare >= words + 2 : spare >= -1;
549
+ function ShareSlot({ share }) {
550
+ const popover = usePopover("share");
551
+ const said = `graview-share-said-${useId().replace(/:/g, "")}`;
552
+ const label = share.label ?? "Share";
553
+ const link = share.link;
554
+ return (_jsxs("div", { className: "graview-bar-share-at", children: [_jsxs("button", { type: "button", className: "graview-bar-tool graview-bar-share", "data-testid": "app-share-open", ...popover.trigger, onClick: popover.toggle, "aria-describedby": said, title: share.description, children: [_jsx(ShareMark, { mark: share.mark }), _jsx("span", { className: "graview-bar-share-words", children: label })] }), _jsx("span", { id: said, className: "graview-bar-said", children: share.description }), _jsxs("div", { ...popover.pane, "data-testid": "app-share", hidden: !popover.open, className: "graview-bar-share-pane", style: POPOVER_STYLE, role: "group", "aria-label": label, children: [_jsx("p", { "data-testid": "app-share-said", children: share.description }), link ? (link.href !== undefined ? (_jsx("a", { href: link.href, "data-testid": "app-share-link", ...(link.target ? { target: link.target, rel: "noopener" } : {}), onClick: () => {
555
+ link.onSelect?.();
556
+ popover.setOpen(false);
557
+ }, children: link.label })) : (_jsx("button", { type: "button", "data-testid": "app-share-link", onClick: () => {
558
+ popover.setOpen(false);
559
+ link.onSelect?.();
560
+ }, children: link.label }))) : null] })] }));
503
561
  }
504
562
  /**
505
563
  * HOW FAR THE ROW RUNS PAST ITS END, in pixels: what stands last on it — a
@@ -509,7 +567,7 @@ function roomForWords(header) {
509
567
  * Activity arriving after the places were weighed, an iPad's 1024 px with
510
568
  * five places standing), Find's end went past the row with them, the room
511
569
  * read as enough, and the person stood off the screen. What runs over is
512
- * room the places and the switch's words do not have.
570
+ * room the places do not have.
513
571
  */
514
572
  function overrun(header) {
515
573
  const row = header.querySelector(".graview-bar-row");
@@ -569,10 +627,6 @@ function Mark({ size, box = size, children }) {
569
627
  function SceneMark() {
570
628
  return (_jsxs(Mark, { size: 14, children: [_jsx("path", { d: "M7 1.5 L12.5 4.5 L7 7.5 L1.5 4.5 Z", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round" }), _jsx("path", { d: "M1.5 7.5 L7 10.5 L12.5 7.5", fill: "none", stroke: "currentColor", strokeWidth: "1.3", strokeLinejoin: "round", strokeLinecap: "round" })] }));
571
629
  }
572
- /** The pages' mark: a page of lines. */
573
- function PagesMark() {
574
- return (_jsxs(Mark, { size: 14, children: [_jsx("rect", { x: "2.25", y: "1.75", width: "9.5", height: "10.5", rx: "1.5", fill: "none", stroke: "currentColor", strokeWidth: "1.3" }), _jsx("path", { d: "M4.5 5 H9.5 M4.5 7.25 H9.5 M4.5 9.5 H7.5", stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round" })] }));
575
- }
576
630
  /** The way a menu opens: a chevron drawn, not a glyph a face may draw as a dot. */
577
631
  function Chevron() {
578
632
  return (_jsx(Mark, { size: 10, children: _jsx("path", { d: "M2 3.5 L5 6.5 L8 3.5", fill: "none", stroke: "currentColor", strokeWidth: "1.4", strokeLinecap: "round", strokeLinejoin: "round" }) }));
@@ -612,10 +666,10 @@ const MORE = "More";
612
666
  function HostButtons({ actions, folded }) {
613
667
  const popover = usePopover("host-actions", { popover: folded });
614
668
  const names = actions.map((action) => action.label).join(", ");
615
- const buttons = (done) => actions.map((action) => (_jsxs("button", { type: "button", className: folded ? "graview-bar-host-action graview-bar-item" : "graview-bar-host-action", "data-testid": "host-bar-action", onClick: () => {
669
+ const buttons = (done) => actions.map((action) => (_jsxs("button", { type: "button", className: folded ? "graview-bar-host-action graview-bar-item" : "graview-bar-host-action", "data-testid": "host-bar-action", ...(!folded && action.iconOnly && action.icon ? { "data-icon-only": "", title: action.label } : {}), onClick: () => {
616
670
  done();
617
671
  action.onSelect();
618
- }, children: [action.icon ? _jsx(HostMark, { icon: action.icon }) : null, _jsx("span", { children: action.label })] }, action.label)));
672
+ }, children: [action.icon ? _jsx(HostMark, { icon: action.icon }) : null, _jsx("span", { className: "graview-bar-host-words", children: action.label })] }, action.label)));
619
673
  if (!folded) {
620
674
  return (_jsx("div", { className: "graview-bar-host", "data-testid": "host-bar-actions-standing", role: "group", "aria-label": "From the host", children: buttons(() => undefined) }));
621
675
  }
@@ -737,8 +791,7 @@ function usePlaceGestures(places, current, scene) {
737
791
  * WHICH PLACES STAND ON THE ROW, WEIGHED FROM THE ROW AS DRAWN (FR-145):
738
792
  * the places' widths read off the ruler (each at its weight as the place
739
793
  * you are on, so marking one never moves the others), and the room from
740
- * where the places begin to where Find ends, less Find at its least, less
741
- * the switch's words when they are asked for. The same answer whichever way
794
+ * where the places begin to where Find ends, less Find at its least. The same answer whichever way
742
795
  * the places are drawn, so the row never flips between the two at one
743
796
  * width. The places begin where they would with the app's name on one
744
797
  * line (`nameGiven`), so they stand only where they leave it there, and the
@@ -747,7 +800,7 @@ function usePlaceGestures(places, current, scene) {
747
800
  * name is wider on one line than its room, or where nothing is laid out
748
801
  * (null).
749
802
  */
750
- function standsIn(header, current, wordsAsked, ranks) {
803
+ function standsIn(header, current, ranks) {
751
804
  const ruler = header.querySelector("[data-graview-bar-ruler]");
752
805
  const mid = header.querySelector(".graview-bar-mid");
753
806
  if (!ruler || !mid)
@@ -762,11 +815,8 @@ function standsIn(header, current, wordsAsked, ranks) {
762
815
  return null;
763
816
  const widths = [...ruler.children].map((one) => one.getBoundingClientRect().width);
764
817
  const more = widths.pop() ?? 0;
765
- const faces = header.querySelector(".graview-bar-faces");
766
- const words = faces && wordsAsked ? wordsOf(faces) : 0;
767
- const drawnWords = faces?.getAttribute("data-switch-drawn") === "words" ? words : 0;
768
818
  const from = mid.getBoundingClientRect().left + given;
769
- const end = find?.getBoundingClientRect();
819
+ const end = find && getComputedStyle(find).display !== "none" ? find.getBoundingClientRect() : undefined;
770
820
  /*
771
821
  * To Find's end, less Find at its least and the room between them — the
772
822
  * row's whole gap: Find stands at `margin-left:auto`, which is nothing
@@ -776,10 +826,6 @@ function standsIn(header, current, wordsAsked, ranks) {
776
826
  */
777
827
  const room = (end && end.width > 0 ? end.right - from - (parseFloat(getComputedStyle(find).minWidth) || 0) - 16 : mid.getBoundingClientRect().width - given) - overrun(header);
778
828
  const primary = [...ranks].map((rank) => rank !== "0");
779
- return placesThatStand({ widths, current, room: Math.floor(room + drawnWords - words), more, primary })?.join(",") ?? null;
780
- }
781
- /** The switch's words, beside each mark: the word, the gap before it, and the button's wider padding. */
782
- function wordsOf(faces) {
783
- return [...faces.querySelectorAll(".graview-bar-face span")].reduce((sum, span) => sum + span.scrollWidth + 10, 0);
829
+ return placesThatStand({ widths, current, room: Math.floor(room), more, primary })?.join(",") ?? null;
784
830
  }
785
831
  //# sourceMappingURL=app-bar.js.map