fancy-select
Advanced tools
Comparing version 2.0.0 to 2.0.1
@@ -34,3 +34,3 @@ // compile this with: | ||
{value: 'analy-amante', label: 'Analy "Feline" Amante', certs: 'V'}, | ||
{value: 'river-brigden', label: 'River "Hiccup" Brigden', certs: 'V'}, | ||
{value: 'river-brigden', label: 'River "Hiccup" Brigden', certs: 'V'} | ||
].sort(sort) | ||
@@ -50,3 +50,3 @@ }, { | ||
var remove = hg.app(document.body, comp.state, render) | ||
hg.app(document.body, comp, render) | ||
} | ||
@@ -53,0 +53,0 @@ |
233
index.js
@@ -1,11 +0,9 @@ | ||
var mercury = require('mercury') | ||
var document = require('global/document') | ||
var hg = require('mercury') | ||
var stringWidth = require('styled-string-width') | ||
var OptionTree = require('option-tree') | ||
var raf = require('raf') | ||
var Update = require('./update') | ||
var render = require('./render') | ||
var slice = Array.prototype.slice | ||
var Key = {ENTER: 13, BACKSPACE: 8, UP: 38, DOWN: 40, ESCAPE: 27, TAB: 9} | ||
@@ -19,85 +17,184 @@ FancySelect.render = render.default | ||
data = data || {} | ||
data.filter = data.filter || defaultFilter | ||
data.filter = data.filter || function (opt, query, value) { | ||
// keep any that start with __ | ||
if (opt.value && opt.value.indexOf('__') === 0) return {keep: true} | ||
var tree = OptionTree(data) | ||
// omit value | ||
for (var i = value.length - 1; i >= 0; i--) { | ||
if (opt.value === value[i].value) { | ||
return {keep: false, passes: false, keepChildren: false} | ||
} | ||
var placeholder = hg.value(data.placeholder || '') | ||
var separator = hg.value(data.separator || 188) | ||
var isOpen = hg.value(false) | ||
var selectOnBlur = hg.value(data.selectOnBlur || false) | ||
var clearQueryOnSelect = hg.value(data.clearQueryOnSelect || false) | ||
var inputWidth = hg.computed([tree.query, placeholder], function (s1, s2) { | ||
var el = '.fancy-select input' | ||
var width = Math.max(stringWidth(s1, el), stringWidth(s2, el)) | ||
return width + 20 // hack to fix browser subtleties | ||
}) | ||
return hg.struct({ | ||
channels: { | ||
clickOption: function (params) { | ||
tree.channels.select(params.data) | ||
if (clearQueryOnSelect()) { | ||
tree.query.set('') | ||
} | ||
focusInput(params.event) | ||
}, | ||
setQuery: function (data) { | ||
tree.query.set(data.query) | ||
}, | ||
next: function () { | ||
tree.channels.next() | ||
}, | ||
inputEvent: inputEvent, | ||
focusInput: focusInput, | ||
focusOnClick: focusOnClick | ||
}, | ||
// local | ||
isOpen: isOpen, | ||
selectOnBlur: selectOnBlur, | ||
clearQueryOnSelect: clearQueryOnSelect, | ||
separator: separator, | ||
placeholder: placeholder, | ||
inputWidth: inputWidth, | ||
// exported from option tree | ||
options: tree.options, | ||
value: tree.value, | ||
filtered: tree.filtered, | ||
query: tree.query, | ||
active: tree.active, | ||
actions: tree.actions | ||
}) | ||
function inputEvent (e) { | ||
if (e.type === 'focus') { | ||
isOpen.set(true) | ||
} | ||
// match query | ||
try { | ||
var regex = new RegExp(query || '', 'i') | ||
return { | ||
keepChildren: true, | ||
passes: ( | ||
(opt.value && regex.test(opt.value)) || | ||
(opt.label && regex.test(opt.label)) | ||
) | ||
if (e.type === 'blur') { | ||
blur(e) | ||
} | ||
if (e.type === 'keydown') { | ||
keydown(e) | ||
} | ||
} | ||
function blur (e) { | ||
// TODO: this will likely stop working until | ||
// https://github.com/Raynos/dom-delegator/pull/4 is fixed | ||
var related = e.relatedTarget | ||
var notInside = !e.currentTarget.parentNode.parentNode.contains(related) | ||
if (!related || notInside) { | ||
// console.log('related element', related) | ||
if (related && isOpen() && selectOnBlur()) { | ||
tree.channels.select(tree.active()) | ||
tree.query.set('') | ||
} | ||
} catch (e) { | ||
return {passes: false} | ||
isOpen.set(false) | ||
raf(function () { | ||
if (related) { | ||
related.focus() | ||
} | ||
}) | ||
} | ||
} | ||
var tree = OptionTree(data) | ||
function keydown (e) { | ||
var code = e.keyCode === separator() ? Key.ENTER : e.keyCode | ||
var events = mercury.input([ | ||
'backspace', 'select', 'dropdown', 'input', | ||
'refocus', 'close', 'next', 'prev' | ||
]) | ||
// must be open when accepting keys | ||
if (code !== Key.TAB && code !== Key.ESCAPE && !isOpen()) { | ||
isOpen.set(true) | ||
} | ||
// prevent default for the following | ||
if ([Key.ENTER, Key.DOWN, Key.UP].indexOf(code) !== -1) { | ||
e.preventDefault() | ||
} | ||
var placeholder = mercury.value(data.placeholder || '') | ||
if (code === Key.ESCAPE) { | ||
isOpen.set(false) | ||
e.currentTarget.blur() | ||
} | ||
var state = mercury.struct({ | ||
events: events, | ||
if (code === Key.BACKSPACE) { | ||
if (!tree.query()) { | ||
tree.channels.pop() | ||
} | ||
} | ||
options: tree.state.options, | ||
value: tree.state.value, | ||
filtered: tree.state.filtered, | ||
query: tree.state.query, | ||
active: tree.state.active, | ||
if (code === Key.ENTER) { | ||
tree.channels.select(tree.active()) | ||
if (clearQueryOnSelect()) { | ||
tree.query.set('') | ||
} | ||
focusInput(e) | ||
} | ||
isOpen: mercury.value(false), | ||
if (code === Key.DOWN) { | ||
tree.channels.next() | ||
} | ||
placeholder: placeholder, | ||
separator: mercury.value(data.separator || 188), | ||
if (code === Key.UP) { | ||
tree.channels.prev() | ||
} | ||
} | ||
} | ||
inputWidth: mercury.computed([ | ||
tree.state.query, placeholder | ||
], function maxWidth () { | ||
var one, max = 0, strs = slice.call(arguments) | ||
strs.forEach(function (str) { | ||
one = stringWidth(str, '.fancy-select input') | ||
if (one > max) max = one | ||
}) | ||
return max + 10 | ||
}) | ||
}) | ||
function defaultFilter (opt, query, value) { | ||
// keep any that start with __ | ||
if (opt.value && opt.value.indexOf('__') === 0) return {keep: true} | ||
// wire up events | ||
events.select(Update.select.bind(null, state, tree)) | ||
events.backspace(Update.backspace.bind(null, state, tree)) | ||
events.input(Update.input.bind(null, state, tree)) | ||
events.next(Update.next.bind(null, state, tree)) | ||
events.prev(Update.prev.bind(null, state, tree)) | ||
// omit value | ||
for (var i = value.length - 1; i >= 0; i--) { | ||
if (opt.value === value[i].value) { | ||
return {keep: false, passes: false, keepChildren: false} | ||
} | ||
} | ||
events.focusBackground = Update.focusBackground.bind(null, state) | ||
events.inputEvent = Update.inputEvent.bind(null, state) | ||
events.setOpen = Update.setOpen.bind(null, state) | ||
events.clickOption = Update.clickOption.bind(null, state) | ||
// match query | ||
try { | ||
var regex = new RegExp(query || '', 'i') | ||
return { | ||
keepChildren: true, | ||
passes: ( | ||
(opt.value && regex.test(opt.value)) || | ||
(opt.label && regex.test(opt.label)) | ||
) | ||
} | ||
} catch (e) { | ||
return {passes: false} | ||
} | ||
} | ||
return { | ||
state: state, | ||
function focusInput (ev) { | ||
var node = ev.currentTarget | ||
setOptions: tree.setOptions, | ||
setValue: tree.setValue, | ||
setFilter: tree.setFilter, | ||
setActions: tree.setActions, | ||
setQuery: tree.setQuery | ||
while (node && !isRootNode(node)) { | ||
node = node.parentNode | ||
} | ||
if (node) { | ||
raf(function () { | ||
node.children[0].children[1].focus() | ||
}) | ||
} | ||
} | ||
function focusOnClick (ev) { | ||
if (ev.type === 'click') { | ||
focusInput(ev) | ||
} | ||
} | ||
function isRootNode (node) { | ||
return node.className.split(/\s/).indexOf('fancy-select') !== -1 | ||
} |
{ | ||
"name": "fancy-select", | ||
"description": "a unidirectional combobox component based on the aria spec", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"repository": { | ||
@@ -17,11 +17,10 @@ "url": "nrw/fancy-select" | ||
"array-equal": "^1.0.0", | ||
"brfs": "^1.4.1", | ||
"global": "^4.2.1", | ||
"insert-css": "^0.2.0", | ||
"mercury": "^6.0.1", | ||
"mercury": "^14.1.0", | ||
"option-tree": "^1.1.0", | ||
"brfs": "^1.2.0", | ||
"styled-string-width": "^0.1.2" | ||
}, | ||
"devDependencies": { | ||
"virtual-hyperscript": "^4.5.0", | ||
"function-bind": "^1.0.0", | ||
@@ -31,5 +30,7 @@ "nib": "^1.0.3", | ||
"synthetic-dom-events": "^0.2.2", | ||
"tape": "^2.14.0", | ||
"zuul": "^1.10.1" | ||
"tape": "^4.2.0", | ||
"virtual-hyperscript": "^4.5.0", | ||
"zuul": "^3.4.0" | ||
}, | ||
"license": "MIT", | ||
"browserify": { | ||
@@ -36,0 +37,0 @@ "transform": [ |
var fs = require('fs') | ||
var mercury = require('mercury') | ||
var hg = require('mercury') | ||
var arrayEqual = require('array-equal') | ||
var slice = Array.prototype.slice | ||
var h = mercury.h | ||
var h = hg.h | ||
var insertCss = require('insert-css') | ||
insertCss(fs.readFileSync(__dirname + '/styles/core.css', 'utf8')) | ||
var sendContext = hg.BaseEvent(function contextLambda (ev, broadcast) { | ||
broadcast(ev) | ||
}) | ||
var sendDataContext = hg.BaseEvent(function contextLambda (ev, broadcast) { | ||
broadcast({event: ev, data: this.data}) | ||
}) | ||
module.exports = { | ||
@@ -25,4 +34,4 @@ custom: custom, | ||
function template (overrides, state, name) { | ||
var temp = overrides && overrides[name] ? | ||
overrides[name] : templates[name] | ||
var temp = overrides && overrides[name] | ||
? overrides[name] : templates[name] | ||
return temp.apply(null, [state, template.bind(null, overrides, state)] | ||
@@ -35,3 +44,3 @@ .concat(slice.call(arguments, 3)) | ||
combobox: function (state, template) { | ||
return h('div.fancy-select', [ | ||
return h('div.fancy-select', {className: state.isOpen ? 'focused' : ''}, [ | ||
template('textbox'), | ||
@@ -43,3 +52,3 @@ template('listbox') | ||
return h('div.background', { | ||
'ev-click': state.events.focusBackground | ||
'ev-click': sendContext(state.channels.focusInput) | ||
}, [ | ||
@@ -53,4 +62,4 @@ h('div.list', [ | ||
listbox: function (state, template) { | ||
return !state.isOpen ? null : | ||
h('div.listbox', template('group', state.filtered)) | ||
return !state.isOpen ? null | ||
: h('div.listbox', template('group', state.filtered)) | ||
}, | ||
@@ -77,3 +86,3 @@ group: function (state, template, items, base) { | ||
className: option.value && arrayEqual(path, state.active) ? 'focused' : '', | ||
'ev-click': state.events.clickOption.bind(null, path) | ||
'ev-click': sendDataContext(state.channels.clickOption, path) | ||
}, template('optionlabel', option, path)) | ||
@@ -96,8 +105,6 @@ }, | ||
'ev-event': state.events.inputEvent, | ||
'ev-input': mercury.valueEvent(state.events.input, { | ||
preventDefault: false | ||
}) | ||
'ev-input': hg.sendValue(state.channels.setQuery, {}), | ||
'ev-event': sendContext(state.channels.inputEvent) | ||
}) | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
216828
0
6268
7
1
+ Addedap@0.1.0(transitive)
+ Addedbody@0.1.0(transitive)
+ Addedbrowser-split@0.0.1(transitive)
+ Addedcontent-types@0.1.0(transitive)
+ Addeddom-delegator@13.1.0(transitive)
+ Addederror@5.2.0(transitive)
+ Addedev-store@7.0.0(transitive)
+ Addedform-data-set@2.0.0(transitive)
+ Addedhttp-hash@1.1.1(transitive)
+ Addedhttp-hash-router@1.1.2(transitive)
+ Addedhttp-methods@1.1.0(transitive)
+ Addedindividual@3.0.0(transitive)
+ Addedis-error@2.2.2(transitive)
+ Addedis-object@1.0.2(transitive)
+ Addediterators@0.1.0(transitive)
+ Addedmain-loop@3.4.0(transitive)
+ Addedmercury@14.2.0(transitive)
+ Addedobserv-array@3.2.1(transitive)
+ Addedobserv-varhash@1.0.8(transitive)
+ Addedvalue-event@5.1.1(transitive)
+ Addedvirtual-dom@2.1.1(transitive)
+ Addedx-is-string@0.1.0(transitive)
- Removeddata-set@3.1.0(transitive)
- Removeddom-delegator@9.2.2(transitive)
- Removedform-data-set@1.2.0(transitive)
- Removedglobal@4.2.1(transitive)
- Removedis-object@0.1.2(transitive)
- Removedmain-loop@2.3.1(transitive)
- Removedmercury@6.1.1(transitive)
- Removednode-hook@0.1.0(transitive)
- Removedobserv-struct@4.2.0(transitive)
- Removedobserv-varhash@0.2.0(transitive)
- Removedprocess@0.5.2(transitive)
- Removedvalue-event@2.3.0(transitive)
- Removedvdom@0.0.16(transitive)
- Removedvirtual-hyperscript@4.6.0(transitive)
- Removedvtree@0.0.16(transitive)
- Removedweakmap-shim@1.0.0(transitive)
Updatedbrfs@^1.4.1
Updatedmercury@^14.1.0