Import all nix files in a directory tree. Discussions: https://oeiuwq.zulipchat.com/join/nqp26cd4kngon6mo3ncgnuap/ dendrix.oeiuwq.com/Dendritic.html
dendritic inputs
at main 173 lines 4.8 kB view raw
1--- 2import TabListItem from './TabListItem.astro'; 3 4export interface Props { 5 /** 6 * List of content for the tab list. 7 * 8 * To use more complex mark-up for the tab list, pass `<TabListItem>`s 9 * inside a `<Fragment slot="tab-list">`. 10 */ 11 tabs?: { label: string; id: string; initial?: boolean }[]; 12 /** Enable default styles for the tab list and panels. */ 13 styled?: boolean; 14 /** Additional class names to apply to `.tab-list` and `.panels`. */ 15 class?: string; 16} 17 18const { tabs, styled } = Astro.props as Props; 19--- 20 21<tabbed-content> 22 <ul class:list={['tab-list', Astro.props.class, { 'tab-list--styled': styled }]}> 23 <slot name="tab-list"> 24 { 25 tabs?.map((tab) => ( 26 <TabListItem id={tab.id} initial={tab.initial}> 27 {tab.label} 28 </TabListItem> 29 )) 30 } 31 </slot> 32 </ul> 33 34 <div class:list={['panels', Astro.props.class, { 'panels--styled': styled }]}> 35 <slot /> 36 </div> 37</tabbed-content> 38 39<style> 40 .tab-list { 41 list-style: none; 42 padding: 0; 43 } 44 .tab-list--styled { 45 display: flex; 46 margin-top: -1px; 47 overflow-x: auto; 48 overflow-y: hidden; 49 } 50 @media (min-width: 72em) { 51 .tab-list--styled { 52 justify-content: space-between; 53 margin-top: 0; 54 padding: 1px; 55 } 56 } 57 58 .panels--styled { 59 padding-left: 1px; 60 padding-right: 1px; 61 } 62</style> 63 64<script> 65 class Tabs extends HTMLElement { 66 readonly id = Math.floor(Math.random() * 10e10).toString(32); 67 count = 0; 68 TabStore: Set<HTMLElement>[] = []; 69 PanelStore: Set<HTMLElement>[] = []; 70 71 constructor() { 72 super(); 73 74 // Get relevant elements and collections 75 const panels = this.querySelectorAll<HTMLElement>('.panels > [id]'); 76 const tablist = this.querySelector('.tab-list')!; 77 const tabs = tablist.querySelectorAll('a'); 78 79 // Add the tablist role to the first <ul> in the .tabbed container 80 tablist.setAttribute('role', 'tablist'); 81 82 let initialTab = 0; 83 84 // Add semantics are remove user focusability for each tab 85 Array.prototype.forEach.call(tabs, (tab: HTMLElement, i: number) => { 86 tab.setAttribute('role', 'tab'); 87 tab.setAttribute('id', this.id + 'tab' + this.count++); 88 tab.setAttribute('tabindex', '-1'); 89 tab.parentElement?.setAttribute('role', 'presentation'); 90 if (!this.TabStore[i]) this.TabStore.push(new Set()); 91 this.TabStore[i].add(tab); 92 if ('initial' in tab.dataset && tab.dataset.initial !== 'false') initialTab = i; 93 94 // Handle clicking of tabs for mouse users 95 const onClick = (e: MouseEvent) => { 96 e.preventDefault(); 97 const currentTab = tablist.querySelector('[aria-selected]'); 98 if (e.currentTarget !== currentTab) { 99 this.switchTab(e.currentTarget as HTMLElement, i); 100 } 101 }; 102 tab.addEventListener('click', onClick); 103 tab.addEventListener('auxclick', onClick); 104 105 // Handle keydown events for keyboard users 106 tab.addEventListener('keydown', (e) => { 107 // Get the index of the current tab in the tabs node list 108 const index: number = Array.prototype.indexOf.call(tabs, e.currentTarget); 109 // Work out which key the user is pressing and 110 // Calculate the new tab's index where appropriate 111 const dir = 112 e.key === 'ArrowLeft' 113 ? index - 1 114 : e.key === 'ArrowRight' 115 ? index + 1 116 : e.key === 'Home' 117 ? 0 118 : e.key === 'End' 119 ? tabs.length - 1 120 : null; 121 if (dir !== null) { 122 e.preventDefault(); 123 if (tabs[dir]) this.switchTab(tabs[dir], dir); 124 } 125 }); 126 }); 127 128 // Add tab panel semantics and hide them all 129 Array.prototype.forEach.call(panels, (panel: HTMLElement, i: number) => { 130 panel.setAttribute('role', 'tabpanel'); 131 panel.setAttribute('tabindex', '-1'); 132 panel.setAttribute('aria-labelledby', tabs[i].id); 133 panel.hidden = true; 134 if (!this.PanelStore[i]) this.PanelStore.push(new Set()); 135 this.PanelStore[i].add(panel); 136 }); 137 138 // Activate and reveal the initial tab panel 139 tabs[initialTab].removeAttribute('tabindex'); 140 tabs[initialTab].setAttribute('aria-selected', 'true'); 141 panels[initialTab].hidden = false; 142 } 143 144 // The tab switching function 145 switchTab(newTab: HTMLElement, index: number) { 146 this.TabStore.forEach((store) => 147 store.forEach((oldTab) => { 148 oldTab.removeAttribute('aria-selected'); 149 oldTab.setAttribute('tabindex', '-1'); 150 }) 151 ); 152 this.TabStore[index].forEach((newTab) => { 153 // Make the active tab focusable by the user (Tab key) 154 newTab.removeAttribute('tabindex'); 155 // Set the selected state 156 newTab.setAttribute('aria-selected', 'true'); 157 }); 158 159 this.PanelStore.forEach((store) => 160 store.forEach((oldPanel) => { 161 oldPanel.hidden = true; 162 }) 163 ); 164 this.PanelStore[index].forEach((newPanel) => { 165 newPanel.hidden = false; 166 }); 167 168 newTab.focus(); 169 } 170 } 171 172 customElements.define('tabbed-content', Tabs); 173</script>