@threadlabs/looma 0.18.6 → 0.19.1
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-editor-table-overlay/ui-editor-table-overlay.html +34 -3
- package/components/ui-editor-table-overlay/ui-editor-table-overlay.js +55 -0
- package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.html +7 -2
- package/components/ui-editor-table-toolbar/ui-editor-table-toolbar.js +29 -0
- package/components/ui-tree-item/ui-tree-item.html +5 -5
- package/components/ui-tree-item/ui-tree-item.js +20 -3
- package/dist/index.js +1 -1
- package/editor/chunks/extensions.js +24 -3
- package/editor/extensions/preset.d.ts +2 -0
- package/editor/extensions/table-commands.d.ts +1 -1
- package/editor/table-overlay.d.ts +8 -3
- package/package.json +2 -1
- package/styles/ui-editor-table-overlay.css +30 -0
- package/styles/ui-editor-table-toolbar.css +7 -2
- package/styles/ui-tree-item.css +5 -5
- package/vanilla/UiEditorTableOverlay.d.ts +1 -1
- package/vanilla/UiEditorTableOverlay.js +9 -3
- package/vue/UiEditorTableOverlay.d.ts +8 -0
- package/vue/UiEditorTableOverlay.vue +45 -2
- package/vue/UiEditorTableToolbar.vue +7 -2
- package/vue/UiTreeItem.vue +5 -5
- package/vue/chunks/1c.js +106 -0
- package/vue/chunks/UiEditorTableOverlay.js +14 -4
- package/vue/chunks/UiEditorTableToolbar.js +1 -1
- package/vue/chunks/UiTreeItem.js +1 -1
- package/vue/chunks/abnf.js +61 -0
- package/vue/chunks/accesslog.js +72 -0
- package/vue/chunks/actionscript.js +139 -0
- package/vue/chunks/ada.js +183 -0
- package/vue/chunks/angelscript.js +141 -0
- package/vue/chunks/apache.js +88 -0
- package/vue/chunks/applescript.js +99 -0
- package/vue/chunks/arcade.js +405 -0
- package/vue/chunks/arduino.js +871 -0
- package/vue/chunks/armasm.js +65 -0
- package/vue/chunks/asciidoc.js +181 -0
- package/vue/chunks/aspectj.js +202 -0
- package/vue/chunks/autohotkey.js +54 -0
- package/vue/chunks/autoit.js +132 -0
- package/vue/chunks/avrasm.js +46 -0
- package/vue/chunks/awk.js +58 -0
- package/vue/chunks/axapta.js +167 -0
- package/vue/chunks/bash.js +358 -0
- package/vue/chunks/basic.js +221 -0
- package/vue/chunks/bnf.js +27 -0
- package/vue/chunks/brainfuck.js +40 -0
- package/vue/chunks/c.js +265 -0
- package/vue/chunks/cal.js +136 -0
- package/vue/chunks/capnproto.js +86 -0
- package/vue/chunks/ceylon.js +125 -0
- package/vue/chunks/clean.js +54 -0
- package/vue/chunks/clojure-repl.js +17 -0
- package/vue/chunks/clojure.js +133 -0
- package/vue/chunks/cmake.js +23 -0
- package/vue/chunks/coffeescript.js +291 -0
- package/vue/chunks/coq.js +436 -0
- package/vue/chunks/cos.js +77 -0
- package/vue/chunks/cpp.js +492 -0
- package/vue/chunks/crmsh.js +80 -0
- package/vue/chunks/crystal.js +260 -0
- package/vue/chunks/csharp.js +376 -0
- package/vue/chunks/csp.js +52 -0
- package/vue/chunks/css.js +890 -0
- package/vue/chunks/d.js +184 -0
- package/vue/chunks/dart.js +226 -0
- package/vue/chunks/delphi.js +227 -0
- package/vue/chunks/diff.js +40 -0
- package/vue/chunks/django.js +43 -0
- package/vue/chunks/dns.js +66 -0
- package/vue/chunks/dockerfile.js +35 -0
- package/vue/chunks/dos.js +147 -0
- package/vue/chunks/dsconfig.js +55 -0
- package/vue/chunks/dts.js +119 -0
- package/vue/chunks/dust.js +33 -0
- package/vue/chunks/ebnf.js +40 -0
- package/vue/chunks/elixir.js +241 -0
- package/vue/chunks/elm.js +103 -0
- package/vue/chunks/erb.js +17 -0
- package/vue/chunks/erlang-repl.js +41 -0
- package/vue/chunks/erlang.js +236 -0
- package/vue/chunks/excel.js +562 -0
- package/vue/chunks/fix.js +30 -0
- package/vue/chunks/flix.js +60 -0
- package/vue/chunks/fortran.js +543 -0
- package/vue/chunks/fsharp.js +440 -0
- package/vue/chunks/gams.js +134 -0
- package/vue/chunks/gauss.js +167 -0
- package/vue/chunks/gcode.js +131 -0
- package/vue/chunks/gherkin.js +41 -0
- package/vue/chunks/glsl.js +25 -0
- package/vue/chunks/gml.js +3119 -0
- package/vue/chunks/go.js +140 -0
- package/vue/chunks/golo.js +72 -0
- package/vue/chunks/gradle.js +181 -0
- package/vue/chunks/graphql.js +68 -0
- package/vue/chunks/groovy.js +159 -0
- package/vue/chunks/haml.js +96 -0
- package/vue/chunks/handlebars.js +191 -0
- package/vue/chunks/haskell.js +149 -0
- package/vue/chunks/haxe.js +142 -0
- package/vue/chunks/hsp.js +45 -0
- package/vue/chunks/http.js +77 -0
- package/vue/chunks/hy.js +92 -0
- package/vue/chunks/inform7.js +45 -0
- package/vue/chunks/ini.js +94 -0
- package/vue/chunks/irpf90.js +52 -0
- package/vue/chunks/isbl.js +112 -0
- package/vue/chunks/java.js +237 -0
- package/vue/chunks/javascript.js +570 -0
- package/vue/chunks/jboss-cli.js +46 -0
- package/vue/chunks/json.js +45 -0
- package/vue/chunks/julia-repl.js +18 -0
- package/vue/chunks/julia.js +353 -0
- package/vue/chunks/kotlin.js +216 -0
- package/vue/chunks/lasso.js +127 -0
- package/vue/chunks/latex.js +206 -0
- package/vue/chunks/ldif.js +25 -0
- package/vue/chunks/leaf.js +72 -0
- package/vue/chunks/less.js +953 -0
- package/vue/chunks/lisp.js +119 -0
- package/vue/chunks/livecodeserver.js +86 -0
- package/vue/chunks/livescript.js +309 -0
- package/vue/chunks/llvm.js +77 -0
- package/vue/chunks/lsl.js +55 -0
- package/vue/chunks/lua.js +49 -0
- package/vue/chunks/makefile.js +62 -0
- package/vue/chunks/markdown.js +194 -0
- package/vue/chunks/mathematica.js +7317 -0
- package/vue/chunks/matlab.js +70 -0
- package/vue/chunks/maxima.js +38 -0
- package/vue/chunks/mel.js +24 -0
- package/vue/chunks/mercury.js +62 -0
- package/vue/chunks/mipsasm.js +53 -0
- package/vue/chunks/mizar.js +10 -0
- package/vue/chunks/mojolicious.js +27 -0
- package/vue/chunks/monkey.js +158 -0
- package/vue/chunks/moonscript.js +102 -0
- package/vue/chunks/n1ql.js +346 -0
- package/vue/chunks/nestedtext.js +66 -0
- package/vue/chunks/nginx.js +127 -0
- package/vue/chunks/nim.js +174 -0
- package/vue/chunks/nix.js +334 -0
- package/vue/chunks/node-repl.js +20 -0
- package/vue/chunks/nsis.js +515 -0
- package/vue/chunks/objectivec.js +235 -0
- package/vue/chunks/ocaml.js +52 -0
- package/vue/chunks/openscad.js +66 -0
- package/vue/chunks/oxygene.js +58 -0
- package/vue/chunks/parser3.js +40 -0
- package/vue/chunks/perl.js +454 -0
- package/vue/chunks/pf.js +30 -0
- package/vue/chunks/pgsql.js +196 -0
- package/vue/chunks/php-template.js +43 -0
- package/vue/chunks/php.js +527 -0
- package/vue/chunks/plaintext.js +10 -0
- package/vue/chunks/pony.js +60 -0
- package/vue/chunks/powershell.js +208 -0
- package/vue/chunks/processing.js +407 -0
- package/vue/chunks/profile.js +38 -0
- package/vue/chunks/prolog.js +62 -0
- package/vue/chunks/properties.js +45 -0
- package/vue/chunks/protobuf.js +63 -0
- package/vue/chunks/puppet.js +87 -0
- package/vue/chunks/purebasic.js +42 -0
- package/vue/chunks/python-repl.js +19 -0
- package/vue/chunks/python.js +357 -0
- package/vue/chunks/q.js +21 -0
- package/vue/chunks/qml.js +134 -0
- package/vue/chunks/r.js +145 -0
- package/vue/chunks/reasonml.js +134 -0
- package/vue/chunks/rib.js +16 -0
- package/vue/chunks/roboconf.js +54 -0
- package/vue/chunks/routeros.js +109 -0
- package/vue/chunks/rsl.js +138 -0
- package/vue/chunks/ruby.js +322 -0
- package/vue/chunks/ruleslanguage.js +26 -0
- package/vue/chunks/rust.js +296 -0
- package/vue/chunks/sas.js +533 -0
- package/vue/chunks/scala.js +173 -0
- package/vue/chunks/scheme.js +118 -0
- package/vue/chunks/scilab.js +45 -0
- package/vue/chunks/scss.js +900 -0
- package/vue/chunks/shell.js +18 -0
- package/vue/chunks/smali.js +113 -0
- package/vue/chunks/smalltalk.js +59 -0
- package/vue/chunks/sml.js +49 -0
- package/vue/chunks/sqf.js +2599 -0
- package/vue/chunks/sql.js +624 -0
- package/vue/chunks/stan.js +471 -0
- package/vue/chunks/stata.js +33 -0
- package/vue/chunks/step21.js +53 -0
- package/vue/chunks/stylus.js +925 -0
- package/vue/chunks/subunit.js +34 -0
- package/vue/chunks/swift.js +699 -0
- package/vue/chunks/taggerscript.js +37 -0
- package/vue/chunks/tap.js +30 -0
- package/vue/chunks/tcl.js +165 -0
- package/vue/chunks/thrift.js +65 -0
- package/vue/chunks/tp.js +155 -0
- package/vue/chunks/twig.js +232 -0
- package/vue/chunks/typescript.js +672 -0
- package/vue/chunks/vala.js +39 -0
- package/vue/chunks/vbnet.js +86 -0
- package/vue/chunks/vbscript-html.js +14 -0
- package/vue/chunks/vbscript.js +200 -0
- package/vue/chunks/verilog.js +534 -0
- package/vue/chunks/vhdl.js +190 -0
- package/vue/chunks/vim.js +54 -0
- package/vue/chunks/wasm.js +111 -0
- package/vue/chunks/wren.js +229 -0
- package/vue/chunks/x86asm.js +64 -0
- package/vue/chunks/xl.js +192 -0
- package/vue/chunks/xml.js +175 -0
- package/vue/chunks/xquery.js +303 -0
- package/vue/chunks/yaml.js +159 -0
- package/vue/chunks/zephir.js +74 -0
- package/vue/components.css +204 -170
- package/vue/editor/LoomaEditor.d.ts +7 -7
- package/vue/editor/code-language-catalog.d.ts +196 -0
- package/vue/editor/index.js +290 -20
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
<state name="activeKey" :value="''"></state>
|
|
30
30
|
<event
|
|
31
31
|
name="action"
|
|
32
|
-
type="object({ action: add-row-before | add-row-after | add-column-before | add-column-after, boundaryIndex: integer }) | object({ action: select-row | select-column, rowIndex: integer, columnIndex: integer }) | object({ action: open-cell-menu | open-row-menu | open-column-menu, rowIndex: integer, columnIndex: integer, anchor: object({ left: number, top: number, right: number, bottom: number }) })"
|
|
32
|
+
type="object({ action: add-row-before | add-row-after | add-column-before | add-column-after, boundaryIndex: integer }) | object({ action: select-row | select-column, rowIndex: integer, columnIndex: integer }) | object({ action: reorder-row | reorder-column, fromIndex: integer, toIndex: integer }) | object({ action: open-cell-menu | open-row-menu | open-column-menu, rowIndex: integer, columnIndex: integer, anchor: object({ left: number, top: number, right: number, bottom: number }) })"
|
|
33
33
|
></event>
|
|
34
34
|
</defs>
|
|
35
35
|
<div aria-label="Table controls" :hidden="not open">
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
data-action="select-row"
|
|
90
90
|
:data-row-index="hovered.rowIndex"
|
|
91
91
|
:data-column-index="hovered.columnIndex"
|
|
92
|
-
title="
|
|
92
|
+
title="Drag row to reorder; click for actions"
|
|
93
93
|
aria-label="Row actions"
|
|
94
94
|
>
|
|
95
95
|
<span class="glyph"><ui-icon name="grip-vertical"></ui-icon></span>
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
data-action="select-column"
|
|
102
102
|
:data-row-index="hovered.rowIndex"
|
|
103
103
|
:data-column-index="hovered.columnIndex"
|
|
104
|
-
title="
|
|
104
|
+
title="Drag column to reorder; click for actions"
|
|
105
105
|
aria-label="Column actions"
|
|
106
106
|
>
|
|
107
107
|
<span class="glyph"><ui-icon name="grip-horizontal"></ui-icon></span>
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
>
|
|
122
122
|
<ui-icon name="chevron-down"></ui-icon>
|
|
123
123
|
</button>
|
|
124
|
+
<span class="drop-indicator" data-drop-indicator hidden aria-hidden="true"></span>
|
|
124
125
|
</div>
|
|
125
126
|
<style>
|
|
126
127
|
:host {
|
|
@@ -346,6 +347,12 @@
|
|
|
346
347
|
|
|
347
348
|
.selector {
|
|
348
349
|
border-radius: var(--ui-radius-round);
|
|
350
|
+
touch-action: none;
|
|
351
|
+
cursor: grab;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
.selector:active {
|
|
355
|
+
cursor: grabbing;
|
|
349
356
|
}
|
|
350
357
|
|
|
351
358
|
.selector::before {
|
|
@@ -431,6 +438,30 @@
|
|
|
431
438
|
--_ui-default-icon-stroke-width: 2;
|
|
432
439
|
}
|
|
433
440
|
|
|
441
|
+
.drop-indicator {
|
|
442
|
+
position: absolute;
|
|
443
|
+
z-index: 3;
|
|
444
|
+
background: var(--ui-accent-solid);
|
|
445
|
+
box-shadow: 0 0 0 2px var(--ui-surface);
|
|
446
|
+
pointer-events: none;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
.drop-indicator.row {
|
|
450
|
+
left: 0;
|
|
451
|
+
width: 100%;
|
|
452
|
+
height: 2px;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.drop-indicator.column {
|
|
456
|
+
top: 0;
|
|
457
|
+
width: 2px;
|
|
458
|
+
height: 100%;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.drop-indicator[hidden] {
|
|
462
|
+
display: none;
|
|
463
|
+
}
|
|
464
|
+
|
|
434
465
|
:host[hidden] {
|
|
435
466
|
display: none;
|
|
436
467
|
}
|
|
@@ -52,6 +52,52 @@ function createProximity(scope, selector = ".handle[data-ui-affordance]", radius
|
|
|
52
52
|
export default function controller(host) {
|
|
53
53
|
const element = host.element;
|
|
54
54
|
const proximity = createProximity(element);
|
|
55
|
+
let drag = null;
|
|
56
|
+
let suppressClick = false;
|
|
57
|
+
const indicator = () => element.querySelector("[data-drop-indicator]");
|
|
58
|
+
const clearDrag = () => {
|
|
59
|
+
drag = null;
|
|
60
|
+
const guide = indicator();
|
|
61
|
+
if (guide) {
|
|
62
|
+
guide.hidden = true;
|
|
63
|
+
guide.classList.remove("row", "column");
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const onPointerDown = (event) => {
|
|
67
|
+
const button = event.target.closest?.("button.selector[data-action]");
|
|
68
|
+
if (!button || event.button !== 0) return;
|
|
69
|
+
const axis = button.dataset.action === "select-row" ? "row" : "column";
|
|
70
|
+
drag = { pointerId: event.pointerId, axis, from: Number(button.dataset[axis === "row" ? "rowIndex" : "columnIndex"]), x: event.clientX, y: event.clientY, to: null };
|
|
71
|
+
button.setPointerCapture(event.pointerId);
|
|
72
|
+
event.preventDefault();
|
|
73
|
+
};
|
|
74
|
+
const onPointerMove = (event) => {
|
|
75
|
+
if (!drag || event.pointerId !== drag.pointerId) return;
|
|
76
|
+
if (Math.hypot(event.clientX - drag.x, event.clientY - drag.y) < 5 && drag.to === null) return;
|
|
77
|
+
const rect = element.getBoundingClientRect();
|
|
78
|
+
const values = boundaries(drag.axis);
|
|
79
|
+
const coordinate = drag.axis === "row" ? event.clientY - rect.top : event.clientX - rect.left;
|
|
80
|
+
const found = values.findIndex((_, index) => index < values.length - 1 && coordinate < values[index + 1]);
|
|
81
|
+
const to = found < 0 ? values.length - 2 : found;
|
|
82
|
+
drag.to = to;
|
|
83
|
+
const guide = indicator();
|
|
84
|
+
if (!guide) return;
|
|
85
|
+
guide.hidden = false;
|
|
86
|
+
guide.classList.toggle("row", drag.axis === "row");
|
|
87
|
+
guide.classList.toggle("column", drag.axis === "column");
|
|
88
|
+
const boundary = to > drag.from ? values[to + 1] : values[to];
|
|
89
|
+
if (drag.axis === "row") guide.style.top = `${boundary}px`;
|
|
90
|
+
else guide.style.left = `${boundary}px`;
|
|
91
|
+
};
|
|
92
|
+
const onPointerUp = (event) => {
|
|
93
|
+
if (!drag || event.pointerId !== drag.pointerId) return;
|
|
94
|
+
const { axis, from, to } = drag;
|
|
95
|
+
clearDrag();
|
|
96
|
+
if (to === null) return;
|
|
97
|
+
suppressClick = true;
|
|
98
|
+
setTimeout(() => { suppressClick = false; }, 0);
|
|
99
|
+
if (to !== from) host.dispatch("action", { action: axis === "row" ? "reorder-row" : "reorder-column", fromIndex: from, toIndex: to });
|
|
100
|
+
};
|
|
55
101
|
const boundaries = (axis) => {
|
|
56
102
|
const values = host.state.geometry?.[axis === "row" ? "rowBoundaries" : "columnBoundaries"];
|
|
57
103
|
if (Array.isArray(values) && values.length >= 2 && values.every(Number.isFinite)) return values;
|
|
@@ -89,6 +135,7 @@ export default function controller(host) {
|
|
|
89
135
|
queueMicrotask(proximity.refresh);
|
|
90
136
|
});
|
|
91
137
|
const onClick = (event) => {
|
|
138
|
+
if (suppressClick) { event.preventDefault(); return; }
|
|
92
139
|
const button = event.target.closest?.("button[data-action]");
|
|
93
140
|
if (!button) return;
|
|
94
141
|
const action = button.dataset.action;
|
|
@@ -106,6 +153,10 @@ export default function controller(host) {
|
|
|
106
153
|
const enter = (event) => { host.state.activeKey = keyOf(event.target); };
|
|
107
154
|
const leave = (event) => { host.state.activeKey = keyOf(event.relatedTarget); };
|
|
108
155
|
element.addEventListener("click", onClick);
|
|
156
|
+
element.addEventListener("pointerdown", onPointerDown);
|
|
157
|
+
document.addEventListener("pointermove", onPointerMove, true);
|
|
158
|
+
document.addEventListener("pointerup", onPointerUp, true);
|
|
159
|
+
document.addEventListener("pointercancel", clearDrag, true);
|
|
109
160
|
element.addEventListener("pointerover", enter);
|
|
110
161
|
element.addEventListener("pointerout", leave);
|
|
111
162
|
element.addEventListener("focusin", enter);
|
|
@@ -114,6 +165,10 @@ export default function controller(host) {
|
|
|
114
165
|
stop();
|
|
115
166
|
proximity.destroy();
|
|
116
167
|
element.removeEventListener("click", onClick);
|
|
168
|
+
element.removeEventListener("pointerdown", onPointerDown);
|
|
169
|
+
document.removeEventListener("pointermove", onPointerMove, true);
|
|
170
|
+
document.removeEventListener("pointerup", onPointerUp, true);
|
|
171
|
+
document.removeEventListener("pointercancel", clearDrag, true);
|
|
117
172
|
element.removeEventListener("pointerover", enter);
|
|
118
173
|
element.removeEventListener("pointerout", leave);
|
|
119
174
|
element.removeEventListener("focusin", enter);
|
|
@@ -211,10 +211,12 @@
|
|
|
211
211
|
}
|
|
212
212
|
|
|
213
213
|
.menu {
|
|
214
|
+
box-sizing: border-box;
|
|
214
215
|
position: absolute;
|
|
215
216
|
top: calc(100% + var(--ui-space-2));
|
|
216
217
|
right: 0;
|
|
217
218
|
min-width: 12rem;
|
|
219
|
+
max-width: min(17rem, calc(100vw - 24px));
|
|
218
220
|
padding: var(--ui-space-1);
|
|
219
221
|
background: var(--ui-surface);
|
|
220
222
|
border: 1px solid var(--ui-border-strong);
|
|
@@ -224,6 +226,9 @@
|
|
|
224
226
|
flex-direction: column;
|
|
225
227
|
gap: 2px;
|
|
226
228
|
z-index: 2;
|
|
229
|
+
overflow-y: auto;
|
|
230
|
+
overscroll-behavior: contain;
|
|
231
|
+
scrollbar-width: thin;
|
|
227
232
|
}
|
|
228
233
|
|
|
229
234
|
.menu-section + .menu-section {
|
|
@@ -250,7 +255,7 @@
|
|
|
250
255
|
|
|
251
256
|
.swatches {
|
|
252
257
|
display: grid;
|
|
253
|
-
grid-template-columns: repeat(
|
|
258
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
254
259
|
gap: 2px;
|
|
255
260
|
}
|
|
256
261
|
|
|
@@ -264,7 +269,7 @@
|
|
|
264
269
|
height: 14px;
|
|
265
270
|
flex: 0 0 14px;
|
|
266
271
|
border: 1px solid color-mix(in srgb, var(--ui-border) 80%, #000);
|
|
267
|
-
border-radius:
|
|
272
|
+
border-radius: var(--ui-radius-round);
|
|
268
273
|
background: var(--_swatch);
|
|
269
274
|
}
|
|
270
275
|
|
|
@@ -9,6 +9,20 @@ const overflowSections = [
|
|
|
9
9
|
// Lists the actions the table selection permits; the overflow menu opens and closes here.
|
|
10
10
|
export default function controller(host) {
|
|
11
11
|
const element = host.element;
|
|
12
|
+
const positionMenu = () => {
|
|
13
|
+
const button = element.querySelector(".more");
|
|
14
|
+
const menu = element.querySelector(".menu");
|
|
15
|
+
if (!button || !menu) return;
|
|
16
|
+
const rect = button.getBoundingClientRect();
|
|
17
|
+
const viewport = window.visualViewport;
|
|
18
|
+
const top = viewport?.offsetTop ?? 0;
|
|
19
|
+
const bottom = top + (viewport?.height ?? window.innerHeight);
|
|
20
|
+
const above = rect.top - top - 20;
|
|
21
|
+
const below = bottom - rect.bottom - 20;
|
|
22
|
+
menu.style.maxHeight = `${Math.max(80, Math.floor(Math.max(above, below)))}px`;
|
|
23
|
+
menu.style.top = below >= above ? "calc(100% + var(--ui-space-2))" : "auto";
|
|
24
|
+
menu.style.bottom = below >= above ? "auto" : "calc(100% + var(--ui-space-2))";
|
|
25
|
+
};
|
|
12
26
|
const stop = host.effect(() => {
|
|
13
27
|
const enabled = new Set(Array.isArray(host.state.actions) ? host.state.actions : []);
|
|
14
28
|
const alignment = host.state.cellAlignment === "center" || host.state.cellAlignment === "right" ? host.state.cellAlignment : "left";
|
|
@@ -36,6 +50,7 @@ export default function controller(host) {
|
|
|
36
50
|
if (!action) return;
|
|
37
51
|
if (action === "toggle-overflow") {
|
|
38
52
|
host.state.overflowOpen = !host.state.overflowOpen;
|
|
53
|
+
if (host.state.overflowOpen) requestAnimationFrame(positionMenu);
|
|
39
54
|
return;
|
|
40
55
|
}
|
|
41
56
|
host.state.overflowOpen = false;
|
|
@@ -44,11 +59,25 @@ export default function controller(host) {
|
|
|
44
59
|
const onOutside = (event) => {
|
|
45
60
|
if (host.state.overflowOpen && !event.composedPath().includes(element)) host.state.overflowOpen = false;
|
|
46
61
|
};
|
|
62
|
+
const onKeydown = (event) => {
|
|
63
|
+
if (event.key === "Escape" && host.state.overflowOpen) {
|
|
64
|
+
host.state.overflowOpen = false;
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
event.stopPropagation();
|
|
67
|
+
element.querySelector(".more")?.focus();
|
|
68
|
+
}
|
|
69
|
+
};
|
|
47
70
|
element.addEventListener("click", onClick);
|
|
48
71
|
document.addEventListener("pointerdown", onOutside, true);
|
|
72
|
+
document.addEventListener("keydown", onKeydown, true);
|
|
73
|
+
window.addEventListener("resize", positionMenu);
|
|
74
|
+
window.visualViewport?.addEventListener("resize", positionMenu);
|
|
49
75
|
return () => {
|
|
50
76
|
stop();
|
|
51
77
|
element.removeEventListener("click", onClick);
|
|
52
78
|
document.removeEventListener("pointerdown", onOutside, true);
|
|
79
|
+
document.removeEventListener("keydown", onKeydown, true);
|
|
80
|
+
window.removeEventListener("resize", positionMenu);
|
|
81
|
+
window.visualViewport?.removeEventListener("resize", positionMenu);
|
|
53
82
|
};
|
|
54
83
|
}
|
|
@@ -324,7 +324,7 @@
|
|
|
324
324
|
padding: var(--_ui-tree-item-label-padding-block, var(--ui-space-1)) var(--_ui-tree-item-label-padding-inline, var(--ui-space-2));
|
|
325
325
|
/* The length of the fade before the controls. The label's one flex child lays out no gap; this
|
|
326
326
|
is where the controller reads the length in pixels to stop a sliding name before the fade. */
|
|
327
|
-
column-gap: var(--ui-space-
|
|
327
|
+
column-gap: var(--ui-space-1);
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/* Slotted label content fills the cell, so a link is the row's hit area rather than sizing to
|
|
@@ -352,7 +352,7 @@
|
|
|
352
352
|
text-overflow: clip;
|
|
353
353
|
mask-image: linear-gradient(
|
|
354
354
|
to right,
|
|
355
|
-
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-
|
|
355
|
+
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-1)),
|
|
356
356
|
transparent calc(100% - var(--_tree-actions-width, 1.75rem))
|
|
357
357
|
);
|
|
358
358
|
}
|
|
@@ -361,7 +361,7 @@
|
|
|
361
361
|
.row:focus-within > .label:dir(rtl) {
|
|
362
362
|
mask-image: linear-gradient(
|
|
363
363
|
to left,
|
|
364
|
-
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-
|
|
364
|
+
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-1)),
|
|
365
365
|
transparent calc(100% - var(--_tree-actions-width, 1.75rem))
|
|
366
366
|
);
|
|
367
367
|
}
|
|
@@ -388,7 +388,7 @@
|
|
|
388
388
|
to right,
|
|
389
389
|
transparent var(--_marquee-icon-width, 0px),
|
|
390
390
|
#000 calc(var(--_marquee-lead, 0px) + var(--_ui-tree-item-label-padding-inline, var(--ui-space-2))),
|
|
391
|
-
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-
|
|
391
|
+
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-1)),
|
|
392
392
|
transparent calc(100% - var(--_tree-actions-width, 1.75rem))
|
|
393
393
|
);
|
|
394
394
|
}
|
|
@@ -398,7 +398,7 @@
|
|
|
398
398
|
to left,
|
|
399
399
|
transparent var(--_marquee-icon-width, 0px),
|
|
400
400
|
#000 calc(var(--_marquee-lead, 0px) + var(--_ui-tree-item-label-padding-inline, var(--ui-space-2))),
|
|
401
|
-
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-
|
|
401
|
+
#000 calc(100% - var(--_tree-actions-width, 1.75rem) - var(--ui-space-1)),
|
|
402
402
|
transparent calc(100% - var(--_tree-actions-width, 1.75rem))
|
|
403
403
|
);
|
|
404
404
|
}
|
|
@@ -112,17 +112,34 @@ export default function controller(host) {
|
|
|
112
112
|
// The tree sets this for its items; an item's own prop overrides it either way.
|
|
113
113
|
const marqueeWanted = () => host.state.marquee
|
|
114
114
|
|| getComputedStyle(element).getPropertyValue("--_ui-default-tree-item-marquee").trim() === "1";
|
|
115
|
+
// A slotted link may fill the row and have padding past its name. Measure the rendered letters,
|
|
116
|
+
// since moving that link's box to the actions would send a short name too far (or move it at all).
|
|
117
|
+
const textEdge = (rightToLeft) => {
|
|
118
|
+
const walker = element.ownerDocument.createTreeWalker(labelText, NodeFilter.SHOW_TEXT);
|
|
119
|
+
const range = element.ownerDocument.createRange();
|
|
120
|
+
let edge = rightToLeft ? Infinity : -Infinity;
|
|
121
|
+
while (walker.nextNode()) {
|
|
122
|
+
if (!walker.currentNode.textContent?.trim()) continue;
|
|
123
|
+
range.selectNodeContents(walker.currentNode);
|
|
124
|
+
for (const rect of range.getClientRects()) {
|
|
125
|
+
if (rect.width === 0 || rect.height === 0) continue;
|
|
126
|
+
edge = rightToLeft ? Math.min(edge, rect.left) : Math.max(edge, rect.right);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return Number.isFinite(edge) ? edge : rightToLeft
|
|
130
|
+
? labelText.getBoundingClientRect().left : labelText.getBoundingClientRect().right;
|
|
131
|
+
};
|
|
115
132
|
const startMarquee = () => {
|
|
116
133
|
if (!labelText || !label || !marqueeWanted()) return;
|
|
117
134
|
labelText.style.flex = "none";
|
|
118
135
|
labelText.style.inlineSize = "max-content";
|
|
119
|
-
const
|
|
136
|
+
const rightToLeft = getComputedStyle(element).direction === "rtl";
|
|
137
|
+
const end = textEdge(rightToLeft);
|
|
120
138
|
labelText.style.flex = labelText.style.inlineSize = "";
|
|
121
139
|
const cell = label.getBoundingClientRect();
|
|
122
140
|
const icon = leading?.getBoundingClientRect();
|
|
123
141
|
const stop = (actions?.offsetWidth ?? 0) + parseFloat(getComputedStyle(label).columnGap);
|
|
124
|
-
const
|
|
125
|
-
const distance = Math.ceil(rightToLeft ? cell.left + stop - text.left : text.right - (cell.right - stop));
|
|
142
|
+
const distance = Math.ceil(rightToLeft ? cell.left + stop - end : end - (cell.right - stop));
|
|
126
143
|
if (distance <= 0) return;
|
|
127
144
|
const lead = icon?.width ? (rightToLeft ? icon.right - cell.right : cell.left - icon.left) : 0;
|
|
128
145
|
row.style.setProperty("--_marquee-lead", `${Math.max(0, lead)}px`);
|