diff --git a/packages/tempo-md/src/index.ts b/packages/tempo-md/src/index.ts
index d3c7351..38b8352 100644
--- a/packages/tempo-md/src/index.ts
+++ b/packages/tempo-md/src/index.ts
@@ -2,5 +2,6 @@ export * from './lib/code-block';
export * from './lib/emoji';
export * from './lib/frontmatter';
export * from './lib/markdown';
+export * from './lib/details';
export { default as supportedLanguages } from './lib/data/supported-languages';
export { default as supportedEmojis } from './lib/data/emojis';
diff --git a/packages/tempo-md/src/lib/__tests__/details.test.ts b/packages/tempo-md/src/lib/__tests__/details.test.ts
new file mode 100644
index 0000000..265020d
--- /dev/null
+++ b/packages/tempo-md/src/lib/__tests__/details.test.ts
@@ -0,0 +1,17 @@
+import { describe, expect, it } from 'vitest';
+import { details } from '../details';
+
+describe('details', () => {
+ it('should return a string', () => {
+ expect(details({ summary: 'Click me', content: 'This is the content of the details element.' })).toBe(
+ `
+
+ Click me
+
+ This is the content of the details element.
+
+
+ `.trim()
+ );
+ });
+});
\ No newline at end of file
diff --git a/packages/tempo-md/src/lib/details.ts b/packages/tempo-md/src/lib/details.ts
new file mode 100644
index 0000000..c130fbf
--- /dev/null
+++ b/packages/tempo-md/src/lib/details.ts
@@ -0,0 +1,29 @@
+export interface DetailsConfig {
+ summary: string;
+ content: string;
+}
+
+/**
+ * Creates a details element.
+ *
+ * @example
+ * ```ts
+ * const details = details({
+ * summary: 'Click me',
+ * content: 'This is the content of the details element.'
+ * });
+ * ```
+ *
+ * @param config - The configuration for the details element.
+ * @returns The details element as a string.
+ */
+export function details(config: DetailsConfig): string {
+ return `
+
+ ${config.summary}
+
+ ${config.content}
+
+
+ `.trim();
+}
diff --git a/packages/tempo-md/src/lib/index.ts b/packages/tempo-md/src/lib/index.ts
index 7b9bef8..fe456c9 100644
--- a/packages/tempo-md/src/lib/index.ts
+++ b/packages/tempo-md/src/lib/index.ts
@@ -6,4 +6,5 @@ export {
} from './emoji';
export { default as supportedLanguages } from './data/supported-languages';
export { default as supportedEmojis } from './data/emojis';
+export { details, type DetailsConfig } from './details';
export * as default from './markdown';
diff --git a/packages/tempo/src/lib/__tests__/tempo-document.test.ts b/packages/tempo/src/lib/__tests__/tempo-document.test.ts
index 191cf4e..53383f6 100644
--- a/packages/tempo/src/lib/__tests__/tempo-document.test.ts
+++ b/packages/tempo/src/lib/__tests__/tempo-document.test.ts
@@ -116,6 +116,15 @@ describe('Text Elements', () => {
});
describe('Special Elements', () => {
+
+ it('should add a details element', () => {
+ const document = new TempoDocument().details({
+ summary: 'Hello World!',
+ content: ['Hello 2 World!', 'Hello 3 World!'],
+ });
+ expect(document.toJSON().nodes).toEqual([]);
+ });
+
it('should add a table', () => {
const document = new TempoDocument().table([
['Hello World!', 'Hello 2 World!'],
diff --git a/packages/tempo/src/lib/tempo-document.ts b/packages/tempo/src/lib/tempo-document.ts
index effa51f..beab703 100644
--- a/packages/tempo/src/lib/tempo-document.ts
+++ b/packages/tempo/src/lib/tempo-document.ts
@@ -34,7 +34,8 @@ export type DocumentNodeType =
| 'break'
| 'numberList'
| 'bulletList'
- | 'alert';
+ | 'alert'
+ | 'details';
interface BaseDocumentNode {
type: DocumentNodeType;
@@ -58,6 +59,27 @@ export interface ParagraphNode extends BaseDocumentNode {
type: 'paragraph';
}
+interface DetailsSummaryNode {
+ type: 'details-summary';
+ data: {
+ nodes: TempoTextNode[];
+ };
+ computed: string;
+}
+
+interface DetailsContentNode {
+ type: 'details-content';
+ data: {
+ nodes: TempoDocumentNode[];
+ };
+ computed: string;
+}
+
+export interface DetailsNode
+ extends BaseDocumentNode<[DetailsSummaryNode, DetailsContentNode]> {
+ type: 'details';
+}
+
interface TableRow {
type: T;
order: T extends 'row' ? number : undefined;
@@ -144,7 +166,8 @@ export type TempoDocumentNode =
| BreakNode
| NumberListNode
| BulletListNode
- | AlertNode;
+ | AlertNode
+ | DetailsNode;
export type TempoDocumentMetadata = md.Frontmatter | null;
@@ -406,6 +429,62 @@ export class TempoDocument {
|------------------
*/
+ /**
+ * Append a details element to the document.
+ *
+ * @example
+ * ```ts
+ * const doc = tempo()
+ * .details({
+ * summary: 'Click me',
+ * content: 'This is the content of the details element.'
+ * })
+ * .toString();
+ * // Output:
+ * // Click me
+ * // This is the content of the details element.
+ * //
+ * ```
+ *
+ * @param config - The configuration for the details element.
+ * @returns The TempoDocument instance with the details element appended.
+ */
+ public details({
+ summary,
+ content,
+ }: {
+ summary: TempoTextInput;
+ content: Array;
+ }): this {
+ this.nodes.push({
+ type: 'details',
+ data: {
+ nodes: [
+ {
+ type: 'details-summary',
+ data: {
+ nodes: computeNodes(summary),
+ },
+ computed: md.paragraph(computeText(summary)),
+ },
+ {
+ type: 'details-content',
+ data: {
+ nodes: content.map(computeNodes),
+ },
+ computed: content.map((node) => node.computed).join('\n'),
+ },
+ ],
+ },
+ computed: md.details({
+ summary: md.paragraph(computeText(summary)),
+ content: transformToMarkdown(content),
+ }),
+ });
+
+ return this;
+ }
+
/**
* Append a table to the document.
*
@@ -736,11 +815,7 @@ export class TempoDocument {
* @returns A string representation of the document, that can be used for rendering.
*/
public toString(): string {
- const result = this.nodes
- .map((section) => section.computed)
- .join('\n\n')
- .trim()
- .concat('\n');
+ const result = transformToMarkdown(this.nodes);
if (this.metadata) {
return md.frontmatter(this.metadata).concat('\n', result);
@@ -794,3 +869,11 @@ export class TempoDocument {
};
}
}
+
+function transformToMarkdown(nodes: TempoDocumentNode[]): string {
+ return nodes
+ .map((section) => section.computed)
+ .join('\n\n')
+ .trim()
+ .concat('\n');
+}