Socket
Socket
Sign inDemoInstall

nuke-input

Package Overview
Dependencies
Maintainers
3
Versions
185
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nuke-input - npm Package Compare versions

Comparing version 0.1.8 to 0.1.9

87

docs/basic.md

@@ -13,3 +13,3 @@ # 类型

import {createElement, Component} from 'weex-rx';
import { View, Text} from 'nuke-components';
import { View, Text, ScrollView} from 'nuke-components';
import Input from 'nuke-input';

@@ -24,6 +24,10 @@ import {mount} from 'nuke-mounter';

this.state = {
inputVal:'',
d1:'',
d2:'',
d3:'',
textVal:'',
valid1:'',
valid2:'',
data1:'',
data2:''
}

@@ -39,9 +43,23 @@

change=(e)=>{
console.log(e)
c1=(e)=>{
console.log('onChange======>',e)
this.setState({
d1: e
})
}
c2=(e)=>{
let value=e.value || e.target.value || '';
console.log('c2 onInput==>',value)
this.setState({
inputVal: value
d2 : value
})
}
c3=(e)=>{
console.log('c3 onChange======>',e)
this.setState({
d3: e
})
}
textChange=(e)=>{

@@ -54,20 +72,60 @@ console.log(e)

}
d1change=(e)=>{
console.log(e)
let value=e.value || e.target.value || '';
this.setState({
d1: value
})
}
d2change=(e)=>{
console.log(e)
let value=e.value || e.target.value || '';
this.setState({
d2: value
})
}
render() {
return (
<View style={{paddingTop:'30rem'}}>
<View style={styles.st}><Text style={styles.stText}>单行</Text></View>
<Input ref="myinput" placeholder="搜一搜..." value={this.state.inputVal} onChange={this.change.bind(this)} />
<ScrollView style={{paddingTop:'30rem'}}>
<View style={styles.st}><Text style={styles.stText}>input inset 使用 onChange 事件</Text></View>
<View style={[styles.row,{paddingLeft:0,paddingRight:0}]}><Input placeholder="搜一搜..." type="inset" value={this.state.d1} onChange={this.c1.bind(this)} style={{backgroundColor:'#f3f3f3'}} /></View>
<Text>输入的是:{this.state.d1}</Text>
<View style={styles.st}><Text style={styles.stText}>input onInput 实时获取</Text></View>
<View style={styles.row}><Input placeholder="搜一搜..." defaultValue="羊绒大衣" onInput={this.c2.bind(this)} /></View>
<Text>实时获取:{this.state.d2}</Text>
<View style={styles.st}><Text style={styles.stText}>自定义样式</Text></View>
<View style={styles.row}><Input style={{borderWidth:0,backgroundColor:'#3089dc',color:'#ffffff'}} /></View>
<View style={styles.st}><Text style={styles.stText}>带clear按钮</Text></View>
<View style={styles.row}><Input hasClear={true} placeholder="搜一搜..." value={this.state.d3} onChange={this.c3.bind(this)} /></View>
<View style={styles.st}><Text style={styles.stText}>输入校验success反馈</Text></View>
<Input hasFeedback="true" onChange={this.validate.bind(this,'success','valid1')} state={this.state.valid1} />
<View style={styles.row}><Input hasFeedback="true" onChange={this.validate.bind(this,'success','valid1')} state={this.state.valid1} /></View>
<View style={styles.st}><Text style={styles.stText}>输入校验error反馈</Text></View>
<Input hasFeedback="true" placeholder="请输入..." onChange={this.validate.bind(this,'error','valid2')} state={this.state.valid2} />
<View style={styles.row}><Input hasFeedback="true" placeholder="请输入..." onChange={this.validate.bind(this,'error','valid2')} state={this.state.valid2} /></View>
<View style={styles.st}><Text style={styles.stText}>textarea</Text></View>
<Input style={{height:'300rem'}} multiple={true} ref="xxx" placeholder="写一句自我介绍" onInput={this.textChange} value={this.state.textVal} onFocus={(e) => console.log('onFocus',e)} onBlur={(e) => console.log('onBlur',e)}/>
<View style={styles.row}><Input style={{height:'300rem',marginBottom:'20rem'}} multiple={true} placeholder="设置高度300rem" onFocus={(e) => console.log('onFocus',e)} onBlur={(e) => console.log('onBlur',e)}/></View>
<View style={styles.row}><Input multiple={true} placeholder="介绍一下" value="大家好我是xxxx"
/></View>
</View>
<View style={styles.st}><Text style={styles.stText}>read only</Text></View>
<View style={[styles.row,{marginBottom:'10rem'}]}><Input readOnly placeholder="输入姓名" defaultValue="只读"/></View>
<View style={styles.row}><Input style={{height:'400rem',marginBottom:'10rem'}} readOnly multiple={true} placeholder="输入简介..." defaultValue="只读textarea"/></View>
<View style={styles.st}><Text style={styles.stText}>disabled</Text></View>
<View style={[styles.row,{marginBottom:'10rem'}]}><Input disabled placeholder="输入姓名" defaultValue="不可用"/></View>
<View style={styles.row}><Input style={{height:'400rem',marginBottom:'10rem'}} disabled multiple={true} ref="d2" placeholder="输入简介..." defaultValue="不可用textarea"/></View>
</ScrollView>
);

@@ -77,2 +135,7 @@ }

const styles={
row:{
paddingLeft:'30rem',
paddingRight:'30rem',
},
st:{

@@ -79,0 +142,0 @@ marginBottom:'30rem',

# Changelog
## 0.1.9 / 2016-12-19
* [[be5c171](http://gitlab.alibaba-inc.com/nuke/input/commit/be5c171dadcad54c06a1cc5cc80f8e5bf6cd4b97)] - `feat` 重构input
* [[c503b16](http://gitlab.alibaba-inc.com/nuke/input/commit/c503b16326db3e6856896f509cf15581bcb26c0a)] - `docs` update docs
## 0.1.7 / 2016-11-30

@@ -5,0 +10,0 @@

475

lib/index.js

@@ -27,2 +27,6 @@ /** @jsx createElement */

var _nukeButton = require('nuke-button');
var _nukeButton2 = _interopRequireDefault(_nukeButton);
var _nukeEnv = require('nuke-env');

@@ -36,2 +40,4 @@

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 _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

@@ -61,2 +67,15 @@

/**
* 构造函数
*/
/**
* 设置默认状态
*/
/**
* 声明属性类型
*/
function Input(props) {

@@ -67,2 +86,8 @@ _classCallCheck(this, Input);

_this.state = {
focus: false,
value: _this.getPropValue('value') || ''
};
_this.changeHandler = _this.changeHandler.bind(_this);

@@ -72,23 +97,222 @@ _this.focusHandler = _this.focusHandler.bind(_this);

_this.clearHandler = _this.clearHandler.bind(_this);
_this.state = {
focus: false,
clearButton: true,
state: '',
value: _this.props.value || ''
};
_this.timer = null;
return _this;
}
/**
* 设置默认属性
*/
_createClass(Input, [{
key: 'componentWillReceiveProps',
value: function componentWillReceiveProps(newProps) {}
value: function componentWillReceiveProps(newProps) {
this.updateStateFromProps(newProps, ['type', 'value']);
}
/**
* 渲染: 当state发生改变时立即执行
*/
}, {
key: 'renderClear',
value: function renderClear() {
//如果已经设置了feedback, 则不出现clear
key: 'render',
value: function render() {
var _props = this.props,
hasFeedback = _props.hasFeedback,
hasClear = _props.hasClear,
_props$prefix = _props.prefix,
prefix = _props$prefix === undefined ? this.defaultPrefix : _props$prefix,
className = _props.className,
state = _props.state,
defaultValue = _props.defaultValue,
value = _props.value,
onChange = _props.onChange,
onFocus = _props.onFocus,
onBlur = _props.onBlur,
readOnly = _props.readOnly,
size = _props.size,
disabled = _props.disabled,
_props$style = _props.style,
style = _props$style === undefined ? {} : _props$style,
addonBefore = _props.addonBefore,
addonAfter = _props.addonAfter,
multiple = _props.multiple,
readOnly = _props.readOnly,
hasFeedback = _props.hasFeedback;
_props$type = _props.type,
type = _props$type === undefined ? 'enclosed' : _props$type,
others = _objectWithoutProperties(_props, ['hasFeedback', 'hasClear', 'prefix', 'className', 'state', 'defaultValue', 'value', 'onChange', 'onFocus', 'onBlur', 'readOnly', 'size', 'disabled', 'style', 'addonBefore', 'addonAfter', 'multiple', 'type']);
// style.textAlign = this.getValueFromContextAndProp('align') || 'left';
// const type = this.getValueFromContextAndProp('type') || 'enclosed';
//如果当前的没有设置, 则取上下文的state
if (state === undefined) state = this.context.state;
var baseClass = 'input';
var newStyle = Object.assign({}, _index2.default['input'], _index2.default[size], _index2.default['' + type], _index2.default['' + (multiple ? 'multiple' : 'single')], _index2.default[type + '-' + (multiple ? 'multiple' : 'single')], this.state.focus ? _index2.default['focus'] : {}, readOnly ? _index2.default[(multiple ? 'multiple' : 'single') + '-readonly'] : {}, disabled ? _index2.default[type + '-' + (multiple ? 'multiple' : 'single') + '-disabled'] : {}, _index2.default[type + '-' + (multiple ? 'multiple' : 'single') + '-' + state], style
// {
// [state]: state
);
var attrs = {
// style: style,
onChange: this.changeHandler,
onFocus: this.focusHandler,
onBlur: this.blurHandler,
disabled: disabled,
readOnly: readOnly,
value: this.state.value,
defaultValue: defaultValue
};
// 防止重复定义带来的warning
if ('value' in attrs) {
delete attrs.defaultValue;
}
// 绕过h5下 readOnly 默认值问题
if ('readOnly' in attrs && !attrs.readOnly) {
delete attrs.readOnly;
}
var inputElement = void 0;
var flexVal = 'flex-' + (10 - (addonBefore ? 1 : 0) - (addonAfter ? 1 : 0));
var isAddonMode = (addonBefore || addonAfter) && !multiple;
var customStyleFilter = {};
['color', 'fontSize', 'fontStyle', 'fontWeight', 'lineHeight', 'textAlign', 'textDecoration'].forEach(function (item) {
if (style[item]) {
// debugger;
customStyleFilter[item] = style[item];
}
});
var inputStyle = Object.assign({}, _index2.default['input-inner'], _index2.default[size], _index2.default[type + '-' + (multiple ? 'multiple' : 'single') + '-input'], _index2.default[(multiple ? 'multiple' : 'single') + '-input'], hasFeedback ? _index2.default['has-feedback-input'] : {}, hasClear && this.state.focus ? _index2.default['has-clear-input'] : {}, this.state.focus ? _index2.default['focus-input'] : {}, readOnly ? _index2.default[(multiple ? 'multiple' : 'single') + '-readonly-input'] : {}, disabled ? _index2.default[type + '-' + (multiple ? 'multiple' : 'single') + '-disabled-input'] : {}, customStyleFilter);
if (multiple) {
if (_nukeEnv.isWeb) {
inputElement = (0, _weexRx.createElement)(
'textarea',
_extends({ style: inputStyle }, attrs, this.pickAttrs(others)),
attrs.value
);
} else {
inputElement = (0, _weexRx.createElement)('textarea', _extends({ style: inputStyle }, attrs, this.pickAttrs(others)));
}
} else {
inputElement = (0, _weexRx.createElement)('input', _extends({}, attrs, this.pickAttrs(others), {
type: this.props.htmlType,
style: inputStyle
}));
}
return (0, _weexRx.createElement)(
_nukeComponents.View,
{ style: newStyle },
(0, _weexRx.createElement)(
_nukeComponents.View,
{ x: 'innner-flex', style: _index2.default['inner-flex'] },
this.renderAddonBefore(baseClass, isAddonMode),
(0, _weexRx.createElement)(
_nukeComponents.View,
{ x: 'innner-input-box', style: [_index2.default['inner-input-box'], _index2.default[flexVal]] },
inputElement,
this.renderClear(baseClass),
this.renderFeedback(baseClass),
this.renderCounter(baseClass)
),
this.renderAddonAfter(baseClass, isAddonMode)
)
);
}
}, {
key: 'getPropValue',
value: function getPropValue(key) {
var capitalKey = key.charAt(0).toUpperCase() + key.slice(1);
var name = this.props[key] !== undefined ? key : 'default' + capitalKey;
return this.props[name];
}
}, {
key: 'getValueFromContextAndProp',
value: function getValueFromContextAndProp(key) {
var value = this.context[key];
if (this.props.hasOwnProperty(key)) {
value = this.props[key];
}
return value;
}
}, {
key: 'renderAddonBefore',
value: function renderAddonBefore(baseClass, isAddonMode) {
if (this.props.addonBefore && isAddonMode) {
return (0, _weexRx.createElement)(
'div',
{ style: [_index2.default[baseClass + '-addon'], _index2.default[baseClass + '-addon-before']] },
this.props.addonBefore
);
}
}
}, {
key: 'renderAddonAfter',
value: function renderAddonAfter(baseClass, isAddonMode) {
if (this.props.addonAfter && isAddonMode) {
return (0, _weexRx.createElement)(
'div',
{ style: [_index2.default[baseClass + '-addon'], _index2.default[baseClass + '-addon-after']] },
this.props.addonAfter
);
}
}
}, {
key: 'pickAttrs',
value: function pickAttrs(props) {
var attributes = 'accept acceptCharset accessKey action allowFullScreen allowTransparency\nalt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge\ncharSet checked classID className colSpan cols content contentEditable contextMenu\ncontrols coords crossOrigin data dateTime default defer dir disabled download draggable\nencType form formAction formEncType formMethod formNoValidate formTarget frameBorder\nheaders height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity\nis keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media\nmediaGroup method min minLength multiple muted name noValidate nonce open\noptimum pattern placeholder poster preload radioGroup readOnly rel required\nreversed role rowSpan rows sandbox scope scoped scrolling seamless selected\nshape size sizes span spellCheck src srcDoc srcLang srcSet start step style\nsummary tabIndex target title type useMap value width wmode wrap'.replace(/\s+/g, ' ').replace(/\t|\n|\r/g, '').split(' '),
eventsName = 'onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown\n onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick\n onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown\n onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onSelect onTouchCancel\n onTouchEnd onTouchMove onTouchStart onScroll onWheel onAbort onCanPlay onCanPlayThrough\n onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata\n onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting onLoad onError'.replace(/\s+/g, ' ').replace(/\t|\n|\r/g, '').split(' '),
attrsPrefix = ['data-', 'aria-'];
var attrs = {};
for (var key in props) {
if (attributes.indexOf(key) > -1 || eventsName.indexOf(key) > -1) {
attrs[key] = props[key];
} else if (attrsPrefix.map(function (prefix) {
return new RegExp('^' + prefix);
}).some(function (reg) {
return key.replace(reg, '') != key;
})) {
attrs[key] = props[key];
}
}
return attrs;
}
//计数器
}, {
key: 'renderCounter',
value: function renderCounter(baseClass) {
var _props2 = this.props,
multiple = _props2.multiple,
maxLength = _props2.maxLength,
hasLimitHint = _props2.hasLimitHint;
if (!(multiple && maxLength && hasLimitHint)) return;
return (0, _weexRx.createElement)(
'div',
{ className: baseClass + '-counter' },
this.state.value.length,
'/',
maxLength
);
}
}, {
key: 'renderClear',
value: function renderClear(baseClass) {
//如果已经设置了feedback, 则不出现clear
var _props3 = this.props,
hasClear = _props3.hasClear,
multiple = _props3.multiple,
inMatrix = _props3.inMatrix,
readOnly = _props3.readOnly,
hasFeedback = _props3.hasFeedback,
htmlType = _props3.htmlType,
_props3$type = _props3.type,
type = _props3$type === undefined ? 'enclosed' : _props3$type;
var _state = this.state,

@@ -98,8 +322,20 @@ focus = _state.focus,

//hasClear与hasFeedback互斥
if (typeof hasFeedback === 'boolean' && hasFeedback === true) {
hasClear = false;
}
if (!hasClear || multiple || !value || readOnly || hasFeedback) return null;
return (0, _weexRx.createElement)(_nukeIcon2.default, {
onPress: this.clearHandler,
src: '//img.alicdn.com/tfs/TB1LKb1LXXXXXXnXpXXXXXXXXXX-40-40.jpg' });
//不在配置平台或者focus的情况下, 不处理
// if(!(inMatrix || focus)) return null;
// 这里的button是一个清空按钮
// 如果input在表单里需要处理回车事件
// 但是这里又没有指定htmlType为button
// 则会直接触发清空操作
var clearStyle = Object.assign({}, _index2.default['clear'], _index2.default['single-clear'], _index2.default[type + '-single-clear']);
// console.log('clearstyle',clearStyle)
return (0, _weexRx.createElement)(_nukeIcon2.default, { x: 'clear-icon', onPress: this.clearHandler, size: 'medium', name: 'deleteFilling', style: clearStyle });
}

@@ -109,25 +345,30 @@ }, {

value: function renderFeedback() {
var _props4 = this.props,
_props4$type = _props4.type,
type = _props4$type === undefined ? 'enclosed' : _props4$type,
multiple = _props4.multiple,
hasFeedback = _props4.hasFeedback;
//多行不出现反馈
if (this.props.multiple || !this.props.hasFeedback) return null;
var src = '';
if (multiple || !hasFeedback) return null;
var iconType = '';
var state = this.props.state;
switch (state) {
case 'success':
src = '//img.alicdn.com/tfs/TB1SpzcLpXXXXcIXVXXXXXXXXXX-64-64.png';
break;
case 'error':
src = '//img.alicdn.com/tfs/TB1wlDmLpXXXXajXFXXXXXXXXXX-64-64.png';
break;
}
if (src) {
return (0, _weexRx.createElement)(_nukeIcon2.default, { src: src });
} else {
return null;
}
//如果没有设置state, 则从context中取state
if (state === undefined) state = this.context.state;
// switch(state){
// case 'success':
// iconType = 'success-filling';
// break;
// case 'error':
// iconType = 'error-filling';
// break;
// }
// debugger;
// console.log('type',type);
// console.log('styles[`${type}-single-feedback`]',styles[`${type}-single-feedback`]);
var feedbackStyle = Object.assign({}, _index2.default['feedback'], _index2.default['single-feedback'], _index2.default[type + '-single-feedback'], _index2.default['single-' + state + '-feedback']);
return (0, _weexRx.createElement)(_nukeIcon2.default, { x: 'feedback-icon', name: state + 'Filling', size: 'medium', style: feedbackStyle });
}
//发生变更
}, {

@@ -137,20 +378,50 @@ key: 'changeHandler',

var value = e.target.value;
var clearButton = !!value;
var maxLength = this.props.maxLength;
this.trigger('onChange', value, e);
this.setUnControlledState('value', value);
// 不管有没有到达maxLength
// !!!必须优先确保trigger onChange,让state与value同步
// 解决中文输入时由于value不同步被打断的问题
if (maxLength && value.length > maxLength) return;
}
}, {
key: 'updateStateFromProps',
value: function updateStateFromProps(props, keys, callback) {
if (!keys) {
keys = this.state && Object.keys(this.state) || [];
} else if (typeof keys === 'string') {
keys = [keys];
}
var newState = {
clearButton: clearButton
};
//没有任何可更新的keys
if (!keys.length) return;
if (!this.props.hasOwnProperty('value')) {
newState.value = value;
var newState = {};
keys.map(function (key) {
if (props.hasOwnProperty(key) && props[key] !== undefined) {
newState[key] = props[key];
};
});
//如果用户提供了回调函数, 则由回调函数
if (callback) {
callback(newState);
} else {
this.setState(newState);
}
}
}, {
key: 'trigger',
value: function trigger(fn) {
if (typeof fn === 'string') fn = this.props[fn];
if (!(typeof fn === 'function')) return;
this.setState(newState);
// debugger;
this.props.onChange && this.props.onChange(e);
// this.trigger('onChange', value, e);
for (var _len = arguments.length, attrs = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
attrs[_key - 1] = arguments[_key];
}
return fn.apply(this, attrs);
}

@@ -164,4 +435,3 @@ }, {

this.props.onFocus && this.props.onFocus(e);
// this.trigger('onFocus', e);
this.trigger('onFocus', e);
}

@@ -171,13 +441,14 @@ }, {

value: function blurHandler(e) {
var _this2 = this;
//避免focus为false后, clear的button被隐藏的这种情况发生
//input的blur会在clear的click事件前触发
clearTimeout(this.timer);
var that = this;
this.timer = setTimeout(function () {
that.setState({
_this2.setState({
focus: false
});
// that.trigger('onBlur', e);
_this2.trigger('onBlur', e);
}, 10);

@@ -188,79 +459,11 @@ }

value: function clearHandler(e) {
this.setState({ 'value': '' });
this.props.onClear && this.props.onClear(e);
this.setUnControlledState('value', '');
this.trigger('onChange', '', e);
}
}, {
key: 'pickAttrs',
value: function pickAttrs(props) {
return props;
}
}, {
key: 'render',
value: function render() {
var self = this;
key: 'setUnControlledState',
value: function setUnControlledState(key, value) {
if (this.props[key] !== undefined) return;
var _props2 = this.props,
hasFeedback = _props2.hasFeedback,
hasClear = _props2.hasClear,
_props2$prefix = _props2.prefix,
prefix = _props2$prefix === undefined ? this.defaultPrefix : _props2$prefix,
state = _props2.state,
onChange = _props2.onChange,
onFocus = _props2.onFocus,
onBlur = _props2.onBlur,
defaultValue = _props2.defaultValue,
readOnly = _props2.readOnly,
disabled = _props2.disabled,
_props2$style = _props2.style,
style = _props2$style === undefined ? {} : _props2$style,
_props2$contentStyle = _props2.contentStyle,
contentStyle = _props2$contentStyle === undefined ? {} : _props2$contentStyle,
multiple = _props2.multiple,
others = _objectWithoutProperties(_props2, ['hasFeedback', 'hasClear', 'prefix', 'state', 'onChange', 'onFocus', 'onBlur', 'defaultValue', 'readOnly', 'disabled', 'style', 'contentStyle', 'multiple']);
//hasClear与hasFeedback互斥
if (hasFeedback) hasClear = false;
// style.textAlign ='left';
var type = 'enclosed';
var disabledStyle = this.props.disabled ? INPUT_TYPES['disabled'] : {};
var errorStyle = this.props.state === 'error' ? INPUT_TYPES['error'] : {};
var errorTextStyle = this.props.state === 'error' ? INPUT_TYPES['errorText'] : {};
var wrapStyle = Object.assign({}, _index2.default[INPUT_TYPES['input']], _index2.default[disabledStyle], _index2.default[errorStyle], style);
var inputStyle = {};
TextAttrArr.forEach(function (item) {
if (wrapStyle[item]) {
inputStyle[item] = wrapStyle[item];
}
});
var attrs = {
style: style,
value: this.state.value,
onChange: this.changeHandler,
onFocus: this.focusHandler,
onBlur: this.blurHandler,
disabled: disabled,
readOnly: readOnly
};
var inputElement = void 0;
if (multiple) {
inputElement = (0, _weexRx.createElement)('textarea', _extends({}, attrs, { style: [_index2.default['nukeInputInput'], inputStyle, _index2.default[errorTextStyle]] }, this.pickAttrs(others)));
} else {
inputElement = (0, _weexRx.createElement)('input', _extends({}, attrs, this.pickAttrs(others), {
type: this.props.htmlType,
style: [_index2.default['nukeInputInput'], inputStyle, _index2.default[errorTextStyle]]
}));
}
return (0, _weexRx.createElement)(
_nukeComponents.View,
{ style: wrapStyle },
inputElement,
self.renderClear(),
self.renderFeedback()
);
this.setState(_defineProperty({}, key, value));
}

@@ -279,5 +482,7 @@ }]);

hasClear: _weexRx.PropTypes.bool,
type: _weexRx.PropTypes.oneOf(['enclosed', 'inset']),
state: _weexRx.PropTypes.oneOf(['', 'success', 'error', 'warning']),
value: _weexRx.PropTypes.string,
defaultValue: _weexRx.PropTypes.string,
value: _weexRx.PropTypes.any,
defaultValue: _weexRx.PropTypes.any,
size: _weexRx.PropTypes.oneOf(['small', 'large', 'medium']),
disabled: _weexRx.PropTypes.bool,

@@ -289,19 +494,25 @@ onClick: _weexRx.PropTypes.func,

// 文本域前后附加内容
addonBefore: _weexRx.PropTypes.string,
addonAfter: _weexRx.PropTypes.string,
// 增加htmlType枚举检查
// mext-input用于文本输入型场景
// search type跟目前的hasClear有冲突
htmlType: _weexRx.PropTypes.oneOf(['text', 'tel', 'email', 'url', 'password'])
htmlType: _weexRx.PropTypes.oneOf(['search', 'text', 'tel', 'email', 'url', 'password', 'number'])
};
/**
* 设置默认属性
*/
Input.defaultProps = {
hasClear: true,
hasClear: false,
hasFeedback: false,
multiple: false,
size: 'medium',
htmlType: 'text',
hasLimitHint: false
};
Input.contextTypes = {
align: _weexRx.PropTypes.string,
type: _weexRx.PropTypes.string,
form: _weexRx.PropTypes.object,
state: _weexRx.PropTypes.string
};
exports.default = Input;
module.exports = exports['default'];
{
"name": "nuke-input",
"version": "0.1.8",
"version": "0.1.9",
"description": "输入框",

@@ -38,3 +38,4 @@ "main": "lib/index",

"nuke-env": "0.x.x",
"nuke-icon": "0.x.x"
"nuke-icon": "0.x.x",
"nuke-button": "0.x.x"
},

@@ -41,0 +42,0 @@ "devDependencies": {},

@@ -14,4 +14,6 @@ /** @jsx createElement */

import Icon from 'nuke-icon';
import Button from 'nuke-button';
import { isWeex , isWeb } from 'nuke-env';
import InputStyles from './index.rxscss';
import styles from './index.rxscss';
const INPUT_TYPES = {

@@ -29,6 +31,78 @@ 'input': 'nukeInput',

const TextAttrArr=['color','fontSize','fontStyle','fontWeight','lineHeight','textAlign','textDecoration','textDecoration'];
class Input extends Component {
constructor(props) {
/**
* 声明属性类型
*/
static propTypes = {
readOnly: PropTypes.bool,
inMatrix: PropTypes.bool,
align: PropTypes.oneOf(['left', 'right', 'center']),
multiple: PropTypes.bool,
hasFeedback: PropTypes.bool,
hasClear: PropTypes.bool,
type: PropTypes.oneOf(['enclosed', 'inset']),
state: PropTypes.oneOf(['', 'success', 'error', 'warning']),
value: PropTypes.any,
defaultValue: PropTypes.any,
size: PropTypes.oneOf(['small', 'large', 'medium']),
disabled: PropTypes.bool,
onClick: PropTypes.func,
maxLength: PropTypes.number,
//是否显示限制长度的提示
hasLimitHint: PropTypes.bool,
// 文本域前后附加内容
addonBefore: PropTypes.string,
addonAfter: PropTypes.string,
// 增加htmlType枚举检查
// mext-input用于文本输入型场景
htmlType: PropTypes.oneOf(
[
'search',
'text',
'tel',
'email',
'url',
'password',
'number'
]
)
};
/**
* 设置默认属性
*/
static defaultProps = {
hasClear: false,
hasFeedback: false,
multiple: false,
size: 'medium',
htmlType: 'text',
hasLimitHint: false
};
/**
* 设置默认状态
*/
state = {
focus: false,
value: this.getPropValue('value') || '',
};
static contextTypes = {
align: PropTypes.string,
type: PropTypes.string,
form: PropTypes.object,
state: PropTypes.string
};
/**
* 构造函数
*/
constructor(props){
super(props);
this.changeHandler = this.changeHandler.bind(this);

@@ -38,70 +112,310 @@ this.focusHandler = this.focusHandler.bind(this);

this.clearHandler = this.clearHandler.bind(this);
this.state={
focus: false,
clearButton: true,
state: '',
value: this.props.value || ''
}
this.timer = null;
}
componentWillReceiveProps (newProps){
this.updateStateFromProps(newProps, ['type', 'value']);
}
renderClear(){
/**
* 渲染: 当state发生改变时立即执行
*/
render() {
let {
hasFeedback,
hasClear,
prefix = this.defaultPrefix,
className,
state,
defaultValue,
value,
onChange,
onFocus,
onBlur,
readOnly,
size,
disabled,
style = {},
addonBefore,
addonAfter,
multiple,
type = 'enclosed',
...others } = this.props;
// style.textAlign = this.getValueFromContextAndProp('align') || 'left';
// const type = this.getValueFromContextAndProp('type') || 'enclosed';
//如果当前的没有设置, 则取上下文的state
if(state === undefined) state = this.context.state;
const baseClass = `input`;
const newStyle = Object.assign({},styles['input'],
styles[size],
styles[`${type}`],
styles[`${multiple ? 'multiple' : 'single'}`],
styles[`${type}-${multiple ? 'multiple' : 'single'}`],
this.state.focus ? styles['focus']:{},
readOnly ? styles[`${multiple ? 'multiple' : 'single'}-readonly`]:{},
disabled ? styles[`${type}-${multiple ? 'multiple' : 'single'}-disabled`]:{},
styles[`${type}-${multiple ? 'multiple' : 'single'}-${state}`],
style
// {
// [state]: state
);
const attrs = {
// style: style,
onChange: this.changeHandler,
onFocus: this.focusHandler,
onBlur: this.blurHandler,
disabled: disabled,
readOnly: readOnly,
value: this.state.value,
defaultValue
};
// 防止重复定义带来的warning
if('value' in attrs) {
delete attrs.defaultValue;
}
// 绕过h5下 readOnly 默认值问题
if('readOnly' in attrs && !attrs.readOnly) {
delete attrs.readOnly;
}
let inputElement;
let flexVal = `flex-${10 - (addonBefore ? 1 : 0) - (addonAfter ? 1 : 0)}`;
let isAddonMode = (addonBefore || addonAfter) && (!multiple);
let customStyleFilter={};
['color','fontSize','fontStyle','fontWeight','lineHeight','textAlign','textDecoration'].forEach((item)=>{
if(style[item]){
// debugger;
customStyleFilter[item]=style[item];
}
})
const inputStyle = Object.assign({},styles[`input-inner`],
styles[size],
styles[`${type}-${multiple ? 'multiple' : 'single'}-input`],
styles[`${multiple ? 'multiple' : 'single'}-input`],
hasFeedback ? styles[`has-feedback-input`]:{},
hasClear && this.state.focus ? styles['has-clear-input']:{},
this.state.focus ? styles['focus-input']:{},
readOnly ? styles[`${multiple ? 'multiple' : 'single'}-readonly-input`]:{},
disabled ? styles[`${type}-${multiple ? 'multiple' : 'single'}-disabled-input`]:{},
customStyleFilter
);
if(multiple){
if(isWeb){
inputElement = <textarea style={inputStyle} {...attrs} {...this.pickAttrs(others)}>{attrs.value}</textarea>
}else{
inputElement = <textarea style={inputStyle} {...attrs} {...this.pickAttrs(others)} />
}
}else{
inputElement = <input
{...attrs}
{...this.pickAttrs(others)}
type={this.props.htmlType}
style={inputStyle}
/>
}
return (
<View style={newStyle}>
<View x="innner-flex" style={styles['inner-flex']}>
{this.renderAddonBefore(baseClass, isAddonMode)}
<View x="innner-input-box" style={[styles[`inner-input-box`],styles[flexVal]]}>
{inputElement}
{this.renderClear(baseClass)}
{this.renderFeedback(baseClass)}
{this.renderCounter(baseClass)}
</View>
{this.renderAddonAfter(baseClass, isAddonMode)}
</View>
</View>
);
}
getPropValue(key){
const capitalKey = key.charAt(0).toUpperCase() + key.slice(1);
const name = this.props[key] !== undefined ? key : `default${capitalKey}`;
return this.props[name];
}
getValueFromContextAndProp(key){
let value = this.context[key];
if(this.props.hasOwnProperty(key)){
value = this.props[key];
}
return value;
}
renderAddonBefore(baseClass, isAddonMode) {
if(this.props.addonBefore && isAddonMode) {
return (
<div style={[styles[`${baseClass}-addon`],styles[`${baseClass}-addon-before`]]}>
{this.props.addonBefore}
</div>
);
}
}
renderAddonAfter(baseClass, isAddonMode) {
if(this.props.addonAfter && isAddonMode) {
return (
<div style={[styles[`${baseClass}-addon`],styles[`${baseClass}-addon-after`]]}>
{this.props.addonAfter}
</div>
);
}
}
pickAttrs(props){
const attributes = `accept acceptCharset accessKey action allowFullScreen allowTransparency
alt async autoComplete autoFocus autoPlay capture cellPadding cellSpacing challenge
charSet checked classID className colSpan cols content contentEditable contextMenu
controls coords crossOrigin data dateTime default defer dir disabled download draggable
encType form formAction formEncType formMethod formNoValidate formTarget frameBorder
headers height hidden high href hrefLang htmlFor httpEquiv icon id inputMode integrity
is keyParams keyType kind label lang list loop low manifest marginHeight marginWidth max maxLength media
mediaGroup method min minLength multiple muted name noValidate nonce open
optimum pattern placeholder poster preload radioGroup readOnly rel required
reversed role rowSpan rows sandbox scope scoped scrolling seamless selected
shape size sizes span spellCheck src srcDoc srcLang srcSet start step style
summary tabIndex target title type useMap value width wmode wrap`.replace(/\s+/g,' ').replace(/\t|\n|\r/g, '').split(' '),
eventsName = `onCopy onCut onPaste onCompositionEnd onCompositionStart onCompositionUpdate onKeyDown
onKeyPress onKeyUp onFocus onBlur onChange onInput onSubmit onClick onContextMenu onDoubleClick
onDrag onDragEnd onDragEnter onDragExit onDragLeave onDragOver onDragStart onDrop onMouseDown
onMouseEnter onMouseLeave onMouseMove onMouseOut onMouseOver onMouseUp onSelect onTouchCancel
onTouchEnd onTouchMove onTouchStart onScroll onWheel onAbort onCanPlay onCanPlayThrough
onDurationChange onEmptied onEncrypted onEnded onError onLoadedData onLoadedMetadata
onLoadStart onPause onPlay onPlaying onProgress onRateChange onSeeked onSeeking onStalled onSuspend onTimeUpdate onVolumeChange onWaiting onLoad onError`
.replace(/\s+/g,' ').replace(/\t|\n|\r/g, '').split(' '),
attrsPrefix = ['data-', 'aria-'];
var attrs = {};
for (var key in props) {
if (attributes.indexOf(key) > -1 || eventsName.indexOf(key) > -1) {
attrs[key] = props[key];
}
else if (attrsPrefix.map(function (prefix) {
return new RegExp('^' + prefix)
}).some(function (reg) {
return key.replace(reg, '') != key;
})) {
attrs[key] = props[key];
}
}
return attrs;
}
//计数器
renderCounter(baseClass){
const {multiple, maxLength, hasLimitHint} = this.props;
if(!(multiple && maxLength && hasLimitHint)) return;
return (
<div className={`${baseClass}-counter`}>
{this.state.value.length}/{maxLength}
</div>
);
}
renderClear(baseClass){
//如果已经设置了feedback, 则不出现clear
const {hasClear, multiple, readOnly, hasFeedback} = this.props;
let {hasClear, multiple, inMatrix, readOnly, hasFeedback, htmlType , type = 'enclosed'} = this.props;
const {focus, value} = this.state;
//hasClear与hasFeedback互斥
if(typeof hasFeedback === 'boolean' && hasFeedback === true) {
hasClear = false;
}
if(!hasClear || multiple || !value || readOnly || hasFeedback) return null;
return (<Icon
onPress={this.clearHandler}
src="//img.alicdn.com/tfs/TB1LKb1LXXXXXXnXpXXXXXXXXXX-40-40.jpg"/>);
//不在配置平台或者focus的情况下, 不处理
// if(!(inMatrix || focus)) return null;
// 这里的button是一个清空按钮
// 如果input在表单里需要处理回车事件
// 但是这里又没有指定htmlType为button
// 则会直接触发清空操作
const clearStyle = Object.assign({}, styles['clear'],
styles['single-clear'],
styles[`${type}-single-clear`]
)
// console.log('clearstyle',clearStyle)
return (
<Icon x="clear-icon" onPress={this.clearHandler} size="medium" name="deleteFilling" style={clearStyle} />
);
}
renderFeedback(){
const { type = 'enclosed', multiple, hasFeedback } = this.props;
//多行不出现反馈
if(this.props.multiple || !this.props.hasFeedback) return null;
if(multiple || !hasFeedback) return null;
let src = '';
let iconType = '';
let state = this.props.state;
switch(state){
case 'success':
src = '//img.alicdn.com/tfs/TB1SpzcLpXXXXcIXVXXXXXXXXXX-64-64.png';
break;
case 'error':
src = '//img.alicdn.com/tfs/TB1wlDmLpXXXXajXFXXXXXXXXXX-64-64.png';
break;
}
if(src){
return (<Icon src={src}/>);
}else{
return null;
}
//如果没有设置state, 则从context中取state
if(state === undefined) state = this.context.state;
// switch(state){
// case 'success':
// iconType = 'success-filling';
// break;
// case 'error':
// iconType = 'error-filling';
// break;
// }
// debugger;
// console.log('type',type);
// console.log('styles[`${type}-single-feedback`]',styles[`${type}-single-feedback`]);
const feedbackStyle = Object.assign({}, styles['feedback'],
styles['single-feedback'],
styles[`${type}-single-feedback`],
styles[`single-${state}-feedback`]
)
return <Icon x="feedback-icon" name={`${state}Filling`} size="medium" style={feedbackStyle} />;
}
//发生变更
changeHandler(e){
const value = e.target.value;
const clearButton = !!value;
const {maxLength} = this.props;
this.trigger('onChange', value, e);
this.setUnControlledState('value', value);
// 不管有没有到达maxLength
// !!!必须优先确保trigger onChange,让state与value同步
// 解决中文输入时由于value不同步被打断的问题
if(maxLength && value.length > maxLength) return;
}
updateStateFromProps(props, keys, callback){
if(!keys){
keys = (this.state && Object.keys(this.state)) || [];
}else if(typeof(keys) === 'string'){
keys = [keys];
}
let newState = {
clearButton: clearButton
}
//没有任何可更新的keys
if(!keys.length) return;
let newState = {};
keys.map((key)=>{
if(props.hasOwnProperty(key) && props[key] !== undefined){
newState[key] = props[key];
};
});
if(!this.props.hasOwnProperty('value')){
newState.value = value;
//如果用户提供了回调函数, 则由回调函数
if(callback){
callback(newState);
}else{
this.setState(newState);
}
}
trigger(fn, ...attrs){
if(typeof fn === 'string') fn = this.props[fn];
if(!(typeof fn === 'function')) return;
this.setState(newState);
// debugger;
this.props.onChange && this.props.onChange(e);
// this.trigger('onChange', value, e);
}
return fn.apply(this, attrs);
}
focusHandler(e){

@@ -112,4 +426,3 @@ this.setState({

this.props.onFocus && this.props.onFocus(e);
// this.trigger('onFocus', e);
this.trigger('onFocus', e);
}

@@ -121,10 +434,9 @@

clearTimeout(this.timer);
const that = this;
this.timer = setTimeout(function(){
that.setState({
this.timer = setTimeout(() => {
this.setState({
focus: false
});
// that.trigger('onBlur', e);
this.trigger('onBlur', e);
}, 10);

@@ -134,115 +446,19 @@ }

clearHandler(e){
this.setState({'value': ''});
this.props.onClear && this.props.onClear(e);
this.setUnControlledState('value', '');
this.trigger('onChange', '', e);
}
pickAttrs(props){
return props;
}
render(){
var self=this;
let {
hasFeedback,
hasClear,
prefix = this.defaultPrefix,
state,
onChange,
onFocus,
onBlur,
defaultValue,
readOnly,
disabled,
style = {},
contentStyle={},
multiple,
...others
} = this.props;
setUnControlledState(key, value){
if(this.props[key] !== undefined) return;
//hasClear与hasFeedback互斥
if(hasFeedback) hasClear = false;
// style.textAlign ='left';
const type = 'enclosed';
const disabledStyle = this.props.disabled?INPUT_TYPES['disabled'] :{};
const errorStyle = this.props.state==='error'?INPUT_TYPES['error'] :{};
const errorTextStyle = this.props.state==='error'?INPUT_TYPES['errorText'] :{};
const wrapStyle = Object.assign({},InputStyles[INPUT_TYPES['input']],InputStyles[disabledStyle],InputStyles[errorStyle], style);
let inputStyle ={};
TextAttrArr.forEach((item)=>{
if(wrapStyle[item]){
inputStyle[item]=wrapStyle[item];
}
})
const attrs = {
style: style,
value: this.state.value,
onChange: this.changeHandler,
onFocus: this.focusHandler,
onBlur: this.blurHandler,
disabled: disabled,
readOnly: readOnly
};
let inputElement;
if(multiple){
inputElement = <textarea {...attrs} style={[InputStyles['nukeInputInput'],inputStyle,InputStyles[errorTextStyle]]} {...this.pickAttrs(others)} />
}else{
inputElement = <input
{...attrs}
{...this.pickAttrs(others)}
type={this.props.htmlType}
style={[InputStyles['nukeInputInput'],inputStyle,InputStyles[errorTextStyle]]}
/>
}
return (
<View style={wrapStyle}>
{inputElement}
{self.renderClear()}
{self.renderFeedback()}
</View>
);
}
this.setState({
[key]: value
})
}
}
Input.propTypes = {
readOnly: PropTypes.bool,
inMatrix: PropTypes.bool,
align: PropTypes.oneOf(['left', 'right', 'center']),
multiple: PropTypes.bool,
hasFeedback: PropTypes.bool,
hasClear: PropTypes.bool,
state: PropTypes.oneOf(['', 'success', 'error', 'warning']),
value: PropTypes.string,
defaultValue: PropTypes.string,
disabled: PropTypes.bool,
onClick: PropTypes.func,
maxLength: PropTypes.number,
//是否显示限制长度的提示
hasLimitHint: PropTypes.bool,
// 增加htmlType枚举检查
// mext-input用于文本输入型场景
// search type跟目前的hasClear有冲突
htmlType: PropTypes.oneOf(['text', 'tel', 'email', 'url', 'password'])
};
/**
* 设置默认属性
*/
Input.defaultProps = {
hasClear: true,
hasFeedback: false,
multiple: false,
htmlType: 'text',
hasLimitHint: false
};
export default Input;
/** @jsx createElement */
import {createElement, Component} from 'weex-rx';
import { View, Text} from 'nuke-components';
import { View, Text,ScrollView} from 'nuke-components';
import Input from 'nuke-input';

@@ -13,6 +13,10 @@ import {mount} from 'nuke-mounter';

this.state = {
inputVal:'',
d1:'',
d2:'',
d3:'',
textVal:'',
valid1:'',
valid2:'',
data1:'',
data2:''
}

@@ -28,9 +32,23 @@

change=(e)=>{
console.log(e)
c1=(e)=>{
console.log('onChange======>',e)
this.setState({
d1: e
})
}
c2=(e)=>{
let value=e.value || e.target.value || '';
console.log('c2 onInput==>',value)
this.setState({
inputVal: value
d2 : value
})
}
c3=(e)=>{
console.log('c3 onChange======>',e)
this.setState({
d3: e
})
}
textChange=(e)=>{

@@ -43,20 +61,60 @@ console.log(e)

}
d1change=(e)=>{
console.log(e)
let value=e.value || e.target.value || '';
this.setState({
d1: value
})
}
d2change=(e)=>{
console.log(e)
let value=e.value || e.target.value || '';
this.setState({
d2: value
})
}
render() {
return (
<View style={{paddingTop:'30rem'}}>
<View style={styles.st}><Text style={styles.stText}>单行</Text></View>
<Input ref="myinput" placeholder="搜一搜..." value={this.state.inputVal} onChange={this.change.bind(this)} />
<ScrollView style={{paddingTop:'30rem'}}>
<View style={styles.st}><Text style={styles.stText}>input inset 使用 onChange 事件</Text></View>
<View style={[styles.row,{paddingLeft:0,paddingRight:0}]}><Input placeholder="搜一搜..." type="inset" value={this.state.d1} onChange={this.c1.bind(this)} style={{backgroundColor:'#f3f3f3'}} /></View>
<Text>输入的是:{this.state.d1}</Text>
<View style={styles.st}><Text style={styles.stText}>input onInput 实时获取</Text></View>
<View style={styles.row}><Input placeholder="搜一搜..." defaultValue="羊绒大衣" onInput={this.c2.bind(this)} /></View>
<Text>实时获取:{this.state.d2}</Text>
<View style={styles.st}><Text style={styles.stText}>自定义样式</Text></View>
<View style={styles.row}><Input style={{borderWidth:0,backgroundColor:'#3089dc',color:'#ffffff'}} /></View>
<View style={styles.st}><Text style={styles.stText}>带clear按钮</Text></View>
<View style={styles.row}><Input hasClear={true} placeholder="搜一搜..." value={this.state.d3} onChange={this.c3.bind(this)} /></View>
<View style={styles.st}><Text style={styles.stText}>输入校验success反馈</Text></View>
<Input hasFeedback="true" onChange={this.validate.bind(this,'success','valid1')} state={this.state.valid1} />
<View style={styles.row}><Input hasFeedback="true" onChange={this.validate.bind(this,'success','valid1')} state={this.state.valid1} /></View>
<View style={styles.st}><Text style={styles.stText}>输入校验error反馈</Text></View>
<Input hasFeedback="true" placeholder="请输入..." onChange={this.validate.bind(this,'error','valid2')} state={this.state.valid2} />
<View style={styles.row}><Input hasFeedback="true" placeholder="请输入..." onChange={this.validate.bind(this,'error','valid2')} state={this.state.valid2} /></View>
<View style={styles.st}><Text style={styles.stText}>textarea</Text></View>
<Input style={{height:'300rem'}} multiple={true} ref="xxx" placeholder="写一句自我介绍" onInput={this.textChange} value={this.state.textVal} onFocus={(e) => console.log('onFocus',e)} onBlur={(e) => console.log('onBlur',e)}/>
<View style={styles.row}><Input style={{height:'300rem',marginBottom:'20rem'}} multiple={true} placeholder="设置高度300rem" onFocus={(e) => console.log('onFocus',e)} onBlur={(e) => console.log('onBlur',e)}/></View>
<View style={styles.row}><Input multiple={true} placeholder="介绍一下" value="大家好我是xxxx"
/></View>
</View>
<View style={styles.st}><Text style={styles.stText}>read only</Text></View>
<View style={[styles.row,{marginBottom:'10rem'}]}><Input readOnly placeholder="输入姓名" defaultValue="只读"/></View>
<View style={styles.row}><Input style={{height:'400rem',marginBottom:'10rem'}} readOnly multiple={true} placeholder="输入简介..." defaultValue="只读textarea"/></View>
<View style={styles.st}><Text style={styles.stText}>disabled</Text></View>
<View style={[styles.row,{marginBottom:'10rem'}]}><Input disabled placeholder="输入姓名" defaultValue="不可用"/></View>
<View style={styles.row}><Input style={{height:'400rem',marginBottom:'10rem'}} disabled multiple={true} ref="d2" placeholder="输入简介..." defaultValue="不可用textarea"/></View>
</ScrollView>
);

@@ -66,2 +124,7 @@ }

const styles={
row:{
paddingLeft:'30rem',
paddingRight:'30rem',
},
st:{

@@ -68,0 +131,0 @@ marginBottom:'30rem',

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

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