uicore-ts
Version:
UICore is a library to build native-like user interfaces using pure Typescript. No HTML is needed at all. Components are described as TS classes and all user interactions are handled explicitly. This library is strongly inspired by the UIKit framework tha
475 lines (243 loc) • 11.5 kB
text/typescript
import { UIButton } from "./UIButton"
import { UIColor } from "./UIColor"
import { UICore } from "./UICore"
import { FIRST_OR_NIL, IS, nil, NO, YES } from "./UIObject"
import { UIRectangle } from "./UIRectangle"
import { UIScrollView } from "./UIScrollView"
import { UITimer } from "./UITimer"
import { UIView, UIViewBroadcastEvent } from "./UIView"
export class UISlideScrollerView extends UIView {
_previousLayoutBounds?: UIRectangle
_targetIndex: number = 0
pageIndicatorsView: UIView
_isAnimating: boolean = NO
_isAnimationOngoing: boolean = NO
_animationTimer?: UITimer
_scrollView: UIScrollView
_slideViews: UIView[] = []
wrapAround: boolean = YES
animationDuration: number = 0.35
animationDelay: number = 2
_currentPageIndex: number = 0
constructor(elementID: string, viewHTMLElement?: HTMLElement) {
super(elementID, viewHTMLElement)
this._scrollView = new UIScrollView(elementID + "ScrollView")
this.addSubview(this._scrollView)
this._scrollView._scrollEnabled = NO
this._scrollView.addTargetForControlEvent(
UIView.controlEvent.PointerMove,
(sender: UIView, event: Event) => {
if (event instanceof MouseEvent) {
this._animationTimer?.invalidate()
}
}
)
this._scrollView.addTargetForControlEvent(UIView.controlEvent.PointerLeave, () => {
if (this._isAnimating && event instanceof MouseEvent) {
this.startAnimating()
}
})
// Touch events
this._scrollView.addTargetForControlEvent(UIView.controlEvent.PointerDown, (sender, event) => {
if (event instanceof TouchEvent) {
this._animationTimer?.invalidate()
}
})
this._scrollView.addTargetForControlEvents([
UIView.controlEvent.PointerUp, UIView.controlEvent.PointerCancel
], (sender, event) => {
if (event instanceof TouchEvent && this._isAnimating) {
this.startAnimating()
}
})
// Page indicator
this.pageIndicatorsView = new UIView(elementID + "PageIndicatorsView")
this.addSubview(this.pageIndicatorsView)
}
buttonForPageIndicatorWithIndex(index: number): UIButton {
const result = new UIButton(this.viewHTMLElement.id + "PageIndicatorButton" + index)
result.addTargetForControlEvents([
UIView.controlEvent.PointerUpInside, UIView.controlEvent.EnterUp
], (sender, event) => {
this.scrollToPageWithIndex(index, YES)
if (this._isAnimating) {
this.startAnimating()
}
})
result.addTargetForControlEvent(UIView.controlEvent.PointerMove, () => {
this._animationTimer?.invalidate()
})
result.updateContentForNormalState = () => {
result.backgroundColor = UIColor.blueColor
FIRST_OR_NIL(result.titleLabel).textColor = UIColor.whiteColor
}
result.frame = new UIRectangle(nil, nil, 20, 50)
// result.style.height = "20px";
// result.style.width = "50px";
result.style.display = "table-cell"
result.style.position = "relative"
// var resultContent = new UIView(result.viewHTMLElement.id + "Content");
// resultContent.backgroundColor = UIColor.whiteColor;
// resultContent.centerYInContainer();
// resultContent.style.height = "10px";
// resultContent.style.height = "100%";
// resultContent.style.borderRadius = "5px";
// result.addSubview(resultContent);
return result
}
addSlideView(view: UIView) {
this.slideViews.push(view)
this.updateSlideViews()
}
set slideViews(views: UIView[]) {
this._slideViews = views
this.updateSlideViews()
}
get slideViews() {
return this._slideViews
}
get currentPageIndex() {
return this._currentPageIndex
}
set currentPageIndex(index: number) {
this._currentPageIndex = index
this._slideViews[index].willAppear()
this._scrollView.contentOffset = this._scrollView.contentOffset.pointWithX(-this._slideViews[index].frame.min.x);
(this.pageIndicatorsView.subviews as UIButton[]).everyElement.selected = NO;
(this.pageIndicatorsView.subviews[index] as UIButton).selected = YES
}
scrollToPreviousPage(animated: boolean) {
if (this.slideViews.length == 0) {
return
}
var targetIndex = this.currentPageIndex
if (this.wrapAround) {
targetIndex = (this.currentPageIndex - 1) % (this.slideViews.length)
}
else if (this.currentPageIndex - 1 < this.slideViews.length) {
targetIndex = this.currentPageIndex - 1
}
else {
return
}
this.scrollToPageWithIndex(targetIndex, animated)
}
scrollToNextPage(animated: boolean) {
if (this.slideViews.length == 0) {
return
}
var targetIndex = this.currentPageIndex
if (this.wrapAround) {
targetIndex = (this.currentPageIndex + 1) % (this.slideViews.length)
}
else if (this.currentPageIndex + 1 < this.slideViews.length) {
targetIndex = this.currentPageIndex + 1
}
else {
return
}
this.scrollToPageWithIndex(targetIndex, animated)
}
scrollToPageWithIndex(targetIndex: number, animated: boolean = YES) {
this._targetIndex = targetIndex
// this._slideViews[this.currentPageIndex]._shouldLayout = NO;
// this._slideViews[this._targetIndex]._shouldLayout = YES;
//this._slideViews[this._targetIndex].hidden = NO;
this.willScrollToPageWithIndex(targetIndex)
this._isAnimationOngoing = YES
//var previousView = this._slideViews[this.currentPageIndex];
if (animated) {
UIView.animateViewOrViewsWithDurationDelayAndFunction(
this._scrollView.containerView,
this.animationDuration,
0,
undefined,
function (this: UISlideScrollerView) {
this.currentPageIndex = targetIndex
}.bind(this),
function (this: UISlideScrollerView) {
this.didScrollToPageWithIndex(targetIndex)
this._isAnimationOngoing = NO
//previousView.hidden = YES;
}.bind(this)
)
}
else {
this.currentPageIndex = targetIndex
this.didScrollToPageWithIndex(targetIndex)
//previousView.hidden = YES;
}
}
willScrollToPageWithIndex(index: number) {
const targetView = this.slideViews[index]
if (IS(targetView) && (targetView as any).willAppear && (targetView as any).willAppear instanceof Function) {
(targetView as any).willAppear()
}
}
didScrollToPageWithIndex(index: number) {
}
startAnimating() {
this._isAnimating = YES
this._animationTimer?.invalidate()
this._animationTimer = new UITimer(this.animationDelay + this.animationDuration, YES, () => {
this.scrollToNextPage(YES)
})
}
stopAnimating() {
this._isAnimating = NO
this._animationTimer?.invalidate()
}
updateSlideViews() {
this._scrollView.containerView.subviews.slice().forEach(function (subview, index, array) {
subview.removeFromSuperview()
})
this.pageIndicatorsView.subviews.slice().forEach(function (subview, index, array) {
subview.removeFromSuperview()
})
this._slideViews.forEach((view, index) => {
this._scrollView.addSubview(view)
this.pageIndicatorsView.addSubview(this.buttonForPageIndicatorWithIndex(index))
})
}
override didReceiveBroadcastEvent(event: UIViewBroadcastEvent) {
super.didReceiveBroadcastEvent(event)
if (event.name == UICore.broadcastEventName.WindowDidResize) {
this.currentPageIndex = this.currentPageIndex
}
}
override set frame(frame: UIRectangle) {
super.frame = frame
this.currentPageIndex = this.currentPageIndex
}
override get frame() {
return super.frame
}
override layoutSubviews() {
super.layoutSubviews()
if (this.bounds.isEqualTo(this._previousLayoutBounds)) {
return
}
const bounds = this.bounds
this._previousLayoutBounds = bounds
this._scrollView.frame = bounds
this._scrollView.containerView.frame = bounds.rectangleWithWidth(bounds.width *
this.slideViews.length).performFunctionWithSelf(function (this: UISlideScrollerView, self: UIRectangle) {
self.offsetByPoint(this._scrollView.contentOffset)
return self
}.bind(this))
this._slideViews.forEach((view, index) => {
view.frame = bounds.rectangleWithX((this.bounds.width + 1) * index)
})
this.layoutPageIndicators()
}
layoutPageIndicators() {
this.pageIndicatorsView.centerXInContainer()
this.pageIndicatorsView.style.bottom = "20px"
this.pageIndicatorsView.style.height = "20px"
this.pageIndicatorsView.style.display = "table-row"
}
override removeFromSuperview() {
super.removeFromSuperview()
this.stopAnimating()
}
}