@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 +5 -229
- package/dist/index.esm.js +213 -298
- package/dist/index.umd.js +5 -229
- package/dist/types/utils/constants.d.ts +1 -13
- package/dist/types/utils/runtime.d.ts +1 -0
- package/package.json +1 -1
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
|
-
`)}
|
|
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
|
-
${
|
|
256
|
-
</label>`;let _=r&&r.length>0?t.parse(r):``;
|
|
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
|
|
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/
|
|
264
|
-
var a =
|
|
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
|
-
},
|
|
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
|
|
292
|
-
let n =
|
|
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:
|
|
295
|
-
if (e.startsWith(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
|
-
|
|
206
|
+
s = t;
|
|
298
207
|
break;
|
|
299
208
|
}
|
|
300
|
-
if (!
|
|
301
|
-
let
|
|
302
|
-
for (;
|
|
303
|
-
if (
|
|
304
|
-
let t = e.indexOf("}",
|
|
305
|
-
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
|
|
308
|
-
for (;
|
|
309
|
-
let t = e.indexOf(r,
|
|
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,
|
|
220
|
+
let r = e.indexOf(t, p);
|
|
312
221
|
r !== -1 && (n === -1 || r < n) && (n = r);
|
|
313
222
|
}
|
|
314
|
-
let
|
|
315
|
-
for (let t of
|
|
316
|
-
let n = e.indexOf(t,
|
|
317
|
-
n !== -1 && (c === -1 || n < c) && (c = n,
|
|
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
|
-
|
|
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,
|
|
233
|
+
else for (let t of a) if (e.indexOf(t, p) === n) {
|
|
325
234
|
i = t;
|
|
326
235
|
break;
|
|
327
236
|
}
|
|
328
|
-
|
|
237
|
+
p = n + i.length;
|
|
329
238
|
continue;
|
|
330
239
|
}
|
|
331
|
-
if (--
|
|
332
|
-
let t = e.substring(
|
|
333
|
-
e.substring(0, c +
|
|
334
|
-
|
|
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
|
-
|
|
248
|
+
p = c + s.length;
|
|
340
249
|
}
|
|
341
250
|
return null;
|
|
342
251
|
}
|
|
343
|
-
var
|
|
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
|
|
256
|
+
case "tabItemBlock": return k(t, e);
|
|
348
257
|
default: throw Error(`Unknown element: ${e}`);
|
|
349
258
|
}
|
|
350
|
-
},
|
|
351
|
-
let n =
|
|
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
|
-
}),
|
|
265
|
+
}), E.lastIndex = 0;
|
|
357
266
|
let i;
|
|
358
|
-
for (; (i =
|
|
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
|
|
367
|
-
let { tabsContainerId:
|
|
368
|
-
if (!
|
|
369
|
-
let
|
|
370
|
-
for (let e of
|
|
371
|
-
let { id: n, tokens: r, props:
|
|
372
|
-
|
|
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
|
-
${
|
|
283
|
+
${g}<span class="tab-label">${c}</span>
|
|
375
284
|
</label>`;
|
|
376
285
|
let _ = r && r.length > 0 ? t.parse(r) : "";
|
|
377
|
-
|
|
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
|
|
383
|
-
|
|
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
|
|
388
|
-
let { animation: n, autoActivate: r, className: i, persistSelection: a, template: o, onBeforeSwitch: s, onAfterSwitch: c, enableKeyboardNavigation:
|
|
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
|
|
394
|
-
if (!
|
|
395
|
-
let [
|
|
396
|
-
for (;
|
|
397
|
-
let e =
|
|
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 =
|
|
400
|
-
|
|
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: `${
|
|
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
|
-
}),
|
|
325
|
+
}), _++, v = v.substring(t.length);
|
|
411
326
|
}
|
|
412
|
-
if (
|
|
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:
|
|
415
|
-
tokens:
|
|
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:
|
|
426
|
-
enableFocusManagement:
|
|
340
|
+
enableKeyboardNavigation: l,
|
|
341
|
+
enableFocusManagement: u
|
|
427
342
|
}
|
|
428
343
|
};
|
|
429
344
|
}
|
|
430
345
|
};
|
|
431
346
|
}
|
|
432
|
-
function
|
|
347
|
+
function P() {
|
|
433
348
|
return {
|
|
434
349
|
name: "tabs",
|
|
435
350
|
renderer(e) {
|
|
436
351
|
let t = e;
|
|
437
|
-
return
|
|
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
|
|
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: [
|
|
375
|
+
extensions: [N(n), P()]
|
|
461
376
|
};
|
|
462
377
|
}
|
|
463
378
|
//#endregion
|
|
464
|
-
export {
|
|
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
|
-
`)}
|
|
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
|
-
${
|
|
256
|
-
</label>`;let _=i&&i.length>0?t.parse(i):``;
|
|
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
|
|
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
|
|
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",
|