PageSourceSearch

https://odysseus-lxcq.onrender.com/static/js/editor/build/transform-popup.js

js odysseus-lxcq.onrender.com collected 2026-10-03 11:44:59 UTC 5,396 bytes, 109 lines download raw bytes

1/**
2 * Static markup for the Transform popup that floats over the canvas
3 * when the user activates the Resize/Transform tool.
4 *
5 * Pure DOM — no module state, no event listeners. The caller wires all
6 * IDs via document.getElementById / pop.querySelector.
7 *
8 * @returns {string}
9 */
10export function transformPopupHTML() {
11  return `
12    <div class="ge-adj-head ge-transform-popup-head" data-transform-drag>
13      <span class="ge-adj-icon">
14        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 7 7 3 11 7"/><line x1="7" y1="3" x2="7" y2="21"/><polyline points="21 17 17 21 13 17"/><line x1="17" y1="21" x2="17" y2="3"/></svg>
15      </span>
16      <span class="ge-adj-title">Transform</span>
17      <button type="button" id="ge-transform-aspect" class="ge-transform-aspect-btn" title="Lock aspect ratio" aria-pressed="true">
18        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
19      </button>
20      <span class="ge-head-btns">
21        <button class="ge-adj-min" type="button" title="Minimise" id="ge-transform-min">&minus;</button>
22        <button class="ge-adj-close" type="button" title="Cancel" id="ge-transform-cancel">&times;</button>
23      </span>
24    </div>
25    <div class="ge-transform-popup-body">
26      <div class="ge-transform-field">
27        <label>W</label>
28        <input type="number" class="ge-transform-popup-input" id="ge-transform-w" step="1" />
29        <span class="ge-transform-spin" data-spin-for="ge-transform-w">
30          <button type="button" data-spin="down" tabindex="-1" aria-label="Decrease width">−</button>
31          <button type="button" data-spin="up" tabindex="-1" aria-label="Increase width">+</button>
32        </span>
33      </div>
34      <div class="ge-transform-field">
35        <label>H</label>
36        <input type="number" class="ge-transform-popup-input" id="ge-transform-h" step="1" />
37        <span class="ge-transform-spin" data-spin-for="ge-transform-h">
38          <button type="button" data-spin="down" tabindex="-1" aria-label="Decrease height">−</button>
39          <button type="button" data-spin="up" tabindex="-1" aria-label="Increase height">+</button>
40        </span>
41      </div>
42      <div class="ge-row-break"></div>
43      <div class="ge-transform-field">
44        <label>↻</label>
45        <input type="number" class="ge-transform-popup-input ge-transform-popup-input-rot" id="ge-transform-rot" step="1" value="0" />
46        <span class="ge-transform-spin" data-spin-for="ge-transform-rot">
47          <button type="button" data-spin="down" tabindex="-1" aria-label="Rotate -1°">−</button>
48          <button type="button" data-spin="up" tabindex="-1" aria-label="Rotate +1°">+</button>
49        </span>
50      </div>
51      <button type="button" class="ge-btn ge-btn-sm" id="ge-transform-cancel-btn">Cancel</button>
52      <button type="button" class="ge-btn ge-btn-sm ge-btn-primary" id="ge-transform-apply">Apply</button>
53    </div>
54    <p class="ge-transform-popup-hint">Type <strong>-</strong> before W / H to flip.</p>
55  `;
56}
57
58
59/**
60 * Wire a `<span class="ge-transform-spin">…<button data-spin="up|down"/>…</span>`
61 * group with tap-to-tick + hold-to-repeat. After 1.5 s the repeat
62 * accelerates from 70ms→30ms intervals so users can rapidly scrub a
63 * numeric field without mashing the button.
64 *
65 * On each tick, the helper looks up the target `<input>` by the
66 * spin-group's `data-spin-for` attribute and dispatches an `input`
67 * event so the rest of the popup's wiring picks up the change.
68 *
69 * @param {HTMLElement} root   Element that owns one or more spin groups
70 *                             (e.g. the transform popup).
71 */
72export function attachSpinRepeat(root) {
73  root.querySelectorAll('.ge-transform-spin button').forEach(btn => {
74    const tick = (shift) => {
75      const targetId = btn.parentElement?.dataset?.spinFor;
76      if (!targetId) return;
77      const input = root.querySelector('#' + CSS.escape(targetId));
78      if (!input || input.readOnly) return;
79      const step = shift ? 10 : 1;
80      const cur = parseInt(input.value, 10) || 0;
81      const next = btn.dataset.spin === 'up' ? cur + step : cur - step;
82      input.value = String(next);
83      input.dispatchEvent(new Event('input', { bubbles: true }));
84    };
85    let holdTimeout = null, repeatInterval = null, started = 0;
86    btn.addEventListener('pointerdown', (e) => {
87      e.preventDefault();
88      tick(e.shiftKey);
89      started = Date.now();
90      holdTimeout = setTimeout(() => {
91        repeatInterval = setInterval(() => {
92          tick(false);
93          if (Date.now() - started > 1500 && repeatInterval) {
94            clearInterval(repeatInterval);
95            repeatInterval = setInterval(() => tick(false), 30);
96          }
97        }, 70);
98      }, 350);
99    });
100    const endHold = () => {
101      if (holdTimeout) clearTimeout(holdTimeout);
102      if (repeatInterval) clearInterval(repeatInterval);
103      holdTimeout = null; repeatInterval = null;
104    };
105    btn.addEventListener('pointerup', endHold);
106    btn.addEventListener('pointerleave', endHold);
107    btn.addEventListener('pointercancel', endHold);
108  });
109}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.