@fsegurai/marked-extended-tabs 17.0.0-beta.7 → 17.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/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var e={value:0},t={className:`marked-extended-tabs-container`,persistSelection:!1,animation:`fade`,autoActivate:!0,template:null,customizeToken:null,onBeforeSwitch:null,onAfterSwitch:null,enableKeyboardNavigation:!0,enableFocusManagement:!0},n=`
2
- <div id="{tabsContainerId}" class="{className}" data-animation="{animation}">
2
+ <div id="{tabsContainerId}" class="{className}" data-animation="{animation}" {runtimeAttrs}>
3
3
  {inputsNav}
4
4
  <div class="marked-extended-tabs-nav" role="tablist">{navList}</div>
5
5
  <div class="marked-extended-tabs-content">{content}</div>
@@ -26,234 +26,10 @@ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{val
26
26
  from { transform: translateY(10px); opacity: 0; }
27
27
  to { transform: translateY(0); opacity: 1; }
28
28
  }`),r.push(`</style>`),r.join(`
29
- `)}function i(e,t,n=!0,r=!0,i=!1){return`<script>
30
- /* eslint-disable no-unused-vars */
31
- (function() {
32
- const containerId = '${e}';
33
- const storageKey = '${`marked-extended-tabs-active-${e}`}';
34
- // @ts-ignore - tabIds is used in the script template
35
- const tabIds = ${JSON.stringify(t.map(e=>e.id))};
36
- const enableKeyboardNav = ${n};
37
- const enableFocusManagement = ${r};
38
- const persistSelection = ${i};
39
-
40
- // Function to update aria-selected state
41
- function updateAriaSelected(container, activeInputId) {
42
- const labels = container.querySelectorAll('.marked-extended-tabs-label');
43
- labels.forEach((label) => {
44
- const forAttr = label.getAttribute('for');
45
- const isActive = forAttr === activeInputId;
46
-
47
- // Only update if value changes to minimize DOM operations
48
- if (label.getAttribute('aria-selected') !== String(isActive)) {
49
- label.setAttribute('aria-selected', isActive ? 'true' : 'false');
50
- if (isActive) {
51
- label.classList.add('active');
52
- } else {
53
- label.classList.remove('active');
54
- }
55
- }
56
-
57
- if (enableFocusManagement) {
58
- label.setAttribute('tabindex', isActive ? '0' : '-1');
59
- }
60
- });
61
- }
62
-
63
- // Helper to handle tab changes (events and persistence)
64
- function handleTabChange(container, actualTabId) {
65
- if (persistSelection) {
66
- localStorage.setItem(storageKey, actualTabId);
67
- }
68
-
69
- container.dispatchEvent(new CustomEvent('tab-switched', {
70
- detail: {
71
- tabId: actualTabId,
72
- timestamp: Date.now(),
73
- },
74
- bubbles: true,
75
- cancelable: true,
76
- }));
77
- }
78
-
79
- // Wait for DOM to be interactive or immediately if script is inline
80
- function initializeTabs() {
81
- const container = document.getElementById(containerId);
82
- if (!container) {
83
- console.warn('[marked-extended-tabs] Container #' + containerId + ' not found');
84
- return;
85
- }
86
-
87
- const inputs = container.querySelectorAll('input[type="radio"]');
88
- const labels = container.querySelectorAll('.marked-extended-tabs-label');
89
-
90
- if (inputs.length === 0 || labels.length === 0) {
91
- console.warn('[marked-extended-tabs] No tabs found in container');
92
- return;
93
- }
94
-
95
- // Update aria-selected on initial load
96
- const checkedInput = container.querySelector('input[type="radio"]:checked');
97
- if (checkedInput) {
98
- updateAriaSelected(container, checkedInput.id);
99
- }
100
-
101
- // Restore persisted selection if available
102
- if (persistSelection) {
103
- const savedTabId = localStorage.getItem(storageKey);
104
- if (savedTabId) {
105
- const savedInput = container.querySelector('#input-' + savedTabId);
106
- if (savedInput) {
107
- setTimeout(() => {
108
- savedInput.checked = true;
109
- updateAriaSelected(container, savedInput.id);
110
- savedInput.dispatchEvent(new Event('change', { bubbles: true }));
111
- }, 0);
112
- }
113
- }
114
- }
115
-
116
- // Handle tab switching via label click events (this is what fires when users click tabs)
117
- labels.forEach((label) => {
118
- label.addEventListener('click', (e) => {
119
- const forAttr = label.getAttribute('for');
120
- if (!forAttr) return;
121
-
122
- const input = document.getElementById(forAttr);
123
- if (!input) return;
124
-
125
- // Set the radio input as checked
126
- input.checked = true;
127
-
128
- // Immediately update aria-selected
129
- updateAriaSelected(container, forAttr);
130
-
131
- const actualTabId = forAttr.replace('input-', '');
132
- handleTabChange(container, actualTabId);
133
- });
134
- });
135
-
136
- // Also handle change events for programmatic updates
137
- inputs.forEach((input) => {
138
- input.addEventListener('change', (e) => {
139
- const inputId = e.target.id;
140
- const actualTabId = inputId.replace('input-', '');
141
-
142
- // Update aria-selected
143
- updateAriaSelected(container, inputId);
144
- handleTabChange(container, actualTabId);
145
- });
146
- });
147
-
148
- // Keyboard navigation
149
- if (enableKeyboardNav) {
150
- labels.forEach((label, index) => {
151
- label.addEventListener('keydown', (e) => {
152
- let targetIndex = index;
153
-
154
- if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
155
- e.preventDefault();
156
- targetIndex = (index + 1) % labels.length;
157
- } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
158
- e.preventDefault();
159
- targetIndex = (index - 1 + labels.length) % labels.length;
160
- } else if (e.key === 'Home') {
161
- e.preventDefault();
162
- targetIndex = 0;
163
- } else if (e.key === 'End') {
164
- e.preventDefault();
165
- targetIndex = labels.length - 1;
166
- } else {
167
- return;
168
- }
169
-
170
- const targetLabel = labels[targetIndex];
171
- if (targetLabel) {
172
- const forAttr = targetLabel.getAttribute('for');
173
- if (forAttr) {
174
- const input = document.getElementById(forAttr);
175
- if (input) {
176
- input.checked = true;
177
- updateAriaSelected(container, forAttr);
178
- input.dispatchEvent(new Event('change', { bubbles: true }));
179
-
180
- // Focus the label for better UX
181
- setTimeout(() => {
182
- targetLabel.focus();
183
- }, 0);
184
- }
185
- }
186
- }
187
- });
188
-
189
- // Make labels keyboard accessible
190
- if (enableFocusManagement) {
191
- label.addEventListener('keypress', (e) => {
192
- if (e.key === ' ' || e.key === 'Enter') {
193
- e.preventDefault();
194
- const forAttr = label.getAttribute('for');
195
- if (forAttr) {
196
- const input = document.getElementById(forAttr);
197
- if (input) {
198
- input.checked = true;
199
- updateAriaSelected(container, forAttr);
200
- input.dispatchEvent(new Event('change', { bubbles: true }));
201
- }
202
- }
203
- }
204
- });
205
- }
206
- });
207
- }
208
-
209
- // Enhanced focus management
210
- if (enableFocusManagement) {
211
- // Set initial tab index for first active or first tab
212
- const activeLabel = container.querySelector('.marked-extended-tabs-label[aria-selected="true"]');
213
- if (activeLabel) {
214
- activeLabel.setAttribute('tabindex', '0');
215
- } else if (labels.length > 0) {
216
- labels[0].setAttribute('tabindex', '0');
217
- }
218
-
219
- // Update tabindex when labels receive focus
220
- labels.forEach((label) => {
221
- label.addEventListener('focus', () => {
222
- labels.forEach((l) => l.setAttribute('tabindex', '-1'));
223
- label.setAttribute('tabindex', '0');
224
- });
225
- });
226
- }
227
-
228
- // Watch for radio input changes to update aria-selected
229
- // This is crucial for CSS to work with [aria-selected="true"]
230
- const observer = new MutationObserver((mutations) => {
231
- mutations.forEach((mutation) => {
232
- if (mutation.type === 'attributes' && mutation.attributeName === 'checked') {
233
- const checkedRadio = container.querySelector('input[type="radio"]:checked');
234
- if (checkedRadio) {
235
- updateAriaSelected(container, checkedRadio.id);
236
- }
237
- }
238
- });
239
- });
240
-
241
- // Observe all radio inputs for checked attribute changes
242
- inputs.forEach((input) => {
243
- observer.observe(input, { attributes: true, attributeFilter: ['checked'] });
244
- });
245
- }
246
-
247
- // Initialize when DOM is ready
248
- if (document.readyState === 'loading') {
249
- document.addEventListener('DOMContentLoaded', initializeTabs);
250
- } else {
251
- initializeTabs();
252
- }
253
- })();<\/script>`}var a=/\s*(\w+)="([^"]+)"/g,o={tabBlock:{start:`::::tabs`,end:`::::tabsend`,aliases:[`:tbs`,`:tabs`],endAliases:[`:tbsend`,`:tabsend`]},tabItemBlock:{start:`:::tab`,end:`:::tabend`,aliases:[`:tab`],endAliases:[`:tabend`]}},s={tabItemBlock:[{name:`label`,defaultValue:``},{name:`active`,defaultValue:`false`},{name:`icon`,defaultValue:null}]};function c(e,t){let n=o[t];if(!n)return null;let{start:r,end:i,aliases:a=[],endAliases:s=[]}=n,c=``;if(e.startsWith(r))c=r;else for(let t of a)if(e.startsWith(t)){c=t;break}if(!c)return null;let l=c.length,u=``,d=l,f=l;for(;f<e.length&&e[f]===` `;)f++;if(f<e.length&&e[f]===`{`){let t=e.indexOf(`}`,f);t!==-1&&(u=e.substring(l,t),d=t+1)}let p=1,m=d;for(;m<e.length&&p>0;){let t=e.indexOf(r,m),n=t;for(let t of a){let r=e.indexOf(t,m);r!==-1&&(n===-1||r<n)&&(n=r)}let o=i,c=e.indexOf(i,m);for(let t of s){let n=e.indexOf(t,m);n!==-1&&(c===-1||n<c)&&(c=n,o=t)}if(c===-1)return null;if(n!==-1&&n<c){p++;let i=r;if(n===t)i=r;else for(let t of a)if(e.indexOf(t,m)===n){i=t;break}m=n+i.length;continue}if(--p===0){let t=e.substring(d,c),n=[e.substring(0,c+o.length),u,t];return n.index=0,n.input=e,n}m=c+o.length}return null}var l=(e,t)=>{if(e instanceof RegExp)return e.exec(t);switch(e){case`tabBlock`:case`tabItemBlock`:return c(t,e);default:throw Error(`Unknown element: ${e}`)}},u=(e,t)=>{let n=s[e];if(!n)throw Error(`Unknown element: ${e}`);let r={};n.forEach(e=>{r[e.name]=e.defaultValue}),a.lastIndex=0;let i;for(;(i=a.exec(t))!==null;){let[,e,t]=i;n.some(t=>t.name===e)&&(r[e]=t)}return r};function d(e,t){let{tabsContainerId:a,tabsData:o,className:s,animation:c=`fade`,template:l,enableKeyboardNavigation:u=!0,enableFocusManagement:d=!0,persistSelection:f}=e,p=l||n;if(!o||o.length===0)return`<div class="error-message">No tab content found</div>`;let m=``,h=``,g=``;for(let e of o){let{id:n,tokens:r,props:i}=e,{active:o,icon:s,label:c}=i,l=`input-${n}`,u=`label-${n}`,d=o?`checked`:``,f=o?`true`:`false`,p=s?`<span class="marked-extended-tabs-icon">${s}</span>`:``;m+=`<input type="radio" name="${a}-tabs" id="${l}" class="marked-extended-tabs-input" ${d}>`,h+=`
29
+ `)}var i=`.marked-extended-tabs-container[data-tabs-runtime="true"]`,a=`.marked-extended-tabs-input`,o=`.marked-extended-tabs-label`,s=(e,t)=>e===`true`?!0:e===`false`?!1:t,c=e=>({enableKeyboardNavigation:s(e.dataset.tabsEnableKeyboardNavigation,!0),enableFocusManagement:s(e.dataset.tabsEnableFocusManagement,!0),persistSelection:s(e.dataset.tabsPersistSelection,!1),storageKey:e.dataset.tabsStorageKey}),l=e=>Array.from(e.querySelectorAll(o)),u=e=>Array.from(e.querySelectorAll(a)),d=e=>{let t=l(e),n=u(e),r=e.querySelector(`${a}:checked`);!r&&n.length>0&&(r=n[0],r.checked=!0);let{enableFocusManagement:i}=c(e);return t.forEach(e=>{let t=r?e.htmlFor===r.id:!1;e.setAttribute(`aria-selected`,t?`true`:`false`),e.classList.toggle(`active`,t),i&&e.setAttribute(`tabindex`,t?`0`:`-1`)}),r},f=(e,t)=>{let{persistSelection:n,storageKey:r}=c(e);if(!(!n||!r))try{window.localStorage.setItem(r,t)}catch{}},p=(e,t)=>{e.dispatchEvent(new CustomEvent(`tab-switched`,{detail:{tabId:t,timestamp:Date.now()},bubbles:!0,cancelable:!0}))},m=e=>{let{persistSelection:t,storageKey:n}=c(e);if(!t||!n)return;let r;try{r=window.localStorage.getItem(n)}catch{return}if(!r)return;let i=`input-${r}`,a=e.querySelector(`#${i}`);a&&(a.checked=!0)},h=(e,t)=>{let n=t.htmlFor;if(!n)return;let r=e.querySelector(`#${n}`);if(!r)return;r.checked=!0,r.dispatchEvent(new Event(`change`,{bubbles:!0}));let{enableFocusManagement:i}=c(e);i&&t.focus()},g=e=>{let t=e.target;if(!x(t)||!t.matches(a))return;let n=t.closest(i);if(!n)return;let r=d(n);if(!r)return;let o=r.id.replace(/^input-/,``);f(n,o),p(n,o)},_=e=>{let t=e.target;if(!S(t)||!t.matches(o))return;let n=t.closest(i);if(!n)return;let{enableKeyboardNavigation:r}=c(n);if(!r)return;let a=l(n),s=a.indexOf(t);if(s===-1||a.length===0)return;let u;switch(e.key){case`ArrowRight`:case`ArrowDown`:u=(s+1)%a.length;break;case`ArrowLeft`:case`ArrowUp`:u=(s-1+a.length)%a.length;break;case`Home`:u=0;break;case`End`:u=a.length-1;break;case`Enter`:case` `:u=s;break;default:return}e.preventDefault();let d=a[u];d&&h(n,d)},v=e=>{let t=e.target;if(!S(t)||!t.matches(o))return;let n=t.closest(i);if(!n)return;let{enableFocusManagement:r}=c(n);r&&l(n).forEach(e=>{e.setAttribute(`tabindex`,e===t?`0`:`-1`)})},y=e=>{m(e),d(e),e.dataset.tabsInitialized=`true`},b=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.matches==`function`&&typeof t.querySelectorAll==`function`},x=e=>b(e)&&e.tagName===`INPUT`,S=e=>b(e)&&e.tagName===`LABEL`,C=e=>{b(e)&&e.matches(i)&&y(e),e.querySelectorAll(i).forEach(e=>{y(e)})},w=()=>{document.body&&new MutationObserver(e=>{e.forEach(e=>{e.addedNodes.forEach(e=>{b(e)&&C(e)})})}).observe(document.body,{childList:!0,subtree:!0})},T=()=>{if(!(typeof window>`u`||typeof document>`u`)){if(window.__markedExtendedTabsRuntimeReady__){window.initMarkedExtendedTabs?.();return}if(window.__markedExtendedTabsRuntimeReady__=!0,document.addEventListener(`change`,g),document.addEventListener(`keydown`,_),document.addEventListener(`focusin`,v),window.initMarkedExtendedTabs=()=>{C(document)},document.readyState===`loading`){document.addEventListener(`DOMContentLoaded`,()=>{window.initMarkedExtendedTabs?.(),w()},{once:!0});return}window.initMarkedExtendedTabs(),w()}},E=/\s*(\w+)="([^"]+)"/g,D={tabBlock:{start:`::::tabs`,end:`::::tabsend`,aliases:[`:tbs`,`:tabs`],endAliases:[`:tbsend`,`:tabsend`]},tabItemBlock:{start:`:::tab`,end:`:::tabend`,aliases:[`:tab`],endAliases:[`:tabend`]}},O={tabItemBlock:[{name:`label`,defaultValue:``},{name:`active`,defaultValue:`false`},{name:`icon`,defaultValue:null}]};function k(e,t){let n=D[t];if(!n)return null;let{start:r,end:i,aliases:a=[],endAliases:o=[]}=n,s=``;if(e.startsWith(r))s=r;else for(let t of a)if(e.startsWith(t)){s=t;break}if(!s)return null;let c=s.length,l=``,u=c,d=c;for(;d<e.length&&e[d]===` `;)d++;if(d<e.length&&e[d]===`{`){let t=e.indexOf(`}`,d);t!==-1&&(l=e.substring(c,t),u=t+1)}let f=1,p=u;for(;p<e.length&&f>0;){let t=e.indexOf(r,p),n=t;for(let t of a){let r=e.indexOf(t,p);r!==-1&&(n===-1||r<n)&&(n=r)}let s=i,c=e.indexOf(i,p);for(let t of o){let n=e.indexOf(t,p);n!==-1&&(c===-1||n<c)&&(c=n,s=t)}if(c===-1)return null;if(n!==-1&&n<c){f++;let i=r;if(n===t)i=r;else for(let t of a)if(e.indexOf(t,p)===n){i=t;break}p=n+i.length;continue}if(--f===0){let t=e.substring(u,c),n=[e.substring(0,c+s.length),l,t];return n.index=0,n.input=e,n}p=c+s.length}return null}var A=(e,t)=>{if(e instanceof RegExp)return e.exec(t);switch(e){case`tabBlock`:case`tabItemBlock`:return k(t,e);default:throw Error(`Unknown element: ${e}`)}},j=(e,t)=>{let n=O[e];if(!n)throw Error(`Unknown element: ${e}`);let r={};n.forEach(e=>{r[e.name]=e.defaultValue}),E.lastIndex=0;let i;for(;(i=E.exec(t))!==null;){let[,e,t]=i;n.some(t=>t.name===e)&&(r[e]=t)}return r};function M(e,t){let{tabsContainerId:i,tabsData:a,className:o,animation:s=`fade`,template:c,enableKeyboardNavigation:l=!0,enableFocusManagement:u=!0,persistSelection:d}=e,f=c||n;if(!a||a.length===0)return`<div class="error-message">No tab content found</div>`;let p=``,m=``,h=``;for(let e of a){let{id:n,tokens:r,props:a}=e,{active:o,icon:s,label:c}=a,l=`input-${n}`,u=`label-${n}`,d=o?`checked`:``,f=o?`true`:`false`,g=s?`<span class="marked-extended-tabs-icon">${s}</span>`:``;p+=`<input type="radio" name="${i}-tabs" id="${l}" class="marked-extended-tabs-input" ${d}>`,m+=`
254
30
  <label for="${l}" id="${u}" class="marked-extended-tabs-label" role="tab" aria-selected="${f}" data-tab-id="${n}" tabindex="${o?`0`:`-1`}">
255
- ${p}<span class="tab-label">${c}</span>
256
- </label>`;let _=r&&r.length>0?t.parse(r):``;g+=`
31
+ ${g}<span class="tab-label">${c}</span>
32
+ </label>`;let _=r&&r.length>0?t.parse(r):``;h+=`
257
33
  <div class="marked-extended-tabs-content-pane" id="${n}" role="tabpanel" aria-labelledby="${l}">
258
34
  ${_}
259
- </div>`}let _=r(a,o,c),v=i(a,o,u,d,f);return p.replace(/{tabsContainerId}/g,a).replace(/{className}/g,s).replace(/{animation}/g,c).replace(/{inputsNav}/g,m).replace(/{navList}/g,h).replace(/{content}/g,g).replace(/{stylesBehavior}/g,_+v)}function f(t){let{animation:n,autoActivate:r,className:i,persistSelection:a,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:d,enableFocusManagement:f}=t,p=`tabItemBlock`;return{name:`tabs`,level:`block`,tokenizer(t){let m=l(`tabBlock`,t);if(!m)return;let[h,,g]=m,_=`tabs-container-${++e.value}`,v=[],y=0,b=g;for(;b&&(b=b.trimStart(),b);){let e=l(p,b);if(!e)break;let[t,n,r]=e,i=u(p,n),a=i.label||`Tab ${y+1}`,o=i.active===`true`,s=this.lexer.blockTokens(r.trim());v.push({type:`tab-item`,raw:t,id:`${_}-tab-${y}`,props:{label:a,active:o,icon:i.icon||void 0},tokens:s}),y++,b=b.substring(t.length)}if(v.length!==0)return r&&!v.some(e=>e.props.active)&&v.length>0&&(v[0].props.active=!0),{type:`tabs`,raw:h,tokens:v,meta:{tabsContainerId:_,className:i,persistSelection:a,animation:n,autoActivate:r,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:d,enableFocusManagement:f}}}}}function p(){return{name:`tabs`,renderer(e){let t=e;return d({...t.meta,tabsData:t.tokens},this.parser)}}}function m(e={}){e.animation&&![`fade`,`slide`,`none`].includes(e.animation)&&(console.warn(`[marked-extended-tabs] Invalid animation value: ${e.animation}. Using default 'fade'.`),e.animation=`fade`),e.onBeforeSwitch&&typeof e.onBeforeSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onBeforeSwitch must be a function`),e.onBeforeSwitch=null),e.onAfterSwitch&&typeof e.onAfterSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onAfterSwitch must be a function`),e.onAfterSwitch=null);let n={...t,...e};return{walkTokens(e){e.type===`tabs`&&n.customizeToken&&typeof n.customizeToken==`function`&&n.customizeToken(e)},extensions:[f(n),p()]}}exports.default=m;
35
+ </div>`}let g=r(i,a,s),_=[`data-tabs-runtime="true"`,`data-tabs-enable-keyboard-navigation="${l?`true`:`false`}"`,`data-tabs-enable-focus-management="${u?`true`:`false`}"`,`data-tabs-persist-selection="${d?`true`:`false`}"`,`data-tabs-storage-key="marked-extended-tabs-active-${i}"`].join(` `);return f.replace(/{tabsContainerId}/g,i).replace(/{className}/g,o).replace(/{animation}/g,s).replace(/{runtimeAttrs}/g,_).replace(/{inputsNav}/g,p).replace(/{navList}/g,m).replace(/{content}/g,h).replace(/{stylesBehavior}/g,g)}function N(t){let{animation:n,autoActivate:r,className:i,persistSelection:a,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:l,enableFocusManagement:u}=t,d=`tabItemBlock`;return{name:`tabs`,level:`block`,tokenizer(t){let f=A(`tabBlock`,t);if(!f)return;let[p,,m]=f,h=`tabs-container-${++e.value}`,g=[],_=0,v=m;for(;v&&(v=v.trimStart(),v);){let e=A(d,v);if(!e)break;let[t,n,r]=e,i=j(d,n),a=i.label||`Tab ${_+1}`,o=i.active===`true`,s=this.lexer.blockTokens(r.trim());g.push({type:`tab-item`,raw:t,id:`${h}-tab-${_}`,props:{label:a,active:o,icon:i.icon||void 0},tokens:s}),_++,v=v.substring(t.length)}if(g.length!==0)return r&&!g.some(e=>e.props.active)&&g.length>0&&(g[0].props.active=!0),{type:`tabs`,raw:p,tokens:g,meta:{tabsContainerId:h,className:i,persistSelection:a,animation:n,autoActivate:r,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:l,enableFocusManagement:u}}}}}function P(){return{name:`tabs`,renderer(e){let t=e;return M({...t.meta,tabsData:t.tokens},this.parser)}}}function F(e={}){e.animation&&![`fade`,`slide`,`none`].includes(e.animation)&&(console.warn(`[marked-extended-tabs] Invalid animation value: ${e.animation}. Using default 'fade'.`),e.animation=`fade`),e.onBeforeSwitch&&typeof e.onBeforeSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onBeforeSwitch must be a function`),e.onBeforeSwitch=null),e.onAfterSwitch&&typeof e.onAfterSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onAfterSwitch must be a function`),e.onAfterSwitch=null);let n={...t,...e};return T(),{walkTokens(e){e.type===`tabs`&&n.customizeToken&&typeof n.customizeToken==`function`&&n.customizeToken(e)},extensions:[N(n),P()]}}exports.default=F;
package/dist/index.esm.js CHANGED
@@ -10,7 +10,7 @@ var e = { value: 0 }, t = {
10
10
  onAfterSwitch: null,
11
11
  enableKeyboardNavigation: !0,
12
12
  enableFocusManagement: !0
13
- }, n = "\n <div id=\"{tabsContainerId}\" class=\"{className}\" data-animation=\"{animation}\">\n {inputsNav}\n <div class=\"marked-extended-tabs-nav\" role=\"tablist\">{navList}</div>\n <div class=\"marked-extended-tabs-content\">{content}</div>\n {stylesBehavior}\n </div>\n";
13
+ }, n = "\n <div id=\"{tabsContainerId}\" class=\"{className}\" data-animation=\"{animation}\" {runtimeAttrs}>\n {inputsNav}\n <div class=\"marked-extended-tabs-nav\" role=\"tablist\">{navList}</div>\n <div class=\"marked-extended-tabs-content\">{content}</div>\n {stylesBehavior}\n </div>\n";
14
14
  function r(e, t, n) {
15
15
  let r = [];
16
16
  return r.push("<style>"), r.push(" /* CSS-only tab selection - show selected tab */"), t.forEach((t) => {
@@ -32,236 +32,145 @@ function r(e, t, n) {
32
32
  `);
33
33
  }), n === "fade" && r.push("@keyframes tab-fade-in {\n from { opacity: 0; }\n to { opacity: 1; }\n}"), n === "slide" && r.push("@keyframes tab-slide-in {\n from { transform: translateY(10px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n}"), r.push("</style>"), r.join("\n");
34
34
  }
35
- function i(e, t, n = !0, r = !0, i = !1) {
36
- return `<script>
37
- /* eslint-disable no-unused-vars */
38
- (function() {
39
- const containerId = '${e}';
40
- const storageKey = '${`marked-extended-tabs-active-${e}`}';
41
- // @ts-ignore - tabIds is used in the script template
42
- const tabIds = ${JSON.stringify(t.map((e) => e.id))};
43
- const enableKeyboardNav = ${n};
44
- const enableFocusManagement = ${r};
45
- const persistSelection = ${i};
46
-
47
- // Function to update aria-selected state
48
- function updateAriaSelected(container, activeInputId) {
49
- const labels = container.querySelectorAll('.marked-extended-tabs-label');
50
- labels.forEach((label) => {
51
- const forAttr = label.getAttribute('for');
52
- const isActive = forAttr === activeInputId;
53
-
54
- // Only update if value changes to minimize DOM operations
55
- if (label.getAttribute('aria-selected') !== String(isActive)) {
56
- label.setAttribute('aria-selected', isActive ? 'true' : 'false');
57
- if (isActive) {
58
- label.classList.add('active');
59
- } else {
60
- label.classList.remove('active');
61
- }
62
- }
63
-
64
- if (enableFocusManagement) {
65
- label.setAttribute('tabindex', isActive ? '0' : '-1');
66
- }
67
- });
68
- }
69
-
70
- // Helper to handle tab changes (events and persistence)
71
- function handleTabChange(container, actualTabId) {
72
- if (persistSelection) {
73
- localStorage.setItem(storageKey, actualTabId);
74
- }
75
-
76
- container.dispatchEvent(new CustomEvent('tab-switched', {
77
- detail: {
78
- tabId: actualTabId,
79
- timestamp: Date.now(),
80
- },
81
- bubbles: true,
82
- cancelable: true,
83
- }));
84
- }
85
-
86
- // Wait for DOM to be interactive or immediately if script is inline
87
- function initializeTabs() {
88
- const container = document.getElementById(containerId);
89
- if (!container) {
90
- console.warn('[marked-extended-tabs] Container #' + containerId + ' not found');
91
- return;
92
- }
93
-
94
- const inputs = container.querySelectorAll('input[type="radio"]');
95
- const labels = container.querySelectorAll('.marked-extended-tabs-label');
96
-
97
- if (inputs.length === 0 || labels.length === 0) {
98
- console.warn('[marked-extended-tabs] No tabs found in container');
99
- return;
100
- }
101
-
102
- // Update aria-selected on initial load
103
- const checkedInput = container.querySelector('input[type="radio"]:checked');
104
- if (checkedInput) {
105
- updateAriaSelected(container, checkedInput.id);
106
- }
107
-
108
- // Restore persisted selection if available
109
- if (persistSelection) {
110
- const savedTabId = localStorage.getItem(storageKey);
111
- if (savedTabId) {
112
- const savedInput = container.querySelector('#input-' + savedTabId);
113
- if (savedInput) {
114
- setTimeout(() => {
115
- savedInput.checked = true;
116
- updateAriaSelected(container, savedInput.id);
117
- savedInput.dispatchEvent(new Event('change', { bubbles: true }));
118
- }, 0);
119
- }
120
- }
121
- }
122
-
123
- // Handle tab switching via label click events (this is what fires when users click tabs)
124
- labels.forEach((label) => {
125
- label.addEventListener('click', (e) => {
126
- const forAttr = label.getAttribute('for');
127
- if (!forAttr) return;
128
-
129
- const input = document.getElementById(forAttr);
130
- if (!input) return;
131
-
132
- // Set the radio input as checked
133
- input.checked = true;
134
-
135
- // Immediately update aria-selected
136
- updateAriaSelected(container, forAttr);
137
-
138
- const actualTabId = forAttr.replace('input-', '');
139
- handleTabChange(container, actualTabId);
140
- });
141
- });
142
-
143
- // Also handle change events for programmatic updates
144
- inputs.forEach((input) => {
145
- input.addEventListener('change', (e) => {
146
- const inputId = e.target.id;
147
- const actualTabId = inputId.replace('input-', '');
148
-
149
- // Update aria-selected
150
- updateAriaSelected(container, inputId);
151
- handleTabChange(container, actualTabId);
152
- });
153
- });
154
-
155
- // Keyboard navigation
156
- if (enableKeyboardNav) {
157
- labels.forEach((label, index) => {
158
- label.addEventListener('keydown', (e) => {
159
- let targetIndex = index;
160
-
161
- if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
162
- e.preventDefault();
163
- targetIndex = (index + 1) % labels.length;
164
- } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
165
- e.preventDefault();
166
- targetIndex = (index - 1 + labels.length) % labels.length;
167
- } else if (e.key === 'Home') {
168
- e.preventDefault();
169
- targetIndex = 0;
170
- } else if (e.key === 'End') {
171
- e.preventDefault();
172
- targetIndex = labels.length - 1;
173
- } else {
174
- return;
175
- }
176
-
177
- const targetLabel = labels[targetIndex];
178
- if (targetLabel) {
179
- const forAttr = targetLabel.getAttribute('for');
180
- if (forAttr) {
181
- const input = document.getElementById(forAttr);
182
- if (input) {
183
- input.checked = true;
184
- updateAriaSelected(container, forAttr);
185
- input.dispatchEvent(new Event('change', { bubbles: true }));
186
-
187
- // Focus the label for better UX
188
- setTimeout(() => {
189
- targetLabel.focus();
190
- }, 0);
191
- }
192
- }
193
- }
194
- });
195
-
196
- // Make labels keyboard accessible
197
- if (enableFocusManagement) {
198
- label.addEventListener('keypress', (e) => {
199
- if (e.key === ' ' || e.key === 'Enter') {
200
- e.preventDefault();
201
- const forAttr = label.getAttribute('for');
202
- if (forAttr) {
203
- const input = document.getElementById(forAttr);
204
- if (input) {
205
- input.checked = true;
206
- updateAriaSelected(container, forAttr);
207
- input.dispatchEvent(new Event('change', { bubbles: true }));
208
- }
209
- }
210
- }
211
- });
212
- }
213
- });
214
- }
215
-
216
- // Enhanced focus management
217
- if (enableFocusManagement) {
218
- // Set initial tab index for first active or first tab
219
- const activeLabel = container.querySelector('.marked-extended-tabs-label[aria-selected="true"]');
220
- if (activeLabel) {
221
- activeLabel.setAttribute('tabindex', '0');
222
- } else if (labels.length > 0) {
223
- labels[0].setAttribute('tabindex', '0');
224
- }
225
-
226
- // Update tabindex when labels receive focus
227
- labels.forEach((label) => {
228
- label.addEventListener('focus', () => {
229
- labels.forEach((l) => l.setAttribute('tabindex', '-1'));
230
- label.setAttribute('tabindex', '0');
231
- });
232
- });
233
- }
234
-
235
- // Watch for radio input changes to update aria-selected
236
- // This is crucial for CSS to work with [aria-selected="true"]
237
- const observer = new MutationObserver((mutations) => {
238
- mutations.forEach((mutation) => {
239
- if (mutation.type === 'attributes' && mutation.attributeName === 'checked') {
240
- const checkedRadio = container.querySelector('input[type="radio"]:checked');
241
- if (checkedRadio) {
242
- updateAriaSelected(container, checkedRadio.id);
243
- }
244
- }
245
- });
246
- });
247
-
248
- // Observe all radio inputs for checked attribute changes
249
- inputs.forEach((input) => {
250
- observer.observe(input, { attributes: true, attributeFilter: ['checked'] });
251
- });
252
- }
253
-
254
- // Initialize when DOM is ready
255
- if (document.readyState === 'loading') {
256
- document.addEventListener('DOMContentLoaded', initializeTabs);
257
- } else {
258
- initializeTabs();
259
- }
260
- })();<\/script>`;
261
- }
262
35
  //#endregion
263
- //#region src/utils/props.ts
264
- var a = /\s*(\w+)="([^"]+)"/g, o = {
36
+ //#region src/utils/runtime.ts
37
+ var i = ".marked-extended-tabs-container[data-tabs-runtime=\"true\"]", a = ".marked-extended-tabs-input", o = ".marked-extended-tabs-label", s = (e, t) => e === "true" ? !0 : e === "false" ? !1 : t, c = (e) => ({
38
+ enableKeyboardNavigation: s(e.dataset.tabsEnableKeyboardNavigation, !0),
39
+ enableFocusManagement: s(e.dataset.tabsEnableFocusManagement, !0),
40
+ persistSelection: s(e.dataset.tabsPersistSelection, !1),
41
+ storageKey: e.dataset.tabsStorageKey
42
+ }), l = (e) => Array.from(e.querySelectorAll(o)), u = (e) => Array.from(e.querySelectorAll(a)), d = (e) => {
43
+ let t = l(e), n = u(e), r = e.querySelector(`${a}:checked`);
44
+ !r && n.length > 0 && (r = n[0], r.checked = !0);
45
+ let { enableFocusManagement: i } = c(e);
46
+ return t.forEach((e) => {
47
+ let t = r ? e.htmlFor === r.id : !1;
48
+ e.setAttribute("aria-selected", t ? "true" : "false"), e.classList.toggle("active", t), i && e.setAttribute("tabindex", t ? "0" : "-1");
49
+ }), r;
50
+ }, f = (e, t) => {
51
+ let { persistSelection: n, storageKey: r } = c(e);
52
+ if (!(!n || !r)) try {
53
+ window.localStorage.setItem(r, t);
54
+ } catch {}
55
+ }, p = (e, t) => {
56
+ e.dispatchEvent(new CustomEvent("tab-switched", {
57
+ detail: {
58
+ tabId: t,
59
+ timestamp: Date.now()
60
+ },
61
+ bubbles: !0,
62
+ cancelable: !0
63
+ }));
64
+ }, m = (e) => {
65
+ let { persistSelection: t, storageKey: n } = c(e);
66
+ if (!t || !n) return;
67
+ let r;
68
+ try {
69
+ r = window.localStorage.getItem(n);
70
+ } catch {
71
+ return;
72
+ }
73
+ if (!r) return;
74
+ let i = `input-${r}`, a = e.querySelector(`#${i}`);
75
+ a && (a.checked = !0);
76
+ }, h = (e, t) => {
77
+ let n = t.htmlFor;
78
+ if (!n) return;
79
+ let r = e.querySelector(`#${n}`);
80
+ if (!r) return;
81
+ r.checked = !0, r.dispatchEvent(new Event("change", { bubbles: !0 }));
82
+ let { enableFocusManagement: i } = c(e);
83
+ i && t.focus();
84
+ }, g = (e) => {
85
+ let t = e.target;
86
+ if (!x(t) || !t.matches(a)) return;
87
+ let n = t.closest(i);
88
+ if (!n) return;
89
+ let r = d(n);
90
+ if (!r) return;
91
+ let o = r.id.replace(/^input-/, "");
92
+ f(n, o), p(n, o);
93
+ }, _ = (e) => {
94
+ let t = e.target;
95
+ if (!S(t) || !t.matches(o)) return;
96
+ let n = t.closest(i);
97
+ if (!n) return;
98
+ let { enableKeyboardNavigation: r } = c(n);
99
+ if (!r) return;
100
+ let a = l(n), s = a.indexOf(t);
101
+ if (s === -1 || a.length === 0) return;
102
+ let u;
103
+ switch (e.key) {
104
+ case "ArrowRight":
105
+ case "ArrowDown":
106
+ u = (s + 1) % a.length;
107
+ break;
108
+ case "ArrowLeft":
109
+ case "ArrowUp":
110
+ u = (s - 1 + a.length) % a.length;
111
+ break;
112
+ case "Home":
113
+ u = 0;
114
+ break;
115
+ case "End":
116
+ u = a.length - 1;
117
+ break;
118
+ case "Enter":
119
+ case " ":
120
+ u = s;
121
+ break;
122
+ default: return;
123
+ }
124
+ e.preventDefault();
125
+ let d = a[u];
126
+ d && h(n, d);
127
+ }, v = (e) => {
128
+ let t = e.target;
129
+ if (!S(t) || !t.matches(o)) return;
130
+ let n = t.closest(i);
131
+ if (!n) return;
132
+ let { enableFocusManagement: r } = c(n);
133
+ r && l(n).forEach((e) => {
134
+ e.setAttribute("tabindex", e === t ? "0" : "-1");
135
+ });
136
+ }, y = (e) => {
137
+ m(e), d(e), e.dataset.tabsInitialized = "true";
138
+ }, b = (e) => {
139
+ if (!e || typeof e != "object") return !1;
140
+ let t = e;
141
+ return typeof t.matches == "function" && typeof t.querySelectorAll == "function";
142
+ }, x = (e) => b(e) && e.tagName === "INPUT", S = (e) => b(e) && e.tagName === "LABEL", C = (e) => {
143
+ b(e) && e.matches(i) && y(e), e.querySelectorAll(i).forEach((e) => {
144
+ y(e);
145
+ });
146
+ }, w = () => {
147
+ document.body && new MutationObserver((e) => {
148
+ e.forEach((e) => {
149
+ e.addedNodes.forEach((e) => {
150
+ b(e) && C(e);
151
+ });
152
+ });
153
+ }).observe(document.body, {
154
+ childList: !0,
155
+ subtree: !0
156
+ });
157
+ }, T = () => {
158
+ if (!(typeof window > "u" || typeof document > "u")) {
159
+ if (window.__markedExtendedTabsRuntimeReady__) {
160
+ window.initMarkedExtendedTabs?.();
161
+ return;
162
+ }
163
+ if (window.__markedExtendedTabsRuntimeReady__ = !0, document.addEventListener("change", g), document.addEventListener("keydown", _), document.addEventListener("focusin", v), window.initMarkedExtendedTabs = () => {
164
+ C(document);
165
+ }, document.readyState === "loading") {
166
+ document.addEventListener("DOMContentLoaded", () => {
167
+ window.initMarkedExtendedTabs?.(), w();
168
+ }, { once: !0 });
169
+ return;
170
+ }
171
+ window.initMarkedExtendedTabs(), w();
172
+ }
173
+ }, E = /\s*(\w+)="([^"]+)"/g, D = {
265
174
  tabBlock: {
266
175
  start: "::::tabs",
267
176
  end: "::::tabsend",
@@ -274,7 +183,7 @@ var a = /\s*(\w+)="([^"]+)"/g, o = {
274
183
  aliases: [":tab"],
275
184
  endAliases: [":tabend"]
276
185
  }
277
- }, s = { tabItemBlock: [
186
+ }, O = { tabItemBlock: [
278
187
  {
279
188
  name: "label",
280
189
  defaultValue: ""
@@ -288,74 +197,74 @@ var a = /\s*(\w+)="([^"]+)"/g, o = {
288
197
  defaultValue: null
289
198
  }
290
199
  ] };
291
- function c(e, t) {
292
- let n = o[t];
200
+ function k(e, t) {
201
+ let n = D[t];
293
202
  if (!n) return null;
294
- let { start: r, end: i, aliases: a = [], endAliases: s = [] } = n, c = "";
295
- if (e.startsWith(r)) c = r;
203
+ let { start: r, end: i, aliases: a = [], endAliases: o = [] } = n, s = "";
204
+ if (e.startsWith(r)) s = r;
296
205
  else for (let t of a) if (e.startsWith(t)) {
297
- c = t;
206
+ s = t;
298
207
  break;
299
208
  }
300
- if (!c) return null;
301
- let l = c.length, u = "", d = l, f = l;
302
- for (; f < e.length && e[f] === " ";) f++;
303
- if (f < e.length && e[f] === "{") {
304
- let t = e.indexOf("}", f);
305
- t !== -1 && (u = e.substring(l, t), d = t + 1);
209
+ if (!s) return null;
210
+ let c = s.length, l = "", u = c, d = c;
211
+ for (; d < e.length && e[d] === " ";) d++;
212
+ if (d < e.length && e[d] === "{") {
213
+ let t = e.indexOf("}", d);
214
+ t !== -1 && (l = e.substring(c, t), u = t + 1);
306
215
  }
307
- let p = 1, m = d;
308
- for (; m < e.length && p > 0;) {
309
- let t = e.indexOf(r, m), n = t;
216
+ let f = 1, p = u;
217
+ for (; p < e.length && f > 0;) {
218
+ let t = e.indexOf(r, p), n = t;
310
219
  for (let t of a) {
311
- let r = e.indexOf(t, m);
220
+ let r = e.indexOf(t, p);
312
221
  r !== -1 && (n === -1 || r < n) && (n = r);
313
222
  }
314
- let o = i, c = e.indexOf(i, m);
315
- for (let t of s) {
316
- let n = e.indexOf(t, m);
317
- n !== -1 && (c === -1 || n < c) && (c = n, o = t);
223
+ let s = i, c = e.indexOf(i, p);
224
+ for (let t of o) {
225
+ let n = e.indexOf(t, p);
226
+ n !== -1 && (c === -1 || n < c) && (c = n, s = t);
318
227
  }
319
228
  if (c === -1) return null;
320
229
  if (n !== -1 && n < c) {
321
- p++;
230
+ f++;
322
231
  let i = r;
323
232
  if (n === t) i = r;
324
- else for (let t of a) if (e.indexOf(t, m) === n) {
233
+ else for (let t of a) if (e.indexOf(t, p) === n) {
325
234
  i = t;
326
235
  break;
327
236
  }
328
- m = n + i.length;
237
+ p = n + i.length;
329
238
  continue;
330
239
  }
331
- if (--p === 0) {
332
- let t = e.substring(d, c), n = [
333
- e.substring(0, c + o.length),
334
- u,
240
+ if (--f === 0) {
241
+ let t = e.substring(u, c), n = [
242
+ e.substring(0, c + s.length),
243
+ l,
335
244
  t
336
245
  ];
337
246
  return n.index = 0, n.input = e, n;
338
247
  }
339
- m = c + o.length;
248
+ p = c + s.length;
340
249
  }
341
250
  return null;
342
251
  }
343
- var l = (e, t) => {
252
+ var A = (e, t) => {
344
253
  if (e instanceof RegExp) return e.exec(t);
345
254
  switch (e) {
346
255
  case "tabBlock":
347
- case "tabItemBlock": return c(t, e);
256
+ case "tabItemBlock": return k(t, e);
348
257
  default: throw Error(`Unknown element: ${e}`);
349
258
  }
350
- }, u = (e, t) => {
351
- let n = s[e];
259
+ }, j = (e, t) => {
260
+ let n = O[e];
352
261
  if (!n) throw Error(`Unknown element: ${e}`);
353
262
  let r = {};
354
263
  n.forEach((e) => {
355
264
  r[e.name] = e.defaultValue;
356
- }), a.lastIndex = 0;
265
+ }), E.lastIndex = 0;
357
266
  let i;
358
- for (; (i = a.exec(t)) !== null;) {
267
+ for (; (i = E.exec(t)) !== null;) {
359
268
  let [, e, t] = i;
360
269
  n.some((t) => t.name === e) && (r[e] = t);
361
270
  }
@@ -363,58 +272,64 @@ var l = (e, t) => {
363
272
  };
364
273
  //#endregion
365
274
  //#region src/renderer.ts
366
- function d(e, t) {
367
- let { tabsContainerId: a, tabsData: o, className: s, animation: c = "fade", template: l, enableKeyboardNavigation: u = !0, enableFocusManagement: d = !0, persistSelection: f } = e, p = l || n;
368
- if (!o || o.length === 0) return "<div class=\"error-message\">No tab content found</div>";
369
- let m = "", h = "", g = "";
370
- for (let e of o) {
371
- let { id: n, tokens: r, props: i } = e, { active: o, icon: s, label: c } = i, l = `input-${n}`, u = `label-${n}`, d = o ? "checked" : "", f = o ? "true" : "false", p = s ? `<span class="marked-extended-tabs-icon">${s}</span>` : "";
372
- m += `<input type="radio" name="${a}-tabs" id="${l}" class="marked-extended-tabs-input" ${d}>`, h += `
275
+ function M(e, t) {
276
+ let { tabsContainerId: i, tabsData: a, className: o, animation: s = "fade", template: c, enableKeyboardNavigation: l = !0, enableFocusManagement: u = !0, persistSelection: d } = e, f = c || n;
277
+ if (!a || a.length === 0) return "<div class=\"error-message\">No tab content found</div>";
278
+ let p = "", m = "", h = "";
279
+ for (let e of a) {
280
+ let { id: n, tokens: r, props: a } = e, { active: o, icon: s, label: c } = a, l = `input-${n}`, u = `label-${n}`, d = o ? "checked" : "", f = o ? "true" : "false", g = s ? `<span class="marked-extended-tabs-icon">${s}</span>` : "";
281
+ p += `<input type="radio" name="${i}-tabs" id="${l}" class="marked-extended-tabs-input" ${d}>`, m += `
373
282
  <label for="${l}" id="${u}" class="marked-extended-tabs-label" role="tab" aria-selected="${f}" data-tab-id="${n}" tabindex="${o ? "0" : "-1"}">
374
- ${p}<span class="tab-label">${c}</span>
283
+ ${g}<span class="tab-label">${c}</span>
375
284
  </label>`;
376
285
  let _ = r && r.length > 0 ? t.parse(r) : "";
377
- g += `
286
+ h += `
378
287
  <div class="marked-extended-tabs-content-pane" id="${n}" role="tabpanel" aria-labelledby="${l}">
379
288
  ${_}
380
289
  </div>`;
381
290
  }
382
- let _ = r(a, o, c), v = i(a, o, u, d, f);
383
- return p.replace(/{tabsContainerId}/g, a).replace(/{className}/g, s).replace(/{animation}/g, c).replace(/{inputsNav}/g, m).replace(/{navList}/g, h).replace(/{content}/g, g).replace(/{stylesBehavior}/g, _ + v);
291
+ let g = r(i, a, s), _ = [
292
+ "data-tabs-runtime=\"true\"",
293
+ `data-tabs-enable-keyboard-navigation="${l ? "true" : "false"}"`,
294
+ `data-tabs-enable-focus-management="${u ? "true" : "false"}"`,
295
+ `data-tabs-persist-selection="${d ? "true" : "false"}"`,
296
+ `data-tabs-storage-key="marked-extended-tabs-active-${i}"`
297
+ ].join(" ");
298
+ return f.replace(/{tabsContainerId}/g, i).replace(/{className}/g, o).replace(/{animation}/g, s).replace(/{runtimeAttrs}/g, _).replace(/{inputsNav}/g, p).replace(/{navList}/g, m).replace(/{content}/g, h).replace(/{stylesBehavior}/g, g);
384
299
  }
385
300
  //#endregion
386
301
  //#region src/tokenizer.ts
387
- function f(t) {
388
- let { animation: n, autoActivate: r, className: i, persistSelection: a, template: o, onBeforeSwitch: s, onAfterSwitch: c, enableKeyboardNavigation: d, enableFocusManagement: f } = t, p = "tabItemBlock";
302
+ function N(t) {
303
+ let { animation: n, autoActivate: r, className: i, persistSelection: a, template: o, onBeforeSwitch: s, onAfterSwitch: c, enableKeyboardNavigation: l, enableFocusManagement: u } = t, d = "tabItemBlock";
389
304
  return {
390
305
  name: "tabs",
391
306
  level: "block",
392
307
  tokenizer(t) {
393
- let m = l("tabBlock", t);
394
- if (!m) return;
395
- let [h, , g] = m, _ = `tabs-container-${++e.value}`, v = [], y = 0, b = g;
396
- for (; b && (b = b.trimStart(), b);) {
397
- let e = l(p, b);
308
+ let f = A("tabBlock", t);
309
+ if (!f) return;
310
+ let [p, , m] = f, h = `tabs-container-${++e.value}`, g = [], _ = 0, v = m;
311
+ for (; v && (v = v.trimStart(), v);) {
312
+ let e = A(d, v);
398
313
  if (!e) break;
399
- let [t, n, r] = e, i = u(p, n), a = i.label || `Tab ${y + 1}`, o = i.active === "true", s = this.lexer.blockTokens(r.trim());
400
- v.push({
314
+ let [t, n, r] = e, i = j(d, n), a = i.label || `Tab ${_ + 1}`, o = i.active === "true", s = this.lexer.blockTokens(r.trim());
315
+ g.push({
401
316
  type: "tab-item",
402
317
  raw: t,
403
- id: `${_}-tab-${y}`,
318
+ id: `${h}-tab-${_}`,
404
319
  props: {
405
320
  label: a,
406
321
  active: o,
407
322
  icon: i.icon || void 0
408
323
  },
409
324
  tokens: s
410
- }), y++, b = b.substring(t.length);
325
+ }), _++, v = v.substring(t.length);
411
326
  }
412
- if (v.length !== 0) return r && !v.some((e) => e.props.active) && v.length > 0 && (v[0].props.active = !0), {
327
+ if (g.length !== 0) return r && !g.some((e) => e.props.active) && g.length > 0 && (g[0].props.active = !0), {
413
328
  type: "tabs",
414
- raw: h,
415
- tokens: v,
329
+ raw: p,
330
+ tokens: g,
416
331
  meta: {
417
- tabsContainerId: _,
332
+ tabsContainerId: h,
418
333
  className: i,
419
334
  persistSelection: a,
420
335
  animation: n,
@@ -422,19 +337,19 @@ function f(t) {
422
337
  template: o,
423
338
  onBeforeSwitch: s,
424
339
  onAfterSwitch: c,
425
- enableKeyboardNavigation: d,
426
- enableFocusManagement: f
340
+ enableKeyboardNavigation: l,
341
+ enableFocusManagement: u
427
342
  }
428
343
  };
429
344
  }
430
345
  };
431
346
  }
432
- function p() {
347
+ function P() {
433
348
  return {
434
349
  name: "tabs",
435
350
  renderer(e) {
436
351
  let t = e;
437
- return d({
352
+ return M({
438
353
  ...t.meta,
439
354
  tabsData: t.tokens
440
355
  }, this.parser);
@@ -443,7 +358,7 @@ function p() {
443
358
  }
444
359
  //#endregion
445
360
  //#region src/index.ts
446
- function m(e = {}) {
361
+ function F(e = {}) {
447
362
  e.animation && ![
448
363
  "fade",
449
364
  "slide",
@@ -453,12 +368,12 @@ function m(e = {}) {
453
368
  ...t,
454
369
  ...e
455
370
  };
456
- return {
371
+ return T(), {
457
372
  walkTokens(e) {
458
373
  e.type === "tabs" && n.customizeToken && typeof n.customizeToken == "function" && n.customizeToken(e);
459
374
  },
460
- extensions: [f(n), p()]
375
+ extensions: [N(n), P()]
461
376
  };
462
377
  }
463
378
  //#endregion
464
- export { m as default };
379
+ export { F as default };
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  (function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports):typeof define==`function`&&define.amd?define([`exports`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.markedExtendedTabs={}))})(this,function(e){Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});var t={value:0},n={className:`marked-extended-tabs-container`,persistSelection:!1,animation:`fade`,autoActivate:!0,template:null,customizeToken:null,onBeforeSwitch:null,onAfterSwitch:null,enableKeyboardNavigation:!0,enableFocusManagement:!0},r=`
2
- <div id="{tabsContainerId}" class="{className}" data-animation="{animation}">
2
+ <div id="{tabsContainerId}" class="{className}" data-animation="{animation}" {runtimeAttrs}>
3
3
  {inputsNav}
4
4
  <div class="marked-extended-tabs-nav" role="tablist">{navList}</div>
5
5
  <div class="marked-extended-tabs-content">{content}</div>
@@ -26,234 +26,10 @@
26
26
  from { transform: translateY(10px); opacity: 0; }
27
27
  to { transform: translateY(0); opacity: 1; }
28
28
  }`),r.push(`</style>`),r.join(`
29
- `)}function a(e,t,n=!0,r=!0,i=!1){return`<script>
30
- /* eslint-disable no-unused-vars */
31
- (function() {
32
- const containerId = '${e}';
33
- const storageKey = '${`marked-extended-tabs-active-${e}`}';
34
- // @ts-ignore - tabIds is used in the script template
35
- const tabIds = ${JSON.stringify(t.map(e=>e.id))};
36
- const enableKeyboardNav = ${n};
37
- const enableFocusManagement = ${r};
38
- const persistSelection = ${i};
39
-
40
- // Function to update aria-selected state
41
- function updateAriaSelected(container, activeInputId) {
42
- const labels = container.querySelectorAll('.marked-extended-tabs-label');
43
- labels.forEach((label) => {
44
- const forAttr = label.getAttribute('for');
45
- const isActive = forAttr === activeInputId;
46
-
47
- // Only update if value changes to minimize DOM operations
48
- if (label.getAttribute('aria-selected') !== String(isActive)) {
49
- label.setAttribute('aria-selected', isActive ? 'true' : 'false');
50
- if (isActive) {
51
- label.classList.add('active');
52
- } else {
53
- label.classList.remove('active');
54
- }
55
- }
56
-
57
- if (enableFocusManagement) {
58
- label.setAttribute('tabindex', isActive ? '0' : '-1');
59
- }
60
- });
61
- }
62
-
63
- // Helper to handle tab changes (events and persistence)
64
- function handleTabChange(container, actualTabId) {
65
- if (persistSelection) {
66
- localStorage.setItem(storageKey, actualTabId);
67
- }
68
-
69
- container.dispatchEvent(new CustomEvent('tab-switched', {
70
- detail: {
71
- tabId: actualTabId,
72
- timestamp: Date.now(),
73
- },
74
- bubbles: true,
75
- cancelable: true,
76
- }));
77
- }
78
-
79
- // Wait for DOM to be interactive or immediately if script is inline
80
- function initializeTabs() {
81
- const container = document.getElementById(containerId);
82
- if (!container) {
83
- console.warn('[marked-extended-tabs] Container #' + containerId + ' not found');
84
- return;
85
- }
86
-
87
- const inputs = container.querySelectorAll('input[type="radio"]');
88
- const labels = container.querySelectorAll('.marked-extended-tabs-label');
89
-
90
- if (inputs.length === 0 || labels.length === 0) {
91
- console.warn('[marked-extended-tabs] No tabs found in container');
92
- return;
93
- }
94
-
95
- // Update aria-selected on initial load
96
- const checkedInput = container.querySelector('input[type="radio"]:checked');
97
- if (checkedInput) {
98
- updateAriaSelected(container, checkedInput.id);
99
- }
100
-
101
- // Restore persisted selection if available
102
- if (persistSelection) {
103
- const savedTabId = localStorage.getItem(storageKey);
104
- if (savedTabId) {
105
- const savedInput = container.querySelector('#input-' + savedTabId);
106
- if (savedInput) {
107
- setTimeout(() => {
108
- savedInput.checked = true;
109
- updateAriaSelected(container, savedInput.id);
110
- savedInput.dispatchEvent(new Event('change', { bubbles: true }));
111
- }, 0);
112
- }
113
- }
114
- }
115
-
116
- // Handle tab switching via label click events (this is what fires when users click tabs)
117
- labels.forEach((label) => {
118
- label.addEventListener('click', (e) => {
119
- const forAttr = label.getAttribute('for');
120
- if (!forAttr) return;
121
-
122
- const input = document.getElementById(forAttr);
123
- if (!input) return;
124
-
125
- // Set the radio input as checked
126
- input.checked = true;
127
-
128
- // Immediately update aria-selected
129
- updateAriaSelected(container, forAttr);
130
-
131
- const actualTabId = forAttr.replace('input-', '');
132
- handleTabChange(container, actualTabId);
133
- });
134
- });
135
-
136
- // Also handle change events for programmatic updates
137
- inputs.forEach((input) => {
138
- input.addEventListener('change', (e) => {
139
- const inputId = e.target.id;
140
- const actualTabId = inputId.replace('input-', '');
141
-
142
- // Update aria-selected
143
- updateAriaSelected(container, inputId);
144
- handleTabChange(container, actualTabId);
145
- });
146
- });
147
-
148
- // Keyboard navigation
149
- if (enableKeyboardNav) {
150
- labels.forEach((label, index) => {
151
- label.addEventListener('keydown', (e) => {
152
- let targetIndex = index;
153
-
154
- if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
155
- e.preventDefault();
156
- targetIndex = (index + 1) % labels.length;
157
- } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
158
- e.preventDefault();
159
- targetIndex = (index - 1 + labels.length) % labels.length;
160
- } else if (e.key === 'Home') {
161
- e.preventDefault();
162
- targetIndex = 0;
163
- } else if (e.key === 'End') {
164
- e.preventDefault();
165
- targetIndex = labels.length - 1;
166
- } else {
167
- return;
168
- }
169
-
170
- const targetLabel = labels[targetIndex];
171
- if (targetLabel) {
172
- const forAttr = targetLabel.getAttribute('for');
173
- if (forAttr) {
174
- const input = document.getElementById(forAttr);
175
- if (input) {
176
- input.checked = true;
177
- updateAriaSelected(container, forAttr);
178
- input.dispatchEvent(new Event('change', { bubbles: true }));
179
-
180
- // Focus the label for better UX
181
- setTimeout(() => {
182
- targetLabel.focus();
183
- }, 0);
184
- }
185
- }
186
- }
187
- });
188
-
189
- // Make labels keyboard accessible
190
- if (enableFocusManagement) {
191
- label.addEventListener('keypress', (e) => {
192
- if (e.key === ' ' || e.key === 'Enter') {
193
- e.preventDefault();
194
- const forAttr = label.getAttribute('for');
195
- if (forAttr) {
196
- const input = document.getElementById(forAttr);
197
- if (input) {
198
- input.checked = true;
199
- updateAriaSelected(container, forAttr);
200
- input.dispatchEvent(new Event('change', { bubbles: true }));
201
- }
202
- }
203
- }
204
- });
205
- }
206
- });
207
- }
208
-
209
- // Enhanced focus management
210
- if (enableFocusManagement) {
211
- // Set initial tab index for first active or first tab
212
- const activeLabel = container.querySelector('.marked-extended-tabs-label[aria-selected="true"]');
213
- if (activeLabel) {
214
- activeLabel.setAttribute('tabindex', '0');
215
- } else if (labels.length > 0) {
216
- labels[0].setAttribute('tabindex', '0');
217
- }
218
-
219
- // Update tabindex when labels receive focus
220
- labels.forEach((label) => {
221
- label.addEventListener('focus', () => {
222
- labels.forEach((l) => l.setAttribute('tabindex', '-1'));
223
- label.setAttribute('tabindex', '0');
224
- });
225
- });
226
- }
227
-
228
- // Watch for radio input changes to update aria-selected
229
- // This is crucial for CSS to work with [aria-selected="true"]
230
- const observer = new MutationObserver((mutations) => {
231
- mutations.forEach((mutation) => {
232
- if (mutation.type === 'attributes' && mutation.attributeName === 'checked') {
233
- const checkedRadio = container.querySelector('input[type="radio"]:checked');
234
- if (checkedRadio) {
235
- updateAriaSelected(container, checkedRadio.id);
236
- }
237
- }
238
- });
239
- });
240
-
241
- // Observe all radio inputs for checked attribute changes
242
- inputs.forEach((input) => {
243
- observer.observe(input, { attributes: true, attributeFilter: ['checked'] });
244
- });
245
- }
246
-
247
- // Initialize when DOM is ready
248
- if (document.readyState === 'loading') {
249
- document.addEventListener('DOMContentLoaded', initializeTabs);
250
- } else {
251
- initializeTabs();
252
- }
253
- })();<\/script>`}var o=/\s*(\w+)="([^"]+)"/g,s={tabBlock:{start:`::::tabs`,end:`::::tabsend`,aliases:[`:tbs`,`:tabs`],endAliases:[`:tbsend`,`:tabsend`]},tabItemBlock:{start:`:::tab`,end:`:::tabend`,aliases:[`:tab`],endAliases:[`:tabend`]}},c={tabItemBlock:[{name:`label`,defaultValue:``},{name:`active`,defaultValue:`false`},{name:`icon`,defaultValue:null}]};function l(e,t){let n=s[t];if(!n)return null;let{start:r,end:i,aliases:a=[],endAliases:o=[]}=n,c=``;if(e.startsWith(r))c=r;else for(let t of a)if(e.startsWith(t)){c=t;break}if(!c)return null;let l=c.length,u=``,d=l,f=l;for(;f<e.length&&e[f]===` `;)f++;if(f<e.length&&e[f]===`{`){let t=e.indexOf(`}`,f);t!==-1&&(u=e.substring(l,t),d=t+1)}let p=1,m=d;for(;m<e.length&&p>0;){let t=e.indexOf(r,m),n=t;for(let t of a){let r=e.indexOf(t,m);r!==-1&&(n===-1||r<n)&&(n=r)}let s=i,c=e.indexOf(i,m);for(let t of o){let n=e.indexOf(t,m);n!==-1&&(c===-1||n<c)&&(c=n,s=t)}if(c===-1)return null;if(n!==-1&&n<c){p++;let i=r;if(n===t)i=r;else for(let t of a)if(e.indexOf(t,m)===n){i=t;break}m=n+i.length;continue}if(--p===0){let t=e.substring(d,c),n=[e.substring(0,c+s.length),u,t];return n.index=0,n.input=e,n}m=c+s.length}return null}var u=(e,t)=>{if(e instanceof RegExp)return e.exec(t);switch(e){case`tabBlock`:case`tabItemBlock`:return l(t,e);default:throw Error(`Unknown element: ${e}`)}},d=(e,t)=>{let n=c[e];if(!n)throw Error(`Unknown element: ${e}`);let r={};n.forEach(e=>{r[e.name]=e.defaultValue}),o.lastIndex=0;let i;for(;(i=o.exec(t))!==null;){let[,e,t]=i;n.some(t=>t.name===e)&&(r[e]=t)}return r};function f(e,t){let{tabsContainerId:n,tabsData:o,className:s,animation:c=`fade`,template:l,enableKeyboardNavigation:u=!0,enableFocusManagement:d=!0,persistSelection:f}=e,p=l||r;if(!o||o.length===0)return`<div class="error-message">No tab content found</div>`;let m=``,h=``,g=``;for(let e of o){let{id:r,tokens:i,props:a}=e,{active:o,icon:s,label:c}=a,l=`input-${r}`,u=`label-${r}`,d=o?`checked`:``,f=o?`true`:`false`,p=s?`<span class="marked-extended-tabs-icon">${s}</span>`:``;m+=`<input type="radio" name="${n}-tabs" id="${l}" class="marked-extended-tabs-input" ${d}>`,h+=`
29
+ `)}var a=`.marked-extended-tabs-container[data-tabs-runtime="true"]`,o=`.marked-extended-tabs-input`,s=`.marked-extended-tabs-label`,c=(e,t)=>e===`true`?!0:e===`false`?!1:t,l=e=>({enableKeyboardNavigation:c(e.dataset.tabsEnableKeyboardNavigation,!0),enableFocusManagement:c(e.dataset.tabsEnableFocusManagement,!0),persistSelection:c(e.dataset.tabsPersistSelection,!1),storageKey:e.dataset.tabsStorageKey}),u=e=>Array.from(e.querySelectorAll(s)),d=e=>Array.from(e.querySelectorAll(o)),f=e=>{let t=u(e),n=d(e),r=e.querySelector(`${o}:checked`);!r&&n.length>0&&(r=n[0],r.checked=!0);let{enableFocusManagement:i}=l(e);return t.forEach(e=>{let t=r?e.htmlFor===r.id:!1;e.setAttribute(`aria-selected`,t?`true`:`false`),e.classList.toggle(`active`,t),i&&e.setAttribute(`tabindex`,t?`0`:`-1`)}),r},p=(e,t)=>{let{persistSelection:n,storageKey:r}=l(e);if(!(!n||!r))try{window.localStorage.setItem(r,t)}catch{}},m=(e,t)=>{e.dispatchEvent(new CustomEvent(`tab-switched`,{detail:{tabId:t,timestamp:Date.now()},bubbles:!0,cancelable:!0}))},h=e=>{let{persistSelection:t,storageKey:n}=l(e);if(!t||!n)return;let r;try{r=window.localStorage.getItem(n)}catch{return}if(!r)return;let i=`input-${r}`,a=e.querySelector(`#${i}`);a&&(a.checked=!0)},g=(e,t)=>{let n=t.htmlFor;if(!n)return;let r=e.querySelector(`#${n}`);if(!r)return;r.checked=!0,r.dispatchEvent(new Event(`change`,{bubbles:!0}));let{enableFocusManagement:i}=l(e);i&&t.focus()},_=e=>{let t=e.target;if(!S(t)||!t.matches(o))return;let n=t.closest(a);if(!n)return;let r=f(n);if(!r)return;let i=r.id.replace(/^input-/,``);p(n,i),m(n,i)},v=e=>{let t=e.target;if(!C(t)||!t.matches(s))return;let n=t.closest(a);if(!n)return;let{enableKeyboardNavigation:r}=l(n);if(!r)return;let i=u(n),o=i.indexOf(t);if(o===-1||i.length===0)return;let c;switch(e.key){case`ArrowRight`:case`ArrowDown`:c=(o+1)%i.length;break;case`ArrowLeft`:case`ArrowUp`:c=(o-1+i.length)%i.length;break;case`Home`:c=0;break;case`End`:c=i.length-1;break;case`Enter`:case` `:c=o;break;default:return}e.preventDefault();let d=i[c];d&&g(n,d)},y=e=>{let t=e.target;if(!C(t)||!t.matches(s))return;let n=t.closest(a);if(!n)return;let{enableFocusManagement:r}=l(n);r&&u(n).forEach(e=>{e.setAttribute(`tabindex`,e===t?`0`:`-1`)})},b=e=>{h(e),f(e),e.dataset.tabsInitialized=`true`},x=e=>{if(!e||typeof e!=`object`)return!1;let t=e;return typeof t.matches==`function`&&typeof t.querySelectorAll==`function`},S=e=>x(e)&&e.tagName===`INPUT`,C=e=>x(e)&&e.tagName===`LABEL`,w=e=>{x(e)&&e.matches(a)&&b(e),e.querySelectorAll(a).forEach(e=>{b(e)})},T=()=>{document.body&&new MutationObserver(e=>{e.forEach(e=>{e.addedNodes.forEach(e=>{x(e)&&w(e)})})}).observe(document.body,{childList:!0,subtree:!0})},E=()=>{if(!(typeof window>`u`||typeof document>`u`)){if(window.__markedExtendedTabsRuntimeReady__){window.initMarkedExtendedTabs?.();return}if(window.__markedExtendedTabsRuntimeReady__=!0,document.addEventListener(`change`,_),document.addEventListener(`keydown`,v),document.addEventListener(`focusin`,y),window.initMarkedExtendedTabs=()=>{w(document)},document.readyState===`loading`){document.addEventListener(`DOMContentLoaded`,()=>{window.initMarkedExtendedTabs?.(),T()},{once:!0});return}window.initMarkedExtendedTabs(),T()}},D=/\s*(\w+)="([^"]+)"/g,O={tabBlock:{start:`::::tabs`,end:`::::tabsend`,aliases:[`:tbs`,`:tabs`],endAliases:[`:tbsend`,`:tabsend`]},tabItemBlock:{start:`:::tab`,end:`:::tabend`,aliases:[`:tab`],endAliases:[`:tabend`]}},k={tabItemBlock:[{name:`label`,defaultValue:``},{name:`active`,defaultValue:`false`},{name:`icon`,defaultValue:null}]};function A(e,t){let n=O[t];if(!n)return null;let{start:r,end:i,aliases:a=[],endAliases:o=[]}=n,s=``;if(e.startsWith(r))s=r;else for(let t of a)if(e.startsWith(t)){s=t;break}if(!s)return null;let c=s.length,l=``,u=c,d=c;for(;d<e.length&&e[d]===` `;)d++;if(d<e.length&&e[d]===`{`){let t=e.indexOf(`}`,d);t!==-1&&(l=e.substring(c,t),u=t+1)}let f=1,p=u;for(;p<e.length&&f>0;){let t=e.indexOf(r,p),n=t;for(let t of a){let r=e.indexOf(t,p);r!==-1&&(n===-1||r<n)&&(n=r)}let s=i,c=e.indexOf(i,p);for(let t of o){let n=e.indexOf(t,p);n!==-1&&(c===-1||n<c)&&(c=n,s=t)}if(c===-1)return null;if(n!==-1&&n<c){f++;let i=r;if(n===t)i=r;else for(let t of a)if(e.indexOf(t,p)===n){i=t;break}p=n+i.length;continue}if(--f===0){let t=e.substring(u,c),n=[e.substring(0,c+s.length),l,t];return n.index=0,n.input=e,n}p=c+s.length}return null}var j=(e,t)=>{if(e instanceof RegExp)return e.exec(t);switch(e){case`tabBlock`:case`tabItemBlock`:return A(t,e);default:throw Error(`Unknown element: ${e}`)}},M=(e,t)=>{let n=k[e];if(!n)throw Error(`Unknown element: ${e}`);let r={};n.forEach(e=>{r[e.name]=e.defaultValue}),D.lastIndex=0;let i;for(;(i=D.exec(t))!==null;){let[,e,t]=i;n.some(t=>t.name===e)&&(r[e]=t)}return r};function N(e,t){let{tabsContainerId:n,tabsData:a,className:o,animation:s=`fade`,template:c,enableKeyboardNavigation:l=!0,enableFocusManagement:u=!0,persistSelection:d}=e,f=c||r;if(!a||a.length===0)return`<div class="error-message">No tab content found</div>`;let p=``,m=``,h=``;for(let e of a){let{id:r,tokens:i,props:a}=e,{active:o,icon:s,label:c}=a,l=`input-${r}`,u=`label-${r}`,d=o?`checked`:``,f=o?`true`:`false`,g=s?`<span class="marked-extended-tabs-icon">${s}</span>`:``;p+=`<input type="radio" name="${n}-tabs" id="${l}" class="marked-extended-tabs-input" ${d}>`,m+=`
254
30
  <label for="${l}" id="${u}" class="marked-extended-tabs-label" role="tab" aria-selected="${f}" data-tab-id="${r}" tabindex="${o?`0`:`-1`}">
255
- ${p}<span class="tab-label">${c}</span>
256
- </label>`;let _=i&&i.length>0?t.parse(i):``;g+=`
31
+ ${g}<span class="tab-label">${c}</span>
32
+ </label>`;let _=i&&i.length>0?t.parse(i):``;h+=`
257
33
  <div class="marked-extended-tabs-content-pane" id="${r}" role="tabpanel" aria-labelledby="${l}">
258
34
  ${_}
259
- </div>`}let _=i(n,o,c),v=a(n,o,u,d,f);return p.replace(/{tabsContainerId}/g,n).replace(/{className}/g,s).replace(/{animation}/g,c).replace(/{inputsNav}/g,m).replace(/{navList}/g,h).replace(/{content}/g,g).replace(/{stylesBehavior}/g,_+v)}function p(e){let{animation:n,autoActivate:r,className:i,persistSelection:a,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:l,enableFocusManagement:f}=e,p=`tabItemBlock`;return{name:`tabs`,level:`block`,tokenizer(e){let m=u(`tabBlock`,e);if(!m)return;let[h,,g]=m,_=`tabs-container-${++t.value}`,v=[],y=0,b=g;for(;b&&(b=b.trimStart(),b);){let e=u(p,b);if(!e)break;let[t,n,r]=e,i=d(p,n),a=i.label||`Tab ${y+1}`,o=i.active===`true`,s=this.lexer.blockTokens(r.trim());v.push({type:`tab-item`,raw:t,id:`${_}-tab-${y}`,props:{label:a,active:o,icon:i.icon||void 0},tokens:s}),y++,b=b.substring(t.length)}if(v.length!==0)return r&&!v.some(e=>e.props.active)&&v.length>0&&(v[0].props.active=!0),{type:`tabs`,raw:h,tokens:v,meta:{tabsContainerId:_,className:i,persistSelection:a,animation:n,autoActivate:r,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:l,enableFocusManagement:f}}}}}function m(){return{name:`tabs`,renderer(e){let t=e;return f({...t.meta,tabsData:t.tokens},this.parser)}}}function h(e={}){e.animation&&![`fade`,`slide`,`none`].includes(e.animation)&&(console.warn(`[marked-extended-tabs] Invalid animation value: ${e.animation}. Using default 'fade'.`),e.animation=`fade`),e.onBeforeSwitch&&typeof e.onBeforeSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onBeforeSwitch must be a function`),e.onBeforeSwitch=null),e.onAfterSwitch&&typeof e.onAfterSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onAfterSwitch must be a function`),e.onAfterSwitch=null);let t={...n,...e};return{walkTokens(e){e.type===`tabs`&&t.customizeToken&&typeof t.customizeToken==`function`&&t.customizeToken(e)},extensions:[p(t),m()]}}e.default=h});
35
+ </div>`}let g=i(n,a,s),_=[`data-tabs-runtime="true"`,`data-tabs-enable-keyboard-navigation="${l?`true`:`false`}"`,`data-tabs-enable-focus-management="${u?`true`:`false`}"`,`data-tabs-persist-selection="${d?`true`:`false`}"`,`data-tabs-storage-key="marked-extended-tabs-active-${n}"`].join(` `);return f.replace(/{tabsContainerId}/g,n).replace(/{className}/g,o).replace(/{animation}/g,s).replace(/{runtimeAttrs}/g,_).replace(/{inputsNav}/g,p).replace(/{navList}/g,m).replace(/{content}/g,h).replace(/{stylesBehavior}/g,g)}function P(e){let{animation:n,autoActivate:r,className:i,persistSelection:a,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:l,enableFocusManagement:u}=e,d=`tabItemBlock`;return{name:`tabs`,level:`block`,tokenizer(e){let f=j(`tabBlock`,e);if(!f)return;let[p,,m]=f,h=`tabs-container-${++t.value}`,g=[],_=0,v=m;for(;v&&(v=v.trimStart(),v);){let e=j(d,v);if(!e)break;let[t,n,r]=e,i=M(d,n),a=i.label||`Tab ${_+1}`,o=i.active===`true`,s=this.lexer.blockTokens(r.trim());g.push({type:`tab-item`,raw:t,id:`${h}-tab-${_}`,props:{label:a,active:o,icon:i.icon||void 0},tokens:s}),_++,v=v.substring(t.length)}if(g.length!==0)return r&&!g.some(e=>e.props.active)&&g.length>0&&(g[0].props.active=!0),{type:`tabs`,raw:p,tokens:g,meta:{tabsContainerId:h,className:i,persistSelection:a,animation:n,autoActivate:r,template:o,onBeforeSwitch:s,onAfterSwitch:c,enableKeyboardNavigation:l,enableFocusManagement:u}}}}}function F(){return{name:`tabs`,renderer(e){let t=e;return N({...t.meta,tabsData:t.tokens},this.parser)}}}function I(e={}){e.animation&&![`fade`,`slide`,`none`].includes(e.animation)&&(console.warn(`[marked-extended-tabs] Invalid animation value: ${e.animation}. Using default 'fade'.`),e.animation=`fade`),e.onBeforeSwitch&&typeof e.onBeforeSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onBeforeSwitch must be a function`),e.onBeforeSwitch=null),e.onAfterSwitch&&typeof e.onAfterSwitch!=`function`&&(console.warn(`[marked-extended-tabs] onAfterSwitch must be a function`),e.onAfterSwitch=null);let t={...n,...e};return E(),{walkTokens(e){e.type===`tabs`&&t.customizeToken&&typeof t.customizeToken==`function`&&t.customizeToken(e)},extensions:[P(t),F()]}}e.default=I});
@@ -28,7 +28,7 @@ export declare const DEFAULT_OPTIONS: Required<TabsOptions>;
28
28
  * - {content}: The content to be displayed in the tabs' content area.
29
29
  * - {stylesBehavior}: Inline styles or behaviors injected into the template.
30
30
  */
31
- export declare const DEFAULT_TEMPLATE = "\n <div id=\"{tabsContainerId}\" class=\"{className}\" data-animation=\"{animation}\">\n {inputsNav}\n <div class=\"marked-extended-tabs-nav\" role=\"tablist\">{navList}</div>\n <div class=\"marked-extended-tabs-content\">{content}</div>\n {stylesBehavior}\n </div>\n";
31
+ export declare const DEFAULT_TEMPLATE = "\n <div id=\"{tabsContainerId}\" class=\"{className}\" data-animation=\"{animation}\" {runtimeAttrs}>\n {inputsNav}\n <div class=\"marked-extended-tabs-nav\" role=\"tablist\">{navList}</div>\n <div class=\"marked-extended-tabs-content\">{content}</div>\n {stylesBehavior}\n </div>\n";
32
32
  /**
33
33
  * Generates a CSS style string for tabs, defining how each tab is displayed and animated
34
34
  * based on the provided container ID, tab data, and animation type.
@@ -41,15 +41,3 @@ export declare const DEFAULT_TEMPLATE = "\n <div id=\"{tabsContainerId}\" class
41
41
  * @return {string} A string of CSS styles defining the behavior of the tabs and their animations.
42
42
  */
43
43
  export declare function createTabsStyles(tabsContainerId: string, tabsData: TabItem[], animation: AnimationType): string;
44
- /**
45
- * Generates JavaScript code for enhanced tab interaction features
46
- * Includes keyboard navigation, focus management, and state persistence
47
- *
48
- * @param {string} tabsContainerId - The ID of the tabs container
49
- * @param {Array<{id: string, props: {label: string}}>} tabsData - Array of tab data
50
- * @param {boolean} enableKeyboardNavigation - Enable arrow key navigation
51
- * @param {boolean} enableFocusManagement - Enable focus management
52
- * @param {boolean} persistSelection - Enable local storage persistence
53
- * @return {string} JavaScript code as a script tag
54
- */
55
- export declare function createTabsScript(tabsContainerId: string, tabsData: TabItem[], enableKeyboardNavigation?: boolean, enableFocusManagement?: boolean, persistSelection?: boolean): string;
@@ -0,0 +1 @@
1
+ export declare const ensureTabsRuntime: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fsegurai/marked-extended-tabs",
3
- "version": "17.0.0-beta.7",
3
+ "version": "17.0.0",
4
4
  "description": "Extension for Marked.js that adds support for extended tabs, allowing the creation of tabbed content sections within Markdown documents. It supports any Markdown rendering and can be customized to fit your needs.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",