Emails-Input
Zero-dependencies library for input, which supports adding and removing multiple email addresses in an accessible way.
Provides email validation and public API. Allows pasting emails from clipboard
(including multiple emails separated by comma), by pressing enter
or ,
, or
by loosing focus from input.
Works in all major browsers.
Allows multiple inputs on the same page.
Provides public init
and destroy
methods, so it can be integrated with other
frontend libraries like React.
Installation
Module version
npm install @marcin-pajak/emails-input --save
You can than import it in your project
import EmailsInput from '@marcin-pajak/emails-input'
You can also use the provided styles:
import '@marcin-pajak/emails-input/lib/styles.css';
UMD version
JavaScript File
<script src="https://cdn.jsdelivr.net/npm/@marcin-pajak/emails-input@0.1.0/lib/index.umd.js"></script>
Styles
<link
rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/@marcin-pajak/emails-input@0.1.0/lib/styles.css"
/>
You can then use it in your project
<div id="emails-input" style="max-height: 121px"></div>
<script>
const element = document.querySelector('#emails-input');
const instance = EmailsInput(element);
</script>
Usage
EmailsInput(element: HTMLElement, userOptions?: Partial<Options>): Library
EmailsInput takes two arguments:
- HTMLElement
element
, where it will be initialized - Optional
options
:
interface Options {
initialValue: string[];
onChange: (changed: string, type: ChangeType, newValue: string[]) => void;
}
initialValue: string[]
List of emails that should be rendered and put on library's start upon initialization.
onChange: (changed: string, type: ChangeType, newValue: string[]) => void
onChange
callback that will be called every time email was added or removed
from input.
ChangeType represents added or removed action:
enum ChangeType {
Added = 'Added',
Removed = 'Removed',
}
Returned value
Creating new instance returns publicly available methods and state properties:
interface Library {
// Public State Properties
emails: string[];
entries: {
email: string;
isValid: boolean;
}[];
// Library actions
destroy: () => void;
init: () => void;
// Email actions
add: (value: string | string[]) => boolean;
removeByIndex: (index: number) => void;
removeByName: (name: string) => void;
}
Requirements
Container element should have specified either height
or max-height
property. Library inherits them in its styles.
Example
<div id="emails-input" style="max-height: 121px"></div>
<script>
const element = document.querySelector('#emails-input');
const instance = EmailsInput(element, {
initialValue: ['some@email.com'],
onChange: (value, action, emails) => {
console.log('Changed: ', value);
console.log('Action type: ', action);
console.log('Input value: ', emails);
},
});
// Add email via method
instance.add('other@email.com');
instance.add('another@email.com, one-more@email.com');
// Remove email via method
instance.removeByName('some@email.com');
// Remove 'other@email.com' email via index
// Notice we use current index in emails property
instance.removeByIndex(0);
// Destroy lib
instance.destroy();
// Re-initialize without losing state
instance.init();
// Show current emails
console.log(instance.emails)
// Get valid emails
const validEmails =>
instance.entries
.filter(entry => Boolean(entry.isValid))
.map(entry => entry.email)
</script>
More examples
Have a look at the examples directory.
See Vanilla Example
Public API
Callbacks
onChange
onChange: (changed: string, type: ChangeType, newValue: string[]) => void;
where ChangeType
indicates action type:
enum ChangeType {
Added = 'Added',
Removed = 'Removed',
}
onChange
, if passed to library, will be triggered every time email was added
or removed from input.
State Properties
Creating new instance of the library returns reference to two state fields:
emails
and entries
.
Reference is kept when input value changes, so these fields are always up to date with displayed value.
emails: string[]
List of all values in the input.
entries: Entry[]
interface Entry {
email: string;
isValid: boolean;
}
List of all entries in the input. Contains email value and information whether or not it's a valid email address.
Library Methods
init: () => void
When creating new instance of the library, it will be initialized itself.
This method should be called only when library was previously destroyed.
It allows integration with external libraries' life cycle methods.
Error
When called while library is already initialized, it will throw
ERR_INVALID_ACTION
.
destroy: () => void
When called, it will remove generated HTML and remove all event listeners.
Internal state of the library is kept intact, and when re-initialized it will render current input's value and attach all event listeners.
It allows integration with external libraries' life cycle methods.
Error
When called while library is already destroyed, it will throw
ERR_INVALID_ACTION
.
Email methods
add: (value: string | string[]) => boolean
Adds unique emails in various formats.
Accepts:
- array of single emails
- string with single email
- string with multiple, comma separated emails
Examples of value:
["email@email.com", "email2@email.com"]
"email@email.com, email2@email.com"
"email@email.com"
Returns true
if email was added and false
if email is already in the list.
Triggers onChange callback as follows:
onChange(addedEmail: string, ChangeType.Added: ChangeType, state.emails: string[]);
In case multiple emails were passed as value, onChange
will be called multiple
times, accordingly to the number of emails added to the state.
removeByName: (name: string) => void
Removes email from library's state based on it's name in emails
property.
Error
When called with invalid index, it throws ERR_EMAIL_NOT_FOUND
.
removeByIndex: (index: number) => void
Removes email from library's state based on it's index in emails
property.
Error
When called with invalid index, it throws ERR_EMAIL_NOT_FOUND
.
Improvements
- Initialize library on real input element and update it's value with current emails.
- Allow specifying
max-height
via options, if not set on container element. - Allow customizing input's placeholder.
- Document custom styling approach.
- Add e2e tests.
- Automate package publishing.
- Add changelog generation.
Errors
List of error codes, that should be handled:
export enum ErrorCodes {
ERR_INVALID_INITIAL_VALUES,
ERR_INVALID_ON_CHANGE_CALLBACK,
ERR_EMAIL_NOT_FOUND,
ERR_INVALID_ACTION,
ERR_COULD_NOT_INITIALIZE,
ERR_INTERNAL,
}
Every thrown error has code
property with one of the above error code.
Contributing
See CONTRIBUTING.md.
License
The files included in this repository are licensed under the MIT license.