Skip to content

Commit

Permalink
feat: add support for comma separated values & dom arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidWells committed Dec 31, 2023
1 parent 6f1d7af commit ab39bb5
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 47 deletions.
5 changes: 2 additions & 3 deletions packages/analytics-util-listener/package.json
Expand Up @@ -13,7 +13,6 @@
"events",
"listeners"
],
"netlifySiteId": "804da5e7-8aa8-4da8-a983-8593754cacb9",
"amdName": "utilListener",
"source": "src/index.js",
"main": "dist/analytics-util-listener.js",
Expand All @@ -30,12 +29,12 @@
"sync": "cp examples/index.html dist",
"watch:dev": "microbundle watch --external none -f iife,umd -o dist/browser --no-compress",
"watch:test": "watchlist src tests examples -- npm t",
"build": "microbundle && npm run build:dev",
"build": "npm run sync && microbundle && npm run build:dev",
"build:dev": "microbundle build --external none -f iife,umd -o dist/browser",
"release:patch": "npm version patch && npm publish",
"release:minor": "npm version minor && npm publish",
"release:major": "npm version major && npm publish",
"deploy": "npm run build:dev && npm run sync && netlify deploy --prod --dir dist --site $npm_package_netlifySiteId"
"deploy": "npm run build:dev && npm run sync && netlify deploy --prod --dir dist --site 804da5e7-8aa8-4da8-a983-8593754cacb9"
},
"files": [
"lib",
Expand Down
79 changes: 37 additions & 42 deletions packages/analytics-util-listener/src/index.js
@@ -1,79 +1,74 @@
import { isBrowser, isString, isFunction, ensureArray, noOp } from '@analytics/type-utils'

const EVENT = 'Event'
const EventListener = EVENT + 'Listener'
const EL = EVENT + 'Listener'

function createListener(add) {
return (els, evts, callback, opts) => {
return (els, evts, callback, options) => {
const handler = callback || noOp
const opts = options || false
/* SSR support */
if (!isBrowser) return handler

const options = opts || false
const events = toArray(evts)
const elements = toArray(els, true)
let fns = []
/* Throw if no element found */
if (!elements.length) throw new Error('noElements')
/* Throw if no events found */
if (!events.length) throw new Error('no' + EVENT)

let fns = []
function smartAttach(isAdd) {
const method = (isAdd) ? 'add' + EventListener : 'remove' + EventListener
// console.log((isAdd) ? '>> setup called' : '>> teardown')
// console.log(`>>> with ${method}`)
if (isAdd) fns = []

const method = (isAdd) ? 'add' + EL : 'remove' + EL
// Apply to all elements
for (let i = 0; i < elements.length; i++) {
const el = elements[i]
fns[i] = (isAdd ? oncify(handler, options) : fns[i] || handler)
fns[i] = (isAdd ? ((opts && opts.once) ? once(handler) : handler) : fns[i] || handler)
// Apply to all events
for (let n = 0; n < events.length; n++) {
if (el[method]) {
el[method](events[n], fns[i], options)
} else {
/* Fallback for older browsers IE <=8 */
el['on' + events[n]] = (isAdd) ? fns[i] : null
if (!el[method]) {
el[method](events[n], fns[i], opts)
continue;
}
el['on' + events[n]] = (isAdd) ? fns[i] : null /* Fallback for older browsers IE <=8 */
}
}
// return opposite function with inverse event handler
return smartAttach.bind(null, !isAdd)
}

/* // debug
console.log('events', events)
console.log('elements', elements)
/** */
return smartAttach(add)
}
}

function toArray(obj, isSelector) {
// Split string
if (isString(obj)) {
return isSelector ? toArray(document.querySelectorAll(obj)) : obj.split(' ').map(e => e.trim())
function strToArray(str) {
return str.split(str.indexOf(',') > -1 ? ',' : ' ').map(e => e.trim())
}

function toArray(val, isSelector) {
if (isString(val)) {
return isSelector ? toArray(document.querySelectorAll(val)) : strToArray(val)
}
// Convert NodeList to Array
if (NodeList.prototype.isPrototypeOf(obj)) {
const array = []
for (var i = obj.length >>> 0; i--;) { // iterate backwards ensuring that length is an UInt32
array[i] = obj[i]
if (NodeList.prototype.isPrototypeOf(val)) {
const nodes = []
for (var i = val.length >>> 0; i--;) {
nodes[i] = val[i] // iterate backwards ensuring that length is an UInt32
}
return array
return nodes
}
// Is Array, return it OR Convert single element to Array
// return isArray(obj) ? obj : [ obj ]
return ensureArray(obj)
// Arrayify
const array = ensureArray(val)
return (!isSelector) ? array : array.map((v) => isString(v) ? toArray(v, true) : v).flat()
}

function oncify(handler, opts) {
return (opts && opts.once) ? once(handler) : handler
}


/**
* Run function once
* @param {Function} fn - Function to run just once
* @param {*} [context] - Extend function context
* @returns
* @returns {Function} Function that only runs 1 time
*/
export function once(fn, context) {
var result
Expand All @@ -87,12 +82,12 @@ export function once(fn, context) {
}

/**
* Element selector
* Element selector or valid DOM node
* @typedef {(string|Node|NodeList|EventTarget|null)} Selector
*/

/**
* Event to listen to
* Event or events to addEventListener to
* @typedef {(string|string[])} EventType
*/

Expand All @@ -103,14 +98,14 @@ export function once(fn, context) {
*/

/**
* ReAttach event listener
* Reattach event listener
* @callback AttachListener
* @returns {RemoveListener}
*/

/**
* Add an event listener
* @callback AddEventListener
* @callback AddListener
* @param {Selector} elements - Element(s) to attach event(s) to.
* @param {EventType} eventType - Event(s) to listen to
* @param {Function} [handler] - Function to fire
Expand All @@ -119,12 +114,12 @@ export function once(fn, context) {
* @returns {RemoveListener}
*/

/** @type {AddEventListener} */
/** @type {AddListener} */
export const addListener = createListener(EVENT)

/**
* Remove an event listener
* @callback RemoveEventListener
* @callback RemoveListener
* @param {Selector} elements - Element(s) to remove event(s) from.
* @param {EventType} eventType - Event(s) to remove
* @param {Function} [handler] - Function to remove
Expand All @@ -133,7 +128,7 @@ export const addListener = createListener(EVENT)
* @returns {AttachListener}
*/

/** @type {RemoveEventListener} */
/** @type {RemoveListener} */
export const removeListener = createListener()


Expand Down
17 changes: 16 additions & 1 deletion packages/analytics-util-listener/tests/api.test.js
Expand Up @@ -33,7 +33,7 @@ test('Heading click handler works', async () => {
test('{ once : true }', async () => {
const button = ENV.getSelector('#once')
assert.ok(button)
assert.is(window['onceCount'] === 0, true)
assert.is(window['onceCount'] === 0, true, 'one')
button.click()
assert.is(window['onceCount'] === 1, true)
button.click()
Expand All @@ -42,6 +42,21 @@ test('{ once : true }', async () => {
assert.is(window['onceCount'] === 1, true)
})

/* Once listener works */
test('{ once : true } multiple items #list-click-once li', async () => {
const listItems = ENV.getSelectorAll('#list-click-once li')
assert.is(window['liClickOnceCount'] === 0, true, 'one')
listItems[0].click()
listItems[0].click() // should be onced
assert.is(window['liClickOnceCount'] === 1, true)
listItems[1].click()
listItems[1].click() // should be onced
assert.is(window['liClickOnceCount'] === 2, true)
listItems[2].click()
listItems[2].click() // should be onced
assert.is(window['liClickOnceCount'] === 3, true)
})

/* Listeners are recursive in nature */
test('Ensure recursive attach/detach', async () => {
let count = 0
Expand Down
7 changes: 7 additions & 0 deletions packages/analytics-util-listener/tests/setup/env.js
Expand Up @@ -87,3 +87,10 @@ export function reset(context) {
export function getSelector(selector) {
return document.querySelector(selector)
}

/**
* @return {RenderOutput}
*/
export function getSelectorAll(selector) {
return document.querySelectorAll(selector)
}
2 changes: 1 addition & 1 deletion site/main/package.json
Expand Up @@ -11,7 +11,7 @@
"build": "gatsby build",
"postbuild": "node scripts/sitemap.js && cp _redirects public",
"serve": "http-server public",
"deploy": "./node_modules/.bin/netlify deploy -p --dir public",
"deploy": "./node_modules/.bin/netlify deploy -p --dir public --site 09429c65-5e92-4873-9805-ae4f30027d8a",
"deploydev": "./node_modules/.bin/netlify deploy --dir public"
},
"dependencies": {
Expand Down

0 comments on commit ab39bb5

Please sign in to comment.