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'); +}