Lachlan McMurtrie
The app is a collaborative component builder. it begin by starting a project, entering in a description. the next page will be an ai assisted project scope, where the user will type the component type, which will be a list of core components (all that are core across ui domains). The ai will then ask for a framework of native react or react for web. the behavioral hooks, interactions and utilites that align with the component will then be showed. for web, these must be all react-aria, for native, these will be react-native. assume react-shared types for cross platform as common, as well as react-stately for states which are also cross platform. provide the user with a the data attributes as per react-aria or native equivilant documentation, ask them how they want to recieve their code files - combined as typed primitives, or separated through the use of an adapter that exports the behaviors etc to the primitive.
499a14d verified
Raw
History Blame Contribute Delete
5.43 kB
// Shared utility functions for the application
// Theme management
class ThemeManager {
constructor() {
this.currentTheme = 'dark';
this.init();
}
init() {
// Check for saved theme preference or respect OS preference
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
this.setTheme(savedTheme);
} else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
this.setTheme('dark');
}
}
setTheme(theme) {
this.currentTheme = theme;
document.documentElement.className = theme;
localStorage.setItem('theme', theme);
}
toggleTheme() {
this.setTheme(this.currentTheme === 'dark' ? 'light' : 'dark');
}
}
// Initialize theme manager
const themeManager = new ThemeManager();
// Form validation helpers
class FormValidator {
static validateRequired(value) {
return value && value.trim().length > 0;
}
static validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
static showError(input, message) {
const errorElement = input.nextElementSibling;
if (errorElement && errorElement.classList.contains('error-message')) {
errorElement.textContent = message;
} else {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message text-red-400 text-sm mt-1';
errorDiv.textContent = message;
input.parentNode.insertBefore(errorDiv, input.nextSibling);
}
input.classList.add('border-red-500');
}
static clearError(input) {
const errorElement = input.nextElementSibling;
if (errorElement && errorElement.classList.contains('error-message')) {
errorElement.remove();
}
input.classList.remove('border-red-500');
}
}
// API service for AI interactions
class AIService {
static async generateComponentScope(description, componentType) {
// Mock AI service - in real implementation, this would call your backend
return new Promise((resolve) => {
setTimeout(() => {
const mockResponse = {
frameworkOptions: ['React Web', 'React Native'],
suggestedHooks: this.getSuggestedHooks(componentType),
dataAttributes: this.getDataAttributes(componentType),
codeStructure: ['Typed Primitives', 'Adapter Pattern']
};
resolve(mockResponse);
}, 1500);
});
}
static getSuggestedHooks(componentType) {
const hooksMap = {
'button': ['useButton', 'usePress', 'useFocusRing'],
'input': ['useTextField', 'useFocus', 'useKeyboard'],
'select': ['useSelect', 'useListBox', 'useOverlay'],
'modal': ['useDialog', 'useOverlay', 'useModal'],
'menu': ['useMenu', 'useMenuItem', 'useSubmenu'],
'tabs': ['useTab', 'useTabList', 'useTabPanel'],
'accordion': ['useAccordion', 'useAccordionItem'],
'slider': ['useSlider', 'useSliderThumb'],
'switch': ['useSwitch'],
'checkbox': ['useCheckbox', 'useCheckboxGroup'],
'radio': ['useRadio', 'useRadioGroup']
};
return hooksMap[componentType] || ['useId', 'useFocus', 'useKeyboard'];
}
static getDataAttributes(componentType) {
const attributesMap = {
'button': ['data-focused', 'data-pressed', 'data-disabled', 'data-loading'],
'input': ['data-focused', 'data-invalid', 'data-disabled', 'data-readonly'],
'select': ['data-focused', 'data-open', 'data-disabled', 'data-invalid'],
'modal': ['data-open', 'data-placement', 'data-size'],
'menu': ['data-open', 'data-focused', 'data-disabled'],
'tabs': ['data-selected', 'data-disabled', 'data-orientation'],
'accordion': ['data-expanded', 'data-disabled'],
'slider': ['data-dragging', 'data-focused', 'data-disabled'],
'switch': ['data-selected', 'data-focused', 'data-disabled'],
'checkbox': ['data-selected', 'data-indeterminate', 'data-focused', 'data-disabled'],
'radio': ['data-selected', 'data-focused', 'data-disabled']
};
return attributesMap[componentType] || ['data-component'];
}
}
// Local storage management
class StorageManager {
static getProjectData() {
return {
name: localStorage.getItem('projectName'),
description: localStorage.getItem('projectDescription'),
scope: JSON.parse(localStorage.getItem('projectScope') || '{}'),
framework: localStorage.getItem('selectedFramework'),
codeStructure: localStorage.getItem('selectedCodeStructure')
};
}
static clearProjectData() {
localStorage.removeItem('projectName');
localStorage.removeItem('projectDescription');
localStorage.removeItem('projectScope');
localStorage.removeItem('selectedFramework');
localStorage.removeItem('selectedCodeStructure');
}
}
// Export for use in other files
window.ThemeManager = ThemeManager;
window.FormValidator = FormValidator;
window.AIService = AIService;
window.StorageManager = StorageManager;