@threadlabs/looma 0.23.7 → 0.23.8
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/components/ui-disclosure/ui-disclosure.html +71 -19
- package/components/ui-disclosure/ui-disclosure.js +50 -4
- package/components/ui-nav-item/ui-nav-item.html +16 -11
- package/components/ui-sidebar/ui-sidebar.html +36 -7
- package/components/ui-sidebar/ui-sidebar.js +6 -0
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/styles/ui-disclosure.css +65 -17
- package/styles/ui-nav-item.css +14 -11
- package/styles/ui-sidebar.css +34 -6
- package/tokens.css +15 -0
- package/vanilla/UiDisclosure.d.ts +2 -0
- package/vanilla/UiDisclosure.js +33 -18
- package/vanilla/UiNavItem.js +42 -18
- package/vanilla/UiSidebar.js +19 -16
- package/vue/UiDisclosure.d.ts +24 -4
- package/vue/UiDisclosure.vue +130 -22
- package/vue/UiNavItem.d.ts +8 -4
- package/vue/UiNavItem.vue +17 -12
- package/vue/UiSection.d.ts +1 -1
- package/vue/UiSidebar.vue +42 -8
- package/vue/chunks/UiDisclosure.js +70 -14
- package/vue/chunks/UiNavItem.js +14 -10
- package/vue/chunks/UiSidebar.js +12 -7
- package/vue/components.css +178 -93
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
<defs>
|
|
3
3
|
<prop name="density" type="keyword" values="comfortable, compact" default="comfortable"
|
|
4
4
|
>Compact trades padding and type size for rows that fit more on screen.</prop>
|
|
5
|
+
<prop name="variant" type="keyword" values="plain, navigation" default="plain"
|
|
6
|
+
>Navigation shares icon columns, spacing, type, hover, and corners with Nav Item. Plain retains the standard disclosure treatment.</prop>
|
|
7
|
+
<prop name="requiredOpen" type="boolean" default="false"
|
|
8
|
+
>In a named exclusive group, keeps at least one member open. Clicking the last open trigger does not close it; opening another member transfers the open
|
|
9
|
+
state.</prop>
|
|
5
10
|
<prop name="fill" type="boolean" default="false"
|
|
6
11
|
>In a column with a bounded height, an open disclosure fills the remaining space and keeps its trigger visible. The content slot can contain a Scroll Area.
|
|
7
12
|
Closed items keep only their trigger height.</prop>
|
|
@@ -38,10 +43,11 @@
|
|
|
38
43
|
</event>
|
|
39
44
|
</defs>
|
|
40
45
|
<div>
|
|
41
|
-
<div class="heading" from:role="headingRole" from:aria-level="headingAriaLevel">
|
|
46
|
+
<div class="heading" $ref="heading" from:role="headingRole" from:aria-level="headingAriaLevel">
|
|
42
47
|
<button class="trigger" type="button" $ref="trigger" from:aria-controls="contentId" from:aria-expanded="internalOpen" from:disabled="disabled">
|
|
48
|
+
<span class="leading"><slot name="leading"></slot></span>
|
|
43
49
|
<span class="summary"><slot name="summary"><template $value="summary"></template></slot></span>
|
|
44
|
-
<slot name="indicator"><span class="chevron"
|
|
50
|
+
<span class="indicator" aria-hidden="true"><slot name="indicator"><span class="chevron"></span></slot></span>
|
|
45
51
|
</button>
|
|
46
52
|
</div>
|
|
47
53
|
<div class="panel" $ref="panel" from:id="contentId"><div class="panel-inner"><slot></slot></div></div>
|
|
@@ -90,8 +96,7 @@
|
|
|
90
96
|
}
|
|
91
97
|
|
|
92
98
|
.trigger {
|
|
93
|
-
display:
|
|
94
|
-
grid-template-columns: minmax(0, 1fr) auto;
|
|
99
|
+
display: flex;
|
|
95
100
|
gap: var(--ui-space-3);
|
|
96
101
|
align-items: center;
|
|
97
102
|
inline-size: 100%;
|
|
@@ -121,19 +126,40 @@
|
|
|
121
126
|
cursor: not-allowed;
|
|
122
127
|
}
|
|
123
128
|
|
|
129
|
+
.summary {
|
|
130
|
+
flex: 1;
|
|
131
|
+
min-inline-size: 0;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.leading,
|
|
135
|
+
.indicator {
|
|
136
|
+
display: inline-flex;
|
|
137
|
+
align-items: center;
|
|
138
|
+
justify-content: center;
|
|
139
|
+
flex: none;
|
|
140
|
+
}
|
|
141
|
+
.leading:empty {
|
|
142
|
+
display: none;
|
|
143
|
+
}
|
|
144
|
+
.indicator {
|
|
145
|
+
transition: transform var(--ui-motion-feedback) var(--ui-motion-feedback-ease);
|
|
146
|
+
}
|
|
147
|
+
:host-state([internalOpen]) .indicator {
|
|
148
|
+
transform: rotate(180deg);
|
|
149
|
+
}
|
|
150
|
+
|
|
124
151
|
.chevron {
|
|
125
152
|
inline-size: 0.5rem;
|
|
126
153
|
block-size: 0.5rem;
|
|
127
154
|
border-inline-end: 2px solid currentColor;
|
|
128
155
|
border-block-end: 2px solid currentColor;
|
|
129
156
|
transform: rotate(45deg) translate(-1px, -1px);
|
|
130
|
-
transition: transform var(--ui-motion-fast) var(--ui-motion-ease);
|
|
131
157
|
}
|
|
132
158
|
|
|
133
159
|
.panel {
|
|
134
160
|
display: grid;
|
|
135
161
|
grid-template-rows: 0fr;
|
|
136
|
-
transition: grid-template-rows var(--ui-motion-
|
|
162
|
+
transition: grid-template-rows var(--ui-motion-reveal) var(--ui-motion-reveal-ease), content-visibility var(--ui-motion-reveal) allow-discrete;
|
|
137
163
|
}
|
|
138
164
|
|
|
139
165
|
.panel-inner {
|
|
@@ -148,22 +174,20 @@
|
|
|
148
174
|
:host-state([internalOpen]) .panel-inner {
|
|
149
175
|
padding-block: 0 var(--ui-space-3);
|
|
150
176
|
}
|
|
151
|
-
:host-state([internalOpen]) .chevron {
|
|
152
|
-
transform: rotate(225deg) translate(-1px, -1px);
|
|
153
|
-
}
|
|
154
177
|
|
|
155
178
|
/* A bounded accordion owns one flexible body, so its trigger never scrolls away. The
|
|
156
179
|
* normal content-sized layout and its animation remain unchanged when fill is omitted. */
|
|
157
180
|
:host-state([fill]) {
|
|
158
181
|
display: flex;
|
|
159
182
|
flex-direction: column;
|
|
160
|
-
flex:
|
|
161
|
-
min-block-size:
|
|
183
|
+
flex: 0 0 var(--_ui-disclosure-heading-size, var(--_trigger-min-block-size));
|
|
184
|
+
min-block-size: calc(var(--_ui-disclosure-heading-size, var(--_trigger-min-block-size)) + var(--ui-border-width));
|
|
185
|
+
transition: flex-grow var(--ui-motion-layout) var(--ui-motion-layout-ease);
|
|
162
186
|
border-block-end: 0;
|
|
163
187
|
border-block-start: var(--ui-border-width) solid var(--ui-border-divider);
|
|
164
188
|
}
|
|
165
189
|
:host-state([fill][internalOpen]) {
|
|
166
|
-
flex: 1;
|
|
190
|
+
flex-grow: 1;
|
|
167
191
|
}
|
|
168
192
|
:host-state([fill]) .heading {
|
|
169
193
|
flex: none;
|
|
@@ -172,14 +196,8 @@
|
|
|
172
196
|
border-radius: 0;
|
|
173
197
|
}
|
|
174
198
|
:host-state([fill]) .panel {
|
|
175
|
-
display: flex;
|
|
176
199
|
flex: 1;
|
|
177
200
|
min-block-size: 0;
|
|
178
|
-
transition: none;
|
|
179
|
-
}
|
|
180
|
-
:host-state([fill]) .panel[hidden] {
|
|
181
|
-
flex: none;
|
|
182
|
-
block-size: 0;
|
|
183
201
|
}
|
|
184
202
|
:host-state([fill]) .panel-inner {
|
|
185
203
|
display: flex;
|
|
@@ -189,9 +207,43 @@
|
|
|
189
207
|
padding: 0;
|
|
190
208
|
}
|
|
191
209
|
|
|
210
|
+
:host-state([variant="navigation"]) .trigger {
|
|
211
|
+
gap: var(--ui-nav-gap);
|
|
212
|
+
padding-inline: var(--ui-nav-padding-inline);
|
|
213
|
+
min-block-size: var(--ui-nav-min-block-size);
|
|
214
|
+
padding-block: var(--ui-nav-padding-block);
|
|
215
|
+
border-radius: var(--ui-selection-radius);
|
|
216
|
+
font-size: var(--ui-font-size-md);
|
|
217
|
+
font-weight: var(--ui-font-normal);
|
|
218
|
+
color: var(--ui-text-secondary);
|
|
219
|
+
}
|
|
220
|
+
:host-state([variant="navigation"][density="compact"]) .trigger {
|
|
221
|
+
padding-inline: var(--ui-nav-compact-padding-inline);
|
|
222
|
+
padding-block: var(--ui-nav-compact-padding-block);
|
|
223
|
+
min-block-size: var(--ui-nav-compact-min-block-size);
|
|
224
|
+
font-size: var(--ui-font-size-sm);
|
|
225
|
+
}
|
|
226
|
+
:host-state([variant="navigation"]) .trigger:hover:not(:disabled) {
|
|
227
|
+
background: var(--ui-nav-hover-surface);
|
|
228
|
+
color: var(--ui-text-primary);
|
|
229
|
+
}
|
|
230
|
+
:host-state([variant="navigation"][internalOpen]) .trigger {
|
|
231
|
+
color: var(--ui-selection-text);
|
|
232
|
+
}
|
|
233
|
+
:host-state([variant="navigation"]) :is(.leading, .indicator) {
|
|
234
|
+
inline-size: var(--ui-nav-icon-size);
|
|
235
|
+
font-size: var(--ui-nav-icon-size);
|
|
236
|
+
}
|
|
237
|
+
@media (pointer: coarse) {
|
|
238
|
+
:host-state([variant="navigation"][density="compact"]) .trigger {
|
|
239
|
+
min-block-size: var(--ui-control-min-block-size);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
192
243
|
@media (prefers-reduced-motion: reduce) {
|
|
244
|
+
:host-state([fill]),
|
|
193
245
|
.panel,
|
|
194
|
-
.
|
|
246
|
+
.indicator {
|
|
195
247
|
transition: none;
|
|
196
248
|
}
|
|
197
249
|
}
|
|
@@ -2,6 +2,7 @@ import { trackTrigger } from "../shared/trigger.js";
|
|
|
2
2
|
|
|
3
3
|
let disclosures = 0;
|
|
4
4
|
const groups = new WeakMap();
|
|
5
|
+
const pendingGroups = new WeakSet();
|
|
5
6
|
|
|
6
7
|
function groupFor(document, name) {
|
|
7
8
|
let byName = groups.get(document);
|
|
@@ -11,6 +12,27 @@ function groupFor(document, name) {
|
|
|
11
12
|
return members;
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
function ensureOpen(members) {
|
|
16
|
+
if (pendingGroups.has(members)) return;
|
|
17
|
+
pendingGroups.add(members);
|
|
18
|
+
queueMicrotask(() => {
|
|
19
|
+
pendingGroups.delete(members);
|
|
20
|
+
const current = [...members];
|
|
21
|
+
if (!current.some(member => member.required()) || current.some(member => member.isOpen())) return;
|
|
22
|
+
const candidate = current.find(member => !member.disabled()) || current[0];
|
|
23
|
+
candidate?.open();
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function leaveGroup(document, name, member) {
|
|
28
|
+
const byName = groups.get(document);
|
|
29
|
+
const members = byName?.get(name);
|
|
30
|
+
if (!members) return;
|
|
31
|
+
members.delete(member);
|
|
32
|
+
if (!members.size) byName.delete(name);
|
|
33
|
+
else ensureOpen(members);
|
|
34
|
+
}
|
|
35
|
+
|
|
14
36
|
// `open` sets the disclosure initially and whenever it changes; the trigger toggles the state.
|
|
15
37
|
export default function controller(host) {
|
|
16
38
|
const document = host.element.ownerDocument;
|
|
@@ -19,7 +41,17 @@ export default function controller(host) {
|
|
|
19
41
|
let external = host.props.open.value;
|
|
20
42
|
host.state.internalOpen = Boolean(external);
|
|
21
43
|
let groupName = "";
|
|
44
|
+
let disposed = false;
|
|
22
45
|
const member = {
|
|
46
|
+
isOpen: () => Boolean(host.state.internalOpen),
|
|
47
|
+
required: () => Boolean(host.props.requiredOpen.value),
|
|
48
|
+
disabled: () => Boolean(host.props.disabled.value),
|
|
49
|
+
open() {
|
|
50
|
+
if (disposed || host.state.internalOpen) return;
|
|
51
|
+
host.state.internalOpen = true;
|
|
52
|
+
syncGroup("programmatic", "programmatic");
|
|
53
|
+
host.dispatch("open", { open: true, reason: "programmatic", trigger: "programmatic" });
|
|
54
|
+
},
|
|
23
55
|
close(reason, how) {
|
|
24
56
|
if (!host.state.internalOpen) return;
|
|
25
57
|
host.state.internalOpen = false;
|
|
@@ -29,18 +61,24 @@ export default function controller(host) {
|
|
|
29
61
|
const syncGroup = (reason, how) => {
|
|
30
62
|
const name = String(host.props.name.value || "");
|
|
31
63
|
if (name !== groupName) {
|
|
32
|
-
if (groupName)
|
|
64
|
+
if (groupName) leaveGroup(document, groupName, member);
|
|
33
65
|
groupName = name;
|
|
34
66
|
if (groupName) groupFor(document, groupName).add(member);
|
|
35
67
|
}
|
|
36
68
|
if (name && host.state.internalOpen) {
|
|
37
69
|
for (const peer of groupFor(document, name)) if (peer !== member) peer.close(reason, how);
|
|
38
70
|
}
|
|
71
|
+
if (name) ensureOpen(groupFor(document, name));
|
|
72
|
+
};
|
|
73
|
+
const mayClose = () => {
|
|
74
|
+
if (!groupName) return true;
|
|
75
|
+
const members = [...groupFor(document, groupName)];
|
|
76
|
+
return !members.some(peer => peer.required()) || members.some(peer => peer !== member && peer.isOpen());
|
|
39
77
|
};
|
|
40
78
|
const stop = host.effect(() => {
|
|
41
79
|
if (host.props.open.value !== external) {
|
|
42
80
|
external = host.props.open.value;
|
|
43
|
-
host.state.internalOpen = Boolean(external);
|
|
81
|
+
if (external || mayClose()) host.state.internalOpen = Boolean(external);
|
|
44
82
|
}
|
|
45
83
|
const level = Number(host.props.headingLevel.value);
|
|
46
84
|
host.state.headingRole = Number.isInteger(level) && level >= 2 && level <= 6 ? "heading" : null;
|
|
@@ -52,23 +90,31 @@ export default function controller(host) {
|
|
|
52
90
|
const onClick = () => {
|
|
53
91
|
if (host.props.disabled.value) return;
|
|
54
92
|
const open = !host.state.internalOpen;
|
|
93
|
+
if (!open && !mayClose()) return;
|
|
55
94
|
const how = trigger();
|
|
56
95
|
host.state.internalOpen = open;
|
|
57
96
|
if (open) syncGroup("action", how);
|
|
58
97
|
host.dispatch(open ? "open" : "close", { open, reason: "action", trigger: how });
|
|
59
98
|
};
|
|
60
99
|
const onBeforematch = () => queueMicrotask(() => {
|
|
61
|
-
if (host.state.internalOpen) return;
|
|
100
|
+
if (disposed || host.state.internalOpen) return;
|
|
62
101
|
host.state.internalOpen = true;
|
|
63
102
|
syncGroup("programmatic", "programmatic");
|
|
64
103
|
host.dispatch("open", { open: true, reason: "programmatic", trigger: "programmatic" });
|
|
65
104
|
});
|
|
66
105
|
host.refs.trigger.addEventListener("click", onClick);
|
|
67
106
|
host.refs.panel.addEventListener("beforematch", onBeforematch);
|
|
107
|
+
const measureHeading = () => host.element.style.setProperty("--_ui-disclosure-heading-size", `${host.refs.heading.getBoundingClientRect().height}px`);
|
|
108
|
+
const ResizeObserver = document.defaultView?.ResizeObserver;
|
|
109
|
+
const headingObserver = ResizeObserver ? new ResizeObserver(measureHeading) : null;
|
|
110
|
+
headingObserver?.observe(host.refs.heading);
|
|
111
|
+
measureHeading();
|
|
68
112
|
return () => {
|
|
113
|
+
disposed = true;
|
|
114
|
+
headingObserver?.disconnect();
|
|
69
115
|
stop();
|
|
70
116
|
stopTracking();
|
|
71
|
-
if (groupName)
|
|
117
|
+
if (groupName) leaveGroup(document, groupName, member);
|
|
72
118
|
host.refs.trigger.removeEventListener("click", onClick);
|
|
73
119
|
host.refs.panel.removeEventListener("beforematch", onBeforematch);
|
|
74
120
|
};
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
<span class="label"><slot></slot></span>
|
|
35
35
|
<span class="description"><slot name="description"></slot></span>
|
|
36
36
|
</span>
|
|
37
|
+
<span class="trailing"><slot name="trailing"></slot></span>
|
|
37
38
|
</a>
|
|
38
39
|
<button $else type="button" from:aria-current="{ true: 'page', false: null, page: 'page', step: 'step', location: 'location' }[format('%s', current)]">
|
|
39
40
|
<span class="indicator" aria-hidden="true"></span>
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
<span class="label"><slot></slot></span>
|
|
43
44
|
<span class="description"><slot name="description"></slot></span>
|
|
44
45
|
</span>
|
|
46
|
+
<span class="trailing"><slot name="trailing"></slot></span>
|
|
45
47
|
</button>
|
|
46
48
|
</template>
|
|
47
49
|
<style>
|
|
@@ -74,13 +76,13 @@
|
|
|
74
76
|
box-sizing: border-box;
|
|
75
77
|
display: flex;
|
|
76
78
|
align-items: center;
|
|
77
|
-
gap: var(--ui-
|
|
79
|
+
gap: var(--ui-nav-gap);
|
|
78
80
|
inline-size: var(--ui-nav-item-inline-size, 100%);
|
|
79
81
|
min-inline-size: 0;
|
|
80
|
-
min-block-size: var(--ui-
|
|
82
|
+
min-block-size: var(--ui-nav-min-block-size);
|
|
81
83
|
margin: 0;
|
|
82
|
-
padding-block: var(--ui-
|
|
83
|
-
padding-inline: var(--ui-
|
|
84
|
+
padding-block: var(--ui-nav-padding-block);
|
|
85
|
+
padding-inline: var(--ui-nav-padding-inline);
|
|
84
86
|
border: 0;
|
|
85
87
|
border-radius: var(--ui-selection-radius);
|
|
86
88
|
background: transparent;
|
|
@@ -95,7 +97,7 @@
|
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
:host:hover:not([aria-current]) {
|
|
98
|
-
background: var(--ui-
|
|
100
|
+
background: var(--ui-nav-hover-surface);
|
|
99
101
|
color: var(--ui-text-primary);
|
|
100
102
|
}
|
|
101
103
|
|
|
@@ -144,12 +146,15 @@
|
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
.leading
|
|
149
|
+
.leading,
|
|
150
|
+
.trailing {
|
|
148
151
|
display: inline-flex;
|
|
149
152
|
flex: none;
|
|
150
153
|
align-items: center;
|
|
154
|
+
justify-content: center;
|
|
155
|
+
inline-size: var(--ui-nav-icon-size);
|
|
151
156
|
color: inherit;
|
|
152
|
-
font-size: var(--ui-
|
|
157
|
+
font-size: var(--ui-nav-icon-size);
|
|
153
158
|
}
|
|
154
159
|
|
|
155
160
|
.content {
|
|
@@ -182,11 +187,10 @@
|
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
:host-state([density="compact"]) {
|
|
185
|
-
min-block-size: var(--ui-
|
|
186
|
-
padding-block: var(--ui-
|
|
187
|
-
padding-inline: var(--ui-
|
|
190
|
+
min-block-size: var(--ui-nav-compact-min-block-size);
|
|
191
|
+
padding-block: var(--ui-nav-compact-padding-block);
|
|
192
|
+
padding-inline: var(--ui-nav-compact-padding-inline);
|
|
188
193
|
}
|
|
189
|
-
:host-state([density="compact"]) .leading,
|
|
190
194
|
:host-state([density="compact"]) .label {
|
|
191
195
|
font-size: var(--ui-font-size-sm);
|
|
192
196
|
}
|
|
@@ -199,6 +203,7 @@
|
|
|
199
203
|
|
|
200
204
|
/* An unused region takes no space. */
|
|
201
205
|
.leading:empty,
|
|
206
|
+
.trailing:empty,
|
|
202
207
|
.description:empty {
|
|
203
208
|
display: none;
|
|
204
209
|
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
>Below this viewport width (40rem, 48rem, 64rem) the sidebar becomes an off-canvas drawer, closed until toggled.</prop>
|
|
13
13
|
<state type="boolean" name="internalCollapsed" value="false"></state>
|
|
14
14
|
<state type="boolean" name="drawer" value="false"></state>
|
|
15
|
+
<state type="boolean" name="resizing" value="false"></state>
|
|
15
16
|
<event name="resize" type="object">
|
|
16
17
|
<prop name="width" required type="number"></prop>
|
|
17
18
|
<prop name="trigger" required type="keyword" values="keyboard, pointer, programmatic"></prop>
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
</event>
|
|
24
25
|
</defs>
|
|
25
26
|
<aside>
|
|
26
|
-
<div class="content"><slot></slot></div>
|
|
27
|
+
<div class="viewport"><div class="content"><slot></slot></div></div>
|
|
27
28
|
<div
|
|
28
29
|
class="resizer"
|
|
29
30
|
$ref="resizer"
|
|
@@ -55,10 +56,15 @@
|
|
|
55
56
|
syntax: "*";
|
|
56
57
|
inherits: false;
|
|
57
58
|
}
|
|
59
|
+
@property --ui-sidebar-collapsed-width {
|
|
60
|
+
syntax: "*";
|
|
61
|
+
inherits: false;
|
|
62
|
+
}
|
|
58
63
|
|
|
59
64
|
:host {
|
|
60
65
|
/* Hooks do not inherit, so the parts below read them from the root. */
|
|
61
66
|
--_ui-sidebar-backdrop: var(--ui-sidebar-backdrop);
|
|
67
|
+
--_ui-sidebar-expanded-width: var(--_sidebar-width, var(--ui-sidebar-width, 280px));
|
|
62
68
|
|
|
63
69
|
position: relative;
|
|
64
70
|
box-sizing: border-box;
|
|
@@ -70,6 +76,7 @@
|
|
|
70
76
|
min-block-size: 0;
|
|
71
77
|
background: var(--ui-sidebar-surface, var(--ui-surface-default));
|
|
72
78
|
border-inline-end: var(--ui-border-width) solid var(--ui-sidebar-border, var(--ui-border-divider));
|
|
79
|
+
transition: inline-size var(--ui-motion-layout) var(--ui-motion-layout-ease), visibility var(--ui-motion-layout);
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
:host-state([side="end"]) {
|
|
@@ -79,10 +86,17 @@
|
|
|
79
86
|
|
|
80
87
|
/* The panel's content scrolls, and each edge that hides content fades: the scroll fade every
|
|
81
88
|
Looma scroller uses (see ui-scroll-area). */
|
|
82
|
-
.
|
|
89
|
+
.viewport {
|
|
83
90
|
flex: 1;
|
|
84
91
|
min-block-size: 0;
|
|
92
|
+
overflow: clip;
|
|
93
|
+
}
|
|
94
|
+
.content {
|
|
95
|
+
block-size: 100%;
|
|
96
|
+
inline-size: calc(var(--_ui-sidebar-expanded-width) - var(--ui-border-width));
|
|
97
|
+
min-block-size: 0;
|
|
85
98
|
overflow: auto;
|
|
99
|
+
transition: transform var(--ui-motion-layout) var(--ui-motion-layout-ease);
|
|
86
100
|
--_ui-scroll-fade-direction: to bottom;
|
|
87
101
|
--_ui-scroll-fade-size: var(--ui-scroll-fade-size, 24px);
|
|
88
102
|
mask-image: linear-gradient(
|
|
@@ -103,7 +117,17 @@
|
|
|
103
117
|
|
|
104
118
|
/* Collapsed while docked. */
|
|
105
119
|
:host-state([internalCollapsed]):not([popover]) {
|
|
106
|
-
|
|
120
|
+
inline-size: var(--ui-sidebar-collapsed-width, 0px);
|
|
121
|
+
visibility: hidden;
|
|
122
|
+
}
|
|
123
|
+
:host-state([resizing]) {
|
|
124
|
+
transition: none;
|
|
125
|
+
}
|
|
126
|
+
:host-state([internalCollapsed]):not([popover]) .content {
|
|
127
|
+
transform: translateX(-100%);
|
|
128
|
+
}
|
|
129
|
+
:host-state([side="end"][internalCollapsed]):not([popover]) .content {
|
|
130
|
+
transform: translateX(100%);
|
|
107
131
|
}
|
|
108
132
|
|
|
109
133
|
.resizer {
|
|
@@ -182,9 +206,9 @@
|
|
|
182
206
|
box-shadow: var(--ui-elevation-overlay, var(--ui-shadow-lg));
|
|
183
207
|
transform: translateX(0);
|
|
184
208
|
transition:
|
|
185
|
-
transform var(--ui-motion-
|
|
186
|
-
display var(--ui-motion-
|
|
187
|
-
overlay var(--ui-motion-
|
|
209
|
+
transform var(--ui-motion-reveal) var(--ui-motion-reveal-ease),
|
|
210
|
+
display var(--ui-motion-reveal) allow-discrete,
|
|
211
|
+
overlay var(--ui-motion-reveal) allow-discrete;
|
|
188
212
|
}
|
|
189
213
|
|
|
190
214
|
/* The author display above would otherwise override the UA rule that hides a closed popover. */
|
|
@@ -214,9 +238,14 @@
|
|
|
214
238
|
:host[popover]::backdrop {
|
|
215
239
|
background: var(--_ui-sidebar-backdrop, rgb(0 0 0 / 0.32));
|
|
216
240
|
}
|
|
241
|
+
:host[popover] .content {
|
|
242
|
+
inline-size: 100%;
|
|
243
|
+
}
|
|
217
244
|
|
|
218
245
|
@media (prefers-reduced-motion: reduce) {
|
|
219
|
-
:host
|
|
246
|
+
:host,
|
|
247
|
+
:host[popover],
|
|
248
|
+
.content {
|
|
220
249
|
transition: none;
|
|
221
250
|
}
|
|
222
251
|
}
|
|
@@ -58,11 +58,13 @@ export default function controller(host) {
|
|
|
58
58
|
const startX = event.clientX;
|
|
59
59
|
const startWidth = currentWidth();
|
|
60
60
|
const multiplier = host.props.side.value === "end" ? -1 : 1;
|
|
61
|
+
host.state.resizing = true;
|
|
61
62
|
pointerAbort = new AbortController();
|
|
62
63
|
const signal = pointerAbort.signal;
|
|
63
64
|
const finish = () => {
|
|
64
65
|
pointerAbort?.abort();
|
|
65
66
|
pointerAbort = undefined;
|
|
67
|
+
host.state.resizing = false;
|
|
66
68
|
};
|
|
67
69
|
view.addEventListener("pointermove", (move) => {
|
|
68
70
|
setWidth(startWidth + ((move.clientX - startX) * multiplier), "pointer");
|
|
@@ -75,8 +77,12 @@ export default function controller(host) {
|
|
|
75
77
|
const endPointer = () => {
|
|
76
78
|
pointerAbort?.abort();
|
|
77
79
|
pointerAbort = undefined;
|
|
80
|
+
host.state.resizing = false;
|
|
78
81
|
};
|
|
79
82
|
const syncHandle = () => {
|
|
83
|
+
element.toggleAttribute("inert", host.state.internalCollapsed && !host.state.drawer);
|
|
84
|
+
if (host.state.internalCollapsed && !host.state.drawer) element.setAttribute("aria-hidden", "true");
|
|
85
|
+
else element.removeAttribute("aria-hidden");
|
|
80
86
|
if (!host.props.resizable.value || host.state.drawer || host.state.internalCollapsed) { endPointer(); return; }
|
|
81
87
|
const { min, max } = bounds();
|
|
82
88
|
handle.setAttribute("aria-valuemin", String(min));
|