@diplodoc/tabs-extension 3.4.0 → 3.5.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.
@@ -80,28 +80,32 @@ __export(plugin_exports, {
80
80
  });
81
81
  module.exports = __toCommonJS(plugin_exports);
82
82
 
83
- // src/plugin/transform.ts
84
- var import_token = __toESM(require("markdown-it/lib/token"));
85
-
86
83
  // src/common.ts
84
+ var TAB_RE = /`?{% list tabs .*?%}`?/;
87
85
  var TABS_CLASSNAME = "yfm-tabs";
88
- var TABS_VERTICAL_CLASSNAME = "yfm-tabs-vertical";
89
- var TABS_LIST_CLASSNAME = "yfm-tab-list";
90
86
  var TAB_CLASSNAME = "yfm-tab";
91
87
  var TAB_PANEL_CLASSNAME = "yfm-tab-panel";
88
+ var TABS_LIST_CLASSNAME = "yfm-tab-list";
92
89
  var ACTIVE_CLASSNAME = "active";
93
- var VERTICAL_TAB_CLASSNAME = "yfm-vertical-tab";
94
- var VERTICAL_TAB_FORCED_OPEN = "data-diplodoc-radio-forced";
90
+ var TAB_GROUP_CLASSNAME = "yfm-tab-group";
91
+ var TAB_ACTIVE_KEY = "data-diplodoc-is-active";
95
92
  var GROUP_DATA_KEY = "data-diplodoc-group";
96
93
  var TAB_DATA_KEY = "data-diplodoc-key";
94
+ var TAB_DATA_VARIANT = "data-diplodoc-variant";
97
95
  var TAB_DATA_ID = "data-diplodoc-id";
98
- var TAB_DATA_VERTICAL_TAB = "data-diplodoc-vertical-tab";
99
- var TAB_ACTIVE_KEY = "data-diplodoc-is-active";
100
96
  var DEFAULT_TABS_GROUP_PREFIX = "defaultTabsGroup-";
101
97
  var ACTIVE_TAB_TEXT = "{selected}";
98
+ var TAB_FORCED_OPEN = "data-diplodoc-forced";
99
+ var TABS_DROPDOWN_CLASSNAME = "yfm-tabs-dropdown";
100
+ var TABS_DROPDOWN_MENU_CLASSNAME = "yfm-tabs-dropdown-menu";
101
+ var TABS_DROPDOWN_SELECT = "yfm-tabs-dropdown-select";
102
+ var TABS_ACCORDION_CLASSNAME = "yfm-tabs-accordion";
103
+ var TABS_RADIO_CLASSNAME = "yfm-tabs-vertical";
104
+ var VERTICAL_TAB_CLASSNAME = "yfm-vertical-tab";
105
+ var TAB_DATA_VERTICAL_TAB = "data-diplodoc-vertical-tab";
102
106
  var GLOBAL_SYMBOL = Symbol.for("diplodocTabs");
103
107
 
104
- // src/plugin/utils.ts
108
+ // src/plugin/utils/strings.ts
105
109
  function generateID() {
106
110
  const id = Math.random().toString(36).substr(2, 8);
107
111
  return id.substring(id.length - 8);
@@ -112,47 +116,8 @@ function trim(target) {
112
116
  function unquote(target) {
113
117
  return target.match(/^(["']).*\1$/) ? target.slice(1, -1) : target;
114
118
  }
115
- function addHiddenProperty(box, field, value) {
116
- if (!(field in box)) {
117
- Object.defineProperty(box, field, {
118
- enumerable: false,
119
- value
120
- });
121
- }
122
- return box;
123
- }
124
119
 
125
- // src/plugin/copyRuntimeFiles.ts
126
- var PATH_TO_RUNTIME = "../runtime";
127
- function copyRuntimeFiles({
128
- runtimeJsPath,
129
- runtimeCssPath,
130
- output
131
- }, cache) {
132
- const { join, resolve } = dynrequire("node:path");
133
- const runtimeFiles = {
134
- "index.js": runtimeJsPath,
135
- "index.css": runtimeCssPath
136
- };
137
- for (const [originFile, outputFile] of Object.entries(runtimeFiles)) {
138
- const file = join(PATH_TO_RUNTIME, originFile);
139
- if (!cache.has(file)) {
140
- cache.add(file);
141
- copyFile(resolve(__dirname, file), join(output, outputFile));
142
- }
143
- }
144
- }
145
- function copyFile(from, to) {
146
- const { mkdirSync, copyFileSync } = dynrequire("node:fs");
147
- const { dirname } = dynrequire("node:path");
148
- mkdirSync(dirname(to), { recursive: true });
149
- copyFileSync(from, to);
150
- }
151
- function dynrequire(module) {
152
- return eval(`require('${module}')`);
153
- }
154
-
155
- // src/plugin/getTabId.ts
120
+ // src/plugin/utils/tabs.ts
156
121
  var import_github_slugger = __toESM(require_github_slugger());
157
122
  var CUSTOM_ID_REGEXP = /\[?{ ?#(\S+) ?}]?/;
158
123
  var sluggersStorage = /* @__PURE__ */ new Map();
@@ -183,6 +148,10 @@ function getTabId(tab, { runId }) {
183
148
  }
184
149
  return slugger.slug(getRawId(tab));
185
150
  }
151
+ function isTabSelected(tab) {
152
+ const { name } = tab;
153
+ return name.includes(ACTIVE_TAB_TEXT);
154
+ }
186
155
  function getTabKey(tab) {
187
156
  return encodeURIComponent(getRawId(tab)).toLocaleLowerCase();
188
157
  }
@@ -194,79 +163,49 @@ function getRawId(tab) {
194
163
  return customAnchor || name;
195
164
  }
196
165
 
197
- // src/plugin/transform.ts
198
- var TAB_RE = /`?{% list tabs .*?%}`?/;
199
- var runsCounter = 0;
200
- function findTabs(tokens, idx, closeTokenIdx) {
201
- const tabs = [];
202
- let i = idx, nestedLevel = -1, pending = { name: "", tokens: [], listItem: new import_token.default("list_item_open", "", 0) };
203
- while (i < tokens.length) {
204
- const token = tokens[i];
205
- switch (token.type) {
206
- case "ordered_list_open":
207
- case "bullet_list_open":
208
- if (nestedLevel > -1) {
209
- pending.tokens.push(token);
210
- }
211
- nestedLevel++;
212
- break;
213
- case "list_item_open":
214
- if (nestedLevel) {
215
- pending.tokens.push(token);
216
- } else {
217
- pending = { name: "", tokens: [], listItem: token };
218
- }
219
- break;
220
- case "list_item_close":
221
- if (nestedLevel) {
222
- pending.tokens.push(token);
223
- } else {
224
- tabs.push(pending);
225
- }
226
- break;
227
- case "ordered_list_close":
228
- case "bullet_list_close":
229
- if (!nestedLevel) {
230
- return {
231
- tabs,
232
- index: i
233
- };
234
- }
235
- nestedLevel--;
236
- pending.tokens.push(token);
237
- break;
238
- case "paragraph_open":
239
- if (i === closeTokenIdx && tokens[i + 1].content && tokens[i + 1].content.trim() === "{% endlist %}") {
240
- if (pending && !nestedLevel) {
241
- tabs.push(pending);
242
- }
243
- return {
244
- tabs,
245
- index: i + 2
246
- };
247
- }
248
- if (!pending.name && tokens[i + 1].type === "inline") {
249
- pending.name = tokens[i + 1].content;
250
- i += 2;
251
- } else {
252
- pending.tokens.push(token);
253
- }
254
- break;
255
- default:
256
- pending.tokens.push(token);
166
+ // src/plugin/utils/files.ts
167
+ var PATH_TO_RUNTIME = "../runtime";
168
+ function copyRuntimeFiles({
169
+ runtimeJsPath,
170
+ runtimeCssPath,
171
+ output
172
+ }, cache) {
173
+ const { join, resolve } = dynrequire("node:path");
174
+ const runtimeFiles = {
175
+ "index.js": runtimeJsPath,
176
+ "index.css": runtimeCssPath
177
+ };
178
+ for (const [originFile, outputFile] of Object.entries(runtimeFiles)) {
179
+ const file = join(PATH_TO_RUNTIME, originFile);
180
+ if (!cache.has(file)) {
181
+ cache.add(file);
182
+ copyFile(resolve(__dirname, file), join(output, outputFile));
257
183
  }
258
- i++;
259
184
  }
260
- return {
261
- tabs,
262
- index: i
263
- };
264
185
  }
265
- function insertTabs(tabs, state, align, { start, end }, {
266
- containerClasses,
267
- tabsGroup,
268
- runId
269
- }) {
186
+ function copyFile(from, to) {
187
+ const { mkdirSync, copyFileSync } = dynrequire("node:fs");
188
+ const { dirname } = dynrequire("node:path");
189
+ mkdirSync(dirname(to), { recursive: true });
190
+ copyFileSync(from, to);
191
+ }
192
+ function dynrequire(module) {
193
+ return eval(`require('${module}')`);
194
+ }
195
+
196
+ // src/plugin/utils/index.ts
197
+ function addHiddenProperty(box, field, value) {
198
+ if (!(field in box)) {
199
+ Object.defineProperty(box, field, {
200
+ enumerable: false,
201
+ value
202
+ });
203
+ }
204
+ return box;
205
+ }
206
+
207
+ // src/plugin/variants/regular.ts
208
+ var regular = (tabs, state, { containerClasses, tabsGroup, runId, variant: orientation }) => {
270
209
  var _a, _b;
271
210
  const tabsTokens = [];
272
211
  const tabListTokens = [];
@@ -276,34 +215,31 @@ function insertTabs(tabs, state, align, { start, end }, {
276
215
  const tabListOpen = new state.Token("tab-list_open", "div", 1);
277
216
  const tabListClose = new state.Token("tab-list_close", "div", -1);
278
217
  if (tabs.length) {
279
- const [start2] = (_a = tabs[0].listItem.map) != null ? _a : [null];
280
- if (start2 == null) {
218
+ const [start] = (_a = tabs[0].listItem.map) != null ? _a : [null];
219
+ if (start == null) {
281
220
  throw new Error("failed to parse line mapping");
282
221
  }
283
- const [_, end2] = (_b = tabs[tabs.length - 1].listItem.map) != null ? _b : [null, null];
284
- if (end2 == null) {
222
+ const [_, end] = (_b = tabs[tabs.length - 1].listItem.map) != null ? _b : [null, null];
223
+ if (end == null) {
285
224
  throw new Error("failed to parse line mapping");
286
225
  }
287
- tabListOpen.map = [start2, end2];
226
+ tabListOpen.map = [start, end];
288
227
  }
289
228
  tabsOpen.block = true;
290
229
  tabsClose.block = true;
291
230
  tabListOpen.block = true;
292
231
  tabListClose.block = true;
293
- const areTabsVerticalClass = align === "radio" && TABS_VERTICAL_CLASSNAME;
294
232
  const activeTabsCount = tabs.filter(isTabSelected).length;
295
233
  if (activeTabsCount > 1) {
296
234
  throw new Error("Unable to render tabs with more than 1 active element");
297
235
  }
298
236
  const hasDefaultOpenTab = activeTabsCount !== 0;
299
- tabsOpen.attrSet(
300
- "class",
301
- [TABS_CLASSNAME, containerClasses, areTabsVerticalClass].filter(Boolean).join(" ")
302
- );
237
+ tabsOpen.attrSet("class", [TABS_CLASSNAME, containerClasses].filter(Boolean).join(" "));
303
238
  tabsOpen.attrSet(GROUP_DATA_KEY, tabsGroup);
239
+ tabsOpen.attrSet(TAB_DATA_VARIANT, "regular");
304
240
  tabListOpen.attrSet("class", TABS_LIST_CLASSNAME);
305
241
  tabListOpen.attrSet("role", "tablist");
306
- if (align === "radio") {
242
+ if (orientation === "radio") {
307
243
  tabsTokens.push(tabsOpen);
308
244
  }
309
245
  for (let i = 0; i < tabs.length; i++) {
@@ -322,7 +258,7 @@ function insertTabs(tabs, state, align, { start, end }, {
322
258
  const tabId = getTabId(tab, { runId });
323
259
  const tabKey = getTabKey(tab);
324
260
  const didTabHasActiveAttr = isTabSelected(tab);
325
- const isTabActive = hasDefaultOpenTab ? didTabHasActiveAttr : align === "horizontal" && i === 0;
261
+ const isTabActive = hasDefaultOpenTab ? didTabHasActiveAttr : i === 0;
326
262
  tab.name = getName(tab);
327
263
  const tabPanelId = generateID();
328
264
  verticalTabInput.block = true;
@@ -350,13 +286,13 @@ function insertTabs(tabs, state, align, { start, end }, {
350
286
  tabPanelOpen.attrSet("role", "tabpanel");
351
287
  tabPanelOpen.attrSet("aria-labelledby", tabId);
352
288
  tabPanelOpen.attrSet("data-title", tab.name);
353
- if (align === "radio") {
289
+ if (orientation === "radio") {
354
290
  tabOpen.attrSet(TAB_DATA_VERTICAL_TAB, "true");
355
291
  tabOpen.attrJoin("class", VERTICAL_TAB_CLASSNAME);
356
292
  }
357
293
  if (isTabActive) {
358
- if (align === "radio") {
359
- tabOpen.attrSet(VERTICAL_TAB_FORCED_OPEN, "true");
294
+ if (orientation === "radio") {
295
+ tabOpen.attrSet(TAB_FORCED_OPEN, "true");
360
296
  verticalTabInput.attrSet("checked", "true");
361
297
  tabPanelOpen.attrJoin("class", ACTIVE_CLASSNAME);
362
298
  } else {
@@ -366,7 +302,7 @@ function insertTabs(tabs, state, align, { start, end }, {
366
302
  tabPanelOpen.attrJoin("class", ACTIVE_CLASSNAME);
367
303
  }
368
304
  }
369
- if (align === "radio") {
305
+ if (orientation === "radio") {
370
306
  tabsTokens.push(
371
307
  tabOpen,
372
308
  verticalTabInput,
@@ -381,22 +317,300 @@ function insertTabs(tabs, state, align, { start, end }, {
381
317
  tabPanelsTokens.push(tabPanelOpen, ...tabs[i].tokens, tabPanelClose);
382
318
  }
383
319
  }
384
- if (align === "horizontal") {
385
- tabsTokens.push(tabsOpen);
386
- tabsTokens.push(tabListOpen);
387
- tabsTokens.push(...tabListTokens);
388
- tabsTokens.push(tabListClose);
389
- tabsTokens.push(...tabPanelsTokens);
320
+ tabsTokens.push(tabsOpen);
321
+ tabsTokens.push(tabListOpen);
322
+ tabsTokens.push(...tabListTokens);
323
+ tabsTokens.push(tabListClose);
324
+ tabsTokens.push(...tabPanelsTokens);
325
+ tabsTokens.push(tabsClose);
326
+ return tabsTokens;
327
+ };
328
+
329
+ // src/plugin/variants/radio.ts
330
+ var radio = (tabs, state, { containerClasses, tabsGroup, runId }) => {
331
+ var _a, _b;
332
+ const tabsTokens = [];
333
+ const tabsOpen = new state.Token("tabs_open", "div", 1);
334
+ const tabsClose = new state.Token("tabs_close", "div", -1);
335
+ if (tabs.length) {
336
+ const [start] = (_a = tabs[0].listItem.map) != null ? _a : [null];
337
+ if (start == null) {
338
+ throw new Error("failed to parse line mapping");
339
+ }
340
+ const [_, end] = (_b = tabs[tabs.length - 1].listItem.map) != null ? _b : [null, null];
341
+ if (end == null) {
342
+ throw new Error("failed to parse line mapping");
343
+ }
344
+ }
345
+ tabsOpen.block = true;
346
+ tabsClose.block = true;
347
+ const activeTabsCount = tabs.filter(isTabSelected).length;
348
+ if (activeTabsCount > 1) {
349
+ throw new Error("Unable to render tabs with more than 1 active element");
350
+ }
351
+ tabsOpen.attrSet(
352
+ "class",
353
+ [TABS_CLASSNAME, containerClasses, TABS_RADIO_CLASSNAME].filter(Boolean).join(" ")
354
+ );
355
+ tabsOpen.attrSet(GROUP_DATA_KEY, tabsGroup);
356
+ tabsOpen.attrSet(TAB_DATA_VARIANT, "radio");
357
+ tabsTokens.push(tabsOpen);
358
+ for (let i = 0; i < tabs.length; i++) {
359
+ const tabOpen = new state.Token("tab_open", "div", 1);
360
+ const tabInline = new state.Token("inline", "", 0);
361
+ const tabText = new state.Token("text", "", 0);
362
+ const tabClose = new state.Token("tab_close", "div", -1);
363
+ const tabPanelOpen = new state.Token("tab-panel_open", "div", 1);
364
+ const tabPanelClose = new state.Token("tab-panel_close", "div", -1);
365
+ const verticalTabInput = new state.Token("tab-input", "input", 0);
366
+ const verticalTabLabelOpen = new state.Token("tab-label_open", "label", 1);
367
+ const verticalTabLabelClose = new state.Token("tab-label_close", "label", -1);
368
+ tabOpen.map = tabs[i].listItem.map;
369
+ tabOpen.markup = tabs[i].listItem.markup;
370
+ const tab = tabs[i];
371
+ const tabId = getTabId(tab, { runId });
372
+ const tabKey = getTabKey(tab);
373
+ const didTabHasActiveAttr = isTabSelected(tab);
374
+ tab.name = getName(tab);
375
+ const tabPanelId = generateID();
376
+ verticalTabInput.block = true;
377
+ verticalTabInput.attrJoin("class", "radio");
378
+ verticalTabInput.attrSet("type", "radio");
379
+ tabOpen.map = tabs[i].listItem.map;
380
+ tabOpen.markup = tabs[i].listItem.markup;
381
+ tabText.content = tabs[i].name;
382
+ tabInline.children = [tabText];
383
+ tabOpen.block = true;
384
+ tabClose.block = true;
385
+ tabPanelOpen.block = true;
386
+ tabPanelClose.block = true;
387
+ tabOpen.attrSet(TAB_DATA_ID, tabId);
388
+ tabOpen.attrSet(TAB_DATA_KEY, tabKey);
389
+ tabOpen.attrSet(
390
+ "class",
391
+ [TAB_CLASSNAME, TAB_GROUP_CLASSNAME, VERTICAL_TAB_CLASSNAME].join(" ")
392
+ );
393
+ tabOpen.attrSet("role", "tab");
394
+ tabOpen.attrSet("aria-controls", tabPanelId);
395
+ tabOpen.attrSet("aria-selected", "false");
396
+ tabOpen.attrSet("tabindex", i === 0 ? "0" : "-1");
397
+ tabOpen.attrSet(TAB_ACTIVE_KEY, "false");
398
+ tabPanelOpen.attrSet("id", tabPanelId);
399
+ tabPanelOpen.attrSet("class", TAB_PANEL_CLASSNAME);
400
+ tabPanelOpen.attrSet("role", "tabpanel");
401
+ tabPanelOpen.attrSet("aria-labelledby", tabId);
402
+ tabPanelOpen.attrSet("data-title", tab.name);
403
+ tabOpen.attrSet(TAB_DATA_VERTICAL_TAB, "true");
404
+ if (didTabHasActiveAttr) {
405
+ tabOpen.attrSet(TAB_FORCED_OPEN, "true");
406
+ verticalTabInput.attrSet("checked", "true");
407
+ tabPanelOpen.attrJoin("class", ACTIVE_CLASSNAME);
408
+ }
409
+ tabsTokens.push(
410
+ tabOpen,
411
+ verticalTabInput,
412
+ verticalTabLabelOpen,
413
+ tabInline,
414
+ verticalTabLabelClose,
415
+ tabClose
416
+ );
417
+ tabsTokens.push(tabPanelOpen, ...tabs[i].tokens, tabPanelClose);
390
418
  }
391
419
  tabsTokens.push(tabsClose);
392
- state.tokens.splice(start, end - start + 1, ...tabsTokens);
393
- return tabsTokens.length;
394
- }
395
- function isTabSelected(tab) {
396
- const { name } = tab;
397
- return name.includes(ACTIVE_TAB_TEXT);
420
+ return tabsTokens;
421
+ };
422
+
423
+ // src/plugin/variants/dropdown.ts
424
+ var dropdown = (tabs, state, { containerClasses, tabsGroup, runId }) => {
425
+ var _a, _b;
426
+ const dropdownTokens = [];
427
+ const dropdownOpen = new state.Token("dropdown_open", "div", 1);
428
+ const dropdownClose = new state.Token("dropdown_close", "div", -1);
429
+ const dropdownSelectOpen = new state.Token("dropdown-select_open", "div", 1);
430
+ const dropdownSelectInline = new state.Token("inline", "", 0);
431
+ const dropdownSelectText = new state.Token("text", "", 0);
432
+ const dropdownSelectClose = new state.Token("dropdown-select_open", "div", -1);
433
+ if (tabs.length) {
434
+ const [start] = (_a = tabs[0].listItem.map) != null ? _a : [null];
435
+ if (start == null) {
436
+ throw new Error("failed to parse line mapping");
437
+ }
438
+ const [_, end] = (_b = tabs[tabs.length - 1].listItem.map) != null ? _b : [null, null];
439
+ if (end == null) {
440
+ throw new Error("failed to parse line mapping");
441
+ }
442
+ }
443
+ dropdownOpen.block = true;
444
+ dropdownClose.block = true;
445
+ dropdownSelectOpen.block = true;
446
+ dropdownSelectClose.block = true;
447
+ const [activeTab, ...restActiveTabs] = tabs.filter(isTabSelected);
448
+ if (restActiveTabs.length) {
449
+ throw new Error("Unable to dropdown tabs with more than 1 active element");
450
+ }
451
+ dropdownOpen.attrSet(
452
+ "class",
453
+ [TABS_CLASSNAME, containerClasses, TABS_DROPDOWN_CLASSNAME].filter(Boolean).join(" ")
454
+ );
455
+ dropdownOpen.attrSet(GROUP_DATA_KEY, tabsGroup);
456
+ dropdownOpen.attrSet(TAB_DATA_VARIANT, "dropdown");
457
+ dropdownSelectOpen.attrSet("role", "tablist");
458
+ dropdownSelectOpen.attrSet("class", TABS_DROPDOWN_SELECT);
459
+ if (activeTab) {
460
+ dropdownSelectOpen.attrJoin("class", "filled");
461
+ }
462
+ dropdownSelectText.content = activeTab ? activeTab.name : "-";
463
+ dropdownSelectInline.children = [dropdownSelectText];
464
+ dropdownTokens.push(
465
+ dropdownOpen,
466
+ dropdownSelectOpen,
467
+ dropdownSelectInline,
468
+ dropdownSelectClose
469
+ );
470
+ const dropdownMenuOpen = new state.Token("dropdown-menu_open", "ul", 1);
471
+ const dropdownMenuClose = new state.Token("dropdown-menu_close", "ul", -1);
472
+ dropdownMenuOpen.attrSet("class", TABS_DROPDOWN_MENU_CLASSNAME);
473
+ const menuTokens = tabs.flatMap((tab) => {
474
+ const menuListItemOpen = new state.Token("dropdown-menu-item_open", "li", 1);
475
+ const menuListItemClose = new state.Token("dropdown-menu-item_close", "li", -1);
476
+ const menuListItemText = new state.Token("text", "", 0);
477
+ const menuListItemInline = new state.Token("inline", "", 0);
478
+ const tabId = getTabId(tab, { runId });
479
+ const tabKey = getTabKey(tab);
480
+ const isActive = tab === activeTab;
481
+ menuListItemOpen.attrSet(
482
+ "class",
483
+ [TAB_CLASSNAME, isActive && ACTIVE_CLASSNAME].filter(Boolean).join(" ")
484
+ );
485
+ menuListItemOpen.attrSet(TAB_DATA_ID, tabId);
486
+ menuListItemOpen.attrSet(TAB_DATA_KEY, tabKey);
487
+ menuListItemOpen.attrSet("aria-selected", String(isActive));
488
+ menuListItemText.content = tab.name;
489
+ menuListItemInline.children = [menuListItemText];
490
+ return [menuListItemOpen, menuListItemInline, menuListItemClose];
491
+ });
492
+ dropdownTokens.push(dropdownMenuOpen, ...menuTokens, dropdownMenuClose);
493
+ for (let i = 0; i < tabs.length; i++) {
494
+ const tabPanelOpen = new state.Token("tab-panel_open", "div", 1);
495
+ const tabPanelClose = new state.Token("tab-panel_close", "div", -1);
496
+ const tab = tabs[i];
497
+ const tabId = getTabId(tab, { runId });
498
+ const didTabHasActiveAttr = isTabSelected(tab);
499
+ tab.name = getName(tab);
500
+ const tabPanelId = generateID();
501
+ tabPanelOpen.block = true;
502
+ tabPanelClose.block = true;
503
+ tabPanelOpen.attrSet("id", tabPanelId);
504
+ tabPanelOpen.attrSet("class", TAB_PANEL_CLASSNAME);
505
+ tabPanelOpen.attrSet("role", "tabpanel");
506
+ tabPanelOpen.attrSet("aria-labelledby", tabId);
507
+ tabPanelOpen.attrSet("data-title", tab.name);
508
+ if (didTabHasActiveAttr) {
509
+ tabPanelOpen.attrJoin("class", ACTIVE_CLASSNAME);
510
+ }
511
+ dropdownTokens.push(tabPanelOpen, ...tabs[i].tokens, tabPanelClose);
512
+ }
513
+ dropdownTokens.push(dropdownClose);
514
+ return dropdownTokens;
515
+ };
516
+
517
+ // src/plugin/variants/accordion.ts
518
+ var accordion = (tabs, state, { containerClasses, tabsGroup, runId }) => {
519
+ var _a, _b;
520
+ const tabsTokens = [];
521
+ const tabsOpen = new state.Token("tabs_open", "div", 1);
522
+ const tabsClose = new state.Token("tabs_close", "div", -1);
523
+ if (tabs.length) {
524
+ const [start] = (_a = tabs[0].listItem.map) != null ? _a : [null];
525
+ if (start == null) {
526
+ throw new Error("failed to parse line mapping");
527
+ }
528
+ const [_, end] = (_b = tabs[tabs.length - 1].listItem.map) != null ? _b : [null, null];
529
+ if (end == null) {
530
+ throw new Error("failed to parse line mapping");
531
+ }
532
+ }
533
+ tabsOpen.block = true;
534
+ tabsClose.block = true;
535
+ const activeTabsCount = tabs.filter(isTabSelected).length;
536
+ if (activeTabsCount > 1) {
537
+ throw new Error("Unable to render tabs with more than 1 active element");
538
+ }
539
+ tabsOpen.attrSet(
540
+ "class",
541
+ [TABS_CLASSNAME, containerClasses, TABS_ACCORDION_CLASSNAME].filter(Boolean).join(" ")
542
+ );
543
+ tabsOpen.attrSet(GROUP_DATA_KEY, tabsGroup);
544
+ tabsOpen.attrSet(TAB_DATA_VARIANT, "accordion" /* Accordion */);
545
+ tabsTokens.push(tabsOpen);
546
+ for (let i = 0; i < tabs.length; i++) {
547
+ const tabOpen = new state.Token("tab_open", "div", 1);
548
+ const tabInline = new state.Token("inline", "", 0);
549
+ const tabText = new state.Token("text", "", 0);
550
+ const tabClose = new state.Token("tab_close", "div", -1);
551
+ const tabPanelOpen = new state.Token("tab-panel_open", "div", 1);
552
+ const tabPanelClose = new state.Token("tab-panel_close", "div", -1);
553
+ tabOpen.map = tabs[i].listItem.map;
554
+ tabOpen.markup = tabs[i].listItem.markup;
555
+ const tab = tabs[i];
556
+ const tabId = getTabId(tab, { runId });
557
+ const tabKey = getTabKey(tab);
558
+ const didTabHasActiveAttr = isTabSelected(tab);
559
+ tab.name = getName(tab);
560
+ const tabPanelId = generateID();
561
+ tabOpen.map = tabs[i].listItem.map;
562
+ tabOpen.markup = tabs[i].listItem.markup;
563
+ tabText.content = tabs[i].name;
564
+ tabInline.children = [tabText];
565
+ tabOpen.block = true;
566
+ tabClose.block = true;
567
+ tabPanelOpen.block = true;
568
+ tabPanelClose.block = true;
569
+ tabOpen.attrSet(TAB_DATA_ID, tabId);
570
+ tabOpen.attrSet(TAB_DATA_KEY, tabKey);
571
+ tabOpen.attrSet("class", [TAB_CLASSNAME, TAB_GROUP_CLASSNAME].join(" "));
572
+ tabOpen.attrSet("role", "tab");
573
+ tabOpen.attrSet("aria-controls", tabPanelId);
574
+ tabOpen.attrSet("aria-selected", "false");
575
+ tabOpen.attrSet("tabindex", i === 0 ? "0" : "-1");
576
+ tabOpen.attrSet(TAB_ACTIVE_KEY, "false");
577
+ tabPanelOpen.attrSet("id", tabPanelId);
578
+ tabPanelOpen.attrSet("class", TAB_PANEL_CLASSNAME);
579
+ tabPanelOpen.attrSet("role", "tabpanel");
580
+ tabPanelOpen.attrSet("aria-labelledby", tabId);
581
+ tabPanelOpen.attrSet("data-title", tab.name);
582
+ if (didTabHasActiveAttr) {
583
+ tabPanelOpen.attrSet(TAB_FORCED_OPEN, "true");
584
+ tabPanelOpen.attrJoin("class", ACTIVE_CLASSNAME);
585
+ tabOpen.attrJoin("class", ACTIVE_CLASSNAME);
586
+ }
587
+ tabsTokens.push(tabOpen, tabInline, tabClose);
588
+ tabsTokens.push(tabPanelOpen, ...tabs[i].tokens, tabPanelClose);
589
+ }
590
+ tabsTokens.push(tabsClose);
591
+ return tabsTokens;
592
+ };
593
+
594
+ // src/plugin/variants/index.ts
595
+ var generateByType = {
596
+ regular,
597
+ radio,
598
+ dropdown,
599
+ accordion
600
+ };
601
+ var generateTokensByType = (type) => {
602
+ return generateByType[type];
603
+ };
604
+
605
+ // src/plugin/generate.ts
606
+ function generateTabsTokens(tabs, state, props2) {
607
+ const tokens = generateTokensByType(props2.variant)(tabs, state, props2);
608
+ return tokens;
398
609
  }
399
- function findCloseTokenIdx(tokens, idx) {
610
+
611
+ // src/plugin/find.ts
612
+ var import_token = __toESM(require("markdown-it/lib/token"));
613
+ function findCloseTokenIndex(tokens, idx) {
400
614
  let level = 0;
401
615
  let i = idx;
402
616
  while (i < tokens.length) {
@@ -416,18 +630,14 @@ function matchCloseToken(tokens, i) {
416
630
  return tokens[i].type === "paragraph_open" && tokens[i + 1].type === "inline" && tokens[i + 1].content.trim() === "{% endlist %}";
417
631
  }
418
632
  function matchOpenToken(tokens, i) {
419
- return tokens[i].type === "paragraph_open" && tokens[i + 1].type === "inline" && props(tokens[i + 1].content);
633
+ return tokens[i].type === "paragraph_open" && tokens[i + 1].type === "inline" && tokens[i + 1].content.match(TAB_RE);
420
634
  }
421
- function props(target) {
422
- const matched = target.match(TAB_RE);
423
- if (!matched) {
424
- return void 0;
425
- }
426
- const clean = trim(matched[0].replace("list tabs", ""));
635
+ function props(content) {
636
+ const clean = trim(content.replace("list tabs", ""));
427
637
  const props2 = clean.split(" ");
428
638
  const result = {
429
639
  content: clean,
430
- orientation: "horizontal",
640
+ variant: "regular" /* Regular */,
431
641
  group: `${DEFAULT_TABS_GROUP_PREFIX}${generateID()}`
432
642
  };
433
643
  for (const prop of props2) {
@@ -435,7 +645,9 @@ function props(target) {
435
645
  switch (key) {
436
646
  case "horizontal":
437
647
  case "radio":
438
- result.orientation = key;
648
+ case "dropdown":
649
+ case "accordion":
650
+ result.variant = key;
439
651
  break;
440
652
  case "group":
441
653
  result.group = unquote(value);
@@ -445,11 +657,104 @@ function props(target) {
445
657
  }
446
658
  return result;
447
659
  }
660
+ function tryToFindTabs(tokens, index) {
661
+ const match = matchOpenToken(tokens, index);
662
+ const openTag = match && match[0];
663
+ const isNotEscaped = openTag && !(openTag.startsWith("`") && openTag.endsWith("`"));
664
+ if (!match || !isNotEscaped) {
665
+ return {
666
+ step: 1
667
+ };
668
+ }
669
+ const closeTokenIndex = findCloseTokenIndex(tokens, index + 3);
670
+ if (!closeTokenIndex) {
671
+ tokens[index].attrSet("YFM005", "true");
672
+ return {
673
+ step: 3
674
+ };
675
+ }
676
+ return {
677
+ content: openTag,
678
+ closeTokenIndex
679
+ };
680
+ }
681
+ function findTabs(tokens, idx, closeTokenIdx) {
682
+ const tabs = [];
683
+ let i = idx;
684
+ let nestedLevel = -1;
685
+ let pending = {
686
+ name: "",
687
+ tokens: [],
688
+ listItem: new import_token.default("list_item_open", "", 0)
689
+ };
690
+ while (i < tokens.length) {
691
+ const token = tokens[i];
692
+ switch (token.type) {
693
+ case "ordered_list_open":
694
+ case "bullet_list_open":
695
+ if (nestedLevel > -1) {
696
+ pending.tokens.push(token);
697
+ }
698
+ nestedLevel++;
699
+ break;
700
+ case "list_item_open":
701
+ if (nestedLevel) {
702
+ pending.tokens.push(token);
703
+ } else {
704
+ pending = { name: "", tokens: [], listItem: token };
705
+ }
706
+ break;
707
+ case "list_item_close":
708
+ if (nestedLevel) {
709
+ pending.tokens.push(token);
710
+ } else {
711
+ tabs.push(pending);
712
+ }
713
+ break;
714
+ case "ordered_list_close":
715
+ case "bullet_list_close":
716
+ if (!nestedLevel) {
717
+ return tabs;
718
+ }
719
+ nestedLevel--;
720
+ pending.tokens.push(token);
721
+ break;
722
+ case "paragraph_open":
723
+ if (i === closeTokenIdx && tokens[i + 1].content && tokens[i + 1].content.trim() === "{% endlist %}") {
724
+ if (pending && !nestedLevel) {
725
+ tabs.push(pending);
726
+ }
727
+ return tabs;
728
+ }
729
+ if (!pending.name && tokens[i + 1].type === "inline") {
730
+ pending.name = tokens[i + 1].content;
731
+ i += 2;
732
+ } else {
733
+ pending.tokens.push(token);
734
+ }
735
+ break;
736
+ default:
737
+ pending.tokens.push(token);
738
+ }
739
+ i++;
740
+ }
741
+ return tabs;
742
+ }
743
+
744
+ // src/plugin/transform.ts
745
+ var defaultFeatures = {
746
+ enabledVariants: {
747
+ regular: true,
748
+ radio: true
749
+ }
750
+ };
751
+ var runsCounter = 0;
448
752
  function transform({
449
753
  runtimeJsPath = "_assets/tabs-extension.js",
450
754
  runtimeCssPath = "_assets/tabs-extension.css",
451
755
  containerClasses = "",
452
- bundle = true
756
+ bundle = true,
757
+ features = defaultFeatures
453
758
  } = {}) {
454
759
  return function tabs(md, options) {
455
760
  const { output = "." } = options || {};
@@ -460,37 +765,29 @@ function transform({
460
765
  let i = 0;
461
766
  let tabsAreInserted = false;
462
767
  while (i < tokens.length) {
463
- const match = matchOpenToken(tokens, i);
464
- const openTag = match && match.content;
465
- const isNotEscaped = openTag && !(openTag.startsWith("`") && openTag.endsWith("`"));
466
- if (!match || !isNotEscaped) {
467
- i++;
768
+ const result = tryToFindTabs(tokens, i);
769
+ if ("step" in result) {
770
+ i += result.step;
468
771
  continue;
469
772
  }
470
- const closeTokenIdx = findCloseTokenIdx(tokens, i + 3);
471
- if (!closeTokenIdx) {
472
- tokens[i].attrSet("YFM005", "true");
473
- i += 3;
474
- continue;
773
+ const { content, closeTokenIndex } = result;
774
+ const parsedProps = props(content);
775
+ if (!features.enabledVariants[parsedProps.variant]) {
776
+ parsedProps.variant = "regular" /* Regular */;
475
777
  }
476
- const tabsGroup = match.group;
477
- const { tabs: tabs2 } = findTabs(state.tokens, i + 3, closeTokenIdx);
778
+ const tabs2 = findTabs(state.tokens, i + 3, closeTokenIndex);
478
779
  if (tabs2.length > 0) {
479
- insertTabs(
480
- tabs2,
481
- state,
482
- match.orientation,
483
- { start: i, end: closeTokenIdx + 2 },
484
- {
485
- containerClasses,
486
- tabsGroup,
487
- runId
488
- }
489
- );
780
+ const tabsTokens = generateTabsTokens(tabs2, state, {
781
+ containerClasses,
782
+ tabsGroup: parsedProps.group,
783
+ variant: parsedProps.variant,
784
+ runId
785
+ });
786
+ state.tokens.splice(i, closeTokenIndex - i + 3, ...tabsTokens);
490
787
  i++;
491
788
  tabsAreInserted = true;
492
789
  } else {
493
- state.tokens.splice(i, closeTokenIdx - i + 3);
790
+ state.tokens.splice(i, closeTokenIndex - i + 3);
494
791
  }
495
792
  }
496
793
  if (tabsAreInserted) {