@customviews-js/customviews 1.1.2 → 1.1.4
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 +143 -57
- package/dist/custom-views.core.cjs.js.map +1 -1
- package/dist/custom-views.core.esm.js +143 -57
- package/dist/custom-views.core.esm.js.map +1 -1
- package/dist/custom-views.esm.js +143 -57
- package/dist/custom-views.esm.js.map +1 -1
- package/dist/custom-views.js +143 -57
- 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/render.d.ts.map +1 -1
- package/dist/types/core/url-state-manager.d.ts +2 -2
- package/dist/types/core/url-state-manager.d.ts.map +1 -1
- package/dist/types/core/widget.d.ts.map +1 -1
- 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/widget-styles.d.ts +1 -1
- package/dist/types/styles/widget-styles.d.ts.map +1 -1
- package/dist/types/types/types.d.ts +1 -1
- package/dist/types/types/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/custom-views.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @customviews-js/customviews v1.1.
|
|
2
|
+
* @customviews-js/customviews v1.1.4
|
|
3
3
|
* (c) 2025 Chan Ger Teck
|
|
4
4
|
* Released under the MIT License.
|
|
5
5
|
*/
|
|
@@ -130,14 +130,16 @@
|
|
|
130
130
|
return url.toString();
|
|
131
131
|
}
|
|
132
132
|
/**
|
|
133
|
-
* Encode state into URL-safe string
|
|
133
|
+
* Encode state into URL-safe string (Toggles and Tabs only currently)
|
|
134
134
|
*/
|
|
135
135
|
static encodeState(state) {
|
|
136
136
|
try {
|
|
137
137
|
// Create a compact representation
|
|
138
|
-
const compact = {
|
|
139
|
-
|
|
140
|
-
|
|
138
|
+
const compact = {};
|
|
139
|
+
// Add toggles if present and non-empty
|
|
140
|
+
if (state.toggles && state.toggles.length > 0) {
|
|
141
|
+
compact.t = state.toggles;
|
|
142
|
+
}
|
|
141
143
|
// Add tab groups if present
|
|
142
144
|
if (state.tabs && Object.keys(state.tabs).length > 0) {
|
|
143
145
|
compact.g = Object.entries(state.tabs);
|
|
@@ -163,7 +165,7 @@
|
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
/**
|
|
166
|
-
* Decode custom state from URL parameter
|
|
168
|
+
* Decode custom state from URL parameter (Toggles and Tabs only currently)
|
|
167
169
|
*/
|
|
168
170
|
static decodeState(encoded) {
|
|
169
171
|
try {
|
|
@@ -188,10 +190,12 @@
|
|
|
188
190
|
if (!compact || typeof compact !== 'object') {
|
|
189
191
|
throw new Error('Invalid compact state structure');
|
|
190
192
|
}
|
|
193
|
+
// Reconstruct State from compact format
|
|
194
|
+
// Reconstruct Toggles
|
|
191
195
|
const state = {
|
|
192
196
|
toggles: Array.isArray(compact.t) ? compact.t : []
|
|
193
197
|
};
|
|
194
|
-
// Reconstruct
|
|
198
|
+
// Reconstruct Tabs
|
|
195
199
|
if (Array.isArray(compact.g)) {
|
|
196
200
|
state.tabs = {};
|
|
197
201
|
for (const [groupId, tabId] of compact.g) {
|
|
@@ -281,8 +285,12 @@
|
|
|
281
285
|
document.head.appendChild(link);
|
|
282
286
|
}
|
|
283
287
|
function replaceIconShortcodes(text) {
|
|
284
|
-
//
|
|
285
|
-
return text.replace(/:fa-([\w-]+):/g, (_, icon) =>
|
|
288
|
+
// Matches :fa-*, :fas-*, :fab-* etc.
|
|
289
|
+
return text.replace(/:(fa[b|s|r]?)-([\w-]+):/g, (_, style, icon) => {
|
|
290
|
+
// style = fa, fas, fab, far, etc.
|
|
291
|
+
// Default to "fa" if only "fa-" is given
|
|
292
|
+
return `<i class="${style} fa-${icon}"></i>`;
|
|
293
|
+
});
|
|
286
294
|
}
|
|
287
295
|
/** --- Basic renderers --- */
|
|
288
296
|
function renderImage(el, asset) {
|
|
@@ -874,7 +882,7 @@ ${TAB_STYLES}
|
|
|
874
882
|
this.rootEl = opt.rootEl || document.body;
|
|
875
883
|
this.persistenceManager = new PersistenceManager();
|
|
876
884
|
this.visibilityManager = new VisibilityManager();
|
|
877
|
-
this.showUrlEnabled = opt.showUrl ??
|
|
885
|
+
this.showUrlEnabled = opt.showUrl ?? false;
|
|
878
886
|
this.lastAppliedState = this.cloneState(this.config?.defaultState);
|
|
879
887
|
}
|
|
880
888
|
getConfig() {
|
|
@@ -1074,9 +1082,9 @@ ${TAB_STYLES}
|
|
|
1074
1082
|
});
|
|
1075
1083
|
}
|
|
1076
1084
|
cloneState(state) {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
return
|
|
1085
|
+
if (!state)
|
|
1086
|
+
return {};
|
|
1087
|
+
return JSON.parse(JSON.stringify(state));
|
|
1080
1088
|
}
|
|
1081
1089
|
getTrackedStateSnapshot() {
|
|
1082
1090
|
if (this.lastAppliedState) {
|
|
@@ -1085,7 +1093,7 @@ ${TAB_STYLES}
|
|
|
1085
1093
|
if (this.config) {
|
|
1086
1094
|
return this.cloneState(this.config.defaultState);
|
|
1087
1095
|
}
|
|
1088
|
-
return {
|
|
1096
|
+
return {};
|
|
1089
1097
|
}
|
|
1090
1098
|
}
|
|
1091
1099
|
|
|
@@ -1176,8 +1184,14 @@ ${TAB_STYLES}
|
|
|
1176
1184
|
const baseURL = opts.baseURL || '';
|
|
1177
1185
|
if (opts.assetsJsonPath) {
|
|
1178
1186
|
const assetsPath = prependBaseUrl(opts.assetsJsonPath, baseURL);
|
|
1179
|
-
|
|
1180
|
-
|
|
1187
|
+
try {
|
|
1188
|
+
const assetsJson = await (await fetch(assetsPath)).json();
|
|
1189
|
+
assetsManager = new AssetsManager(assetsJson, baseURL);
|
|
1190
|
+
}
|
|
1191
|
+
catch (error) {
|
|
1192
|
+
console.error(`[CustomViews] Failed to load assets JSON from ${assetsPath}:`, error);
|
|
1193
|
+
assetsManager = new AssetsManager({}, baseURL);
|
|
1194
|
+
}
|
|
1181
1195
|
}
|
|
1182
1196
|
else {
|
|
1183
1197
|
assetsManager = new AssetsManager({}, baseURL);
|
|
@@ -1190,7 +1204,7 @@ ${TAB_STYLES}
|
|
|
1190
1204
|
else {
|
|
1191
1205
|
console.error("No config provided, using minimal default config");
|
|
1192
1206
|
// Create a minimal default config
|
|
1193
|
-
config = { allToggles: [], defaultState: {
|
|
1207
|
+
config = { allToggles: [], defaultState: {} };
|
|
1194
1208
|
}
|
|
1195
1209
|
const coreOptions = {
|
|
1196
1210
|
assetsManager,
|
|
@@ -1651,9 +1665,61 @@ ${TAB_STYLES}
|
|
|
1651
1665
|
margin: 0;
|
|
1652
1666
|
}
|
|
1653
1667
|
|
|
1654
|
-
.cv-
|
|
1655
|
-
|
|
1656
|
-
width:
|
|
1668
|
+
.cv-toggle-switch {
|
|
1669
|
+
position: relative;
|
|
1670
|
+
width: 44px;
|
|
1671
|
+
height: 24px;
|
|
1672
|
+
background: #ccc;
|
|
1673
|
+
border-radius: 12px;
|
|
1674
|
+
margin-right: 12px;
|
|
1675
|
+
cursor: pointer;
|
|
1676
|
+
transition: background-color 0.3s ease;
|
|
1677
|
+
flex-shrink: 0;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
.cv-toggle-switch:hover {
|
|
1681
|
+
background: #bbb;
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
.cv-toggle-switch.cv-toggle-active {
|
|
1685
|
+
background: #007bff;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
.cv-toggle-switch.cv-toggle-active:hover {
|
|
1689
|
+
background: #0056b3;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
.cv-toggle-handle {
|
|
1693
|
+
position: absolute;
|
|
1694
|
+
top: 2px;
|
|
1695
|
+
left: 2px;
|
|
1696
|
+
width: 20px;
|
|
1697
|
+
height: 20px;
|
|
1698
|
+
background: white;
|
|
1699
|
+
border-radius: 50%;
|
|
1700
|
+
transition: transform 0.3s ease;
|
|
1701
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.cv-toggle-switch.cv-toggle-active .cv-toggle-handle {
|
|
1705
|
+
transform: translateX(20px);
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
/* Dark theme toggle switch styles */
|
|
1709
|
+
.cv-widget-theme-dark .cv-toggle-switch {
|
|
1710
|
+
background: #4a5568;
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
.cv-widget-theme-dark .cv-toggle-switch:hover {
|
|
1714
|
+
background: #5a6578;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active {
|
|
1718
|
+
background: #63b3ed;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active:hover {
|
|
1722
|
+
background: #4299e1;
|
|
1657
1723
|
}
|
|
1658
1724
|
|
|
1659
1725
|
.cv-tab-groups {
|
|
@@ -1882,11 +1948,11 @@ ${TAB_STYLES}
|
|
|
1882
1948
|
position: options.position || 'middle-left',
|
|
1883
1949
|
theme: options.theme || 'light',
|
|
1884
1950
|
showReset: options.showReset ?? true,
|
|
1885
|
-
title: options.title || '
|
|
1951
|
+
title: options.title || 'Customize View',
|
|
1886
1952
|
description: options.description || 'Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.',
|
|
1887
1953
|
showWelcome: options.showWelcome ?? false,
|
|
1888
|
-
welcomeTitle: options.welcomeTitle || '
|
|
1889
|
-
welcomeMessage: options.welcomeMessage || 'This site
|
|
1954
|
+
welcomeTitle: options.welcomeTitle || 'Site Customization',
|
|
1955
|
+
welcomeMessage: options.welcomeMessage || 'This site is powered by Custom Views. Use the widget on the side (⚙) to customize your experience. Your preferences will be saved and can be shared via URL.<br><br>Learn more at <a href="https://github.com/customviews-js/customviews" target="_blank">customviews GitHub</a>.',
|
|
1890
1956
|
showTabGroups: options.showTabGroups ?? true
|
|
1891
1957
|
};
|
|
1892
1958
|
// No external state manager to initialize
|
|
@@ -1969,7 +2035,9 @@ ${TAB_STYLES}
|
|
|
1969
2035
|
? toggles.map(toggle => `
|
|
1970
2036
|
<div class="cv-custom-state-toggle">
|
|
1971
2037
|
<label>
|
|
1972
|
-
<
|
|
2038
|
+
<div class="cv-toggle-switch" data-toggle="${toggle}">
|
|
2039
|
+
<div class="cv-toggle-handle"></div>
|
|
2040
|
+
</div>
|
|
1973
2041
|
${this.formatToggleName(toggle)}
|
|
1974
2042
|
</label>
|
|
1975
2043
|
</div>
|
|
@@ -1978,12 +2046,34 @@ ${TAB_STYLES}
|
|
|
1978
2046
|
// Get tab groups
|
|
1979
2047
|
const tabGroups = this.core.getTabGroups();
|
|
1980
2048
|
let tabGroupsHTML = '';
|
|
2049
|
+
// Check if any tab group or tab labels contain Font Awesome shortcodes
|
|
2050
|
+
let hasFontAwesomeShortcodes = false;
|
|
2051
|
+
if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
|
|
2052
|
+
for (const group of tabGroups) {
|
|
2053
|
+
if (group.label && /:fa-[\w-]+:/.test(group.label)) {
|
|
2054
|
+
hasFontAwesomeShortcodes = true;
|
|
2055
|
+
break;
|
|
2056
|
+
}
|
|
2057
|
+
for (const tab of group.tabs) {
|
|
2058
|
+
if (tab.label && /:fa-[\w-]+:/.test(tab.label)) {
|
|
2059
|
+
hasFontAwesomeShortcodes = true;
|
|
2060
|
+
break;
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
if (hasFontAwesomeShortcodes)
|
|
2064
|
+
break;
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
// Inject Font Awesome only if shortcodes are found
|
|
2068
|
+
if (hasFontAwesomeShortcodes) {
|
|
2069
|
+
ensureFontAwesomeInjected();
|
|
2070
|
+
}
|
|
1981
2071
|
if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
|
|
1982
2072
|
const tabGroupControls = tabGroups.map(group => {
|
|
1983
|
-
const options = group.tabs.map(tab => `<option value="${tab.id}">${tab.label || tab.id}</option>`).join('');
|
|
2073
|
+
const options = group.tabs.map(tab => `<option value="${tab.id}">${replaceIconShortcodes(tab.label || tab.id)}</option>`).join('');
|
|
1984
2074
|
return `
|
|
1985
2075
|
<div class="cv-tab-group-control">
|
|
1986
|
-
<label for="tab-group-${group.id}">${group.label || group.id}</label>
|
|
2076
|
+
<label for="tab-group-${group.id}">${replaceIconShortcodes(group.label || group.id)}</label>
|
|
1987
2077
|
<select id="tab-group-${group.id}" class="cv-tab-group-select" data-group-id="${group.id}">
|
|
1988
2078
|
${options}
|
|
1989
2079
|
</select>
|
|
@@ -2000,7 +2090,7 @@ ${TAB_STYLES}
|
|
|
2000
2090
|
this.modal.innerHTML = `
|
|
2001
2091
|
<div class="cv-widget-modal cv-custom-state-modal">
|
|
2002
2092
|
<div class="cv-widget-modal-header">
|
|
2003
|
-
<h3
|
|
2093
|
+
<h3>${this.options.title}</h3>
|
|
2004
2094
|
<button class="cv-widget-modal-close" aria-label="Close modal">×</button>
|
|
2005
2095
|
</div>
|
|
2006
2096
|
<div class="cv-widget-modal-content">
|
|
@@ -2055,10 +2145,11 @@ ${TAB_STYLES}
|
|
|
2055
2145
|
this.loadCurrentStateIntoForm();
|
|
2056
2146
|
});
|
|
2057
2147
|
}
|
|
2058
|
-
// Listen to toggle
|
|
2059
|
-
const
|
|
2060
|
-
|
|
2061
|
-
|
|
2148
|
+
// Listen to toggle switches
|
|
2149
|
+
const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
|
|
2150
|
+
toggleSwitches.forEach(toggleSwitch => {
|
|
2151
|
+
toggleSwitch.addEventListener('click', () => {
|
|
2152
|
+
toggleSwitch.classList.toggle('cv-toggle-active');
|
|
2062
2153
|
const state = this.getCurrentCustomStateFromModal();
|
|
2063
2154
|
this.core.applyState(state);
|
|
2064
2155
|
});
|
|
@@ -2107,14 +2198,14 @@ ${TAB_STYLES}
|
|
|
2107
2198
|
*/
|
|
2108
2199
|
getCurrentCustomStateFromModal() {
|
|
2109
2200
|
if (!this.modal) {
|
|
2110
|
-
return {
|
|
2201
|
+
return {};
|
|
2111
2202
|
}
|
|
2112
2203
|
// Collect toggle values
|
|
2113
2204
|
const toggles = [];
|
|
2114
|
-
const
|
|
2115
|
-
|
|
2116
|
-
const toggle =
|
|
2117
|
-
if (toggle &&
|
|
2205
|
+
const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
|
|
2206
|
+
toggleSwitches.forEach(toggleSwitch => {
|
|
2207
|
+
const toggle = toggleSwitch.dataset.toggle;
|
|
2208
|
+
if (toggle && toggleSwitch.classList.contains('cv-toggle-active')) {
|
|
2118
2209
|
toggles.push(toggle);
|
|
2119
2210
|
}
|
|
2120
2211
|
});
|
|
@@ -2127,7 +2218,11 @@ ${TAB_STYLES}
|
|
|
2127
2218
|
tabs[groupId] = select.value;
|
|
2128
2219
|
}
|
|
2129
2220
|
});
|
|
2130
|
-
|
|
2221
|
+
const result = { toggles };
|
|
2222
|
+
if (Object.keys(tabs).length > 0) {
|
|
2223
|
+
result.tabs = tabs;
|
|
2224
|
+
}
|
|
2225
|
+
return result;
|
|
2131
2226
|
}
|
|
2132
2227
|
/**
|
|
2133
2228
|
* Copy shareable URL to clipboard
|
|
@@ -2147,20 +2242,16 @@ ${TAB_STYLES}
|
|
|
2147
2242
|
return;
|
|
2148
2243
|
// Get currently active toggles (from custom state or default configuration)
|
|
2149
2244
|
const activeToggles = this.core.getCurrentActiveToggles();
|
|
2150
|
-
// First,
|
|
2151
|
-
const
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
checkbox.disabled = false;
|
|
2155
|
-
checkbox.parentElement?.removeAttribute('aria-hidden');
|
|
2245
|
+
// First, deactivate all toggle switches
|
|
2246
|
+
const allToggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
|
|
2247
|
+
allToggleSwitches.forEach(toggleSwitch => {
|
|
2248
|
+
toggleSwitch.classList.remove('cv-toggle-active');
|
|
2156
2249
|
});
|
|
2157
|
-
// Then
|
|
2250
|
+
// Then activate the ones that should be active
|
|
2158
2251
|
activeToggles.forEach(toggle => {
|
|
2159
|
-
const
|
|
2160
|
-
if (
|
|
2161
|
-
|
|
2162
|
-
checkbox.checked = true;
|
|
2163
|
-
}
|
|
2252
|
+
const toggleSwitch = this.modal?.querySelector(`[data-toggle="${toggle}"]`);
|
|
2253
|
+
if (toggleSwitch) {
|
|
2254
|
+
toggleSwitch.classList.add('cv-toggle-active');
|
|
2164
2255
|
}
|
|
2165
2256
|
});
|
|
2166
2257
|
// Load tab group selections
|
|
@@ -2213,7 +2304,7 @@ ${TAB_STYLES}
|
|
|
2213
2304
|
</div>
|
|
2214
2305
|
<div class="cv-widget-modal-content">
|
|
2215
2306
|
<div class="cv-welcome-content">
|
|
2216
|
-
<p>${this.options.welcomeMessage}</p>
|
|
2307
|
+
<p style="text-align: justify;">${this.options.welcomeMessage}</p>
|
|
2217
2308
|
|
|
2218
2309
|
<div class="cv-welcome-widget-preview">
|
|
2219
2310
|
<div class="cv-welcome-widget-icon">⚙</div>
|
|
@@ -2334,13 +2425,8 @@ ${TAB_STYLES}
|
|
|
2334
2425
|
console.warn(`[CustomViews] Config file not found at ${fullConfigPath}. Using defaults.`);
|
|
2335
2426
|
// Provide minimal default config structure
|
|
2336
2427
|
configFile = {
|
|
2337
|
-
config: {
|
|
2338
|
-
|
|
2339
|
-
defaultState: { toggles: [] }
|
|
2340
|
-
},
|
|
2341
|
-
widget: {
|
|
2342
|
-
enabled: true
|
|
2343
|
-
}
|
|
2428
|
+
config: { allToggles: [], defaultState: {} },
|
|
2429
|
+
widget: { enabled: true }
|
|
2344
2430
|
};
|
|
2345
2431
|
}
|
|
2346
2432
|
else {
|