Reel Trackpad

Description

07.26c31v2 Reel Trackpad

#1. Install Code

#1.1. Hover on Page where you use Reel Gallery > Click Gear icon

07.26c31v2 Reel Trackpad

#1.2. Click Advanced > Paste this code

  • If you use Personal/Basic Plan and your plan doesn’t support Injection, see #3.1
<!-- 07.26c31v2 Reel Trackpad -->
<script>
window.ReelTrackpadConfig = {
  reelSelector: '.gallery-reel[data-controller="GalleryReel"]',
  enableWheel: true,
  wheelIncludeVertical: true,
  wheelThreshold: 40,
  wheelCooldown: 350,
  enableDrag: true,
  dragIncludeTouch: false,
  dragThreshold: 45,
  dragStepDistance: 220,
  enableKeyboard: true,
  keyboardScope: 'hover'
};
</script>
<script>
(function () {
  'use strict';
  var cfg = window.ReelTrackpadConfig;
  if (!cfg) return;

  var MOVE_TOLERANCE = 5;
  var STYLE_ID = 'reel-tp-style';

  function injectStyle() {
    if (document.getElementById(STYLE_ID)) return;
    var s = document.createElement('style');
    s.id = STYLE_ID;
    s.textContent =
      '.reel-tp-grab{cursor:grab}' +
      '.reel-tp-grab.reel-tp-dragging{cursor:grabbing;user-select:none}' +
      '.reel-tp-grab.reel-tp-dragging img{pointer-events:none}';
    document.head.appendChild(s);
  }

  function step(reel, dir) {
    var sel = dir > 0 ? '[data-next]' : '[data-previous]';
    var btn = reel.querySelector('.gallery-reel-controls ' + sel);
    if (btn) btn.click();
  }

  function isTyping() {
    var el = document.activeElement;
    if (!el) return false;
    var tag = el.tagName;
    return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
  }

  function handleKey(reel, e) {
    if (isTyping()) return;
    if (e.key === 'ArrowRight') { step(reel, 1); e.preventDefault(); }
    else if (e.key === 'ArrowLeft') { step(reel, -1); e.preventDefault(); }
  }

  function bindWheel(reel) {
    var accum = 0, locked = false, timer;
    function release() { locked = false; accum = 0; }
    reel.addEventListener('wheel', function (e) {
      var horizontal = Math.abs(e.deltaX) >= Math.abs(e.deltaY);
      var delta = horizontal ? e.deltaX : (cfg.wheelIncludeVertical ? e.deltaY : 0);
      if (!delta) return;
      e.preventDefault();
      clearTimeout(timer);
      timer = setTimeout(release, cfg.wheelCooldown);
      if (locked) return;
      accum += delta;
      if (Math.abs(accum) >= cfg.wheelThreshold) {
        step(reel, accum > 0 ? 1 : -1);
        accum = 0;
        locked = true;
      }
    }, { passive: false });
  }

  function bindDrag(reel) {
    var list = reel.querySelector('.gallery-reel-list') || reel;
    var dragging = false, moved = false, startX = 0, pid = null;
    list.classList.add('reel-tp-grab');

    list.addEventListener('pointerdown', function (e) {
      if (!cfg.dragIncludeTouch && e.pointerType === 'touch') return;
      if (e.button !== undefined && e.button !== 0) return;
      dragging = true; moved = false; startX = e.clientX; pid = e.pointerId;
    });

    list.addEventListener('pointermove', function (e) {
      if (!dragging) return;
      var dist = e.clientX - startX;
      if (!moved && Math.abs(dist) > MOVE_TOLERANCE) {
        moved = true;
        list.classList.add('reel-tp-dragging');
        if (list.setPointerCapture && pid != null) {
          try { list.setPointerCapture(pid); } catch (err) {}
        }
      }
      if (moved) e.preventDefault();
    });

    function endDrag(e) {
      if (!dragging) return;
      dragging = false;
      list.classList.remove('reel-tp-dragging');
      var dist = e.clientX - startX;
      if (!moved || Math.abs(dist) < cfg.dragThreshold) return;
      var dir = dist < 0 ? 1 : -1;
      var steps = cfg.dragStepDistance > 0
        ? Math.max(1, Math.round(Math.abs(dist) / cfg.dragStepDistance))
        : 1;
      for (var i = 0; i < steps; i++) step(reel, dir);
    }

    list.addEventListener('pointerup', endDrag);
    list.addEventListener('pointercancel', function () {
      dragging = false;
      list.classList.remove('reel-tp-dragging');
    });

    list.addEventListener('click', function (e) {
      if (moved) { e.preventDefault(); e.stopPropagation(); moved = false; }
    }, true);

    list.addEventListener('dragstart', function (e) {
      if (dragging) e.preventDefault();
    });
  }

  function bindKeyboard(reel) {
    if (cfg.keyboardScope === 'focus') {
      if (!reel.hasAttribute('tabindex')) reel.setAttribute('tabindex', '0');
      reel.addEventListener('keydown', function (e) { handleKey(reel, e); });
      return;
    }
    if (cfg.keyboardScope === 'hover') {
      var over = false;
      reel.addEventListener('pointerenter', function () { over = true; });
      reel.addEventListener('pointerleave', function () { over = false; });
      document.addEventListener('keydown', function (e) { if (over) handleKey(reel, e); });
      return;
    }
    var inView = false;
    if ('IntersectionObserver' in window) {
      new IntersectionObserver(function (entries) {
        entries.forEach(function (en) { inView = en.isIntersecting; });
      }, { threshold: 0.3 }).observe(reel);
    } else {
      inView = true;
    }
    document.addEventListener('keydown', function (e) { if (inView) handleKey(reel, e); });
  }

  function initReel(reel) {
    if (reel.dataset.reelTrackpadDone) return;
    var next = reel.querySelector('.gallery-reel-controls [data-next]');
    var prev = reel.querySelector('.gallery-reel-controls [data-previous]');
    if (!next && !prev) return;
    reel.dataset.reelTrackpadDone = '1';
    if (cfg.enableWheel) bindWheel(reel);
    if (cfg.enableDrag) { injectStyle(); bindDrag(reel); }
    if (cfg.enableKeyboard) bindKeyboard(reel);
  }

  function run() {
    if (document.body.classList.contains('sqs-edit-mode-active')) return;
    document.querySelectorAll(cfg.reelSelector).forEach(initReel);
  }

  window.addEventListener('load', run);
  window.addEventListener('mercury:load', run);
})();
</script>

07.26c31v2 Reel Trackpad

#2. Customize

nothing now

#3. Other

#3.1. If you use Personal/Basic Plan and your plan doesn’t support Injection, you can edit current page (page where you use Slideshow Reel) > Add a Block > Choose Markdown

04.26c10v2 Header Sound Icon

Add this code into Markdown

<script>
window.ReelTrackpadConfig = {
  reelSelector: '.gallery-reel[data-controller="GalleryReel"]',
  enableWheel: true,
  wheelIncludeVertical: true,
  wheelThreshold: 40,
  wheelCooldown: 350,
  enableDrag: true,
  dragIncludeTouch: false,
  dragThreshold: 45,
  dragStepDistance: 220,
  enableKeyboard: true,
  keyboardScope: 'hover'
};
</script>
<script>
(function () {
  'use strict';
  var cfg = window.ReelTrackpadConfig;
  if (!cfg) return;

  var MOVE_TOLERANCE = 5;
  var STYLE_ID = 'reel-tp-style';

  function injectStyle() {
    if (document.getElementById(STYLE_ID)) return;
    var s = document.createElement('style');
    s.id = STYLE_ID;
    s.textContent =
      '.reel-tp-grab{cursor:grab}' +
      '.reel-tp-grab.reel-tp-dragging{cursor:grabbing;user-select:none}' +
      '.reel-tp-grab.reel-tp-dragging img{pointer-events:none}';
    document.head.appendChild(s);
  }

  function step(reel, dir) {
    var sel = dir > 0 ? '[data-next]' : '[data-previous]';
    var btn = reel.querySelector('.gallery-reel-controls ' + sel);
    if (btn) btn.click();
  }

  function isTyping() {
    var el = document.activeElement;
    if (!el) return false;
    var tag = el.tagName;
    return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
  }

  function handleKey(reel, e) {
    if (isTyping()) return;
    if (e.key === 'ArrowRight') { step(reel, 1); e.preventDefault(); }
    else if (e.key === 'ArrowLeft') { step(reel, -1); e.preventDefault(); }
  }

  function bindWheel(reel) {
    var accum = 0, locked = false, timer;
    function release() { locked = false; accum = 0; }
    reel.addEventListener('wheel', function (e) {
      var horizontal = Math.abs(e.deltaX) >= Math.abs(e.deltaY);
      var delta = horizontal ? e.deltaX : (cfg.wheelIncludeVertical ? e.deltaY : 0);
      if (!delta) return;
      e.preventDefault();
      clearTimeout(timer);
      timer = setTimeout(release, cfg.wheelCooldown);
      if (locked) return;
      accum += delta;
      if (Math.abs(accum) >= cfg.wheelThreshold) {
        step(reel, accum > 0 ? 1 : -1);
        accum = 0;
        locked = true;
      }
    }, { passive: false });
  }

  function bindDrag(reel) {
    var list = reel.querySelector('.gallery-reel-list') || reel;
    var dragging = false, moved = false, startX = 0, pid = null;
    list.classList.add('reel-tp-grab');

    list.addEventListener('pointerdown', function (e) {
      if (!cfg.dragIncludeTouch && e.pointerType === 'touch') return;
      if (e.button !== undefined && e.button !== 0) return;
      dragging = true; moved = false; startX = e.clientX; pid = e.pointerId;
    });

    list.addEventListener('pointermove', function (e) {
      if (!dragging) return;
      var dist = e.clientX - startX;
      if (!moved && Math.abs(dist) > MOVE_TOLERANCE) {
        moved = true;
        list.classList.add('reel-tp-dragging');
        if (list.setPointerCapture && pid != null) {
          try { list.setPointerCapture(pid); } catch (err) {}
        }
      }
      if (moved) e.preventDefault();
    });

    function endDrag(e) {
      if (!dragging) return;
      dragging = false;
      list.classList.remove('reel-tp-dragging');
      var dist = e.clientX - startX;
      if (!moved || Math.abs(dist) < cfg.dragThreshold) return;
      var dir = dist < 0 ? 1 : -1;
      var steps = cfg.dragStepDistance > 0
        ? Math.max(1, Math.round(Math.abs(dist) / cfg.dragStepDistance))
        : 1;
      for (var i = 0; i < steps; i++) step(reel, dir);
    }

    list.addEventListener('pointerup', endDrag);
    list.addEventListener('pointercancel', function () {
      dragging = false;
      list.classList.remove('reel-tp-dragging');
    });

    list.addEventListener('click', function (e) {
      if (moved) { e.preventDefault(); e.stopPropagation(); moved = false; }
    }, true);

    list.addEventListener('dragstart', function (e) {
      if (dragging) e.preventDefault();
    });
  }

  function bindKeyboard(reel) {
    if (cfg.keyboardScope === 'focus') {
      if (!reel.hasAttribute('tabindex')) reel.setAttribute('tabindex', '0');
      reel.addEventListener('keydown', function (e) { handleKey(reel, e); });
      return;
    }
    if (cfg.keyboardScope === 'hover') {
      var over = false;
      reel.addEventListener('pointerenter', function () { over = true; });
      reel.addEventListener('pointerleave', function () { over = false; });
      document.addEventListener('keydown', function (e) { if (over) handleKey(reel, e); });
      return;
    }
    var inView = false;
    if ('IntersectionObserver' in window) {
      new IntersectionObserver(function (entries) {
        entries.forEach(function (en) { inView = en.isIntersecting; });
      }, { threshold: 0.3 }).observe(reel);
    } else {
      inView = true;
    }
    document.addEventListener('keydown', function (e) { if (inView) handleKey(reel, e); });
  }

  function initReel(reel) {
    if (reel.dataset.reelTrackpadDone) return;
    var next = reel.querySelector('.gallery-reel-controls [data-next]');
    var prev = reel.querySelector('.gallery-reel-controls [data-previous]');
    if (!next && !prev) return;
    reel.dataset.reelTrackpadDone = '1';
    if (cfg.enableWheel) bindWheel(reel);
    if (cfg.enableDrag) { injectStyle(); bindDrag(reel); }
    if (cfg.enableKeyboard) bindKeyboard(reel);
  }

  function run() {
    if (document.body.classList.contains('sqs-edit-mode-active')) return;
    document.querySelectorAll(cfg.reelSelector).forEach(initReel);
  }

  window.addEventListener('load', run);
  window.addEventListener('mercury:load', run);
})();
</script>

07.26c31v2 Reel Trackpad

 

Buy me a coffee