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

@manifoldco/react-select-zero

Package Overview
Dependencies
Maintainers
20
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@manifoldco/react-select-zero - npm Package Compare versions

Comparing version 0.0.1-0 to 0.0.1-1

685

dist-node/index.js

@@ -10,36 +10,2 @@ 'use strict';

function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {

@@ -81,27 +47,2 @@ if (source == null) return {};

function elId(name, component) {
return `rsz-${name}-${component}`;
}
function optionId(name, option) {
return elId(name, `option-${option}`);
}
var Action;
(function (Action) {
Action["APPEND"] = "APPEND";
Action["ADD_MULTI_CLICK"] = "ADD_MULTI_CLICK";
Action["ADD_MULTI_KEYBOARD"] = "ADD_MULTI_KEYBOARD";
Action["ADD_SINGLE_CLICK"] = "ADD_SINGLE_CLICK";
Action["ADD_SINGLE_KEYBOARD"] = "ADD_SINGLE_KEYBOARD";
Action["DROPDOWN_CLOSE"] = "DROPDOWN_CLOSE";
Action["DROPDOWN_OPEN"] = "DROPDOWN_OPEN";
Action["DROPDOWN_TOGGLE"] = "DROPDOWN_TOGGLE";
Action["INIT"] = "INIT";
Action["MOVE_NEXT"] = "MOVE_NEXT";
Action["MOVE_PREV"] = "MOVE_PREV";
Action["MOVE_TO_END"] = "MOVE_TO_END";
Action["MOVE_TO_START"] = "MOVE_TO_START";
Action["SEARCH"] = "SEARCH";
})(Action || (Action = {}));
var KEY;

@@ -118,289 +59,2 @@

const initialState = {
activeDescendant: '0',
created: [],
isOpen: false,
search: '',
selected: [],
visibleOptions: []
};
function reducer(state, action) {
switch (action.type) {
// Add or remove an option from mouse click
case Action.ADD_MULTI_CLICK:
{
const isNew = action.options.indexOf(action.option) === -1;
const total = state.selected.length + state.created.length; // if new
if (isNew) {
const created = [...state.created];
const index = state.created.indexOf(action.option);
if (index !== -1) {
created.splice(index, 1); // if existing, remove
} else if (total < action.max) {
// if new, add unless we’re at max
created.push(action.option);
}
return _objectSpread({}, state, {
created
});
} // if existing
const selected = [...state.selected];
const index = state.selected.indexOf(action.option);
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
} else if (total < action.max) {
selected.push(action.option); // if new, add unless we’re at max
}
return _objectSpread({}, state, {
selected: action.options.filter(order => selected.indexOf(order) !== -1)
});
}
// Add or remove an option from keyboard
case Action.ADD_MULTI_KEYBOARD:
{
const match = state.activeDescendant.match(/\d*$/);
if (!match) {
return state;
}
const total = state.selected.length + state.created.length;
const number = parseInt(match[0], 10); // if new
if (number > action.options.length - 1) {
const created = [...state.created];
const option = state.search;
const index = state.created.indexOf(option);
if (index === -1) {
created.splice(index, 1); // if existing, remove
} else if (total < action.max) {
// if new, add unless we’re at max
created.push(option);
}
return _objectSpread({}, state, {
created
});
} // if existing
const option = action.options[number];
const selected = [...state.selected];
const index = state.selected.indexOf(option);
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
} else if (total < action.max) {
selected.push(option); // if new, add unless we’re at max
}
return _objectSpread({}, state, {
selected: action.options.filter(order => selected.indexOf(order) !== -1)
});
}
// Set the option from mouse click
case Action.ADD_SINGLE_CLICK:
{
const newState = _objectSpread({}, state, {
isOpen: false,
search: '',
visibleOptions: action.options
});
if (action.options.indexOf(action.option) === -1) {
return _objectSpread({}, newState, {
created: [action.option]
}); // if new
}
return _objectSpread({}, newState, {
selected: [action.option]
}); // if existing
}
// Set the option from keyboard
case Action.ADD_SINGLE_KEYBOARD:
{
const match = state.activeDescendant.match(/\d*$/);
if (!match) {
return state;
}
const number = parseInt(match[0], 10);
const newState = _objectSpread({}, state, {
isOpen: false,
visibleOptions: action.options
});
if (number > action.options.length - 1) {
return _objectSpread({}, newState, {
created: [state.search]
}); // if new
}
return _objectSpread({}, newState, {
selected: [action.options[number]]
}); // if existing
}
// Close dropdown
case Action.DROPDOWN_CLOSE:
{
if (state.isOpen === false) {
return state; // prevent other state from reseting
}
const activeDescendant = optionId(action.name, 0);
return _objectSpread({}, state, {
activeDescendant,
isOpen: false,
search: '',
visibleOptions: action.options
});
}
// Open dropdown
case Action.DROPDOWN_OPEN:
{
if (state.isOpen === true) {
return state; // prevent other state from reseting
}
const activeDescendant = optionId(action.name, 0);
return _objectSpread({}, state, {
activeDescendant,
isOpen: true,
search: '',
visibleOptions: action.options
});
}
// Invert dropdown state
case Action.DROPDOWN_TOGGLE:
{
const nextOpen = !state.isOpen;
const activeDescendant = optionId(action.name, 0);
return _objectSpread({}, state, {
activeDescendant,
isOpen: nextOpen,
search: '',
visibleOptions: action.options
});
}
// Move to start / end
case Action.MOVE_TO_END:
case Action.MOVE_TO_START:
{
const index = action.type === Action.MOVE_TO_START ? action.options.indexOf(state.visibleOptions[0]) : action.options.indexOf(state.visibleOptions[state.visibleOptions.length - 1]);
const activeDescendant = action.allowCreate === true ? optionId(action.name, action.options.length) : optionId(action.name, index);
if (action.wrapper) {
const el = action.wrapper.querySelector(`#${activeDescendant}`);
if (el) {
el.scrollIntoView(false);
}
}
return _objectSpread({}, state, {
activeDescendant
});
}
// Initialize dropdown & set default state (while still letting props be mutable)
case Action.INIT:
{
// Users may specify created options within defaults, if they’re missing from the options array
const defaults = action.defaults || [];
const selected = action.options.filter(option => defaults.indexOf(option) !== -1);
const created = defaults.filter(option => action.options.indexOf(option) === -1);
return _objectSpread({}, state, {
created,
selected,
visibleOptions: action.options
});
}
// Move up or down with keyboard
case Action.MOVE_NEXT:
case Action.MOVE_PREV:
{
const match = state.activeDescendant.match(/\d*$/); // get number at end of ID
if (!match) {
return state;
}
const index = parseInt(match[0], 10);
const selected = action.options[index];
const visibleIndex = state.visibleOptions.indexOf(selected);
const options = [...action.options];
if (action.allowCreate === true) {
options.push(state.search); // if allowCreate, allow keyboard to highlight newly-created item
}
let next;
if (action.type === Action.MOVE_PREV) {
const prevVisible = options.indexOf(state.visibleOptions[visibleIndex - 1]);
const lastVisible = options.indexOf(state.visibleOptions[state.visibleOptions.length - 1]);
next = prevVisible !== -1 ? prevVisible : lastVisible;
} else {
const nextVisible = options.indexOf(state.visibleOptions[visibleIndex + 1]);
const firstVisible = options.indexOf(state.visibleOptions[0]);
next = nextVisible !== -1 ? nextVisible : firstVisible;
}
const activeDescendant = optionId(action.name, next);
if (action.wrapper) {
const el = action.wrapper.querySelector(`#${activeDescendant}`);
if (el) {
el.scrollIntoView(false);
}
}
return _objectSpread({}, state, {
activeDescendant
});
}
// Filter results
case Action.SEARCH:
{
const visibleOptions = action.options.filter(option => new RegExp(action.search, 'i').test(option));
const options = [...action.options];
if (action.allowCreate === true) {
options.push(action.search);
}
return _objectSpread({}, state, {
activeDescendant: optionId(action.name, options.indexOf(visibleOptions[0] || state.search)),
search: action.search,
visibleOptions
});
}
default:
return state;
}
}
const SelectZero = (_ref) => {

@@ -410,5 +64,5 @@ let {

className = 'rsz',
defaultValue = [],
max = Infinity,
multi = false,
name,
noSearch = false,

@@ -418,176 +72,140 @@ onChange,

placeholder = 'Select',
name
value = []
} = _ref,
rest = _objectWithoutProperties(_ref, ["allowCreate", "className", "defaultValue", "max", "multi", "noSearch", "onChange", "options", "placeholder", "name"]);
rest = _objectWithoutProperties(_ref, ["allowCreate", "className", "max", "multi", "name", "noSearch", "onChange", "options", "placeholder", "value"]);
const [isInitialized, setIsInitialized] = React.useState(false);
const [searchListener, setSearchListener] = React.useState();
const [triggerListener, setTriggerListener] = React.useState();
// state
const listRef = React.useRef(null);
const searchRef = React.useRef(null);
const triggerRef = React.useRef(null);
const [state, dispatch] = React.useReducer(reducer, initialState);
const [activeDescendantIndex, setActiveDescendantIndex] = React.useState(0); // Active descendant. Numbers are easier to manipulate than element IDs.
function onClick(option) {
if (multi === true) {
dispatch({
max,
name,
option,
options,
type: Action.ADD_MULTI_CLICK
});
} else {
dispatch({
name,
option,
options,
type: Action.ADD_SINGLE_CLICK
});
const [search, setSearch] = React.useState('');
const [isOpen, setIsOpen] = React.useState(false); // computed
const visibleIndices = [];
const visibleOptions = [];
options.forEach((option, index) => {
if (new RegExp(search, 'i').test(option)) {
// filter results in one pass
visibleOptions.push(option);
visibleIndices.push(index);
}
} // effect 1: user callback
});
const shouldDisplaySearch = noSearch !== true && options.length > 4;
const shouldDisplayCreate = allowCreate === true && search.length > 0 && options.findIndex(option => option === search) === -1;
const isMaxed = value.length === max; // methods
function elId(component) {
return `rsz-${name}-${component}`;
}
React.useEffect(() => {
if (typeof onChange === 'function') {
onChange(state.selected, state.created);
}
}, [state.selected, state.created]); // eslint-disable-line react-hooks/exhaustive-deps
// effect 2: initialization
function optionId(option) {
return elId(`option-${option}`);
}
React.useEffect(() => {
if (!isInitialized) {
dispatch({
defaults: defaultValue,
options,
type: Action.INIT
});
setIsInitialized(true);
}
}, [defaultValue, options, isInitialized]); // effect 3: maintain focus
function scrollTo(wrapper, selector) {
if (wrapper) {
const el = wrapper.querySelector(selector);
React.useEffect(() => {
if (state.isOpen) {
if (searchRef.current) {
searchRef.current.focus();
} else if (triggerRef.current) {
triggerRef.current.focus();
if (el) {
el.scrollIntoView(false);
}
} else if (!state.isOpen && triggerRef.current) {
triggerRef.current.focus();
}
}, [state.activeDescendant, state.isOpen]); // effect 4: navigation listener
}
React.useEffect(() => {
function onKeydown(evt) {
// eslint-disable-next-line default-case
switch (evt.key) {
case KEY.ENTER:
{
evt.preventDefault();
dispatch({
max,
options,
type: multi === true ? Action.ADD_MULTI_KEYBOARD : Action.ADD_SINGLE_KEYBOARD
});
break;
}
// move down
function addItem(option) {
// multi
if (multi === true) {
let selected = [...value];
const index = selected.indexOf(option);
case KEY.DOWN:
{
evt.preventDefault();
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_NEXT,
wrapper: listRef.current
});
break;
}
// move to end
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
} else if (selected.length < max) {
selected.push(option); // if new, add unless we’re at max
}
case KEY.END:
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_TO_END,
wrapper: listRef.current
});
if (options.indexOf(option) !== -1) {
selected = options.filter(order => selected.indexOf(order) !== -1); // if existing option, keep original order
}
onChange(selected);
return;
} // single
onChange([option]);
setIsOpen(false);
}
function onKeyDown(evt) {
const first = visibleIndices[0];
const last = visibleIndices[visibleIndices.length - 1];
switch (evt.key) {
// select active item
case KEY.ENTER:
{
evt.preventDefault();
addItem(options[activeDescendantIndex]);
break;
// move to start
}
// move down / up
case KEY.HOME:
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_TO_START,
wrapper: listRef.current
});
case KEY.DOWN:
case KEY.UP:
{
evt.preventDefault();
const sum = evt.key === KEY.UP ? -1 : 1;
const fallback = evt.key === KEY.UP ? last : first; // if at beginning, loop around to end, and vice-versa
const nextIndex = visibleIndices.indexOf(activeDescendantIndex) + sum;
const next = visibleIndices[nextIndex] !== undefined ? visibleIndices[nextIndex] : fallback;
setActiveDescendantIndex(next); // set to last index if at beginning of list
scrollTo(listRef.current, `#${optionId(next)}`);
break;
// move up
}
// move to start / end
case KEY.ESC:
dispatch({
name,
options,
type: Action.DROPDOWN_CLOSE
});
case KEY.END:
case KEY.HOME:
{
const next = evt.key === KEY.HOME ? first : last;
setActiveDescendantIndex(next);
scrollTo(listRef.current, `#${optionId(next)}`);
break;
// close
}
// close
case KEY.UP:
{
evt.preventDefault();
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_PREV,
wrapper: listRef.current
});
break;
}
}
}
case KEY.ESC:
setIsOpen(false);
break;
if (!searchListener && searchRef.current) {
searchRef.current.addEventListener('keydown', onKeydown);
setSearchListener(true);
default:
if (noSearch) {
evt.preventDefault();
}
break;
}
}, [searchListener]); // eslint-disable-line react-hooks/exhaustive-deps
// effect 5: trigger listener
} // effect 1. maintain focus
React.useEffect(() => {
function onKeydown(e) {
if (e.key === KEY.DOWN) {
e.preventDefault();
dispatch({
name,
options,
type: Action.DROPDOWN_OPEN
});
}
if (searchRef.current && isOpen) {
searchRef.current.focus();
} else if (triggerRef.current && !isOpen) {
triggerRef.current.focus();
}
}); // effect 2. active descendant
if (triggerRef.current && !triggerListener) {
triggerRef.current.addEventListener('keyup', onKeydown);
setTriggerListener(true);
React.useEffect(() => {
if (visibleIndices.indexOf(activeDescendantIndex) === -1) {
setActiveDescendantIndex(visibleIndices[0]);
}
}, [name, options, triggerListener]);
const shouldDisplaySearch = noSearch !== true && options.length > 4;
const shouldDisplayCreate = allowCreate !== false && state.search.length > 0 && state.visibleOptions.findIndex(option => option === state.search) === -1; // maintain focus on re-render
if (searchRef.current && state.isOpen) {
searchRef.current.focus();
} else if (triggerRef.current && !state.isOpen) {
triggerRef.current.focus();
}
const selection = [...state.selected, ...state.created];
}, [activeDescendantIndex, visibleIndices]);
return React__default.createElement("div", Object.assign({
"aria-expanded": state.isOpen || undefined,
"aria-expanded": isOpen === true,
"aria-multiselectable": multi === true || undefined,

@@ -599,18 +217,19 @@ className: className,

}, React__default.createElement("button", {
"aria-controls": elId(name, 'menu'),
"aria-controls": elId('menu'),
"aria-haspopup": "listbox",
className: "rsz__trigger-button",
onClick: () => setIsOpen(true),
onKeyDown: e => {
if (e.key === KEY.DOWN) {
setIsOpen(true);
}
},
ref: triggerRef,
type: "button",
onClick: () => dispatch({
name,
options,
type: Action.DROPDOWN_TOGGLE
})
type: "button"
}, multi === true ? `Select options for ${name}` : `Select an option for ${name}`, React__default.createElement("span", {
"aria-hidden": true,
className: "rsz__arrow"
}, "\u2193")), selection.length > 0 ? React__default.createElement("ul", {
}, "\u2193")), value.length > 0 ? React__default.createElement("ul", {
className: "rsz__selection"
}, selection.map(option => React__default.createElement("li", {
}, value.map(option => React__default.createElement("li", {
key: option,

@@ -621,12 +240,6 @@ className: "rsz__selected"

}, option), React__default.createElement("button", {
"aria-label": `remove ${option}`,
className: "rsz__selected-action",
"aria-hidden": true,
type: "button",
onClick: () => dispatch({
max,
name,
option,
options,
type: Action.ADD_MULTI_CLICK
})
onClick: () => addItem(option),
type: "button"
}, "\u2715")))) : React__default.createElement("div", {

@@ -637,8 +250,5 @@ className: "rsz__selection rsz__placeholder"

className: "rsz__overlay",
onClick: () => dispatch({
name,
options,
type: Action.DROPDOWN_CLOSE
})
onClick: () => setIsOpen(false)
}), React__default.createElement("menu", {
id: elId('menu'),
className: "rsz__dropdown-wrapper"

@@ -648,16 +258,11 @@ }, React__default.createElement("div", {

}, React__default.createElement("input", {
"aria-activedescendant": state.activeDescendant,
"aria-activedescendant": optionId(activeDescendantIndex),
"aria-hidden": shouldDisplaySearch === false || undefined,
"aria-label": "Filter options",
className: "rsz__search",
onChange: e => noSearch !== true && dispatch({
allowCreate,
name,
options,
search: e.target.value,
type: Action.SEARCH
}),
onChange: e => setSearch(e.target.value),
onKeyDown: onKeyDown,
ref: searchRef,
type: "search",
value: state.search
value: search
}), React__default.createElement("div", {

@@ -668,9 +273,7 @@ className: "rsz__search-icon"

ref: listRef
}, state.visibleOptions.length > 0 ? state.visibleOptions.map(option => {
const id = optionId(name, options.indexOf(option));
const isSelected = selection.indexOf(option) !== -1;
const isMaxxed = selection.length === max;
}, visibleOptions.map((option, index) => {
const isSelected = value.indexOf(option) !== -1;
let ariaSelected = isSelected;
if (isMaxxed) {
if (ariaSelected === false && isMaxed === true) {
ariaSelected = undefined;

@@ -684,21 +287,23 @@ }

"aria-selected": ariaSelected,
"data-highlighted": state.activeDescendant === id || undefined,
disabled: isMaxxed && !isSelected,
id: id,
onClick: () => onClick(option),
"data-highlighted": activeDescendantIndex === visibleIndices[index] || undefined,
disabled: isMaxed && !isSelected,
id: optionId(visibleIndices[index]),
onClick: () => addItem(option),
role: "option",
type: "button"
}, option));
}) : React__default.createElement("span", {
}), options.length > 0 && visibleOptions.length === 0 && React__default.createElement("span", {
className: "rsz__no-results"
}, "No results for \u201C", state.search, "\u201D"), shouldDisplayCreate && React__default.createElement("li", {
}, "No results for \u201C", search, "\u201D"), shouldDisplayCreate && React__default.createElement("li", {
className: "rsz__option"
}, React__default.createElement("button", {
className: "rsz__create",
id: optionId(name, options.length),
type: "button",
onClick: () => onClick(state.search)
"data-highlighted": activeDescendantIndex === options.length || undefined,
disabled: isMaxed,
id: optionId(options.length),
onClick: () => addItem(search),
type: "button"
}, "Create ", React__default.createElement("span", {
className: "rsz__search-term"
}, state.search))), allowCreate === true && state.search === '' && React__default.createElement("li", {
}, search))), allowCreate === true && search === '' && React__default.createElement("li", {
className: "rsz__option"

@@ -710,3 +315,3 @@ }, React__default.createElement("span", {

name: name,
value: selection.join(',')
value: value.join(',')
}));

@@ -716,3 +321,1 @@ };

exports.default = SelectZero;
exports.elId = elId;
exports.optionId = optionId;

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

"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var Action,KEY,React=require("react"),React__default=_interopDefault(React);function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},a=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(a=a.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),a.forEach(function(t){_defineProperty(e,t,n[t])})}return e}function _objectWithoutPropertiesLoose(e,t){if(null==e)return{};var n,a,o={},c=Object.keys(e);for(a=0;a<c.length;a++)n=c[a],t.indexOf(n)>=0||(o[n]=e[n]);return o}function _objectWithoutProperties(e,t){if(null==e)return{};var n,a,o=_objectWithoutPropertiesLoose(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(a=0;a<c.length;a++)n=c[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(o[n]=e[n])}return o}function elId(e,t){return`rsz-${e}-${t}`}function optionId(e,t){return elId(e,`option-${t}`)}!function(e){e.APPEND="APPEND",e.ADD_MULTI_CLICK="ADD_MULTI_CLICK",e.ADD_MULTI_KEYBOARD="ADD_MULTI_KEYBOARD",e.ADD_SINGLE_CLICK="ADD_SINGLE_CLICK",e.ADD_SINGLE_KEYBOARD="ADD_SINGLE_KEYBOARD",e.DROPDOWN_CLOSE="DROPDOWN_CLOSE",e.DROPDOWN_OPEN="DROPDOWN_OPEN",e.DROPDOWN_TOGGLE="DROPDOWN_TOGGLE",e.INIT="INIT",e.MOVE_NEXT="MOVE_NEXT",e.MOVE_PREV="MOVE_PREV",e.MOVE_TO_END="MOVE_TO_END",e.MOVE_TO_START="MOVE_TO_START",e.SEARCH="SEARCH"}(Action||(Action={})),function(e){e.DOWN="ArrowDown",e.END="End",e.ENTER="Enter",e.ESC="Escape",e.HOME="Home",e.UP="ArrowUp"}(KEY||(KEY={}));const initialState={activeDescendant:"0",created:[],isOpen:!1,search:"",selected:[],visibleOptions:[]};function reducer(e,t){switch(t.type){case Action.ADD_MULTI_CLICK:{const n=-1===t.options.indexOf(t.option),a=e.selected.length+e.created.length;if(n){const n=[...e.created],o=e.created.indexOf(t.option);return-1!==o?n.splice(o,1):a<t.max&&n.push(t.option),_objectSpread({},e,{created:n})}const o=[...e.selected],c=e.selected.indexOf(t.option);return-1!==c?o.splice(c,1):a<t.max&&o.push(t.option),_objectSpread({},e,{selected:t.options.filter(e=>-1!==o.indexOf(e))})}case Action.ADD_MULTI_KEYBOARD:{const n=e.activeDescendant.match(/\d*$/);if(!n)return e;const a=e.selected.length+e.created.length,o=parseInt(n[0],10);if(o>t.options.length-1){const n=[...e.created],o=e.search,c=e.created.indexOf(o);return-1===c?n.splice(c,1):a<t.max&&n.push(o),_objectSpread({},e,{created:n})}const c=t.options[o],r=[...e.selected],s=e.selected.indexOf(c);return-1!==s?r.splice(s,1):a<t.max&&r.push(c),_objectSpread({},e,{selected:t.options.filter(e=>-1!==r.indexOf(e))})}case Action.ADD_SINGLE_CLICK:{const n=_objectSpread({},e,{isOpen:!1,search:"",visibleOptions:t.options});return-1===t.options.indexOf(t.option)?_objectSpread({},n,{created:[t.option]}):_objectSpread({},n,{selected:[t.option]})}case Action.ADD_SINGLE_KEYBOARD:{const n=e.activeDescendant.match(/\d*$/);if(!n)return e;const a=parseInt(n[0],10),o=_objectSpread({},e,{isOpen:!1,visibleOptions:t.options});return a>t.options.length-1?_objectSpread({},o,{created:[e.search]}):_objectSpread({},o,{selected:[t.options[a]]})}case Action.DROPDOWN_CLOSE:if(!1===e.isOpen)return e;return _objectSpread({},e,{activeDescendant:optionId(t.name,0),isOpen:!1,search:"",visibleOptions:t.options});case Action.DROPDOWN_OPEN:if(!0===e.isOpen)return e;return _objectSpread({},e,{activeDescendant:optionId(t.name,0),isOpen:!0,search:"",visibleOptions:t.options});case Action.DROPDOWN_TOGGLE:{const n=!e.isOpen;return _objectSpread({},e,{activeDescendant:optionId(t.name,0),isOpen:n,search:"",visibleOptions:t.options})}case Action.MOVE_TO_END:case Action.MOVE_TO_START:{const n=t.type===Action.MOVE_TO_START?t.options.indexOf(e.visibleOptions[0]):t.options.indexOf(e.visibleOptions[e.visibleOptions.length-1]),a=!0===t.allowCreate?optionId(t.name,t.options.length):optionId(t.name,n);if(t.wrapper){const e=t.wrapper.querySelector(`#${a}`);e&&e.scrollIntoView(!1)}return _objectSpread({},e,{activeDescendant:a})}case Action.INIT:{const n=t.defaults||[],a=t.options.filter(e=>-1!==n.indexOf(e));return _objectSpread({},e,{created:n.filter(e=>-1===t.options.indexOf(e)),selected:a,visibleOptions:t.options})}case Action.MOVE_NEXT:case Action.MOVE_PREV:{const n=e.activeDescendant.match(/\d*$/);if(!n)return e;const a=parseInt(n[0],10),o=t.options[a],c=e.visibleOptions.indexOf(o),r=[...t.options];let s;if(!0===t.allowCreate&&r.push(e.search),t.type===Action.MOVE_PREV){const t=r.indexOf(e.visibleOptions[c-1]),n=r.indexOf(e.visibleOptions[e.visibleOptions.length-1]);s=-1!==t?t:n}else{const t=r.indexOf(e.visibleOptions[c+1]),n=r.indexOf(e.visibleOptions[0]);s=-1!==t?t:n}const i=optionId(t.name,s);if(t.wrapper){const e=t.wrapper.querySelector(`#${i}`);e&&e.scrollIntoView(!1)}return _objectSpread({},e,{activeDescendant:i})}case Action.SEARCH:{const n=t.options.filter(e=>new RegExp(t.search,"i").test(e)),a=[...t.options];return!0===t.allowCreate&&a.push(t.search),_objectSpread({},e,{activeDescendant:optionId(t.name,a.indexOf(n[0]||e.search)),search:t.search,visibleOptions:n})}default:return e}}const SelectZero=e=>{let{allowCreate:t=!1,className:n="rsz",defaultValue:a=[],max:o=1/0,multi:c=!1,noSearch:r=!1,onChange:s,options:i,placeholder:l="Select",name:p}=e,_=_objectWithoutProperties(e,["allowCreate","className","defaultValue","max","multi","noSearch","onChange","options","placeholder","name"]);const[d,u]=React.useState(!1),[O,f]=React.useState(),[E,D]=React.useState(),m=React.useRef(null),b=React.useRef(null),R=React.useRef(null),[h,A]=React.useReducer(reducer,initialState);function N(e){A(!0===c?{max:o,name:p,option:e,options:i,type:Action.ADD_MULTI_CLICK}:{name:p,option:e,options:i,type:Action.ADD_SINGLE_CLICK})}React.useEffect(()=>{"function"==typeof s&&s(h.selected,h.created)},[h.selected,h.created]),React.useEffect(()=>{d||(A({defaults:a,options:i,type:Action.INIT}),u(!0))},[a,i,d]),React.useEffect(()=>{h.isOpen?b.current?b.current.focus():R.current&&R.current.focus():!h.isOpen&&R.current&&R.current.focus()},[h.activeDescendant,h.isOpen]),React.useEffect(()=>{!O&&b.current&&(b.current.addEventListener("keydown",function(e){switch(e.key){case KEY.ENTER:e.preventDefault(),A({max:o,options:i,type:!0===c?Action.ADD_MULTI_KEYBOARD:Action.ADD_SINGLE_KEYBOARD});break;case KEY.DOWN:e.preventDefault(),A({allowCreate:t,name:p,options:i,type:Action.MOVE_NEXT,wrapper:m.current});break;case KEY.END:A({allowCreate:t,name:p,options:i,type:Action.MOVE_TO_END,wrapper:m.current});break;case KEY.HOME:A({allowCreate:t,name:p,options:i,type:Action.MOVE_TO_START,wrapper:m.current});break;case KEY.ESC:A({name:p,options:i,type:Action.DROPDOWN_CLOSE});break;case KEY.UP:e.preventDefault(),A({allowCreate:t,name:p,options:i,type:Action.MOVE_PREV,wrapper:m.current})}}),f(!0))},[O]),React.useEffect(()=>{R.current&&!E&&(R.current.addEventListener("keyup",function(e){e.key===KEY.DOWN&&(e.preventDefault(),A({name:p,options:i,type:Action.DROPDOWN_OPEN}))}),D(!0))},[p,i,E]);const v=!0!==r&&i.length>4,S=!1!==t&&h.search.length>0&&-1===h.visibleOptions.findIndex(e=>e===h.search);b.current&&h.isOpen?b.current.focus():R.current&&!h.isOpen&&R.current.focus();const I=[...h.selected,...h.created];return React__default.createElement("div",Object.assign({"aria-expanded":h.isOpen||void 0,"aria-multiselectable":!0===c||void 0,className:n,role:"listbox"},_),React__default.createElement("div",{className:"rsz__trigger"},React__default.createElement("button",{"aria-controls":elId(p,"menu"),"aria-haspopup":"listbox",className:"rsz__trigger-button",ref:R,type:"button",onClick:()=>A({name:p,options:i,type:Action.DROPDOWN_TOGGLE})},!0===c?`Select options for ${p}`:`Select an option for ${p}`,React__default.createElement("span",{"aria-hidden":!0,className:"rsz__arrow"},"↓")),I.length>0?React__default.createElement("ul",{className:"rsz__selection"},I.map(e=>React__default.createElement("li",{key:e,className:"rsz__selected"},React__default.createElement("span",{className:"rsz__selected-text"},e),React__default.createElement("button",{className:"rsz__selected-action","aria-hidden":!0,type:"button",onClick:()=>A({max:o,name:p,option:e,options:i,type:Action.ADD_MULTI_CLICK})},"✕")))):React__default.createElement("div",{className:"rsz__selection rsz__placeholder"},l)),React__default.createElement("div",{"aria-label":"Close dropdown",className:"rsz__overlay",onClick:()=>A({name:p,options:i,type:Action.DROPDOWN_CLOSE})}),React__default.createElement("menu",{className:"rsz__dropdown-wrapper"},React__default.createElement("div",{className:"rsz__dropdown"},React__default.createElement("input",{"aria-activedescendant":h.activeDescendant,"aria-hidden":!1===v||void 0,"aria-label":"Filter options",className:"rsz__search",onChange:e=>!0!==r&&A({allowCreate:t,name:p,options:i,search:e.target.value,type:Action.SEARCH}),ref:b,type:"search",value:h.search}),React__default.createElement("div",{className:"rsz__search-icon"}),React__default.createElement("ul",{className:"rsz__option-list",ref:m},h.visibleOptions.length>0?h.visibleOptions.map(e=>{const t=optionId(p,i.indexOf(e)),n=-1!==I.indexOf(e),a=I.length===o;let c=n;return a&&(c=void 0),React__default.createElement("li",{className:"rsz__option",key:e},React__default.createElement("button",{"aria-selected":c,"data-highlighted":h.activeDescendant===t||void 0,disabled:a&&!n,id:t,onClick:()=>N(e),role:"option",type:"button"},e))}):React__default.createElement("span",{className:"rsz__no-results"},"No results for “",h.search,"”"),S&&React__default.createElement("li",{className:"rsz__option"},React__default.createElement("button",{className:"rsz__create",id:optionId(p,i.length),type:"button",onClick:()=>N(h.search)},"Create ",React__default.createElement("span",{className:"rsz__search-term"},h.search))),!0===t&&""===h.search&&React__default.createElement("li",{className:"rsz__option"},React__default.createElement("span",{className:"rsz__create"},"Start typing to create an item"))))),React__default.createElement("input",{type:"hidden",name:p,value:I.join(",")}))};exports.default=SelectZero,exports.elId=elId,exports.optionId=optionId;
"use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var KEY,React=require("react"),React__default=_interopDefault(React);function _objectWithoutPropertiesLoose(e,t){if(null==e)return{};var a,r,l={},n=Object.keys(e);for(r=0;r<n.length;r++)a=n[r],t.indexOf(a)>=0||(l[a]=e[a]);return l}function _objectWithoutProperties(e,t){if(null==e)return{};var a,r,l=_objectWithoutPropertiesLoose(e,t);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);for(r=0;r<n.length;r++)a=n[r],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(l[a]=e[a])}return l}!function(e){e.DOWN="ArrowDown",e.END="End",e.ENTER="Enter",e.ESC="Escape",e.HOME="Home",e.UP="ArrowUp"}(KEY||(KEY={}));const SelectZero=e=>{let{allowCreate:t=!1,className:a="rsz",max:r=1/0,multi:l=!1,name:n,noSearch:c=!1,onChange:s,options:o,placeholder:i="Select",value:u=[]}=e,_=_objectWithoutProperties(e,["allowCreate","className","max","multi","name","noSearch","onChange","options","placeholder","value"]);const d=React.useRef(null),f=React.useRef(null),m=React.useRef(null),[p,E]=React.useState(0),[h,R]=React.useState(""),[b,N]=React.useState(!1),g=[],v=[];o.forEach((e,t)=>{new RegExp(h,"i").test(e)&&(v.push(e),g.push(t))});const y=!0!==c&&o.length>4,z=!0===t&&h.length>0&&-1===o.findIndex(e=>e===h),O=u.length===r;function x(e){return`rsz-${n}-${e}`}function k(e){return x(`option-${e}`)}function w(e,t){if(e){const a=e.querySelector(t);a&&a.scrollIntoView(!1)}}function S(e){if(!0===l){let t=[...u];const a=t.indexOf(e);return-1!==a?t.splice(a,1):t.length<r&&t.push(e),-1!==o.indexOf(e)&&(t=o.filter(e=>-1!==t.indexOf(e))),void s(t)}s([e]),N(!1)}return React.useEffect(()=>{f.current&&b?f.current.focus():m.current&&!b&&m.current.focus()}),React.useEffect(()=>{-1===g.indexOf(p)&&E(g[0])},[p,g]),React__default.createElement("div",Object.assign({"aria-expanded":!0===b,"aria-multiselectable":!0===l||void 0,className:a,role:"listbox"},_),React__default.createElement("div",{className:"rsz__trigger"},React__default.createElement("button",{"aria-controls":x("menu"),"aria-haspopup":"listbox",className:"rsz__trigger-button",onClick:()=>N(!0),onKeyDown:e=>{e.key===KEY.DOWN&&N(!0)},ref:m,type:"button"},!0===l?`Select options for ${n}`:`Select an option for ${n}`,React__default.createElement("span",{"aria-hidden":!0,className:"rsz__arrow"},"↓")),u.length>0?React__default.createElement("ul",{className:"rsz__selection"},u.map(e=>React__default.createElement("li",{key:e,className:"rsz__selected"},React__default.createElement("span",{className:"rsz__selected-text"},e),React__default.createElement("button",{"aria-label":`remove ${e}`,className:"rsz__selected-action",onClick:()=>S(e),type:"button"},"✕")))):React__default.createElement("div",{className:"rsz__selection rsz__placeholder"},i)),React__default.createElement("div",{"aria-label":"Close dropdown",className:"rsz__overlay",onClick:()=>N(!1)}),React__default.createElement("menu",{id:x("menu"),className:"rsz__dropdown-wrapper"},React__default.createElement("div",{className:"rsz__dropdown"},React__default.createElement("input",{"aria-activedescendant":k(p),"aria-hidden":!1===y||void 0,"aria-label":"Filter options",className:"rsz__search",onChange:e=>R(e.target.value),onKeyDown:function(e){const t=g[0],a=g[g.length-1];switch(e.key){case KEY.ENTER:e.preventDefault(),S(o[p]);break;case KEY.DOWN:case KEY.UP:{e.preventDefault();const r=e.key===KEY.UP?-1:1,l=e.key===KEY.UP?a:t,n=g.indexOf(p)+r,c=void 0!==g[n]?g[n]:l;E(c),w(d.current,`#${k(c)}`);break}case KEY.END:case KEY.HOME:{const r=e.key===KEY.HOME?t:a;E(r),w(d.current,`#${k(r)}`);break}case KEY.ESC:N(!1);break;default:c&&e.preventDefault()}},ref:f,type:"search",value:h}),React__default.createElement("div",{className:"rsz__search-icon"}),React__default.createElement("ul",{className:"rsz__option-list",ref:d},v.map((e,t)=>{const a=-1!==u.indexOf(e);let r=a;return!1===r&&!0===O&&(r=void 0),React__default.createElement("li",{className:"rsz__option",key:e},React__default.createElement("button",{"aria-selected":r,"data-highlighted":p===g[t]||void 0,disabled:O&&!a,id:k(g[t]),onClick:()=>S(e),role:"option",type:"button"},e))}),o.length>0&&0===v.length&&React__default.createElement("span",{className:"rsz__no-results"},"No results for “",h,"”"),z&&React__default.createElement("li",{className:"rsz__option"},React__default.createElement("button",{className:"rsz__create","data-highlighted":p===o.length||void 0,disabled:O,id:k(o.length),onClick:()=>S(h),type:"button"},"Create ",React__default.createElement("span",{className:"rsz__search-term"},h))),!0===t&&""===h&&React__default.createElement("li",{className:"rsz__option"},React__default.createElement("span",{className:"rsz__create"},"Start typing to create an item"))))),React__default.createElement("input",{type:"hidden",name:n,value:u.join(",")}))};exports.default=SelectZero;

@@ -1,26 +0,2 @@

// TODO: when searching, up/down doesn’t work
import React, { useState, useEffect, useRef, useReducer } from 'react';
export function elId(name, component) {
return `rsz-${name}-${component}`;
}
export function optionId(name, option) {
return elId(name, `option-${option}`);
}
var Action;
(function (Action) {
Action["APPEND"] = "APPEND";
Action["ADD_MULTI_CLICK"] = "ADD_MULTI_CLICK";
Action["ADD_MULTI_KEYBOARD"] = "ADD_MULTI_KEYBOARD";
Action["ADD_SINGLE_CLICK"] = "ADD_SINGLE_CLICK";
Action["ADD_SINGLE_KEYBOARD"] = "ADD_SINGLE_KEYBOARD";
Action["DROPDOWN_CLOSE"] = "DROPDOWN_CLOSE";
Action["DROPDOWN_OPEN"] = "DROPDOWN_OPEN";
Action["DROPDOWN_TOGGLE"] = "DROPDOWN_TOGGLE";
Action["INIT"] = "INIT";
Action["MOVE_NEXT"] = "MOVE_NEXT";
Action["MOVE_PREV"] = "MOVE_PREV";
Action["MOVE_TO_END"] = "MOVE_TO_END";
Action["MOVE_TO_START"] = "MOVE_TO_START";
Action["SEARCH"] = "SEARCH";
})(Action || (Action = {}));
import React, { useState, useRef, useEffect } from 'react';
var KEY;

@@ -35,392 +11,156 @@ (function (KEY) {

})(KEY || (KEY = {}));
const initialState = {
activeDescendant: '0',
created: [],
isOpen: false,
search: '',
selected: [],
visibleOptions: [],
};
function reducer(state, action) {
switch (action.type) {
// Add or remove an option from mouse click
case Action.ADD_MULTI_CLICK: {
const isNew = action.options.indexOf(action.option) === -1;
const total = state.selected.length + state.created.length;
// if new
if (isNew) {
const created = [...state.created];
const index = state.created.indexOf(action.option);
if (index !== -1) {
created.splice(index, 1); // if existing, remove
}
else if (total < action.max) {
// if new, add unless we’re at max
created.push(action.option);
}
return { ...state, created };
const SelectZero = ({ allowCreate = false, className = 'rsz', max = Infinity, multi = false, name, noSearch = false, onChange, options, placeholder = 'Select', value = [], ...rest }) => {
// state
const listRef = useRef(null);
const searchRef = useRef(null);
const triggerRef = useRef(null);
const [activeDescendantIndex, setActiveDescendantIndex] = useState(0); // Active descendant. Numbers are easier to manipulate than element IDs.
const [search, setSearch] = useState('');
const [isOpen, setIsOpen] = useState(false);
// computed
const visibleIndices = [];
const visibleOptions = [];
options.forEach((option, index) => {
if (new RegExp(search, 'i').test(option)) {
// filter results in one pass
visibleOptions.push(option);
visibleIndices.push(index);
}
});
const shouldDisplaySearch = noSearch !== true && options.length > 4;
const shouldDisplayCreate = allowCreate === true &&
search.length > 0 &&
options.findIndex(option => option === search) === -1;
const isMaxed = value.length === max;
// methods
function elId(component) {
return `rsz-${name}-${component}`;
}
function optionId(option) {
return elId(`option-${option}`);
}
function scrollTo(wrapper, selector) {
if (wrapper) {
const el = wrapper.querySelector(selector);
if (el) {
el.scrollIntoView(false);
}
// if existing
const selected = [...state.selected];
const index = state.selected.indexOf(action.option);
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
}
else if (total < action.max) {
selected.push(action.option); // if new, add unless we’re at max
}
return {
...state,
selected: action.options.filter(order => selected.indexOf(order) !== -1),
};
}
// Add or remove an option from keyboard
case Action.ADD_MULTI_KEYBOARD: {
const match = state.activeDescendant.match(/\d*$/);
if (!match) {
return state;
}
const total = state.selected.length + state.created.length;
const number = parseInt(match[0], 10);
// if new
if (number > action.options.length - 1) {
const created = [...state.created];
const option = state.search;
const index = state.created.indexOf(option);
if (index === -1) {
created.splice(index, 1); // if existing, remove
}
else if (total < action.max) {
// if new, add unless we’re at max
created.push(option);
}
return { ...state, created };
}
// if existing
const option = action.options[number];
const selected = [...state.selected];
const index = state.selected.indexOf(option);
}
function addItem(option) {
// multi
if (multi === true) {
let selected = [...value];
const index = selected.indexOf(option);
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
}
else if (total < action.max) {
else if (selected.length < max) {
selected.push(option); // if new, add unless we’re at max
}
return {
...state,
selected: action.options.filter(order => selected.indexOf(order) !== -1),
};
}
// Set the option from mouse click
case Action.ADD_SINGLE_CLICK: {
const newState = { ...state, isOpen: false, search: '', visibleOptions: action.options };
if (action.options.indexOf(action.option) === -1) {
return { ...newState, created: [action.option] }; // if new
if (options.indexOf(option) !== -1) {
selected = options.filter(order => selected.indexOf(order) !== -1); // if existing option, keep original order
}
return { ...newState, selected: [action.option] }; // if existing
onChange(selected);
return;
}
// Set the option from keyboard
case Action.ADD_SINGLE_KEYBOARD: {
const match = state.activeDescendant.match(/\d*$/);
if (!match) {
return state;
// single
onChange([option]);
setIsOpen(false);
}
function onKeyDown(evt) {
const first = visibleIndices[0];
const last = visibleIndices[visibleIndices.length - 1];
switch (evt.key) {
// select active item
case KEY.ENTER: {
evt.preventDefault();
addItem(options[activeDescendantIndex]);
break;
}
const number = parseInt(match[0], 10);
const newState = { ...state, isOpen: false, visibleOptions: action.options };
if (number > action.options.length - 1) {
return { ...newState, created: [state.search] }; // if new
// move down / up
case KEY.DOWN:
case KEY.UP: {
evt.preventDefault();
const sum = evt.key === KEY.UP ? -1 : 1;
const fallback = evt.key === KEY.UP ? last : first; // if at beginning, loop around to end, and vice-versa
const nextIndex = visibleIndices.indexOf(activeDescendantIndex) + sum;
const next = visibleIndices[nextIndex] !== undefined ? visibleIndices[nextIndex] : fallback;
setActiveDescendantIndex(next); // set to last index if at beginning of list
scrollTo(listRef.current, `#${optionId(next)}`);
break;
}
return { ...newState, selected: [action.options[number]] }; // if existing
}
// Close dropdown
case Action.DROPDOWN_CLOSE: {
if (state.isOpen === false) {
return state; // prevent other state from reseting
// move to start / end
case KEY.END:
case KEY.HOME: {
const next = evt.key === KEY.HOME ? first : last;
setActiveDescendantIndex(next);
scrollTo(listRef.current, `#${optionId(next)}`);
break;
}
const activeDescendant = optionId(action.name, 0);
return {
...state,
activeDescendant,
isOpen: false,
search: '',
visibleOptions: action.options,
};
}
// Open dropdown
case Action.DROPDOWN_OPEN: {
if (state.isOpen === true) {
return state; // prevent other state from reseting
}
const activeDescendant = optionId(action.name, 0);
return {
...state,
activeDescendant,
isOpen: true,
search: '',
visibleOptions: action.options,
};
}
// Invert dropdown state
case Action.DROPDOWN_TOGGLE: {
const nextOpen = !state.isOpen;
const activeDescendant = optionId(action.name, 0);
return {
...state,
activeDescendant,
isOpen: nextOpen,
search: '',
visibleOptions: action.options,
};
}
// Move to start / end
case Action.MOVE_TO_END:
case Action.MOVE_TO_START: {
const index = action.type === Action.MOVE_TO_START
? action.options.indexOf(state.visibleOptions[0])
: action.options.indexOf(state.visibleOptions[state.visibleOptions.length - 1]);
const activeDescendant = action.allowCreate === true
? optionId(action.name, action.options.length)
: optionId(action.name, index);
if (action.wrapper) {
const el = action.wrapper.querySelector(`#${activeDescendant}`);
if (el) {
el.scrollIntoView(false);
// close
case KEY.ESC:
setIsOpen(false);
break;
default:
if (noSearch) {
evt.preventDefault();
}
}
return { ...state, activeDescendant };
break;
}
// Initialize dropdown & set default state (while still letting props be mutable)
case Action.INIT: {
// Users may specify created options within defaults, if they’re missing from the options array
const defaults = action.defaults || [];
const selected = action.options.filter(option => defaults.indexOf(option) !== -1);
const created = defaults.filter(option => action.options.indexOf(option) === -1);
return { ...state, created, selected, visibleOptions: action.options };
}
// Move up or down with keyboard
case Action.MOVE_NEXT:
case Action.MOVE_PREV: {
const match = state.activeDescendant.match(/\d*$/); // get number at end of ID
if (!match) {
return state;
}
const index = parseInt(match[0], 10);
const selected = action.options[index];
const visibleIndex = state.visibleOptions.indexOf(selected);
const options = [...action.options];
if (action.allowCreate === true) {
options.push(state.search); // if allowCreate, allow keyboard to highlight newly-created item
}
let next;
if (action.type === Action.MOVE_PREV) {
const prevVisible = options.indexOf(state.visibleOptions[visibleIndex - 1]);
const lastVisible = options.indexOf(state.visibleOptions[state.visibleOptions.length - 1]);
next = prevVisible !== -1 ? prevVisible : lastVisible;
}
else {
const nextVisible = options.indexOf(state.visibleOptions[visibleIndex + 1]);
const firstVisible = options.indexOf(state.visibleOptions[0]);
next = nextVisible !== -1 ? nextVisible : firstVisible;
}
const activeDescendant = optionId(action.name, next);
if (action.wrapper) {
const el = action.wrapper.querySelector(`#${activeDescendant}`);
if (el) {
el.scrollIntoView(false);
}
}
return { ...state, activeDescendant };
}
// Filter results
case Action.SEARCH: {
const visibleOptions = action.options.filter(option => new RegExp(action.search, 'i').test(option));
const options = [...action.options];
if (action.allowCreate === true) {
options.push(action.search);
}
return {
...state,
activeDescendant: optionId(action.name, options.indexOf(visibleOptions[0] || state.search)),
search: action.search,
visibleOptions,
};
}
default:
return state;
}
}
const SelectZero = ({ allowCreate = false, className = 'rsz', defaultValue = [], max = Infinity, multi = false, noSearch = false, onChange, options, placeholder = 'Select', name, ...rest }) => {
const [isInitialized, setIsInitialized] = useState(false);
const [searchListener, setSearchListener] = useState();
const [triggerListener, setTriggerListener] = useState();
const listRef = useRef(null);
const searchRef = useRef(null);
const triggerRef = useRef(null);
const [state, dispatch] = useReducer(reducer, initialState);
function onClick(option) {
if (multi === true) {
dispatch({ max, name, option, options, type: Action.ADD_MULTI_CLICK });
}
else {
dispatch({ name, option, options, type: Action.ADD_SINGLE_CLICK });
}
}
// effect 1: user callback
// effect 1. maintain focus
useEffect(() => {
if (typeof onChange === 'function') {
onChange(state.selected, state.created);
if (searchRef.current && isOpen) {
searchRef.current.focus();
}
}, [state.selected, state.created]); // eslint-disable-line react-hooks/exhaustive-deps
// effect 2: initialization
useEffect(() => {
if (!isInitialized) {
dispatch({ defaults: defaultValue, options, type: Action.INIT });
setIsInitialized(true);
}
}, [defaultValue, options, isInitialized]);
// effect 3: maintain focus
useEffect(() => {
if (state.isOpen) {
if (searchRef.current) {
searchRef.current.focus();
}
else if (triggerRef.current) {
triggerRef.current.focus();
}
}
else if (!state.isOpen && triggerRef.current) {
else if (triggerRef.current && !isOpen) {
triggerRef.current.focus();
}
}, [state.activeDescendant, state.isOpen]);
// effect 4: navigation listener
});
// effect 2. active descendant
useEffect(() => {
function onKeydown(evt) {
// eslint-disable-next-line default-case
switch (evt.key) {
case KEY.ENTER: {
evt.preventDefault();
dispatch({
max,
options,
type: multi === true ? Action.ADD_MULTI_KEYBOARD : Action.ADD_SINGLE_KEYBOARD,
});
break;
}
// move down
case KEY.DOWN: {
evt.preventDefault();
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_NEXT,
wrapper: listRef.current,
});
break;
}
// move to end
case KEY.END:
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_TO_END,
wrapper: listRef.current,
});
break;
// move to start
case KEY.HOME:
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_TO_START,
wrapper: listRef.current,
});
break;
// move up
case KEY.ESC:
dispatch({ name, options, type: Action.DROPDOWN_CLOSE });
break;
// close
case KEY.UP: {
evt.preventDefault();
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_PREV,
wrapper: listRef.current,
});
break;
}
}
if (visibleIndices.indexOf(activeDescendantIndex) === -1) {
setActiveDescendantIndex(visibleIndices[0]);
}
if (!searchListener && searchRef.current) {
searchRef.current.addEventListener('keydown', onKeydown);
setSearchListener(true);
}
}, [searchListener]); // eslint-disable-line react-hooks/exhaustive-deps
// effect 5: trigger listener
useEffect(() => {
function onKeydown(e) {
if (e.key === KEY.DOWN) {
e.preventDefault();
dispatch({ name, options, type: Action.DROPDOWN_OPEN });
}
}
if (triggerRef.current && !triggerListener) {
triggerRef.current.addEventListener('keyup', onKeydown);
setTriggerListener(true);
}
}, [name, options, triggerListener]);
const shouldDisplaySearch = noSearch !== true && options.length > 4;
const shouldDisplayCreate = allowCreate !== false &&
state.search.length > 0 &&
state.visibleOptions.findIndex(option => option === state.search) === -1;
// maintain focus on re-render
if (searchRef.current && state.isOpen) {
searchRef.current.focus();
}
else if (triggerRef.current && !state.isOpen) {
triggerRef.current.focus();
}
const selection = [...state.selected, ...state.created];
return (React.createElement("div", Object.assign({ "aria-expanded": state.isOpen || undefined, "aria-multiselectable": multi === true || undefined, className: className, role: "listbox" }, rest),
}, [activeDescendantIndex, visibleIndices]);
return (React.createElement("div", Object.assign({ "aria-expanded": isOpen === true, "aria-multiselectable": multi === true || undefined, className: className, role: "listbox" }, rest),
React.createElement("div", { className: "rsz__trigger" },
React.createElement("button", { "aria-controls": elId(name, 'menu'), "aria-haspopup": "listbox", className: "rsz__trigger-button", ref: triggerRef, type: "button", onClick: () => dispatch({ name, options, type: Action.DROPDOWN_TOGGLE }) },
React.createElement("button", { "aria-controls": elId('menu'), "aria-haspopup": "listbox", className: "rsz__trigger-button", onClick: () => setIsOpen(true), onKeyDown: e => {
if (e.key === KEY.DOWN) {
setIsOpen(true);
}
}, ref: triggerRef, type: "button" },
multi === true ? `Select options for ${name}` : `Select an option for ${name}`,
React.createElement("span", { "aria-hidden": true, className: "rsz__arrow" }, "\u2193")),
selection.length > 0 ? (React.createElement("ul", { className: "rsz__selection" }, selection.map(option => (React.createElement("li", { key: option, className: "rsz__selected" },
value.length > 0 ? (React.createElement("ul", { className: "rsz__selection" }, value.map(option => (React.createElement("li", { key: option, className: "rsz__selected" },
React.createElement("span", { className: "rsz__selected-text" }, option),
React.createElement("button", { className: "rsz__selected-action", "aria-hidden": true, type: "button", onClick: () => dispatch({ max, name, option, options, type: Action.ADD_MULTI_CLICK }) }, "\u2715")))))) : (React.createElement("div", { className: "rsz__selection rsz__placeholder" }, placeholder))),
React.createElement("div", { "aria-label": "Close dropdown", className: "rsz__overlay", onClick: () => dispatch({ name, options, type: Action.DROPDOWN_CLOSE }) }),
React.createElement("menu", { className: "rsz__dropdown-wrapper" },
React.createElement("button", { "aria-label": `remove ${option}`, className: "rsz__selected-action", onClick: () => addItem(option), type: "button" }, "\u2715")))))) : (React.createElement("div", { className: "rsz__selection rsz__placeholder" }, placeholder))),
React.createElement("div", { "aria-label": "Close dropdown", className: "rsz__overlay", onClick: () => setIsOpen(false) }),
React.createElement("menu", { id: elId('menu'), className: "rsz__dropdown-wrapper" },
React.createElement("div", { className: "rsz__dropdown" },
React.createElement("input", { "aria-activedescendant": state.activeDescendant, "aria-hidden": shouldDisplaySearch === false || undefined, "aria-label": "Filter options", className: "rsz__search", onChange: e => noSearch !== true &&
dispatch({ allowCreate, name, options, search: e.target.value, type: Action.SEARCH }), ref: searchRef, type: "search", value: state.search }),
React.createElement("input", { "aria-activedescendant": optionId(activeDescendantIndex), "aria-hidden": shouldDisplaySearch === false || undefined, "aria-label": "Filter options", className: "rsz__search", onChange: e => setSearch(e.target.value), onKeyDown: onKeyDown, ref: searchRef, type: "search", value: search }),
React.createElement("div", { className: "rsz__search-icon" }),
React.createElement("ul", { className: "rsz__option-list", ref: listRef },
state.visibleOptions.length > 0 ? (state.visibleOptions.map(option => {
const id = optionId(name, options.indexOf(option));
const isSelected = selection.indexOf(option) !== -1;
const isMaxxed = selection.length === max;
visibleOptions.map((option, index) => {
const isSelected = value.indexOf(option) !== -1;
let ariaSelected = isSelected;
if (isMaxxed) {
if (ariaSelected === false && isMaxed === true) {
ariaSelected = undefined;
}
return (React.createElement("li", { className: "rsz__option", key: option },
React.createElement("button", { "aria-selected": ariaSelected, "data-highlighted": state.activeDescendant === id || undefined, disabled: isMaxxed && !isSelected, id: id, onClick: () => onClick(option), role: "option", type: "button" }, option)));
})) : (React.createElement("span", { className: "rsz__no-results" },
React.createElement("button", { "aria-selected": ariaSelected, "data-highlighted": activeDescendantIndex === visibleIndices[index] || undefined, disabled: isMaxed && !isSelected, id: optionId(visibleIndices[index]), onClick: () => addItem(option), role: "option", type: "button" }, option)));
}),
options.length > 0 && visibleOptions.length === 0 && (React.createElement("span", { className: "rsz__no-results" },
"No results for \u201C",
state.search,
search,
"\u201D")),
shouldDisplayCreate && (React.createElement("li", { className: "rsz__option" },
React.createElement("button", { className: "rsz__create", id: optionId(name, options.length), type: "button", onClick: () => onClick(state.search) },
React.createElement("button", { className: "rsz__create", "data-highlighted": activeDescendantIndex === options.length || undefined, disabled: isMaxed, id: optionId(options.length), onClick: () => addItem(search), type: "button" },
"Create ",
React.createElement("span", { className: "rsz__search-term" }, state.search)))),
allowCreate === true && state.search === '' && (React.createElement("li", { className: "rsz__option" },
React.createElement("span", { className: "rsz__search-term" }, search)))),
allowCreate === true && search === '' && (React.createElement("li", { className: "rsz__option" },
React.createElement("span", { className: "rsz__create" }, "Start typing to create an item")))))),
React.createElement("input", { type: "hidden", name: name, value: selection.join(',') })));
React.createElement("input", { type: "hidden", name: name, value: value.join(',') })));
};
export default SelectZero;

@@ -1,7 +0,7 @@

import React, { ReactNode } from 'react';
import React, { ReactNode, Dispatch, SetStateAction } from 'react';
declare type Selection = string[];
declare type OnChangeCallback = (selected: Selection) => void | Dispatch<SetStateAction<Selection>>;
interface SelectProps {
allowCreate?: boolean;
className?: string;
defaultValue?: Selection;
max?: number;

@@ -11,9 +11,8 @@ multi?: boolean;

noSearch?: boolean;
onChange?: (selected: Selection, created: Selection) => void;
onChange: OnChangeCallback;
options: Selection;
placeholder?: ReactNode;
value?: Selection;
}
export declare function elId(name: string, component: string): string;
export declare function optionId(name: string, option: number): string;
declare const SelectZero: React.FunctionComponent<SelectProps>;
export default SelectZero;

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

import React, { useState, useRef, useReducer, useEffect } from 'react';
import React, { useRef, useState, useEffect } from 'react';
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _objectSpread(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
var ownKeys = Object.keys(source);
if (typeof Object.getOwnPropertySymbols === 'function') {
ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) {
return Object.getOwnPropertyDescriptor(source, sym).enumerable;
}));
}
ownKeys.forEach(function (key) {
_defineProperty(target, key, source[key]);
});
}
return target;
}
function _objectWithoutPropertiesLoose(source, excluded) {

@@ -73,27 +39,2 @@ if (source == null) return {};

function elId(name, component) {
return "rsz-".concat(name, "-").concat(component);
}
function optionId(name, option) {
return elId(name, "option-".concat(option));
}
var Action;
(function (Action) {
Action["APPEND"] = "APPEND";
Action["ADD_MULTI_CLICK"] = "ADD_MULTI_CLICK";
Action["ADD_MULTI_KEYBOARD"] = "ADD_MULTI_KEYBOARD";
Action["ADD_SINGLE_CLICK"] = "ADD_SINGLE_CLICK";
Action["ADD_SINGLE_KEYBOARD"] = "ADD_SINGLE_KEYBOARD";
Action["DROPDOWN_CLOSE"] = "DROPDOWN_CLOSE";
Action["DROPDOWN_OPEN"] = "DROPDOWN_OPEN";
Action["DROPDOWN_TOGGLE"] = "DROPDOWN_TOGGLE";
Action["INIT"] = "INIT";
Action["MOVE_NEXT"] = "MOVE_NEXT";
Action["MOVE_PREV"] = "MOVE_PREV";
Action["MOVE_TO_END"] = "MOVE_TO_END";
Action["MOVE_TO_START"] = "MOVE_TO_START";
Action["SEARCH"] = "SEARCH";
})(Action || (Action = {}));
var KEY;

@@ -110,289 +51,2 @@

const initialState = {
activeDescendant: '0',
created: [],
isOpen: false,
search: '',
selected: [],
visibleOptions: []
};
function reducer(state, action) {
switch (action.type) {
// Add or remove an option from mouse click
case Action.ADD_MULTI_CLICK:
{
const isNew = action.options.indexOf(action.option) === -1;
const total = state.selected.length + state.created.length; // if new
if (isNew) {
const created = [...state.created];
const index = state.created.indexOf(action.option);
if (index !== -1) {
created.splice(index, 1); // if existing, remove
} else if (total < action.max) {
// if new, add unless we’re at max
created.push(action.option);
}
return _objectSpread({}, state, {
created
});
} // if existing
const selected = [...state.selected];
const index = state.selected.indexOf(action.option);
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
} else if (total < action.max) {
selected.push(action.option); // if new, add unless we’re at max
}
return _objectSpread({}, state, {
selected: action.options.filter(order => selected.indexOf(order) !== -1)
});
}
// Add or remove an option from keyboard
case Action.ADD_MULTI_KEYBOARD:
{
const match = state.activeDescendant.match(/\d*$/);
if (!match) {
return state;
}
const total = state.selected.length + state.created.length;
const number = parseInt(match[0], 10); // if new
if (number > action.options.length - 1) {
const created = [...state.created];
const option = state.search;
const index = state.created.indexOf(option);
if (index === -1) {
created.splice(index, 1); // if existing, remove
} else if (total < action.max) {
// if new, add unless we’re at max
created.push(option);
}
return _objectSpread({}, state, {
created
});
} // if existing
const option = action.options[number];
const selected = [...state.selected];
const index = state.selected.indexOf(option);
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
} else if (total < action.max) {
selected.push(option); // if new, add unless we’re at max
}
return _objectSpread({}, state, {
selected: action.options.filter(order => selected.indexOf(order) !== -1)
});
}
// Set the option from mouse click
case Action.ADD_SINGLE_CLICK:
{
const newState = _objectSpread({}, state, {
isOpen: false,
search: '',
visibleOptions: action.options
});
if (action.options.indexOf(action.option) === -1) {
return _objectSpread({}, newState, {
created: [action.option]
}); // if new
}
return _objectSpread({}, newState, {
selected: [action.option]
}); // if existing
}
// Set the option from keyboard
case Action.ADD_SINGLE_KEYBOARD:
{
const match = state.activeDescendant.match(/\d*$/);
if (!match) {
return state;
}
const number = parseInt(match[0], 10);
const newState = _objectSpread({}, state, {
isOpen: false,
visibleOptions: action.options
});
if (number > action.options.length - 1) {
return _objectSpread({}, newState, {
created: [state.search]
}); // if new
}
return _objectSpread({}, newState, {
selected: [action.options[number]]
}); // if existing
}
// Close dropdown
case Action.DROPDOWN_CLOSE:
{
if (state.isOpen === false) {
return state; // prevent other state from reseting
}
const activeDescendant = optionId(action.name, 0);
return _objectSpread({}, state, {
activeDescendant,
isOpen: false,
search: '',
visibleOptions: action.options
});
}
// Open dropdown
case Action.DROPDOWN_OPEN:
{
if (state.isOpen === true) {
return state; // prevent other state from reseting
}
const activeDescendant = optionId(action.name, 0);
return _objectSpread({}, state, {
activeDescendant,
isOpen: true,
search: '',
visibleOptions: action.options
});
}
// Invert dropdown state
case Action.DROPDOWN_TOGGLE:
{
const nextOpen = !state.isOpen;
const activeDescendant = optionId(action.name, 0);
return _objectSpread({}, state, {
activeDescendant,
isOpen: nextOpen,
search: '',
visibleOptions: action.options
});
}
// Move to start / end
case Action.MOVE_TO_END:
case Action.MOVE_TO_START:
{
const index = action.type === Action.MOVE_TO_START ? action.options.indexOf(state.visibleOptions[0]) : action.options.indexOf(state.visibleOptions[state.visibleOptions.length - 1]);
const activeDescendant = action.allowCreate === true ? optionId(action.name, action.options.length) : optionId(action.name, index);
if (action.wrapper) {
const el = action.wrapper.querySelector("#".concat(activeDescendant));
if (el) {
el.scrollIntoView(false);
}
}
return _objectSpread({}, state, {
activeDescendant
});
}
// Initialize dropdown & set default state (while still letting props be mutable)
case Action.INIT:
{
// Users may specify created options within defaults, if they’re missing from the options array
const defaults = action.defaults || [];
const selected = action.options.filter(option => defaults.indexOf(option) !== -1);
const created = defaults.filter(option => action.options.indexOf(option) === -1);
return _objectSpread({}, state, {
created,
selected,
visibleOptions: action.options
});
}
// Move up or down with keyboard
case Action.MOVE_NEXT:
case Action.MOVE_PREV:
{
const match = state.activeDescendant.match(/\d*$/); // get number at end of ID
if (!match) {
return state;
}
const index = parseInt(match[0], 10);
const selected = action.options[index];
const visibleIndex = state.visibleOptions.indexOf(selected);
const options = [...action.options];
if (action.allowCreate === true) {
options.push(state.search); // if allowCreate, allow keyboard to highlight newly-created item
}
let next;
if (action.type === Action.MOVE_PREV) {
const prevVisible = options.indexOf(state.visibleOptions[visibleIndex - 1]);
const lastVisible = options.indexOf(state.visibleOptions[state.visibleOptions.length - 1]);
next = prevVisible !== -1 ? prevVisible : lastVisible;
} else {
const nextVisible = options.indexOf(state.visibleOptions[visibleIndex + 1]);
const firstVisible = options.indexOf(state.visibleOptions[0]);
next = nextVisible !== -1 ? nextVisible : firstVisible;
}
const activeDescendant = optionId(action.name, next);
if (action.wrapper) {
const el = action.wrapper.querySelector("#".concat(activeDescendant));
if (el) {
el.scrollIntoView(false);
}
}
return _objectSpread({}, state, {
activeDescendant
});
}
// Filter results
case Action.SEARCH:
{
const visibleOptions = action.options.filter(option => new RegExp(action.search, 'i').test(option));
const options = [...action.options];
if (action.allowCreate === true) {
options.push(action.search);
}
return _objectSpread({}, state, {
activeDescendant: optionId(action.name, options.indexOf(visibleOptions[0] || state.search)),
search: action.search,
visibleOptions
});
}
default:
return state;
}
}
const SelectZero = (_ref) => {

@@ -402,5 +56,5 @@ let {

className = 'rsz',
defaultValue = [],
max = Infinity,
multi = false,
name,
noSearch = false,

@@ -410,176 +64,140 @@ onChange,

placeholder = 'Select',
name
value = []
} = _ref,
rest = _objectWithoutProperties(_ref, ["allowCreate", "className", "defaultValue", "max", "multi", "noSearch", "onChange", "options", "placeholder", "name"]);
rest = _objectWithoutProperties(_ref, ["allowCreate", "className", "max", "multi", "name", "noSearch", "onChange", "options", "placeholder", "value"]);
const [isInitialized, setIsInitialized] = useState(false);
const [searchListener, setSearchListener] = useState();
const [triggerListener, setTriggerListener] = useState();
// state
const listRef = useRef(null);
const searchRef = useRef(null);
const triggerRef = useRef(null);
const [state, dispatch] = useReducer(reducer, initialState);
const [activeDescendantIndex, setActiveDescendantIndex] = useState(0); // Active descendant. Numbers are easier to manipulate than element IDs.
function _onClick(option) {
if (multi === true) {
dispatch({
max,
name,
option,
options,
type: Action.ADD_MULTI_CLICK
});
} else {
dispatch({
name,
option,
options,
type: Action.ADD_SINGLE_CLICK
});
const [search, setSearch] = useState('');
const [isOpen, setIsOpen] = useState(false); // computed
const visibleIndices = [];
const visibleOptions = [];
options.forEach((option, index) => {
if (new RegExp(search, 'i').test(option)) {
// filter results in one pass
visibleOptions.push(option);
visibleIndices.push(index);
}
} // effect 1: user callback
});
const shouldDisplaySearch = noSearch !== true && options.length > 4;
const shouldDisplayCreate = allowCreate === true && search.length > 0 && options.findIndex(option => option === search) === -1;
const isMaxed = value.length === max; // methods
function elId(component) {
return "rsz-".concat(name, "-").concat(component);
}
useEffect(() => {
if (typeof onChange === 'function') {
onChange(state.selected, state.created);
}
}, [state.selected, state.created]); // eslint-disable-line react-hooks/exhaustive-deps
// effect 2: initialization
function optionId(option) {
return elId("option-".concat(option));
}
useEffect(() => {
if (!isInitialized) {
dispatch({
defaults: defaultValue,
options,
type: Action.INIT
});
setIsInitialized(true);
}
}, [defaultValue, options, isInitialized]); // effect 3: maintain focus
function scrollTo(wrapper, selector) {
if (wrapper) {
const el = wrapper.querySelector(selector);
useEffect(() => {
if (state.isOpen) {
if (searchRef.current) {
searchRef.current.focus();
} else if (triggerRef.current) {
triggerRef.current.focus();
if (el) {
el.scrollIntoView(false);
}
} else if (!state.isOpen && triggerRef.current) {
triggerRef.current.focus();
}
}, [state.activeDescendant, state.isOpen]); // effect 4: navigation listener
}
useEffect(() => {
function onKeydown(evt) {
// eslint-disable-next-line default-case
switch (evt.key) {
case KEY.ENTER:
{
evt.preventDefault();
dispatch({
max,
options,
type: multi === true ? Action.ADD_MULTI_KEYBOARD : Action.ADD_SINGLE_KEYBOARD
});
break;
}
// move down
function addItem(option) {
// multi
if (multi === true) {
let selected = [...value];
const index = selected.indexOf(option);
case KEY.DOWN:
{
evt.preventDefault();
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_NEXT,
wrapper: listRef.current
});
break;
}
// move to end
if (index !== -1) {
selected.splice(index, 1); // if existing, remove
} else if (selected.length < max) {
selected.push(option); // if new, add unless we’re at max
}
case KEY.END:
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_TO_END,
wrapper: listRef.current
});
if (options.indexOf(option) !== -1) {
selected = options.filter(order => selected.indexOf(order) !== -1); // if existing option, keep original order
}
onChange(selected);
return;
} // single
onChange([option]);
setIsOpen(false);
}
function onKeyDown(evt) {
const first = visibleIndices[0];
const last = visibleIndices[visibleIndices.length - 1];
switch (evt.key) {
// select active item
case KEY.ENTER:
{
evt.preventDefault();
addItem(options[activeDescendantIndex]);
break;
// move to start
}
// move down / up
case KEY.HOME:
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_TO_START,
wrapper: listRef.current
});
case KEY.DOWN:
case KEY.UP:
{
evt.preventDefault();
const sum = evt.key === KEY.UP ? -1 : 1;
const fallback = evt.key === KEY.UP ? last : first; // if at beginning, loop around to end, and vice-versa
const nextIndex = visibleIndices.indexOf(activeDescendantIndex) + sum;
const next = visibleIndices[nextIndex] !== undefined ? visibleIndices[nextIndex] : fallback;
setActiveDescendantIndex(next); // set to last index if at beginning of list
scrollTo(listRef.current, "#".concat(optionId(next)));
break;
// move up
}
// move to start / end
case KEY.ESC:
dispatch({
name,
options,
type: Action.DROPDOWN_CLOSE
});
case KEY.END:
case KEY.HOME:
{
const next = evt.key === KEY.HOME ? first : last;
setActiveDescendantIndex(next);
scrollTo(listRef.current, "#".concat(optionId(next)));
break;
// close
}
// close
case KEY.UP:
{
evt.preventDefault();
dispatch({
allowCreate,
name,
options,
type: Action.MOVE_PREV,
wrapper: listRef.current
});
break;
}
}
}
case KEY.ESC:
setIsOpen(false);
break;
if (!searchListener && searchRef.current) {
searchRef.current.addEventListener('keydown', onKeydown);
setSearchListener(true);
default:
if (noSearch) {
evt.preventDefault();
}
break;
}
}, [searchListener]); // eslint-disable-line react-hooks/exhaustive-deps
// effect 5: trigger listener
} // effect 1. maintain focus
useEffect(() => {
function onKeydown(e) {
if (e.key === KEY.DOWN) {
e.preventDefault();
dispatch({
name,
options,
type: Action.DROPDOWN_OPEN
});
}
if (searchRef.current && isOpen) {
searchRef.current.focus();
} else if (triggerRef.current && !isOpen) {
triggerRef.current.focus();
}
}); // effect 2. active descendant
if (triggerRef.current && !triggerListener) {
triggerRef.current.addEventListener('keyup', onKeydown);
setTriggerListener(true);
useEffect(() => {
if (visibleIndices.indexOf(activeDescendantIndex) === -1) {
setActiveDescendantIndex(visibleIndices[0]);
}
}, [name, options, triggerListener]);
const shouldDisplaySearch = noSearch !== true && options.length > 4;
const shouldDisplayCreate = allowCreate !== false && state.search.length > 0 && state.visibleOptions.findIndex(option => option === state.search) === -1; // maintain focus on re-render
if (searchRef.current && state.isOpen) {
searchRef.current.focus();
} else if (triggerRef.current && !state.isOpen) {
triggerRef.current.focus();
}
const selection = [...state.selected, ...state.created];
}, [activeDescendantIndex, visibleIndices]);
return React.createElement("div", Object.assign({
"aria-expanded": state.isOpen || undefined,
"aria-expanded": isOpen === true,
"aria-multiselectable": multi === true || undefined,

@@ -591,18 +209,19 @@ className: className,

}, React.createElement("button", {
"aria-controls": elId(name, 'menu'),
"aria-controls": elId('menu'),
"aria-haspopup": "listbox",
className: "rsz__trigger-button",
onClick: () => setIsOpen(true),
onKeyDown: e => {
if (e.key === KEY.DOWN) {
setIsOpen(true);
}
},
ref: triggerRef,
type: "button",
onClick: () => dispatch({
name,
options,
type: Action.DROPDOWN_TOGGLE
})
type: "button"
}, multi === true ? "Select options for ".concat(name) : "Select an option for ".concat(name), React.createElement("span", {
"aria-hidden": true,
className: "rsz__arrow"
}, "\u2193")), selection.length > 0 ? React.createElement("ul", {
}, "\u2193")), value.length > 0 ? React.createElement("ul", {
className: "rsz__selection"
}, selection.map(option => React.createElement("li", {
}, value.map(option => React.createElement("li", {
key: option,

@@ -613,12 +232,6 @@ className: "rsz__selected"

}, option), React.createElement("button", {
"aria-label": "remove ".concat(option),
className: "rsz__selected-action",
"aria-hidden": true,
type: "button",
onClick: () => dispatch({
max,
name,
option,
options,
type: Action.ADD_MULTI_CLICK
})
onClick: () => addItem(option),
type: "button"
}, "\u2715")))) : React.createElement("div", {

@@ -629,8 +242,5 @@ className: "rsz__selection rsz__placeholder"

className: "rsz__overlay",
onClick: () => dispatch({
name,
options,
type: Action.DROPDOWN_CLOSE
})
onClick: () => setIsOpen(false)
}), React.createElement("menu", {
id: elId('menu'),
className: "rsz__dropdown-wrapper"

@@ -640,16 +250,11 @@ }, React.createElement("div", {

}, React.createElement("input", {
"aria-activedescendant": state.activeDescendant,
"aria-activedescendant": optionId(activeDescendantIndex),
"aria-hidden": shouldDisplaySearch === false || undefined,
"aria-label": "Filter options",
className: "rsz__search",
onChange: e => noSearch !== true && dispatch({
allowCreate,
name,
options,
search: e.target.value,
type: Action.SEARCH
}),
onChange: e => setSearch(e.target.value),
onKeyDown: onKeyDown,
ref: searchRef,
type: "search",
value: state.search
value: search
}), React.createElement("div", {

@@ -660,9 +265,7 @@ className: "rsz__search-icon"

ref: listRef
}, state.visibleOptions.length > 0 ? state.visibleOptions.map(option => {
const id = optionId(name, options.indexOf(option));
const isSelected = selection.indexOf(option) !== -1;
const isMaxxed = selection.length === max;
}, visibleOptions.map((option, index) => {
const isSelected = value.indexOf(option) !== -1;
let ariaSelected = isSelected;
if (isMaxxed) {
if (ariaSelected === false && isMaxed === true) {
ariaSelected = undefined;

@@ -676,21 +279,23 @@ }

"aria-selected": ariaSelected,
"data-highlighted": state.activeDescendant === id || undefined,
disabled: isMaxxed && !isSelected,
id: id,
onClick: () => _onClick(option),
"data-highlighted": activeDescendantIndex === visibleIndices[index] || undefined,
disabled: isMaxed && !isSelected,
id: optionId(visibleIndices[index]),
onClick: () => addItem(option),
role: "option",
type: "button"
}, option));
}) : React.createElement("span", {
}), options.length > 0 && visibleOptions.length === 0 && React.createElement("span", {
className: "rsz__no-results"
}, "No results for \u201C", state.search, "\u201D"), shouldDisplayCreate && React.createElement("li", {
}, "No results for \u201C", search, "\u201D"), shouldDisplayCreate && React.createElement("li", {
className: "rsz__option"
}, React.createElement("button", {
className: "rsz__create",
id: optionId(name, options.length),
type: "button",
onClick: () => _onClick(state.search)
"data-highlighted": activeDescendantIndex === options.length || undefined,
disabled: isMaxed,
id: optionId(options.length),
onClick: () => addItem(search),
type: "button"
}, "Create ", React.createElement("span", {
className: "rsz__search-term"
}, state.search))), allowCreate === true && state.search === '' && React.createElement("li", {
}, search))), allowCreate === true && search === '' && React.createElement("li", {
className: "rsz__option"

@@ -702,3 +307,3 @@ }, React.createElement("span", {

name: name,
value: selection.join(',')
value: value.join(',')
}));

@@ -708,2 +313,1 @@ };

export default SelectZero;
export { elId, optionId };

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

import React,{useState,useRef,useReducer,useEffect}from"react";function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _objectSpread(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{},o=Object.keys(n);"function"==typeof Object.getOwnPropertySymbols&&(o=o.concat(Object.getOwnPropertySymbols(n).filter(function(e){return Object.getOwnPropertyDescriptor(n,e).enumerable}))),o.forEach(function(t){_defineProperty(e,t,n[t])})}return e}function _objectWithoutPropertiesLoose(e,t){if(null==e)return{};var n,o,a={},c=Object.keys(e);for(o=0;o<c.length;o++)n=c[o],t.indexOf(n)>=0||(a[n]=e[n]);return a}function _objectWithoutProperties(e,t){if(null==e)return{};var n,o,a=_objectWithoutPropertiesLoose(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(o=0;o<c.length;o++)n=c[o],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(a[n]=e[n])}return a}function elId(e,t){return"rsz-".concat(e,"-").concat(t)}function optionId(e,t){return elId(e,"option-".concat(t))}var Action,KEY;!function(e){e.APPEND="APPEND",e.ADD_MULTI_CLICK="ADD_MULTI_CLICK",e.ADD_MULTI_KEYBOARD="ADD_MULTI_KEYBOARD",e.ADD_SINGLE_CLICK="ADD_SINGLE_CLICK",e.ADD_SINGLE_KEYBOARD="ADD_SINGLE_KEYBOARD",e.DROPDOWN_CLOSE="DROPDOWN_CLOSE",e.DROPDOWN_OPEN="DROPDOWN_OPEN",e.DROPDOWN_TOGGLE="DROPDOWN_TOGGLE",e.INIT="INIT",e.MOVE_NEXT="MOVE_NEXT",e.MOVE_PREV="MOVE_PREV",e.MOVE_TO_END="MOVE_TO_END",e.MOVE_TO_START="MOVE_TO_START",e.SEARCH="SEARCH"}(Action||(Action={})),function(e){e.DOWN="ArrowDown",e.END="End",e.ENTER="Enter",e.ESC="Escape",e.HOME="Home",e.UP="ArrowUp"}(KEY||(KEY={}));const initialState={activeDescendant:"0",created:[],isOpen:!1,search:"",selected:[],visibleOptions:[]};function reducer(e,t){switch(t.type){case Action.ADD_MULTI_CLICK:{const n=-1===t.options.indexOf(t.option),o=e.selected.length+e.created.length;if(n){const n=[...e.created],a=e.created.indexOf(t.option);return-1!==a?n.splice(a,1):o<t.max&&n.push(t.option),_objectSpread({},e,{created:n})}const a=[...e.selected],c=e.selected.indexOf(t.option);return-1!==c?a.splice(c,1):o<t.max&&a.push(t.option),_objectSpread({},e,{selected:t.options.filter(e=>-1!==a.indexOf(e))})}case Action.ADD_MULTI_KEYBOARD:{const n=e.activeDescendant.match(/\d*$/);if(!n)return e;const o=e.selected.length+e.created.length,a=parseInt(n[0],10);if(a>t.options.length-1){const n=[...e.created],a=e.search,c=e.created.indexOf(a);return-1===c?n.splice(c,1):o<t.max&&n.push(a),_objectSpread({},e,{created:n})}const c=t.options[a],r=[...e.selected],s=e.selected.indexOf(c);return-1!==s?r.splice(s,1):o<t.max&&r.push(c),_objectSpread({},e,{selected:t.options.filter(e=>-1!==r.indexOf(e))})}case Action.ADD_SINGLE_CLICK:{const n=_objectSpread({},e,{isOpen:!1,search:"",visibleOptions:t.options});return-1===t.options.indexOf(t.option)?_objectSpread({},n,{created:[t.option]}):_objectSpread({},n,{selected:[t.option]})}case Action.ADD_SINGLE_KEYBOARD:{const n=e.activeDescendant.match(/\d*$/);if(!n)return e;const o=parseInt(n[0],10),a=_objectSpread({},e,{isOpen:!1,visibleOptions:t.options});return o>t.options.length-1?_objectSpread({},a,{created:[e.search]}):_objectSpread({},a,{selected:[t.options[o]]})}case Action.DROPDOWN_CLOSE:if(!1===e.isOpen)return e;return _objectSpread({},e,{activeDescendant:optionId(t.name,0),isOpen:!1,search:"",visibleOptions:t.options});case Action.DROPDOWN_OPEN:if(!0===e.isOpen)return e;return _objectSpread({},e,{activeDescendant:optionId(t.name,0),isOpen:!0,search:"",visibleOptions:t.options});case Action.DROPDOWN_TOGGLE:{const n=!e.isOpen;return _objectSpread({},e,{activeDescendant:optionId(t.name,0),isOpen:n,search:"",visibleOptions:t.options})}case Action.MOVE_TO_END:case Action.MOVE_TO_START:{const n=t.type===Action.MOVE_TO_START?t.options.indexOf(e.visibleOptions[0]):t.options.indexOf(e.visibleOptions[e.visibleOptions.length-1]),o=!0===t.allowCreate?optionId(t.name,t.options.length):optionId(t.name,n);if(t.wrapper){const e=t.wrapper.querySelector("#".concat(o));e&&e.scrollIntoView(!1)}return _objectSpread({},e,{activeDescendant:o})}case Action.INIT:{const n=t.defaults||[],o=t.options.filter(e=>-1!==n.indexOf(e));return _objectSpread({},e,{created:n.filter(e=>-1===t.options.indexOf(e)),selected:o,visibleOptions:t.options})}case Action.MOVE_NEXT:case Action.MOVE_PREV:{const n=e.activeDescendant.match(/\d*$/);if(!n)return e;const o=parseInt(n[0],10),a=t.options[o],c=e.visibleOptions.indexOf(a),r=[...t.options];let s;if(!0===t.allowCreate&&r.push(e.search),t.type===Action.MOVE_PREV){const t=r.indexOf(e.visibleOptions[c-1]),n=r.indexOf(e.visibleOptions[e.visibleOptions.length-1]);s=-1!==t?t:n}else{const t=r.indexOf(e.visibleOptions[c+1]),n=r.indexOf(e.visibleOptions[0]);s=-1!==t?t:n}const i=optionId(t.name,s);if(t.wrapper){const e=t.wrapper.querySelector("#".concat(i));e&&e.scrollIntoView(!1)}return _objectSpread({},e,{activeDescendant:i})}case Action.SEARCH:{const n=t.options.filter(e=>new RegExp(t.search,"i").test(e)),o=[...t.options];return!0===t.allowCreate&&o.push(t.search),_objectSpread({},e,{activeDescendant:optionId(t.name,o.indexOf(n[0]||e.search)),search:t.search,visibleOptions:n})}default:return e}}const SelectZero=e=>{let{allowCreate:t=!1,className:n="rsz",defaultValue:o=[],max:a=1/0,multi:c=!1,noSearch:r=!1,onChange:s,options:i,placeholder:l="Select",name:p}=e,d=_objectWithoutProperties(e,["allowCreate","className","defaultValue","max","multi","noSearch","onChange","options","placeholder","name"]);const[O,u]=useState(!1),[_,E]=useState(),[f,m]=useState(),D=useRef(null),b=useRef(null),h=useRef(null),[A,N]=useReducer(reducer,initialState);function R(e){N(!0===c?{max:a,name:p,option:e,options:i,type:Action.ADD_MULTI_CLICK}:{name:p,option:e,options:i,type:Action.ADD_SINGLE_CLICK})}useEffect(()=>{"function"==typeof s&&s(A.selected,A.created)},[A.selected,A.created]),useEffect(()=>{O||(N({defaults:o,options:i,type:Action.INIT}),u(!0))},[o,i,O]),useEffect(()=>{A.isOpen?b.current?b.current.focus():h.current&&h.current.focus():!A.isOpen&&h.current&&h.current.focus()},[A.activeDescendant,A.isOpen]),useEffect(()=>{!_&&b.current&&(b.current.addEventListener("keydown",function(e){switch(e.key){case KEY.ENTER:e.preventDefault(),N({max:a,options:i,type:!0===c?Action.ADD_MULTI_KEYBOARD:Action.ADD_SINGLE_KEYBOARD});break;case KEY.DOWN:e.preventDefault(),N({allowCreate:t,name:p,options:i,type:Action.MOVE_NEXT,wrapper:D.current});break;case KEY.END:N({allowCreate:t,name:p,options:i,type:Action.MOVE_TO_END,wrapper:D.current});break;case KEY.HOME:N({allowCreate:t,name:p,options:i,type:Action.MOVE_TO_START,wrapper:D.current});break;case KEY.ESC:N({name:p,options:i,type:Action.DROPDOWN_CLOSE});break;case KEY.UP:e.preventDefault(),N({allowCreate:t,name:p,options:i,type:Action.MOVE_PREV,wrapper:D.current})}}),E(!0))},[_]),useEffect(()=>{h.current&&!f&&(h.current.addEventListener("keyup",function(e){e.key===KEY.DOWN&&(e.preventDefault(),N({name:p,options:i,type:Action.DROPDOWN_OPEN}))}),m(!0))},[p,i,f]);const v=!0!==r&&i.length>4,S=!1!==t&&A.search.length>0&&-1===A.visibleOptions.findIndex(e=>e===A.search);b.current&&A.isOpen?b.current.focus():h.current&&!A.isOpen&&h.current.focus();const I=[...A.selected,...A.created];return React.createElement("div",Object.assign({"aria-expanded":A.isOpen||void 0,"aria-multiselectable":!0===c||void 0,className:n,role:"listbox"},d),React.createElement("div",{className:"rsz__trigger"},React.createElement("button",{"aria-controls":elId(p,"menu"),"aria-haspopup":"listbox",className:"rsz__trigger-button",ref:h,type:"button",onClick:()=>N({name:p,options:i,type:Action.DROPDOWN_TOGGLE})},!0===c?"Select options for ".concat(p):"Select an option for ".concat(p),React.createElement("span",{"aria-hidden":!0,className:"rsz__arrow"},"↓")),I.length>0?React.createElement("ul",{className:"rsz__selection"},I.map(e=>React.createElement("li",{key:e,className:"rsz__selected"},React.createElement("span",{className:"rsz__selected-text"},e),React.createElement("button",{className:"rsz__selected-action","aria-hidden":!0,type:"button",onClick:()=>N({max:a,name:p,option:e,options:i,type:Action.ADD_MULTI_CLICK})},"✕")))):React.createElement("div",{className:"rsz__selection rsz__placeholder"},l)),React.createElement("div",{"aria-label":"Close dropdown",className:"rsz__overlay",onClick:()=>N({name:p,options:i,type:Action.DROPDOWN_CLOSE})}),React.createElement("menu",{className:"rsz__dropdown-wrapper"},React.createElement("div",{className:"rsz__dropdown"},React.createElement("input",{"aria-activedescendant":A.activeDescendant,"aria-hidden":!1===v||void 0,"aria-label":"Filter options",className:"rsz__search",onChange:e=>!0!==r&&N({allowCreate:t,name:p,options:i,search:e.target.value,type:Action.SEARCH}),ref:b,type:"search",value:A.search}),React.createElement("div",{className:"rsz__search-icon"}),React.createElement("ul",{className:"rsz__option-list",ref:D},A.visibleOptions.length>0?A.visibleOptions.map(e=>{const t=optionId(p,i.indexOf(e)),n=-1!==I.indexOf(e),o=I.length===a;let c=n;return o&&(c=void 0),React.createElement("li",{className:"rsz__option",key:e},React.createElement("button",{"aria-selected":c,"data-highlighted":A.activeDescendant===t||void 0,disabled:o&&!n,id:t,onClick:()=>R(e),role:"option",type:"button"},e))}):React.createElement("span",{className:"rsz__no-results"},"No results for “",A.search,"”"),S&&React.createElement("li",{className:"rsz__option"},React.createElement("button",{className:"rsz__create",id:optionId(p,i.length),type:"button",onClick:()=>R(A.search)},"Create ",React.createElement("span",{className:"rsz__search-term"},A.search))),!0===t&&""===A.search&&React.createElement("li",{className:"rsz__option"},React.createElement("span",{className:"rsz__create"},"Start typing to create an item"))))),React.createElement("input",{type:"hidden",name:p,value:I.join(",")}))};export default SelectZero;export{elId,optionId};
import React,{useRef,useState,useEffect}from"react";function _objectWithoutPropertiesLoose(e,t){if(null==e)return{};var a,n,r={},c=Object.keys(e);for(n=0;n<c.length;n++)a=c[n],t.indexOf(a)>=0||(r[a]=e[a]);return r}function _objectWithoutProperties(e,t){if(null==e)return{};var a,n,r=_objectWithoutPropertiesLoose(e,t);if(Object.getOwnPropertySymbols){var c=Object.getOwnPropertySymbols(e);for(n=0;n<c.length;n++)a=c[n],t.indexOf(a)>=0||Object.prototype.propertyIsEnumerable.call(e,a)&&(r[a]=e[a])}return r}var KEY;!function(e){e.DOWN="ArrowDown",e.END="End",e.ENTER="Enter",e.ESC="Escape",e.HOME="Home",e.UP="ArrowUp"}(KEY||(KEY={}));const SelectZero=e=>{let{allowCreate:t=!1,className:a="rsz",max:n=1/0,multi:r=!1,name:c,noSearch:s=!1,onChange:l,options:o,placeholder:i="Select",value:u=[]}=e,m=_objectWithoutProperties(e,["allowCreate","className","max","multi","name","noSearch","onChange","options","placeholder","value"]);const p=useRef(null),d=useRef(null),E=useRef(null),[f,_]=useState(0),[h,b]=useState(""),[N,R]=useState(!1),g=[],v=[];o.forEach((e,t)=>{new RegExp(h,"i").test(e)&&(v.push(e),g.push(t))});const y=!0!==s&&o.length>4,z=!0===t&&h.length>0&&-1===o.findIndex(e=>e===h),O=u.length===n;function k(e){return"rsz-".concat(c,"-").concat(e)}function w(e){return k("option-".concat(e))}function x(e,t){if(e){const a=e.querySelector(t);a&&a.scrollIntoView(!1)}}function S(e){if(!0===r){let t=[...u];const a=t.indexOf(e);return-1!==a?t.splice(a,1):t.length<n&&t.push(e),-1!==o.indexOf(e)&&(t=o.filter(e=>-1!==t.indexOf(e))),void l(t)}l([e]),R(!1)}return useEffect(()=>{d.current&&N?d.current.focus():E.current&&!N&&E.current.focus()}),useEffect(()=>{-1===g.indexOf(f)&&_(g[0])},[f,g]),React.createElement("div",Object.assign({"aria-expanded":!0===N,"aria-multiselectable":!0===r||void 0,className:a,role:"listbox"},m),React.createElement("div",{className:"rsz__trigger"},React.createElement("button",{"aria-controls":k("menu"),"aria-haspopup":"listbox",className:"rsz__trigger-button",onClick:()=>R(!0),onKeyDown:e=>{e.key===KEY.DOWN&&R(!0)},ref:E,type:"button"},!0===r?"Select options for ".concat(c):"Select an option for ".concat(c),React.createElement("span",{"aria-hidden":!0,className:"rsz__arrow"},"↓")),u.length>0?React.createElement("ul",{className:"rsz__selection"},u.map(e=>React.createElement("li",{key:e,className:"rsz__selected"},React.createElement("span",{className:"rsz__selected-text"},e),React.createElement("button",{"aria-label":"remove ".concat(e),className:"rsz__selected-action",onClick:()=>S(e),type:"button"},"✕")))):React.createElement("div",{className:"rsz__selection rsz__placeholder"},i)),React.createElement("div",{"aria-label":"Close dropdown",className:"rsz__overlay",onClick:()=>R(!1)}),React.createElement("menu",{id:k("menu"),className:"rsz__dropdown-wrapper"},React.createElement("div",{className:"rsz__dropdown"},React.createElement("input",{"aria-activedescendant":w(f),"aria-hidden":!1===y||void 0,"aria-label":"Filter options",className:"rsz__search",onChange:e=>b(e.target.value),onKeyDown:function(e){const t=g[0],a=g[g.length-1];switch(e.key){case KEY.ENTER:e.preventDefault(),S(o[f]);break;case KEY.DOWN:case KEY.UP:{e.preventDefault();const n=e.key===KEY.UP?-1:1,r=e.key===KEY.UP?a:t,c=g.indexOf(f)+n,s=void 0!==g[c]?g[c]:r;_(s),x(p.current,"#".concat(w(s)));break}case KEY.END:case KEY.HOME:{const n=e.key===KEY.HOME?t:a;_(n),x(p.current,"#".concat(w(n)));break}case KEY.ESC:R(!1);break;default:s&&e.preventDefault()}},ref:d,type:"search",value:h}),React.createElement("div",{className:"rsz__search-icon"}),React.createElement("ul",{className:"rsz__option-list",ref:p},v.map((e,t)=>{const a=-1!==u.indexOf(e);let n=a;return!1===n&&!0===O&&(n=void 0),React.createElement("li",{className:"rsz__option",key:e},React.createElement("button",{"aria-selected":n,"data-highlighted":f===g[t]||void 0,disabled:O&&!a,id:w(g[t]),onClick:()=>S(e),role:"option",type:"button"},e))}),o.length>0&&0===v.length&&React.createElement("span",{className:"rsz__no-results"},"No results for “",h,"”"),z&&React.createElement("li",{className:"rsz__option"},React.createElement("button",{className:"rsz__create","data-highlighted":f===o.length||void 0,disabled:O,id:w(o.length),onClick:()=>S(h),type:"button"},"Create ",React.createElement("span",{className:"rsz__search-term"},h))),!0===t&&""===h&&React.createElement("li",{className:"rsz__option"},React.createElement("span",{className:"rsz__create"},"Start typing to create an item"))))),React.createElement("input",{type:"hidden",name:c,value:u.join(",")}))};export default SelectZero;
{
"name": "@manifoldco/react-select-zero",
"description": "Zero-dependency, a11y multiselect React component",
"version": "0.0.1-0",
"version": "0.0.1-1",
"license": "ISC",

@@ -6,0 +6,0 @@ "files": [

@@ -9,3 +9,3 @@ # 🥢 React Select Zero

[react-select][react-select]. Supports single selection, multiselection,
search, and full keyboard controls in a handsome `8 KB` component (`2.5 KB`
search, and full keyboard controls in a handsome `5 KB` component (`1.8 KB`
gzipped).

@@ -23,3 +23,3 @@

| :------------------------------------- | ---------: | --------------: |
| `@manifoldco/react-select-zero` | 🔥`8 KB`🔥 | `2.5 KB` |
| `@manifoldco/react-select-zero` | 🔥`5 KB`🔥 | `1.8 KB` |
| `@zendeskgarden/react-selection@6.0.1` | `26.6 KB` | `6.6 KB` |

@@ -39,9 +39,14 @@ | `downshift` | `21.9 KB` | `7.1 KB` |

```jsx
<Select
name="pokemon"
options={['Bulbasaur', 'Charmander', 'Squirtle']}
onChange={selected => console.log(selected)} // ['Bulbasaur']
>
Select a Pokémon
</Select>
const [selection, setSelection] = useState([]);
return (
<Select
name="pokemon"
options={['Bulbasaur', 'Charmander', 'Squirtle']}
onChange={setSelection} // ['Bulbasaur']
value={selection}
>
Select a Pokémon
</Select>
);
```

@@ -54,13 +59,50 @@

```jsx
<Select name="pokemon" options={['Bulbasaur', 'Charmander', 'Squirtle']} multi>
Select a Pokémon
</Select>
const [selection, setSelection] = useState([]);
return (
<Select
multi
name="pokemon"
onChange={setSelection}
options={['Bulbasaur', 'Charmander', 'Squirtle']}
value={selection}
>
Select a Pokémon
</Select>
);
```
### Set initial selection
```jsx
const [selection, setSelection] = useState(['Bulbasaur']);
return (
<Select
name="pokemon"
onChange={setSelection}
options={['Bulbasaur', 'Charmander', 'Squirtle']}
value={selection}
>
Select a Pokémon
</Select>
);
```
### Hide search (shown by default)
```jsx
<Select name="pokemon" options={['Bulbasaur', 'Charmander', 'Squirtle']} noSearch>
Select a Pokémon
</Select>
const [selection, setSelection] = useState([]);
return (
<Select
noSearch
name="pokemon"
onChange={setSelection}
options={['Bulbasaur', 'Charmander', 'Squirtle']}
value={selection}
>
Select a Pokémon
</Select>
);
```

@@ -73,13 +115,15 @@

```jsx
<Select
name="pokemon"
options={['Bulbasaur', 'Charmander', 'Squirtle']}
allowCreate
onChange={(selected, created) => {
console.log(selected); // []
console.log(created); // ['Missingno']
}}
>
Select a Pokémon
</Select>
const [selection, setSelection] = useState([]);
return (
<Select
name="pokemon"
options={['Bulbasaur', 'Charmander', 'Squirtle']}
allowCreate
onChange={setSelection}
value={selection}
>
Select a Pokémon
</Select>
);
```

@@ -91,13 +135,14 @@

| Name | Type | Default | Description |
| :------------- | :--------- | :--------- | :---------------------------------------------------------------------------------------------- |
| **`name`** | `string` | | **Required** Form name of this input. Query this like a normal form input. Also assits in a11y. |
| **`options`** | `string[]` | | **Required**: Array of strings to display as options |
| `allowCreate` | `boolean` | `false` | Set `<Select allowCreate />` to allow creating new entries (note: `noSearch` can’t be set) |
| `defaultValue` | `string[]` | `[]` | Set `<Select defaultValue={['Charmander']} />` to set the default selected items. |
| `max` | `number` | `Infinity` | Set maximum number of items (only works with `multi`) |
| `multi` | `boolean` | `false` | Set `<Select multi />` to allow multiple selection |
| `noSearch` | `boolean` | `false` | Set `<Select noSearch />` to hide searching (by default shows with > 5 options) |
| `onChange` | `Function` | | Callback to fire when value changes |
| `placeholder` | `string` | | Specify placeholder text |
| Name | Type | Default | Description |
| :------------- | :------------------- | :--------- | :---------------------------------------------------------------------------------------------- |
| **`name`** | `string` | | **Required** Form name of this input. Query this like a normal form input. Also assits in a11y. |
| **`onChange`** | `(string[]) => void` | | **Required** Form callback called when state changes |
| **`options`** | `string[]` | | **Required** Array of strings to display as options |
| **`value`** | `string[]` | | **Required** Set selected values |
| `allowCreate` | `boolean` | `false` | Set `<Select allowCreate />` to allow creating new entries (note: `noSearch` can’t be set) |
| `max` | `number` | `Infinity` | Set maximum number of items (only works with `multi`) |
| `multi` | `boolean` | `false` | Set `<Select multi />` to allow multiple selection |
| `noSearch` | `boolean` | `false` | Set `<Select noSearch />` to hide searching (by default shows with > 5 options) |
| `onChange` | `Function` | | Callback to fire when value changes |
| `placeholder` | `string` | | Specify placeholder text |

@@ -104,0 +149,0 @@ ## 💅 Styling

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