@roadlittledawn/docs-design-system-react 0.1.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.md +136 -0
- package/dist/components/Button.d.ts +17 -0
- package/dist/components/Button.js +28 -0
- package/dist/components/Button.stories.d.ts +45 -0
- package/dist/components/Button.stories.js +98 -0
- package/dist/components/Callout.d.ts +17 -0
- package/dist/components/Callout.js +19 -0
- package/dist/components/Callout.stories.d.ts +36 -0
- package/dist/components/Callout.stories.js +80 -0
- package/dist/components/Card.d.ts +23 -0
- package/dist/components/Card.js +21 -0
- package/dist/components/Card.stories.d.ts +32 -0
- package/dist/components/Card.stories.js +68 -0
- package/dist/components/CardGrid.d.ts +14 -0
- package/dist/components/CardGrid.js +6 -0
- package/dist/components/CardGrid.stories.d.ts +24 -0
- package/dist/components/CardGrid.stories.js +55 -0
- package/dist/components/CodeBlock.d.ts +43 -0
- package/dist/components/CodeBlock.js +240 -0
- package/dist/components/CodeBlock.stories.d.ts +45 -0
- package/dist/components/CodeBlock.stories.js +154 -0
- package/dist/components/Collapser.d.ts +18 -0
- package/dist/components/Collapser.js +25 -0
- package/dist/components/Collapser.stories.d.ts +28 -0
- package/dist/components/Collapser.stories.js +62 -0
- package/dist/components/Heading.d.ts +11 -0
- package/dist/components/Heading.js +7 -0
- package/dist/components/Heading.stories.d.ts +32 -0
- package/dist/components/Heading.stories.js +66 -0
- package/dist/components/Link.d.ts +11 -0
- package/dist/components/Link.js +10 -0
- package/dist/components/Link.stories.d.ts +32 -0
- package/dist/components/Link.stories.js +63 -0
- package/dist/components/Typography.d.ts +14 -0
- package/dist/components/Typography.js +9 -0
- package/dist/components/Typography.stories.d.ts +36 -0
- package/dist/components/Typography.stories.js +78 -0
- package/dist/hooks/useKeyPress.d.ts +5 -0
- package/dist/hooks/useKeyPress.js +60 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -0
- package/dist/styles.css +857 -0
- package/package.json +44 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { CardGrid } from './CardGrid';
|
|
3
|
+
import { Card } from './Card';
|
|
4
|
+
/**
|
|
5
|
+
* The CardGrid component arranges Card components in a responsive grid layout.
|
|
6
|
+
*/
|
|
7
|
+
var meta = {
|
|
8
|
+
title: 'Components/CardGrid',
|
|
9
|
+
component: CardGrid,
|
|
10
|
+
tags: ['autodocs'],
|
|
11
|
+
parameters: {
|
|
12
|
+
docs: {
|
|
13
|
+
description: {
|
|
14
|
+
component: "\nThe CardGrid component provides a responsive grid layout for displaying multiple cards.\n\n## When to Use\n\n- To display multiple related cards in an organized layout\n- To create feature grids or product showcases\n- To organize navigation options or menu items\n- When you need a responsive multi-column layout\n\n## When Not to Use\n\n- For single cards (just use the Card component)\n- For lists without card-style containers (use regular list elements)\n- When you need a more complex custom layout\n\n## Accessibility\n\n- Uses CSS Grid for proper layout semantics\n- Responsive design ensures content is accessible at all screen sizes\n ",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
export default meta;
|
|
20
|
+
/**
|
|
21
|
+
* Grid with 2 columns (responsive).
|
|
22
|
+
*/
|
|
23
|
+
export var TwoColumns = {
|
|
24
|
+
args: {
|
|
25
|
+
columns: 2,
|
|
26
|
+
children: (_jsxs(_Fragment, { children: [_jsx(Card, { title: "Getting Started", children: "Learn the basics of our documentation system." }), _jsx(Card, { title: "Components", children: "Explore all available UI components." }), _jsx(Card, { title: "Best Practices", children: "Follow guidelines for effective documentation." }), _jsx(Card, { title: "Examples", children: "See real-world usage examples." })] })),
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Grid with 3 columns (default, responsive).
|
|
31
|
+
*/
|
|
32
|
+
export var ThreeColumns = {
|
|
33
|
+
args: {
|
|
34
|
+
columns: 3,
|
|
35
|
+
children: (_jsxs(_Fragment, { children: [_jsx(Card, { title: "Tutorials", children: "Step-by-step learning guides." }), _jsx(Card, { title: "How-To Guides", children: "Task-oriented instructions." }), _jsx(Card, { title: "Reference", children: "Technical reference documentation." }), _jsx(Card, { title: "Explanation", children: "Conceptual background information." }), _jsx(Card, { title: "API", children: "Complete API documentation." }), _jsx(Card, { title: "FAQ", children: "Frequently asked questions." })] })),
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Grid with 4 columns (responsive).
|
|
40
|
+
*/
|
|
41
|
+
export var FourColumns = {
|
|
42
|
+
args: {
|
|
43
|
+
columns: 4,
|
|
44
|
+
children: (_jsxs(_Fragment, { children: [_jsx(Card, { titleColor: "blue", backgroundColor: "blue", children: "Feature 1" }), _jsx(Card, { titleColor: "green", backgroundColor: "green", children: "Feature 2" }), _jsx(Card, { titleColor: "purple", backgroundColor: "purple", children: "Feature 3" }), _jsx(Card, { titleColor: "red", backgroundColor: "red", children: "Feature 4" }), _jsx(Card, { titleColor: "yellow", backgroundColor: "yellow", children: "Feature 5" }), _jsx(Card, { titleColor: "gray", backgroundColor: "gray", children: "Feature 6" }), _jsx(Card, { titleColor: "blue", backgroundColor: "blue", children: "Feature 7" }), _jsx(Card, { titleColor: "green", backgroundColor: "green", children: "Feature 8" })] })),
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Grid with clickable cards.
|
|
49
|
+
*/
|
|
50
|
+
export var ClickableCards = {
|
|
51
|
+
args: {
|
|
52
|
+
columns: 3,
|
|
53
|
+
children: (_jsxs(_Fragment, { children: [_jsx(Card, { title: "Documentation", href: "/docs", children: "Complete documentation guide" }), _jsx(Card, { title: "API Reference", href: "/api", children: "Detailed API reference" }), _jsx(Card, { title: "Examples", href: "/examples", children: "Code examples and patterns" })] })),
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import "prismjs/components/prism-javascript";
|
|
2
|
+
import "prismjs/components/prism-typescript";
|
|
3
|
+
import "prismjs/components/prism-jsx";
|
|
4
|
+
import "prismjs/components/prism-tsx";
|
|
5
|
+
import "prismjs/components/prism-css";
|
|
6
|
+
import "prismjs/components/prism-markdown";
|
|
7
|
+
import "prismjs/components/prism-json";
|
|
8
|
+
import "prismjs/components/prism-bash";
|
|
9
|
+
import "prismjs/components/prism-ruby";
|
|
10
|
+
import "prismjs/components/prism-python";
|
|
11
|
+
import "prismjs/components/prism-java";
|
|
12
|
+
import "prismjs/components/prism-sql";
|
|
13
|
+
import "prismjs/components/prism-yaml";
|
|
14
|
+
export interface CodeSnippet {
|
|
15
|
+
/** The code content */
|
|
16
|
+
code: string;
|
|
17
|
+
/** Language for syntax highlighting */
|
|
18
|
+
language?: string;
|
|
19
|
+
/** Optional filename to display */
|
|
20
|
+
filename?: string;
|
|
21
|
+
/** Optional tab title (defaults to filename if not provided) */
|
|
22
|
+
tabTitle?: string;
|
|
23
|
+
/** Optional line numbers to highlight (1-indexed) */
|
|
24
|
+
highlightLines?: number[];
|
|
25
|
+
}
|
|
26
|
+
interface CodeBlockProps {
|
|
27
|
+
/** Single code snippet (for simple usage) */
|
|
28
|
+
code?: string;
|
|
29
|
+
/** Language for syntax highlighting */
|
|
30
|
+
language?: string;
|
|
31
|
+
/** Optional filename to display */
|
|
32
|
+
filename?: string;
|
|
33
|
+
/** Optional line numbers to highlight (1-indexed) */
|
|
34
|
+
highlightLines?: number[];
|
|
35
|
+
/** Multiple code snippets (for tabs) */
|
|
36
|
+
snippets?: CodeSnippet[];
|
|
37
|
+
/** Path to markdown file containing snippets */
|
|
38
|
+
path?: string;
|
|
39
|
+
/** Additional CSS classes */
|
|
40
|
+
className?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function CodeBlock({ code, language, filename, highlightLines, snippets, path, className, }: CodeBlockProps): import("react/jsx-runtime").JSX.Element | null;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
11
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
12
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
13
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
14
|
+
function step(op) {
|
|
15
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
16
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
17
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
18
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
19
|
+
switch (op[0]) {
|
|
20
|
+
case 0: case 1: t = op; break;
|
|
21
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
22
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
23
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
24
|
+
default:
|
|
25
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
26
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
27
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
28
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
29
|
+
if (t[2]) _.ops.pop();
|
|
30
|
+
_.trys.pop(); continue;
|
|
31
|
+
}
|
|
32
|
+
op = body.call(thisArg, _);
|
|
33
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
34
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
38
|
+
import { useState, useEffect, useMemo } from "react";
|
|
39
|
+
// @ts-ignore - prismjs doesn't have perfect TypeScript support
|
|
40
|
+
import Prism from "prismjs";
|
|
41
|
+
// Import common languages
|
|
42
|
+
import "prismjs/components/prism-javascript";
|
|
43
|
+
import "prismjs/components/prism-typescript";
|
|
44
|
+
import "prismjs/components/prism-jsx";
|
|
45
|
+
import "prismjs/components/prism-tsx";
|
|
46
|
+
import "prismjs/components/prism-css";
|
|
47
|
+
import "prismjs/components/prism-markdown";
|
|
48
|
+
import "prismjs/components/prism-json";
|
|
49
|
+
import "prismjs/components/prism-bash";
|
|
50
|
+
import "prismjs/components/prism-ruby";
|
|
51
|
+
import "prismjs/components/prism-python";
|
|
52
|
+
import "prismjs/components/prism-java";
|
|
53
|
+
import "prismjs/components/prism-sql";
|
|
54
|
+
import "prismjs/components/prism-yaml";
|
|
55
|
+
/**
|
|
56
|
+
* Parses a markdown file content to extract code snippets
|
|
57
|
+
*/
|
|
58
|
+
function parseMarkdownSnippets(content) {
|
|
59
|
+
var snippets = [];
|
|
60
|
+
var codeBlockRegex = /```(\w+)?\s*([^\n]*)\n([\s\S]*?)```/g;
|
|
61
|
+
var match;
|
|
62
|
+
while ((match = codeBlockRegex.exec(content)) !== null) {
|
|
63
|
+
var language = match[1] || undefined;
|
|
64
|
+
var attributes = match[2].trim();
|
|
65
|
+
var code = match[3].trim();
|
|
66
|
+
// Parse attributes (filename="...", language="...", tabTitle="...", highlightLines="1,2,3")
|
|
67
|
+
var attrs = {};
|
|
68
|
+
var attrRegex = /(\w+)="([^"]+)"/g;
|
|
69
|
+
var attrMatch = void 0;
|
|
70
|
+
while ((attrMatch = attrRegex.exec(attributes)) !== null) {
|
|
71
|
+
attrs[attrMatch[1]] = attrMatch[2];
|
|
72
|
+
}
|
|
73
|
+
// Parse highlightLines if present
|
|
74
|
+
var highlightLines = void 0;
|
|
75
|
+
if (attrs.highlightLines) {
|
|
76
|
+
highlightLines = attrs.highlightLines
|
|
77
|
+
.split(",")
|
|
78
|
+
.map(function (s) { return parseInt(s.trim(), 10); })
|
|
79
|
+
.filter(function (n) { return !isNaN(n); });
|
|
80
|
+
}
|
|
81
|
+
snippets.push({
|
|
82
|
+
code: code,
|
|
83
|
+
language: attrs.language || language,
|
|
84
|
+
filename: attrs.filename,
|
|
85
|
+
tabTitle: attrs.tabTitle || attrs.filename,
|
|
86
|
+
highlightLines: highlightLines,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return snippets;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Groups snippets by language for language dropdown
|
|
93
|
+
*/
|
|
94
|
+
function groupSnippetsByLanguage(snippets) {
|
|
95
|
+
var grouped = new Map();
|
|
96
|
+
for (var _i = 0, snippets_1 = snippets; _i < snippets_1.length; _i++) {
|
|
97
|
+
var snippet = snippets_1[_i];
|
|
98
|
+
var lang = snippet.language || "text";
|
|
99
|
+
if (!grouped.has(lang)) {
|
|
100
|
+
grouped.set(lang, []);
|
|
101
|
+
}
|
|
102
|
+
grouped.get(lang).push(snippet);
|
|
103
|
+
}
|
|
104
|
+
return grouped;
|
|
105
|
+
}
|
|
106
|
+
export function CodeBlock(_a) {
|
|
107
|
+
var _this = this;
|
|
108
|
+
var code = _a.code, language = _a.language, filename = _a.filename, highlightLines = _a.highlightLines, snippets = _a.snippets, path = _a.path, _b = _a.className, className = _b === void 0 ? "" : _b;
|
|
109
|
+
var _c = useState([]), loadedSnippets = _c[0], setLoadedSnippets = _c[1];
|
|
110
|
+
var _d = useState(false), loading = _d[0], setLoading = _d[1];
|
|
111
|
+
var _e = useState(null), error = _e[0], setError = _e[1];
|
|
112
|
+
var _f = useState(0), activeTabIndex = _f[0], setActiveTabIndex = _f[1];
|
|
113
|
+
var _g = useState(null), activeLanguage = _g[0], setActiveLanguage = _g[1];
|
|
114
|
+
var _h = useState(false), copied = _h[0], setCopied = _h[1];
|
|
115
|
+
// Load snippets from path if provided
|
|
116
|
+
useEffect(function () {
|
|
117
|
+
if (!path)
|
|
118
|
+
return;
|
|
119
|
+
setLoading(true);
|
|
120
|
+
setError(null);
|
|
121
|
+
fetch(path)
|
|
122
|
+
.then(function (res) {
|
|
123
|
+
if (!res.ok) {
|
|
124
|
+
throw new Error("Failed to load: ".concat(res.statusText));
|
|
125
|
+
}
|
|
126
|
+
return res.text();
|
|
127
|
+
})
|
|
128
|
+
.then(function (content) {
|
|
129
|
+
var parsed = parseMarkdownSnippets(content);
|
|
130
|
+
setLoadedSnippets(parsed);
|
|
131
|
+
setLoading(false);
|
|
132
|
+
})
|
|
133
|
+
.catch(function (err) {
|
|
134
|
+
setError(err.message);
|
|
135
|
+
setLoading(false);
|
|
136
|
+
});
|
|
137
|
+
}, [path]);
|
|
138
|
+
// Determine which snippets to use
|
|
139
|
+
var finalSnippets = useMemo(function () {
|
|
140
|
+
if (snippets) {
|
|
141
|
+
return snippets;
|
|
142
|
+
}
|
|
143
|
+
if (loadedSnippets.length > 0) {
|
|
144
|
+
return loadedSnippets;
|
|
145
|
+
}
|
|
146
|
+
if (code) {
|
|
147
|
+
return [
|
|
148
|
+
{
|
|
149
|
+
code: code,
|
|
150
|
+
language: language,
|
|
151
|
+
filename: filename,
|
|
152
|
+
tabTitle: filename,
|
|
153
|
+
highlightLines: highlightLines,
|
|
154
|
+
},
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
return [];
|
|
158
|
+
}, [snippets, loadedSnippets, code, language, filename, highlightLines]);
|
|
159
|
+
// Group snippets by language if we have multiple languages
|
|
160
|
+
var snippetsByLanguage = useMemo(function () {
|
|
161
|
+
if (finalSnippets.length === 0)
|
|
162
|
+
return new Map();
|
|
163
|
+
// Check if we have multiple languages
|
|
164
|
+
var languages = new Set(finalSnippets.map(function (s) { return s.language || "text"; }));
|
|
165
|
+
if (languages.size <= 1)
|
|
166
|
+
return new Map();
|
|
167
|
+
return groupSnippetsByLanguage(finalSnippets);
|
|
168
|
+
}, [finalSnippets]);
|
|
169
|
+
// Determine active snippets (filtered by language if language dropdown is active)
|
|
170
|
+
var activeSnippets = useMemo(function () {
|
|
171
|
+
if (activeLanguage && snippetsByLanguage.size > 0) {
|
|
172
|
+
return snippetsByLanguage.get(activeLanguage) || [];
|
|
173
|
+
}
|
|
174
|
+
return finalSnippets;
|
|
175
|
+
}, [finalSnippets, activeLanguage, snippetsByLanguage]);
|
|
176
|
+
// Get current snippet to display
|
|
177
|
+
var currentSnippet = activeSnippets[activeTabIndex] || activeSnippets[0];
|
|
178
|
+
// Highlight code with Prism
|
|
179
|
+
var highlightedCode = useMemo(function () {
|
|
180
|
+
if (!currentSnippet)
|
|
181
|
+
return "";
|
|
182
|
+
var lang = currentSnippet.language || "text";
|
|
183
|
+
try {
|
|
184
|
+
return Prism.highlight(currentSnippet.code, Prism.languages[lang] || Prism.languages.text, lang);
|
|
185
|
+
}
|
|
186
|
+
catch (e) {
|
|
187
|
+
return Prism.highlight(currentSnippet.code, Prism.languages.text, "text");
|
|
188
|
+
}
|
|
189
|
+
}, [currentSnippet]);
|
|
190
|
+
// Copy to clipboard
|
|
191
|
+
var handleCopy = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
192
|
+
var err_1;
|
|
193
|
+
return __generator(this, function (_a) {
|
|
194
|
+
switch (_a.label) {
|
|
195
|
+
case 0:
|
|
196
|
+
if (!currentSnippet)
|
|
197
|
+
return [2 /*return*/];
|
|
198
|
+
_a.label = 1;
|
|
199
|
+
case 1:
|
|
200
|
+
_a.trys.push([1, 3, , 4]);
|
|
201
|
+
return [4 /*yield*/, navigator.clipboard.writeText(currentSnippet.code)];
|
|
202
|
+
case 2:
|
|
203
|
+
_a.sent();
|
|
204
|
+
setCopied(true);
|
|
205
|
+
setTimeout(function () { return setCopied(false); }, 2000);
|
|
206
|
+
return [3 /*break*/, 4];
|
|
207
|
+
case 3:
|
|
208
|
+
err_1 = _a.sent();
|
|
209
|
+
console.error("Failed to copy:", err_1);
|
|
210
|
+
return [3 /*break*/, 4];
|
|
211
|
+
case 4: return [2 /*return*/];
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}); };
|
|
215
|
+
// Initialize active language
|
|
216
|
+
useEffect(function () {
|
|
217
|
+
if (snippetsByLanguage.size > 0 && !activeLanguage) {
|
|
218
|
+
var firstLang = Array.from(snippetsByLanguage.keys())[0];
|
|
219
|
+
setActiveLanguage(firstLang);
|
|
220
|
+
setActiveTabIndex(0);
|
|
221
|
+
}
|
|
222
|
+
}, [snippetsByLanguage, activeLanguage]);
|
|
223
|
+
if (loading) {
|
|
224
|
+
return (_jsx("div", { className: "dds-code-block dds-code-block-loading ".concat(className), children: _jsx("div", { className: "dds-code-block-loading-text", children: "Loading code snippets..." }) }));
|
|
225
|
+
}
|
|
226
|
+
if (error) {
|
|
227
|
+
return (_jsx("div", { className: "dds-code-block dds-code-block-error ".concat(className), children: _jsxs("div", { className: "dds-code-block-error-text", children: ["Error: ", error] }) }));
|
|
228
|
+
}
|
|
229
|
+
if (!currentSnippet) {
|
|
230
|
+
return null;
|
|
231
|
+
}
|
|
232
|
+
var hasTabs = activeSnippets.length > 1;
|
|
233
|
+
var hasLanguageDropdown = snippetsByLanguage.size > 1;
|
|
234
|
+
var displayLanguage = currentSnippet.language || "text";
|
|
235
|
+
return (_jsxs("div", { className: "dds-code-block ".concat(className), children: [_jsxs("div", { className: "dds-code-block-header", children: [_jsx("div", { className: "dds-code-block-header-left", children: hasTabs && (_jsx("div", { className: "dds-code-block-tabs", children: activeSnippets.map(function (snippet, index) { return (_jsx("button", { className: "dds-code-block-tab ".concat(index === activeTabIndex ? "dds-code-block-tab-active" : ""), onClick: function () { return setActiveTabIndex(index); }, type: "button", children: snippet.tabTitle || snippet.filename || "Tab ".concat(index + 1) }, index)); }) })) }), _jsxs("div", { className: "dds-code-block-header-right", children: [hasLanguageDropdown && (_jsx("select", { className: "dds-code-block-language-select", value: activeLanguage || "", onChange: function (e) {
|
|
236
|
+
setActiveLanguage(e.target.value);
|
|
237
|
+
setActiveTabIndex(0);
|
|
238
|
+
}, children: Array.from(snippetsByLanguage.keys()).map(function (lang) { return (_jsx("option", { value: lang, children: lang }, lang)); }) })), !hasLanguageDropdown && displayLanguage !== "text" && (_jsx("span", { className: "dds-code-block-language-label", children: displayLanguage })), _jsx("button", { className: "dds-code-block-copy-button", onClick: handleCopy, type: "button", "aria-label": "Copy code", children: copied ? "Copied!" : "Copy" })] })] }), _jsxs("div", { className: "dds-code-block-content", children: [_jsx("pre", { className: "language-".concat(displayLanguage), children: _jsx("code", { className: "language-".concat(displayLanguage), dangerouslySetInnerHTML: { __html: highlightedCode } }) }), currentSnippet.highlightLines &&
|
|
239
|
+
currentSnippet.highlightLines.length > 0 && (_jsx("div", { className: "dds-code-block-line-highlights", children: currentSnippet.highlightLines.map(function (lineNum) { return (_jsx("div", { className: "dds-code-block-line-highlight", style: { top: "".concat((lineNum - 1) * 1.5, "em") } }, lineNum)); }) }))] })] }));
|
|
240
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { CodeBlock } from './CodeBlock';
|
|
3
|
+
/**
|
|
4
|
+
* The CodeBlock component displays code snippets with syntax highlighting, copy functionality, and support for multiple tabs and languages.
|
|
5
|
+
*/
|
|
6
|
+
declare const meta: Meta<typeof CodeBlock>;
|
|
7
|
+
export default meta;
|
|
8
|
+
type Story = StoryObj<typeof CodeBlock>;
|
|
9
|
+
/**
|
|
10
|
+
* Basic code block with a simple code snippet.
|
|
11
|
+
*/
|
|
12
|
+
export declare const Basic: Story;
|
|
13
|
+
/**
|
|
14
|
+
* Code block with a filename displayed.
|
|
15
|
+
*/
|
|
16
|
+
export declare const WithFilename: Story;
|
|
17
|
+
/**
|
|
18
|
+
* Code block with highlighted lines.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WithHighlightedLines: Story;
|
|
21
|
+
/**
|
|
22
|
+
* Multiple code snippets displayed in tabs.
|
|
23
|
+
*/
|
|
24
|
+
export declare const WithTabs: Story;
|
|
25
|
+
/**
|
|
26
|
+
* Multiple language versions with language dropdown.
|
|
27
|
+
*/
|
|
28
|
+
export declare const WithLanguageDropdown: Story;
|
|
29
|
+
/**
|
|
30
|
+
* Complex example with tabs and highlighted lines.
|
|
31
|
+
*/
|
|
32
|
+
export declare const ComplexExample: Story;
|
|
33
|
+
/**
|
|
34
|
+
* Example using path prop to load from external markdown file.
|
|
35
|
+
* Note: This will only work if the path is accessible.
|
|
36
|
+
*/
|
|
37
|
+
export declare const WithPath: Story;
|
|
38
|
+
/**
|
|
39
|
+
* JSON code example.
|
|
40
|
+
*/
|
|
41
|
+
export declare const JSONExample: Story;
|
|
42
|
+
/**
|
|
43
|
+
* Bash/shell script example.
|
|
44
|
+
*/
|
|
45
|
+
export declare const BashExample: Story;
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { CodeBlock } from './CodeBlock';
|
|
2
|
+
/**
|
|
3
|
+
* The CodeBlock component displays code snippets with syntax highlighting, copy functionality, and support for multiple tabs and languages.
|
|
4
|
+
*/
|
|
5
|
+
var meta = {
|
|
6
|
+
title: 'Components/CodeBlock',
|
|
7
|
+
component: CodeBlock,
|
|
8
|
+
tags: ['autodocs'],
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component: "\nThe CodeBlock component provides a comprehensive solution for displaying code snippets in documentation.\n\n## Features\n\n- **Syntax Highlighting**: Uses PrismJS for syntax highlighting across many languages\n- **Copy to Clipboard**: One-click copy button for easy code sharing\n- **Line Highlighting**: Highlight specific lines to draw attention\n- **Tabs**: Display multiple code snippets in tabs (typically for different files)\n- **Language Switching**: Dropdown to switch between different language versions\n- **External Snippets**: Load code snippets from markdown files via path prop\n\n## When to Use\n\n- For code examples in documentation\n- When displaying multiple related code snippets\n- For showing code in different languages (TypeScript, JavaScript, etc.)\n- When you need users to easily copy code\n\n## When Not to Use\n\n- For inline code (use `<code>` tags)\n- For code that doesn't need syntax highlighting\n- For executable code editors (use a code editor component)\n\n## Accessibility\n\n- Copy button includes proper ARIA labels\n- Keyboard navigation for tabs and dropdowns\n- High contrast syntax highlighting colors\n ",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
export default meta;
|
|
18
|
+
/**
|
|
19
|
+
* Basic code block with a simple code snippet.
|
|
20
|
+
*/
|
|
21
|
+
export var Basic = {
|
|
22
|
+
args: {
|
|
23
|
+
code: "function greet(name: string) {\n return `Hello, ${name}!`;\n}\n\nconsole.log(greet(\"World\"));",
|
|
24
|
+
language: 'typescript',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Code block with a filename displayed.
|
|
29
|
+
*/
|
|
30
|
+
export var WithFilename = {
|
|
31
|
+
args: {
|
|
32
|
+
code: "export const Button = ({ children, onClick }) => {\n return (\n <button onClick={onClick}>\n {children}\n </button>\n );\n};",
|
|
33
|
+
language: 'jsx',
|
|
34
|
+
filename: 'Button.jsx',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Code block with highlighted lines.
|
|
39
|
+
*/
|
|
40
|
+
export var WithHighlightedLines = {
|
|
41
|
+
args: {
|
|
42
|
+
code: "function calculateTotal(items) {\n let total = 0;\n for (const item of items) {\n total += item.price * item.quantity;\n }\n return total;\n}",
|
|
43
|
+
language: 'javascript',
|
|
44
|
+
highlightLines: [2, 3, 4],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Multiple code snippets displayed in tabs.
|
|
49
|
+
*/
|
|
50
|
+
export var WithTabs = {
|
|
51
|
+
args: {
|
|
52
|
+
snippets: [
|
|
53
|
+
{
|
|
54
|
+
code: "export const Button = ({ children }) => {\n return <button>{children}</button>;\n};",
|
|
55
|
+
language: 'jsx',
|
|
56
|
+
filename: 'Button.jsx',
|
|
57
|
+
tabTitle: 'Button.jsx',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: ".button {\n padding: 0.5rem 1rem;\n background-color: blue;\n color: white;\n}",
|
|
61
|
+
language: 'css',
|
|
62
|
+
filename: 'Button.css',
|
|
63
|
+
tabTitle: 'Button.css',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
code: "import { Button } from './Button';\n\nexport default {\n component: Button,\n};",
|
|
67
|
+
language: 'typescript',
|
|
68
|
+
filename: 'Button.stories.ts',
|
|
69
|
+
tabTitle: 'Button.stories.ts',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Multiple language versions with language dropdown.
|
|
76
|
+
*/
|
|
77
|
+
export var WithLanguageDropdown = {
|
|
78
|
+
args: {
|
|
79
|
+
snippets: [
|
|
80
|
+
{
|
|
81
|
+
code: "function greet(name: string): string {\n return `Hello, ${name}!`;\n}",
|
|
82
|
+
language: 'typescript',
|
|
83
|
+
filename: 'greet.ts',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
code: "function greet(name) {\n return `Hello, ${name}!`;\n}",
|
|
87
|
+
language: 'javascript',
|
|
88
|
+
filename: 'greet.js',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
code: "def greet(name):\n return f\"Hello, {name}!\"",
|
|
92
|
+
language: 'python',
|
|
93
|
+
filename: 'greet.py',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
code: "def greet(name)\n \"Hello, #{name}!\"\nend",
|
|
97
|
+
language: 'ruby',
|
|
98
|
+
filename: 'greet.rb',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Complex example with tabs and highlighted lines.
|
|
105
|
+
*/
|
|
106
|
+
export var ComplexExample = {
|
|
107
|
+
args: {
|
|
108
|
+
snippets: [
|
|
109
|
+
{
|
|
110
|
+
code: "import React from 'react';\n\nexport function Button({ children, onClick }) {\n return (\n <button onClick={onClick} className=\"btn\">\n {children}\n </button>\n );\n}",
|
|
111
|
+
language: 'jsx',
|
|
112
|
+
filename: 'Button.jsx',
|
|
113
|
+
tabTitle: 'Button.jsx',
|
|
114
|
+
highlightLines: [3, 4, 5],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
code: ".btn {\n padding: 0.5rem 1rem;\n background-color: #3b82f6;\n color: white;\n border-radius: 0.375rem;\n}",
|
|
118
|
+
language: 'css',
|
|
119
|
+
filename: 'Button.css',
|
|
120
|
+
tabTitle: 'Button.css',
|
|
121
|
+
highlightLines: [2, 3],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Example using path prop to load from external markdown file.
|
|
128
|
+
* Note: This will only work if the path is accessible.
|
|
129
|
+
*/
|
|
130
|
+
export var WithPath = {
|
|
131
|
+
args: {
|
|
132
|
+
path: 'https://raw.githubusercontent.com/storybookjs/storybook/47e331ffbaa61a476ddb873bdb12bf46a93a5131/docs/_snippets/before-each-in-meta-mock-date.md',
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* JSON code example.
|
|
137
|
+
*/
|
|
138
|
+
export var JSONExample = {
|
|
139
|
+
args: {
|
|
140
|
+
code: "{\n \"name\": \"docs-design-system\",\n \"version\": \"0.1.0\",\n \"dependencies\": {\n \"react\": \"^18.0.0\"\n }\n}",
|
|
141
|
+
language: 'json',
|
|
142
|
+
filename: 'package.json',
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* Bash/shell script example.
|
|
147
|
+
*/
|
|
148
|
+
export var BashExample = {
|
|
149
|
+
args: {
|
|
150
|
+
code: "#!/bin/bash\n\n# Install dependencies\nnpm install\n\n# Build the project\nnpm run build\n\n# Run tests\nnpm test",
|
|
151
|
+
language: 'bash',
|
|
152
|
+
filename: 'setup.sh',
|
|
153
|
+
},
|
|
154
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
interface CollapserProps {
|
|
3
|
+
/** Title text or element displayed in the collapsible header */
|
|
4
|
+
title: string | ReactNode;
|
|
5
|
+
/** Optional ID for the title element */
|
|
6
|
+
id?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Whether the collapser should be open by default
|
|
9
|
+
* @default false
|
|
10
|
+
*/
|
|
11
|
+
defaultOpen?: boolean;
|
|
12
|
+
/** Content to show/hide when toggling */
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
/** Additional CSS classes */
|
|
15
|
+
className?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function Collapser({ title, id, defaultOpen, children, className, }: CollapserProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from "react";
|
|
3
|
+
import { useKeyPress } from "../hooks/useKeyPress";
|
|
4
|
+
export function Collapser(_a) {
|
|
5
|
+
var title = _a.title, id = _a.id, _b = _a.defaultOpen, defaultOpen = _b === void 0 ? false : _b, children = _a.children, _c = _a.className, className = _c === void 0 ? "" : _c;
|
|
6
|
+
var _d = useState(defaultOpen), isOpen = _d[0], setIsOpen = _d[1];
|
|
7
|
+
var _e = useState(undefined), height = _e[0], setHeight = _e[1];
|
|
8
|
+
var contentRef = useRef(null);
|
|
9
|
+
// Keyboard shortcuts: 's' or 'f' to show, 'h' to hide
|
|
10
|
+
useKeyPress(['s', 'f', 'h'], function (e) { return setIsOpen(e.key !== 'h'); });
|
|
11
|
+
useEffect(function () {
|
|
12
|
+
if (contentRef.current) {
|
|
13
|
+
setHeight(contentRef.current.scrollHeight);
|
|
14
|
+
}
|
|
15
|
+
}, [children]);
|
|
16
|
+
var toggleOpen = function () {
|
|
17
|
+
setIsOpen(!isOpen);
|
|
18
|
+
};
|
|
19
|
+
var collapserClasses = ["dds-collapser", className]
|
|
20
|
+
.filter(Boolean)
|
|
21
|
+
.join(" ");
|
|
22
|
+
return (_jsxs("div", { className: collapserClasses, children: [_jsxs("button", { onClick: toggleOpen, type: "button", className: "dds-collapser-button", "aria-expanded": isOpen, children: [_jsx("h5", { id: id, className: "dds-collapser-title", children: title }), _jsx("svg", { className: "dds-collapser-icon ".concat(isOpen ? "dds-collapser-icon-open" : ""), width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: _jsx("polyline", { points: "6 9 12 15 18 9" }) })] }), _jsx("div", { className: "dds-collapser-content-wrapper", style: {
|
|
23
|
+
height: isOpen ? height : 0,
|
|
24
|
+
}, children: _jsx("div", { ref: contentRef, className: "dds-collapser-content", children: children }) })] }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react';
|
|
2
|
+
import { Collapser } from './Collapser';
|
|
3
|
+
/**
|
|
4
|
+
* The Collapser component creates expandable/collapsible content sections with smooth animations.
|
|
5
|
+
*/
|
|
6
|
+
declare const meta: Meta<typeof Collapser>;
|
|
7
|
+
export default meta;
|
|
8
|
+
type Story = StoryObj<typeof Collapser>;
|
|
9
|
+
/**
|
|
10
|
+
* Basic collapser that starts collapsed.
|
|
11
|
+
*/
|
|
12
|
+
export declare const Basic: Story;
|
|
13
|
+
/**
|
|
14
|
+
* Collapser that starts in the open state.
|
|
15
|
+
*/
|
|
16
|
+
export declare const DefaultOpen: Story;
|
|
17
|
+
/**
|
|
18
|
+
* Collapser with an ID for the title.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WithID: Story;
|
|
21
|
+
/**
|
|
22
|
+
* Collapser containing complex content.
|
|
23
|
+
*/
|
|
24
|
+
export declare const ComplexContent: Story;
|
|
25
|
+
/**
|
|
26
|
+
* Multiple collapsers in a FAQ-style layout.
|
|
27
|
+
*/
|
|
28
|
+
export declare const FAQExample: Story;
|