@openleaf-editor/ui 0.1.0-beta.2 → 0.1.0-beta.4

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 (78) hide show
  1. package/README.md +85 -0
  2. package/dist/block-type.d.ts +5 -0
  3. package/dist/block-type.d.ts.map +1 -0
  4. package/dist/block-type.js +133 -0
  5. package/dist/block-type.js.map +1 -0
  6. package/dist/content-css.d.ts +18 -0
  7. package/dist/content-css.d.ts.map +1 -1
  8. package/dist/content-css.js +231 -16
  9. package/dist/content-css.js.map +1 -1
  10. package/dist/css.d.ts +23 -0
  11. package/dist/css.d.ts.map +1 -0
  12. package/dist/css.js +846 -0
  13. package/dist/css.js.map +1 -0
  14. package/dist/dialog.d.ts +93 -0
  15. package/dist/dialog.d.ts.map +1 -1
  16. package/dist/dialog.js +414 -86
  17. package/dist/dialog.js.map +1 -1
  18. package/dist/floating.d.ts +4 -0
  19. package/dist/floating.d.ts.map +1 -1
  20. package/dist/floating.js +50 -2
  21. package/dist/floating.js.map +1 -1
  22. package/dist/help.d.ts +1 -1
  23. package/dist/help.d.ts.map +1 -1
  24. package/dist/help.js +29 -5
  25. package/dist/help.js.map +1 -1
  26. package/dist/i18n.d.ts +8 -0
  27. package/dist/i18n.d.ts.map +1 -1
  28. package/dist/i18n.js +32 -7
  29. package/dist/i18n.js.map +1 -1
  30. package/dist/icons.d.ts +7 -1
  31. package/dist/icons.d.ts.map +1 -1
  32. package/dist/icons.js +6 -2
  33. package/dist/icons.js.map +1 -1
  34. package/dist/index.d.ts +10 -8
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +8 -6
  37. package/dist/index.js.map +1 -1
  38. package/dist/items.d.ts +0 -1
  39. package/dist/items.d.ts.map +1 -1
  40. package/dist/items.js +211 -20
  41. package/dist/items.js.map +1 -1
  42. package/dist/live.d.ts +42 -0
  43. package/dist/live.d.ts.map +1 -0
  44. package/dist/live.js +83 -0
  45. package/dist/live.js.map +1 -0
  46. package/dist/menu.d.ts +31 -2
  47. package/dist/menu.d.ts.map +1 -1
  48. package/dist/menu.js +163 -23
  49. package/dist/menu.js.map +1 -1
  50. package/dist/openleaf.css +147 -12
  51. package/dist/overflow.d.ts +47 -4
  52. package/dist/overflow.d.ts.map +1 -1
  53. package/dist/overflow.js +338 -83
  54. package/dist/overflow.js.map +1 -1
  55. package/dist/registry.d.ts +72 -18
  56. package/dist/registry.d.ts.map +1 -1
  57. package/dist/registry.js +7 -6
  58. package/dist/registry.js.map +1 -1
  59. package/dist/skins.d.ts.map +1 -1
  60. package/dist/skins.js +48 -14
  61. package/dist/skins.js.map +1 -1
  62. package/dist/styles.d.ts +61 -1
  63. package/dist/styles.d.ts.map +1 -1
  64. package/dist/styles.js +77 -637
  65. package/dist/styles.js.map +1 -1
  66. package/dist/testing.d.ts +3 -0
  67. package/dist/testing.d.ts.map +1 -0
  68. package/dist/testing.js +3 -0
  69. package/dist/testing.js.map +1 -0
  70. package/dist/toolbar.d.ts +71 -12
  71. package/dist/toolbar.d.ts.map +1 -1
  72. package/dist/toolbar.js +410 -166
  73. package/dist/toolbar.js.map +1 -1
  74. package/dist/upload.d.ts +32 -7
  75. package/dist/upload.d.ts.map +1 -1
  76. package/dist/upload.js +64 -8
  77. package/dist/upload.js.map +1 -1
  78. package/package.json +17 -3
@@ -1,17 +1,60 @@
1
1
  /**
2
- * Collapse overflowing toolbar groups into a "More" menu.
2
+ * Collapse overflowing toolbar groups into a "More" panel.
3
3
  *
4
4
  * Wrapping is the default for a reason: a control that moved behind a click is
5
5
  * a control the author does not have. Overflow is therefore opt-in. When it is
6
- * on, groups that do not fit the current width are hidden in the bar and
7
- * offered in a menu at the end, so a narrow CMS sidebar still has every item
6
+ * on, groups that do not fit the current width are moved out of the bar and
7
+ * offered in a panel at the end, so a narrow CMS sidebar still has every item
8
8
  * without a two-row bar.
9
+ *
10
+ * ## Why the real controls move rather than being cloned
11
+ *
12
+ * The first implementation cloned each control into a `role="menu"`. Cloning a
13
+ * live control produces a listener-free copy that has to forward every
14
+ * interaction back to an original it can only find by id, duplicates that id
15
+ * and the accessible name, and goes stale the moment the caret moves -- three
16
+ * bugs that only exist because there were two of everything. Moving the group
17
+ * node itself has none of them: the control in the panel IS the control, so it
18
+ * carries its own listeners, its own state and its own accessible name, and
19
+ * nothing can drift.
20
+ *
21
+ * ## The keyboard pattern
22
+ *
23
+ * The panel is a vertical `role="toolbar"`, not a `role="menu"`: a menu owns a
24
+ * content model of `menuitem` children, and a `<select>` is not one -- the
25
+ * default bar has four. So the panel is the same widget as the bar it came
26
+ * from, turned ninety degrees, and it keeps the same contract: one tab stop,
27
+ * arrow keys to move, Home/End to the ends, Escape back to the trigger. Tab
28
+ * closes, because a panel a keyboard user can walk out of and leave open is the
29
+ * failure mode of every hand-rolled popup.
30
+ *
31
+ * Up/Down are taken from a `<select>` inside the panel the same way Left/Right
32
+ * are taken from one in the bar; Alt+Down still opens its list, and typeahead
33
+ * still selects. Home/End are left to the select, which uses them for its own
34
+ * first and last option.
9
35
  */
10
36
  export declare class ToolbarOverflow {
11
37
  #private;
12
- constructor(toolbar: HTMLElement, host: HTMLElement, doc: Document);
38
+ constructor(toolbar: HTMLElement, host: HTMLElement, doc: Document, onLayout?: () => void);
39
+ /** Re-home the trigger after the toolbar rebuilt its children. */
13
40
  reattach(): void;
14
41
  destroy(): void;
42
+ /**
43
+ * Lay the bar out without stealing focus.
44
+ *
45
+ * `#reflow` moves real controls -- the trigger included -- between the bar and
46
+ * the panel, and `appendChild` of an already-connected node is a removal
47
+ * followed by an insertion. Every engine resets focus to the body when a
48
+ * focused element is moved that way (chromium, firefox and webkit alike; it is
49
+ * not a WebKit quirk, WebKit just loses the race more often on a loaded
50
+ * machine). A ResizeObserver fires whenever the bar changes size -- a font
51
+ * finishing, `--openleaf-button-size` changing density, a CMS sidebar
52
+ * animating open -- so a keyboard user standing on More could have the button
53
+ * pulled out from under them between focusing it and pressing Enter, and the
54
+ * Enter went to the document.
55
+ *
56
+ * So the layout records what it is about to move focus off, and puts it back.
57
+ */
15
58
  layout(): void;
16
59
  }
17
60
  //# sourceMappingURL=overflow.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../src/overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,qBAAa,eAAe;;gBAOd,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ;IAmClE,QAAQ,IAAI,IAAI;IAKhB,OAAO,IAAI,IAAI;IAQf,MAAM,IAAI,IAAI;CA6Ef"}
1
+ {"version":3,"file":"overflow.d.ts","sourceRoot":"","sources":["../src/overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAUH,qBAAa,eAAe;;gBAuBd,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,MAAM,IAAI;IA+CzF,kEAAkE;IAClE,QAAQ,IAAI,IAAI;IAiChB,OAAO,IAAI,IAAI;IAgBf;;;;;;;;;;;;;;;OAeG;IACH,MAAM,IAAI,IAAI;CAsOf"}
package/dist/overflow.js CHANGED
@@ -1,135 +1,390 @@
1
1
  /**
2
- * Collapse overflowing toolbar groups into a "More" menu.
2
+ * Collapse overflowing toolbar groups into a "More" panel.
3
3
  *
4
4
  * Wrapping is the default for a reason: a control that moved behind a click is
5
5
  * a control the author does not have. Overflow is therefore opt-in. When it is
6
- * on, groups that do not fit the current width are hidden in the bar and
7
- * offered in a menu at the end, so a narrow CMS sidebar still has every item
6
+ * on, groups that do not fit the current width are moved out of the bar and
7
+ * offered in a panel at the end, so a narrow CMS sidebar still has every item
8
8
  * without a two-row bar.
9
+ *
10
+ * ## Why the real controls move rather than being cloned
11
+ *
12
+ * The first implementation cloned each control into a `role="menu"`. Cloning a
13
+ * live control produces a listener-free copy that has to forward every
14
+ * interaction back to an original it can only find by id, duplicates that id
15
+ * and the accessible name, and goes stale the moment the caret moves -- three
16
+ * bugs that only exist because there were two of everything. Moving the group
17
+ * node itself has none of them: the control in the panel IS the control, so it
18
+ * carries its own listeners, its own state and its own accessible name, and
19
+ * nothing can drift.
20
+ *
21
+ * ## The keyboard pattern
22
+ *
23
+ * The panel is a vertical `role="toolbar"`, not a `role="menu"`: a menu owns a
24
+ * content model of `menuitem` children, and a `<select>` is not one -- the
25
+ * default bar has four. So the panel is the same widget as the bar it came
26
+ * from, turned ninety degrees, and it keeps the same contract: one tab stop,
27
+ * arrow keys to move, Home/End to the ends, Escape back to the trigger. Tab
28
+ * closes, because a panel a keyboard user can walk out of and leave open is the
29
+ * failure mode of every hand-rolled popup.
30
+ *
31
+ * Up/Down are taken from a `<select>` inside the panel the same way Left/Right
32
+ * are taken from one in the bar; Alt+Down still opens its list, and typeahead
33
+ * still selects. Home/End are left to the select, which uses them for its own
34
+ * first and last option.
9
35
  */
10
36
  import { t } from './i18n.js';
11
37
  import { iconElement } from './icons.js';
38
+ /** Focusable controls, matching what the toolbar's own roving walks. */
39
+ const FOCUSABLE = 'button.ol-btn, select.ol-select';
40
+ let panelCounter = 0;
12
41
  export class ToolbarOverflow {
13
42
  #toolbar;
14
43
  #host;
44
+ #doc;
15
45
  #more;
16
- #menu;
46
+ #panel;
17
47
  #observer = null;
18
- constructor(toolbar, host, doc) {
48
+ #onLayout;
49
+ /**
50
+ * A pending coalesced layout.
51
+ *
52
+ * A ResizeObserver fires once per observed element, so a ten-group bar paid
53
+ * for ten layouts in one frame. One rAF collapses them, and the frame is
54
+ * cancelled on destroy so a queued layout cannot run against a torn-down bar.
55
+ */
56
+ #frame = null;
57
+ /**
58
+ * A layout that arrived while the panel was open, and the guard that keeps
59
+ * `#close` from re-entering the reflow that called it.
60
+ */
61
+ #deferred = false;
62
+ #reflowing = false;
63
+ constructor(toolbar, host, doc, onLayout) {
19
64
  this.#toolbar = toolbar;
20
65
  this.#host = host;
66
+ this.#doc = doc;
67
+ this.#onLayout = onLayout;
21
68
  toolbar.classList.add('ol-toolbar--overflow');
69
+ this.#panel = doc.createElement('div');
70
+ this.#panel.className = 'ol-menu ol-overflow-menu';
71
+ this.#panel.id = `ol-overflow-${(panelCounter += 1)}`;
72
+ this.#panel.setAttribute('role', 'toolbar');
73
+ this.#panel.setAttribute('aria-orientation', 'vertical');
74
+ this.#panel.setAttribute('aria-label', t('More formatting'));
75
+ this.#panel.hidden = true;
76
+ this.#panel.addEventListener('keydown', this.#onKeydown);
22
77
  this.#more = doc.createElement('button');
23
78
  this.#more.type = 'button';
24
79
  this.#more.className = 'ol-btn ol-overflow-more';
25
80
  this.#more.setAttribute('aria-label', t('More'));
26
- this.#more.setAttribute('aria-haspopup', 'true');
81
+ // Not `aria-haspopup`: none of its values describes a toolbar, and "true"
82
+ // means menu, which is what this deliberately is not. Expanded plus a
83
+ // pointer at the panel is the honest relationship.
27
84
  this.#more.setAttribute('aria-expanded', 'false');
85
+ this.#more.setAttribute('aria-controls', this.#panel.id);
28
86
  this.#more.hidden = true;
29
87
  this.#more.appendChild(iconElement('more', doc));
30
- this.#menu = doc.createElement('div');
31
- this.#menu.className = 'ol-menu ol-overflow-menu';
32
- this.#menu.setAttribute('role', 'menu');
33
- this.#menu.hidden = true;
88
+ this.#more.addEventListener('mousedown', (event) => event.preventDefault());
34
89
  this.#more.addEventListener('click', () => {
35
- const open = this.#menu.hidden;
36
- this.#menu.hidden = !open;
37
- this.#more.setAttribute('aria-expanded', open ? 'true' : 'false');
90
+ if (this.#panel.hidden)
91
+ this.#open();
92
+ else
93
+ this.#close(true);
38
94
  });
39
- host.appendChild(this.#menu);
95
+ // Immediately after the bar, never at the end of the editor: appended to the
96
+ // host it landed after the editable region, so Tab from More walked into the
97
+ // content and the panel could only be reached by going backwards.
98
+ if (toolbar.parentNode)
99
+ toolbar.after(this.#panel);
100
+ else
101
+ host.appendChild(this.#panel);
40
102
  toolbar.appendChild(this.#more);
41
103
  if (typeof ResizeObserver !== 'undefined') {
42
- this.#observer = new ResizeObserver(() => this.layout());
104
+ this.#observer = new ResizeObserver(() => this.#schedule());
43
105
  this.#observer.observe(toolbar);
44
106
  }
45
107
  this.layout();
46
108
  }
109
+ /** Re-home the trigger after the toolbar rebuilt its children. */
47
110
  reattach() {
111
+ // Closed first: the panel is about to be emptied, and an open panel would
112
+ // both defer the layout below and leave the author holding an empty one.
113
+ this.#deferred = false;
114
+ this.#close();
115
+ // The groups in the panel belong to the render that was just thrown away.
116
+ // Restoring them would put two of every control in the bar.
117
+ this.#panel.replaceChildren();
48
118
  if (!this.#more.isConnected)
49
119
  this.#toolbar.appendChild(this.#more);
50
120
  this.layout();
51
121
  }
122
+ /**
123
+ * Coalesce resize notifications into one layout per frame.
124
+ *
125
+ * The observer watches the very element `layout` resizes, so an unthrottled
126
+ * callback fed itself: hiding a group changes the bar's size, which notifies
127
+ * the observer, which lays out again. A frame is the right budget for work
128
+ * whose only purpose is deciding what the next paint shows.
129
+ */
130
+ #schedule() {
131
+ if (this.#frame !== null)
132
+ return;
133
+ const win = this.#toolbar.ownerDocument.defaultView;
134
+ if (!win?.requestAnimationFrame) {
135
+ this.layout();
136
+ return;
137
+ }
138
+ this.#frame = win.requestAnimationFrame(() => {
139
+ this.#frame = null;
140
+ this.layout();
141
+ });
142
+ }
52
143
  destroy() {
144
+ // A queued frame outlives destroy() and would lay out a torn-down bar.
145
+ if (this.#frame !== null) {
146
+ this.#toolbar.ownerDocument.defaultView?.cancelAnimationFrame(this.#frame);
147
+ this.#frame = null;
148
+ }
53
149
  this.#observer?.disconnect();
150
+ // Dropped before the close, or the close would lay out a bar being torn down.
151
+ this.#deferred = false;
152
+ this.#close();
153
+ this.#restore();
54
154
  this.#more.remove();
55
- this.#menu.remove();
155
+ this.#panel.remove();
56
156
  this.#toolbar.classList.remove('ol-toolbar--overflow');
57
- for (const group of this.#groups())
58
- group.hidden = false;
59
157
  }
158
+ /**
159
+ * Lay the bar out without stealing focus.
160
+ *
161
+ * `#reflow` moves real controls -- the trigger included -- between the bar and
162
+ * the panel, and `appendChild` of an already-connected node is a removal
163
+ * followed by an insertion. Every engine resets focus to the body when a
164
+ * focused element is moved that way (chromium, firefox and webkit alike; it is
165
+ * not a WebKit quirk, WebKit just loses the race more often on a loaded
166
+ * machine). A ResizeObserver fires whenever the bar changes size -- a font
167
+ * finishing, `--openleaf-button-size` changing density, a CMS sidebar
168
+ * animating open -- so a keyboard user standing on More could have the button
169
+ * pulled out from under them between focusing it and pressing Enter, and the
170
+ * Enter went to the document.
171
+ *
172
+ * So the layout records what it is about to move focus off, and puts it back.
173
+ */
60
174
  layout() {
61
- const groups = this.#groups();
62
- for (const group of groups)
63
- group.hidden = false;
64
- this.#more.hidden = true;
65
- this.#menu.hidden = true;
66
- this.#menu.replaceChildren();
67
- const budget = this.#toolbar.clientWidth;
68
- if (budget === 0)
175
+ // Never reflow under an open panel. Measuring means putting every group
176
+ // back in the bar, which means closing the panel the author is reading --
177
+ // so a ResizeObserver pass that lands just after they opened it took it
178
+ // away again, along with the focus inside it. A real viewport resize still
179
+ // closes the panel through `#onViewportChange`, and that close runs the
180
+ // deferred layout immediately; a font or a density change simply waits
181
+ // until the author is done with the panel.
182
+ if (!this.#panel.hidden) {
183
+ this.#deferred = true;
69
184
  return;
70
- const overflowing = [];
71
- for (let i = groups.length - 1; i >= 0; i -= 1) {
72
- const fits = this.#toolbar.scrollWidth <= budget + 1;
73
- if (fits)
74
- break;
75
- const group = groups[i];
76
- if (!group)
77
- break;
78
- group.hidden = true;
79
- overflowing.unshift(group);
80
185
  }
81
- if (overflowing.length === 0)
186
+ const active = this.#doc.activeElement;
187
+ const held = active instanceof HTMLElement &&
188
+ (this.#toolbar.contains(active) || this.#panel.contains(active))
189
+ ? active
190
+ : null;
191
+ this.#reflowing = true;
192
+ try {
193
+ this.#reflow();
194
+ }
195
+ finally {
196
+ this.#reflowing = false;
197
+ }
198
+ if (!held || this.#doc.activeElement === held)
82
199
  return;
200
+ // A control the reflow pushed into the panel cannot hold focus: the panel is
201
+ // hidden by now. The trigger is where the panel's own Escape sends focus, so
202
+ // it is the honest destination for a control that just went behind it.
203
+ const target = held.isConnected && !this.#panel.contains(held) ? held : this.#more;
204
+ if (!target.hidden && target.isConnected)
205
+ target.focus();
206
+ }
207
+ #reflow() {
208
+ this.#restore();
209
+ // Revealed BEFORE the reads, not after the compute. The trigger takes a
210
+ // width and a gap in the row the moment it is shown, and it cannot be
211
+ // measured while hidden -- `display: none` reports 0. Showing it here means
212
+ // its footprint is inside the same `scrollWidth` as everything else, so the
213
+ // arithmetic needs no second measurement and no second layout pass.
214
+ //
215
+ // Measured every time rather than cached: the width follows
216
+ // `--openleaf-button-size`, which a skin or an integrator token changes at
217
+ // runtime (compact is 28px, the default 32px, coarse-pointer 40px), so a
218
+ // value pinned at first layout goes quietly wrong on the next density
219
+ // change.
83
220
  this.#more.hidden = false;
84
- for (const group of overflowing) {
85
- for (const control of group.querySelectorAll('[aria-label], select')) {
86
- const item = control.cloneNode(true);
87
- item.removeAttribute('tabindex');
88
- // cloneNode copies the option elements but not the select's current
89
- // value, which the toolbar sets as a property rather than an attribute.
90
- // Without this the menu opens showing the first option instead of the
91
- // block the caret is actually in.
92
- if (control instanceof HTMLSelectElement && item instanceof HTMLSelectElement) {
93
- item.value = control.value;
94
- }
95
- this.#menu.appendChild(item);
96
- }
97
- }
98
- // Cloning controls duplicates listener-less buttons. Activation on a clone is
99
- // forwarded to the original, which still owns the command.
100
- this.#menu.onclick = (event) => {
101
- const target = event.target;
102
- // A <select> is driven by `change`, not `click`. Forwarding a click here
103
- // fired before any option was chosen and carried no value with it.
104
- if (target?.closest('select'))
105
- return;
106
- const clone = target?.closest('[data-ol-id]');
107
- if (!clone)
108
- return;
109
- const original = this.#toolbar.querySelector(`[data-ol-id="${clone.dataset['olId']}"]`);
110
- original?.click();
111
- this.#close();
112
- };
113
- // The clone has no listeners of its own, so a chosen value has to be written
114
- // onto the real control and announced there. Clicking the original was the
115
- // previous behaviour and never carried the value, so headings and custom
116
- // formats could not be applied from the overflow menu at all.
117
- this.#menu.onchange = (event) => {
118
- const clone = event.target;
119
- if (!(clone instanceof HTMLSelectElement))
120
- return;
121
- const original = this.#toolbar.querySelector('select');
122
- if (!original)
123
- return;
124
- original.value = clone.value;
125
- original.dispatchEvent(new Event('change', { bubbles: true }));
126
- this.#close();
127
- };
221
+ this.#close();
222
+ // READ. Every measurement is taken here, while the bar is fully expanded,
223
+ // because a width read after a style write forces the browser to lay the
224
+ // whole bar out again. The old loop alternated the two once per group, so a
225
+ // ten-group bar paid for ten layouts -- from a ResizeObserver watching the
226
+ // element it was resizing.
227
+ const budget = this.#toolbar.clientWidth;
228
+ const groups = this.#groups();
229
+ const total = this.#toolbar.scrollWidth;
230
+ const widths = groups.map((group) => group.offsetWidth);
231
+ const moreWidth = this.#more.offsetWidth;
232
+ const gap = Number.parseFloat(this.#toolbar.ownerDocument.defaultView?.getComputedStyle(this.#toolbar).columnGap ?? '') || 0;
233
+ if (budget === 0) {
234
+ this.#more.hidden = true;
235
+ return;
236
+ }
237
+ // COMPUTE. In overflow mode the bar is a nowrap flex row with one uniform
238
+ // gap, so moving the last k groups out takes exactly their widths and k
239
+ // gaps off the scroll width. Nothing has to be re-measured between them.
240
+ //
241
+ // The trigger's footprint has to come back OUT to answer "does this bar
242
+ // overflow at all", because a bar that fits never shows one -- and charging
243
+ // it for a button it will not show would collapse a group for nothing.
244
+ const trigger = moreWidth > 0 ? moreWidth + gap : 0;
245
+ if (total - trigger <= budget + 1) {
246
+ this.#more.hidden = true;
247
+ return;
248
+ }
249
+ // Past here the trigger stays, so `total` -- which already includes it -- is
250
+ // the right starting point. Leaving it out is what let a bar stay
251
+ // overflowing by roughly the width of the More button: the groups fit, then
252
+ // revealing the trigger pushed the row back over its budget, and it
253
+ // converged only on the next ResizeObserver pass, with a visible flash in
254
+ // between.
255
+ let used = total;
256
+ let count = 0;
257
+ while (used > budget + 1 && count < groups.length) {
258
+ used -= (widths[groups.length - 1 - count] ?? 0) + gap;
259
+ count += 1;
260
+ }
261
+ if (count === 0) {
262
+ this.#more.hidden = true;
263
+ return;
264
+ }
265
+ // WRITE. Backwards, inserted at the front, so the panel keeps the bar's
266
+ // order. The controls themselves move -- they are not cloned -- which is
267
+ // what makes the panel operable rather than a picture of the bar.
268
+ for (let i = groups.length - 1; i >= groups.length - count; i -= 1) {
269
+ const group = groups[i];
270
+ if (group)
271
+ this.#panel.insertBefore(group, this.#panel.firstChild);
272
+ }
273
+ // The trigger is already visible -- revealed before the reads, above. And
274
+ // there are no separator elements to hide: the divider is a border on the
275
+ // group, so a group that moved into the panel takes its own rule with it
276
+ // and cannot leave one stranded at the end of the bar.
277
+ this.#onLayout?.();
128
278
  void this.#host;
129
279
  }
130
- #close() {
131
- this.#menu.hidden = true;
280
+ /* -------------------------------------------------------------- *
281
+ * Open, close, and the keyboard
282
+ * -------------------------------------------------------------- */
283
+ #open() {
284
+ this.#panel.hidden = false;
285
+ this.#more.setAttribute('aria-expanded', 'true');
286
+ this.#position();
287
+ this.#doc.addEventListener('pointerdown', this.#onPointerDown, true);
288
+ this.#doc.defaultView?.addEventListener('scroll', this.#onViewportChange, true);
289
+ this.#doc.defaultView?.addEventListener('resize', this.#onViewportChange);
290
+ this.#focus(0);
291
+ }
292
+ #close(returnFocus = false) {
293
+ if (this.#panel.hidden)
294
+ return;
295
+ this.#panel.hidden = true;
132
296
  this.#more.setAttribute('aria-expanded', 'false');
297
+ this.#doc.removeEventListener('pointerdown', this.#onPointerDown, true);
298
+ this.#doc.defaultView?.removeEventListener('scroll', this.#onViewportChange, true);
299
+ this.#doc.defaultView?.removeEventListener('resize', this.#onViewportChange);
300
+ if (returnFocus)
301
+ this.#more.focus();
302
+ // The reflow's own `#close` must not turn round and call it again.
303
+ if (this.#deferred && !this.#reflowing) {
304
+ this.#deferred = false;
305
+ this.layout();
306
+ }
307
+ }
308
+ /**
309
+ * Under the trigger, in viewport coordinates and clamped to it.
310
+ *
311
+ * Fixed rather than absolute because the bar may sit inside a host with
312
+ * `overflow: hidden`, which is the case the whole feature exists for.
313
+ */
314
+ #position() {
315
+ const rect = this.#more.getBoundingClientRect();
316
+ const box = this.#panel.getBoundingClientRect();
317
+ const view = this.#doc.defaultView;
318
+ const width = view?.innerWidth ?? 0;
319
+ const height = view?.innerHeight ?? 0;
320
+ const below = rect.bottom + 4;
321
+ const top = below + box.height > height ? Math.max(4, rect.top - box.height - 4) : below;
322
+ this.#panel.style.position = 'fixed';
323
+ this.#panel.style.left = `${Math.round(Math.max(4, Math.min(rect.left, width - box.width - 4)))}px`;
324
+ this.#panel.style.top = `${Math.round(top)}px`;
325
+ }
326
+ #focusables() {
327
+ return [...this.#panel.querySelectorAll(FOCUSABLE)];
328
+ }
329
+ #focus(index) {
330
+ const items = this.#focusables();
331
+ if (items.length === 0)
332
+ return;
333
+ const next = items[(index + items.length) % items.length];
334
+ for (const item of items)
335
+ item.tabIndex = item === next ? 0 : -1;
336
+ next?.focus();
337
+ }
338
+ #onKeydown = (event) => {
339
+ const target = event.target;
340
+ if (event.key === 'Escape' || event.key === 'Tab') {
341
+ event.preventDefault();
342
+ this.#close(true);
343
+ return;
344
+ }
345
+ const items = this.#focusables();
346
+ const index = items.indexOf(target);
347
+ if (index < 0)
348
+ return;
349
+ if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
350
+ // Alt+Down is how a keyboard user opens a native select's list.
351
+ if (event.altKey)
352
+ return;
353
+ event.preventDefault();
354
+ this.#focus(index + (event.key === 'ArrowDown' ? 1 : -1));
355
+ return;
356
+ }
357
+ if (event.key === 'Home' || event.key === 'End') {
358
+ // Left to the select, which uses them for its own first and last option.
359
+ if (target?.tagName === 'SELECT')
360
+ return;
361
+ event.preventDefault();
362
+ this.#focus(event.key === 'Home' ? 0 : items.length - 1);
363
+ }
364
+ };
365
+ #onPointerDown = (event) => {
366
+ const target = event.target;
367
+ if (!(target instanceof Node))
368
+ return;
369
+ if (this.#panel.contains(target) || this.#more.contains(target))
370
+ return;
371
+ this.#close();
372
+ };
373
+ #onViewportChange = () => this.#close();
374
+ /* -------------------------------------------------------------- *
375
+ * Moving groups back and forth
376
+ * -------------------------------------------------------------- */
377
+ #restore() {
378
+ for (const group of [...this.#panel.children])
379
+ this.#toolbar.appendChild(group);
380
+ // Overflow always takes a suffix of the groups, so appending in panel order
381
+ // puts them back where they were. The trigger stays last -- but only if it
382
+ // is not already there. An unconditional `appendChild` re-inserts it on
383
+ // every pass, and a re-insertion is what drops focus, so the steady-state
384
+ // layout of a bar that fits now moves nothing at all.
385
+ if (this.#more.isConnected && this.#toolbar.lastElementChild !== this.#more) {
386
+ this.#toolbar.appendChild(this.#more);
387
+ }
133
388
  }
134
389
  #groups() {
135
390
  return [...this.#toolbar.querySelectorAll(':scope > .ol-group')];
@@ -1 +1 @@
1
- {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../src/overflow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"overflow.js","sourceRoot":"","sources":["../src/overflow.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,WAAW,CAAA;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAExC,wEAAwE;AACxE,MAAM,SAAS,GAAG,iCAAiC,CAAA;AAEnD,IAAI,YAAY,GAAG,CAAC,CAAA;AAEpB,MAAM,OAAO,eAAe;IAC1B,QAAQ,CAAa;IACrB,KAAK,CAAa;IAClB,IAAI,CAAU;IACd,KAAK,CAAmB;IACxB,MAAM,CAAgB;IACtB,SAAS,GAA0B,IAAI,CAAA;IACvC,SAAS,CAA0B;IACnC;;;;;;OAMG;IACH,MAAM,GAAkB,IAAI,CAAA;IAC5B;;;OAGG;IACH,SAAS,GAAG,KAAK,CAAA;IACjB,UAAU,GAAG,KAAK,CAAA;IAElB,YAAY,OAAoB,EAAE,IAAiB,EAAE,GAAa,EAAE,QAAqB;QACvF,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAA;QACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,IAAI,GAAG,GAAG,CAAA;QACf,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;QACzB,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;QAE7C,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;QACtC,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,0BAA0B,CAAA;QAClD,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,eAAe,CAAC,YAAY,IAAI,CAAC,CAAC,EAAE,CAAA;QACrD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;QAC3C,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAA;QACxD,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAA;QAC5D,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAA;QACzB,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,CAAA;QAExD,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;QACxC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,QAAQ,CAAA;QAC1B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,yBAAyB,CAAA;QAChD,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAA;QAChD,0EAA0E;QAC1E,sEAAsE;QACtE,mDAAmD;QACnD,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;QACjD,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACxD,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;QACxB,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAA;QAChD,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC,CAAA;QAC3E,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;YACxC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;gBAAE,IAAI,CAAC,KAAK,EAAE,CAAA;;gBAC/B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC,CAAC,CAAA;QAEF,6EAA6E;QAC7E,6EAA6E;QAC7E,kEAAkE;QAClE,IAAI,OAAO,CAAC,UAAU;YAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;;YAC7C,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QAClC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAE/B,IAAI,OAAO,cAAc,KAAK,WAAW,EAAE,CAAC;YAC1C,IAAI,CAAC,SAAS,GAAG,IAAI,cAAc,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAA;YAC3D,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QACjC,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;IAED,kEAAkE;IAClE,QAAQ;QACN,0EAA0E;QAC1E,yEAAyE;QACzE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;QACtB,IAAI,CAAC,MAAM,EAAE,CAAA;QACb,0EAA0E;QAC1E,4DAA4D;QAC5D,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAA;QAC7B,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW;YAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAClE,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC;IAED;;;;;;;OAOG;IACH,SAAS;QACP,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI;YAAE,OAAM;QAChC,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,CAAA;QACnD,IAAI,CAAC,GAAG,EAAE,qBAAqB,EAAE,CAAC;YAChC,IAAI,CAAC,MAAM,EAAE,CAAA;YACb,OAAM;QACR,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,qBAAqB,CAAC,GAAG,EAAE;YAC3C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;YAClB,IAAI,CAAC,MAAM,EAAE,CAAA;QACf,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,OAAO;QACL,uEAAuE;QACvE,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YAC1E,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QACpB,CAAC;QACD,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,CAAA;QAC5B,8EAA8E;QAC9E,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;QACtB,IAAI,CAAC,MAAM,EAAE,CAAA;QACb,IAAI,CAAC,QAAQ,EAAE,CAAA;QACf,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;QACnB,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAA;QACpB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAA;IACxD,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACH,MAAM;QACJ,wEAAwE;QACxE,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,wEAAwE;QACxE,uEAAuE;QACvE,2CAA2C;QAC3C,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;YACxB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;YACrB,OAAM;QACR,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,CAAA;QACtC,MAAM,IAAI,GACR,MAAM,YAAY,WAAW;YAC7B,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC9D,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,IAAI,CAAA;QAEV,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QACtB,IAAI,CAAC;YACH,IAAI,CAAC,OAAO,EAAE,CAAA;QAChB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;QACzB,CAAC;QAED,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,aAAa,KAAK,IAAI;YAAE,OAAM;QACrD,6EAA6E;QAC7E,6EAA6E;QAC7E,uEAAuE;QACvE,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAA;QAClF,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,WAAW;YAAE,MAAM,CAAC,KAAK,EAAE,CAAA;IAC1D,CAAC;IAED,OAAO;QACL,IAAI,CAAC,QAAQ,EAAE,CAAA;QACf,wEAAwE;QACxE,sEAAsE;QACtE,4EAA4E;QAC5E,4EAA4E;QAC5E,oEAAoE;QACpE,EAAE;QACF,4DAA4D;QAC5D,2EAA2E;QAC3E,yEAAyE;QACzE,sEAAsE;QACtE,UAAU;QACV,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,KAAK,CAAA;QACzB,IAAI,CAAC,MAAM,EAAE,CAAA;QAEb,0EAA0E;QAC1E,yEAAyE;QACzE,4EAA4E;QAC5E,2EAA2E;QAC3E,2BAA2B;QAC3B,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAA;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,CAAA;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAA;QACvC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAA;QACvD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAA;QACxC,MAAM,GAAG,GACP,MAAM,CAAC,UAAU,CACf,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,WAAW,EAAE,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,IAAI,EAAE,CACzF,IAAI,CAAC,CAAA;QAER,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;YACjB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;YACxB,OAAM;QACR,CAAC;QAED,0EAA0E;QAC1E,wEAAwE;QACxE,yEAAyE;QACzE,EAAE;QACF,wEAAwE;QACxE,4EAA4E;QAC5E,uEAAuE;QACvE,MAAM,OAAO,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QACnD,IAAI,KAAK,GAAG,OAAO,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;YACxB,OAAM;QACR,CAAC;QAED,6EAA6E;QAC7E,kEAAkE;QAClE,4EAA4E;QAC5E,oEAAoE;QACpE,0EAA0E;QAC1E,WAAW;QACX,IAAI,IAAI,GAAG,KAAK,CAAA;QAChB,IAAI,KAAK,GAAG,CAAC,CAAA;QACb,OAAO,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;YAClD,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,GAAG,CAAA;YACtD,KAAK,IAAI,CAAC,CAAA;QACZ,CAAC;QACD,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YAChB,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAA;YACxB,OAAM;QACR,CAAC;QAED,wEAAwE;QACxE,yEAAyE;QACzE,kEAAkE;QAClE,KAAK,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACnE,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;YACvB,IAAI,KAAK;gBAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;QACpE,CAAC;QAED,0EAA0E;QAC1E,0EAA0E;QAC1E,yEAAyE;QACzE,uDAAuD;QACvD,IAAI,CAAC,SAAS,EAAE,EAAE,CAAA;QAClB,KAAK,IAAI,CAAC,KAAK,CAAA;IACjB,CAAC;IAED;;wEAEoE;IAEpE,KAAK;QACH,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,KAAK,CAAA;QAC1B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAAA;QAChD,IAAI,CAAC,SAAS,EAAE,CAAA;QAChB,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAA;QACpE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAA;QAC/E,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAA;QACzE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;IAChB,CAAC;IAED,MAAM,CAAC,WAAW,GAAG,KAAK;QACxB,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM;YAAE,OAAM;QAC9B,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAA;QACzB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,OAAO,CAAC,CAAA;QACjD,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,aAAa,EAAE,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,CAAA;QACvE,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAA;QAClF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,mBAAmB,CAAC,QAAQ,EAAE,IAAI,CAAC,iBAAiB,CAAC,CAAA;QAC5E,IAAI,WAAW;YAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QACnC,mEAAmE;QACnE,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACvC,IAAI,CAAC,SAAS,GAAG,KAAK,CAAA;YACtB,IAAI,CAAC,MAAM,EAAE,CAAA;QACf,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,SAAS;QACP,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAA;QAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAA;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,CAAA;QAClC,MAAM,KAAK,GAAG,IAAI,EAAE,UAAU,IAAI,CAAC,CAAA;QACnC,MAAM,MAAM,GAAG,IAAI,EAAE,WAAW,IAAI,CAAC,CAAA;QACrC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAA;QAC7B,MAAM,GAAG,GAAG,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;QACxF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,GAAG,OAAO,CAAA;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QACnG,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAA;IAChD,CAAC;IAED,WAAW;QACT,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAc,SAAS,CAAC,CAAC,CAAA;IAClE,CAAC;IAED,MAAM,CAAC,KAAa;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAChC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAM;QAC9B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,CAAA;QACzD,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;QAChE,IAAI,EAAE,KAAK,EAAE,CAAA;IACf,CAAC;IAED,UAAU,GAAG,CAAC,KAAoB,EAAQ,EAAE;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,MAA4B,CAAA;QACjD,IAAI,KAAK,CAAC,GAAG,KAAK,QAAQ,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YAClD,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,MAAqB,CAAC,CAAA;QAClD,IAAI,KAAK,GAAG,CAAC;YAAE,OAAM;QACrB,IAAI,KAAK,CAAC,GAAG,KAAK,WAAW,IAAI,KAAK,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YACzD,gEAAgE;YAChE,IAAI,KAAK,CAAC,MAAM;gBAAE,OAAM;YACxB,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,CAAC,KAAK,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YACzD,OAAM;QACR,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,KAAK,MAAM,IAAI,KAAK,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YAChD,yEAAyE;YACzE,IAAI,MAAM,EAAE,OAAO,KAAK,QAAQ;gBAAE,OAAM;YACxC,KAAK,CAAC,cAAc,EAAE,CAAA;YACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC,CAAA;IAED,cAAc,GAAG,CAAC,KAAY,EAAQ,EAAE;QACtC,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;QAC3B,IAAI,CAAC,CAAC,MAAM,YAAY,IAAI,CAAC;YAAE,OAAM;QACrC,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC;YAAE,OAAM;QACvE,IAAI,CAAC,MAAM,EAAE,CAAA;IACf,CAAC,CAAA;IAED,iBAAiB,GAAG,GAAS,EAAE,CAAC,IAAI,CAAC,MAAM,EAAE,CAAA;IAE7C;;wEAEoE;IAEpE,QAAQ;QACN,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAC/E,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACxE,0EAA0E;QAC1E,sDAAsD;QACtD,IAAI,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC,QAAQ,CAAC,gBAAgB,KAAK,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5E,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACvC,CAAC;IACH,CAAC;IAED,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAc,oBAAoB,CAAC,CAAC,CAAA;IAC/E,CAAC;CACF"}