@customviews-js/customviews 1.1.0 → 1.1.1
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/README.md +362 -219
- package/dist/custom-views.core.cjs.js +1133 -1016
- package/dist/custom-views.core.cjs.js.map +1 -1
- package/dist/custom-views.core.esm.js +1133 -1016
- package/dist/custom-views.core.esm.js.map +1 -1
- package/dist/custom-views.esm.js +1133 -1016
- package/dist/custom-views.esm.js.map +1 -1
- package/dist/custom-views.js +1130 -1013
- package/dist/custom-views.js.map +1 -1
- package/dist/custom-views.min.js +2 -2
- package/dist/custom-views.min.js.map +1 -1
- package/dist/types/core/core.d.ts.map +1 -1
- package/dist/types/core/custom-elements.d.ts.map +1 -1
- package/dist/types/core/render.d.ts +3 -0
- package/dist/types/core/render.d.ts.map +1 -1
- package/dist/types/core/tab-manager.d.ts.map +1 -1
- package/dist/types/core/toggle-manager.d.ts +28 -0
- package/dist/types/core/toggle-manager.d.ts.map +1 -0
- package/dist/types/entry/browser-entry.d.ts.map +1 -1
- package/dist/types/lib/custom-views.d.ts.map +1 -1
- package/dist/types/styles/toggle-styles.d.ts +1 -1
- package/dist/types/styles/toggle-styles.d.ts.map +1 -1
- package/dist/types/styles/widget-styles.d.ts +1 -1
- package/dist/types/styles/widget-styles.d.ts.map +1 -1
- package/package.json +61 -61
|
@@ -1,85 +1,8 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @customviews-js/customviews v1.1.
|
|
2
|
+
* @customviews-js/customviews v1.1.1
|
|
3
3
|
* (c) 2025 Chan Ger Teck
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
6
|
-
/** --- Basic renderers --- */
|
|
7
|
-
function renderImage(el, asset) {
|
|
8
|
-
if (!asset.src)
|
|
9
|
-
return;
|
|
10
|
-
el.innerHTML = '';
|
|
11
|
-
const img = document.createElement('img');
|
|
12
|
-
img.src = asset.src;
|
|
13
|
-
img.alt = asset.alt || '';
|
|
14
|
-
// Apply custom styling if provided
|
|
15
|
-
if (asset.className) {
|
|
16
|
-
img.className = asset.className;
|
|
17
|
-
}
|
|
18
|
-
if (asset.style) {
|
|
19
|
-
img.setAttribute('style', asset.style);
|
|
20
|
-
}
|
|
21
|
-
// Default styles (can be overridden by asset.style)
|
|
22
|
-
img.style.maxWidth = img.style.maxWidth || '100%';
|
|
23
|
-
img.style.height = img.style.height || 'auto';
|
|
24
|
-
img.style.display = img.style.display || 'block';
|
|
25
|
-
el.appendChild(img);
|
|
26
|
-
}
|
|
27
|
-
function renderText(el, asset) {
|
|
28
|
-
if (asset.content != null) {
|
|
29
|
-
el.textContent = asset.content;
|
|
30
|
-
}
|
|
31
|
-
// Apply custom styling if provided
|
|
32
|
-
if (asset.className) {
|
|
33
|
-
el.className = asset.className;
|
|
34
|
-
}
|
|
35
|
-
if (asset.style) {
|
|
36
|
-
el.setAttribute('style', asset.style);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function renderHtml(el, asset) {
|
|
40
|
-
if (asset.content != null) {
|
|
41
|
-
el.innerHTML = asset.content;
|
|
42
|
-
}
|
|
43
|
-
// Apply custom styling if provided
|
|
44
|
-
if (asset.className) {
|
|
45
|
-
el.className = asset.className;
|
|
46
|
-
}
|
|
47
|
-
if (asset.style) {
|
|
48
|
-
el.setAttribute('style', asset.style);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
/** --- Unified asset renderer --- */
|
|
52
|
-
function detectAssetType(asset) {
|
|
53
|
-
// If src exists, it's an image
|
|
54
|
-
if (asset.src)
|
|
55
|
-
return 'image';
|
|
56
|
-
// If content contains HTML tags, it's HTML
|
|
57
|
-
if (asset.content && /<[^>]+>/.test(asset.content)) {
|
|
58
|
-
return 'html';
|
|
59
|
-
}
|
|
60
|
-
return 'text';
|
|
61
|
-
}
|
|
62
|
-
function renderAssetInto(el, assetId, assetsManager) {
|
|
63
|
-
const asset = assetsManager.get(assetId);
|
|
64
|
-
if (!asset)
|
|
65
|
-
return;
|
|
66
|
-
const type = asset.type || detectAssetType(asset);
|
|
67
|
-
switch (type) {
|
|
68
|
-
case 'image':
|
|
69
|
-
renderImage(el, asset);
|
|
70
|
-
break;
|
|
71
|
-
case 'text':
|
|
72
|
-
renderText(el, asset);
|
|
73
|
-
break;
|
|
74
|
-
case 'html':
|
|
75
|
-
renderHtml(el, asset);
|
|
76
|
-
break;
|
|
77
|
-
default:
|
|
78
|
-
el.innerHTML = asset.content || String(asset);
|
|
79
|
-
console.warn('[CustomViews] Unknown asset type:', type);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
6
|
/**
|
|
84
7
|
* Manages persistence of custom views state using browser localStorage
|
|
85
8
|
*/
|
|
@@ -340,6 +263,98 @@ class VisibilityManager {
|
|
|
340
263
|
}
|
|
341
264
|
}
|
|
342
265
|
|
|
266
|
+
/** --- Icon utilities --- */
|
|
267
|
+
function ensureFontAwesomeInjected() {
|
|
268
|
+
const isFontAwesomeLoaded = Array.from(document.styleSheets).some(sheet => sheet.href && (sheet.href.includes('font-awesome') || sheet.href.includes('fontawesome')));
|
|
269
|
+
if (isFontAwesomeLoaded)
|
|
270
|
+
return;
|
|
271
|
+
const link = document.createElement('link');
|
|
272
|
+
link.rel = 'stylesheet';
|
|
273
|
+
link.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css';
|
|
274
|
+
link.setAttribute('data-customviews-fontawesome', 'true');
|
|
275
|
+
document.head.appendChild(link);
|
|
276
|
+
}
|
|
277
|
+
function replaceIconShortcodes(text) {
|
|
278
|
+
// Handle Font Awesome shortcodes
|
|
279
|
+
return text.replace(/:fa-([\w-]+):/g, (_, icon) => `<i class="fa fa-${icon}"></i>`);
|
|
280
|
+
}
|
|
281
|
+
/** --- Basic renderers --- */
|
|
282
|
+
function renderImage(el, asset) {
|
|
283
|
+
if (!asset.src)
|
|
284
|
+
return;
|
|
285
|
+
el.innerHTML = '';
|
|
286
|
+
const img = document.createElement('img');
|
|
287
|
+
img.src = asset.src;
|
|
288
|
+
img.alt = asset.alt || '';
|
|
289
|
+
// Apply custom styling if provided
|
|
290
|
+
if (asset.className) {
|
|
291
|
+
img.className = asset.className;
|
|
292
|
+
}
|
|
293
|
+
if (asset.style) {
|
|
294
|
+
img.setAttribute('style', asset.style);
|
|
295
|
+
}
|
|
296
|
+
// Default styles (can be overridden by asset.style)
|
|
297
|
+
img.style.maxWidth = img.style.maxWidth || '100%';
|
|
298
|
+
img.style.height = img.style.height || 'auto';
|
|
299
|
+
img.style.display = img.style.display || 'block';
|
|
300
|
+
el.appendChild(img);
|
|
301
|
+
}
|
|
302
|
+
function renderText(el, asset) {
|
|
303
|
+
if (asset.content != null) {
|
|
304
|
+
el.textContent = asset.content;
|
|
305
|
+
}
|
|
306
|
+
// Apply custom styling if provided
|
|
307
|
+
if (asset.className) {
|
|
308
|
+
el.className = asset.className;
|
|
309
|
+
}
|
|
310
|
+
if (asset.style) {
|
|
311
|
+
el.setAttribute('style', asset.style);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function renderHtml(el, asset) {
|
|
315
|
+
if (asset.content != null) {
|
|
316
|
+
el.innerHTML = asset.content;
|
|
317
|
+
}
|
|
318
|
+
// Apply custom styling if provided
|
|
319
|
+
if (asset.className) {
|
|
320
|
+
el.className = asset.className;
|
|
321
|
+
}
|
|
322
|
+
if (asset.style) {
|
|
323
|
+
el.setAttribute('style', asset.style);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
/** --- Unified asset renderer --- */
|
|
327
|
+
function detectAssetType(asset) {
|
|
328
|
+
// If src exists, it's an image
|
|
329
|
+
if (asset.src)
|
|
330
|
+
return 'image';
|
|
331
|
+
// If content contains HTML tags, it's HTML
|
|
332
|
+
if (asset.content && /<[^>]+>/.test(asset.content)) {
|
|
333
|
+
return 'html';
|
|
334
|
+
}
|
|
335
|
+
return 'text';
|
|
336
|
+
}
|
|
337
|
+
function renderAssetInto(el, assetId, assetsManager) {
|
|
338
|
+
const asset = assetsManager.get(assetId);
|
|
339
|
+
if (!asset)
|
|
340
|
+
return;
|
|
341
|
+
const type = asset.type || detectAssetType(asset);
|
|
342
|
+
switch (type) {
|
|
343
|
+
case 'image':
|
|
344
|
+
renderImage(el, asset);
|
|
345
|
+
break;
|
|
346
|
+
case 'text':
|
|
347
|
+
renderText(el, asset);
|
|
348
|
+
break;
|
|
349
|
+
case 'html':
|
|
350
|
+
renderHtml(el, asset);
|
|
351
|
+
break;
|
|
352
|
+
default:
|
|
353
|
+
el.innerHTML = asset.content || String(asset);
|
|
354
|
+
console.warn('[CustomViews] Unknown asset type:', type);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
343
358
|
// Constants for selectors
|
|
344
359
|
const TABGROUP_SELECTOR = 'cv-tabgroup';
|
|
345
360
|
const TAB_SELECTOR = 'cv-tab';
|
|
@@ -361,8 +376,8 @@ class TabManager {
|
|
|
361
376
|
return;
|
|
362
377
|
// Determine the active tab for this group
|
|
363
378
|
const activeTabId = this.resolveActiveTab(groupId, tabs, cfgGroups, groupEl);
|
|
364
|
-
// Apply visibility to child cv-tab elements
|
|
365
|
-
const tabElements = groupEl.
|
|
379
|
+
// Apply visibility to direct child cv-tab elements only (not nested ones)
|
|
380
|
+
const tabElements = Array.from(groupEl.children).filter((child) => child.tagName.toLowerCase() === TAB_SELECTOR);
|
|
366
381
|
tabElements.forEach((tabEl) => {
|
|
367
382
|
const tabId = tabEl.getAttribute('id');
|
|
368
383
|
if (!tabId)
|
|
@@ -394,8 +409,8 @@ class TabManager {
|
|
|
394
409
|
}
|
|
395
410
|
}
|
|
396
411
|
}
|
|
397
|
-
// 3. Fallback to first cv-tab child in DOM
|
|
398
|
-
const firstTab = groupEl.
|
|
412
|
+
// 3. Fallback to first direct cv-tab child in DOM
|
|
413
|
+
const firstTab = Array.from(groupEl.children).find((child) => child.tagName.toLowerCase() === TAB_SELECTOR);
|
|
399
414
|
if (firstTab) {
|
|
400
415
|
return firstTab.getAttribute('id');
|
|
401
416
|
}
|
|
@@ -420,6 +435,28 @@ class TabManager {
|
|
|
420
435
|
static buildNavs(rootEl, cfgGroups, onTabClick) {
|
|
421
436
|
// Find all cv-tabgroup elements with nav="auto" or no nav attribute
|
|
422
437
|
const tabGroups = rootEl.querySelectorAll(NAV_AUTO_SELECTOR);
|
|
438
|
+
// Check if any tab headers contain Font Awesome shortcodes
|
|
439
|
+
// Inject Font Awesome CSS only if needed
|
|
440
|
+
let hasFontAwesomeShortcodes = false;
|
|
441
|
+
tabGroups.forEach((groupEl) => {
|
|
442
|
+
const groupId = groupEl.getAttribute('id');
|
|
443
|
+
if (!groupId)
|
|
444
|
+
return;
|
|
445
|
+
const tabElements = Array.from(groupEl.children).filter((child) => child.tagName.toLowerCase() === 'cv-tab');
|
|
446
|
+
tabElements.forEach((tabEl) => {
|
|
447
|
+
const tabId = tabEl.getAttribute('id');
|
|
448
|
+
if (!tabId)
|
|
449
|
+
return;
|
|
450
|
+
const header = tabEl.getAttribute('header') || this.getTabLabel(tabId, groupId, cfgGroups) || tabId;
|
|
451
|
+
if (/:fa-[\w-]+:/.test(header)) {
|
|
452
|
+
hasFontAwesomeShortcodes = true;
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
// Inject Font Awesome only if shortcodes are found
|
|
457
|
+
if (hasFontAwesomeShortcodes) {
|
|
458
|
+
ensureFontAwesomeInjected();
|
|
459
|
+
}
|
|
423
460
|
tabGroups.forEach((groupEl) => {
|
|
424
461
|
const groupId = groupEl.getAttribute('id');
|
|
425
462
|
if (!groupId)
|
|
@@ -428,8 +465,8 @@ class TabManager {
|
|
|
428
465
|
let navContainer = groupEl.querySelector(`.${NAV_CONTAINER_CLASS}`);
|
|
429
466
|
if (navContainer)
|
|
430
467
|
return; // Already built
|
|
431
|
-
// Get
|
|
432
|
-
const tabElements = Array.from(groupEl.
|
|
468
|
+
// Get only direct child tabs (not nested ones)
|
|
469
|
+
const tabElements = Array.from(groupEl.children).filter((child) => child.tagName.toLowerCase() === TAB_SELECTOR);
|
|
433
470
|
if (tabElements.length === 0)
|
|
434
471
|
return;
|
|
435
472
|
// Create nav container
|
|
@@ -447,7 +484,8 @@ class TabManager {
|
|
|
447
484
|
listItem.className = 'nav-item';
|
|
448
485
|
const navLink = document.createElement('a');
|
|
449
486
|
navLink.className = 'nav-link';
|
|
450
|
-
|
|
487
|
+
// Replace icon shortcodes in header
|
|
488
|
+
navLink.innerHTML = replaceIconShortcodes(header);
|
|
451
489
|
navLink.href = '#';
|
|
452
490
|
navLink.setAttribute('data-tab-id', tabId);
|
|
453
491
|
navLink.setAttribute('data-group-id', groupId);
|
|
@@ -539,157 +577,267 @@ class TabManager {
|
|
|
539
577
|
}
|
|
540
578
|
}
|
|
541
579
|
|
|
580
|
+
class AssetsManager {
|
|
581
|
+
assets;
|
|
582
|
+
baseURL;
|
|
583
|
+
constructor(assets, baseURL = '') {
|
|
584
|
+
this.assets = assets;
|
|
585
|
+
this.baseURL = baseURL;
|
|
586
|
+
if (!this.validate()) {
|
|
587
|
+
console.warn('Invalid assets:', this.assets);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
// Check each asset has content or src
|
|
591
|
+
validate() {
|
|
592
|
+
return Object.values(this.assets).every(a => a.src || a.content);
|
|
593
|
+
}
|
|
594
|
+
get(assetId) {
|
|
595
|
+
const asset = this.assets[assetId];
|
|
596
|
+
if (!asset)
|
|
597
|
+
return undefined;
|
|
598
|
+
// If there's a baseURL and the asset has a src property, prepend the baseURL
|
|
599
|
+
if (this.baseURL && asset.src) {
|
|
600
|
+
// Create a shallow copy to avoid mutating the original asset
|
|
601
|
+
return {
|
|
602
|
+
...asset,
|
|
603
|
+
src: this.prependBaseURL(asset.src)
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
return asset;
|
|
607
|
+
}
|
|
608
|
+
prependBaseURL(path) {
|
|
609
|
+
// Don't prepend if the path is already absolute (starts with http:// or https://)
|
|
610
|
+
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
611
|
+
return path;
|
|
612
|
+
}
|
|
613
|
+
// Ensure baseURL doesn't end with / and path starts with /
|
|
614
|
+
const cleanBaseURL = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;
|
|
615
|
+
const cleanPath = path.startsWith('/') ? path : '/' + path;
|
|
616
|
+
return cleanBaseURL + cleanPath;
|
|
617
|
+
}
|
|
618
|
+
loadFromJSON(json) {
|
|
619
|
+
this.assets = json;
|
|
620
|
+
}
|
|
621
|
+
loadAdditionalAssets(additionalAssets) {
|
|
622
|
+
this.assets = { ...this.assets, ...additionalAssets };
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Constants for selectors
|
|
627
|
+
const TOGGLE_DATA_SELECTOR = "[data-cv-toggle], [data-customviews-toggle]";
|
|
628
|
+
const TOGGLE_ELEMENT_SELECTOR = "cv-toggle";
|
|
629
|
+
const TOGGLE_SELECTOR = `${TOGGLE_DATA_SELECTOR}, ${TOGGLE_ELEMENT_SELECTOR}`;
|
|
542
630
|
/**
|
|
543
|
-
*
|
|
631
|
+
* ToggleManager handles discovery, visibility, and asset rendering for toggle elements
|
|
544
632
|
*/
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
.
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
633
|
+
class ToggleManager {
|
|
634
|
+
/**
|
|
635
|
+
* Apply toggle visibility to all toggle elements in the DOM
|
|
636
|
+
*/
|
|
637
|
+
static applyToggles(rootEl, activeToggles) {
|
|
638
|
+
rootEl.querySelectorAll(TOGGLE_SELECTOR).forEach(el => {
|
|
639
|
+
const categories = this.getToggleCategories(el);
|
|
640
|
+
const shouldShow = categories.some(cat => activeToggles.includes(cat));
|
|
641
|
+
this.applyToggleVisibility(el, shouldShow);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Render assets into toggle elements that are currently visible
|
|
646
|
+
*/
|
|
647
|
+
static renderAssets(rootEl, activeToggles, assetsManager) {
|
|
648
|
+
rootEl.querySelectorAll(TOGGLE_SELECTOR).forEach(el => {
|
|
649
|
+
const categories = this.getToggleCategories(el);
|
|
650
|
+
const toggleId = this.getToggleId(el);
|
|
651
|
+
if (toggleId && categories.some(cat => activeToggles.includes(cat))) {
|
|
652
|
+
renderAssetInto(el, toggleId, assetsManager);
|
|
653
|
+
}
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Get toggle categories from an element (supports both data attributes and cv-toggle elements)
|
|
658
|
+
*/
|
|
659
|
+
static getToggleCategories(el) {
|
|
660
|
+
if (el.tagName.toLowerCase() === 'cv-toggle') {
|
|
661
|
+
const category = el.getAttribute('category');
|
|
662
|
+
return (category || '').split(/\s+/).filter(Boolean);
|
|
663
|
+
}
|
|
664
|
+
else {
|
|
665
|
+
const data = el.dataset.cvToggle || el.dataset.customviewsToggle;
|
|
666
|
+
return (data || '').split(/\s+/).filter(Boolean);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
/**
|
|
670
|
+
* Get toggle ID from an element
|
|
671
|
+
*/
|
|
672
|
+
static getToggleId(el) {
|
|
673
|
+
return el.dataset.cvId || el.dataset.customviewsId || el.getAttribute('data-cv-id') || el.getAttribute('data-customviews-id') || undefined;
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Apply simple class-based visibility to a toggle element
|
|
677
|
+
*/
|
|
678
|
+
static applyToggleVisibility(el, visible) {
|
|
679
|
+
if (visible) {
|
|
680
|
+
el.classList.remove('cv-hidden');
|
|
681
|
+
el.classList.add('cv-visible');
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
el.classList.add('cv-hidden');
|
|
685
|
+
el.classList.remove('cv-visible');
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
575
689
|
|
|
576
690
|
/**
|
|
577
|
-
* Styles for
|
|
691
|
+
* Styles for toggle visibility and animations
|
|
578
692
|
*/
|
|
579
|
-
const
|
|
580
|
-
/*
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
padding: 0
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
.cv-tabs-nav-
|
|
645
|
-
|
|
646
|
-
border-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
.cv-tabs-nav-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
cv-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
693
|
+
const TOGGLE_STYLES = `
|
|
694
|
+
/* Core toggle visibility transitions */
|
|
695
|
+
[data-cv-toggle], [data-customviews-toggle], cv-toggle {
|
|
696
|
+
transition: opacity 150ms ease,
|
|
697
|
+
transform 150ms ease,
|
|
698
|
+
max-height 200ms ease,
|
|
699
|
+
margin 150ms ease;
|
|
700
|
+
will-change: opacity, transform, max-height, margin;
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
.cv-visible {
|
|
704
|
+
opacity: 1 !important;
|
|
705
|
+
transform: translateY(0) !important;
|
|
706
|
+
max-height: var(--cv-max-height, 9999px) !important;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
.cv-hidden {
|
|
710
|
+
opacity: 0 !important;
|
|
711
|
+
transform: translateY(-4px) !important;
|
|
712
|
+
pointer-events: none !important;
|
|
713
|
+
padding-top: 0 !important;
|
|
714
|
+
padding-bottom: 0 !important;
|
|
715
|
+
border-top-width: 0 !important;
|
|
716
|
+
border-bottom-width: 0 !important;
|
|
717
|
+
max-height: 0 !important;
|
|
718
|
+
margin-top: 0 !important;
|
|
719
|
+
margin-bottom: 0 !important;
|
|
720
|
+
overflow: hidden !important;
|
|
721
|
+
}
|
|
722
|
+
`;
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Styles for tab groups and tab navigation
|
|
726
|
+
*/
|
|
727
|
+
const TAB_STYLES = `
|
|
728
|
+
/* Tab navigation styles - Bootstrap-style tabs matching MarkBind */
|
|
729
|
+
.cv-tabs-nav {
|
|
730
|
+
display: flex;
|
|
731
|
+
flex-wrap: wrap;
|
|
732
|
+
padding-left: 0;
|
|
733
|
+
margin-top: 0.5rem;
|
|
734
|
+
margin-bottom: 1rem;
|
|
735
|
+
list-style: none;
|
|
736
|
+
border-bottom: 1px solid #dee2e6;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
.cv-tabs-nav .nav-item {
|
|
740
|
+
margin-bottom: -1px;
|
|
741
|
+
list-style: none;
|
|
742
|
+
display: inline-block;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
.cv-tabs-nav .nav-link {
|
|
746
|
+
display: block;
|
|
747
|
+
padding: 0.5rem 1rem;
|
|
748
|
+
color: #495057;
|
|
749
|
+
text-decoration: none;
|
|
750
|
+
background-color: transparent;
|
|
751
|
+
border: 1px solid transparent;
|
|
752
|
+
border-top-left-radius: 0.25rem;
|
|
753
|
+
border-top-right-radius: 0.25rem;
|
|
754
|
+
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
|
|
755
|
+
cursor: pointer;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.cv-tabs-nav .nav-link:hover,
|
|
759
|
+
.cv-tabs-nav .nav-link:focus {
|
|
760
|
+
border-color: #e9ecef #e9ecef #dee2e6;
|
|
761
|
+
isolation: isolate;
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
.cv-tabs-nav .nav-link.active {
|
|
765
|
+
color: #495057;
|
|
766
|
+
background-color: #fff;
|
|
767
|
+
border-color: #dee2e6 #dee2e6 #fff;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
.cv-tabs-nav .nav-link:focus {
|
|
771
|
+
outline: 0;
|
|
772
|
+
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/* Legacy button-based nav (deprecated, kept for compatibility) */
|
|
776
|
+
.cv-tabs-nav-item {
|
|
777
|
+
background: none;
|
|
778
|
+
border: none;
|
|
779
|
+
border-bottom: 2px solid transparent;
|
|
780
|
+
padding: 0.5rem 1rem;
|
|
781
|
+
cursor: pointer;
|
|
782
|
+
font-size: 1rem;
|
|
783
|
+
color: #6c757d;
|
|
784
|
+
transition: color 150ms ease, border-color 150ms ease;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
.cv-tabs-nav-item:hover {
|
|
788
|
+
color: #495057;
|
|
789
|
+
border-bottom-color: #dee2e6;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.cv-tabs-nav-item.active {
|
|
793
|
+
color: #007bff;
|
|
794
|
+
border-bottom-color: #007bff;
|
|
795
|
+
font-weight: 500;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
.cv-tabs-nav-item:focus {
|
|
799
|
+
outline: 2px solid #007bff;
|
|
800
|
+
outline-offset: 2px;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/* Tab panel base styles */
|
|
804
|
+
cv-tab {
|
|
805
|
+
display: block;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
/* Override visibility for tab panels - use display instead of collapse animation */
|
|
809
|
+
cv-tab.cv-hidden {
|
|
810
|
+
display: none !important;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
cv-tab.cv-visible {
|
|
814
|
+
display: block !important;
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
cv-tabgroup {
|
|
818
|
+
display: block;
|
|
819
|
+
margin-bottom: 1.5rem;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
/* Bottom border line for tab groups */
|
|
823
|
+
.cv-tabgroup-bottom-border {
|
|
824
|
+
border-bottom: 1px solid #dee2e6;
|
|
825
|
+
margin-top: 1rem;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
/* Tab content wrapper */
|
|
829
|
+
.cv-tab-content {
|
|
830
|
+
padding: 1rem 0;
|
|
831
|
+
}
|
|
684
832
|
`;
|
|
685
833
|
|
|
686
834
|
/**
|
|
687
835
|
* Combined core styles for toggles and tabs
|
|
688
836
|
*/
|
|
689
|
-
const CORE_STYLES = `
|
|
690
|
-
${TOGGLE_STYLES}
|
|
691
|
-
|
|
692
|
-
${TAB_STYLES}
|
|
837
|
+
const CORE_STYLES = `
|
|
838
|
+
${TOGGLE_STYLES}
|
|
839
|
+
|
|
840
|
+
${TAB_STYLES}
|
|
693
841
|
`;
|
|
694
842
|
/**
|
|
695
843
|
* Add styles for hiding and showing toggles animations and transitions to the document head
|
|
@@ -819,23 +967,10 @@ class CustomViewsCore {
|
|
|
819
967
|
this.lastAppliedState = this.cloneState(state);
|
|
820
968
|
const toggles = state.toggles || [];
|
|
821
969
|
const finalToggles = this.visibilityManager.filterVisibleToggles(toggles);
|
|
822
|
-
//
|
|
823
|
-
this.rootEl
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
this.visibilityManager.applyElementVisibility(el, shouldShow);
|
|
827
|
-
});
|
|
828
|
-
// Render toggles
|
|
829
|
-
for (const category of finalToggles) {
|
|
830
|
-
this.rootEl.querySelectorAll(`[data-cv-toggle="${category}"], [data-customviews-toggle="${category}"]`).forEach(el => {
|
|
831
|
-
// if it has an id, then we should render the asset into it
|
|
832
|
-
// Support both (data-cv-id) and (data-customviews-id) attributes
|
|
833
|
-
const toggleId = el.dataset.cvId || el.dataset.customviewsId;
|
|
834
|
-
if (toggleId) {
|
|
835
|
-
renderAssetInto(el, toggleId, this.assetsManager);
|
|
836
|
-
}
|
|
837
|
-
});
|
|
838
|
-
}
|
|
970
|
+
// Apply toggle visibility
|
|
971
|
+
ToggleManager.applyToggles(this.rootEl, finalToggles);
|
|
972
|
+
// Render assets into toggles
|
|
973
|
+
ToggleManager.renderAssets(this.rootEl, finalToggles, this.assetsManager);
|
|
839
974
|
// Apply tab selections
|
|
840
975
|
TabManager.applySelections(this.rootEl, state.tabs || {}, this.config.tabGroups);
|
|
841
976
|
// Update nav active states (without rebuilding)
|
|
@@ -948,52 +1083,6 @@ class CustomViewsCore {
|
|
|
948
1083
|
}
|
|
949
1084
|
}
|
|
950
1085
|
|
|
951
|
-
class AssetsManager {
|
|
952
|
-
assets;
|
|
953
|
-
baseURL;
|
|
954
|
-
constructor(assets, baseURL = '') {
|
|
955
|
-
this.assets = assets;
|
|
956
|
-
this.baseURL = baseURL;
|
|
957
|
-
if (!this.validate()) {
|
|
958
|
-
console.warn('Invalid assets:', this.assets);
|
|
959
|
-
}
|
|
960
|
-
}
|
|
961
|
-
// Check each asset has content or src
|
|
962
|
-
validate() {
|
|
963
|
-
return Object.values(this.assets).every(a => a.src || a.content);
|
|
964
|
-
}
|
|
965
|
-
get(assetId) {
|
|
966
|
-
const asset = this.assets[assetId];
|
|
967
|
-
if (!asset)
|
|
968
|
-
return undefined;
|
|
969
|
-
// If there's a baseURL and the asset has a src property, prepend the baseURL
|
|
970
|
-
if (this.baseURL && asset.src) {
|
|
971
|
-
// Create a shallow copy to avoid mutating the original asset
|
|
972
|
-
return {
|
|
973
|
-
...asset,
|
|
974
|
-
src: this.prependBaseURL(asset.src)
|
|
975
|
-
};
|
|
976
|
-
}
|
|
977
|
-
return asset;
|
|
978
|
-
}
|
|
979
|
-
prependBaseURL(path) {
|
|
980
|
-
// Don't prepend if the path is already absolute (starts with http:// or https://)
|
|
981
|
-
if (path.startsWith('http://') || path.startsWith('https://')) {
|
|
982
|
-
return path;
|
|
983
|
-
}
|
|
984
|
-
// Ensure baseURL doesn't end with / and path starts with /
|
|
985
|
-
const cleanBaseURL = this.baseURL.endsWith('/') ? this.baseURL.slice(0, -1) : this.baseURL;
|
|
986
|
-
const cleanPath = path.startsWith('/') ? path : '/' + path;
|
|
987
|
-
return cleanBaseURL + cleanPath;
|
|
988
|
-
}
|
|
989
|
-
loadFromJSON(json) {
|
|
990
|
-
this.assets = json;
|
|
991
|
-
}
|
|
992
|
-
loadAdditionalAssets(additionalAssets) {
|
|
993
|
-
this.assets = { ...this.assets, ...additionalAssets };
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
|
|
997
1086
|
/**
|
|
998
1087
|
* Helper function to prepend baseUrl to a path
|
|
999
1088
|
* @param path The path to prepend the baseUrl to
|
|
@@ -1013,6 +1102,57 @@ function prependBaseUrl(path, baseUrl) {
|
|
|
1013
1102
|
return cleanbaseUrl + cleanPath;
|
|
1014
1103
|
}
|
|
1015
1104
|
|
|
1105
|
+
/**
|
|
1106
|
+
* Custom Elements for Tab Groups and Tabs
|
|
1107
|
+
*/
|
|
1108
|
+
/**
|
|
1109
|
+
* <cv-tab> element - represents a single tab panel
|
|
1110
|
+
*/
|
|
1111
|
+
class CVTab extends HTMLElement {
|
|
1112
|
+
connectedCallback() {
|
|
1113
|
+
// Element is managed by TabManager
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
/**
|
|
1117
|
+
* <cv-tabgroup> element - represents a group of tabs
|
|
1118
|
+
*/
|
|
1119
|
+
class CVTabgroup extends HTMLElement {
|
|
1120
|
+
connectedCallback() {
|
|
1121
|
+
// Element is managed by TabManager
|
|
1122
|
+
// Emit ready event after a brief delay to ensure children are parsed
|
|
1123
|
+
setTimeout(() => {
|
|
1124
|
+
const event = new CustomEvent('cv:tabgroup-ready', {
|
|
1125
|
+
bubbles: true,
|
|
1126
|
+
detail: { groupId: this.getAttribute('id') }
|
|
1127
|
+
});
|
|
1128
|
+
this.dispatchEvent(event);
|
|
1129
|
+
}, 0);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* <cv-toggle> element - represents a toggleable content block
|
|
1134
|
+
*/
|
|
1135
|
+
class CVToggle extends HTMLElement {
|
|
1136
|
+
connectedCallback() {
|
|
1137
|
+
// Element is managed by Core
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Register custom elements
|
|
1142
|
+
*/
|
|
1143
|
+
function registerCustomElements() {
|
|
1144
|
+
// Only register if not already defined
|
|
1145
|
+
if (!customElements.get('cv-tab')) {
|
|
1146
|
+
customElements.define('cv-tab', CVTab);
|
|
1147
|
+
}
|
|
1148
|
+
if (!customElements.get('cv-tabgroup')) {
|
|
1149
|
+
customElements.define('cv-tabgroup', CVTabgroup);
|
|
1150
|
+
}
|
|
1151
|
+
if (!customElements.get('cv-toggle')) {
|
|
1152
|
+
customElements.define('cv-toggle', CVToggle);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1016
1156
|
/**
|
|
1017
1157
|
* Main CustomViews class for initializing and managing custom views
|
|
1018
1158
|
*/
|
|
@@ -1023,6 +1163,8 @@ class CustomViews {
|
|
|
1023
1163
|
* @returns Promise resolving to the CustomViewsCore instance or null if initialization fails
|
|
1024
1164
|
*/
|
|
1025
1165
|
static async init(opts) {
|
|
1166
|
+
// Register custom elements
|
|
1167
|
+
registerCustomElements();
|
|
1026
1168
|
// Load assets JSON if provided
|
|
1027
1169
|
let assetsManager;
|
|
1028
1170
|
const baseURL = opts.baseURL || '';
|
|
@@ -1065,627 +1207,644 @@ class CustomViews {
|
|
|
1065
1207
|
* Note: Styles are kept as a TypeScript string for compatibility with the build system.
|
|
1066
1208
|
* This approach ensures the styles are properly bundled and don't require separate CSS file handling.
|
|
1067
1209
|
*/
|
|
1068
|
-
const WIDGET_STYLES = `
|
|
1069
|
-
/* Rounded rectangle widget icon styles */
|
|
1070
|
-
.cv-widget-icon {
|
|
1071
|
-
position: fixed;
|
|
1072
|
-
/* Slightly transparent by default so the widget is subtle at the page edge */
|
|
1073
|
-
background: rgba(255, 255, 255, 0.92);
|
|
1074
|
-
color: rgba(0, 0, 0, 0.9);
|
|
1075
|
-
opacity: 0.6;
|
|
1076
|
-
display: flex;
|
|
1077
|
-
align-items: center;
|
|
1078
|
-
justify-content: center;
|
|
1079
|
-
font-size: 18px;
|
|
1080
|
-
font-weight: bold;
|
|
1081
|
-
cursor: pointer;
|
|
1082
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1083
|
-
z-index: 9998;
|
|
1084
|
-
transition: all 0.3s ease;
|
|
1085
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
.cv-widget-icon:hover {
|
|
1089
|
-
/* Become fully opaque on hover to improve readability */
|
|
1090
|
-
background: rgba(255, 255, 255, 1);
|
|
1091
|
-
color: rgba(0, 0, 0, 1);
|
|
1092
|
-
opacity: 1;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
/* Top-right: rounded end on left, sticks out leftward on hover */
|
|
1096
|
-
.cv-widget-top-right {
|
|
1097
|
-
top: 20px;
|
|
1098
|
-
right: 0;
|
|
1099
|
-
border-radius: 18px 0 0 18px;
|
|
1100
|
-
padding-left: 8px;
|
|
1101
|
-
justify-content: flex-start;
|
|
1102
|
-
}
|
|
1103
|
-
|
|
1104
|
-
/* Top-left: rounded end on right, sticks out rightward on hover */
|
|
1105
|
-
.cv-widget-top-left {
|
|
1106
|
-
top: 20px;
|
|
1107
|
-
left: 0;
|
|
1108
|
-
border-radius: 0 18px 18px 0;
|
|
1109
|
-
padding-right: 8px;
|
|
1110
|
-
justify-content: flex-end;
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
/* Bottom-right: rounded end on left, sticks out leftward on hover */
|
|
1114
|
-
.cv-widget-bottom-right {
|
|
1115
|
-
bottom: 20px;
|
|
1116
|
-
right: 0;
|
|
1117
|
-
border-radius: 18px 0 0 18px;
|
|
1118
|
-
padding-left: 8px;
|
|
1119
|
-
justify-content: flex-start;
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
/* Bottom-left: rounded end on right, sticks out rightward on hover */
|
|
1123
|
-
.cv-widget-bottom-left {
|
|
1124
|
-
bottom: 20px;
|
|
1125
|
-
left: 0;
|
|
1126
|
-
border-radius: 0 18px 18px 0;
|
|
1127
|
-
padding-right: 8px;
|
|
1128
|
-
justify-content: flex-end;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
/* Middle-left: rounded end on right, sticks out rightward on hover */
|
|
1132
|
-
.cv-widget-middle-left {
|
|
1133
|
-
top: 50%;
|
|
1134
|
-
left: 0;
|
|
1135
|
-
transform: translateY(-50%);
|
|
1136
|
-
border-radius: 0 18px 18px 0;
|
|
1137
|
-
padding-right: 8px;
|
|
1138
|
-
justify-content: flex-end;
|
|
1139
|
-
}
|
|
1140
|
-
|
|
1141
|
-
/* Middle-right: rounded end on left, sticks out leftward on hover */
|
|
1142
|
-
.cv-widget-middle-right {
|
|
1143
|
-
top: 50%;
|
|
1144
|
-
right: 0;
|
|
1145
|
-
transform: translateY(-50%);
|
|
1146
|
-
border-radius: 18px 0 0 18px;
|
|
1147
|
-
padding-left: 8px;
|
|
1148
|
-
justify-content: flex-start;
|
|
1149
|
-
}
|
|
1150
|
-
|
|
1151
|
-
.cv-widget-top-right,
|
|
1152
|
-
.cv-widget-middle-right,
|
|
1153
|
-
.cv-widget-bottom-right,
|
|
1154
|
-
.cv-widget-top-left,
|
|
1155
|
-
.cv-widget-middle-left,
|
|
1156
|
-
.cv-widget-bottom-left {
|
|
1157
|
-
height: 36px;
|
|
1158
|
-
width: 36px;
|
|
1159
|
-
}
|
|
1160
|
-
|
|
1161
|
-
.cv-widget-middle-right:hover,
|
|
1162
|
-
.cv-widget-top-right:hover,
|
|
1163
|
-
.cv-widget-bottom-right:hover,
|
|
1164
|
-
.cv-widget-top-left:hover,
|
|
1165
|
-
.cv-widget-middle-left:hover,
|
|
1166
|
-
.cv-widget-bottom-left:hover {
|
|
1167
|
-
width: 55px;
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
/* Modal content styles */
|
|
1171
|
-
.cv-widget-section {
|
|
1172
|
-
margin-bottom: 16px;
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
.cv-widget-section:last-child {
|
|
1176
|
-
margin-bottom: 0;
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
.cv-widget-section label {
|
|
1180
|
-
display: block;
|
|
1181
|
-
margin-bottom: 4px;
|
|
1182
|
-
font-weight: 500;
|
|
1183
|
-
color: #555;
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
.cv-widget-profile-select,
|
|
1187
|
-
.cv-widget-state-select {
|
|
1188
|
-
width: 100%;
|
|
1189
|
-
padding: 8px 12px;
|
|
1190
|
-
border: 1px solid #ddd;
|
|
1191
|
-
border-radius: 4px;
|
|
1192
|
-
background: white;
|
|
1193
|
-
font-size: 14px;
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
.cv-widget-profile-select:focus,
|
|
1197
|
-
.cv-widget-state-select:focus {
|
|
1198
|
-
outline: none;
|
|
1199
|
-
border-color: #007bff;
|
|
1200
|
-
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
.cv-widget-profile-select:disabled,
|
|
1204
|
-
.cv-widget-state-select:disabled {
|
|
1205
|
-
background: #f8f9fa;
|
|
1206
|
-
color: #6c757d;
|
|
1207
|
-
cursor: not-allowed;
|
|
1208
|
-
}
|
|
1209
|
-
|
|
1210
|
-
.cv-widget-current {
|
|
1211
|
-
margin: 16px 0;
|
|
1212
|
-
padding: 12px;
|
|
1213
|
-
background: #f8f9fa;
|
|
1214
|
-
border-radius: 4px;
|
|
1215
|
-
border-left: 4px solid #007bff;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
.cv-widget-current label {
|
|
1219
|
-
font-size: 12px;
|
|
1220
|
-
text-transform: uppercase;
|
|
1221
|
-
letter-spacing: 0.5px;
|
|
1222
|
-
color: #666;
|
|
1223
|
-
margin-bottom: 4px;
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
.cv-widget-current-view {
|
|
1227
|
-
font-weight: 500;
|
|
1228
|
-
color: #333;
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
.cv-widget-reset {
|
|
1232
|
-
width: 100%;
|
|
1233
|
-
padding: 8px 16px;
|
|
1234
|
-
background: #dc3545;
|
|
1235
|
-
color: white;
|
|
1236
|
-
border: none;
|
|
1237
|
-
border-radius: 4px;
|
|
1238
|
-
cursor: pointer;
|
|
1239
|
-
font-size: 14px;
|
|
1240
|
-
font-weight: 500;
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
.cv-widget-reset:hover {
|
|
1244
|
-
background: #c82333;
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
.cv-widget-reset:active {
|
|
1248
|
-
background: #bd2130;
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
/* Responsive design for mobile */
|
|
1252
|
-
@media (max-width: 768px) {
|
|
1253
|
-
.cv-widget-top-right,
|
|
1254
|
-
.cv-widget-top-left {
|
|
1255
|
-
top: 10px;
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
.cv-widget-bottom-right,
|
|
1259
|
-
.cv-widget-bottom-left {
|
|
1260
|
-
bottom: 10px;
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
/* All widgets stay flush with screen edges */
|
|
1264
|
-
.cv-widget-top-right,
|
|
1265
|
-
.cv-widget-bottom-right,
|
|
1266
|
-
.cv-widget-middle-right {
|
|
1267
|
-
right: 0;
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
.cv-widget-top-left,
|
|
1271
|
-
.cv-widget-bottom-left,
|
|
1272
|
-
.cv-widget-middle-left {
|
|
1273
|
-
left: 0;
|
|
1274
|
-
}
|
|
1275
|
-
|
|
1276
|
-
/* Slightly smaller on mobile */
|
|
1277
|
-
.cv-widget-icon {
|
|
1278
|
-
width: 60px;
|
|
1279
|
-
height: 32px;
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
.cv-widget-icon:hover {
|
|
1283
|
-
width: 75px;
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
/* Modal styles */
|
|
1288
|
-
.cv-widget-modal-overlay {
|
|
1289
|
-
position: fixed;
|
|
1290
|
-
top: 0;
|
|
1291
|
-
left: 0;
|
|
1292
|
-
right: 0;
|
|
1293
|
-
bottom: 0;
|
|
1294
|
-
background: rgba(0, 0, 0, 0.5);
|
|
1295
|
-
display: flex;
|
|
1296
|
-
align-items: center;
|
|
1297
|
-
justify-content: center;
|
|
1298
|
-
z-index: 10002;
|
|
1299
|
-
animation: fadeIn 0.2s ease;
|
|
1300
|
-
}
|
|
1301
|
-
|
|
1302
|
-
@keyframes fadeIn {
|
|
1303
|
-
from { opacity: 0; }
|
|
1304
|
-
to { opacity: 1; }
|
|
1305
|
-
}
|
|
1306
|
-
|
|
1307
|
-
.cv-widget-modal {
|
|
1308
|
-
background: white;
|
|
1309
|
-
border-radius: 8px;
|
|
1310
|
-
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
1311
|
-
max-width: 400px;
|
|
1312
|
-
width: 90vw;
|
|
1313
|
-
max-height: 80vh;
|
|
1314
|
-
overflow-y: auto;
|
|
1315
|
-
animation: slideIn 0.2s ease;
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
@keyframes slideIn {
|
|
1319
|
-
from {
|
|
1320
|
-
opacity: 0;
|
|
1321
|
-
transform: scale(0.9) translateY(-20px);
|
|
1322
|
-
}
|
|
1323
|
-
to {
|
|
1324
|
-
opacity: 1;
|
|
1325
|
-
transform: scale(1) translateY(0);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
.cv-widget-modal-header {
|
|
1330
|
-
display: flex;
|
|
1331
|
-
justify-content: space-between;
|
|
1332
|
-
align-items: center;
|
|
1333
|
-
padding: 16px 20px;
|
|
1334
|
-
border-bottom: 1px solid #e9ecef;
|
|
1335
|
-
background: #f8f9fa;
|
|
1336
|
-
border-radius: 8px 8px 0 0;
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
.cv-widget-modal-header h3 {
|
|
1340
|
-
margin: 0;
|
|
1341
|
-
font-size: 18px;
|
|
1342
|
-
font-weight: 600;
|
|
1343
|
-
color: #333;
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
.cv-widget-modal-close {
|
|
1347
|
-
background: none;
|
|
1348
|
-
border: none;
|
|
1349
|
-
font-size:
|
|
1350
|
-
cursor: pointer;
|
|
1351
|
-
padding: 0;
|
|
1352
|
-
width: 32px;
|
|
1353
|
-
height: 32px;
|
|
1354
|
-
display: flex;
|
|
1355
|
-
align-items: center;
|
|
1356
|
-
justify-content: center;
|
|
1357
|
-
border-radius: 4px;
|
|
1358
|
-
color: #666;
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
.cv-custom-state-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
.cv-custom-state-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
.cv-custom-state-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
.cv-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
.cv-custom-state-reset
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
}
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
.cv-welcome-got-it
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1686
|
-
.cv-
|
|
1687
|
-
|
|
1688
|
-
}
|
|
1210
|
+
const WIDGET_STYLES = `
|
|
1211
|
+
/* Rounded rectangle widget icon styles */
|
|
1212
|
+
.cv-widget-icon {
|
|
1213
|
+
position: fixed;
|
|
1214
|
+
/* Slightly transparent by default so the widget is subtle at the page edge */
|
|
1215
|
+
background: rgba(255, 255, 255, 0.92);
|
|
1216
|
+
color: rgba(0, 0, 0, 0.9);
|
|
1217
|
+
opacity: 0.6;
|
|
1218
|
+
display: flex;
|
|
1219
|
+
align-items: center;
|
|
1220
|
+
justify-content: center;
|
|
1221
|
+
font-size: 18px;
|
|
1222
|
+
font-weight: bold;
|
|
1223
|
+
cursor: pointer;
|
|
1224
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1225
|
+
z-index: 9998;
|
|
1226
|
+
transition: all 0.3s ease;
|
|
1227
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.cv-widget-icon:hover {
|
|
1231
|
+
/* Become fully opaque on hover to improve readability */
|
|
1232
|
+
background: rgba(255, 255, 255, 1);
|
|
1233
|
+
color: rgba(0, 0, 0, 1);
|
|
1234
|
+
opacity: 1;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/* Top-right: rounded end on left, sticks out leftward on hover */
|
|
1238
|
+
.cv-widget-top-right {
|
|
1239
|
+
top: 20px;
|
|
1240
|
+
right: 0;
|
|
1241
|
+
border-radius: 18px 0 0 18px;
|
|
1242
|
+
padding-left: 8px;
|
|
1243
|
+
justify-content: flex-start;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
/* Top-left: rounded end on right, sticks out rightward on hover */
|
|
1247
|
+
.cv-widget-top-left {
|
|
1248
|
+
top: 20px;
|
|
1249
|
+
left: 0;
|
|
1250
|
+
border-radius: 0 18px 18px 0;
|
|
1251
|
+
padding-right: 8px;
|
|
1252
|
+
justify-content: flex-end;
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
/* Bottom-right: rounded end on left, sticks out leftward on hover */
|
|
1256
|
+
.cv-widget-bottom-right {
|
|
1257
|
+
bottom: 20px;
|
|
1258
|
+
right: 0;
|
|
1259
|
+
border-radius: 18px 0 0 18px;
|
|
1260
|
+
padding-left: 8px;
|
|
1261
|
+
justify-content: flex-start;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
/* Bottom-left: rounded end on right, sticks out rightward on hover */
|
|
1265
|
+
.cv-widget-bottom-left {
|
|
1266
|
+
bottom: 20px;
|
|
1267
|
+
left: 0;
|
|
1268
|
+
border-radius: 0 18px 18px 0;
|
|
1269
|
+
padding-right: 8px;
|
|
1270
|
+
justify-content: flex-end;
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
/* Middle-left: rounded end on right, sticks out rightward on hover */
|
|
1274
|
+
.cv-widget-middle-left {
|
|
1275
|
+
top: 50%;
|
|
1276
|
+
left: 0;
|
|
1277
|
+
transform: translateY(-50%);
|
|
1278
|
+
border-radius: 0 18px 18px 0;
|
|
1279
|
+
padding-right: 8px;
|
|
1280
|
+
justify-content: flex-end;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
/* Middle-right: rounded end on left, sticks out leftward on hover */
|
|
1284
|
+
.cv-widget-middle-right {
|
|
1285
|
+
top: 50%;
|
|
1286
|
+
right: 0;
|
|
1287
|
+
transform: translateY(-50%);
|
|
1288
|
+
border-radius: 18px 0 0 18px;
|
|
1289
|
+
padding-left: 8px;
|
|
1290
|
+
justify-content: flex-start;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
.cv-widget-top-right,
|
|
1294
|
+
.cv-widget-middle-right,
|
|
1295
|
+
.cv-widget-bottom-right,
|
|
1296
|
+
.cv-widget-top-left,
|
|
1297
|
+
.cv-widget-middle-left,
|
|
1298
|
+
.cv-widget-bottom-left {
|
|
1299
|
+
height: 36px;
|
|
1300
|
+
width: 36px;
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
.cv-widget-middle-right:hover,
|
|
1304
|
+
.cv-widget-top-right:hover,
|
|
1305
|
+
.cv-widget-bottom-right:hover,
|
|
1306
|
+
.cv-widget-top-left:hover,
|
|
1307
|
+
.cv-widget-middle-left:hover,
|
|
1308
|
+
.cv-widget-bottom-left:hover {
|
|
1309
|
+
width: 55px;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
/* Modal content styles */
|
|
1313
|
+
.cv-widget-section {
|
|
1314
|
+
margin-bottom: 16px;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
.cv-widget-section:last-child {
|
|
1318
|
+
margin-bottom: 0;
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
.cv-widget-section label {
|
|
1322
|
+
display: block;
|
|
1323
|
+
margin-bottom: 4px;
|
|
1324
|
+
font-weight: 500;
|
|
1325
|
+
color: #555;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.cv-widget-profile-select,
|
|
1329
|
+
.cv-widget-state-select {
|
|
1330
|
+
width: 100%;
|
|
1331
|
+
padding: 8px 12px;
|
|
1332
|
+
border: 1px solid #ddd;
|
|
1333
|
+
border-radius: 4px;
|
|
1334
|
+
background: white;
|
|
1335
|
+
font-size: 14px;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
.cv-widget-profile-select:focus,
|
|
1339
|
+
.cv-widget-state-select:focus {
|
|
1340
|
+
outline: none;
|
|
1341
|
+
border-color: #007bff;
|
|
1342
|
+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
.cv-widget-profile-select:disabled,
|
|
1346
|
+
.cv-widget-state-select:disabled {
|
|
1347
|
+
background: #f8f9fa;
|
|
1348
|
+
color: #6c757d;
|
|
1349
|
+
cursor: not-allowed;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
.cv-widget-current {
|
|
1353
|
+
margin: 16px 0;
|
|
1354
|
+
padding: 12px;
|
|
1355
|
+
background: #f8f9fa;
|
|
1356
|
+
border-radius: 4px;
|
|
1357
|
+
border-left: 4px solid #007bff;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
.cv-widget-current label {
|
|
1361
|
+
font-size: 12px;
|
|
1362
|
+
text-transform: uppercase;
|
|
1363
|
+
letter-spacing: 0.5px;
|
|
1364
|
+
color: #666;
|
|
1365
|
+
margin-bottom: 4px;
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
.cv-widget-current-view {
|
|
1369
|
+
font-weight: 500;
|
|
1370
|
+
color: #333;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
.cv-widget-reset {
|
|
1374
|
+
width: 100%;
|
|
1375
|
+
padding: 8px 16px;
|
|
1376
|
+
background: #dc3545;
|
|
1377
|
+
color: white;
|
|
1378
|
+
border: none;
|
|
1379
|
+
border-radius: 4px;
|
|
1380
|
+
cursor: pointer;
|
|
1381
|
+
font-size: 14px;
|
|
1382
|
+
font-weight: 500;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
.cv-widget-reset:hover {
|
|
1386
|
+
background: #c82333;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
.cv-widget-reset:active {
|
|
1390
|
+
background: #bd2130;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
/* Responsive design for mobile */
|
|
1394
|
+
@media (max-width: 768px) {
|
|
1395
|
+
.cv-widget-top-right,
|
|
1396
|
+
.cv-widget-top-left {
|
|
1397
|
+
top: 10px;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
.cv-widget-bottom-right,
|
|
1401
|
+
.cv-widget-bottom-left {
|
|
1402
|
+
bottom: 10px;
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
/* All widgets stay flush with screen edges */
|
|
1406
|
+
.cv-widget-top-right,
|
|
1407
|
+
.cv-widget-bottom-right,
|
|
1408
|
+
.cv-widget-middle-right {
|
|
1409
|
+
right: 0;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
.cv-widget-top-left,
|
|
1413
|
+
.cv-widget-bottom-left,
|
|
1414
|
+
.cv-widget-middle-left {
|
|
1415
|
+
left: 0;
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
/* Slightly smaller on mobile */
|
|
1419
|
+
.cv-widget-icon {
|
|
1420
|
+
width: 60px;
|
|
1421
|
+
height: 32px;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
.cv-widget-icon:hover {
|
|
1425
|
+
width: 75px;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
/* Modal styles */
|
|
1430
|
+
.cv-widget-modal-overlay {
|
|
1431
|
+
position: fixed;
|
|
1432
|
+
top: 0;
|
|
1433
|
+
left: 0;
|
|
1434
|
+
right: 0;
|
|
1435
|
+
bottom: 0;
|
|
1436
|
+
background: rgba(0, 0, 0, 0.5);
|
|
1437
|
+
display: flex;
|
|
1438
|
+
align-items: center;
|
|
1439
|
+
justify-content: center;
|
|
1440
|
+
z-index: 10002;
|
|
1441
|
+
animation: fadeIn 0.2s ease;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
@keyframes fadeIn {
|
|
1445
|
+
from { opacity: 0; }
|
|
1446
|
+
to { opacity: 1; }
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
.cv-widget-modal {
|
|
1450
|
+
background: white;
|
|
1451
|
+
border-radius: 8px;
|
|
1452
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
1453
|
+
max-width: 400px;
|
|
1454
|
+
width: 90vw;
|
|
1455
|
+
max-height: 80vh;
|
|
1456
|
+
overflow-y: auto;
|
|
1457
|
+
animation: slideIn 0.2s ease;
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
@keyframes slideIn {
|
|
1461
|
+
from {
|
|
1462
|
+
opacity: 0;
|
|
1463
|
+
transform: scale(0.9) translateY(-20px);
|
|
1464
|
+
}
|
|
1465
|
+
to {
|
|
1466
|
+
opacity: 1;
|
|
1467
|
+
transform: scale(1) translateY(0);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
.cv-widget-modal-header {
|
|
1472
|
+
display: flex;
|
|
1473
|
+
justify-content: space-between;
|
|
1474
|
+
align-items: center;
|
|
1475
|
+
padding: 16px 20px;
|
|
1476
|
+
border-bottom: 1px solid #e9ecef;
|
|
1477
|
+
background: #f8f9fa;
|
|
1478
|
+
border-radius: 8px 8px 0 0;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
.cv-widget-modal-header h3 {
|
|
1482
|
+
margin: 0;
|
|
1483
|
+
font-size: 18px;
|
|
1484
|
+
font-weight: 600;
|
|
1485
|
+
color: #333;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
.cv-widget-modal-close {
|
|
1489
|
+
background: none;
|
|
1490
|
+
border: none;
|
|
1491
|
+
font-size: 20px;
|
|
1492
|
+
cursor: pointer;
|
|
1493
|
+
padding: 0;
|
|
1494
|
+
width: 32px;
|
|
1495
|
+
height: 32px;
|
|
1496
|
+
display: flex;
|
|
1497
|
+
align-items: center;
|
|
1498
|
+
justify-content: center;
|
|
1499
|
+
border-radius: 4px;
|
|
1500
|
+
color: #666;
|
|
1501
|
+
line-height: 1;
|
|
1502
|
+
transition: all 0.2s ease;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
.cv-widget-modal-close:hover {
|
|
1506
|
+
background: #e9ecef;
|
|
1507
|
+
color: #333;
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1510
|
+
.cv-widget-modal-content {
|
|
1511
|
+
padding: 20px;
|
|
1512
|
+
}
|
|
1513
|
+
|
|
1514
|
+
.cv-widget-modal-actions {
|
|
1515
|
+
margin-top: 20px;
|
|
1516
|
+
padding-top: 16px;
|
|
1517
|
+
border-top: 1px solid #e9ecef;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
.cv-widget-restore {
|
|
1521
|
+
width: 100%;
|
|
1522
|
+
padding: 10px 16px;
|
|
1523
|
+
background: #28a745;
|
|
1524
|
+
color: white;
|
|
1525
|
+
border: none;
|
|
1526
|
+
border-radius: 4px;
|
|
1527
|
+
cursor: pointer;
|
|
1528
|
+
font-size: 14px;
|
|
1529
|
+
font-weight: 500;
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
.cv-widget-restore:hover {
|
|
1533
|
+
background: #218838;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
.cv-widget-create-state {
|
|
1537
|
+
width: 100%;
|
|
1538
|
+
padding: 10px 16px;
|
|
1539
|
+
background: #007bff;
|
|
1540
|
+
color: white;
|
|
1541
|
+
border: none;
|
|
1542
|
+
border-radius: 4px;
|
|
1543
|
+
cursor: pointer;
|
|
1544
|
+
font-size: 14px;
|
|
1545
|
+
font-weight: 500;
|
|
1546
|
+
margin-bottom: 10px;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
.cv-widget-create-state:hover {
|
|
1550
|
+
background: #0056b3;
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
/* Dark theme modal styles */
|
|
1554
|
+
.cv-widget-theme-dark .cv-widget-modal {
|
|
1555
|
+
background: #2d3748;
|
|
1556
|
+
color: #e2e8f0;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
.cv-widget-theme-dark .cv-widget-modal-header {
|
|
1560
|
+
background: #1a202c;
|
|
1561
|
+
border-color: #4a5568;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
.cv-widget-theme-dark .cv-widget-modal-header h3 {
|
|
1565
|
+
color: #e2e8f0;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
.cv-widget-theme-dark .cv-widget-modal-close {
|
|
1569
|
+
color: #a0aec0;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
.cv-widget-theme-dark .cv-widget-modal-close:hover {
|
|
1573
|
+
background: #4a5568;
|
|
1574
|
+
color: #e2e8f0;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
.cv-widget-theme-dark .cv-widget-modal-actions {
|
|
1578
|
+
border-color: #4a5568;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
/* Custom state creator styles */
|
|
1582
|
+
.cv-custom-state-modal {
|
|
1583
|
+
max-width: 500px;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
.cv-custom-state-form h4 {
|
|
1587
|
+
margin: 20px 0 10px 0;
|
|
1588
|
+
font-size: 16px;
|
|
1589
|
+
font-weight: 600;
|
|
1590
|
+
color: #333;
|
|
1591
|
+
border-bottom: 1px solid #e9ecef;
|
|
1592
|
+
padding-bottom: 5px;
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
.cv-custom-state-form p {
|
|
1596
|
+
font-size: 15px;
|
|
1597
|
+
line-height: 1.6;
|
|
1598
|
+
color: #555;
|
|
1599
|
+
margin-bottom: 24px;
|
|
1600
|
+
text-align: justify;
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
.cv-custom-state-section {
|
|
1604
|
+
margin-bottom: 16px;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
.cv-custom-state-section label {
|
|
1608
|
+
display: block;
|
|
1609
|
+
margin-bottom: 4px;
|
|
1610
|
+
font-weight: 500;
|
|
1611
|
+
color: #555;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.cv-custom-state-input {
|
|
1615
|
+
width: 100%;
|
|
1616
|
+
padding: 8px 12px;
|
|
1617
|
+
border: 1px solid #ddd;
|
|
1618
|
+
border-radius: 4px;
|
|
1619
|
+
background: white;
|
|
1620
|
+
font-size: 14px;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
.cv-custom-state-input:focus {
|
|
1624
|
+
outline: none;
|
|
1625
|
+
border-color: #007bff;
|
|
1626
|
+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.cv-custom-toggles {
|
|
1630
|
+
display: grid;
|
|
1631
|
+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
|
1632
|
+
gap: 10px;
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
.cv-custom-state-toggle {
|
|
1636
|
+
display: flex;
|
|
1637
|
+
align-items: center;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
.cv-custom-state-toggle label {
|
|
1641
|
+
display: flex;
|
|
1642
|
+
align-items: center;
|
|
1643
|
+
cursor: pointer;
|
|
1644
|
+
font-weight: normal;
|
|
1645
|
+
margin: 0;
|
|
1646
|
+
}
|
|
1647
|
+
|
|
1648
|
+
.cv-custom-toggle-checkbox {
|
|
1649
|
+
margin-right: 8px;
|
|
1650
|
+
width: auto;
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
.cv-tab-groups {
|
|
1654
|
+
margin-top: 20px;
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
.cv-tab-group-control {
|
|
1658
|
+
margin-bottom: 15px;
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
.cv-tab-group-control label {
|
|
1662
|
+
display: block;
|
|
1663
|
+
margin-bottom: 5px;
|
|
1664
|
+
font-weight: 500;
|
|
1665
|
+
font-size: 14px;
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
.cv-tab-group-select {
|
|
1669
|
+
width: 100%;
|
|
1670
|
+
padding: 8px 12px;
|
|
1671
|
+
border: 1px solid #ced4da;
|
|
1672
|
+
border-radius: 4px;
|
|
1673
|
+
font-size: 14px;
|
|
1674
|
+
background-color: white;
|
|
1675
|
+
cursor: pointer;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
.cv-tab-group-select:focus {
|
|
1679
|
+
outline: none;
|
|
1680
|
+
border-color: #007bff;
|
|
1681
|
+
box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.cv-widget-theme-dark .cv-tab-group-select {
|
|
1685
|
+
background-color: #2d3748;
|
|
1686
|
+
border-color: #4a5568;
|
|
1687
|
+
color: #e2e8f0;
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
.cv-custom-state-actions {
|
|
1691
|
+
display: flex;
|
|
1692
|
+
gap: 10px;
|
|
1693
|
+
margin-top: 20px;
|
|
1694
|
+
padding-top: 16px;
|
|
1695
|
+
border-top: 1px solid #e9ecef;
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
.cv-custom-state-cancel,
|
|
1699
|
+
.cv-custom-state-copy-url {
|
|
1700
|
+
flex: 1;
|
|
1701
|
+
padding: 10px 16px;
|
|
1702
|
+
border: none;
|
|
1703
|
+
border-radius: 4px;
|
|
1704
|
+
cursor: pointer;
|
|
1705
|
+
font-size: 14px;
|
|
1706
|
+
font-weight: 500;
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
.cv-custom-state-reset {
|
|
1710
|
+
flex: 1;
|
|
1711
|
+
padding: 10px 16px;
|
|
1712
|
+
border: none;
|
|
1713
|
+
border-radius: 4px;
|
|
1714
|
+
cursor: pointer;
|
|
1715
|
+
font-size: 14px;
|
|
1716
|
+
font-weight: 500;
|
|
1717
|
+
background: #dc3545;
|
|
1718
|
+
color: white;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
.cv-custom-state-reset:hover {
|
|
1722
|
+
background: #c82333;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
.cv-custom-state-cancel {
|
|
1726
|
+
background: #6c757d;
|
|
1727
|
+
color: white;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
.cv-custom-state-cancel:hover {
|
|
1731
|
+
background: #5a6268;
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
.cv-custom-state-copy-url {
|
|
1735
|
+
background: #28a745;
|
|
1736
|
+
color: white;
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
.cv-custom-state-copy-url:hover {
|
|
1740
|
+
background: #218838;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
/* Dark theme custom state styles */
|
|
1744
|
+
.cv-widget-theme-dark .cv-custom-state-form h4 {
|
|
1745
|
+
color: #e2e8f0;
|
|
1746
|
+
border-color: #4a5568;
|
|
1747
|
+
}
|
|
1748
|
+
|
|
1749
|
+
.cv-widget-theme-dark .cv-custom-state-form p {
|
|
1750
|
+
color: #cbd5e0;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
.cv-widget-theme-dark .cv-custom-state-section label {
|
|
1754
|
+
color: #a0aec0;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
.cv-widget-theme-dark .cv-custom-state-input {
|
|
1758
|
+
background: #1a202c;
|
|
1759
|
+
border-color: #4a5568;
|
|
1760
|
+
color: #e2e8f0;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
.cv-widget-theme-dark .cv-custom-state-actions {
|
|
1764
|
+
border-color: #4a5568;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
/* Welcome modal styles */
|
|
1768
|
+
.cv-welcome-modal {
|
|
1769
|
+
max-width: 500px;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
.cv-welcome-content {
|
|
1773
|
+
text-align: center;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
.cv-welcome-content p {
|
|
1777
|
+
font-size: 15px;
|
|
1778
|
+
line-height: 1.6;
|
|
1779
|
+
color: #555;
|
|
1780
|
+
margin-bottom: 24px;
|
|
1781
|
+
text-align: justify;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
.cv-welcome-widget-preview {
|
|
1785
|
+
display: flex;
|
|
1786
|
+
flex-direction: column;
|
|
1787
|
+
align-items: center;
|
|
1788
|
+
gap: 12px;
|
|
1789
|
+
padding: 20px;
|
|
1790
|
+
background: #f8f9fa;
|
|
1791
|
+
border-radius: 8px;
|
|
1792
|
+
margin-bottom: 24px;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
.cv-welcome-widget-icon {
|
|
1796
|
+
width: 36px;
|
|
1797
|
+
height: 36px;
|
|
1798
|
+
background: white;
|
|
1799
|
+
color: black;
|
|
1800
|
+
border-radius: 0 18px 18px 0;
|
|
1801
|
+
display: flex;
|
|
1802
|
+
align-items: center;
|
|
1803
|
+
justify-content: center;
|
|
1804
|
+
font-size: 18px;
|
|
1805
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
.cv-welcome-widget-label {
|
|
1809
|
+
font-size: 14px;
|
|
1810
|
+
color: #666;
|
|
1811
|
+
margin: 0;
|
|
1812
|
+
font-weight: 500;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
.cv-welcome-got-it {
|
|
1816
|
+
width: 100%;
|
|
1817
|
+
padding: 12px 24px;
|
|
1818
|
+
background: #007bff;
|
|
1819
|
+
color: white;
|
|
1820
|
+
border: none;
|
|
1821
|
+
border-radius: 4px;
|
|
1822
|
+
cursor: pointer;
|
|
1823
|
+
font-size: 16px;
|
|
1824
|
+
font-weight: 600;
|
|
1825
|
+
transition: background 0.2s ease;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
.cv-welcome-got-it:hover {
|
|
1829
|
+
background: #0056b3;
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
.cv-welcome-got-it:active {
|
|
1833
|
+
background: #004494;
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/* Dark theme welcome modal styles */
|
|
1837
|
+
.cv-widget-theme-dark .cv-welcome-content p {
|
|
1838
|
+
color: #cbd5e0;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
.cv-widget-theme-dark .cv-welcome-widget-preview {
|
|
1842
|
+
background: #1a202c;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
.cv-widget-theme-dark .cv-welcome-widget-label {
|
|
1846
|
+
color: #a0aec0;
|
|
1847
|
+
}
|
|
1689
1848
|
`;
|
|
1690
1849
|
/**
|
|
1691
1850
|
* Inject widget styles into the document head
|
|
@@ -1720,8 +1879,8 @@ class CustomViewsWidget {
|
|
|
1720
1879
|
title: options.title || 'Custom Views',
|
|
1721
1880
|
description: options.description || 'Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.',
|
|
1722
1881
|
showWelcome: options.showWelcome ?? false,
|
|
1723
|
-
welcomeTitle: options.welcomeTitle || '
|
|
1724
|
-
welcomeMessage: options.welcomeMessage || 'This
|
|
1882
|
+
welcomeTitle: options.welcomeTitle || 'This website uses Custom Views',
|
|
1883
|
+
welcomeMessage: options.welcomeMessage || 'This site uses Custom Views to let you personalize your experience. Use the widget on the side (⚙) to show or hide different content sections based on your preferences. Your selections will be saved and can be shared via URL.',
|
|
1725
1884
|
showTabGroups: options.showTabGroups ?? true
|
|
1726
1885
|
};
|
|
1727
1886
|
// No external state manager to initialize
|
|
@@ -1801,13 +1960,13 @@ class CustomViewsWidget {
|
|
|
1801
1960
|
this.modal.className = 'cv-widget-modal-overlay';
|
|
1802
1961
|
this.applyThemeToModal();
|
|
1803
1962
|
const toggleControls = toggles.length
|
|
1804
|
-
? toggles.map(toggle => `
|
|
1805
|
-
<div class="cv-custom-state-toggle">
|
|
1806
|
-
<label>
|
|
1807
|
-
<input type="checkbox" class="cv-custom-toggle-checkbox" data-toggle="${toggle}" />
|
|
1808
|
-
${this.formatToggleName(toggle)}
|
|
1809
|
-
</label>
|
|
1810
|
-
</div>
|
|
1963
|
+
? toggles.map(toggle => `
|
|
1964
|
+
<div class="cv-custom-state-toggle">
|
|
1965
|
+
<label>
|
|
1966
|
+
<input type="checkbox" class="cv-custom-toggle-checkbox" data-toggle="${toggle}" />
|
|
1967
|
+
${this.formatToggleName(toggle)}
|
|
1968
|
+
</label>
|
|
1969
|
+
</div>
|
|
1811
1970
|
`).join('')
|
|
1812
1971
|
: `<p class="cv-no-toggles">No configurable sections available.</p>`;
|
|
1813
1972
|
// Get tab groups
|
|
@@ -1816,46 +1975,46 @@ class CustomViewsWidget {
|
|
|
1816
1975
|
if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
|
|
1817
1976
|
const tabGroupControls = tabGroups.map(group => {
|
|
1818
1977
|
const options = group.tabs.map(tab => `<option value="${tab.id}">${tab.label || tab.id}</option>`).join('');
|
|
1819
|
-
return `
|
|
1820
|
-
<div class="cv-tab-group-control">
|
|
1821
|
-
<label for="tab-group-${group.id}">${group.label || group.id}</label>
|
|
1822
|
-
<select id="tab-group-${group.id}" class="cv-tab-group-select" data-group-id="${group.id}">
|
|
1823
|
-
${options}
|
|
1824
|
-
</select>
|
|
1825
|
-
</div>
|
|
1978
|
+
return `
|
|
1979
|
+
<div class="cv-tab-group-control">
|
|
1980
|
+
<label for="tab-group-${group.id}">${group.label || group.id}</label>
|
|
1981
|
+
<select id="tab-group-${group.id}" class="cv-tab-group-select" data-group-id="${group.id}">
|
|
1982
|
+
${options}
|
|
1983
|
+
</select>
|
|
1984
|
+
</div>
|
|
1826
1985
|
`;
|
|
1827
1986
|
}).join('');
|
|
1828
|
-
tabGroupsHTML = `
|
|
1829
|
-
<h4>Tab Groups</h4>
|
|
1830
|
-
<div class="cv-tab-groups">
|
|
1831
|
-
${tabGroupControls}
|
|
1832
|
-
</div>
|
|
1987
|
+
tabGroupsHTML = `
|
|
1988
|
+
<h4>Tab Groups</h4>
|
|
1989
|
+
<div class="cv-tab-groups">
|
|
1990
|
+
${tabGroupControls}
|
|
1991
|
+
</div>
|
|
1833
1992
|
`;
|
|
1834
1993
|
}
|
|
1835
|
-
this.modal.innerHTML = `
|
|
1836
|
-
<div class="cv-widget-modal cv-custom-state-modal">
|
|
1837
|
-
<div class="cv-widget-modal-header">
|
|
1838
|
-
<h3>Customize View</h3>
|
|
1839
|
-
<button class="cv-widget-modal-close" aria-label="Close modal"
|
|
1840
|
-
</div>
|
|
1841
|
-
<div class="cv-widget-modal-content">
|
|
1842
|
-
<div class="cv-custom-state-form">
|
|
1843
|
-
<p>${this.options.description}</p>
|
|
1844
|
-
|
|
1845
|
-
<h4>Content Sections</h4>
|
|
1846
|
-
<div class="cv-custom-toggles">
|
|
1847
|
-
${toggleControls}
|
|
1848
|
-
</div>
|
|
1849
|
-
|
|
1850
|
-
${tabGroupsHTML}
|
|
1851
|
-
|
|
1852
|
-
<div class="cv-custom-state-actions">
|
|
1853
|
-
${this.options.showReset ? `<button class="cv-custom-state-reset">Reset to Default</button>` : ''}
|
|
1854
|
-
<button class="cv-custom-state-copy-url">Copy Shareable URL</button>
|
|
1855
|
-
</div>
|
|
1856
|
-
</div>
|
|
1857
|
-
</div>
|
|
1858
|
-
</div>
|
|
1994
|
+
this.modal.innerHTML = `
|
|
1995
|
+
<div class="cv-widget-modal cv-custom-state-modal">
|
|
1996
|
+
<div class="cv-widget-modal-header">
|
|
1997
|
+
<h3>Customize View</h3>
|
|
1998
|
+
<button class="cv-widget-modal-close" aria-label="Close modal">×</button>
|
|
1999
|
+
</div>
|
|
2000
|
+
<div class="cv-widget-modal-content">
|
|
2001
|
+
<div class="cv-custom-state-form">
|
|
2002
|
+
<p>${this.options.description}</p>
|
|
2003
|
+
|
|
2004
|
+
<h4>Content Sections</h4>
|
|
2005
|
+
<div class="cv-custom-toggles">
|
|
2006
|
+
${toggleControls}
|
|
2007
|
+
</div>
|
|
2008
|
+
|
|
2009
|
+
${tabGroupsHTML}
|
|
2010
|
+
|
|
2011
|
+
<div class="cv-custom-state-actions">
|
|
2012
|
+
${this.options.showReset ? `<button class="cv-custom-state-reset">Reset to Default</button>` : ''}
|
|
2013
|
+
<button class="cv-custom-state-copy-url">Copy Shareable URL</button>
|
|
2014
|
+
</div>
|
|
2015
|
+
</div>
|
|
2016
|
+
</div>
|
|
2017
|
+
</div>
|
|
1859
2018
|
`;
|
|
1860
2019
|
document.body.appendChild(this.modal);
|
|
1861
2020
|
this.attachStateModalEventListeners();
|
|
@@ -2040,25 +2199,25 @@ class CustomViewsWidget {
|
|
|
2040
2199
|
this.modal = document.createElement('div');
|
|
2041
2200
|
this.modal.className = 'cv-widget-modal-overlay cv-welcome-modal-overlay';
|
|
2042
2201
|
this.applyThemeToModal();
|
|
2043
|
-
this.modal.innerHTML = `
|
|
2044
|
-
<div class="cv-widget-modal cv-welcome-modal">
|
|
2045
|
-
<div class="cv-widget-modal-header">
|
|
2046
|
-
<h3>${this.options.welcomeTitle}</h3>
|
|
2047
|
-
<button class="cv-widget-modal-close" aria-label="Close modal">×</button>
|
|
2048
|
-
</div>
|
|
2049
|
-
<div class="cv-widget-modal-content">
|
|
2050
|
-
<div class="cv-welcome-content">
|
|
2051
|
-
<p>${this.options.welcomeMessage}</p>
|
|
2052
|
-
|
|
2053
|
-
<div class="cv-welcome-widget-preview">
|
|
2054
|
-
<div class="cv-welcome-widget-icon">⚙</div>
|
|
2055
|
-
<p class="cv-welcome-widget-label">Look for this widget on the side of the screen</p>
|
|
2056
|
-
</div>
|
|
2057
|
-
|
|
2058
|
-
<button class="cv-welcome-got-it">Got it!</button>
|
|
2059
|
-
</div>
|
|
2060
|
-
</div>
|
|
2061
|
-
</div>
|
|
2202
|
+
this.modal.innerHTML = `
|
|
2203
|
+
<div class="cv-widget-modal cv-welcome-modal">
|
|
2204
|
+
<div class="cv-widget-modal-header">
|
|
2205
|
+
<h3>${this.options.welcomeTitle}</h3>
|
|
2206
|
+
<button class="cv-widget-modal-close" aria-label="Close modal">×</button>
|
|
2207
|
+
</div>
|
|
2208
|
+
<div class="cv-widget-modal-content">
|
|
2209
|
+
<div class="cv-welcome-content">
|
|
2210
|
+
<p>${this.options.welcomeMessage}</p>
|
|
2211
|
+
|
|
2212
|
+
<div class="cv-welcome-widget-preview">
|
|
2213
|
+
<div class="cv-welcome-widget-icon">⚙</div>
|
|
2214
|
+
<p class="cv-welcome-widget-label">Look for this widget on the side of the screen</p>
|
|
2215
|
+
</div>
|
|
2216
|
+
|
|
2217
|
+
<button class="cv-welcome-got-it">Got it!</button>
|
|
2218
|
+
</div>
|
|
2219
|
+
</div>
|
|
2220
|
+
</div>
|
|
2062
2221
|
`;
|
|
2063
2222
|
document.body.appendChild(this.modal);
|
|
2064
2223
|
this.attachWelcomeModalEventListeners();
|
|
@@ -2100,46 +2259,6 @@ class CustomViewsWidget {
|
|
|
2100
2259
|
}
|
|
2101
2260
|
}
|
|
2102
2261
|
|
|
2103
|
-
/**
|
|
2104
|
-
* Custom Elements for Tab Groups and Tabs
|
|
2105
|
-
*/
|
|
2106
|
-
/**
|
|
2107
|
-
* <cv-tab> element - represents a single tab panel
|
|
2108
|
-
*/
|
|
2109
|
-
class CVTab extends HTMLElement {
|
|
2110
|
-
connectedCallback() {
|
|
2111
|
-
// Element is managed by TabManager
|
|
2112
|
-
}
|
|
2113
|
-
}
|
|
2114
|
-
/**
|
|
2115
|
-
* <cv-tabgroup> element - represents a group of tabs
|
|
2116
|
-
*/
|
|
2117
|
-
class CVTabgroup extends HTMLElement {
|
|
2118
|
-
connectedCallback() {
|
|
2119
|
-
// Element is managed by TabManager
|
|
2120
|
-
// Emit ready event after a brief delay to ensure children are parsed
|
|
2121
|
-
setTimeout(() => {
|
|
2122
|
-
const event = new CustomEvent('cv:tabgroup-ready', {
|
|
2123
|
-
bubbles: true,
|
|
2124
|
-
detail: { groupId: this.getAttribute('id') }
|
|
2125
|
-
});
|
|
2126
|
-
this.dispatchEvent(event);
|
|
2127
|
-
}, 0);
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
/**
|
|
2131
|
-
* Register custom elements
|
|
2132
|
-
*/
|
|
2133
|
-
function registerCustomElements() {
|
|
2134
|
-
// Only register if not already defined
|
|
2135
|
-
if (!customElements.get('cv-tab')) {
|
|
2136
|
-
customElements.define('cv-tab', CVTab);
|
|
2137
|
-
}
|
|
2138
|
-
if (!customElements.get('cv-tabgroup')) {
|
|
2139
|
-
customElements.define('cv-tabgroup', CVTabgroup);
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
2262
|
/**
|
|
2144
2263
|
* Initialize CustomViews from script tag attributes and config file
|
|
2145
2264
|
* This function handles the automatic initialization of CustomViews when included via script tag
|
|
@@ -2168,8 +2287,6 @@ function initializeFromScript() {
|
|
|
2168
2287
|
return;
|
|
2169
2288
|
}
|
|
2170
2289
|
window.__customViewsInitInProgress = true;
|
|
2171
|
-
// Register custom elements early
|
|
2172
|
-
registerCustomElements();
|
|
2173
2290
|
try {
|
|
2174
2291
|
// Find the script tag
|
|
2175
2292
|
let scriptTag = document.currentScript;
|