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

@vaadin/checkbox

Package Overview
Dependencies
Maintainers
19
Versions
416
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@vaadin/checkbox - npm Package Compare versions

Comparing version 22.0.0-alpha6 to 22.0.0-alpha7

50

package.json
{
"name": "@vaadin/checkbox",
"version": "22.0.0-alpha6",
"version": "22.0.0-alpha7",
"publishConfig": {
"access": "public"
},
"description": "vaadin-checkbox",
"license": "Apache-2.0",
"repository": {
"type": "git",
"url": "https://github.com/vaadin/web-components.git",
"directory": "packages/checkbox"
},
"author": "Vaadin Ltd",
"homepage": "https://vaadin.com/components",
"bugs": {
"url": "https://github.com/vaadin/web-components/issues"
},
"main": "vaadin-checkbox.js",
"module": "vaadin-checkbox.js",
"repository": "vaadin/web-components",
"files": [
"src",
"theme",
"vaadin-*.d.ts",
"vaadin-*.js"
],
"keywords": [

@@ -15,21 +34,9 @@ "Vaadin",

],
"author": "Vaadin Ltd",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/vaadin/web-components/issues"
},
"homepage": "https://vaadin.com/components",
"files": [
"vaadin-*.d.ts",
"vaadin-*.js",
"src",
"theme"
],
"dependencies": {
"@polymer/polymer": "^3.0.0",
"@vaadin/vaadin-control-state-mixin": "^22.0.0-alpha6",
"@vaadin/vaadin-element-mixin": "^22.0.0-alpha6",
"@vaadin/vaadin-lumo-styles": "^22.0.0-alpha6",
"@vaadin/vaadin-material-styles": "^22.0.0-alpha6",
"@vaadin/vaadin-themable-mixin": "^22.0.0-alpha6"
"@vaadin/component-base": "22.0.0-alpha7",
"@vaadin/field-base": "22.0.0-alpha7",
"@vaadin/vaadin-lumo-styles": "22.0.0-alpha7",
"@vaadin/vaadin-material-styles": "22.0.0-alpha7",
"@vaadin/vaadin-themable-mixin": "22.0.0-alpha7"
},

@@ -41,6 +48,3 @@ "devDependencies": {

},
"publishConfig": {
"access": "public"
},
"gitHead": "4b136b1c7da8942960e7255f40c27859125b3a45"
"gitHead": "8e89419c6b44a1d225d5859e180d7b35e47ddb52"
}

@@ -1,9 +0,13 @@

import { GestureEventListeners } from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
/**
* @license
* Copyright (c) 2021 Vaadin Ltd.
* This program is available under Apache License Version 2.0, available at https://vaadin.com/license/
*/
import { ActiveMixin } from '@vaadin/component-base/src/active-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { CheckedMixin } from '@vaadin/field-base/src/checked-mixin.js';
import { DelegateFocusMixin } from '@vaadin/field-base/src/delegate-focus-mixin.js';
import { SlotLabelMixin } from '@vaadin/field-base/src/slot-label-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ControlStateMixin } from '@vaadin/vaadin-control-state-mixin/vaadin-control-state-mixin.js';
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
/**

@@ -19,3 +23,3 @@ * Fired when the `checked` property changes.

export interface CheckboxElementEventMap {
export interface CheckboxCustomEventMap {
'checked-changed': CheckboxCheckedChangedEvent;

@@ -26,11 +30,9 @@

export interface CheckboxEventMap extends HTMLElementEventMap, CheckboxElementEventMap {}
export interface CheckboxEventMap extends HTMLElementEventMap, CheckboxCustomEventMap {}
/**
* `<vaadin-checkbox>` is a Web Component for customized checkboxes.
* `<vaadin-checkbox>` is an input field representing a binary choice.
*
* ```html
* <vaadin-checkbox>
* Make my profile visible
* </vaadin-checkbox>
* <vaadin-checkbox>I accept the terms and conditions</vaadin-checkbox>
* ```

@@ -42,42 +44,33 @@ *

*
* Part name | Description
* ------------------|----------------
* `checkbox` | The wrapper element for the native <input type="checkbox">
* `label` | The wrapper element in which the component's children, namely the label, is slotted
* Part name | Description
* ------------|----------------
* `container` | The container element
* `checkbox` | The wrapper element that contains slotted `<input type="checkbox">`
* `label` | The wrapper element that contains slotted `<label>`
*
* The following state attributes are available for styling:
*
* Attribute | Description | Part name
* -------------|-------------|--------------
* `active` | Set when the checkbox is pressed down, either with mouse, touch or the keyboard. | `:host`
* `disabled` | Set when the checkbox is disabled. | `:host`
* `focus-ring` | Set when the checkbox is focused using the keyboard. | `:host`
* `focused` | Set when the checkbox is focused. | `:host`
* `indeterminate` | Set when the checkbox is in indeterminate mode. | `:host`
* `checked` | Set when the checkbox is checked. | `:host`
* `empty` | Set when there is no label provided. | `label`
* Attribute | Description | Part name
* ----------------|-------------|--------------
* `active` | Set when the checkbox is pressed down, either with mouse, touch or the keyboard. | `:host`
* `disabled` | Set when the checkbox is disabled. | `:host`
* `focus-ring` | Set when the checkbox is focused using the keyboard. | `:host`
* `focused` | Set when the checkbox is focused. | `:host`
* `indeterminate` | Set when the checkbox is in the indeterminate state. | `:host`
* `checked` | Set when the checkbox is checked. | `:host`
* `has-label` | Set when the checkbox has a label. | `:host`
*
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
*
* @fires {Event} change - Fired when the user commits a value change.
* @fires {CustomEvent} checked-changed - Fired when the `checked` property changes.
* @fires {CustomEvent} indeterminate-changed - Fired when the `indeterminate` property changes.
*/
declare class CheckboxElement extends ElementMixin(
ControlStateMixin(ThemableMixin(GestureEventListeners(HTMLElement)))
declare class Checkbox extends SlotLabelMixin(
CheckedMixin(DelegateFocusMixin(ActiveMixin(ElementMixin(ThemableMixin(HTMLElement)))))
) {
readonly focusElement: HTMLInputElement;
/**
* Name of the element.
*/
name: string;
/**
* True if the checkbox is checked.
*/
checked: boolean;
/**
* Indeterminate state of the checkbox when it's neither checked nor unchecked, but undetermined.
* True if the checkbox is in the indeterminate state which means
* it is not possible to say whether it is checked or unchecked.
* The state is reset once the user switches the checkbox by hand.
*
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#Indeterminate_state_checkboxes

@@ -88,11 +81,9 @@ */

/**
* The value given to the data submitted with the checkbox's name to the server when the control is inside a form.
* The name of the checkbox.
*/
value: string | null | undefined;
name: string;
_toggleChecked(): void;
addEventListener<K extends keyof CheckboxEventMap>(
type: K,
listener: (this: CheckboxElement, ev: CheckboxEventMap[K]) => void,
listener: (this: Checkbox, ev: CheckboxEventMap[K]) => void,
options?: boolean | AddEventListenerOptions

@@ -103,3 +94,3 @@ ): void;

type: K,
listener: (this: CheckboxElement, ev: CheckboxEventMap[K]) => void,
listener: (this: Checkbox, ev: CheckboxEventMap[K]) => void,
options?: boolean | EventListenerOptions

@@ -111,6 +102,6 @@ ): void;

interface HTMLElementTagNameMap {
'vaadin-checkbox': CheckboxElement;
'vaadin-checkbox': Checkbox;
}
}
export { CheckboxElement };
export { Checkbox };

@@ -7,14 +7,16 @@ /**

import { PolymerElement, html } from '@polymer/polymer/polymer-element.js';
import { GestureEventListeners } from '@polymer/polymer/lib/mixins/gesture-event-listeners.js';
import { ActiveMixin } from '@vaadin/component-base/src/active-mixin.js';
import { ElementMixin } from '@vaadin/component-base/src/element-mixin.js';
import { AriaLabelController } from '@vaadin/field-base/src/aria-label-controller.js';
import { CheckedMixin } from '@vaadin/field-base/src/checked-mixin.js';
import { DelegateFocusMixin } from '@vaadin/field-base/src/delegate-focus-mixin.js';
import { InputController } from '@vaadin/field-base/src/input-controller.js';
import { SlotLabelMixin } from '@vaadin/field-base/src/slot-label-mixin.js';
import { ThemableMixin } from '@vaadin/vaadin-themable-mixin/vaadin-themable-mixin.js';
import { ControlStateMixin } from '@vaadin/vaadin-control-state-mixin/vaadin-control-state-mixin.js';
import { ElementMixin } from '@vaadin/vaadin-element-mixin/vaadin-element-mixin.js';
/**
* `<vaadin-checkbox>` is a Web Component for customized checkboxes.
* `<vaadin-checkbox>` is an input field representing a binary choice.
*
* ```html
* <vaadin-checkbox>
* Make my profile visible
* </vaadin-checkbox>
* <vaadin-checkbox>I accept the terms and conditions</vaadin-checkbox>
* ```

@@ -26,22 +28,22 @@ *

*
* Part name | Description
* ------------------|----------------
* `checkbox` | The wrapper element for the native <input type="checkbox">
* `label` | The wrapper element in which the component's children, namely the label, is slotted
* Part name | Description
* ------------|----------------
* `container` | The container element.
* `checkbox` | The wrapper element that contains slotted <input type="checkbox">.
* `label` | The wrapper element that contains slotted <label>.
*
* The following state attributes are available for styling:
*
* Attribute | Description | Part name
* -------------|-------------|--------------
* `active` | Set when the checkbox is pressed down, either with mouse, touch or the keyboard. | `:host`
* `disabled` | Set when the checkbox is disabled. | `:host`
* `focus-ring` | Set when the checkbox is focused using the keyboard. | `:host`
* `focused` | Set when the checkbox is focused. | `:host`
* `indeterminate` | Set when the checkbox is in indeterminate mode. | `:host`
* `checked` | Set when the checkbox is checked. | `:host`
* `empty` | Set when there is no label provided. | `label`
* Attribute | Description | Part name
* ----------------|-------------|--------------
* `active` | Set when the checkbox is pressed down, either with mouse, touch or the keyboard. | `:host`
* `disabled` | Set when the checkbox is disabled. | `:host`
* `focus-ring` | Set when the checkbox is focused using the keyboard. | `:host`
* `focused` | Set when the checkbox is focused. | `:host`
* `indeterminate` | Set when the checkbox is in the indeterminate state. | `:host`
* `checked` | Set when the checkbox is checked. | `:host`
* `has-label` | Set when the checkbox has a label. | `:host`
*
* See [Styling Components](https://vaadin.com/docs/latest/ds/customization/styling-components) documentation.
*
* @fires {Event} change - Fired when the user commits a value change.
* @fires {CustomEvent} checked-changed - Fired when the `checked` property changes.

@@ -51,8 +53,16 @@ * @fires {CustomEvent} indeterminate-changed - Fired when the `indeterminate` property changes.

* @extends HTMLElement
* @mixes ThemableMixin
* @mixes ElementMixin
* @mixes ControlStateMixin
* @mixes ThemableMixin
* @mixes GestureEventListeners
* @mixes ActiveMixin
* @mixes DelegateFocusMixin
* @mixes CheckedMixin
* @mixes SlotLabelMixin
*/
class CheckboxElement extends ElementMixin(ControlStateMixin(ThemableMixin(GestureEventListeners(PolymerElement)))) {
class Checkbox extends SlotLabelMixin(
CheckedMixin(DelegateFocusMixin(ActiveMixin(ElementMixin(ThemableMixin(PolymerElement)))))
) {
static get is() {
return 'vaadin-checkbox';
}
static get template() {

@@ -69,15 +79,18 @@ return html`

label {
:host([disabled]) {
-webkit-tap-highlight-color: transparent;
}
.vaadin-checkbox-container {
display: inline-flex;
align-items: baseline;
outline: none;
}
[part='checkbox'] {
.vaadin-checkbox-wrapper {
position: relative;
display: inline-block;
flex: none;
}
input[type='checkbox'] {
/* visually hidden */
::slotted(input) {
position: absolute;

@@ -93,48 +106,27 @@ top: 0;

}
:host([disabled]) {
-webkit-tap-highlight-color: transparent;
}
</style>
<div class="vaadin-checkbox-container">
<div class="vaadin-checkbox-wrapper">
<div part="checkbox"></div>
<slot name="input"></slot>
</div>
<label>
<span part="checkbox">
<input
type="checkbox"
checked="{{checked::change}}"
disabled$="[[disabled]]"
indeterminate="{{indeterminate::change}}"
role="presentation"
tabindex="-1"
/>
</span>
<slot name="label"></slot>
<span part="label">
<slot></slot>
</span>
</label>
<div style="display: none !important">
<slot id="noop"></slot>
</div>
</div>
`;
}
static get is() {
return 'vaadin-checkbox';
}
static get properties() {
return {
/**
* True if the checkbox is checked.
* @type {boolean}
*/
checked: {
type: Boolean,
value: false,
notify: true,
observer: '_checkedChanged',
reflectToAttribute: true
},
/**
* Indeterminate state of the checkbox when it's neither checked nor unchecked, but undetermined.
* True if the checkbox is in the indeterminate state which means
* it is not possible to say whether it is checked or unchecked.
* The state is reset once the user switches the checkbox by hand.
*
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#Indeterminate_state_checkboxes
*
* @type {boolean}

@@ -145,18 +137,14 @@ */

notify: true,
observer: '_indeterminateChanged',
reflectToAttribute: true,
value: false
value: false,
reflectToAttribute: true
},
/**
* The value given to the data submitted with the checkbox's name to the server when the control is inside a form.
* The name of the checkbox.
*
* @type {string}
*/
value: {
name: {
type: String,
value: 'on'
},
/** @private */
_nativeCheckbox: {
type: Object
value: ''
}

@@ -166,175 +154,98 @@ };

constructor() {
super();
/**
* @type {string}
* Name of the element.
*/
this.name;
/** @override */
static get delegateProps() {
return [...super.delegateProps, 'indeterminate'];
}
get name() {
return this.checked ? this._storedName : '';
/** @override */
static get delegateAttrs() {
return [...super.delegateAttrs, 'name'];
}
set name(name) {
this._storedName = name;
constructor() {
super();
this._setType('checkbox');
// Set the string "on" as the default value for the checkbox following the HTML specification:
// https://html.spec.whatwg.org/multipage/input.html#dom-input-value-default-on
this.value = 'on';
}
/** @protected */
ready() {
super.ready();
this.setAttribute('role', 'checkbox');
this._nativeCheckbox = this.shadowRoot.querySelector('input[type="checkbox"]');
this.addEventListener('click', this._handleClick.bind(this));
this._addActiveListeners();
const attrName = this.getAttribute('name');
if (attrName) {
this.name = attrName;
}
this.shadowRoot
.querySelector('[part~="label"]')
.querySelector('slot')
.addEventListener('slotchange', this._updateLabelAttribute.bind(this));
this._updateLabelAttribute();
}
/** @private */
_updateLabelAttribute() {
const label = this.shadowRoot.querySelector('[part~="label"]');
const assignedNodes = label.firstElementChild.assignedNodes();
if (this._isAssignedNodesEmpty(assignedNodes)) {
label.setAttribute('empty', '');
} else {
label.removeAttribute('empty');
}
}
/** @private */
_isAssignedNodesEmpty(nodes) {
// The assigned nodes considered to be empty if there is no slotted content or only one empty text node
return (
nodes.length === 0 ||
(nodes.length == 1 && nodes[0].nodeType == Node.TEXT_NODE && nodes[0].textContent.trim() === '')
this.addController(
new InputController(this, (input) => {
this._setInputElement(input);
this._setFocusElement(input);
this.stateTarget = input;
})
);
this.addController(new AriaLabelController(this.inputElement, this._labelNode));
}
/** @private */
_checkedChanged(checked) {
if (this.indeterminate) {
this.setAttribute('aria-checked', 'mixed');
} else {
this.setAttribute('aria-checked', Boolean(checked));
}
/**
* A reference to the default slot from which nodes are copied to the label node.
*
* @override
* @protected
* @type {HTMLSlotElement}
*/
get _sourceSlot() {
return this.$.noop;
}
/** @private */
_indeterminateChanged(indeterminate) {
if (indeterminate) {
this.setAttribute('aria-checked', 'mixed');
} else {
this.setAttribute('aria-checked', this.checked);
}
}
/**
* Override __copyNodesToSlotTarget from SlotTargetMixin to show a warning.
* @override
* @protected
* @param {!Array<!Node>} nodes
**/
__copyNodesToSlotTarget(nodes) {
super.__copyNodesToSlotTarget(nodes);
/** @private */
_addActiveListeners() {
// DOWN
this._addEventListenerToNode(this, 'down', (e) => {
if (this.__interactionsAllowed(e)) {
this.setAttribute('active', '');
}
});
// UP
this._addEventListenerToNode(this, 'up', () => this.removeAttribute('active'));
// KEYDOWN
this.addEventListener('keydown', (e) => {
if (this.__interactionsAllowed(e) && e.keyCode === 32) {
e.preventDefault();
this.setAttribute('active', '');
}
});
// KEYUP
this.addEventListener('keyup', (e) => {
if (this.__interactionsAllowed(e) && e.keyCode === 32) {
e.preventDefault();
this._toggleChecked();
this.removeAttribute('active');
if (this.indeterminate) {
this.indeterminate = false;
}
}
});
console.warn(
`WARNING: Since Vaadin 22, placing the label as a direct child of a <vaadin-checkbox> is deprecated.
Please use <label slot="label"> wrapper or the label property instead.`
);
}
/**
* @return {!HTMLInputElement}
* Extends the method from `ActiveMixin` in order to
* prevent setting the `active` attribute when interacting with a link inside the label.
*
* @param {Event} event
* @return {boolean}
* @protected
* @override
*/
get focusElement() {
return this.shadowRoot.querySelector('input');
}
/**
* True if users' interactions (mouse or keyboard)
* should toggle the checkbox
*/
__interactionsAllowed(e) {
if (this.disabled) {
_shouldSetActive(event) {
if (event.target.localName === 'a') {
return false;
}
// https://github.com/vaadin/vaadin-checkbox/issues/63
if (e.target.localName === 'a') {
return false;
}
return true;
return super._shouldSetActive(event);
}
/** @private */
_handleClick(e) {
if (this.__interactionsAllowed(e)) {
if (!this.indeterminate) {
if (e.composedPath()[0] !== this._nativeCheckbox) {
e.preventDefault();
this._toggleChecked();
}
} else {
/*
* Required for IE 11 and Edge.
* See issue here: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/7344418/
*/
this.indeterminate = false;
e.preventDefault();
this._toggleChecked();
}
/**
* Extends the method from `CheckedMixin` in order to
* reset the indeterminate state once the user switches the checkbox.
*
* @param {boolean} checked
* @protected
* @override
*/
_toggleChecked(checked) {
if (this.indeterminate) {
this.indeterminate = false;
}
}
/** @protected */
_toggleChecked() {
this.checked = !this.checked;
this.dispatchEvent(new CustomEvent('change', { composed: false, bubbles: true }));
super._toggleChecked(checked);
}
/**
* Fired when the user commits a value change.
*
* @event change
*/
}
customElements.define(CheckboxElement.is, CheckboxElement);
customElements.define(Checkbox.is, Checkbox);
export { CheckboxElement };
export { Checkbox };
import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';
import '@vaadin/vaadin-lumo-styles/color.js';
import '@vaadin/vaadin-lumo-styles/sizing.js';
import '@vaadin/vaadin-lumo-styles/spacing.js';
import '@vaadin/vaadin-lumo-styles/style.js';
import '@vaadin/vaadin-lumo-styles/typography.js';

@@ -9,2 +12,8 @@ registerStyles(

:host {
color: var(--lumo-body-text-color);
font-size: var(--lumo-font-size-m);
font-family: var(--lumo-font-family);
line-height: var(--lumo-line-height-s);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-tap-highlight-color: transparent;

@@ -18,10 +27,10 @@ -webkit-user-select: none;

[part='label']:not([empty]) {
margin: 0.1875em 0.875em 0.1875em 0.375em;
:host([has-label]) ::slotted(label) {
padding: var(--lumo-space-xs) var(--lumo-space-s) var(--lumo-space-xs) var(--lumo-space-xs);
}
[part='checkbox'] {
width: calc(1em + 2px);
height: calc(1em + 2px);
margin: 0.1875em;
width: calc(var(--lumo-size-m) / 2);
height: calc(var(--lumo-size-m) / 2);
margin: var(--lumo-space-xs);
position: relative;

@@ -31,5 +40,5 @@ border-radius: var(--lumo-border-radius-s);

transition: transform 0.2s cubic-bezier(0.12, 0.32, 0.54, 2), background-color 0.15s;
pointer-events: none;
line-height: 1.2;
cursor: var(--lumo-clickable-cursor);
flex: none;
}

@@ -50,2 +59,3 @@

content: '';
pointer-events: none;
display: inline-block;

@@ -87,3 +97,3 @@ width: 0;

:host([focus-ring]) [part='checkbox'] {
box-shadow: 0 0 0 3px var(--lumo-primary-color-50pct);
box-shadow: 0 0 0 1px var(--lumo-base-color), 0 0 0 3px var(--lumo-primary-color-50pct);
}

@@ -97,3 +107,3 @@

:host([disabled]) [part='label'] ::slotted(*) {
:host([disabled]) ::slotted(label) {
color: inherit;

@@ -115,4 +125,4 @@ }

/* RTL specific styles */
:host([dir='rtl']) [part='label']:not([empty]) {
margin: 0.1875em 0.375em 0.1875em 0.875em;
:host([dir='rtl'][has-label]) ::slotted(label) {
padding: var(--lumo-space-xs) var(--lumo-space-xs) var(--lumo-space-xs) var(--lumo-space-s);
}

@@ -127,2 +137,3 @@

[part='checkbox']::before {
pointer-events: none;
color: transparent;

@@ -129,0 +140,0 @@ display: inline-block;

@@ -17,11 +17,6 @@ import { registerStyles, css } from '@vaadin/vaadin-themable-mixin/register-styles.js';

[part='label']:not([empty]) {
margin: 3px 12px 3px 6px;
:host([has-label]) ::slotted(label) {
padding: 3px 12px 3px 6px;
}
[part='native-checkbox'] {
opacity: 0;
position: absolute;
}
[part='checkbox'] {

@@ -31,3 +26,2 @@ display: inline-block;

height: 16px;
flex: none;
margin: 4px;

@@ -37,3 +31,2 @@ position: relative;

box-shadow: inset 0 0 0 2px var(--material-secondary-text-color);
pointer-events: none;
line-height: 1.275;

@@ -47,2 +40,3 @@ background-color: transparent;

content: '\\2003';
pointer-events: none;
display: inline-block;

@@ -62,2 +56,3 @@ width: 100%;

content: '';
pointer-events: none;
display: inline-block;

@@ -121,3 +116,3 @@ width: 10px;

:host([disabled]) ::slotted(*) {
:host([disabled]) ::slotted(label) {
color: inherit;

@@ -136,4 +131,4 @@ }

/* RTL specific styles */
:host([dir='rtl']) [part='label']:not([empty]) {
margin: 3px 6px 3px 12px;
:host([dir='rtl'][has-label]) ::slotted(label) {
padding: 3px 6px 3px 12px;
}

@@ -140,0 +135,0 @@ `,

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