@fsegurai/marked-extended-tabs 15.1.0 → 17.0.0-beta.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/README.custom.md +462 -0
- package/README.md +461 -63
- package/dist/index.cjs +507 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.esm.js +505 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.umd.js +513 -0
- package/dist/index.umd.js.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/renderer.d.ts +8 -0
- package/dist/types/tokenizer.d.ts +10 -0
- package/dist/types/types.d.ts +58 -0
- package/dist/types/utils/constants.d.ts +49 -0
- package/dist/types/utils/inject-styles.d.ts +1 -0
- package/dist/types/utils/props.d.ts +19 -0
- package/package.json +22 -24
- package/src/global.d.ts +14 -0
- package/src/index.ts +40 -0
- package/src/{renderer.js → renderer.ts} +13 -23
- package/src/tokenizer.ts +123 -0
- package/src/types.ts +71 -0
- package/src/{constants.js → utils/constants.ts} +26 -116
- package/src/utils/inject-styles.ts +30 -0
- package/src/utils/props.ts +214 -0
- package/src/index.d.ts +0 -108
- package/src/index.js +0 -44
- package/src/tokenizer.js +0 -93
package/README.custom.md
ADDED
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
<!-- SECTION:CUSTOM_INTRO -->
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
This extension enables you to create interactive tabbed content sections with support for custom icons, animations, and
|
|
5
|
+
nested Markdown content. All tabs are implemented using CSS-only interactions, providing excellent performance and
|
|
6
|
+
accessibility.
|
|
7
|
+
|
|
8
|
+
<!-- SECTION:CUSTOM_TOC_USAGE -->
|
|
9
|
+
|
|
10
|
+
- [Aliases](#aliases)
|
|
11
|
+
- [Configuration Options](#configuration-options)
|
|
12
|
+
- [Animation Types](#animation-types)
|
|
13
|
+
- [Advanced Examples](#advanced-examples)
|
|
14
|
+
|
|
15
|
+
<!-- SECTION:CUSTOM_BASIC_USAGE -->
|
|
16
|
+
**`tabs` is the identifier for the extended tabs block, and `tab` for individual tab items.**
|
|
17
|
+
|
|
18
|
+
<!-- SECTION:CUSTOM_USAGE_EXAMPLE -->
|
|
19
|
+
const exampleMarkdown = `
|
|
20
|
+
::::tabs
|
|
21
|
+
:::tab{label="JavaScript" active="true"}
|
|
22
|
+
\`\`\`javascript
|
|
23
|
+
console.log("Hello from JavaScript!");
|
|
24
|
+
\`\`\`
|
|
25
|
+
:::tabend
|
|
26
|
+
|
|
27
|
+
:::tab{label="Python" icon="🐍"}
|
|
28
|
+
\`\`\`python
|
|
29
|
+
print("Hello from Python!")
|
|
30
|
+
\`\`\`
|
|
31
|
+
:::tabend
|
|
32
|
+
|
|
33
|
+
:::tab{label="HTML" icon="🌐"}
|
|
34
|
+
\`\`\`html
|
|
35
|
+
<h1>Hello from HTML!</h1>
|
|
36
|
+
\`\`\`
|
|
37
|
+
:::tabend
|
|
38
|
+
::::tabsend
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
marked.parse(exampleMarkdown);
|
|
42
|
+
|
|
43
|
+
<!-- SECTION:CUSTOM_USAGE_NOTES -->
|
|
44
|
+
The extension supports **nested Markdown content** within tabs, including code blocks, tables, lists, and even other
|
|
45
|
+
marked extensions. Tabs use a CSS-only implementation for optimal performance and can be styled to match your design
|
|
46
|
+
system.
|
|
47
|
+
|
|
48
|
+
### Styling Your Tabs
|
|
49
|
+
|
|
50
|
+
This extension injects minimal structural CSS for functionality. Visual styling is completely separated for maximum
|
|
51
|
+
flexibility.
|
|
52
|
+
|
|
53
|
+
#### Generated HTML Structure
|
|
54
|
+
|
|
55
|
+
```html
|
|
56
|
+
|
|
57
|
+
<div class="marked-extended-tabs-container" id="tabs-{id}">
|
|
58
|
+
<nav class="marked-extended-tabs-nav" role="tablist">
|
|
59
|
+
<input type="radio" name="tabs-{id}" id="tab-{id}-0" class="marked-extended-tabs-input" checked>
|
|
60
|
+
<label for="tab-{id}-0" class="marked-extended-tabs-label" role="tab">
|
|
61
|
+
<span class="marked-extended-tabs-icon">🔧</span>
|
|
62
|
+
<span class="marked-extended-tabs-label-text">JavaScript</span>
|
|
63
|
+
</label>
|
|
64
|
+
<!-- More tabs... -->
|
|
65
|
+
</nav>
|
|
66
|
+
<div class="marked-extended-tabs-content">
|
|
67
|
+
<div class="marked-extended-tabs-content-pane" role="tabpanel">
|
|
68
|
+
<!-- Tab 1 content -->
|
|
69
|
+
</div>
|
|
70
|
+
<div class="marked-extended-tabs-content-pane" role="tabpanel">
|
|
71
|
+
<!-- Tab 2 content -->
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### CSS Classes Reference
|
|
78
|
+
|
|
79
|
+
| Class | Purpose | Element |
|
|
80
|
+
|-----------------------------------------------------|----------------------|----------------|
|
|
81
|
+
| `.marked-extended-tabs-container` | Main wrapper | Container |
|
|
82
|
+
| `.marked-extended-tabs-nav` | Tab navigation bar | Nav |
|
|
83
|
+
| `.marked-extended-tabs-input` | Hidden radio input | Input (hidden) |
|
|
84
|
+
| `.marked-extended-tabs-label` | Tab button/label | Label |
|
|
85
|
+
| `.marked-extended-tabs-label[aria-selected="true"]` | Active tab | Active Label |
|
|
86
|
+
| `.marked-extended-tabs-icon` | Tab icon | Span |
|
|
87
|
+
| `.marked-extended-tabs-label-text` | Tab text | Span |
|
|
88
|
+
| `.marked-extended-tabs-content` | Content area wrapper | Div |
|
|
89
|
+
| `.marked-extended-tabs-content-pane` | Individual tab pane | Div |
|
|
90
|
+
|
|
91
|
+
#### Complete Styling Example
|
|
92
|
+
|
|
93
|
+
```css
|
|
94
|
+
/* Container */
|
|
95
|
+
.marked-extended-tabs-container {
|
|
96
|
+
border: 1px solid #ddd;
|
|
97
|
+
border-radius: 8px;
|
|
98
|
+
background: #fff;
|
|
99
|
+
overflow: hidden;
|
|
100
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Navigation Bar */
|
|
104
|
+
.marked-extended-tabs-nav {
|
|
105
|
+
display: flex;
|
|
106
|
+
background: #f5f5f5;
|
|
107
|
+
border-bottom: 1px solid #ddd;
|
|
108
|
+
overflow-x: auto;
|
|
109
|
+
scrollbar-width: thin;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Hide radio inputs */
|
|
113
|
+
.marked-extended-tabs-input {
|
|
114
|
+
position: absolute;
|
|
115
|
+
opacity: 0;
|
|
116
|
+
pointer-events: none;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Tab Labels (Buttons) */
|
|
120
|
+
.marked-extended-tabs-label {
|
|
121
|
+
display: flex;
|
|
122
|
+
align-items: center;
|
|
123
|
+
gap: 0.5rem;
|
|
124
|
+
padding: 0.75rem 1.5rem;
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
border-bottom: 2px solid transparent;
|
|
127
|
+
background: transparent;
|
|
128
|
+
transition: all 0.2s ease;
|
|
129
|
+
font-weight: 500;
|
|
130
|
+
color: #666;
|
|
131
|
+
white-space: nowrap;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.marked-extended-tabs-label:hover {
|
|
135
|
+
background: #e8e8e8;
|
|
136
|
+
color: #333;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/* Active Tab */
|
|
140
|
+
.marked-extended-tabs-label[aria-selected="true"] {
|
|
141
|
+
background: #fff;
|
|
142
|
+
border-bottom-color: #0066cc;
|
|
143
|
+
color: #0066cc;
|
|
144
|
+
font-weight: 600;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* Tab Icon */
|
|
148
|
+
.marked-extended-tabs-icon {
|
|
149
|
+
font-size: 1.2rem;
|
|
150
|
+
opacity: 0.8;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/* Content Area */
|
|
154
|
+
.marked-extended-tabs-content {
|
|
155
|
+
padding: 1.5rem;
|
|
156
|
+
background: #fff;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.marked-extended-tabs-content-pane {
|
|
160
|
+
display: none;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Show active pane (controlled by radio input) */
|
|
164
|
+
.marked-extended-tabs-input:checked + .marked-extended-tabs-label + * .marked-extended-tabs-content-pane:nth-of-type(1),
|
|
165
|
+
#tab-id-0:checked ~ .marked-extended-tabs-content .marked-extended-tabs-content-pane:nth-of-type(1) {
|
|
166
|
+
display: block;
|
|
167
|
+
animation: tab-fade-in 0.3s ease;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/* Fade animation */
|
|
171
|
+
@keyframes tab-fade-in {
|
|
172
|
+
from {
|
|
173
|
+
opacity: 0;
|
|
174
|
+
transform: translateY(10px);
|
|
175
|
+
}
|
|
176
|
+
to {
|
|
177
|
+
opacity: 1;
|
|
178
|
+
transform: translateY(0);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Variant: Compact Tabs
|
|
184
|
+
|
|
185
|
+
```css
|
|
186
|
+
.marked-extended-tabs-container.tabs-compact .marked-extended-tabs-label {
|
|
187
|
+
padding: 0.5rem 0.75rem;
|
|
188
|
+
font-size: 0.875rem;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
.marked-extended-tabs-container.tabs-compact .marked-extended-tabs-content {
|
|
192
|
+
padding: 0.75rem;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Variant: Vertical Tabs
|
|
197
|
+
|
|
198
|
+
```css
|
|
199
|
+
.marked-extended-tabs-container.tabs-vertical {
|
|
200
|
+
display: flex;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-nav {
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
min-width: 200px;
|
|
206
|
+
border-right: 1px solid #ddd;
|
|
207
|
+
border-bottom: none;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-label {
|
|
211
|
+
border-bottom: none;
|
|
212
|
+
border-right: 2px solid transparent;
|
|
213
|
+
justify-content: flex-start;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-label[aria-selected="true"] {
|
|
217
|
+
border-right-color: #0066cc;
|
|
218
|
+
border-bottom: none;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.marked-extended-tabs-container.tabs-vertical .marked-extended-tabs-content {
|
|
222
|
+
flex: 1;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### Variant: Pills Style
|
|
227
|
+
|
|
228
|
+
```css
|
|
229
|
+
.marked-extended-tabs-container.tabs-pills .marked-extended-tabs-nav {
|
|
230
|
+
gap: 0.5rem;
|
|
231
|
+
padding: 0.5rem;
|
|
232
|
+
background: #f8f9fa;
|
|
233
|
+
border-bottom: none;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
.marked-extended-tabs-container.tabs-pills .marked-extended-tabs-label {
|
|
237
|
+
border-radius: 20px;
|
|
238
|
+
border: none;
|
|
239
|
+
padding: 0.5rem 1rem;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
.marked-extended-tabs-container.tabs-pills .marked-extended-tabs-label:hover {
|
|
243
|
+
background: #e9ecef;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.marked-extended-tabs-container.tabs-pills .marked-extended-tabs-label[aria-selected="true"] {
|
|
247
|
+
background: #0066cc;
|
|
248
|
+
color: white;
|
|
249
|
+
border: none;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Dark Mode Support
|
|
254
|
+
|
|
255
|
+
```css
|
|
256
|
+
/* Light theme */
|
|
257
|
+
body.light .marked-extended-tabs-container {
|
|
258
|
+
background: #ffffff;
|
|
259
|
+
border-color: #ddd;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
body.light .marked-extended-tabs-nav {
|
|
263
|
+
background: #f5f5f5;
|
|
264
|
+
border-bottom-color: #ddd;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
body.light .marked-extended-tabs-label {
|
|
268
|
+
color: #333;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
body.light .marked-extended-tabs-label:hover {
|
|
272
|
+
background: #e8e8e8;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
body.light .marked-extended-tabs-content {
|
|
276
|
+
background: #ffffff;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/* Dark theme */
|
|
280
|
+
body.dark .marked-extended-tabs-container {
|
|
281
|
+
background: #22272e;
|
|
282
|
+
border-color: #444c56;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
body.dark .marked-extended-tabs-nav {
|
|
286
|
+
background: #2d333b;
|
|
287
|
+
border-bottom-color: #444c56;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
body.dark .marked-extended-tabs-label {
|
|
291
|
+
color: #d1d5da;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
body.dark .marked-extended-tabs-label:hover {
|
|
295
|
+
background: #444c56;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
body.dark .marked-extended-tabs-label[aria-selected="true"] {
|
|
299
|
+
background: #3b4551;
|
|
300
|
+
color: #58a6ff;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
body.dark .marked-extended-tabs-content {
|
|
304
|
+
background: #22272e;
|
|
305
|
+
color: #d1d5da;
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
#### Accessibility Enhancements
|
|
310
|
+
|
|
311
|
+
```css
|
|
312
|
+
/* Focus styles for keyboard navigation */
|
|
313
|
+
.marked-extended-tabs-input:focus + .marked-extended-tabs-label {
|
|
314
|
+
outline: 2px solid #0066cc;
|
|
315
|
+
outline-offset: 2px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Remove outline for mouse users */
|
|
319
|
+
.marked-extended-tabs-input:focus:not(:focus-visible) + .marked-extended-tabs-label {
|
|
320
|
+
outline: none;
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
#### Copy Demo Theme
|
|
325
|
+
|
|
326
|
+
For complete styling with all
|
|
327
|
+
variants: [tabs-theme.css](https://github.com/fsegurai/marked-extensions/blob/main/demo/styles/extensions/tabs-theme.css)
|
|
328
|
+
|
|
329
|
+
Check the [demo](https://fsegurai.github.io/marked-extensions) to see all tab variants in action.
|
|
330
|
+
|
|
331
|
+
<!-- SECTION:CUSTOM_SECTIONS -->
|
|
332
|
+
|
|
333
|
+
### Aliases
|
|
334
|
+
|
|
335
|
+
The `tabs` block can be rendered using alternative aliases for start and end blocks:
|
|
336
|
+
|
|
337
|
+
* Start Aliases
|
|
338
|
+
- `:tbs`
|
|
339
|
+
- `:tabs`
|
|
340
|
+
* End Aliases
|
|
341
|
+
- `:tbsend`
|
|
342
|
+
- `:tabsend`
|
|
343
|
+
|
|
344
|
+
Individual tabs can use these aliases:
|
|
345
|
+
|
|
346
|
+
* Tab Start Aliases
|
|
347
|
+
- `:tab`
|
|
348
|
+
* Tab End Aliases
|
|
349
|
+
- `:tabend`
|
|
350
|
+
|
|
351
|
+
### Configuration Options
|
|
352
|
+
|
|
353
|
+
The marked-extended-tabs extension accepts the following configuration options:
|
|
354
|
+
|
|
355
|
+
- `className`: The base CSS class name for tabs container. Defaults to 'marked-extended-tabs-container'.
|
|
356
|
+
- `persistSelection`: Whether to remember tab selection across page reloads. Defaults to false.
|
|
357
|
+
- `animation`: Animation type for tab transitions. Defaults to 'fade'. See [Animation Types](#animation-types).
|
|
358
|
+
- `autoActivate`: Automatically activate the first tab if none is explicitly active. Defaults to true.
|
|
359
|
+
- `template`: A custom HTML template for the tabs structure. Defaults to the built-in template.
|
|
360
|
+
- `customizeToken`: A function that allows you to customize the token object before rendering. Defaults to null.
|
|
361
|
+
- `injectStyles`: Whether to inject default CSS styles. Defaults to true.
|
|
362
|
+
|
|
363
|
+
Tab syntax parameters:
|
|
364
|
+
|
|
365
|
+
- `label`: The display text for the tab header. Defaults to 'Tab N' where N is the tab number.
|
|
366
|
+
- `active`: Whether this tab is active by default ("true" or "false"). Defaults to "false".
|
|
367
|
+
- `icon`: An optional icon (emoji or Unicode) to display in the tab header. No default.
|
|
368
|
+
|
|
369
|
+
### Animation Types
|
|
370
|
+
|
|
371
|
+
The extension supports three animation types:
|
|
372
|
+
|
|
373
|
+
- `'fade'`: Smooth fade in/out transition between tabs (default)
|
|
374
|
+
- `'slide'`: Horizontal sliding animation between tab contents
|
|
375
|
+
- `'none'`: No animation, instant tab switching
|
|
376
|
+
|
|
377
|
+
Example with custom animation:
|
|
378
|
+
|
|
379
|
+
```javascript
|
|
380
|
+
marked.use(markedExtendedTabs({
|
|
381
|
+
animation: 'slide',
|
|
382
|
+
autoActivate: true
|
|
383
|
+
}));
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Advanced Examples
|
|
387
|
+
|
|
388
|
+
#### Tabs with Icons and Mixed Content
|
|
389
|
+
|
|
390
|
+
```markdown
|
|
391
|
+
::::tabs
|
|
392
|
+
:::tab{label="Overview" icon="📋" active="true"}
|
|
393
|
+
|
|
394
|
+
## Project Overview
|
|
395
|
+
|
|
396
|
+
This project demonstrates the power of **nested Markdown** within tabs.
|
|
397
|
+
|
|
398
|
+
- ✅ Code highlighting
|
|
399
|
+
- ✅ Tables and lists
|
|
400
|
+
- ✅ Images and links
|
|
401
|
+
- ✅ Other extensions
|
|
402
|
+
|
|
403
|
+
[Learn more →](https://example.com)
|
|
404
|
+
:::tabend
|
|
405
|
+
|
|
406
|
+
:::tab{label="API Reference" icon="⚙️"}
|
|
407
|
+
|
|
408
|
+
### REST API Endpoints
|
|
409
|
+
|
|
410
|
+
| Method | Endpoint | Description |
|
|
411
|
+
|--------|----------|-------------|
|
|
412
|
+
| GET | `/api/users` | List all users |
|
|
413
|
+
| POST | `/api/users` | Create user |
|
|
414
|
+
| PUT | `/api/users/{id}` | Update user |
|
|
415
|
+
|
|
416
|
+
> **Note**: All endpoints require authentication.
|
|
417
|
+
:::tabend
|
|
418
|
+
|
|
419
|
+
:::tab{label="Examples" icon="💻"}
|
|
420
|
+
|
|
421
|
+
### Code Examples
|
|
422
|
+
|
|
423
|
+
JavaScript implementation:
|
|
424
|
+
\`\`\`javascript
|
|
425
|
+
const api = new APIClient({
|
|
426
|
+
baseUrl: 'https://api.example.com',
|
|
427
|
+
apiKey: 'your-key-here'
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
const users = await api.get('/users');
|
|
431
|
+
console.log(users);
|
|
432
|
+
\`\`\`
|
|
433
|
+
|
|
434
|
+
Python implementation:
|
|
435
|
+
\`\`\`python
|
|
436
|
+
import requests
|
|
437
|
+
|
|
438
|
+
response = requests.get('https://api.example.com/users')
|
|
439
|
+
users = response.json()
|
|
440
|
+
print(users)
|
|
441
|
+
\`\`\`
|
|
442
|
+
:::tabend
|
|
443
|
+
::::tabsend
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
#### Configuration Example
|
|
447
|
+
|
|
448
|
+
```javascript
|
|
449
|
+
import {marked} from "marked";
|
|
450
|
+
import markedExtendedTabs from "@fsegurai/marked-extended-tabs";
|
|
451
|
+
|
|
452
|
+
marked.use(markedExtendedTabs({
|
|
453
|
+
className: 'my-custom-tabs',
|
|
454
|
+
animation: 'slide',
|
|
455
|
+
persistSelection: true,
|
|
456
|
+
autoActivate: true,
|
|
457
|
+
customizeToken: (token) => {
|
|
458
|
+
// Add custom logic here
|
|
459
|
+
console.log('Processing tabs token:', token);
|
|
460
|
+
}
|
|
461
|
+
}));
|
|
462
|
+
```
|