Eduard Kuzmenko
4 years ago
19 changed files with 515 additions and 352 deletions
@ -1,33 +1,119 @@ |
|||||||
const InputField = (placeholder: string, label: string, name: string, maxLength?: number, showLengthOn: number = maxLength ? maxLength / 3 : 0) => { |
import { getRichValue, isInputEmpty } from "../helpers/dom"; |
||||||
|
import { checkRTL } from "../helpers/string"; |
||||||
|
import RichTextProcessor from "../lib/richtextprocessor"; |
||||||
|
|
||||||
|
let init = () => { |
||||||
|
document.addEventListener('paste', (e) => { |
||||||
|
if(!(e.target as HTMLElement).hasAttribute('contenteditable') && !(e.target as HTMLElement).parentElement.hasAttribute('contenteditable')) { |
||||||
|
return; |
||||||
|
} |
||||||
|
//console.log('document paste');
|
||||||
|
|
||||||
|
//console.log('messageInput paste');
|
||||||
|
|
||||||
|
e.preventDefault(); |
||||||
|
// @ts-ignore
|
||||||
|
let text = (e.originalEvent || e).clipboardData.getData('text/plain'); |
||||||
|
|
||||||
|
let entities = RichTextProcessor.parseEntities(text); |
||||||
|
//console.log('messageInput paste', text, entities);
|
||||||
|
entities = entities.filter(e => e._ == 'messageEntityEmoji' || e._ == 'messageEntityLinebreak'); |
||||||
|
//text = RichTextProcessor.wrapEmojiText(text);
|
||||||
|
text = RichTextProcessor.wrapRichText(text, {entities, noLinks: true}); |
||||||
|
|
||||||
|
// console.log('messageInput paste after', text);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
//let html = (e.originalEvent || e).clipboardData.getData('text/html');
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
//console.log('paste text', text, );
|
||||||
|
window.document.execCommand('insertHTML', false, text); |
||||||
|
}); |
||||||
|
|
||||||
|
init = null; |
||||||
|
}; |
||||||
|
|
||||||
|
const InputField = (options: { |
||||||
|
placeholder?: string, |
||||||
|
label?: string, |
||||||
|
name: string, |
||||||
|
maxLength?: number, |
||||||
|
showLengthOn?: number, |
||||||
|
plainText?: true |
||||||
|
}) => { |
||||||
const div = document.createElement('div'); |
const div = document.createElement('div'); |
||||||
div.classList.add('input-field'); |
div.classList.add('input-field'); |
||||||
|
|
||||||
|
if(options.maxLength) { |
||||||
|
options.showLengthOn = Math.round(options.maxLength / 3); |
||||||
|
} |
||||||
|
|
||||||
|
const {placeholder, label, maxLength, showLengthOn, name, plainText} = options; |
||||||
|
|
||||||
|
if(!plainText) { |
||||||
|
if(init) { |
||||||
|
init(); |
||||||
|
} |
||||||
|
|
||||||
|
div.innerHTML = ` |
||||||
|
<div id="input-${name}" ${placeholder ? `data-placeholder="${placeholder}"` : ''} contenteditable="true" class="input-field-input"></div> |
||||||
|
${label ? `<label for="input-${name}">${label}</label>` : ''} |
||||||
|
`;
|
||||||
|
|
||||||
|
const input = div.firstElementChild as HTMLElement; |
||||||
|
const observer = new MutationObserver((mutationsList, observer) => { |
||||||
|
const isEmpty = isInputEmpty(input); |
||||||
|
console.log('input', isEmpty); |
||||||
|
|
||||||
|
const char = input.innerText[0]; |
||||||
|
let direction = 'ltr'; |
||||||
|
if(char && checkRTL(char)) { |
||||||
|
direction = 'rtl'; |
||||||
|
} |
||||||
|
|
||||||
|
input.style.direction = direction; |
||||||
|
|
||||||
|
if(processInput) { |
||||||
|
processInput(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
// ! childList for paste first symbol
|
||||||
|
observer.observe(input, {characterData: true, childList: true, subtree: true}); |
||||||
|
} else { |
||||||
div.innerHTML = ` |
div.innerHTML = ` |
||||||
<input type="text" name="${name}" id="input-${name}" placeholder="${placeholder}" autocomplete="off" required=""> |
<input type="text" name="${name}" id="input-${name}" ${placeholder ? `placeholder="${placeholder}"` : ''} autocomplete="off" required="" class="input-field-input"> |
||||||
<label for="input-${name}">${label}</label> |
${label ? `<label for="input-${name}">${label}</label>` : ''} |
||||||
`;
|
`;
|
||||||
|
} |
||||||
|
|
||||||
|
let processInput: () => void; |
||||||
if(maxLength) { |
if(maxLength) { |
||||||
const input = div.firstElementChild as HTMLInputElement; |
const input = div.firstElementChild as HTMLInputElement; |
||||||
const labelEl = div.lastElementChild as HTMLLabelElement; |
const labelEl = div.lastElementChild as HTMLLabelElement; |
||||||
let showingLength = false; |
let showingLength = false; |
||||||
input.addEventListener('input', (e) => { |
|
||||||
|
processInput = () => { |
||||||
const wasError = input.classList.contains('error'); |
const wasError = input.classList.contains('error'); |
||||||
const diff = maxLength - input.value.length; |
const inputLength = plainText ? input.value.length : getRichValue(input).length; |
||||||
|
const diff = maxLength - inputLength; |
||||||
const isError = diff < 0; |
const isError = diff < 0; |
||||||
input.classList.toggle('error', isError); |
input.classList.toggle('error', isError); |
||||||
|
|
||||||
if(isError || diff <= showLengthOn) { |
if(isError || diff <= showLengthOn) { |
||||||
labelEl.innerText = label + ` (${maxLength - input.value.length})`; |
labelEl.innerText = label + ` (${maxLength - inputLength})`; |
||||||
if(!showingLength) showingLength = true; |
if(!showingLength) showingLength = true; |
||||||
} else if((wasError && !isError) || showingLength) { |
} else if((wasError && !isError) || showingLength) { |
||||||
labelEl.innerText = label; |
labelEl.innerText = label; |
||||||
showingLength = false; |
showingLength = false; |
||||||
} |
} |
||||||
}); |
}; |
||||||
|
|
||||||
|
input.addEventListener('input', processInput); |
||||||
} |
} |
||||||
|
|
||||||
return div; |
return {container: div, input: div.firstElementChild as HTMLInputElement}; |
||||||
}; |
}; |
||||||
|
|
||||||
export default InputField; |
export default InputField; |
@ -0,0 +1,155 @@ |
|||||||
|
.input-wrapper { |
||||||
|
width: 360px; |
||||||
|
margin: 0 auto; |
||||||
|
} |
||||||
|
|
||||||
|
.input-field { |
||||||
|
position: relative; |
||||||
|
|
||||||
|
.arrow-down { |
||||||
|
position: absolute; |
||||||
|
content: " "; |
||||||
|
top: 50%; |
||||||
|
bottom: 0; |
||||||
|
right: 21px; |
||||||
|
cursor: pointer; |
||||||
|
|
||||||
|
height: 0; |
||||||
|
width: 0; |
||||||
|
|
||||||
|
border: solid #707579; |
||||||
|
border-radius: 1px; |
||||||
|
border-width: 0 2px 2px 0; |
||||||
|
display: inline-block; |
||||||
|
padding: 5px; |
||||||
|
vertical-align: middle; |
||||||
|
|
||||||
|
z-index: 2; |
||||||
|
|
||||||
|
margin-top: -9px; |
||||||
|
transform: rotate(45deg); |
||||||
|
-webkit-transform: rotate(45deg); |
||||||
|
transition: .2s all; |
||||||
|
} |
||||||
|
|
||||||
|
label { |
||||||
|
position: absolute; |
||||||
|
color: $placeholder-color; |
||||||
|
left: 1rem; |
||||||
|
right: auto; |
||||||
|
z-index: 2; |
||||||
|
top: 50%; |
||||||
|
transform: translateY(-50%); |
||||||
|
background-color: #fff; |
||||||
|
transition: .2s all, .1s opacity; |
||||||
|
display: inline-block; |
||||||
|
pointer-events: none; |
||||||
|
} |
||||||
|
|
||||||
|
input, &-input { |
||||||
|
--border-width: 1px; |
||||||
|
--border-width-top: 2px; |
||||||
|
border: var(--border-width) solid #DADCE0; |
||||||
|
border-radius: $border-radius-medium; |
||||||
|
//padding: 0 1rem; |
||||||
|
padding: calc(1rem - var(--border-width-top)) calc(1rem - var(--border-width)); |
||||||
|
box-sizing: border-box; |
||||||
|
width: 100%; |
||||||
|
min-height: 54px; |
||||||
|
transition: .2s border-color; |
||||||
|
position: relative; |
||||||
|
z-index: 1; |
||||||
|
//line-height: calc(54px - var(--border-width)); |
||||||
|
/* overflow: hidden; |
||||||
|
white-space: nowrap; */ |
||||||
|
|
||||||
|
html.no-touch & { |
||||||
|
&:hover:not(:focus):not(.error):not(.valid) { |
||||||
|
border-color: var(--color-gray); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@include respond-to(handhelds) { |
||||||
|
height: 50px; |
||||||
|
} |
||||||
|
/* font-weight: 500; */ |
||||||
|
|
||||||
|
/* &:hover { |
||||||
|
border-color: #000; |
||||||
|
} */ |
||||||
|
|
||||||
|
&:focus { |
||||||
|
--border-width: 2px; |
||||||
|
--border-width-top: 3px; |
||||||
|
border-color: $button-primary-background; |
||||||
|
//padding: 0 calc(1rem - 1px); |
||||||
|
} |
||||||
|
|
||||||
|
&:disabled { |
||||||
|
background-color: #fff; |
||||||
|
color: #000; |
||||||
|
} |
||||||
|
|
||||||
|
&.error { |
||||||
|
border-color: $color-error; |
||||||
|
|
||||||
|
& + label { |
||||||
|
color: $color-error!important; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
&.valid { |
||||||
|
border-color: #26962F; |
||||||
|
|
||||||
|
& + label { |
||||||
|
color: #26962F !important; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* &.error, &.valid { |
||||||
|
transition: .2s border-width; |
||||||
|
} */ |
||||||
|
|
||||||
|
&:focus ~ .arrow-down { |
||||||
|
margin-top: -4px; |
||||||
|
transform: rotate(225deg); |
||||||
|
-webkit-transform: rotate(225deg); |
||||||
|
border-color: $button-primary-background; |
||||||
|
} |
||||||
|
|
||||||
|
&:focus + label { |
||||||
|
color: $button-primary-background; |
||||||
|
} |
||||||
|
|
||||||
|
&:focus + label, &:valid + label, &:not(:empty) + label, &:disabled + label { |
||||||
|
top: -.5rem; |
||||||
|
transform: none; |
||||||
|
padding: 0 5px; |
||||||
|
left: .75rem; |
||||||
|
font-size: 0.75rem!important; |
||||||
|
//color: #666; |
||||||
|
opacity: 1; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
.input-wrapper > * + * { |
||||||
|
margin-top: 1.5rem; |
||||||
|
} |
||||||
|
|
||||||
|
::placeholder { /* Chrome, Firefox, Opera, Safari 10.1+ */ |
||||||
|
color: #909192; |
||||||
|
opacity: 1; /* Firefox */ |
||||||
|
} |
||||||
|
|
||||||
|
:-ms-input-placeholder { /* Internet Explorer 10-11 */ |
||||||
|
color: #a2acb4; |
||||||
|
} |
||||||
|
|
||||||
|
::-ms-input-placeholder { /* Microsoft Edge */ |
||||||
|
color: #a2acb4; |
||||||
|
} |
||||||
|
|
||||||
|
input:focus, button:focus { |
||||||
|
outline: none; |
||||||
|
} |
Loading…
Reference in new issue