Files
librenotes/internal/notesium/web/app/pane.js
T
libretechandClaude Opus 4.7 49ad467aa9 Switch pane resize handle to pointer events
internal/notesium/web/app/pane.js drove the sidebar/pane resize
via mousedown + window-level mousemove/mouseup, which doesn't
fire on touch (browsers only emulate mouse for taps, not drags).

Replaced with pointer events:
- @pointerdown on the handle (covers mouse, touch, pen).
- setPointerCapture so we keep receiving pointermove/pointerup
  events when the pointer drifts off the handle. This eliminates
  the need for document-level listeners and avoids stuck-drag
  states when the user releases outside the window.
- pointermove + pointerup + pointercancel listeners on the
  captured target only — when the capture ends they're removed
  regardless of whether the user is still on top of the handle.
- Filter on event.pointerId so a second simultaneous touch
  (e.g., a multi-finger gesture) cannot hijack the in-progress
  resize.
- event.button !== 0 guard rejects right-click / middle-click.
- touch-action: none on the handle so the browser doesn't try
  to interpret a horizontal drag as a page scroll.

CodeMirror's internal mousedown handlers in note.js / preview.js
are left alone — those are link-click guards, not drags, and
CodeMirror's own pointer support handles touch internally.

Closes #20.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:30:43 +02:00

93 lines
3.4 KiB
JavaScript

var t = `
<div class="relative flex-none" :style="{ width: paneWidth + 'px' }">
<div class="flex flex-col h-full w-full">
<slot></slot>
</div>
<div v-show="resizing" v-text="paneWidth + 'px'" class="absolute bottom-0 right-0 p-4 text-gray-400 text-xs"></div>
<div @dblclick="setDefaultWidth()" @pointerdown="startResize"
:class="direction == 'right' ? 'left-full pl-1.5 mr-1.5' : 'right-full pr-1.5 ml-1.5'"
class="z-50 absolute group inset-y-0 cursor-ew-resize flex items-center"
style="touch-action: none;">
<div class="h-8 w-1 rounded-full group-hover:bg-gray-300"></div>
</div>
</div>
`
export default {
props: {
name: { type: String },
defaultWidth: { type: Number, default: 200 },
minWidth: { type: Number, default: 100 },
direction: { type: String, default: "right" },
},
data() {
return {
paneWidth: null,
maxWidth: null,
startResizeClientX: null,
startResizePaneWidth: null,
resizing: false,
activePointerId: null,
}
},
methods: {
startResize(event) {
// Only respond to primary button / single touch / pen.
// event.button is 0 for primary mouse and is also 0 for
// touch / pen via PointerEvent.
if (event.button !== undefined && event.button !== 0) return;
this.startResizeClientX = event.clientX;
this.startResizePaneWidth = this.paneWidth;
this.maxWidth = this.$el.parentElement.offsetWidth - 50;
this.resizing = true;
this.activePointerId = event.pointerId;
event.preventDefault();
// Capture the pointer so we keep getting move/up events even
// when the pointer leaves the handle. This replaces the
// window-level mousemove/mouseup listeners and handles touch
// and pen identically.
try { event.target.setPointerCapture(event.pointerId); } catch (_) {}
event.target.addEventListener('pointermove', this.doResize);
event.target.addEventListener('pointerup', this.stopResize);
event.target.addEventListener('pointercancel', this.stopResize);
},
doResize(event) {
if (event.pointerId !== this.activePointerId) return;
let draggedDistance = event.clientX - this.startResizeClientX;
if (this.direction === 'left') draggedDistance = -draggedDistance;
const newWidth = this.startResizePaneWidth + draggedDistance;
if (newWidth <= this.maxWidth && newWidth >= this.minWidth ) this.paneWidth = newWidth;
},
stopResize(event) {
if (event && event.pointerId !== this.activePointerId) return;
this.resizing = false;
this.savePreferredWidth();
const target = event && event.target;
if (target) {
try { target.releasePointerCapture(this.activePointerId); } catch (_) {}
target.removeEventListener('pointermove', this.doResize);
target.removeEventListener('pointerup', this.stopResize);
target.removeEventListener('pointercancel', this.stopResize);
}
this.activePointerId = null;
},
setDefaultWidth() {
this.paneWidth = this.defaultWidth;
this.savePreferredWidth()
},
savePreferredWidth() {
const key = `${this.name}Width`;
this.$notesiumState[key] = this.paneWidth;
},
loadPreferredWidth() {
const key = `${this.name}Width`;
this.paneWidth = parseInt(this.$notesiumState[key], 10) || this.defaultWidth;
},
},
mounted() {
this.loadPreferredWidth();
},
template: t
}