Now based on mailtoui

This commit is contained in:
Nicolai Ort 2020-08-29 13:55:43 +02:00
parent 4266ba931a
commit f35b067076
27 changed files with 10984 additions and 6396 deletions

5
.csslintrc Normal file
View File

@ -0,0 +1,5 @@
{
"box-model": false,
"box-sizing": false,
"outline-none": false
}

1
.eslintignore Normal file
View File

@ -0,0 +1 @@
/dist

23
.eslintrc Normal file
View File

@ -0,0 +1,23 @@
{
"parserOptions": {
"ecmaVersion": 8,
"sourceType": "module"
},
"plugins": ["prettier"],
"extends": ["plugin:prettier/recommended"],
"rules": {
"prettier/prettier": [
"error",
{
"semi": true,
"singleQuote": true,
"printWidth": 125,
"tabWidth": 4,
"useTabs": false,
"trailingComma": "none",
"bracketSpacing": true,
"parser": "flow"
}
]
}
}

View File

@ -1,19 +0,0 @@
{
"env": {
"browser": true,
"es2020": true
},
"extends": [
"airbnb-base"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
}
}

9
.gitignore vendored
View File

@ -1,4 +1,7 @@
node_modules
/node_modules
.DS_Store
.cache
yarn.lock
.eslintrc.json
.vscode
serve
compile
publish

View File

@ -1,22 +1,59 @@
# 📧 mailymaily
<p align="center">
<a href="https://mailtoui.com">
<img src="https://mailtoui.com/assets/img/heading.jpg" alt="MailtoUI">
</a>
</p>
## Usage
### CC
`data-cc-address` and `data-cc-domain` to recreate cc: `data-cc-address@data-cc-domain`
### BCC
`data-bcc-address` and `data-bcc-domain` to recreate bcc: `data-bcc-address@data-bcc-domain`
### Subject
`data-subject` for the subject of the email
### Body
`data-body` for the body of the email
<p align="center">A simple way to enhance your mailto links with a convenient user interface.</p>
## Dev
### Dependencies
```
yarn
<p align="center">
<a href="https://github.com/mariordev/mailtoui/releases"><img src="https://img.shields.io/npm/v/mailtoui.svg" alt="Latest release"></a>
<a href="https://www.npmjs.com/package/mailtoui"><img src="https://img.shields.io/npm/dt/mailtoui.svg" alt="Total downloads"></a>
<a href="https://github.com/mariordev/mailtoui/blob/master/LICENSE"><img src="https://img.shields.io/github/license/mariordev/mailtoui.svg" alt="License"></a>
<a href="https://twitter.com/intent/tweet?text=Check%20this%20out!%20&url=https%3A%2F%2Fmailtoui.com"><img src="https://img.shields.io/twitter/url/https/mailtoui.com.svg?style=social" alt="Share on Twitter"></a>
</p>
## Introduction
MailtoUI is a JavaScript library that enhances your mailto links with a convenient user interface. It gives your users the flexibility to compose a new message using a browser-based email client <strong><i>or</i></strong> their default local email app.
MailtoUI is ideal for static sites or any other site where you don't want to spend time setting up a "Contact Us" form solution.
## Quick Setup
### STEP 1
Add MailtoUI via CDN to the bottom of your page, just before the closing `</body>` tag.
IMPORTANT: Be sure to replace `[version]` with the latest version number.
```html
<body>
...
...
<script src="https://cdn.jsdelivr.net/npm/mailtoui@[version]/dist/mailtoui-min.js"></script>
</body>
```
### Building
### STEP 2
Attach your mailto link to MailtoUI by adding the class `mailtoui` to the `<a>` tag.
```html
<a class="mailtoui" href="mailto:tony.stark@example.com">Contact Tony</a>
```
yarn build
```
That's it! Your mailto link is now using MailtoUI. Refresh your page and try it out.
## Documentation
For full documentation, visit [mailtoui.com](https://mailtoui.com).
## Contributing
If you're interested in contributing to MailtoUI, please follow the directions in the [contributing docs](https://github.com/mariordev/mailtoui/blob/master/.github/CONTRIBUTING.md) **before working on a pull request**.
## License
[MIT](https://github.com/mariordev/mailtoui/blob/master/LICENSE)

View File

@ -1,13 +0,0 @@
const presets = [
[
'@babel/preset-env',
{
targets: {
browsers: [ 'defaults' ]
}
}
],
'@babel/preset-typescript'
];
module.exports = { presets };

1
dist/mailgo.min.js vendored

File diff suppressed because one or more lines are too long

8
dist/mailtoui-min.js vendored Normal file

File diff suppressed because one or more lines are too long

138
gulpfile.js Normal file
View File

@ -0,0 +1,138 @@
const { src, dest, parallel, series, watch } = require('gulp');
const csslint = require('gulp-csslint');
const autoprefixer = require('gulp-autoprefixer');
const minify = require('gulp-minify');
const cleanCss = require('gulp-clean-css');
const rename = require('gulp-rename');
const header = require('gulp-header');
const eslint = require('gulp-eslint');
const fs = require('fs-extra');
const htmlMin = require('gulp-htmlmin');
const browserify = require('browserify');
const source = require('vinyl-source-stream');
const buffer = require('vinyl-buffer');
const sass = require('gulp-sass');
var packageJson = null;
var banner = [
'/**',
' * <%= pkg.name %> - <%= pkg.description %>',
' * @version v<%= pkg.version %>',
' * @link <%= pkg.homepage %>',
' * @author <%= pkg.author.name %> - <%= pkg.author.url %>',
' * @license <%= pkg.license %>',
' */',
''
].join('\n');
/**
* Process HTML files.
*/
function html() {
return src('./src/html/component.html')
.pipe(htmlMin({ collapseWhitespace: true }))
.pipe(rename('./src/html/component-min.html'))
.pipe(dest('./'));
}
/**
* Process CSS file.
*/
function css() {
return src('./src/scss/component.scss')
.pipe(sass())
.pipe(csslint())
.pipe(csslint.formatter())
.pipe(
autoprefixer({
browsers: ['last 2 versions'],
cascade: false
})
)
.pipe(cleanCss())
.pipe(rename('./src/css/component-min.css'))
.pipe(dest('./'));
}
/**
* Process the JavaScript library file.
*/
function js() {
packageJson = fs.readJsonSync('./package.json');
return src('./src/js/mailtoui.js')
.pipe(eslint())
.pipe(eslint.format())
.pipe(minify({ noSource: true }))
.pipe(header(banner, { pkg: packageJson }))
.pipe(dest('dist'));
}
/**
* Lint JavaScript file used on demo page.
*/
function lintDemoJs() {
return src('./demo/demo.js')
.pipe(eslint())
.pipe(eslint.format());
}
/**
* Process JavaScript file used on demo page.
*/
function processDemoJs() {
return browserify('./demo/demo.js')
.bundle()
.pipe(source('./demo/demo.js'))
.pipe(buffer())
.pipe(minify({ noSource: true }))
.pipe(dest('./'));
}
/**
* Process CSS file used on demo page.
*/
function demoCss() {
return src('./demo/demo.scss')
.pipe(sass())
.pipe(csslint())
.pipe(csslint.formatter())
.pipe(
autoprefixer({
browsers: ['last 2 versions'],
cascade: false
})
)
.pipe(cleanCss())
.pipe(rename('./demo/demo-min.css'))
.pipe(dest('./'));
}
/**
* The all seeing eye...
*/
function watchFiles() {
watch('./src/html/component.html', html);
watch('./src/scss/component.scss', css);
watch(['./src/js/mailtoui.js', './package.json'], series(js, lintDemoJs, processDemoJs));
watch('./demo/demo.scss', demoCss);
watch('./demo/demo.js', demoJs);
}
/**
* Define complex tasks.
*/
const demoJs = series(lintDemoJs, processDemoJs);
const build = series(parallel(html, css), js, demoJs, demoCss);
const watching = series(build, watchFiles);
/**
* Make tasks available to the outside world.
*/
exports.html = html;
exports.css = css;
exports.js = js;
exports.demoJs = demoJs;
exports.demoCss = demoCss;
exports.watch = watching;
exports.default = build;

View File

@ -1,32 +0,0 @@
{
"languages": [ "en", "de" ],
"translations": {
"en": {
"open_in_": "open in ",
"cc_": "cc ",
"bcc_": "bcc ",
"subject_": "subject ",
"body_": "body ",
"gmail": "Gmail",
"outlook": "Outlook",
"telegram": "Telegram",
"whatsapp": "WhatsApp",
"skype": "Skype",
"call": "call",
"open": "open",
"_default": " default",
"_as_default": " as default",
"copy": "copy"
},
"de": {
"open_in_": "Öffnen in ",
"subject_": "Betreff ",
"body_": "Nachricht ",
"call": "Anrufen",
"open": "Öffnen",
"_default": " mit Standard Programm",
"_as_default": " mit Standard Programm",
"copy": "In die Zwischenablage kopieren"
}
}
}

52
mailgo.d.ts vendored
View File

@ -1,52 +0,0 @@
declare module 'mailgo' {
export type MailgoConfig = {
initEvent?: string;
listenerOptions?: ListenerOptions | boolean;
dark?: boolean;
lang?: string;
forceLang?: boolean;
validateEmail?: boolean;
validateTel?: boolean;
showFooter?: boolean;
};
export type MailgoTranslation = {
open_in_?: string;
cc_?: string;
bcc_?: string;
subject_?: string;
body_?: string;
gmail?: string;
outlook?: string;
whatsapp?: string;
skype?: string;
call?: string;
open?: string;
_default?: string;
_as_default?: string;
copy?: string;
};
export type MailgoTranslations = {
[language: string]: MailgoTranslation;
};
export type MailgoI18n = {
languages: string[];
translations: MailgoTranslations;
};
export type ListenerOptions = {
capture?: boolean;
once?: boolean;
passive?: boolean;
};
export function mailgoRender(type: string, mailgoElement: HTMLLinkElement): void;
export function isMailgo(element: HTMLElement, type?: string): boolean;
export function mailgoCheckRender(event: Event): boolean;
export default function mailgo(mailgoConfig?: MailgoConfig): void;
}

9195
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,32 +1,61 @@
{
"name": "mailgo",
"version": "0.9.14",
"description": "a new concept of mailto and tel links",
"scripts": {
"build": "webpack"
},
"main": "./lib/mailgo.js",
"module": "./lib/mailgo.js",
"types": "./mailgo.d.ts",
"devDependencies": {
"@babel/cli": "^7.10.5",
"@babel/core": "^7.11.1",
"@babel/parser": "^7.11.3",
"@babel/preset-env": "^7.11.0",
"@babel/preset-typescript": "^7.10.4",
"@babel/types": "^7.11.0",
"@types/node": "^14.0.27",
"babel-loader": "^8.1.0",
"css-loader": "^4.2.1",
"node-sass": "^4.14.1",
"sass-loader": "^9.0.3",
"style-loader": "^1.2.1",
"to-string-loader": "^1.1.6",
"ts-loader": "^8.0.2",
"typescript": "^3.9.7",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
},
"dependencies": {},
"sideEffects": false
}
"name": "mailtoui",
"description": "A simple way to enhance your mailto links with a convenient user interface.",
"version": "1.0.3",
"homepage": "https://mailtoui.com",
"author": {
"name": "Mario Rodriguez",
"url": "https://twitter.com/mariordev"
},
"keywords": [
"mailtoui",
"mailto",
"ui",
"mail",
"email",
"interface",
"user interface"
],
"license": "MIT",
"repository": "https://github.com/mariordev/mailtoui.git",
"bugs": "https://github.com/mariordev/mailtoui/issues",
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/plugin-transform-regenerator": "^7.4.5",
"@babel/preset-env": "^7.5.5",
"acorn": "^6.3.0",
"babelify": "^10.0.0",
"browserify": "^16.5.0",
"eslint": "^5.16.0",
"eslint-config-prettier": "^3.6.0",
"eslint-plugin-prettier": "^3.1.0",
"fs-extra": "^7.0.1",
"gulp": "^4.0.2",
"gulp-autoprefixer": "^6.1.0",
"gulp-clean-css": "^4.2.0",
"gulp-csslint": "^1.0.1",
"gulp-eslint": "^5.0.0",
"gulp-header": "^2.0.9",
"gulp-htmlmin": "^5.0.1",
"gulp-minify": "^3.1.0",
"gulp-rename": "^1.4.0",
"gulp-sass": "^4.0.2",
"gulp-util": "^3.0.8",
"gulp-watch": "^5.0.1",
"node-sass": "^4.12.0",
"prettier": "1.15.3",
"shell-quote": "^1.7.2",
"tar": "^4.4.10",
"vinyl-buffer": "^1.0.1",
"vinyl-source-stream": "^2.0.0",
"vue": "^2.6.10"
},
"dependencies": {},
"files": [
"dist/*"
],
"jsdelivr": "dist/mailtoui-min.js",
"scripts": {
"lint": "eslint ./src/js/mailtoui.js"
}
}

View File

@ -1,28 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<title>mailymaily examples</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/open-fonts@1.1.1/fonts/inter.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@exampledev/new.css@1.1.2/new.min.css">
</head>
<body>
<h1>📧 mailymaily</h1>
<br>
<h2>MailTo</h2>
<h3>🌞 Light mode</h3>
<a href="#mailgo" data-address="info" data-domain="odit.services">info@odit.services</a>
<h3>🌑 Dark mode</h3>
<a class="dark" href="#mailgo" data-address="info" data-domain="odit.services" data-cc="info@odit.services">info@odit.services</a><br>
<br><br>
<h2>CallTo</h2>
<h3>🌞 Light mode</h3>
<a href="callto:+493012345678">+493012345678</a>
<h3>🌑 Dark mode</h3>
<a class="dark" href="callto:+493012345678">+493012345678</a>
<script src="./dist/mailgo.min.js"></script>
</body>
</html>

1
src/css/component-min.css vendored Normal file
View File

@ -0,0 +1 @@
.mailtoui-modal{background-color:#000;background-color:rgba(0,0,0,.4);bottom:0;color:#303131;display:none;height:100%;left:0;margin:0;padding:0;position:fixed;right:0;top:0;width:100%;z-index:1000}.mailtoui-modal-content{-webkit-animation:mailtoui-appear .4s;animation:mailtoui-appear .4s;background-color:#f1f5f8;border-radius:8px;bottom:auto;-webkit-box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);box-shadow:0 4px 8px 0 rgba(0,0,0,.2),0 6px 20px 0 rgba(0,0,0,.19);left:50%;max-height:calc(100% - 100px);overflow:auto;padding:0;position:fixed;right:-45%;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.mailtoui-modal-content:focus,.mailtoui-modal-content:hover{overflow-y:auto}@media only screen and (min-width:768px){.mailtoui-modal-content{right:auto}}.mailtoui-modal-head{-webkit-box-align:center;-ms-flex-align:center;align-items:center;background-color:#fff;clear:both;display:-webkit-box;display:-ms-flexbox;display:flex;min-width:0;padding:10px 20px}.mailtoui-modal-title{color:#303131;-webkit-box-flex:1;-ms-flex:1;flex:1;font-family:sans-serif;font-size:120%;font-weight:700;margin:0;overflow:hidden;padding:0;text-overflow:ellipsis;white-space:nowrap}.mailtoui-modal-close{color:#aaa;-webkit-box-flex:initial;-ms-flex:initial;flex:initial;font-size:38px;margin-left:20px;position:relative;text-align:right;text-decoration:none;top:-4px}.mailtoui-modal-close:focus,.mailtoui-modal-close:hover{color:#000;cursor:pointer;font-weight:700;outline:0}.mailtoui-modal-body{height:100%;padding:20px}.mailtoui-button{color:#333;text-decoration:none}.mailtoui-button:focus{outline:0}.mailtoui-button:focus .mailtoui-button-content{background-color:#555;color:#fff}.mailtoui-button-content{background-color:#fff;border:none;border-radius:8px;-webkit-box-shadow:0 2px 4px rgba(0,0,0,.18);box-shadow:0 2px 4px rgba(0,0,0,.18);margin-bottom:20px;overflow:hidden;padding:15px 20px;text-overflow:ellipsis;white-space:nowrap}.mailtoui-button-content:hover{background-color:#555;color:#fff}.mailtoui-button:last-child .mailtoui-button-content{margin-bottom:0}.mailtoui-button-icon{display:inline-block;font-weight:700;position:relative;top:4px}.mailtoui-button-icon svg{height:24px;width:24px}.mailtoui-button-text{display:inline-block;margin-left:5px;position:relative;top:-2px}.mailtoui-copy{border-radius:8px;-webkit-box-shadow:0 2px 4px rgba(0,0,0,.18);box-shadow:0 2px 4px rgba(0,0,0,.18);height:59px;margin-top:20px;position:relative}.mailtoui-button-copy{background-color:#fff;border:none;border-bottom-left-radius:8px;border-top-left-radius:8px;bottom:21px;color:#333;font-size:100%;height:100%;left:0;overflow:hidden;padding:15px 20px;position:absolute;text-overflow:ellipsis;top:0;white-space:nowrap;width:120px}.mailtoui-button-copy:focus,.mailtoui-button-copy:hover{background-color:#555;color:#fff;cursor:pointer;outline:0}.mailtoui-button-copy-clicked,.mailtoui-button-copy-clicked:focus,.mailtoui-button-copy-clicked:hover{background-color:#1f9d55;color:#fff}.mailtoui-button-copy-clicked .mailtoui-button-icon,.mailtoui-button-copy-clicked:focus .mailtoui-button-icon,.mailtoui-button-copy-clicked:hover .mailtoui-button-icon{display:none;visibility:hidden}.mailtoui-button-copy-clicked .mailtoui-button-text,.mailtoui-button-copy-clicked:focus .mailtoui-button-text,.mailtoui-button-copy-clicked:hover .mailtoui-button-text{color:#fff;top:2px}.mailtoui-email-address{background-color:#d8dcdf;border:none;border-radius:8px;-webkit-box-shadow:unset;box-shadow:unset;-webkit-box-sizing:border-box;box-sizing:border-box;color:#48494a;font-size:100%;height:100%;overflow:hidden;padding:20px 20px 20px 140px;text-overflow:ellipsis;white-space:nowrap;width:100%}.mailtoui-brand{color:#888;font-size:80%;margin-top:20px;text-align:center}.mailtoui-brand a{color:#888}.mailtoui-brand a:focus,.mailtoui-brand a:hover{font-weight:700;outline:0}.mailtoui-no-scroll{overflow:hidden;position:fixed;width:100%}.mailtoui-is-hidden{display:none;visibility:hidden}@-webkit-keyframes mailtoui-appear{0%{opacity:0;-webkit-transform:translate(-50%,-50%) scale(0,0);transform:translate(-50%,-50%) scale(0,0)}100%{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1,1);transform:translate(-50%,-50%) scale(1,1)}}@keyframes mailtoui-appear{0%{opacity:0;-webkit-transform:translate(-50%,-50%) scale(0,0);transform:translate(-50%,-50%) scale(0,0)}100%{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1,1);transform:translate(-50%,-50%) scale(1,1)}}

View File

@ -0,0 +1 @@
<div class="mailtoui-modal-content"><div class="mailtoui-modal-head"><div id="mailtoui-modal-title" class="mailtoui-modal-title">${options.title}</div><a id="mailtoui-modal-close" class="mailtoui-modal-close" href="#">&times</a></div><div class="mailtoui-modal-body"><div class="mailtoui-clients"><a id="mailtoui-button-1" class="mailtoui-button" href="#"><div class="mailtoui-button-content"><span id="mailtoui-button-icon-1" class="mailtoui-button-icon">${options.buttonIcon1}</span> <span id="mailtoui-button-text-1" class="mailtoui-button-text">${options.buttonText1}</span></div></a><a id="mailtoui-button-2" class="mailtoui-button" href="#"><div class="mailtoui-button-content"><span id="mailtoui-button-icon-2" class="mailtoui-button-icon">${options.buttonIcon2}</span> <span id="mailtoui-button-text-2" class="mailtoui-button-text">${options.buttonText2}</span></div></a><a id="mailtoui-button-3" class="mailtoui-button" href="#"><div class="mailtoui-button-content"><span id="mailtoui-button-icon-3" class="mailtoui-button-icon">${options.buttonIcon3}</span> <span id="mailtoui-button-text-3" class="mailtoui-button-text">${options.buttonText3}</span></div></a><a id="mailtoui-button-4" class="mailtoui-button" href="#"><div class="mailtoui-button-content"><span id="mailtoui-button-icon-4" class="mailtoui-button-icon">${options.buttonIcon4}</span> <span id="mailtoui-button-text-4" class="mailtoui-button-text">${options.buttonText4}</span></div></a></div><div id="mailtoui-copy" class="mailtoui-copy"><div id="mailtoui-email-address" class="mailtoui-email-address"></div><button id="mailtoui-button-copy" class="mailtoui-button-copy" data-copytargetid="mailtoui-email-address"><span id="mailtoui-button-icon-copy" class="mailtoui-button-icon">${options.buttonIconCopy}</span> <span id="mailtoui-button-text-copy" class="mailtoui-button-text">${options.buttonTextCopy}</span></button></div><div class="mailtoui-brand"><a href="https://mailtoui.com?ref=ui" target="_blank">Powered by MailtoUI</a></div></div></div>

49
src/html/component.html Normal file
View File

@ -0,0 +1,49 @@
<div class="mailtoui-modal-content">
<div class="mailtoui-modal-head">
<div id="mailtoui-modal-title" class="mailtoui-modal-title">${options.title}</div>
<a id="mailtoui-modal-close" class="mailtoui-modal-close" href="#">&times</a>
</div>
<div class="mailtoui-modal-body">
<div class="mailtoui-clients">
<a id="mailtoui-button-1" class="mailtoui-button" href="#">
<div class="mailtoui-button-content">
<span id="mailtoui-button-icon-1" class="mailtoui-button-icon">${options.buttonIcon1}</span>
<span id="mailtoui-button-text-1" class="mailtoui-button-text">${options.buttonText1}</span>
</div>
</a>
<a id="mailtoui-button-2" class="mailtoui-button" href="#">
<div class="mailtoui-button-content">
<span id="mailtoui-button-icon-2" class="mailtoui-button-icon">${options.buttonIcon2}</span>
<span id="mailtoui-button-text-2" class="mailtoui-button-text">${options.buttonText2}</span>
</div>
</a>
<a id="mailtoui-button-3" class="mailtoui-button" href="#">
<div class="mailtoui-button-content">
<span id="mailtoui-button-icon-3" class="mailtoui-button-icon">${options.buttonIcon3}</span>
<span id="mailtoui-button-text-3" class="mailtoui-button-text">${options.buttonText3}</span>
</div>
</a>
<a id="mailtoui-button-4" class="mailtoui-button" href="#">
<div class="mailtoui-button-content">
<span id="mailtoui-button-icon-4" class="mailtoui-button-icon">${options.buttonIcon4}</span>
<span id="mailtoui-button-text-4" class="mailtoui-button-text">${options.buttonText4}</span>
</div>
</a>
</div>
<div id="mailtoui-copy" class="mailtoui-copy">
<div id="mailtoui-email-address" class="mailtoui-email-address"></div>
<button id="mailtoui-button-copy" class="mailtoui-button-copy" data-copytargetid="mailtoui-email-address">
<span id="mailtoui-button-icon-copy" class="mailtoui-button-icon">${options.buttonIconCopy}</span>
<span id="mailtoui-button-text-copy" class="mailtoui-button-text">${options.buttonTextCopy}</span>
</button>
</div>
<div class="mailtoui-brand">
<a href="https://mailtoui.com?ref=ui" target="_blank">Powered by MailtoUI</a>
</div>
</div>
</div>

1138
src/js/mailtoui.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,281 +0,0 @@
$mailgo-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
// colors
$default-color: #4a4a4a;
$gmail-color: #c0372a;
$outlook-color: #0967aa;
$wa-color: #067466;
$telegram-color: #086da0;
$skype-color: #076d92;
$default-color-hover: #3d3d3d;
$default-dark-color: #eaeaea;
$gmail-dark-color: #e07d73;
$outlook-dark-color: #4c9cd7;
$wa-dark-color: #4cd2c0;
$telegram-dark-color: #4cabdb;
$skype-dark-color: #4cc7f4;
// other vars
$default-border-radius: 20px;
.m-modal {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
justify-content: center;
align-items: center;
flex-direction: column;
overflow: hidden;
font-size: 16.5px;
z-index: 10000;
p,
span,
strong,
a {
margin: 0;
padding: 0;
font-size: 100%;
line-height: 1;
font-family: $mailgo-font-family;
text-rendering: optimizeLegibility;
}
strong {
font-weight: 700;
}
.m-modal-back {
position: absolute;
z-index: 10001;
top: 0;
right: 0;
bottom: 0;
left: 0;
background-color: #20232a;
opacity: 0.8;
}
.m-modal-content {
position: relative;
z-index: 10002;
box-sizing: content-box;
text-align: center;
min-width: 200px;
max-width: 240px;
background-color: #fff;
opacity: 0.95;
border-radius: $default-border-radius;
box-shadow: 0 3px 20px rgba(32, 35, 42, 0.5);
color: $default-color;
display: flex;
flex-direction: column;
overflow: auto;
padding: 24px;
transition: 0.5s box-shadow;
&:hover,
&:focus,
&:active {
opacity: 1;
}
.m-title {
margin-bottom: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 1.2em;
}
.m-details {
margin-bottom: 10px;
p {
font-size: 12px;
margin-top: 3px;
margin-bottom: 3px;
}
}
a {
cursor: pointer;
padding: 10px;
color: $default-color;
border-radius: $default-border-radius;
text-decoration: none;
&.m-gmail {
color: $gmail-color;
&:hover,
&:focus,
&:active {
background-color: rgba(212, 70, 56, 0.08);
color: $gmail-color;
}
}
&.m-outlook {
color: $outlook-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 114, 198, 0.08);
color: $outlook-color;
}
}
&.m-tg {
color: $telegram-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 136, 204, 0.08);
color: $telegram-color;
}
}
&.m-wa {
color: $wa-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 191, 165, 0.08);
color: $wa-color;
}
}
&.m-skype {
color: $skype-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 175, 240, 0.08);
color: $skype-color;
}
}
&.m-copy {
padding: 16px 10px;
font-size: 16px;
}
&.m-default,
&.m-copy {
&:hover,
&:focus,
&:active {
background-color: rgba(0, 0, 0, 0.08);
color: $default-color;
}
}
&.m-by {
font-size: 12px;
margin-top: 0.8rem;
padding: 5px;
color: $default-color;
&:hover,
&:focus,
&:active {
color: $default-color-hover;
}
}
}
.w-500 {
font-weight: 500;
}
}
&.m-dark {
.m-modal-content {
background-color: #20232a;
&,
& p,
& p span,
& strong {
color: #fff;
}
a {
color: $default-dark-color;
&:not(.m-by) {
&:hover,
&:focus,
&:active {
background-color: rgba(134, 134, 134, 0.08);
color: $default-dark-color;
}
}
&.m-gmail {
color: $gmail-dark-color;
&:hover,
&:focus,
&:active {
background-color: rgba(212, 70, 56, 0.08);
color: $gmail-dark-color;
}
}
&.m-outlook {
color: $outlook-dark-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 114, 198, 0.08);
color: $outlook-dark-color;
}
}
&.m-tg {
color: $telegram-dark-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 136, 204, 0.08);
color: $telegram-dark-color;
}
}
&.m-wa {
color: $wa-dark-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 191, 165, 0.08);
color: $wa-dark-color;
}
}
&.m-skype {
color: $skype-dark-color;
&:hover,
&:focus,
&:active {
background-color: rgba(0, 175, 240, 0.08);
color: $skype-dark-color;
}
}
&.m-by {
&:hover,
&:focus,
&:active {
color: #fff;
}
}
}
}
}
}

View File

@ -1,990 +0,0 @@
import {
MailgoConfig,
MailgoTranslations,
MailgoTranslation,
MailgoI18n,
} from "mailgo";
// i18n for mailgo
const i18n: MailgoI18n = require("../i18n/i18n.json");
// mailgo scss
const mailgoCSS: string = require("./mailgo.scss").toString();
// default lang
const DEFAULT_LANG: string = "en";
// links
const MAILTO: string = "mailto:";
const TEL: string = "tel:";
const CALLTO: string = "callto:";
// deep linking
const outlookDeepLink: string = "ms-outlook://";
// mailgo types
const MAIL_TYPE: string = "mail";
const TEL_TYPE: string = "tel";
// default href for links
const DEFAULT_BTN_HREF: string = "javascript:void(0);";
// useful html tags
const spanHTMLTag: string = "span";
const aHTMLTag: string = "a";
const pHTMLTag: string = "p";
// global mailgo config object
let config: MailgoConfig;
// default language
let lang: string = DEFAULT_LANG;
// modals global object
let modalMailto: HTMLElement, modalTel: HTMLElement;
// mailgo variables
let url: URL,
mail: string = "",
encEmail: string = "",
cc: string = "",
bcc: string = "",
subject: string = "",
bodyMail: string = "";
// mailgo tel variables
let tel: string = "",
msg: string = "",
skypeUsername: string = "";
// the DOM elements
let title: HTMLElement,
titleTel: HTMLElement,
detailCc: HTMLElement,
detailBcc: HTMLElement,
detailSubject: HTMLElement,
detailBody: HTMLElement,
ccValue: HTMLElement,
bccValue: HTMLElement,
subjectValue: HTMLElement,
bodyValue: HTMLElement,
activatedLink: HTMLElement;
// mailgo buttons (actions)
let gmail: HTMLLinkElement,
outlook: HTMLLinkElement,
open: HTMLLinkElement,
wa: HTMLLinkElement,
skype: HTMLLinkElement,
call: HTMLLinkElement,
copyMail: HTMLLinkElement,
copyTel: HTMLLinkElement;
/**
* mailgoInit
* the function that creates the mailgo elements in DOM
*/
const mailgoInit = (): void => {
// translations
let {
translations,
}: { translations: MailgoTranslations } = i18n as MailgoI18n;
// if a default language is defined use it
if (config?.lang && i18n.languages.indexOf(config.lang) !== -1) {
lang = config.lang;
}
// if is defined <html lang=""> use it!
if (!config?.forceLang) {
// keep the lang from html
let htmlLang: string = document.documentElement.lang;
// if there are translations...
if (i18n.languages.indexOf(htmlLang) !== -1) {
lang = document.documentElement.lang;
}
}
// strings
let defaultStrings: MailgoTranslation = translations[DEFAULT_LANG];
let strings: MailgoTranslation = translations[lang];
// mailgo, if mailgo not already exists
let mailgoExists = !!document.getElementById("mailgo");
if (!mailgoExists) {
// modal
modalMailto = createElement() as HTMLElement;
modalMailto.style.display = "none";
modalMailto.id = "mailgo";
modalMailto.classList.add("m-modal");
modalMailto.setAttribute("role", "dialog");
modalMailto.setAttribute("tabindex", "-1");
modalMailto.setAttribute("aria-labelledby", "m-title");
// if dark is in config
if (config?.dark) {
enableDarkMode(MAIL_TYPE);
} else {
disableDarkMode(MAIL_TYPE);
}
// background
let modalBackground: HTMLElement = createElement();
modalBackground.className = "m-modal-back";
modalMailto.appendChild(modalBackground);
// modal content
let modalContent: HTMLElement = createElement();
modalContent.className = "m-modal-content";
modalMailto.appendChild(modalContent);
// title (email address)
title = createElement("strong");
title.id = "m-title";
title.className = "m-title";
modalContent.appendChild(title);
// details
let details: HTMLElement = createElement();
details.id = "m-details";
details.className = "m-details";
detailCc = createElement(pHTMLTag);
detailCc.id = "m-cc";
let ccSpan: HTMLElement = createElement(spanHTMLTag);
ccSpan.className = "w-500";
ccSpan.appendChild(createTextNode(strings.cc_ || defaultStrings.cc_));
ccValue = createElement(spanHTMLTag);
ccValue.id = "m-cc-value";
detailCc.appendChild(ccSpan);
detailCc.appendChild(ccValue);
details.appendChild(detailCc);
detailBcc = createElement(pHTMLTag);
detailBcc.id = "m-bcc";
let bccSpan: HTMLElement = createElement(spanHTMLTag);
bccSpan.className = "w-500";
bccSpan.appendChild(createTextNode(strings.bcc_ || defaultStrings.bcc_));
bccValue = createElement(spanHTMLTag);
bccValue.id = "m-bcc-value";
detailBcc.appendChild(bccSpan);
detailBcc.appendChild(bccValue);
details.appendChild(detailBcc);
detailSubject = createElement(pHTMLTag);
detailSubject.id = "m-subject";
let subjectSpan: HTMLElement = createElement(spanHTMLTag);
subjectSpan.className = "w-500";
subjectSpan.appendChild(
createTextNode(strings.subject_ || defaultStrings.subject_)
);
subjectValue = createElement(spanHTMLTag);
subjectValue.id = "m-subject-value";
detailSubject.appendChild(subjectSpan);
detailSubject.appendChild(subjectValue);
details.appendChild(detailSubject);
detailBody = createElement(pHTMLTag);
detailBody.id = "m-body";
let bodySpan: HTMLElement = createElement(spanHTMLTag);
bodySpan.className = "w-500";
bodySpan.appendChild(createTextNode(strings.body_ || defaultStrings.body_));
bodyValue = createElement(spanHTMLTag);
bodyValue.id = "m-body-value";
detailBody.appendChild(bodySpan);
detailBody.appendChild(bodyValue);
details.appendChild(detailBody);
modalContent.appendChild(details);
// Gmail
gmail = createElement(aHTMLTag) as HTMLLinkElement;
gmail.id = "m-gmail";
gmail.href = DEFAULT_BTN_HREF;
gmail.classList.add("m-open");
gmail.classList.add("m-gmail");
gmail.appendChild(
createTextNode(strings.open_in_ || defaultStrings.open_in_)
);
let gmailSpan: HTMLElement = createElement(spanHTMLTag);
gmailSpan.className = "w-500";
gmailSpan.appendChild(
createTextNode(strings.gmail || defaultStrings.gmail)
);
gmail.appendChild(gmailSpan);
modalContent.appendChild(gmail);
// Outlook
outlook = createElement(aHTMLTag) as HTMLLinkElement;
outlook.id = "m-outlook";
outlook.href = DEFAULT_BTN_HREF;
outlook.classList.add("m-open");
outlook.classList.add("m-outlook");
outlook.appendChild(
createTextNode(strings.open_in_ || defaultStrings.open_in_)
);
let outlookSpan: HTMLElement = createElement(spanHTMLTag);
outlookSpan.className = "w-500";
outlookSpan.appendChild(
createTextNode(strings.outlook || defaultStrings.outlook)
);
outlook.appendChild(outlookSpan);
modalContent.appendChild(outlook);
// open default
open = createElement(aHTMLTag) as HTMLLinkElement;
open.id = "m-open";
open.href = DEFAULT_BTN_HREF;
open.classList.add("m-open");
open.classList.add("m-default");
let openSpan: HTMLElement = createElement(spanHTMLTag);
openSpan.className = "w-500";
openSpan.appendChild(createTextNode(strings.open || defaultStrings.open));
open.appendChild(openSpan);
open.appendChild(
createTextNode(strings._default || defaultStrings._default)
);
modalContent.appendChild(open);
// copy
copyMail = createElement(aHTMLTag) as HTMLLinkElement;
copyMail.id = "m-copy";
copyMail.href = DEFAULT_BTN_HREF;
copyMail.classList.add("m-copy");
copyMail.classList.add("w-500");
copyMail.appendChild(createTextNode(strings.copy || defaultStrings.copy));
modalContent.appendChild(copyMail);
// add the modal at the end of the body
document.body.appendChild(modalMailto);
// every click outside the modal will hide the modal
modalBackground.addEventListener("click", hideMailgo);
}
// mailgo tel, if mailgo-tel not already exists
let mailgoTelExists = !!document.getElementById("mailgo-tel");
if (!mailgoTelExists) {
// modal
modalTel = createElement() as HTMLElement;
modalTel.style.display = "none";
modalTel.id = "mailgo-tel";
modalTel.classList.add("m-modal");
modalTel.setAttribute("role", "dialog");
modalTel.setAttribute("tabindex", "-1");
modalTel.setAttribute("aria-labelledby", "m-tel-title");
// if dark is in config
if (config?.dark) {
enableDarkMode(TEL_TYPE);
} else {
disableDarkMode(TEL_TYPE);
}
// background
let modalBackground: HTMLElement = createElement();
modalBackground.className = "m-modal-back";
modalTel.appendChild(modalBackground);
// modal content
let modalContent: HTMLElement = createElement();
modalContent.className = "m-modal-content";
modalTel.appendChild(modalContent);
// title (telephone number)
titleTel = createElement("strong");
titleTel.id = "m-tel-title";
titleTel.className = "m-title";
modalContent.appendChild(titleTel);
// WhatsApp
wa = createElement(aHTMLTag) as HTMLLinkElement;
wa.id = "m-wa";
wa.href = DEFAULT_BTN_HREF;
wa.classList.add("m-open");
wa.classList.add("m-wa");
wa.appendChild(createTextNode(strings.open_in_ || defaultStrings.open_in_));
let waSpan: HTMLElement = createElement(spanHTMLTag);
waSpan.className = "w-500";
waSpan.appendChild(
createTextNode(strings.whatsapp || defaultStrings.whatsapp)
);
wa.appendChild(waSpan);
modalContent.appendChild(wa);
// Skype
skype = createElement(aHTMLTag) as HTMLLinkElement;
skype.id = "m-skype";
skype.href = DEFAULT_BTN_HREF;
skype.classList.add("m-open");
skype.classList.add("m-skype");
skype.appendChild(
createTextNode(strings.open_in_ || defaultStrings.open_in_)
);
let skypeSpan: HTMLElement = createElement(spanHTMLTag);
skypeSpan.className = "w-500";
skypeSpan.appendChild(
createTextNode(strings.skype || defaultStrings.skype)
);
skype.appendChild(skypeSpan);
modalContent.appendChild(skype);
// call default
call = createElement(aHTMLTag) as HTMLLinkElement;
call.id = "m-call";
call.href = DEFAULT_BTN_HREF;
call.classList.add("m-open");
call.classList.add("m-default");
let callSpan: HTMLElement = createElement(spanHTMLTag);
callSpan.className = "w-500";
callSpan.appendChild(createTextNode(strings.call || defaultStrings.call));
call.appendChild(callSpan);
call.appendChild(
createTextNode(strings._as_default || defaultStrings._as_default)
);
modalContent.appendChild(call);
// copy
copyTel = createElement(aHTMLTag) as HTMLLinkElement;
copyTel.id = "m-tel-copy";
copyTel.href = DEFAULT_BTN_HREF;
copyTel.classList.add("m-copy");
copyTel.classList.add("w-500");
copyTel.appendChild(createTextNode(strings.copy || defaultStrings.copy));
modalContent.appendChild(copyTel);
// add the modal at the end of the body
document.body.appendChild(modalTel);
// every click outside the modal will hide the modal
modalBackground.addEventListener("click", hideMailgo);
}
// event listener on body, if the element is mailgo-compatible the mailgo modal will be rendered
document.addEventListener("click", mailgoCheckRender);
};
/**
* mailgoRender
* function to render a mailgo (mail or tel)
*/
export function mailgoRender(
type: string = MAIL_TYPE,
mailgoElement: HTMLLinkElement
): void {
// mailgo mail
if (type === MAIL_TYPE) {
// if the element href=^"mailto:"
if (
mailgoElement.href &&
mailgoElement.href.toLowerCase().startsWith(MAILTO)
) {
mail = decodeURIComponent(
mailgoElement.href.split("?")[0].split(MAILTO)[1].trim()
);
url = new URL(mailgoElement.href);
let urlParams: URLSearchParams = url.searchParams;
// optional parameters for the email
cc = urlParams.get("cc");
bcc = urlParams.get("bcc");
subject = urlParams.get("subject");
bodyMail = urlParams.get("body");
} else {
// if the element href="#mailgo" or class="mailgo"
// mail = data-address + @ + data-domain
mail =
mailgoElement.getAttribute("data-address") +
"@" +
mailgoElement.getAttribute("data-domain");
url = new URL(MAILTO + encodeURIComponent(mail));
// cc = data-cc-address + @ + data-cc-domain
cc =
mailgoElement.getAttribute("data-cc-address") +
"@" +
mailgoElement.getAttribute("data-cc-domain");
// bcc = data-bcc-address + @ + data-bcc-domain
bcc =
mailgoElement.getAttribute("data-bcc-address") +
"@" +
mailgoElement.getAttribute("data-bcc-domain");
// subject = data-subject
subject = mailgoElement.getAttribute("data-subject");
// body = data-body
bodyMail = mailgoElement.getAttribute("data-body");
}
if (
typeof config?.validateEmail === "undefined" ||
config?.validateEmail === true
) {
// validate the email address
if (!validateEmails(mail.split(","))) return;
// if cc, bcc are not valid cc, bcc = ""
if (cc && !validateEmails(cc.split(","))) cc = "";
if (bcc && !validateEmails(bcc.split(","))) bcc = "";
}
// the title of the modal (email address)
title.innerHTML = mail.split(",").join("<br/>");
// add the details if provided
cc
? ((detailCc.style.display = "block"),
(ccValue.innerHTML = cc.split(",").join("<br/>")))
: (detailCc.style.display = "none");
bcc
? ((detailBcc.style.display = "block"),
(bccValue.innerHTML = bcc.split(",").join("<br/>")))
: (detailBcc.style.display = "none");
subject
? ((detailSubject.style.display = "block"),
(subjectValue.textContent = subject))
: (detailSubject.style.display = "none");
bodyMail
? ((detailBody.style.display = "block"),
(bodyValue.textContent = bodyMail))
: (detailBody.style.display = "none");
// add the actions
gmail.addEventListener("click", openGmail);
outlook.addEventListener("click", openOutlook);
encEmail = encodeEmail(mail);
open.addEventListener("click", openDefault);
copyMail.addEventListener("click", () => copy(mail));
}
// mailgo tel
else if (type === TEL_TYPE) {
if (
mailgoElement.href &&
mailgoElement.href.toLowerCase().startsWith(TEL)
) {
tel = decodeURIComponent(
mailgoElement.href.split("?")[0].split(TEL)[1].trim()
);
} else if (
mailgoElement.href &&
mailgoElement.href.toLowerCase().startsWith(CALLTO)
) {
tel = decodeURIComponent(
mailgoElement.href.split("?")[0].split(CALLTO)[1].trim()
);
} else if (mailgoElement.hasAttribute("data-tel")) {
tel = mailgoElement.getAttribute("data-tel");
msg = mailgoElement.getAttribute("data-msg");
}
// validate the phone number
if (!validateTel(tel)) return;
if (mailgoElement.hasAttribute("data-skype")) {
skypeUsername = mailgoElement.getAttribute("data-skype");
}
// the title of the modal (tel)
titleTel.innerHTML = tel;
// add the actions to buttons
wa.addEventListener("click", openWhatsApp);
skype.addEventListener("click", openSkype);
call.addEventListener("click", callDefault);
copyTel.addEventListener("click", () => copy(tel));
}
// if config.dark is set to true then all the modals will be in dark mode
if (!config?.dark) {
// if the element contains dark as class enable dark mode
if (mailgoElement.classList.contains("dark")) {
enableDarkMode(type);
} else {
disableDarkMode(type);
}
}
// show the mailgo
showMailgo(type);
// add listener keyDown
document.addEventListener("keydown", mailgoKeydown);
}
// actions
const openGmail = (): void => {
// Gmail url
let gmailUrl: string =
"https://mail.google.com/mail/u/0/?view=cm&source=mailto&to=" +
encodeURIComponent(mail);
// the details if provided
if (cc) gmailUrl = gmailUrl.concat("&cc=" + encodeURIComponent(cc));
if (bcc) gmailUrl = gmailUrl.concat("&bcc=" + encodeURIComponent(bcc));
if (subject) gmailUrl = gmailUrl.concat("&subject=" + subject);
if (bodyMail) gmailUrl = gmailUrl.concat("&body=" + bodyMail);
// open the link
window.open(gmailUrl, "_blank");
// hide the modal
hideMailgo();
};
const openOutlook = (): void => {
// Outlook url
let outlookUrl: string =
"https://outlook.live.com/owa/?path=/mail/action/compose&to=" +
encodeURIComponent(mail);
// the details if provided
if (subject) outlookUrl = outlookUrl.concat("&subject=" + subject);
if (bodyMail) outlookUrl = outlookUrl.concat("&body=" + bodyMail);
// open the link
window.open(outlookUrl, "_blank");
// hide the modal
hideMailgo();
};
const openDefault = (): void => {
mailToEncoded(encEmail);
hideMailgo();
};
const openSkype = (): void => {
let skype: string = skypeUsername !== "" ? skypeUsername : tel;
let skypeUrl: string = "skype:" + skype;
// open the url
window.open(skypeUrl, "_blank");
// hide the modal
hideMailgo();
};
const openWhatsApp = (): void => {
// WhatsApp url
let waUrl: string = "https://wa.me/" + tel;
// the details if provided
if (msg) waUrl + "?text=" + msg;
// open the url
window.open(waUrl, "_blank");
// hide the modal
hideMailgo();
};
const callDefault = () => {
let callUrl: string = "tel:" + tel;
window.open(callUrl);
hideMailgo();
};
const copy = (content: string): void => {
copyToClipboard(content);
let activeCopy: HTMLElement;
// the correct copyButton (mail or tel)
mailgoIsShowing(MAIL_TYPE) ? (activeCopy = copyMail) : (activeCopy = copyTel);
activeCopy.textContent = "kopiert";
setTimeout(() => {
activeCopy.textContent = "copy";
// hide after the timeout
hideMailgo();
}, 999);
};
// function that returns if an element is a mailgo
export function isMailgo(
element: HTMLElement,
type: string = MAIL_TYPE
): boolean {
let href: string = (element as HTMLLinkElement).href;
// mailgo type mail
if (type === MAIL_TYPE) {
return (
// first case: it is an <a> element with "mailto:..." in href and no no-mailgo in classList
(href &&
href.toLowerCase().startsWith(MAILTO) &&
!element.classList.contains("no-mailgo")) ||
(element.hasAttribute("data-address") &&
// second case: the href=#mailgo
((href && element.getAttribute("href").toLowerCase() === "#mailgo") ||
// third case: the classList contains mailgo
(element.classList && element.classList.contains("mailgo"))))
);
}
// mailgo type tel
if (type === TEL_TYPE) {
return (
// first case: it is an <a> element with "tel:..." or "callto:..." in href and no no-mailgo in classList
(href &&
(href.toLowerCase().startsWith(TEL) ||
href.toLowerCase().startsWith(CALLTO)) &&
!element.classList.contains("no-mailgo")) ||
(element.hasAttribute("data-tel") &&
// second case: the href=#mailgo
href &&
element.getAttribute("href").toLowerCase() === "#mailgo") ||
// third case: the classList contains mailgo
(element.classList && element.classList.contains("mailgo"))
);
}
return false;
}
/**
* mailgoCheckRender
* function to check if an element is mailgo-enabled or not referencing to
* mail:
* document.querySelectorAll(
* 'a[href^="mailto:" i]:not(.no-mailgo), a[href="#mailgo"], a.mailgo'
* );
* tel:
* document.querySelectorAll(
* 'a[href^="tel:" i]:not(.no-mailgo), a[href="#mailgo"], a.mailgo'
* );
* or
* document.querySelectorAll(
* 'a[href^="callto:" i]:not(.no-mailgo), a[href="#mailgo"], a.mailgo'
* );
*/
export function mailgoCheckRender(event: Event): boolean {
// check if the id=mailgo exists in the body
if (!document.contains(modalMailto) || !document.contains(modalTel))
return false;
// if a mailgo is already showing do nothing
if (mailgoIsShowing(MAIL_TYPE) || mailgoIsShowing(TEL_TYPE)) return false;
// the path of the event
let path =
(event.composedPath && event.composedPath()) ||
composedPath(event.target as HTMLElement);
if (path) {
path.forEach((element: HTMLElement) => {
if (element instanceof HTMLDocument || element instanceof Window)
return false;
// go in the event.path to find if the user has clicked on a mailgo element
if (isMailgo(element, MAIL_TYPE)) {
// stop the normal execution of the element click
event.preventDefault();
// render mailgo
mailgoRender(MAIL_TYPE, element as HTMLLinkElement);
return true;
}
if (isMailgo(element, TEL_TYPE)) {
// stop the normal execution of the element click
event.preventDefault();
// render mailgo
mailgoRender(TEL_TYPE, element as HTMLLinkElement);
return true;
}
});
}
return false;
}
/**
* mailgoKeydown
* function to manage the keydown event when the modal is showing
*/
const mailgoKeydown = (keyboardEvent: KeyboardEvent): void => {
// if mailgo is showing
if (mailgoIsShowing(MAIL_TYPE)) {
switch (keyboardEvent.keyCode) {
case 27:
// Escape
hideMailgo();
break;
case 71:
// g -> open GMail
openGmail();
break;
case 79:
// o -> open Outlook
openOutlook();
break;
case 32:
case 13:
// spacebar or enter -> open default
openDefault();
break;
case 67:
// c -> copy
copy(mail);
break;
default:
return;
}
} else if (mailgoIsShowing(TEL_TYPE)) {
switch (keyboardEvent.keyCode) {
case 27:
// Escape
hideMailgo();
break;
case 87:
// w -> open WhatsApp
openWhatsApp();
break;
case 32:
case 13:
// spacebar or enter -> call default
callDefault();
break;
case 67:
// c -> copy
copy(tel);
break;
default:
return;
}
}
return;
};
// show the modal
const showMailgo = (type = MAIL_TYPE): void => {
// show the correct modal
setModalDisplay(type, "flex");
};
// hide the modal
const hideMailgo = (): void => {
// hide all the modals
setModalDisplay(MAIL_TYPE, "none");
setModalDisplay(TEL_TYPE, "none");
// remove listener keyDown
document.removeEventListener("keydown", mailgoKeydown);
};
// is the mailgo modal hidden?
const mailgoIsShowing = (type = MAIL_TYPE): boolean => {
return getModalDisplay(type) === "flex";
};
// create element
const createElement = (element: string = "div"): HTMLElement =>
document.createElement(element);
// create text node
const createTextNode = (element: string): Text =>
document.createTextNode(element);
// decrypt email
const mailToEncoded = (encoded: string): string =>
(window.location.href = MAILTO + atob(encoded));
// encode email
const encodeEmail = (email: string): string => btoa(email);
// get the correct HTMLElement from a type
const getModalHTMLElement = (type: string = MAIL_TYPE) =>
type === TEL_TYPE ? modalTel : modalMailto;
// get display value
const getModalDisplay = (ref: string = MAIL_TYPE): string =>
getModalHTMLElement(ref).style.display;
// set display value
const setModalDisplay = (ref: string = MAIL_TYPE, value: string): void => {
let modal = getModalHTMLElement(ref);
modal.style.display = value;
if (value === "flex") {
// "save" the activated link.
activatedLink = document.activeElement as HTMLElement;
modal.setAttribute("aria-hidden", "false");
// Focus on the modal container.
modal.setAttribute("tabindex", "0");
modal.focus();
setFocusLoop(modal);
} else {
modal.setAttribute("aria-hidden", "true");
// focus back the activated link for getting back to the context.
modal.setAttribute("tabindex", "-1");
activatedLink.focus();
}
};
// set focus loop within modal
const setFocusLoop = (ref: HTMLElement): void => {
let modal = ref;
modal
.querySelector(".m-modal-content a:last-of-type")
.addEventListener("keydown", leaveLastLink);
modal
.querySelector(".m-modal-content a:first-of-type")
.addEventListener("keydown", leaveFirstLink);
};
const leaveLastLink = (e: KeyboardEvent): void => {
// going back to the first link to force looping
if (e.code === "Tab" && e.shiftKey === false) {
e.preventDefault();
((e.target as HTMLElement)
.closest("div")
.querySelector("a:first-of-type") as HTMLElement).focus();
}
};
const leaveFirstLink = (e: KeyboardEvent): void => {
// going back to the first link to force looping
if (e.code === "Tab" && e.shiftKey === true) {
e.preventDefault();
((e.target as HTMLElement)
.closest("div")
.querySelector("a:last-of-type") as HTMLElement).focus();
}
};
// enable dark mode
const enableDarkMode = (type: string = MAIL_TYPE) =>
getModalHTMLElement(type).classList.add("m-dark");
// disable dark mode
const disableDarkMode = (type: string = MAIL_TYPE) =>
getModalHTMLElement(type).classList.remove("m-dark");
// custom composedPath if path or event.composedPath() are not defined
const composedPath = (
el: HTMLElement
): (HTMLElement | Document | (Window & typeof globalThis))[] => {
let path = [];
while (el) {
path.push(el);
if (el.tagName === "HTML") {
path.push(document);
path.push(window);
return path;
}
el = el.parentElement;
}
};
// validate a single email with regex
const validateEmail = (email: string): boolean =>
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(
email
);
// validate an array of emails
const validateEmails = (arr: string[]): boolean => arr.every(validateEmail);
// validate a single tel with regex
const validateTel = (tel: string): boolean =>
/^[+]{0,1}[\s0-9]{0,}[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$/.test(tel);
// copy of a string
const copyToClipboard = (str: string): boolean => {
let el: HTMLInputElement = createElement("textarea") as HTMLInputElement;
el.value = str;
el.setAttribute("readonly", "");
el.style.position = "absolute";
el.style.left = "-9999px";
document.body.appendChild(el);
let selected: Range | boolean =
document.getSelection().rangeCount > 0
? document.getSelection().getRangeAt(0)
: false;
el.select();
document.execCommand("copy");
document.body.removeChild(el);
if (selected) {
document.getSelection().removeAllRanges();
document.getSelection().addRange(selected);
return true;
}
return false;
};
const mailgoStyle = (): void => {
// mailgo style
let mailgoCSSElement: HTMLStyleElement = createElement(
"style"
) as HTMLStyleElement;
mailgoCSSElement.id = "mailgo-style";
mailgoCSSElement.type = "text/css";
mailgoCSSElement.appendChild(createTextNode(mailgoCSS));
document.head.appendChild(mailgoCSSElement);
};
// mailgo
function mailgo(mailgoConfig?: MailgoConfig): void {
// set the global config merging window mailgConfig and mailgoConfig passed as a parameter
config = { ...mailgoConfig, ...((window as any)?.mailgoConfig || null) };
// if the window is defined...
if (window && typeof window !== "undefined") {
// add the style for mailgo
mailgoStyle();
// if is set an initEvent add the listener
if (config?.initEvent) {
if (config?.listenerOptions) {
// listener options specified
document.addEventListener(
config.initEvent,
() => {
mailgoInit();
},
config.listenerOptions
);
} else {
// no listener options
document.addEventListener(config.initEvent, () => {
mailgoInit();
});
}
} else {
mailgoInit();
}
}
}
export default mailgo;

305
src/scss/component.scss Normal file
View File

@ -0,0 +1,305 @@
$animation-speed: 0.4s; //0.4s
$overlay-bg-color: rgba(0, 0, 0, 0.4); //rgba(0, 0, 0, 0.4)
$radius: 8px; //8px
$success-bg-color: #1f9d55; //#1f9d55
$success-color: #fff; //#fff
$modal-border-radius: $radius; //$radius
$modal-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); //0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19)
$modal-head-bg-color: #fff; //#fff
$modal-head-padding-x: 20px; //20px
$modal-head-padding-y: 10px; //10px
$modal-title-color: #303131; //#303131
$modal-title-font-family: sans-serif; //sans-serif
$modal-title-font-size: 120%; //120%
$modal-title-font-weight: 700; //700
$modal-close-color-1: #aaa; //#aaa
$modal-close-color-2: #000; //#000
$modal-close-font-size: 38px; //38px
$modal-close-top: -4px; //-4px
$modal-content-bg-color: #f1f5f8; //#f1f5f8
$modal-body-padding: 20px; //20px
$button-bg-color: #fff; //#fff
$button-border-radius: $radius; //$radius
$button-border: none; //none
$button-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18); //0 2px 4px rgba(0, 0, 0, 0.18)
$button-color: #333; //#333
$button-margin-bottom: 20px; //20px
$button-hover-bg-color: #555; //#555
$button-hover-color: #fff; //#fff
$button-padding-x: 20px; //20px
$button-padding-y: 15px; //15px
$button-icon-height: 24px; //24px
$button-icon-top: 4px; //4px
$button-icon-width: 24px; //24px
$button-copy-border: $button-border; //$button-border
$email-bg-color: #d8dcdf; //#d8dcdf
$email-box-shadow: unset; //unset
$email-color: #48494a; //#48494a
$email-margin-top: 20px; //20px
$brand-color: #888; //#888
$brand-font-size: 80%; //80%
.mailtoui-modal {
background-color: #000;
background-color: $overlay-bg-color;
bottom: 0;
color: #303131;
display: none;
height: 100%;
left: 0;
margin: 0;
padding: 0;
position: fixed;
right: 0;
top: 0;
width: 100%;
z-index: 1000;
}
.mailtoui-modal-content {
animation: mailtoui-appear $animation-speed;
background-color: $modal-content-bg-color;
border-radius: $modal-border-radius;
bottom: auto;
box-shadow: $modal-box-shadow;
left: 50%;
max-height: calc(100% - 100px);
overflow: auto;
padding: 0;
position: fixed;
right: -45%;
top: 50%;
transform: translate(-50%, -50%);
}
.mailtoui-modal-content:focus,
.mailtoui-modal-content:hover {
overflow-y: auto;
}
@media only screen and (min-width: 768px) {
.mailtoui-modal-content {
right: auto;
}
}
.mailtoui-modal-head {
align-items: center;
background-color: $modal-head-bg-color;
clear: both;
display: flex;
min-width: 0;
padding: $modal-head-padding-y $modal-head-padding-x;
}
.mailtoui-modal-title {
color: $modal-title-color;
flex: 1;
font-family: $modal-title-font-family;
font-size: $modal-title-font-size;
font-weight: $modal-title-font-weight;
margin: 0;
overflow: hidden;
padding: 0;
text-overflow: ellipsis;
white-space: nowrap;
}
.mailtoui-modal-close {
color: $modal-close-color-1;
flex: initial;
font-size: $modal-close-font-size;
margin-left: 20px;
position: relative;
text-align: right;
text-decoration: none;
top: $modal-close-top;
}
.mailtoui-modal-close:focus,
.mailtoui-modal-close:hover {
color: $modal-close-color-2;
cursor: pointer;
font-weight: 700;
outline: 0;
}
.mailtoui-modal-body {
height: 100%;
padding: $modal-body-padding;
}
.mailtoui-button {
color: $button-color;
text-decoration: none;
}
.mailtoui-button:focus {
outline: 0;
}
.mailtoui-button:focus .mailtoui-button-content {
background-color: #555;
color: #fff;
}
.mailtoui-button-content {
background-color: $button-bg-color;
border: $button-border;
border-radius: $button-border-radius;
box-shadow: $button-box-shadow;
margin-bottom: $button-margin-bottom;
overflow: hidden;
padding: $button-padding-y $button-padding-x;
text-overflow: ellipsis;
white-space: nowrap;
}
.mailtoui-button-content:hover {
background-color: $button-hover-bg-color;
color: $button-hover-color;
}
.mailtoui-button:last-child .mailtoui-button-content {
margin-bottom: 0;
}
.mailtoui-button-icon {
display: inline-block;
font-weight: 700;
position: relative;
top: $button-icon-top;
}
.mailtoui-button-icon svg {
height: $button-icon-height;
width: $button-icon-width;
}
.mailtoui-button-text {
display: inline-block;
margin-left: 5px;
position: relative;
top: -2px;
}
.mailtoui-copy {
border-radius: $button-border-radius;
box-shadow: $button-box-shadow;
height: 59px;
margin-top: $email-margin-top;
position: relative;
}
.mailtoui-button-copy {
background-color: $button-bg-color;
border: $button-copy-border;
border-bottom-left-radius: $button-border-radius;
border-top-left-radius: $button-border-radius;
bottom: 21px;
color: $button-color;
font-size: 100%;
height: 100%;
left: 0;
overflow: hidden;
padding: 15px 20px;
position: absolute;
text-overflow: ellipsis;
top: 0;
white-space: nowrap;
width: 120px;
}
.mailtoui-button-copy:focus,
.mailtoui-button-copy:hover {
background-color: $button-hover-bg-color;
color: $button-hover-color;
cursor: pointer;
outline: 0;
}
.mailtoui-button-copy-clicked,
.mailtoui-button-copy-clicked:focus,
.mailtoui-button-copy-clicked:hover {
background-color: $success-bg-color;
color: $success-color;
}
.mailtoui-button-copy-clicked .mailtoui-button-icon,
.mailtoui-button-copy-clicked:focus .mailtoui-button-icon,
.mailtoui-button-copy-clicked:hover .mailtoui-button-icon {
display: none;
visibility: hidden;
}
.mailtoui-button-copy-clicked .mailtoui-button-text,
.mailtoui-button-copy-clicked:focus .mailtoui-button-text,
.mailtoui-button-copy-clicked:hover .mailtoui-button-text {
color: #fff;
top: 2px;
}
.mailtoui-email-address {
background-color: $email-bg-color;
border: none;
border-radius: $button-border-radius;
box-shadow: $email-box-shadow;
box-sizing: border-box;
color: $email-color;
font-size: 100%;
height: 100%;
overflow: hidden;
padding: 20px 20px 20px 140px;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.mailtoui-brand {
color: $brand-color;
font-size: $brand-font-size;
margin-top: 20px;
text-align: center;
}
.mailtoui-brand a {
color: $brand-color;
}
.mailtoui-brand a:focus,
.mailtoui-brand a:hover {
font-weight: 700;
outline: 0;
}
.mailtoui-no-scroll {
overflow: hidden;
position: fixed;
width: 100%;
}
.mailtoui-is-hidden {
display: none;
visibility: hidden;
}
@keyframes mailtoui-appear {
0% {
opacity: 0;
transform: translate(-50%, -50%) scale(0, 0);
}
100% {
opacity: 1;
transform: translate(-50%, -50%) scale(1, 1);
}
}

View File

@ -1,19 +0,0 @@
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": true,
"module": "ES6",
"target": "ES6",
"jsx": "react",
"allowJs": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"declaration": true
},
"include": [
"mailgo.d.ts",
"src",
"webpack/mailgo.dist.ts"
]
}

View File

@ -1,45 +0,0 @@
const path = require('path');
const mailgoRules = [
{
test: /\.tsx?$/,
include: path.resolve(__dirname, './src/'),
use: [ 'babel-loader' ],
exclude: /node_modules/
},
{
test: /\.scss$/i,
use: [
'to-string-loader',
{
loader: 'css-loader',
options: {
esModule: false,
sourceMap: false
}
},
'sass-loader'
]
}
];
module.exports = [
{
mode: 'production',
target: 'web',
entry: './mailgo.dist.ts',
context: path.join(__dirname, 'webpack'),
module: {
rules: mailgoRules
},
resolve: {
extensions: [ '.ts', '.js' ]
},
output: {
filename: 'mailgo.min.js',
library: 'mailgo',
libraryTarget: 'window',
path: path.resolve(__dirname, 'dist')
}
}
];

View File

@ -1,8 +0,0 @@
import mailgo from '../src/mailgo';
// call init mailgo attached to the event DOMContentLoaded
const mailgoConfig = {
initEvent: 'DOMContentLoaded'
};
mailgo(mailgoConfig);

View File

@ -1,2 +0,0 @@
import mailgo from '../src/mailgo';
export default mailgo;

4855
yarn.lock

File diff suppressed because it is too large Load Diff