@fsegurai/marked-extended-tabs 17.0.0-beta.3 → 17.0.0-beta.5

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.custom.md CHANGED
@@ -712,7 +712,7 @@ Individual tabs can use these aliases:
712
712
  The marked-extended-tabs extension accepts the following configuration options:
713
713
 
714
714
  - `className`: The base CSS class name for tabs container. Defaults to 'marked-extended-tabs-container'.
715
- - `persistSelection`: Whether to remember tab selection across page reloads. Defaults to **true**.
715
+ - `persistSelection`: Whether to remember tab selection across page reloads. Defaults to **false**.
716
716
  - `animation`: Animation type for tab transitions. Defaults to 'fade'. See [Animation Types](#animation-types).
717
717
  - `autoActivate`: Automatically activate the first tab if none is explicitly active. Defaults to true.
718
718
  - `template`: A custom HTML template for the tabs structure. Defaults to the built-in template.
@@ -782,7 +782,7 @@ Enable/disable with:
782
782
 
783
783
  ```javascript
784
784
  marked.use(markedExtendedTabs({
785
- persistSelection: true // Default: true
785
+ persistSelection: true // Default: false
786
786
  }));
787
787
  ```
788
788
 
@@ -859,7 +859,7 @@ import '@fsegurai/marked-extended-tabs/styles/tabs.css';
859
859
  marked.use(markedExtendedTabs({
860
860
  animation: 'fade',
861
861
  autoActivate: true,
862
- persistSelection: true,
862
+ persistSelection: false,
863
863
  enableKeyboardNavigation: true,
864
864
  enableFocusManagement: true,
865
865
 
@@ -968,7 +968,7 @@ import markedExtendedTabs from "@fsegurai/marked-extended-tabs";
968
968
  marked.use(markedExtendedTabs({
969
969
  className: 'my-custom-tabs',
970
970
  animation: 'slide',
971
- persistSelection: true,
971
+ persistSelection: false,
972
972
  autoActivate: true,
973
973
  customizeToken: (token) => {
974
974
  // Add custom logic here
package/README.md CHANGED
@@ -763,7 +763,7 @@ Individual tabs can use these aliases:
763
763
  The marked-extended-tabs extension accepts the following configuration options:
764
764
 
765
765
  - `className`: The base CSS class name for tabs container. Defaults to 'marked-extended-tabs-container'.
766
- - `persistSelection`: Whether to remember tab selection across page reloads. Defaults to **true**.
766
+ - `persistSelection`: Whether to remember tab selection across page reloads. Defaults to **false**.
767
767
  - `animation`: Animation type for tab transitions. Defaults to 'fade'. See [Animation Types](#animation-types).
768
768
  - `autoActivate`: Automatically activate the first tab if none is explicitly active. Defaults to true.
769
769
  - `template`: A custom HTML template for the tabs structure. Defaults to the built-in template.
@@ -833,7 +833,7 @@ Enable/disable with:
833
833
 
834
834
  ```javascript
835
835
  marked.use(markedExtendedTabs({
836
- persistSelection: true // Default: true
836
+ persistSelection: true // Default: false
837
837
  }));
838
838
  ```
839
839
 
@@ -910,7 +910,7 @@ import '@fsegurai/marked-extended-tabs/styles/tabs.css';
910
910
  marked.use(markedExtendedTabs({
911
911
  animation: 'fade',
912
912
  autoActivate: true,
913
- persistSelection: true,
913
+ persistSelection: false,
914
914
  enableKeyboardNavigation: true,
915
915
  enableFocusManagement: true,
916
916
 
@@ -1019,7 +1019,7 @@ import markedExtendedTabs from "@fsegurai/marked-extended-tabs";
1019
1019
  marked.use(markedExtendedTabs({
1020
1020
  className: 'my-custom-tabs',
1021
1021
  animation: 'slide',
1022
- persistSelection: true,
1022
+ persistSelection: false,
1023
1023
  autoActivate: true,
1024
1024
  customizeToken: (token) => {
1025
1025
  // Add custom logic here
@@ -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: true,
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 = true) {
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;