-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathuseEmailAutocomplete.js
139 lines (122 loc) · 4.3 KB
/
useEmailAutocomplete.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import { useState, useRef, useCallback, useEffect } from 'react'
// eslint-disable-next-line
const defaultDomains = ['yahoo.com', 'hotmail.com', 'gmail.com', 'me.com', 'aol.com', 'mac.com', 'live.com', 'googlemail.com', 'msn.com', 'facebook.com', 'verizon.net', 'outlook.com', 'icloud.com', 'table.co', 'fb.com']
export default function useEmailAutocomplete({
domains = [],
validation = true,
} = {}) {
const theDomains = [...(domains || []), ...defaultDomains]
const prevEmail = useRef('')
const prevVal = useRef('')
const container = useRef()
const input = useRef()
const email = useRef('')
const isValid = useRef(null)
const [, forceUpdate] = useState(false)
const findInput = useCallback(element => {
if (element && element.tagName === 'INPUT') return element
if (element && element.children && element.children.length > 0) {
for (const child of element.children) {
const potentialInput = findInput(child)
if (potentialInput) return potentialInput
}
}
}, [])
useEffect(() => {
input.current = findInput(container.current)
if (!input.current) console.error('There is no input in the component you\'re trying to attach useEmailAutocomplete to')
}, [findInput])
const suggest = useCallback(email => {
const [/* emailName */, partialDomain] = email.split('@')
if (!partialDomain || email.length <= prevVal.current.length) return ''
const domain = theDomains.find(d => d.indexOf(partialDomain) === 0) || ''
return domain.replace(partialDomain, '')
}, [theDomains])
const validate = useCallback(email => {
const inputIsFocused = input.current === document.activeElement
// eslint-disable-next-line
const isValidEmail = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i
if (!email) {
return null
} else if (isValidEmail.test(email)) {
return 'yes'
} else if (inputIsFocused && prevEmail.current.length !== email.length) {
return 'maybe'
} else {
return 'no'
}
}, [])
const onChange = useCallback(e => {
// works for strings, and objects, not for numbers
if (!e.hasOwnProperty('target')) {
return console.error('NOT IMPLEMENTED YET')
}
const { value } = e.target
const suggestion = suggest(value)
const theEmail = value + suggestion
const isValidEmail = validate(theEmail)
email.current = theEmail
isValid.current = isValidEmail
forceUpdate(x => !x)
if (suggestion) highlight(suggestion)
prevEmail.current = theEmail
prevVal.current = value
}, [suggest, validate])
function highlight(suggestion) {
setTimeout(() => {
const email = prevVal.current + suggestion
const startPos = email.lastIndexOf(suggestion)
const endPos = startPos + suggestion.length
input.current.setSelectionRange(startPos, endPos)
}, 0)
}
const doValidationCheck = useCallback(e => {
if (validation) {
isValid.current = validate(email)
forceUpdate(x => !x)
}
}, [email, validate, validation])
const htmlAttributes = {
value: email.current,
onChange,
ref: container,
onBlur: doValidationCheck,
onFocus: doValidationCheck,
}
return {
email: new Proxy({ address: email, isValid }, { get: (obj, key) => obj[key].current }),
...htmlAttributes,
bind: htmlAttributes,
}
}
// const borderColors = {
// yes: '#28a745',
// maybe: '#cfdc00',
// no: '#dc3545'
// }
// const outlineColors = { // source: http://bit.ly/2j2sbyx
// yes: 'rgba(40, 167, 69, .25)',
// maybe: 'rgba(207, 220, 0, .25)',
// no: 'rgba(220, 53, 69, .25)'
// }
// const ValidationInput = styled.input`
// ${props => props.isValid && css`
// outline: none;
// &:focus {
// box-shadow: 0 0 0 0.2rem ${outlineColors[isValid]};
// }
// border: 1px solid ${borderColors[isValid]} !important;
// transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
// `}
// `
// const Input = styled(ValidationInput)`
// font-family: Helvetica, Arial, sans-serif;
// letter-spacing: 0.5px;
// line-height: 1.3em;
// box-sizing: border-box;
// border: 1px solid lightgray;
// padding: 0.5rem;
// border-radius: 3px;
// font-size: 12pt;
// outline: none;
// `