@customviews-js/customviews 1.1.2 → 1.1.3

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.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * @customviews-js/customviews v1.1.2
2
+ * @customviews-js/customviews v1.1.3
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
- t: state.toggles
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 tabs from compact format
192
+ // Reconstruct Tabs
189
193
  if (Array.isArray(compact.g)) {
190
194
  state.tabs = {};
191
195
  for (const [groupId, tabId] of compact.g) {
@@ -868,7 +872,7 @@ class CustomViewsCore {
868
872
  this.rootEl = opt.rootEl || document.body;
869
873
  this.persistenceManager = new PersistenceManager();
870
874
  this.visibilityManager = new VisibilityManager();
871
- this.showUrlEnabled = opt.showUrl ?? true;
875
+ this.showUrlEnabled = opt.showUrl ?? false;
872
876
  this.lastAppliedState = this.cloneState(this.config?.defaultState);
873
877
  }
874
878
  getConfig() {
@@ -1068,9 +1072,9 @@ class CustomViewsCore {
1068
1072
  });
1069
1073
  }
1070
1074
  cloneState(state) {
1071
- const toggles = state?.toggles ? [...state.toggles] : [];
1072
- const tabs = state?.tabs ? { ...state.tabs } : undefined;
1073
- return tabs ? { toggles, tabs } : { toggles };
1075
+ if (!state)
1076
+ return {};
1077
+ return JSON.parse(JSON.stringify(state));
1074
1078
  }
1075
1079
  getTrackedStateSnapshot() {
1076
1080
  if (this.lastAppliedState) {
@@ -1079,7 +1083,7 @@ class CustomViewsCore {
1079
1083
  if (this.config) {
1080
1084
  return this.cloneState(this.config.defaultState);
1081
1085
  }
1082
- return { toggles: [] };
1086
+ return {};
1083
1087
  }
1084
1088
  }
1085
1089
 
@@ -1170,8 +1174,14 @@ class CustomViews {
1170
1174
  const baseURL = opts.baseURL || '';
1171
1175
  if (opts.assetsJsonPath) {
1172
1176
  const assetsPath = prependBaseUrl(opts.assetsJsonPath, baseURL);
1173
- const assetsJson = await (await fetch(assetsPath)).json();
1174
- assetsManager = new AssetsManager(assetsJson, baseURL);
1177
+ try {
1178
+ const assetsJson = await (await fetch(assetsPath)).json();
1179
+ assetsManager = new AssetsManager(assetsJson, baseURL);
1180
+ }
1181
+ catch (error) {
1182
+ console.error(`[CustomViews] Failed to load assets JSON from ${assetsPath}:`, error);
1183
+ assetsManager = new AssetsManager({}, baseURL);
1184
+ }
1175
1185
  }
1176
1186
  else {
1177
1187
  assetsManager = new AssetsManager({}, baseURL);
@@ -1184,7 +1194,7 @@ class CustomViews {
1184
1194
  else {
1185
1195
  console.error("No config provided, using minimal default config");
1186
1196
  // Create a minimal default config
1187
- config = { allToggles: [], defaultState: { toggles: [] } };
1197
+ config = { allToggles: [], defaultState: {} };
1188
1198
  }
1189
1199
  const coreOptions = {
1190
1200
  assetsManager,
@@ -1645,9 +1655,61 @@ const WIDGET_STYLES = `
1645
1655
  margin: 0;
1646
1656
  }
1647
1657
 
1648
- .cv-custom-toggle-checkbox {
1649
- margin-right: 8px;
1650
- width: auto;
1658
+ .cv-toggle-switch {
1659
+ position: relative;
1660
+ width: 44px;
1661
+ height: 24px;
1662
+ background: #ccc;
1663
+ border-radius: 12px;
1664
+ margin-right: 12px;
1665
+ cursor: pointer;
1666
+ transition: background-color 0.3s ease;
1667
+ flex-shrink: 0;
1668
+ }
1669
+
1670
+ .cv-toggle-switch:hover {
1671
+ background: #bbb;
1672
+ }
1673
+
1674
+ .cv-toggle-switch.cv-toggle-active {
1675
+ background: #007bff;
1676
+ }
1677
+
1678
+ .cv-toggle-switch.cv-toggle-active:hover {
1679
+ background: #0056b3;
1680
+ }
1681
+
1682
+ .cv-toggle-handle {
1683
+ position: absolute;
1684
+ top: 2px;
1685
+ left: 2px;
1686
+ width: 20px;
1687
+ height: 20px;
1688
+ background: white;
1689
+ border-radius: 50%;
1690
+ transition: transform 0.3s ease;
1691
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
1692
+ }
1693
+
1694
+ .cv-toggle-switch.cv-toggle-active .cv-toggle-handle {
1695
+ transform: translateX(20px);
1696
+ }
1697
+
1698
+ /* Dark theme toggle switch styles */
1699
+ .cv-widget-theme-dark .cv-toggle-switch {
1700
+ background: #4a5568;
1701
+ }
1702
+
1703
+ .cv-widget-theme-dark .cv-toggle-switch:hover {
1704
+ background: #5a6578;
1705
+ }
1706
+
1707
+ .cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active {
1708
+ background: #63b3ed;
1709
+ }
1710
+
1711
+ .cv-widget-theme-dark .cv-toggle-switch.cv-toggle-active:hover {
1712
+ background: #4299e1;
1651
1713
  }
1652
1714
 
1653
1715
  .cv-tab-groups {
@@ -1876,11 +1938,11 @@ class CustomViewsWidget {
1876
1938
  position: options.position || 'middle-left',
1877
1939
  theme: options.theme || 'light',
1878
1940
  showReset: options.showReset ?? true,
1879
- title: options.title || 'Custom Views',
1941
+ title: options.title || 'Customize View',
1880
1942
  description: options.description || 'Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.',
1881
1943
  showWelcome: options.showWelcome ?? false,
1882
- welcomeTitle: options.welcomeTitle || 'This website uses Custom Views',
1883
- welcomeMessage: options.welcomeMessage || 'This site uses Custom Views to let you personalize your experience. Use the widget on the side (⚙) to show or hide different content sections based on your preferences. Your selections will be saved and can be shared via URL.',
1944
+ welcomeTitle: options.welcomeTitle || 'Site Customization',
1945
+ 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
1946
  showTabGroups: options.showTabGroups ?? true
1885
1947
  };
1886
1948
  // No external state manager to initialize
@@ -1963,7 +2025,9 @@ class CustomViewsWidget {
1963
2025
  ? toggles.map(toggle => `
1964
2026
  <div class="cv-custom-state-toggle">
1965
2027
  <label>
1966
- <input type="checkbox" class="cv-custom-toggle-checkbox" data-toggle="${toggle}" />
2028
+ <div class="cv-toggle-switch" data-toggle="${toggle}">
2029
+ <div class="cv-toggle-handle"></div>
2030
+ </div>
1967
2031
  ${this.formatToggleName(toggle)}
1968
2032
  </label>
1969
2033
  </div>
@@ -1994,7 +2058,7 @@ class CustomViewsWidget {
1994
2058
  this.modal.innerHTML = `
1995
2059
  <div class="cv-widget-modal cv-custom-state-modal">
1996
2060
  <div class="cv-widget-modal-header">
1997
- <h3>Customize View</h3>
2061
+ <h3>${this.options.title}</h3>
1998
2062
  <button class="cv-widget-modal-close" aria-label="Close modal">×</button>
1999
2063
  </div>
2000
2064
  <div class="cv-widget-modal-content">
@@ -2049,10 +2113,11 @@ class CustomViewsWidget {
2049
2113
  this.loadCurrentStateIntoForm();
2050
2114
  });
2051
2115
  }
2052
- // Listen to toggle checkboxes
2053
- const toggleCheckboxes = this.modal.querySelectorAll('.cv-custom-toggle-checkbox');
2054
- toggleCheckboxes.forEach(checkbox => {
2055
- checkbox.addEventListener('change', () => {
2116
+ // Listen to toggle switches
2117
+ const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
2118
+ toggleSwitches.forEach(toggleSwitch => {
2119
+ toggleSwitch.addEventListener('click', () => {
2120
+ toggleSwitch.classList.toggle('cv-toggle-active');
2056
2121
  const state = this.getCurrentCustomStateFromModal();
2057
2122
  this.core.applyState(state);
2058
2123
  });
@@ -2101,14 +2166,14 @@ class CustomViewsWidget {
2101
2166
  */
2102
2167
  getCurrentCustomStateFromModal() {
2103
2168
  if (!this.modal) {
2104
- return { toggles: [] };
2169
+ return {};
2105
2170
  }
2106
2171
  // Collect toggle values
2107
2172
  const toggles = [];
2108
- const toggleCheckboxes = this.modal.querySelectorAll('.cv-custom-toggle-checkbox');
2109
- toggleCheckboxes.forEach(checkbox => {
2110
- const toggle = checkbox.dataset.toggle;
2111
- if (toggle && checkbox.checked) {
2173
+ const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
2174
+ toggleSwitches.forEach(toggleSwitch => {
2175
+ const toggle = toggleSwitch.dataset.toggle;
2176
+ if (toggle && toggleSwitch.classList.contains('cv-toggle-active')) {
2112
2177
  toggles.push(toggle);
2113
2178
  }
2114
2179
  });
@@ -2121,7 +2186,11 @@ class CustomViewsWidget {
2121
2186
  tabs[groupId] = select.value;
2122
2187
  }
2123
2188
  });
2124
- return Object.keys(tabs).length > 0 ? { toggles, tabs } : { toggles };
2189
+ const result = { toggles };
2190
+ if (Object.keys(tabs).length > 0) {
2191
+ result.tabs = tabs;
2192
+ }
2193
+ return result;
2125
2194
  }
2126
2195
  /**
2127
2196
  * Copy shareable URL to clipboard
@@ -2141,20 +2210,16 @@ class CustomViewsWidget {
2141
2210
  return;
2142
2211
  // Get currently active toggles (from custom state or default configuration)
2143
2212
  const activeToggles = this.core.getCurrentActiveToggles();
2144
- // First, uncheck all checkboxes
2145
- const allCheckboxes = this.modal.querySelectorAll('.cv-custom-toggle-checkbox');
2146
- allCheckboxes.forEach(checkbox => {
2147
- checkbox.checked = false;
2148
- checkbox.disabled = false;
2149
- checkbox.parentElement?.removeAttribute('aria-hidden');
2213
+ // First, deactivate all toggle switches
2214
+ const allToggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
2215
+ allToggleSwitches.forEach(toggleSwitch => {
2216
+ toggleSwitch.classList.remove('cv-toggle-active');
2150
2217
  });
2151
- // Then check the ones that should be active
2218
+ // Then activate the ones that should be active
2152
2219
  activeToggles.forEach(toggle => {
2153
- const checkbox = this.modal?.querySelector(`[data-toggle="${toggle}"]`);
2154
- if (checkbox) {
2155
- if (!checkbox.disabled) {
2156
- checkbox.checked = true;
2157
- }
2220
+ const toggleSwitch = this.modal?.querySelector(`[data-toggle="${toggle}"]`);
2221
+ if (toggleSwitch) {
2222
+ toggleSwitch.classList.add('cv-toggle-active');
2158
2223
  }
2159
2224
  });
2160
2225
  // Load tab group selections
@@ -2207,7 +2272,7 @@ class CustomViewsWidget {
2207
2272
  </div>
2208
2273
  <div class="cv-widget-modal-content">
2209
2274
  <div class="cv-welcome-content">
2210
- <p>${this.options.welcomeMessage}</p>
2275
+ <p style="text-align: justify;">${this.options.welcomeMessage}</p>
2211
2276
 
2212
2277
  <div class="cv-welcome-widget-preview">
2213
2278
  <div class="cv-welcome-widget-icon">⚙</div>
@@ -2328,13 +2393,8 @@ function initializeFromScript() {
2328
2393
  console.warn(`[CustomViews] Config file not found at ${fullConfigPath}. Using defaults.`);
2329
2394
  // Provide minimal default config structure
2330
2395
  configFile = {
2331
- config: {
2332
- allToggles: [],
2333
- defaultState: { toggles: [] }
2334
- },
2335
- widget: {
2336
- enabled: true
2337
- }
2396
+ config: { allToggles: [], defaultState: {} },
2397
+ widget: { enabled: true }
2338
2398
  };
2339
2399
  }
2340
2400
  else {