@digital-ai/dot-illustrations 2.0.21 → 2.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/demo/demo.css +37 -104
- package/demo/index.html +51 -13
- package/demo/script.js +286 -135
- package/index.css +130 -130
- package/package.json +1 -1
package/demo/script.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
// Make currentTab and root globally accessible
|
|
2
|
+
const root = document.documentElement;
|
|
3
|
+
let currentTab = 'all';
|
|
4
|
+
|
|
1
5
|
function myFunction() {
|
|
2
6
|
var input, filter, divs, i, div, txtValue;
|
|
3
7
|
input = document.getElementById('myInput');
|
|
@@ -15,114 +19,6 @@ function myFunction() {
|
|
|
15
19
|
}
|
|
16
20
|
}
|
|
17
21
|
|
|
18
|
-
function doMagic() {
|
|
19
|
-
const list = document.getElementById("illustration-list");
|
|
20
|
-
const illustrations = [
|
|
21
|
-
// Global arrange from A-Z
|
|
22
|
-
"add-new",
|
|
23
|
-
"ai",
|
|
24
|
-
"add-new-grid",
|
|
25
|
-
"add-team",
|
|
26
|
-
"add-user",
|
|
27
|
-
"assets",
|
|
28
|
-
"analysis-circle",
|
|
29
|
-
"community",
|
|
30
|
-
"chart",
|
|
31
|
-
"custom-dashboards",
|
|
32
|
-
"dependency-down",
|
|
33
|
-
"dependency-up",
|
|
34
|
-
"disconnected",
|
|
35
|
-
"done",
|
|
36
|
-
"dora-metrics-circle",
|
|
37
|
-
"download",
|
|
38
|
-
"empty",
|
|
39
|
-
"error-fourhundredfour",
|
|
40
|
-
"error-fivehundred",
|
|
41
|
-
"error-fivehundredthree",
|
|
42
|
-
"error-fourhundredthree",
|
|
43
|
-
"favorite",
|
|
44
|
-
"features",
|
|
45
|
-
"folder-versioning",
|
|
46
|
-
"forbiden-access",
|
|
47
|
-
"filter-empty",
|
|
48
|
-
"launch",
|
|
49
|
-
"no-files",
|
|
50
|
-
"no-filters",
|
|
51
|
-
"nothing-defined",
|
|
52
|
-
"password-token",
|
|
53
|
-
"protection-in-progress",
|
|
54
|
-
"reports",
|
|
55
|
-
"scan-document",
|
|
56
|
-
"survey",
|
|
57
|
-
"user",
|
|
58
|
-
"ui-ux-improvements",
|
|
59
|
-
"upload",
|
|
60
|
-
"work-item",
|
|
61
|
-
"workflows-main",
|
|
62
|
-
"research",
|
|
63
|
-
"commitrepo",
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
// Dashboards arrange from A-Z
|
|
67
|
-
"analysis",
|
|
68
|
-
"automation",
|
|
69
|
-
"burn-down",
|
|
70
|
-
"burn-up",
|
|
71
|
-
"change-credit-score",
|
|
72
|
-
"change-data-quality",
|
|
73
|
-
"change-executive",
|
|
74
|
-
"change-impact-detection",
|
|
75
|
-
"change-impact-hotspots",
|
|
76
|
-
"change-failure-factors",
|
|
77
|
-
"change-failure-prediction",
|
|
78
|
-
"CRP-failure-prediction-monitoring",
|
|
79
|
-
"custom",
|
|
80
|
-
"defect",
|
|
81
|
-
"dependency",
|
|
82
|
-
"dora",
|
|
83
|
-
"executive",
|
|
84
|
-
"change-impact",
|
|
85
|
-
"item",
|
|
86
|
-
"late-task",
|
|
87
|
-
"onboarding",
|
|
88
|
-
"data-extraction",
|
|
89
|
-
"workflow"
|
|
90
|
-
];
|
|
91
|
-
illustrations.forEach(illustration => {
|
|
92
|
-
const div = document.createElement("div");
|
|
93
|
-
div.setAttribute("class", "copy-container");
|
|
94
|
-
div.innerHTML = `
|
|
95
|
-
<img class="${illustration} light"></img>
|
|
96
|
-
<!-- Inner div wrapping only the buttons -->
|
|
97
|
-
<!-- Button for copying ID -->
|
|
98
|
-
<div class="button-group">
|
|
99
|
-
<button id="CopyID" class="button" onmouseover="showTooltip(this)" onmouseout="hideTooltip(this)"
|
|
100
|
-
onclick="copyText(this)">Copy ID
|
|
101
|
-
<span class="tooltiptext">Copy ID</span>
|
|
102
|
-
</button>
|
|
103
|
-
<!-- Button for copying Code -->
|
|
104
|
-
<button class="button" onmouseover="showTooltip(this)" onmouseout="hideTooltip(this)" onclick="copyCode(this)">
|
|
105
|
-
Copy Code
|
|
106
|
-
<span class="tooltiptext">Copy Code</span>
|
|
107
|
-
</button>
|
|
108
|
-
</div>
|
|
109
|
-
<span class="iconID">${illustration}</span>
|
|
110
|
-
<!-- Code block to be copied -->
|
|
111
|
-
<pre class="codeBlock">
|
|
112
|
-
<code class="codeBlock">
|
|
113
|
-
<span class="dot-illustration">
|
|
114
|
-
<img class="${illustration} light"/>
|
|
115
|
-
</span>
|
|
116
|
-
</code>
|
|
117
|
-
</pre>
|
|
118
|
-
`;
|
|
119
|
-
list.appendChild(div);
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
document.addEventListener("DOMContentLoaded", doMagic);
|
|
124
|
-
|
|
125
|
-
|
|
126
22
|
function showTooltip(button) {
|
|
127
23
|
var tooltip = button.querySelector('.tooltiptext');
|
|
128
24
|
tooltip.style.visibility = 'visible';
|
|
@@ -133,59 +29,57 @@ function hideTooltip(button) {
|
|
|
133
29
|
tooltip.style.visibility = 'hidden';
|
|
134
30
|
}
|
|
135
31
|
|
|
136
|
-
|
|
32
|
+
// Toast logic
|
|
33
|
+
function showToast(message) {
|
|
34
|
+
let toast = document.getElementById('toast-message');
|
|
35
|
+
if (!toast) {
|
|
36
|
+
toast = document.createElement('div');
|
|
37
|
+
toast.id = 'toast-message';
|
|
38
|
+
toast.className = 'fixed bottom-6 left-1/2 -translate-x-1/2 z-50 bg-gray-900 text-white px-6 py-3 rounded-lg shadow-lg flex items-center gap-2 text-base animate-fade-in';
|
|
39
|
+
document.body.appendChild(toast);
|
|
40
|
+
}
|
|
41
|
+
toast.textContent = message;
|
|
42
|
+
toast.style.display = 'flex';
|
|
43
|
+
clearTimeout(window._toastTimeout);
|
|
44
|
+
window._toastTimeout = setTimeout(() => {
|
|
45
|
+
toast.style.display = 'none';
|
|
46
|
+
}, 2000);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Patch copyCode and copyText to show toast
|
|
50
|
+
const origCopyCode = window.copyCode;
|
|
51
|
+
window.copyCode = function(button) {
|
|
137
52
|
// Find the closest div with class 'copy-container'
|
|
138
53
|
var parentDiv = button.closest('.copy-container');
|
|
139
|
-
|
|
140
|
-
// Find the code block element within the same parent div
|
|
141
54
|
var copyCode = parentDiv.querySelector('.codeBlock code');
|
|
142
|
-
|
|
143
|
-
// Get the HTML content inside the code block as a string
|
|
144
55
|
var codeToCopy = copyCode.innerHTML;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
// Use the Clipboard API to copy the text
|
|
149
56
|
navigator.clipboard.writeText(codeToCopy).then(function() {
|
|
150
|
-
// Show the tooltip with the copied code message
|
|
151
57
|
var tooltip = button.querySelector('.tooltiptext');
|
|
152
58
|
tooltip.innerHTML = "Copied code block";
|
|
153
59
|
tooltip.style.visibility = 'visible';
|
|
154
|
-
|
|
155
|
-
// Reset the tooltip back to "Copy Code" after 2 seconds
|
|
156
60
|
setTimeout(function() {
|
|
157
|
-
tooltip.innerHTML = "Copy Code
|
|
61
|
+
tooltip.innerHTML = "Copy Code";
|
|
158
62
|
tooltip.style.visibility = 'hidden';
|
|
159
63
|
}, 2000);
|
|
160
|
-
|
|
64
|
+
showToast('Copied code: ' + codeToCopy);
|
|
161
65
|
}, function(err) {
|
|
162
66
|
console.error('Failed to copy code block: ', err);
|
|
163
67
|
});
|
|
164
68
|
}
|
|
165
|
-
|
|
166
|
-
function
|
|
167
|
-
// Find the closest div with class 'copy-container'
|
|
69
|
+
const origCopyText = window.copyText;
|
|
70
|
+
window.copyText = function(button) {
|
|
168
71
|
var parentDiv = button.closest('.copy-container');
|
|
169
|
-
|
|
170
|
-
// Find the span with the class 'iconID'
|
|
171
72
|
var copyText = parentDiv.querySelector('.iconID');
|
|
172
|
-
|
|
173
|
-
// Get the text content of the span
|
|
174
73
|
var textToCopy = copyText.textContent;
|
|
175
|
-
|
|
176
|
-
// Use the Clipboard API to copy the text
|
|
177
74
|
navigator.clipboard.writeText(textToCopy).then(function() {
|
|
178
|
-
// Show the tooltip with the copied ID message
|
|
179
75
|
var tooltip = button.querySelector('.tooltiptext');
|
|
180
76
|
tooltip.innerHTML = "Copied: " + textToCopy;
|
|
181
77
|
tooltip.style.visibility = 'visible';
|
|
182
|
-
|
|
183
|
-
// Reset the tooltip back to "Copy ID" after 2 seconds
|
|
184
78
|
setTimeout(function() {
|
|
185
79
|
tooltip.innerHTML = "Copy ID";
|
|
186
80
|
tooltip.style.visibility = 'hidden';
|
|
187
81
|
}, 2000);
|
|
188
|
-
|
|
82
|
+
showToast('Copied ID: ' + textToCopy);
|
|
189
83
|
}, function(err) {
|
|
190
84
|
console.error('Failed to copy text: ', err);
|
|
191
85
|
});
|
|
@@ -222,3 +116,260 @@ button.addEventListener('click', () => {
|
|
|
222
116
|
button.textContent = 'Switch to dark mode';
|
|
223
117
|
}
|
|
224
118
|
});
|
|
119
|
+
|
|
120
|
+
// --- THEME SWITCHER & TABS ---
|
|
121
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
122
|
+
// Theme Switcher
|
|
123
|
+
const themeToggle = document.getElementById('theme-toggle');
|
|
124
|
+
const themeIcon = document.getElementById('theme-icon');
|
|
125
|
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
126
|
+
|
|
127
|
+
function setTheme(dark) {
|
|
128
|
+
const imgs = document.querySelectorAll('img');
|
|
129
|
+
if (dark) {
|
|
130
|
+
root.classList.add('dark');
|
|
131
|
+
localStorage.setItem('theme', 'dark');
|
|
132
|
+
themeIcon.innerHTML = `<svg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' fill='none' viewBox='0 0 24 24' stroke='currentColor'><path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M21 12.79A9 9 0 1111.21 3a7 7 0 109.79 9.79z' /></svg>`;
|
|
133
|
+
imgs.forEach(img => {
|
|
134
|
+
img.classList.remove('light');
|
|
135
|
+
img.classList.add('dark');
|
|
136
|
+
});
|
|
137
|
+
} else {
|
|
138
|
+
root.classList.remove('dark');
|
|
139
|
+
localStorage.setItem('theme', 'light');
|
|
140
|
+
themeIcon.innerHTML = `<svg xmlns='http://www.w3.org/2000/svg' class='h-5 w-5' fill='none' viewBox='0 0 24 24' stroke='currentColor'><path stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M12 3v1m0 16v1m8.66-13.66l-.71.71M4.05 19.07l-.71.71M21 12h-1M4 12H3m16.66 6.66l-.71-.71M4.05 4.93l-.71-.71M16 12a4 4 0 11-8 0 4 4 0 018 0z' /></svg>`;
|
|
141
|
+
imgs.forEach(img => {
|
|
142
|
+
img.classList.remove('dark');
|
|
143
|
+
img.classList.add('light');
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Initial theme
|
|
149
|
+
const savedTheme = localStorage.getItem('theme');
|
|
150
|
+
setTheme(savedTheme === 'dark' || (!savedTheme && prefersDark));
|
|
151
|
+
|
|
152
|
+
themeToggle.addEventListener('click', () => {
|
|
153
|
+
setTheme(!root.classList.contains('dark'));
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// --- TABS ---
|
|
157
|
+
const mainTabs = document.querySelectorAll('.tab-btn');
|
|
158
|
+
const floatingTabs = document.querySelectorAll('.floating-tab');
|
|
159
|
+
|
|
160
|
+
function setTab(tab) {
|
|
161
|
+
currentTab = tab;
|
|
162
|
+
// Update main tabs
|
|
163
|
+
mainTabs.forEach(t => {
|
|
164
|
+
t.classList.remove('text-blue-600', 'dark:text-blue-400', 'bg-blue-100', 'dark:bg-blue-900');
|
|
165
|
+
t.classList.add('text-gray-700', 'dark:text-gray-300');
|
|
166
|
+
t.setAttribute('aria-selected', 'false');
|
|
167
|
+
if (t.dataset.tab === tab) {
|
|
168
|
+
t.classList.add('text-blue-600', 'dark:text-blue-400', 'bg-blue-100', 'dark:bg-blue-900');
|
|
169
|
+
t.classList.remove('text-gray-700', 'dark:text-gray-300');
|
|
170
|
+
t.setAttribute('aria-selected', 'true');
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
// Update floating tabs
|
|
174
|
+
floatingTabs.forEach(t => {
|
|
175
|
+
t.classList.remove('text-blue-600', 'dark:text-blue-400', 'bg-blue-100', 'dark:bg-blue-900');
|
|
176
|
+
t.classList.add('text-gray-700', 'dark:text-gray-300');
|
|
177
|
+
t.setAttribute('aria-selected', 'false');
|
|
178
|
+
if (t.dataset.tab === tab) {
|
|
179
|
+
t.classList.add('text-blue-600', 'dark:text-blue-400', 'bg-blue-100', 'dark:bg-blue-900');
|
|
180
|
+
t.classList.remove('text-gray-700', 'dark:text-gray-300');
|
|
181
|
+
t.setAttribute('aria-selected', 'true');
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
renderIllustrations();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
mainTabs.forEach(tab => {
|
|
188
|
+
tab.addEventListener('click', function() {
|
|
189
|
+
setTab(this.dataset.tab);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
floatingTabs.forEach(tab => {
|
|
193
|
+
tab.addEventListener('click', function() {
|
|
194
|
+
setTab(this.dataset.tab);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// --- ILLUSTRATION DATA ---
|
|
199
|
+
const globalList = [
|
|
200
|
+
"add-new","ai","add-new-grid","add-team","add-user","assets","analysis-circle","community","chart","custom-dashboards","dependency-down","dependency-up","disconnected","done","dora-metrics-circle","download","empty","error-fourhundredfour","error-fivehundred","error-fivehundredthree","error-fourhundredthree","favorite","features","folder-versioning","forbiden-access","filter-empty","launch","no-files","no-filters","nothing-defined","password-token","protection-in-progress","reports","scan-document","survey","user","ui-ux-improvements","upload","work-item","workflows-main","research","commitrepo"
|
|
201
|
+
];
|
|
202
|
+
const dashboardsList = [
|
|
203
|
+
"analysis","automation","burn-down","burn-up","change-credit-score","change-data-quality","change-executive","change-impact-detection","change-impact-hotspots","change-failure-factors","change-failure-prediction","CRP-failure-prediction-monitoring","custom","defect","dependency","dora","executive","change-impact","item","late-task","onboarding","data-extraction","workflow"
|
|
204
|
+
];
|
|
205
|
+
const illustrations = [...globalList, ...dashboardsList, "my-new-illustration"];
|
|
206
|
+
|
|
207
|
+
// --- FAVOURITES LOGIC ---
|
|
208
|
+
function getFavourites() {
|
|
209
|
+
try {
|
|
210
|
+
return JSON.parse(localStorage.getItem('dot-illustration-favourites') || '[]');
|
|
211
|
+
} catch {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function setFavourites(favs) {
|
|
216
|
+
localStorage.setItem('dot-illustration-favourites', JSON.stringify(favs));
|
|
217
|
+
}
|
|
218
|
+
window.toggleFavourite = function(id) {
|
|
219
|
+
let favs = getFavourites();
|
|
220
|
+
if (favs.includes(id)) {
|
|
221
|
+
favs = favs.filter(f => f !== id);
|
|
222
|
+
} else {
|
|
223
|
+
favs.push(id);
|
|
224
|
+
}
|
|
225
|
+
setFavourites(favs);
|
|
226
|
+
renderIllustrations();
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
function renderIllustrations() {
|
|
230
|
+
const list = document.getElementById("illustration-list");
|
|
231
|
+
list.innerHTML = "";
|
|
232
|
+
let toShow = [];
|
|
233
|
+
if (currentTab === 'all') {
|
|
234
|
+
toShow = illustrations;
|
|
235
|
+
} else if (currentTab === 'global') {
|
|
236
|
+
toShow = globalList;
|
|
237
|
+
} else if (currentTab === 'dashboards') {
|
|
238
|
+
toShow = dashboardsList;
|
|
239
|
+
} else if (currentTab === 'favourites') {
|
|
240
|
+
const favs = getFavourites();
|
|
241
|
+
toShow = illustrations.filter(id => favs.includes(id));
|
|
242
|
+
}
|
|
243
|
+
const filter = (document.getElementById('myInput')?.value || '').toUpperCase();
|
|
244
|
+
toShow = toShow.filter(id => id.toUpperCase().includes(filter));
|
|
245
|
+
if (toShow.length === 0) {
|
|
246
|
+
list.innerHTML = '<div class="col-span-full text-center text-gray-500 dark:text-gray-400">No illustrations found.</div>';
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
const favs = getFavourites();
|
|
250
|
+
const themeClass = root.classList.contains('dark') ? 'dark' : 'light';
|
|
251
|
+
toShow.forEach(illustration => {
|
|
252
|
+
let categoryClass = "";
|
|
253
|
+
if (globalList.includes(illustration)) categoryClass = "global-illustration";
|
|
254
|
+
else if (dashboardsList.includes(illustration)) categoryClass = "dashboards-illustration";
|
|
255
|
+
const isFav = favs.includes(illustration);
|
|
256
|
+
const div = document.createElement("div");
|
|
257
|
+
div.setAttribute("class", "copy-container relative group flex flex-col items-center justify-between bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-lg p-2 md:p-3 transition-transform duration-200 hover:scale-105 hover:shadow-2xl h-[302px] w-full justify-center");
|
|
258
|
+
div.innerHTML = `
|
|
259
|
+
<button class="absolute top-2 right-2 z-10 p-1 rounded-full bg-white/80 dark:bg-gray-800/80 hover:bg-yellow-100 dark:hover:bg-yellow-300 transition" title="Toggle favourite" onclick="event.stopPropagation(); window.toggleFavourite && window.toggleFavourite('${illustration}')">
|
|
260
|
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="${isFav ? 'gold' : 'none'}" viewBox="0 0 24 24" stroke="gold">
|
|
261
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.049 2.927c.3-.921 1.603-.921 1.902 0l2.036 6.29a1 1 0 00.95.69h6.631c.969 0 1.371 1.24.588 1.81l-5.37 3.905a1 1 0 00-.364 1.118l2.036 6.29c.3.921-.755 1.688-1.54 1.118l-5.37-3.905a1 1 0 00-1.176 0l-5.37 3.905c-.784.57-1.838-.197-1.54-1.118l2.036-6.29a1 1 0 00-.364-1.118L2.342 11.717c-.783-.57-.38-1.81.588-1.81h6.631a1 1 0 00.95-.69l2.036-6.29z"/>
|
|
262
|
+
</svg>
|
|
263
|
+
</button>
|
|
264
|
+
<div class="flex-1 flex flex-col justify-center items-center">
|
|
265
|
+
<span class="dot-illustration">
|
|
266
|
+
<img src="./svgs/${illustration}.svg" class="${illustration} ${themeClass} ${categoryClass}"/>
|
|
267
|
+
</span>
|
|
268
|
+
<span class="iconID block text-center text-base font-medium text-gray-800 dark:text-gray-200 my-2">${illustration}</span>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="button-group flex flex-row items-center justify-center gap-2 mt-2">
|
|
271
|
+
<button id="CopyID" class="button flex items-center justify-center min-w-[80px] px-1.5 py-0.5 text-[11px] text-gray-400 border border-gray-300 dark:border-gray-700 rounded bg-white dark:bg-gray-800 font-normal hover:bg-blue-100 dark:hover:bg-blue-900 transition relative text-center" onmouseover="showTooltip(this)" onmouseout="hideTooltip(this)" onclick="copyText(this)">
|
|
272
|
+
Copy ID
|
|
273
|
+
<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy ID</span>
|
|
274
|
+
</button>
|
|
275
|
+
<button class="button flex items-center justify-center min-w-[80px] px-1.5 py-0.5 text-[11px] text-gray-400 border border-gray-300 dark:border-gray-700 rounded bg-white dark:bg-gray-800 font-normal hover:bg-blue-100 dark:hover:bg-blue-900 transition relative text-center" onmouseover="showTooltip(this)" onmouseout="hideTooltip(this)" onclick="copyCode(this)">
|
|
276
|
+
Copy Code
|
|
277
|
+
<span class="tooltiptext left-1/2 -translate-x-1/2 bottom-10 shadow-md">Copy Code</span>
|
|
278
|
+
</button>
|
|
279
|
+
</div>
|
|
280
|
+
<pre class="codeBlock"><code class="codeBlock"><span class="dot-illustration"><img src="./svgs/${illustration}.svg" class="${illustration} ${themeClass} ${categoryClass}"/></span></code></pre>
|
|
281
|
+
`;
|
|
282
|
+
list.appendChild(div);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Search input
|
|
287
|
+
document.getElementById('myInput').addEventListener('input', renderIllustrations);
|
|
288
|
+
document.getElementById('floatingInput').addEventListener('input', () => {
|
|
289
|
+
document.getElementById('myInput').value = document.getElementById('floatingInput').value;
|
|
290
|
+
renderIllustrations();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Clear both search inputs on load
|
|
294
|
+
document.getElementById('myInput').value = '';
|
|
295
|
+
document.getElementById('floatingInput').value = '';
|
|
296
|
+
|
|
297
|
+
// Set initial tab state to 'all' and update UI (this will also render illustrations)
|
|
298
|
+
setTab('all');
|
|
299
|
+
|
|
300
|
+
// --- FLOATING BAR LOGIC ---
|
|
301
|
+
const floatingBar = document.getElementById('floating-bar');
|
|
302
|
+
window.addEventListener('scroll', () => {
|
|
303
|
+
if (window.scrollY > 80) {
|
|
304
|
+
floatingBar.classList.add('floating-bar-visible');
|
|
305
|
+
} else {
|
|
306
|
+
floatingBar.classList.remove('floating-bar-visible');
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
// --- FUZZY SEARCH SUGGESTIONS ---
|
|
312
|
+
const searchInput = document.getElementById('myInput');
|
|
313
|
+
const suggestionsBox = document.getElementById('search-suggestions');
|
|
314
|
+
|
|
315
|
+
function fuzzyMatch(str, query) {
|
|
316
|
+
// Simple fuzzy: all query chars in order in str
|
|
317
|
+
query = query.toLowerCase();
|
|
318
|
+
str = str.toLowerCase();
|
|
319
|
+
let qIdx = 0;
|
|
320
|
+
for (let s = 0; s < str.length && qIdx < query.length; s++) {
|
|
321
|
+
if (str[s] === query[qIdx]) qIdx++;
|
|
322
|
+
}
|
|
323
|
+
return qIdx === query.length;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function getSuggestions(query) {
|
|
327
|
+
if (!query) return [];
|
|
328
|
+
// Use current tab's list
|
|
329
|
+
let pool = [];
|
|
330
|
+
if (currentTab === 'all') pool = illustrations;
|
|
331
|
+
else if (currentTab === 'global') pool = globalList;
|
|
332
|
+
else if (currentTab === 'dashboards') pool = dashboardsList;
|
|
333
|
+
else if (currentTab === 'favourites') pool = getFavourites();
|
|
334
|
+
// Fuzzy match, sort by closeness (shorter index of first match, then length)
|
|
335
|
+
let scored = pool
|
|
336
|
+
.map(id => {
|
|
337
|
+
let idx = id.toLowerCase().indexOf(query.toLowerCase());
|
|
338
|
+
let fuzzy = fuzzyMatch(id, query);
|
|
339
|
+
return fuzzy ? {id, idx: idx === -1 ? 999 : idx, len: id.length} : null;
|
|
340
|
+
})
|
|
341
|
+
.filter(Boolean)
|
|
342
|
+
.sort((a, b) => a.idx - b.idx || a.len - b.len)
|
|
343
|
+
.slice(0, 7);
|
|
344
|
+
return scored.map(s => s.id);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function showSuggestions() {
|
|
348
|
+
const val = searchInput.value.trim();
|
|
349
|
+
const suggestions = getSuggestions(val);
|
|
350
|
+
if (!val || suggestions.length === 0) {
|
|
351
|
+
suggestionsBox.classList.add('hidden');
|
|
352
|
+
suggestionsBox.innerHTML = '';
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
suggestionsBox.innerHTML = suggestions.map(s => `<div class="px-4 py-2 cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-900 transition" data-suggestion="${s}">${s}</div>`).join('');
|
|
356
|
+
suggestionsBox.classList.remove('hidden');
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
searchInput.addEventListener('input', () => {
|
|
360
|
+
renderIllustrations();
|
|
361
|
+
showSuggestions();
|
|
362
|
+
});
|
|
363
|
+
searchInput.addEventListener('focus', showSuggestions);
|
|
364
|
+
searchInput.addEventListener('blur', () => setTimeout(() => suggestionsBox.classList.add('hidden'), 150));
|
|
365
|
+
suggestionsBox.addEventListener('mousedown', e => {
|
|
366
|
+
if (e.target.dataset.suggestion) {
|
|
367
|
+
searchInput.value = e.target.dataset.suggestion;
|
|
368
|
+
renderIllustrations();
|
|
369
|
+
suggestionsBox.classList.add('hidden');
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// Remove classic grid rendering to avoid conflicts with tabbed UI
|
|
374
|
+
// document.addEventListener("DOMContentLoaded", doMagic);
|
|
375
|
+
|