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