react-markdown-table-ts 1.3.0 → 1.3.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-markdown-table-ts",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "A React component that converts structured data into Markdown table syntax and displays it within a `<pre>` tag.",
5
5
  "keywords": [
6
6
  "markdown",
@@ -13,13 +13,37 @@
13
13
  "convert",
14
14
  "converter"
15
15
  ],
16
+ "type": "module",
17
+ "main": "./dist/index.cjs",
18
+ "module": "./dist/index.mjs",
19
+ "types": "./dist/index.d.ts",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/index.d.ts",
24
+ "default": "./dist/index.mjs"
25
+ },
26
+ "require": {
27
+ "types": "./dist/index.d.ts",
28
+ "default": "./dist/index.cjs"
29
+ }
30
+ }
31
+ },
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "sideEffects": false,
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/keithwalsh/react-markdown-table-ts.git"
39
+ },
16
40
  "homepage": "https://github.com/keithwalsh/react-markdown-table-ts#readme",
17
41
  "bugs": {
18
42
  "url": "https://github.com/keithwalsh/react-markdown-table-ts/issues"
19
43
  },
20
44
  "scripts": {
21
45
  "dev": "vite",
22
- "build": "tsc -b && vite build",
46
+ "build": "tsc -p tsconfig.build.json && vite build",
23
47
  "preview": "vite preview",
24
48
  "test": "jest",
25
49
  "test:watch": "jest --watch",
@@ -27,6 +51,10 @@
27
51
  "storybook": "storybook dev -p 6006",
28
52
  "build-storybook": "storybook build"
29
53
  },
54
+ "peerDependencies": {
55
+ "react": ">=18.0.0",
56
+ "react-dom": ">=18.0.0"
57
+ },
30
58
  "dependencies": {
31
59
  "prismjs": "^1.30.0",
32
60
  "react": "^19.1.1",
@@ -1,35 +0,0 @@
1
- name: build
2
-
3
- on:
4
- push:
5
- branches: [main]
6
- pull_request:
7
- branches: [main]
8
-
9
- jobs:
10
- build:
11
- runs-on: ubuntu-latest
12
-
13
- steps:
14
- - name: Checkout code
15
- uses: actions/checkout@v4
16
- with:
17
- fetch-depth: 0
18
-
19
- - name: Setup Node.js
20
- uses: actions/setup-node@v4
21
- with:
22
- node-version: 24
23
- cache: 'npm'
24
-
25
- - name: Install dependencies
26
- run: npm ci
27
-
28
- - name: Build Storybook
29
- run: npm run build-storybook
30
-
31
- - name: Publish to Chromatic
32
- uses: chromaui/action@latest
33
- with:
34
- projectToken: ${{ secrets.CHROMATIC_TOKEN }}
35
- storybookBuildDir: storybook-static
@@ -1,31 +0,0 @@
1
- name: Publish to NPM
2
-
3
- on:
4
- workflow_dispatch:
5
-
6
- jobs:
7
- publish:
8
- runs-on: ubuntu-latest
9
- steps:
10
- - name: Checkout repository
11
- uses: actions/checkout@v4
12
-
13
- - name: Setup Node.js
14
- uses: actions/setup-node@v4
15
- with:
16
- node-version: '22'
17
- registry-url: 'https://registry.npmjs.org'
18
-
19
- - name: Install dependencies
20
- run: npm ci
21
-
22
- - name: Run tests
23
- run: npm test
24
-
25
- - name: Build package
26
- run: npx tsc
27
-
28
- - name: Publish to NPM
29
- run: npm publish
30
- env:
31
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -1,42 +0,0 @@
1
- name: Test Coverage
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
-
9
- jobs:
10
- test:
11
- runs-on: ubuntu-latest
12
-
13
- strategy:
14
- matrix:
15
- node-version: [20.x]
16
-
17
- steps:
18
- - name: Checkout code
19
- uses: actions/checkout@v4
20
-
21
- - name: Setup Node.js ${{ matrix.node-version }}
22
- uses: actions/setup-node@v4
23
- with:
24
- node-version: ${{ matrix.node-version }}
25
- cache: 'npm'
26
-
27
- - name: Install dependencies
28
- run: npm ci
29
-
30
- - name: Run tests with coverage
31
- run: npm run test:coverage
32
-
33
- - name: Upload coverage to Codecov
34
- uses: codecov/codecov-action@v4
35
- with:
36
- token: ${{ secrets.CODECOV_TOKEN }}
37
- files: ./coverage/clover.xml,./coverage/lcov.info
38
- flags: unittests
39
- name: codecov-umbrella
40
- fail_ci_if_error: true
41
- verbose: true
42
-
package/.gitignore DELETED
@@ -1,12 +0,0 @@
1
-
2
- node_modules
3
- dist
4
- dist-ssr
5
- package-lock.json
6
- .*
7
- !.github
8
- !.storybook
9
- !.gitignore
10
- coverage/
11
- *storybook.log
12
- storybook-static
@@ -1,13 +0,0 @@
1
- import type { StorybookConfig } from '@storybook/react-vite';
2
-
3
- const config: StorybookConfig = {
4
- "stories": [
5
- "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"
6
- ],
7
- "addons": [],
8
- "framework": {
9
- "name": "@storybook/react-vite",
10
- "options": {}
11
- }
12
- };
13
- export default config;
@@ -1,6 +0,0 @@
1
- import { addons } from "storybook/manager-api";
2
- import theme from "./theme";
3
-
4
- addons.setConfig({
5
- theme: theme,
6
- });
@@ -1,14 +0,0 @@
1
- import type { Preview } from '@storybook/react-vite'
2
-
3
- const preview: Preview = {
4
- parameters: {
5
- controls: {
6
- matchers: {
7
- color: /(background|color)$/i,
8
- date: /Date$/i,
9
- },
10
- },
11
- },
12
- };
13
-
14
- export default preview;
@@ -1,8 +0,0 @@
1
- import { create } from "storybook/theming";
2
-
3
- export default create({
4
- base: "light",
5
- brandTitle: "react-markdown-table-ts",
6
- brandUrl: "https://github.com/keithwalsh/react-markdown-table-ts",
7
- brandTarget: "_self",
8
- });
package/jest.config.js DELETED
@@ -1,38 +0,0 @@
1
- module.exports = {
2
- preset: 'ts-jest',
3
- testEnvironment: 'jsdom',
4
- roots: ['<rootDir>/src'],
5
- testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
6
- moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
7
- setupFilesAfterEnv: ['<rootDir>/src/tests/setup.ts'],
8
- collectCoverageFrom: [
9
- 'src/**/*.{ts,tsx}',
10
- '!src/**/*.d.ts',
11
- '!src/stories/**',
12
- '!src/tests/**',
13
- ],
14
- coverageThreshold: {
15
- global: {
16
- branches: 80,
17
- functions: 80,
18
- lines: 80,
19
- statements: 80,
20
- },
21
- },
22
- transform: {
23
- '^.+\\.tsx?$': ['ts-jest', {
24
- tsconfig: {
25
- jsx: 'react-jsx',
26
- esModuleInterop: true,
27
- allowSyntheticDefaultImports: true,
28
- verbatimModuleSyntax: false,
29
- module: 'commonjs',
30
- moduleResolution: 'node',
31
- },
32
- }],
33
- },
34
- moduleNameMapper: {
35
- '\\.(css|less|scss|sass)$': '<rootDir>/src/tests/__mocks__/styleMock.js',
36
- },
37
- };
38
-
package/public/dark.png DELETED
Binary file
package/public/light.png DELETED
Binary file
package/src/index.tsx DELETED
@@ -1,176 +0,0 @@
1
- /**
2
- * @fileoverview Main MarkdownTable component that generates and displays markdown
3
- * table syntax with Prism.js syntax highlighting.
4
- */
5
-
6
- import { useEffect, useMemo, useRef, useDeferredValue, useTransition, useId } from 'react';
7
- import Prism from 'prismjs';
8
- import 'prismjs/components/prism-markdown';
9
- import 'prismjs/plugins/line-numbers/prism-line-numbers';
10
- import type { Alignment, MarkdownTableProps } from './types';
11
- import { generateMarkdownTableString, generateAlphabetHeaders } from './utils';
12
- import { validateInputData, MarkdownTableError } from './validation';
13
-
14
- const LIGHT_THEME_CSS = `
15
- code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}pre[class*=language-].line-numbers{position:relative;padding-left:2.4em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.5em;text-align:right}
16
- `;
17
-
18
- const DARK_THEME_CSS = `
19
- code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#282a36}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}pre[class*=language-].line-numbers{position:relative;padding-left:2.4em;counter-reset:linenumber}pre[class*=language-].line-numbers>code{position:relative;white-space:inherit}.line-numbers .line-numbers-rows{position:absolute;pointer-events:none;top:0;font-size:100%;left:-3.8em;width:3em;letter-spacing:-1px;border-right:1px solid #999;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.line-numbers-rows>span{display:block;counter-increment:linenumber}.line-numbers-rows>span:before{content:counter(linenumber);color:#999;display:block;padding-right:.5em;text-align:right}
20
- `;
21
-
22
- function getTableData(inputData: string[][], hasHeader: boolean) {
23
- return hasHeader
24
- ? { inputDataHeader: inputData[0], inputDataBody: inputData.slice(1) }
25
- : { inputDataHeader: generateAlphabetHeaders(inputData[0].length), inputDataBody: inputData };
26
- }
27
-
28
- function generateTableSyntax(
29
- inputData: string[][] | null,
30
- hasHeader: boolean,
31
- columnAlignments: readonly Alignment[],
32
- adjustColumnWidths: boolean,
33
- hasTabs: boolean,
34
- canReplaceNewlines: boolean,
35
- hasPadding: boolean
36
- ): string {
37
- try {
38
- validateInputData(inputData);
39
- const { inputDataHeader, inputDataBody } = getTableData(inputData as string[][], hasHeader);
40
-
41
- return generateMarkdownTableString(
42
- { inputDataHeader, inputDataBody },
43
- columnAlignments,
44
- adjustColumnWidths,
45
- hasTabs,
46
- canReplaceNewlines,
47
- hasPadding
48
- );
49
- } catch (error) {
50
- if (error instanceof MarkdownTableError) {
51
- return `Error: ${error.message}`;
52
- }
53
- throw error;
54
- }
55
- }
56
-
57
- export function MarkdownTable({
58
- inputData = null,
59
- hasHeader = true,
60
- columnAlignments = [],
61
- isCompact = false,
62
- hasTabs = false,
63
- hasPadding = true,
64
- convertLineBreaks = false,
65
- className,
66
- onGenerate,
67
- theme = 'light',
68
- preStyle,
69
- topPadding = 16,
70
- minWidth,
71
- showLineNumbers = true,
72
- }: MarkdownTableProps) {
73
- const preElementRef = useRef<HTMLPreElement>(null);
74
- const id = useId();
75
- const deferredInputData = useDeferredValue(inputData);
76
- const [, startTransition] = useTransition();
77
-
78
- const markdownTableSyntax = useMemo(() => generateTableSyntax(
79
- deferredInputData,
80
- hasHeader,
81
- columnAlignments,
82
- !isCompact,
83
- hasTabs,
84
- convertLineBreaks,
85
- hasPadding
86
- ), [
87
- deferredInputData,
88
- hasHeader,
89
- columnAlignments,
90
- isCompact,
91
- hasTabs,
92
- convertLineBreaks,
93
- hasPadding,
94
- ]);
95
-
96
- useEffect(() => {
97
- if (onGenerate) {
98
- onGenerate(markdownTableSyntax);
99
- }
100
- }, [markdownTableSyntax, onGenerate]);
101
-
102
- useEffect(() => {
103
- const codeElement = preElementRef.current?.querySelector('code');
104
- if (codeElement && markdownTableSyntax) {
105
- startTransition(() => {
106
- requestAnimationFrame(() => {
107
- Prism.highlightElement(codeElement as HTMLElement);
108
-
109
- // Remove line numbers if showLineNumbers is false
110
- if (!showLineNumbers && preElementRef.current) {
111
- const lineNumbersRows = preElementRef.current.querySelector('.line-numbers-rows');
112
- if (lineNumbersRows) {
113
- lineNumbersRows.remove();
114
- }
115
- }
116
- });
117
- });
118
- }
119
- }, [markdownTableSyntax, startTransition, showLineNumbers]);
120
-
121
- return (
122
- <>
123
- <style>
124
- {theme === 'light' ? LIGHT_THEME_CSS : DARK_THEME_CSS}
125
- {`
126
- pre {
127
- position: relative;
128
- padding-top: ${topPadding}px !important;
129
- }
130
- pre::before {
131
- position: absolute;
132
- top: 8px;
133
- left: 12px;
134
- color: ${theme === 'light' ? '#666' : '#999'};
135
- letter-spacing: 2px;
136
- font-size: 12px;
137
- }
138
- /* Hide line numbers when showLineNumbers is false */
139
- pre:not(.line-numbers) .line-numbers-rows {
140
- display: none !important;
141
- }
142
- /* Remove left padding when line numbers are hidden */
143
- pre:not(.line-numbers) {
144
- padding-left: 1em !important;
145
- }
146
- pre:not(.line-numbers) > code {
147
- padding-left: 0 !important;
148
- }
149
- `}
150
- </style>
151
- <div
152
- id={id}
153
- style={{
154
- position: 'relative',
155
- isolation: 'isolate',
156
- display: 'inline-block'
157
- }}
158
- >
159
- <pre
160
- ref={preElementRef}
161
- className={`${className} language-markdown ${showLineNumbers ? 'line-numbers' : ''} ${theme === 'dark' ? 'dark-theme' : ''}`}
162
- style={{
163
- width: 'fit-content',
164
- minWidth: minWidth ? `${minWidth}px` : 'min-content',
165
- margin: 0,
166
- ...preStyle
167
- }}
168
- >
169
- <code className="language-markdown" role="code">
170
- {markdownTableSyntax}
171
- </code>
172
- </pre>
173
- </div>
174
- </>
175
- );
176
- }
package/src/prism.d.ts DELETED
@@ -1,6 +0,0 @@
1
- declare module 'prismjs' {
2
- const Prism: {
3
- highlightElement: (element: HTMLElement) => void;
4
- };
5
- export default Prism;
6
- }
@@ -1,212 +0,0 @@
1
- /**
2
- * @fileoverview Storybook stories for the MarkdownTable component, demonstrating
3
- * various configurations and use cases for markdown table generation and display.
4
- */
5
-
6
- import type { Meta, StoryObj } from '@storybook/react';
7
- import { MarkdownTable } from '../index'
8
-
9
- const meta = {
10
- title: 'Components/MarkdownTable',
11
- component: MarkdownTable,
12
- parameters: {
13
- layout: 'centered'
14
- },
15
- tags: ['autodocs'],
16
- args: {
17
- inputData: null,
18
- columnAlignments: [],
19
- isCompact: false,
20
- hasPadding: true,
21
- hasTabs: false,
22
- hasHeader: true,
23
- convertLineBreaks: false,
24
- topPadding: 16,
25
- theme: 'light' as const,
26
- showLineNumbers: true
27
- },
28
- argTypes: {
29
- inputData: {
30
- control: {
31
- type: 'object'
32
- },
33
- description: 'The outer array represents rows. The inner array represent cells within each row.',
34
- table: {
35
- category: 'Core Data Props',
36
- type: {
37
- summary: 'string[][] | null'
38
- }
39
- }
40
- },
41
- columnAlignments: {
42
- control: {
43
- type: 'object'
44
- },
45
- description: 'An array specifying the alignment for each column.',
46
- table: {
47
- category: 'Core Data Props',
48
- type: {
49
- summary: 'readonly Alignment[]',
50
- detail: 'export type Alignment = "left" | "right" | "center" | "none";'
51
- }
52
- }
53
- },
54
- isCompact: {
55
- control: 'boolean',
56
- description: 'Disables column width alignment to provide a more compact markdown table string.',
57
- table: {
58
- category: 'Configuration Props',
59
- type: {
60
- summary: 'boolean'
61
- }
62
- }
63
- },
64
- hasPadding: {
65
- control: 'boolean',
66
- description: 'Optional flag to add a single space around cell content in the markdown table.',
67
- table: {
68
- category: 'Configuration Props',
69
- type: {
70
- summary: 'boolean'
71
- }
72
- }
73
- },
74
- hasTabs: {
75
- control: 'boolean',
76
- description: 'Optional flag to add tabs as additional padding between column pipes.',
77
- table: {
78
- category: 'Configuration Props',
79
- type: {
80
- summary: 'boolean'
81
- }
82
- }
83
- },
84
- hasHeader: {
85
- control: 'boolean',
86
- description: 'Indicates whether the first row of `data` is a header.',
87
- table: {
88
- category: 'Configuration Props',
89
- type: {
90
- summary: 'boolean'
91
- }
92
- }
93
- },
94
- convertLineBreaks: {
95
- control: 'boolean',
96
- description: 'Optional flag to replace newlines with `<br>` tags in table cells.',
97
- table: {
98
- category: 'Configuration Props',
99
- type: {
100
- summary: 'boolean'
101
- }
102
- }
103
- },
104
- topPadding: {
105
- control: {
106
- type: 'number'
107
- },
108
- description: 'Controls the padding-top (in pixels) of the pre element display.',
109
- table: {
110
- category: 'Visual/UI Props',
111
- type: {
112
- summary: 'number'
113
- }
114
- }
115
- },
116
- theme: {
117
- options: ['light', 'dark'],
118
- control: { type: 'select' },
119
- description: 'Switch between light and dark mode.',
120
- table: {
121
- category: 'Visual/UI Props',
122
- type: {
123
- summary: "'light' | 'dark'"
124
- },
125
- defaultValue: { summary: 'light' }
126
- }
127
- },
128
- className: {
129
- control: 'text',
130
- description: 'Optional CSS class for styling the rendered Markdown table.',
131
- table: {
132
- category: 'Visual/UI Props',
133
- type: {
134
- summary: 'string'
135
- }
136
- }
137
- },
138
- preStyle: {
139
- control: {
140
- type: 'object'
141
- },
142
- description: 'Optional inline styles for the pre element.',
143
- table: {
144
- category: 'Visual/UI Props',
145
- type: {
146
- summary: 'React.CSSProperties'
147
- }
148
- }
149
- },
150
- minWidth: {
151
- control: {
152
- type: 'number'
153
- },
154
- description: 'Optional minimum width in pixels for the table container.',
155
- table: {
156
- category: 'Visual/UI Props',
157
- type: {
158
- summary: 'number'
159
- }
160
- }
161
- },
162
- showLineNumbers: {
163
- control: 'boolean',
164
- description: 'Optional flag to show or hide line numbers in the Prism syntax highlighting.',
165
- table: {
166
- category: 'Visual/UI Props',
167
- type: {
168
- summary: 'boolean'
169
- },
170
- defaultValue: { summary: 'true' }
171
- }
172
- },
173
- onGenerate: {
174
- description: 'Callback function that receives the generated Markdown table string.',
175
- table: {
176
- category: 'Callbacks',
177
- type: {
178
- summary: '(markdownTableString: string) => void'
179
- }
180
- }
181
- }
182
- }
183
- } satisfies Meta<typeof MarkdownTable>
184
-
185
- export default meta
186
- type Story = StoryObj<typeof MarkdownTable>
187
-
188
- // Move sample data before its usage
189
- const sampleData = [
190
- ['Package ID', 'Weight (kg)', 'Status', 'Destination'],
191
- ['PKG-2024-001', '12.50', 'In Transit', 'Dublin, IE'],
192
- ['PKG-2024-002', '3.75', 'Delivered', 'New York, US'],
193
- ['PKG-2024-003', '8.20', 'Processing', 'Frankfurt, DE'],
194
- ['PKG-2024-004', '5.60', 'In Transit', 'London, GB']
195
- ]
196
-
197
- export const Default: Story = {
198
- args: {
199
- inputData: sampleData,
200
- columnAlignments: ['left', 'right', 'center', 'none'],
201
- isCompact: false,
202
- hasPadding: true,
203
- hasTabs: false,
204
- hasHeader: true,
205
- convertLineBreaks: false,
206
- topPadding: 16,
207
- theme: 'light',
208
- className: 'markdown-table-pre',
209
- preStyle: {borderRadius: '8px'},
210
- minWidth: 450
211
- }
212
- }
@@ -1,2 +0,0 @@
1
- module.exports = {};
2
-