@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.esm.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
|
*/
|
|
@@ -124,14 +124,16 @@ class URLStateManager {
|
|
|
124
124
|
return url.toString();
|
|
125
125
|
}
|
|
126
126
|
/**
|
|
127
|
-
* Encode state into URL-safe string
|
|
127
|
+
* Encode state into URL-safe string (Toggles and Tabs only currently)
|
|
128
128
|
*/
|
|
129
129
|
static encodeState(state) {
|
|
130
130
|
try {
|
|
131
131
|
// Create a compact representation
|
|
132
|
-
const compact = {
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
const compact = {};
|
|
133
|
+
// Add toggles if present and non-empty
|
|
134
|
+
if (state.toggles && state.toggles.length > 0) {
|
|
135
|
+
compact.t = state.toggles;
|
|
136
|
+
}
|
|
135
137
|
// Add tab groups if present
|
|
136
138
|
if (state.tabs && Object.keys(state.tabs).length > 0) {
|
|
137
139
|
compact.g = Object.entries(state.tabs);
|
|
@@ -157,7 +159,7 @@ class URLStateManager {
|
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
161
|
/**
|
|
160
|
-
* Decode custom state from URL parameter
|
|
162
|
+
* Decode custom state from URL parameter (Toggles and Tabs only currently)
|
|
161
163
|
*/
|
|
162
164
|
static decodeState(encoded) {
|
|
163
165
|
try {
|
|
@@ -182,10 +184,12 @@ class URLStateManager {
|
|
|
182
184
|
if (!compact || typeof compact !== 'object') {
|
|
183
185
|
throw new Error('Invalid compact state structure');
|
|
184
186
|
}
|
|
187
|
+
// Reconstruct State from compact format
|
|
188
|
+
// Reconstruct Toggles
|
|
185
189
|
const state = {
|
|
186
190
|
toggles: Array.isArray(compact.t) ? compact.t : []
|
|
187
191
|
};
|
|
188
|
-
// Reconstruct
|
|
192
|
+
// Reconstruct Tabs
|
|
189
193
|
if (Array.isArray(compact.g)) {
|
|
190
194
|
state.tabs = {};
|
|
191
195
|
for (const [groupId, tabId] of compact.g) {
|
|
@@ -275,8 +279,12 @@ function ensureFontAwesomeInjected() {
|
|
|
275
279
|
document.head.appendChild(link);
|
|
276
280
|
}
|
|
277
281
|
function replaceIconShortcodes(text) {
|
|
278
|
-
//
|
|
279
|
-
return text.replace(/:fa-([\w-]+):/g, (_, icon) =>
|
|
282
|
+
// Matches :fa-*, :fas-*, :fab-* etc.
|
|
283
|
+
return text.replace(/:(fa[b|s|r]?)-([\w-]+):/g, (_, style, icon) => {
|
|
284
|
+
// style = fa, fas, fab, far, etc.
|
|
285
|
+
// Default to "fa" if only "fa-" is given
|
|
286
|
+
return `<i class="${style} fa-${icon}"></i>`;
|
|
287
|
+
});
|
|
280
288
|
}
|
|
281
289
|
/** --- Basic renderers --- */
|
|
282
290
|
function renderImage(el, asset) {
|
|
@@ -868,7 +876,7 @@ class CustomViewsCore {
|
|
|
868
876
|
this.rootEl = opt.rootEl || document.body;
|
|
869
877
|
this.persistenceManager = new PersistenceManager();
|
|
870
878
|
this.visibilityManager = new VisibilityManager();
|
|
871
|
-
this.showUrlEnabled = opt.showUrl ??
|
|
879
|
+
this.showUrlEnabled = opt.showUrl ?? false;
|
|
872
880
|
this.lastAppliedState = this.cloneState(this.config?.defaultState);
|
|
873
881
|
}
|
|
874
882
|
getConfig() {
|
|
@@ -1068,9 +1076,9 @@ class CustomViewsCore {
|
|
|
1068
1076
|
});
|
|
1069
1077
|
}
|
|
1070
1078
|
cloneState(state) {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
return
|
|
1079
|
+
if (!state)
|
|
1080
|
+
return {};
|
|
1081
|
+
return JSON.parse(JSON.stringify(state));
|
|
1074
1082
|
}
|
|
1075
1083
|
getTrackedStateSnapshot() {
|
|
1076
1084
|
if (this.lastAppliedState) {
|
|
@@ -1079,7 +1087,7 @@ class CustomViewsCore {
|
|
|
1079
1087
|
if (this.config) {
|
|
1080
1088
|
return this.cloneState(this.config.defaultState);
|
|
1081
1089
|
}
|
|
1082
|
-
return {
|
|
1090
|
+
return {};
|
|
1083
1091
|
}
|
|
1084
1092
|
}
|
|
1085
1093
|
|
|
@@ -1170,8 +1178,14 @@ class CustomViews {
|
|
|
1170
1178
|
const baseURL = opts.baseURL || '';
|
|
1171
1179
|
if (opts.assetsJsonPath) {
|
|
1172
1180
|
const assetsPath = prependBaseUrl(opts.assetsJsonPath, baseURL);
|
|
1173
|
-
|
|
1174
|
-
|
|
1181
|
+
try {
|
|
1182
|
+
const assetsJson = await (await fetch(assetsPath)).json();
|
|
1183
|
+
assetsManager = new AssetsManager(assetsJson, baseURL);
|
|
1184
|
+
}
|
|
1185
|
+
catch (error) {
|
|
1186
|
+
console.error(`[CustomViews] Failed to load assets JSON from ${assetsPath}:`, error);
|
|
1187
|
+
assetsManager = new AssetsManager({}, baseURL);
|
|
1188
|
+
}
|
|
1175
1189
|
}
|
|
1176
1190
|
else {
|
|
1177
1191
|
assetsManager = new AssetsManager({}, baseURL);
|
|
@@ -1184,7 +1198,7 @@ class CustomViews {
|
|
|
1184
1198
|
else {
|
|
1185
1199
|
console.error("No config provided, using minimal default config");
|
|
1186
1200
|
// Create a minimal default config
|
|
1187
|
-
config = { allToggles: [], defaultState: {
|
|
1201
|
+
config = { allToggles: [], defaultState: {} };
|
|
1188
1202
|
}
|
|
1189
1203
|
const coreOptions = {
|
|
1190
1204
|
assetsManager,
|
|
@@ -1645,9 +1659,61 @@ const WIDGET_STYLES = `
|
|
|
1645
1659
|
margin: 0;
|
|
1646
1660
|
}
|
|
1647
1661
|
|
|
1648
|
-
.cv-
|
|
1649
|
-
|
|
1650
|
-
width:
|
|
1662
|
+
.cv-toggle-switch {
|
|
1663
|
+
position: relative;
|
|
1664
|
+
width: 44px;
|
|
1665
|
+
height: 24px;
|
|
1666
|
+
background: #ccc;
|
|
1667
|
+
border-radius: 12px;
|
|
1668
|
+
margin-right: 12px;
|
|
1669
|
+
cursor: pointer;
|
|
1670
|
+
transition: background-color 0.3s ease;
|
|
1671
|
+
flex-shrink: 0;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
.cv-toggle-switch:hover {
|
|
1675
|
+
background: #bbb;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
.cv-toggle-switch.cv-toggle-active {
|
|
1679
|
+
background: #007bff;
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
.cv-toggle-switch.cv-toggle-active:hover {
|
|
1683
|
+
background: #0056b3;
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
.cv-toggle-handle {
|
|
1687
|
+
position: absolute;
|
|
1688
|
+
top: 2px;
|
|
1689
|
+
left: 2px;
|
|
1690
|
+
width: 20px;
|
|
1691
|
+
height: 20px;
|
|
1692
|
+
background: white;
|
|
1693
|
+
border-radius: 50%;
|
|
1694
|
+
transition: transform 0.3s ease;
|
|
1695
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
.cv-toggle-switch.cv-toggle-active .cv-toggle-handle {
|
|
1699
|
+
transform: translateX(20px);
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
/* Dark theme toggle switch styles */
|
|
1703
|
+
.cv-widget-theme-dark .cv-toggle-switch {
|
|
1704
|
+
background: #4a5568;
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
.cv-widget-theme-dark .cv-toggle-switch:hover {
|
|
1708
|
+
background: #5a6578;
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active {
|
|
1712
|
+
background: #63b3ed;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
.cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active:hover {
|
|
1716
|
+
background: #4299e1;
|
|
1651
1717
|
}
|
|
1652
1718
|
|
|
1653
1719
|
.cv-tab-groups {
|
|
@@ -1876,11 +1942,11 @@ class CustomViewsWidget {
|
|
|
1876
1942
|
position: options.position || 'middle-left',
|
|
1877
1943
|
theme: options.theme || 'light',
|
|
1878
1944
|
showReset: options.showReset ?? true,
|
|
1879
|
-
title: options.title || '
|
|
1945
|
+
title: options.title || 'Customize View',
|
|
1880
1946
|
description: options.description || 'Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.',
|
|
1881
1947
|
showWelcome: options.showWelcome ?? false,
|
|
1882
|
-
welcomeTitle: options.welcomeTitle || '
|
|
1883
|
-
welcomeMessage: options.welcomeMessage || 'This site
|
|
1948
|
+
welcomeTitle: options.welcomeTitle || 'Site Customization',
|
|
1949
|
+
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>.',
|
|
1884
1950
|
showTabGroups: options.showTabGroups ?? true
|
|
1885
1951
|
};
|
|
1886
1952
|
// No external state manager to initialize
|
|
@@ -1963,7 +2029,9 @@ class CustomViewsWidget {
|
|
|
1963
2029
|
? toggles.map(toggle => `
|
|
1964
2030
|
<div class="cv-custom-state-toggle">
|
|
1965
2031
|
<label>
|
|
1966
|
-
<
|
|
2032
|
+
<div class="cv-toggle-switch" data-toggle="${toggle}">
|
|
2033
|
+
<div class="cv-toggle-handle"></div>
|
|
2034
|
+
</div>
|
|
1967
2035
|
${this.formatToggleName(toggle)}
|
|
1968
2036
|
</label>
|
|
1969
2037
|
</div>
|
|
@@ -1972,12 +2040,34 @@ class CustomViewsWidget {
|
|
|
1972
2040
|
// Get tab groups
|
|
1973
2041
|
const tabGroups = this.core.getTabGroups();
|
|
1974
2042
|
let tabGroupsHTML = '';
|
|
2043
|
+
// Check if any tab group or tab labels contain Font Awesome shortcodes
|
|
2044
|
+
let hasFontAwesomeShortcodes = false;
|
|
2045
|
+
if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
|
|
2046
|
+
for (const group of tabGroups) {
|
|
2047
|
+
if (group.label && /:fa-[\w-]+:/.test(group.label)) {
|
|
2048
|
+
hasFontAwesomeShortcodes = true;
|
|
2049
|
+
break;
|
|
2050
|
+
}
|
|
2051
|
+
for (const tab of group.tabs) {
|
|
2052
|
+
if (tab.label && /:fa-[\w-]+:/.test(tab.label)) {
|
|
2053
|
+
hasFontAwesomeShortcodes = true;
|
|
2054
|
+
break;
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2057
|
+
if (hasFontAwesomeShortcodes)
|
|
2058
|
+
break;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
// Inject Font Awesome only if shortcodes are found
|
|
2062
|
+
if (hasFontAwesomeShortcodes) {
|
|
2063
|
+
ensureFontAwesomeInjected();
|
|
2064
|
+
}
|
|
1975
2065
|
if (this.options.showTabGroups && tabGroups && tabGroups.length > 0) {
|
|
1976
2066
|
const tabGroupControls = tabGroups.map(group => {
|
|
1977
|
-
const options = group.tabs.map(tab => `<option value="${tab.id}">${tab.label || tab.id}</option>`).join('');
|
|
2067
|
+
const options = group.tabs.map(tab => `<option value="${tab.id}">${replaceIconShortcodes(tab.label || tab.id)}</option>`).join('');
|
|
1978
2068
|
return `
|
|
1979
2069
|
<div class="cv-tab-group-control">
|
|
1980
|
-
<label for="tab-group-${group.id}">${group.label || group.id}</label>
|
|
2070
|
+
<label for="tab-group-${group.id}">${replaceIconShortcodes(group.label || group.id)}</label>
|
|
1981
2071
|
<select id="tab-group-${group.id}" class="cv-tab-group-select" data-group-id="${group.id}">
|
|
1982
2072
|
${options}
|
|
1983
2073
|
</select>
|
|
@@ -1994,7 +2084,7 @@ class CustomViewsWidget {
|
|
|
1994
2084
|
this.modal.innerHTML = `
|
|
1995
2085
|
<div class="cv-widget-modal cv-custom-state-modal">
|
|
1996
2086
|
<div class="cv-widget-modal-header">
|
|
1997
|
-
<h3
|
|
2087
|
+
<h3>${this.options.title}</h3>
|
|
1998
2088
|
<button class="cv-widget-modal-close" aria-label="Close modal">×</button>
|
|
1999
2089
|
</div>
|
|
2000
2090
|
<div class="cv-widget-modal-content">
|
|
@@ -2049,10 +2139,11 @@ class CustomViewsWidget {
|
|
|
2049
2139
|
this.loadCurrentStateIntoForm();
|
|
2050
2140
|
});
|
|
2051
2141
|
}
|
|
2052
|
-
// Listen to toggle
|
|
2053
|
-
const
|
|
2054
|
-
|
|
2055
|
-
|
|
2142
|
+
// Listen to toggle switches
|
|
2143
|
+
const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
|
|
2144
|
+
toggleSwitches.forEach(toggleSwitch => {
|
|
2145
|
+
toggleSwitch.addEventListener('click', () => {
|
|
2146
|
+
toggleSwitch.classList.toggle('cv-toggle-active');
|
|
2056
2147
|
const state = this.getCurrentCustomStateFromModal();
|
|
2057
2148
|
this.core.applyState(state);
|
|
2058
2149
|
});
|
|
@@ -2101,14 +2192,14 @@ class CustomViewsWidget {
|
|
|
2101
2192
|
*/
|
|
2102
2193
|
getCurrentCustomStateFromModal() {
|
|
2103
2194
|
if (!this.modal) {
|
|
2104
|
-
return {
|
|
2195
|
+
return {};
|
|
2105
2196
|
}
|
|
2106
2197
|
// Collect toggle values
|
|
2107
2198
|
const toggles = [];
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2110
|
-
const toggle =
|
|
2111
|
-
if (toggle &&
|
|
2199
|
+
const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
|
|
2200
|
+
toggleSwitches.forEach(toggleSwitch => {
|
|
2201
|
+
const toggle = toggleSwitch.dataset.toggle;
|
|
2202
|
+
if (toggle && toggleSwitch.classList.contains('cv-toggle-active')) {
|
|
2112
2203
|
toggles.push(toggle);
|
|
2113
2204
|
}
|
|
2114
2205
|
});
|
|
@@ -2121,7 +2212,11 @@ class CustomViewsWidget {
|
|
|
2121
2212
|
tabs[groupId] = select.value;
|
|
2122
2213
|
}
|
|
2123
2214
|
});
|
|
2124
|
-
|
|
2215
|
+
const result = { toggles };
|
|
2216
|
+
if (Object.keys(tabs).length > 0) {
|
|
2217
|
+
result.tabs = tabs;
|
|
2218
|
+
}
|
|
2219
|
+
return result;
|
|
2125
2220
|
}
|
|
2126
2221
|
/**
|
|
2127
2222
|
* Copy shareable URL to clipboard
|
|
@@ -2141,20 +2236,16 @@ class CustomViewsWidget {
|
|
|
2141
2236
|
return;
|
|
2142
2237
|
// Get currently active toggles (from custom state or default configuration)
|
|
2143
2238
|
const activeToggles = this.core.getCurrentActiveToggles();
|
|
2144
|
-
// First,
|
|
2145
|
-
const
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
checkbox.disabled = false;
|
|
2149
|
-
checkbox.parentElement?.removeAttribute('aria-hidden');
|
|
2239
|
+
// First, deactivate all toggle switches
|
|
2240
|
+
const allToggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
|
|
2241
|
+
allToggleSwitches.forEach(toggleSwitch => {
|
|
2242
|
+
toggleSwitch.classList.remove('cv-toggle-active');
|
|
2150
2243
|
});
|
|
2151
|
-
// Then
|
|
2244
|
+
// Then activate the ones that should be active
|
|
2152
2245
|
activeToggles.forEach(toggle => {
|
|
2153
|
-
const
|
|
2154
|
-
if (
|
|
2155
|
-
|
|
2156
|
-
checkbox.checked = true;
|
|
2157
|
-
}
|
|
2246
|
+
const toggleSwitch = this.modal?.querySelector(`[data-toggle="${toggle}"]`);
|
|
2247
|
+
if (toggleSwitch) {
|
|
2248
|
+
toggleSwitch.classList.add('cv-toggle-active');
|
|
2158
2249
|
}
|
|
2159
2250
|
});
|
|
2160
2251
|
// Load tab group selections
|
|
@@ -2207,7 +2298,7 @@ class CustomViewsWidget {
|
|
|
2207
2298
|
</div>
|
|
2208
2299
|
<div class="cv-widget-modal-content">
|
|
2209
2300
|
<div class="cv-welcome-content">
|
|
2210
|
-
<p>${this.options.welcomeMessage}</p>
|
|
2301
|
+
<p style="text-align: justify;">${this.options.welcomeMessage}</p>
|
|
2211
2302
|
|
|
2212
2303
|
<div class="cv-welcome-widget-preview">
|
|
2213
2304
|
<div class="cv-welcome-widget-icon">⚙</div>
|
|
@@ -2328,13 +2419,8 @@ function initializeFromScript() {
|
|
|
2328
2419
|
console.warn(`[CustomViews] Config file not found at ${fullConfigPath}. Using defaults.`);
|
|
2329
2420
|
// Provide minimal default config structure
|
|
2330
2421
|
configFile = {
|
|
2331
|
-
config: {
|
|
2332
|
-
|
|
2333
|
-
defaultState: { toggles: [] }
|
|
2334
|
-
},
|
|
2335
|
-
widget: {
|
|
2336
|
-
enabled: true
|
|
2337
|
-
}
|
|
2422
|
+
config: { allToggles: [], defaultState: {} },
|
|
2423
|
+
widget: { enabled: true }
|
|
2338
2424
|
};
|
|
2339
2425
|
}
|
|
2340
2426
|
else {
|