isotone 1.1.0 → 1.1.2
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/CHANGELOG.md +8 -0
- package/README.md +7 -2
- package/dist/{chunk-B3JNZITE.js → chunk-I7CZUPDO.js} +2 -2
- package/dist/{chunk-B3JNZITE.js.map → chunk-I7CZUPDO.js.map} +1 -1
- package/dist/cli.js +1 -1
- package/dist/index.js +409 -183
- package/dist/index.js.map +1 -1
- package/dist/isotone.global.js +46 -29
- package/dist/serve.js +1 -1
- package/dist/vite-plugin.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -754,7 +754,7 @@ ${APPEARANCE_CSS}
|
|
|
754
754
|
/* Panels move independently; their content and corners retain their natural size. */
|
|
755
755
|
.isotone-root[data-open='true'] { width: var(--iso-dock-width); }
|
|
756
756
|
.isotone-root { pointer-events: none; }
|
|
757
|
-
.isotone-root :is(.isotone-panel, .isotone-rail-button, .isotone-fab, .isotone-
|
|
757
|
+
.isotone-root :is(.isotone-panel, .isotone-rail-button, .isotone-fab, .isotone-float) { pointer-events: auto; }
|
|
758
758
|
/* The horizontal pill stays anchored while its panels open above it. */
|
|
759
759
|
.isotone-rail, .isotone-rail-tools { display: contents; border: 0; margin: 0; padding: 0; min-width: 0; }
|
|
760
760
|
.isotone-root[data-expanded='true'] { min-height: 44px; }
|
|
@@ -840,7 +840,7 @@ ${APPEARANCE_CSS}
|
|
|
840
840
|
.isotone-picker input { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; cursor: pointer; }
|
|
841
841
|
.isotone-picker:has(input:focus-visible) { outline: 2px solid var(--iso-focus); outline-offset: 3px; }
|
|
842
842
|
/* Raised actions share one face, edge and hover treatment. */
|
|
843
|
-
.isotone-save, .isotone-secondary, .isotone-send, .isotone-
|
|
843
|
+
.isotone-save, .isotone-secondary, .isotone-send, .isotone-picking button, .isotone-away-action { border-radius: var(--iso-control-radius); color: var(--iso-fg); background: var(--iso-control-face); box-shadow: var(--iso-control-shadow); }
|
|
844
844
|
/* Persistent selections share the raised action material, never the recessed
|
|
845
845
|
field/slot material. Keep the face and shadow together in this one rule. */
|
|
846
846
|
.isotone-note-editing .isotone-note-hit,
|
|
@@ -915,7 +915,7 @@ ${FEEDBACK_LAYOUT_CSS}
|
|
|
915
915
|
.isotone-chip button { display: grid; place-items: center; flex: none; width: 16px; height: 16px; border-radius: 3px; background: transparent; box-shadow: none; }
|
|
916
916
|
.isotone-root .isotone-textarea { --iso-textarea-inset: 8px; display: block; width: 100%; flex: none; height: 22px; min-height: calc(22px + var(--iso-textarea-inset)); max-height: calc(120px + var(--iso-textarea-inset)); overflow-y: auto; resize: none; margin-top: calc(0px - var(--iso-textarea-inset)); padding: calc(var(--iso-textarea-inset) + 1px) 1px 1px; scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--iso-fg) 14%, transparent) transparent; color: var(--iso-fg); background: transparent; border: 0; border-radius: 0; font-size: 12px; line-height: 20px; box-shadow: none; cursor: text; }
|
|
917
917
|
/* Text scrolls through the gap below tags, or the field inset without tags. */
|
|
918
|
-
.isotone-root .isotone-context:empty + .isotone-textarea { --iso-textarea-inset: var(--iso-input-padding); }
|
|
918
|
+
.isotone-root .isotone-context:empty + .isotone-entry .isotone-textarea { --iso-textarea-inset: var(--iso-input-padding); }
|
|
919
919
|
.isotone-textarea::placeholder { color: var(--iso-faint); }
|
|
920
920
|
.isotone-root .isotone-textarea:focus-visible { outline: none; }
|
|
921
921
|
.isotone-textarea[data-scrolled='true'] { mask-image: linear-gradient(to bottom, transparent, #000 8px); }
|
|
@@ -926,11 +926,28 @@ ${FEEDBACK_LAYOUT_CSS}
|
|
|
926
926
|
.isotone-pick::after, .isotone-send::after { content: ''; position: absolute; inset: -2px; border-radius: inherit; }
|
|
927
927
|
.isotone-send { display: grid; place-items: center; margin-left: auto; flex: none; }
|
|
928
928
|
.isotone-notice { font-size: 10px; color: var(--iso-muted); }
|
|
929
|
-
.isotone-
|
|
930
|
-
/*
|
|
931
|
-
.isotone-
|
|
932
|
-
.isotone-
|
|
933
|
-
.isotone-
|
|
929
|
+
.isotone-entry { position: relative; display: flex; flex-direction: column; gap: 8px; }
|
|
930
|
+
/* Select mode covers the field and says how to finish, next to the elements the user is picking. */
|
|
931
|
+
.isotone-picking { position: absolute; inset: -4px; z-index: 1; display: flex; align-items: center; gap: 10px; padding: 0 4px 0 8px; border-radius: 6px; background: var(--iso-well); cursor: default; }
|
|
932
|
+
.isotone-picking > svg { flex: none; color: var(--iso-fg); }
|
|
933
|
+
.isotone-picking p { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; margin: 0; }
|
|
934
|
+
.isotone-picking span { font-size: 12px; line-height: 16px; color: var(--iso-fg); font-variant-numeric: tabular-nums; }
|
|
935
|
+
.isotone-picking small { font-size: 10px; line-height: 14px; color: var(--iso-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
936
|
+
.isotone-picking button { flex: none; display: flex; align-items: center; gap: 8px; height: 28px; padding: 0 9px; }
|
|
937
|
+
.isotone-picking kbd { font: inherit; color: var(--iso-muted); }
|
|
938
|
+
/* After a pin, the composer floats beside the element on a panel surface, placed by
|
|
939
|
+
IterationPanel inside the viewport. It grows out of the point that was clicked. */
|
|
940
|
+
.isotone-float { position: fixed; z-index: 2147483002; width: min(300px, calc(100vw - 16px)); padding: 4px; background: var(--iso-bg); border: 1px solid var(--iso-line); border-radius: 12px; box-shadow: var(--iso-panel-shadow); }
|
|
941
|
+
.isotone-float-hint { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 10px; color: var(--iso-muted); }
|
|
942
|
+
/* Meanwhile the dock keeps the composer's slot and says where the writing went. */
|
|
943
|
+
.isotone-away { display: flex; align-items: center; gap: 10px; width: 100%; min-height: 50px; padding: 8px 8px 8px 10px; text-align: left; color: var(--iso-fg); background: var(--iso-well); border: 1px solid var(--iso-input-border); box-shadow: var(--iso-input-shadow); border-radius: 8px; }
|
|
944
|
+
.isotone-away > svg { flex: none; }
|
|
945
|
+
.isotone-root .isotone-float.isotone-exit { pointer-events: none; }
|
|
946
|
+
.isotone-away-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
947
|
+
.isotone-away-text > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
948
|
+
.isotone-away-text > span { font-size: 12px; line-height: 16px; }
|
|
949
|
+
.isotone-away-text > small { font-size: 10px; line-height: 14px; color: var(--iso-muted); }
|
|
950
|
+
.isotone-away-action { flex: none; display: flex; align-items: center; height: 28px; padding: 0 9px; font-size: 11px; }
|
|
934
951
|
.isotone-hover { position: fixed; pointer-events: none; border: 1px solid var(--iso-pick-line); background: var(--iso-pick-fill); border-radius: 5px; z-index: 2147483001; }
|
|
935
952
|
.isotone-selected { border: 2px solid var(--iso-pick-line); background: var(--iso-pick-selected-fill); }
|
|
936
953
|
@media (hover: hover) and (pointer: fine) {
|
|
@@ -940,7 +957,7 @@ ${FEEDBACK_LAYOUT_CSS}
|
|
|
940
957
|
.isotone-seg:not(:disabled):hover { color: var(--iso-fg); }
|
|
941
958
|
.isotone-seg[aria-pressed='false']:not(:disabled):hover { box-shadow: var(--iso-hover-shadow); }
|
|
942
959
|
.isotone-thumb[aria-pressed='false']:not(:disabled):hover { box-shadow: var(--iso-thumb-hover-shadow); }
|
|
943
|
-
.isotone-root :where(.isotone-save, .isotone-secondary, .isotone-send, .isotone-
|
|
960
|
+
.isotone-root :where(.isotone-save, .isotone-secondary, .isotone-send, .isotone-picking button):not(:disabled):hover, .isotone-away:hover .isotone-away-action { background: var(--iso-control-hover-face); box-shadow: var(--iso-control-hover-shadow); }
|
|
944
961
|
.isotone-chip button:hover { color: var(--iso-fg); background: var(--iso-hover); }
|
|
945
962
|
.isotone-x:hover { color: var(--iso-fg); }
|
|
946
963
|
.isotone-x:hover::before { background: var(--iso-control-hover-face); }
|
|
@@ -1788,6 +1805,8 @@ function useDockMotion(ref, visible) {
|
|
|
1788
1805
|
// src/react/IterationPanel.tsx
|
|
1789
1806
|
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1790
1807
|
var PULSE_MS = 2e3;
|
|
1808
|
+
var FLOAT_GAP = 12;
|
|
1809
|
+
var FLOAT_MARGIN = 8;
|
|
1791
1810
|
function syncPulse(spread) {
|
|
1792
1811
|
return (node) => {
|
|
1793
1812
|
if (!node || typeof node.animate !== "function") return;
|
|
@@ -1801,6 +1820,19 @@ function syncPulse(spread) {
|
|
|
1801
1820
|
pulse.startTime = 0;
|
|
1802
1821
|
};
|
|
1803
1822
|
}
|
|
1823
|
+
function stillMotion(node) {
|
|
1824
|
+
return Boolean(node.closest('[data-instant="true"]')) || typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1825
|
+
}
|
|
1826
|
+
function fadeIn(node) {
|
|
1827
|
+
if (!node || typeof node.animate !== "function" || node.dataset.still === "true" || stillMotion(node)) return;
|
|
1828
|
+
node.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 140, easing: "ease-out" });
|
|
1829
|
+
}
|
|
1830
|
+
function fadeInContent(node) {
|
|
1831
|
+
if (!node || stillMotion(node)) return;
|
|
1832
|
+
for (const child of node.children)
|
|
1833
|
+
if (typeof child.animate === "function")
|
|
1834
|
+
child.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 140, easing: "ease-out" });
|
|
1835
|
+
}
|
|
1804
1836
|
var dotPulse = syncPulse(6);
|
|
1805
1837
|
var ringPulse = syncPulse(8);
|
|
1806
1838
|
var POLL_MS = 2500;
|
|
@@ -1838,13 +1870,22 @@ function IterationPanel({
|
|
|
1838
1870
|
const [hovered, setHovered] = useState3(false);
|
|
1839
1871
|
const [focused, setFocused] = useState3(false);
|
|
1840
1872
|
const [picking, setPicking] = useState3(false);
|
|
1873
|
+
const [pickingWithPanel, setPickingWithPanel] = useState3(false);
|
|
1841
1874
|
const [targets, setTargets] = useState3([]);
|
|
1875
|
+
const targetsRef = useRef4(targets);
|
|
1876
|
+
targetsRef.current = targets;
|
|
1877
|
+
const [anchor, setAnchor] = useState3(null);
|
|
1878
|
+
const anchorRef = useRef4(anchor);
|
|
1879
|
+
anchorRef.current = anchor;
|
|
1880
|
+
const floating = anchor !== null;
|
|
1881
|
+
const floatRef = useRef4(null);
|
|
1882
|
+
const slotHeight = useRef4(0);
|
|
1883
|
+
const lastFloat = useRef4(null);
|
|
1884
|
+
const floatNode = useRef4(null);
|
|
1842
1885
|
const [variantTarget, setVariantTarget] = useState3(
|
|
1843
1886
|
null
|
|
1844
1887
|
);
|
|
1845
1888
|
const [hoverRect, setHoverRect] = useState3(null);
|
|
1846
|
-
const [hintAway, setHintAway] = useState3(false);
|
|
1847
|
-
const hintRef = useRef4(null);
|
|
1848
1889
|
const [rects, setRects] = useState3([]);
|
|
1849
1890
|
const [text, setText] = useState3("");
|
|
1850
1891
|
const [notes, setNotes] = useState3([]);
|
|
@@ -1895,7 +1936,7 @@ function IterationPanel({
|
|
|
1895
1936
|
});
|
|
1896
1937
|
observer.observe(field);
|
|
1897
1938
|
return () => observer.disconnect();
|
|
1898
|
-
}, [text, notesOpen, visible, hasContext]);
|
|
1939
|
+
}, [text, notesOpen, visible, hasContext, floating]);
|
|
1899
1940
|
useEffect3(() => {
|
|
1900
1941
|
if (!visible) {
|
|
1901
1942
|
setHovered(false);
|
|
@@ -1982,7 +2023,7 @@ function IterationPanel({
|
|
|
1982
2023
|
cancelAnimationFrame(focusFrame.current);
|
|
1983
2024
|
focusFrame.current = requestAnimationFrame(() => {
|
|
1984
2025
|
keepPicking.current = true;
|
|
1985
|
-
textareaRef.current?.focus();
|
|
2026
|
+
textareaRef.current?.focus({ preventScroll: true });
|
|
1986
2027
|
keepPicking.current = false;
|
|
1987
2028
|
});
|
|
1988
2029
|
}, []);
|
|
@@ -1997,21 +2038,33 @@ function IterationPanel({
|
|
|
1997
2038
|
setNotesOpen(false);
|
|
1998
2039
|
setVariantsOpen(false);
|
|
1999
2040
|
setPicking(false);
|
|
2041
|
+
setAnchor(null);
|
|
2000
2042
|
setExpanded(false);
|
|
2001
2043
|
setHovered(false);
|
|
2002
2044
|
(previousFocus.current?.isConnected ? previousFocus.current : fabRef.current)?.focus();
|
|
2003
2045
|
}
|
|
2004
|
-
function
|
|
2046
|
+
function startFeedback(select) {
|
|
2047
|
+
setNotesOpen(true);
|
|
2048
|
+
if (select) {
|
|
2049
|
+
cancelAnimationFrame(focusFrame.current);
|
|
2050
|
+
setPickingWithPanel(!notesOpen);
|
|
2051
|
+
setPicking(true);
|
|
2052
|
+
} else focusComposer();
|
|
2053
|
+
}
|
|
2054
|
+
function openInitial(select) {
|
|
2005
2055
|
previousFocus.current = document.activeElement;
|
|
2006
2056
|
setExpanded(true);
|
|
2007
2057
|
if ((defaultTab === "variants" || !notesAvailable) && variantsAvailable)
|
|
2008
2058
|
setVariantsOpen(true);
|
|
2009
|
-
else
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2059
|
+
else startFeedback(select);
|
|
2060
|
+
}
|
|
2061
|
+
function dockComposer() {
|
|
2062
|
+
setAnchor(null);
|
|
2063
|
+
setPicking(false);
|
|
2064
|
+
focusComposer();
|
|
2013
2065
|
}
|
|
2014
2066
|
function feedback(reference) {
|
|
2067
|
+
setAnchor(null);
|
|
2015
2068
|
setVariantTarget(reference);
|
|
2016
2069
|
setNotesOpen(true);
|
|
2017
2070
|
setExpanded(true);
|
|
@@ -2082,6 +2135,7 @@ function IterationPanel({
|
|
|
2082
2135
|
cancelEdit();
|
|
2083
2136
|
setNotice("");
|
|
2084
2137
|
setPicking(false);
|
|
2138
|
+
setAnchor(null);
|
|
2085
2139
|
focusComposer();
|
|
2086
2140
|
try {
|
|
2087
2141
|
const savedId = await store.saveNote(
|
|
@@ -2110,6 +2164,7 @@ function IterationPanel({
|
|
|
2110
2164
|
}
|
|
2111
2165
|
}
|
|
2112
2166
|
function beginEdit(note) {
|
|
2167
|
+
setAnchor(null);
|
|
2113
2168
|
setEditing(note);
|
|
2114
2169
|
setText(note.feedback);
|
|
2115
2170
|
setTargets(note.elements ?? (note.element ? [note.element] : []));
|
|
@@ -2191,6 +2246,7 @@ function IterationPanel({
|
|
|
2191
2246
|
if (draftRestored.current === draftKey) return;
|
|
2192
2247
|
draftRestored.current = draftKey;
|
|
2193
2248
|
restoringDraft.current = true;
|
|
2249
|
+
setAnchor(null);
|
|
2194
2250
|
setEditing(null);
|
|
2195
2251
|
setText("");
|
|
2196
2252
|
setTargets([]);
|
|
@@ -2250,16 +2306,16 @@ function IterationPanel({
|
|
|
2250
2306
|
return;
|
|
2251
2307
|
e.preventDefault();
|
|
2252
2308
|
e.stopImmediatePropagation();
|
|
2253
|
-
if (
|
|
2254
|
-
|
|
2255
|
-
|
|
2309
|
+
if (!notesAvailable) setVariantsOpen((v) => !v);
|
|
2310
|
+
else if (picking) {
|
|
2311
|
+
setPicking(false);
|
|
2312
|
+
focusComposer();
|
|
2313
|
+
} else {
|
|
2256
2314
|
if (!open)
|
|
2257
2315
|
previousFocus.current = document.activeElement;
|
|
2258
|
-
setNotesOpen(true);
|
|
2259
2316
|
setExpanded(true);
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
} else setVariantsOpen((v) => !v);
|
|
2317
|
+
startFeedback(true);
|
|
2318
|
+
}
|
|
2263
2319
|
};
|
|
2264
2320
|
const onPointer = () => {
|
|
2265
2321
|
rootRef.current?.setAttribute("data-instant", "false");
|
|
@@ -2276,7 +2332,8 @@ function IterationPanel({
|
|
|
2276
2332
|
if (!open || picking) return;
|
|
2277
2333
|
const onKey = (e) => {
|
|
2278
2334
|
if (e.key !== "Escape" || e.defaultPrevented) return;
|
|
2279
|
-
if (
|
|
2335
|
+
if (anchor) dockComposer();
|
|
2336
|
+
else if (editing) cancelEdit();
|
|
2280
2337
|
else closeAll();
|
|
2281
2338
|
};
|
|
2282
2339
|
window.addEventListener("keydown", onKey);
|
|
@@ -2295,20 +2352,17 @@ function IterationPanel({
|
|
|
2295
2352
|
cancelAnimationFrame(frame);
|
|
2296
2353
|
const deferred = e.shiftKey;
|
|
2297
2354
|
frame = requestAnimationFrame(() => {
|
|
2298
|
-
const hint = hintRef.current?.getBoundingClientRect();
|
|
2299
|
-
const reach = 48;
|
|
2300
|
-
setHintAway(
|
|
2301
|
-
!!hint && e.clientX > hint.left - reach && e.clientX < hint.right + reach && e.clientY > hint.top - reach && e.clientY < hint.bottom + reach
|
|
2302
|
-
);
|
|
2303
2355
|
const el = deferred ? null : elementAt(e);
|
|
2304
2356
|
setHoverRect(el ? el.getBoundingClientRect() : null);
|
|
2305
2357
|
});
|
|
2306
2358
|
};
|
|
2307
2359
|
const toggleTarget = (el) => {
|
|
2308
2360
|
const target = describeIterationElement(elementAsNode(el));
|
|
2361
|
+
const added = !targetsRef.current.some((t) => t.selector === target.selector);
|
|
2309
2362
|
setTargets(
|
|
2310
2363
|
(prev) => prev.some((t) => t.selector === target.selector) ? prev.filter((t) => t.selector !== target.selector) : prev.length < 50 ? [...prev, target] : prev
|
|
2311
2364
|
);
|
|
2365
|
+
return added ? target : null;
|
|
2312
2366
|
};
|
|
2313
2367
|
const onClick = (e) => {
|
|
2314
2368
|
if (e.shiftKey) return;
|
|
@@ -2316,8 +2370,18 @@ function IterationPanel({
|
|
|
2316
2370
|
if (!el) return;
|
|
2317
2371
|
e.preventDefault();
|
|
2318
2372
|
e.stopImmediatePropagation();
|
|
2319
|
-
toggleTarget(el);
|
|
2320
|
-
|
|
2373
|
+
const added = toggleTarget(el);
|
|
2374
|
+
if (added) {
|
|
2375
|
+
const r = el.getBoundingClientRect();
|
|
2376
|
+
const pointed = e.detail > 0 || e.clientX !== 0 || e.clientY !== 0;
|
|
2377
|
+
const x = pointed ? e.clientX : r.left;
|
|
2378
|
+
const y = pointed ? e.clientY : r.bottom;
|
|
2379
|
+
if (!anchorRef.current)
|
|
2380
|
+
slotHeight.current = rootRef.current?.querySelector(".isotone-notes-panel .isotone-input")?.offsetHeight ?? 0;
|
|
2381
|
+
setAnchor({ selector: added.selector, dx: x - r.left, dy: y - r.top, x, y });
|
|
2382
|
+
setNotesOpen(true);
|
|
2383
|
+
focusWhilePicking();
|
|
2384
|
+
}
|
|
2321
2385
|
};
|
|
2322
2386
|
const onKey = (e) => {
|
|
2323
2387
|
if (e.key === " ") {
|
|
@@ -2328,11 +2392,25 @@ function IterationPanel({
|
|
|
2328
2392
|
if (!e.repeat) toggleTarget(el);
|
|
2329
2393
|
return;
|
|
2330
2394
|
}
|
|
2395
|
+
const typing = e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey && !e.isComposing;
|
|
2396
|
+
if (typing) {
|
|
2397
|
+
const el = e.composedPath().find((node) => node instanceof Element);
|
|
2398
|
+
if (el && !el.closest("[data-isotone]") && el.closest("input, textarea, select, [contenteditable]")) return;
|
|
2399
|
+
setPicking(false);
|
|
2400
|
+
setNotesOpen(true);
|
|
2401
|
+
textareaRef.current?.focus();
|
|
2402
|
+
return;
|
|
2403
|
+
}
|
|
2331
2404
|
if (e.key !== "Escape" && e.key !== "Enter") return;
|
|
2332
|
-
|
|
2405
|
+
const field = textareaRef.current;
|
|
2406
|
+
if (e.key === "Enter" && anchorRef.current && field && !e.shiftKey && field.value.trim() && e.composedPath().includes(field))
|
|
2333
2407
|
return;
|
|
2334
2408
|
e.preventDefault();
|
|
2335
2409
|
e.stopImmediatePropagation();
|
|
2410
|
+
if (e.key === "Escape") {
|
|
2411
|
+
setTargets([]);
|
|
2412
|
+
setAnchor(null);
|
|
2413
|
+
}
|
|
2336
2414
|
setPicking(false);
|
|
2337
2415
|
setNotesOpen(true);
|
|
2338
2416
|
focusComposer();
|
|
@@ -2344,7 +2422,6 @@ function IterationPanel({
|
|
|
2344
2422
|
document.body.style.cursor = cursor;
|
|
2345
2423
|
cancelAnimationFrame(frame);
|
|
2346
2424
|
setHoverRect(null);
|
|
2347
|
-
setHintAway(false);
|
|
2348
2425
|
window.removeEventListener("mousemove", onMove, true);
|
|
2349
2426
|
window.removeEventListener("click", onClick, true);
|
|
2350
2427
|
window.removeEventListener("keydown", onKey, true);
|
|
@@ -2361,7 +2438,113 @@ function IterationPanel({
|
|
|
2361
2438
|
};
|
|
2362
2439
|
}, []);
|
|
2363
2440
|
useEffect3(() => {
|
|
2364
|
-
if (!
|
|
2441
|
+
if (!notesOpen) setAnchor(null);
|
|
2442
|
+
}, [notesOpen]);
|
|
2443
|
+
useLayoutEffect4(() => {
|
|
2444
|
+
const float = floatRef.current;
|
|
2445
|
+
if (!anchor || !float) {
|
|
2446
|
+
lastFloat.current = null;
|
|
2447
|
+
return;
|
|
2448
|
+
}
|
|
2449
|
+
floatNode.current = float;
|
|
2450
|
+
const from = lastFloat.current;
|
|
2451
|
+
let frame;
|
|
2452
|
+
let side = null;
|
|
2453
|
+
const place = () => {
|
|
2454
|
+
frame = void 0;
|
|
2455
|
+
let { x, y } = anchor;
|
|
2456
|
+
try {
|
|
2457
|
+
const el = document.querySelector(anchor.selector);
|
|
2458
|
+
if (el) {
|
|
2459
|
+
const r = el.getBoundingClientRect();
|
|
2460
|
+
x = r.left + anchor.dx;
|
|
2461
|
+
y = r.top + anchor.dy;
|
|
2462
|
+
}
|
|
2463
|
+
} catch {
|
|
2464
|
+
}
|
|
2465
|
+
const vw = document.documentElement.clientWidth || innerWidth;
|
|
2466
|
+
const vh = innerHeight;
|
|
2467
|
+
const w = float.offsetWidth;
|
|
2468
|
+
const h = float.offsetHeight;
|
|
2469
|
+
let left = x + FLOAT_GAP;
|
|
2470
|
+
if (left + w > vw - FLOAT_MARGIN) left = x - FLOAT_GAP - w;
|
|
2471
|
+
left = Math.max(FLOAT_MARGIN, Math.min(left, vw - w - FLOAT_MARGIN));
|
|
2472
|
+
const fitsBelow = y + FLOAT_GAP + h <= vh - FLOAT_MARGIN;
|
|
2473
|
+
const fitsAbove = y - FLOAT_GAP - h >= FLOAT_MARGIN;
|
|
2474
|
+
if (side === null || (side === "below" ? !fitsBelow && fitsAbove : !fitsAbove && fitsBelow))
|
|
2475
|
+
side = fitsBelow || !fitsAbove ? "below" : "above";
|
|
2476
|
+
float.style.left = `${left}px`;
|
|
2477
|
+
if (side === "below") {
|
|
2478
|
+
const top = Math.max(FLOAT_MARGIN, Math.min(y + FLOAT_GAP, vh - h - FLOAT_MARGIN));
|
|
2479
|
+
float.style.top = `${top}px`;
|
|
2480
|
+
float.style.bottom = "auto";
|
|
2481
|
+
float.style.transformOrigin = `${x - left}px ${y - top}px`;
|
|
2482
|
+
} else {
|
|
2483
|
+
const bottom = Math.max(FLOAT_MARGIN, Math.min(vh - y + FLOAT_GAP, vh - h - FLOAT_MARGIN));
|
|
2484
|
+
float.style.bottom = `${bottom}px`;
|
|
2485
|
+
float.style.top = "auto";
|
|
2486
|
+
float.style.transformOrigin = `${x - left}px ${y - (vh - bottom - h)}px`;
|
|
2487
|
+
}
|
|
2488
|
+
lastFloat.current = { left, top: side === "below" ? Number.parseFloat(float.style.top) : vh - Number.parseFloat(float.style.bottom) - h };
|
|
2489
|
+
};
|
|
2490
|
+
const schedule = () => {
|
|
2491
|
+
frame ??= requestAnimationFrame(place);
|
|
2492
|
+
};
|
|
2493
|
+
place();
|
|
2494
|
+
const to = lastFloat.current;
|
|
2495
|
+
const still = stillMotion(float) || typeof float.animate !== "function";
|
|
2496
|
+
let glide;
|
|
2497
|
+
if (still) {
|
|
2498
|
+
} else if (!from)
|
|
2499
|
+
glide = float.animate(
|
|
2500
|
+
[{ opacity: 0, transform: "scale(.96)" }, { opacity: 1, transform: "none" }],
|
|
2501
|
+
{ duration: MOTION.enter, easing: MOTION.easeOut }
|
|
2502
|
+
);
|
|
2503
|
+
else if (to && (from.left !== to.left || from.top !== to.top))
|
|
2504
|
+
glide = float.animate(
|
|
2505
|
+
[{ translate: `${from.left - to.left}px ${from.top - to.top}px` }, { translate: "0 0" }],
|
|
2506
|
+
{ duration: MOTION.enter, easing: MOTION.easeOut }
|
|
2507
|
+
);
|
|
2508
|
+
const observer = typeof ResizeObserver !== "undefined" ? new ResizeObserver(schedule) : void 0;
|
|
2509
|
+
observer?.observe(float);
|
|
2510
|
+
window.addEventListener("scroll", schedule, { capture: true, passive: true });
|
|
2511
|
+
window.addEventListener("resize", schedule);
|
|
2512
|
+
return () => {
|
|
2513
|
+
if (frame !== void 0) cancelAnimationFrame(frame);
|
|
2514
|
+
glide?.cancel();
|
|
2515
|
+
observer?.disconnect();
|
|
2516
|
+
window.removeEventListener("scroll", schedule, true);
|
|
2517
|
+
window.removeEventListener("resize", schedule);
|
|
2518
|
+
};
|
|
2519
|
+
}, [anchor]);
|
|
2520
|
+
useLayoutEffect4(() => {
|
|
2521
|
+
const last = floatNode.current;
|
|
2522
|
+
const root = rootRef.current;
|
|
2523
|
+
if (floating || !last || !root) return;
|
|
2524
|
+
floatNode.current = null;
|
|
2525
|
+
if (typeof last.animate !== "function" || stillMotion(root)) return;
|
|
2526
|
+
const ghost = last.cloneNode(true);
|
|
2527
|
+
const field = last.querySelector("textarea");
|
|
2528
|
+
const copy = ghost.querySelector("textarea");
|
|
2529
|
+
if (field && copy) copy.value = field.value;
|
|
2530
|
+
ghost.classList.add("isotone-exit");
|
|
2531
|
+
ghost.inert = true;
|
|
2532
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
2533
|
+
ghost.removeAttribute("role");
|
|
2534
|
+
root.appendChild(ghost);
|
|
2535
|
+
const fade = ghost.animate([{ opacity: 1 }, { opacity: 0, transform: "scale(.98)" }], {
|
|
2536
|
+
duration: MOTION.exit,
|
|
2537
|
+
easing: MOTION.easeOut,
|
|
2538
|
+
fill: "forwards"
|
|
2539
|
+
});
|
|
2540
|
+
fade.onfinish = () => ghost.remove();
|
|
2541
|
+
return () => {
|
|
2542
|
+
fade.cancel();
|
|
2543
|
+
ghost.remove();
|
|
2544
|
+
};
|
|
2545
|
+
}, [floating]);
|
|
2546
|
+
useEffect3(() => {
|
|
2547
|
+
if (!picking && !floating) {
|
|
2365
2548
|
setRects([]);
|
|
2366
2549
|
return;
|
|
2367
2550
|
}
|
|
@@ -2400,7 +2583,7 @@ function IterationPanel({
|
|
|
2400
2583
|
window.removeEventListener("scroll", schedule, true);
|
|
2401
2584
|
window.removeEventListener("resize", schedule);
|
|
2402
2585
|
};
|
|
2403
|
-
}, [targets, picking]);
|
|
2586
|
+
}, [targets, picking, floating]);
|
|
2404
2587
|
const variantsPanel = variants && /* @__PURE__ */ jsxs4(
|
|
2405
2588
|
"section",
|
|
2406
2589
|
{
|
|
@@ -2436,6 +2619,162 @@ function IterationPanel({
|
|
|
2436
2619
|
]
|
|
2437
2620
|
}
|
|
2438
2621
|
);
|
|
2622
|
+
const anchored = targets.find((t) => t.selector === anchor?.selector) ?? targets[0];
|
|
2623
|
+
const awayLabel = anchored ? `Next to ${anchored.text || anchored.tag}${targets.length > 1 ? ` and ${targets.length - 1} more` : ""}` : "Next to where you clicked";
|
|
2624
|
+
const composer = (
|
|
2625
|
+
// biome-ignore lint/a11y/useKeyWithClickEvents: This extends the native textarea's pointer area; keyboard users focus the textarea directly.
|
|
2626
|
+
/* @__PURE__ */ jsxs4(
|
|
2627
|
+
"fieldset",
|
|
2628
|
+
{
|
|
2629
|
+
className: "isotone-input",
|
|
2630
|
+
"aria-label": "Feedback composer",
|
|
2631
|
+
onClick: (event) => {
|
|
2632
|
+
if (event.defaultPrevented || event.button !== 0) return;
|
|
2633
|
+
const target = event.target;
|
|
2634
|
+
if (target.closest('button, textarea, input, select, a, [contenteditable="true"]')) return;
|
|
2635
|
+
textareaRef.current?.focus({ preventScroll: true });
|
|
2636
|
+
},
|
|
2637
|
+
children: [
|
|
2638
|
+
/* @__PURE__ */ jsxs4("div", { className: "isotone-context", "aria-live": "polite", children: [
|
|
2639
|
+
editing && /* @__PURE__ */ jsxs4("span", { className: "isotone-chip", children: [
|
|
2640
|
+
"Editing note",
|
|
2641
|
+
/* @__PURE__ */ jsx5(
|
|
2642
|
+
"button",
|
|
2643
|
+
{
|
|
2644
|
+
type: "button",
|
|
2645
|
+
"aria-label": "Cancel edit",
|
|
2646
|
+
title: "Cancel edit",
|
|
2647
|
+
onClick: cancelEdit,
|
|
2648
|
+
children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 })
|
|
2649
|
+
}
|
|
2650
|
+
)
|
|
2651
|
+
] }),
|
|
2652
|
+
variantTarget && /* @__PURE__ */ jsxs4("span", { className: "isotone-chip", children: [
|
|
2653
|
+
/* @__PURE__ */ jsx5(Icon2, { name: "sliders", size: 12 }),
|
|
2654
|
+
/* @__PURE__ */ jsxs4("span", { onPointerOver: showOverflowTitle, children: [
|
|
2655
|
+
variantTarget.label,
|
|
2656
|
+
variantTarget.optionLabel ? ` / ${variantTarget.optionLabel}` : ""
|
|
2657
|
+
] }),
|
|
2658
|
+
!editing?.variantAction && /* @__PURE__ */ jsx5(
|
|
2659
|
+
"button",
|
|
2660
|
+
{
|
|
2661
|
+
type: "button",
|
|
2662
|
+
"aria-label": "Remove variant reference",
|
|
2663
|
+
title: "Remove reference",
|
|
2664
|
+
onClick: () => setVariantTarget(null),
|
|
2665
|
+
children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 })
|
|
2666
|
+
}
|
|
2667
|
+
)
|
|
2668
|
+
] }),
|
|
2669
|
+
targets.map((target, index) => /* @__PURE__ */ jsxs4(
|
|
2670
|
+
"span",
|
|
2671
|
+
{
|
|
2672
|
+
className: "isotone-chip",
|
|
2673
|
+
children: [
|
|
2674
|
+
/* @__PURE__ */ jsx5("span", { onPointerOver: showOverflowTitle, children: target.text || target.tag }),
|
|
2675
|
+
/* @__PURE__ */ jsx5(
|
|
2676
|
+
"button",
|
|
2677
|
+
{
|
|
2678
|
+
type: "button",
|
|
2679
|
+
"aria-label": `Remove pinned element ${index + 1}`,
|
|
2680
|
+
title: "Unpin element",
|
|
2681
|
+
onClick: () => setTargets(
|
|
2682
|
+
(prev) => prev.filter((t) => t.selector !== target.selector)
|
|
2683
|
+
),
|
|
2684
|
+
children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 })
|
|
2685
|
+
}
|
|
2686
|
+
)
|
|
2687
|
+
]
|
|
2688
|
+
},
|
|
2689
|
+
target.selector
|
|
2690
|
+
)),
|
|
2691
|
+
notice && /* @__PURE__ */ jsx5("p", { role: "status", className: "isotone-notice", children: notice })
|
|
2692
|
+
] }),
|
|
2693
|
+
/* @__PURE__ */ jsxs4("div", { className: "isotone-entry", children: [
|
|
2694
|
+
/* @__PURE__ */ jsx5(
|
|
2695
|
+
"textarea",
|
|
2696
|
+
{
|
|
2697
|
+
ref: textareaRef,
|
|
2698
|
+
value: text,
|
|
2699
|
+
"aria-label": "Feedback for the agent",
|
|
2700
|
+
onChange: (e) => setText(e.target.value),
|
|
2701
|
+
onFocus: () => {
|
|
2702
|
+
if (!keepPicking.current) setPicking(false);
|
|
2703
|
+
},
|
|
2704
|
+
onPointerDown: () => setPicking(false),
|
|
2705
|
+
onScroll: (event) => {
|
|
2706
|
+
event.currentTarget.dataset.scrolled = String(event.currentTarget.scrollTop > 0);
|
|
2707
|
+
},
|
|
2708
|
+
onKeyDown: (e) => {
|
|
2709
|
+
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
2710
|
+
e.preventDefault();
|
|
2711
|
+
void send();
|
|
2712
|
+
}
|
|
2713
|
+
},
|
|
2714
|
+
placeholder: targets.length || variantTarget ? "Feedback on your selection\u2026" : "What would you change?",
|
|
2715
|
+
rows: 1,
|
|
2716
|
+
className: "isotone-textarea"
|
|
2717
|
+
}
|
|
2718
|
+
),
|
|
2719
|
+
/* @__PURE__ */ jsxs4("div", { className: "isotone-toolbar", children: [
|
|
2720
|
+
/* @__PURE__ */ jsx5(
|
|
2721
|
+
"button",
|
|
2722
|
+
{
|
|
2723
|
+
type: "button",
|
|
2724
|
+
title: `Select elements${shortcutLabel ? ` \xB7 ${shortcutLabel}` : ""}`,
|
|
2725
|
+
"aria-label": "Select elements",
|
|
2726
|
+
"aria-pressed": picking,
|
|
2727
|
+
className: "isotone-pick isotone-icon",
|
|
2728
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2729
|
+
onClick: () => {
|
|
2730
|
+
cancelAnimationFrame(focusFrame.current);
|
|
2731
|
+
setPickingWithPanel(false);
|
|
2732
|
+
setPicking((v) => !v);
|
|
2733
|
+
},
|
|
2734
|
+
children: /* @__PURE__ */ jsx5(Icon2, { name: "pin", size: 14 })
|
|
2735
|
+
}
|
|
2736
|
+
),
|
|
2737
|
+
floating && picking && /* @__PURE__ */ jsx5("span", { ref: fadeIn, className: "isotone-float-hint", children: "Click to pin more \xB7 hold \u21E7 to use the page" }),
|
|
2738
|
+
/* @__PURE__ */ jsx5(
|
|
2739
|
+
"button",
|
|
2740
|
+
{
|
|
2741
|
+
type: "button",
|
|
2742
|
+
"aria-label": editing ? "Save edit" : "Send note",
|
|
2743
|
+
disabled: !text.trim(),
|
|
2744
|
+
className: "isotone-send",
|
|
2745
|
+
title: editing ? "Save edit (Enter)" : "Send note (Enter)",
|
|
2746
|
+
onClick: () => void send(),
|
|
2747
|
+
children: /* @__PURE__ */ jsx5(Icon2, { name: "send", size: 14 })
|
|
2748
|
+
}
|
|
2749
|
+
)
|
|
2750
|
+
] }),
|
|
2751
|
+
picking && !floating && /* @__PURE__ */ jsxs4("div", { ref: fadeIn, className: "isotone-picking", "data-still": pickingWithPanel, children: [
|
|
2752
|
+
/* @__PURE__ */ jsx5(Icon2, { name: "pin", size: 14 }),
|
|
2753
|
+
/* @__PURE__ */ jsxs4("p", { children: [
|
|
2754
|
+
/* @__PURE__ */ jsx5("span", { children: targets.length ? `${targets.length} selected` : "Click elements on the page" }),
|
|
2755
|
+
/* @__PURE__ */ jsx5("small", { children: "Hold \u21E7 to use the page normally" })
|
|
2756
|
+
] }),
|
|
2757
|
+
/* @__PURE__ */ jsxs4(
|
|
2758
|
+
"button",
|
|
2759
|
+
{
|
|
2760
|
+
type: "button",
|
|
2761
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2762
|
+
onClick: () => {
|
|
2763
|
+
setPicking(false);
|
|
2764
|
+
focusComposer();
|
|
2765
|
+
},
|
|
2766
|
+
children: [
|
|
2767
|
+
"Done ",
|
|
2768
|
+
/* @__PURE__ */ jsx5("kbd", { children: "\u21B5" })
|
|
2769
|
+
]
|
|
2770
|
+
}
|
|
2771
|
+
)
|
|
2772
|
+
] })
|
|
2773
|
+
] })
|
|
2774
|
+
]
|
|
2775
|
+
}
|
|
2776
|
+
)
|
|
2777
|
+
);
|
|
2439
2778
|
const notesPanel = notesAvailable && /* @__PURE__ */ jsxs4(
|
|
2440
2779
|
"section",
|
|
2441
2780
|
{
|
|
@@ -2531,131 +2870,26 @@ function IterationPanel({
|
|
|
2531
2870
|
},
|
|
2532
2871
|
note.id
|
|
2533
2872
|
)) }),
|
|
2534
|
-
/* @__PURE__ */ jsx5("div", { className: "isotone-compose", children: /* @__PURE__ */ jsxs4(
|
|
2535
|
-
"
|
|
2873
|
+
/* @__PURE__ */ jsx5("div", { className: "isotone-compose", children: floating ? /* @__PURE__ */ jsxs4(
|
|
2874
|
+
"button",
|
|
2536
2875
|
{
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
},
|
|
2876
|
+
type: "button",
|
|
2877
|
+
ref: fadeInContent,
|
|
2878
|
+
className: "isotone-away",
|
|
2879
|
+
title: "Write here instead",
|
|
2880
|
+
style: { minHeight: slotHeight.current || void 0 },
|
|
2881
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
2882
|
+
onClick: dockComposer,
|
|
2545
2883
|
children: [
|
|
2546
|
-
/* @__PURE__ */
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
"button",
|
|
2551
|
-
{
|
|
2552
|
-
type: "button",
|
|
2553
|
-
"aria-label": "Cancel edit",
|
|
2554
|
-
title: "Cancel edit",
|
|
2555
|
-
onClick: cancelEdit,
|
|
2556
|
-
children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 })
|
|
2557
|
-
}
|
|
2558
|
-
)
|
|
2559
|
-
] }),
|
|
2560
|
-
variantTarget && /* @__PURE__ */ jsxs4("span", { className: "isotone-chip", children: [
|
|
2561
|
-
/* @__PURE__ */ jsx5(Icon2, { name: "sliders", size: 12 }),
|
|
2562
|
-
/* @__PURE__ */ jsxs4("span", { onPointerOver: showOverflowTitle, children: [
|
|
2563
|
-
variantTarget.label,
|
|
2564
|
-
variantTarget.optionLabel ? ` / ${variantTarget.optionLabel}` : ""
|
|
2565
|
-
] }),
|
|
2566
|
-
!editing?.variantAction && /* @__PURE__ */ jsx5(
|
|
2567
|
-
"button",
|
|
2568
|
-
{
|
|
2569
|
-
type: "button",
|
|
2570
|
-
"aria-label": "Remove variant reference",
|
|
2571
|
-
title: "Remove reference",
|
|
2572
|
-
onClick: () => setVariantTarget(null),
|
|
2573
|
-
children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 })
|
|
2574
|
-
}
|
|
2575
|
-
)
|
|
2576
|
-
] }),
|
|
2577
|
-
targets.map((target, index) => /* @__PURE__ */ jsxs4(
|
|
2578
|
-
"span",
|
|
2579
|
-
{
|
|
2580
|
-
className: "isotone-chip",
|
|
2581
|
-
children: [
|
|
2582
|
-
/* @__PURE__ */ jsx5("span", { onPointerOver: showOverflowTitle, children: target.text || target.tag }),
|
|
2583
|
-
/* @__PURE__ */ jsx5(
|
|
2584
|
-
"button",
|
|
2585
|
-
{
|
|
2586
|
-
type: "button",
|
|
2587
|
-
"aria-label": `Remove pinned element ${index + 1}`,
|
|
2588
|
-
title: "Unpin element",
|
|
2589
|
-
onClick: () => setTargets(
|
|
2590
|
-
(prev) => prev.filter((t) => t.selector !== target.selector)
|
|
2591
|
-
),
|
|
2592
|
-
children: /* @__PURE__ */ jsx5(Icon2, { name: "close", size: 12 })
|
|
2593
|
-
}
|
|
2594
|
-
)
|
|
2595
|
-
]
|
|
2596
|
-
},
|
|
2597
|
-
target.selector
|
|
2598
|
-
)),
|
|
2599
|
-
notice && /* @__PURE__ */ jsx5("p", { role: "status", className: "isotone-notice", children: notice })
|
|
2884
|
+
/* @__PURE__ */ jsx5(Icon2, { name: "pin", size: 14 }),
|
|
2885
|
+
/* @__PURE__ */ jsxs4("span", { className: "isotone-away-text", children: [
|
|
2886
|
+
/* @__PURE__ */ jsx5("span", { children: "Writing on the page" }),
|
|
2887
|
+
/* @__PURE__ */ jsx5("small", { children: awayLabel })
|
|
2600
2888
|
] }),
|
|
2601
|
-
/* @__PURE__ */ jsx5(
|
|
2602
|
-
"textarea",
|
|
2603
|
-
{
|
|
2604
|
-
ref: textareaRef,
|
|
2605
|
-
value: text,
|
|
2606
|
-
"aria-label": "Feedback for the agent",
|
|
2607
|
-
onChange: (e) => setText(e.target.value),
|
|
2608
|
-
onFocus: () => {
|
|
2609
|
-
if (!keepPicking.current) setPicking(false);
|
|
2610
|
-
},
|
|
2611
|
-
onPointerDown: () => setPicking(false),
|
|
2612
|
-
onScroll: (event) => {
|
|
2613
|
-
event.currentTarget.dataset.scrolled = String(event.currentTarget.scrollTop > 0);
|
|
2614
|
-
},
|
|
2615
|
-
onKeyDown: (e) => {
|
|
2616
|
-
if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing) {
|
|
2617
|
-
e.preventDefault();
|
|
2618
|
-
void send();
|
|
2619
|
-
}
|
|
2620
|
-
},
|
|
2621
|
-
placeholder: targets.length || variantTarget ? "Feedback on your selection\u2026" : "What would you change?",
|
|
2622
|
-
rows: 1,
|
|
2623
|
-
className: "isotone-textarea"
|
|
2624
|
-
}
|
|
2625
|
-
),
|
|
2626
|
-
/* @__PURE__ */ jsxs4("div", { className: "isotone-toolbar", children: [
|
|
2627
|
-
/* @__PURE__ */ jsx5(
|
|
2628
|
-
"button",
|
|
2629
|
-
{
|
|
2630
|
-
type: "button",
|
|
2631
|
-
title: "Select elements",
|
|
2632
|
-
"aria-label": "Select elements",
|
|
2633
|
-
"aria-pressed": picking,
|
|
2634
|
-
className: "isotone-pick isotone-icon",
|
|
2635
|
-
onMouseDown: (e) => e.preventDefault(),
|
|
2636
|
-
onClick: () => {
|
|
2637
|
-
cancelAnimationFrame(focusFrame.current);
|
|
2638
|
-
setPicking((v) => !v);
|
|
2639
|
-
},
|
|
2640
|
-
children: /* @__PURE__ */ jsx5(Icon2, { name: "pin", size: 14 })
|
|
2641
|
-
}
|
|
2642
|
-
),
|
|
2643
|
-
/* @__PURE__ */ jsx5(
|
|
2644
|
-
"button",
|
|
2645
|
-
{
|
|
2646
|
-
type: "button",
|
|
2647
|
-
"aria-label": editing ? "Save edit" : "Send note",
|
|
2648
|
-
disabled: !text.trim(),
|
|
2649
|
-
className: "isotone-send",
|
|
2650
|
-
title: editing ? "Save edit (Enter)" : "Send note (Enter)",
|
|
2651
|
-
onClick: () => void send(),
|
|
2652
|
-
children: /* @__PURE__ */ jsx5(Icon2, { name: "send", size: 14 })
|
|
2653
|
-
}
|
|
2654
|
-
)
|
|
2655
|
-
] })
|
|
2889
|
+
/* @__PURE__ */ jsx5("span", { className: "isotone-away-action", children: "Move here" })
|
|
2656
2890
|
]
|
|
2657
2891
|
}
|
|
2658
|
-
) })
|
|
2892
|
+
) : composer })
|
|
2659
2893
|
]
|
|
2660
2894
|
}
|
|
2661
2895
|
);
|
|
@@ -2675,27 +2909,7 @@ function IterationPanel({
|
|
|
2675
2909
|
className: "isotone-root",
|
|
2676
2910
|
children: [
|
|
2677
2911
|
/* @__PURE__ */ jsx5("div", { className: "isotone-rail-track", "aria-hidden": "true" }),
|
|
2678
|
-
picking
|
|
2679
|
-
/* @__PURE__ */ jsxs4("span", { children: [
|
|
2680
|
-
targets.length,
|
|
2681
|
-
" selected \xB7 click to add or remove \xB7 hold \u21E7 to use the page"
|
|
2682
|
-
] }),
|
|
2683
|
-
/* @__PURE__ */ jsxs4(
|
|
2684
|
-
"button",
|
|
2685
|
-
{
|
|
2686
|
-
type: "button",
|
|
2687
|
-
onClick: () => {
|
|
2688
|
-
setPicking(false);
|
|
2689
|
-
focusComposer();
|
|
2690
|
-
},
|
|
2691
|
-
children: [
|
|
2692
|
-
"Done ",
|
|
2693
|
-
/* @__PURE__ */ jsx5("kbd", { children: "\u21B5" })
|
|
2694
|
-
]
|
|
2695
|
-
}
|
|
2696
|
-
)
|
|
2697
|
-
] }),
|
|
2698
|
-
picking && [
|
|
2912
|
+
(picking || floating) && [
|
|
2699
2913
|
...rects,
|
|
2700
2914
|
...hoverRect ? [
|
|
2701
2915
|
{
|
|
@@ -2720,6 +2934,16 @@ function IterationPanel({
|
|
|
2720
2934
|
},
|
|
2721
2935
|
rect.selector
|
|
2722
2936
|
)),
|
|
2937
|
+
floating && notesOpen && /* @__PURE__ */ jsx5(
|
|
2938
|
+
"div",
|
|
2939
|
+
{
|
|
2940
|
+
ref: floatRef,
|
|
2941
|
+
className: "isotone-float",
|
|
2942
|
+
role: "dialog",
|
|
2943
|
+
"aria-label": "Feedback on the selection",
|
|
2944
|
+
children: composer
|
|
2945
|
+
}
|
|
2946
|
+
),
|
|
2723
2947
|
/* @__PURE__ */ jsx5(UpdateNotice, { endpoint: updateEndpoint, active: open }),
|
|
2724
2948
|
/* @__PURE__ */ jsxs4(
|
|
2725
2949
|
"fieldset",
|
|
@@ -2781,10 +3005,12 @@ function IterationPanel({
|
|
|
2781
3005
|
title: "Feedback",
|
|
2782
3006
|
"aria-controls": `${id}-notes`,
|
|
2783
3007
|
"aria-expanded": notesOpen,
|
|
2784
|
-
onClick: () => {
|
|
2785
|
-
setNotesOpen((v) => !v);
|
|
3008
|
+
onClick: (event) => {
|
|
2786
3009
|
setExpanded(false);
|
|
2787
|
-
if (
|
|
3010
|
+
if (notesOpen) {
|
|
3011
|
+
setNotesOpen(false);
|
|
3012
|
+
setPicking(false);
|
|
3013
|
+
} else startFeedback(event.detail > 0);
|
|
2788
3014
|
},
|
|
2789
3015
|
children: [
|
|
2790
3016
|
/* @__PURE__ */ jsx5(Icon2, { name: "message" }),
|
|
@@ -2809,10 +3035,10 @@ function IterationPanel({
|
|
|
2809
3035
|
"aria-label": notesAvailable ? "Isotone" : "Variants",
|
|
2810
3036
|
title: `Isotone${working.size ? ` \xB7 ${working.size} in progress` : ""}${shortcutLabel ? ` \xB7 ${shortcutLabel}` : ""}`,
|
|
2811
3037
|
"aria-expanded": railOpen,
|
|
2812
|
-
onClick: () => {
|
|
3038
|
+
onClick: (event) => {
|
|
2813
3039
|
if (open) closeAll();
|
|
2814
3040
|
else if (expanded) setExpanded(false);
|
|
2815
|
-
else openInitial();
|
|
3041
|
+
else openInitial(event.detail > 0);
|
|
2816
3042
|
},
|
|
2817
3043
|
children: [
|
|
2818
3044
|
working.size > 0 && /* @__PURE__ */ jsx5("span", { className: "isotone-ring", ref: ringPulse }),
|