302 lines
9.4 KiB
JavaScript
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;
|
||
|
}
|
||
|
|
||
|
};
|