md-editor-lite 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 ADDED
@@ -0,0 +1,175 @@
1
+ # md-editor-lite
2
+
3
+ A lightweight React markdown editor focused on the **core editing experience**.
4
+ Built with **TypeScript**, designed for **library-friendly usage**, and has **no runtime dependency other than React**.
5
+
6
+ > Minimal. Predictable. Easy to integrate.
7
+
8
+ ---
9
+
10
+ ## ✨ Features
11
+
12
+ - ⚡ **Minimal dependencies**
13
+ → No heavy editor engines, no unnecessary packages
14
+
15
+ - 🔁 **Controlled & Uncontrolled modes**
16
+ → Works well with forms and external state management
17
+
18
+ - 🧩 **Customizable toolbar**
19
+ → Enable only the features you need
20
+
21
+ - 🎨 **No CSS framework dependency**
22
+ → No Tailwind, no opinionated styles
23
+ → Fully styleable with plain CSS
24
+
25
+ ---
26
+
27
+ ## 🧠 Supported Markdown Syntax
28
+
29
+ - **Bold**
30
+ - _Italic_
31
+ - `Inline code`
32
+ - > Blockquote
33
+ - Headings (`#`, `##`, `###`)
34
+ - Ordered / Unordered lists
35
+ - Images: `![alt](url)`
36
+ - Links: `[text](url)`
37
+
38
+ ---
39
+
40
+ ## 📦 Installation
41
+
42
+ ```bash
43
+ npm install md-editor-lite
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 🚀 Basic Usage (Uncontrolled)
49
+
50
+ ```tsx
51
+ import { MarkdownEditor } from "md-editor-lite";
52
+
53
+ function App() {
54
+ return <MarkdownEditor />;
55
+ }
56
+ ```
57
+
58
+ ---
59
+
60
+ ## 🎛 Controlled Usage
61
+
62
+ ```tsx
63
+ import { useState } from "react";
64
+ import { MarkdownEditor } from "md-editor-lite";
65
+
66
+ function App() {
67
+ const [value, setValue] = useState("");
68
+
69
+ return (
70
+ <MarkdownEditor
71
+ value={value}
72
+ onChange={setValue}
73
+ placeholder="Write your markdown here..."
74
+ />
75
+ );
76
+ }
77
+ ```
78
+
79
+ > Controlled mode is recommended when:
80
+ >
81
+ > - Integrating with forms
82
+ > - Saving content externally
83
+ > - Syncing editor state with other UI
84
+
85
+ ---
86
+
87
+ ## 🧩 Props API
88
+
89
+ ### `MarkdownEditor`
90
+
91
+ | Prop | Type | Description |
92
+ | -------------- | ------------------------- | ---------------------------------- |
93
+ | `value` | `string` | Markdown content (controlled mode) |
94
+ | `onChange` | `(value: string) => void` | Called when content changes |
95
+ | `defaultValue` | `string` | Initial value (uncontrolled mode) |
96
+ | `placeholder` | `string` | Placeholder text for the editor |
97
+ | `readOnly` | `boolean` | Disable editing |
98
+ | `toolbar` | `ToolbarItem[]` | Customize visible toolbar items |
99
+ | `className` | `string` | Custom class for root container |
100
+
101
+ > If `value` is provided, the editor automatically works in **controlled mode**.
102
+
103
+ ---
104
+
105
+ ## 📱 Responsive Behavior
106
+
107
+ This library relies on the **browser viewport** for responsive layouts.
108
+
109
+ Make sure the following meta tag exists in your HTML document:
110
+
111
+ ```html
112
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
113
+ ```
114
+
115
+ > ⚠️ This library does **not** inject meta tags automatically
116
+ > → Host applications should manage viewport settings themselves
117
+
118
+ ---
119
+
120
+ <details>
121
+ <summary>🇰🇷 한국어 설명</summary>
122
+
123
+ ## 소개
124
+
125
+ **md-editor-lite**는
126
+ “필요한 기능만 제공하고, 나머지는 개발자에게 맡긴다”는 철학으로 만든
127
+ **경량 React 마크다운 에디터 라이브러리**입니다.
128
+
129
+ 대형 에디터들이 흔히 가지는
130
+
131
+ - 과도한 번들 사이즈
132
+ - 복잡한 설정
133
+ - 강제되는 스타일 구조
134
+
135
+ 이런 문제를 피하고,
136
+ **폼 / CMS / 관리자 페이지 / 사이드 프로젝트** 어디에든
137
+ 부담 없이 사용할 수 있도록 설계되었습니다.
138
+
139
+ ---
140
+
141
+ ## 주요 특징
142
+
143
+ ### ⚡ 가볍고 예측 가능한 구조
144
+
145
+ - 런타임 의존성은 **React만 필요**
146
+ - 외부 에디터 엔진 없이 동작
147
+ - 라이브러리·디자인 시스템 내부에서도 안전하게 사용 가능
148
+
149
+ ### 🎛 Controlled / Uncontrolled 모두 지원
150
+
151
+ - 단순 textarea처럼 빠르게 사용 가능
152
+ - 상태를 외부에서 완전히 제어하는 구조도 지원
153
+ - React Hook Form 등과 호환 가능
154
+
155
+ ### 🎨 스타일 독립성
156
+
157
+ - Tailwind / CSS Modules / styled-components 등
158
+ **어떤 스타일링 방식과도 충돌하지 않음**
159
+ - 기본 스타일은 최소한만 제공
160
+
161
+ ### 📦 라이브러리 배포를 고려한 설계
162
+
163
+ - 전역 CSS 주입 없음
164
+ - meta tag 자동 삽입 없음
165
+ - 호스트 애플리케이션의 환경을 침범하지 않음
166
+
167
+ ---
168
+
169
+ ## 이런 경우에 잘 어울려요
170
+
171
+ - “에디터 하나 때문에 라이브러리가 무거워지는 게 싫을 때”
172
+ - 관리자 페이지에 **심플한 마크다운 입력 UI**가 필요할 때
173
+ - 디자인 시스템 안에 자연스럽게 녹아드는 에디터가 필요할 때
174
+
175
+ </details>
package/dist/index.css ADDED
@@ -0,0 +1,181 @@
1
+ /* src/css/editor.css */
2
+ .editor {
3
+ display: flex;
4
+ flex-direction: column;
5
+ width: 100%;
6
+ border: 1px solid #e5e7eb;
7
+ border-radius: 8px;
8
+ box-sizing: border-box;
9
+ overflow: hidden;
10
+ }
11
+ .editor-header {
12
+ display: flex;
13
+ flex-direction: column;
14
+ justify-content: space-evenly;
15
+ height: 88px;
16
+ padding: 0 16px;
17
+ background-color: #f9fafb;
18
+ border-bottom: 1px solid #e5e7eb;
19
+ border-top-left-radius: 8px;
20
+ border-top-right-radius: 8px;
21
+ }
22
+ @media (min-width: 768px) {
23
+ .editor-header {
24
+ flex-direction: row;
25
+ align-items: center;
26
+ justify-content: space-between;
27
+ height: 48px;
28
+ }
29
+ }
30
+ .editor-tab-container {
31
+ display: flex;
32
+ gap: 1rem;
33
+ font-weight: 500;
34
+ }
35
+ .editor-tab {
36
+ cursor: pointer;
37
+ padding: 0.25rem 0.5rem;
38
+ font-size: small;
39
+ font-weight: 500;
40
+ transition: color 0.2s;
41
+ color: #4b5563;
42
+ &:hover {
43
+ color: #1f2937;
44
+ }
45
+ }
46
+ .editor-tab--active {
47
+ color: #111827;
48
+ border-bottom: 2px solid #111827;
49
+ }
50
+ .editor-textarea {
51
+ width: 100%;
52
+ min-height: 200px;
53
+ margin: 0;
54
+ padding: 16px;
55
+ border: none;
56
+ resize: none;
57
+ outline: none;
58
+ box-sizing: border-box;
59
+ }
60
+
61
+ /* src/css/preview.css */
62
+ .preview {
63
+ color: #111827;
64
+ width: 100%;
65
+ min-height: 200px;
66
+ padding: 16px;
67
+ border: none;
68
+ resize: none;
69
+ outline: none;
70
+ box-sizing: border-box;
71
+ list-style: decimal inside;
72
+ }
73
+
74
+ /* src/css/toolbar.css */
75
+ .toolbar-menu {
76
+ display: flex;
77
+ align-items: center;
78
+ margin: 0;
79
+ padding: 0;
80
+ gap: 16px;
81
+ box-sizing: border-box;
82
+ }
83
+ .toolbar-button {
84
+ background: none;
85
+ width: 28px;
86
+ height: 28px;
87
+ border: none;
88
+ cursor: pointer;
89
+ transition: background-color 0.2s;
90
+ &:hover {
91
+ background-color: #f3f4f6;
92
+ }
93
+ &:active {
94
+ background-color: #e5e7eb;
95
+ }
96
+ }
97
+
98
+ /* src/css/dropdown.css */
99
+ .md-dropdown {
100
+ position: relative;
101
+ display: inline-flex;
102
+ align-items: center;
103
+ }
104
+ .md-dropdown__trigger {
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: center;
108
+ width: 28px;
109
+ height: 28px;
110
+ background: transparent;
111
+ border: none;
112
+ cursor: pointer;
113
+ transition: background-color 0.15s ease;
114
+ }
115
+ .md-dropdown__trigger:hover {
116
+ background-color: #f3f4f6;
117
+ }
118
+ .md-dropdown__trigger--disabled {
119
+ cursor: not-allowed;
120
+ opacity: 0.5;
121
+ }
122
+ .md-dropdown__menu {
123
+ position: absolute;
124
+ top: 0;
125
+ left: -35%;
126
+ z-index: 50;
127
+ margin-top: 4px;
128
+ min-width: 40px;
129
+ max-height: 240px;
130
+ overflow-y: auto;
131
+ background: #ffffff;
132
+ border: 1px solid #e5e7eb;
133
+ border-radius: 8px;
134
+ padding: 4px 0;
135
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
136
+ }
137
+ .md-dropdown__item {
138
+ display: flex;
139
+ align-items: center;
140
+ gap: 8px;
141
+ padding: 10px 12px;
142
+ font-size: 14px;
143
+ cursor: pointer;
144
+ user-select: none;
145
+ transition: background-color 0.15s ease;
146
+ }
147
+ .md-dropdown__item:hover {
148
+ background-color: #f3f4f6;
149
+ }
150
+ .md-dropdown__item--selected {
151
+ background-color: #fefce8;
152
+ color: #a16207;
153
+ font-weight: 500;
154
+ }
155
+ .md-dropdown__icon {
156
+ display: flex;
157
+ color: #9ca3af;
158
+ }
159
+ .md-dropdown__label {
160
+ white-space: nowrap;
161
+ }
162
+ .dropdown-item {
163
+ color: #374151;
164
+ background-color: #f3f4f6;
165
+ position: relative;
166
+ display: flex;
167
+ cursor: pointer;
168
+ align-items: center;
169
+ gap: 0.5rem;
170
+ padding-left: 0.75rem;
171
+ padding-right: 0.75rem;
172
+ padding-top: 0.625rem;
173
+ padding-bottom: 0.625rem;
174
+ font-size: 0.875rem;
175
+ transition: all ease-in-out;
176
+ }
177
+ .dropdown-item--selected {
178
+ background-color: #fefce8;
179
+ color: #a16207;
180
+ font-weight: 500;
181
+ }
@@ -0,0 +1,81 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import react__default from 'react';
4
+ import * as lucide_react from 'lucide-react';
5
+
6
+ interface EditorHeaderProps {
7
+ mode: "write" | "preview";
8
+ setMode: (mode: "write" | "preview") => void;
9
+ insertMarkdown: (before: string, after?: string) => void;
10
+ }
11
+ declare function EditorHeader({ mode, setMode, insertMarkdown, }: EditorHeaderProps): react_jsx_runtime.JSX.Element;
12
+
13
+ interface EditorTabProps {
14
+ mode: string;
15
+ setMode: (mode: "write" | "preview") => void;
16
+ }
17
+ declare function EditorTab({ mode, setMode }: EditorTabProps): react_jsx_runtime.JSX.Element;
18
+
19
+ interface EditorTextareaProps {
20
+ value: string;
21
+ placeholder?: string;
22
+ setValue: (value: string) => void;
23
+ onImageUpload?: (file: File) => Promise<string>;
24
+ }
25
+ declare const EditorTextarea: react.ForwardRefExoticComponent<EditorTextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
26
+
27
+ interface MarkdownEditorProps {
28
+ value: string;
29
+ onChange: (v: string) => void;
30
+ onImageUpload?: (file: File) => Promise<string>;
31
+ }
32
+ declare function MarkdownEditor({ value, onChange: setValue, onImageUpload, }: MarkdownEditorProps): react_jsx_runtime.JSX.Element;
33
+
34
+ interface PreviewProps {
35
+ value: string;
36
+ }
37
+ declare function Preview({ value }: PreviewProps): react_jsx_runtime.JSX.Element;
38
+
39
+ interface ToolbarProps {
40
+ insertMarkdown: (before: string, after?: string) => void;
41
+ }
42
+ declare function Toolbar({ insertMarkdown }: ToolbarProps): react_jsx_runtime.JSX.Element;
43
+
44
+ interface ToolbarDropdownProps {
45
+ insertMarkdown: (before: string, after?: string) => void;
46
+ }
47
+ declare function ToolbarDropdownHeading({ insertMarkdown, }: ToolbarDropdownProps): react_jsx_runtime.JSX.Element;
48
+ declare function ToolbarDropdownList({ insertMarkdown }: ToolbarDropdownProps): react_jsx_runtime.JSX.Element;
49
+
50
+ type DropdownOption = {
51
+ value: string;
52
+ label?: string;
53
+ Icon?: react__default.ComponentType<react__default.SVGProps<SVGSVGElement>>;
54
+ };
55
+
56
+ type ToolbarDropdownOption = DropdownOption & {
57
+ before: string;
58
+ };
59
+ declare const TOOLBAR_BUTTONS: {
60
+ key: string;
61
+ before: string;
62
+ after: string;
63
+ Icon: react.ForwardRefExoticComponent<Omit<lucide_react.LucideProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
64
+ }[];
65
+ declare const headingOptions: ToolbarDropdownOption[];
66
+ declare const listOptions: ToolbarDropdownOption[];
67
+
68
+ interface UseMarkdownEditorProps {
69
+ value: string;
70
+ setValue: (v: string) => void;
71
+ }
72
+ declare function useMarkdownEditor({ value, setValue }: UseMarkdownEditorProps): {
73
+ textareaRef: react.RefObject<HTMLTextAreaElement | null>;
74
+ insertMarkdown: (before: string, after?: string) => void;
75
+ };
76
+
77
+ declare function cx(...classes: (string | false | undefined)[]): string;
78
+
79
+ declare function markdownToHtml(markdown: string): string;
80
+
81
+ export { EditorHeader, EditorTab, EditorTextarea, MarkdownEditor, Preview, TOOLBAR_BUTTONS, Toolbar, ToolbarDropdownHeading, ToolbarDropdownList, cx, headingOptions, listOptions, markdownToHtml, useMarkdownEditor };
@@ -0,0 +1,81 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import * as react from 'react';
3
+ import react__default from 'react';
4
+ import * as lucide_react from 'lucide-react';
5
+
6
+ interface EditorHeaderProps {
7
+ mode: "write" | "preview";
8
+ setMode: (mode: "write" | "preview") => void;
9
+ insertMarkdown: (before: string, after?: string) => void;
10
+ }
11
+ declare function EditorHeader({ mode, setMode, insertMarkdown, }: EditorHeaderProps): react_jsx_runtime.JSX.Element;
12
+
13
+ interface EditorTabProps {
14
+ mode: string;
15
+ setMode: (mode: "write" | "preview") => void;
16
+ }
17
+ declare function EditorTab({ mode, setMode }: EditorTabProps): react_jsx_runtime.JSX.Element;
18
+
19
+ interface EditorTextareaProps {
20
+ value: string;
21
+ placeholder?: string;
22
+ setValue: (value: string) => void;
23
+ onImageUpload?: (file: File) => Promise<string>;
24
+ }
25
+ declare const EditorTextarea: react.ForwardRefExoticComponent<EditorTextareaProps & react.RefAttributes<HTMLTextAreaElement>>;
26
+
27
+ interface MarkdownEditorProps {
28
+ value: string;
29
+ onChange: (v: string) => void;
30
+ onImageUpload?: (file: File) => Promise<string>;
31
+ }
32
+ declare function MarkdownEditor({ value, onChange: setValue, onImageUpload, }: MarkdownEditorProps): react_jsx_runtime.JSX.Element;
33
+
34
+ interface PreviewProps {
35
+ value: string;
36
+ }
37
+ declare function Preview({ value }: PreviewProps): react_jsx_runtime.JSX.Element;
38
+
39
+ interface ToolbarProps {
40
+ insertMarkdown: (before: string, after?: string) => void;
41
+ }
42
+ declare function Toolbar({ insertMarkdown }: ToolbarProps): react_jsx_runtime.JSX.Element;
43
+
44
+ interface ToolbarDropdownProps {
45
+ insertMarkdown: (before: string, after?: string) => void;
46
+ }
47
+ declare function ToolbarDropdownHeading({ insertMarkdown, }: ToolbarDropdownProps): react_jsx_runtime.JSX.Element;
48
+ declare function ToolbarDropdownList({ insertMarkdown }: ToolbarDropdownProps): react_jsx_runtime.JSX.Element;
49
+
50
+ type DropdownOption = {
51
+ value: string;
52
+ label?: string;
53
+ Icon?: react__default.ComponentType<react__default.SVGProps<SVGSVGElement>>;
54
+ };
55
+
56
+ type ToolbarDropdownOption = DropdownOption & {
57
+ before: string;
58
+ };
59
+ declare const TOOLBAR_BUTTONS: {
60
+ key: string;
61
+ before: string;
62
+ after: string;
63
+ Icon: react.ForwardRefExoticComponent<Omit<lucide_react.LucideProps, "ref"> & react.RefAttributes<SVGSVGElement>>;
64
+ }[];
65
+ declare const headingOptions: ToolbarDropdownOption[];
66
+ declare const listOptions: ToolbarDropdownOption[];
67
+
68
+ interface UseMarkdownEditorProps {
69
+ value: string;
70
+ setValue: (v: string) => void;
71
+ }
72
+ declare function useMarkdownEditor({ value, setValue }: UseMarkdownEditorProps): {
73
+ textareaRef: react.RefObject<HTMLTextAreaElement | null>;
74
+ insertMarkdown: (before: string, after?: string) => void;
75
+ };
76
+
77
+ declare function cx(...classes: (string | false | undefined)[]): string;
78
+
79
+ declare function markdownToHtml(markdown: string): string;
80
+
81
+ export { EditorHeader, EditorTab, EditorTextarea, MarkdownEditor, Preview, TOOLBAR_BUTTONS, Toolbar, ToolbarDropdownHeading, ToolbarDropdownList, cx, headingOptions, listOptions, markdownToHtml, useMarkdownEditor };