Skip to content

Commit

Permalink
Add JSDoc annotations and documentation to the JS code
Browse files Browse the repository at this point in the history
  • Loading branch information
tgroshon committed Oct 27, 2023
1 parent 6c285c9 commit 851bccb
Show file tree
Hide file tree
Showing 16 changed files with 2,202 additions and 119 deletions.
47 changes: 44 additions & 3 deletions assets/js/phoenix_live_view/aria.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
/**
* Utilities for accessibility behaviors and affordances
*/
let ARIA = {
/**
* Focus a main element of the page
*/
focusMain(){
let target = document.querySelector("main h1, main, h1")
if(target){
Expand All @@ -9,10 +15,22 @@ let ARIA = {
}
},

/**
* Find the first class in the collection that the given object is an instance of.
* @param {object} instance
* @param {object[]} classes
* @returns {object | null}
*/
anyOf(instance, classes){ return classes.find(name => instance instanceof name) },

/**
* Can the element be focused?
* @param {Element} el
* @param {boolean} [interactiveOnly]
* @returns {boolean}
*/
isFocusable(el, interactiveOnly){
return(
return (
(el instanceof HTMLAnchorElement && el.rel !== "ignore") ||
(el instanceof HTMLAreaElement && el.href !== undefined) ||
(!el.disabled && (this.anyOf(el, [HTMLInputElement, HTMLSelectElement, HTMLTextAreaElement, HTMLButtonElement]))) ||
Expand All @@ -21,11 +39,23 @@ let ARIA = {
)
},

/**
* Focus the given element, reporting the result.
* @param {Element} el
* @param {boolean} [interactiveOnly]
* @returns {boolean} Is the given element now focused?
*/
attemptFocus(el, interactiveOnly){
if(this.isFocusable(el, interactiveOnly)){ try{ el.focus() } catch(e){} }
/* eslint-disable-next-line no-empty */
if(this.isFocusable(el, interactiveOnly)){ try { el.focus() } catch (e){} }
return !!document.activeElement && document.activeElement.isSameNode(el)
},

/**
* Focus the first interactive child element; depth-first search
* @param {Element} el
* @returns {boolean}
*/
focusFirstInteractive(el){
let child = el.firstElementChild
while(child){
Expand All @@ -36,6 +66,11 @@ let ARIA = {
}
},

/**
* Focus the first child element; depth-first search
* @param {Element} el
* @returns {boolean} Is the given element now focused?
*/
focusFirst(el){
let child = el.firstElementChild
while(child){
Expand All @@ -46,6 +81,11 @@ let ARIA = {
}
},

/**
* Focus the last child; depth-first search
* @param {Element} el
* @returns {boolean} Is the given element now focused?
*/
focusLast(el){
let child = el.lastElementChild
while(child){
Expand All @@ -56,4 +96,5 @@ let ARIA = {
}
}
}
export default ARIA

export default ARIA
63 changes: 63 additions & 0 deletions assets/js/phoenix_live_view/browser.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,30 @@
let Browser = {
/**
* Does browser support pushState feature of the History API?
* @returns {boolean}
*/
canPushState(){ return (typeof (history.pushState) !== "undefined") },

/**
* Remove item from local storage
* @param {Storage} localStorage
* @param {string} namespace
* @param {string} subkey
*/
dropLocal(localStorage, namespace, subkey){
return localStorage.removeItem(this.localKey(namespace, subkey))
},

/**
* Update item in local storage
* @template {T}
* @param {Storage} localStorage
* @param {string} namespace
* @param {string} subkey
* @param {T} initial - value to set if first save
* @param {function} func - updating function; receives current, JSON-parsed value of store item and saves the JSON-stringified return value;
* @returns {T}
*/
updateLocal(localStorage, namespace, subkey, initial, func){
let current = this.getLocal(localStorage, namespace, subkey)
let key = this.localKey(namespace, subkey)
Expand All @@ -13,15 +33,32 @@ let Browser = {
return newVal
},

/**
* Read item from local storage. NOTE: will parse as JSON; might throw
* @param {Storage} localStorage
* @param {string} namespace
* @param {string} subkey
* @returns {any}
*/
getLocal(localStorage, namespace, subkey){
return JSON.parse(localStorage.getItem(this.localKey(namespace, subkey)))
},

/**
* Replace current history state data without navigating
* @param {function} callback
*/
updateCurrentState(callback){
if(!this.canPushState()){ return }
history.replaceState(callback(history.state || {}), "", window.location.href)
},

/**
* Perform history state change to URL
* @param {("push"|"replace")} kind
* @param {{type: string, scroll: number|undefined, root: boolean, id: string}} meta
* @param {string} to - URL of destination
*/
pushState(kind, meta, to){
if(this.canPushState()){
if(to !== window.location.href){
Expand All @@ -47,21 +84,47 @@ let Browser = {
}
},

/**
* Set document cookie value
* @param {string} name
* @param {string} value
*/
setCookie(name, value){
document.cookie = `${name}=${value}`
},

/**
* Get value from cookie
* @param {string} name
* @returns {string}
*/
getCookie(name){
return document.cookie.replace(new RegExp(`(?:(?:^|.*;\s*)${name}\s*\=\s*([^;]*).*$)|^.*$`), "$1")
},

/**
* Redirect to URL and set flash message if given
* @param {string} toURL
* @param {string} [flash]
*/
redirect(toURL, flash){
if(flash){ Browser.setCookie("__phoenix_flash__", flash + "; max-age=60000; path=/") }
window.location = toURL
},

/**
* Get the namespaced key for use in browser Storage API
* @param {string} namespace
* @param {string} subkey
* @returns {string}
*/
localKey(namespace, subkey){ return `${namespace}-${subkey}` },

/**
* Find element target of the URL hash segment, if it exists
* @param {string} maybeHash
* @returns {HTMLElement|null}
*/
getHashTargetEl(maybeHash){
let hash = maybeHash.toString().substring(1)
if(hash === ""){ return }
Expand Down
2 changes: 1 addition & 1 deletion assets/js/phoenix_live_view/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@ export const EVENTS = "e"
export const REPLY = "r"
export const TITLE = "t"
export const TEMPLATES = "p"
export const STREAM = "stream"
export const STREAM = "stream"
Loading

0 comments on commit 851bccb

Please sign in to comment.