@gramlot/gramlot-examples 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -3
- package/package.json +4 -3
- package/src/gramlot_examples/catalog.json +54 -0
- package/src/gramlot_examples/gallery/dist/frame.js +548 -36
- package/src/gramlot_examples/gallery/dist/gallery.js +2387 -16677
- package/src/gramlot_examples/gallery/dist/inputs.js +12 -6
- package/src/gramlot_examples/gallery/dist/inspector.js +547 -35
- package/src/gramlot_examples/gallery/dist/layout.js +847 -28
- package/src/gramlot_examples/gallery/dist/notices.json +0 -24
- package/src/gramlot_examples/gallery/dist/theme-styles.js +1 -1
- package/src/gramlot_examples/gallery/gallery-page.js +66 -45
- package/src/gramlot_examples/gallery/gallery.css +61 -38
- package/src/gramlot_examples/gallery/guided-demo.js +151 -0
- package/src/gramlot_examples/gallery/page.js +1 -1
- package/src/gramlot_examples/index.js +1 -0
- package/src/gramlot_examples/pages/layout/01_border.js +80 -0
- package/src/gramlot_examples/pages/layout/01_border.md +23 -0
- package/src/gramlot_examples/pages/layout/02_nested.js +99 -0
- package/src/gramlot_examples/pages/layout/02_nested.md +23 -0
- package/src/gramlot_examples/pages/layout/03_rounded.js +111 -0
- package/src/gramlot_examples/pages/layout/03_rounded.md +25 -0
- package/src/gramlot_examples/pages/layout/04_drawer_handles.js +140 -0
- package/src/gramlot_examples/pages/layout/04_drawer_handles.md +33 -0
- package/src/gramlot_examples/pages/layout/05_nested_lab.js +251 -0
- package/src/gramlot_examples/pages/layout/05_nested_lab.md +25 -0
- package/src/gramlot_examples/pages/layout/06_border_events.js +118 -0
- package/src/gramlot_examples/pages/layout/06_border_events.md +25 -0
- package/src/gramlot_examples/pages/layout/07_tabs.js +300 -0
- package/src/gramlot_examples/pages/layout/07_tabs.md +37 -0
- package/src/gramlot_examples/pages/layout/08_stack.js +142 -0
- package/src/gramlot_examples/pages/layout/08_stack.md +27 -0
- package/src/gramlot_examples/pages/layout/README.md +15 -0
|
@@ -34,7 +34,7 @@ function annotation(parameter) {
|
|
|
34
34
|
});
|
|
35
35
|
return alternatives.length === 1 ? alternatives[0] : { kind: "union", items: alternatives };
|
|
36
36
|
}
|
|
37
|
-
function defineComponent({ name, tag, base, mixins = [], parameters = {}, subTags = "", presentationDefaults = [], dataScope = false, command = false, implementation }) {
|
|
37
|
+
function defineComponent({ name, tag, base, mixins = [], parameters = {}, subTags = "", presentationDefaults = [], dataScope = false, command = false, childParameters = {}, sourceContext = false, implementation }) {
|
|
38
38
|
webcomponentCode({ code: tag });
|
|
39
39
|
if (!/^[a-zA-Z][a-zA-Z0-9]*$/.test(name)) throw new TypeError("Invalid component authoring name");
|
|
40
40
|
const effective = { ...base.parameters };
|
|
@@ -56,6 +56,8 @@ function defineComponent({ name, tag, base, mixins = [], parameters = {}, subTag
|
|
|
56
56
|
command,
|
|
57
57
|
mixins: Object.freeze([...mixins]),
|
|
58
58
|
parameters: freeze(structuredClone(effective)),
|
|
59
|
+
childParameters: freeze(structuredClone(childParameters)),
|
|
60
|
+
sourceContext,
|
|
59
61
|
implementation
|
|
60
62
|
});
|
|
61
63
|
}
|
|
@@ -112,7 +114,9 @@ var ComponentCollection = class {
|
|
|
112
114
|
parameters: structuredClone(component.parameters),
|
|
113
115
|
presentationDefaults: [...component.presentationDefaults],
|
|
114
116
|
dataScope: component.dataScope,
|
|
115
|
-
command: component.command
|
|
117
|
+
command: component.command,
|
|
118
|
+
...Object.keys(component.childParameters).length ? { childParameters: structuredClone(component.childParameters) } : {},
|
|
119
|
+
...component.sourceContext ? { sourceContext: true } : {}
|
|
116
120
|
} }
|
|
117
121
|
}]))
|
|
118
122
|
};
|
|
@@ -220,15 +224,848 @@ function projectParameters(element, attrs) {
|
|
|
220
224
|
return Object.fromEntries(Object.entries(attrs).filter(([name]) => !Object.hasOwn(element.parameters, name)));
|
|
221
225
|
}
|
|
222
226
|
|
|
227
|
+
// node_modules/@gramlot/gramlot/src/components/container.js
|
|
228
|
+
function Container(HTMLElement) {
|
|
229
|
+
return class extends HTMLElement {
|
|
230
|
+
constructor() {
|
|
231
|
+
super();
|
|
232
|
+
const shadow = this.attachShadow({ mode: "open" });
|
|
233
|
+
const style = this.ownerDocument.createElement("style");
|
|
234
|
+
style.textContent = `:host{display:block;min-width:0;font:inherit;color:var(--gramlot-ink,CanvasText)}
|
|
235
|
+
*,*::before,*::after{box-sizing:border-box}.control-box{min-width:0}slot{display:contents}
|
|
236
|
+
::slotted(*){min-width:0;max-width:100%}`;
|
|
237
|
+
this.controlBox = this.ownerDocument.createElement("div");
|
|
238
|
+
this.controlBox.className = "control-box";
|
|
239
|
+
this.control = this.controlBox;
|
|
240
|
+
this.control.id = "control";
|
|
241
|
+
this.control.append(this.ownerDocument.createElement("slot"));
|
|
242
|
+
shadow.append(style, this.controlBox);
|
|
243
|
+
}
|
|
244
|
+
projectAttributes(attrs) {
|
|
245
|
+
return projectParameters(this, attrs);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
Container.parameters = {};
|
|
250
|
+
|
|
251
|
+
// node_modules/@gramlot/gramlot/src/components/border-container.js
|
|
252
|
+
var SIDES = ["top", "bottom", "left", "right"];
|
|
253
|
+
var REGIONS = [...SIDES, "center"];
|
|
254
|
+
var borderRegions = /* @__PURE__ */ new WeakMap();
|
|
255
|
+
var horizontal = (side) => side === "top" || side === "bottom";
|
|
256
|
+
function projectBorderChild(element, attrs, { adapt, invoke, publish, publishParent }) {
|
|
257
|
+
const rest = { ...attrs }, splitter = {}, drawer = {};
|
|
258
|
+
for (const [name, value] of Object.entries(rest)) {
|
|
259
|
+
for (const [prefix, group] of [["splitter_", splitter], ["drawer_", drawer]]) {
|
|
260
|
+
if (name.startsWith(prefix)) {
|
|
261
|
+
group[name.slice(prefix.length)] = value;
|
|
262
|
+
delete rest[name];
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
const region = rest.region;
|
|
267
|
+
if (!REGIONS.includes(region)) throw new TypeError(`Invalid border region: ${region}`);
|
|
268
|
+
const min = rest.minSize ?? 0, max = rest.maxSize ?? Infinity;
|
|
269
|
+
if (typeof min !== "number" || typeof max !== "number" || !Number.isFinite(min) || min < 0 || max < min || Number.isNaN(max)) {
|
|
270
|
+
throw new TypeError("Border minSize/maxSize must be nonnegative pixel limits, with maxSize >= minSize");
|
|
271
|
+
}
|
|
272
|
+
if (![void 0, null, false, true, "close"].includes(rest.drawer)) throw new TypeError("drawer must be true, false or close");
|
|
273
|
+
const probe = element.ownerDocument.createElement("div");
|
|
274
|
+
probe.style.cssText = rest.style ?? "";
|
|
275
|
+
const size = probe.style[horizontal(region) ? "height" : "width"];
|
|
276
|
+
probe.style.width = "100%";
|
|
277
|
+
probe.style.height = "100%";
|
|
278
|
+
rest.style = probe.style.cssText;
|
|
279
|
+
const { label, icon, minimize = true, onclick, ...drawerStyle } = drawer;
|
|
280
|
+
if (typeof minimize !== "boolean") throw new TypeError("drawer_minimize must be a boolean");
|
|
281
|
+
const drawerAttrs = adapt(drawerStyle);
|
|
282
|
+
const drawerProbe = element.ownerDocument.createElement("div");
|
|
283
|
+
drawerProbe.style.cssText = drawerAttrs.style ?? "";
|
|
284
|
+
const background = drawerProbe.style.backgroundColor;
|
|
285
|
+
if (background) drawerProbe.style.setProperty("--gramlot-drawer-background", background);
|
|
286
|
+
if (drawerAttrs.style != null) drawerAttrs.style = drawerProbe.style.cssText;
|
|
287
|
+
borderRegions.set(element, {
|
|
288
|
+
region,
|
|
289
|
+
size,
|
|
290
|
+
min,
|
|
291
|
+
max,
|
|
292
|
+
splitter: rest.splitter === true,
|
|
293
|
+
drawer: rest.drawer,
|
|
294
|
+
hidden: rest.hidden === true,
|
|
295
|
+
splitterAttrs: adapt(splitter),
|
|
296
|
+
drawerAttrs,
|
|
297
|
+
label,
|
|
298
|
+
icon,
|
|
299
|
+
minimize,
|
|
300
|
+
notifications: {
|
|
301
|
+
closableChange: (payload) => publish("closable_change", payload),
|
|
302
|
+
splitterResize: (payload) => publishParent("splitter_resize", payload)
|
|
303
|
+
},
|
|
304
|
+
onToggle: onclick ? (evt, show) => invoke("drawer_onclick", onclick, { evt, show }) : null
|
|
305
|
+
});
|
|
306
|
+
for (const key of ["region", "splitter", "drawer", "minSize", "maxSize"]) delete rest[key];
|
|
307
|
+
rest.slot = region;
|
|
308
|
+
if (element.parentElement?.constructor.gramlotComponent?.name === "borderContainer") element.parentElement.projectRegion(element);
|
|
309
|
+
return rest;
|
|
310
|
+
}
|
|
311
|
+
function applyAttributes(element, attrs, previous) {
|
|
312
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(previous), ...Object.keys(attrs)])) {
|
|
313
|
+
if (attrs[key] == null) element.removeAttribute(key);
|
|
314
|
+
else element.setAttribute(key, String(attrs[key]));
|
|
315
|
+
}
|
|
316
|
+
return attrs;
|
|
317
|
+
}
|
|
318
|
+
var ContentPane = defineComponent({
|
|
319
|
+
name: "contentPane",
|
|
320
|
+
tag: "gramlot-content-pane",
|
|
321
|
+
base: Container,
|
|
322
|
+
subTags: "*",
|
|
323
|
+
implementation: (Base) => class extends Base {
|
|
324
|
+
constructor() {
|
|
325
|
+
super();
|
|
326
|
+
const style = this.ownerDocument.createElement("style");
|
|
327
|
+
style.textContent = ":host{min-height:0;overflow:auto}.control-box{height:100%;min-height:0}";
|
|
328
|
+
this.shadowRoot.append(style);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
var BorderContainer = defineComponent({
|
|
333
|
+
name: "borderContainer",
|
|
334
|
+
tag: "gramlot-border-container",
|
|
335
|
+
base: Container,
|
|
336
|
+
subTags: "*",
|
|
337
|
+
childParameters: { region: { required: true, enum: REGIONS, unique: true }, drawer_onclick: { inline: ["evt", "show"] } },
|
|
338
|
+
parameters: {
|
|
339
|
+
design: { dtype: "T", default: "headline", binding: "read", enum: ["headline", "sidebar"] },
|
|
340
|
+
persist: { dtype: "B", default: false, binding: "read" }
|
|
341
|
+
},
|
|
342
|
+
implementation: (Base) => class extends Base {
|
|
343
|
+
static projectChild(element, attrs, context) {
|
|
344
|
+
return projectBorderChild(element, attrs, context);
|
|
345
|
+
}
|
|
346
|
+
#regions = /* @__PURE__ */ new Map();
|
|
347
|
+
#observer;
|
|
348
|
+
#contentObserver;
|
|
349
|
+
#drag;
|
|
350
|
+
constructor() {
|
|
351
|
+
super();
|
|
352
|
+
this.control.replaceChildren();
|
|
353
|
+
const doc = this.ownerDocument;
|
|
354
|
+
this.#contentObserver = new doc.defaultView.ResizeObserver((entries) => {
|
|
355
|
+
for (const { target } of entries) {
|
|
356
|
+
const region = target.closest(".region");
|
|
357
|
+
region.style.setProperty("--drawer-content-width", `${target.offsetWidth}px`);
|
|
358
|
+
region.style.setProperty("--drawer-content-height", `${target.offsetHeight}px`);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const style = doc.createElement("style");
|
|
362
|
+
style.textContent = `:host{min-height:0;overflow:hidden}
|
|
363
|
+
.control-box{height:100%;min-height:0;display:grid;
|
|
364
|
+
grid-template-columns:var(--left,0px) minmax(0,1fr) var(--right,0px);
|
|
365
|
+
grid-template-rows:var(--top,0px) minmax(0,1fr) var(--bottom,0px);
|
|
366
|
+
grid-template-areas:'top top top' 'left center right' 'bottom bottom bottom'}
|
|
367
|
+
.control-box[data-design=sidebar]{grid-template-areas:'left top right' 'left center right' 'left bottom right'}
|
|
368
|
+
.region{position:relative;min-width:0;min-height:0;overflow:visible}
|
|
369
|
+
.viewport{height:100%;min-width:0;min-height:0;overflow:auto}
|
|
370
|
+
.region[data-side=left]>.viewport{padding-right:var(--region-splitter,0px)}
|
|
371
|
+
.region[data-side=right]>.viewport{padding-left:var(--region-splitter,0px)}
|
|
372
|
+
.region[data-side=top]>.viewport{padding-bottom:var(--region-splitter,0px)}
|
|
373
|
+
.region[data-side=bottom]>.viewport{padding-top:var(--region-splitter,0px)}
|
|
374
|
+
.viewport[hidden]{display:none}::slotted(*){min-height:0}
|
|
375
|
+
.separator{position:absolute;z-index:2;touch-action:none;user-select:none;background:var(--gramlot-splitter-background,var(--gramlot-border-control,#aaa))}
|
|
376
|
+
.separator[data-axis=x]{top:0;bottom:0;width:var(--gramlot-splitter-size,1px);cursor:col-resize}
|
|
377
|
+
.separator[data-axis=y]{left:0;right:0;height:var(--gramlot-splitter-size,1px);cursor:row-resize}
|
|
378
|
+
.separator[data-side=left]{right:0}.separator[data-side=right]{left:0}
|
|
379
|
+
.separator[data-side=top]{bottom:0}.separator[data-side=bottom]{top:0}
|
|
380
|
+
.separator:hover{background:var(--gramlot-splitter-hover,#91a4bd)}
|
|
381
|
+
.separator::before{content:'';position:absolute;inset:0}
|
|
382
|
+
.separator[data-axis=x]::before{left:calc((100% - max(100%,var(--gramlot-touch-target,12px))) / 2);width:max(100%,var(--gramlot-touch-target,12px))}
|
|
383
|
+
.separator[data-axis=y]::before{top:calc((100% - max(100%,var(--gramlot-touch-target,12px))) / 2);height:max(100%,var(--gramlot-touch-target,12px))}
|
|
384
|
+
.separator[data-draggable=false]{cursor:default;background:transparent}
|
|
385
|
+
.separator[data-draggable=false]::before{display:none}
|
|
386
|
+
.separator[data-axis=x][data-draggable=false]{width:0!important}
|
|
387
|
+
.separator[data-axis=y][data-draggable=false]{height:0!important}
|
|
388
|
+
.separator:focus-visible{outline:2px solid var(--gramlot-focus-ring,Highlight);outline-offset:1px}
|
|
389
|
+
.drawer{position:absolute;z-index:3;cursor:pointer;appearance:none;font:inherit;
|
|
390
|
+
font-size:var(--gramlot-drawer-font-size,11px);line-height:1.2;white-space:nowrap;
|
|
391
|
+
--drawer-background:var(--gramlot-drawer-background,#777);
|
|
392
|
+
color:var(--gramlot-drawer-color,hsl(from var(--drawer-background) 0 0 clamp(20,calc((60 - l) * 1000),100)));
|
|
393
|
+
background:var(--drawer-background);outline:none;
|
|
394
|
+
border:0;border-radius:0;box-shadow:none;
|
|
395
|
+
min-width:0;min-height:0;padding:0;
|
|
396
|
+
opacity:var(--gramlot-drawer-opacity,.45);
|
|
397
|
+
transition:opacity var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out),
|
|
398
|
+
transform var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out),
|
|
399
|
+
width var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out),
|
|
400
|
+
height var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out),
|
|
401
|
+
max-width var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out),
|
|
402
|
+
max-height var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out),
|
|
403
|
+
margin var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out)}
|
|
404
|
+
.drawer:hover,.drawer:focus-visible{opacity:var(--gramlot-drawer-active-opacity,1)}
|
|
405
|
+
[data-axis=x]>.drawer[data-resizable]{cursor:col-resize}
|
|
406
|
+
[data-axis=y]>.drawer[data-resizable]{cursor:row-resize}
|
|
407
|
+
@media (hover:hover){
|
|
408
|
+
[data-axis=x]>.drawer:hover{transform:scale(var(--gramlot-drawer-hover-cross-scale,1.75),var(--gramlot-drawer-hover-length-scale,1.12))}
|
|
409
|
+
[data-axis=y]>.drawer:hover{transform:scale(var(--gramlot-drawer-hover-length-scale,1.12),var(--gramlot-drawer-hover-cross-scale,1.75))}
|
|
410
|
+
.separator>.drawer:is([data-labelled],[data-icon]):hover{transform:scale(var(--gramlot-drawer-label-hover-scale,1.04))}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
[data-axis=x]>.drawer{--drawer-closed-width:var(--gramlot-drawer-thickness,4px);--drawer-closed-height:var(--gramlot-drawer-length,28px);
|
|
414
|
+
top:50%;margin-top:calc(var(--gramlot-drawer-length,28px) / -2);width:var(--drawer-closed-width);height:var(--drawer-closed-height)}
|
|
415
|
+
[data-axis=y]>.drawer{--drawer-closed-width:var(--gramlot-drawer-length,28px);--drawer-closed-height:var(--gramlot-drawer-thickness,4px);
|
|
416
|
+
left:50%;margin-left:calc(var(--gramlot-drawer-length,28px) / -2);width:var(--drawer-closed-width);height:var(--drawer-closed-height)}
|
|
417
|
+
.drawer::after{content:'';position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
|
|
418
|
+
width:max(100%,var(--gramlot-touch-target,var(--gramlot-drawer-hit-target,12px)));height:max(100%,var(--gramlot-touch-target,var(--gramlot-drawer-hit-target,12px)))}
|
|
419
|
+
.drawer{--drawer-reveal-width:var(--gramlot-drawer-reveal-width,var(--drawer-content-width,0px));--drawer-reveal-height:var(--gramlot-drawer-reveal-height,var(--drawer-content-height,0px))}
|
|
420
|
+
.drawer[data-labelled]{opacity:var(--gramlot-drawer-active-opacity,1)}
|
|
421
|
+
[data-side=left]>.drawer{transform-origin:left center;left:100%;border-left:0;border-radius:0 var(--gramlot-radius-sm,4px) var(--gramlot-radius-sm,4px) 0}
|
|
422
|
+
[data-side=right]>.drawer{transform-origin:right center;right:100%;border-right:0;border-radius:var(--gramlot-radius-sm,4px) 0 0 var(--gramlot-radius-sm,4px)}
|
|
423
|
+
[data-side=top]>.drawer{transform-origin:center top;top:100%;border-top:0;border-radius:0 0 var(--gramlot-radius-sm,4px) var(--gramlot-radius-sm,4px)}
|
|
424
|
+
[data-side=bottom]>.drawer{transform-origin:center bottom;bottom:100%;border-bottom:0;border-radius:var(--gramlot-radius-sm,4px) var(--gramlot-radius-sm,4px) 0 0}
|
|
425
|
+
.drawer:is([data-labelled],[data-icon]){width:max-content;height:max-content;padding:0}
|
|
426
|
+
.drawer-viewport{display:block;overflow:hidden;min-width:0;min-height:0;max-width:inherit;max-height:inherit}
|
|
427
|
+
.drawer-content{display:flex;width:max-content;height:max-content;align-items:center;gap:var(--gramlot-drawer-content-gap,4px);box-sizing:border-box;
|
|
428
|
+
padding:var(--gramlot-drawer-padding,2px 8px);overflow:hidden;pointer-events:none;
|
|
429
|
+
min-width:0;min-height:0}
|
|
430
|
+
.drawer-icon{display:block;flex:none;font-size:var(--gramlot-drawer-icon-size,1em);line-height:1}
|
|
431
|
+
.drawer-icon[hidden],.drawer-label:empty{display:none}
|
|
432
|
+
.drawer-label{display:block;flex:none;pointer-events:none}
|
|
433
|
+
.drawer-rotated .drawer-content{flex-direction:column;padding:var(--gramlot-drawer-padding,8px 2px)}
|
|
434
|
+
.drawer-rotated .drawer-label{writing-mode:vertical-rl;text-orientation:mixed}
|
|
435
|
+
.separator[data-axis=x]>.drawer.drawer-rotated{margin-top:0;translate:0 -50%}
|
|
436
|
+
/* Icon and caption reveal as one unit; the resting grip stays thin. */
|
|
437
|
+
.separator>.drawer.drawer-reveal:is([data-labelled],[data-icon]){--drawer-open:var(--gramlot-drawer-reveal,0);
|
|
438
|
+
opacity:calc(var(--gramlot-drawer-opacity,.45) + (var(--gramlot-drawer-active-opacity,1) - var(--gramlot-drawer-opacity,.45)) * var(--drawer-open));
|
|
439
|
+
display:grid;grid-template-columns:minmax(0,1fr);grid-template-rows:minmax(0,1fr);
|
|
440
|
+
min-width:0;min-height:0;
|
|
441
|
+
max-width:calc(var(--drawer-closed-width) + (var(--drawer-reveal-width) - var(--drawer-closed-width)) * var(--drawer-open));
|
|
442
|
+
max-height:calc(var(--drawer-closed-height) + (var(--drawer-reveal-height) - var(--drawer-closed-height)) * var(--drawer-open))}
|
|
443
|
+
.separator[data-axis=x]>.drawer.drawer-reveal{margin-top:0;translate:0 -50%}
|
|
444
|
+
[data-axis=y]>.drawer:is([data-labelled],[data-icon]){margin-left:0;translate:-50% 0}
|
|
445
|
+
.drawer-reveal .drawer-content{opacity:var(--drawer-open);
|
|
446
|
+
transition:opacity var(--gramlot-drawer-duration,.16s) var(--gramlot-drawer-easing,ease-out)}
|
|
447
|
+
@media (hover:none),(pointer:coarse){.separator>.drawer.drawer-reveal:is([data-labelled],[data-icon]){--drawer-open:1}}
|
|
448
|
+
@media (hover:hover){.separator>.drawer.drawer-reveal:is([data-labelled],[data-icon]):hover{--drawer-open:1;transform:scale(var(--gramlot-drawer-label-hover-scale,1.04))}}
|
|
449
|
+
.separator>.drawer:is([data-labelled],[data-icon]):is(:active,[data-active]){transform:scale(var(--gramlot-drawer-label-hover-scale,1.04))}
|
|
450
|
+
.separator>.drawer.drawer-reveal:is([data-labelled],[data-icon]):is(:focus-visible,:active,[data-active]){--drawer-open:1;transform:scale(var(--gramlot-drawer-label-hover-scale,1.04))}
|
|
451
|
+
@media (prefers-reduced-motion:reduce){.drawer,.drawer-reveal .drawer-content{transition:none}}
|
|
452
|
+
.drawer[hidden],.separator[hidden]{display:none}
|
|
453
|
+
.drag-cover{position:absolute;inset:0;z-index:4;display:none}
|
|
454
|
+
:host{position:relative}.drag-cover[data-active]{display:block}`;
|
|
455
|
+
for (const side of REGIONS) {
|
|
456
|
+
const cell = doc.createElement("div");
|
|
457
|
+
cell.className = "region";
|
|
458
|
+
cell.style.gridArea = side;
|
|
459
|
+
const viewport = doc.createElement("div");
|
|
460
|
+
viewport.className = "viewport";
|
|
461
|
+
cell.dataset.side = side;
|
|
462
|
+
viewport.id = `region-${side}`;
|
|
463
|
+
const slot = doc.createElement("slot");
|
|
464
|
+
slot.name = side;
|
|
465
|
+
viewport.append(slot);
|
|
466
|
+
cell.append(viewport);
|
|
467
|
+
const state = { cell, viewport, child: null, config: null, visibility: null, closed: false, suppressDrawerClick: false, size: null, splitterAttrs: {}, drawerAttrs: {} };
|
|
468
|
+
if (side !== "center") {
|
|
469
|
+
const separator = doc.createElement("div");
|
|
470
|
+
separator.className = "separator";
|
|
471
|
+
separator.dataset.side = side;
|
|
472
|
+
separator.dataset.axis = horizontal(side) ? "y" : "x";
|
|
473
|
+
separator.setAttribute("role", "separator");
|
|
474
|
+
separator.setAttribute("aria-label", `Resize ${side} region`);
|
|
475
|
+
separator.setAttribute("aria-orientation", horizontal(side) ? "horizontal" : "vertical");
|
|
476
|
+
separator.setAttribute("aria-controls", viewport.id);
|
|
477
|
+
const drawer = doc.createElement("button");
|
|
478
|
+
drawer.type = "button";
|
|
479
|
+
drawer.className = "drawer";
|
|
480
|
+
drawer.setAttribute("aria-controls", viewport.id);
|
|
481
|
+
const drawerIcon = doc.createElement("span");
|
|
482
|
+
drawerIcon.className = "drawer-icon";
|
|
483
|
+
drawerIcon.setAttribute("aria-hidden", "true");
|
|
484
|
+
const drawerLabel = doc.createElement("span");
|
|
485
|
+
drawerLabel.className = "drawer-label";
|
|
486
|
+
const drawerContent = doc.createElement("span");
|
|
487
|
+
drawerContent.className = "drawer-content";
|
|
488
|
+
const drawerViewport = doc.createElement("span");
|
|
489
|
+
drawerViewport.className = "drawer-viewport";
|
|
490
|
+
drawerContent.append(drawerIcon, drawerLabel);
|
|
491
|
+
drawerViewport.append(drawerContent);
|
|
492
|
+
drawer.append(drawerViewport);
|
|
493
|
+
separator.append(drawer);
|
|
494
|
+
cell.append(separator);
|
|
495
|
+
drawer.addEventListener("pointerdown", (evt) => {
|
|
496
|
+
if (evt.button === 0) drawer.setAttribute("data-active", "");
|
|
497
|
+
});
|
|
498
|
+
for (const event of ["pointerup", "pointercancel", "lostpointercapture", "pointerleave"]) {
|
|
499
|
+
drawer.addEventListener(event, (evt) => {
|
|
500
|
+
if (event !== "pointerleave" || !drawer.hasPointerCapture(evt.pointerId)) drawer.removeAttribute("data-active");
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
Object.assign(state, { separator, drawer, drawerIcon, drawerLabel, drawerContent });
|
|
504
|
+
separator.addEventListener("pointerdown", (evt) => this.#startDrag(side, evt));
|
|
505
|
+
separator.addEventListener("keydown", (evt) => this.#keyResize(side, evt));
|
|
506
|
+
drawer.addEventListener("click", (evt) => {
|
|
507
|
+
evt.stopPropagation();
|
|
508
|
+
if (state.suppressDrawerClick && evt.detail !== 0) {
|
|
509
|
+
state.suppressDrawerClick = false;
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
state.closed = !state.closed;
|
|
513
|
+
this.#render(side);
|
|
514
|
+
state.config.onToggle?.(evt, !state.closed);
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
this.#regions.set(side, state);
|
|
518
|
+
this.control.append(cell);
|
|
519
|
+
slot.addEventListener("slotchange", () => this.#sync());
|
|
520
|
+
}
|
|
521
|
+
this.cover = doc.createElement("div");
|
|
522
|
+
this.cover.className = "drag-cover";
|
|
523
|
+
this.shadowRoot.append(style, this.cover);
|
|
524
|
+
this.#observer = new doc.defaultView.MutationObserver(() => this.#sync());
|
|
525
|
+
}
|
|
526
|
+
connectedCallback() {
|
|
527
|
+
this.#observer.observe(this, { childList: true });
|
|
528
|
+
for (const state of this.#regions.values()) if (state.drawerContent) this.#contentObserver.observe(state.drawerContent);
|
|
529
|
+
this.#sync();
|
|
530
|
+
}
|
|
531
|
+
disconnectedCallback() {
|
|
532
|
+
this.#observer.disconnect();
|
|
533
|
+
this.#contentObserver.disconnect();
|
|
534
|
+
this.#stopDrag();
|
|
535
|
+
}
|
|
536
|
+
projectAttributes(attrs, adapt) {
|
|
537
|
+
const rest = super.projectAttributes(attrs, adapt);
|
|
538
|
+
if (!["headline", "sidebar"].includes(this.parameters.design)) throw new TypeError("Invalid border design");
|
|
539
|
+
if (Object.hasOwn(attrs, "liveSplitters")) throw new TypeError("Border splitters are always live; omit liveSplitters");
|
|
540
|
+
if (this.parameters.persist && !attrs.id) throw new TypeError("Persistent borderContainer requires a stable id");
|
|
541
|
+
this.control.dataset.design = this.parameters.design;
|
|
542
|
+
return rest;
|
|
543
|
+
}
|
|
544
|
+
#sync() {
|
|
545
|
+
const children = /* @__PURE__ */ new Map();
|
|
546
|
+
for (const child of this.children) {
|
|
547
|
+
const config = borderRegions.get(child);
|
|
548
|
+
if (!config) continue;
|
|
549
|
+
if (children.has(config.region)) throw new Error(`Duplicate border region: ${config.region}`);
|
|
550
|
+
children.set(config.region, child);
|
|
551
|
+
}
|
|
552
|
+
for (const [side, state] of this.#regions) {
|
|
553
|
+
const child = children.get(side) ?? null;
|
|
554
|
+
if (state.child !== child) {
|
|
555
|
+
if (this.#drag?.side === side) this.#stopDrag();
|
|
556
|
+
state.child = child;
|
|
557
|
+
state.config = null;
|
|
558
|
+
state.visibility = null;
|
|
559
|
+
state.size = null;
|
|
560
|
+
state.closed = false;
|
|
561
|
+
}
|
|
562
|
+
if (child) this.projectRegion(child);
|
|
563
|
+
else {
|
|
564
|
+
state.config = null;
|
|
565
|
+
this.#render(side);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
projectRegion(child) {
|
|
570
|
+
const config = borderRegions.get(child), side = config.region, state = this.#regions.get(side);
|
|
571
|
+
if (state.child !== child) {
|
|
572
|
+
this.#sync();
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
const before = state.config;
|
|
576
|
+
if (!before || before.size !== config.size) {
|
|
577
|
+
state.size = config.size || "auto";
|
|
578
|
+
if (!before && this.parameters.persist) {
|
|
579
|
+
const saved = this.ownerDocument.cookie.split("; ").find((item) => item.startsWith(`${this.#cookie(side)}=`));
|
|
580
|
+
if (saved) state.size = decodeURIComponent(saved.split("=").slice(1).join("="));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
if (!before || before.drawer !== config.drawer) state.closed = config.drawer === "close";
|
|
584
|
+
state.config = config;
|
|
585
|
+
if (side !== "center") {
|
|
586
|
+
state.splitterAttrs = applyAttributes(state.separator, config.splitterAttrs, state.splitterAttrs);
|
|
587
|
+
state.drawerAttrs = applyAttributes(state.drawer, config.drawerAttrs, state.drawerAttrs);
|
|
588
|
+
state.separator.classList.add("separator");
|
|
589
|
+
state.drawer.classList.add("drawer");
|
|
590
|
+
state.drawer.classList.toggle("drawer-reveal", config.minimize);
|
|
591
|
+
state.drawer.classList.toggle("drawer-rotated", !horizontal(side));
|
|
592
|
+
state.drawerLabel.innerHTML = config.label ?? "";
|
|
593
|
+
state.drawerIcon.textContent = config.icon ?? "";
|
|
594
|
+
state.drawerIcon.hidden = !config.icon;
|
|
595
|
+
state.drawer.toggleAttribute("data-icon", !!config.icon);
|
|
596
|
+
state.drawer.toggleAttribute("data-labelled", !!config.label);
|
|
597
|
+
}
|
|
598
|
+
this.#render(side);
|
|
599
|
+
}
|
|
600
|
+
#render(side) {
|
|
601
|
+
const state = this.#regions.get(side), c = state.config;
|
|
602
|
+
const visible = !!c && !c.hidden && !state.closed;
|
|
603
|
+
const previous = state.visibility;
|
|
604
|
+
state.visibility = c ? { open: visible, drawer: !!c.drawer } : null;
|
|
605
|
+
state.viewport.hidden = !visible;
|
|
606
|
+
if (side === "center") return;
|
|
607
|
+
this.control.style.setProperty(`--${side}`, visible ? state.size : "0px");
|
|
608
|
+
state.separator.hidden = !c || c.hidden || !c.splitter && !c.drawer;
|
|
609
|
+
const draggable = visible && c.splitter;
|
|
610
|
+
state.cell.style.setProperty("--region-splitter", draggable ? state.separator.style[horizontal(side) ? "height" : "width"] || "var(--gramlot-splitter-size,1px)" : "0px");
|
|
611
|
+
state.separator.dataset.draggable = String(!!draggable);
|
|
612
|
+
state.separator.setAttribute("role", draggable ? "separator" : "presentation");
|
|
613
|
+
state.separator.tabIndex = draggable ? 0 : -1;
|
|
614
|
+
state.drawer.hidden = !c?.drawer;
|
|
615
|
+
state.drawer.toggleAttribute("data-resizable", !!c?.splitter);
|
|
616
|
+
state.drawer.setAttribute("aria-expanded", String(visible));
|
|
617
|
+
state.drawer.setAttribute("aria-label", `${visible ? "Close" : "Open"} ${side} region`);
|
|
618
|
+
state.drawer.title = c?.drawerAttrs.title ?? state.drawer.getAttribute("aria-label");
|
|
619
|
+
state.separator.setAttribute("aria-valuemin", String(c?.min ?? 0));
|
|
620
|
+
state.separator.setAttribute("aria-valuenow", String(Math.round(this.#extent(side))));
|
|
621
|
+
if (c && Number.isFinite(c.max)) state.separator.setAttribute("aria-valuemax", String(c.max));
|
|
622
|
+
else state.separator.removeAttribute("aria-valuemax");
|
|
623
|
+
if (c && previous && (previous.drawer || c.drawer) && previous.open !== visible) {
|
|
624
|
+
c.notifications.closableChange({ open: visible, region: side });
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
#extent(side) {
|
|
628
|
+
const box = this.#regions.get(side).cell.getBoundingClientRect();
|
|
629
|
+
return horizontal(side) ? box.height : box.width;
|
|
630
|
+
}
|
|
631
|
+
#resize(side, desired) {
|
|
632
|
+
const state = this.#regions.get(side), c = state.config;
|
|
633
|
+
const opposite = { left: "right", right: "left", top: "bottom", bottom: "top" }[side];
|
|
634
|
+
const box = this.control.getBoundingClientRect();
|
|
635
|
+
const available = Math.max(0, (horizontal(side) ? box.height : box.width) - this.#extent(opposite));
|
|
636
|
+
const size = Math.max(Math.min(c.min, available), Math.min(desired, c.max, available));
|
|
637
|
+
state.size = `${size}px`;
|
|
638
|
+
this.#render(side);
|
|
639
|
+
}
|
|
640
|
+
#keyResize(side, evt) {
|
|
641
|
+
const state = this.#regions.get(side);
|
|
642
|
+
if (evt.target !== state.separator || !state.config?.splitter || state.closed) return;
|
|
643
|
+
const keys = horizontal(side) ? ["ArrowUp", "ArrowDown"] : ["ArrowLeft", "ArrowRight"];
|
|
644
|
+
if (!keys.includes(evt.key)) return;
|
|
645
|
+
evt.preventDefault();
|
|
646
|
+
const sign = ["left", "top"].includes(side) ? 1 : -1;
|
|
647
|
+
const previousSize = this.#extent(side);
|
|
648
|
+
this.#resize(side, previousSize + (evt.key === keys[0] ? -1 : 1) * sign);
|
|
649
|
+
this.#commitResize(side, previousSize);
|
|
650
|
+
}
|
|
651
|
+
#startDrag(side, evt) {
|
|
652
|
+
const state = this.#regions.get(side);
|
|
653
|
+
if (evt.button !== 0) return;
|
|
654
|
+
state.suppressDrawerClick = false;
|
|
655
|
+
if (!state.config?.splitter) return;
|
|
656
|
+
const fromDrawer = state.drawer.contains(evt.target);
|
|
657
|
+
if (!fromDrawer) evt.preventDefault();
|
|
658
|
+
this.#stopDrag();
|
|
659
|
+
const axis = horizontal(side) ? "clientY" : "clientX", start = evt[axis], size = this.#extent(side);
|
|
660
|
+
const sign = ["left", "top"].includes(side) ? 1 : -1;
|
|
661
|
+
const controller = new this.ownerDocument.defaultView.AbortController();
|
|
662
|
+
const captureTarget = fromDrawer ? state.drawer : state.separator, pointerId = evt.pointerId;
|
|
663
|
+
let resizing = !fromDrawer;
|
|
664
|
+
this.#drag = { side, controller, captureTarget, pointerId };
|
|
665
|
+
captureTarget.setPointerCapture(pointerId);
|
|
666
|
+
if (resizing) this.cover.setAttribute("data-active", "");
|
|
667
|
+
const options = { signal: controller.signal };
|
|
668
|
+
captureTarget.addEventListener("pointermove", (event) => {
|
|
669
|
+
if (event.pointerId !== pointerId) return;
|
|
670
|
+
const distance = event[axis] - start;
|
|
671
|
+
if (!resizing && Math.abs(distance) < 4) return;
|
|
672
|
+
state.suppressDrawerClick = fromDrawer;
|
|
673
|
+
if (state.closed && sign * distance <= 0) return;
|
|
674
|
+
const opening = state.closed;
|
|
675
|
+
state.closed = false;
|
|
676
|
+
resizing = true;
|
|
677
|
+
this.cover.setAttribute("data-active", "");
|
|
678
|
+
this.#resize(side, size + sign * distance);
|
|
679
|
+
if (opening) state.config.onToggle?.(event, true);
|
|
680
|
+
}, options);
|
|
681
|
+
const finish = (event) => {
|
|
682
|
+
if (event.pointerId !== pointerId) return;
|
|
683
|
+
this.#stopDrag();
|
|
684
|
+
if (resizing) this.#commitResize(side, size);
|
|
685
|
+
};
|
|
686
|
+
captureTarget.addEventListener("pointerup", finish, options);
|
|
687
|
+
captureTarget.addEventListener("pointercancel", finish, options);
|
|
688
|
+
captureTarget.addEventListener("lostpointercapture", finish, options);
|
|
689
|
+
}
|
|
690
|
+
#stopDrag() {
|
|
691
|
+
if (!this.#drag) return;
|
|
692
|
+
const { controller, captureTarget, pointerId } = this.#drag;
|
|
693
|
+
this.#drag = null;
|
|
694
|
+
controller.abort();
|
|
695
|
+
this.cover.removeAttribute("data-active");
|
|
696
|
+
if (captureTarget.hasPointerCapture(pointerId)) captureTarget.releasePointerCapture(pointerId);
|
|
697
|
+
}
|
|
698
|
+
#commitResize(side, previousSize) {
|
|
699
|
+
const state = this.#regions.get(side), size = this.#extent(side);
|
|
700
|
+
if (size === previousSize) return;
|
|
701
|
+
this.#save(side);
|
|
702
|
+
state.config.notifications.splitterResize({ region: side, size: `${size}px`, previousSize: `${previousSize}px` });
|
|
703
|
+
}
|
|
704
|
+
#cookie(side) {
|
|
705
|
+
return encodeURIComponent(`gramlot-border-${this.id}-${side}`);
|
|
706
|
+
}
|
|
707
|
+
#save(side) {
|
|
708
|
+
if (this.parameters.persist) this.ownerDocument.cookie = `${this.#cookie(side)}=${encodeURIComponent(this.#regions.get(side).size)}; Path=/; SameSite=Lax`;
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
// node_modules/@gramlot/gramlot/src/components/selected-container.js
|
|
714
|
+
var selectionPanels = /* @__PURE__ */ new WeakMap();
|
|
715
|
+
var panelSequence = 0;
|
|
716
|
+
function projectSelectionChild(element, attrs, actions) {
|
|
717
|
+
const { pageName = null, title = "", icon = "", badge = null, badge_color = "", closable = false, onClose, hidden = false, disabled = false, ...rest } = attrs;
|
|
718
|
+
if (pageName !== null && typeof pageName !== "string") throw new TypeError("pageName must be a string");
|
|
719
|
+
if (typeof closable !== "boolean") throw new TypeError("closable must be a boolean");
|
|
720
|
+
const slot = selectionPanels.get(element)?.slot ?? `panel-${++panelSequence}`;
|
|
721
|
+
selectionPanels.set(element, {
|
|
722
|
+
pageName,
|
|
723
|
+
title,
|
|
724
|
+
icon,
|
|
725
|
+
badge,
|
|
726
|
+
badge_color,
|
|
727
|
+
closable,
|
|
728
|
+
hidden,
|
|
729
|
+
disabled,
|
|
730
|
+
slot,
|
|
731
|
+
remove: actions.remove,
|
|
732
|
+
allowClose: () => onClose == null ? true : actions.invoke("onClose", onClose, {})
|
|
733
|
+
});
|
|
734
|
+
rest.slot = slot;
|
|
735
|
+
if (element.parentElement) element.parentElement.scheduleSelection();
|
|
736
|
+
return rest;
|
|
737
|
+
}
|
|
738
|
+
function SelectedContainer(HTMLElement) {
|
|
739
|
+
return class extends Container(HTMLElement) {
|
|
740
|
+
static projectChild(element, attrs, actions) {
|
|
741
|
+
return projectSelectionChild(element, attrs, actions);
|
|
742
|
+
}
|
|
743
|
+
#panels = /* @__PURE__ */ new Map();
|
|
744
|
+
#selected = null;
|
|
745
|
+
#initialized = false;
|
|
746
|
+
#scheduled = false;
|
|
747
|
+
#requested = { selectedPage: null, selected: null };
|
|
748
|
+
#observer;
|
|
749
|
+
#context;
|
|
750
|
+
constructor() {
|
|
751
|
+
super();
|
|
752
|
+
this.control.replaceChildren();
|
|
753
|
+
this.control.className = "control-box selection";
|
|
754
|
+
const style = this.ownerDocument.createElement("style");
|
|
755
|
+
style.textContent = `:host{min-height:0;overflow:hidden}.selection{height:100%;min-height:0;display:flex;flex-direction:column}
|
|
756
|
+
.panels{flex:1;min-height:0;min-width:0;position:relative}.panel{height:100%;min-height:0;overflow:auto}
|
|
757
|
+
.panel[hidden]{display:none}::slotted(*){height:100%;min-height:0}`;
|
|
758
|
+
this.panelArea = this.ownerDocument.createElement("div");
|
|
759
|
+
this.panelArea.className = "panels";
|
|
760
|
+
this.control.append(this.panelArea);
|
|
761
|
+
this.shadowRoot.append(style);
|
|
762
|
+
this.#observer = new this.ownerDocument.defaultView.MutationObserver(() => this.scheduleSelection());
|
|
763
|
+
}
|
|
764
|
+
setSourceContext(context) {
|
|
765
|
+
this.#context = context;
|
|
766
|
+
}
|
|
767
|
+
connectedCallback() {
|
|
768
|
+
this.#observer.observe(this, { childList: true });
|
|
769
|
+
this.scheduleSelection();
|
|
770
|
+
}
|
|
771
|
+
disconnectedCallback() {
|
|
772
|
+
this.#observer.disconnect();
|
|
773
|
+
}
|
|
774
|
+
scheduleSelection() {
|
|
775
|
+
if (this.#scheduled) return;
|
|
776
|
+
this.#scheduled = true;
|
|
777
|
+
queueMicrotask(() => {
|
|
778
|
+
this.#scheduled = false;
|
|
779
|
+
if (this.isConnected) this.#sync();
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
projectAttributes(attrs, adapt) {
|
|
783
|
+
const rest = super.projectAttributes(attrs, adapt);
|
|
784
|
+
const { selected, selectedPage } = this.parameters;
|
|
785
|
+
if (selected != null && (!Number.isInteger(selected) || selected < 0)) throw new TypeError("selected must be a nonnegative index or null");
|
|
786
|
+
if (selectedPage != null && typeof selectedPage !== "string") throw new TypeError("selectedPage must be a string or null");
|
|
787
|
+
this.scheduleSelection();
|
|
788
|
+
return rest;
|
|
789
|
+
}
|
|
790
|
+
#sync() {
|
|
791
|
+
const records = [], names = /* @__PURE__ */ new Set();
|
|
792
|
+
for (const child of this.children) {
|
|
793
|
+
const config = selectionPanels.get(child);
|
|
794
|
+
if (!config) continue;
|
|
795
|
+
if (config.pageName != null && names.has(config.pageName)) throw new Error(`Duplicate pageName: ${config.pageName}`);
|
|
796
|
+
names.add(config.pageName);
|
|
797
|
+
let record = this.#panels.get(child);
|
|
798
|
+
if (!record) {
|
|
799
|
+
const viewport = this.ownerDocument.createElement("div");
|
|
800
|
+
viewport.className = "panel";
|
|
801
|
+
viewport.hidden = true;
|
|
802
|
+
viewport.id = config.slot;
|
|
803
|
+
viewport.setAttribute("role", "group");
|
|
804
|
+
const slot = this.ownerDocument.createElement("slot");
|
|
805
|
+
slot.name = config.slot;
|
|
806
|
+
viewport.append(slot);
|
|
807
|
+
this.panelArea.append(viewport);
|
|
808
|
+
record = { child, viewport, config, index: records.length };
|
|
809
|
+
this.#panels.set(child, record);
|
|
810
|
+
}
|
|
811
|
+
record.config = config;
|
|
812
|
+
records.push(record);
|
|
813
|
+
}
|
|
814
|
+
const previousIndex = this.#selected?.index ?? 0;
|
|
815
|
+
for (const [child, record] of this.#panels) {
|
|
816
|
+
if (!records.includes(record)) {
|
|
817
|
+
record.viewport.remove();
|
|
818
|
+
this.#panels.delete(child);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
records.forEach((record, index) => {
|
|
822
|
+
record.index = index;
|
|
823
|
+
});
|
|
824
|
+
this.#panels = new Map(records.map((record) => [record.child, record]));
|
|
825
|
+
const { selectedPage, selected } = this.parameters;
|
|
826
|
+
const nameChanged = !Object.is(selectedPage, this.#requested.selectedPage);
|
|
827
|
+
const indexChanged = !Object.is(selected, this.#requested.selected);
|
|
828
|
+
this.#requested = { selectedPage, selected };
|
|
829
|
+
const available = (record) => record && !record.config.hidden && !record.config.disabled;
|
|
830
|
+
let target = this.#selected;
|
|
831
|
+
if (!this.#initialized || nameChanged || indexChanged) {
|
|
832
|
+
const requested = nameChanged || !this.#initialized && selectedPage != null ? records.find((record) => record.config.pageName === selectedPage) : records[selected];
|
|
833
|
+
if (available(requested)) target = requested;
|
|
834
|
+
}
|
|
835
|
+
if (!records.includes(target) || !available(target)) {
|
|
836
|
+
target = records.slice(previousIndex).find(available) ?? records.slice(0, previousIndex).reverse().find(available) ?? null;
|
|
837
|
+
}
|
|
838
|
+
this.renderTabs(records, target);
|
|
839
|
+
if (records.length || this.#initialized) this.#select(target);
|
|
840
|
+
}
|
|
841
|
+
renderTabs() {
|
|
842
|
+
}
|
|
843
|
+
selectPanel(record) {
|
|
844
|
+
if (record.config.hidden || record.config.disabled) return;
|
|
845
|
+
this.#select(record);
|
|
846
|
+
}
|
|
847
|
+
#payload(record, selected) {
|
|
848
|
+
const page = record.config.pageName ?? record.index;
|
|
849
|
+
return { page, index: record.index, selected, change: `${page}_${selected ? "show" : "hide"}` };
|
|
850
|
+
}
|
|
851
|
+
#writeSelection(target) {
|
|
852
|
+
this.#context.writeParameter("selectedPage", target?.config.pageName ?? null);
|
|
853
|
+
this.#context.writeParameter("selected", target?.index ?? null);
|
|
854
|
+
}
|
|
855
|
+
#select(target) {
|
|
856
|
+
const previous = this.#selected, initial = !this.#initialized;
|
|
857
|
+
if (!initial && target === previous) {
|
|
858
|
+
this.#writeSelection(target);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
861
|
+
if (!initial) {
|
|
862
|
+
if (previous) this.#context.publish("hiding", { pageName: previous.config.pageName, index: previous.index });
|
|
863
|
+
if (target) this.#context.publish("showing", { pageName: target.config.pageName, index: target.index });
|
|
864
|
+
}
|
|
865
|
+
this.#selected = target;
|
|
866
|
+
this.#initialized = true;
|
|
867
|
+
for (const record of this.#panels.values()) record.viewport.hidden = record !== target;
|
|
868
|
+
this.renderTabs([...this.#panels.values()], target);
|
|
869
|
+
this.#writeSelection(target);
|
|
870
|
+
if (!initial && previous) this.#context.publish("selected", this.#payload(previous, false));
|
|
871
|
+
if (target) this.#context.publish("selected", this.#payload(target, true));
|
|
872
|
+
}
|
|
873
|
+
closePanel(record) {
|
|
874
|
+
if (!record.config.closable) return;
|
|
875
|
+
const allowed = record.config.allowClose();
|
|
876
|
+
if (typeof allowed !== "boolean") throw new TypeError("onClose must return a synchronous boolean");
|
|
877
|
+
if (!allowed) return;
|
|
878
|
+
const payload = { pageName: record.config.pageName, index: record.index, wasSelected: record === this.#selected };
|
|
879
|
+
this.#context.publish("closing", payload);
|
|
880
|
+
record.config.remove();
|
|
881
|
+
this.#sync();
|
|
882
|
+
this.#context.publish("closed", payload);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
SelectedContainer.parameters = {
|
|
887
|
+
selectedPage: { dtype: "T", default: null, nullable: true, binding: "twoWay" },
|
|
888
|
+
selected: { dtype: "L", default: null, nullable: true, binding: "twoWay" }
|
|
889
|
+
};
|
|
890
|
+
var selectionChildParameters = { onClose: { inline: [] } };
|
|
891
|
+
var StackContainer = defineComponent({
|
|
892
|
+
name: "stackContainer",
|
|
893
|
+
tag: "gramlot-stack-container",
|
|
894
|
+
base: SelectedContainer,
|
|
895
|
+
subTags: "*",
|
|
896
|
+
sourceContext: true,
|
|
897
|
+
childParameters: selectionChildParameters,
|
|
898
|
+
implementation: (Base) => class extends Base {
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
var TabContainer = defineComponent({
|
|
902
|
+
name: "tabContainer",
|
|
903
|
+
tag: "gramlot-tab-container",
|
|
904
|
+
base: SelectedContainer,
|
|
905
|
+
subTags: "*",
|
|
906
|
+
sourceContext: true,
|
|
907
|
+
childParameters: selectionChildParameters,
|
|
908
|
+
parameters: {
|
|
909
|
+
tabMode: { dtype: "T", default: "tabs", binding: "read", enum: ["tabs", "underline"] },
|
|
910
|
+
tabPosition: { dtype: "T", default: "top", binding: "read", enum: ["top", "bottom", "left", "right"] },
|
|
911
|
+
tabAlign: { dtype: "T", default: "start", binding: "read", enum: ["start", "center", "end"] }
|
|
912
|
+
},
|
|
913
|
+
implementation: (Base) => class extends Base {
|
|
914
|
+
#tabs = /* @__PURE__ */ new Map();
|
|
915
|
+
constructor() {
|
|
916
|
+
super();
|
|
917
|
+
const doc = this.ownerDocument;
|
|
918
|
+
this.tabStrip = doc.createElement("div");
|
|
919
|
+
this.tabStrip.className = "tabs";
|
|
920
|
+
this.tabStrip.setAttribute("role", "tablist");
|
|
921
|
+
this.control.prepend(this.tabStrip);
|
|
922
|
+
const style = doc.createElement("style");
|
|
923
|
+
style.textContent = `
|
|
924
|
+
.selection{min-width:0;--tab-border:var(--gramlot-tab-border,var(--gramlot-border,#aaa));
|
|
925
|
+
--tab-radius:var(--gramlot-tab-radius,4px);--tab-indicator:var(--gramlot-tab-indicator-color,var(--gramlot-action,#3766b5))}
|
|
926
|
+
.panels{background:var(--gramlot-tab-selected-background,var(--gramlot-surface,white))}
|
|
927
|
+
.tabs{display:var(--gramlot-tab-strip-display,flex);flex:none;align-items:stretch;gap:var(--gramlot-tab-gap,3px);min-width:0;min-height:0;
|
|
928
|
+
padding:var(--gramlot-tab-strip-padding,4px);overflow:auto;overscroll-behavior:contain;
|
|
929
|
+
border-bottom:1px solid var(--tab-border);background:var(--gramlot-tab-background,var(--gramlot-surface-subtle,#eef1f5))}
|
|
930
|
+
[data-align=center] .tabs{justify-content:safe center}
|
|
931
|
+
[data-align=end] .tabs{justify-content:safe flex-end}
|
|
932
|
+
.tab-entry{position:relative;display:flex;flex:none;align-items:stretch;border:1px solid transparent;border-bottom:0;
|
|
933
|
+
border-radius:var(--tab-radius) var(--tab-radius) 0 0;color:var(--gramlot-tab-color,var(--gramlot-text-secondary,#555))}
|
|
934
|
+
.tab-entry:has([aria-selected=true]){border-color:var(--tab-border);color:var(--gramlot-tab-selected-color,var(--gramlot-ink,#222));
|
|
935
|
+
background:var(--gramlot-tab-selected-background,var(--gramlot-surface,white))}
|
|
936
|
+
button{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:var(--gramlot-tab-content-gap,.4em);
|
|
937
|
+
font:inherit;line-height:1.2;color:inherit;border:0;background:transparent;cursor:pointer;white-space:nowrap;min-width:0;
|
|
938
|
+
padding:var(--gramlot-tab-padding,4px 10px);min-height:max(var(--gramlot-touch-target,0px),var(--gramlot-tab-height,28px));border-radius:inherit}
|
|
939
|
+
button[aria-selected=true]{font-weight:600}
|
|
940
|
+
.icon,.badge{flex:none}.icon:empty{display:none}.caption{overflow:hidden;text-overflow:ellipsis}
|
|
941
|
+
.badge{font-size:var(--gramlot-tab-badge-size,.8em);font-weight:600;line-height:1.3;border-radius:999px;
|
|
942
|
+
min-width:1.3em;padding:var(--gramlot-tab-badge-padding,0 .35em);text-align:center;
|
|
943
|
+
background:var(--gramlot-tab-badge-background,var(--gramlot-brand-blue,#3766b5));color:var(--gramlot-tab-badge-color,white)}
|
|
944
|
+
@media(hover:hover){button:not(:disabled):hover{background:var(--gramlot-tab-hover-background,var(--gramlot-selected,#e8f1fc))}}
|
|
945
|
+
button:disabled{opacity:.5;cursor:default}
|
|
946
|
+
button:focus-visible{outline:2px solid var(--gramlot-focus-ring,Highlight);outline-offset:-3px}
|
|
947
|
+
.close{align-self:center;flex:none;min-height:var(--gramlot-touch-target,20px);min-width:var(--gramlot-touch-target,20px);
|
|
948
|
+
padding:0;margin-inline-end:3px;border-radius:var(--tab-radius);font-size:1.15em}
|
|
949
|
+
[hidden]{display:none!important}
|
|
950
|
+
[data-position=top] .tabs{padding-bottom:0}
|
|
951
|
+
[data-position=bottom]{flex-direction:column-reverse}
|
|
952
|
+
[data-position=bottom] .tabs{border-bottom:0;border-top:1px solid var(--tab-border);padding-top:0}
|
|
953
|
+
[data-position=bottom] .tab-entry{border-bottom:1px solid transparent;border-top:0;border-radius:0 0 var(--tab-radius) var(--tab-radius)}
|
|
954
|
+
[data-position=left]{flex-direction:row}[data-position=right]{flex-direction:row-reverse}
|
|
955
|
+
:is([data-position=left],[data-position=right]) .tabs{flex-direction:column;max-width:min(40%,var(--gramlot-tab-side-width,12rem));border-bottom:0}
|
|
956
|
+
:is([data-position=left],[data-position=right]) button[role=tab]{flex:1;justify-content:flex-start}
|
|
957
|
+
[data-position=left] .tabs{border-right:1px solid var(--tab-border);padding-right:0}
|
|
958
|
+
[data-position=right] .tabs{border-left:1px solid var(--tab-border);padding-left:0}
|
|
959
|
+
[data-position=left] .tab-entry{border-bottom:1px solid transparent;border-right:0;border-radius:var(--tab-radius) 0 0 var(--tab-radius)}
|
|
960
|
+
[data-position=right] .tab-entry{border-bottom:1px solid transparent;border-left:0;border-radius:0 var(--tab-radius) var(--tab-radius) 0}
|
|
961
|
+
.tab-entry:has([aria-selected=true]){border-color:var(--tab-border)}
|
|
962
|
+
[data-mode=underline] .tabs{background:transparent}
|
|
963
|
+
[data-mode=underline] .tab-entry{border:0;border-radius:0;background:transparent}
|
|
964
|
+
[data-mode=underline] .tab-entry:has([aria-selected=true])::after{content:'';position:absolute;pointer-events:none;
|
|
965
|
+
background:var(--tab-indicator);height:var(--gramlot-tab-indicator-size,2px);bottom:0;left:0;right:0}
|
|
966
|
+
[data-mode=underline][data-position=bottom] .tab-entry::after{top:0;bottom:auto}
|
|
967
|
+
[data-mode=underline]:is([data-position=left],[data-position=right]) .tab-entry::after{height:auto;width:var(--gramlot-tab-indicator-size,2px);top:0;bottom:0}
|
|
968
|
+
[data-mode=underline][data-position=left] .tab-entry::after{left:auto;right:0}
|
|
969
|
+
[data-mode=underline][data-position=right] .tab-entry::after{left:0;right:auto}`;
|
|
970
|
+
this.shadowRoot.append(style);
|
|
971
|
+
}
|
|
972
|
+
projectAttributes(attrs, adapt) {
|
|
973
|
+
const rest = super.projectAttributes(attrs, adapt);
|
|
974
|
+
const { tabMode, tabPosition, tabAlign } = this.parameters;
|
|
975
|
+
if (!["tabs", "underline"].includes(tabMode)) throw new TypeError("Invalid tabMode");
|
|
976
|
+
if (!["top", "bottom", "left", "right"].includes(tabPosition)) throw new TypeError("Invalid tabPosition");
|
|
977
|
+
if (!["start", "center", "end"].includes(tabAlign)) throw new TypeError("Invalid tabAlign");
|
|
978
|
+
this.control.dataset.mode = tabMode;
|
|
979
|
+
this.control.dataset.position = tabPosition;
|
|
980
|
+
this.control.dataset.align = tabAlign;
|
|
981
|
+
this.tabStrip.setAttribute("aria-orientation", ["left", "right"].includes(tabPosition) ? "vertical" : "horizontal");
|
|
982
|
+
return rest;
|
|
983
|
+
}
|
|
984
|
+
renderTabs(records, selected) {
|
|
985
|
+
for (const [record, entry] of this.#tabs) {
|
|
986
|
+
if (!records.includes(record)) {
|
|
987
|
+
entry.root.remove();
|
|
988
|
+
this.#tabs.delete(record);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
for (const [index, record] of records.entries()) {
|
|
992
|
+
let entry = this.#tabs.get(record);
|
|
993
|
+
if (!entry) {
|
|
994
|
+
const doc = this.ownerDocument, root = doc.createElement("div"), button = doc.createElement("button"), close = doc.createElement("button");
|
|
995
|
+
root.className = "tab-entry";
|
|
996
|
+
button.type = close.type = "button";
|
|
997
|
+
button.setAttribute("role", "tab");
|
|
998
|
+
button.id = `tab-${record.config.slot}`;
|
|
999
|
+
button.setAttribute("aria-controls", record.viewport.id);
|
|
1000
|
+
record.viewport.setAttribute("role", "tabpanel");
|
|
1001
|
+
record.viewport.setAttribute("aria-labelledby", button.id);
|
|
1002
|
+
const icon = doc.createElement("span"), caption2 = doc.createElement("span"), badge = doc.createElement("span");
|
|
1003
|
+
icon.className = "icon";
|
|
1004
|
+
icon.setAttribute("aria-hidden", "true");
|
|
1005
|
+
caption2.className = "caption";
|
|
1006
|
+
badge.className = "badge";
|
|
1007
|
+
button.append(icon, caption2, badge);
|
|
1008
|
+
close.className = "close";
|
|
1009
|
+
close.textContent = "\xD7";
|
|
1010
|
+
button.addEventListener("click", () => this.selectPanel(record));
|
|
1011
|
+
close.addEventListener("click", () => {
|
|
1012
|
+
this.closePanel(record);
|
|
1013
|
+
if (!close.isConnected) this.tabStrip.querySelector("[aria-selected=true]")?.focus();
|
|
1014
|
+
});
|
|
1015
|
+
button.addEventListener("keydown", (event) => {
|
|
1016
|
+
if (event.key === "Delete") {
|
|
1017
|
+
event.preventDefault();
|
|
1018
|
+
this.closePanel(record);
|
|
1019
|
+
this.tabStrip.querySelector("[aria-selected=true]")?.focus();
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
const vertical = ["left", "right"].includes(this.parameters.tabPosition);
|
|
1023
|
+
const previous = vertical ? "ArrowUp" : "ArrowLeft", nextKey = vertical ? "ArrowDown" : "ArrowRight";
|
|
1024
|
+
if (![previous, nextKey, "Home", "End"].includes(event.key)) return;
|
|
1025
|
+
event.preventDefault();
|
|
1026
|
+
const available = [...this.#tabs].filter(([r]) => !r.config.hidden && !r.config.disabled).sort(([a], [b]) => a.index - b.index);
|
|
1027
|
+
let index2 = available.findIndex(([r]) => r === record);
|
|
1028
|
+
index2 = event.key === "Home" ? 0 : event.key === "End" ? available.length - 1 : (index2 + (event.key === nextKey ? 1 : -1) + available.length) % available.length;
|
|
1029
|
+
const [next, tab] = available[index2];
|
|
1030
|
+
this.selectPanel(next);
|
|
1031
|
+
tab.button.focus();
|
|
1032
|
+
tab.button.scrollIntoView({ block: "nearest", inline: "nearest" });
|
|
1033
|
+
});
|
|
1034
|
+
root.append(button, close);
|
|
1035
|
+
this.tabStrip.append(root);
|
|
1036
|
+
entry = { root, button, close, icon, caption: caption2, badge };
|
|
1037
|
+
this.#tabs.set(record, entry);
|
|
1038
|
+
}
|
|
1039
|
+
if (this.tabStrip.children[index] !== entry.root) this.tabStrip.insertBefore(entry.root, this.tabStrip.children[index] ?? null);
|
|
1040
|
+
entry.root.hidden = !!record.config.hidden;
|
|
1041
|
+
const caption = record.config.title || record.config.pageName || String(record.index + 1);
|
|
1042
|
+
entry.caption.textContent = caption;
|
|
1043
|
+
entry.button.title = caption;
|
|
1044
|
+
entry.icon.textContent = record.config.icon;
|
|
1045
|
+
entry.badge.textContent = record.config.badge ?? "";
|
|
1046
|
+
entry.badge.hidden = record.config.badge == null || record.config.badge === "";
|
|
1047
|
+
entry.badge.style.backgroundColor = record.config.badge_color;
|
|
1048
|
+
entry.button.setAttribute("aria-label", entry.badge.hidden ? caption : `${caption} ${record.config.badge}`);
|
|
1049
|
+
entry.button.disabled = !!record.config.disabled;
|
|
1050
|
+
entry.button.setAttribute("aria-selected", String(record === selected));
|
|
1051
|
+
entry.button.tabIndex = record === selected ? 0 : -1;
|
|
1052
|
+
entry.close.hidden = !record.config.closable;
|
|
1053
|
+
entry.close.disabled = !!record.config.disabled;
|
|
1054
|
+
entry.close.setAttribute("aria-label", `Close ${caption}`);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
|
|
223
1060
|
// node_modules/@gramlot/gramlot/src/components/popup.js
|
|
224
1061
|
var VIEWPORT_MARGIN = 8;
|
|
225
1062
|
var ANCHOR_GAP = 3;
|
|
226
|
-
function positionPopup(popup, anchor, { align = "start", matchWidth = false } = {}) {
|
|
1063
|
+
function positionPopup(popup, anchor, { align = "start", matchWidth = false, inset = 0 } = {}) {
|
|
227
1064
|
const view = anchor.ownerDocument.defaultView;
|
|
228
1065
|
const field = anchor.getBoundingClientRect();
|
|
229
1066
|
const availableWidth = Math.max(0, view.innerWidth - 2 * VIEWPORT_MARGIN);
|
|
230
1067
|
popup.style.setProperty("--gramlot-popup-available-width", `${availableWidth}px`);
|
|
231
|
-
if (matchWidth) popup.style.width = `${Math.min(field.width, availableWidth)}px`;
|
|
1068
|
+
if (matchWidth) popup.style.width = `${Math.min(Math.max(0, field.width - 2 * inset), availableWidth)}px`;
|
|
232
1069
|
popup.style.removeProperty("--gramlot-popup-available-height");
|
|
233
1070
|
const natural = popup.getBoundingClientRect();
|
|
234
1071
|
const below = Math.max(0, view.innerHeight - VIEWPORT_MARGIN - field.bottom - ANCHOR_GAP);
|
|
@@ -236,7 +1073,7 @@ function positionPopup(popup, anchor, { align = "start", matchWidth = false } =
|
|
|
236
1073
|
const upwards = natural.height > below && above > below;
|
|
237
1074
|
popup.style.setProperty("--gramlot-popup-available-height", `${upwards ? above : below}px`);
|
|
238
1075
|
const size = popup.getBoundingClientRect();
|
|
239
|
-
const left = align === "end" ? field.right - size.width : field.left;
|
|
1076
|
+
const left = align === "end" ? field.right - size.width - inset : field.left + inset;
|
|
240
1077
|
const top = upwards ? field.top - size.height - ANCHOR_GAP : field.bottom + ANCHOR_GAP;
|
|
241
1078
|
popup.style.left = `${Math.max(VIEWPORT_MARGIN, Math.min(left, view.innerWidth - size.width - VIEWPORT_MARGIN))}px`;
|
|
242
1079
|
popup.style.top = `${Math.max(VIEWPORT_MARGIN, Math.min(top, view.innerHeight - size.height - VIEWPORT_MARGIN))}px`;
|
|
@@ -318,28 +1155,6 @@ var FormletLabels = class {
|
|
|
318
1155
|
|
|
319
1156
|
// node_modules/@gramlot/gramlot/src/components/layout.js
|
|
320
1157
|
var COPY_FEEDBACK_DURATION = 1600;
|
|
321
|
-
function Container(HTMLElement) {
|
|
322
|
-
return class extends HTMLElement {
|
|
323
|
-
constructor() {
|
|
324
|
-
super();
|
|
325
|
-
const shadow = this.attachShadow({ mode: "open" });
|
|
326
|
-
const style = this.ownerDocument.createElement("style");
|
|
327
|
-
style.textContent = `:host{display:block;min-width:0;font:inherit;color:var(--gramlot-ink,CanvasText)}
|
|
328
|
-
*,*::before,*::after{box-sizing:border-box}.control-box{min-width:0}slot{display:contents}
|
|
329
|
-
::slotted(*){min-width:0;max-width:100%}`;
|
|
330
|
-
this.controlBox = this.ownerDocument.createElement("div");
|
|
331
|
-
this.controlBox.className = "control-box";
|
|
332
|
-
this.control = this.controlBox;
|
|
333
|
-
this.control.id = "control";
|
|
334
|
-
this.control.append(this.ownerDocument.createElement("slot"));
|
|
335
|
-
shadow.append(style, this.controlBox);
|
|
336
|
-
}
|
|
337
|
-
projectAttributes(attrs) {
|
|
338
|
-
return projectParameters(this, attrs);
|
|
339
|
-
}
|
|
340
|
-
};
|
|
341
|
-
}
|
|
342
|
-
Container.parameters = {};
|
|
343
1158
|
var Formlet = defineComponent({
|
|
344
1159
|
name: "formlet",
|
|
345
1160
|
tag: "gramlot-formlet",
|
|
@@ -533,9 +1348,13 @@ var GroupBox = defineComponent({
|
|
|
533
1348
|
}
|
|
534
1349
|
}
|
|
535
1350
|
});
|
|
536
|
-
var layout_default = defineCollection({ name: "layout", components: [Formlet, GroupBox] });
|
|
1351
|
+
var layout_default = defineCollection({ name: "layout", components: [Formlet, GroupBox, ContentPane, BorderContainer, StackContainer, TabContainer] });
|
|
537
1352
|
export {
|
|
1353
|
+
BorderContainer,
|
|
1354
|
+
ContentPane,
|
|
538
1355
|
Formlet,
|
|
539
1356
|
GroupBox,
|
|
1357
|
+
StackContainer,
|
|
1358
|
+
TabContainer,
|
|
540
1359
|
layout_default as default
|
|
541
1360
|
};
|