pvbemu/app/toolkit/Splitter.js

302 lines
9.4 KiB
JavaScript

"use strict";
// Interactive splitter
Toolkit.Splitter = class Splitter extends Toolkit.Component {
// Object constructor
constructor(application, options) {
super(application, "div", options);
options = options || {};
// Configure instance fields
this.component = options.component || null;
this.dragPointer = null;
this.dragPos = 0;
this.dragSize = 0;
this.orientation = options.orientation || "horizontal";
this.name = options.name || "";
this.edge = options.edge ||
(this.orientation == "horizontal" ? "top" : "left");
// Configure element
this.element.setAttribute("role" , "separator");
this.element.setAttribute("tabindex" , "0");
this.element.setAttribute("aria-valuemin", "0");
this.element.addEventListener("keydown" , e=>this.onkeydown (e));
this.element.addEventListener("pointerdown", e=>this.onpointerdown(e));
this.element.addEventListener("pointermove", e=>this.onpointermove(e));
this.element.addEventListener("pointerup" , e=>this.onpointerup (e));
// Configure properties
this.setComponent (this.component );
this.setName (this.name );
this.setOrientation(this.orientation);
this.application.addComponent(this);
}
///////////////////////////// Public Methods //////////////////////////////
// Request focus on the appropriate element
focus() {
this.element.focus();
}
// Retrieve the component managed by this Splitter
getComponent() {
return this.component;
}
// Retrieve the component's accessible name
getName() {
return this.name;
}
// Retrieve the component's orientation
getOrientation() {
return this.orientation;
}
// Determine the current and maximum separator values
measure() {
let max = 0;
let now = 0;
// Mesure the component
if (this.component != null && this.parent != null) {
let component = this.component.getBounds();
let bounds = this.getBounds();
let panel = this.parent.getBounds();
// Horizontal Splitter
if (this.orientation == "horizontal") {
max = panel.height - bounds.height;
now = Math.max(0, Math.min(max, component.height));
this.component.setSize(null, now);
}
// Vertical Splitter
else {
max = panel.width - bounds.width;
now = Math.max(0, Math.min(max, component.width));
this.component.setSize(now, null);
}
}
// Configure element
this.element.setAttribute("aria-valuemax", max);
this.element.setAttribute("aria-valuenow", now);
}
// Specify the component managed by this Splitter
setComponent(component) {
this.component = component = component || null;
this.element.setAttribute("aria-controls",
component == null ? "" : component.id);
this.measure();
}
// Specify the component's accessible name
setName(name) {
this.name = name || "";
this.localize();
}
// Specify the component's orientation
setOrientation(orientation) {
switch (orientation) {
case "horizontal":
this.orientation = "horizontal";
this.setSize(null, 3, true);
this.element.setAttribute("aria-orientation", "horizontal");
this.element.style.cursor = "ew-resize";
break;
case "vertical":
this.orientation = "vertical";
this.setSize(3, null, true);
this.element.setAttribute("aria-orientation", "vertical");
this.element.style.cursor = "ns-resize";
}
this.measure();
}
///////////////////////////// Package Methods /////////////////////////////
// Update display text with localized strings
localize() {
let name = this.name;
if (this.application) {
name = this.application.translate(name, this);
}
this.element.setAttribute("aria-label", name);
}
///////////////////////////// Private Methods /////////////////////////////
// Key press event handler
onkeydown(e) {
// Error checking
if (this.component == null)
return;
let pos = this.component.getBounds();
let size = this .getBounds();
let max = this.parent .getBounds();
if (this.orientation == "horizontal") {
max = max .height;
pos = pos .height;
size = size.height;
} else {
max = max .width;
pos = pos .width;
size = size.width;
}
if (this.edge == "top" || this.edge == "left")
max -= size;
// Processing by key
if (this.component != null) switch (e.key) {
case "ArrowDown":
switch (this.edge) {
case "bottom":
this.component.setSize(null, Math.min(max, pos - 6));
break;
case "top":
this.component.setSize(null, Math.min(max, pos + 6));
}
break;
case "ArrowLeft":
switch (this.edge) {
case "left":
this.component.setSize(Math.min(max, pos - 6), null);
break;
case "right":
this.component.setSize(Math.min(max, pos + 6), null);
}
break;
case "ArrowRight":
switch (this.edge) {
case "left":
this.component.setSize(Math.min(max, pos + 6), null);
break;
case "right":
this.component.setSize(Math.min(max, pos - 6), null);
}
break;
case "ArrowUp":
switch (this.edge) {
case "bottom":
this.component.setSize(null, Math.min(max, pos + 6));
break;
case "top":
this.component.setSize(null, Math.min(max, pos - 6));
}
break;
case "Escape":
if (this.dragPointer === null)
return;
this.element.releasePointerCapture(this.dragPointer);
this.dragPointer = null;
if (this.orientation == "horizontal")
this.component.setHeight(null, this.dragSize);
else this.component.setWidth(this.dragSize, null);
break;
default: return;
}
// Configure event
e.preventDefault();
e.stopPropagation();
}
// Pointer down event handler
onpointerdown(e) {
// Request focus
this.focus();
// Configure event
e.stopPropagation();
// Error checking
if (
this.component == null ||
e.button != 0 ||
this.element.hasPointerCapture(e.pointerId)
) return;
// Capture the pointer
this.element.setPointerCapture(e.pointerId);
this.dragPointer = e.pointerId;
let bounds = this.component.getBounds();
if (this.orientation == "horizontal") {
this.dragPos = e.y;
this.dragSize = bounds.height;
} else {
this.dragPos = e.x;
this.dragSize = bounds.width;
}
}
// Pointer move event handler
onpointermove(e) {
// Configure event
e.preventDefault();
e.stopPropagation();
// Error checking
if (
this.component == null ||
!this.element.hasPointerCapture(e.pointerId)
) return;
// Resize the component
let bounds = this.getBounds();
let panel = this.parent.getBounds();
switch (this.edge) {
case "bottom":
this.component.setSize(null, Math.max(0, Math.min(
this.dragSize - e.y + this.dragPos,
panel.height - bounds.height
)));
break;
case "left":
this.component.setSize(Math.max(0, Math.min(
this.dragSize + e.x - this.dragPos,
panel.width - bounds.width
)), null);
break;
case "right":
this.component.setSize(Math.max(0, Math.min(
this.dragSize - e.x + this.dragPos,
panel.width - bounds.width
)), null);
break;
case "top":
this.component.setSize(null, Math.max(0, Math.min(
this.dragSize + e.y - this.dragPos,
panel.height - bounds.height
)));
break;
}
this.measure();
}
// Pointer up event handler
onpointerup(e) {
e.preventDefault();
e.stopPropagation();
this.element.releasePointerCapture(e.pointerId);
this.dragPointer = null;
}
};