Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@nrk/core-toggle

Package Overview
Dependencies
Maintainers
128
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@nrk/core-toggle - npm Package Compare versions

Comparing version 2.2.3 to 3.0.0

.DS_Store

399

core-toggle.cjs.js
'use strict';
var name = "@nrk/core-toggle";
var version = "2.2.2";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _construct(Parent, args, Class) {
if (isNativeReflectConstruct()) {
_construct = Reflect.construct;
} else {
_construct = function _construct(Parent, args, Class) {
var a = [null];
a.push.apply(a, args);
var Constructor = Function.bind.apply(Parent, a);
var instance = new Constructor();
if (Class) _setPrototypeOf(instance, Class.prototype);
return instance;
};
}
return _construct.apply(null, arguments);
}
function _isNativeFunction(fn) {
return Function.toString.call(fn).indexOf("[native code]") !== -1;
}
function _wrapNativeSuper(Class) {
var _cache = typeof Map === "function" ? new Map() : undefined;
_wrapNativeSuper = function _wrapNativeSuper(Class) {
if (Class === null || !_isNativeFunction(Class)) return Class;
if (typeof Class !== "function") {
throw new TypeError("Super expression must either be null or a function");
}
if (typeof _cache !== "undefined") {
if (_cache.has(Class)) return _cache.get(Class);
_cache.set(Class, Wrapper);
}
function Wrapper() {
return _construct(Class, arguments, _getPrototypeOf(this).constructor);
}
Wrapper.prototype = Object.create(Class.prototype, {
constructor: {
value: Wrapper,
enumerable: false,
writable: true,
configurable: true
}
});
return _setPrototypeOf(Wrapper, Class);
};
return _wrapNativeSuper(Class);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
var IS_BROWSER = typeof window !== 'undefined';
var IS_ANDROID = IS_BROWSER && /(android)/i.test(navigator.userAgent); // Bad, but needed
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform));
var HAS_EVENT_OPTIONS = (function (has) {
if ( has === void 0 ) has = false;
try { window.addEventListener('test', null, { get passive () { has = true; } }); } catch (e) {}
return has
})();
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); // Polyfill toggleAttribute for IE
if (IS_BROWSER && !window.Element.prototype.toggleAttribute) {
window.Element.prototype.toggleAttribute = function (name) {
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !this.hasAttribute(name);
if (!force === this.hasAttribute(name)) this[force ? 'setAttribute' : 'removeAttribute'](name, '');
return force;
};
}
/**
* addEvent
* @param {String} uuid An unique ID of the event to bind - ensurnes single instance
* @param {String} type The type of event to bind
* @param {Function} handler The function to call on event
* @param {Boolean|Object} options useCapture or options object for addEventListener. Defaults to false
* closest
* @param {Element} element Element to traverse up from
* @param {String} selector A selector to search for matching parents or element itself
* @return {Element|null} Element which is the closest ancestor matching selector
*/
function addEvent (uuid, type, handler, options) {
if ( options === void 0 ) options = false;
if (typeof window === 'undefined' || window[uuid = uuid + "-" + type]) { return } // Ensure single instance
if (!HAS_EVENT_OPTIONS && typeof options === 'object') { options = Boolean(options.capture); } // Fix unsupported options
var node = (type === 'resize' || type === 'load') ? window : document;
node.addEventListener(window[uuid] = type, handler, options);
}
var closest = function () {
var proto = typeof window === 'undefined' ? {} : window.Element.prototype;
var match = proto.matches || proto.msMatchesSelector || proto.webkitMatchesSelector;
return proto.closest ? function (el, css) {
return el.closest(css);
} : function (el, css) {
for (; el; el = el.parentElement) {
if (match.call(el, css)) return el;
}
return null;
};
}();
/**

@@ -39,26 +179,28 @@ * dispatchEvent - with infinite loop prevention

*/
var IGNORE = 'prevent_recursive_dispatch_maximum_callstack';
function dispatchEvent (element, name, detail) {
if ( detail === void 0 ) detail = {};
var ignore = "" + IGNORE + name;
function dispatchEvent(element, name) {
var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var ignore = "prevent_recursive_dispatch_maximum_callstack".concat(name);
var event;
if (element[ignore]) return true; // We are already processing this event, so skip sending a new one
else element[ignore] = true; // Add name to dispatching ignore
if (element[ignore]) { return true } // We are already processing this event, so skip sending a new one
else { element[ignore] = true; } // Add name to dispatching ignore
if (typeof window.CustomEvent === 'function') {
event = new window.CustomEvent(name, { bubbles: true, cancelable: true, detail: detail });
event = new window.CustomEvent(name, {
bubbles: true,
cancelable: true,
detail: detail
});
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(name, true, true, detail);
}
// IE reports incorrect event.defaultPrevented
} // IE reports incorrect event.defaultPrevented
// but correct return value on element.dispatchEvent
var result = element.dispatchEvent(event);
element[ignore] = null; // Remove name from dispatching ignore
return result // Follow W3C standard for return value
return result; // Follow W3C standard for return value
}
/**

@@ -68,107 +210,134 @@ * getUUID

*/
function getUUID (el) {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5)
function getUUID() {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
}
/**
* queryAll
* @param {String|NodeList|Array|Element} elements A CSS selector string, nodeList, element array, or single element
* @return {Element[]} Array of elements
*/
function queryAll (elements, context) {
if ( context === void 0 ) context = document;
var CoreToggle =
/*#__PURE__*/
function (_HTMLElement) {
_inherits(CoreToggle, _HTMLElement);
if (elements) {
if (elements.nodeType) { return [elements] }
if (typeof elements === 'string') { return [].slice.call(context.querySelectorAll(elements)) }
if (elements.length) { return [].slice.call(elements) }
function CoreToggle() {
_classCallCheck(this, CoreToggle);
return _possibleConstructorReturn(this, _getPrototypeOf(CoreToggle).apply(this, arguments));
}
return []
}
var UUID = ("data-" + name + "-" + version).replace(/\W+/g, '-'); // Strip invalid attribute characters
var ARIA = IS_ANDROID ? 'data' : 'aria'; // Andriod has a bug and reads only label instead of content
var KEYS = { ESC: 27 };
_createClass(CoreToggle, [{
key: "connectedCallback",
value: function connectedCallback() {
if (IS_IOS) document.documentElement.style.cursor = 'pointer'; // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
function toggle (toggles, open) {
var options = typeof open === 'object' ? open : { open: open };
if (IS_IOS) { document.documentElement.style.cursor = 'pointer'; } // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
if (!IS_ANDROID) this.setAttribute('aria-labelledby', this.button.id = this.button.id || getUUID()); // Andriod reads only label instead of content
return queryAll(toggles).map(function (toggle) {
var content = getContentElement(toggle);
var isOpen = toggle.getAttribute('aria-expanded') === 'true';
var open = typeof options.open === 'boolean' ? options.open : (options.open === 'toggle' ? !isOpen : isOpen);
var popup = String((options.hasOwnProperty('popup') ? options.popup : toggle.getAttribute(UUID)) || false);
this.value = this.button.textContent; // Set up aria-label
if (options.value) { toggle.innerHTML = options.value; } // Set innerHTML before updating aria-label
if (popup !== 'false' && popup !== 'true') { toggle.setAttribute('aria-label', ((toggle.textContent) + ", " + popup)); } // Only update aria-label if popup-mode
this.setAttribute('role', 'group'); // Help Edge
toggle.setAttribute(UUID, popup); // aria-haspopup triggers forms mode in JAWS, therefore store in uuid
toggle.setAttribute('aria-controls', content.id = content.id || getUUID());
content.setAttribute((ARIA + "-labelledby"), toggle.id = toggle.id || getUUID());
setOpen(toggle, open);
return toggle
})
}
this.button.setAttribute('aria-expanded', this._open = !this.hidden);
this.button.setAttribute('aria-controls', this.id = this.id || getUUID());
document.addEventListener('keydown', this, true); // Use capture to enable checking defaultPrevented (from ESC key) in parents
function getContentElement (toggle) {
return document.getElementById(toggle.getAttribute('aria-controls')) || toggle.nextElementSibling
}
document.addEventListener('click', this);
}
}, {
key: "disconnectedCallback",
value: function disconnectedCallback() {
this._button = null;
document.removeEventListener('keydown', this, true);
document.removeEventListener('click', this);
}
}, {
key: "attributeChangedCallback",
value: function attributeChangedCallback() {
if (this._open === this.hidden) {
// this._open comparison ensures actual change
this.button.setAttribute('aria-expanded', this._open = !this.hidden);
addEvent(UUID, 'keydown', function (event) {
if (event.keyCode !== KEYS.ESC) { return }
for (var el = event.target; el; el = el.parentElement) {
var toggle = (el.id && document.querySelector(("[aria-controls=\"" + (el.id) + "\"]"))) || el;
try {
this.querySelector('[autofocus]').focus();
} catch (err) {}
if (toggle.getAttribute(UUID) !== 'false' && toggle.getAttribute('aria-expanded') === 'true') {
event.preventDefault(); // Prevent leaving maximized window in Safari
toggle.focus();
return setOpen(toggle, false)
dispatchEvent(this, 'toggle');
}
}
}
}, true); // Use capture to enable checking defaultPrevented (from ESC key) in parents
}, {
key: "handleEvent",
value: function handleEvent(event) {
if (event.defaultPrevented) return;
addEvent(UUID, 'click', function (ref) {
var target = ref.target;
var defaultPrevented = ref.defaultPrevented;
if (event.type === 'keydown' && event.keyCode === 27) {
var isButton = event.target.getAttribute && event.target.getAttribute('aria-expanded') === 'true';
var isHiding = isButton ? event.target === this.button : closest(event.target, this.nodeName) === this;
if (defaultPrevented) { return false } // Do not toggle if someone run event.preventDefault()
if (isHiding) {
this.hidden = true;
this.button.focus(); // Move focus back to button
for (var el = target, item = (void 0); el; el = el.parentElement) {
var toggle = item && el.id && document.querySelector(("[" + UUID + "][aria-controls=\"" + (el.id) + "\"]"));
if ((el.nodeName === 'BUTTON' || el.nodeName === 'A') && !el.hasAttribute(UUID)) { item = el; } // interactive element clicked
if (toggle) {
dispatchEvent(toggle, 'toggle.select', {
relatedTarget: getContentElement(toggle),
currentTarget: item,
value: item.textContent.trim()
});
break
return event.preventDefault(); // Prevent closing maximized Safari and other coreToggles
}
}
if (event.type === 'click') {
var btn = closest(event.target, 'a,button');
if (btn && !btn.hasAttribute('aria-expanded') && closest(event.target, this.nodeName) === this) dispatchEvent(this, 'toggle.select', btn);else if (btn && btn.getAttribute('aria-controls') === this.id) this.hidden = !this.hidden;else if (this.popup && !this.contains(event.target)) this.hidden = true; // Click in content or outside
}
}
}
}, {
key: "button",
get: function get() {
if (this._button && this._button.getAttribute('for') === this.id) return this._button; // Speed up
queryAll(("[" + UUID + "]")).forEach(function (toggle) {
var open = toggle.getAttribute('aria-expanded') === 'true';
var popup = toggle.getAttribute(UUID) !== 'false';
var content = getContentElement(toggle);
return (this._button = this.id && document.querySelector("[for=\"".concat(this.id, "\"]"))) || this.previousElementSibling;
} // aria-haspopup triggers forms mode in JAWS, therefore store as custom attr
if (toggle.contains(target)) { setOpen(toggle, !open); } // Click on toggle
else if (popup && open) { setOpen(toggle, content.contains(target)); } // Click in content or outside
});
});
}, {
key: "popup",
get: function get() {
return this.getAttribute('popup') === 'true' || this.getAttribute('popup') || this.hasAttribute('popup');
},
set: function set(val) {
this[val === false ? 'removeAttribute' : 'setAttribute']('popup', val);
} // Must set attribute for IE11
function setOpen (toggle, open) {
var content = getContentElement(toggle);
var isOpen = toggle.getAttribute('aria-expanded') === 'true';
var willOpen = typeof open === 'boolean' ? open : (open === 'toggle' ? !isOpen : isOpen);
var isUpdate = isOpen === willOpen || dispatchEvent(toggle, 'toggle', { relatedTarget: content, isOpen: isOpen, willOpen: willOpen });
var nextOpen = isUpdate ? willOpen : toggle.getAttribute('aria-expanded') === 'true'; // dispatchEvent can change attributes
var focus = !isOpen && nextOpen && content.querySelector('[autofocus]');
}, {
key: "hidden",
get: function get() {
return this.hasAttribute('hidden');
},
set: function set(val) {
this.toggleAttribute('hidden', val);
} // Sets this.button aria-label, so visible button text can be augmentet with intension of button
// Example: Button text: "01.02.2019", aria-label: "01.02.2019, Choose date"
// Does not updates aria-label if not allready set to something else than this.popup
if (focus) { setTimeout(function () { return focus && focus.focus(); }); } // Move focus on next render (if element stil exists)
}, {
key: "value",
get: function get() {
return this.button.value || this.button.textContent;
},
set: function set() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (!this.button || !this.popup.length) return;
var button = this.button;
var popup = (button.getAttribute('aria-label') || ",".concat(this.popup)).split(',')[1];
var label = data.textContent || data || ''; // data can be Element, Object or String
toggle.setAttribute('aria-expanded', nextOpen);
content[nextOpen ? 'removeAttribute' : 'setAttribute']('hidden', '');
}
if (popup === this.popup) {
button.value = data.value || label;
button[data.innerHTML ? 'innerHTML' : 'textContent'] = data.innerHTML || label;
button.setAttribute('aria-label', "".concat(button.textContent, ",").concat(this.popup));
}
}
}], [{
key: "observedAttributes",
get: function get() {
return ['hidden'];
}
}]);
module.exports = toggle;
return CoreToggle;
}(_wrapNativeSuper(HTMLElement));
module.exports = CoreToggle;

@@ -1,84 +0,76 @@

import { name, version } from './package.json'
import { IS_ANDROID, IS_IOS, addEvent, dispatchEvent, getUUID, queryAll } from '../utils'
import { IS_ANDROID, IS_IOS, closest, dispatchEvent, getUUID } from '../utils'
const UUID = `data-${name}-${version}`.replace(/\W+/g, '-') // Strip invalid attribute characters
const ARIA = IS_ANDROID ? 'data' : 'aria' // Andriod has a bug and reads only label instead of content
const KEYS = { ESC: 27 }
export default class CoreToggle extends HTMLElement {
static get observedAttributes () { return ['hidden'] }
export default function toggle (toggles, open) {
const options = typeof open === 'object' ? open : { open }
if (IS_IOS) document.documentElement.style.cursor = 'pointer' // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
connectedCallback () {
if (IS_IOS) document.documentElement.style.cursor = 'pointer' // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
if (!IS_ANDROID) this.setAttribute('aria-labelledby', this.button.id = this.button.id || getUUID()) // Andriod reads only label instead of content
return queryAll(toggles).map((toggle) => {
const content = getContentElement(toggle)
const isOpen = toggle.getAttribute('aria-expanded') === 'true'
const open = typeof options.open === 'boolean' ? options.open : (options.open === 'toggle' ? !isOpen : isOpen)
const popup = String((options.hasOwnProperty('popup') ? options.popup : toggle.getAttribute(UUID)) || false)
if (options.value) toggle.innerHTML = options.value // Set innerHTML before updating aria-label
if (popup !== 'false' && popup !== 'true') toggle.setAttribute('aria-label', `${toggle.textContent}, ${popup}`) // Only update aria-label if popup-mode
toggle.setAttribute(UUID, popup) // aria-haspopup triggers forms mode in JAWS, therefore store in uuid
toggle.setAttribute('aria-controls', content.id = content.id || getUUID())
content.setAttribute(`${ARIA}-labelledby`, toggle.id = toggle.id || getUUID())
setOpen(toggle, open)
return toggle
})
}
function getContentElement (toggle) {
return document.getElementById(toggle.getAttribute('aria-controls')) || toggle.nextElementSibling
}
addEvent(UUID, 'keydown', (event) => {
if (event.keyCode !== KEYS.ESC) return
for (let el = event.target; el; el = el.parentElement) {
const toggle = (el.id && document.querySelector(`[aria-controls="${el.id}"]`)) || el
if (toggle.getAttribute(UUID) !== 'false' && toggle.getAttribute('aria-expanded') === 'true') {
event.preventDefault() // Prevent leaving maximized window in Safari
toggle.focus()
return setOpen(toggle, false)
this.value = this.button.textContent // Set up aria-label
this.setAttribute('role', 'group') // Help Edge
this.button.setAttribute('aria-expanded', this._open = !this.hidden)
this.button.setAttribute('aria-controls', this.id = this.id || getUUID())
document.addEventListener('keydown', this, true) // Use capture to enable checking defaultPrevented (from ESC key) in parents
document.addEventListener('click', this)
}
disconnectedCallback () {
this._button = null
document.removeEventListener('keydown', this, true)
document.removeEventListener('click', this)
}
attributeChangedCallback () {
if (this._open === this.hidden) { // this._open comparison ensures actual change
this.button.setAttribute('aria-expanded', this._open = !this.hidden)
try { this.querySelector('[autofocus]').focus() } catch (err) {}
dispatchEvent(this, 'toggle')
}
}
}, true) // Use capture to enable checking defaultPrevented (from ESC key) in parents
addEvent(UUID, 'click', ({ target, defaultPrevented }) => {
if (defaultPrevented) return false // Do not toggle if someone run event.preventDefault()
for (let el = target, item; el; el = el.parentElement) {
const toggle = item && el.id && document.querySelector(`[${UUID}][aria-controls="${el.id}"]`)
if ((el.nodeName === 'BUTTON' || el.nodeName === 'A') && !el.hasAttribute(UUID)) item = el // interactive element clicked
if (toggle) {
dispatchEvent(toggle, 'toggle.select', {
relatedTarget: getContentElement(toggle),
currentTarget: item,
value: item.textContent.trim()
})
break
handleEvent (event) {
if (event.defaultPrevented) return
if (event.type === 'keydown' && event.keyCode === 27) {
const isButton = event.target.getAttribute && event.target.getAttribute('aria-expanded') === 'true'
const isHiding = isButton ? event.target === this.button : closest(event.target, this.nodeName) === this
if (isHiding) {
this.hidden = true
this.button.focus() // Move focus back to button
return event.preventDefault() // Prevent closing maximized Safari and other coreToggles
}
}
if (event.type === 'click') {
const btn = closest(event.target, 'a,button')
if (btn && !btn.hasAttribute('aria-expanded') && closest(event.target, this.nodeName) === this) dispatchEvent(this, 'toggle.select', btn)
else if (btn && btn.getAttribute('aria-controls') === this.id) this.hidden = !this.hidden
else if (this.popup && !this.contains(event.target)) this.hidden = true // Click in content or outside
}
}
get button () {
if (this._button && this._button.getAttribute('for') === this.id) return this._button // Speed up
return (this._button = this.id && document.querySelector(`[for="${this.id}"]`)) || this.previousElementSibling
}
queryAll(`[${UUID}]`).forEach((toggle) => {
const open = toggle.getAttribute('aria-expanded') === 'true'
const popup = toggle.getAttribute(UUID) !== 'false'
const content = getContentElement(toggle)
// aria-haspopup triggers forms mode in JAWS, therefore store as custom attr
get popup () { return this.getAttribute('popup') === 'true' || this.getAttribute('popup') || this.hasAttribute('popup') }
set popup (val) { this[val === false ? 'removeAttribute' : 'setAttribute']('popup', val) }
if (toggle.contains(target)) setOpen(toggle, !open) // Click on toggle
else if (popup && open) setOpen(toggle, content.contains(target)) // Click in content or outside
})
})
// Must set attribute for IE11
get hidden () { return this.hasAttribute('hidden') }
set hidden (val) { this.toggleAttribute('hidden', val) }
function setOpen (toggle, open) {
const content = getContentElement(toggle)
const isOpen = toggle.getAttribute('aria-expanded') === 'true'
const willOpen = typeof open === 'boolean' ? open : (open === 'toggle' ? !isOpen : isOpen)
const isUpdate = isOpen === willOpen || dispatchEvent(toggle, 'toggle', { relatedTarget: content, isOpen, willOpen })
const nextOpen = isUpdate ? willOpen : toggle.getAttribute('aria-expanded') === 'true' // dispatchEvent can change attributes
const focus = !isOpen && nextOpen && content.querySelector('[autofocus]')
// Sets this.button aria-label, so visible button text can be augmentet with intension of button
// Example: Button text: "01.02.2019", aria-label: "01.02.2019, Choose date"
// Does not updates aria-label if not allready set to something else than this.popup
get value () { return this.button.value || this.button.textContent }
set value (data = false) {
if (!this.button || !this.popup.length) return
const button = this.button
const popup = (button.getAttribute('aria-label') || `,${this.popup}`).split(',')[1]
const label = data.textContent || data || '' // data can be Element, Object or String
if (focus) setTimeout(() => focus && focus.focus()) // Move focus on next render (if element stil exists)
toggle.setAttribute('aria-expanded', nextOpen)
content[nextOpen ? 'removeAttribute' : 'setAttribute']('hidden', '')
if (popup === this.popup) {
button.value = data.value || label
button[data.innerHTML ? 'innerHTML' : 'textContent'] = data.innerHTML || label
button.setAttribute('aria-label', `${button.textContent},${this.popup}`)
}
}
}

@@ -1,54 +0,4 @@

import React from 'react'
import PropTypes from 'prop-types'
import coreToggle from './core-toggle'
import { exclude } from '../utils'
import CoreToggle from './core-toggle.js'
import { elementToReact } from '../utils.js'
export default class Toggle extends React.Component {
static get defaultProps () { return { open: null, popup: null, onToggle: null, onToggleSelect: null } }
constructor (props) {
super(props)
this.onToggle = this.onToggle.bind(this)
this.onToggleSelect = this.onToggleSelect.bind(this)
}
update () {
coreToggle(this.el.firstElementChild, {
popup: this.props.popup,
open: this.props.open
})
}
componentDidMount () {
this.update()
this.el.addEventListener('toggle', this.onToggle)
this.el.addEventListener('toggle.select', this.onToggleSelect)
}
componentDidUpdate () { this.update() }
componentWillUnmount () {
this.el.removeEventListener('toggle', this.onToggle)
this.el.removeEventListener('toggle.select', this.onToggleSelect)
}
onToggle (event) {
this.props.onToggle && this.props.onToggle(event)
}
onToggleSelect (event) {
this.props.onToggleSelect && this.props.onToggleSelect(event)
}
render () {
return React.createElement('div', exclude(this.props, Toggle.defaultProps, { ref: (el) => (this.el = el) }),
React.Children.map(this.props.children, (child, adjacent) => {
if (adjacent === 0) {
return React.cloneElement(child, {
'aria-expanded': String(Boolean(this.props.open))
})
}
if (adjacent === 1) return React.cloneElement(child, { 'hidden': !this.props.open })
return child
})
)
}
}
Toggle.propTypes = {
open: PropTypes.bool,
popup: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
onToggle: PropTypes.func
}
export default elementToReact(CoreToggle, 'toggle', 'toggle.select')

@@ -1,3 +0,437 @@

/*! @nrk/core-toggle v2.2.2 - Copyright (c) 2017-2019 NRK */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t(require("react"),require("prop-types")):"function"==typeof define&&define.amd?define(["react","prop-types"],t):(e=e||self).CoreToggle=t(e.React,e.PropTypes)}(this,function(u,e){"use strict";u=u&&u.hasOwnProperty("default")?u.default:u,e=e&&e.hasOwnProperty("default")?e.default:e;var t="undefined"!=typeof window,n=t&&/(android)/i.test(navigator.userAgent),o=t&&/iPad|iPhone|iPod/.test(String(navigator.platform)),r=function(e){void 0===e&&(e=!1);try{window.addEventListener("test",null,{get passive(){e=!0}})}catch(e){}return e}();function i(e,t,n,o){(void 0===o&&(o=!1),"undefined"==typeof window||window[e=e+"-"+t])||(r||"object"!=typeof o||(o=Boolean(o.capture)),("resize"===t||"load"===t?window:document).addEventListener(window[e]=t,n,o))}var a="prevent_recursive_dispatch_maximum_callstack";function l(e,t,n){void 0===n&&(n={});var o,r=""+a+t;if(e[r])return!0;e[r]=!0,"function"==typeof window.CustomEvent?o=new window.CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n}):(o=document.createEvent("CustomEvent")).initCustomEvent(t,!0,!0,n);var i=e.dispatchEvent(o);return e[r]=null,i}function p(e){return Date.now().toString(36)+Math.random().toString(36).slice(2,5)}function d(e,t){if(void 0===t&&(t=document),e){if(e.nodeType)return[e];if("string"==typeof e)return[].slice.call(t.querySelectorAll(e));if(e.length)return[].slice.call(e)}return[]}var s="data-@nrk/core-toggle-2.2.2".replace(/\W+/g,"-"),c=n?"data":"aria",g=27;function f(e){return document.getElementById(e.getAttribute("aria-controls"))||e.nextElementSibling}function v(e,t){var n=f(e),o="true"===e.getAttribute("aria-expanded"),r="boolean"==typeof t?t:"toggle"===t?!o:o,i=o===r||l(e,"toggle",{relatedTarget:n,isOpen:o,willOpen:r})?r:"true"===e.getAttribute("aria-expanded"),u=!o&&i&&n.querySelector("[autofocus]");u&&setTimeout(function(){return u&&u.focus()}),e.setAttribute("aria-expanded",i),n[i?"removeAttribute":"setAttribute"]("hidden","")}i(s,"keydown",function(e){if(e.keyCode===g)for(var t=e.target;t;t=t.parentElement){var n=t.id&&document.querySelector('[aria-controls="'+t.id+'"]')||t;if("false"!==n.getAttribute(s)&&"true"===n.getAttribute("aria-expanded"))return e.preventDefault(),n.focus(),v(n,!1)}},!0),i(s,"click",function(e){var r=e.target;if(e.defaultPrevented)return!1;for(var t=r,n=void 0;t;t=t.parentElement){var o=n&&t.id&&document.querySelector("["+s+'][aria-controls="'+t.id+'"]');if("BUTTON"!==t.nodeName&&"A"!==t.nodeName||t.hasAttribute(s)||(n=t),o){l(o,"toggle.select",{relatedTarget:f(o),currentTarget:n,value:n.textContent.trim()});break}}d("["+s+"]").forEach(function(e){var t="true"===e.getAttribute("aria-expanded"),n="false"!==e.getAttribute(s),o=f(e);e.contains(r)?v(e,!t):n&&t&&v(e,o.contains(r))})});var h=function(t){function i(e){t.call(this,e),this.onToggle=this.onToggle.bind(this),this.onToggleSelect=this.onToggleSelect.bind(this)}t&&(i.__proto__=t),(i.prototype=Object.create(t&&t.prototype)).constructor=i;var e={defaultProps:{configurable:!0}};return e.defaultProps.get=function(){return{open:null,popup:null,onToggle:null,onToggleSelect:null}},i.prototype.update=function(){var e,t,i;e=this.el.firstElementChild,t={popup:this.props.popup,open:this.props.open},i="object"==typeof t?t:{open:t},o&&(document.documentElement.style.cursor="pointer"),d(e).map(function(e){var t=f(e),n="true"===e.getAttribute("aria-expanded"),o="boolean"==typeof i.open?i.open:"toggle"===i.open?!n:n,r=String((i.hasOwnProperty("popup")?i.popup:e.getAttribute(s))||!1);return i.value&&(e.innerHTML=i.value),"false"!==r&&"true"!==r&&e.setAttribute("aria-label",e.textContent+", "+r),e.setAttribute(s,r),e.setAttribute("aria-controls",t.id=t.id||p()),t.setAttribute(c+"-labelledby",e.id=e.id||p()),v(e,o),e})},i.prototype.componentDidMount=function(){this.update(),this.el.addEventListener("toggle",this.onToggle),this.el.addEventListener("toggle.select",this.onToggleSelect)},i.prototype.componentDidUpdate=function(){this.update()},i.prototype.componentWillUnmount=function(){this.el.removeEventListener("toggle",this.onToggle),this.el.removeEventListener("toggle.select",this.onToggleSelect)},i.prototype.onToggle=function(e){this.props.onToggle&&this.props.onToggle(e)},i.prototype.onToggleSelect=function(e){this.props.onToggleSelect&&this.props.onToggleSelect(e)},i.prototype.render=function(){var n,o,e,r=this;return u.createElement("div",(n=this.props,o=i.defaultProps,void 0===(e={ref:function(e){return r.el=e}})&&(e={}),Object.keys(n).reduce(function(e,t){return o.hasOwnProperty(t)||(e[t]=n[t]),e},e)),u.Children.map(this.props.children,function(e,t){return 0===t?u.cloneElement(e,{"aria-expanded":String(Boolean(r.props.open))}):1===t?u.cloneElement(e,{hidden:!r.props.open}):e}))},Object.defineProperties(i,e),i}(u.Component);return h.propTypes={open:e.bool,popup:e.oneOfType([e.bool,e.string]),onToggle:e.func},h});
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('react')) :
typeof define === 'function' && define.amd ? define(['react'], factory) :
(global = global || self, global.CoreToggle = factory(global.React));
}(this, function (React) { 'use strict';
React = React && React.hasOwnProperty('default') ? React['default'] : React;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _construct(Parent, args, Class) {
if (isNativeReflectConstruct()) {
_construct = Reflect.construct;
} else {
_construct = function _construct(Parent, args, Class) {
var a = [null];
a.push.apply(a, args);
var Constructor = Function.bind.apply(Parent, a);
var instance = new Constructor();
if (Class) _setPrototypeOf(instance, Class.prototype);
return instance;
};
}
return _construct.apply(null, arguments);
}
function _isNativeFunction(fn) {
return Function.toString.call(fn).indexOf("[native code]") !== -1;
}
function _wrapNativeSuper(Class) {
var _cache = typeof Map === "function" ? new Map() : undefined;
_wrapNativeSuper = function _wrapNativeSuper(Class) {
if (Class === null || !_isNativeFunction(Class)) return Class;
if (typeof Class !== "function") {
throw new TypeError("Super expression must either be null or a function");
}
if (typeof _cache !== "undefined") {
if (_cache.has(Class)) return _cache.get(Class);
_cache.set(Class, Wrapper);
}
function Wrapper() {
return _construct(Class, arguments, _getPrototypeOf(this).constructor);
}
Wrapper.prototype = Object.create(Class.prototype, {
constructor: {
value: Wrapper,
enumerable: false,
writable: true,
configurable: true
}
});
return _setPrototypeOf(Wrapper, Class);
};
return _wrapNativeSuper(Class);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
var IS_BROWSER = typeof window !== 'undefined';
var IS_ANDROID = IS_BROWSER && /(android)/i.test(navigator.userAgent); // Bad, but needed
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); // Polyfill toggleAttribute for IE
if (IS_BROWSER && !window.Element.prototype.toggleAttribute) {
window.Element.prototype.toggleAttribute = function (name) {
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !this.hasAttribute(name);
if (!force === this.hasAttribute(name)) this[force ? 'setAttribute' : 'removeAttribute'](name, '');
return force;
};
}
/**
* closest
* @param {Element} element Element to traverse up from
* @param {String} selector A selector to search for matching parents or element itself
* @return {Element|null} Element which is the closest ancestor matching selector
*/
var closest = function () {
var proto = typeof window === 'undefined' ? {} : window.Element.prototype;
var match = proto.matches || proto.msMatchesSelector || proto.webkitMatchesSelector;
return proto.closest ? function (el, css) {
return el.closest(css);
} : function (el, css) {
for (; el; el = el.parentElement) {
if (match.call(el, css)) return el;
}
return null;
};
}();
/**
* dispatchEvent - with infinite loop prevention
* @param {Element} elem The target object
* @param {String} name The source object(s)
* @param {Object} detail Detail object (bubbles and cancelable is set to true)
* @return {Boolean} Whether the event was canceled
*/
function dispatchEvent(element, name) {
var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var ignore = "prevent_recursive_dispatch_maximum_callstack".concat(name);
var event;
if (element[ignore]) return true; // We are already processing this event, so skip sending a new one
else element[ignore] = true; // Add name to dispatching ignore
if (typeof window.CustomEvent === 'function') {
event = new window.CustomEvent(name, {
bubbles: true,
cancelable: true,
detail: detail
});
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(name, true, true, detail);
} // IE reports incorrect event.defaultPrevented
// but correct return value on element.dispatchEvent
var result = element.dispatchEvent(event);
element[ignore] = null; // Remove name from dispatching ignore
return result; // Follow W3C standard for return value
}
function elementToReact(elementClass) {
for (var _len = arguments.length, attr = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
attr[_key - 1] = arguments[_key];
}
var name = elementClass.name || String(elementClass).match(/function ([^(]+)/)[1]; // String match for IE11
var tag = "".concat(name.replace(/\W+/, '-'), "-").concat(getUUID()).toLowerCase();
if (IS_BROWSER && !window.customElements.get(tag)) window.customElements.define(tag, elementClass);
return (
/*#__PURE__*/
function (_React$Component) {
_inherits(_class, _React$Component);
function _class(props) {
var _this;
_classCallCheck(this, _class);
_this = _possibleConstructorReturn(this, _getPrototypeOf(_class).call(this, props));
_this.ref = function (el) {
return _this.el = el;
};
attr.forEach(function (k) {
var on = "on".concat(k.replace(/(^|\.)./g, function (m) {
return m.slice(-1).toUpperCase();
})); // input.filter => onInputFilter
_this[k] = function (event) {
return _this.props[on] && _this.props[on](event);
};
});
return _this;
}
_createClass(_class, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this;
attr.forEach(function (k) {
return _this2.props[k] ? _this2.el[k] = _this2.props[k] : _this2.el.addEventListener(k, _this2[k]);
});
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prev) {
var _this3 = this;
attr.forEach(function (k) {
return prev[k] !== _this3.props[k] && (_this3.el[k] = _this3.props[k]);
});
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var _this4 = this;
attr.forEach(function (k) {
return _this4.el.removeEventListener(k, _this4[k]);
});
}
}, {
key: "render",
value: function render() {
var _this5 = this;
// Convert React props to CustomElement props https://github.com/facebook/react/issues/12810
return React.createElement(tag, Object.keys(this.props).reduce(function (props, k) {
if (k === 'className') props["class"] = _this5.props[k]; // Fixes className for custom elements
else if (_this5.props[k] === true) props[k] = ''; // Fixes boolean attributes
else if (_this5.props[k] !== false) props[k] = _this5.props[k];
return props;
}, {
ref: this.ref
}));
}
}]);
return _class;
}(React.Component)
);
}
/**
* getUUID
* @return {String} A generated unique ID
*/
function getUUID() {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
}
var CoreToggle =
/*#__PURE__*/
function (_HTMLElement) {
_inherits(CoreToggle, _HTMLElement);
function CoreToggle() {
_classCallCheck(this, CoreToggle);
return _possibleConstructorReturn(this, _getPrototypeOf(CoreToggle).apply(this, arguments));
}
_createClass(CoreToggle, [{
key: "connectedCallback",
value: function connectedCallback() {
if (IS_IOS) document.documentElement.style.cursor = 'pointer'; // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
if (!IS_ANDROID) this.setAttribute('aria-labelledby', this.button.id = this.button.id || getUUID()); // Andriod reads only label instead of content
this.value = this.button.textContent; // Set up aria-label
this.setAttribute('role', 'group'); // Help Edge
this.button.setAttribute('aria-expanded', this._open = !this.hidden);
this.button.setAttribute('aria-controls', this.id = this.id || getUUID());
document.addEventListener('keydown', this, true); // Use capture to enable checking defaultPrevented (from ESC key) in parents
document.addEventListener('click', this);
}
}, {
key: "disconnectedCallback",
value: function disconnectedCallback() {
this._button = null;
document.removeEventListener('keydown', this, true);
document.removeEventListener('click', this);
}
}, {
key: "attributeChangedCallback",
value: function attributeChangedCallback() {
if (this._open === this.hidden) {
// this._open comparison ensures actual change
this.button.setAttribute('aria-expanded', this._open = !this.hidden);
try {
this.querySelector('[autofocus]').focus();
} catch (err) {}
dispatchEvent(this, 'toggle');
}
}
}, {
key: "handleEvent",
value: function handleEvent(event) {
if (event.defaultPrevented) return;
if (event.type === 'keydown' && event.keyCode === 27) {
var isButton = event.target.getAttribute && event.target.getAttribute('aria-expanded') === 'true';
var isHiding = isButton ? event.target === this.button : closest(event.target, this.nodeName) === this;
if (isHiding) {
this.hidden = true;
this.button.focus(); // Move focus back to button
return event.preventDefault(); // Prevent closing maximized Safari and other coreToggles
}
}
if (event.type === 'click') {
var btn = closest(event.target, 'a,button');
if (btn && !btn.hasAttribute('aria-expanded') && closest(event.target, this.nodeName) === this) dispatchEvent(this, 'toggle.select', btn);else if (btn && btn.getAttribute('aria-controls') === this.id) this.hidden = !this.hidden;else if (this.popup && !this.contains(event.target)) this.hidden = true; // Click in content or outside
}
}
}, {
key: "button",
get: function get() {
if (this._button && this._button.getAttribute('for') === this.id) return this._button; // Speed up
return (this._button = this.id && document.querySelector("[for=\"".concat(this.id, "\"]"))) || this.previousElementSibling;
} // aria-haspopup triggers forms mode in JAWS, therefore store as custom attr
}, {
key: "popup",
get: function get() {
return this.getAttribute('popup') === 'true' || this.getAttribute('popup') || this.hasAttribute('popup');
},
set: function set(val) {
this[val === false ? 'removeAttribute' : 'setAttribute']('popup', val);
} // Must set attribute for IE11
}, {
key: "hidden",
get: function get() {
return this.hasAttribute('hidden');
},
set: function set(val) {
this.toggleAttribute('hidden', val);
} // Sets this.button aria-label, so visible button text can be augmentet with intension of button
// Example: Button text: "01.02.2019", aria-label: "01.02.2019, Choose date"
// Does not updates aria-label if not allready set to something else than this.popup
}, {
key: "value",
get: function get() {
return this.button.value || this.button.textContent;
},
set: function set() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (!this.button || !this.popup.length) return;
var button = this.button;
var popup = (button.getAttribute('aria-label') || ",".concat(this.popup)).split(',')[1];
var label = data.textContent || data || ''; // data can be Element, Object or String
if (popup === this.popup) {
button.value = data.value || label;
button[data.innerHTML ? 'innerHTML' : 'textContent'] = data.innerHTML || label;
button.setAttribute('aria-label', "".concat(button.textContent, ",").concat(this.popup));
}
}
}], [{
key: "observedAttributes",
get: function get() {
return ['hidden'];
}
}]);
return CoreToggle;
}(_wrapNativeSuper(HTMLElement));
var coreToggle = elementToReact(CoreToggle, 'toggle', 'toggle.select');
return coreToggle;
}));
//# sourceMappingURL=core-toggle.jsx.js.map

@@ -1,3 +0,3 @@

/*! @nrk/core-toggle v2.2.2 - Copyright (c) 2017-2019 NRK */
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).coreToggle=t()}(this,function(){"use strict";var e="undefined"!=typeof window,t=e&&/(android)/i.test(navigator.userAgent),n=e&&/iPad|iPhone|iPod/.test(String(navigator.platform)),o=function(e){void 0===e&&(e=!1);try{window.addEventListener("test",null,{get passive(){e=!0}})}catch(e){}return e}();function r(e,t,n,r){(void 0===r&&(r=!1),"undefined"==typeof window||window[e=e+"-"+t])||(o||"object"!=typeof r||(r=Boolean(r.capture)),("resize"===t||"load"===t?window:document).addEventListener(window[e]=t,n,r))}var a="prevent_recursive_dispatch_maximum_callstack";function u(e,t,n){void 0===n&&(n={});var r,o=""+a+t;if(e[o])return!0;e[o]=!0,"function"==typeof window.CustomEvent?r=new window.CustomEvent(t,{bubbles:!0,cancelable:!0,detail:n}):(r=document.createEvent("CustomEvent")).initCustomEvent(t,!0,!0,n);var i=e.dispatchEvent(r);return e[o]=null,i}function d(e){return Date.now().toString(36)+Math.random().toString(36).slice(2,5)}function l(e,t){if(void 0===t&&(t=document),e){if(e.nodeType)return[e];if("string"==typeof e)return[].slice.call(t.querySelectorAll(e));if(e.length)return[].slice.call(e)}return[]}var c="data-@nrk/core-toggle-2.2.2".replace(/\W+/g,"-"),f=t?"data":"aria",i=27;function s(e){return document.getElementById(e.getAttribute("aria-controls"))||e.nextElementSibling}function p(e,t){var n=s(e),r="true"===e.getAttribute("aria-expanded"),o="boolean"==typeof t?t:"toggle"===t?!r:r,i=r===o||u(e,"toggle",{relatedTarget:n,isOpen:r,willOpen:o})?o:"true"===e.getAttribute("aria-expanded"),a=!r&&i&&n.querySelector("[autofocus]");a&&setTimeout(function(){return a&&a.focus()}),e.setAttribute("aria-expanded",i),n[i?"removeAttribute":"setAttribute"]("hidden","")}return r(c,"keydown",function(e){if(e.keyCode===i)for(var t=e.target;t;t=t.parentElement){var n=t.id&&document.querySelector('[aria-controls="'+t.id+'"]')||t;if("false"!==n.getAttribute(c)&&"true"===n.getAttribute("aria-expanded"))return e.preventDefault(),n.focus(),p(n,!1)}},!0),r(c,"click",function(e){var o=e.target;if(e.defaultPrevented)return!1;for(var t=o,n=void 0;t;t=t.parentElement){var r=n&&t.id&&document.querySelector("["+c+'][aria-controls="'+t.id+'"]');if("BUTTON"!==t.nodeName&&"A"!==t.nodeName||t.hasAttribute(c)||(n=t),r){u(r,"toggle.select",{relatedTarget:s(r),currentTarget:n,value:n.textContent.trim()});break}}l("["+c+"]").forEach(function(e){var t="true"===e.getAttribute("aria-expanded"),n="false"!==e.getAttribute(c),r=s(e);e.contains(o)?p(e,!t):n&&t&&p(e,r.contains(o))})}),function(e,t){var i="object"==typeof t?t:{open:t};return n&&(document.documentElement.style.cursor="pointer"),l(e).map(function(e){var t=s(e),n="true"===e.getAttribute("aria-expanded"),r="boolean"==typeof i.open?i.open:"toggle"===i.open?!n:n,o=String((i.hasOwnProperty("popup")?i.popup:e.getAttribute(c))||!1);return i.value&&(e.innerHTML=i.value),"false"!==o&&"true"!==o&&e.setAttribute("aria-label",e.textContent+", "+o),e.setAttribute(c,o),e.setAttribute("aria-controls",t.id=t.id||d()),t.setAttribute(f+"-labelledby",e.id=e.id||d()),p(e,r),e})}});
/*! @nrk/core-toggle v3.0.0 - Copyright (c) 2017-2019 NRK */
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).coreToggle=e()}(this,function(){"use strict";function r(t,e){for(var n=0;n<e.length;n++){var i=e[n];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(t,i.key,i)}}function u(t){return(u=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function s(t,e){return(s=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function o(t,e,n){return(o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}()?Reflect.construct:function(t,e,n){var i=[null];i.push.apply(i,e);var o=new(Function.bind.apply(t,i));return n&&s(o,n.prototype),o}).apply(null,arguments)}function c(t){var i="function"==typeof Map?new Map:void 0;return(c=function(t){if(null===t||(e=t,-1===Function.toString.call(e).indexOf("[native code]")))return t;var e;if("function"!=typeof t)throw new TypeError("Super expression must either be null or a function");if(void 0!==i){if(i.has(t))return i.get(t);i.set(t,n)}function n(){return o(t,arguments,u(this).constructor)}return n.prototype=Object.create(t.prototype,{constructor:{value:n,enumerable:!1,writable:!0,configurable:!0}}),s(n,t)})(t)}function a(t,e){return!e||"object"!=typeof e&&"function"!=typeof e?function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t):e}var t="undefined"!=typeof window,l=t&&/(android)/i.test(navigator.userAgent),f=t&&/iPad|iPhone|iPod/.test(String(navigator.platform));t&&!window.Element.prototype.toggleAttribute&&(window.Element.prototype.toggleAttribute=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:!this.hasAttribute(t);return!e===this.hasAttribute(t)&&this[e?"setAttribute":"removeAttribute"](t,""),e});var e,n,h=(e="undefined"==typeof window?{}:window.Element.prototype,n=e.matches||e.msMatchesSelector||e.webkitMatchesSelector,e.closest?function(t,e){return t.closest(e)}:function(t,e){for(;t;t=t.parentElement)if(n.call(t,e))return t;return null});function p(t,e){var n,i=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{},o="prevent_recursive_dispatch_maximum_callstack".concat(e);if(t[o])return!0;t[o]=!0,"function"==typeof window.CustomEvent?n=new window.CustomEvent(e,{bubbles:!0,cancelable:!0,detail:i}):(n=document.createEvent("CustomEvent")).initCustomEvent(e,!0,!0,i);var r=t.dispatchEvent(n);return t[o]=null,r}function d(){return Date.now().toString(36)+Math.random().toString(36).slice(2,5)}return function(t){function e(){return function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}(this,e),a(this,u(e).apply(this,arguments))}var n,i,o;return function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&s(t,e)}(e,c(HTMLElement)),n=e,o=[{key:"observedAttributes",get:function(){return["hidden"]}}],(i=[{key:"connectedCallback",value:function(){f&&(document.documentElement.style.cursor="pointer"),l||this.setAttribute("aria-labelledby",this.button.id=this.button.id||d()),this.value=this.button.textContent,this.setAttribute("role","group"),this.button.setAttribute("aria-expanded",this._open=!this.hidden),this.button.setAttribute("aria-controls",this.id=this.id||d()),document.addEventListener("keydown",this,!0),document.addEventListener("click",this)}},{key:"disconnectedCallback",value:function(){this._button=null,document.removeEventListener("keydown",this,!0),document.removeEventListener("click",this)}},{key:"attributeChangedCallback",value:function(){if(this._open===this.hidden){this.button.setAttribute("aria-expanded",this._open=!this.hidden);try{this.querySelector("[autofocus]").focus()}catch(t){}p(this,"toggle")}}},{key:"handleEvent",value:function(t){if(!t.defaultPrevented){if("keydown"===t.type&&27===t.keyCode)if(t.target.getAttribute&&"true"===t.target.getAttribute("aria-expanded")?t.target===this.button:h(t.target,this.nodeName)===this)return this.hidden=!0,this.button.focus(),t.preventDefault();if("click"===t.type){var e=h(t.target,"a,button");e&&!e.hasAttribute("aria-expanded")&&h(t.target,this.nodeName)===this?p(this,"toggle.select",e):e&&e.getAttribute("aria-controls")===this.id?this.hidden=!this.hidden:this.popup&&!this.contains(t.target)&&(this.hidden=!0)}}}},{key:"button",get:function(){return this._button&&this._button.getAttribute("for")===this.id?this._button:(this._button=this.id&&document.querySelector('[for="'.concat(this.id,'"]')))||this.previousElementSibling}},{key:"popup",get:function(){return"true"===this.getAttribute("popup")||this.getAttribute("popup")||this.hasAttribute("popup")},set:function(t){this[!1===t?"removeAttribute":"setAttribute"]("popup",t)}},{key:"hidden",get:function(){return this.hasAttribute("hidden")},set:function(t){this.toggleAttribute("hidden",t)}},{key:"value",get:function(){return this.button.value||this.button.textContent},set:function(){var t=0<arguments.length&&void 0!==arguments[0]&&arguments[0];if(this.button&&this.popup.length){var e=this.button,n=(e.getAttribute("aria-label")||",".concat(this.popup)).split(",")[1],i=t.textContent||t||"";n===this.popup&&(e.value=t.value||i,e[t.innerHTML?"innerHTML":"textContent"]=t.innerHTML||i,e.setAttribute("aria-label","".concat(e.textContent,",").concat(this.popup)))}}}])&&r(n.prototype,i),o&&r(n,o),e}()}),window.customElements.define("core-toggle",coreToggle);
//# sourceMappingURL=core-toggle.min.js.map

@@ -1,85 +0,167 @@

/* global expect, describe, it */
const { name, version } = require('./package.json')
const coreToggle = require('./core-toggle.min')
const UUID = `data-${name}-${version}`.replace(/\W+/g, '-')
import test from 'ava'
import path from 'path'
import puppeteer from 'puppeteer'
const standardHTML = `
<button class="my-toggle">Toggle VanillaJS</button>
<div hidden>Content</div>
`
async function withPage (t, run) {
const browser = await puppeteer.launch()
const page = await browser.newPage()
page.on('console', msg => console.log(msg._text))
await page.addScriptTag({ path: path.join(__dirname, 'core-toggle.min.js') })
try {
await run(t, page)
} finally {
await page.close()
await browser.close()
}
}
describe('toggle', () => {
it('should exists', () => {
expect(coreToggle).toBeInstanceOf(Function)
})
test('sets up all properties', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle hidden></core-toggle>
`)
t.is(await page.$eval('button', el => el.getAttribute('aria-expanded')), 'false')
t.true(await page.$eval('button', el => el.getAttribute('aria-controls') === document.querySelector('core-toggle').id))
t.true(await page.$eval('core-toggle', el => el.hasAttribute('hidden')))
t.true(await page.$eval('core-toggle', el => el.getAttribute('aria-labelledby') === document.querySelector('button').id))
})
it('should initialize button and container', () => {
document.body.innerHTML = standardHTML
const button = document.querySelector('.my-toggle')
const container = document.querySelector('.my-toggle + *')
coreToggle(button)
expect(button.getAttribute(UUID)).toEqual('false')
expect(button.getAttribute('aria-expanded')).toEqual('false')
expect(button.getAttribute('aria-controls')).toEqual(container.id)
expect(container.hasAttribute('hidden')).toEqual(true)
expect(container.getAttribute('aria-labelledby')).toEqual(button.id)
})
test('opens and closes toggle', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle hidden></core-toggle>
`)
await page.click('button')
t.is(await page.$eval('button', el => el.getAttribute('aria-expanded')), 'true')
await page.click('button')
t.is(await page.$eval('button', el => el.getAttribute('aria-expanded')), 'false')
await page.click('button')
t.is(await page.$eval('button', el => el.getAttribute('aria-expanded')), 'true')
await page.click('button')
t.is(await page.$eval('button', el => el.getAttribute('aria-expanded')), 'false')
})
it('should open with open attribute', () => {
document.body.innerHTML = standardHTML
const button = document.querySelector('.my-toggle')
const container = document.querySelector('.my-toggle + *')
coreToggle(button, { open: true })
expect(container.hasAttribute('hidden')).toEqual(false)
expect(button.getAttribute('aria-expanded')).toEqual('true')
})
test('opens and closes nested toggle', withPage, async (t, page) => {
await page.setContent(`
<button id="outer">Toggle outer</button>
<core-toggle hidden>
<button id="inner">Toggle inner</button>
<core-toggle hidden>
<div>Inner content</div>
</core-toggle>
</core-toggle>
`)
await page.click('button#outer')
await page.click('button#inner')
t.false(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
t.false(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
await page.click('button#inner')
t.true(await page.$eval('button#inner + core-toggle', el => el.hasAttribute('hidden')))
t.false(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
await page.click('button#outer')
t.true(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
})
it('should close an opened toggle', () => {
document.body.innerHTML = standardHTML
const button = document.querySelector('.my-toggle')
const container = document.querySelector('.my-toggle + *')
coreToggle(button, { open: true })
coreToggle(button, { open: false })
expect(container.hasAttribute('hidden')).toEqual(true)
})
test('closes nested toggle with esc', withPage, async (t, page) => {
await page.setContent(`
<button id="outer">Toggle outer</button>
<core-toggle hidden>
<button id="inner">Toggle inner</button>
<core-toggle hidden>
<div>Inner content</div>
</core-toggle>
</core-toggle>
`)
await page.click('button#outer')
await page.click('button#inner')
t.false(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
t.false(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
await page.keyboard.press('Escape')
t.true(await page.$eval('button#inner + core-toggle', el => el.hasAttribute('hidden')))
t.false(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
await page.keyboard.press('Escape')
t.true(await page.$eval('button#outer + core-toggle', el => el.hasAttribute('hidden')))
})
it('should initialize as popup', () => {
document.body.innerHTML = standardHTML
const button = document.querySelector('.my-toggle')
coreToggle(button, { popup: 'Test' })
expect(button.getAttribute(UUID)).toEqual('Test')
})
test('closes on outside click with popup', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle popup hidden></core-toggle>
`)
await page.click('button')
t.false(await page.$eval('core-toggle', el => el.hasAttribute('hidden')))
await page.click('body')
t.true(await page.$eval('core-toggle', el => el.hasAttribute('hidden')))
})
it('should open popup with open', () => {
document.body.innerHTML = standardHTML
const button = document.querySelector('.my-toggle')
const container = document.querySelector('.my-toggle + *')
coreToggle(button, { popup: 'Tekst', open: true })
expect(button.getAttribute(UUID)).toEqual('Tekst')
expect(container.hasAttribute('hidden')).toEqual(false)
})
test('respects "for" attribute', withPage, async (t, page) => {
await page.setContent(`
<div><button for="content">Toggle</button></div>
<core-toggle id="content" hidden></core-toggle>
`)
t.true(await page.$eval('core-toggle', el => el.button.getAttribute('for') === el.id))
t.true(await page.$eval('core-toggle', el => el.button.getAttribute('aria-controls') === el.id))
})
it('should close popup', () => {
document.body.innerHTML = standardHTML
const button = document.querySelector('.my-toggle')
const container = document.querySelector('.my-toggle + *')
coreToggle(button, { popup: 'Tekst', open: true })
coreToggle(button, { open: false })
expect(container.hasAttribute('hidden')).toEqual(true)
test('respects exisiting aria-label with popup and value', withPage, async (t, page) => {
await page.setContent(`
<button aria-label="Label">Toggle</button>
<core-toggle popup="Another label" hidden></core-toggle>
`)
await page.$eval('core-toggle', el => (el.value = 'Button text'))
t.is(await page.$eval('button', el => el.textContent), await page.$eval('core-toggle', el => el.value))
t.is(await page.$eval('button', el => el.getAttribute('aria-label')), 'Label')
})
test('sets aria-label with popup attr and value', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle popup="Some label" hidden></core-toggle>
`)
await page.$eval('core-toggle', el => (el.value = 'Button text'))
t.is(await page.$eval('button', el => el.textContent), await page.$eval('core-toggle', el => el.value))
t.is(await page.$eval('button', el => el.getAttribute('aria-label')), 'Button text,Some label')
})
test('sets aria-label with popup prop and value', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle hidden></core-toggle>
`)
await page.$eval('core-toggle', el => (el.popup = 'Some label'))
await page.$eval('core-toggle', el => (el.value = 'Button text'))
t.is(await page.$eval('button', el => el.textContent), await page.$eval('core-toggle', el => el.value))
t.is(await page.$eval('button', el => el.getAttribute('aria-label')), 'Button text,Some label')
})
test('triggers toggle event', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle hidden></core-toggle>
`)
await page.evaluate(() => {
return new Promise((resolve, reject) => {
window.addEventListener('toggle', resolve)
document.querySelector('core-toggle').hidden = false
})
})
t.pass()
})
it('should respect existing aria-controls', () => {
document.body.innerHTML = `
<div><button class="my-toggle" aria-controls="content">Toggle VanillaJS</button></div>
<div id="content" hidden>Content</div>`
const button = document.querySelector('.my-toggle')
const container = document.querySelector('#content')
coreToggle(button, { open: false })
expect(container.hasAttribute('hidden')).toEqual(true)
coreToggle(button, { open: true })
expect(container.hasAttribute('hidden')).toEqual(false)
expect(button.getAttribute('aria-expanded')).toEqual('true')
expect(button.getAttribute('aria-controls')).toEqual(container.id)
expect(container.getAttribute('aria-labelledby')).toEqual(button.id)
test('triggers select event', withPage, async (t, page) => {
await page.setContent(`
<button>Toggle</button>
<core-toggle hidden>
<button id="item">Select me</button>
</core-toggle>
`)
const selected = await page.evaluate(() => {
return new Promise((resolve, reject) => {
window.addEventListener('toggle.select', ({ detail }) => resolve(detail.id))
const toggle = document.querySelector('core-toggle')
toggle.hidden = false
toggle.children[0].click()
})
})
t.is(selected, 'item')
})

@@ -6,48 +6,172 @@ 'use strict';

var React = _interopDefault(require('react'));
var PropTypes = _interopDefault(require('prop-types'));
var name = "@nrk/core-toggle";
var version = "2.2.2";
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
return Constructor;
}
function _inherits(subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function");
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
writable: true,
configurable: true
}
});
if (superClass) _setPrototypeOf(subClass, superClass);
}
function _getPrototypeOf(o) {
_getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
return o.__proto__ || Object.getPrototypeOf(o);
};
return _getPrototypeOf(o);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
function isNativeReflectConstruct() {
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
if (Reflect.construct.sham) return false;
if (typeof Proxy === "function") return true;
try {
Date.prototype.toString.call(Reflect.construct(Date, [], function () {}));
return true;
} catch (e) {
return false;
}
}
function _construct(Parent, args, Class) {
if (isNativeReflectConstruct()) {
_construct = Reflect.construct;
} else {
_construct = function _construct(Parent, args, Class) {
var a = [null];
a.push.apply(a, args);
var Constructor = Function.bind.apply(Parent, a);
var instance = new Constructor();
if (Class) _setPrototypeOf(instance, Class.prototype);
return instance;
};
}
return _construct.apply(null, arguments);
}
function _isNativeFunction(fn) {
return Function.toString.call(fn).indexOf("[native code]") !== -1;
}
function _wrapNativeSuper(Class) {
var _cache = typeof Map === "function" ? new Map() : undefined;
_wrapNativeSuper = function _wrapNativeSuper(Class) {
if (Class === null || !_isNativeFunction(Class)) return Class;
if (typeof Class !== "function") {
throw new TypeError("Super expression must either be null or a function");
}
if (typeof _cache !== "undefined") {
if (_cache.has(Class)) return _cache.get(Class);
_cache.set(Class, Wrapper);
}
function Wrapper() {
return _construct(Class, arguments, _getPrototypeOf(this).constructor);
}
Wrapper.prototype = Object.create(Class.prototype, {
constructor: {
value: Wrapper,
enumerable: false,
writable: true,
configurable: true
}
});
return _setPrototypeOf(Wrapper, Class);
};
return _wrapNativeSuper(Class);
}
function _assertThisInitialized(self) {
if (self === void 0) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
}
return self;
}
function _possibleConstructorReturn(self, call) {
if (call && (typeof call === "object" || typeof call === "function")) {
return call;
}
return _assertThisInitialized(self);
}
var IS_BROWSER = typeof window !== 'undefined';
var IS_ANDROID = IS_BROWSER && /(android)/i.test(navigator.userAgent); // Bad, but needed
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform));
var HAS_EVENT_OPTIONS = (function (has) {
if ( has === void 0 ) has = false;
try { window.addEventListener('test', null, { get passive () { has = true; } }); } catch (e) {}
return has
})();
var IS_IOS = IS_BROWSER && /iPad|iPhone|iPod/.test(String(navigator.platform)); // Polyfill toggleAttribute for IE
/**
* addEvent
* @param {String} uuid An unique ID of the event to bind - ensurnes single instance
* @param {String} type The type of event to bind
* @param {Function} handler The function to call on event
* @param {Boolean|Object} options useCapture or options object for addEventListener. Defaults to false
*/
function addEvent (uuid, type, handler, options) {
if ( options === void 0 ) options = false;
if (typeof window === 'undefined' || window[uuid = uuid + "-" + type]) { return } // Ensure single instance
if (!HAS_EVENT_OPTIONS && typeof options === 'object') { options = Boolean(options.capture); } // Fix unsupported options
var node = (type === 'resize' || type === 'load') ? window : document;
node.addEventListener(window[uuid] = type, handler, options);
if (IS_BROWSER && !window.Element.prototype.toggleAttribute) {
window.Element.prototype.toggleAttribute = function (name) {
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : !this.hasAttribute(name);
if (!force === this.hasAttribute(name)) this[force ? 'setAttribute' : 'removeAttribute'](name, '');
return force;
};
}
/**
* exclude
* @param {Object} target The target object
* @param {Object} exclude The source to exclude keys from
* @return {Object} The target object without keys found in source
* closest
* @param {Element} element Element to traverse up from
* @param {String} selector A selector to search for matching parents or element itself
* @return {Element|null} Element which is the closest ancestor matching selector
*/
function exclude (target, exclude, include) {
if ( include === void 0 ) include = {};
return Object.keys(target).reduce(function (acc, key) {
if (!exclude.hasOwnProperty(key)) { acc[key] = target[key]; }
return acc
}, include)
}
var closest = function () {
var proto = typeof window === 'undefined' ? {} : window.Element.prototype;
var match = proto.matches || proto.msMatchesSelector || proto.webkitMatchesSelector;
return proto.closest ? function (el, css) {
return el.closest(css);
} : function (el, css) {
for (; el; el = el.parentElement) {
if (match.call(el, css)) return el;
}
return null;
};
}();
/**

@@ -60,198 +184,251 @@ * dispatchEvent - with infinite loop prevention

*/
var IGNORE = 'prevent_recursive_dispatch_maximum_callstack';
function dispatchEvent (element, name, detail) {
if ( detail === void 0 ) detail = {};
var ignore = "" + IGNORE + name;
function dispatchEvent(element, name) {
var detail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
var ignore = "prevent_recursive_dispatch_maximum_callstack".concat(name);
var event;
if (element[ignore]) return true; // We are already processing this event, so skip sending a new one
else element[ignore] = true; // Add name to dispatching ignore
if (element[ignore]) { return true } // We are already processing this event, so skip sending a new one
else { element[ignore] = true; } // Add name to dispatching ignore
if (typeof window.CustomEvent === 'function') {
event = new window.CustomEvent(name, { bubbles: true, cancelable: true, detail: detail });
event = new window.CustomEvent(name, {
bubbles: true,
cancelable: true,
detail: detail
});
} else {
event = document.createEvent('CustomEvent');
event.initCustomEvent(name, true, true, detail);
}
// IE reports incorrect event.defaultPrevented
} // IE reports incorrect event.defaultPrevented
// but correct return value on element.dispatchEvent
var result = element.dispatchEvent(event);
element[ignore] = null; // Remove name from dispatching ignore
return result // Follow W3C standard for return value
return result; // Follow W3C standard for return value
}
function elementToReact(elementClass) {
for (var _len = arguments.length, attr = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
attr[_key - 1] = arguments[_key];
}
/**
* getUUID
* @return {String} A generated unique ID
*/
function getUUID (el) {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5)
}
var name = elementClass.name || String(elementClass).match(/function ([^(]+)/)[1]; // String match for IE11
/**
* queryAll
* @param {String|NodeList|Array|Element} elements A CSS selector string, nodeList, element array, or single element
* @return {Element[]} Array of elements
*/
function queryAll (elements, context) {
if ( context === void 0 ) context = document;
var tag = "".concat(name.replace(/\W+/, '-'), "-").concat(getUUID()).toLowerCase();
if (IS_BROWSER && !window.customElements.get(tag)) window.customElements.define(tag, elementClass);
return (
/*#__PURE__*/
function (_React$Component) {
_inherits(_class, _React$Component);
if (elements) {
if (elements.nodeType) { return [elements] }
if (typeof elements === 'string') { return [].slice.call(context.querySelectorAll(elements)) }
if (elements.length) { return [].slice.call(elements) }
}
return []
}
function _class(props) {
var _this;
var UUID = ("data-" + name + "-" + version).replace(/\W+/g, '-'); // Strip invalid attribute characters
var ARIA = IS_ANDROID ? 'data' : 'aria'; // Andriod has a bug and reads only label instead of content
var KEYS = { ESC: 27 };
_classCallCheck(this, _class);
function toggle (toggles, open) {
var options = typeof open === 'object' ? open : { open: open };
if (IS_IOS) { document.documentElement.style.cursor = 'pointer'; } // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
_this = _possibleConstructorReturn(this, _getPrototypeOf(_class).call(this, props));
return queryAll(toggles).map(function (toggle) {
var content = getContentElement(toggle);
var isOpen = toggle.getAttribute('aria-expanded') === 'true';
var open = typeof options.open === 'boolean' ? options.open : (options.open === 'toggle' ? !isOpen : isOpen);
var popup = String((options.hasOwnProperty('popup') ? options.popup : toggle.getAttribute(UUID)) || false);
_this.ref = function (el) {
return _this.el = el;
};
if (options.value) { toggle.innerHTML = options.value; } // Set innerHTML before updating aria-label
if (popup !== 'false' && popup !== 'true') { toggle.setAttribute('aria-label', ((toggle.textContent) + ", " + popup)); } // Only update aria-label if popup-mode
attr.forEach(function (k) {
var on = "on".concat(k.replace(/(^|\.)./g, function (m) {
return m.slice(-1).toUpperCase();
})); // input.filter => onInputFilter
toggle.setAttribute(UUID, popup); // aria-haspopup triggers forms mode in JAWS, therefore store in uuid
toggle.setAttribute('aria-controls', content.id = content.id || getUUID());
content.setAttribute((ARIA + "-labelledby"), toggle.id = toggle.id || getUUID());
setOpen(toggle, open);
return toggle
})
_this[k] = function (event) {
return _this.props[on] && _this.props[on](event);
};
});
return _this;
}
_createClass(_class, [{
key: "componentDidMount",
value: function componentDidMount() {
var _this2 = this;
attr.forEach(function (k) {
return _this2.props[k] ? _this2.el[k] = _this2.props[k] : _this2.el.addEventListener(k, _this2[k]);
});
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prev) {
var _this3 = this;
attr.forEach(function (k) {
return prev[k] !== _this3.props[k] && (_this3.el[k] = _this3.props[k]);
});
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var _this4 = this;
attr.forEach(function (k) {
return _this4.el.removeEventListener(k, _this4[k]);
});
}
}, {
key: "render",
value: function render() {
var _this5 = this;
// Convert React props to CustomElement props https://github.com/facebook/react/issues/12810
return React.createElement(tag, Object.keys(this.props).reduce(function (props, k) {
if (k === 'className') props["class"] = _this5.props[k]; // Fixes className for custom elements
else if (_this5.props[k] === true) props[k] = ''; // Fixes boolean attributes
else if (_this5.props[k] !== false) props[k] = _this5.props[k];
return props;
}, {
ref: this.ref
}));
}
}]);
return _class;
}(React.Component)
);
}
/**
* getUUID
* @return {String} A generated unique ID
*/
function getContentElement (toggle) {
return document.getElementById(toggle.getAttribute('aria-controls')) || toggle.nextElementSibling
function getUUID() {
return Date.now().toString(36) + Math.random().toString(36).slice(2, 5);
}
addEvent(UUID, 'keydown', function (event) {
if (event.keyCode !== KEYS.ESC) { return }
for (var el = event.target; el; el = el.parentElement) {
var toggle = (el.id && document.querySelector(("[aria-controls=\"" + (el.id) + "\"]"))) || el;
var CoreToggle =
/*#__PURE__*/
function (_HTMLElement) {
_inherits(CoreToggle, _HTMLElement);
if (toggle.getAttribute(UUID) !== 'false' && toggle.getAttribute('aria-expanded') === 'true') {
event.preventDefault(); // Prevent leaving maximized window in Safari
toggle.focus();
return setOpen(toggle, false)
}
function CoreToggle() {
_classCallCheck(this, CoreToggle);
return _possibleConstructorReturn(this, _getPrototypeOf(CoreToggle).apply(this, arguments));
}
}, true); // Use capture to enable checking defaultPrevented (from ESC key) in parents
addEvent(UUID, 'click', function (ref) {
var target = ref.target;
var defaultPrevented = ref.defaultPrevented;
_createClass(CoreToggle, [{
key: "connectedCallback",
value: function connectedCallback() {
if (IS_IOS) document.documentElement.style.cursor = 'pointer'; // Fix iOS events for closing popups (https://stackoverflow.com/a/16006333/8819615)
if (defaultPrevented) { return false } // Do not toggle if someone run event.preventDefault()
if (!IS_ANDROID) this.setAttribute('aria-labelledby', this.button.id = this.button.id || getUUID()); // Andriod reads only label instead of content
for (var el = target, item = (void 0); el; el = el.parentElement) {
var toggle = item && el.id && document.querySelector(("[" + UUID + "][aria-controls=\"" + (el.id) + "\"]"));
if ((el.nodeName === 'BUTTON' || el.nodeName === 'A') && !el.hasAttribute(UUID)) { item = el; } // interactive element clicked
if (toggle) {
dispatchEvent(toggle, 'toggle.select', {
relatedTarget: getContentElement(toggle),
currentTarget: item,
value: item.textContent.trim()
});
break
this.value = this.button.textContent; // Set up aria-label
this.setAttribute('role', 'group'); // Help Edge
this.button.setAttribute('aria-expanded', this._open = !this.hidden);
this.button.setAttribute('aria-controls', this.id = this.id || getUUID());
document.addEventListener('keydown', this, true); // Use capture to enable checking defaultPrevented (from ESC key) in parents
document.addEventListener('click', this);
}
}
}, {
key: "disconnectedCallback",
value: function disconnectedCallback() {
this._button = null;
document.removeEventListener('keydown', this, true);
document.removeEventListener('click', this);
}
}, {
key: "attributeChangedCallback",
value: function attributeChangedCallback() {
if (this._open === this.hidden) {
// this._open comparison ensures actual change
this.button.setAttribute('aria-expanded', this._open = !this.hidden);
queryAll(("[" + UUID + "]")).forEach(function (toggle) {
var open = toggle.getAttribute('aria-expanded') === 'true';
var popup = toggle.getAttribute(UUID) !== 'false';
var content = getContentElement(toggle);
try {
this.querySelector('[autofocus]').focus();
} catch (err) {}
if (toggle.contains(target)) { setOpen(toggle, !open); } // Click on toggle
else if (popup && open) { setOpen(toggle, content.contains(target)); } // Click in content or outside
});
});
dispatchEvent(this, 'toggle');
}
}
}, {
key: "handleEvent",
value: function handleEvent(event) {
if (event.defaultPrevented) return;
function setOpen (toggle, open) {
var content = getContentElement(toggle);
var isOpen = toggle.getAttribute('aria-expanded') === 'true';
var willOpen = typeof open === 'boolean' ? open : (open === 'toggle' ? !isOpen : isOpen);
var isUpdate = isOpen === willOpen || dispatchEvent(toggle, 'toggle', { relatedTarget: content, isOpen: isOpen, willOpen: willOpen });
var nextOpen = isUpdate ? willOpen : toggle.getAttribute('aria-expanded') === 'true'; // dispatchEvent can change attributes
var focus = !isOpen && nextOpen && content.querySelector('[autofocus]');
if (event.type === 'keydown' && event.keyCode === 27) {
var isButton = event.target.getAttribute && event.target.getAttribute('aria-expanded') === 'true';
var isHiding = isButton ? event.target === this.button : closest(event.target, this.nodeName) === this;
if (focus) { setTimeout(function () { return focus && focus.focus(); }); } // Move focus on next render (if element stil exists)
if (isHiding) {
this.hidden = true;
this.button.focus(); // Move focus back to button
toggle.setAttribute('aria-expanded', nextOpen);
content[nextOpen ? 'removeAttribute' : 'setAttribute']('hidden', '');
}
return event.preventDefault(); // Prevent closing maximized Safari and other coreToggles
}
}
var Toggle = /*@__PURE__*/(function (superclass) {
function Toggle (props) {
superclass.call(this, props);
this.onToggle = this.onToggle.bind(this);
this.onToggleSelect = this.onToggleSelect.bind(this);
}
if (event.type === 'click') {
var btn = closest(event.target, 'a,button');
if (btn && !btn.hasAttribute('aria-expanded') && closest(event.target, this.nodeName) === this) dispatchEvent(this, 'toggle.select', btn);else if (btn && btn.getAttribute('aria-controls') === this.id) this.hidden = !this.hidden;else if (this.popup && !this.contains(event.target)) this.hidden = true; // Click in content or outside
}
}
}, {
key: "button",
get: function get() {
if (this._button && this._button.getAttribute('for') === this.id) return this._button; // Speed up
if ( superclass ) Toggle.__proto__ = superclass;
Toggle.prototype = Object.create( superclass && superclass.prototype );
Toggle.prototype.constructor = Toggle;
return (this._button = this.id && document.querySelector("[for=\"".concat(this.id, "\"]"))) || this.previousElementSibling;
} // aria-haspopup triggers forms mode in JAWS, therefore store as custom attr
var staticAccessors = { defaultProps: { configurable: true } };
staticAccessors.defaultProps.get = function () { return { open: null, popup: null, onToggle: null, onToggleSelect: null } };
}, {
key: "popup",
get: function get() {
return this.getAttribute('popup') === 'true' || this.getAttribute('popup') || this.hasAttribute('popup');
},
set: function set(val) {
this[val === false ? 'removeAttribute' : 'setAttribute']('popup', val);
} // Must set attribute for IE11
Toggle.prototype.update = function update () {
toggle(this.el.firstElementChild, {
popup: this.props.popup,
open: this.props.open
});
};
Toggle.prototype.componentDidMount = function componentDidMount () {
this.update();
this.el.addEventListener('toggle', this.onToggle);
this.el.addEventListener('toggle.select', this.onToggleSelect);
};
Toggle.prototype.componentDidUpdate = function componentDidUpdate () { this.update(); };
Toggle.prototype.componentWillUnmount = function componentWillUnmount () {
this.el.removeEventListener('toggle', this.onToggle);
this.el.removeEventListener('toggle.select', this.onToggleSelect);
};
Toggle.prototype.onToggle = function onToggle (event) {
this.props.onToggle && this.props.onToggle(event);
};
Toggle.prototype.onToggleSelect = function onToggleSelect (event) {
this.props.onToggleSelect && this.props.onToggleSelect(event);
};
Toggle.prototype.render = function render () {
var this$1 = this;
}, {
key: "hidden",
get: function get() {
return this.hasAttribute('hidden');
},
set: function set(val) {
this.toggleAttribute('hidden', val);
} // Sets this.button aria-label, so visible button text can be augmentet with intension of button
// Example: Button text: "01.02.2019", aria-label: "01.02.2019, Choose date"
// Does not updates aria-label if not allready set to something else than this.popup
return React.createElement('div', exclude(this.props, Toggle.defaultProps, { ref: function (el) { return (this$1.el = el); } }),
React.Children.map(this.props.children, function (child, adjacent) {
if (adjacent === 0) {
return React.cloneElement(child, {
'aria-expanded': String(Boolean(this$1.props.open))
})
}
if (adjacent === 1) { return React.cloneElement(child, { 'hidden': !this$1.props.open }) }
return child
})
)
};
}, {
key: "value",
get: function get() {
return this.button.value || this.button.textContent;
},
set: function set() {
var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
if (!this.button || !this.popup.length) return;
var button = this.button;
var popup = (button.getAttribute('aria-label') || ",".concat(this.popup)).split(',')[1];
var label = data.textContent || data || ''; // data can be Element, Object or String
Object.defineProperties( Toggle, staticAccessors );
if (popup === this.popup) {
button.value = data.value || label;
button[data.innerHTML ? 'innerHTML' : 'textContent'] = data.innerHTML || label;
button.setAttribute('aria-label', "".concat(button.textContent, ",").concat(this.popup));
}
}
}], [{
key: "observedAttributes",
get: function get() {
return ['hidden'];
}
}]);
return Toggle;
}(React.Component));
return CoreToggle;
}(_wrapNativeSuper(HTMLElement));
Toggle.propTypes = {
open: PropTypes.bool,
popup: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
onToggle: PropTypes.func
};
var coreToggle = elementToReact(CoreToggle, 'toggle', 'toggle.select');
module.exports = Toggle;
module.exports = coreToggle;

@@ -5,3 +5,3 @@ {

"author": "NRK <opensource@nrk.no> (https://www.nrk.no/)",
"version": "2.2.3",
"version": "3.0.0",
"license": "MIT",

@@ -12,6 +12,3 @@ "main": "core-toggle.cjs.js",

"url": "git+https://github.com/nrkno/core-components.git"
},
"dependencies": {
"prop-types": "15.7.2"
}
}
# Core Toggle
> `@nrk/core-toggle` simply makes a `<button>` toggle the visibility of next element sibling. Toggles can be nested and easily extended with custom animations or behavior through the [toggle event](#events). It has two modes:
> `@nrk/core-toggle` makes a `<button>` toggle the visibility of next element sibling. Toggles can be nested and easily extended with custom animations or behavior through the [toggle event](#events).
<!-- <script src="https://unpkg.com/preact"></script>
<script src="https://unpkg.com/preact-compat"></script>
<script>
window.React = preactCompat
window.ReactDOM = preactCompat
</script> -->
<!--demo
<script src="https://unpkg.com/@webcomponents/custom-elements"></script>
<script src="core-toggle/core-toggle.min.js"></script>
<script src="core-toggle/core-toggle.jsx.js"></script>
<style>core-toggle:not([hidden]){display:block}</style>
demo-->
## Installation
## Example
```bash
npm install @nrk/core-toggle --save-exact
```
```js
import coreToggle from '@nrk/core-toggle' // Vanilla JS
import CoreToggle from '@nrk/core-toggle/jsx' // React/Preact JSX
```
## Demo
```html
<!--demo-->
<button class="my-popup">Popup VanillaJS</button>
<ul class="my-dropdown" hidden>
<li><a>Link</a></li>
<li>
<button class="my-popup">Can also be nested</button>
<ul class="my-dropdown" hidden>
<li><a>Sub-link</a></li>
<li><input type="text" autofocus aria-label="Skriv her"></li>
</ul>
</li>
</ul>
<script>
coreToggle('.my-popup', { popup: 'Example picker' })
</script>
<button>Popup VanillaJS</button>
<core-toggle class="my-dropdown" popup hidden>
<ul>
<li><a>Link</a></li>
<li>
<button>Can also be nested</button>
<core-toggle class="my-dropdown" popup hidden>
<ul>
<li><a>Sub-link</a></li>
<li><input type="text" autofocus aria-label="Skriv her"></li>
</ul>
</core-toggle>
</li>
</ul>
</core-toggle>
```

@@ -46,19 +43,37 @@

<script type="text/jsx">
ReactDOM.render(<CoreToggle popup='Example picker'>
ReactDOM.render(<>
<button>Popup JSX</button>
<ul className='my-dropdown'>
<li><a href='#'>Link</a></li>
<li>
<CoreToggle popup='Example picker'>
<CoreToggle className='my-dropdown' hidden popup onToggleSelect={console.warn}>
<ul>
<li><button>Select</button></li>
<li><a href='#'>Link</a></li>
<li>
<button>Can also be nested</button>
<ul className='my-dropdown'>
<li><a href='#'>Sub-link</a></li>
</ul>
</CoreToggle>
</li>
</ul>
</CoreToggle>, document.getElementById('jsx-toggle-popup'))
<CoreToggle className='my-dropdown' hidden popup>
<ul>
<li><a href='#'>Sub-link</a></li>
</ul>
</CoreToggle>
</li>
</ul>
</CoreToggle>
</>, document.getElementById('jsx-toggle-popup'))
</script>
```
## Installation
Using NPM provides own element namespace and extensibility.
Recommended:
```bash
npm install @nrk/core-toggle # Using NPM
```
Using static registers the custom element with default name automatically:
```html
<script src="https://static.nrk.no/core-components/major/5/core-toggle/core-toggle.min.js"></script> <!-- Using static -->
```
## Usage

@@ -69,14 +84,25 @@

```html
<button class="my-toggle">Toggle VanillaJS</button> <!-- must be <button> -->
<div hidden>Content</div> <!-- use hidden to prevent flash of unstyled content -->
<button>Toggle VanillaJS</button> <!-- Must be <button> placed directly before <core-toggle> or use id + for attributes -->
<core-toggle
hidden <!-- Set hidden attribute to prevent FOUC -->
popup="{Boolean|String}"> <!-- Optional. Defaults to false. Enable or disable if clicking outside toggle should close it. Provide a string to control the aria-label text on the toggle -->
<div>Content</div>
</core-toggle>
```
```js
import coreToggle from '@nrk/core-toggle'
import CoreToggle from '@nrk/core-toggle' // Using NPM
window.customElements.define('core-toggle', CoreToggle) // Using NPM. Replace 'core-toggle' with 'my-toggle' to namespace
coreToggle(String|Element|Elements, { // Accepts a selector string, NodeList, Element or array of Elements
open: Boolean // Optional. Defaults to aria-expanded or false. Set to force open state.
popup: Boolean|String // Optional. Defaults to false. Enable or disable if clicking outside toggle should close it. Provide a string to control the aria-label text on the toggle.
value: String // Optional. Defaults to button.innerHTML. Sets innerHTML of the button and safely updates aria-label for screen readers.
})
const myToggle = document.querySelector('core-toggle')
// Getters
myToggle.button // Get toggle button element
myToggle.popup // Get popup value
myToggle.hidden // Get hidden value
myToggle.value // Get toggle button text
// Setters
myToggle.popup = true // Enable or disable if clicking outside toggle should close it. Provide a string to control the aria-label text on the toggle
myToggle.hidden = true // Set hidden attribute
myToggle.value = 'Velg' // Sets innerHTML of the button and safely updates aria-label for screen readers. Defaults to button.innerHTML
```

@@ -86,11 +112,11 @@

```jsx
```js
import CoreToggle from '@nrk/core-toggle/jsx'
// All props are optional, and defaults are shown below
// Props like className, style, etc. will be applied as actual attributes
// <CoreToggle> will handle state itself unless you call event.preventDefault() in onToggle
<CoreToggle open={false} popup={false} onToggle={(event) => {}}>
<button>Use with JSX</button> // First element must result in a <button>-tag. Accepts both elements and components
<CoreToggle
hidden // Set hidden attribute to prevent FOUC
popup={Boolean|String} // Optional. Defaults to false. Enable or disable if clicking outside toggle should close it. Provide a string to control the aria-label text on the toggle
onToggle={Function} // Optional. Toggle event listener. See event 'toggle'
onToggleSelect={Function}> // Optional. Toggle select event listener. See event 'toggle.select'
<button>Use with JSX</button> // First element must result in a <button>. Accepts both elements and components
<div>Content</div> // Next element will be toggled. Accepts both elements and components

@@ -102,11 +128,11 @@ </CoreToggle>

### With aria-controls
### With for
Putting the toggle button directly before the content is highly recommended, as this fulfills all accessibility requirements by default. There might be scenarios though, where styling makes this DOM structure impractical. In such cases, give the toggle button an `aria-controls` attribute, and the content an `id` with corresponding value. Make sure there is no text between the button and toggle content, as this will break the experience for screen reader users:
Putting the toggle button directly before the content is highly recommended, as this fulfills all accessibility requirements by default. There might be scenarios though, where styling makes this DOM structure impractical. In such cases, give the `<button>` a `for` attribute, and the `core-toggle` a `id` with corresponding value. Make sure there is no text between the button and toggle content, as this will break the experience for screen reader users:
```html
<div>
<button class="my-toggle" aria-controls="content">Toggle VanillaJS</button>
<button for="my-toggle">Toggle VanillaJS</button>
</div>
<div id="content" hidden>Content</div>
<core-toggle id="my-toggle" hidden>Content</core-toggle>
```

@@ -116,7 +142,4 @@

If you have form elements inside a `@nrk/core-toggle`, you can optionally add a `autofocus` attribute to the most prominent form element. This helps the user navigate quickly when toggle is opened.
If you have form elements inside a `core-toggle`, you can optionally add a `autofocus` attribute to the most prominent form element. This helps the user navigate quickly when toggle is opened.
## Events

@@ -126,3 +149,3 @@

Before a `@nrk/core-toggle` changes open state, a [toggle event](https://www.w3schools.com/jsref/event_ontoggle.asp) is fired (both for VanillaJS and React/Preact components). The toggle event is cancelable, meaning you can use [`event.preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault) to cancel toggling. The event also [bubbles](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture), and can therefore be detected both from the button element itself, or any parent element (read [event delegation](https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation)):
Fired after open state changes:

@@ -132,6 +155,3 @@

document.addEventListener('toggle', (event) => {
event.target // The button element triggering toggle event
event.detail.relatedTarget // The content element controlled by button
event.detail.isOpen // The current toggle state (before toggle event has run)
event.detail.willOpen // The wanted toggle state
event.target // The toggle element
})

@@ -142,3 +162,3 @@ ```

The `toggle.select` event is fired whenever an item is selected inside a toggle with the `popup` option enabled.
Fired whenever an `<a>` or `<button>` element is selected inside a toggle with the `popup` option enabled.
Useful for setting the value of the toggle button with the selected value.

@@ -149,6 +169,5 @@

document.addEventListener('toggle.select', (event) => {
event.target // The buttom element triggering the event
event.detail.relatedTarget // The content element controlled by button
event.detail.currentTarget // The item element selected
event.detail.value // The selected item's value
event.target // The toggle element
event.detail // The selected element
event.target.value = event.detail // Example: set value of toggle to selected element
})

@@ -160,8 +179,8 @@ ```

All styling in documentation is example only. Both the `<button>` and content element receive attributes reflecting the current toggle state:
**Note:** `core-toggle` is `display: inline` by default. Change this by for instance setting `core-tabs:not([hidden]) { display: block | flex | grid }` or similar in your app. Not needed when `position` or `float` is used. All styling in documentation is example only. Both the `<button>` and `core-toggle` element receive attributes reflecting the current toggle state:
```css
.my-toggle {} /* Target button in any state */
.my-toggle[aria-expanded="true"] {} /* Target only open button */
.my-toggle[aria-expanded="false"] {} /* Target only closed button */
.my-button {} /* Target button in any state */
.my-button[aria-expanded="true"] {} /* Target only open button */
.my-button[aria-expanded="false"] {} /* Target only closed button */

@@ -173,3 +192,3 @@ .my-toggle-content {} /* Target content in any state */

## Demo: Expand
## Example: Expand

@@ -180,7 +199,4 @@ Content is only toggled when clicking the button. Great for accordions and expand/collapse panels.

<!--demo-->
<button class="my-toggle">Toggle VanillaJS</button> <!-- must be <button> -->
<div hidden>Content</div> <!-- hidden prevents flash of unstyled content -->
<script>
coreToggle('.my-toggle') // Optionally pass {open: true|false} as second argument to open/close
</script>
<button>Toggle VanillaJS</button> <!-- must be <button> -->
<core-toggle hidden>Content</core-toggle> <!-- hidden prevents flash of unstyled content -->
```

@@ -191,6 +207,6 @@ ```html

<script type="text/jsx">
ReactDOM.render(<CoreToggle popup={false} open={false} onToggle={function(){}}>
ReactDOM.render(<>
<button>Toggle JSX</button>
<div>Content</div>
</CoreToggle>, document.getElementById('jsx-toggle-default'))
<CoreToggle hidden onToggle={console.log}>Content</CoreToggle>
</>, document.getElementById('jsx-toggle-default'))
</script>

@@ -200,3 +216,3 @@ ```

## Demo: Select
## Example: Select

@@ -208,14 +224,17 @@ Listen to the `toggle.select` event and update the button's value from the selected item

<!--demo-->
<button class="my-popup-value">Select number</button>
<ul class="my-dropdown" hidden>
<li><button>One</button></li>
<li><button>Two</button></li>
<li><button>Three</button></li>
</ul>
<button>Episode 1</button>
<core-toggle class="my-select my-dropdown" hidden popup="Choose episode">
<ul>
<li><button>Episode 1</button></li>
<li><button>Episode 2</button></li>
<li><button>Episode 3</button></li>
</ul>
</core-toggle>
<script>
document.addEventListener('toggle.select', (event) => {
if (event.target.className !== 'my-popup-value') return
coreToggle(event.target, { value: event.detail.value, open: false })
if (!event.target.classList.contains('my-select')) return
event.target.value = event.detail
event.target.hidden = true
event.target.button.focus()
})
coreToggle('.my-popup-value', { popup: 'Number picker'})
</script>

@@ -235,13 +254,16 @@ ```

onSelect (event) {
this.setState({ value: event.detail.value })
event.target.hidden = true
this.setState({ value: event.detail.textContent })
}
render () {
return <CoreToggle popup='Example picker' open={false} onToggleSelect={this.onSelect}>
return <>
<button>{this.state.value}</button>
<ul className='my-dropdown'>
<li><button>One</button></li>
<li><button>Two</button></li>
<li><button>Three</button></li>
</ul>
</CoreToggle>
<CoreToggle className='my-dropdown' popup='Example picker' hidden onToggleSelect={this.onSelect}>
<ul>
<li><button>One</button></li>
<li><button>Two</button></li>
<li><button>Three</button></li>
</ul>
</CoreToggle>
</>
}

@@ -253,4 +275,2 @@ }

## FAQ

@@ -257,0 +277,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc