master
 1---
 2interface Props {
 3  syncKey?: string;
 4}
 5
 6const syncKey = Astro.props.syncKey;
 7const tabsName = `tabs-${crypto.randomUUID()}`;
 8
 9const html = (await Astro.slots.render('default')).replaceAll(/{{{tabs-name}}}/g, tabsName);
10---
11
12<div
13  class="tabs bg-base-200 tabs-box border-base-content/25 rounded-none not-[.card-body>:only-child]:my-4 not-[.card-body>:only-child]:rounded-xl not-[.card-body>:only-child]:border"
14  {...syncKey ? { 'data-sync-key': syncKey } : {}}
15>
16  <Fragment set:html={html} />
17</div>
18
19<script>
20  interface SyncTabs {
21    [key: string]: number;
22  }
23
24  async function init() {
25    const tabsNeedSync = document.querySelectorAll('.tabs[data-sync-key]');
26    if (tabsNeedSync.length === 0) return;
27    const { listenKeys, map } = await import('nanostores');
28    const syncTabs = map<SyncTabs>();
29    tabsNeedSync.forEach((tab) => {
30      const syncKey = tab.getAttribute('data-sync-key')!;
31      const tabItems: NodeListOf<HTMLInputElement> = tab.querySelectorAll(
32        ':scope > input[type="radio"]'
33      );
34      if (syncKey in syncTabs.get()) {
35        const activeTabIndex = syncTabs.get()[syncKey];
36        if (activeTabIndex === -1) {
37          tabItems.forEach((tab) => tab.removeAttribute('checked'));
38        } else {
39          tabItems[activeTabIndex].setAttribute('checked', 'checked');
40        }
41      } else {
42        const activeTabIndex = Array.from(tabItems).findIndex((tab) => tab.checked);
43        syncTabs.setKey(syncKey, activeTabIndex);
44      }
45      tabItems.forEach((tab, index) => {
46        tab.addEventListener('change', () => {
47          if (tab.checked) {
48            syncTabs.setKey(syncKey, index);
49          }
50        });
51      });
52      listenKeys(syncTabs, [syncKey], (curr) => {
53        const activeTabIndex = curr[syncKey];
54        if (activeTabIndex === -1) {
55          tabItems.forEach((tab) => (tab.checked = false));
56        } else {
57          tabItems[activeTabIndex].checked = true;
58        }
59      });
60    });
61  }
62
63  document.addEventListener('astro:after-swap', init);
64  await init();
65</script>