pvbemu/app/toolkit/ScrollBar.js

1150 lines
35 KiB
JavaScript

import { Component } from /**/"./Component.js";
let Toolkit;
///////////////////////////////////////////////////////////////////////////////
// ScrollBar //
///////////////////////////////////////////////////////////////////////////////
// Range picker with track, scroll box and scroll buttons
class ScrollBar extends Component {
static Component = Component;
///////////////////////// Initialization Methods //////////////////////////
constructor(gui, options) {
super(gui, options, {
className: "tk tk-scrollbar",
focusable: true,
role : "scrollbar",
tabStop : true,
tagName : "div",
style : {
alignItems : "stretch",
display : "flex",
flexDirection: "column"
}
});
// Configure instance fields
this.extent = 50;
this.increment = 1;
this.isEnabled = true;
this.maximum = 100;
this.minimum = 0;
this.orientation = "vertical";
this.value = 25;
// Unit decrement button
this.unitDown = document.createElement("div");
this.unitDown.className = "tk tk-unit-down";
this.append(this.unitDown);
this.unitDown.addEventListener("pointerdown",
e=>this.onIncrementPointerDown(e));
this.unitDown.addEventListener("pointermove",
e=>this.onIncrementPointerMove(e));
this.unitDown.addEventListener("pointerup" ,
e=>this.onIncrementPointerUp (e));
// Block decrement track
this.blockDown = document.createElement("div");
this.blockDown.className = "tk tk-block-down";
this.append(this.blockDown);
this.blockDown.addEventListener("pointerdown",
e=>this.onIncrementPointerDown(e));
this.blockDown.addEventListener("pointermove",
e=>this.onIncrementPointerMove(e));
this.blockDown.addEventListener("pointerup" ,
e=>this.onIncrementPointerUp (e));
// Scroll box
this.thumb = document.createElement("div");
this.thumb.className = "tk tk-thumb";
this.append(this.thumb);
this.thumb.addEventListener("pointerdown",
e=>this.onThumbPointerDown(e));
this.thumb.addEventListener("pointermove",
e=>this.onThumbPointerMove(e));
this.thumb.addEventListener("pointerup" ,
e=>this.onThumbPointerUp (e));
// Block increment track
this.blockUp = document.createElement("div");
this.blockUp.className = "tk tk-block-up";
this.append(this.blockUp);
this.blockUp.addEventListener("pointerdown",
e=>this.onIncrementPointerDown(e));
this.blockUp.addEventListener("pointermove",
e=>this.onIncrementPointerMove(e));
this.blockUp.addEventListener("pointerup" ,
e=>this.onIncrementPointerUp (e));
// Unit increment track
this.unitUp = document.createElement("div");
this.unitUp.className = "tk tk-unit-up";
this.append(this.unitUp);
this.unitUp.addEventListener("pointerdown",
e=>this.onIncrementPointerDown(e));
this.unitUp.addEventListener("pointermove",
e=>this.onIncrementPointerMove(e));
this.unitUp.addEventListener("pointerup" ,
e=>this.onIncrementPointerUp (e));
// Configure component
options = options || {};
this.setOrientation("vertical", true);
this.addEventListener("resize", ()=>this.update());
this.addEventListener("keydown", e=>this.onKeyDown(e));
if ("enabled" in options)
this.setEnabled (options.enabled );
if ("extent" in options)
this.setExtent (options.extent , true);
if ("increment" in options)
this.setIncrement (options.increment , true);
if ("minimum" in options)
this.setMinimum (options.minimum , true);
if ("maximum" in options)
this.setMaximum (options.maximum , true);
if ("orientation" in options)
this.setOrientation(options.orientation, true);
this.setValue("value" in options ? options.value :
Math.round((this.minimum + this.maximum - this.extent) / 2));
}
///////////////////////////// Event Listeners /////////////////////////////
// Increment pointer down
onIncrementPointerDown(e) {
this.focus();
// Error checking
if (
!this.isEnabled || e.button != 0 ||
this.extent >= this.maximum - this.minimum ||
e.target.hasPointerCapture(e.pointerCapture)
) return;
// Configure event
e.target.setPointerCapture(e.pointerId);
e.stopPropagation();
e.preventDefault();
// Configure element
e.target.classList.add("tk-active");
}
// Increment pointer move
onIncrementPointerMove(e) {
// Error checking
if (!e.target.hasPointerCapture(e.pointerId))
return;
// Configure event
e.stopPropagation();
e.preventDefault();
// Determine whether the event is within the element's bounds
let bounds = e.target.getBoundingClientRect();
e.target.classList[
e.offsetX >= 0 && e.offsetX < bounds.width &&
e.offsetY >= 0 && e.offsetY < bounds.height ?
"add" : "remove"
]("tk-active");
}
// Increment pointer up
onIncrementPointerUp(e) {
// Error checking
if (!e.target.hasPointerCapture(e.pointerId) || e.button != 0)
return;
// Configure event
e.target.releasePointerCapture(e.pointerId);
e.stopPropagation();
e.preventDefault();
// Configure component
e.target.classList.remove("tk-active");
// Take the appropriate action
let bounds = e.target.getBoundingClientRect();
if (
e.offsetX >= 0 && e.offsetX < bounds.width &&
e.offsetY >= 0 && e.offsetY < bounds.height
) switch (e.target) {
case this.blockDown:
this.setValue(this.value - this.extent ); break;
case this.blockUp:
this.setValue(this.value + this.extent ); break;
case this.unitDown:
this.setValue(this.value - this.increment); break;
case this.unitUp:
this.setValue(this.value + this.increment); break;
}
}
// Scroll bar key press
onKeyDown(e) {
// Error checking
if (!this.isEnabled)
return;
// Processing by key
switch (e.key) {
// Arrow key navigation
case "ArrowDown":
if (this.orientation == "horizontal")
return;
this.setValue(this.value + this.increment);
break;
case "ArrowLeft":
if (this.orientation == "vertical")
return;
this.setValue(this.value - this.increment);
break;
case "ArrowRight":
if (this.orientation == "vertical")
return;
this.setValue(this.value + this.increment);
break;
case "ArrowUp":
if (this.orientation == "horizontal")
return;
this.setValue(this.value - this.increment);
break;
// Page key navigation
case "PageDown":
this.setValue(this.value + this.extent);
break;
case "PageUp":
this.setValue(this.value - this.extent);
break;
// Cancel a thumb drag
case "Escape":
// No thumb drag is in progress
if (
this.drag == null ||
!this.thumb.hasPointerCapture(this.drag.pointerId)
) return;
// Cancel the thumb drag
this.thumb.releasePointerCapture(this.drag.pointerId);
this.setValue(this.drag.value);
this.drag = null;
break;
default: return;
}
// Configure element
e.stopPropagation();
e.preventDefault();
}
// Thumb pointer down
onThumbPointerDown(e) {
this.focus();
// Error checking
if (
!this.isEnabled || e.button != 0 ||
this.extent >= this.maximum - this.minimum ||
this.thumb.hasPointerCapture(e.pointerId)
) return;
// Configure event
this.thumb.setPointerCapture(e.pointerId);
e.stopPropagation();
e.preventDefault();
// Begin dragging
this.measure();
this.drag = {
pointerId: e.pointerId,
thumbPos : this.thumbPos,
value : this.value,
x : e.screenX / devicePixelRatio,
y : e.screenY / devicePixelRatio
};
}
// Thumb pointer move
onThumbPointerMove(e) {
// Error checking
if (!this.thumb.hasPointerCapture(e.pointerId))
return;
// Configure event
e.stopPropagation();
e.preventDefault();
// Update the thumb's position and potentially the value
let delta = this.orientation == "horizontal" ?
e.screenX / devicePixelRatio - this.drag.x :
e.screenY / devicePixelRatio - this.drag.y
;
this.update(this.drag.thumbPos + delta);
}
// Thumb pointer up
onThumbPointerUp(e) {
// Error checking
if (!this.thumb.hasPointerCapture(e.pointerId) || e.button != 0)
return;
// Configure event
this.thumb.releasePointerCapture(e.pointerId);
e.stopPropagation();
e.preventDefault();
// Configure instance fields
this.drag = null;
}
///////////////////////////// Public Methods //////////////////////////////
// Specify whether or not the scroll bar is enabled
setEnabled(enabled) {
enabled = !!enabled;
if (enabled == this.isEnabled)
return;
this.isEnabled = enabled;
this.setAttribute("aria-disabled", !enabled);
this.update();
}
// Specify how many scroll units are currently visible
setExtent(extent, noUpdate) {
// Error checking
extent = parseInt(extent);
if (isNaN(extent))
return;
// Configure instance fields
this.extent = Math.max(0, extent);
// Update elements
this.setAttribute("aria-valuemax",
Math.max(this.minimum, this.maximum - this.extent));
if (!noUpdate)
this.update();
}
// Specify the value change when clicking a unit scroll button
setIncrement(increment) {
// Error checking
increment = parseInt(increment);
if (isNaN(increment) || increment < 1)
return;
// Configure instance fields
this.increment = increment;
}
// Specify the maximum scroll value
setMaximum(maximum, noUpdate) {
// Error checking
maximum = parseInt(maximum);
if (isNaN(maximum))
return;
// Update maximum
this.maximum = maximum;
this.setAttribute("aria-valuemax", maximum);
// Update other properties as needed
if (maximum < this.minimum)
this.setMinimum(maximum , true);
if (maximum - this.extent < this.value)
this.setValue (maximum - this.extent , true);
// Update elements
this.setAttribute("aria-valuemax",
Math.max(this.minimum, this.maximum - this.extent));
if (!noUpdate)
this.update();
}
// Specify the minimum scroll value
setMinimum(minimum, noUpdate) {
// Error checking
minimum = parseInt(minimum);
if (isNaN(minimum))
return;
// Update minimum
this.minimum = minimum;
this.setAttribute("aria-valuemin", minimum);
// Update other properties as needed
if (minimum > this.maximum)
this.setMaximum(minimum, true);
if (minimum > this.value)
this.setValue (minimum, true);
// Update elements
if (!noUpdate)
this.update();
}
// Specify the widget's orientation
setOrientation(orientation, noUpdate) {
// Configure element
switch (orientation) {
case "horizontal":
this.element.style.flexDirection = "row";
this.element.setAttribute("aria-orientation", "horizontal");
break;
case "vertical":
this.element.style.flexDirection = "column";
this.element.setAttribute("aria-orientation", "vertical");
break;
default: return;
}
// Configure instance fields
this.orientation = orientation;
// Update elements
if (!noUpdate)
this.update();
}
// Specify the current scroll value
setValue(value, noUpdate) {
// Error checking
value = parseInt(value);
if (isNaN(value) || value == this.value)
return;
// Update value
value = Math.max(this.minimum,
Math.min(value, this.maximum - this.extent));
if (value == this.value)
return;
this.value = value;
this.setAttribute("aria-valuenow", value);
// Update elements
if (!noUpdate)
this.update();
// Notify event listeners
this.event("input", { value: value });
}
///////////////////////////// Package Methods /////////////////////////////
// Update the global Toolkit object
static setToolkit(toolkit) {
Toolkit = toolkit;
}
// Configure elements given the current widget state
update(thumbPos) {
this.measure();
// Update the value according to the given thumb position
if (thumbPos !== undefined) {
let maxPos = this.trackSize - this.thumbSize;
thumbPos = Math.max(0, Math.min(maxPos, thumbPos));
this.setValue(Math.round(
this.minimum + thumbPos / maxPos *
(this.maximum - this.extent - this.minimum)
), true);
}
// Reposition the thumb according to current value
else {
thumbPos = Math.round(
(this.trackSize - this.thumbSize ) *
(this.value - this.minimum) /
(this.maximum - this.extent - this.minimum)
);
}
// Configure elements
this.thumb .style.flexBasis = this.thumbSize + "px";
this.blockDown.style.flexBasis = thumbPos + "px";
this.blockUp .style.flexBasis =
(this.trackSize - this.thumbSize - thumbPos) + "px";
this.element.classList[
this.isEnabled &&
this.extent >= this.maximum - this.minimum ?
"add" : "remove"
]("tk-full");
}
///////////////////////////// Private Methods /////////////////////////////
// Measure the current dimensions of widget components
measure() {
let bndBlockDown = this.blockDown.getBoundingClientRect();
let bndThis = this.getBounds();
let bndUnitDown = this.unitDown.getBoundingClientRect();
let bndUnitUp = this.unitUp .getBoundingClientRect();
let dim = this.orientation=="horizontal" ? "width" : "height";
// Track size is total size less the unit buttons
this.trackSize = bndThis[dim] - bndUnitDown[dim] - bndUnitUp[dim];
// Thumb size is proportional to extent
this.thumbSize = Math.max(0, Math.min(this.trackSize, Math.max(4,
this.minimum == this.maximum ? this.trackSize : Math.round(
this.trackSize * this.extent /
(this.maximum - this.minimum)
)
)));
// Thumb position is the size of the block down track
this.thumbPos = bndBlockDown[dim];
}
};
///////////////////////////////////////////////////////////////////////////////
// ScrollPane //
///////////////////////////////////////////////////////////////////////////////
// Scrolling viewport for an external view
class ScrollPane extends Component {
//////////////////////////////// Constants ////////////////////////////////
// Scroll bar policies
static ALWAYS = 0;
static AS_NEEDED = 1;
static NEVER = 2;
///////////////////////// Initialization Methods //////////////////////////
constructor(gui, options) {
super(gui, options, {
className: "tk tk-scrollpane",
tagName : "div",
style : {
overflow: "hidden",
position: "relative"
}
});
// Configure instance fields
this.view = null;
this.viewResize = null;
// Viewport
this.viewport = document.createElement("div");
this.viewport.className = "tk tk-viewport";
Object.assign(this.viewport.style, {
position: "absolute",
bottom : "0",
left : "0",
overflow: "hidden",
right : "0",
top : "0"
});
this.append(this.viewport);
// Vertical scroll bar
this.vertical = new ScrollBar(gui, {
orientation: "vertical",
visibility : true,
style : {
bottom : "0",
position: "absolute",
right : "0",
top : "0"
}
});
this.append(this.vertical);
this.vertical.addEventListener("input",
e=>this.onVerticalScroll(e));
// Horizontal scroll bar
this.horizontal = new ScrollBar(gui, {
orientation: "horizontal",
visibility : true,
style : {
bottom : "0",
left : "0",
position: "absolute",
right : "0"
}
});
this.append(this.horizontal);
this.horizontal.addEventListener("input",
e=>this.onHorizontalScroll(e));
// Configure component
options = options || {};
this.viewport.addEventListener("scroll", e=>this.onScroll(e));
this.addEventListener("resize" , ()=>this.update());
this.addEventListener("pointerdown", ()=>this.focus (), true);
if ("horizontal" in options)
this.setPolicy("horizontal", options.horizontal, true);
if ("vertical" in options)
this.setPolicy("vertical" , options.vertical , true);
if ("view" in options)
this.setView (options.view , true);
this.update();
}
///////////////////////////// Event Handlers //////////////////////////////
// Horizontal scroll bar scroll
onHorizontalScroll(e) {
if (this.view != null)
this.viewport.scrollLeft = e.value;
}
// Placeholder for element resize
onResize(e) { }
// Viewport scrolled
onScroll(e) {
this.horizontal.setValue(this.viewport.scrollLeft);
this.vertical .setValue(this.viewport.scrollTop );
}
// Vertical scroll bar scroll
onVerticalScroll(e) {
if (this.view != null)
this.viewport.scrollTop = e.value;
}
///////////////////////////// Public Methods //////////////////////////////
// Specify a scroll bar visibility policy
setPolicy(orientation, value, noUpdate) {
// Error checking
switch (orientation) {
case "horizontal": break;
case "vertical" : break;
default : return;
}
switch (value) {
case ScrollPane.ALWAYS : break;
case ScrollPane.AS_NEEDED: break;
case ScrollPane.NEVER : break;
default : return;
}
// Configure instance fields
this[orientation].policy = value;
// Update elements
if (!noUpdate)
this.update();
}
// Specify the internal view
setView(view, noUpdate) {
// Error checking
if (view == this.view)
return;
// Remove the previous view
if (this.view != null) {
if (Toolkit.isComponent(this.view)) {
this.view.parent = null;
this.view.element.remove;
this.view.removeEventListener("scroll", this.viewScroll);
} else this.view.remove();
Toolkit.removeResizeListener(this.viewResize);
}
// Error checking
if (!(view instanceof Element || Toolkit.isComponent(view)))
view = null;
// Associate the new view
if (view != null) {
this.viewport.append(view instanceof Element?view:view.element);
if (Toolkit.isComponent(view))
view.parent = this;
}
// Configure instance fields
this.view = view;
// Monitor events
if (view) {
if (Toolkit.isComponent(view))
view = view.element;
this.viewResize = e=>this.onResize(e);
Toolkit.addResizeListener(view, this.viewResize);
}
// Update elements
if (!noUpdate)
this.update();
}
///////////////////////////// Private Methods /////////////////////////////
// Configure elements given the current widget state
update(noUpdate) {
let bndHorz = this.horizontal.getBounds();
let bndThis = this .getBounds();
let bndVert = this.vertical .getBounds();
// Configure the initial dimensions of the viewport
let height = bndThis.height;
let width = bndThis.width;
// Determine the view element
let view = Toolkit.isComponent(this.view)?this.view.element:this.view;
// Check whether the horizontal scroll bar is visible
let horz =
this.horizontal.policy == ScrollPane.ALWAYS ||
this.horizontal.policy != ScrollPane.NEVER &&
view != null && width < view.scrollWidth
;
if (horz) height = Math.max(0, bndThis.height - bndHorz.height);
// Check whether the vertical scroll bar is visible
let vert =
this.vertical.policy == ScrollPane.ALWAYS ||
this.vertical.policy != ScrollPane.NEVER &&
view != null && height < view.scrollHeight
;
if (vert) width = Math.max(0, bndThis.width - bndVert.width);
// Check the horizontal scroll bar again
if (!horz) {
horz =
this.horizontal.policy != ScrollPane.NEVER &&
view != null && width < view.scrollWidth
;
// The vertical scroll bar necessitated the horizontal scroll bar
if (horz) height = Math.max(0, bndThis.height - bndHorz.height);
}
// Resize the viewport
Object.assign(this.viewport.style, {
height: height + "px",
width : width + "px"
});
// Configure horizontal scroll bar
this.horizontal.setMaximum(
view == null ? 0 : view.scrollWidth, true);
this.horizontal.setExtent(this.view == null ? 0 : width);
this.horizontal.setVisible(horz);
this.horizontal.element.style.right =
vert ? bndVert.width + "px" : 0;
// Configure vertical scroll bar
this.vertical.setMaximum(
view == null ? 0 : view.scrollHeight, true);
this.vertical.setExtent(view == null ? 0 : height);
this.vertical.setVisible(vert);
this.vertical.element.style.bottom =
horz ? bndHorz.height + "px" : 0;
}
}
///////////////////////////////////////////////////////////////////////////////
// SplitPane //
///////////////////////////////////////////////////////////////////////////////
// Window splitter with resizable regions
class SplitPane extends Component {
//////////////////////////////// Constants ////////////////////////////////
// Edges
static BOTTOM = 0;
static LEFT = 1;
static RIGHT = 2;
static TOP = 3;
///////////////////////// Initialization Methods //////////////////////////
constructor(gui, options) {
super(gui, options, {
className: "tk tk-splitpane",
tagName : "div",
style : {
alignItems : "stretch",
display : "flex",
flexDirection: "row"
}
});
// Configure instance fields
this.collapsed = null;
this.increment = 10;
// Configure top/left region
this[0] = document.createElement("div");
this[0].className = "tk tk-a";
this[0].id = Toolkit.id();
Object.assign(this[0].style, {
alignItems : "stretch",
display : "grid",
gridTemplateRows: "auto",
justifyContent : "stretch"
});
// Configure the bottom/right region
this[1] = document.createElement("div");
this[1].className = "tk tk-b";
Object.assign(this[1].style, {
alignItems : "stretch",
display : "grid",
gridTemplateRows: "auto",
justifyContent : "stretch"
});
// Configure the splitter
this.splitter = new Toolkit.Component(gui, {
className: "tk tk-splitter",
focusable: true,
role : "separator",
tagName : "div",
});
this.splitter.setAttribute("aria-controls", this[0].id);
this.splitter.addEventListener("keydown" ,e=>this.onKeyDown (e));
this.splitter.addEventListener("pointerdown",e=>this.onPointerDown(e));
this.splitter.addEventListener("pointermove",e=>this.onPointerMove(e));
this.splitter.addEventListener("pointerup" ,e=>this.onPointerUp (e));
// Configure layout
this.append(this[0]);
this.append(this.splitter);
this.append(this[1]);
// Configure component
options = options || {};
this.setEdge(("edge" in options) ? options.edge : SplitPane.LEFT);
this.addEventListener("resize", e=>this.measure());
}
///////////////////////////// Event Handlers //////////////////////////////
// Key press
onKeyDown(e) {
let args = this.getArgs();
// Dragging is in progress
if (this.drag != null) switch (e.key) {
case "Escape":
this.splitter.element
.releasePointerCapture(this.drag.pointerId);
this.setValue(this.drag.value);
this.drag = null;
break;
default: return;
}
// No drag is in progress
else switch (e.key) {
// Arrow keys
case "ArrowDown":
if (!args.horizontal)
return;
this.setValue(args.value + this.increment *
(this.edge == SplitPane.TOP ? 1 : -1));
break;
case "ArrowLeft":
if (args.horizontal)
return;
this.setValue(args.value + this.increment *
(this.edge == SplitPane.LEFT ? -1 : 1));
break;
case "ArrowRight":
if (args.horizontal)
return;
this.setValue(args.value + this.increment *
(this.edge == SplitPane.LEFT ? 1 : -1));
break;
case "ArrowUp":
if (!args.horizontal)
return;
this.setValue(args.value + this.increment *
(this.edge == SplitPane.TOP ? -1 : 1));
break;
// Extent keys
case "End":
this.setValue(
this.edge == SplitPane.TOP ||
this.edge == SplitPane.LEFT ?
args.max : 0
);
break;
case "Home":
this.setValue(
this.edge == SplitPane.TOP ||
this.edge == SplitPane.LEFT ?
0 : args.max
);
break;
// Miscellaneous
case "Enter":
if (this.collapsed === null) {
this.setValue(0);
this.collapsed = args.value;
} else this.setValue(this.collapsed);
break;
default: return;
}
// Configure event
e.stopPropagation();
e.preventDefault();
}
// Pointer down
onPointerDown(e) {
this.splitter.focus();
// Error checking
if (this.splitter.element.hasPointerCapture(e.pointerId)||e.button!=0)
return;
// Configure event
this.splitter.element.setPointerCapture(e.pointerId);
e.stopPropagation();
e.preventDefault();
// Record pointer parameters
this.drag = this.getArgs();
Object.assign(this.drag, {
pointerId: e.pointerId,
primary : this[this.drag.primary],
property : this.drag.horizontal ? "height" : "width",
x : e.screenX / devicePixelRatio,
y : e.screenY / devicePixelRatio
});
}
// Pointer move
onPointerMove(e) {
// Error checking
if (!this.splitter.element.hasPointerCapture(e.pointerId))
return;
// Configure event
e.stopPropagation();
e.preventDefault();
// Update splitter position
let coord=e[this.drag.horizontal?"screenY":"screenX"]/devicePixelRatio;
let value = this.drag.value;
switch (this.edge) {
case SplitPane.BOTTOM: value += this.drag.y - coord; break;
case SplitPane.LEFT : value += coord - this.drag.x; break;
case SplitPane.RIGHT : value += this.drag.x - coord; break;
case SplitPane.TOP : value += coord - this.drag.y; break;
}
this.setValue(value);
}
// Pointer up
onPointerUp(e) {
// Error checking
if (!this.splitter.element.hasPointerCapture(e.pointerId)||e.button!=0)
return;
// Configure event
this.splitter.element.releasePointerCapture(e.pointerId);
e.stopPropagation();
e.preventDefault();
// Configure instance fields
this.drag = null;
}
///////////////////////////// Public Methods //////////////////////////////
// Retrieve the current position
getValue() {
return this.getArgs().value;
}
// Specify which edge is controlled by the splitter
setEdge(edge) {
// Error checking
let args = this.getArgs(edge);
if (args == null)
return;
// Configure instance fields
this.edge = edge;
// Configure elements
let pri = this[args.primary ].style;
let sec = this[args.primary ^ 1].style;
this.element.style.flexDirection = args.horizontal ? "column" : "row";
this.splitter.setAttribute("aria-orientation",
args.horizontal ? "horizontal" : "vertical");
pri.removeProperty("flex-grow");
pri.removeProperty("height" );
pri.removeProperty("width" );
sec.flexGrow = "1";
sec.removeProperty("height" );
sec.removeProperty("width" );
this.measure();
}
// Specify how many pixels to change for an arrow key press
setIncrement(increment) {
if (typeof increment == "number" && !isNaN(increment))
this.increment = Math.max(1, Math.round(increment));
}
// Specify the position of the splitter
setValue(value, args) {
args = args || this.getArgs();
this[args.primary].style[args.horizontal ? "height" : "width"] =
Math.min(args.max, Math.max(0, Math.round(value))) + "px";
this.collapsed = null;
this.measure();
}
// Specify a child element
setView(index, view, noMeasure) {
index = this[index];
// Error checking
if (view == index.view)
return;
// Remove the previous view
if (index.view != null) {
if (Toolkit.isComponent(index.view)) {
index.view.parent = null;
index.view.element.remove;
} else index.view.remove();
}
// Error checking
if (!(view instanceof Element || Toolkit.isComponent(view)))
view = null;
// Configure instance fields
index.view = view;
// Associate the new view
if (view != null) {
index.append(view instanceof Element ? view : view.element);
if (Toolkit.isComponent(view))
view.parent = this;
}
// Update elements
if (!noMeasure)
this.measure();
}
///////////////////////////// Private Methods /////////////////////////////
// Determine information regarding the current element configuration
getArgs(edge = this.edge) {
let bndA = this[0].getBoundingClientRect();
let bndB = this[1].getBoundingClientRect();
// Processing by edge
let horizontal;
let primary;
switch (edge) {
case SplitPane.BOTTOM: horizontal = true ; primary = 1; break;
case SplitPane.LEFT : horizontal = false; primary = 0; break;
case SplitPane.RIGHT : horizontal = false; primary = 1; break;
case SplitPane.TOP : horizontal = true ; primary = 0; break;
default: return null;
}
// Processing by orientation
let bndPrimary = primary ? bndB : bndA;
let max;
let value;
if (horizontal) {
max = bndA.height + bndB.height;
value = bndPrimary.height;
} else {
max = bndA.width + bndB.width;
value = bndPrimary.width;
}
return {
horizontal: horizontal,
max : Math.round(max),
primary : primary,
value : Math.round(value)
};
}
// Measure the current element configuration
measure() {
let args = this.getArgs();
this.splitter.setAttribute("aria-valuemax", args.max);
this.splitter.setAttribute("aria-valuemin", 0);
this.splitter.setAttribute("aria-valuenow", args.value);
}
}
export { ScrollBar, ScrollPane, SplitPane };