UNPKG

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
/* * 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;