@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/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
- function copyCode(button) {
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 copyText(button) {
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
+