@fsegurai/marked-extended-tabs 17.0.0-beta.4 → 17.0.0-beta.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 fsegurai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -2315,6 +2315,7 @@ on [GitHub](https://github.com/fsegurai/marked-extensions/issues).
2315
2315
 
2316
2316
  | Extension | Package | Version | Description |
2317
2317
  |-------------|--------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|----------------------------------------------------------------------|
2318
+ | All - Bundle | [@fsegurai/marked-extended-bundle](https://www.npmjs.com/package/@fsegurai/marked-extended-bundle) | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-bundle) | Includes all extensions in a single package for easy integration |
2318
2319
  | Accordion | [@fsegurai/marked-extended-accordion](https://www.npmjs.com/package/@fsegurai/marked-extended-accordion) | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-accordion) | Add collapsible accordion sections to your markdown |
2319
2320
  | Alert | [@fsegurai/marked-extended-alert](https://www.npmjs.com/package/@fsegurai/marked-extended-alert) | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-alert) | Create styled alert boxes for important information |
2320
2321
  | Comments | [@fsegurai/marked-extended-comments](https://www.npmjs.com/package/@fsegurai/marked-extended-comments) | ![npm](https://img.shields.io/npm/v/@fsegurai/marked-extended-comments) | Add comment sections with author and timestamp metadata |
package/dist/index.cjs CHANGED
@@ -1,125 +1,41 @@
1
- 'use strict';
2
-
3
- // Counter for generating unique IDs for tab containers
4
- const tabCounter = { value: 0 };
5
- /**
6
- * Default configuration options for the extended tabs component.
7
- *
8
- * @constant {Object} DEFAULT_OPTIONS
9
- * @property {string} className - The CSS class name applied to the tabs container.
10
- * @property {boolean} persistSelection - Determines if the selection state should be persisted across sessions.
11
- * @property {string} animation - The animation style for tab transitions, either 'fade', 'slide', or 'none'.
12
- * @property {boolean} autoActivate - If true, the first tab is automatically activated when no tab is marked as active.
13
- * @property {?string} template - A custom template for rendering the tabs, or null to use the default template.
14
- * @property {?Function} customizeToken - A function for customizing tab-related tokens, or null for default handling.
15
- */
16
- const DEFAULT_OPTIONS = {
17
- className: 'marked-extended-tabs-container',
18
- persistSelection: false,
19
- animation: 'fade', // 'fade', 'slide', or 'none'
20
- autoActivate: true, // Automatically activate first tab if none marked active
21
- template: null,
22
- customizeToken: null,
23
- onBeforeSwitch: null,
24
- onAfterSwitch: null,
25
- enableKeyboardNavigation: true,
26
- enableFocusManagement: true,
27
- };
28
- /**
29
- * DEFAULT_TEMPLATE is a template string used to generate the HTML structure
30
- * for a custom tab component. It contains placeholders that are dynamically
31
- * replaced with actual values during runtime to customize the generated markup.
32
- *
33
- * Placeholders:
34
- * - {tabsContainerId}: The unique identifier for the container of the tabs.
35
- * - {className}: The CSS class names to be applied to the container.
36
- * - {animation}: The data-animation attribute value for defining the animation type.
37
- * - {inputsNav}: Additional HTML or elements used in the navigation section of the tabs.
38
- * - {navList}: The list of navigation items for the tabs, rendered as part of the tablist.
39
- * - {content}: The content to be displayed in the tabs' content area.
40
- * - {stylesBehavior}: Inline styles or behaviors injected into the template.
41
- */
42
- const DEFAULT_TEMPLATE = `
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=`
43
2
  <div id="{tabsContainerId}" class="{className}" data-animation="{animation}">
44
3
  {inputsNav}
45
4
  <div class="marked-extended-tabs-nav" role="tablist">{navList}</div>
46
5
  <div class="marked-extended-tabs-content">{content}</div>
47
6
  {stylesBehavior}
48
7
  </div>
49
- `;
50
- /**
51
- * Generates a CSS style string for tabs, defining how each tab is displayed and animated
52
- * based on the provided container ID, tab data, and animation type.
53
- *
54
- * @param {string} tabsContainerId - The ID of the container element that wraps the tabs.
55
- * @param {Array<{id: string}>} tabsData - An array of objects representing tabs,
56
- * each object must have a unique `id`.
57
- * @param {string} animation - The type of animation to apply when switching tabs.
58
- * Acceptable values are 'fade', 'slide', or 'none'.
59
- * @return {string} A string of CSS styles defining the behavior of the tabs and their animations.
60
- */
61
- function createTabsStyles(tabsContainerId, tabsData, animation) {
62
- const parts = [];
63
- parts.push('<style>');
64
- parts.push(' /* CSS-only tab selection - show selected tab */');
65
- tabsData.forEach((tab) => {
66
- const inputId = `input-${tab.id}`;
67
- const labelId = `label-${tab.id}`;
68
- const tabId = tab.id;
69
- parts.push(`
8
+ `;function r(e,t,n){let r=[];return r.push(`<style>`),r.push(` /* CSS-only tab selection - show selected tab */`),t.forEach(t=>{let i=`input-${t.id}`,a=`label-${t.id}`,o=t.id;r.push(`
70
9
  /* Show tab content when corresponding input is checked */
71
- #${tabsContainerId} #${inputId}:checked ~ .marked-extended-tabs-content #${tabId} {
10
+ #${e} #${i}:checked ~ .marked-extended-tabs-content #${o} {
72
11
  display: block;
73
- animation: ${animation === 'fade' ? 'tab-fade-in' : animation === 'slide' ? 'tab-slide-in' : 'none'} 0.3s ease-in-out;
12
+ animation: ${n===`fade`?`tab-fade-in`:n===`slide`?`tab-slide-in`:`none`} 0.3s ease-in-out;
74
13
  }
75
14
 
76
15
  /* Style for active tab */
77
- #${tabsContainerId} #${inputId}:checked ~ .marked-extended-tabs-nav #${labelId} {
16
+ #${e} #${i}:checked ~ .marked-extended-tabs-nav #${a} {
78
17
  background: var(--marked-extended-tabs-selected-background) !important;
79
18
  border-bottom-color: var(--marked-extended-tabs-selected-border-color) !important;
80
19
  color: var(--marked-extended-tabs-selected-color) !important;
81
20
  font-weight: 600 !important;
82
21
  }
83
- `);
84
- });
85
- if (animation === 'fade') {
86
- parts.push(`@keyframes tab-fade-in {
22
+ `)}),n===`fade`&&r.push(`@keyframes tab-fade-in {
87
23
  from { opacity: 0; }
88
24
  to { opacity: 1; }
89
- }`);
90
- }
91
- if (animation === 'slide') {
92
- parts.push(`@keyframes tab-slide-in {
25
+ }`),n===`slide`&&r.push(`@keyframes tab-slide-in {
93
26
  from { transform: translateY(10px); opacity: 0; }
94
27
  to { transform: translateY(0); opacity: 1; }
95
- }`);
96
- }
97
- parts.push('</style>');
98
- return parts.join('\n');
99
- }
100
- /**
101
- * Generates JavaScript code for enhanced tab interaction features
102
- * Includes keyboard navigation, focus management, and state persistence
103
- *
104
- * @param {string} tabsContainerId - The ID of the tabs container
105
- * @param {Array<{id: string, props: {label: string}}>} tabsData - Array of tab data
106
- * @param {boolean} enableKeyboardNavigation - Enable arrow key navigation
107
- * @param {boolean} enableFocusManagement - Enable focus management
108
- * @param {boolean} persistSelection - Enable local storage persistence
109
- * @return {string} JavaScript code as a script tag
110
- */
111
- function createTabsScript(tabsContainerId, tabsData, enableKeyboardNavigation = true, enableFocusManagement = true, persistSelection = false) {
112
- const storageKey = `marked-extended-tabs-active-${tabsContainerId}`;
113
- const js = `
28
+ }`),r.push(`</style>`),r.join(`
29
+ `)}function i(e,t,n=!0,r=!0,i=!1){return`<script>
114
30
  /* eslint-disable no-unused-vars */
115
31
  (function() {
116
- const containerId = '${tabsContainerId}';
117
- const storageKey = '${storageKey}';
32
+ const containerId = '${e}';
33
+ const storageKey = '${`marked-extended-tabs-active-${e}`}';
118
34
  // @ts-ignore - tabIds is used in the script template
119
- const tabIds = ${JSON.stringify(tabsData.map((tab) => tab.id))};
120
- const enableKeyboardNav = ${enableKeyboardNavigation};
121
- const enableFocusManagement = ${enableFocusManagement};
122
- const persistSelection = ${persistSelection};
35
+ const tabIds = ${JSON.stringify(t.map(e=>e.id))};
36
+ const enableKeyboardNav = ${n};
37
+ const enableFocusManagement = ${r};
38
+ const persistSelection = ${i};
123
39
 
124
40
  // Function to update aria-selected state
125
41
  function updateAriaSelected(container, activeInputId) {
@@ -334,379 +250,10 @@ function createTabsScript(tabsContainerId, tabsData, enableKeyboardNavigation =
334
250
  } else {
335
251
  initializeTabs();
336
252
  }
337
- })();`;
338
- return '<script>' + js + '</script>';
339
- }
340
-
341
- /**
342
- * Regular expression for parsing properties from attribute strings
343
- * Matches patterns like: property="value"
344
- */
345
- const propRegex = /\s*(\w+)="([^"]+)"/g;
346
- /**
347
- * Element patterns configuration for tabs blocks
348
- */
349
- const elementPatterns = {
350
- tabBlock: {
351
- start: '::::tabs',
352
- end: '::::tabsend',
353
- aliases: [':tbs', ':tabs'],
354
- endAliases: [':tbsend', ':tabsend'],
355
- },
356
- tabItemBlock: {
357
- start: ':::tab',
358
- end: ':::tabend',
359
- aliases: [':tab'],
360
- endAliases: [':tabend'],
361
- },
362
- };
363
- /**
364
- * Supported properties by element type
365
- */
366
- const supportedPropsByElement = {
367
- tabItemBlock: [
368
- { name: 'label', defaultValue: '' },
369
- { name: 'active', defaultValue: 'false' },
370
- { name: 'icon', defaultValue: null },
371
- ],
372
- };
373
- /**
374
- * Parses balanced opening and closing tags with proper nesting support
375
- * @param src - Source string to parse
376
- * @param elementType - Type of element to parse
377
- * @returns RegExpExecArray or null if no match
378
- */
379
- function parseBalancedTags(src, elementType) {
380
- const pattern = elementPatterns[elementType];
381
- if (!pattern)
382
- return null;
383
- const { start, end, aliases = [], endAliases = [] } = pattern;
384
- // Find which start pattern matches
385
- let matchedStart = '';
386
- if (src.startsWith(start)) {
387
- matchedStart = start;
388
- }
389
- else {
390
- for (const alias of aliases) {
391
- if (src.startsWith(alias)) {
392
- matchedStart = alias;
393
- break;
394
- }
395
- }
396
- }
397
- if (!matchedStart)
398
- return null;
399
- const startPos = matchedStart.length;
400
- // Parse property block (optional)
401
- let propString = '';
402
- let contentStart = startPos;
403
- // Skip whitespace and check for property block
404
- let scanPos = startPos;
405
- while (scanPos < src.length && src[scanPos] === ' ') {
406
- scanPos++;
407
- }
408
- if (scanPos < src.length && src[scanPos] === '{') {
409
- const propEnd = src.indexOf('}', scanPos);
410
- if (propEnd !== -1) {
411
- propString = src.substring(startPos, propEnd);
412
- contentStart = propEnd + 1;
413
- }
414
- }
415
- // Parse nested content with depth tracking
416
- let depth = 1;
417
- let pos = contentStart;
418
- while (pos < src.length && depth > 0) {
419
- // Find next start tag
420
- const mainStartPos = src.indexOf(start, pos);
421
- let nextStartPos = mainStartPos;
422
- for (const alias of aliases) {
423
- const aliasPos = src.indexOf(alias, pos);
424
- if (aliasPos !== -1 && (nextStartPos === -1 || aliasPos < nextStartPos)) {
425
- nextStartPos = aliasPos;
426
- }
427
- }
428
- // Find next end tag
429
- let foundEndPattern = end;
430
- let nextEndPos = src.indexOf(end, pos);
431
- for (const endAlias of endAliases) {
432
- const endAliasPos = src.indexOf(endAlias, pos);
433
- if (endAliasPos !== -1 && (nextEndPos === -1 || endAliasPos < nextEndPos)) {
434
- nextEndPos = endAliasPos;
435
- foundEndPattern = endAlias;
436
- }
437
- }
438
- if (nextEndPos === -1)
439
- return null;
440
- // Handle nested tags
441
- if (nextStartPos !== -1 && nextStartPos < nextEndPos) {
442
- depth++;
443
- let matchedPattern = start;
444
- if (nextStartPos === mainStartPos) {
445
- matchedPattern = start;
446
- }
447
- else {
448
- for (const alias of aliases) {
449
- if (src.indexOf(alias, pos) === nextStartPos) {
450
- matchedPattern = alias;
451
- break;
452
- }
453
- }
454
- }
455
- pos = nextStartPos + matchedPattern.length;
456
- continue;
457
- }
458
- // Check if we've closed all nested tags
459
- if (--depth === 0) {
460
- const content = src.substring(contentStart, nextEndPos);
461
- const fullMatch = src.substring(0, nextEndPos + foundEndPattern.length);
462
- const result = [fullMatch, propString, content];
463
- result.index = 0;
464
- result.input = src;
465
- return result;
466
- }
467
- pos = nextEndPos + foundEndPattern.length;
468
- }
469
- return null;
470
- }
471
- /**
472
- * Validates and parses element patterns from source string
473
- * @param element - Element type or RegExp to validate
474
- * @param src - Source string to parse
475
- * @returns RegExpExecArray or null if no match
476
- */
477
- const validateRegex = (element, src) => {
478
- if (element instanceof RegExp) {
479
- return element.exec(src);
480
- }
481
- switch (element) {
482
- case 'tabBlock':
483
- case 'tabItemBlock':
484
- return parseBalancedTags(src, element);
485
- default:
486
- throw new Error(`Unknown element: ${element}`);
487
- }
488
- };
489
- /**
490
- * Constructs properties object from property string
491
- * @param element - Element type
492
- * @param propString - String containing properties
493
- * @returns Record of property names to values
494
- */
495
- const constructProps = (element, propString) => {
496
- const supportedProps = supportedPropsByElement[element];
497
- if (!supportedProps) {
498
- throw new Error(`Unknown element: ${element}`);
499
- }
500
- // Initialize with default values
501
- const props = {};
502
- supportedProps.forEach((prop) => {
503
- props[prop.name] = prop.defaultValue;
504
- });
505
- // Parse and override with actual values
506
- propRegex.lastIndex = 0;
507
- let propMatch;
508
- while ((propMatch = propRegex.exec(propString)) !== null) {
509
- const [, name, value] = propMatch;
510
- if (supportedProps.some((p) => p.name === name)) {
511
- props[name] = value;
512
- }
513
- }
514
- return props;
515
- };
516
-
517
- /**
518
- * Renders the tab component with enhanced interactivity
519
- * @param options - Rendering options
520
- * @param parser - Parser instance from marked.js renderer context
521
- * @returns HTML string for the tabs
522
- */
523
- function renderTabs(options, parser) {
524
- const { tabsContainerId, tabsData, className, animation = 'fade', template, enableKeyboardNavigation = true, enableFocusManagement = true, persistSelection, } = options;
525
- // Get the tab template
526
- const tabsTemplate = template || DEFAULT_TEMPLATE;
527
- if (!tabsData || tabsData.length === 0) {
528
- return '<div class="error-message">No tab content found</div>';
529
- }
530
- let inputsNav = '';
531
- let navList = '';
532
- let markedContent = '';
533
- for (const tab of tabsData) {
534
- const { id, tokens, props } = tab;
535
- const { active, icon, label } = props;
536
- const inputId = `input-${id}`;
537
- const labelId = `label-${id}`;
538
- const isChecked = active ? 'checked' : '';
539
- const ariaSelected = active ? 'true' : 'false';
540
- const iconHtml = icon ? `<span class="marked-extended-tabs-icon">${icon}</span>` : '';
541
- // Inputs first
542
- inputsNav += `<input type="radio" name="${tabsContainerId}-tabs" id="${inputId}" class="marked-extended-tabs-input" ${isChecked}>`;
543
- // Navigation labels with data attributes for JavaScript enhancement
544
- navList += `
545
- <label for="${inputId}" id="${labelId}" class="marked-extended-tabs-label" role="tab" aria-selected="${ariaSelected}" data-tab-id="${id}" tabindex="${active ? '0' : '-1'}">
546
- ${iconHtml}<span class="tab-label">${label}</span>
547
- </label>`;
548
- // Render the content using the parser context which has all extensions loaded
549
- // This allows nested extensions to be properly rendered
550
- const parsedContent = tokens && tokens.length > 0 ? parser.parse(tokens) : '';
551
- // Tab content
552
- markedContent += `
553
- <div class="marked-extended-tabs-content-pane" id="${id}" role="tabpanel" aria-labelledby="${inputId}">
554
- ${parsedContent}
555
- </div>`;
556
- }
557
- const styles = createTabsStyles(tabsContainerId, tabsData, animation);
558
- // Generate JavaScript for enhanced tab switching
559
- const script = createTabsScript(tabsContainerId, tabsData, enableKeyboardNavigation, enableFocusManagement, persistSelection);
560
- return tabsTemplate
561
- .replace(/{tabsContainerId}/g, tabsContainerId)
562
- .replace(/{className}/g, className)
563
- .replace(/{animation}/g, animation)
564
- .replace(/{inputsNav}/g, inputsNav)
565
- .replace(/{navList}/g, navList)
566
- .replace(/{content}/g, markedContent)
567
- .replace(/{stylesBehavior}/g, styles + script);
568
- }
569
-
570
- /**
571
- * Creates a tokenizer function for the tab extension
572
- */
573
- function createTokenizer(options) {
574
- const { animation, autoActivate, className, persistSelection, template, onBeforeSwitch, onAfterSwitch, enableKeyboardNavigation, enableFocusManagement } = options;
575
- const tabElement = 'tabBlock';
576
- const tabItemElement = 'tabItemBlock';
577
- return {
578
- name: 'tabs',
579
- level: 'block',
580
- tokenizer(src) {
581
- const blockMatch = validateRegex(tabElement, src);
582
- if (!blockMatch)
583
- return undefined;
584
- // The first position is the full match, the second is props, the third is content
585
- const [raw, , tabContent] = blockMatch;
586
- // Generate unique ID for this tab container
587
- const tabsContainerId = `tabs-container-${++tabCounter.value}`;
588
- // Find individual tabs directly in the extracted tab content
589
- const items = [];
590
- let tabIndex = 0;
591
- let remainingContent = tabContent;
592
- while (remainingContent) {
593
- // Skip whitespace
594
- remainingContent = remainingContent.trimStart();
595
- if (!remainingContent)
596
- break;
597
- const tabMatch = validateRegex(tabItemElement, remainingContent);
598
- if (!tabMatch) {
599
- // If we have content but it doesn't match a tab item, strictly speaking this might be invalid content
600
- // inside a tabs container, or we could just breaking to avoid infinite loop.
601
- break;
602
- }
603
- const [itemRaw, propsStr, tabContentStr] = tabMatch;
604
- // Parse properties with defaults
605
- const rawProps = constructProps(tabItemElement, propsStr);
606
- // If no label is provided, use a default
607
- const label = rawProps['label'] || `Tab ${tabIndex + 1}`;
608
- // Convert string 'true'/'false' to boolean for the active property
609
- const active = rawProps['active'] === 'true';
610
- // Tokenize the tab content using this.lexer.blockTokens
611
- // This ensures nested extensions are properly tokenized
612
- const innerTokens = this.lexer.blockTokens(tabContentStr.trim());
613
- // Add to tabs data - include raw so it satisfies Tokens.Generic
614
- items.push({
615
- type: 'tab-item',
616
- raw: itemRaw,
617
- id: `${tabsContainerId}-tab-${tabIndex}`,
618
- props: {
619
- label,
620
- active,
621
- icon: rawProps['icon'] || undefined,
622
- },
623
- tokens: innerTokens,
624
- });
625
- tabIndex++;
626
- // Advance cursor
627
- remainingContent = remainingContent.substring(itemRaw.length);
628
- }
629
- // If no tabs were found, return undefined
630
- if (items.length === 0)
631
- return undefined;
632
- // Apply auto-activation if enabled, and no tab is explicitly marked as active
633
- if (autoActivate) {
634
- const hasActiveTab = items.some((tab) => tab.props.active);
635
- if (!hasActiveTab && items.length > 0) {
636
- items[0].props.active = true;
637
- }
638
- }
639
- // Create a token with metadata for the renderer
640
- return {
641
- type: 'tabs',
642
- raw,
643
- tokens: items,
644
- meta: {
645
- tabsContainerId,
646
- className,
647
- persistSelection,
648
- animation,
649
- autoActivate,
650
- template,
651
- onBeforeSwitch,
652
- onAfterSwitch,
653
- enableKeyboardNavigation,
654
- enableFocusManagement,
655
- },
656
- };
657
- },
658
- };
659
- }
660
- /**
661
- * Create the renderer extension for tabs
662
- */
663
- function createRenderer() {
664
- return {
665
- name: 'tabs',
666
- renderer(token) {
667
- const tabsToken = token;
668
- // Pass meta and parser to the renderer for proper nested extension support
669
- return renderTabs({ ...tabsToken.meta, tabsData: tabsToken.tokens }, this.parser);
670
- },
671
- };
672
- }
673
-
674
- /**
675
- * Marked Extended Tabs extension
676
- * @param options - Configuration options
677
- * @returns Marked extension object
678
- */
679
- function markedExtendedTabs(options = {}) {
680
- // Validate animation option
681
- if (options.animation && !['fade', 'slide', 'none'].includes(options.animation)) {
682
- console.warn(`[marked-extended-tabs] Invalid animation value: ${options.animation}. Using default 'fade'.`);
683
- options.animation = 'fade';
684
- }
685
- // Validate callbacks
686
- if (options.onBeforeSwitch && typeof options.onBeforeSwitch !== 'function') {
687
- console.warn('[marked-extended-tabs] onBeforeSwitch must be a function');
688
- options.onBeforeSwitch = null;
689
- }
690
- if (options.onAfterSwitch && typeof options.onAfterSwitch !== 'function') {
691
- console.warn('[marked-extended-tabs] onAfterSwitch must be a function');
692
- options.onAfterSwitch = null;
693
- }
694
- // Set sensible defaults
695
- const config = { ...DEFAULT_OPTIONS, ...options };
696
- // NOTE: Styles are no longer injected automatically. Import the CSS/SCSS files manually in your project.
697
- // Return the extension
698
- return {
699
- walkTokens(token) {
700
- if (token.type !== 'tabs')
701
- return;
702
- // Apply custom token modifications if configured
703
- if (config.customizeToken && typeof config.customizeToken === 'function') {
704
- config.customizeToken(token);
705
- }
706
- },
707
- extensions: [createTokenizer(config), createRenderer()],
708
- };
709
- }
710
-
711
- module.exports = markedExtendedTabs;
712
- //# sourceMappingURL=index.cjs.map
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+=`
254
+ <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+=`
257
+ <div class="marked-extended-tabs-content-pane" id="${n}" role="tabpanel" aria-labelledby="${l}">
258
+ ${_}
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;