@customviews-js/customviews 1.3.0 → 1.4.1-beta.0
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/dist/custom-views.core.cjs.js +578 -257
- package/dist/custom-views.core.cjs.js.map +1 -1
- package/dist/custom-views.core.esm.js +578 -257
- package/dist/custom-views.core.esm.js.map +1 -1
- package/dist/custom-views.esm.js +578 -257
- package/dist/custom-views.esm.js.map +1 -1
- package/dist/custom-views.js +578 -257
- 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 +6 -5
- package/dist/types/core/core.d.ts.map +1 -1
- package/dist/types/core/toggle-manager.d.ts +12 -2
- package/dist/types/core/toggle-manager.d.ts.map +1 -1
- package/dist/types/core/url-state-manager.d.ts.map +1 -1
- package/dist/types/core/widget.d.ts +1 -0
- package/dist/types/core/widget.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/dist/types/types/types.d.ts +4 -2
- package/dist/types/types/types.d.ts.map +1 -1
- package/dist/types/utils/icons.d.ts +6 -0
- package/dist/types/utils/icons.d.ts.map +1 -1
- package/package.json +10 -4
- package/dist/types/core/share-button.d.ts +0 -13
- package/dist/types/core/share-button.d.ts.map +0 -1
- package/dist/types/styles/share-button-styles.d.ts +0 -3
- package/dist/types/styles/share-button-styles.d.ts.map +0 -1
package/dist/custom-views.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @customviews-js/customviews v1.
|
|
2
|
+
* @customviews-js/customviews v1.4.1-beta.0
|
|
3
3
|
* (c) 2025 Chan Ger Teck
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -169,8 +169,12 @@
|
|
|
169
169
|
// Create a compact representation
|
|
170
170
|
const compact = {};
|
|
171
171
|
// Add toggles if present and non-empty
|
|
172
|
-
if (state.
|
|
173
|
-
compact.t = state.
|
|
172
|
+
if (state.shownToggles && state.shownToggles.length > 0) {
|
|
173
|
+
compact.t = state.shownToggles;
|
|
174
|
+
}
|
|
175
|
+
// Add peek toggles if present and non-empty
|
|
176
|
+
if (state.peekToggles && state.peekToggles.length > 0) {
|
|
177
|
+
compact.p = state.peekToggles;
|
|
174
178
|
}
|
|
175
179
|
// Add tab groups if present
|
|
176
180
|
if (state.tabs && Object.keys(state.tabs).length > 0) {
|
|
@@ -231,7 +235,8 @@
|
|
|
231
235
|
// Reconstruct State from compact format
|
|
232
236
|
// Reconstruct Toggles
|
|
233
237
|
const state = {
|
|
234
|
-
|
|
238
|
+
shownToggles: Array.isArray(compact.t) ? compact.t : [],
|
|
239
|
+
peekToggles: Array.isArray(compact.p) ? compact.p : []
|
|
235
240
|
};
|
|
236
241
|
// Reconstruct Tabs
|
|
237
242
|
if (Array.isArray(compact.g)) {
|
|
@@ -436,6 +441,20 @@
|
|
|
436
441
|
<path fill="currentColor" d="M11 6.41V12a1 1 0 0 0 2 0V6.41l1.29 1.3a1 1 0 0 0 1.42 0a1 1 0 0 0 0-1.42l-3-3a1 1 0 0 0-1.42 0l-3 3a1 1 0 1 0 1.42 1.42L11 6.41z"/>
|
|
437
442
|
</svg>`;
|
|
438
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* GitHub icon for footer link
|
|
446
|
+
*/
|
|
447
|
+
function getGitHubIcon() {
|
|
448
|
+
return `<svg viewBox="0 0 98 96" width="16" height="16" xmlns="http://www.w3.org/2000/svg" fill="currentColor">
|
|
449
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M48.854 0C21.839 0 0 22 0 49.217c0 21.756 13.993 40.172 33.405 46.69 2.427.49 3.316-1.059 3.316-2.362 0-1.141-.08-5.052-.08-9.127-13.59 2.934-16.42-5.867-16.42-5.867-2.184-5.704-5.42-7.17-5.42-7.17-4.448-3.015.324-3.015.324-3.015 4.934.326 7.523 5.052 7.523 5.052 4.367 7.496 11.404 5.378 14.235 4.074.404-3.178 1.699-5.378 3.074-6.6-10.839-1.141-22.243-5.378-22.243-24.283 0-5.378 1.94-9.778 5.014-13.2-.485-1.222-2.184-6.275.486-13.038 0 0 4.125-1.304 13.426 5.052a46.97 46.97 0 0 1 12.214-1.63c4.125 0 8.33.571 12.213 1.63 9.302-6.356 13.427-5.052 13.427-5.052 2.67 6.763.97 11.816.485 13.038 3.155 3.422 5.015 7.822 5.015 13.2 0 18.905-11.404 23.06-22.324 24.283 1.78 1.548 3.316 4.481 3.316 9.126 0 6.6-.08 11.897-.08 13.526 0 1.304.89 2.853 3.316 2.364 19.412-6.52 33.405-24.935 33.405-46.691C97.707 22 75.788 0 48.854 0z"/>
|
|
450
|
+
</svg>`;
|
|
451
|
+
}
|
|
452
|
+
function getChevronDownIcon() {
|
|
453
|
+
return `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"></polyline></svg>`;
|
|
454
|
+
}
|
|
455
|
+
function getChevronUpIcon() {
|
|
456
|
+
return `<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="18 15 12 9 6 15"></polyline></svg>`;
|
|
457
|
+
}
|
|
439
458
|
|
|
440
459
|
// Constants for selectors
|
|
441
460
|
const TABGROUP_SELECTOR$1 = 'cv-tabgroup';
|
|
@@ -1006,20 +1025,33 @@
|
|
|
1006
1025
|
* ToggleManager handles discovery, visibility, and asset rendering for toggle elements
|
|
1007
1026
|
*/
|
|
1008
1027
|
class ToggleManager {
|
|
1028
|
+
/**
|
|
1029
|
+
* Track locally expanded elements (that were in peek mode but user expanded them)
|
|
1030
|
+
*/
|
|
1031
|
+
static expandedPeekElements = new WeakSet();
|
|
1009
1032
|
/**
|
|
1010
1033
|
* Apply toggle visibility to a given list of toggle elements
|
|
1011
1034
|
*/
|
|
1012
|
-
static
|
|
1013
|
-
|
|
1035
|
+
static applyTogglesVisibility(allToggleElements, activeToggles, peekToggles = []) {
|
|
1036
|
+
allToggleElements.forEach(el => {
|
|
1014
1037
|
const categories = this.getToggleCategories(el);
|
|
1015
1038
|
const shouldShow = categories.some(cat => activeToggles.includes(cat));
|
|
1016
|
-
|
|
1039
|
+
const shouldPeek = !shouldShow && categories.some(cat => peekToggles.includes(cat));
|
|
1040
|
+
if (!shouldPeek) {
|
|
1041
|
+
this.expandedPeekElements.delete(el);
|
|
1042
|
+
}
|
|
1043
|
+
// If locally expanded, treat as shown (override peek)
|
|
1044
|
+
// Note: If neither show nor peek is active (i.e. hidden), local expansion is ignored/cleared effectively
|
|
1045
|
+
this.applyToggleVisibility(el, shouldShow || (shouldPeek && this.expandedPeekElements.has(el)), shouldPeek && !this.expandedPeekElements.has(el));
|
|
1017
1046
|
});
|
|
1018
1047
|
}
|
|
1019
1048
|
/**
|
|
1020
1049
|
* Render assets into a given list of toggle elements that are currently visible
|
|
1050
|
+
* Toggles that have a toggleId and are currently visible will have their assets rendered (if any)
|
|
1021
1051
|
*/
|
|
1022
|
-
static
|
|
1052
|
+
static renderToggleAssets(elements, activeToggles, assetsManager) {
|
|
1053
|
+
// TO DO: (gerteck) Enable for peek toggles as well
|
|
1054
|
+
// Also, rework the rendering logic again to make it more user friendly.
|
|
1023
1055
|
elements.forEach(el => {
|
|
1024
1056
|
const categories = this.getToggleCategories(el);
|
|
1025
1057
|
const toggleId = this.getToggleId(el);
|
|
@@ -1032,6 +1064,7 @@
|
|
|
1032
1064
|
}
|
|
1033
1065
|
/**
|
|
1034
1066
|
* Get toggle categories from an element (supports both data attributes and cv-toggle elements)
|
|
1067
|
+
* Note: a toggle can have multiple categories.
|
|
1035
1068
|
*/
|
|
1036
1069
|
static getToggleCategories(el) {
|
|
1037
1070
|
if (el.tagName.toLowerCase() === 'cv-toggle') {
|
|
@@ -1052,14 +1085,101 @@
|
|
|
1052
1085
|
/**
|
|
1053
1086
|
* Apply simple class-based visibility to a toggle element
|
|
1054
1087
|
*/
|
|
1055
|
-
static applyToggleVisibility(
|
|
1088
|
+
static applyToggleVisibility(toggleElement, visible, peek = false) {
|
|
1089
|
+
const isLocallyExpanded = this.expandedPeekElements.has(toggleElement);
|
|
1056
1090
|
if (visible) {
|
|
1057
|
-
|
|
1058
|
-
|
|
1091
|
+
toggleElement.classList.remove('cv-hidden', 'cv-peek');
|
|
1092
|
+
toggleElement.classList.add('cv-visible');
|
|
1093
|
+
// Show collapse button ONLY if locally expanded (meaning we are actually in peek mode but expanded).
|
|
1094
|
+
// If globally visible (because of 'Show' state), isLocallyExpanded should have been cleared by applyTogglesVisibility,
|
|
1095
|
+
// so this will be false, and button will be removed.
|
|
1096
|
+
this.manageExpandButton(toggleElement, false, isLocallyExpanded);
|
|
1097
|
+
}
|
|
1098
|
+
else if (peek) {
|
|
1099
|
+
toggleElement.classList.remove('cv-hidden', 'cv-visible');
|
|
1100
|
+
toggleElement.classList.add('cv-peek');
|
|
1101
|
+
// Show/create expand button if peeked
|
|
1102
|
+
this.manageExpandButton(toggleElement, true, false);
|
|
1059
1103
|
}
|
|
1060
1104
|
else {
|
|
1061
|
-
|
|
1062
|
-
|
|
1105
|
+
toggleElement.classList.add('cv-hidden');
|
|
1106
|
+
toggleElement.classList.remove('cv-visible', 'cv-peek');
|
|
1107
|
+
// Ensure button is gone/hidden
|
|
1108
|
+
this.manageExpandButton(toggleElement, false, false);
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Manage the presence of the inline Expand/Collapse button using a wrapper approach
|
|
1113
|
+
*/
|
|
1114
|
+
static manageExpandButton(toggleElement, showExpand, showCollapse = false) {
|
|
1115
|
+
// 1. Ensure wrapper exists
|
|
1116
|
+
let wrapper = toggleElement.parentElement;
|
|
1117
|
+
if (!wrapper || !wrapper.classList.contains('cv-wrapper')) {
|
|
1118
|
+
wrapper = document.createElement('div');
|
|
1119
|
+
wrapper.className = 'cv-wrapper';
|
|
1120
|
+
toggleElement.parentNode?.insertBefore(wrapper, toggleElement);
|
|
1121
|
+
wrapper.appendChild(toggleElement);
|
|
1122
|
+
}
|
|
1123
|
+
const btn = wrapper.querySelector('.cv-expand-btn');
|
|
1124
|
+
// 2. Handle "No Button" case (neither expand nor collapse)
|
|
1125
|
+
if (!showExpand && !showCollapse) {
|
|
1126
|
+
if (btn)
|
|
1127
|
+
btn.style.display = 'none';
|
|
1128
|
+
// If content is visible globally (not hidden), ensure wrapper has 'cv-expanded'
|
|
1129
|
+
// to hide the peek fade effect (since fade is for peek state only).
|
|
1130
|
+
if (!toggleElement.classList.contains('cv-hidden')) {
|
|
1131
|
+
wrapper.classList.add('cv-expanded');
|
|
1132
|
+
}
|
|
1133
|
+
else {
|
|
1134
|
+
wrapper.classList.remove('cv-expanded');
|
|
1135
|
+
}
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
// 3. Handle Button Needed (Expand or Collapse)
|
|
1139
|
+
const action = showExpand ? 'expand' : 'collapse';
|
|
1140
|
+
// Update Wrapper Class Logic
|
|
1141
|
+
// If showExpand (Peek state) -> remove cv-expanded (show fade)
|
|
1142
|
+
// If showCollapse (Expanded peek) -> add cv-expanded (hide fade)
|
|
1143
|
+
if (showExpand) {
|
|
1144
|
+
wrapper.classList.remove('cv-expanded');
|
|
1145
|
+
}
|
|
1146
|
+
else {
|
|
1147
|
+
if (!wrapper.classList.contains('cv-expanded'))
|
|
1148
|
+
wrapper.classList.add('cv-expanded');
|
|
1149
|
+
}
|
|
1150
|
+
// Check if existing button matches desired state
|
|
1151
|
+
const currentAction = btn?.getAttribute('data-action');
|
|
1152
|
+
if (btn && currentAction === action) {
|
|
1153
|
+
btn.style.display = 'flex';
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
// 4. Create New Button (if missing or state changed)
|
|
1157
|
+
const iconSvg = showExpand ? getChevronDownIcon() : getChevronUpIcon();
|
|
1158
|
+
const newBtn = document.createElement('button');
|
|
1159
|
+
newBtn.className = 'cv-expand-btn';
|
|
1160
|
+
newBtn.innerHTML = iconSvg;
|
|
1161
|
+
newBtn.setAttribute('aria-label', showExpand ? 'Expand content' : 'Collapse content');
|
|
1162
|
+
newBtn.setAttribute('data-action', action); // Track state
|
|
1163
|
+
newBtn.style.display = 'flex';
|
|
1164
|
+
newBtn.addEventListener('click', (e) => {
|
|
1165
|
+
e.stopPropagation();
|
|
1166
|
+
// Logic: Toggle expansion state
|
|
1167
|
+
if (showExpand) {
|
|
1168
|
+
wrapper.classList.add('cv-expanded');
|
|
1169
|
+
this.expandedPeekElements.add(toggleElement);
|
|
1170
|
+
this.applyToggleVisibility(toggleElement, true, false);
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
wrapper.classList.remove('cv-expanded');
|
|
1174
|
+
this.expandedPeekElements.delete(toggleElement);
|
|
1175
|
+
this.applyToggleVisibility(toggleElement, false, true);
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
if (btn) {
|
|
1179
|
+
btn.replaceWith(newBtn);
|
|
1180
|
+
}
|
|
1181
|
+
else {
|
|
1182
|
+
wrapper.appendChild(newBtn);
|
|
1063
1183
|
}
|
|
1064
1184
|
}
|
|
1065
1185
|
/**
|
|
@@ -1067,15 +1187,15 @@
|
|
|
1067
1187
|
* This includes applying visibility and rendering assets.
|
|
1068
1188
|
*/
|
|
1069
1189
|
static initializeToggles(root, activeToggles, assetsManager) {
|
|
1070
|
-
const
|
|
1190
|
+
const allToggleElements = [];
|
|
1071
1191
|
if (root.matches('[data-cv-toggle], [data-customviews-toggle], cv-toggle')) {
|
|
1072
|
-
|
|
1192
|
+
allToggleElements.push(root);
|
|
1073
1193
|
}
|
|
1074
|
-
root.querySelectorAll('[data-cv-toggle], [data-customviews-toggle], cv-toggle').forEach(el =>
|
|
1075
|
-
if (
|
|
1194
|
+
root.querySelectorAll('[data-cv-toggle], [data-customviews-toggle], cv-toggle').forEach(el => allToggleElements.push(el));
|
|
1195
|
+
if (allToggleElements.length === 0)
|
|
1076
1196
|
return;
|
|
1077
|
-
this.
|
|
1078
|
-
this.
|
|
1197
|
+
this.applyTogglesVisibility(allToggleElements, activeToggles);
|
|
1198
|
+
this.renderToggleAssets(allToggleElements, activeToggles, assetsManager);
|
|
1079
1199
|
}
|
|
1080
1200
|
}
|
|
1081
1201
|
|
|
@@ -1183,17 +1303,16 @@
|
|
|
1183
1303
|
const TOGGLE_STYLES = `
|
|
1184
1304
|
/* Core toggle visibility transitions */
|
|
1185
1305
|
[data-cv-toggle], [data-customviews-toggle], cv-toggle {
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
margin 150ms ease;
|
|
1190
|
-
will-change: opacity, transform, max-height, margin;
|
|
1306
|
+
display: block;
|
|
1307
|
+
overflow: hidden;
|
|
1308
|
+
/* Removed transitions for instant toggling */
|
|
1191
1309
|
}
|
|
1192
1310
|
|
|
1311
|
+
/* Open State */
|
|
1193
1312
|
.cv-visible {
|
|
1194
1313
|
opacity: 1 !important;
|
|
1195
1314
|
transform: translateY(0) !important;
|
|
1196
|
-
max-height:
|
|
1315
|
+
max-height: none !important;
|
|
1197
1316
|
}
|
|
1198
1317
|
|
|
1199
1318
|
.cv-hidden {
|
|
@@ -1209,6 +1328,61 @@
|
|
|
1209
1328
|
margin-bottom: 0 !important;
|
|
1210
1329
|
overflow: hidden !important;
|
|
1211
1330
|
}
|
|
1331
|
+
|
|
1332
|
+
/* Close/Peek State */
|
|
1333
|
+
.cv-peek {
|
|
1334
|
+
display: block !important;
|
|
1335
|
+
max-height: 70px !important;
|
|
1336
|
+
overflow: hidden !important;
|
|
1337
|
+
opacity: 1 !important;
|
|
1338
|
+
transform: translateY(0) !important;
|
|
1339
|
+
mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
|
|
1340
|
+
-webkit-mask-image: linear-gradient(to bottom, black 50%, transparent 100%);
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
.cv-wrapper {
|
|
1344
|
+
position: relative;
|
|
1345
|
+
width: 100%;
|
|
1346
|
+
display: block;
|
|
1347
|
+
margin-bottom: 24px; /* Space for the button */
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.cv-expand-btn {
|
|
1351
|
+
position: absolute;
|
|
1352
|
+
bottom: -28px; /* Mostly outside, slight overlap */
|
|
1353
|
+
left: 50%;
|
|
1354
|
+
transform: translateX(-50%);
|
|
1355
|
+
display: flex;
|
|
1356
|
+
background: transparent;
|
|
1357
|
+
border: none;
|
|
1358
|
+
border-radius: 50%;
|
|
1359
|
+
padding: 4px;
|
|
1360
|
+
width: 32px;
|
|
1361
|
+
height: 32px;
|
|
1362
|
+
cursor: pointer;
|
|
1363
|
+
z-index: 100;
|
|
1364
|
+
align-items: center;
|
|
1365
|
+
justify-content: center;
|
|
1366
|
+
color: #888;
|
|
1367
|
+
transition: all 0.2s ease;
|
|
1368
|
+
box-shadow: none;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.cv-expand-btn:hover {
|
|
1372
|
+
background: rgba(0, 0, 0, 0.05);
|
|
1373
|
+
color: #000;
|
|
1374
|
+
transform: translateX(-50%) scale(1.1);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
.cv-expand-btn svg {
|
|
1378
|
+
display: block;
|
|
1379
|
+
opacity: 0.6;
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
.cv-expand-btn:hover svg {
|
|
1383
|
+
opacity: 1;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1212
1386
|
`;
|
|
1213
1387
|
|
|
1214
1388
|
/**
|
|
@@ -2139,114 +2313,6 @@ ${TAB_STYLES}
|
|
|
2139
2313
|
}
|
|
2140
2314
|
}
|
|
2141
2315
|
|
|
2142
|
-
const SHARE_BUTTON_ID = 'cv-share-button';
|
|
2143
|
-
const SHARE_BUTTON_STYLES = `
|
|
2144
|
-
#${SHARE_BUTTON_ID} {
|
|
2145
|
-
position: fixed;
|
|
2146
|
-
bottom: 20px;
|
|
2147
|
-
right: 100px;
|
|
2148
|
-
width: 50px;
|
|
2149
|
-
height: 50px;
|
|
2150
|
-
border-radius: 50%;
|
|
2151
|
-
background-color: #007bff; /* Primary Blue */
|
|
2152
|
-
color: white;
|
|
2153
|
-
border: none;
|
|
2154
|
-
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08); /* Drop shadow */
|
|
2155
|
-
z-index: 9998; /* Below modals (9999) but above content */
|
|
2156
|
-
cursor: pointer;
|
|
2157
|
-
display: flex;
|
|
2158
|
-
align-items: center;
|
|
2159
|
-
justify-content: center;
|
|
2160
|
-
transition: opacity 0.3s ease, transform 0.3s ease, background-color 0.2s;
|
|
2161
|
-
opacity: 1;
|
|
2162
|
-
transform: scale(1);
|
|
2163
|
-
padding: 0;
|
|
2164
|
-
margin: 0;
|
|
2165
|
-
}
|
|
2166
|
-
|
|
2167
|
-
#${SHARE_BUTTON_ID}:hover {
|
|
2168
|
-
background-color: #0056b3; /* Darker blue on hover */
|
|
2169
|
-
transform: scale(1.05);
|
|
2170
|
-
}
|
|
2171
|
-
|
|
2172
|
-
#${SHARE_BUTTON_ID}:active {
|
|
2173
|
-
transform: scale(0.95);
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
#${SHARE_BUTTON_ID}.cv-hidden {
|
|
2177
|
-
opacity: 0;
|
|
2178
|
-
pointer-events: none;
|
|
2179
|
-
transform: scale(0.8);
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
@media print {
|
|
2183
|
-
#${SHARE_BUTTON_ID} {
|
|
2184
|
-
display: none !important;
|
|
2185
|
-
}
|
|
2186
|
-
}
|
|
2187
|
-
|
|
2188
|
-
#${SHARE_BUTTON_ID} svg {
|
|
2189
|
-
width: 24px;
|
|
2190
|
-
height: 24px;
|
|
2191
|
-
fill: currentColor;
|
|
2192
|
-
}
|
|
2193
|
-
`;
|
|
2194
|
-
|
|
2195
|
-
class ShareButton {
|
|
2196
|
-
shareManager;
|
|
2197
|
-
button = null;
|
|
2198
|
-
boundHandleClick;
|
|
2199
|
-
constructor(shareManager) {
|
|
2200
|
-
this.shareManager = shareManager;
|
|
2201
|
-
this.boundHandleClick = () => this.shareManager.toggleShareMode();
|
|
2202
|
-
}
|
|
2203
|
-
init() {
|
|
2204
|
-
if (this.button)
|
|
2205
|
-
return;
|
|
2206
|
-
this.injectStyles();
|
|
2207
|
-
this.button = this.createButton();
|
|
2208
|
-
document.body.appendChild(this.button);
|
|
2209
|
-
// Subscribe to share manager state changes
|
|
2210
|
-
this.shareManager.addStateChangeListener((isActive) => {
|
|
2211
|
-
this.setShareModeActive(isActive);
|
|
2212
|
-
});
|
|
2213
|
-
}
|
|
2214
|
-
destroy() {
|
|
2215
|
-
if (this.button) {
|
|
2216
|
-
this.button.remove();
|
|
2217
|
-
this.button = null;
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
createButton() {
|
|
2221
|
-
const btn = document.createElement('button');
|
|
2222
|
-
btn.id = SHARE_BUTTON_ID;
|
|
2223
|
-
btn.setAttribute('aria-label', 'Share specific sections');
|
|
2224
|
-
btn.title = 'Select sections to share';
|
|
2225
|
-
// Using the link icon from utils, ensuring it's white via CSS currentColor
|
|
2226
|
-
btn.innerHTML = getShareIcon();
|
|
2227
|
-
btn.addEventListener('click', this.boundHandleClick);
|
|
2228
|
-
return btn;
|
|
2229
|
-
}
|
|
2230
|
-
injectStyles() {
|
|
2231
|
-
if (!document.getElementById('cv-share-button-styles')) {
|
|
2232
|
-
const style = document.createElement('style');
|
|
2233
|
-
style.id = 'cv-share-button-styles';
|
|
2234
|
-
style.textContent = SHARE_BUTTON_STYLES;
|
|
2235
|
-
document.head.appendChild(style);
|
|
2236
|
-
}
|
|
2237
|
-
}
|
|
2238
|
-
setShareModeActive(isActive) {
|
|
2239
|
-
if (!this.button)
|
|
2240
|
-
return;
|
|
2241
|
-
if (isActive) {
|
|
2242
|
-
this.button.classList.add('cv-hidden');
|
|
2243
|
-
}
|
|
2244
|
-
else {
|
|
2245
|
-
this.button.classList.remove('cv-hidden');
|
|
2246
|
-
}
|
|
2247
|
-
}
|
|
2248
|
-
}
|
|
2249
|
-
|
|
2250
2316
|
const FOCUS_MODE_STYLE_ID = 'cv-focus-mode-styles';
|
|
2251
2317
|
const BODY_FOCUS_CLASS = 'cv-focus-mode';
|
|
2252
2318
|
const HIDDEN_CLASS = 'cv-focus-hidden';
|
|
@@ -2588,7 +2654,6 @@ ${TAB_STYLES}
|
|
|
2588
2654
|
visibilityManager;
|
|
2589
2655
|
observer = null;
|
|
2590
2656
|
shareManager;
|
|
2591
|
-
shareButton;
|
|
2592
2657
|
focusManager;
|
|
2593
2658
|
componentRegistry = {
|
|
2594
2659
|
toggles: new Set(),
|
|
@@ -2611,7 +2676,6 @@ ${TAB_STYLES}
|
|
|
2611
2676
|
const excludedIds = [...DEFAULT_EXCLUDED_IDS, ...(this.config.shareExclusions?.ids || [])];
|
|
2612
2677
|
const commonOptions = { excludedTags, excludedIds };
|
|
2613
2678
|
this.shareManager = new ShareManager(commonOptions);
|
|
2614
|
-
this.shareButton = new ShareButton(this.shareManager);
|
|
2615
2679
|
this.focusManager = new FocusManager(this.rootEl, commonOptions);
|
|
2616
2680
|
}
|
|
2617
2681
|
getShareManager() {
|
|
@@ -2708,7 +2772,7 @@ ${TAB_STYLES}
|
|
|
2708
2772
|
});
|
|
2709
2773
|
}
|
|
2710
2774
|
const computedState = {
|
|
2711
|
-
|
|
2775
|
+
shownToggles: this.config.toggles?.map(t => t.id) || [],
|
|
2712
2776
|
tabs
|
|
2713
2777
|
};
|
|
2714
2778
|
return computedState;
|
|
@@ -2762,7 +2826,6 @@ ${TAB_STYLES}
|
|
|
2762
2826
|
});
|
|
2763
2827
|
this.loadAndCallApplyState();
|
|
2764
2828
|
this.focusManager.init();
|
|
2765
|
-
this.shareButton.init();
|
|
2766
2829
|
this.initObserver();
|
|
2767
2830
|
}
|
|
2768
2831
|
initializeNewComponents() {
|
|
@@ -2781,9 +2844,9 @@ ${TAB_STYLES}
|
|
|
2781
2844
|
const initialTop = anchorElement.getBoundingClientRect().top;
|
|
2782
2845
|
const currentTabs = this.getCurrentActiveTabs();
|
|
2783
2846
|
currentTabs[groupId] = tabId;
|
|
2784
|
-
const
|
|
2847
|
+
const currentState = this.getCurrentState();
|
|
2785
2848
|
const newState = {
|
|
2786
|
-
|
|
2849
|
+
...currentState,
|
|
2787
2850
|
tabs: currentTabs,
|
|
2788
2851
|
};
|
|
2789
2852
|
// 2. Apply state with scroll anchor information
|
|
@@ -2839,7 +2902,8 @@ ${TAB_STYLES}
|
|
|
2839
2902
|
// 1. URL State
|
|
2840
2903
|
const urlState = URLStateManager.parseURL();
|
|
2841
2904
|
if (urlState) {
|
|
2842
|
-
|
|
2905
|
+
// Apply URL state temporarily (do not persist until interaction)
|
|
2906
|
+
this.applyState(urlState, { persist: false });
|
|
2843
2907
|
return;
|
|
2844
2908
|
}
|
|
2845
2909
|
// 2. Persisted State
|
|
@@ -2853,9 +2917,10 @@ ${TAB_STYLES}
|
|
|
2853
2917
|
}
|
|
2854
2918
|
/**
|
|
2855
2919
|
* Apply a custom state, saves to localStorage and updates the URL
|
|
2856
|
-
*
|
|
2920
|
+
* 'source' in options indicates the origin of the state change
|
|
2857
2921
|
* (e.g., 'widget' to trigger scroll behavior)
|
|
2858
|
-
*
|
|
2922
|
+
* 'scrollAnchor' in options indicates the element to maintain scroll position of
|
|
2923
|
+
* 'persist' (default true) to control whether to save to localStorage
|
|
2859
2924
|
*/
|
|
2860
2925
|
applyState(state, options) {
|
|
2861
2926
|
// console.log(`[Core] applyState called with source: ${options?.source}`, state);
|
|
@@ -2865,7 +2930,10 @@ ${TAB_STYLES}
|
|
|
2865
2930
|
}
|
|
2866
2931
|
const snapshot = this.cloneState(state);
|
|
2867
2932
|
this.renderState(snapshot);
|
|
2868
|
-
|
|
2933
|
+
// Only persist if explicitly requested (default true)
|
|
2934
|
+
if (options?.persist !== false) {
|
|
2935
|
+
this.persistenceManager.persistState(snapshot);
|
|
2936
|
+
}
|
|
2869
2937
|
if (this.showUrlEnabled) {
|
|
2870
2938
|
URLStateManager.updateURL(snapshot);
|
|
2871
2939
|
}
|
|
@@ -2892,14 +2960,13 @@ ${TAB_STYLES}
|
|
|
2892
2960
|
renderState(state) {
|
|
2893
2961
|
this.observer?.disconnect();
|
|
2894
2962
|
this.lastAppliedState = this.cloneState(state);
|
|
2895
|
-
const toggles = state?.
|
|
2963
|
+
const toggles = state?.shownToggles || [];
|
|
2896
2964
|
const finalToggles = this.visibilityManager.filterVisibleToggles(toggles);
|
|
2897
|
-
const
|
|
2965
|
+
const allToggleElements = Array.from(this.componentRegistry.toggles);
|
|
2898
2966
|
const tabGroupElements = Array.from(this.componentRegistry.tabGroups);
|
|
2899
|
-
|
|
2900
|
-
ToggleManager.applyToggles(toggleElements, finalToggles);
|
|
2967
|
+
ToggleManager.applyTogglesVisibility(allToggleElements, finalToggles, state.peekToggles);
|
|
2901
2968
|
// Render assets into toggles
|
|
2902
|
-
ToggleManager.
|
|
2969
|
+
ToggleManager.renderToggleAssets(allToggleElements, finalToggles, this.assetsManager);
|
|
2903
2970
|
// Apply tab selections
|
|
2904
2971
|
TabManager.applyTabSelections(tabGroupElements, state.tabs || {}, this.config.tabGroups);
|
|
2905
2972
|
// Update nav active states (without rebuilding)
|
|
@@ -2930,16 +2997,16 @@ ${TAB_STYLES}
|
|
|
2930
2997
|
URLStateManager.clearURL();
|
|
2931
2998
|
}
|
|
2932
2999
|
/**
|
|
2933
|
-
* Get the
|
|
3000
|
+
* Get the full current state including active toggles, peek toggles, and tabs
|
|
2934
3001
|
*/
|
|
2935
|
-
|
|
3002
|
+
getCurrentState() {
|
|
2936
3003
|
if (this.lastAppliedState) {
|
|
2937
|
-
return this.lastAppliedState
|
|
3004
|
+
return this.cloneState(this.lastAppliedState);
|
|
2938
3005
|
}
|
|
2939
3006
|
if (this.config) {
|
|
2940
|
-
return this.getComputedDefaultState()
|
|
3007
|
+
return this.cloneState(this.getComputedDefaultState());
|
|
2941
3008
|
}
|
|
2942
|
-
return
|
|
3009
|
+
return {};
|
|
2943
3010
|
}
|
|
2944
3011
|
/**
|
|
2945
3012
|
* Clear all persistence and reset to default
|
|
@@ -3635,10 +3702,6 @@ ${TAB_STYLES}
|
|
|
3635
3702
|
color: rgba(255, 255, 255, 0.6);
|
|
3636
3703
|
}
|
|
3637
3704
|
|
|
3638
|
-
.cv-widget-theme-dark .cv-toggle-slider {
|
|
3639
|
-
background: rgba(255, 255, 255, 0.2);
|
|
3640
|
-
}
|
|
3641
|
-
|
|
3642
3705
|
.cv-widget-theme-dark .cv-tab-group-description {
|
|
3643
3706
|
color: rgba(255, 255, 255, 0.8);
|
|
3644
3707
|
}
|
|
@@ -3759,40 +3822,32 @@ ${TAB_STYLES}
|
|
|
3759
3822
|
}
|
|
3760
3823
|
|
|
3761
3824
|
.cv-toggle-input {
|
|
3825
|
+
/* Only hide if it is part of a custom slider toggle */
|
|
3826
|
+
}
|
|
3827
|
+
.cv-toggle-label .cv-toggle-input {
|
|
3762
3828
|
opacity: 0;
|
|
3763
3829
|
width: 0;
|
|
3764
3830
|
height: 0;
|
|
3765
3831
|
}
|
|
3766
3832
|
|
|
3767
|
-
.cv-toggle-
|
|
3768
|
-
|
|
3769
|
-
|
|
3770
|
-
left: 0;
|
|
3771
|
-
right: 0;
|
|
3772
|
-
bottom: 0;
|
|
3773
|
-
background: rgba(0, 0, 0, 0.2);
|
|
3774
|
-
border-radius: 9999px;
|
|
3775
|
-
transition: background-color 0.2s ease;
|
|
3776
|
-
}
|
|
3777
|
-
|
|
3778
|
-
.cv-toggle-slider:before {
|
|
3779
|
-
position: absolute;
|
|
3780
|
-
content: "";
|
|
3781
|
-
height: 1rem;
|
|
3782
|
-
width: 1rem;
|
|
3783
|
-
left: 0.25rem;
|
|
3784
|
-
bottom: 0.25rem;
|
|
3785
|
-
background: white;
|
|
3786
|
-
border-radius: 50%;
|
|
3787
|
-
transition: transform 0.2s ease;
|
|
3833
|
+
.cv-toggle-radios {
|
|
3834
|
+
display: flex;
|
|
3835
|
+
gap: 8px;
|
|
3788
3836
|
}
|
|
3789
3837
|
|
|
3790
|
-
.cv-
|
|
3791
|
-
|
|
3838
|
+
.cv-radio-label {
|
|
3839
|
+
display: flex;
|
|
3840
|
+
align-items: center;
|
|
3841
|
+
gap: 4px;
|
|
3842
|
+
font-size: 0.85rem;
|
|
3843
|
+
cursor: pointer;
|
|
3792
3844
|
}
|
|
3793
3845
|
|
|
3794
|
-
.cv-
|
|
3795
|
-
|
|
3846
|
+
.cv-radio-label input {
|
|
3847
|
+
margin: 0;
|
|
3848
|
+
opacity: 1;
|
|
3849
|
+
width: auto;
|
|
3850
|
+
height: auto;
|
|
3796
3851
|
}
|
|
3797
3852
|
|
|
3798
3853
|
/* Dark theme toggle switch styles */
|
|
@@ -4071,6 +4126,33 @@ ${TAB_STYLES}
|
|
|
4071
4126
|
padding: 0.75rem;
|
|
4072
4127
|
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
|
4073
4128
|
}
|
|
4129
|
+
|
|
4130
|
+
.cv-footer-link {
|
|
4131
|
+
display: flex;
|
|
4132
|
+
align-items: center;
|
|
4133
|
+
justify-content: center;
|
|
4134
|
+
gap: 0.5rem;
|
|
4135
|
+
font-size: 0.75rem;
|
|
4136
|
+
color: rgba(0, 0, 0, 0.5);
|
|
4137
|
+
text-decoration: none;
|
|
4138
|
+
transition: color 0.2s ease;
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
.cv-footer-link:hover {
|
|
4142
|
+
color: #3e84f4;
|
|
4143
|
+
}
|
|
4144
|
+
|
|
4145
|
+
.cv-footer-link svg {
|
|
4146
|
+
opacity: 0.8;
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
.cv-widget-theme-dark .cv-footer-link {
|
|
4150
|
+
color: rgba(255, 255, 255, 0.4);
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
.cv-widget-theme-dark .cv-footer-link:hover {
|
|
4154
|
+
color: #60a5fa;
|
|
4155
|
+
}
|
|
4074
4156
|
|
|
4075
4157
|
.cv-reset-btn,
|
|
4076
4158
|
.cv-share-btn {
|
|
@@ -4262,6 +4344,154 @@ ${TAB_STYLES}
|
|
|
4262
4344
|
display: none !important;
|
|
4263
4345
|
}
|
|
4264
4346
|
}
|
|
4347
|
+
/* Widget Modal Tabs */
|
|
4348
|
+
.cv-modal-tabs {
|
|
4349
|
+
display: flex;
|
|
4350
|
+
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
|
4351
|
+
margin-bottom: 0.5rem;
|
|
4352
|
+
}
|
|
4353
|
+
|
|
4354
|
+
.cv-tab-content > .cv-content-section + .cv-content-section {
|
|
4355
|
+
margin-top: 1.5rem;
|
|
4356
|
+
}
|
|
4357
|
+
|
|
4358
|
+
.cv-modal-tab {
|
|
4359
|
+
padding: 0.75rem 1.5rem;
|
|
4360
|
+
font-size: 0.875rem;
|
|
4361
|
+
font-weight: 500;
|
|
4362
|
+
color: rgba(0, 0, 0, 0.6);
|
|
4363
|
+
background: none;
|
|
4364
|
+
border: none;
|
|
4365
|
+
border-bottom: 2px solid transparent;
|
|
4366
|
+
cursor: pointer;
|
|
4367
|
+
transition: all 0.2s ease;
|
|
4368
|
+
}
|
|
4369
|
+
|
|
4370
|
+
.cv-modal-tab:hover {
|
|
4371
|
+
color: rgba(0, 0, 0, 0.9);
|
|
4372
|
+
}
|
|
4373
|
+
|
|
4374
|
+
.cv-modal-tab.active {
|
|
4375
|
+
color: #3e84f4;
|
|
4376
|
+
border-bottom-color: #3e84f4;
|
|
4377
|
+
}
|
|
4378
|
+
|
|
4379
|
+
.cv-tab-content {
|
|
4380
|
+
display: none;
|
|
4381
|
+
animation: fadeIn 0.3s ease;
|
|
4382
|
+
}
|
|
4383
|
+
|
|
4384
|
+
.cv-tab-content.active {
|
|
4385
|
+
display: block;
|
|
4386
|
+
}
|
|
4387
|
+
|
|
4388
|
+
/* Share Tab Content */
|
|
4389
|
+
.cv-share-content {
|
|
4390
|
+
display: flex;
|
|
4391
|
+
flex-direction: column;
|
|
4392
|
+
gap: 1rem;
|
|
4393
|
+
padding: 1rem 0;
|
|
4394
|
+
align-items: center;
|
|
4395
|
+
text-align: center;
|
|
4396
|
+
}
|
|
4397
|
+
|
|
4398
|
+
.cv-share-instruction {
|
|
4399
|
+
font-size: 0.9rem;
|
|
4400
|
+
color: rgba(0, 0, 0, 0.7);
|
|
4401
|
+
margin-bottom: 1rem;
|
|
4402
|
+
}
|
|
4403
|
+
|
|
4404
|
+
.cv-share-action-btn {
|
|
4405
|
+
display: flex;
|
|
4406
|
+
align-items: center;
|
|
4407
|
+
justify-content: center;
|
|
4408
|
+
gap: 0.5rem;
|
|
4409
|
+
width: 100%;
|
|
4410
|
+
padding: 12px 16px;
|
|
4411
|
+
background: white;
|
|
4412
|
+
color: #333;
|
|
4413
|
+
border: 1px solid rgba(0, 0, 0, 0.15);
|
|
4414
|
+
border-radius: 6px;
|
|
4415
|
+
cursor: pointer;
|
|
4416
|
+
font-size: 0.9rem;
|
|
4417
|
+
font-weight: 500;
|
|
4418
|
+
transition: all 0.2s ease;
|
|
4419
|
+
}
|
|
4420
|
+
|
|
4421
|
+
.cv-share-action-btn:hover {
|
|
4422
|
+
background: #f8f9fa;
|
|
4423
|
+
border-color: rgba(0, 0, 0, 0.25);
|
|
4424
|
+
transform: translateY(-1px);
|
|
4425
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
|
4426
|
+
}
|
|
4427
|
+
|
|
4428
|
+
.cv-share-action-btn.primary {
|
|
4429
|
+
background: #3e84f4;
|
|
4430
|
+
color: white;
|
|
4431
|
+
border-color: #3e84f4;
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
.cv-share-action-btn.primary:hover {
|
|
4435
|
+
background: #2b74e6;
|
|
4436
|
+
border-color: #2b74e6;
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
.cv-done-btn {
|
|
4440
|
+
padding: 0.375rem 1rem;
|
|
4441
|
+
background: #3e84f4;
|
|
4442
|
+
color: white;
|
|
4443
|
+
border: none;
|
|
4444
|
+
border-radius: 0.5rem;
|
|
4445
|
+
font-weight: 600;
|
|
4446
|
+
font-size: 0.875rem;
|
|
4447
|
+
cursor: pointer;
|
|
4448
|
+
transition: all 0.2s ease;
|
|
4449
|
+
}
|
|
4450
|
+
|
|
4451
|
+
.cv-done-btn:hover {
|
|
4452
|
+
background: #2b74e6;
|
|
4453
|
+
}
|
|
4454
|
+
|
|
4455
|
+
/* Dark Theme Adjustments */
|
|
4456
|
+
.cv-widget-theme-dark .cv-modal-tabs {
|
|
4457
|
+
border-color: rgba(255, 255, 255, 0.1);
|
|
4458
|
+
}
|
|
4459
|
+
|
|
4460
|
+
.cv-widget-theme-dark .cv-modal-tab {
|
|
4461
|
+
color: rgba(255, 255, 255, 0.6);
|
|
4462
|
+
}
|
|
4463
|
+
|
|
4464
|
+
.cv-widget-theme-dark .cv-modal-tab:hover {
|
|
4465
|
+
color: rgba(255, 255, 255, 0.9);
|
|
4466
|
+
}
|
|
4467
|
+
|
|
4468
|
+
.cv-widget-theme-dark .cv-modal-tab.active {
|
|
4469
|
+
color: #60a5fa;
|
|
4470
|
+
border-bottom-color: #60a5fa;
|
|
4471
|
+
}
|
|
4472
|
+
|
|
4473
|
+
.cv-widget-theme-dark .cv-share-instruction {
|
|
4474
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4475
|
+
}
|
|
4476
|
+
|
|
4477
|
+
.cv-widget-theme-dark .cv-share-action-btn {
|
|
4478
|
+
background: #1a202c;
|
|
4479
|
+
color: white;
|
|
4480
|
+
border-color: rgba(255, 255, 255, 0.15);
|
|
4481
|
+
}
|
|
4482
|
+
|
|
4483
|
+
.cv-widget-theme-dark .cv-share-action-btn:hover {
|
|
4484
|
+
background: #2d3748;
|
|
4485
|
+
}
|
|
4486
|
+
|
|
4487
|
+
.cv-widget-theme-dark .cv-share-action-btn.primary {
|
|
4488
|
+
background: #3e84f4;
|
|
4489
|
+
border-color: #3e84f4;
|
|
4490
|
+
}
|
|
4491
|
+
|
|
4492
|
+
.cv-widget-theme-dark .cv-share-action-btn.primary:hover {
|
|
4493
|
+
background: #2b74e6;
|
|
4494
|
+
}
|
|
4265
4495
|
`;
|
|
4266
4496
|
/**
|
|
4267
4497
|
* Inject widget styles into the document head
|
|
@@ -4284,6 +4514,7 @@ ${TAB_STYLES}
|
|
|
4284
4514
|
_hasVisibleConfig = false;
|
|
4285
4515
|
pageToggleIds = new Set();
|
|
4286
4516
|
pageTabIds = new Set();
|
|
4517
|
+
currentTab = 'customize';
|
|
4287
4518
|
// Modal state
|
|
4288
4519
|
stateModal = null;
|
|
4289
4520
|
constructor(options) {
|
|
@@ -4433,10 +4664,20 @@ ${TAB_STYLES}
|
|
|
4433
4664
|
<div>
|
|
4434
4665
|
<p class="cv-toggle-title">${toggle.label || toggle.id}</p>
|
|
4435
4666
|
</div>
|
|
4436
|
-
<
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4667
|
+
<div class="cv-toggle-radios">
|
|
4668
|
+
<label class="cv-radio-label" title="Hide">
|
|
4669
|
+
<input class="cv-toggle-input" type="radio" name="cv-toggle-${toggle.id}" value="hide" data-toggle="${toggle.id}"/>
|
|
4670
|
+
<span>Hide</span>
|
|
4671
|
+
</label>
|
|
4672
|
+
<label class="cv-radio-label" title="Peek">
|
|
4673
|
+
<input class="cv-toggle-input" type="radio" name="cv-toggle-${toggle.id}" value="peek" data-toggle="${toggle.id}"/>
|
|
4674
|
+
<span>Peek</span>
|
|
4675
|
+
</label>
|
|
4676
|
+
<label class="cv-radio-label" title="Show">
|
|
4677
|
+
<input class="cv-toggle-input" type="radio" name="cv-toggle-${toggle.id}" value="show" data-toggle="${toggle.id}"/>
|
|
4678
|
+
<span>Show</span>
|
|
4679
|
+
</label>
|
|
4680
|
+
</div>
|
|
4440
4681
|
</div>
|
|
4441
4682
|
</div>
|
|
4442
4683
|
`).join('');
|
|
@@ -4459,12 +4700,12 @@ ${TAB_STYLES}
|
|
|
4459
4700
|
</div>
|
|
4460
4701
|
<div class="cv-tabgroup-info">
|
|
4461
4702
|
<div class="cv-tabgroup-title-container">
|
|
4462
|
-
<p class="cv-tabgroup-title">
|
|
4703
|
+
<p class="cv-tabgroup-title">Show only the selected tab</p>
|
|
4463
4704
|
</div>
|
|
4464
|
-
<p class="cv-tabgroup-description">
|
|
4705
|
+
<p class="cv-tabgroup-description">Hide the navigation headers</p>
|
|
4465
4706
|
</div>
|
|
4466
4707
|
<label class="cv-toggle-switch cv-nav-toggle">
|
|
4467
|
-
<input class="cv-nav-pref-input" type="checkbox" ${initialNavsVisible ? '
|
|
4708
|
+
<input class="cv-nav-pref-input" type="checkbox" ${initialNavsVisible ? '' : 'checked'} aria-label="Show only the selected tab" />
|
|
4468
4709
|
<span class="cv-switch-bg"></span>
|
|
4469
4710
|
<span class="cv-switch-knob"></span>
|
|
4470
4711
|
</label>
|
|
@@ -4500,37 +4741,64 @@ ${TAB_STYLES}
|
|
|
4500
4741
|
<main class="cv-modal-main">
|
|
4501
4742
|
${this.options.description ? `<p class="cv-modal-description">${this.options.description}</p>` : ''}
|
|
4502
4743
|
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
<
|
|
4506
|
-
|
|
4507
|
-
|
|
4744
|
+
<div class="cv-modal-tabs">
|
|
4745
|
+
<button class="cv-modal-tab ${this.currentTab === 'customize' ? 'active' : ''}" data-tab="customize">Customize</button>
|
|
4746
|
+
<button class="cv-modal-tab ${this.currentTab === 'share' ? 'active' : ''}" data-tab="share">Share</button>
|
|
4747
|
+
</div>
|
|
4748
|
+
|
|
4749
|
+
<div class="cv-tab-content ${this.currentTab === 'customize' ? 'active' : ''}" data-content="customize">
|
|
4750
|
+
${visibleToggles.length ? `
|
|
4751
|
+
<div class="cv-content-section">
|
|
4752
|
+
<div class="cv-section-heading">Toggles</div>
|
|
4753
|
+
<div class="cv-toggles-container">
|
|
4754
|
+
${toggleControlsHtml}
|
|
4755
|
+
</div>
|
|
4756
|
+
</div>
|
|
4757
|
+
` : ''}
|
|
4758
|
+
|
|
4759
|
+
${this.options.showTabGroups && tabGroups && tabGroups.length > 0 ? `
|
|
4760
|
+
<div class="cv-content-section">
|
|
4761
|
+
<div class="cv-section-heading">Tab Groups</div>
|
|
4762
|
+
<div class="cv-tabgroups-container">
|
|
4763
|
+
${tabGroupControlsHTML}
|
|
4764
|
+
</div>
|
|
4508
4765
|
</div>
|
|
4766
|
+
` : ''}
|
|
4509
4767
|
</div>
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4768
|
+
|
|
4769
|
+
<div class="cv-tab-content ${this.currentTab === 'share' ? 'active' : ''}" data-content="share">
|
|
4770
|
+
<div class="cv-share-content">
|
|
4771
|
+
<div class="cv-share-instruction">
|
|
4772
|
+
Create a shareable link for your current customization, or select specific parts of the page to share.
|
|
4773
|
+
</div>
|
|
4774
|
+
|
|
4775
|
+
<button class="cv-share-action-btn primary cv-start-share-btn">
|
|
4776
|
+
<span class="cv-btn-icon">${getShareIcon()}</span>
|
|
4777
|
+
<span>Select elements to share</span>
|
|
4778
|
+
</button>
|
|
4779
|
+
|
|
4780
|
+
<button class="cv-share-action-btn cv-copy-url-btn">
|
|
4781
|
+
<span class="cv-btn-icon">${getCopyIcon()}</span>
|
|
4782
|
+
<span>Copy Shareable URL of Settings</span>
|
|
4783
|
+
</button>
|
|
4517
4784
|
</div>
|
|
4518
4785
|
</div>
|
|
4519
|
-
` : ''}
|
|
4520
4786
|
</main>
|
|
4521
4787
|
|
|
4522
4788
|
<footer class="cv-modal-footer">
|
|
4523
4789
|
${this.options.showReset ? `
|
|
4524
|
-
<button class="cv-reset-btn">
|
|
4790
|
+
<button class="cv-reset-btn" title="Reset to Default">
|
|
4525
4791
|
<span class="cv-reset-btn-icon">${getResetIcon()}</span>
|
|
4526
|
-
<span>Reset
|
|
4527
|
-
</button>
|
|
4528
|
-
` : ''}
|
|
4529
|
-
<button class="cv-share-btn">
|
|
4530
|
-
<span>Copy Shareable URL</span>
|
|
4531
|
-
<span class="cv-share-btn-icon">${getCopyIcon()}</span>
|
|
4792
|
+
<span>Reset</span>
|
|
4532
4793
|
</button>
|
|
4794
|
+
` : '<div></div>'}
|
|
4795
|
+
|
|
4796
|
+
<a href="https://github.com/customviews-js/customviews" target="_blank" class="cv-footer-link">
|
|
4797
|
+
${getGitHubIcon()}
|
|
4798
|
+
<span>View on GitHub</span>
|
|
4799
|
+
</a>
|
|
4533
4800
|
|
|
4801
|
+
<button class="cv-done-btn">Done</button>
|
|
4534
4802
|
</footer>
|
|
4535
4803
|
</div>
|
|
4536
4804
|
`;
|
|
@@ -4551,20 +4819,6 @@ ${TAB_STYLES}
|
|
|
4551
4819
|
this.closeModal();
|
|
4552
4820
|
return;
|
|
4553
4821
|
}
|
|
4554
|
-
// Copy URL button
|
|
4555
|
-
if (target.closest('.cv-share-btn')) {
|
|
4556
|
-
this.copyShareableURL();
|
|
4557
|
-
const copyUrlBtn = target.closest('.cv-share-btn');
|
|
4558
|
-
const iconContainer = copyUrlBtn?.querySelector('.cv-share-btn-icon');
|
|
4559
|
-
if (iconContainer) {
|
|
4560
|
-
const originalIcon = iconContainer.innerHTML;
|
|
4561
|
-
iconContainer.innerHTML = getTickIcon();
|
|
4562
|
-
setTimeout(() => {
|
|
4563
|
-
iconContainer.innerHTML = originalIcon;
|
|
4564
|
-
}, 3000);
|
|
4565
|
-
}
|
|
4566
|
-
return;
|
|
4567
|
-
}
|
|
4568
4822
|
// Reset to default button
|
|
4569
4823
|
if (target.closest('.cv-reset-btn')) {
|
|
4570
4824
|
const resetBtn = target.closest('.cv-reset-btn');
|
|
@@ -4581,6 +4835,11 @@ ${TAB_STYLES}
|
|
|
4581
4835
|
}, 600);
|
|
4582
4836
|
return;
|
|
4583
4837
|
}
|
|
4838
|
+
// Done button
|
|
4839
|
+
if (target.closest('.cv-done-btn')) {
|
|
4840
|
+
this.closeModal();
|
|
4841
|
+
return;
|
|
4842
|
+
}
|
|
4584
4843
|
// Overlay click to close
|
|
4585
4844
|
if (e.target === this.stateModal) {
|
|
4586
4845
|
this.closeModal();
|
|
@@ -4619,10 +4878,11 @@ ${TAB_STYLES}
|
|
|
4619
4878
|
if (groupId && tabId) {
|
|
4620
4879
|
const currentTabs = this.core.getCurrentActiveTabs();
|
|
4621
4880
|
currentTabs[groupId] = tabId;
|
|
4622
|
-
const
|
|
4881
|
+
const currentState = this.core.getCurrentState();
|
|
4623
4882
|
const newState = {
|
|
4624
|
-
|
|
4625
|
-
|
|
4883
|
+
shownToggles: currentState.shownToggles || [],
|
|
4884
|
+
peekToggles: currentState.peekToggles || [], // Preserve peek state, fallback to empty array
|
|
4885
|
+
tabs: currentTabs,
|
|
4626
4886
|
};
|
|
4627
4887
|
this.core.applyState(newState, { source: 'widget' });
|
|
4628
4888
|
}
|
|
@@ -4641,10 +4901,10 @@ ${TAB_STYLES}
|
|
|
4641
4901
|
navIcon.innerHTML = isVisible ? getNavHeadingOnIcon() : getNavHeadingOffIcon();
|
|
4642
4902
|
}
|
|
4643
4903
|
};
|
|
4644
|
-
navHeaderCard.addEventListener('mouseenter', () => updateIcon(tabNavToggle.checked, true));
|
|
4645
|
-
navHeaderCard.addEventListener('mouseleave', () => updateIcon(tabNavToggle.checked, false));
|
|
4904
|
+
navHeaderCard.addEventListener('mouseenter', () => updateIcon(!tabNavToggle.checked, true));
|
|
4905
|
+
navHeaderCard.addEventListener('mouseleave', () => updateIcon(!tabNavToggle.checked, false));
|
|
4646
4906
|
tabNavToggle.addEventListener('change', () => {
|
|
4647
|
-
const visible = tabNavToggle.checked;
|
|
4907
|
+
const visible = !tabNavToggle.checked;
|
|
4648
4908
|
updateIcon(visible, false);
|
|
4649
4909
|
this.core.persistTabNavVisibility(visible);
|
|
4650
4910
|
try {
|
|
@@ -4655,6 +4915,48 @@ ${TAB_STYLES}
|
|
|
4655
4915
|
}
|
|
4656
4916
|
});
|
|
4657
4917
|
}
|
|
4918
|
+
// Tab switching
|
|
4919
|
+
const tabs = this.stateModal.querySelectorAll('.cv-modal-tab');
|
|
4920
|
+
tabs.forEach(tab => {
|
|
4921
|
+
tab.addEventListener('click', () => {
|
|
4922
|
+
const tabId = tab.dataset.tab;
|
|
4923
|
+
if (tabId === 'customize' || tabId === 'share') {
|
|
4924
|
+
this.currentTab = tabId;
|
|
4925
|
+
// Update UI without full re-render
|
|
4926
|
+
tabs.forEach(t => t.classList.remove('active'));
|
|
4927
|
+
tab.classList.add('active');
|
|
4928
|
+
const contents = this.stateModal?.querySelectorAll('.cv-tab-content');
|
|
4929
|
+
contents?.forEach(c => {
|
|
4930
|
+
c.classList.remove('active');
|
|
4931
|
+
if (c.dataset.content === tabId) {
|
|
4932
|
+
c.classList.add('active');
|
|
4933
|
+
}
|
|
4934
|
+
});
|
|
4935
|
+
}
|
|
4936
|
+
});
|
|
4937
|
+
});
|
|
4938
|
+
// Share buttons (inside content)
|
|
4939
|
+
const startShareBtn = this.stateModal.querySelector('.cv-start-share-btn');
|
|
4940
|
+
if (startShareBtn) {
|
|
4941
|
+
startShareBtn.addEventListener('click', () => {
|
|
4942
|
+
this.closeModal();
|
|
4943
|
+
this.core.toggleShareMode();
|
|
4944
|
+
});
|
|
4945
|
+
}
|
|
4946
|
+
const copyUrlBtn = this.stateModal.querySelector('.cv-copy-url-btn');
|
|
4947
|
+
if (copyUrlBtn) {
|
|
4948
|
+
copyUrlBtn.addEventListener('click', () => {
|
|
4949
|
+
this.copyShareableURL();
|
|
4950
|
+
const iconContainer = copyUrlBtn.querySelector('.cv-btn-icon');
|
|
4951
|
+
if (iconContainer) {
|
|
4952
|
+
const originalIcon = iconContainer.innerHTML;
|
|
4953
|
+
iconContainer.innerHTML = getTickIcon();
|
|
4954
|
+
setTimeout(() => {
|
|
4955
|
+
iconContainer.innerHTML = originalIcon;
|
|
4956
|
+
}, 2000);
|
|
4957
|
+
}
|
|
4958
|
+
});
|
|
4959
|
+
}
|
|
4658
4960
|
}
|
|
4659
4961
|
/**
|
|
4660
4962
|
* Apply theme class to the modal overlay based on options
|
|
@@ -4678,25 +4980,33 @@ ${TAB_STYLES}
|
|
|
4678
4980
|
}
|
|
4679
4981
|
// Collect toggle values
|
|
4680
4982
|
const toggles = [];
|
|
4681
|
-
const
|
|
4682
|
-
|
|
4683
|
-
|
|
4684
|
-
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
tabs[groupId] = select.value;
|
|
4983
|
+
const peekToggles = [];
|
|
4984
|
+
// Get all radio inputs
|
|
4985
|
+
const radios = this.stateModal.querySelectorAll('input[type="radio"]:checked');
|
|
4986
|
+
radios.forEach(radio => {
|
|
4987
|
+
const input = radio;
|
|
4988
|
+
const toggleId = input.getAttribute('data-toggle');
|
|
4989
|
+
if (toggleId) {
|
|
4990
|
+
if (input.value === 'show') {
|
|
4991
|
+
toggles.push(toggleId);
|
|
4992
|
+
}
|
|
4993
|
+
else if (input.value === 'peek') {
|
|
4994
|
+
peekToggles.push(toggleId);
|
|
4995
|
+
}
|
|
4695
4996
|
}
|
|
4696
4997
|
});
|
|
4697
|
-
const result = { toggles };
|
|
4698
|
-
|
|
4699
|
-
|
|
4998
|
+
const result = { shownToggles: toggles, peekToggles };
|
|
4999
|
+
// Get active tabs from selects
|
|
5000
|
+
const selects = this.stateModal.querySelectorAll('select[data-group-id]');
|
|
5001
|
+
if (selects.length > 0) {
|
|
5002
|
+
result.tabs = {};
|
|
5003
|
+
selects.forEach(select => {
|
|
5004
|
+
const el = select;
|
|
5005
|
+
const groupId = el.getAttribute('data-group-id');
|
|
5006
|
+
if (groupId) {
|
|
5007
|
+
result.tabs[groupId] = el.value;
|
|
5008
|
+
}
|
|
5009
|
+
});
|
|
4700
5010
|
}
|
|
4701
5011
|
return result;
|
|
4702
5012
|
}
|
|
@@ -4716,18 +5026,29 @@ ${TAB_STYLES}
|
|
|
4716
5026
|
loadCurrentStateIntoForm() {
|
|
4717
5027
|
if (!this.stateModal)
|
|
4718
5028
|
return;
|
|
4719
|
-
//
|
|
4720
|
-
const
|
|
4721
|
-
|
|
5029
|
+
// We need complete state for both shown and peek toggles
|
|
5030
|
+
const currentState = this.core.getCurrentState();
|
|
5031
|
+
const currentToggles = currentState.shownToggles || [];
|
|
5032
|
+
const currentPeekToggles = currentState.peekToggles || [];
|
|
5033
|
+
// Reset all inputs first (optional, but good for clarity)
|
|
4722
5034
|
const allToggleInputs = this.stateModal.querySelectorAll('.cv-toggle-input');
|
|
4723
|
-
|
|
4724
|
-
|
|
5035
|
+
// Identify unique toggles present in the modal
|
|
5036
|
+
const uniqueToggles = new Set();
|
|
5037
|
+
allToggleInputs.forEach(input => {
|
|
5038
|
+
if (input.dataset.toggle)
|
|
5039
|
+
uniqueToggles.add(input.dataset.toggle);
|
|
4725
5040
|
});
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
5041
|
+
uniqueToggles.forEach(toggleId => {
|
|
5042
|
+
let valueToSelect = 'hide';
|
|
5043
|
+
if (currentToggles.includes(toggleId)) {
|
|
5044
|
+
valueToSelect = 'show';
|
|
5045
|
+
}
|
|
5046
|
+
else if (currentPeekToggles.includes(toggleId)) {
|
|
5047
|
+
valueToSelect = 'peek';
|
|
5048
|
+
}
|
|
5049
|
+
const input = this.stateModal.querySelector(`input[name="cv-toggle-${toggleId}"][value="${valueToSelect}"]`);
|
|
5050
|
+
if (input) {
|
|
5051
|
+
input.checked = true;
|
|
4731
5052
|
}
|
|
4732
5053
|
});
|
|
4733
5054
|
// Load tab group selections
|
|
@@ -4750,7 +5071,7 @@ ${TAB_STYLES}
|
|
|
4750
5071
|
const tabNavToggle = this.stateModal.querySelector('.cv-nav-pref-input');
|
|
4751
5072
|
const navIcon = this.stateModal?.querySelector('#cv-nav-icon');
|
|
4752
5073
|
if (tabNavToggle) {
|
|
4753
|
-
tabNavToggle.checked = navPref;
|
|
5074
|
+
tabNavToggle.checked = !navPref;
|
|
4754
5075
|
// Ensure UI matches actual visibility
|
|
4755
5076
|
TabManager.setNavsVisibility(document.body, navPref);
|
|
4756
5077
|
// Update the nav icon to reflect the current state
|