@fusion.dev/helix-headless 0.2.0 → 0.4.0
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/custom-elements.json +234 -234
- package/dist/accordion/item/accordion-item.js +0 -108
- package/dist/accordion/root/accordion-root.js +0 -33
- package/dist/announcement/root/announcement-root.js +1 -76
- package/dist/app-header/root/app-header-root.js +0 -84
- package/dist/avatar/group/avatar-group.js +0 -77
- package/dist/avatar/root/avatar-root.js +0 -72
- package/dist/badge/root/badge-root.js +0 -67
- package/dist/bar-chart/root/bar-chart-root.js +3 -263
- package/dist/blockquote/root/blockquote-root.js +0 -46
- package/dist/breadcrumbs/breadcrumb/breadcrumb.js +0 -72
- package/dist/breadcrumbs/root/breadcrumbs-root.js +0 -52
- package/dist/button/root/button-root.js +0 -215
- package/dist/button-group/root/button-group-root.js +0 -30
- package/dist/callout/root/callout-root.js +0 -52
- package/dist/card/root/card-root.js +0 -41
- package/dist/carousel/root/carousel-root.d.ts +2 -2
- package/dist/carousel/root/carousel-root.js +2 -362
- package/dist/chat/composer/chat-composer.js +0 -131
- package/dist/chat/message/chat-message.js +0 -137
- package/dist/chat/root/chat-root.js +0 -193
- package/dist/checkbox/root/checkbox-root.js +0 -113
- package/dist/checkbox-group/root/checkbox-group-root.js +1 -157
- package/dist/code/root/code-root.js +0 -26
- package/dist/code-block/root/code-block-root.js +0 -100
- package/dist/color-input/root/color-input-root.js +0 -217
- package/dist/color-picker/color.js +0 -69
- package/dist/color-picker/root/color-picker-root.js +0 -272
- package/dist/combobox/root/combobox-root.js +0 -428
- package/dist/command/root/command-root.js +0 -177
- package/dist/copy-button/root/copy-button-root.js +0 -134
- package/dist/data-table/root/data-table-root.js +0 -336
- package/dist/data-table/root/order.js +0 -100
- package/dist/date-input/root/date-input-root.js +1 -398
- package/dist/date-picker/root/date-picker-root.js +0 -358
- package/dist/dialog/root/dialog-root.js +0 -189
- package/dist/divider/root/divider-root.js +0 -71
- package/dist/donut-chart/root/donut-chart-root.js +0 -214
- package/dist/drawer/root/drawer-root.js +0 -116
- package/dist/drilldown/item/drilldown-item.js +0 -82
- package/dist/drilldown/root/drilldown-root.js +0 -91
- package/dist/element-events.generated.js +0 -5
- package/dist/empty/root/empty-root.js +0 -35
- package/dist/file-input/root/file-input-root.js +0 -232
- package/dist/format-date/root/format-date-root.js +0 -95
- package/dist/heatmap/root/buckets.js +0 -57
- package/dist/heatmap/root/heatmap-root.js +0 -313
- package/dist/icon/root/icon-root.js +0 -48
- package/dist/icon-button/root/icon-button-root.js +0 -30
- package/dist/image/root/image-root.js +0 -174
- package/dist/index.js +0 -9
- package/dist/input/root/input-root.js +0 -228
- package/dist/kbd/root/kbd-root.js +0 -117
- package/dist/legend/root/legend-root.js +0 -73
- package/dist/line-chart/root/line-chart-root.js +0 -172
- package/dist/list/item/list-item.js +0 -92
- package/dist/list/root/list-root.js +0 -26
- package/dist/listbox/root/listbox-root.js +2 -429
- package/dist/loader/root/loader-root.js +0 -40
- package/dist/megamenu/root/megamenu-root.js +0 -284
- package/dist/menu/item/menu-item.js +1 -221
- package/dist/menu/root/menu-root.js +1 -171
- package/dist/meter/root/meter-root.js +1 -152
- package/dist/navigation/root/navigation-root.js +0 -56
- package/dist/notifications/notification/notification.js +0 -159
- package/dist/notifications/root/notifications-root.js +0 -148
- package/dist/number-input/root/number-input-root.js +1 -263
- package/dist/option/group/option-group.js +0 -26
- package/dist/option/root/option-root.js +0 -157
- package/dist/outline/root/outline-root.js +0 -292
- package/dist/pagination/root/pagination-root.js +0 -361
- package/dist/pagination/size/pagination-size.js +0 -104
- package/dist/pagination/summary/pagination-summary.js +0 -90
- package/dist/pin-input/root/pin-input-root.js +0 -276
- package/dist/popover/root/popover-root.js +0 -103
- package/dist/progress/root/progress-root.js +1 -133
- package/dist/qr-code/root/qr-code-root.js +0 -96
- package/dist/quote/root/quote-root.js +0 -25
- package/dist/radio/group/radio-group.js +0 -81
- package/dist/radio/root/radio-root.js +0 -85
- package/dist/rating/root/rating-root.js +0 -220
- package/dist/scroll-area/root/scroll-area-root.js +0 -100
- package/dist/select/root/select-root.js +2 -218
- package/dist/skeleton/root/skeleton-root.js +0 -20
- package/dist/slider/root/slider-root.js +1 -267
- package/dist/sparkline/root/sparkline-root.js +3 -164
- package/dist/speed-dial/item/speed-dial-item.js +0 -52
- package/dist/speed-dial/root/speed-dial-root.js +1 -116
- package/dist/splitter/root/splitter-root.js +0 -489
- package/dist/stat/group/stat-group.js +0 -18
- package/dist/stat/root/stat-root.js +0 -23
- package/dist/steps/root/steps-root.js +0 -62
- package/dist/steps/step/step.js +0 -131
- package/dist/switch/root/switch-root.js +0 -80
- package/dist/table/cell/table-cell.js +0 -23
- package/dist/table/column/table-column.js +0 -158
- package/dist/table/root/table-root.js +0 -82
- package/dist/table/row/table-row.js +0 -21
- package/dist/tabs/panel/tab-panel.js +0 -49
- package/dist/tabs/root/tabs-root.js +0 -98
- package/dist/tabs/tab/tab.js +0 -59
- package/dist/tag/root/tag-root.js +0 -31
- package/dist/textarea/root/textarea-root.js +0 -162
- package/dist/time-input/root/time-input-root.js +0 -283
- package/dist/time-picker/root/time-picker-root.js +0 -326
- package/dist/timeline/item/timeline-item.js +0 -66
- package/dist/timeline/root/timeline-root.js +0 -41
- package/dist/toast/item/toast-item.js +0 -165
- package/dist/toast/root/toast-root.js +0 -51
- package/dist/toggle/group/toggle-group.js +0 -150
- package/dist/toggle/root/toggle-root.js +0 -78
- package/dist/toolbar/root/toolbar-root.js +0 -116
- package/dist/tooltip/root/tooltip-root.js +0 -427
- package/dist/tour/root/tour-root.js +0 -130
- package/dist/tour/stop/tour-stop.js +1 -127
- package/dist/tree/item/tree-item.js +0 -198
- package/dist/tree/root/tree-root.js +0 -97
- package/dist/trend/root/trend-root.js +0 -53
- package/dist/video/root/video-root.d.ts +8 -12
- package/dist/video/root/video-root.js +5 -756
- package/dist/video-playlist/item/video-playlist-item.js +1 -90
- package/dist/video-playlist/menu/video-playlist-menu.js +1 -132
- package/dist/video-playlist/root/video-playlist-root.js +0 -113
- package/dist/virtual-list/root/virtual-list-root.js +0 -105
- package/jsx.d.ts +1408 -2576
- package/package.json +6 -6
- package/vue.d.ts +1 -1
|
@@ -8,251 +8,27 @@ var HelixHeadlessSplitter_1;
|
|
|
8
8
|
import { capturePointer, closestInstance, customElement, HelixBeforeCollapseEvent, HelixBeforeResizeEvent, HelixElement, requestChange, } from '@fusion.dev/helix-core';
|
|
9
9
|
import { html } from 'lit';
|
|
10
10
|
import { property, state } from 'lit/decorators.js';
|
|
11
|
-
/**
|
|
12
|
-
* The inline size under which `orientation="auto"` stacks, in pixels.
|
|
13
|
-
*
|
|
14
|
-
* 32rem at the default root size. Two panes side by side under this have about
|
|
15
|
-
* 240px each before the divider, which is narrower than the content anybody
|
|
16
|
-
* puts in one: the sandbox at 390 gave its source pane 237px and cut every line
|
|
17
|
-
* mid-token, which is the measurement this number comes from.
|
|
18
|
-
*
|
|
19
|
-
* A constant and not a property, until something wants a different one. The
|
|
20
|
-
* axis is the decision worth exposing; where it turns is a detail of this
|
|
21
|
-
* component doing what it was asked.
|
|
22
|
-
*/
|
|
23
11
|
const STACK_BELOW = 512;
|
|
24
|
-
/**
|
|
25
|
-
* @summary Two panes and a divider the reader can move.
|
|
26
|
-
*
|
|
27
|
-
* @description
|
|
28
|
-
* **One axis, and nesting is how you get two.** The sandbox this was taken from
|
|
29
|
-
* moves a cross: a divider down the middle, another across the bottom, and a
|
|
30
|
-
* corner that moves both. That is a layout rather than a component, and every
|
|
31
|
-
* part of it that is worth sharing is one divider between two panes. A splitter
|
|
32
|
-
* inside a splitter gives the cross back, and gives the three-pane and the
|
|
33
|
-
* four-pane arrangements it never had.
|
|
34
|
-
*
|
|
35
|
-
* **Where two of them cross there is a corner, and it moves both.** A splitter
|
|
36
|
-
* slotted into a pane of another, at right angles to it, has one end of its
|
|
37
|
-
* divider sitting exactly on the other's. That end grows a handle: `move` under
|
|
38
|
-
* the pointer, both arrow axes on the keyboard, and a drag that carries the two
|
|
39
|
-
* positions at once.
|
|
40
|
-
*
|
|
41
|
-
* **The outer splitter draws it and the nested one announces itself upward.**
|
|
42
|
-
* The nested one drawing its own is the tempting version, since it already
|
|
43
|
-
* knows where its divider begins and needs no measurement of the other, and it
|
|
44
|
-
* is clipped: the crossing lies on the boundary between a pane and the divider
|
|
45
|
-
* beside it, a handle centered there reaches back over the boundary, and a pane
|
|
46
|
-
* is `overflow: auto`. Exactly half the corner is cut off. The outer separator
|
|
47
|
-
* is the one element that already straddles the seam, so the corner belongs in
|
|
48
|
-
* it.
|
|
49
|
-
*
|
|
50
|
-
* That makes the channel between the two a single call in a single direction:
|
|
51
|
-
* `cross(child, at)` says where the child crosses, `moveTo` drives either one,
|
|
52
|
-
* and neither reads the other's state. The child is what calls it, because a
|
|
53
|
-
* parent cannot poll for a child that has not upgraded yet.
|
|
54
|
-
*
|
|
55
|
-
* The one number it carries is a percentage rather than a measurement, which is
|
|
56
|
-
* what keeps either of them from measuring the other, and it assumes the nested
|
|
57
|
-
* splitter fills its pane. That is what slotting one into a pane does. Give the
|
|
58
|
-
* nested splitter its own inset, or wrap it in something smaller than the pane,
|
|
59
|
-
* and the corner is drawn where the crossing would be rather than where it is.
|
|
60
|
-
*
|
|
61
|
-
* **It is a client-side enhancement and says so.** Finding the parent means
|
|
62
|
-
* inspecting the tree, which a server cannot do, so the corner appears at
|
|
63
|
-
* `firstUpdated` and never in the served markup. That is allowed for exactly
|
|
64
|
-
* this: it is an affordance rather than content, and a splitter with no corner
|
|
65
|
-
* yet is a splitter with two working dividers.
|
|
66
|
-
*
|
|
67
|
-
* Where two dividers are parallel there is no corner, because there is nothing
|
|
68
|
-
* to take hold of. Where they cross without a corner, the inner divider takes
|
|
69
|
-
* the pointer: every separator sits on the same layer, so a tie goes to document
|
|
70
|
-
* order and a deeper splitter is always later.
|
|
71
|
-
*
|
|
72
|
-
* **A divider that is moving says so, whoever is moving it.** `:hover` cannot
|
|
73
|
-
* carry that: a drag holds the pointer through capture and a fast one leaves the
|
|
74
|
-
* target behind, and a corner moves the nested divider while the reader is on
|
|
75
|
-
* the crossing, nowhere near it. So a drag in progress is a state, and both
|
|
76
|
-
* dividers a corner moves are lit for as long as it moves them.
|
|
77
|
-
*
|
|
78
|
-
* **The divider is a real `role="separator"` with `aria-valuenow`, moved by the
|
|
79
|
-
* arrow keys as well as by pointer.** A divider only a mouse can move is
|
|
80
|
-
* unusable without one, which is the reason the sandbox's carries the same, and
|
|
81
|
-
* `Home` and `End` take it to its bounds.
|
|
82
|
-
*
|
|
83
|
-
* **It goes where it is put, all the way to either end.** `min` and `max` are
|
|
84
|
-
* nothing and everything unless a consumer says otherwise, because a divider
|
|
85
|
-
* that stops dead short of the edge gives a reader no way to put a pane away.
|
|
86
|
-
* Where a pane should keep a sliver of itself, only whoever put the tabs on it
|
|
87
|
-
* knows how much, so they say so.
|
|
88
|
-
*
|
|
89
|
-
* **A pane can be shut, and that is not the same as a divider at the edge.**
|
|
90
|
-
* The geometry is identical and the knowledge is not: only a shut pane knows
|
|
91
|
-
* where to open back to. So `collapsed` is a second state rather than a reading
|
|
92
|
-
* of the position, and the position it opens to is simply the one collapsing
|
|
93
|
-
* never overwrote. `collapsed-size` keeps a sliver showing where a pane has a
|
|
94
|
-
* tab strip worth leaving, and collapse overrides `min` and `max`, which is
|
|
95
|
-
* what those are for: they keep a pane from vanishing by accident, and this is
|
|
96
|
-
* the deliberate version.
|
|
97
|
-
*
|
|
98
|
-
* The two are kept in step in one direction only: a drag that settles on the
|
|
99
|
-
* collapsed size latches shut, and any move opens it again. Otherwise a reader
|
|
100
|
-
* could drag a pane closed and the consumer's own button would still be
|
|
101
|
-
* offering to close it.
|
|
102
|
-
*
|
|
103
|
-
* A pane shut to nothing is told apart from one shut to a sliver, because the
|
|
104
|
-
* two want different dividers: a sliver still has a boundary worth drawing, and
|
|
105
|
-
* nothing leaves the divider sitting on the container's own edge.
|
|
106
|
-
*
|
|
107
|
-
* **The box is measured once, when a drag starts.** Measuring per move is a
|
|
108
|
-
* forced layout on every pointer event, in the middle of a gesture whose whole
|
|
109
|
-
* job is to stay ahead of the pointer, and nothing about the box moves while the
|
|
110
|
-
* divider does.
|
|
111
|
-
*
|
|
112
|
-
* **`helix-before-resize` fires when it settles, not while it moves.** A drag is a
|
|
113
|
-
* stream of positions and only the one it ends at is a preference; announcing
|
|
114
|
-
* each would ask a consumer to write to storage a hundred times to record one
|
|
115
|
-
* decision. Nothing is stored here either: where a preference lives, and whether
|
|
116
|
-
* it should be kept at all, is the consumer's to decide, and a component writing
|
|
117
|
-
* to storage on its own would be deciding for all of them under a key it chose.
|
|
118
|
-
*
|
|
119
|
-
* The properties below are written by this component and read by whoever draws
|
|
120
|
-
* it. A consumer sets none of them: they carry a number this element computed
|
|
121
|
-
* and CSS cannot, which is what lets a stylesheet on this tier draw the thing
|
|
122
|
-
* without measuring anything itself.
|
|
123
|
-
*
|
|
124
|
-
* @since 0.1.0
|
|
125
|
-
* @status experimental
|
|
126
|
-
* @category layout
|
|
127
|
-
* @aliases SplitPane, Resizable, SplitView
|
|
128
|
-
* @documentation https://helix.fusion.dev/components/splitter
|
|
129
|
-
*
|
|
130
|
-
* @slot end - The pane after it.
|
|
131
|
-
* @slot start - The pane before the divider.
|
|
132
|
-
*
|
|
133
|
-
* @event {HelixBeforeCollapseEvent} helix-before-collapse - Fired before a pane shuts or reopens, and cancelable.
|
|
134
|
-
* @event {HelixBeforeResizeEvent} helix-before-resize - Fired before the divider settles, and cancelable.
|
|
135
|
-
*
|
|
136
|
-
* @csspart corner - The handle where two dividers cross, added on the client.
|
|
137
|
-
* @csspart end - The box holding the second.
|
|
138
|
-
* @csspart separator - The divider itself.
|
|
139
|
-
* @csspart start - The box holding the first pane.
|
|
140
|
-
*
|
|
141
|
-
* @cssproperty --splitter-corner-at - Where a nested splitter crosses this one, as a percentage.
|
|
142
|
-
* @cssproperty --splitter-position - Where the divider sits, as a percentage of the box.
|
|
143
|
-
*/
|
|
144
12
|
let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitter extends HelixElement {
|
|
145
13
|
constructor() {
|
|
146
14
|
super(...arguments);
|
|
147
|
-
/**
|
|
148
|
-
* How much of a shut pane is still shown, as a percentage.
|
|
149
|
-
*
|
|
150
|
-
* Zero by default, which is a pane fully gone. A tab strip or a title bar
|
|
151
|
-
* that should survive its own collapse says how much of itself to keep, and
|
|
152
|
-
* only whoever put it there knows that, which is the same reason `min` is a
|
|
153
|
-
* consumer's to set.
|
|
154
|
-
*/
|
|
155
15
|
this.collapsedSize = 0;
|
|
156
|
-
/**
|
|
157
|
-
* Accessible name for the handle where two dividers cross.
|
|
158
|
-
*/
|
|
159
16
|
this.cornerLabel = 'Resize both';
|
|
160
|
-
/**
|
|
161
|
-
* Where the divider starts, where nothing declared it.
|
|
162
|
-
*/
|
|
163
17
|
this.defaultPosition = 50;
|
|
164
|
-
/**
|
|
165
|
-
* Accessible name for the divider.
|
|
166
|
-
*/
|
|
167
18
|
this.label = 'Resize panes';
|
|
168
|
-
/**
|
|
169
|
-
* Nearest it may come to the end, on the same terms as `min`.
|
|
170
|
-
*/
|
|
171
19
|
this.max = 100;
|
|
172
|
-
/**
|
|
173
|
-
* Nearest the divider may come to the start.
|
|
174
|
-
*
|
|
175
|
-
* A bare number is a percentage, which is what this always took. A CSS length
|
|
176
|
-
* is resolved against the splitter's own box along its axis, so `min="15rem"`
|
|
177
|
-
* means fifteen rem of pane whatever the container is doing.
|
|
178
|
-
*
|
|
179
|
-
* **The length is the one that holds.** A percentage minimum does not: twenty
|
|
180
|
-
* per cent is 280px in a 1400px splitter and 100px in a 500px one, and the
|
|
181
|
-
* number a consumer picked was almost certainly about how much text fits
|
|
182
|
-
* rather than about a ratio. The pane that a percentage protects at desk
|
|
183
|
-
* width is squeezed past reading at half of it.
|
|
184
|
-
*
|
|
185
|
-
* **It does not fight `collapsed`.** Collapse overrides `min` and `max`, as
|
|
186
|
-
* the class docstring says, so a pane can still be shut and reopened. What a
|
|
187
|
-
* minimum forbids is the space between shut and usable: a pane wide enough to
|
|
188
|
-
* be there and too narrow to read is the state nobody wants and every drag
|
|
189
|
-
* passes through.
|
|
190
|
-
*/
|
|
191
20
|
this.min = 0;
|
|
192
|
-
/**
|
|
193
|
-
* How far one arrow press moves it, as a percentage.
|
|
194
|
-
*/
|
|
195
21
|
this.step = 2;
|
|
196
|
-
/** The position, whoever set it, and `undefined` until somebody has. */
|
|
197
22
|
this.positionState = undefined;
|
|
198
|
-
/** The collapse, whoever set it, and `undefined` until somebody has. */
|
|
199
23
|
this.collapsedState = undefined;
|
|
200
|
-
/**
|
|
201
|
-
* Where the divider was before the gesture that may be about to shut it.
|
|
202
|
-
*
|
|
203
|
-
* A drag writes a position on every pointer move, so by the time it reaches
|
|
204
|
-
* the edge the number it started from is gone. Taken once, on the press, and
|
|
205
|
-
* written back if the gesture ends shut, so the pane opens to where it was
|
|
206
|
-
* rather than to the edge it was dragged against.
|
|
207
|
-
*/
|
|
208
24
|
this.opened = undefined;
|
|
209
|
-
/**
|
|
210
|
-
* Where the gesture has moved to, whether or not it was applied. See `move`.
|
|
211
|
-
*
|
|
212
|
-
* `@state`, because the divider is drawn from it while a drag is under way.
|
|
213
|
-
*/
|
|
214
25
|
this.reached = undefined;
|
|
215
|
-
/**
|
|
216
|
-
* Whether this divider is being moved right now, by anything.
|
|
217
|
-
*
|
|
218
|
-
* `:hover` is not enough on its own. A drag holds the pointer through capture
|
|
219
|
-
* and a fast one leaves the eight pixel target behind, and a corner moves a
|
|
220
|
-
* divider the pointer is nowhere near: the nested one, while the reader is on
|
|
221
|
-
* the crossing. Both are dividers that are moving and should say so.
|
|
222
|
-
*
|
|
223
|
-
* A `data-` attribute on an element in this shadow root, never on the host.
|
|
224
|
-
*/
|
|
225
26
|
this.dragging = false;
|
|
226
|
-
/**
|
|
227
|
-
* Nested splitters that cross this one, and how far along they cross it.
|
|
228
|
-
*
|
|
229
|
-
* Written by the child, not found by the parent: a parent cannot poll for a
|
|
230
|
-
* child that has not upgraded, so the child announces itself once it has. The
|
|
231
|
-
* number is the child's own `position`, which is also its offset along this
|
|
232
|
-
* divider, because the child fills the pane and the pane spans this splitter
|
|
233
|
-
* end to end on the axis the divider runs.
|
|
234
|
-
*
|
|
235
|
-
* Not `@state`, because the array is mutated in place. `cross` and `uncross`
|
|
236
|
-
* request the update themselves.
|
|
237
|
-
*/
|
|
238
27
|
this.crossings = [];
|
|
239
|
-
/** The splitter this one is nested in, once it has found it. */
|
|
240
28
|
this.outer = null;
|
|
241
|
-
/** The last position announced upward, so an unchanged one costs nothing. */
|
|
242
29
|
this.crossedAt = undefined;
|
|
243
|
-
/** The two boxes a corner drag is measured against, taken once. */
|
|
244
30
|
this.cornerOrigin = null;
|
|
245
|
-
/**
|
|
246
|
-
* The host's box, measured once when a drag starts.
|
|
247
|
-
*
|
|
248
|
-
* Measuring per move is a forced layout on every pointer event, in the middle
|
|
249
|
-
* of a gesture whose whole job is to stay ahead of the pointer. Nothing about
|
|
250
|
-
* it moves while the divider does.
|
|
251
|
-
*/
|
|
252
31
|
this.origin = null;
|
|
253
|
-
/**
|
|
254
|
-
* Whether `auto` has resolved to stacked, which only the observer sets.
|
|
255
|
-
*/
|
|
256
32
|
this.stacked = false;
|
|
257
33
|
this.handlePointerDown = (event) => {
|
|
258
34
|
const separator = event.currentTarget;
|
|
@@ -260,14 +36,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
260
36
|
this.origin = this.vertical
|
|
261
37
|
? { length: box.height, start: box.top }
|
|
262
38
|
: { length: box.width, start: box.left };
|
|
263
|
-
/* Captured, so the divider keeps the pointer once it has it: a drag that
|
|
264
|
-
outruns a thin target otherwise ends the moment the pointer leaves it,
|
|
265
|
-
which on a fast gesture is immediately. */
|
|
266
39
|
capturePointer(separator, event.pointerId);
|
|
267
40
|
this.opened = this.collapsed ? undefined : this.position;
|
|
268
|
-
// Cleared, so a number from the last gesture cannot settle this one.
|
|
269
41
|
this.reached = undefined;
|
|
270
|
-
// On the press rather than the first move, so holding it still says so too.
|
|
271
42
|
this.dragging = true;
|
|
272
43
|
event.preventDefault();
|
|
273
44
|
};
|
|
@@ -277,9 +48,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
277
48
|
return;
|
|
278
49
|
}
|
|
279
50
|
const along = this.vertical ? event.clientY : event.clientX;
|
|
280
|
-
/* Signed against the writing direction, because a pointer is physical and
|
|
281
|
-
the panes are not: in a right-to-left document the start pane is on the
|
|
282
|
-
right, so dragging right makes it *larger*. */
|
|
283
51
|
const raw = ((along - origin.start) / origin.length) * 100;
|
|
284
52
|
const rtl = !this.vertical && getComputedStyle(this).direction === 'rtl';
|
|
285
53
|
this.move(rtl ? 100 - raw : raw);
|
|
@@ -294,32 +62,11 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
294
62
|
}
|
|
295
63
|
this.origin = null;
|
|
296
64
|
this.dragging = false;
|
|
297
|
-
/* Nothing moved, so nothing is announced. Settling on every press would
|
|
298
|
-
dispatch a change carrying the position it already had, so a plain click
|
|
299
|
-
rewrites a consumer's stored value from a gesture that moved nothing:
|
|
300
|
-
harmless where the value round-trips exactly, and not where it does not,
|
|
301
|
-
which is any consumer storing pixels and dividing by a box. */
|
|
302
65
|
if (this.reached === undefined) {
|
|
303
66
|
return;
|
|
304
67
|
}
|
|
305
|
-
/* `reached` is deliberately left standing. It is what the divider is drawn
|
|
306
|
-
from until the consumer answers, and the setter is what clears it. */
|
|
307
68
|
this.settle(this.reached);
|
|
308
69
|
};
|
|
309
|
-
/**
|
|
310
|
-
* The keys a separator answers.
|
|
311
|
-
*
|
|
312
|
-
* One axis, the one the divider actually travels along, plus `Home` and `End`
|
|
313
|
-
* for its bounds. The other two arrows are left alone and the page scrolls
|
|
314
|
-
* under them as it would anywhere else.
|
|
315
|
-
*
|
|
316
|
-
* **Not all four, which is the tempting version.** Taking every arrow would
|
|
317
|
-
* mean `ArrowLeft` moving a horizontal divider up and down: not a reader
|
|
318
|
-
* being helped past a wrong guess, an arrow that does not go the way it
|
|
319
|
-
* points. A reader who guesses wrong is answered better by the corner, where
|
|
320
|
-
* all four are live and each moves the divider it points along, which is the
|
|
321
|
-
* only place in a splitter where a second axis means anything.
|
|
322
|
-
*/
|
|
323
70
|
this.handleKeydown = (event) => {
|
|
324
71
|
const back = this.vertical ? 'ArrowUp' : 'ArrowLeft';
|
|
325
72
|
const forward = this.vertical ? 'ArrowDown' : 'ArrowRight';
|
|
@@ -341,19 +88,11 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
341
88
|
}
|
|
342
89
|
this.move(next);
|
|
343
90
|
this.settle(next);
|
|
344
|
-
// Or the arrows scroll the page under the divider being moved.
|
|
345
91
|
event.preventDefault();
|
|
346
92
|
};
|
|
347
93
|
this.handleDoubleClick = () => {
|
|
348
94
|
this.reset();
|
|
349
95
|
};
|
|
350
|
-
/**
|
|
351
|
-
* Both of them, because the corner moves both.
|
|
352
|
-
*
|
|
353
|
-
* Its own handler, because the corner is a sibling of the separator rather
|
|
354
|
-
* than a child of it, for the stacking reason in the stylesheet, so nothing
|
|
355
|
-
* reaches the separator's own double click by bubbling.
|
|
356
|
-
*/
|
|
357
96
|
this.handleCornerDoubleClick = (event) => {
|
|
358
97
|
const crossing = this.crossings[Number(event.currentTarget.dataset.cross)];
|
|
359
98
|
if (!crossing) {
|
|
@@ -385,14 +124,10 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
385
124
|
crossing.by.opened = crossing.by.collapsed ? undefined : crossing.by.position;
|
|
386
125
|
this.reached = undefined;
|
|
387
126
|
crossing.by.reached = undefined;
|
|
388
|
-
/* Both of them, on the press. The nested one is reachable here because it is
|
|
389
|
-
the same class, and this is the whole of what the corner needs beyond
|
|
390
|
-
`moveTo`: a divider being moved looks like one. */
|
|
391
127
|
this.dragging = true;
|
|
392
128
|
crossing.by.dragging = true;
|
|
393
129
|
this.leaveSeparator(event);
|
|
394
130
|
};
|
|
395
|
-
/** Both axes at once, each measured against the splitter that owns it. */
|
|
396
131
|
this.handleCornerMove = (event) => {
|
|
397
132
|
const origin = this.cornerOrigin;
|
|
398
133
|
if (!origin) {
|
|
@@ -430,11 +165,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
430
165
|
origin.by.moveTo(origin.by.reached ?? origin.by.position, true);
|
|
431
166
|
this.leaveSeparator(event);
|
|
432
167
|
};
|
|
433
|
-
/**
|
|
434
|
-
* The corner takes every arrow, each moving the splitter whose axis it points
|
|
435
|
-
* along, which is the only reading a keyboard can give a handle that moves in
|
|
436
|
-
* two directions.
|
|
437
|
-
*/
|
|
438
168
|
this.handleCornerKeydown = (event) => {
|
|
439
169
|
const crossing = this.crossings[Number(event.currentTarget.dataset.cross)];
|
|
440
170
|
if (!crossing) {
|
|
@@ -457,13 +187,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
457
187
|
this.leaveSeparator(event);
|
|
458
188
|
};
|
|
459
189
|
}
|
|
460
|
-
/**
|
|
461
|
-
* Where the divider sits, as a percentage of the whole.
|
|
462
|
-
*
|
|
463
|
-
* Reading gives where it is; writing declares it the consumer's, and
|
|
464
|
-
* `default-position` seeds one this element owns. The same accessor pair
|
|
465
|
-
* `Pagination.page` and the calendar's `month` carry.
|
|
466
|
-
*/
|
|
467
190
|
get position() {
|
|
468
191
|
const shut = this.collapsed;
|
|
469
192
|
if (shut) {
|
|
@@ -474,27 +197,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
474
197
|
set position(next) {
|
|
475
198
|
const previous = this.position;
|
|
476
199
|
this.positionState = next;
|
|
477
|
-
/* The consumer has spoken, so the drag stops standing in for them. Without
|
|
478
|
-
this the divider flashes back: `showing` falls to the position set before
|
|
479
|
-
the gesture and is drawn there for the frame it takes them to answer the
|
|
480
|
-
announcement, snapping to where the drag started and then jumping to
|
|
481
|
-
where it was dropped. */
|
|
482
200
|
this.reached = undefined;
|
|
483
201
|
this.requestUpdate('position', previous);
|
|
484
202
|
}
|
|
485
|
-
/**
|
|
486
|
-
* Which pane is shut, if either.
|
|
487
|
-
*
|
|
488
|
-
* The same accessor pair as `position`, and deliberately a second state
|
|
489
|
-
* rather than a reading of the first. A pane shut by dragging the divider to
|
|
490
|
-
* the edge and a pane shut on purpose look identical in geometry and are not
|
|
491
|
-
* the same thing: only the second knows where to go back to. Kept apart, the
|
|
492
|
-
* open position is simply the one collapsing never overwrote, so restoring
|
|
493
|
-
* needs no memory of its own.
|
|
494
|
-
*
|
|
495
|
-
* It overrides `min` and `max`, which is the point of it. Those keep a sliver
|
|
496
|
-
* of a pane during a drag; this is the thing that says the sliver can go.
|
|
497
|
-
*/
|
|
498
203
|
get collapsed() {
|
|
499
204
|
const held = this.collapsedState;
|
|
500
205
|
if (held === undefined) {
|
|
@@ -504,21 +209,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
504
209
|
}
|
|
505
210
|
set collapsed(next) {
|
|
506
211
|
const previous = this.collapsed;
|
|
507
|
-
/* `none` rather than `undefined`, because the two mean different things
|
|
508
|
-
here and conflating them cost a spec: `undefined` is nobody having
|
|
509
|
-
spoken, which is the seed's cue to answer, so clearing a collapse handed
|
|
510
|
-
it straight back to `default-collapsed` and the pane would not open. */
|
|
511
212
|
this.collapsedState = next ?? 'none';
|
|
512
213
|
this.requestUpdate('collapsed', previous);
|
|
513
214
|
}
|
|
514
|
-
/**
|
|
515
|
-
* Either kind of bound as a percentage of the box.
|
|
516
|
-
*
|
|
517
|
-
* Measured rather than cached: a length is only meaningful against the size
|
|
518
|
-
* the splitter is right now, and the drag that asks is already reading layout.
|
|
519
|
-
* Falls back where there is nothing to measure, which is the server and an
|
|
520
|
-
* element that is not displayed, so `aria-valuemin` is a number either way.
|
|
521
|
-
*/
|
|
522
215
|
toPercent(value, fallback) {
|
|
523
216
|
if (typeof value === 'number') {
|
|
524
217
|
return Number.isFinite(value) ? value : fallback;
|
|
@@ -543,41 +236,16 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
543
236
|
const extent = this.vertical ? box.height : box.width;
|
|
544
237
|
return extent > 0 ? (pixels / extent) * 100 : fallback;
|
|
545
238
|
}
|
|
546
|
-
/** `min` as a percentage, whichever way it was written. */
|
|
547
239
|
get minPercent() {
|
|
548
240
|
return this.toPercent(this.min, 0);
|
|
549
241
|
}
|
|
550
|
-
/** `max` as a percentage, whichever way it was written. */
|
|
551
242
|
get maxPercent() {
|
|
552
243
|
return this.toPercent(this.max, 100);
|
|
553
244
|
}
|
|
554
|
-
/**
|
|
555
|
-
* The position to draw, which is not always the position to report.
|
|
556
|
-
*
|
|
557
|
-
* A controlled splitter applies nothing, so `position` holds still for the
|
|
558
|
-
* whole gesture: drawn from it, the divider stays where it was until the
|
|
559
|
-
* pointer comes up and the consumer answers, jumping to the end rather than
|
|
560
|
-
* following the hand.
|
|
561
|
-
*
|
|
562
|
-
* So a drag draws itself. The number is still not announced until it settles,
|
|
563
|
-
* and the consumer's answer takes over the moment they give one.
|
|
564
|
-
*/
|
|
565
245
|
get showing() {
|
|
566
246
|
return this.reached === undefined ? this.position : this.clamp(this.reached);
|
|
567
247
|
}
|
|
568
|
-
/**
|
|
569
|
-
* Moves the divider, and optionally says so.
|
|
570
|
-
*
|
|
571
|
-
* Public because a corner on a nested splitter has to drive the one it is
|
|
572
|
-
* nested in, and that is the whole of what it needs: the pair are not
|
|
573
|
-
* otherwise coupled and neither reads the other's state. `settled` is false
|
|
574
|
-
* for each step of a drag and true once, at the end, for the reason a drag
|
|
575
|
-
* announces once: only the position it ends at is a preference.
|
|
576
|
-
*/
|
|
577
248
|
moveTo(next, settled = false) {
|
|
578
|
-
/* An unsettled move is a drag in progress, which is what `settled` already
|
|
579
|
-
means, so the nested splitter a corner drives lights up without either of
|
|
580
|
-
them being told about the other's presentation. */
|
|
581
249
|
this.dragging = !settled;
|
|
582
250
|
if (settled) {
|
|
583
251
|
this.settle(next);
|
|
@@ -586,34 +254,12 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
586
254
|
this.move(next);
|
|
587
255
|
}
|
|
588
256
|
}
|
|
589
|
-
/** The collapsed size, held inside the range a percentage can be. */
|
|
590
257
|
get sliver() {
|
|
591
258
|
return Math.min(100, Math.max(0, this.collapsedSize));
|
|
592
259
|
}
|
|
593
|
-
/**
|
|
594
|
-
* Whether the divider is against an edge with nothing beside it.
|
|
595
|
-
*
|
|
596
|
-
* Geometry, not state. Asking whether the pane is `collapsed` is the wrong
|
|
597
|
-
* question: a divider driven to the edge by a consumer setting `position` is
|
|
598
|
-
* just as flush and would still get a line drawn. What the stylesheet needs
|
|
599
|
-
* to know is whether there is a pane on both sides, which is a fact about
|
|
600
|
-
* where the divider is however it got there.
|
|
601
|
-
*
|
|
602
|
-
* Still not the same question as `collapsed`: a pane shut to a sliver has a
|
|
603
|
-
* boundary worth drawing, and one shut to nothing has the container's own edge
|
|
604
|
-
* there instead.
|
|
605
|
-
*/
|
|
606
260
|
get flush() {
|
|
607
261
|
return this.sliver === 0 && this.shutAt(this.position) !== undefined;
|
|
608
262
|
}
|
|
609
|
-
/**
|
|
610
|
-
* The collapse a settled position amounts to, if any, so a pane dragged shut
|
|
611
|
-
* and a pane shut by a consumer's button are the same state.
|
|
612
|
-
*
|
|
613
|
-
* No separate threshold. Landing on the collapsed size *is* the gesture, and
|
|
614
|
-
* `clamp` puts every overshoot exactly on the bound, so the default of zero
|
|
615
|
-
* latches on any drag that runs off the end.
|
|
616
|
-
*/
|
|
617
263
|
shutAt(position) {
|
|
618
264
|
const size = this.sliver;
|
|
619
265
|
if (position <= size) {
|
|
@@ -621,20 +267,11 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
621
267
|
}
|
|
622
268
|
return position >= 100 - size ? 'end' : undefined;
|
|
623
269
|
}
|
|
624
|
-
/** Pulled between the bounds, which are the ends unless a consumer moved them. */
|
|
625
270
|
clamp(next) {
|
|
626
271
|
const min = Math.max(0, Math.min(100, this.minPercent));
|
|
627
272
|
const max = Math.max(min, Math.min(100, this.maxPercent));
|
|
628
273
|
return Math.min(max, Math.max(min, Number.isFinite(next) ? next : min));
|
|
629
274
|
}
|
|
630
|
-
/**
|
|
631
|
-
* Starts or stops watching, according to what the axis was asked to be.
|
|
632
|
-
*
|
|
633
|
-
* Called from `firstUpdated` and from `updated`, so a consumer switching to
|
|
634
|
-
* `auto` after mount is observed and one switching away stops being: an
|
|
635
|
-
* observer left running would keep writing `stacked` that nothing reads, and
|
|
636
|
-
* a splitter told `horizontal` has been given its answer already.
|
|
637
|
-
*/
|
|
638
275
|
watchWidth() {
|
|
639
276
|
if (this.orientation !== 'auto') {
|
|
640
277
|
this.sizes?.disconnect();
|
|
@@ -646,58 +283,26 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
646
283
|
}
|
|
647
284
|
this.sizes = new ResizeObserver(([entry]) => {
|
|
648
285
|
const width = entry?.borderBoxSize?.[0]?.inlineSize ?? entry?.contentRect.width ?? 0;
|
|
649
|
-
/* Zero while the element is `display: none` or not yet laid out, and a
|
|
650
|
-
zero is not a narrow splitter: taking it would stack every instance
|
|
651
|
-
that starts hidden and never turn it back. */
|
|
652
286
|
if (width > 0) {
|
|
653
287
|
this.stacked = width < STACK_BELOW;
|
|
654
288
|
}
|
|
655
289
|
});
|
|
656
290
|
this.sizes.observe(this);
|
|
657
291
|
}
|
|
658
|
-
/**
|
|
659
|
-
* Public because a nested splitter compares axes with the one it is in.
|
|
660
|
-
*
|
|
661
|
-
* The one place the axis is decided, which is why `auto` costs a single line
|
|
662
|
-
* here rather than a pass over seventeen readers: the drag, the arrow keys and
|
|
663
|
-
* `aria-orientation` all ask this and all turn together.
|
|
664
|
-
*/
|
|
665
292
|
get vertical() {
|
|
666
293
|
return this.orientation === 'vertical' || (this.orientation === 'auto' && this.stacked);
|
|
667
294
|
}
|
|
668
|
-
/**
|
|
669
|
-
* Asks to move, and applies it where the position is this element's to move.
|
|
670
|
-
*
|
|
671
|
-
* Not announced per step: `helix-before-resize` is the settling, below. A drag that
|
|
672
|
-
* announced every position would be asking a consumer to record a hundred
|
|
673
|
-
* decisions to keep one.
|
|
674
|
-
*/
|
|
675
295
|
move(next) {
|
|
676
296
|
const wanted = this.clamp(next);
|
|
677
|
-
/* Where the gesture has reached, recorded before anything decides whether to
|
|
678
|
-
apply it. A controlled splitter applies nothing, so its `position` is
|
|
679
|
-
whatever the consumer last said, and settling at *that* would announce
|
|
680
|
-
the number it already had: the drag ends where it began and the divider
|
|
681
|
-
never moves. Only the pointer is exposed to it, the keyboard computing
|
|
682
|
-
its next position rather than reading it back. */
|
|
683
297
|
this.reached = wanted;
|
|
684
298
|
if (wanted === this.position) {
|
|
685
299
|
return;
|
|
686
300
|
}
|
|
687
301
|
const previous = this.position;
|
|
688
302
|
this.positionState = wanted;
|
|
689
|
-
/* A divider that is moving is a pane that is opening, and a shut one is
|
|
690
|
-
still draggable precisely so it can be. Silent, like the move itself:
|
|
691
|
-
the settling below is what announces either. */
|
|
692
303
|
this.collapsedState = 'none';
|
|
693
304
|
this.requestUpdate('position', previous);
|
|
694
305
|
}
|
|
695
|
-
/**
|
|
696
|
-
* Says the arrangement has settled, so a consumer can keep it.
|
|
697
|
-
*
|
|
698
|
-
* Through the audited path, so a consumer who owns `position` is told what was
|
|
699
|
-
* asked for and decides, and one who does not has already had it applied.
|
|
700
|
-
*/
|
|
701
306
|
settle(next) {
|
|
702
307
|
const wanted = this.clamp(next);
|
|
703
308
|
const shut = this.shutAt(wanted);
|
|
@@ -712,14 +317,9 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
712
317
|
next: wanted,
|
|
713
318
|
});
|
|
714
319
|
if (shut !== this.collapsed) {
|
|
715
|
-
/* Two values from one gesture, so two announcements. `ChangeDetail`
|
|
716
|
-
carries a name for exactly this, and a consumer listening for one of
|
|
717
|
-
them is not made to care about the other. */
|
|
718
320
|
requestChange({
|
|
719
321
|
apply: (value) => {
|
|
720
322
|
const previous = this.collapsed;
|
|
721
|
-
// Back to where the gesture started, so opening it again is a return
|
|
722
|
-
// rather than a jump to the edge it was dragged against.
|
|
723
323
|
if (value !== '' && this.opened !== undefined) {
|
|
724
324
|
this.positionState = this.opened;
|
|
725
325
|
}
|
|
@@ -732,30 +332,11 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
732
332
|
});
|
|
733
333
|
}
|
|
734
334
|
}
|
|
735
|
-
/** Puts the divider back where the markup put it. */
|
|
736
335
|
reset() {
|
|
737
|
-
/* `default-position`, never the position captured at first render. Those
|
|
738
|
-
are the same only when nobody owns the position: a consumer who does owns
|
|
739
|
-
what it was on that first frame too, and theirs can be a number they
|
|
740
|
-
cannot compute yet, as the sandbox's is, sizing its panel in pixels with
|
|
741
|
-
no box to divide by until it has rendered once. Home would then be
|
|
742
|
-
"panel closed" and a double click would shut the panel rather than
|
|
743
|
-
restore it.
|
|
744
|
-
|
|
745
|
-
Declared rather than remembered, so it is a value a consumer can state
|
|
746
|
-
and change, and it needs no capture at all. */
|
|
747
336
|
const home = this.clamp(this.defaultPosition);
|
|
748
337
|
this.move(home);
|
|
749
338
|
this.settle(home);
|
|
750
339
|
}
|
|
751
|
-
/**
|
|
752
|
-
* Records that a nested splitter crosses this one at `at` percent along it.
|
|
753
|
-
*
|
|
754
|
-
* Public because it is the whole of the channel between the two, and it runs
|
|
755
|
-
* one way: the child tells the parent where it is, and the parent draws the
|
|
756
|
-
* corner and drives them both. Nothing is read back, so neither has to know
|
|
757
|
-
* anything about the other's state.
|
|
758
|
-
*/
|
|
759
340
|
cross(by, at) {
|
|
760
341
|
const found = this.crossings.find((crossing) => crossing.by === by);
|
|
761
342
|
if (found) {
|
|
@@ -769,7 +350,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
769
350
|
}
|
|
770
351
|
this.requestUpdate();
|
|
771
352
|
}
|
|
772
|
-
/** Forgets a nested splitter that has left the tree. See `cross`. */
|
|
773
353
|
uncross(by) {
|
|
774
354
|
const at = this.crossings.findIndex((crossing) => crossing.by === by);
|
|
775
355
|
if (at === -1) {
|
|
@@ -778,63 +358,22 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
778
358
|
this.crossings.splice(at, 1);
|
|
779
359
|
this.requestUpdate();
|
|
780
360
|
}
|
|
781
|
-
/**
|
|
782
|
-
* Announces this splitter to the one it is nested in, if they cross.
|
|
783
|
-
*
|
|
784
|
-
* Client only. The server has no tree to walk, and a parent that has not
|
|
785
|
-
* upgraded has no `orientation` to compare against, so this waits for
|
|
786
|
-
* `firstUpdated`. That is what the corner is allowed to be: an affordance
|
|
787
|
-
* rather than content, absent from the served markup and appearing at
|
|
788
|
-
* hydration, with two working dividers in the meantime.
|
|
789
|
-
*
|
|
790
|
-
* `parentElement` first, so a splitter does not find itself.
|
|
791
|
-
*
|
|
792
|
-
* **By identity rather than by tag, which is what `closestInstance` exists
|
|
793
|
-
* for.** A selector like `'helix-splitter, helix-headless-splitter'` names a
|
|
794
|
-
* styled tag from a headless package and misses any third tag, which is
|
|
795
|
-
* exactly what subclassing a headless element produces: the lookup returns
|
|
796
|
-
* null, the nested splitter gets no corner, and nothing reports an error.
|
|
797
|
-
*
|
|
798
|
-
* It also crosses shadow boundaries, which `closest` does not: a splitter
|
|
799
|
-
* slotted into another component's shadow root has a `parentNode` chain
|
|
800
|
-
* ending at a `ShadowRoot`, and nesting through a slot is the ordinary case
|
|
801
|
-
* for anything composable.
|
|
802
|
-
*/
|
|
803
361
|
findOuter() {
|
|
804
362
|
const outer = closestInstance(this.parentElement, HelixHeadlessSplitter_1);
|
|
805
363
|
if (!outer || outer === this || outer.vertical === this.vertical) {
|
|
806
|
-
// Parallel dividers never meet, so there is no corner to take hold of.
|
|
807
364
|
return;
|
|
808
365
|
}
|
|
809
366
|
this.outer = outer;
|
|
810
367
|
this.crossedAt = this.position;
|
|
811
368
|
outer.cross(this, this.position);
|
|
812
369
|
}
|
|
813
|
-
/**
|
|
814
|
-
* Keeps a gesture on the corner to itself.
|
|
815
|
-
*
|
|
816
|
-
* The stop is not redundant with the corner being a sibling of the separator.
|
|
817
|
-
* Should a press on the corner ever reach the separator, the separator calls
|
|
818
|
-
* `setPointerCapture`, which retargets every later pointer event to *it*: the
|
|
819
|
-
* corner's `pointerup` never arrives, its origin is never cleared, and it
|
|
820
|
-
* goes on resizing with no button held. Worse than moving twice.
|
|
821
|
-
*
|
|
822
|
-
* `preventDefault` is needed either way: a drag across a pane would otherwise
|
|
823
|
-
* select the text under it.
|
|
824
|
-
*/
|
|
825
370
|
leaveSeparator(event) {
|
|
826
371
|
event.stopPropagation();
|
|
827
372
|
event.preventDefault();
|
|
828
373
|
}
|
|
829
374
|
connectedCallback() {
|
|
830
375
|
super.connectedCallback();
|
|
831
|
-
// Only on a re-connect. The first pass waits for `firstUpdated`, which is
|
|
832
|
-
// where a parent is guaranteed to have upgraded.
|
|
833
376
|
if (this.hasUpdated) {
|
|
834
|
-
/* The width watch too, and it belongs here for a plainer reason than the
|
|
835
|
-
one above: `disconnectedCallback` drops the observer on every move, so
|
|
836
|
-
a splitter reordered in the DOM would stop watching its own width and
|
|
837
|
-
`auto` would freeze on whatever it last saw. */
|
|
838
377
|
this.watchWidth();
|
|
839
378
|
this.findOuter();
|
|
840
379
|
}
|
|
@@ -852,41 +391,23 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
852
391
|
this.watchWidth();
|
|
853
392
|
this.findOuter();
|
|
854
393
|
}
|
|
855
|
-
/** Keeps the corner in step while this divider moves under it. */
|
|
856
394
|
updated(changed) {
|
|
857
395
|
super.updated(changed);
|
|
858
396
|
if (changed.has('orientation')) {
|
|
859
397
|
this.watchWidth();
|
|
860
398
|
}
|
|
861
|
-
/* The resolved collapse, mirrored for a consumer's own rules: a shut pane
|
|
862
|
-
usually wants its contents to stop being read as well as stop being seen.
|
|
863
|
-
`data-` and not `collapsed`, so a serialized host cannot be re-parsed with
|
|
864
|
-
it declared, which is the trap `value` records. It carries which pane
|
|
865
|
-
rather than a bare boolean, and that is still a state and not a payload:
|
|
866
|
-
two names for the two sides, nothing of the consumer's in it. */
|
|
867
399
|
if (this.collapsed) {
|
|
868
400
|
this.dataset.collapsed = this.collapsed;
|
|
869
401
|
}
|
|
870
402
|
else {
|
|
871
403
|
delete this.dataset.collapsed;
|
|
872
404
|
}
|
|
873
|
-
/* The resolved axis, mirrored for the same reason and on the same terms: a
|
|
874
|
-
stylesheet selecting `[orientation="vertical"]` cannot see an `auto` that
|
|
875
|
-
has turned, so the layout would stay side by side while the drag, the keys
|
|
876
|
-
and `aria-orientation` had all gone the other way. That is the lie the
|
|
877
|
-
policy warns a CSS-only version tells, arrived at from the other side.
|
|
878
|
-
|
|
879
|
-
Only where `auto` produced it. An explicit `vertical` already carries an
|
|
880
|
-
attribute saying so, and writing a second would put two sources of one
|
|
881
|
-
truth on the host and a flash of the wrong axis before hydration. */
|
|
882
405
|
if (this.orientation === 'auto' && this.stacked) {
|
|
883
406
|
this.dataset.axis = 'vertical';
|
|
884
407
|
}
|
|
885
408
|
else {
|
|
886
409
|
delete this.dataset.axis;
|
|
887
410
|
}
|
|
888
|
-
/* `showing` rather than `position`, so a corner drawn on the splitter above
|
|
889
|
-
follows this divider through a drag instead of catching up at the end. */
|
|
890
411
|
if (this.outer && this.showing !== this.crossedAt) {
|
|
891
412
|
this.crossedAt = this.showing;
|
|
892
413
|
this.outer.cross(this, this.showing);
|
|
@@ -897,16 +418,6 @@ let HelixHeadlessSplitter = HelixHeadlessSplitter_1 = class HelixHeadlessSplitte
|
|
|
897
418
|
}
|
|
898
419
|
render() {
|
|
899
420
|
const position = this.showing;
|
|
900
|
-
/* The panes are marked while the divider moves, so the styled tier can take
|
|
901
|
-
them out of the pointer's way.
|
|
902
|
-
A pane holds whatever a consumer put in it, and an `<iframe>` is the case
|
|
903
|
-
that breaks: a cross-origin frame is its own hit-testing world, and a drag
|
|
904
|
-
whose path crosses one can be lost to it. The sandbox is exactly this, a
|
|
905
|
-
sandboxed preview sitting against the panel divider, so the drag that
|
|
906
|
-
makes the panel bigger is the drag that goes straight over it.
|
|
907
|
-
On the panes rather than on the host, which the `dragging` state above
|
|
908
|
-
records as a rule: a `data-` attribute here is one this element owns, and
|
|
909
|
-
one on the host is one a React consumer's server markup never had. */
|
|
910
421
|
return html `<div
|
|
911
422
|
part="start"
|
|
912
423
|
?data-dragging=${this.dragging}
|