ai-mirror-nebula / components /lockscreen.js
Logistics's picture
<!DOCTYPE html>
041c6bd verified
class CustomLockscreen extends HTMLElement {
connectedCallback() {
this.attachShadow({ mode: 'open' });
this.shadowRoot.innerHTML = `
<style>
:host {
display: block;
position: fixed;
inset: 0;
z-index: 70;
background: var(--background);
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
transition: opacity 0.3s ease;
}
.lockscreen {
background: linear-gradient(135deg, rgba(79, 70, 229, 0.1), rgba(236, 72, 153, 0.1));
backdrop-filter: blur(20px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 2rem;
padding: 3rem 2rem;
text-align: center;
max-width: 90%;
width: 400px;
}
.unlock-btn {
background: linear-gradient(135deg, var(--primary), var(--secondary));
color: white;
border: none;
border-radius: 1rem;
padding: 1rem 2rem;
font-weight: 600;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.unlock-btn::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
transform: translateX(-100%);
transition: transform 0.5s ease;
}
.unlock-btn:hover::before {
transform: translateX(100%);
}
.pattern-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
margin: 2rem 0;
}
.pattern-dot {
width: 20px;
height: 20px;
background: var(--subtle);
border-radius: 50%;
transition: all 0.3s ease;
cursor: pointer;
}
.pattern-dot.selected {
background: var(--accent);
transform: scale(1.2);
}
.pattern-line {
position: absolute;
background: var(--accent);
height: 4px;
border-radius: 2px;
}
.time-display {
font-size: 4rem;
font-weight: 300;
letter-spacing: -2px;
}
.date-display {
font-size: 1.2rem;
color: var(--subtle);
margin-bottom: 2rem;
}
.hint-text {
color: var(--subtle);
font-size: 0.9rem;
margin-top: 1rem;
}
.lock-icon {
width: 60px;
height: 60px;
background: linear-gradient(135deg, var(--primary), var(--secondary));
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 2rem;
}
</style>
<div class="lockscreen p-8">
<div class="lock-icon mb-6">
<i data-feather="lock" class="w-8 h-8 text-white"></i>
</div>
<div class="time-display mb-2">00:00</div>
<div class="date-display mb-8">Loading...</div>
<div class="pattern-grid relative">
${Array.from({ length: 9 }, (_, i) => `
<div class="pattern-dot" data-dot="${i}"></div>
`).join('')}
</div>
<p class="hint-text mt-6">Draw your pattern to unlock</p>
</div>
`;
this.initTime();
this.initPatternLock();
}
initTime() {
const timeElement = this.shadowRoot.querySelector('.time-display');
const dateElement = this.shadowRoot.querySelector('.date-display');
const updateTime = () => {
const now = new Date();
const timeString = now.toLocaleTimeString('en-US', {
hour12: false,
hour: '2-digit',
minute: '2-digit'
});
const dateString = now.toLocaleDateString('en-US', {
weekday: 'long',
year: 'numeric',
month: 'long',
day: 'numeric'
});
timeElement.textContent = timeString;
dateElement.textContent = dateString;
};
updateTime();
setInterval(updateTime, 1000);
}
initPatternLock() {
const dots = this.shadowRoot.querySelectorAll('.pattern-dot');
let selectedDots = [];
const correctPattern = [0, 3, 6, 7, 8];
dots.forEach((dot, index) => {
dot.addEventListener('click', () => {
if (!selectedDots.includes(index)) {
selectedDots.push(index);
dot.classList.add('selected');
// Draw lines between selected dots
if (selectedDots.length > 1) {
const prevIndex = selectedDots[selectedDots.length - 2];
const prevDot = dots[prevIndex];
const line = document.createElement('div');
line.className = 'pattern-line';
// Calculate line position and dimensions
const rect1 = prevDot.getBoundingClientRect();
const rect2 = dot.getBoundingClientRect();
const x1 = rect1.left + rect1.width / 2;
const y1 = rect1.top + rect1.height / 2;
const x2 = rect2.left + rect2.width / 2;
const y2 = rect2.top + rect2.height / 2;
const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2);
const angle = Math.atan2(y2 - y1, x2 - x1);
line.style.width = `${length}px`;
line.style.transform = `rotate(${angle}rad)';
line.style.left = `${x1}px`;
line.style.top = `${y1}px';
this.shadowRoot.querySelector('.pattern-grid').appendChild(line);
}
// Check if pattern is complete
if (selectedDots.length === correctPattern.length) {
if (JSON.stringify(selectedDots) === JSON.stringify(correctPattern)) {
this.toggleLock();
} else {
// Wrong pattern - reset
setTimeout(() => {
this.resetPattern();
}, 500);
}
}
});
});
// Reset pattern on mouse up (for mobile)
document.addEventListener('mouseup', () => this.resetPattern());
document.addEventListener('touchend', () => this.resetPattern());
}
resetPattern() {
const dots = this.shadowRoot.querySelectorAll('.pattern-dot');
const lines = this.shadowRoot.querySelectorAll('.pattern-line');
lines.forEach(line => line.remove());
dots.forEach(dot => dot.classList.remove('selected'));
selectedDots = [];
}
toggleLock() {
if (this.style.display === 'none') {
this.style.display = 'flex';
document.body.style.overflow = 'hidden';
} else {
this.style.display = 'none';
document.body.style.overflow = '';
// Remove all pattern lines
this.shadowRoot.querySelectorAll('.pattern-line').forEach(line => line.remove());
selectedDots = [];
}
}
}
customElements.define('custom-lockscreen', CustomLockscreen);