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