@threadlabs/looma 0.1.7 → 0.1.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/layout/index.cjs +140 -1
- package/layout/index.css +48 -3
- package/layout/index.d.cts +6 -1
- package/layout/index.d.ts +6 -1
- package/layout/index.js +140 -1
- package/layout.css +48 -3
- package/package.json +1 -1
package/layout/index.cjs
CHANGED
|
@@ -176,7 +176,24 @@ if (typeof HTMLElement !== "undefined") {
|
|
|
176
176
|
}
|
|
177
177
|
class UISidebarElement extends UILayoutElement {
|
|
178
178
|
static get observedAttributes() {
|
|
179
|
-
return ["gap", "side", "width", "align"];
|
|
179
|
+
return ["gap", "side", "width", "align", "resizable", "storage-key", "min-width", "max-width", "resize-step", "resize-label"];
|
|
180
|
+
}
|
|
181
|
+
resizeHandle = null;
|
|
182
|
+
resizeAbort = null;
|
|
183
|
+
pointerAbort = null;
|
|
184
|
+
connectedCallback() {
|
|
185
|
+
this.syncResizable();
|
|
186
|
+
}
|
|
187
|
+
disconnectedCallback() {
|
|
188
|
+
this.resizeAbort?.abort();
|
|
189
|
+
this.resizeAbort = null;
|
|
190
|
+
this.pointerAbort?.abort();
|
|
191
|
+
this.pointerAbort = null;
|
|
192
|
+
}
|
|
193
|
+
attributeChangedCallback(name) {
|
|
194
|
+
if (["resizable", "storage-key", "min-width", "max-width", "resize-step", "resize-label", "side", "width"].includes(name)) {
|
|
195
|
+
this.syncResizable();
|
|
196
|
+
}
|
|
180
197
|
}
|
|
181
198
|
get gap() {
|
|
182
199
|
return this.getPrimitiveAttribute("gap");
|
|
@@ -202,6 +219,128 @@ if (typeof HTMLElement !== "undefined") {
|
|
|
202
219
|
set align(value) {
|
|
203
220
|
this.setPrimitiveAttribute("align", value);
|
|
204
221
|
}
|
|
222
|
+
get resizable() {
|
|
223
|
+
return this.hasAttribute("resizable");
|
|
224
|
+
}
|
|
225
|
+
set resizable(value) {
|
|
226
|
+
this.toggleAttribute("resizable", value);
|
|
227
|
+
}
|
|
228
|
+
numericAttribute(name, fallback) {
|
|
229
|
+
const parsed = Number(this.getAttribute(name));
|
|
230
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
231
|
+
}
|
|
232
|
+
bounds() {
|
|
233
|
+
const min = this.numericAttribute("min-width", 176);
|
|
234
|
+
const max = Math.max(min, this.numericAttribute("max-width", 480));
|
|
235
|
+
return { min, max, step: this.numericAttribute("resize-step", 16) };
|
|
236
|
+
}
|
|
237
|
+
defaultWidth() {
|
|
238
|
+
if (this.width === "narrow") return 224;
|
|
239
|
+
if (this.width === "wide") return 384;
|
|
240
|
+
return 288;
|
|
241
|
+
}
|
|
242
|
+
storageId() {
|
|
243
|
+
const key = this.getAttribute("storage-key")?.trim();
|
|
244
|
+
return key ? `looma:sidebar-width:${key}` : null;
|
|
245
|
+
}
|
|
246
|
+
readStoredWidth() {
|
|
247
|
+
const key = this.storageId();
|
|
248
|
+
if (!key || typeof localStorage === "undefined") return null;
|
|
249
|
+
try {
|
|
250
|
+
const parsed = Number(localStorage.getItem(key));
|
|
251
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
252
|
+
} catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
persistWidth(width) {
|
|
257
|
+
const key = this.storageId();
|
|
258
|
+
if (!key || typeof localStorage === "undefined") return;
|
|
259
|
+
try {
|
|
260
|
+
localStorage.setItem(key, String(width));
|
|
261
|
+
} catch {
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
currentWidth() {
|
|
265
|
+
const inline = Number.parseFloat(this.style.getPropertyValue("--ui-sidebar-width"));
|
|
266
|
+
if (Number.isFinite(inline)) return inline;
|
|
267
|
+
const sideRegion = this.getAttribute("side") === "end" ? this.querySelector(":scope > :last-child:not([data-ui-sidebar-resizer])") : this.querySelector(":scope > :first-child:not([data-ui-sidebar-resizer])");
|
|
268
|
+
return sideRegion?.getBoundingClientRect().width || this.defaultWidth();
|
|
269
|
+
}
|
|
270
|
+
setWidth(value, persist = false, trigger = "programmatic") {
|
|
271
|
+
const { min, max } = this.bounds();
|
|
272
|
+
const width = Math.round(Math.max(min, Math.min(max, value)));
|
|
273
|
+
const previous = Number.parseFloat(this.style.getPropertyValue("--ui-sidebar-width"));
|
|
274
|
+
const changed = !Number.isFinite(previous) || previous !== width;
|
|
275
|
+
this.style.setProperty("--ui-sidebar-width", `${width}px`);
|
|
276
|
+
this.resizeHandle?.setAttribute("aria-valuemin", String(min));
|
|
277
|
+
this.resizeHandle?.setAttribute("aria-valuemax", String(max));
|
|
278
|
+
this.resizeHandle?.setAttribute("aria-valuenow", String(width));
|
|
279
|
+
if (persist && changed) this.persistWidth(width);
|
|
280
|
+
if (changed) {
|
|
281
|
+
this.dispatchEvent(new CustomEvent("resize", { detail: { width, trigger }, bubbles: true }));
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
syncResizable() {
|
|
285
|
+
if (!this.isConnected) return;
|
|
286
|
+
if (!this.resizable) {
|
|
287
|
+
this.resizeAbort?.abort();
|
|
288
|
+
this.resizeAbort = null;
|
|
289
|
+
this.pointerAbort?.abort();
|
|
290
|
+
this.pointerAbort = null;
|
|
291
|
+
this.resizeHandle?.remove();
|
|
292
|
+
this.resizeHandle = null;
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (!this.resizeHandle) {
|
|
296
|
+
this.resizeHandle = document.createElement("div");
|
|
297
|
+
this.resizeHandle.setAttribute("data-ui-sidebar-resizer", "");
|
|
298
|
+
this.resizeHandle.setAttribute("role", "separator");
|
|
299
|
+
this.resizeHandle.setAttribute("aria-orientation", "vertical");
|
|
300
|
+
this.resizeHandle.setAttribute("tabindex", "0");
|
|
301
|
+
this.append(this.resizeHandle);
|
|
302
|
+
}
|
|
303
|
+
this.resizeHandle.setAttribute("aria-label", this.getAttribute("resize-label") || "Resize sidebar");
|
|
304
|
+
this.resizeAbort?.abort();
|
|
305
|
+
this.resizeAbort = new AbortController();
|
|
306
|
+
const signal = this.resizeAbort.signal;
|
|
307
|
+
this.resizeHandle.addEventListener("keydown", (event) => this.onResizeKey(event), { signal });
|
|
308
|
+
this.resizeHandle.addEventListener("pointerdown", (event) => this.onResizePointer(event), { signal });
|
|
309
|
+
this.resizeHandle.addEventListener("dblclick", () => this.setWidth(this.defaultWidth(), true, "pointer"), { signal });
|
|
310
|
+
this.setWidth(this.readStoredWidth() ?? this.currentWidth());
|
|
311
|
+
}
|
|
312
|
+
onResizeKey(event) {
|
|
313
|
+
const { min, max, step } = this.bounds();
|
|
314
|
+
let next = null;
|
|
315
|
+
if (event.key === "Home") next = min;
|
|
316
|
+
if (event.key === "End") next = max;
|
|
317
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
318
|
+
const physicalDelta = event.key === "ArrowRight" ? step : -step;
|
|
319
|
+
next = this.currentWidth() + (this.side === "end" ? -physicalDelta : physicalDelta);
|
|
320
|
+
}
|
|
321
|
+
if (next === null) return;
|
|
322
|
+
event.preventDefault();
|
|
323
|
+
this.setWidth(next, true, "keyboard");
|
|
324
|
+
}
|
|
325
|
+
onResizePointer(event) {
|
|
326
|
+
if (event.button !== 0 || this.pointerAbort) return;
|
|
327
|
+
event.preventDefault();
|
|
328
|
+
const startX = event.clientX;
|
|
329
|
+
const startWidth = this.currentWidth();
|
|
330
|
+
const multiplier = this.side === "end" ? -1 : 1;
|
|
331
|
+
const controller = new AbortController();
|
|
332
|
+
this.pointerAbort = controller;
|
|
333
|
+
const finish = () => {
|
|
334
|
+
this.persistWidth(this.currentWidth());
|
|
335
|
+
controller.abort();
|
|
336
|
+
this.pointerAbort = null;
|
|
337
|
+
};
|
|
338
|
+
window.addEventListener("pointermove", (moveEvent) => {
|
|
339
|
+
this.setWidth(startWidth + (moveEvent.clientX - startX) * multiplier, false, "pointer");
|
|
340
|
+
}, { signal: controller.signal });
|
|
341
|
+
window.addEventListener("pointerup", finish, { once: true, signal: controller.signal });
|
|
342
|
+
window.addEventListener("pointercancel", finish, { once: true, signal: controller.signal });
|
|
343
|
+
}
|
|
205
344
|
}
|
|
206
345
|
class UIReelElement extends UILayoutElement {
|
|
207
346
|
static get observedAttributes() {
|
package/layout/index.css
CHANGED
|
@@ -210,14 +210,15 @@
|
|
|
210
210
|
gap: var(--ui-sidebar-gap);
|
|
211
211
|
align-items: var(--ui-sidebar-align);
|
|
212
212
|
}
|
|
213
|
-
ui-sidebar >
|
|
213
|
+
ui-sidebar > :not([data-ui-sidebar-resizer]) {
|
|
214
214
|
min-inline-size: 0;
|
|
215
215
|
}
|
|
216
216
|
ui-sidebar > :first-child {
|
|
217
217
|
flex-basis: var(--ui-sidebar-width);
|
|
218
218
|
flex-grow: 1;
|
|
219
219
|
}
|
|
220
|
-
ui-sidebar > :last-child
|
|
220
|
+
ui-sidebar > :last-child:not([data-ui-sidebar-resizer]),
|
|
221
|
+
ui-sidebar[resizable] > :nth-last-child(2):not([data-ui-sidebar-resizer]) {
|
|
221
222
|
flex-basis: 0;
|
|
222
223
|
flex-grow: 999;
|
|
223
224
|
min-inline-size: min(var(--ui-sidebar-content-min), 100%);
|
|
@@ -227,7 +228,8 @@
|
|
|
227
228
|
flex-grow: 999;
|
|
228
229
|
min-inline-size: min(var(--ui-sidebar-content-min), 100%);
|
|
229
230
|
}
|
|
230
|
-
ui-sidebar[side=end] > :last-child
|
|
231
|
+
ui-sidebar[side=end] > :last-child:not([data-ui-sidebar-resizer]),
|
|
232
|
+
ui-sidebar[side=end][resizable] > :nth-last-child(2):not([data-ui-sidebar-resizer]) {
|
|
231
233
|
flex-basis: var(--ui-sidebar-width);
|
|
232
234
|
flex-grow: 1;
|
|
233
235
|
min-inline-size: 0;
|
|
@@ -259,6 +261,49 @@
|
|
|
259
261
|
ui-sidebar[align=end] {
|
|
260
262
|
--ui-sidebar-align: flex-end;
|
|
261
263
|
}
|
|
264
|
+
ui-sidebar[resizable] {
|
|
265
|
+
position: relative;
|
|
266
|
+
flex-wrap: nowrap;
|
|
267
|
+
}
|
|
268
|
+
ui-sidebar[resizable] > :first-child:not([data-ui-sidebar-resizer]) {
|
|
269
|
+
flex: 0 0 var(--ui-sidebar-width);
|
|
270
|
+
}
|
|
271
|
+
ui-sidebar[side=end][resizable] > :first-child:not([data-ui-sidebar-resizer]) {
|
|
272
|
+
flex: 1 1 0;
|
|
273
|
+
}
|
|
274
|
+
ui-sidebar[side=end][resizable] > :nth-last-child(2):not([data-ui-sidebar-resizer]) {
|
|
275
|
+
flex: 0 0 var(--ui-sidebar-width);
|
|
276
|
+
}
|
|
277
|
+
ui-sidebar > [data-ui-sidebar-resizer] {
|
|
278
|
+
position: absolute;
|
|
279
|
+
z-index: 1;
|
|
280
|
+
inset-block: 0;
|
|
281
|
+
inset-inline-start: var(--ui-sidebar-width);
|
|
282
|
+
inline-size: 8px;
|
|
283
|
+
min-inline-size: 8px;
|
|
284
|
+
transform: translateX(-50%);
|
|
285
|
+
cursor: col-resize;
|
|
286
|
+
touch-action: none;
|
|
287
|
+
outline: none;
|
|
288
|
+
}
|
|
289
|
+
ui-sidebar[side=end] > [data-ui-sidebar-resizer] {
|
|
290
|
+
inset-inline-start: auto;
|
|
291
|
+
inset-inline-end: var(--ui-sidebar-width);
|
|
292
|
+
transform: translateX(50%);
|
|
293
|
+
}
|
|
294
|
+
ui-sidebar > [data-ui-sidebar-resizer]::after {
|
|
295
|
+
content: "";
|
|
296
|
+
position: absolute;
|
|
297
|
+
inset-block: 0;
|
|
298
|
+
inset-inline-start: 3px;
|
|
299
|
+
inline-size: 2px;
|
|
300
|
+
background: transparent;
|
|
301
|
+
transition: background-color 120ms ease;
|
|
302
|
+
}
|
|
303
|
+
ui-sidebar > [data-ui-sidebar-resizer]:hover::after,
|
|
304
|
+
ui-sidebar > [data-ui-sidebar-resizer]:focus-visible::after {
|
|
305
|
+
background: var(--ui-color-focus, Highlight);
|
|
306
|
+
}
|
|
262
307
|
ui-reel {
|
|
263
308
|
--ui-reel-gap: var(--ui-space-4, 1rem);
|
|
264
309
|
--ui-reel-item-width: auto;
|
package/layout/index.d.cts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
type SidebarResizeTrigger = "keyboard" | "pointer" | "programmatic";
|
|
2
|
+
interface SidebarResizeDetail {
|
|
3
|
+
width: number;
|
|
4
|
+
trigger: SidebarResizeTrigger;
|
|
5
|
+
}
|
|
1
6
|
declare function registerLayoutPrimitives(): void;
|
|
2
7
|
|
|
3
|
-
export { registerLayoutPrimitives };
|
|
8
|
+
export { type SidebarResizeDetail, type SidebarResizeTrigger, registerLayoutPrimitives };
|
package/layout/index.d.ts
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
type SidebarResizeTrigger = "keyboard" | "pointer" | "programmatic";
|
|
2
|
+
interface SidebarResizeDetail {
|
|
3
|
+
width: number;
|
|
4
|
+
trigger: SidebarResizeTrigger;
|
|
5
|
+
}
|
|
1
6
|
declare function registerLayoutPrimitives(): void;
|
|
2
7
|
|
|
3
|
-
export { registerLayoutPrimitives };
|
|
8
|
+
export { type SidebarResizeDetail, type SidebarResizeTrigger, registerLayoutPrimitives };
|
package/layout/index.js
CHANGED
|
@@ -152,7 +152,24 @@ if (typeof HTMLElement !== "undefined") {
|
|
|
152
152
|
}
|
|
153
153
|
class UISidebarElement extends UILayoutElement {
|
|
154
154
|
static get observedAttributes() {
|
|
155
|
-
return ["gap", "side", "width", "align"];
|
|
155
|
+
return ["gap", "side", "width", "align", "resizable", "storage-key", "min-width", "max-width", "resize-step", "resize-label"];
|
|
156
|
+
}
|
|
157
|
+
resizeHandle = null;
|
|
158
|
+
resizeAbort = null;
|
|
159
|
+
pointerAbort = null;
|
|
160
|
+
connectedCallback() {
|
|
161
|
+
this.syncResizable();
|
|
162
|
+
}
|
|
163
|
+
disconnectedCallback() {
|
|
164
|
+
this.resizeAbort?.abort();
|
|
165
|
+
this.resizeAbort = null;
|
|
166
|
+
this.pointerAbort?.abort();
|
|
167
|
+
this.pointerAbort = null;
|
|
168
|
+
}
|
|
169
|
+
attributeChangedCallback(name) {
|
|
170
|
+
if (["resizable", "storage-key", "min-width", "max-width", "resize-step", "resize-label", "side", "width"].includes(name)) {
|
|
171
|
+
this.syncResizable();
|
|
172
|
+
}
|
|
156
173
|
}
|
|
157
174
|
get gap() {
|
|
158
175
|
return this.getPrimitiveAttribute("gap");
|
|
@@ -178,6 +195,128 @@ if (typeof HTMLElement !== "undefined") {
|
|
|
178
195
|
set align(value) {
|
|
179
196
|
this.setPrimitiveAttribute("align", value);
|
|
180
197
|
}
|
|
198
|
+
get resizable() {
|
|
199
|
+
return this.hasAttribute("resizable");
|
|
200
|
+
}
|
|
201
|
+
set resizable(value) {
|
|
202
|
+
this.toggleAttribute("resizable", value);
|
|
203
|
+
}
|
|
204
|
+
numericAttribute(name, fallback) {
|
|
205
|
+
const parsed = Number(this.getAttribute(name));
|
|
206
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
207
|
+
}
|
|
208
|
+
bounds() {
|
|
209
|
+
const min = this.numericAttribute("min-width", 176);
|
|
210
|
+
const max = Math.max(min, this.numericAttribute("max-width", 480));
|
|
211
|
+
return { min, max, step: this.numericAttribute("resize-step", 16) };
|
|
212
|
+
}
|
|
213
|
+
defaultWidth() {
|
|
214
|
+
if (this.width === "narrow") return 224;
|
|
215
|
+
if (this.width === "wide") return 384;
|
|
216
|
+
return 288;
|
|
217
|
+
}
|
|
218
|
+
storageId() {
|
|
219
|
+
const key = this.getAttribute("storage-key")?.trim();
|
|
220
|
+
return key ? `looma:sidebar-width:${key}` : null;
|
|
221
|
+
}
|
|
222
|
+
readStoredWidth() {
|
|
223
|
+
const key = this.storageId();
|
|
224
|
+
if (!key || typeof localStorage === "undefined") return null;
|
|
225
|
+
try {
|
|
226
|
+
const parsed = Number(localStorage.getItem(key));
|
|
227
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
228
|
+
} catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
persistWidth(width) {
|
|
233
|
+
const key = this.storageId();
|
|
234
|
+
if (!key || typeof localStorage === "undefined") return;
|
|
235
|
+
try {
|
|
236
|
+
localStorage.setItem(key, String(width));
|
|
237
|
+
} catch {
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
currentWidth() {
|
|
241
|
+
const inline = Number.parseFloat(this.style.getPropertyValue("--ui-sidebar-width"));
|
|
242
|
+
if (Number.isFinite(inline)) return inline;
|
|
243
|
+
const sideRegion = this.getAttribute("side") === "end" ? this.querySelector(":scope > :last-child:not([data-ui-sidebar-resizer])") : this.querySelector(":scope > :first-child:not([data-ui-sidebar-resizer])");
|
|
244
|
+
return sideRegion?.getBoundingClientRect().width || this.defaultWidth();
|
|
245
|
+
}
|
|
246
|
+
setWidth(value, persist = false, trigger = "programmatic") {
|
|
247
|
+
const { min, max } = this.bounds();
|
|
248
|
+
const width = Math.round(Math.max(min, Math.min(max, value)));
|
|
249
|
+
const previous = Number.parseFloat(this.style.getPropertyValue("--ui-sidebar-width"));
|
|
250
|
+
const changed = !Number.isFinite(previous) || previous !== width;
|
|
251
|
+
this.style.setProperty("--ui-sidebar-width", `${width}px`);
|
|
252
|
+
this.resizeHandle?.setAttribute("aria-valuemin", String(min));
|
|
253
|
+
this.resizeHandle?.setAttribute("aria-valuemax", String(max));
|
|
254
|
+
this.resizeHandle?.setAttribute("aria-valuenow", String(width));
|
|
255
|
+
if (persist && changed) this.persistWidth(width);
|
|
256
|
+
if (changed) {
|
|
257
|
+
this.dispatchEvent(new CustomEvent("resize", { detail: { width, trigger }, bubbles: true }));
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
syncResizable() {
|
|
261
|
+
if (!this.isConnected) return;
|
|
262
|
+
if (!this.resizable) {
|
|
263
|
+
this.resizeAbort?.abort();
|
|
264
|
+
this.resizeAbort = null;
|
|
265
|
+
this.pointerAbort?.abort();
|
|
266
|
+
this.pointerAbort = null;
|
|
267
|
+
this.resizeHandle?.remove();
|
|
268
|
+
this.resizeHandle = null;
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (!this.resizeHandle) {
|
|
272
|
+
this.resizeHandle = document.createElement("div");
|
|
273
|
+
this.resizeHandle.setAttribute("data-ui-sidebar-resizer", "");
|
|
274
|
+
this.resizeHandle.setAttribute("role", "separator");
|
|
275
|
+
this.resizeHandle.setAttribute("aria-orientation", "vertical");
|
|
276
|
+
this.resizeHandle.setAttribute("tabindex", "0");
|
|
277
|
+
this.append(this.resizeHandle);
|
|
278
|
+
}
|
|
279
|
+
this.resizeHandle.setAttribute("aria-label", this.getAttribute("resize-label") || "Resize sidebar");
|
|
280
|
+
this.resizeAbort?.abort();
|
|
281
|
+
this.resizeAbort = new AbortController();
|
|
282
|
+
const signal = this.resizeAbort.signal;
|
|
283
|
+
this.resizeHandle.addEventListener("keydown", (event) => this.onResizeKey(event), { signal });
|
|
284
|
+
this.resizeHandle.addEventListener("pointerdown", (event) => this.onResizePointer(event), { signal });
|
|
285
|
+
this.resizeHandle.addEventListener("dblclick", () => this.setWidth(this.defaultWidth(), true, "pointer"), { signal });
|
|
286
|
+
this.setWidth(this.readStoredWidth() ?? this.currentWidth());
|
|
287
|
+
}
|
|
288
|
+
onResizeKey(event) {
|
|
289
|
+
const { min, max, step } = this.bounds();
|
|
290
|
+
let next = null;
|
|
291
|
+
if (event.key === "Home") next = min;
|
|
292
|
+
if (event.key === "End") next = max;
|
|
293
|
+
if (event.key === "ArrowLeft" || event.key === "ArrowRight") {
|
|
294
|
+
const physicalDelta = event.key === "ArrowRight" ? step : -step;
|
|
295
|
+
next = this.currentWidth() + (this.side === "end" ? -physicalDelta : physicalDelta);
|
|
296
|
+
}
|
|
297
|
+
if (next === null) return;
|
|
298
|
+
event.preventDefault();
|
|
299
|
+
this.setWidth(next, true, "keyboard");
|
|
300
|
+
}
|
|
301
|
+
onResizePointer(event) {
|
|
302
|
+
if (event.button !== 0 || this.pointerAbort) return;
|
|
303
|
+
event.preventDefault();
|
|
304
|
+
const startX = event.clientX;
|
|
305
|
+
const startWidth = this.currentWidth();
|
|
306
|
+
const multiplier = this.side === "end" ? -1 : 1;
|
|
307
|
+
const controller = new AbortController();
|
|
308
|
+
this.pointerAbort = controller;
|
|
309
|
+
const finish = () => {
|
|
310
|
+
this.persistWidth(this.currentWidth());
|
|
311
|
+
controller.abort();
|
|
312
|
+
this.pointerAbort = null;
|
|
313
|
+
};
|
|
314
|
+
window.addEventListener("pointermove", (moveEvent) => {
|
|
315
|
+
this.setWidth(startWidth + (moveEvent.clientX - startX) * multiplier, false, "pointer");
|
|
316
|
+
}, { signal: controller.signal });
|
|
317
|
+
window.addEventListener("pointerup", finish, { once: true, signal: controller.signal });
|
|
318
|
+
window.addEventListener("pointercancel", finish, { once: true, signal: controller.signal });
|
|
319
|
+
}
|
|
181
320
|
}
|
|
182
321
|
class UIReelElement extends UILayoutElement {
|
|
183
322
|
static get observedAttributes() {
|
package/layout.css
CHANGED
|
@@ -151,12 +151,13 @@
|
|
|
151
151
|
align-items: var(--ui-sidebar-align);
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
ui-sidebar >
|
|
154
|
+
ui-sidebar > :not([data-ui-sidebar-resizer]) { min-inline-size: 0; }
|
|
155
155
|
ui-sidebar > :first-child {
|
|
156
156
|
flex-basis: var(--ui-sidebar-width);
|
|
157
157
|
flex-grow: 1;
|
|
158
158
|
}
|
|
159
|
-
ui-sidebar > :last-child
|
|
159
|
+
ui-sidebar > :last-child:not([data-ui-sidebar-resizer]),
|
|
160
|
+
ui-sidebar[resizable] > :nth-last-child(2):not([data-ui-sidebar-resizer]) {
|
|
160
161
|
flex-basis: 0;
|
|
161
162
|
flex-grow: 999;
|
|
162
163
|
min-inline-size: min(var(--ui-sidebar-content-min), 100%);
|
|
@@ -166,7 +167,8 @@
|
|
|
166
167
|
flex-grow: 999;
|
|
167
168
|
min-inline-size: min(var(--ui-sidebar-content-min), 100%);
|
|
168
169
|
}
|
|
169
|
-
ui-sidebar[side="end"] > :last-child
|
|
170
|
+
ui-sidebar[side="end"] > :last-child:not([data-ui-sidebar-resizer]),
|
|
171
|
+
ui-sidebar[side="end"][resizable] > :nth-last-child(2):not([data-ui-sidebar-resizer]) {
|
|
170
172
|
flex-basis: var(--ui-sidebar-width);
|
|
171
173
|
flex-grow: 1;
|
|
172
174
|
min-inline-size: 0;
|
|
@@ -180,6 +182,49 @@
|
|
|
180
182
|
ui-sidebar[align="start"] { --ui-sidebar-align: flex-start; }
|
|
181
183
|
ui-sidebar[align="center"] { --ui-sidebar-align: center; }
|
|
182
184
|
ui-sidebar[align="end"] { --ui-sidebar-align: flex-end; }
|
|
185
|
+
ui-sidebar[resizable] {
|
|
186
|
+
position: relative;
|
|
187
|
+
flex-wrap: nowrap;
|
|
188
|
+
}
|
|
189
|
+
ui-sidebar[resizable] > :first-child:not([data-ui-sidebar-resizer]) {
|
|
190
|
+
flex: 0 0 var(--ui-sidebar-width);
|
|
191
|
+
}
|
|
192
|
+
ui-sidebar[side="end"][resizable] > :first-child:not([data-ui-sidebar-resizer]) {
|
|
193
|
+
flex: 1 1 0;
|
|
194
|
+
}
|
|
195
|
+
ui-sidebar[side="end"][resizable] > :nth-last-child(2):not([data-ui-sidebar-resizer]) {
|
|
196
|
+
flex: 0 0 var(--ui-sidebar-width);
|
|
197
|
+
}
|
|
198
|
+
ui-sidebar > [data-ui-sidebar-resizer] {
|
|
199
|
+
position: absolute;
|
|
200
|
+
z-index: 1;
|
|
201
|
+
inset-block: 0;
|
|
202
|
+
inset-inline-start: var(--ui-sidebar-width);
|
|
203
|
+
inline-size: 8px;
|
|
204
|
+
min-inline-size: 8px;
|
|
205
|
+
transform: translateX(-50%);
|
|
206
|
+
cursor: col-resize;
|
|
207
|
+
touch-action: none;
|
|
208
|
+
outline: none;
|
|
209
|
+
}
|
|
210
|
+
ui-sidebar[side="end"] > [data-ui-sidebar-resizer] {
|
|
211
|
+
inset-inline-start: auto;
|
|
212
|
+
inset-inline-end: var(--ui-sidebar-width);
|
|
213
|
+
transform: translateX(50%);
|
|
214
|
+
}
|
|
215
|
+
ui-sidebar > [data-ui-sidebar-resizer]::after {
|
|
216
|
+
content: "";
|
|
217
|
+
position: absolute;
|
|
218
|
+
inset-block: 0;
|
|
219
|
+
inset-inline-start: 3px;
|
|
220
|
+
inline-size: 2px;
|
|
221
|
+
background: transparent;
|
|
222
|
+
transition: background-color 120ms ease;
|
|
223
|
+
}
|
|
224
|
+
ui-sidebar > [data-ui-sidebar-resizer]:hover::after,
|
|
225
|
+
ui-sidebar > [data-ui-sidebar-resizer]:focus-visible::after {
|
|
226
|
+
background: var(--ui-color-focus, Highlight);
|
|
227
|
+
}
|
|
183
228
|
|
|
184
229
|
ui-reel {
|
|
185
230
|
--ui-reel-gap: var(--ui-space-4, 1rem);
|