<div class="detail-demo " data-initial="30">
<div class="detail-demo__line-wrapper">
<div class="detail-demo__text" contentEditable="true"></div>
<div class="detail-demo__single-line">
Väike mölder hüppas üle rongi
</div>
</div>
</div>
{% set lineHeight %}
{% if data.customLineHeight %}style="line-height: {{ data.lineHeight }}em"{% endif %}
{% endset %}
<div class="detail-demo {{ class }} {{ modifier }}" data-initial="{{ data.initialSize }}">
<div class="detail-demo__line-wrapper">
<div class="detail-demo__text" contentEditable="true" {{ lineHeight }}></div>
<div class="detail-demo__single-line" {{ lineHeight }}>
{{ data.placeholder }}
</div>
</div>
</div>
{
"language": "en-US",
"data": {
"placeholder": "Väike mölder hüppas üle rongi",
"initialSize": 30,
"lineHeight": "normal"
}
}
.detail-demo {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
cursor: text;
overflow: hidden;
padding: 16px;
font-size: 35px;
@include bp(sm-min) {
font-size: 50px;
padding: 32px;
}
}
.detail-demo__single-line {
position: absolute;
pointer-events: none;
line-height: normal;
opacity: .2;
top: 0;
.detail-demo__text:focus + & {
display: none;
}
}
.detail-demo__line-wrapper {
position: relative;
max-height: 100%;
overflow: hidden;
}
.detail-demo__text {
line-height: normal;
}
import './detail-demo.scss';
import Component from '@component';
interface IDetailDemoSettings {
textClass: string;
singleLineClass: string;
lineWrapperClass: string;
initial: number;
}
export default class DetailDemo extends Component {
static initSelector: string = '.detail-demo';
settings: IDetailDemoSettings;
text: JQuery;
singleLine: JQuery;
lines: JQuery;
constructor(element: HTMLElement) {
super(element);
this.settings = $.extend({
initial: null,
lineWrapperClass: 'detail-demo__line-wrapper',
singleLineClass: 'detail-demo__single-line',
textClass: 'detail-demo__text',
}, this.element.data());
this.text = this.element.find('.' + this.settings.textClass);
this.lines = this.element.find('.' + this.settings.lineWrapperClass);
this.element.css('font-size', this.settings.initial);
this.singleLine = this.element.find('.' + this.settings.singleLineClass);
this.init();
}
init(): void {
this.element.on('click', this.clickHandler.bind(this));
this.text.on('blur', this.unclickHandler.bind(this));
this.text.on('input', this.inputHandler.bind(this));
this.text[0].addEventListener('paste', this.pasteHandler.bind(this));
requestAnimationFrame(this.inputHandler.bind(this));
const observer: MutationObserver = new MutationObserver((mutations: MutationRecord[]) => {
mutations.forEach((): void => {
this.updateHeight();
});
});
observer.observe(this.element[0], { attributeFilter: ['style'], attributes: true });
}
pasteHandler(event: ClipboardEvent): void {
event.preventDefault();
const paste: string = event.clipboardData.getData('text/plain');
const range: Range = document.createRange();
this.text.append(paste);
range.selectNodeContents(this.text[0]);
range.collapse(false);
const selection: Selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
this.inputHandler();
}
inputHandler(): void {
this.updateHeight();
this.updateHintVisibility();
}
updateHintVisibility(): void {
if (this.text.html().length === 0) {
this.singleLine.css('visibility', 'visible');
} else {
this.singleLine.css('visibility', 'hidden');
}
}
updateHeight(): void {
const demoHeight: number = this.element[0].getBoundingClientRect().height - parseInt(this.element.css('padding')) * 2;
const singleLineHeight: number = this.singleLine.height();
const maxLines: number = Math.max(1, Math.floor(demoHeight / singleLineHeight));
this.lines.css('height', Math.min(maxLines * singleLineHeight, this.text[0].getBoundingClientRect().height));
}
unclickHandler(): void {
this.text.attr('contenteditable', 'false');
}
clickHandler(): void {
this.text.attr('contenteditable', 'true');
this.text.trigger('focus');
}
}