UNPKG

text-fields

Version:

TextFields is designed to create and manage text fields with advanced visuals and functionality, including dynamic notched outlines, floating labels, and adaptive text areas.

1 lines 6.07 kB
{"mappings":"AAAA,qBAAM,UAAU;;IA+IP,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,mBAAmB;IAanD,IAAI;CASlB;AAED,eAAe,UAAU,CAAC","sources":["src/src/index.ts","src/index.ts"],"sourcesContent":[null,"class TextFields {\n private textFieldContainer: (HTMLInputElement | HTMLTextAreaElement)[];\n\n private floatingLabel: HTMLElement[];\n\n private resizeObserver: ResizeObserver;\n\n private notches: { container: HTMLElement; notch: HTMLElement }[] = [];\n\n private mutationObserver: MutationObserver;\n\n constructor() {\n this.textFieldContainer = [];\n this.floatingLabel = [];\n this.resizeObserver = new ResizeObserver(TextFields.handleResize.bind(this));\n this.mutationObserver = new MutationObserver(this.initializeElements.bind(this));\n this.observeDOMChanges();\n }\n\n private observeDOMChanges() {\n this.mutationObserver.observe(document.body, { childList: true, subtree: true });\n }\n\n private static handleResize(entries: ResizeObserverEntry[]) {\n entries.forEach((entry) => {\n const notch = entry.target.closest('.notched-outline')?.querySelector<HTMLElement>('.notched-outline__notch');\n\n if (notch) {\n TextFields.setNotchWidth(notch, TextFields.getNotchWidth(notch));\n }\n });\n }\n\n private initializeElements() {\n this.textFieldContainer = Array.from(document.querySelectorAll<HTMLInputElement | HTMLTextAreaElement>('.text-field-container input, .text-field-container textarea'));\n this.floatingLabel = Array.from(document.querySelectorAll<HTMLElement>('.floating-label'));\n\n if (this.textFieldContainer.length > 0 && this.floatingLabel.length > 0) {\n this.setupNotches();\n this.handleEvents();\n this.updateInitialNotchWidths();\n }\n }\n\n private setupNotches() {\n this.floatingLabel.forEach((label) => {\n const notchedOutline = label.closest('.notched-outline') ?? TextFields.createNotchedOutline(label);\n\n if (notchedOutline) {\n const notch = notchedOutline.querySelector<HTMLElement>('.notched-outline__notch');\n\n if (notch) {\n this.notches.push({ container: notchedOutline.parentElement as HTMLElement, notch });\n\n TextFields.setNotchWidth(notch, TextFields.getNotchWidth(notch));\n\n this.resizeObserver.observe(label);\n }\n }\n });\n }\n\n private static createNotchedOutline(label: HTMLElement): HTMLElement {\n const notchedOutline = document.createElement('div');\n\n notchedOutline.classList.add('notched-outline');\n notchedOutline.innerHTML = `\n <div class=\"notched-outline__leading\"></div>\n <div class=\"notched-outline__notch\">${label.outerHTML}</div>\n <div class=\"notched-outline__trailing\"></div>\n `;\n label.replaceWith(notchedOutline);\n\n return notchedOutline;\n }\n\n private static setNotchWidth(notch: HTMLElement, width: string) {\n const newNotch = notch;\n\n newNotch.style.width = width;\n }\n\n private static getNotchWidth(notch: HTMLElement): string {\n const label = notch.querySelector<HTMLElement>('.floating-label');\n\n return label ? `${(parseFloat(getComputedStyle(label).width) + 13) * 0.75}px` : 'auto';\n }\n\n private handleEvents() {\n this.textFieldContainer.forEach((field) => {\n const notchData = this.notches.find((data) => data.container.contains(field));\n\n if (notchData) {\n TextFields.setupFieldEvents(field, notchData.container, notchData.notch);\n }\n });\n }\n\n private updateInitialNotchWidths() {\n this.notches.forEach(({ notch }) => {\n TextFields.setNotchWidth(notch, TextFields.getNotchWidth(notch));\n });\n }\n\n private static setupFieldEvents(field: HTMLInputElement | HTMLTextAreaElement, container: HTMLElement, notch: HTMLElement) {\n const fieldType = field instanceof HTMLTextAreaElement;\n const eventType = fieldType ? 'input' : 'change';\n\n field.addEventListener('focus', () => {\n container.classList.add(fieldType ? 'textarea--focused' : 'input--focused');\n TextFields.setNotchWidth(notch, TextFields.getNotchWidth(notch));\n });\n\n field.addEventListener('blur', () => {\n container.classList.remove(fieldType ? 'textarea--focused' : 'input--focused');\n TextFields.setNotchWidth(notch, field.value.trim() ? TextFields.getNotchWidth(notch) : 'auto');\n });\n\n field.addEventListener(eventType, () => {\n TextFields.updateStyles(field, container, fieldType);\n\n if (fieldType) {\n TextFields.resizeTextarea(field as HTMLTextAreaElement, container);\n }\n });\n\n TextFields.updateStyles(field, container, fieldType);\n }\n\n private static updateStyles(field: HTMLInputElement | HTMLTextAreaElement, container: HTMLElement, fieldType: boolean) {\n container.classList.toggle(fieldType ? 'textarea--filled' : 'input--filled', field.value.trim().length > 0);\n container.classList.toggle(fieldType ? 'textarea--disabled' : 'input--disabled', field.disabled);\n }\n\n private static resizeTextarea(field: HTMLTextAreaElement, container: HTMLElement) {\n if (container.classList.contains('textarea--auto-resizeable')) {\n const newField = field;\n\n newField.style.height = 'auto';\n newField.style.height = `${field.scrollHeight}px`;\n }\n }\n\n public updateField(field: HTMLInputElement | HTMLTextAreaElement) {\n const container = field.closest('.text-field-container') as HTMLElement;\n const notchData = this.notches.find((data) => data.container.contains(field));\n\n if (container) {\n TextFields.updateStyles(field, container, field instanceof HTMLTextAreaElement);\n\n if (notchData) {\n TextFields.setNotchWidth(notchData.notch, TextFields.getNotchWidth(notchData.notch));\n }\n }\n }\n\n public async init() {\n await new Promise<void>((resolve) => {\n setTimeout(() => {\n resolve();\n }, 0);\n });\n\n this.initializeElements();\n }\n}\n\nexport default TextFields;\n"],"names":[],"version":3,"file":"index.d.ts.map"}