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