@fsegurai/marked-extended-tabs 17.0.0-beta.0 → 17.0.0-beta.2
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 +1567 -18
- package/README.md +1561 -18
- package/dist/index.cjs +35 -89
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +35 -89
- package/dist/index.esm.js.map +1 -1
- package/dist/index.umd.js +431 -485
- package/dist/index.umd.js.map +1 -1
- package/dist/styles/README.md +32 -0
- package/dist/styles/tabs-theme.css +40 -0
- package/dist/styles/tabs-theme.scss +37 -0
- package/dist/styles/tabs.css +107 -0
- package/dist/styles/tabs.scss +108 -0
- package/dist/types/types.d.ts +0 -1
- package/dist/types/utils/constants.d.ts +0 -6
- package/dist/types/utils/inject-styles.d.ts +1 -1
- package/package.json +8 -5
- package/src/global.d.ts +0 -14
- package/src/index.ts +0 -40
- package/src/renderer.ts +0 -64
- package/src/tokenizer.ts +0 -123
- package/src/types.ts +0 -71
- package/src/utils/constants.ts +0 -139
- package/src/utils/inject-styles.ts +0 -30
- package/src/utils/props.ts +0 -214
package/README.custom.md
CHANGED
|
@@ -1,44 +1,379 @@
|
|
|
1
1
|
<!-- SECTION:CUSTOM_INTRO -->
|
|
2
|
-
---
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
accessibility.
|
|
3
|
+
## 🎯 Overview
|
|
4
|
+
|
|
5
|
+
The **marked-extended-tabs** extension transforms your Markdown into interactive tabbed content sections with support for custom icons, smooth animations, and nested Markdown content. With a CSS-only implementation for excellent performance and accessibility, it's perfect for code examples, documentation, API references, tutorials, and organizing complex content.
|
|
6
|
+
|
|
7
|
+
### ✨ Key Features
|
|
8
|
+
|
|
9
|
+
- 📑 **Interactive Tabs**: Create multi-tab content sections with easy navigation
|
|
10
|
+
- 🎨 **Custom Icons**: Add emojis or Unicode icons to tab headers
|
|
11
|
+
- ✨ **3 Animation Types**: Fade, slide, or no animation between tabs
|
|
12
|
+
- 🚀 **CSS-Only**: Pure CSS implementation for optimal performance
|
|
13
|
+
- 📝 **Rich Content**: Full Markdown support including code, tables, lists
|
|
14
|
+
- ♿ **Fully Accessible**: ARIA labels, keyboard navigation, semantic HTML
|
|
15
|
+
- 🎯 **Auto-Activation**: Automatically activate first tab
|
|
16
|
+
- 💾 **Persist Selection**: Remember tab choice across sessions
|
|
17
|
+
- 🎨 **Customizable Styles**: Multiple variants (compact, vertical, pills)
|
|
18
|
+
- 🌈 **Dark Mode Ready**: Theme support included
|
|
19
|
+
- 📱 **Responsive**: Mobile-friendly with scrollable tabs
|
|
20
|
+
- 🔧 **Highly Configurable**: Extensive customization options
|
|
21
|
+
|
|
22
|
+
### 🎪 Live Demo
|
|
23
|
+
|
|
24
|
+
Experience all tab styles and animations: [View Demo](https://fsegurai.github.io/marked-extensions)
|
|
25
|
+
|
|
26
|
+
---
|
|
7
27
|
|
|
8
28
|
<!-- SECTION:CUSTOM_TOC_USAGE -->
|
|
9
29
|
|
|
10
|
-
- [
|
|
11
|
-
- [
|
|
30
|
+
- [Quick Start](#quick-start)
|
|
31
|
+
- [Syntax & Usage](#syntax--usage)
|
|
32
|
+
- [Tab Features](#tab-features)
|
|
12
33
|
- [Animation Types](#animation-types)
|
|
34
|
+
- [Configuration Options](#configuration-options)
|
|
35
|
+
- [Use Cases](#use-cases)
|
|
36
|
+
- [Aliases](#aliases)
|
|
13
37
|
- [Advanced Examples](#advanced-examples)
|
|
14
38
|
|
|
15
39
|
<!-- SECTION:CUSTOM_BASIC_USAGE -->
|
|
16
|
-
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### Basic Concept
|
|
44
|
+
|
|
45
|
+
**`tabs` is the identifier for the tabbed container, and `tab` for individual tab items. Each tab can have a label, icon, and active state.**
|
|
17
46
|
|
|
18
47
|
<!-- SECTION:CUSTOM_USAGE_EXAMPLE -->
|
|
19
|
-
|
|
48
|
+
|
|
49
|
+
### Installation
|
|
50
|
+
|
|
51
|
+
Install the package using your preferred package manager:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Using Bun (recommended)
|
|
55
|
+
bun add @fsegurai/marked-extended-tabs
|
|
56
|
+
|
|
57
|
+
# Using npm
|
|
58
|
+
npm install @fsegurai/marked-extended-tabs
|
|
59
|
+
|
|
60
|
+
# Using yarn
|
|
61
|
+
yarn add @fsegurai/marked-extended-tabs
|
|
62
|
+
|
|
63
|
+
# Using pnpm
|
|
64
|
+
pnpm add @fsegurai/marked-extended-tabs
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Basic Implementation
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
import { marked } from 'marked';
|
|
71
|
+
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
72
|
+
|
|
73
|
+
// Import styles (required for functionality)
|
|
74
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
75
|
+
// Optional: Import theme for enhanced visuals
|
|
76
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
77
|
+
|
|
78
|
+
// Register the extension
|
|
79
|
+
marked.use(markedExtendedTabs({
|
|
80
|
+
animation: 'fade',
|
|
81
|
+
autoActivate: true,
|
|
82
|
+
persistSelection: false
|
|
83
|
+
}));
|
|
84
|
+
|
|
85
|
+
// Your markdown content with tabs
|
|
86
|
+
const markdown = `
|
|
87
|
+
# API Documentation
|
|
88
|
+
|
|
89
|
+
## Quick Start Guide
|
|
90
|
+
|
|
20
91
|
::::tabs
|
|
21
|
-
:::tab{label="JavaScript" active="true"}
|
|
92
|
+
:::tab{label="JavaScript" icon="📜" active="true"}
|
|
93
|
+
## Installation
|
|
94
|
+
|
|
95
|
+
\`\`\`bash
|
|
96
|
+
npm install @myapi/client
|
|
97
|
+
\`\`\`
|
|
98
|
+
|
|
99
|
+
## Basic Usage
|
|
100
|
+
|
|
22
101
|
\`\`\`javascript
|
|
23
|
-
|
|
102
|
+
import { APIClient } from '@myapi/client';
|
|
103
|
+
|
|
104
|
+
const client = new APIClient({
|
|
105
|
+
apiKey: 'your-api-key',
|
|
106
|
+
baseURL: 'https://api.example.com'
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Fetch data
|
|
110
|
+
const users = await client.users.list();
|
|
111
|
+
console.log(users);
|
|
112
|
+
|
|
113
|
+
// Create a resource
|
|
114
|
+
const newUser = await client.users.create({
|
|
115
|
+
name: 'John Doe',
|
|
116
|
+
email: 'john@example.com'
|
|
117
|
+
});
|
|
24
118
|
\`\`\`
|
|
119
|
+
|
|
120
|
+
**Key Features:**
|
|
121
|
+
- ✅ TypeScript support
|
|
122
|
+
- ✅ Promise-based API
|
|
123
|
+
- ✅ Automatic retries
|
|
124
|
+
- ✅ Request/response interceptors
|
|
25
125
|
:::tabend
|
|
26
126
|
|
|
27
127
|
:::tab{label="Python" icon="🐍"}
|
|
128
|
+
## Installation
|
|
129
|
+
|
|
130
|
+
\`\`\`bash
|
|
131
|
+
pip install myapi-client
|
|
132
|
+
\`\`\`
|
|
133
|
+
|
|
134
|
+
## Basic Usage
|
|
135
|
+
|
|
28
136
|
\`\`\`python
|
|
29
|
-
|
|
137
|
+
from myapi import APIClient
|
|
138
|
+
|
|
139
|
+
# Initialize client
|
|
140
|
+
client = APIClient(
|
|
141
|
+
api_key='your-api-key',
|
|
142
|
+
base_url='https://api.example.com'
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Fetch data
|
|
146
|
+
users = client.users.list()
|
|
147
|
+
print(users)
|
|
148
|
+
|
|
149
|
+
# Create a resource
|
|
150
|
+
new_user = client.users.create(
|
|
151
|
+
name='John Doe',
|
|
152
|
+
email='john@example.com'
|
|
153
|
+
)
|
|
154
|
+
\`\`\`
|
|
155
|
+
|
|
156
|
+
**Key Features:**
|
|
157
|
+
- ✅ Type hints included
|
|
158
|
+
- ✅ Async/await support
|
|
159
|
+
- ✅ Automatic pagination
|
|
160
|
+
- ✅ Built-in error handling
|
|
161
|
+
:::tabend
|
|
162
|
+
|
|
163
|
+
:::tab{label="cURL" icon="🔧"}
|
|
164
|
+
## Authentication
|
|
165
|
+
|
|
166
|
+
Add your API key to the request headers:
|
|
167
|
+
|
|
168
|
+
\`\`\`bash
|
|
169
|
+
curl -H "Authorization: Bearer YOUR_API_KEY" \\
|
|
170
|
+
https://api.example.com/users
|
|
171
|
+
\`\`\`
|
|
172
|
+
|
|
173
|
+
## List Users
|
|
174
|
+
|
|
175
|
+
\`\`\`bash
|
|
176
|
+
curl -X GET https://api.example.com/users \\
|
|
177
|
+
-H "Authorization: Bearer YOUR_API_KEY" \\
|
|
178
|
+
-H "Content-Type: application/json"
|
|
179
|
+
\`\`\`
|
|
180
|
+
|
|
181
|
+
## Create User
|
|
182
|
+
|
|
183
|
+
\`\`\`bash
|
|
184
|
+
curl -X POST https://api.example.com/users \\
|
|
185
|
+
-H "Authorization: Bearer YOUR_API_KEY" \\
|
|
186
|
+
-H "Content-Type: application/json" \\
|
|
187
|
+
-d '{
|
|
188
|
+
"name": "John Doe",
|
|
189
|
+
"email": "john@example.com"
|
|
190
|
+
}'
|
|
191
|
+
\`\`\`
|
|
192
|
+
|
|
193
|
+
**Response:**
|
|
194
|
+
\`\`\`json
|
|
195
|
+
{
|
|
196
|
+
"id": "usr_123",
|
|
197
|
+
"name": "John Doe",
|
|
198
|
+
"email": "john@example.com",
|
|
199
|
+
"created_at": "2026-02-17T10:00:00Z"
|
|
200
|
+
}
|
|
30
201
|
\`\`\`
|
|
31
202
|
:::tabend
|
|
32
203
|
|
|
33
|
-
:::tab{label="
|
|
34
|
-
|
|
35
|
-
|
|
204
|
+
:::tab{label="Ruby" icon="💎"}
|
|
205
|
+
## Installation
|
|
206
|
+
|
|
207
|
+
\`\`\`bash
|
|
208
|
+
gem install myapi-client
|
|
209
|
+
\`\`\`
|
|
210
|
+
|
|
211
|
+
## Basic Usage
|
|
212
|
+
|
|
213
|
+
\`\`\`ruby
|
|
214
|
+
require 'myapi'
|
|
215
|
+
|
|
216
|
+
# Initialize client
|
|
217
|
+
client = MyAPI::Client.new(
|
|
218
|
+
api_key: 'your-api-key',
|
|
219
|
+
base_url: 'https://api.example.com'
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# Fetch data
|
|
223
|
+
users = client.users.list
|
|
224
|
+
puts users
|
|
225
|
+
|
|
226
|
+
# Create a resource
|
|
227
|
+
new_user = client.users.create(
|
|
228
|
+
name: 'John Doe',
|
|
229
|
+
email: 'john@example.com'
|
|
230
|
+
)
|
|
36
231
|
\`\`\`
|
|
232
|
+
|
|
233
|
+
**Key Features:**
|
|
234
|
+
- ✅ Ruby 3.0+ compatible
|
|
235
|
+
- ✅ ActiveRecord-style interface
|
|
236
|
+
- ✅ Comprehensive test suite
|
|
237
|
+
- ✅ Webhook support
|
|
37
238
|
:::tabend
|
|
38
239
|
::::tabsend
|
|
240
|
+
|
|
241
|
+
## Response Format
|
|
242
|
+
|
|
243
|
+
All API responses follow this structure:
|
|
244
|
+
|
|
245
|
+
\`\`\`json
|
|
246
|
+
{
|
|
247
|
+
"data": { /* Your resource */ },
|
|
248
|
+
"meta": {
|
|
249
|
+
"timestamp": "2026-02-17T10:00:00Z",
|
|
250
|
+
"version": "v2.0"
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
\`\`\`
|
|
39
254
|
`;
|
|
40
255
|
|
|
41
|
-
|
|
256
|
+
// Parse and render
|
|
257
|
+
const html = marked.parse(markdown);
|
|
258
|
+
document.getElementById('content').innerHTML = html;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Syntax & Usage
|
|
262
|
+
|
|
263
|
+
#### Basic Tabs
|
|
264
|
+
|
|
265
|
+
Create simple tabs with labels:
|
|
266
|
+
|
|
267
|
+
```markdown
|
|
268
|
+
::::tabs
|
|
269
|
+
:::tab{label="Tab 1" active="true"}
|
|
270
|
+
Content for tab 1
|
|
271
|
+
:::tabend
|
|
272
|
+
|
|
273
|
+
:::tab{label="Tab 2"}
|
|
274
|
+
Content for tab 2
|
|
275
|
+
:::tabend
|
|
276
|
+
|
|
277
|
+
:::tab{label="Tab 3"}
|
|
278
|
+
Content for tab 3
|
|
279
|
+
:::tabend
|
|
280
|
+
::::tabsend
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Tabs with Icons
|
|
284
|
+
|
|
285
|
+
Add emojis or Unicode icons:
|
|
286
|
+
|
|
287
|
+
```markdown
|
|
288
|
+
::::tabs
|
|
289
|
+
:::tab{label="JavaScript" icon="📜" active="true"}
|
|
290
|
+
JavaScript code here
|
|
291
|
+
:::tabend
|
|
292
|
+
|
|
293
|
+
:::tab{label="Python" icon="🐍"}
|
|
294
|
+
Python code here
|
|
295
|
+
:::tabend
|
|
296
|
+
|
|
297
|
+
:::tab{label="Ruby" icon="💎"}
|
|
298
|
+
Ruby code here
|
|
299
|
+
:::tabend
|
|
300
|
+
::::tabsend
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### Auto-Active First Tab
|
|
304
|
+
|
|
305
|
+
When no tab has `active="true"`, the first tab activates automatically (if `autoActivate: true`):
|
|
306
|
+
|
|
307
|
+
```markdown
|
|
308
|
+
::::tabs
|
|
309
|
+
:::tab{label="Overview"}
|
|
310
|
+
First tab (auto-active)
|
|
311
|
+
:::tabend
|
|
312
|
+
|
|
313
|
+
:::tab{label="Details"}
|
|
314
|
+
Second tab
|
|
315
|
+
:::tabend
|
|
316
|
+
::::tabsend
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
#### Rich Content in Tabs
|
|
320
|
+
|
|
321
|
+
Tabs support full Markdown:
|
|
322
|
+
|
|
323
|
+
```markdown
|
|
324
|
+
::::tabs
|
|
325
|
+
:::tab{label="Documentation" icon="📖" active="true"}
|
|
326
|
+
# Getting Started
|
|
327
|
+
|
|
328
|
+
Follow these steps:
|
|
329
|
+
|
|
330
|
+
1. Install the package
|
|
331
|
+
2. Import the library
|
|
332
|
+
3. Initialize the client
|
|
333
|
+
|
|
334
|
+
## Code Example
|
|
335
|
+
|
|
336
|
+
\`\`\`javascript
|
|
337
|
+
import { Client } from 'mylib';
|
|
338
|
+
const client = new Client();
|
|
339
|
+
\`\`\`
|
|
340
|
+
|
|
341
|
+
**Important**: Don't forget to set your API key!
|
|
342
|
+
|
|
343
|
+
[Read more →](https://docs.example.com)
|
|
344
|
+
:::tabend
|
|
345
|
+
|
|
346
|
+
:::tab{label="FAQ" icon="❓"}
|
|
347
|
+
## Frequently Asked Questions
|
|
348
|
+
|
|
349
|
+
### How do I authenticate?
|
|
350
|
+
|
|
351
|
+
Use your API key in the Authorization header.
|
|
352
|
+
|
|
353
|
+
### What's the rate limit?
|
|
354
|
+
|
|
355
|
+
- Free tier: 100 requests/hour
|
|
356
|
+
- Pro tier: 10,000 requests/hour
|
|
357
|
+
- Enterprise: Custom limits
|
|
358
|
+
:::tabend
|
|
359
|
+
::::tabsend
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
#### Short Aliases
|
|
363
|
+
|
|
364
|
+
Use shorter syntax:
|
|
365
|
+
|
|
366
|
+
```markdown
|
|
367
|
+
:tabs
|
|
368
|
+
:tab{label="Tab 1"}
|
|
369
|
+
Content here
|
|
370
|
+
:tabend
|
|
371
|
+
|
|
372
|
+
:tab{label="Tab 2"}
|
|
373
|
+
More content
|
|
374
|
+
:tabend
|
|
375
|
+
:tabsend
|
|
376
|
+
```
|
|
42
377
|
|
|
43
378
|
<!-- SECTION:CUSTOM_USAGE_NOTES -->
|
|
44
379
|
The extension supports **nested Markdown content** within tabs, including code blocks, tables, lists, and even other
|
|
@@ -47,8 +382,22 @@ system.
|
|
|
47
382
|
|
|
48
383
|
### Styling Your Tabs
|
|
49
384
|
|
|
50
|
-
This extension
|
|
51
|
-
|
|
385
|
+
This extension provides optional CSS/SCSS files located in the `styles/` directory. Import them into your project to style the tabs.
|
|
386
|
+
|
|
387
|
+
Usage examples:
|
|
388
|
+
|
|
389
|
+
```javascript
|
|
390
|
+
// JS/TS
|
|
391
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
392
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
```html
|
|
396
|
+
<!-- Plain HTML -->
|
|
397
|
+
<link rel="stylesheet" href="node_modules/@fsegurai/marked-extended-tabs/styles/tabs.css">
|
|
398
|
+
<link rel="stylesheet" href="node_modules/@fsegurai/marked-extended-tabs/styles/tabs-theme.css">
|
|
399
|
+
```
|
|
400
|
+
|
|
52
401
|
|
|
53
402
|
#### Generated HTML Structure
|
|
54
403
|
|
|
@@ -358,7 +707,6 @@ The marked-extended-tabs extension accepts the following configuration options:
|
|
|
358
707
|
- `autoActivate`: Automatically activate the first tab if none is explicitly active. Defaults to true.
|
|
359
708
|
- `template`: A custom HTML template for the tabs structure. Defaults to the built-in template.
|
|
360
709
|
- `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
710
|
|
|
363
711
|
Tab syntax parameters:
|
|
364
712
|
|
|
@@ -460,3 +808,1204 @@ marked.use(markedExtendedTabs({
|
|
|
460
808
|
}
|
|
461
809
|
}));
|
|
462
810
|
```
|
|
811
|
+
|
|
812
|
+
### Tab Features Summary
|
|
813
|
+
|
|
814
|
+
The extension provides comprehensive tab functionality:
|
|
815
|
+
|
|
816
|
+
| Feature | Description | Example | Default |
|
|
817
|
+
|---------|-------------|---------|---------|
|
|
818
|
+
| **Labels** | Tab header text | `label="Overview"` | "Tab N" |
|
|
819
|
+
| **Icons** | Emoji or Unicode | `icon="📋"` | None |
|
|
820
|
+
| **Active State** | Default active tab | `active="true"` | First tab (if autoActivate) |
|
|
821
|
+
| **Animations** | Transition effects | `animation="fade"` | "fade" |
|
|
822
|
+
| **Persist Selection** | Remember choice | `persistSelection: true` | false |
|
|
823
|
+
| **Auto-Activate** | First tab active | `autoActivate: true` | true |
|
|
824
|
+
| **Rich Content** | Full Markdown | All Markdown syntax | Supported |
|
|
825
|
+
| **Nested Extensions** | Other extensions | Accordions, tables, etc. | Supported |
|
|
826
|
+
|
|
827
|
+
### Best Practices
|
|
828
|
+
|
|
829
|
+
#### 1. **Use Descriptive Labels**
|
|
830
|
+
|
|
831
|
+
```markdown
|
|
832
|
+
<!-- Good: Clear, descriptive labels -->
|
|
833
|
+
::::tabs
|
|
834
|
+
:::tab{label="Installation Guide"}
|
|
835
|
+
:::tab{label="Configuration Options"}
|
|
836
|
+
:::tab{label="API Reference"}
|
|
837
|
+
::::tabsend
|
|
838
|
+
|
|
839
|
+
<!-- Avoid: Vague labels -->
|
|
840
|
+
::::tabs
|
|
841
|
+
:::tab{label="Info"}
|
|
842
|
+
:::tab{label="More"}
|
|
843
|
+
:::tab{label="Other"}
|
|
844
|
+
::::tabsend
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
#### 2. **Appropriate Icon Usage**
|
|
848
|
+
|
|
849
|
+
```markdown
|
|
850
|
+
<!-- Good: Relevant icons -->
|
|
851
|
+
:::tab{label="JavaScript" icon="📜"}
|
|
852
|
+
:::tab{label="Python" icon="🐍"}
|
|
853
|
+
:::tab{label="Ruby" icon="💎"}
|
|
854
|
+
|
|
855
|
+
<!-- Avoid: Confusing or irrelevant -->
|
|
856
|
+
:::tab{label="JavaScript" icon="🌮"} <!-- ❌ -->
|
|
857
|
+
:::tab{label="Python" icon="🚗"} <!-- ❌ -->
|
|
858
|
+
```
|
|
859
|
+
|
|
860
|
+
#### 3. **Limit Number of Tabs**
|
|
861
|
+
|
|
862
|
+
```markdown
|
|
863
|
+
<!-- Good: 3-7 tabs for easy navigation -->
|
|
864
|
+
::::tabs
|
|
865
|
+
:::tab{label="Overview"}
|
|
866
|
+
:::tab{label="Installation"}
|
|
867
|
+
:::tab{label="Usage"}
|
|
868
|
+
:::tab{label="Examples"}
|
|
869
|
+
::::tabsend
|
|
870
|
+
|
|
871
|
+
<!-- Avoid: Too many tabs -->
|
|
872
|
+
::::tabs
|
|
873
|
+
<!-- 15+ tabs make navigation difficult -->
|
|
874
|
+
::::tabsend
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
#### 4. **Consistent Content Structure**
|
|
878
|
+
|
|
879
|
+
```markdown
|
|
880
|
+
<!-- Good: Similar structure across tabs -->
|
|
881
|
+
::::tabs
|
|
882
|
+
:::tab{label="JavaScript" icon="📜"}
|
|
883
|
+
## Installation
|
|
884
|
+
\`\`\`bash
|
|
885
|
+
npm install
|
|
886
|
+
\`\`\`
|
|
887
|
+
## Usage
|
|
888
|
+
\`\`\`javascript
|
|
889
|
+
// code
|
|
890
|
+
\`\`\`
|
|
891
|
+
:::tabend
|
|
892
|
+
|
|
893
|
+
:::tab{label="Python" icon="🐍"}
|
|
894
|
+
## Installation
|
|
895
|
+
\`\`\`bash
|
|
896
|
+
pip install
|
|
897
|
+
\`\`\`
|
|
898
|
+
## Usage
|
|
899
|
+
\`\`\`python
|
|
900
|
+
# code
|
|
901
|
+
\`\`\`
|
|
902
|
+
:::tabend
|
|
903
|
+
::::tabsend
|
|
904
|
+
|
|
905
|
+
<!-- Avoid: Inconsistent structure -->
|
|
906
|
+
::::tabs
|
|
907
|
+
:::tab{label="Tab 1"}
|
|
908
|
+
# Heading
|
|
909
|
+
Content
|
|
910
|
+
:::tabend
|
|
911
|
+
|
|
912
|
+
:::tab{label="Tab 2"}
|
|
913
|
+
Just some text without headings or structure
|
|
914
|
+
:::tabend
|
|
915
|
+
::::tabsend
|
|
916
|
+
```
|
|
917
|
+
|
|
918
|
+
#### 5. **Mark Appropriate Default Tab**
|
|
919
|
+
|
|
920
|
+
```markdown
|
|
921
|
+
<!-- Good: First tab or most relevant active -->
|
|
922
|
+
::::tabs
|
|
923
|
+
:::tab{label="Quick Start" icon="⚡" active="true"}
|
|
924
|
+
Most users start here
|
|
925
|
+
:::tabend
|
|
926
|
+
|
|
927
|
+
:::tab{label="Advanced"}
|
|
928
|
+
For experienced users
|
|
929
|
+
:::tabend
|
|
930
|
+
::::tabsend
|
|
931
|
+
|
|
932
|
+
<!-- Avoid: Marking wrong tab as active -->
|
|
933
|
+
::::tabs
|
|
934
|
+
:::tab{label="Quick Start"}
|
|
935
|
+
:::tabend
|
|
936
|
+
|
|
937
|
+
:::tab{label="Advanced Setup" active="true"}
|
|
938
|
+
<!-- ❌ New users shouldn't start here -->
|
|
939
|
+
:::tabend
|
|
940
|
+
::::tabsend
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### Use Cases
|
|
944
|
+
|
|
945
|
+
#### 1. **Multi-Language Code Examples**
|
|
946
|
+
|
|
947
|
+
```markdown
|
|
948
|
+
# Authentication API
|
|
949
|
+
|
|
950
|
+
## Making Your First Request
|
|
951
|
+
|
|
952
|
+
::::tabs
|
|
953
|
+
:::tab{label="JavaScript" icon="📜" active="true"}
|
|
954
|
+
### Using Fetch API
|
|
955
|
+
|
|
956
|
+
\`\`\`javascript
|
|
957
|
+
const response = await fetch('https://api.example.com/auth/login', {
|
|
958
|
+
method: 'POST',
|
|
959
|
+
headers: {
|
|
960
|
+
'Content-Type': 'application/json'
|
|
961
|
+
},
|
|
962
|
+
body: JSON.stringify({
|
|
963
|
+
username: 'user@example.com',
|
|
964
|
+
password: 'secure_password'
|
|
965
|
+
})
|
|
966
|
+
});
|
|
967
|
+
|
|
968
|
+
const data = await response.json();
|
|
969
|
+
console.log('Token:', data.access_token);
|
|
970
|
+
|
|
971
|
+
// Use token in subsequent requests
|
|
972
|
+
const userData = await fetch('https://api.example.com/user/profile', {
|
|
973
|
+
headers: {
|
|
974
|
+
'Authorization': \`Bearer \${data.access_token}\`
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
\`\`\`
|
|
978
|
+
|
|
979
|
+
### Using Axios
|
|
980
|
+
|
|
981
|
+
\`\`\`javascript
|
|
982
|
+
import axios from 'axios';
|
|
983
|
+
|
|
984
|
+
const { data } = await axios.post('https://api.example.com/auth/login', {
|
|
985
|
+
username: 'user@example.com',
|
|
986
|
+
password: 'secure_password'
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
console.log('Token:', data.access_token);
|
|
990
|
+
|
|
991
|
+
// Set default authorization header
|
|
992
|
+
axios.defaults.headers.common['Authorization'] = \`Bearer \${data.access_token}\`;
|
|
993
|
+
\`\`\`
|
|
994
|
+
:::tabend
|
|
995
|
+
|
|
996
|
+
:::tab{label="Python" icon="🐍"}
|
|
997
|
+
### Using Requests
|
|
998
|
+
|
|
999
|
+
\`\`\`python
|
|
1000
|
+
import requests
|
|
1001
|
+
|
|
1002
|
+
# Login request
|
|
1003
|
+
response = requests.post(
|
|
1004
|
+
'https://api.example.com/auth/login',
|
|
1005
|
+
json={
|
|
1006
|
+
'username': 'user@example.com',
|
|
1007
|
+
'password': 'secure_password'
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
data = response.json()
|
|
1012
|
+
access_token = data['access_token']
|
|
1013
|
+
print(f'Token: {access_token}')
|
|
1014
|
+
|
|
1015
|
+
# Use token for authenticated requests
|
|
1016
|
+
user_response = requests.get(
|
|
1017
|
+
'https://api.example.com/user/profile',
|
|
1018
|
+
headers={'Authorization': f'Bearer {access_token}'}
|
|
1019
|
+
)
|
|
1020
|
+
|
|
1021
|
+
print(user_response.json())
|
|
1022
|
+
\`\`\`
|
|
1023
|
+
|
|
1024
|
+
### Using HTTPX (Async)
|
|
1025
|
+
|
|
1026
|
+
\`\`\`python
|
|
1027
|
+
import httpx
|
|
1028
|
+
import asyncio
|
|
1029
|
+
|
|
1030
|
+
async def authenticate():
|
|
1031
|
+
async with httpx.AsyncClient() as client:
|
|
1032
|
+
# Login
|
|
1033
|
+
response = await client.post(
|
|
1034
|
+
'https://api.example.com/auth/login',
|
|
1035
|
+
json={
|
|
1036
|
+
'username': 'user@example.com',
|
|
1037
|
+
'password': 'secure_password'
|
|
1038
|
+
}
|
|
1039
|
+
)
|
|
1040
|
+
|
|
1041
|
+
data = response.json()
|
|
1042
|
+
return data['access_token']
|
|
1043
|
+
|
|
1044
|
+
# Run async function
|
|
1045
|
+
token = asyncio.run(authenticate())
|
|
1046
|
+
\`\`\`
|
|
1047
|
+
:::tabend
|
|
1048
|
+
|
|
1049
|
+
:::tab{label="Go" icon="🔷"}
|
|
1050
|
+
### Using net/http
|
|
1051
|
+
|
|
1052
|
+
\`\`\`go
|
|
1053
|
+
package main
|
|
1054
|
+
|
|
1055
|
+
import (
|
|
1056
|
+
"bytes"
|
|
1057
|
+
"encoding/json"
|
|
1058
|
+
"fmt"
|
|
1059
|
+
"io/ioutil"
|
|
1060
|
+
"net/http"
|
|
1061
|
+
)
|
|
1062
|
+
|
|
1063
|
+
type LoginRequest struct {
|
|
1064
|
+
Username string \`json:"username"\`
|
|
1065
|
+
Password string \`json:"password"\`
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
type LoginResponse struct {
|
|
1069
|
+
AccessToken string \`json:"access_token"\`
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
func main() {
|
|
1073
|
+
// Prepare login request
|
|
1074
|
+
loginData := LoginRequest{
|
|
1075
|
+
Username: "user@example.com",
|
|
1076
|
+
Password: "secure_password",
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
jsonData, _ := json.Marshal(loginData)
|
|
1080
|
+
|
|
1081
|
+
// Make request
|
|
1082
|
+
resp, err := http.Post(
|
|
1083
|
+
"https://api.example.com/auth/login",
|
|
1084
|
+
"application/json",
|
|
1085
|
+
bytes.NewBuffer(jsonData),
|
|
1086
|
+
)
|
|
1087
|
+
if err != nil {
|
|
1088
|
+
panic(err)
|
|
1089
|
+
}
|
|
1090
|
+
defer resp.Body.Close()
|
|
1091
|
+
|
|
1092
|
+
// Parse response
|
|
1093
|
+
body, _ := ioutil.ReadAll(resp.Body)
|
|
1094
|
+
var loginResp LoginResponse
|
|
1095
|
+
json.Unmarshal(body, &loginResp)
|
|
1096
|
+
|
|
1097
|
+
fmt.Println("Token:", loginResp.AccessToken)
|
|
1098
|
+
}
|
|
1099
|
+
\`\`\`
|
|
1100
|
+
:::tabend
|
|
1101
|
+
|
|
1102
|
+
:::tab{label="cURL" icon="🔧"}
|
|
1103
|
+
### Basic Authentication Request
|
|
1104
|
+
|
|
1105
|
+
\`\`\`bash
|
|
1106
|
+
curl -X POST https://api.example.com/auth/login \\
|
|
1107
|
+
-H "Content-Type: application/json" \\
|
|
1108
|
+
-d '{
|
|
1109
|
+
"username": "user@example.com",
|
|
1110
|
+
"password": "secure_password"
|
|
1111
|
+
}'
|
|
1112
|
+
\`\`\`
|
|
1113
|
+
|
|
1114
|
+
**Response:**
|
|
1115
|
+
\`\`\`json
|
|
1116
|
+
{
|
|
1117
|
+
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
|
1118
|
+
"token_type": "Bearer",
|
|
1119
|
+
"expires_in": 3600
|
|
1120
|
+
}
|
|
1121
|
+
\`\`\`
|
|
1122
|
+
|
|
1123
|
+
### Using the Token
|
|
1124
|
+
|
|
1125
|
+
\`\`\`bash
|
|
1126
|
+
# Save token to variable
|
|
1127
|
+
TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
1128
|
+
|
|
1129
|
+
# Make authenticated request
|
|
1130
|
+
curl -X GET https://api.example.com/user/profile \\
|
|
1131
|
+
-H "Authorization: Bearer \$TOKEN"
|
|
1132
|
+
\`\`\`
|
|
1133
|
+
:::tabend
|
|
1134
|
+
::::tabsend
|
|
1135
|
+
|
|
1136
|
+
## Error Handling
|
|
1137
|
+
|
|
1138
|
+
All endpoints return standard error responses:
|
|
1139
|
+
|
|
1140
|
+
\`\`\`json
|
|
1141
|
+
{
|
|
1142
|
+
"error": {
|
|
1143
|
+
"code": "INVALID_CREDENTIALS",
|
|
1144
|
+
"message": "The username or password is incorrect",
|
|
1145
|
+
"timestamp": "2026-02-17T10:30:00Z"
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
\`\`\`
|
|
1149
|
+
```
|
|
1150
|
+
|
|
1151
|
+
#### 2. **Platform-Specific Installation**
|
|
1152
|
+
|
|
1153
|
+
```markdown
|
|
1154
|
+
# Installation Guide
|
|
1155
|
+
|
|
1156
|
+
## Choose Your Platform
|
|
1157
|
+
|
|
1158
|
+
::::tabs
|
|
1159
|
+
:::tab{label="Windows" icon="🪟" active="true"}
|
|
1160
|
+
### Using Installer
|
|
1161
|
+
|
|
1162
|
+
1. **Download** the Windows installer:
|
|
1163
|
+
- [Download for Windows (64-bit)](https://example.com/download/windows-x64)
|
|
1164
|
+
- [Download for Windows (32-bit)](https://example.com/download/windows-x86)
|
|
1165
|
+
|
|
1166
|
+
2. **Run** the installer and follow the setup wizard
|
|
1167
|
+
|
|
1168
|
+
3. **Verify** installation:
|
|
1169
|
+
\`\`\`powershell
|
|
1170
|
+
myapp --version
|
|
1171
|
+
\`\`\`
|
|
1172
|
+
|
|
1173
|
+
### Using Package Manager (Chocolatey)
|
|
1174
|
+
|
|
1175
|
+
\`\`\`powershell
|
|
1176
|
+
choco install myapp
|
|
1177
|
+
\`\`\`
|
|
1178
|
+
|
|
1179
|
+
### Using Package Manager (Winget)
|
|
1180
|
+
|
|
1181
|
+
\`\`\`powershell
|
|
1182
|
+
winget install MyCompany.MyApp
|
|
1183
|
+
\`\`\`
|
|
1184
|
+
|
|
1185
|
+
### System Requirements
|
|
1186
|
+
|
|
1187
|
+
- Windows 10 or later
|
|
1188
|
+
- 4GB RAM minimum (8GB recommended)
|
|
1189
|
+
- 500MB free disk space
|
|
1190
|
+
- .NET Runtime 6.0 or later
|
|
1191
|
+
|
|
1192
|
+
### Troubleshooting
|
|
1193
|
+
|
|
1194
|
+
**"Command not found" error:**
|
|
1195
|
+
Add installation directory to PATH:
|
|
1196
|
+
1. Open System Properties → Environment Variables
|
|
1197
|
+
2. Edit PATH variable
|
|
1198
|
+
3. Add: \`C:\\Program Files\\MyApp\\bin\`
|
|
1199
|
+
:::tabend
|
|
1200
|
+
|
|
1201
|
+
:::tab{label="macOS" icon="🍎"}
|
|
1202
|
+
### Using Homebrew (Recommended)
|
|
1203
|
+
|
|
1204
|
+
\`\`\`bash
|
|
1205
|
+
brew install myapp
|
|
1206
|
+
\`\`\`
|
|
1207
|
+
|
|
1208
|
+
### Using DMG Installer
|
|
1209
|
+
|
|
1210
|
+
1. **Download** the macOS installer:
|
|
1211
|
+
- [Download for macOS (Apple Silicon)](https://example.com/download/macos-arm64)
|
|
1212
|
+
- [Download for macOS (Intel)](https://example.com/download/macos-x64)
|
|
1213
|
+
|
|
1214
|
+
2. **Open** the .dmg file
|
|
1215
|
+
|
|
1216
|
+
3. **Drag** MyApp to Applications folder
|
|
1217
|
+
|
|
1218
|
+
4. **Verify** installation:
|
|
1219
|
+
\`\`\`bash
|
|
1220
|
+
myapp --version
|
|
1221
|
+
\`\`\`
|
|
1222
|
+
|
|
1223
|
+
### System Requirements
|
|
1224
|
+
|
|
1225
|
+
- macOS 11 (Big Sur) or later
|
|
1226
|
+
- 4GB RAM minimum
|
|
1227
|
+
- 500MB free disk space
|
|
1228
|
+
|
|
1229
|
+
### Troubleshooting
|
|
1230
|
+
|
|
1231
|
+
**"App can't be opened" error:**
|
|
1232
|
+
Right-click the app → Open → Confirm to bypass Gatekeeper
|
|
1233
|
+
:::tabend
|
|
1234
|
+
|
|
1235
|
+
:::tab{label="Linux" icon="🐧"}
|
|
1236
|
+
### Using Package Manager
|
|
1237
|
+
|
|
1238
|
+
**Ubuntu/Debian:**
|
|
1239
|
+
\`\`\`bash
|
|
1240
|
+
sudo apt update
|
|
1241
|
+
sudo apt install myapp
|
|
1242
|
+
\`\`\`
|
|
1243
|
+
|
|
1244
|
+
**Fedora/RHEL:**
|
|
1245
|
+
\`\`\`bash
|
|
1246
|
+
sudo dnf install myapp
|
|
1247
|
+
\`\`\`
|
|
1248
|
+
|
|
1249
|
+
**Arch Linux:**
|
|
1250
|
+
\`\`\`bash
|
|
1251
|
+
sudo pacman -S myapp
|
|
1252
|
+
\`\`\`
|
|
1253
|
+
|
|
1254
|
+
### Using Snap
|
|
1255
|
+
|
|
1256
|
+
\`\`\`bash
|
|
1257
|
+
sudo snap install myapp
|
|
1258
|
+
\`\`\`
|
|
1259
|
+
|
|
1260
|
+
### Building from Source
|
|
1261
|
+
|
|
1262
|
+
\`\`\`bash
|
|
1263
|
+
git clone https://github.com/mycompany/myapp.git
|
|
1264
|
+
cd myapp
|
|
1265
|
+
./configure
|
|
1266
|
+
make
|
|
1267
|
+
sudo make install
|
|
1268
|
+
\`\`\`
|
|
1269
|
+
|
|
1270
|
+
### System Requirements
|
|
1271
|
+
|
|
1272
|
+
- Linux kernel 5.0 or later
|
|
1273
|
+
- 4GB RAM minimum
|
|
1274
|
+
- 500MB free disk space
|
|
1275
|
+
- glibc 2.31 or later
|
|
1276
|
+
|
|
1277
|
+
### Verify Installation
|
|
1278
|
+
|
|
1279
|
+
\`\`\`bash
|
|
1280
|
+
myapp --version
|
|
1281
|
+
which myapp
|
|
1282
|
+
\`\`\`
|
|
1283
|
+
:::tabend
|
|
1284
|
+
|
|
1285
|
+
:::tab{label="Docker" icon="🐳"}
|
|
1286
|
+
### Using Docker
|
|
1287
|
+
|
|
1288
|
+
Pull the official image:
|
|
1289
|
+
\`\`\`bash
|
|
1290
|
+
docker pull mycompany/myapp:latest
|
|
1291
|
+
\`\`\`
|
|
1292
|
+
|
|
1293
|
+
Run the container:
|
|
1294
|
+
\`\`\`bash
|
|
1295
|
+
docker run -d \\
|
|
1296
|
+
--name myapp \\
|
|
1297
|
+
-p 8080:8080 \\
|
|
1298
|
+
-v \$(pwd)/data:/app/data \\
|
|
1299
|
+
mycompany/myapp:latest
|
|
1300
|
+
\`\`\`
|
|
1301
|
+
|
|
1302
|
+
### Using Docker Compose
|
|
1303
|
+
|
|
1304
|
+
Create \`docker-compose.yml\`:
|
|
1305
|
+
\`\`\`yaml
|
|
1306
|
+
version: '3.8'
|
|
1307
|
+
services:
|
|
1308
|
+
myapp:
|
|
1309
|
+
image: mycompany/myapp:latest
|
|
1310
|
+
ports:
|
|
1311
|
+
- "8080:8080"
|
|
1312
|
+
volumes:
|
|
1313
|
+
- ./data:/app/data
|
|
1314
|
+
environment:
|
|
1315
|
+
- APP_ENV=production
|
|
1316
|
+
- LOG_LEVEL=info
|
|
1317
|
+
\`\`\`
|
|
1318
|
+
|
|
1319
|
+
Start services:
|
|
1320
|
+
\`\`\`bash
|
|
1321
|
+
docker-compose up -d
|
|
1322
|
+
\`\`\`
|
|
1323
|
+
|
|
1324
|
+
### Available Tags
|
|
1325
|
+
|
|
1326
|
+
- \`latest\` - Latest stable release
|
|
1327
|
+
- \`2.0.5\` - Specific version
|
|
1328
|
+
- \`alpine\` - Lightweight Alpine-based image
|
|
1329
|
+
- \`dev\` - Development build
|
|
1330
|
+
:::tabend
|
|
1331
|
+
::::tabsend
|
|
1332
|
+
|
|
1333
|
+
## Post-Installation
|
|
1334
|
+
|
|
1335
|
+
After installation, configure your environment:
|
|
1336
|
+
|
|
1337
|
+
\`\`\`bash
|
|
1338
|
+
myapp init
|
|
1339
|
+
myapp config set api-key YOUR_KEY
|
|
1340
|
+
\`\`\`
|
|
1341
|
+
```
|
|
1342
|
+
|
|
1343
|
+
#### 3. **Framework Comparison**
|
|
1344
|
+
|
|
1345
|
+
```markdown
|
|
1346
|
+
# Web Framework Comparison
|
|
1347
|
+
|
|
1348
|
+
## React vs Vue vs Angular
|
|
1349
|
+
|
|
1350
|
+
::::tabs
|
|
1351
|
+
:::tab{label="React" icon="⚛️" active="true"}
|
|
1352
|
+
### Overview
|
|
1353
|
+
|
|
1354
|
+
React is a JavaScript library for building user interfaces, focusing on component-based architecture.
|
|
1355
|
+
|
|
1356
|
+
**Pros:**
|
|
1357
|
+
- ✅ Large ecosystem and community
|
|
1358
|
+
- ✅ Virtual DOM for performance
|
|
1359
|
+
- ✅ Flexible and unopinionated
|
|
1360
|
+
- ✅ Excellent for SPAs
|
|
1361
|
+
- ✅ Strong TypeScript support
|
|
1362
|
+
|
|
1363
|
+
**Cons:**
|
|
1364
|
+
- ❌ Steep learning curve
|
|
1365
|
+
- ❌ JSX syntax to learn
|
|
1366
|
+
- ❌ Need additional libraries for routing, state
|
|
1367
|
+
|
|
1368
|
+
### Hello World Example
|
|
1369
|
+
|
|
1370
|
+
\`\`\`jsx
|
|
1371
|
+
import React from 'react';
|
|
1372
|
+
|
|
1373
|
+
function App() {
|
|
1374
|
+
const [count, setCount] = React.useState(0);
|
|
1375
|
+
|
|
1376
|
+
return (
|
|
1377
|
+
<div>
|
|
1378
|
+
<h1>Hello, React!</h1>
|
|
1379
|
+
<p>Count: {count}</p>
|
|
1380
|
+
<button onClick={() => setCount(count + 1)}>
|
|
1381
|
+
Increment
|
|
1382
|
+
</button>
|
|
1383
|
+
</div>
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
export default App;
|
|
1388
|
+
\`\`\`
|
|
1389
|
+
|
|
1390
|
+
### When to Choose React
|
|
1391
|
+
|
|
1392
|
+
- Building complex SPAs
|
|
1393
|
+
- Need flexibility in architecture
|
|
1394
|
+
- Large team with React expertise
|
|
1395
|
+
- Mobile app with React Native
|
|
1396
|
+
:::tabend
|
|
1397
|
+
|
|
1398
|
+
:::tab{label="Vue" icon="💚"}
|
|
1399
|
+
### Overview
|
|
1400
|
+
|
|
1401
|
+
Vue is a progressive framework for building user interfaces with a gentle learning curve.
|
|
1402
|
+
|
|
1403
|
+
**Pros:**
|
|
1404
|
+
- ✅ Easy to learn
|
|
1405
|
+
- ✅ Great documentation
|
|
1406
|
+
- ✅ Two-way data binding
|
|
1407
|
+
- ✅ Smaller bundle size
|
|
1408
|
+
- ✅ Template syntax familiar to HTML
|
|
1409
|
+
|
|
1410
|
+
**Cons:**
|
|
1411
|
+
- ❌ Smaller ecosystem than React
|
|
1412
|
+
- ❌ Less corporate backing
|
|
1413
|
+
- ❌ Fewer job opportunities
|
|
1414
|
+
|
|
1415
|
+
### Hello World Example
|
|
1416
|
+
|
|
1417
|
+
\`\`\`vue
|
|
1418
|
+
<template>
|
|
1419
|
+
<div>
|
|
1420
|
+
<h1>Hello, Vue!</h1>
|
|
1421
|
+
<p>Count: {{ count }}</p>
|
|
1422
|
+
<button @click="count++">
|
|
1423
|
+
Increment
|
|
1424
|
+
</button>
|
|
1425
|
+
</div>
|
|
1426
|
+
</template>
|
|
1427
|
+
|
|
1428
|
+
<script setup>
|
|
1429
|
+
import { ref } from 'vue';
|
|
1430
|
+
|
|
1431
|
+
const count = ref(0);
|
|
1432
|
+
</script>
|
|
1433
|
+
\`\`\`
|
|
1434
|
+
|
|
1435
|
+
### When to Choose Vue
|
|
1436
|
+
|
|
1437
|
+
- Quick prototyping
|
|
1438
|
+
- Small to medium projects
|
|
1439
|
+
- Team with HTML/CSS background
|
|
1440
|
+
- Gradual migration from legacy code
|
|
1441
|
+
:::tabend
|
|
1442
|
+
|
|
1443
|
+
:::tab{label="Angular" icon="🅰️"}
|
|
1444
|
+
### Overview
|
|
1445
|
+
|
|
1446
|
+
Angular is a full-featured framework with batteries included for enterprise applications.
|
|
1447
|
+
|
|
1448
|
+
**Pros:**
|
|
1449
|
+
- ✅ Complete solution (routing, HTTP, forms)
|
|
1450
|
+
- ✅ TypeScript by default
|
|
1451
|
+
- ✅ Strong corporate backing (Google)
|
|
1452
|
+
- ✅ Great for large applications
|
|
1453
|
+
- ✅ CLI tooling
|
|
1454
|
+
|
|
1455
|
+
**Cons:**
|
|
1456
|
+
- ❌ Steeper learning curve
|
|
1457
|
+
- ❌ Larger bundle size
|
|
1458
|
+
- ❌ More opinionated
|
|
1459
|
+
- ❌ Verbose syntax
|
|
1460
|
+
|
|
1461
|
+
### Hello World Example
|
|
1462
|
+
|
|
1463
|
+
\`\`\`typescript
|
|
1464
|
+
import { Component } from '@angular/core';
|
|
1465
|
+
|
|
1466
|
+
@Component({
|
|
1467
|
+
selector: 'app-root',
|
|
1468
|
+
template: \`
|
|
1469
|
+
<div>
|
|
1470
|
+
<h1>Hello, Angular!</h1>
|
|
1471
|
+
<p>Count: {{ count }}</p>
|
|
1472
|
+
<button (click)="increment()">
|
|
1473
|
+
Increment
|
|
1474
|
+
</button>
|
|
1475
|
+
</div>
|
|
1476
|
+
\`
|
|
1477
|
+
})
|
|
1478
|
+
export class AppComponent {
|
|
1479
|
+
count = 0;
|
|
1480
|
+
|
|
1481
|
+
increment() {
|
|
1482
|
+
this.count++;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
\`\`\`
|
|
1486
|
+
|
|
1487
|
+
### When to Choose Angular
|
|
1488
|
+
|
|
1489
|
+
- Enterprise applications
|
|
1490
|
+
- Large teams
|
|
1491
|
+
- Need complete framework
|
|
1492
|
+
- TypeScript-first development
|
|
1493
|
+
:::tabend
|
|
1494
|
+
|
|
1495
|
+
:::tab{label="Comparison" icon="📊"}
|
|
1496
|
+
### Side-by-Side Comparison
|
|
1497
|
+
|
|
1498
|
+
| Feature | React | Vue | Angular |
|
|
1499
|
+
|---------|-------|-----|---------|
|
|
1500
|
+
| **Type** | Library | Framework | Framework |
|
|
1501
|
+
| **Learning Curve** | Moderate | Easy | Steep |
|
|
1502
|
+
| **Bundle Size** | Medium | Small | Large |
|
|
1503
|
+
| **TypeScript** | Optional | Optional | Default |
|
|
1504
|
+
| **Performance** | Excellent | Excellent | Good |
|
|
1505
|
+
| **Ecosystem** | Huge | Growing | Large |
|
|
1506
|
+
| **Best For** | SPAs, Complex UIs | All projects | Enterprise |
|
|
1507
|
+
| **Mobile** | React Native | Weex, NativeScript | Ionic |
|
|
1508
|
+
| **State Mgmt** | Redux, Context | Vuex, Pinia | Services, RxJS |
|
|
1509
|
+
| **Community** | Very Large | Large | Large |
|
|
1510
|
+
|
|
1511
|
+
### Performance Benchmarks
|
|
1512
|
+
|
|
1513
|
+
| Metric | React | Vue | Angular |
|
|
1514
|
+
|--------|-------|-----|---------|
|
|
1515
|
+
| Initial Load | 45 KB | 38 KB | 95 KB |
|
|
1516
|
+
| Time to Interactive | 1.2s | 0.9s | 1.8s |
|
|
1517
|
+
| Memory Usage | 12 MB | 9 MB | 18 MB |
|
|
1518
|
+
| DOM Operations | Fast | Fast | Good |
|
|
1519
|
+
|
|
1520
|
+
### Popularity (GitHub Stars)
|
|
1521
|
+
|
|
1522
|
+
- React: ⭐ 220K+
|
|
1523
|
+
- Vue: ⭐ 205K+
|
|
1524
|
+
- Angular: ⭐ 93K+
|
|
1525
|
+
|
|
1526
|
+
*As of February 2026*
|
|
1527
|
+
:::tabend
|
|
1528
|
+
::::tabsend
|
|
1529
|
+
|
|
1530
|
+
## Making Your Choice
|
|
1531
|
+
|
|
1532
|
+
Consider your:
|
|
1533
|
+
- Team expertise
|
|
1534
|
+
- Project requirements
|
|
1535
|
+
- Timeline
|
|
1536
|
+
- Long-term maintenance
|
|
1537
|
+
```
|
|
1538
|
+
|
|
1539
|
+
#### 4. **Configuration Environments**
|
|
1540
|
+
|
|
1541
|
+
```markdown
|
|
1542
|
+
# Environment Configuration
|
|
1543
|
+
|
|
1544
|
+
## Setup for Different Environments
|
|
1545
|
+
|
|
1546
|
+
::::tabs
|
|
1547
|
+
:::tab{label="Development" icon="💻" active="true"}
|
|
1548
|
+
### Development Environment Setup
|
|
1549
|
+
|
|
1550
|
+
\`\`\`bash
|
|
1551
|
+
# Install dependencies
|
|
1552
|
+
npm install
|
|
1553
|
+
|
|
1554
|
+
# Copy environment file
|
|
1555
|
+
cp .env.example .env.development
|
|
1556
|
+
|
|
1557
|
+
# Start dev server
|
|
1558
|
+
npm run dev
|
|
1559
|
+
\`\`\`
|
|
1560
|
+
|
|
1561
|
+
### Configuration (\`.env.development\`)
|
|
1562
|
+
|
|
1563
|
+
\`\`\`env
|
|
1564
|
+
# API Configuration
|
|
1565
|
+
API_URL=http://localhost:3000
|
|
1566
|
+
API_KEY=dev_key_1234567890
|
|
1567
|
+
|
|
1568
|
+
# Database
|
|
1569
|
+
DB_HOST=localhost
|
|
1570
|
+
DB_PORT=5432
|
|
1571
|
+
DB_NAME=myapp_dev
|
|
1572
|
+
DB_USER=dev_user
|
|
1573
|
+
DB_PASSWORD=dev_password
|
|
1574
|
+
|
|
1575
|
+
# Features
|
|
1576
|
+
ENABLE_DEBUG=true
|
|
1577
|
+
ENABLE_HOT_RELOAD=true
|
|
1578
|
+
ENABLE_SOURCE_MAPS=true
|
|
1579
|
+
|
|
1580
|
+
# Logging
|
|
1581
|
+
LOG_LEVEL=debug
|
|
1582
|
+
LOG_TO_FILE=false
|
|
1583
|
+
\`\`\`
|
|
1584
|
+
|
|
1585
|
+
### Dev Tools
|
|
1586
|
+
|
|
1587
|
+
- ✅ Hot module replacement
|
|
1588
|
+
- ✅ Source maps enabled
|
|
1589
|
+
- ✅ Verbose logging
|
|
1590
|
+
- ✅ Mock API responses
|
|
1591
|
+
- ✅ Local database
|
|
1592
|
+
:::tabend
|
|
1593
|
+
|
|
1594
|
+
:::tab{label="Staging" icon="🧪"}
|
|
1595
|
+
### Staging Environment Setup
|
|
1596
|
+
|
|
1597
|
+
\`\`\`bash
|
|
1598
|
+
# Build for staging
|
|
1599
|
+
npm run build:staging
|
|
1600
|
+
|
|
1601
|
+
# Deploy to staging
|
|
1602
|
+
npm run deploy:staging
|
|
1603
|
+
\`\`\`
|
|
1604
|
+
|
|
1605
|
+
### Configuration (\`.env.staging\`)
|
|
1606
|
+
|
|
1607
|
+
\`\`\`env
|
|
1608
|
+
# API Configuration
|
|
1609
|
+
API_URL=https://staging-api.example.com
|
|
1610
|
+
API_KEY=staging_key_secure_token
|
|
1611
|
+
|
|
1612
|
+
# Database
|
|
1613
|
+
DB_HOST=staging-db.example.com
|
|
1614
|
+
DB_PORT=5432
|
|
1615
|
+
DB_NAME=myapp_staging
|
|
1616
|
+
DB_USER=staging_user
|
|
1617
|
+
DB_PASSWORD=staging_secure_password
|
|
1618
|
+
|
|
1619
|
+
# Features
|
|
1620
|
+
ENABLE_DEBUG=false
|
|
1621
|
+
ENABLE_HOT_RELOAD=false
|
|
1622
|
+
ENABLE_SOURCE_MAPS=true
|
|
1623
|
+
|
|
1624
|
+
# Logging
|
|
1625
|
+
LOG_LEVEL=info
|
|
1626
|
+
LOG_TO_FILE=true
|
|
1627
|
+
LOG_PATH=/var/log/myapp
|
|
1628
|
+
|
|
1629
|
+
# Monitoring
|
|
1630
|
+
SENTRY_DSN=https://...@sentry.io/staging
|
|
1631
|
+
ANALYTICS_ID=staging-analytics-id
|
|
1632
|
+
\`\`\`
|
|
1633
|
+
|
|
1634
|
+
### Staging Features
|
|
1635
|
+
|
|
1636
|
+
- ✅ Production-like environment
|
|
1637
|
+
- ✅ Real database (non-production)
|
|
1638
|
+
- ✅ Error tracking enabled
|
|
1639
|
+
- ✅ Performance monitoring
|
|
1640
|
+
- ✅ QA testing environment
|
|
1641
|
+
:::tabend
|
|
1642
|
+
|
|
1643
|
+
:::tab{label="Production" icon="🚀"}
|
|
1644
|
+
### Production Environment Setup
|
|
1645
|
+
|
|
1646
|
+
\`\`\`bash
|
|
1647
|
+
# Build for production
|
|
1648
|
+
npm run build:production
|
|
1649
|
+
|
|
1650
|
+
# Deploy to production
|
|
1651
|
+
npm run deploy:production
|
|
1652
|
+
\`\`\`
|
|
1653
|
+
|
|
1654
|
+
### Configuration (\`.env.production\`)
|
|
1655
|
+
|
|
1656
|
+
\`\`\`env
|
|
1657
|
+
# API Configuration
|
|
1658
|
+
API_URL=https://api.example.com
|
|
1659
|
+
API_KEY=prod_key_super_secure_token
|
|
1660
|
+
|
|
1661
|
+
# Database
|
|
1662
|
+
DB_HOST=prod-db.example.com
|
|
1663
|
+
DB_PORT=5432
|
|
1664
|
+
DB_NAME=myapp_prod
|
|
1665
|
+
DB_USER=prod_user
|
|
1666
|
+
DB_PASSWORD=prod_ultra_secure_password
|
|
1667
|
+
|
|
1668
|
+
# Features
|
|
1669
|
+
ENABLE_DEBUG=false
|
|
1670
|
+
ENABLE_HOT_RELOAD=false
|
|
1671
|
+
ENABLE_SOURCE_MAPS=false
|
|
1672
|
+
|
|
1673
|
+
# Logging
|
|
1674
|
+
LOG_LEVEL=error
|
|
1675
|
+
LOG_TO_FILE=true
|
|
1676
|
+
LOG_PATH=/var/log/myapp
|
|
1677
|
+
LOG_ROTATION=daily
|
|
1678
|
+
|
|
1679
|
+
# Monitoring
|
|
1680
|
+
SENTRY_DSN=https://...@sentry.io/production
|
|
1681
|
+
ANALYTICS_ID=prod-analytics-id
|
|
1682
|
+
|
|
1683
|
+
# Performance
|
|
1684
|
+
ENABLE_CACHING=true
|
|
1685
|
+
CACHE_TTL=3600
|
|
1686
|
+
CDN_URL=https://cdn.example.com
|
|
1687
|
+
|
|
1688
|
+
# Security
|
|
1689
|
+
RATE_LIMIT=100
|
|
1690
|
+
CORS_ORIGIN=https://example.com
|
|
1691
|
+
SSL_CERT=/path/to/cert.pem
|
|
1692
|
+
\`\`\`
|
|
1693
|
+
|
|
1694
|
+
### Production Requirements
|
|
1695
|
+
|
|
1696
|
+
- ✅ SSL/TLS enabled
|
|
1697
|
+
- ✅ CDN configured
|
|
1698
|
+
- ✅ Caching enabled
|
|
1699
|
+
- ✅ Rate limiting active
|
|
1700
|
+
- ✅ Monitoring and alerts
|
|
1701
|
+
- ✅ Automated backups
|
|
1702
|
+
- ✅ Load balancing
|
|
1703
|
+
:::tabend
|
|
1704
|
+
|
|
1705
|
+
:::tab{label="Docker" icon="🐳"}
|
|
1706
|
+
### Docker Configuration
|
|
1707
|
+
|
|
1708
|
+
Create \`docker-compose.yml\` per environment:
|
|
1709
|
+
|
|
1710
|
+
**Development:**
|
|
1711
|
+
\`\`\`yaml
|
|
1712
|
+
version: '3.8'
|
|
1713
|
+
services:
|
|
1714
|
+
app:
|
|
1715
|
+
build:
|
|
1716
|
+
context: .
|
|
1717
|
+
target: development
|
|
1718
|
+
ports:
|
|
1719
|
+
- "3000:3000"
|
|
1720
|
+
volumes:
|
|
1721
|
+
- .:/app
|
|
1722
|
+
- /app/node_modules
|
|
1723
|
+
environment:
|
|
1724
|
+
- NODE_ENV=development
|
|
1725
|
+
command: npm run dev
|
|
1726
|
+
\`\`\`
|
|
1727
|
+
|
|
1728
|
+
**Production:**
|
|
1729
|
+
\`\`\`yaml
|
|
1730
|
+
version: '3.8'
|
|
1731
|
+
services:
|
|
1732
|
+
app:
|
|
1733
|
+
build:
|
|
1734
|
+
context: .
|
|
1735
|
+
target: production
|
|
1736
|
+
ports:
|
|
1737
|
+
- "3000:3000"
|
|
1738
|
+
environment:
|
|
1739
|
+
- NODE_ENV=production
|
|
1740
|
+
restart: always
|
|
1741
|
+
healthcheck:
|
|
1742
|
+
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
|
1743
|
+
interval: 30s
|
|
1744
|
+
timeout: 10s
|
|
1745
|
+
retries: 3
|
|
1746
|
+
\`\`\`
|
|
1747
|
+
|
|
1748
|
+
### Multi-stage Dockerfile
|
|
1749
|
+
|
|
1750
|
+
\`\`\`dockerfile
|
|
1751
|
+
# Development stage
|
|
1752
|
+
FROM node:18-alpine AS development
|
|
1753
|
+
WORKDIR /app
|
|
1754
|
+
COPY package*.json ./
|
|
1755
|
+
RUN npm install
|
|
1756
|
+
COPY . .
|
|
1757
|
+
CMD ["npm", "run", "dev"]
|
|
1758
|
+
|
|
1759
|
+
# Production stage
|
|
1760
|
+
FROM node:18-alpine AS production
|
|
1761
|
+
WORKDIR /app
|
|
1762
|
+
COPY package*.json ./
|
|
1763
|
+
RUN npm ci --only=production
|
|
1764
|
+
COPY . .
|
|
1765
|
+
RUN npm run build
|
|
1766
|
+
CMD ["npm", "start"]
|
|
1767
|
+
\`\`\`
|
|
1768
|
+
:::tabend
|
|
1769
|
+
::::tabsend
|
|
1770
|
+
|
|
1771
|
+
## Environment Variables Best Practices
|
|
1772
|
+
|
|
1773
|
+
1. ✅ Never commit secrets to version control
|
|
1774
|
+
2. ✅ Use different keys for each environment
|
|
1775
|
+
3. ✅ Rotate credentials regularly
|
|
1776
|
+
4. ✅ Use secret management tools (Vault, AWS Secrets Manager)
|
|
1777
|
+
5. ✅ Document all environment variables
|
|
1778
|
+
```
|
|
1779
|
+
|
|
1780
|
+
### Troubleshooting
|
|
1781
|
+
|
|
1782
|
+
#### Tabs Not Switching
|
|
1783
|
+
|
|
1784
|
+
**Problem**: Clicking tabs doesn't change content.
|
|
1785
|
+
|
|
1786
|
+
**Solutions**:
|
|
1787
|
+
|
|
1788
|
+
1. **Import CSS:**
|
|
1789
|
+
```javascript
|
|
1790
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
1791
|
+
```
|
|
1792
|
+
|
|
1793
|
+
2. **Check for conflicting styles:**
|
|
1794
|
+
```css
|
|
1795
|
+
/* Make sure nothing is overriding tab display */
|
|
1796
|
+
.marked-extended-tabs-content-pane {
|
|
1797
|
+
display: none; /* Should be hidden by default */
|
|
1798
|
+
}
|
|
1799
|
+
```
|
|
1800
|
+
|
|
1801
|
+
3. **Verify tab structure:**
|
|
1802
|
+
```markdown
|
|
1803
|
+
<!-- Ensure proper nesting -->
|
|
1804
|
+
::::tabs
|
|
1805
|
+
:::tab{label="Tab 1"}
|
|
1806
|
+
Content
|
|
1807
|
+
:::tabend
|
|
1808
|
+
::::tabsend
|
|
1809
|
+
```
|
|
1810
|
+
|
|
1811
|
+
#### Icons Not Showing
|
|
1812
|
+
|
|
1813
|
+
**Problem**: Tab icons don't display.
|
|
1814
|
+
|
|
1815
|
+
**Solutions**:
|
|
1816
|
+
|
|
1817
|
+
1. **Use valid Unicode/emoji:**
|
|
1818
|
+
```markdown
|
|
1819
|
+
<!-- Correct -->
|
|
1820
|
+
:::tab{label="Code" icon="📜"}
|
|
1821
|
+
:::tab{label="Python" icon="🐍"}
|
|
1822
|
+
|
|
1823
|
+
<!-- Wrong -->
|
|
1824
|
+
:::tab{label="Code" icon="invalid"} <!-- ❌ -->
|
|
1825
|
+
```
|
|
1826
|
+
|
|
1827
|
+
2. **Check font support:**
|
|
1828
|
+
```css
|
|
1829
|
+
/* Ensure emoji font is loaded */
|
|
1830
|
+
.marked-extended-tabs-icon {
|
|
1831
|
+
font-family: "Apple Color Emoji", "Segoe UI Emoji", sans-serif;
|
|
1832
|
+
}
|
|
1833
|
+
```
|
|
1834
|
+
|
|
1835
|
+
#### Animation Not Working
|
|
1836
|
+
|
|
1837
|
+
**Problem**: No animation between tabs.
|
|
1838
|
+
|
|
1839
|
+
**Solutions**:
|
|
1840
|
+
|
|
1841
|
+
1. **Set animation type:**
|
|
1842
|
+
```javascript
|
|
1843
|
+
marked.use(markedExtendedTabs({
|
|
1844
|
+
animation: 'fade' // or 'slide'
|
|
1845
|
+
}));
|
|
1846
|
+
```
|
|
1847
|
+
|
|
1848
|
+
2. **Import theme CSS:**
|
|
1849
|
+
```javascript
|
|
1850
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
1851
|
+
```
|
|
1852
|
+
|
|
1853
|
+
#### First Tab Not Active
|
|
1854
|
+
|
|
1855
|
+
**Problem**: No tab is active by default.
|
|
1856
|
+
|
|
1857
|
+
**Solution**:
|
|
1858
|
+
```javascript
|
|
1859
|
+
// Enable auto-activation
|
|
1860
|
+
marked.use(markedExtendedTabs({
|
|
1861
|
+
autoActivate: true // Default is true
|
|
1862
|
+
}));
|
|
1863
|
+
```
|
|
1864
|
+
|
|
1865
|
+
Or mark a tab as active:
|
|
1866
|
+
```markdown
|
|
1867
|
+
:::tab{label="Tab 1" active="true"}
|
|
1868
|
+
```
|
|
1869
|
+
|
|
1870
|
+
### Framework Integration
|
|
1871
|
+
|
|
1872
|
+
#### React Integration
|
|
1873
|
+
|
|
1874
|
+
```tsx
|
|
1875
|
+
// TabsContent.tsx
|
|
1876
|
+
import { marked } from 'marked';
|
|
1877
|
+
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
1878
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
1879
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
1880
|
+
import { useEffect, useState } from 'react';
|
|
1881
|
+
|
|
1882
|
+
marked.use(markedExtendedTabs({
|
|
1883
|
+
animation: 'fade',
|
|
1884
|
+
autoActivate: true
|
|
1885
|
+
}));
|
|
1886
|
+
|
|
1887
|
+
interface Props {
|
|
1888
|
+
content: string;
|
|
1889
|
+
animation?: 'fade' | 'slide' | 'none';
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
export function TabsContent({ content, animation = 'fade' }: Props) {
|
|
1893
|
+
const [html, setHtml] = useState('');
|
|
1894
|
+
|
|
1895
|
+
useEffect(() => {
|
|
1896
|
+
marked.use(markedExtendedTabs({ animation }));
|
|
1897
|
+
const parsed = marked.parse(content);
|
|
1898
|
+
setHtml(parsed);
|
|
1899
|
+
}, [content, animation]);
|
|
1900
|
+
|
|
1901
|
+
return (
|
|
1902
|
+
<div
|
|
1903
|
+
className="tabs-wrapper"
|
|
1904
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
1905
|
+
/>
|
|
1906
|
+
);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
// Usage:
|
|
1910
|
+
// <TabsContent content={markdownWithTabs} animation="fade" />
|
|
1911
|
+
```
|
|
1912
|
+
|
|
1913
|
+
#### Vue 3 Integration
|
|
1914
|
+
|
|
1915
|
+
```vue
|
|
1916
|
+
<script setup lang="ts">
|
|
1917
|
+
import { marked } from 'marked';
|
|
1918
|
+
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
1919
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs.css';
|
|
1920
|
+
import '@fsegurai/marked-extended-tabs/styles/tabs-theme.css';
|
|
1921
|
+
import { computed } from 'vue';
|
|
1922
|
+
|
|
1923
|
+
marked.use(markedExtendedTabs({
|
|
1924
|
+
animation: 'fade'
|
|
1925
|
+
}));
|
|
1926
|
+
|
|
1927
|
+
interface Props {
|
|
1928
|
+
content: string;
|
|
1929
|
+
animation?: 'fade' | 'slide' | 'none';
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
1933
|
+
animation: 'fade'
|
|
1934
|
+
});
|
|
1935
|
+
|
|
1936
|
+
const html = computed(() => {
|
|
1937
|
+
marked.use(markedExtendedTabs({ animation: props.animation }));
|
|
1938
|
+
return marked.parse(props.content);
|
|
1939
|
+
});
|
|
1940
|
+
</script>
|
|
1941
|
+
|
|
1942
|
+
<template>
|
|
1943
|
+
<div class="tabs-container" v-html="html" />
|
|
1944
|
+
</template>
|
|
1945
|
+
```
|
|
1946
|
+
|
|
1947
|
+
#### Angular Integration
|
|
1948
|
+
|
|
1949
|
+
```typescript
|
|
1950
|
+
// tabs-content.component.ts
|
|
1951
|
+
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
|
|
1952
|
+
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
|
1953
|
+
import { marked } from 'marked';
|
|
1954
|
+
import markedExtendedTabs from '@fsegurai/marked-extended-tabs';
|
|
1955
|
+
|
|
1956
|
+
marked.use(markedExtendedTabs({
|
|
1957
|
+
animation: 'fade'
|
|
1958
|
+
}));
|
|
1959
|
+
|
|
1960
|
+
@Component({
|
|
1961
|
+
selector: 'app-tabs-content',
|
|
1962
|
+
template: \`<div [innerHTML]="parsedContent"></div>\`,
|
|
1963
|
+
styleUrls: [
|
|
1964
|
+
'../node_modules/@fsegurai/marked-extended-tabs/styles/tabs.css',
|
|
1965
|
+
'../node_modules/@fsegurai/marked-extended-tabs/styles/tabs-theme.css'
|
|
1966
|
+
]
|
|
1967
|
+
})
|
|
1968
|
+
export class TabsContentComponent implements OnChanges {
|
|
1969
|
+
@Input() content: string = '';
|
|
1970
|
+
@Input() animation: 'fade' | 'slide' | 'none' = 'fade';
|
|
1971
|
+
parsedContent: SafeHtml = '';
|
|
1972
|
+
|
|
1973
|
+
constructor(private sanitizer: DomSanitizer) {}
|
|
1974
|
+
|
|
1975
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
1976
|
+
if (changes['content'] || changes['animation']) {
|
|
1977
|
+
marked.use(markedExtendedTabs({ animation: this.animation }));
|
|
1978
|
+
const html = marked.parse(this.content);
|
|
1979
|
+
this.parsedContent = this.sanitizer.bypassSecurityTrustHtml(html);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
```
|
|
1984
|
+
|
|
1985
|
+
### Performance Tips
|
|
1986
|
+
|
|
1987
|
+
1. **Limit number of tabs**: Keep under 10 tabs for best UX
|
|
1988
|
+
2. **Lazy load content**: For heavy content, consider lazy loading
|
|
1989
|
+
3. **Optimize animations**: Use 'none' for instant switching if needed
|
|
1990
|
+
|
|
1991
|
+
```javascript
|
|
1992
|
+
// Disable animations for better performance
|
|
1993
|
+
marked.use(markedExtendedTabs({
|
|
1994
|
+
animation: 'none'
|
|
1995
|
+
}));
|
|
1996
|
+
```
|
|
1997
|
+
|
|
1998
|
+
### Contributing
|
|
1999
|
+
|
|
2000
|
+
Found a bug or have a feature request? Please open an issue on [GitHub](https://github.com/fsegurai/marked-extensions/issues).
|
|
2001
|
+
|
|
2002
|
+
### Related Resources
|
|
2003
|
+
|
|
2004
|
+
- [Marked.js Documentation](https://marked.js.org/)
|
|
2005
|
+
- [Extension Demo](https://fsegurai.github.io/marked-extensions)
|
|
2006
|
+
- [Source Code](https://github.com/fsegurai/marked-extensions)
|
|
2007
|
+
- [npm Package](https://www.npmjs.com/package/@fsegurai/marked-extended-tabs)
|
|
2008
|
+
- [ARIA Tabs Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/)
|
|
2009
|
+
|
|
2010
|
+
|
|
2011
|
+
|