/**
*
* ## A simple screen size manager
*
* - keeps track of screen size and ratio changes
* - helper to compute a component dimensions based on other components: getHeightMinus(...), getWidthMinus(...)
* - observe screen size changes and publish them to the PubSub on the EVT_WINDOW_RESIZED channel
* - observe when container components (Block, Panel) are resized to propagate the change on children
*
* @namespace
*
*/
kiss.screen = {
isMobile: false,
/**
* Check if a screen is horizontal (= landscape)
*
* @returns {boolean}
*/
isHorizontal: () => kiss.screen.current.width > kiss.screen.current.height,
/**
* Check if a screen is vertical (= portrait)
*
* @returns {boolean}
*/
isVertical: () => kiss.screen.current.width < kiss.screen.current.height,
/**
* Check if a screen is a touch screen
*
* @returns {boolean}
*/
isTouch: () => 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0,
/**
* Previous dimensions and ratio
*
* @example
* kiss.screen.previous.height
* kiss.screen.previous.ratio
*/
previous: {
width: window.innerWidth,
height: window.innerHeight,
ratio: window.innerWidth / window.innerHeight
},
/**
* Current dimensions and ratio
*
* @example
* kiss.screen.current.width
* kiss.screen.current.ratio
*/
current: {
width: window.innerWidth,
height: window.innerHeight,
ratio: window.innerWidth / window.innerHeight
},
/**
* Init the screen size observer
*/
init() {
// Startup test to check if it's a mobile environment
if (kiss.tools.isMobile()) kiss.screen.isMobile = true
// Init screen size listener
kiss.screen.observe()
},
/**
* Update the current screen size and cache the previous one
*
* @private
* @ignore
*/
_update() {
kiss.screen.previous = kiss.screen.current
kiss.screen.current = {
width: window.innerWidth,
height: window.innerHeight,
ratio: window.innerWidth / window.innerHeight
}
},
/**
* Compute the delta between previous and new screen size
*
* @private
* @ignore
* @returns {object} For example: {width: 100, height: 50}
*/
_delta() {
let deltaWidth = kiss.screen.current.width - kiss.screen.previous.width
let deltaHeight = kiss.screen.current.height - kiss.screen.previous.height
let delta = {
width: deltaWidth,
height: deltaHeight
}
return delta
},
/**
* Compute the remaining window's height in pixels (= window's height minus a computed delta)
*
* @param {...*} something - Either a number, or a CSS height in pixels, or an array of ids of the items to consider while computing the remaining height
* @returns {number} The remaining height, in pixels
*
* @example
* // With number:
* kiss.screen.getHeightMinus(60)
*
* // With a CSS size:
* kiss.screen.getHeightMinus("60px")
*
* // With a component id:
* kiss.screen.getHeightMinus("top-bar")
*
* // With multiple component ids:
* kiss.screen.getHeightMinus("top-bar", "button-bar")
*
* // With multiple numbers:
* kiss.screen.getHeightMinus(60, 20, $("top-bar").offsetHeight)
*
* // Mixed stuff:
* kiss.screen.getHeightMinus("top-bar", 20, "60px")
*/
getHeightMinus(...something) {
let delta = 0
something.forEach(function (item) {
// Item given as a number: 60
if (typeof item == "number") {
delta += item
} else {
// Item given as CSS size: "60px"
if (item.indexOf("px") != -1) {
delta += Number(item.substring(0, item.indexOf("px")))
}
// Item given as a DOM Element id
else {
let node = $(item)
if (node) {
if (!node.offsetHeight) node = node.firstElementChild
delta += node.offsetHeight
}
}
}
})
return (window.innerHeight - delta)
},
/**
* Compute the remaining window's width in pixels (= window's width minus a computed delta)
*
* @param {...*} something - Either a number, or a CSS height in pixels, or an array of ids of the items to consider while computing the remaining width
* @returns {number} The remaining height, in pixels
*
* @example
* // With number:
* kiss.screen.getWidthMinus(60)
*
* // With a CSS size:
* kiss.screen.getWidthMinus("60px")
*
* // With a component id:
* kiss.screen.getWidthMinus("left-nav")
*
* // With multiple component ids:
* kiss.screen.getWidthMinus("left-nav", "left-nav-margin")
*
* // With multiple numbers:
* kiss.screen.getWidthMinus(60, 20, $("left-nav").offsetWidth)
*
* // Mixed stuff:
* kiss.screen.getWidthMinus("left-nav", 20, "60px")
*/
getWidthMinus(...something) {
let delta = 0
something.forEach(function (item) {
// Item given as a number: 60
if (typeof item == "number") {
delta += item
} else {
// Item given as CSS size: "60px"
if (item.indexOf("px") != -1) {
delta += Number(item.substring(0, item.indexOf("px")))
}
// Item given as a DOM Element id
else {
let node = $(item)
if (node) {
if (!node.offsetWidth) node = node.firstElementChild
delta += node.offsetWidth
}
}
}
})
return (window.innerWidth - delta)
},
/**
* Get the screen orientation
*
* @returns {string} "vertical" or "horizontal"
*/
getOrientation() {
return (kiss.screen.current.height > kiss.screen.current.width) ? "vertical" : "horizontal"
},
/**
* Debounce a function which occurs at a high frequency, and force it to occur at a specific interval (in milliseconds)
*
* @private
* @ignore
* @param {integer} interval - Interval in milliseconds used to call the debounced function
* @param {function} fn - The function to debounce
* @returns {function} A function calling the function to debounce, passing it the {event} that occured.
*
* @example
* window.addEventListener("resize", kiss.screen._debounce(function(event) { console.log(event) })
*/
_debounce(interval, fn) {
let timer
return function (event) {
if (timer) clearTimeout(timer)
timer = setTimeout(fn, interval, event)
}
},
/**
* Observe the window resize event, and publish the changes in the EVT_WINDOW_RESIZED pubsub channel.
*
* @example
* kiss.screen.observe()
*/
observe() {
window.addEventListener("resize", kiss.screen._debounce(100, function () {
kiss.screen._update()
kiss.pubsub.publish("EVT_WINDOW_RESIZED", {
previous: kiss.screen.previous,
current: kiss.screen.current,
delta: kiss.screen._delta()
})
}))
},
/**
* Observe when container components (Block, Panel) are resized.
* Propagate the event EVT_CONTAINERS_RESIZED with the list of ids of the resized containers
*
* @ignore
* @param {function} callback to execute when the observer is triggered
*/
getResizeObserver() {
if (kiss.screen.resize) return kiss.screen.resize
kiss.screen.resize = new ResizeObserver(kiss.screen._debounce(100, function (entries) {
let elements = Array.from(entries)
.filter(entry => entry.borderBoxSize[0].blockSize != 0)
.map(entry => entry.target.id)
// Propagate the list of container ids
if (elements.length > 0) kiss.pubsub.publish("EVT_CONTAINERS_RESIZED", elements)
}))
return kiss.screen.resize
}
}
;
Source