neumorphic-peripheral
Version:
A lightweight, framework-agnostic JavaScript/TypeScript library for beautiful neumorphic styling
218 lines (217 loc) • 8.21 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.InputComponent = void 0;
exports.input = input;
const base_1 = require("./base");
const utils_1 = require("../utils");
const validators_1 = require("../validators");
class InputComponent extends base_1.BaseComponent {
constructor(element, config = {}) {
if (!(element instanceof HTMLInputElement)) {
throw new Error('Input component requires an HTMLInputElement');
}
super(element, config);
this._validationResult = null;
this._inputConfig = {
validateOn: 'blur',
showValidation: true,
...config
};
this._debouncedValidate = (0, utils_1.debounce)(() => this.performValidation(), 300);
}
get inputElement() {
return this._element;
}
init() {
this.applyBaseStyles();
this.applyInputStyles();
this.setupValidation();
}
bindEvents() {
super.bindEvents();
// Focus events
this.addEventListener(this._element, 'focus', this.handleFocus.bind(this));
this.addEventListener(this._element, 'blur', this.handleBlur.bind(this));
// Input validation events
if (this._inputConfig.validateOn === 'change') {
this.addEventListener(this._element, 'input', this._debouncedValidate);
}
else if (this._inputConfig.validateOn === 'blur') {
this.addEventListener(this._element, 'blur', () => this.performValidation());
}
// Custom input event
this.addEventListener(this._element, 'input', () => {
this.emit('input', { value: this.getValue() });
});
}
applyInputStyles() {
(0, utils_1.addClassName)(this._element, 'input');
// Core input styles
this._element.style.boxShadow = this.createShadowStyle('inset');
this._element.style.padding = '12px 16px';
this._element.style.fontSize = '16px';
this._element.style.lineHeight = '1.5';
this._element.style.width = this._element.style.width || '100%';
this._element.style.boxSizing = 'border-box';
// Placeholder styling
this.applyPlaceholderStyles();
// Set up hover and focus effects
this.setupInteractionEffects();
}
applyPlaceholderStyles() {
if (this._inputConfig.placeholder) {
this.inputElement.placeholder = this._inputConfig.placeholder;
}
// Create placeholder color CSS if it doesn't exist
const placeholderColor = `color: ${this._theme.colors.textSecondary}; opacity: 1;`;
const styleId = 'np-placeholder-styles';
if (!document.getElementById(styleId)) {
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
.np-input::placeholder {
${placeholderColor}
}
.np-input::-webkit-input-placeholder {
${placeholderColor}
}
.np-input::-moz-placeholder {
${placeholderColor}
}
.np-input:-ms-input-placeholder {
${placeholderColor}
}
`;
document.head.appendChild(style);
}
}
setupInteractionEffects() {
const originalShadow = this.createShadowStyle('inset');
const focusShadow = `${this.createShadowStyle('inset')}, 0 0 0 2px ${this._theme.colors.accent}40`;
this.addEventListener(this._element, 'mouseenter', () => {
if (!this._config.disabled && !this.inputElement.matches(':focus')) {
this._element.style.boxShadow = this.createHoverShadowStyle('inset');
}
});
this.addEventListener(this._element, 'mouseleave', () => {
if (!this.inputElement.matches(':focus')) {
this._element.style.boxShadow = originalShadow;
}
});
}
handleFocus() {
(0, utils_1.addClassName)(this._element, 'focused');
this._element.style.boxShadow = `${this.createShadowStyle('inset')}, 0 0 0 2px ${this._theme.colors.accent}40`;
this.emit('focus');
}
handleBlur() {
this._element.classList.remove('np-focused');
this._element.style.boxShadow = this.createShadowStyle('inset');
this.emit('blur');
}
setupValidation() {
if (!this._inputConfig.validate)
return;
// Set up ARIA attributes for accessibility
this._element.setAttribute('aria-invalid', 'false');
if (this._inputConfig.errorMessage) {
const errorId = `error-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
this._element.setAttribute('aria-describedby', errorId);
}
}
performValidation() {
if (!this._inputConfig.validate || !this._inputConfig.showValidation)
return;
const value = this.getValue();
this._validationResult = (0, validators_1.validateValue)(value, this._inputConfig.validate);
// Update global validation manager
validators_1.globalValidationManager.validate(this._element, this._inputConfig.validate);
// Update visual state
this.updateValidationState();
// Emit validation event
this.emit('validation', {
result: this._validationResult,
value
});
return this._validationResult;
}
updateValidationState() {
if (!this._validationResult)
return;
// Update ARIA attributes
this._element.setAttribute('aria-invalid', this._validationResult.isValid ? 'false' : 'true');
if (this._validationResult.isValid) {
this._element.classList.remove('np-error');
this._element.style.boxShadow = this.createShadowStyle('inset');
}
else {
(0, utils_1.addClassName)(this._element, 'error');
this._element.style.boxShadow = `${this.createShadowStyle('inset')}, 0 0 0 2px ${this._theme.colors.error}40`;
}
}
// Public API methods
validate() {
return this.performValidation() || { isValid: true, errors: [] };
}
getValue() {
return (0, utils_1.getElementValue)(this._element);
}
setValue(value) {
(0, utils_1.setElementValue)(this._element, value);
this.emit('input', { value });
// Trigger validation if configured
if (this._inputConfig.validateOn === 'change') {
this._debouncedValidate();
}
}
clearErrors() {
this._validationResult = null;
validators_1.globalValidationManager.clearValidation(this._element);
this._element.classList.remove('np-error');
this._element.setAttribute('aria-invalid', 'false');
this._element.style.boxShadow = this.createShadowStyle('inset');
}
focus() {
this.inputElement.focus();
}
blur() {
this.inputElement.blur();
}
select() {
this.inputElement.select();
}
isValid() {
return this._validationResult ? this._validationResult.isValid : true;
}
getValidationResult() {
return this._validationResult;
}
onUpdate(newConfig) {
const oldConfig = { ...this._inputConfig };
this._inputConfig = { ...this._inputConfig, ...newConfig };
// Update placeholder
if (newConfig.placeholder !== oldConfig.placeholder) {
this.applyPlaceholderStyles();
}
// Update validation configuration
if (newConfig.validate !== oldConfig.validate) {
this.clearErrors();
if (newConfig.validate) {
this.setupValidation();
}
}
// Update validation timing
if (newConfig.validateOn !== oldConfig.validateOn) {
// Re-bind validation events
this.bindEvents();
}
}
onDestroy() {
validators_1.globalValidationManager.clearValidation(this._element);
}
}
exports.InputComponent = InputComponent;
// Factory function for easy usage
function input(element, config = {}) {
return new InputComponent(element, config);
}