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

@react-aria/selection

Package Overview
Dependencies
Maintainers
1
Versions
792
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@react-aria/selection - npm Package Compare versions

Comparing version 3.1.0 to 3.2.0

131

dist/main.js

@@ -6,2 +6,7 @@ var {

var {
mergeProps
} = require("@react-aria/utils");
var {
focusSafely,
getFocusableTreeWalker

@@ -11,7 +16,2 @@ } = require("@react-aria/focus");

var {
focusWithoutScrolling,
mergeProps
} = require("@react-aria/utils");
var {
useEffect,

@@ -22,2 +22,8 @@ useRef,

var _babelRuntimeHelpersExtends = $parcel$interopDefault(require("@babel/runtime/helpers/extends"));
function $parcel$interopDefault(a) {
return a && a.__esModule ? a.default : a;
}
/**

@@ -42,2 +48,11 @@ * Handles typeahead interactions with collections.

return;
} // Do not propagate the Spacebar event if it's meant to be part of the search.
// When we time out, the search term becomes empty, hence the check on length.
// Trimming is to account for the case of pressing the Spacebar more than once,
// which should cycle through the selection/deselection of the focused item.
if (character === ' ' && state.search.trim().length > 0) {
e.preventDefault();
e.stopPropagation();
}

@@ -68,3 +83,5 @@

typeSelectProps: {
onKeyDown: keyboardDelegate.getKeyForSearch ? onKeyDown : null
// Using a capturing listener to catch the keydown event before
// other hooks in order to handle the Spacebar event.
onKeyDownCapture: keyboardDelegate.getKeyForSearch ? onKeyDown : null
}

@@ -109,3 +126,5 @@ };

disallowEmptySelection = false,
disallowSelectAll = false
disallowSelectAll = false,
selectOnFocus = false,
disallowTypeAhead = false
} = options;

@@ -130,4 +149,13 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
} else if (shouldFocusWrap) {
manager.setFocusedKey(delegate.getFirstKey(manager.focusedKey));
let wrapKey = delegate.getFirstKey(manager.focusedKey);
manager.setFocusedKey(wrapKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(wrapKey);
}
}

@@ -151,4 +179,13 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
} else if (shouldFocusWrap) {
manager.setFocusedKey(delegate.getLastKey(manager.focusedKey));
let wrapKey = delegate.getLastKey(manager.focusedKey);
manager.setFocusedKey(wrapKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(wrapKey);
}
}

@@ -172,2 +209,6 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
}

@@ -191,2 +232,6 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
}

@@ -208,2 +253,6 @@

if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(firstKey);
}
if ($f791fefd7189e0e4d903034fb2925$var$isCtrlKeyPressed(e) && e.shiftKey && manager.selectionMode === 'multiple') {

@@ -222,2 +271,6 @@ manager.extendSelection(firstKey);

if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(lastKey);
}
if ($f791fefd7189e0e4d903034fb2925$var$isCtrlKeyPressed(e) && e.shiftKey && manager.selectionMode === 'multiple') {

@@ -338,5 +391,9 @@ manager.extendSelection(lastKey);

if (relatedTarget && e.currentTarget.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING) {
manager.setFocusedKey(delegate.getLastKey());
var _manager$lastSelected;
manager.setFocusedKey((_manager$lastSelected = manager.lastSelectedKey) != null ? _manager$lastSelected : delegate.getLastKey());
} else {
manager.setFocusedKey(delegate.getFirstKey());
var _manager$firstSelecte;
manager.setFocusedKey((_manager$firstSelecte = manager.firstSelectedKey) != null ? _manager$firstSelecte : delegate.getFirstKey());
}

@@ -376,3 +433,3 @@ }

if (focusedKey == null) {
focusWithoutScrolling(ref.current);
focusSafely(ref.current);
}

@@ -382,2 +439,16 @@ } // eslint-disable-next-line react-hooks/exhaustive-deps

}, []);
let handlers = {
// We use a capturing listener to ensure that the keyboard events for the collection
// override those of the children. For example, ArrowDown in a table should always go
// to the cell below, and not open a menu.
onKeyDownCapture: onKeyDown,
onFocus,
onBlur,
onMouseDown(e) {
// Prevent focus going to the collection when clicking on the scrollbar.
e.preventDefault();
}
};
let {

@@ -389,17 +460,12 @@ typeSelectProps

});
return {
collectionProps: mergeProps(typeSelectProps, {
tabIndex: -1,
// We use a capturing listener to ensure that the keyboard events for the collection
// override those of the children. For example, ArrowDown in a table should always go
// to the cell below, and not open a menu.
onKeyDownCapture: onKeyDown,
onFocus,
onBlur,
onMouseDown(e) {
// Prevent focus going to the collection when clicking on the scrollbar.
e.preventDefault();
}
if (!disallowTypeAhead) {
handlers = mergeProps(typeSelectProps, handlers);
}
return {
collectionProps: _babelRuntimeHelpersExtends({}, handlers, {
// If nothing is focused within the collection, make the collection itself tabbable.
// This will be marshalled to either the first or last item depending on where focus came from.
tabIndex: manager.focusedKey == null ? 0 : -1
})

@@ -421,2 +487,3 @@ };

isVirtualized,
shouldUseVirtualFocus,
focus

@@ -446,10 +513,10 @@ } = options;

useEffect(() => {
if (isFocused && manager.isFocused && document.activeElement !== ref.current) {
if (isFocused && manager.isFocused && !shouldUseVirtualFocus && document.activeElement !== ref.current) {
if (focus) {
focus();
} else {
focusWithoutScrolling(ref.current);
focusSafely(ref.current);
}
}
}, [ref, isFocused, manager.focusedKey, manager.isFocused]);
}, [ref, isFocused, manager.focusedKey, manager.isFocused, shouldUseVirtualFocus]);
let itemProps = {

@@ -668,3 +735,5 @@ tabIndex: isFocused ? 0 : -1,

isVirtualized,
disallowEmptySelection
disallowEmptySelection,
selectOnFocus = false,
disallowTypeAhead
} = props; // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).

@@ -697,3 +766,5 @@ // When virtualized, the layout object will be passed in as a prop and override this.

shouldFocusWrap,
disallowEmptySelection
disallowEmptySelection,
selectOnFocus,
disallowTypeAhead
});

@@ -700,0 +771,0 @@ return {

import { useCollator } from "@react-aria/i18n";
import { getFocusableTreeWalker } from "@react-aria/focus";
import { focusWithoutScrolling, mergeProps } from "@react-aria/utils";
import { mergeProps } from "@react-aria/utils";
import { focusSafely, getFocusableTreeWalker } from "@react-aria/focus";
import { useEffect, useRef, useMemo } from "react";
import _babelRuntimeHelpersEsmExtends from "@babel/runtime/helpers/esm/extends";

@@ -25,2 +26,11 @@ /**

return;
} // Do not propagate the Spacebar event if it's meant to be part of the search.
// When we time out, the search term becomes empty, hence the check on length.
// Trimming is to account for the case of pressing the Spacebar more than once,
// which should cycle through the selection/deselection of the focused item.
if (character === ' ' && state.search.trim().length > 0) {
e.preventDefault();
e.stopPropagation();
}

@@ -51,3 +61,5 @@

typeSelectProps: {
onKeyDown: keyboardDelegate.getKeyForSearch ? onKeyDown : null
// Using a capturing listener to catch the keydown event before
// other hooks in order to handle the Spacebar event.
onKeyDownCapture: keyboardDelegate.getKeyForSearch ? onKeyDown : null
}

@@ -90,3 +102,5 @@ };

disallowEmptySelection = false,
disallowSelectAll = false
disallowSelectAll = false,
selectOnFocus = false,
disallowTypeAhead = false
} = options;

@@ -111,4 +125,13 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
} else if (shouldFocusWrap) {
manager.setFocusedKey(delegate.getFirstKey(manager.focusedKey));
let wrapKey = delegate.getFirstKey(manager.focusedKey);
manager.setFocusedKey(wrapKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(wrapKey);
}
}

@@ -132,4 +155,13 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
} else if (shouldFocusWrap) {
manager.setFocusedKey(delegate.getLastKey(manager.focusedKey));
let wrapKey = delegate.getLastKey(manager.focusedKey);
manager.setFocusedKey(wrapKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(wrapKey);
}
}

@@ -153,2 +185,6 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
}

@@ -172,2 +208,6 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
}

@@ -189,2 +229,6 @@

if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(firstKey);
}
if ($a9b9aa71af07c56ab1d89ca45381f4b$var$isCtrlKeyPressed(e) && e.shiftKey && manager.selectionMode === 'multiple') {

@@ -203,2 +247,6 @@ manager.extendSelection(firstKey);

if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(lastKey);
}
if ($a9b9aa71af07c56ab1d89ca45381f4b$var$isCtrlKeyPressed(e) && e.shiftKey && manager.selectionMode === 'multiple') {

@@ -319,5 +367,9 @@ manager.extendSelection(lastKey);

if (relatedTarget && e.currentTarget.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING) {
manager.setFocusedKey(delegate.getLastKey());
var _manager$lastSelected;
manager.setFocusedKey((_manager$lastSelected = manager.lastSelectedKey) != null ? _manager$lastSelected : delegate.getLastKey());
} else {
manager.setFocusedKey(delegate.getFirstKey());
var _manager$firstSelecte;
manager.setFocusedKey((_manager$firstSelecte = manager.firstSelectedKey) != null ? _manager$firstSelecte : delegate.getFirstKey());
}

@@ -357,3 +409,3 @@ }

if (focusedKey == null) {
focusWithoutScrolling(ref.current);
focusSafely(ref.current);
}

@@ -363,2 +415,16 @@ } // eslint-disable-next-line react-hooks/exhaustive-deps

}, []);
let handlers = {
// We use a capturing listener to ensure that the keyboard events for the collection
// override those of the children. For example, ArrowDown in a table should always go
// to the cell below, and not open a menu.
onKeyDownCapture: onKeyDown,
onFocus,
onBlur,
onMouseDown(e) {
// Prevent focus going to the collection when clicking on the scrollbar.
e.preventDefault();
}
};
let {

@@ -370,17 +436,12 @@ typeSelectProps

});
return {
collectionProps: mergeProps(typeSelectProps, {
tabIndex: -1,
// We use a capturing listener to ensure that the keyboard events for the collection
// override those of the children. For example, ArrowDown in a table should always go
// to the cell below, and not open a menu.
onKeyDownCapture: onKeyDown,
onFocus,
onBlur,
onMouseDown(e) {
// Prevent focus going to the collection when clicking on the scrollbar.
e.preventDefault();
}
if (!disallowTypeAhead) {
handlers = mergeProps(typeSelectProps, handlers);
}
return {
collectionProps: _babelRuntimeHelpersEsmExtends({}, handlers, {
// If nothing is focused within the collection, make the collection itself tabbable.
// This will be marshalled to either the first or last item depending on where focus came from.
tabIndex: manager.focusedKey == null ? 0 : -1
})

@@ -400,2 +461,3 @@ };

isVirtualized,
shouldUseVirtualFocus,
focus

@@ -425,10 +487,10 @@ } = options;

useEffect(() => {
if (isFocused && manager.isFocused && document.activeElement !== ref.current) {
if (isFocused && manager.isFocused && !shouldUseVirtualFocus && document.activeElement !== ref.current) {
if (focus) {
focus();
} else {
focusWithoutScrolling(ref.current);
focusSafely(ref.current);
}
}
}, [ref, isFocused, manager.focusedKey, manager.isFocused]);
}, [ref, isFocused, manager.focusedKey, manager.isFocused, shouldUseVirtualFocus]);
let itemProps = {

@@ -643,3 +705,5 @@ tabIndex: isFocused ? 0 : -1,

isVirtualized,
disallowEmptySelection
disallowEmptySelection,
selectOnFocus = false,
disallowTypeAhead
} = props; // By default, a KeyboardDelegate is provided which uses the DOM to query layout information (e.g. for page up/page down).

@@ -672,3 +736,5 @@ // When virtualized, the layout object will be passed in as a prop and override this.

shouldFocusWrap,
disallowEmptySelection
disallowEmptySelection,
selectOnFocus,
disallowTypeAhead
});

@@ -675,0 +741,0 @@ return {

@@ -62,2 +62,12 @@ import { HTMLAttributes, Key, RefObject } from "react";

disallowSelectAll?: boolean;
/**
* Whether selection should occur automatically on focus.
* @default false
*/
selectOnFocus?: boolean;
/**
* Whether typeahead is disabled.
* @default false
*/
disallowTypeAhead?: boolean;
}

@@ -98,2 +108,6 @@ interface SelectableCollectionAria {

focus?: () => void;
/**
* Whether the option should use virtual focus instead of being focused directly.
*/
shouldUseVirtualFocus?: boolean;
}

@@ -160,2 +174,12 @@ interface SelectableItemAria {

disallowEmptySelection?: boolean;
/**
* Whether selection should occur automatically on focus.
* @default false
*/
selectOnFocus?: boolean;
/**
* Whether typeahead is disabled.
* @default false
*/
disallowTypeAhead?: boolean;
}

@@ -162,0 +186,0 @@ interface SelectableListAria {

{
"name": "@react-aria/selection",
"version": "3.1.0",
"version": "3.2.0",
"description": "Spectrum UI components in React",

@@ -21,9 +21,9 @@ "license": "Apache-2.0",

"@babel/runtime": "^7.6.2",
"@react-aria/focus": "^3.1.0",
"@react-aria/focus": "^3.2.0",
"@react-aria/i18n": "^3.1.0",
"@react-aria/interactions": "^3.1.0",
"@react-aria/utils": "^3.1.0",
"@react-stately/collections": "^3.1.0",
"@react-stately/selection": "^3.1.0",
"@react-types/shared": "^3.1.0"
"@react-aria/interactions": "^3.2.0",
"@react-aria/utils": "^3.2.0",
"@react-stately/collections": "^3.2.0",
"@react-stately/selection": "^3.2.0",
"@react-types/shared": "^3.2.0"
},

@@ -36,3 +36,3 @@ "peerDependencies": {

},
"gitHead": "211099972fe75ee581892efd01a7f89dfb9cdf69"
"gitHead": "661f0f2e3b8648a75aae83043267954700059fe0"
}

@@ -14,5 +14,4 @@ /*

import {FocusEvent, HTMLAttributes, KeyboardEvent, RefObject, useEffect} from 'react';
import {focusSafely, getFocusableTreeWalker} from '@react-aria/focus';
import {FocusStrategy, KeyboardDelegate} from '@react-types/shared';
import {focusWithoutScrolling} from '@react-aria/utils';
import {getFocusableTreeWalker} from '@react-aria/focus';
import {mergeProps} from '@react-aria/utils';

@@ -67,3 +66,13 @@ import {MultipleSelectionManager} from '@react-stately/selection';

*/
disallowSelectAll?: boolean
disallowSelectAll?: boolean,
/**
* Whether selection should occur automatically on focus.
* @default false
*/
selectOnFocus?: boolean,
/**
* Whether typeahead is disabled.
* @default false
*/
disallowTypeAhead?: boolean
}

@@ -87,3 +96,5 @@

disallowEmptySelection = false,
disallowSelectAll = false
disallowSelectAll = false,
selectOnFocus = false,
disallowTypeAhead = false
} = options;

@@ -109,4 +120,11 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
} else if (shouldFocusWrap) {
manager.setFocusedKey(delegate.getFirstKey(manager.focusedKey));
let wrapKey = delegate.getFirstKey(manager.focusedKey);
manager.setFocusedKey(wrapKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(wrapKey);
}
}

@@ -129,4 +147,11 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
} else if (shouldFocusWrap) {
manager.setFocusedKey(delegate.getLastKey(manager.focusedKey));
let wrapKey = delegate.getLastKey(manager.focusedKey);
manager.setFocusedKey(wrapKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(wrapKey);
}
}

@@ -146,2 +171,5 @@

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
}

@@ -160,2 +188,5 @@ if (e.shiftKey && manager.selectionMode === 'multiple') {

manager.setFocusedKey(nextKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(nextKey);
}
}

@@ -173,2 +204,5 @@ if (e.shiftKey && manager.selectionMode === 'multiple') {

manager.setFocusedKey(firstKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(firstKey);
}
if (isCtrlKeyPressed(e) && e.shiftKey && manager.selectionMode === 'multiple') {

@@ -184,2 +218,5 @@ manager.extendSelection(firstKey);

manager.setFocusedKey(lastKey);
if (manager.selectionMode === 'single' && selectOnFocus) {
manager.replaceSelection(lastKey);
}
if (isCtrlKeyPressed(e) && e.shiftKey && manager.selectionMode === 'multiple') {

@@ -278,5 +315,5 @@ manager.extendSelection(lastKey);

if (relatedTarget && (e.currentTarget.compareDocumentPosition(relatedTarget) & Node.DOCUMENT_POSITION_FOLLOWING)) {
manager.setFocusedKey(delegate.getLastKey());
manager.setFocusedKey(manager.lastSelectedKey ?? delegate.getLastKey());
} else {
manager.setFocusedKey(delegate.getFirstKey());
manager.setFocusedKey(manager.firstSelectedKey ?? delegate.getFirstKey());
}

@@ -315,3 +352,3 @@ }

if (focusedKey == null) {
focusWithoutScrolling(ref.current);
focusSafely(ref.current);
}

@@ -322,2 +359,15 @@ }

let handlers = {
// We use a capturing listener to ensure that the keyboard events for the collection
// override those of the children. For example, ArrowDown in a table should always go
// to the cell below, and not open a menu.
onKeyDownCapture: onKeyDown,
onFocus,
onBlur,
onMouseDown(e) {
// Prevent focus going to the collection when clicking on the scrollbar.
e.preventDefault();
}
};
let {typeSelectProps} = useTypeSelect({

@@ -328,17 +378,14 @@ keyboardDelegate: delegate,

if (!disallowTypeAhead) {
handlers = mergeProps(typeSelectProps, handlers);
}
return {
collectionProps: mergeProps(typeSelectProps, {
tabIndex: -1,
// We use a capturing listener to ensure that the keyboard events for the collection
// override those of the children. For example, ArrowDown in a table should always go
// to the cell below, and not open a menu.
onKeyDownCapture: onKeyDown,
onFocus,
onBlur,
onMouseDown(e) {
// Prevent focus going to the collection when clicking on the scrollbar.
e.preventDefault();
}
})
collectionProps: {
...handlers,
// If nothing is focused within the collection, make the collection itself tabbable.
// This will be marshalled to either the first or last item depending on where focus came from.
tabIndex: manager.focusedKey == null ? 0 : -1
}
};
}

@@ -13,3 +13,3 @@ /*

import {focusWithoutScrolling} from '@react-aria/utils';
import {focusSafely} from '@react-aria/focus';
import {HTMLAttributes, Key, RefObject, useEffect} from 'react';

@@ -45,3 +45,7 @@ import {MultipleSelectionManager} from '@react-stately/selection';

*/
focus?: () => void
focus?: () => void,
/**
* Whether the option should use virtual focus instead of being focused directly.
*/
shouldUseVirtualFocus?: boolean
}

@@ -66,2 +70,3 @@

isVirtualized,
shouldUseVirtualFocus,
focus

@@ -91,10 +96,10 @@ } = options;

useEffect(() => {
if (isFocused && manager.isFocused && document.activeElement !== ref.current) {
if (isFocused && manager.isFocused && !shouldUseVirtualFocus && document.activeElement !== ref.current) {
if (focus) {
focus();
} else {
focusWithoutScrolling(ref.current);
focusSafely(ref.current);
}
}
}, [ref, isFocused, manager.focusedKey, manager.isFocused]);
}, [ref, isFocused, manager.focusedKey, manager.isFocused, shouldUseVirtualFocus]);

@@ -101,0 +106,0 @@ let itemProps: SelectableItemAria['itemProps'] = {

@@ -59,3 +59,13 @@ /*

*/
disallowEmptySelection?: boolean
disallowEmptySelection?: boolean,
/**
* Whether selection should occur automatically on focus.
* @default false
*/
selectOnFocus?: boolean,
/**
* Whether typeahead is disabled.
* @default false
*/
disallowTypeAhead?: boolean
}

@@ -83,3 +93,5 @@

isVirtualized,
disallowEmptySelection
disallowEmptySelection,
selectOnFocus = false,
disallowTypeAhead
} = props;

@@ -109,3 +121,5 @@

shouldFocusWrap,
disallowEmptySelection
disallowEmptySelection,
selectOnFocus,
disallowTypeAhead
});

@@ -112,0 +126,0 @@

@@ -55,2 +55,11 @@ /*

// Do not propagate the Spacebar event if it's meant to be part of the search.
// When we time out, the search term becomes empty, hence the check on length.
// Trimming is to account for the case of pressing the Spacebar more than once,
// which should cycle through the selection/deselection of the focused item.
if (character === ' ' && state.search.trim().length > 0) {
e.preventDefault();
e.stopPropagation();
}
state.search += character;

@@ -80,3 +89,5 @@

typeSelectProps: {
onKeyDown: keyboardDelegate.getKeyForSearch ? onKeyDown : null
// Using a capturing listener to catch the keydown event before
// other hooks in order to handle the Spacebar event.
onKeyDownCapture: keyboardDelegate.getKeyForSearch ? onKeyDown : null
}

@@ -83,0 +94,0 @@ };

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