Socket
Socket
Sign inDemoInstall

react-native-head-tab-view

Package Overview
Dependencies
1
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.0 to 1.0.1

README.md

21

Button.js

@@ -13,8 +13,8 @@ import React, { Component } from 'react';

static propTypes = {
preventDoubleClick: PropTypes.bool,
onPress:PropTypes.func
preventDoubleClick: PropTypes.bool,
onPress: PropTypes.func
}
static defaultProps = {
preventDoubleClick: true,
onPress:()=>{}
onPress: () => { }
}

@@ -42,3 +42,3 @@ constructor(props) {

const { onPress } = this.props;
if (!this.props.preventDoubleClick) { //如果不需要防止重复点击
if (!this.props.preventDoubleClick) {
onPress()

@@ -50,8 +50,8 @@ return

const diff = currentTime.getTime() - this.lastTime.getTime();
this.lastTime = currentTime;
if (diff < 800) {
return;
}
}else{
this.lastTime = currentTime;
} else {
this.lastTime = currentTime;
}

@@ -74,6 +74,6 @@ if (onPress && typeof onPress == 'function') {

}
else{
else {
this.lastTime2 = currentTime1;
}
const { onPressOut } = this.props;

@@ -86,6 +86,2 @@ if (onPressOut && typeof onPressOut == 'function') {

componentWillUnmount() {
this.timer && clearTimeout(this.timer);
}
render() {

@@ -116,3 +112,2 @@ const { children, style = {}, pressInColor, pressInOpacity, disabledOpacity, disableColor, onPress, ...restProps } = this.props;

<View
ref={(o) => { this.viewCls = o }}
style={[{ opacity, overflow: 'hidden', backfaceVisibility: 'hidden' }, { backgroundColor }, style]}

@@ -119,0 +114,0 @@ >

@@ -1,9 +0,5 @@

/**
* 容器组件 内部的ScrollView的高阶组件
*/
import React from 'react';
import {
Animated,
Platform,
Dimensions
} from 'react-native';

@@ -13,5 +9,2 @@ import PropTypes from 'prop-types';

const faultHeight = 100
const G_WIN_HEIGHT = Dimensions.get('window').height;
export default HPageViewHoc = (WrappedComponent) => {

@@ -40,13 +33,11 @@

this.state = {
placeHeight: 0,
isResponder: false,
hideContent: true
placeHeight: 0, //占位高
hideContent: true //是否显示内容
}
//是否didMount
this.didMount = false
}
getOnScroll() {
const { containerTrans, isActive, index } = this.props
const { containerTrans, isActive } = this.props

@@ -66,43 +57,34 @@ if (isActive) {

tabDidClick = () => {
// console.log('tabDidClick');
// console.log(this.props.index);
this.stopScroll = true
// if (this.props.isActive) {
// this.scrollTo({ y: this.scrollTop })
// }
}
becomeResponder = (index) => {
// console.log('becomeResponder');
// console.log(index);
// console.log(this.props.index);
this.stopScroll = false
const isResponder = index == this.props.index
// if (this.state.isResponder != isResponder) {
// this.setState({ isResponder })
// }
}
componentDidMount() {
this.didMount = true;
this.addListener()
}
const { addListener, containerTrans, makeHeaderHeight } = this.props
componentWillUnmount() {
this.removeListener()
}
addListener() {
const { addListener, containerTrans } = this.props
addListener(this, TABVIEW_TABDIDCLICK, this.tabDidClick)
addListener(this, TABVIEW_BECOME_RESPONDER, this.becomeResponder)
this.didMount = true;
this.props.containerTrans && this.props.containerTrans.addListener(this.updateView);
containerTrans && containerTrans.addListener(this.updateView);
}
componentWillUnmount() {
const { removeListener } = this.props
removeListener() {
const { removeListener, containerTrans } = this.props
removeListener(this, TABVIEW_TABDIDCLICK, this.tabDidClick)
removeListener(this, TABVIEW_BECOME_RESPONDER, this.becomeResponder)
this.props.containerTrans && this.props.containerTrans.removeListener(this.updateView);
containerTrans && containerTrans.removeListener(this.updateView);
}
onScrollBeginDrag() {
const { pageDidDrag, index } = this.props;
pageDidDrag(index)
const { scenePageDidDrag, index } = this.props;
scenePageDidDrag(index)
}

@@ -127,3 +109,2 @@

this.scrollTo({ y: e.value })

@@ -136,10 +117,10 @@ }

if (this._scrollView && this._scrollView.getNode && this._scrollView.getNode()) {
const element = this._scrollView.getNode()
const elementNode = this._scrollView.getNode()
if (this.canScroll('scrollTo')) {
element.scrollTo({ x: 0, y: e.y, animated: false });
elementNode.scrollTo({ x: 0, y: e.y, animated: false });
} else if (this.canScroll('scrollToOffset')) {
element.scrollToOffset({ offset: e.y, animated: false });
elementNode.scrollToOffset({ offset: e.y, animated: false });
} else if (this.canScroll('scrollToLocation')) {
element.scrollToLocation({ itemIndex: 0, sectionIndex: 0, viewOffset: -e.y, animated: false });
elementNode.scrollToLocation({ itemIndex: 0, sectionIndex: 0, viewOffset: -e.y, animated: false });
}

@@ -153,2 +134,17 @@ }

tryScroll() {
if (this.didMount) {
this.didMount = false;
const { containerTrans, makeHeaderHeight } = this.props
const scrollValue = containerTrans._value > makeHeaderHeight() ? makeHeaderHeight() : containerTrans._value
this.scrollTo({ y: scrollValue })
setTimeout(() => {
this.setState({ hideContent: false })
}, 0)
}
}
/**

@@ -158,45 +154,22 @@ * 根据contentSize决定占位视图高度

_onContentSizeChange(contentWidth, contentHeight) {
const { placeHeight } = this.state;
const { makeHeaderHeight } = this.props;
const headerHeight = makeHeaderHeight()
const { expectHeight, faultHeight } = this.props;
const containerHeight = expectHeight + faultHeight;
// const fullHeight = headerHeight + G_WIN_HEIGHT - G_NAVBAR_HEIGHT - G_STATUS_HEIGHT
const fullHeight = headerHeight + G_WIN_HEIGHT - 0 - 20
// console.log('_onContentSizeChange====' + contentHeight + '----fullHeight=' + fullHeight + '----placeHeight=' + placeHeight);
// console.log('_onContentSizeChange----==');
if (contentHeight < containerHeight) {//添加占位高度 placeHeight
const newPlaceHeight = placeHeight + containerHeight - contentHeight;
this.setState({ placeHeight: newPlaceHeight })
} else {
this.tryScroll();
if ((contentHeight - placeHeight) > fullHeight) {
if (placeHeight > 0) {//有占位高,考虑减少占位高
if (this.didMount) {
// console.log('1231231231231233333');
const { containerTrans } = this.props
const scrollValue = containerTrans._value > makeHeaderHeight() ? makeHeaderHeight() : containerTrans._value
this.scrollTo({ y: scrollValue })
this.didMount = false;
setTimeout(() =>{
this.setState({hideContent:false})
},0)
const moreHeight = contentHeight - containerHeight
const newPlaceHeight = moreHeight > placeHeight ? 0 : placeHeight - moreHeight
if (newPlaceHeight != placeHeight) {
this.setState({ placeHeight: newPlaceHeight })
}
}
if (placeHeight == 0) return;
this.setState({ placeHeight: 0 })
} else {
if (contentHeight - fullHeight >= faultHeight) { //超过容错高度,减少占位高
const height = fullHeight - contentHeight
}
this.setState({ placeHeight: placeHeight + height })
} else if (contentHeight - fullHeight > 0) { //高度在容错范围内,不做处理 (目的就是改变占位高度,维持在容错范围内)
console.log('容错');
this.setState({hideContent:false})
return;
} else {//占位不够,增加占位高
const height = fullHeight - contentHeight
this.setState({ placeHeight: placeHeight + height })
}
}
}

@@ -211,10 +184,9 @@

this._scrollView = _ref
if (forwardedRef && forwardedRef.constructor) {
if (typeof forwardedRef === 'function') {
forwardedRef(_ref)
} else if (typeof forwardedRef === 'object') {
forwardedRef.current = _ref
}
}
}}

@@ -224,3 +196,2 @@ style={{ opacity: this.state.hideContent ? 0 : 1 }}

directionalLockEnabled
showsVerticalScrollIndicator={false}
automaticallyAdjustContentInsets={false}

@@ -230,12 +201,10 @@ onScrollBeginDrag={this.onScrollBeginDrag}

overScrollMode={'never'}
disableScrollViewPanResponder={true}
removeClippedSubviews={Platform.OS === 'ios'}
contentContainerStyle={{ paddingTop: headerHeight, paddingBottom: placeHeight }}
contentStyle={{ paddingTop: headerHeight, paddingBottom: placeHeight }}
onContentSizeChange={this._onContentSizeChange}
{...rest} >
{...rest}
showsVerticalScrollIndicator={false}
>
{children}
</AnimatePageView>
}

@@ -242,0 +211,0 @@ };

@@ -10,3 +10,3 @@

/**
* 数据源,每个标签页的标题
* 数据源,每个标签页的标题(可配合tabNameConvert使用)
*/

@@ -21,2 +21,4 @@ tabs: string[];

* tab标签的名称转换方法
* 例:
* tabNameConvert={(tabname)=>return tabname+'_aguai'}
*/

@@ -39,5 +41,39 @@ tabNameConvert?: (tabname: string) => string;

export interface TabViewItemInfo<TabItemT> {
/**
* 标签的名字(`tabs`数组的其中一个元素)
*/
item: string;
/**
* 标签页的序号 ,从0开始排序
*/
index: number;
isActive: boolean;
//当使用了renderScrollHeader时会有以下参数
/**
* 是否是当前活跃标签页
*/
isActive?: boolean;
/**
* 统管全局纵向动画对象
*/
containerTrans?: Animated.Value;
/**
* 获取renderScrollHeader的高度方法
*/
makeHeaderHeight?: () => number;
/**
* 标签页添加整个组件的事件监听方法,instance:this , eventName:事件名,callback:事件回调
*/
addListener?: (instance: any, eventName: string, callback: function) => void;
/**
* 标签页移除整个组件的事件监听方法
*/
removeListener?: (instance: any, eventName: string, callback: function) => void;
/**
* 标签页页面被拖拽时回调方法
*/
scenePageDidDrag?: (index: number) => void;
/**
* 整个组件上下滑动时,期望标签页所拥有的内容高度(用于`HPageViewHoc`中计算补位视图高度)
*/
expectHeight?: number;
}

@@ -149,3 +185,5 @@

locked?: boolean;
/**
* tabbar的样式
*/
tabbarStyle?: StyleProp<ViewStyle>;

@@ -156,2 +194,7 @@ /**

extraData?: any;
/**
* 大部分情况用不上这个值(默认:0)
* 这个值是在标签页内容不够,计算补位视图时的容错高度,后面考虑删掉该字段
*/
faultHeight?: number;
}

@@ -158,0 +201,0 @@

{
"name": "react-native-head-tab-view",
"version": "1.0.0",
"version": "1.0.1",
"main": "index.js",

@@ -8,3 +8,3 @@ "scripts": {

},
"author": "rain",
"author": "yu zou <zouyu123007@163.com>",
"license": "ISC",

@@ -25,3 +25,6 @@ "keywords": [

],
"dependencies": {
"@react-native-community/viewpager": "^3.3.0"
},
"description": "Tab view component for React Native"
}

@@ -8,3 +8,2 @@ import React from 'react';

Animated,
Image,
Text,

@@ -16,3 +15,2 @@ Dimensions

import { TabProps } from './TabViewProps'
import Button from './Button';

@@ -69,3 +67,2 @@

this.tabOnLayout = this.tabOnLayout.bind(this)
this.onPressPackup = this.onPressPackup.bind(this)
this.state = {

@@ -76,3 +73,3 @@ leftUnderline: new Animated.Value(0),

this.tabFrames = []
this.scrollX = 0 //TODO 如果出事tabindex值不少0
this.scrollX = 0

@@ -137,6 +134,3 @@ }

const pageNum = this.props.tabs.length;
const value = offset.value

@@ -304,9 +298,2 @@

/**
* 点击收起按钮
*/
onPressPackup() {
this.pullDidClick(false)
}
/**
* 获取一个tabItem的宽度

@@ -332,4 +319,2 @@ */

this.tabbarFrame = event.nativeEvent.layout
console.log('tabOnLayout---');
console.log(this.tabbarFrame);
if (this.state.tabbarWidth != this.tabbarFrame.width) {

@@ -347,3 +332,2 @@ this.setState({ tabbarWidth: this.tabbarFrame.width })

this.tabFrames[page] = { left: x, right: x + width, width, height, };
console.log('measureTab====');

@@ -375,3 +359,2 @@ this.updateView({ value: this.props.scrollValue ? this.props.scrollValue._value ? this.props.scrollValue._value : 0 : 0, });

height: TABBAR_HEIGHT,
zIndex: 100,
},

@@ -378,0 +361,0 @@ leftRightView: {

import React from 'react';
import PropTypes from 'prop-types';
import {

@@ -8,7 +7,4 @@ View,

Platform,
TouchableWithoutFeedback,
Dimensions
} from 'react-native';
import { TabViewProps, TabProps, TABVIEW_TABDIDCLICK, TABVIEW_BECOME_RESPONDER } from './TabViewProps'
const Scene = require('./Scene');
import Tabbar from './Tabbar'

@@ -21,3 +17,2 @@

undefined;
let self = null;

@@ -33,2 +28,3 @@ export default class TabView extends React.PureComponent {

preInitSceneNum: 0,
faultHeight: 0,
}

@@ -41,6 +37,6 @@

this.onScroll = this.onScroll.bind(this)
this.decisionShowScene = this.decisionShowScene.bind(this)
this.sceneWillShow = this.sceneWillShow.bind(this)
this.addListener = this.addListener.bind(this)
this.removeListener = this.removeListener.bind(this)
this.pageDidDrag = this.pageDidDrag.bind(this)
this.scenePageDidDrag = this.scenePageDidDrag.bind(this)

@@ -52,2 +48,4 @@ this.positionAndroid = new Animated.Value(props.initialPage)

sceneWidth: 0,
tabviewHeight: 0,
tabbarHeight: 0,
currentIndex: props.initialPage,

@@ -57,3 +55,2 @@ displayKeys: this.getDisplayScene(props.tabs, props.initialPage), //所有页面是否需要显示和更新

tabs: props.tabs, //所有tab
containerY: 0,
containerTrans: new Animated.Value(0) //整体上下滑动的动画对象

@@ -65,23 +62,9 @@ }

this.observers = {} //观察者
this.handleScrollValue()
this.makeScrollTrans()
self = this
}
//render之前
// static getDerivedStateFromProps(props, state) {
// if (props.tabs.toString() !== state.tabs.toString()) {
// const newKeys = self.getDisplayScene(props.tabs)
// return { displayKeys: newKeys, tabs: props.tabs }
// }
// return null
// }
componentDidMount() {
if (this.state.tabs && this.state.tabs.length) {
this.pageDidShow()
if (this.props.renderScrollHeader && !this.props.makeHeaderHeight) {
console.warn('必须实现 makeHeaderHeight方法')
}

@@ -92,6 +75,5 @@ }

if (prevState.tabs.toString() !== this.props.tabs.toString()) {
const newKeys = this.getDisplayScene(this.props.tabs,this.state.currentIndex)
this.setState({displayKeys: newKeys,tabs: this.props.tabs})
const newKeys = this.getDisplayScene(this.props.tabs, this.state.currentIndex)
this.setState({ displayKeys: newKeys, tabs: this.props.tabs })
}
this.pageBeginShow(prevState)
}

@@ -109,5 +91,3 @@

} else {
this.positionAndroid.addListener(({ value }) => {
// this.state.scrollValue && this.state.scrollValue.setValue(value + this.offsetAndroid._value)
})
this.positionAndroid.addListener(({ value }) => { })
this.offsetAndroid.addListener(({ value }) => {

@@ -122,2 +102,3 @@ this.state.scrollValue && this.state.scrollValue.setValue(value + this.positionAndroid._value)

<View style={{ flex: 1 }} onLayout={this.containerOnLayout}>
{this._renderScrollHead()}
{this._renderTabBar()}

@@ -127,7 +108,6 @@ {this._renderHeader()}

{this._renderFooter()}
{this._renderScrollHead()}
</View>
)
}
//渲染头部
_renderHeader() {

@@ -150,3 +130,3 @@ const { renderHeader, makeHeaderHeight } = this.props

}
//渲染尾部
_renderFooter() {

@@ -158,2 +138,3 @@ const { renderFooter } = this.props

//渲染可滑动头部
_renderScrollHead() {

@@ -191,11 +172,13 @@ const { renderScrollHeader, makeHeaderHeight } = this.props

const tabbarProps = this.makeTabParams()
if (renderTabBar) {
return renderTabBar(tabbarProps) || null
} else {
console.log('_renderTabBar===');
console.log(tabs);
const { style, ...rest } = tabbarProps;
const { transform, ...restStyle } = style;
const newProps = { ...rest, ...{ style: restStyle } }
const tabbarContent = renderTabBar ? (renderTabBar(newProps) || null) : <Tabbar {...newProps} />
return (
<Animated.View onLayout={this.tabbarOnLayout} style={[tabbarProps.style, { zIndex: 10 }]}>
{tabbarContent}
</Animated.View>
)
}
return <Tabbar {...tabbarProps} />
}
}
/**

@@ -205,4 +188,2 @@ * 渲染内容

_renderContent() {
// console.log('_renderContent====_renderContent===');
if (Platform.OS === 'ios') {

@@ -236,3 +217,3 @@

initialPage={this.props.initialPage}
onPageSelected={this.decisionShowScene}
onPageSelected={this.sceneWillShow}
keyboardDismissMode="on-drag"

@@ -257,18 +238,9 @@ scrollEnabled={!this.props.locked}

}
/**
* 渲染阴影view
* 获取标签子页面
*/
renderShadowView() {
return (
<TouchableWithoutFeedback onPress={this.onPressShadowView.bind(this)}>
<View style={{ backgroundColor: 'rgba(0,0,0,0.3)', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }} />
</TouchableWithoutFeedback>
)
}
/**
* 获取页面组件
*/
getScene() {
const { renderScene, makeHeaderHeight, renderScrollHeader } = this.props;
const { sceneWidth, displayKeys, currentIndex, tabs, containerTrans } = this.state;
const { renderScene, renderScrollHeader } = this.props;
const { sceneWidth, displayKeys, currentIndex, tabs } = this.state;

@@ -278,8 +250,7 @@ return tabs.map((item, index) => {

const display = this.sceneKeyIsDisplay(displayKeys, key)
console.log('display:' + display + '---key:' + key);
//如果有scrollHeader,标签页必须保持update状态
const showUpdate = renderScrollHeader ? true : this.sceneShouldPreInit(currentIndex, index)
const isActive = currentIndex == index
return <Scene key={this.makeSceneKey(item, index)} style={{ width: sceneWidth }} shouldUpdate={showUpdate}>
{(display && renderScene) ? renderScene({ item, index, isActive, containerTrans, makeHeaderHeight, addListener: this.addListener, removeListener: this.removeListener, pageDidDrag: this.pageDidDrag }) : <View />}
{(display && renderScene) ? renderScene(this.makeSceneParams(item, index)) : <View />}
</Scene>

@@ -296,3 +267,2 @@

const offset = this.state.sceneWidth * index;
this.scrollView && this.scrollView.getNode() && this.scrollView.getNode().scrollTo({ x: offset, y: 0, animated: true })

@@ -302,4 +272,3 @@ } else {

}
const curIndex = this.state.currentIndex
this.decisionShowScene(index, this.onChangeTab.bind(this, curIndex, index))
this.sceneWillShow(index)
}

@@ -320,8 +289,8 @@ /**

pageHasChange(page) {
this.decisionShowScene(page)
this.sceneWillShow(page)
}
/**
* 决定显示的页面
* 页面将要显示
*/
decisionShowScene(page) {
sceneWillShow(page) {

@@ -336,8 +305,3 @@ let zPage = page;

}
/**
* 阴影视图被点击
*/
onPressShadowView() {
}
/**

@@ -347,38 +311,8 @@ * tabbar切换

onChangeTab(currentIndex, page) {
if (currentIndex === page) return;
if (this.props.onChangeTab) {
this.props.onChangeTab({ from: currentIndex, curIndex: page })
if (currentIndex != page) {
this.pageDidShow();
}
}
}
/**
* @description: tabs变化后,判断页面是否show
* @param {page} 上一次的状态
* @return: 页面是否show
*/
pageBeginShow(prevState) {
if (prevState.tabs != this.state.tabs) {
const { currentIndex } = this.state
const { prevIndex } = prevState
if (!prevState.tabs.length) this.pageDidShow()
if (prevIndex < prevState.tabs.length
&& currentIndex < this.state.tabs.length
&& prevState.tabs[prevIndex] != this.state.tabs[currentIndex]) {
this.pageDidShow()
}
}
}
/**
* 当前展示那个页面
*/
pageDidShow() {
if (this.props.pageDidShow) {
const { tabs, currentIndex } = this.state;
this.props.pageDidShow(tabs[currentIndex], currentIndex)
}
}
onScroll(e) {

@@ -437,3 +371,2 @@ if (Platform.OS === 'ios') {

const { preInitSceneNum } = this.props
return ((sceneIndex >= page - preInitSceneNum) || (page - preInitSceneNum <= 0)) && sceneIndex <= page + preInitSceneNum

@@ -450,13 +383,2 @@ }

/**
* 回调时的参数装配
*/
makeParams() {
const { tabs, currentIndex } = this.state;
if (tabs && tabs.length > currentIndex) {
return { item: tabs[currentIndex], index: currentIndex }
}
return { item: '', index: 0 }
}
makeSceneKey(name, index) {

@@ -466,2 +388,5 @@ return name + '_' + index

tabbarOnLayout = (event) => {
this.setState({ tabbarHeight: event.nativeEvent.layout.height })
}
/**

@@ -471,3 +396,3 @@ * 整体的layout方法

containerOnLayout = (event) => {
this.setState({ containerY: event.nativeEvent.layout.y, sceneWidth: event.nativeEvent.layout.width })
this.setState({ sceneWidth: event.nativeEvent.layout.width, tabviewHeight: event.nativeEvent.layout.height })
}

@@ -481,12 +406,10 @@

}
addListenerTab(target, eventName, callback) {
if (this.observers.find((el) => { el == target })) return;
this.observers.push(target);
/**
* 标签页被拉拽
* @param {number} index 标签页的序号
*/
scenePageDidDrag(index) {
this.emitListener(TABVIEW_BECOME_RESPONDER, index)
}
// removeListenerTab(target) {
// this.observers.splice(this.observers.findIndex(e => e == target), 1)
// }
emitListener(eventName, params) {

@@ -497,3 +420,2 @@

allObservers.forEach(observer => {

@@ -512,6 +434,2 @@ const { instance, callback } = observer;

pageDidDrag(index) {
this.emitListener(TABVIEW_BECOME_RESPONDER, index)
}
removeListener(instance, eventName, callback) {

@@ -539,4 +457,33 @@ if (this.observers.hasOwnProperty(eventName)) {

}
/**
* renderHeader和renderFooter的参数装配
*/
makeParams() {
const { tabs, currentIndex } = this.state;
if (tabs && tabs.length > currentIndex) {
return { item: tabs[currentIndex], index: currentIndex }
}
return { item: '', index: 0 }
}
/**
* 组装子页面的参数
*/
makeSceneParams(item, index) {
const { makeHeaderHeight, faultHeight, renderScrollHeader } = this.props;
if (!renderScrollHeader) {
return { item, index }
}
const { currentIndex, containerTrans, tabviewHeight, tabbarHeight } = this.state;
const params = { item, index, isActive: currentIndex == index, containerTrans, makeHeaderHeight, faultHeight };
params.addListener = this.addListener;
params.removeListener = this.removeListener;
params.scenePageDidDrag = this.scenePageDidDrag;
params.expectHeight = makeHeaderHeight() + tabviewHeight - tabbarHeight;
return params;
}
/**
* 组装给tabbar的参数

@@ -575,2 +522,23 @@ */

}
}
class SceneView extends React.Component {
shouldComponentUpdate(nextProps) {
return !!nextProps.shouldUpdate;
}
render() {
return React.Children.only(this.props.children)
}
}
const Scene = (props) => {
return <View {...props}>
<SceneView shouldUpdate={props.shouldUpdate}>
{props.children}
</SceneView>
</View>
}
SocketSocket SOC 2 Logo

Product

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

Packages

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc