infinite-agents-public/themed_hybrids/ui_hybrid_2.html

805 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Glass Morphism Input Intelligence</title>
<style>
:root {
--glass-bg: rgba(255, 255, 255, 0.1);
--glass-border: rgba(255, 255, 255, 0.2);
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
--glass-blur: blur(10px);
--text-primary: #1a1a1a;
--text-secondary: #666;
--accent-valid: #22c55e;
--accent-invalid: #ef4444;
--accent-info: #3b82f6;
--gradient-edge: linear-gradient(135deg,
rgba(255, 255, 255, 0.3) 0%,
rgba(255, 255, 255, 0.1) 100%);
}
@media (prefers-color-scheme: dark) {
:root {
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.1);
--glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
--text-primary: #f5f5f5;
--text-secondary: #a8a8a8;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
color: var(--text-primary);
}
main {
width: 100%;
max-width: 600px;
}
h1 {
text-align: center;
margin-bottom: 40px;
color: white;
font-weight: 300;
font-size: 2.5em;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
}
.input-intelligence {
position: relative;
margin-bottom: 40px;
}
.glass-container {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border-radius: 16px;
border: 1px solid var(--glass-border);
box-shadow: var(--glass-shadow);
padding: 24px;
position: relative;
overflow: hidden;
}
.glass-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 1px;
background: var(--gradient-edge);
}
.input-wrapper {
position: relative;
margin-bottom: 16px;
}
.glass-input {
width: 100%;
padding: 16px 48px 16px 16px;
font-size: 16px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--glass-border);
border-radius: 12px;
color: var(--text-primary);
backdrop-filter: blur(5px);
-webkit-backdrop-filter: blur(5px);
transition: all 0.3s ease;
outline: none;
}
.glass-input:focus {
background: rgba(255, 255, 255, 0.08);
border-color: rgba(255, 255, 255, 0.3);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.input-status {
position: absolute;
right: 16px;
top: 50%;
transform: translateY(-50%);
width: 24px;
height: 24px;
opacity: 0;
transition: opacity 0.3s ease;
}
.input-status.show {
opacity: 1;
}
.status-icon {
width: 100%;
height: 100%;
stroke-width: 2;
}
.status-icon.valid {
stroke: var(--accent-valid);
}
.status-icon.invalid {
stroke: var(--accent-invalid);
}
.status-icon.loading {
stroke: var(--accent-info);
animation: rotate 1s linear infinite;
}
@keyframes rotate {
to { transform: rotate(360deg); }
}
.help-tooltip {
position: absolute;
top: calc(100% + 8px);
left: 0;
right: 0;
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 12px 16px;
box-shadow: var(--glass-shadow);
opacity: 0;
transform: translateY(-10px);
transition: all 0.3s ease;
pointer-events: none;
z-index: 10;
}
.help-tooltip.show {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
.help-tooltip::before {
content: '';
position: absolute;
top: -6px;
left: 24px;
width: 12px;
height: 12px;
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-right: none;
border-bottom: none;
transform: rotate(45deg);
}
.help-text {
font-size: 14px;
color: var(--text-secondary);
line-height: 1.4;
}
.autocomplete-dropdown {
position: absolute;
top: calc(100% + 8px);
left: 0;
right: 0;
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 12px;
box-shadow: var(--glass-shadow);
opacity: 0;
transform: translateY(-10px) scale(0.98);
transition: all 0.3s ease;
pointer-events: none;
z-index: 20;
max-height: 240px;
overflow-y: auto;
}
.autocomplete-dropdown.show {
opacity: 1;
transform: translateY(0) scale(1);
pointer-events: auto;
}
.autocomplete-item {
padding: 12px 16px;
cursor: pointer;
transition: all 0.2s ease;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
position: relative;
overflow: hidden;
}
.autocomplete-item:last-child {
border-bottom: none;
}
.autocomplete-item::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.1);
transition: left 0.3s ease;
}
.autocomplete-item:hover::before,
.autocomplete-item.selected::before {
left: 0;
}
.autocomplete-item.selected {
background: rgba(255, 255, 255, 0.05);
}
.autocomplete-primary {
color: var(--text-primary);
font-weight: 500;
margin-bottom: 4px;
}
.autocomplete-secondary {
color: var(--text-secondary);
font-size: 12px;
}
.format-hint {
position: absolute;
top: 50%;
right: 48px;
transform: translateY(-50%);
font-size: 12px;
color: var(--text-secondary);
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.format-hint.show {
opacity: 0.7;
}
.demo-section {
margin-bottom: 32px;
}
.demo-title {
color: white;
font-size: 1.2em;
margin-bottom: 16px;
font-weight: 300;
}
.feature-list {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 16px;
font-size: 14px;
color: var(--text-secondary);
line-height: 1.6;
}
.feature-list li {
margin-left: 20px;
margin-bottom: 8px;
}
@media (max-width: 640px) {
h1 {
font-size: 2em;
}
.glass-container {
padding: 20px;
}
.glass-input {
font-size: 16px;
}
}
</style>
</head>
<body>
<main>
<h1>Input Intelligence - Glass Morphism Theme</h1>
<div class="demo-section">
<h2 class="demo-title">Email Input with Validation</h2>
<div class="input-intelligence">
<div class="glass-container">
<div class="input-wrapper">
<input
type="text"
class="glass-input"
id="emailInput"
placeholder="Enter your email address"
autocomplete="off"
>
<span class="format-hint" id="emailFormatHint"></span>
<div class="input-status" id="emailStatus">
<svg class="status-icon" viewBox="0 0 24 24" fill="none">
<!-- Dynamic SVG content inserted by JS -->
</svg>
</div>
</div>
<div class="help-tooltip" id="emailHelp">
<p class="help-text"></p>
</div>
<div class="autocomplete-dropdown" id="emailAutocomplete"></div>
</div>
</div>
</div>
<div class="demo-section">
<h2 class="demo-title">Phone Number with Smart Formatting</h2>
<div class="input-intelligence">
<div class="glass-container">
<div class="input-wrapper">
<input
type="text"
class="glass-input"
id="phoneInput"
placeholder="Enter phone number"
autocomplete="off"
>
<span class="format-hint" id="phoneFormatHint"></span>
<div class="input-status" id="phoneStatus">
<svg class="status-icon" viewBox="0 0 24 24" fill="none">
<!-- Dynamic SVG content inserted by JS -->
</svg>
</div>
</div>
<div class="help-tooltip" id="phoneHelp">
<p class="help-text"></p>
</div>
<div class="autocomplete-dropdown" id="phoneAutocomplete"></div>
</div>
</div>
</div>
<div class="demo-section">
<h2 class="demo-title">Credit Card with Security Features</h2>
<div class="input-intelligence">
<div class="glass-container">
<div class="input-wrapper">
<input
type="text"
class="glass-input"
id="cardInput"
placeholder="Enter card number"
autocomplete="off"
>
<span class="format-hint" id="cardFormatHint"></span>
<div class="input-status" id="cardStatus">
<svg class="status-icon" viewBox="0 0 24 24" fill="none">
<!-- Dynamic SVG content inserted by JS -->
</svg>
</div>
</div>
<div class="help-tooltip" id="cardHelp">
<p class="help-text"></p>
</div>
<div class="autocomplete-dropdown" id="cardAutocomplete"></div>
</div>
</div>
</div>
<div class="feature-list">
<ul>
<li>Real-time validation with visual feedback</li>
<li>Contextual help that adapts to input</li>
<li>Smart formatting as you type</li>
<li>Intelligent autocomplete suggestions</li>
<li>Glass morphism design with depth hierarchy</li>
<li>Light/dark mode adaptive styling</li>
</ul>
</div>
</main>
<script>
// SVG Icons
const icons = {
valid: '<circle cx="12" cy="12" r="10"></circle><path d="M9 12l2 2 4-4"></path>',
invalid: '<circle cx="12" cy="12" r="10"></circle><path d="M15 9l-6 6"></path><path d="M9 9l6 6"></path>',
loading: '<circle cx="12" cy="12" r="10" stroke-dasharray="50" stroke-dashoffset="10"></circle>'
};
// Input Intelligence Class
class InputIntelligence {
constructor(inputId, config) {
this.input = document.getElementById(inputId);
this.status = document.getElementById(inputId.replace('Input', 'Status'));
this.help = document.getElementById(inputId.replace('Input', 'Help'));
this.autocomplete = document.getElementById(inputId.replace('Input', 'Autocomplete'));
this.formatHint = document.getElementById(inputId.replace('Input', 'FormatHint'));
this.config = config;
this.selectedIndex = -1;
this.suggestions = [];
this.init();
}
init() {
this.input.addEventListener('input', this.handleInput.bind(this));
this.input.addEventListener('focus', this.handleFocus.bind(this));
this.input.addEventListener('blur', this.handleBlur.bind(this));
this.input.addEventListener('keydown', this.handleKeydown.bind(this));
this.autocomplete.addEventListener('click', this.handleAutocompleteClick.bind(this));
}
handleInput(e) {
const value = e.target.value;
// Clear previous timeout
clearTimeout(this.validationTimeout);
clearTimeout(this.autocompleteTimeout);
// Show loading state
this.showStatus('loading');
// Format input
if (this.config.formatter) {
const formatted = this.config.formatter(value);
if (formatted !== value) {
e.target.value = formatted;
this.showFormatHint(this.config.formatHint);
}
}
// Validate after delay
this.validationTimeout = setTimeout(() => {
this.validate(e.target.value);
}, 500);
// Show autocomplete after delay
if (value.length >= this.config.minLength) {
this.autocompleteTimeout = setTimeout(() => {
this.showAutocomplete(value);
}, 300);
} else {
this.hideAutocomplete();
}
}
handleFocus() {
if (this.input.value.length >= this.config.minLength) {
this.showAutocomplete(this.input.value);
}
this.showHelp(this.config.initialHelp);
}
handleBlur(e) {
// Delay to allow autocomplete clicks
setTimeout(() => {
if (!this.autocomplete.contains(e.relatedTarget)) {
this.hideAutocomplete();
this.hideHelp();
this.hideFormatHint();
}
}, 200);
}
handleKeydown(e) {
if (!this.autocomplete.classList.contains('show')) return;
const items = this.autocomplete.querySelectorAll('.autocomplete-item');
switch(e.key) {
case 'ArrowDown':
e.preventDefault();
this.selectedIndex = Math.min(this.selectedIndex + 1, items.length - 1);
this.updateSelection(items);
break;
case 'ArrowUp':
e.preventDefault();
this.selectedIndex = Math.max(this.selectedIndex - 1, -1);
this.updateSelection(items);
break;
case 'Enter':
if (this.selectedIndex >= 0 && this.selectedIndex < items.length) {
e.preventDefault();
this.selectSuggestion(this.suggestions[this.selectedIndex]);
}
break;
case 'Escape':
this.hideAutocomplete();
break;
}
}
handleAutocompleteClick(e) {
const item = e.target.closest('.autocomplete-item');
if (item) {
const index = Array.from(item.parentNode.children).indexOf(item);
this.selectSuggestion(this.suggestions[index]);
}
}
validate(value) {
if (!value) {
this.hideStatus();
this.showHelp(this.config.initialHelp);
return;
}
const result = this.config.validator(value);
if (result.valid) {
this.showStatus('valid');
this.showHelp(result.help || this.config.validHelp);
} else {
this.showStatus('invalid');
this.showHelp(result.help || this.config.invalidHelp);
}
}
showAutocomplete(value) {
this.suggestions = this.config.getSuggestions(value);
this.selectedIndex = -1;
if (this.suggestions.length === 0) {
this.hideAutocomplete();
return;
}
this.autocomplete.innerHTML = this.suggestions.map((suggestion, index) => `
<div class="autocomplete-item">
<div class="autocomplete-primary">${suggestion.primary}</div>
${suggestion.secondary ? `<div class="autocomplete-secondary">${suggestion.secondary}</div>` : ''}
</div>
`).join('');
this.autocomplete.classList.add('show');
}
hideAutocomplete() {
this.autocomplete.classList.remove('show');
this.selectedIndex = -1;
}
updateSelection(items) {
items.forEach((item, index) => {
item.classList.toggle('selected', index === this.selectedIndex);
});
}
selectSuggestion(suggestion) {
this.input.value = suggestion.value;
this.hideAutocomplete();
this.validate(suggestion.value);
this.input.focus();
}
showStatus(type) {
const icon = this.status.querySelector('.status-icon');
icon.innerHTML = icons[type];
icon.className = `status-icon ${type}`;
this.status.classList.add('show');
}
hideStatus() {
this.status.classList.remove('show');
}
showHelp(text) {
this.help.querySelector('.help-text').textContent = text;
this.help.classList.add('show');
}
hideHelp() {
this.help.classList.remove('show');
}
showFormatHint(text) {
this.formatHint.textContent = text;
this.formatHint.classList.add('show');
clearTimeout(this.formatHintTimeout);
this.formatHintTimeout = setTimeout(() => {
this.hideFormatHint();
}, 2000);
}
hideFormatHint() {
this.formatHint.classList.remove('show');
}
}
// Email configuration
const emailConfig = {
minLength: 3,
initialHelp: 'Enter a valid email address',
validHelp: 'Email address looks good!',
invalidHelp: 'Please enter a valid email format',
formatHint: 'Auto-formatted',
validator: (value) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return {
valid: emailRegex.test(value),
help: value.includes('@') && !value.includes('.') ?
'Don\'t forget the domain extension (e.g., .com)' : null
};
},
formatter: (value) => {
return value.toLowerCase().replace(/\s/g, '');
},
getSuggestions: (value) => {
const domains = [
{ domain: 'gmail.com', provider: 'Google' },
{ domain: 'outlook.com', provider: 'Microsoft' },
{ domain: 'yahoo.com', provider: 'Yahoo' },
{ domain: 'icloud.com', provider: 'Apple' },
{ domain: 'protonmail.com', provider: 'ProtonMail' }
];
if (value.includes('@') && !value.split('@')[1]) {
const [username] = value.split('@');
return domains.map(d => ({
primary: `${username}@${d.domain}`,
secondary: `${d.provider} email`,
value: `${username}@${d.domain}`
}));
}
return [];
}
};
// Phone configuration
const phoneConfig = {
minLength: 3,
initialHelp: 'Enter a phone number',
validHelp: 'Phone number is valid',
invalidHelp: 'Please enter a valid phone number',
formatHint: 'Auto-formatted',
validator: (value) => {
const digits = value.replace(/\D/g, '');
return {
valid: digits.length >= 10 && digits.length <= 15,
help: digits.length < 10 ? 'Phone number too short' :
digits.length > 15 ? 'Phone number too long' : null
};
},
formatter: (value) => {
const digits = value.replace(/\D/g, '');
if (digits.length <= 3) return digits;
if (digits.length <= 6) return `(${digits.slice(0, 3)}) ${digits.slice(3)}`;
if (digits.length <= 10) return `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`;
return `+${digits.slice(0, digits.length - 10)} (${digits.slice(-10, -7)}) ${digits.slice(-7, -4)}-${digits.slice(-4)}`;
},
getSuggestions: (value) => {
const countryCodes = [
{ code: '+1', country: 'United States / Canada' },
{ code: '+44', country: 'United Kingdom' },
{ code: '+33', country: 'France' },
{ code: '+49', country: 'Germany' },
{ code: '+81', country: 'Japan' }
];
if (value === '+' || value.startsWith('+')) {
return countryCodes
.filter(c => c.code.startsWith(value))
.map(c => ({
primary: c.code,
secondary: c.country,
value: c.code + ' '
}));
}
return [];
}
};
// Credit card configuration
const cardConfig = {
minLength: 4,
initialHelp: 'Enter your credit card number',
validHelp: 'Card number is valid',
invalidHelp: 'Please enter a valid card number',
formatHint: 'Formatting applied',
validator: (value) => {
const digits = value.replace(/\s/g, '');
// Basic Luhn algorithm check
if (digits.length < 13 || digits.length > 19) {
return { valid: false };
}
let sum = 0;
let isEven = false;
for (let i = digits.length - 1; i >= 0; i--) {
let digit = parseInt(digits[i]);
if (isEven) {
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
isEven = !isEven;
}
return {
valid: sum % 10 === 0,
help: digits.length < 16 ? 'Card number incomplete' : null
};
},
formatter: (value) => {
const digits = value.replace(/\s/g, '');
const groups = [];
// Detect card type and format accordingly
if (digits.startsWith('34') || digits.startsWith('37')) {
// American Express: 4-6-5
groups.push(digits.slice(0, 4), digits.slice(4, 10), digits.slice(10, 15));
} else {
// Others: 4-4-4-4
for (let i = 0; i < digits.length; i += 4) {
groups.push(digits.slice(i, i + 4));
}
}
return groups.filter(g => g).join(' ');
},
getSuggestions: (value) => {
const cardTypes = [
{ prefix: '4', type: 'Visa', icon: '💳' },
{ prefix: '5', type: 'Mastercard', icon: '💳' },
{ prefix: '34', type: 'American Express', icon: '💳' },
{ prefix: '37', type: 'American Express', icon: '💳' },
{ prefix: '6', type: 'Discover', icon: '💳' }
];
const digits = value.replace(/\s/g, '');
if (digits.length <= 2) {
return cardTypes
.filter(c => c.prefix.startsWith(digits))
.map(c => ({
primary: `${c.icon} ${c.type}`,
secondary: `Starts with ${c.prefix}`,
value: c.prefix
}));
}
return [];
}
};
// Initialize all inputs
new InputIntelligence('emailInput', emailConfig);
new InputIntelligence('phoneInput', phoneConfig);
new InputIntelligence('cardInput', cardConfig);
</script>
</body>
</html>