preline
Version:
Preline UI is an open-source set of prebuilt UI components based on the utility-first Tailwind CSS framework.
212 lines (170 loc) • 5.87 kB
text/typescript
/*
* HSTextareaAutoHeight
* @version: 3.0.1
* @author: Preline Labs Ltd.
* @license: Licensed under MIT and Preline UI Fair Use License (https://preline.co/docs/license.html)
* Copyright 2024 Preline Labs Ltd.
*/
import {
ITextareaAutoHeightOptions,
ITextareaAutoHeight,
} from '../textarea-auto-height/interfaces';
import HSBasePlugin from '../base-plugin';
import { ICollectionItem } from '../../interfaces';
import { ITabsOnChangePayload } from '../tabs/interfaces'
class HSTextareaAutoHeight
extends HSBasePlugin<ITextareaAutoHeightOptions>
implements ITextareaAutoHeight {
private readonly defaultHeight: number;
private onElementInputListener: () => void;
constructor(el: HTMLTextAreaElement, options?: ITextareaAutoHeightOptions) {
super(el, options);
const data = el.getAttribute('data-hs-copy-markup');
const dataOptions: ITextareaAutoHeightOptions = data
? JSON.parse(data)
: {};
const concatOptions = {
...dataOptions,
...options,
};
this.defaultHeight = concatOptions?.defaultHeight || 0;
this.init();
}
private elementInput() {
this.textareaSetHeight(3);
}
private init() {
this.createCollection(window.$hsTextareaAutoHeightCollection, this);
this.setAutoHeight();
}
private setAutoHeight() {
if (this.isParentHidden()) this.callbackAccordingToType();
else this.textareaSetHeight(3);
this.onElementInputListener = () => this.elementInput();
this.el.addEventListener('input', this.onElementInputListener);
}
private textareaSetHeight(offsetTop = 0) {
this.el.style.height = 'auto';
this.el.style.height =
this.checkIfOneLine() && this.defaultHeight
? `${this.defaultHeight}px`
: `${this.el.scrollHeight + offsetTop}px`;
}
private checkIfOneLine(): boolean {
const clientHeight = this.el.clientHeight;
const scrollHeight = this.el.scrollHeight;
if (scrollHeight > clientHeight) return false;
else return true;
}
private isParentHidden() {
return (
this.el.closest('.hs-overlay.hidden') ||
this.el.closest('[role="tabpanel"].hidden') ||
this.el.closest('.hs-collapse.hidden')
);
}
private parentType(): string | boolean {
if (this.el.closest('.hs-collapse')) return 'collapse';
else if (this.el.closest('.hs-overlay')) return 'overlay';
else if (this.el.closest('[role="tabpanel"]')) return 'tabs';
else return false;
}
private callbackAccordingToType() {
if (this.parentType() === 'collapse') {
const collapseId = this.el.closest('.hs-collapse').id;
const { element } = (window.HSCollapse as any).getInstance(
`[data-hs-collapse="#${collapseId}"]`,
true,
);
element.on('beforeOpen', () => {
if (!this.el) return false;
this.textareaSetHeight(3);
});
} else if (this.parentType() === 'overlay') {
const overlay = (window.HSOverlay as any).getInstance(this.el.closest('.hs-overlay'), true);
overlay.element.on('open', () => {
const collection = window.$hsTextareaAutoHeightCollection.filter(({ element }) => element.el.closest('.hs-overlay') === overlay.element.el);
collection.forEach(({ element }) => element.textareaSetHeight(3));
});
} else if (this.parentType() === 'tabs') {
const tabId = this.el.closest('[role="tabpanel"]')?.id;
const tab = document.querySelector(`[data-hs-tab="#${tabId}"]`);
const tabs = tab.closest('[role="tablist"]');
const { element } = (window.HSTabs as any).getInstance(tabs, true) || null;
element.on('change', (payload: ITabsOnChangePayload) => {
const textareas = document.querySelectorAll(`${payload.current} [data-hs-textarea-auto-height]`);
if (!textareas.length) return false;
textareas.forEach((el: HTMLTextAreaElement) => {
const instance = (window.HSTextareaAutoHeight as any).getInstance(el, true) || null;
if (instance) instance.element.textareaSetHeight(3);
});
});
} else return false;
}
// Public methods
public destroy() {
// Remove listeners
this.el.removeEventListener('input', this.onElementInputListener);
window.$hsTextareaAutoHeightCollection =
window.$hsTextareaAutoHeightCollection.filter(
({ element }) => element.el !== this.el,
);
}
// Static method
static getInstance(
target: HTMLTextAreaElement | string,
isInstance?: boolean,
) {
const elInCollection = window.$hsTextareaAutoHeightCollection.find(
(el) =>
el.element.el ===
(typeof target === 'string' ? document.querySelector(target) : target),
);
return elInCollection
? isInstance
? elInCollection
: elInCollection.element
: null;
}
static autoInit() {
if (!window.$hsTextareaAutoHeightCollection)
window.$hsTextareaAutoHeightCollection = [];
if (window.$hsTextareaAutoHeightCollection)
window.$hsTextareaAutoHeightCollection =
window.$hsTextareaAutoHeightCollection.filter(({ element }) =>
document.contains(element.el),
);
document
.querySelectorAll(
'[data-hs-textarea-auto-height]:not(.--prevent-on-load-init)',
)
.forEach((el: HTMLTextAreaElement) => {
if (
!window.$hsTextareaAutoHeightCollection.find(
(elC) => (elC?.element?.el as HTMLTextAreaElement) === el,
)
) {
const data = el.getAttribute('data-hs-textarea-auto-height');
const options: ITextareaAutoHeightOptions = data
? JSON.parse(data)
: {};
new HSTextareaAutoHeight(el, options);
}
});
}
}
declare global {
interface Window {
HSTextareaAutoHeight: Function;
$hsTextareaAutoHeightCollection: ICollectionItem<HSTextareaAutoHeight>[];
}
}
window.addEventListener('load', () => {
HSTextareaAutoHeight.autoInit();
// Uncomment for debug
// console.log('Textarea Autoheight collection:', window.$hsTextareaAutoHeightCollection);
});
if (typeof window !== 'undefined') {
window.HSTextareaAutoHeight = HSTextareaAutoHeight;
}
export default HSTextareaAutoHeight;