@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
  */
@@ -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
- t: state.toggles
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 tabs from compact format
198
+ // Reconstruct Tabs
195
199
  if (Array.isArray(compact.g)) {
196
200
  state.tabs = {};
197
201
  for (const [groupId, tabId] of compact.g) {
@@ -874,7 +878,7 @@ ${TAB_STYLES}
874
878
  this.rootEl = opt.rootEl || document.body;
875
879
  this.persistenceManager = new PersistenceManager();
876
880
  this.visibilityManager = new VisibilityManager();
877
- this.showUrlEnabled = opt.showUrl ?? true;
881
+ this.showUrlEnabled = opt.showUrl ?? false;
878
882
  this.lastAppliedState = this.cloneState(this.config?.defaultState);
879
883
  }
880
884
  getConfig() {
@@ -1074,9 +1078,9 @@ ${TAB_STYLES}
1074
1078
  });
1075
1079
  }
1076
1080
  cloneState(state) {
1077
- const toggles = state?.toggles ? [...state.toggles] : [];
1078
- const tabs = state?.tabs ? { ...state.tabs } : undefined;
1079
- return tabs ? { toggles, tabs } : { toggles };
1081
+ if (!state)
1082
+ return {};
1083
+ return JSON.parse(JSON.stringify(state));
1080
1084
  }
1081
1085
  getTrackedStateSnapshot() {
1082
1086
  if (this.lastAppliedState) {
@@ -1085,7 +1089,7 @@ ${TAB_STYLES}
1085
1089
  if (this.config) {
1086
1090
  return this.cloneState(this.config.defaultState);
1087
1091
  }
1088
- return { toggles: [] };
1092
+ return {};
1089
1093
  }
1090
1094
  }
1091
1095
 
@@ -1176,8 +1180,14 @@ ${TAB_STYLES}
1176
1180
  const baseURL = opts.baseURL || '';
1177
1181
  if (opts.assetsJsonPath) {
1178
1182
  const assetsPath = prependBaseUrl(opts.assetsJsonPath, baseURL);
1179
- const assetsJson = await (await fetch(assetsPath)).json();
1180
- assetsManager = new AssetsManager(assetsJson, baseURL);
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
+ }
1181
1191
  }
1182
1192
  else {
1183
1193
  assetsManager = new AssetsManager({}, baseURL);
@@ -1190,7 +1200,7 @@ ${TAB_STYLES}
1190
1200
  else {
1191
1201
  console.error("No config provided, using minimal default config");
1192
1202
  // Create a minimal default config
1193
- config = { allToggles: [], defaultState: { toggles: [] } };
1203
+ config = { allToggles: [], defaultState: {} };
1194
1204
  }
1195
1205
  const coreOptions = {
1196
1206
  assetsManager,
@@ -1651,9 +1661,61 @@ ${TAB_STYLES}
1651
1661
  margin: 0;
1652
1662
  }
1653
1663
 
1654
- .cv-custom-toggle-checkbox {
1655
- margin-right: 8px;
1656
- width: auto;
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;
1657
1719
  }
1658
1720
 
1659
1721
  .cv-tab-groups {
@@ -1882,11 +1944,11 @@ ${TAB_STYLES}
1882
1944
  position: options.position || 'middle-left',
1883
1945
  theme: options.theme || 'light',
1884
1946
  showReset: options.showReset ?? true,
1885
- title: options.title || 'Custom Views',
1947
+ title: options.title || 'Customize View',
1886
1948
  description: options.description || 'Toggle different content sections to customize your view. Changes are applied instantly and the URL will be updated for sharing.',
1887
1949
  showWelcome: options.showWelcome ?? false,
1888
- welcomeTitle: options.welcomeTitle || 'This website uses Custom Views',
1889
- 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.',
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>.',
1890
1952
  showTabGroups: options.showTabGroups ?? true
1891
1953
  };
1892
1954
  // No external state manager to initialize
@@ -1969,7 +2031,9 @@ ${TAB_STYLES}
1969
2031
  ? toggles.map(toggle => `
1970
2032
  <div class="cv-custom-state-toggle">
1971
2033
  <label>
1972
- <input type="checkbox" class="cv-custom-toggle-checkbox" data-toggle="${toggle}" />
2034
+ <div class="cv-toggle-switch" data-toggle="${toggle}">
2035
+ <div class="cv-toggle-handle"></div>
2036
+ </div>
1973
2037
  ${this.formatToggleName(toggle)}
1974
2038
  </label>
1975
2039
  </div>
@@ -2000,7 +2064,7 @@ ${TAB_STYLES}
2000
2064
  this.modal.innerHTML = `
2001
2065
  <div class="cv-widget-modal cv-custom-state-modal">
2002
2066
  <div class="cv-widget-modal-header">
2003
- <h3>Customize View</h3>
2067
+ <h3>${this.options.title}</h3>
2004
2068
  <button class="cv-widget-modal-close" aria-label="Close modal">×</button>
2005
2069
  </div>
2006
2070
  <div class="cv-widget-modal-content">
@@ -2055,10 +2119,11 @@ ${TAB_STYLES}
2055
2119
  this.loadCurrentStateIntoForm();
2056
2120
  });
2057
2121
  }
2058
- // Listen to toggle checkboxes
2059
- const toggleCheckboxes = this.modal.querySelectorAll('.cv-custom-toggle-checkbox');
2060
- toggleCheckboxes.forEach(checkbox => {
2061
- checkbox.addEventListener('change', () => {
2122
+ // Listen to toggle switches
2123
+ const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
2124
+ toggleSwitches.forEach(toggleSwitch => {
2125
+ toggleSwitch.addEventListener('click', () => {
2126
+ toggleSwitch.classList.toggle('cv-toggle-active');
2062
2127
  const state = this.getCurrentCustomStateFromModal();
2063
2128
  this.core.applyState(state);
2064
2129
  });
@@ -2107,14 +2172,14 @@ ${TAB_STYLES}
2107
2172
  */
2108
2173
  getCurrentCustomStateFromModal() {
2109
2174
  if (!this.modal) {
2110
- return { toggles: [] };
2175
+ return {};
2111
2176
  }
2112
2177
  // Collect toggle values
2113
2178
  const toggles = [];
2114
- const toggleCheckboxes = this.modal.querySelectorAll('.cv-custom-toggle-checkbox');
2115
- toggleCheckboxes.forEach(checkbox => {
2116
- const toggle = checkbox.dataset.toggle;
2117
- if (toggle && checkbox.checked) {
2179
+ const toggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
2180
+ toggleSwitches.forEach(toggleSwitch => {
2181
+ const toggle = toggleSwitch.dataset.toggle;
2182
+ if (toggle && toggleSwitch.classList.contains('cv-toggle-active')) {
2118
2183
  toggles.push(toggle);
2119
2184
  }
2120
2185
  });
@@ -2127,7 +2192,11 @@ ${TAB_STYLES}
2127
2192
  tabs[groupId] = select.value;
2128
2193
  }
2129
2194
  });
2130
- return Object.keys(tabs).length > 0 ? { toggles, tabs } : { toggles };
2195
+ const result = { toggles };
2196
+ if (Object.keys(tabs).length > 0) {
2197
+ result.tabs = tabs;
2198
+ }
2199
+ return result;
2131
2200
  }
2132
2201
  /**
2133
2202
  * Copy shareable URL to clipboard
@@ -2147,20 +2216,16 @@ ${TAB_STYLES}
2147
2216
  return;
2148
2217
  // Get currently active toggles (from custom state or default configuration)
2149
2218
  const activeToggles = this.core.getCurrentActiveToggles();
2150
- // First, uncheck all checkboxes
2151
- const allCheckboxes = this.modal.querySelectorAll('.cv-custom-toggle-checkbox');
2152
- allCheckboxes.forEach(checkbox => {
2153
- checkbox.checked = false;
2154
- checkbox.disabled = false;
2155
- checkbox.parentElement?.removeAttribute('aria-hidden');
2219
+ // First, deactivate all toggle switches
2220
+ const allToggleSwitches = this.modal.querySelectorAll('.cv-toggle-switch');
2221
+ allToggleSwitches.forEach(toggleSwitch => {
2222
+ toggleSwitch.classList.remove('cv-toggle-active');
2156
2223
  });
2157
- // Then check the ones that should be active
2224
+ // Then activate the ones that should be active
2158
2225
  activeToggles.forEach(toggle => {
2159
- const checkbox = this.modal?.querySelector(`[data-toggle="${toggle}"]`);
2160
- if (checkbox) {
2161
- if (!checkbox.disabled) {
2162
- checkbox.checked = true;
2163
- }
2226
+ const toggleSwitch = this.modal?.querySelector(`[data-toggle="${toggle}"]`);
2227
+ if (toggleSwitch) {
2228
+ toggleSwitch.classList.add('cv-toggle-active');
2164
2229
  }
2165
2230
  });
2166
2231
  // Load tab group selections
@@ -2213,7 +2278,7 @@ ${TAB_STYLES}
2213
2278
  </div>
2214
2279
  <div class="cv-widget-modal-content">
2215
2280
  <div class="cv-welcome-content">
2216
- <p>${this.options.welcomeMessage}</p>
2281
+ <p style="text-align: justify;">${this.options.welcomeMessage}</p>
2217
2282
 
2218
2283
  <div class="cv-welcome-widget-preview">
2219
2284
  <div class="cv-welcome-widget-icon">⚙</div>
@@ -2334,13 +2399,8 @@ ${TAB_STYLES}
2334
2399
  console.warn(`[CustomViews] Config file not found at ${fullConfigPath}. Using defaults.`);
2335
2400
  // Provide minimal default config structure
2336
2401
  configFile = {
2337
- config: {
2338
- allToggles: [],
2339
- defaultState: { toggles: [] }
2340
- },
2341
- widget: {
2342
- enabled: true
2343
- }
2402
+ config: { allToggles: [], defaultState: {} },
2403
+ widget: { enabled: true }
2344
2404
  };
2345
2405
  }
2346
2406
  else {