@phantompixeldev/retrocss 2.8.0 → 3.0.0

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/retro.js CHANGED
@@ -1,28 +1,4 @@
1
- var RetroCSS = (() => {
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/js/retro.js
21
- var retro_exports = {};
22
- __export(retro_exports, {
23
- default: () => retro_default
24
- });
25
-
1
+ (() => {
26
2
  // src/js/components/modal.js
27
3
  var FOCUSABLE = [
28
4
  "a[href]",
@@ -130,7 +106,6 @@ var RetroCSS = (() => {
130
106
  }
131
107
  this._trap(e, openModal);
132
108
  });
133
- console.log("Modal component initialized");
134
109
  },
135
110
  show(modalId) {
136
111
  const modal = document.getElementById(modalId);
@@ -155,7 +130,6 @@ var RetroCSS = (() => {
155
130
  this._isolate(modal);
156
131
  const target = modal.querySelector("[autofocus]") || this._focusable(modal)[0] || modal;
157
132
  target.focus();
158
- console.log(`Modal ${modalId} opened`);
159
133
  },
160
134
  hide(modalId) {
161
135
  const modal = document.getElementById(modalId);
@@ -168,35 +142,74 @@ var RetroCSS = (() => {
168
142
  this._lastFocused.focus();
169
143
  }
170
144
  this._lastFocused = null;
171
- console.log(`Modal ${modalId} closed`);
172
145
  }
173
146
  };
174
147
  var modal_default = RetroModal;
175
148
 
176
149
  // src/js/components/toast.js
177
150
  var RetroToast = {
178
- show(message, options = {}) {
179
- let container = document.getElementById("retro-toast-container");
180
- if (!container) {
181
- container = document.createElement("div");
182
- container.id = "retro-toast-container";
183
- document.body.appendChild(container);
151
+ /**
152
+ * The container is a live region. Without this, toasts were shown silently:
153
+ * a screen reader had no way to know anything had appeared, which made the
154
+ * theme toggle and every `data-retro-toast` trigger inaudible.
155
+ */
156
+ container() {
157
+ let el = document.getElementById("retro-toast-container");
158
+ if (!el) {
159
+ el = document.createElement("div");
160
+ el.id = "retro-toast-container";
161
+ document.body.appendChild(el);
184
162
  }
163
+ el.setAttribute("role", "status");
164
+ el.setAttribute("aria-live", "polite");
165
+ el.setAttribute("aria-atomic", "false");
166
+ return el;
167
+ },
168
+ show(message, options = {}) {
169
+ const container = this.container();
185
170
  const toast = document.createElement("div");
186
171
  toast.className = "retro-toast";
187
- if (options.type) {
188
- toast.classList.add(`retro-toast-${options.type}`);
172
+ if (options.type) toast.classList.add(`retro-toast-${options.type}`);
173
+ if (options.type === "danger") {
174
+ toast.setAttribute("role", "alert");
175
+ toast.setAttribute("aria-live", "assertive");
189
176
  }
177
+ const body = document.createElement("div");
178
+ body.className = "retro-toast-body";
190
179
  if (options.html) {
191
180
  toast.classList.add("retro-toast-html");
192
- toast.innerHTML = message;
181
+ body.innerHTML = message;
193
182
  } else {
194
- toast.textContent = message;
183
+ body.textContent = message;
195
184
  }
185
+ toast.appendChild(body);
186
+ const close = document.createElement("button");
187
+ close.type = "button";
188
+ close.className = "retro-toast-close";
189
+ close.setAttribute("aria-label", "Dismiss notification");
190
+ close.textContent = "\xD7";
191
+ close.addEventListener("click", () => dismiss());
192
+ toast.appendChild(close);
196
193
  container.appendChild(toast);
197
- setTimeout(() => {
194
+ const duration = options.duration === void 0 ? 3e3 : options.duration;
195
+ let timer = null;
196
+ let dismissed = false;
197
+ function dismiss() {
198
+ if (dismissed) return;
199
+ dismissed = true;
200
+ clearTimeout(timer);
198
201
  toast.remove();
199
- }, options.duration || 3e3);
202
+ }
203
+ const start = () => {
204
+ if (duration > 0) timer = setTimeout(dismiss, duration);
205
+ };
206
+ const pause = () => clearTimeout(timer);
207
+ toast.addEventListener("mouseenter", pause);
208
+ toast.addEventListener("mouseleave", start);
209
+ toast.addEventListener("focusin", pause);
210
+ toast.addEventListener("focusout", start);
211
+ start();
212
+ return { dismiss, element: toast };
200
213
  }
201
214
  };
202
215
  var toast_default = RetroToast;
@@ -249,32 +262,76 @@ var RetroCSS = (() => {
249
262
  var form_default = RetroForm;
250
263
 
251
264
  // src/js/components/table.js
265
+ var cellValue = (row, index) => {
266
+ const cell = row.children[index];
267
+ if (!cell) return "";
268
+ return (cell.dataset.sortValue ?? cell.textContent).trim();
269
+ };
270
+ var asNumber = (s) => {
271
+ const m = String(s).trim().replace(/,/g, "").match(/^[$€£¥\s]*([+-]?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)\s*[^\d]*$/);
272
+ return m ? parseFloat(m[1]) : null;
273
+ };
252
274
  var RetroTable = {
253
275
  init(root = document) {
254
276
  root.querySelectorAll(".retro-table-sortable").forEach((table) => {
255
- table.addEventListener("click", function(e) {
256
- const th = e.target.closest("th");
257
- if (!th || !table.contains(th)) return;
258
- const ths = table.querySelectorAll("th");
259
- const idx = Array.from(ths).indexOf(th);
260
- if (idx === -1) return;
277
+ if (table.dataset.retroSortBound === "true") return;
278
+ table.dataset.retroSortBound = "true";
279
+ const headers = Array.from(table.querySelectorAll("thead th"));
280
+ if (!headers.length) return;
281
+ const sortable = headers.filter((th) => th.dataset.sort !== "none");
282
+ sortable.forEach((th) => {
283
+ if (!th.hasAttribute("tabindex")) th.tabIndex = 0;
284
+ if (!th.hasAttribute("aria-sort")) th.setAttribute("aria-sort", "none");
285
+ th.classList.add("retro-th-sortable");
286
+ });
287
+ const sortBy = (th) => {
288
+ const index = headers.indexOf(th);
261
289
  const tbody = table.querySelector("tbody");
262
- const rows = Array.from(tbody.querySelectorAll("tr"));
263
- const isAsc = th.classList.toggle("asc");
264
- th.classList.toggle("desc", !isAsc);
265
- ths.forEach((oth) => {
266
- if (oth !== th) oth.classList.remove("asc", "desc");
290
+ if (index === -1 || !tbody) return;
291
+ const rows = Array.from(tbody.rows);
292
+ if (rows.length < 2) return;
293
+ const ascending = th.getAttribute("aria-sort") !== "ascending";
294
+ headers.forEach((other) => {
295
+ if (other === th) return;
296
+ other.setAttribute("aria-sort", "none");
297
+ other.classList.remove("asc", "desc");
267
298
  });
299
+ th.setAttribute("aria-sort", ascending ? "ascending" : "descending");
300
+ th.classList.toggle("asc", ascending);
301
+ th.classList.toggle("desc", !ascending);
302
+ let numeric = th.dataset.sort === "number";
303
+ if (!th.dataset.sort) {
304
+ const sample = rows.map((r) => cellValue(r, index)).find((v) => v !== "");
305
+ numeric = sample !== void 0 && asNumber(sample) !== null;
306
+ }
307
+ const dir = ascending ? 1 : -1;
268
308
  rows.sort((a, b) => {
269
- let t1 = a.children[idx].textContent.trim();
270
- let t2 = b.children[idx].textContent.trim();
271
- let n1 = parseFloat(t1), n2 = parseFloat(t2);
272
- if (!isNaN(n1) && !isNaN(n2)) {
273
- return isAsc ? n1 - n2 : n2 - n1;
309
+ const x = cellValue(a, index);
310
+ const y = cellValue(b, index);
311
+ if (numeric) {
312
+ const nx = asNumber(x);
313
+ const ny = asNumber(y);
314
+ if (nx === null && ny === null) return 0;
315
+ if (nx === null) return 1;
316
+ if (ny === null) return -1;
317
+ return (nx - ny) * dir;
274
318
  }
275
- return isAsc ? t1.localeCompare(t2) : t2.localeCompare(t1);
319
+ return x.localeCompare(y, void 0, { numeric: true }) * dir;
276
320
  });
277
- rows.forEach((row) => tbody.appendChild(row));
321
+ const frag = document.createDocumentFragment();
322
+ rows.forEach((row) => frag.appendChild(row));
323
+ tbody.appendChild(frag);
324
+ };
325
+ table.addEventListener("click", (e) => {
326
+ const th = e.target.closest("thead th");
327
+ if (th && sortable.includes(th)) sortBy(th);
328
+ });
329
+ table.addEventListener("keydown", (e) => {
330
+ if (e.key !== "Enter" && e.key !== " ") return;
331
+ const th = e.target.closest("thead th");
332
+ if (!th || !sortable.includes(th)) return;
333
+ e.preventDefault();
334
+ sortBy(th);
278
335
  });
279
336
  });
280
337
  }
@@ -479,7 +536,6 @@ var RetroCSS = (() => {
479
536
  });
480
537
  }
481
538
  });
482
- console.log("File uploads initialized:", root.querySelectorAll(".retro-file-upload").length);
483
539
  }
484
540
  };
485
541
  var file_upload_default = RetroFileUpload;
@@ -593,7 +649,6 @@ var RetroCSS = (() => {
593
649
  startX = null;
594
650
  moveX = null;
595
651
  });
596
- console.log("Carousel initialized:", this.slides.length, "slides");
597
652
  };
598
653
  RetroCarousel.prototype.goTo = function(idx) {
599
654
  if (idx < 0) idx = this.slides.length - 1;
@@ -617,17 +672,29 @@ var RetroCSS = (() => {
617
672
 
618
673
  // src/js/tabs.js
619
674
  var RetroTabs = class {
620
- constructor(selector, options = {}) {
621
- this.tabContainer = document.querySelector(selector);
675
+ constructor(target, options = {}) {
676
+ this.tabContainer = typeof target === "string" ? document.querySelector(target) : target;
622
677
  if (!this.tabContainer) return;
623
678
  this.options = {
624
679
  contentSelector: options.contentSelector || ".retro-tab-content",
625
680
  activeClass: options.activeClass || "active",
626
- defaultTab: options.defaultTab || 0,
627
- ...options
681
+ ...options,
682
+ // findIndex returns -1 when no tab carries .active, and `-1 || 0` is -1
683
+ // because -1 is truthy -- which then indexes this.tabs[-1].
684
+ defaultTab: Math.max(0, options.defaultTab ?? 0)
628
685
  };
629
686
  this.tabs = Array.from(this.tabContainer.querySelectorAll(".retro-nav-item"));
630
- this.contentElements = this.options.contentContainer ? Array.from(document.querySelector(this.options.contentContainer).children) : Array.from(this.tabContainer.nextElementSibling.querySelectorAll(this.options.contentSelector));
687
+ let paneRoot = this.options.contentContainer;
688
+ if (typeof paneRoot === "string") paneRoot = document.querySelector(paneRoot);
689
+ if (!paneRoot) paneRoot = this.tabContainer.nextElementSibling;
690
+ if (!paneRoot) return;
691
+ this.contentElements = Array.from(
692
+ paneRoot.querySelectorAll(this.options.contentSelector)
693
+ );
694
+ if (!this.contentElements.length) {
695
+ this.contentElements = Array.from(paneRoot.children);
696
+ }
697
+ if (!this.tabs.length || !this.contentElements.length) return;
631
698
  this.init();
632
699
  }
633
700
  init() {
@@ -704,11 +771,6 @@ var RetroCSS = (() => {
704
771
  if (i === 0 && tabs[0].classList.contains("active")) {
705
772
  content.appendChild(existingContent);
706
773
  } else {
707
- content.textContent = `Content for ${tab.textContent}`;
708
- content.style.padding = "20px";
709
- content.style.border = "2px solid #000";
710
- content.style.borderTop = "0";
711
- content.style.background = "#fff";
712
774
  }
713
775
  wrapper.appendChild(content);
714
776
  });
@@ -716,7 +778,7 @@ var RetroCSS = (() => {
716
778
  }
717
779
  }
718
780
  new RetroTabs(nav, {
719
- contentContainer: ".retro-tab-pane",
781
+ contentContainer,
720
782
  defaultTab: Array.from(nav.querySelectorAll(".retro-nav-item")).findIndex((tab) => tab.classList.contains("active"))
721
783
  });
722
784
  });
@@ -736,8 +798,6 @@ var RetroCSS = (() => {
736
798
  if (i === 0 && tabs[0].classList.contains("active")) {
737
799
  content.appendChild(existingContent);
738
800
  } else {
739
- content.textContent = `Content for ${tab.textContent}`;
740
- content.style.padding = "20px";
741
801
  content.style.marginTop = "10px";
742
802
  content.style.background = "#f5f5f5";
743
803
  }
@@ -747,7 +807,7 @@ var RetroCSS = (() => {
747
807
  }
748
808
  }
749
809
  new RetroTabs(nav, {
750
- contentContainer: ".retro-tab-pane-underlined",
810
+ contentContainer,
751
811
  defaultTab: Array.from(nav.querySelectorAll(".retro-nav-item")).findIndex((tab) => tab.classList.contains("active"))
752
812
  });
753
813
  });
@@ -767,8 +827,6 @@ var RetroCSS = (() => {
767
827
  if (i === 0 && tabs[0].classList.contains("active") || tab.classList.contains("active")) {
768
828
  content.appendChild(nextEl);
769
829
  } else {
770
- content.textContent = `Content for ${tab.textContent}`;
771
- content.style.padding = "20px";
772
830
  content.style.marginTop = "10px";
773
831
  content.style.background = "#f5f5f5";
774
832
  }
@@ -777,7 +835,7 @@ var RetroCSS = (() => {
777
835
  contentContainer = wrapper;
778
836
  }
779
837
  new RetroTabs(nav, {
780
- contentContainer: ".retro-tab-pane-buttons",
838
+ contentContainer,
781
839
  defaultTab: Array.from(nav.querySelectorAll(".retro-nav-item")).findIndex((tab) => tab.classList.contains("active"))
782
840
  });
783
841
  }
@@ -816,7 +874,6 @@ var RetroCSS = (() => {
816
874
  });
817
875
  });
818
876
  });
819
- console.log("Accordion component initialized");
820
877
  }
821
878
  static init(selector = ".retro-accordion") {
822
879
  new _RetroAccordion(selector);
@@ -842,109 +899,110 @@ var RetroCSS = (() => {
842
899
 
843
900
  // src/js/code-copy.js
844
901
  var RetroCodeCopy = {
845
- init() {
846
- const codeBlocks = document.querySelectorAll(".retro-code");
847
- codeBlocks.forEach((block) => {
848
- if (!block.querySelector(".retro-code-copy")) {
849
- const copyButton = document.createElement("button");
850
- copyButton.className = "retro-code-copy";
851
- copyButton.textContent = "Copy";
852
- block.appendChild(copyButton);
853
- copyButton.addEventListener("click", async () => {
854
- const code = block.querySelector("code").textContent;
855
- try {
856
- await navigator.clipboard.writeText(code);
857
- const originalText = copyButton.textContent;
858
- copyButton.textContent = "Copied!";
859
- copyButton.style.background = "#c0c0c0";
860
- setTimeout(() => {
861
- copyButton.textContent = originalText;
862
- copyButton.style.background = "";
863
- }, 2e3);
864
- } catch (err) {
865
- console.error("Failed to copy code:", err);
866
- copyButton.textContent = "Failed to copy";
867
- copyButton.style.background = "#ffcccc";
868
- setTimeout(() => {
869
- copyButton.textContent = "Copy";
870
- copyButton.style.background = "";
871
- }, 2e3);
902
+ /** How long the "Copied!" / "Failed" state stays up, in ms. */
903
+ feedbackDuration: 2e3,
904
+ init(root = document) {
905
+ root.querySelectorAll(".retro-code").forEach((block) => {
906
+ if (block.querySelector(".retro-code-copy")) return;
907
+ const code = block.querySelector("code");
908
+ if (!code) return;
909
+ const button = document.createElement("button");
910
+ button.type = "button";
911
+ button.className = "retro-code-copy";
912
+ button.textContent = "Copy";
913
+ button.setAttribute("aria-label", "Copy code to clipboard");
914
+ button.setAttribute("aria-live", "polite");
915
+ let timer = null;
916
+ const report = (label, state) => {
917
+ clearTimeout(timer);
918
+ button.textContent = label;
919
+ button.classList.toggle("retro-code-copy-ok", state === "ok");
920
+ button.classList.toggle("retro-code-copy-fail", state === "fail");
921
+ timer = setTimeout(() => {
922
+ button.textContent = "Copy";
923
+ button.classList.remove("retro-code-copy-ok", "retro-code-copy-fail");
924
+ }, this.feedbackDuration);
925
+ };
926
+ button.addEventListener("click", async () => {
927
+ const text = code.textContent;
928
+ try {
929
+ if (navigator.clipboard?.writeText) {
930
+ await navigator.clipboard.writeText(text);
931
+ } else {
932
+ throw new Error("clipboard unavailable");
872
933
  }
873
- });
874
- }
934
+ report("Copied!", "ok");
935
+ } catch {
936
+ const range = document.createRange();
937
+ range.selectNodeContents(code);
938
+ const sel = window.getSelection();
939
+ sel.removeAllRanges();
940
+ sel.addRange(range);
941
+ report("Press Ctrl+C", "fail");
942
+ }
943
+ });
944
+ block.appendChild(button);
875
945
  });
876
946
  }
877
947
  };
878
948
  window.RetroCodeCopy = RetroCodeCopy;
949
+ var code_copy_default = RetroCodeCopy;
879
950
 
880
951
  // src/js/infinite-scroll.js
881
952
  var RetroInfiniteScroll = {
882
- isLoading: false,
883
- page: 1,
884
- itemsPerPage: 5,
885
- maxPages: 5,
886
- // For demo purposes
887
- init() {
888
- const containers = document.querySelectorAll(".retro-infinite-scroll");
889
- if (!containers.length) return;
953
+ /** How close to the bottom, in px, before more content is requested. */
954
+ threshold: 50,
955
+ init(root = document) {
956
+ const containers = root.querySelectorAll(".retro-infinite-scroll");
890
957
  containers.forEach((container) => {
891
- this.appendItems(container, 1);
958
+ if (container.dataset.retroInfiniteBound === "true") return;
959
+ container.dataset.retroInfiniteBound = "true";
960
+ const state = { page: 1, loading: false, finished: false };
892
961
  container.addEventListener("scroll", () => {
893
- if (this.isNearBottom(container) && !this.isLoading && this.page < this.maxPages) {
894
- this.loadMoreItems(container);
895
- }
962
+ if (state.loading || state.finished) return;
963
+ if (!this.isNearBottom(container)) return;
964
+ this.requestMore(container, state);
896
965
  });
897
966
  });
898
967
  },
899
968
  isNearBottom(container) {
900
- return container.scrollHeight - container.scrollTop - container.clientHeight < 50;
969
+ return container.scrollHeight - container.scrollTop - container.clientHeight < this.threshold;
901
970
  },
902
- loadMoreItems(container) {
971
+ requestMore(container, state) {
903
972
  const loader = container.querySelector(".retro-infinite-loader");
904
- this.isLoading = true;
973
+ state.loading = true;
905
974
  if (loader) loader.style.display = "flex";
906
- setTimeout(() => {
907
- this.page++;
908
- this.appendItems(container, this.page);
909
- this.isLoading = false;
975
+ const settle = () => {
976
+ state.loading = false;
910
977
  if (loader) loader.style.display = "none";
911
- if (this.page >= this.maxPages) {
912
- const endMessage = document.createElement("div");
913
- endMessage.className = "retro-infinite-end";
914
- endMessage.textContent = "End of content";
915
- endMessage.style.textAlign = "center";
916
- endMessage.style.padding = "20px";
917
- endMessage.style.color = "var(--retro-border-medium)";
918
- container.appendChild(endMessage);
919
- }
920
- }, 800);
921
- },
922
- appendItems(container, page) {
923
- const loader = container.querySelector(".retro-infinite-loader");
924
- for (let i = 1; i <= this.itemsPerPage; i++) {
925
- const itemIndex = (page - 1) * this.itemsPerPage + i;
926
- const item = document.createElement("div");
927
- item.className = "retro-card retro-mb-3";
928
- const card = `
929
- <div class="retro-card-header">
930
- Item ${itemIndex}
931
- </div>
932
- <div class="retro-card-content">
933
- <p>This is demo content for infinite scroll. Scroll down to load more items.</p>
934
- <div class="retro-progress" style="margin-top: 10px;">
935
- <div class="retro-progress-bar" style="width: ${Math.floor(
936
- Math.random() * 100
937
- )}%;"></div>
938
- </div>
939
- </div>
940
- `;
941
- item.innerHTML = card;
942
- if (loader) {
943
- container.insertBefore(item, loader);
944
- } else {
945
- container.appendChild(item);
978
+ };
979
+ const detail = {
980
+ page: state.page + 1,
981
+ /** Insert a node above the loader. */
982
+ append(node) {
983
+ if (loader) container.insertBefore(node, loader);
984
+ else container.appendChild(node);
985
+ },
986
+ /** Call when a page has been added. */
987
+ loaded() {
988
+ state.page += 1;
989
+ settle();
990
+ },
991
+ /** Call when there is nothing left. */
992
+ done() {
993
+ state.finished = true;
994
+ settle();
995
+ if (container.querySelector(".retro-infinite-end")) return;
996
+ const end = document.createElement("div");
997
+ end.className = "retro-infinite-end";
998
+ end.textContent = "End of content";
999
+ container.appendChild(end);
946
1000
  }
947
- }
1001
+ };
1002
+ const delivered = container.dispatchEvent(
1003
+ new CustomEvent("retro:loadmore", { detail, bubbles: true, cancelable: true })
1004
+ );
1005
+ if (delivered && state.loading) settle();
948
1006
  }
949
1007
  };
950
1008
  window.RetroInfiniteScroll = RetroInfiniteScroll;
@@ -961,192 +1019,15 @@ var RetroCSS = (() => {
961
1019
  });
962
1020
  });
963
1021
 
964
- // src/js/sidebar.js
965
- var RetroSidebar = {
966
- // Configuration
967
- config: {
968
- sidebarSelector: ".retro-sidebar",
969
- toggleSelector: ".retro-sidebar-toggle",
970
- overlaySelector: ".retro-sidebar-overlay",
971
- activeClass: "active",
972
- mobileBreakpoint: 768,
973
- transitionDuration: 300,
974
- // ms
975
- bodyOpenClass: "sidebar-open"
976
- },
977
- // State
978
- isOpen: false,
979
- // Initialize the sidebar
980
- init() {
981
- const sidebar = document.querySelector(this.config.sidebarSelector);
982
- if (!sidebar) return;
983
- sidebar.setAttribute("role", "navigation");
984
- sidebar.setAttribute("aria-label", "Sidebar Navigation");
985
- this.setupLinks(sidebar);
986
- this.setupMobile();
987
- this.setupScrollTracking();
988
- this.setupResizeHandler();
989
- this.setActiveLink();
990
- },
991
- // Setup sidebar links
992
- setupLinks(sidebar) {
993
- const sidebarLinks = sidebar.querySelectorAll("a");
994
- sidebarLinks.forEach((link) => {
995
- link.addEventListener("focus", () => {
996
- link.style.outline = "1px dotted var(--retro-primary)";
997
- });
998
- link.addEventListener("blur", () => {
999
- link.style.outline = "";
1000
- });
1001
- if (link.getAttribute("href")?.startsWith("#")) {
1002
- link.addEventListener("click", (e) => {
1003
- const targetId = link.getAttribute("href");
1004
- const targetElement = document.querySelector(targetId);
1005
- if (targetElement) {
1006
- e.preventDefault();
1007
- targetElement.scrollIntoView({
1008
- behavior: "smooth",
1009
- block: "start"
1010
- });
1011
- history.pushState(null, "", targetId);
1012
- this.setActiveLink(link);
1013
- if (window.innerWidth < this.config.mobileBreakpoint) {
1014
- this.closeSidebar();
1015
- }
1016
- }
1017
- });
1018
- }
1019
- });
1020
- },
1021
- // Setup mobile functionality
1022
- setupMobile() {
1023
- let toggleBtn = document.querySelector(this.config.toggleSelector);
1024
- if (!toggleBtn) {
1025
- toggleBtn = document.createElement("button");
1026
- toggleBtn.className = "retro-sidebar-toggle";
1027
- toggleBtn.innerHTML = '<div class="retro-sidebar-toggle-icon"><span></span></div>';
1028
- toggleBtn.setAttribute("aria-label", "Toggle Sidebar");
1029
- const sidebar = document.querySelector(this.config.sidebarSelector);
1030
- if (sidebar && sidebar.parentNode) {
1031
- sidebar.parentNode.insertBefore(toggleBtn, sidebar);
1032
- }
1033
- }
1034
- let overlay = document.querySelector(this.config.overlaySelector);
1035
- if (!overlay) {
1036
- overlay = document.createElement("div");
1037
- overlay.className = "retro-sidebar-overlay";
1038
- document.body.appendChild(overlay);
1039
- }
1040
- toggleBtn.addEventListener("click", () => this.toggleSidebar());
1041
- overlay.addEventListener("click", () => this.closeSidebar());
1042
- document.addEventListener("keydown", (e) => {
1043
- if (e.key === "Escape" && this.isOpen) {
1044
- this.closeSidebar();
1045
- }
1046
- });
1047
- },
1048
- // Toggle the sidebar open/closed
1049
- toggleSidebar() {
1050
- const sidebar = document.querySelector(this.config.sidebarSelector);
1051
- const overlay = document.querySelector(this.config.overlaySelector);
1052
- if (!sidebar || !overlay) return;
1053
- if (this.isOpen) {
1054
- this.closeSidebar();
1055
- } else {
1056
- sidebar.classList.add(this.config.activeClass);
1057
- overlay.classList.add(this.config.activeClass);
1058
- document.body.classList.add(this.config.bodyOpenClass);
1059
- this.isOpen = true;
1060
- }
1061
- },
1062
- // Close the sidebar
1063
- closeSidebar() {
1064
- const sidebar = document.querySelector(this.config.sidebarSelector);
1065
- const overlay = document.querySelector(this.config.overlaySelector);
1066
- if (!sidebar || !overlay) return;
1067
- sidebar.classList.remove(this.config.activeClass);
1068
- overlay.classList.remove(this.config.activeClass);
1069
- document.body.classList.remove(this.config.bodyOpenClass);
1070
- this.isOpen = false;
1071
- },
1072
- // Setup scroll position tracking
1073
- setupScrollTracking() {
1074
- const sidebar = document.querySelector(this.config.sidebarSelector);
1075
- if (!sidebar) return;
1076
- window.addEventListener("scroll", () => {
1077
- this.updateActiveOnScroll();
1078
- });
1079
- },
1080
- // Update active link based on scroll position
1081
- updateActiveOnScroll() {
1082
- const sections = document.querySelectorAll("section[id], div[id]");
1083
- if (sections.length === 0) return;
1084
- let currentSection = null;
1085
- let maxVisiblePercentage = 0;
1086
- sections.forEach((section) => {
1087
- const rect = section.getBoundingClientRect();
1088
- const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
1089
- if (isVisible) {
1090
- const visibleHeight = Math.min(rect.bottom, window.innerHeight) - Math.max(rect.top, 0);
1091
- const visiblePercentage = visibleHeight / rect.height;
1092
- if (visiblePercentage > maxVisiblePercentage) {
1093
- maxVisiblePercentage = visiblePercentage;
1094
- currentSection = section;
1095
- }
1096
- }
1097
- });
1098
- if (currentSection) {
1099
- const id = currentSection.getAttribute("id");
1100
- const link = document.querySelector(`.retro-sidebar a[href="#${id}"]`);
1101
- if (link) {
1102
- this.setActiveLink(link);
1103
- }
1104
- }
1105
- },
1106
- // Set active link
1107
- setActiveLink(activeLink = null) {
1108
- const sidebar = document.querySelector(this.config.sidebarSelector);
1109
- if (!sidebar) return;
1110
- if (!activeLink) {
1111
- const hash = window.location.hash;
1112
- if (hash) {
1113
- activeLink = sidebar.querySelector(`a[href="${hash}"]`);
1114
- } else {
1115
- const currentPath = window.location.pathname;
1116
- activeLink = sidebar.querySelector(`a[href="${currentPath}"]`) || sidebar.querySelector("a");
1117
- }
1118
- }
1119
- sidebar.querySelectorAll("a").forEach((link) => {
1120
- link.classList.remove(this.config.activeClass);
1121
- });
1122
- if (activeLink) {
1123
- activeLink.classList.add(this.config.activeClass);
1124
- }
1125
- },
1126
- // Setup resize handler
1127
- setupResizeHandler() {
1128
- window.addEventListener("resize", () => {
1129
- if (window.innerWidth >= this.config.mobileBreakpoint && this.isOpen) {
1130
- this.closeSidebar();
1131
- }
1132
- });
1133
- }
1134
- };
1135
- window.RetroSidebar = RetroSidebar;
1136
-
1137
- // src/js/datetime.js
1138
- (function() {
1139
- document.querySelectorAll('input[type="date"], input[type="time"]').forEach(function(input) {
1140
- input.addEventListener("change", function() {
1141
- if (window.RetroCSS && RetroCSS.toast) {
1142
- RetroCSS.toast.show("Selected: " + input.value, { type: "info" });
1143
- }
1144
- });
1145
- });
1146
- })();
1147
-
1148
1022
  // src/js/retro.js
1149
- var RetroCSS2 = {
1023
+ function readStoredTheme() {
1024
+ try {
1025
+ return localStorage.getItem("retro-theme") || "light";
1026
+ } catch (e) {
1027
+ return "light";
1028
+ }
1029
+ }
1030
+ var RetroCSS = {
1150
1031
  modal: modal_default,
1151
1032
  toast: toast_default,
1152
1033
  form: form_default,
@@ -1159,11 +1040,13 @@ var RetroCSS = (() => {
1159
1040
  accordion: window.RetroAccordion,
1160
1041
  tabs: window.RetroTabs,
1161
1042
  infiniteScroll: window.RetroInfiniteScroll,
1162
- // Store theme preference
1163
- theme: localStorage.getItem("retro-theme") || "light",
1043
+ // Store theme preference. Read defensively: in a sandboxed iframe, or with
1044
+ // third-party storage blocked, touching localStorage throws SecurityError —
1045
+ // and this runs during module evaluation, so it would take the whole bundle
1046
+ // down before init() ever ran.
1047
+ theme: readStoredTheme(),
1164
1048
  // Initialize all components
1165
1049
  init() {
1166
- console.log("RetroCSS initializing components...");
1167
1050
  modal_default.init();
1168
1051
  dropdown_default.init();
1169
1052
  form_default.init();
@@ -1171,22 +1054,19 @@ var RetroCSS = (() => {
1171
1054
  file_upload_default.init();
1172
1055
  if (window.RetroCarousel && typeof window.RetroCarousel.init === "function") {
1173
1056
  window.RetroCarousel.init();
1174
- console.log("Carousel initialized");
1175
1057
  } else {
1176
1058
  console.warn("RetroCarousel not available");
1177
1059
  }
1178
- if (window.RetroTabsInit && typeof window.RetroTabsInit.init === "function") {
1179
- window.RetroTabsInit.init();
1180
- console.log("Tabs initialized");
1060
+ if (window.RetroTabs && typeof window.RetroTabs.init === "function") {
1061
+ window.RetroTabs.init();
1181
1062
  }
1182
1063
  if (window.RetroAccordion && typeof window.RetroAccordion.init === "function") {
1183
1064
  window.RetroAccordion.init();
1184
- console.log("Accordion initialized");
1185
1065
  }
1186
1066
  if (window.RetroInfiniteScroll && typeof window.RetroInfiniteScroll.init === "function") {
1187
1067
  window.RetroInfiniteScroll.init();
1188
- console.log("Infinite Scroll initialized");
1189
1068
  }
1069
+ code_copy_default.init();
1190
1070
  this.initTooltips();
1191
1071
  this.initToastTriggers();
1192
1072
  this.initSearchBars();
@@ -1220,7 +1100,6 @@ var RetroCSS = (() => {
1220
1100
  tooltip.classList.remove("show");
1221
1101
  });
1222
1102
  });
1223
- console.log("Tooltips initialized:", tooltipTriggers.length);
1224
1103
  },
1225
1104
  // Initialize toast trigger elements
1226
1105
  initToastTriggers() {
@@ -1236,17 +1115,8 @@ var RetroCSS = (() => {
1236
1115
  duration,
1237
1116
  html
1238
1117
  });
1239
- if (message === "Stacked Toast 1") {
1240
- setTimeout(() => {
1241
- toast_default.show("Stacked Toast 2", { type: "primary" });
1242
- setTimeout(() => {
1243
- toast_default.show("Stacked Toast 3", { type: "success" });
1244
- }, 600);
1245
- }, 600);
1246
- }
1247
1118
  });
1248
1119
  });
1249
- console.log("Toast triggers initialized:", toastTriggers.length);
1250
1120
  },
1251
1121
  // Initialize search bar components
1252
1122
  initSearchBars() {
@@ -1255,55 +1125,35 @@ var RetroCSS = (() => {
1255
1125
  const input = searchBar.querySelector(".retro-search-input");
1256
1126
  const suggestions = searchBar.querySelector(".retro-search-suggestions");
1257
1127
  if (!input) return;
1128
+ const items = suggestions ? Array.from(suggestions.querySelectorAll(".retro-search-suggestion")) : [];
1129
+ const choose = (item) => {
1130
+ input.value = item.textContent.trim();
1131
+ searchBar.classList.remove("active");
1132
+ input.dispatchEvent(new Event("change", { bubbles: true }));
1133
+ input.focus();
1134
+ };
1135
+ items.forEach((item) => item.addEventListener("click", () => choose(item)));
1258
1136
  input.addEventListener("focus", () => {
1259
- if (suggestions) {
1260
- searchBar.classList.add("active");
1261
- }
1262
- });
1263
- document.addEventListener("click", (e) => {
1264
- if (!searchBar.contains(e.target)) {
1265
- searchBar.classList.remove("active");
1266
- }
1137
+ if (suggestions) searchBar.classList.add("active");
1267
1138
  });
1268
- if (suggestions) {
1269
- const suggestionItems = suggestions.querySelectorAll(".retro-search-suggestion");
1270
- suggestionItems.forEach((item) => {
1271
- item.addEventListener("click", () => {
1272
- input.value = item.textContent;
1273
- searchBar.classList.remove("active");
1274
- input.dispatchEvent(new Event("change", { bubbles: true }));
1275
- input.focus();
1276
- });
1277
- });
1278
- }
1279
1139
  input.addEventListener("input", () => {
1280
- if (!suggestions) return;
1281
1140
  const value = input.value.trim().toLowerCase();
1282
- suggestions.innerHTML = "";
1283
- if (value) {
1284
- const demoItems = [
1285
- `${value} - Result 1`,
1286
- `${value} - Result 2`,
1287
- `${value} - Result 3`
1288
- ];
1289
- demoItems.forEach((item) => {
1290
- const suggestion = document.createElement("div");
1291
- suggestion.className = "retro-search-suggestion";
1292
- suggestion.textContent = item;
1293
- suggestion.addEventListener("click", () => {
1294
- input.value = item;
1295
- searchBar.classList.remove("active");
1296
- input.focus();
1297
- });
1298
- suggestions.appendChild(suggestion);
1299
- });
1300
- searchBar.classList.add("active");
1301
- } else {
1302
- searchBar.classList.remove("active");
1303
- }
1141
+ events_default.emit("search", { searchBar, input, value });
1142
+ if (!suggestions) return;
1143
+ let visible = 0;
1144
+ items.forEach((item) => {
1145
+ const match = !value || item.textContent.toLowerCase().includes(value);
1146
+ item.hidden = !match;
1147
+ if (match) visible += 1;
1148
+ });
1149
+ searchBar.classList.toggle("active", visible > 0);
1150
+ });
1151
+ });
1152
+ document.addEventListener("click", (e) => {
1153
+ document.querySelectorAll(".retro-search-bar.active").forEach((bar) => {
1154
+ if (!bar.contains(e.target)) bar.classList.remove("active");
1304
1155
  });
1305
1156
  });
1306
- console.log("Search bars initialized:", searchBars.length);
1307
1157
  },
1308
1158
  // Initialize tag input components
1309
1159
  initTagInputs() {
@@ -1364,7 +1214,6 @@ var RetroCSS = (() => {
1364
1214
  }
1365
1215
  });
1366
1216
  });
1367
- console.log("Tag inputs initialized:", tagInputs.length);
1368
1217
  },
1369
1218
  // Initialize theme toggle button
1370
1219
  initThemeToggle() {
@@ -1379,7 +1228,6 @@ var RetroCSS = (() => {
1379
1228
  });
1380
1229
  });
1381
1230
  });
1382
- console.log("Theme toggles initialized:", themeToggles.length);
1383
1231
  },
1384
1232
  // Apply theme to document
1385
1233
  applyTheme(theme) {
@@ -1420,15 +1268,20 @@ var RetroCSS = (() => {
1420
1268
  });
1421
1269
  });
1422
1270
  });
1423
- console.log("Rating stars initialized:", ratings.length);
1424
1271
  }
1425
1272
  };
1426
- window.RetroCSS = RetroCSS2;
1273
+ window.RetroCSS = RetroCSS;
1274
+ window.RetroModal = modal_default;
1275
+ window.RetroToast = toast_default;
1276
+ window.RetroForm = form_default;
1277
+ window.RetroTable = table_default;
1278
+ window.RetroDropdown = dropdown_default;
1279
+ window.RetroFileUpload = file_upload_default;
1280
+ window.RetroEvents = events_default;
1427
1281
  if (document.readyState === "loading") {
1428
- document.addEventListener("DOMContentLoaded", () => RetroCSS2.init());
1282
+ document.addEventListener("DOMContentLoaded", () => RetroCSS.init());
1429
1283
  } else {
1430
- RetroCSS2.init();
1284
+ RetroCSS.init();
1431
1285
  }
1432
- var retro_default = RetroCSS2;
1433
- return __toCommonJS(retro_exports);
1286
+ var retro_default = RetroCSS;
1434
1287
  })();