svarog-ui-core
Version:
Core components of Svarog UI without themes
215 lines (187 loc) • 6.52 kB
JavaScript
// src/components/ContactInfo/ContactInfo.js
import {
createElement,
validateProps,
createComponent,
} from '../../utils/componentFactory.js';
import { createBaseComponent } from '../../utils/baseComponent.js';
import { createStyleInjector } from '../../utils/styleInjection.js';
import { contactInfoStyles } from './ContactInfo.styles.js';
// Create style injector for ContactInfo component
const injectContactInfoStyles = createStyleInjector('ContactInfo');
/**
* Migrates legacy prop names to standardized prop names.
* @param {Object} props - The component props
* @returns {Object} - The normalized props
*/
const migrateLegacyProps = (props) => {
const migrated = { ...props };
// Add migration handlers if needed in the future
return migrated;
};
/**
* Creates ContactInfo DOM element
* @param {Object} state - ContactInfo state
* @returns {HTMLElement} - ContactInfo element
*/
const renderContactInfo = (state) => {
console.log('renderContactInfo called - injecting styles');
// Inject styles on render
injectContactInfoStyles(contactInfoStyles);
console.log('Styles injected in render, checking DOM...');
const injectedStyle = document.querySelector('[data-svarog="contactinfo"]');
console.log('Style found in DOM:', !!injectedStyle);
// Inject styles on render
injectContactInfoStyles(contactInfoStyles);
// Create the contact info container
const contactInfoClasses = ['contact-info'];
if (state.className) {
contactInfoClasses.push(state.className);
}
const contactInfoElement = createElement('div', {
classes: contactInfoClasses,
});
// Create location element
const locationHref = `#${state.locationId}`;
const locationLink = createElement('a', {
classes: ['contact-info__item', 'contact-info__item--location'],
attributes: {
href: locationHref,
title: `Go to ${state.location} location`,
'data-href': locationHref,
},
events: state.onLocationClick
? {
click: (event) => {
if (state.onLocationClick(event) === false) {
event.preventDefault();
}
},
}
: {},
children: [
createElement('span', {
classes: ['contact-info__icon', 'contact-info__icon--location'],
}),
createElement('span', {
classes: ['contact-info__text'],
text: state.location,
}),
],
});
// Create phone element
const cleanPhone = state.phone.replace(/[\s()/-]/g, '');
const phoneHref = `tel:${cleanPhone}`;
const phoneLink = createElement('a', {
classes: ['contact-info__item', 'contact-info__item--phone'],
attributes: {
href: phoneHref,
title: `Call ${state.phone}`,
'data-href': phoneHref,
},
events: state.onPhoneClick
? {
click: (event) => {
if (state.onPhoneClick(event) === false) {
event.preventDefault();
}
},
}
: {},
children: [
createElement('span', {
classes: ['contact-info__icon', 'contact-info__icon--phone'],
}),
createElement('span', {
classes: ['contact-info__text'],
text: state.phone,
}),
],
});
// Create email element
const emailHref = `mailto:${state.email}`;
const emailLink = createElement('a', {
classes: ['contact-info__item', 'contact-info__item--email'],
attributes: {
href: emailHref,
title: `Email ${state.email}`,
'data-href': emailHref,
},
events: state.onEmailClick
? {
click: (event) => {
if (state.onEmailClick(event) === false) {
event.preventDefault();
}
},
}
: {},
children: [
createElement('span', {
classes: ['contact-info__icon', 'contact-info__icon--email'],
}),
createElement('span', {
classes: ['contact-info__text'],
text: state.email,
}),
],
});
// Append all elements to the container
contactInfoElement.appendChild(locationLink);
contactInfoElement.appendChild(phoneLink);
contactInfoElement.appendChild(emailLink);
return contactInfoElement;
};
/**
* Creates a ContactInfo component
* @param {Object} props - ContactInfo properties
* @param {string} props.location - Location address text
* @param {string} props.phone - Phone number
* @param {string} props.email - Email address
* @param {string} [props.locationId="location"] - ID of the location section to scroll to
* @param {Function} [props.onLocationClick] - Click handler for location link
* @param {Function} [props.onPhoneClick] - Click handler for phone link
* @param {Function} [props.onEmailClick] - Click handler for email link
* @param {string} [props.className=""] - Additional CSS classes for the container element
* @returns {Object} ContactInfo component API
*/
const createContactInfo = (props) => {
console.log('createContactInfo called - NOT injecting styles here');
// Migrate any legacy props to standardized props
const normalizedProps = migrateLegacyProps(props);
// Validate required props
validateProps(normalizedProps, createContactInfo.requiredProps);
// Initial state with defaults
const initialState = {
location: normalizedProps.location,
phone: normalizedProps.phone,
email: normalizedProps.email,
locationId: normalizedProps.locationId || 'location',
onLocationClick: normalizedProps.onLocationClick || null,
onPhoneClick: normalizedProps.onPhoneClick || null,
onEmailClick: normalizedProps.onEmailClick || null,
className: normalizedProps.className || '',
};
// Create the base component
const contactInfoComponent =
createBaseComponent(renderContactInfo)(initialState);
// Define the shouldRerender method to ensure DOM updates on state changes
contactInfoComponent.shouldRerender = () => true;
// Add convenience methods
contactInfoComponent.setLocation = function (location) {
return this.update({ location });
};
contactInfoComponent.setPhone = function (phone) {
return this.update({ phone });
};
contactInfoComponent.setEmail = function (email) {
return this.update({ email });
};
return contactInfoComponent;
};
// Define required props for validation
createContactInfo.requiredProps = ['location', 'phone', 'email'];
// Create the component
const ContactInfo = createComponent('ContactInfo', createContactInfo);
// Export as a factory function
export default ContactInfo;