-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.js
More file actions
134 lines (122 loc) · 5.13 KB
/
main.js
File metadata and controls
134 lines (122 loc) · 5.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
const projects = [
{ title: 'Calculator', desc: 'Basic calculator with keyboard support.', path: 'calculator/index.html' },
{ title: 'Bookmark', desc: 'Save and manage quick links locally.', path: 'Bookmark/index.html' },
{ title: 'RandomPasswordGenerator', desc: 'Basic Random Password Generator', path: 'RandomPasswordGenerator/index.html' },
{ title: 'Employee App', desc: 'Simple CRUD demo for employees.', path: 'EmployeeApp/index.html' },
{ title: 'Memory Game', desc: 'Classic card matching game.', path: 'Memory_Game/index.html' },
{ title: 'Movie App', desc: 'Search and browse movies.', path: 'Movie_APP/index.html' }
];
// Utility: sanitize text for search
const normalize = s => s.toLowerCase();
// Render project cards
function renderProjects(list) {
const grid = document.getElementById('projectsGrid');
grid.innerHTML = '';
if (!list.length) {
grid.innerHTML = '<div class="col-12"><div class="alert alert-info mb-0">No projects found.</div></div>';
return;
}
list.forEach(p => {
const col = document.createElement('div');
col.className = 'col-12 col-sm-6 col-lg-4';
col.innerHTML = `
<div class="card app-card h-100">
<div class="card-body d-flex flex-column">
<h5 class="card-title mb-1">${escapeHtml(p.title)}</h5>
<p class="card-text text-muted small mb-3">${escapeHtml(p.desc)}</p>
<div class="mt-auto d-flex justify-content-between align-items-center">
<a href="${encodeURI(p.path)}" class="btn btn-outline-primary btn-sm" aria-label="Open ${escapeHtml(p.title)}">Open</a>
<button class="btn btn-sm btn-outline-secondary copy-btn" data-path="${encodeURI(p.path)}" type="button" title="Copy path">Copy path</button>
</div>
</div>
</div>
`;
grid.appendChild(col);
});
}
// Simple HTML escaper for safety
function escapeHtml(str) {
return String(str)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
}
// Live filter
function filterProjects(query) {
const q = normalize(query.trim());
if (!q) return projects.slice();
return projects.filter(p => normalize(p.title).includes(q) || normalize(p.desc).includes(q));
}
document.addEventListener('DOMContentLoaded', () => {
const search = document.getElementById('search');
const clearBtn = document.getElementById('clearSearch');
// Initial render
renderProjects(projects);
// Search input handler
search.addEventListener('input', () => {
const val = search.value;
const results = filterProjects(val);
renderProjects(results);
clearBtn.classList.toggle('d-none', !val);
});
// Clear button
clearBtn.addEventListener('click', () => {
search.value = '';
search.dispatchEvent(new Event('input'));
search.focus();
clearBtn.classList.add('d-none');
});
// Keyboard shortcut: press "/" to focus search (if not typing in an input)
document.addEventListener('keydown', (e) => {
if (e.key === '/' && !['INPUT','TEXTAREA'].includes(document.activeElement.tagName)) {
e.preventDefault();
search.focus();
search.select();
}
});
// Smooth scroll for internal links
document.querySelectorAll('a[href^="#"]').forEach(link => {
link.addEventListener('click', (e) => {
const target = document.querySelector(link.getAttribute('href'));
if (target) {
e.preventDefault();
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
});
});
// Copy path buttons (event delegation)
document.getElementById('projectsGrid').addEventListener('click', (e) => {
const btn = e.target.closest('.copy-btn');
if (!btn) return;
const path = decodeURI(btn.getAttribute('data-path') || '');
if (navigator.clipboard) {
navigator.clipboard.writeText(path).then(() => {
btn.textContent = 'Copied';
setTimeout(() => btn.textContent = 'Copy path', 1200);
}, () => {
alert('Unable to copy to clipboard.');
});
} else {
// Fallback
const ta = document.createElement('textarea');
ta.value = path;
document.body.appendChild(ta);
ta.select();
try { document.execCommand('copy'); btn.textContent = 'Copied'; }
catch { alert('Unable to copy to clipboard.'); }
document.body.removeChild(ta);
setTimeout(() => btn.textContent = 'Copy path', 1200);
}
});
// Optional: allow Enter in search to open first result
search.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
const firstLink = document.querySelector('#projectsGrid a.btn');
if (firstLink) {
firstLink.click();
}
}
});
});