erl-mathtextx-editor 0.1.5 → 0.1.7

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 CHANGED
@@ -1,251 +1,253 @@
1
- # erl-mathtextx-editor
2
-
3
- **Visual Math Editor Component — Zero LaTeX Required**
4
-
5
- [![npm version](https://badge.fury.io/js/erl-mathtextx-editor.svg)](https://www.npmjs.com/package/erl-mathtextx-editor)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
8
- Embeddable visual math editor widget untuk CMS dan platform edukasi. User tidak perlu tahu LaTeX — semua input matematika dilakukan secara visual.
9
-
10
- ---
11
-
12
- ## ✨ Fitur Utama
13
-
14
- - 🎯 **Visual Math Keyboard** — Klik simbol, operator, template formula
15
- - 📝 **Rich Text Editor** — Bold, italic, tables, lists, links, images
16
- - 🧮 **100+ Formula Templates** — Algebra, calculus, trigonometry, chemistry
17
- - 👁️ **Content Viewer** — Read-only renderer dengan KaTeX
18
- - 🎨 **Table Editor** — 6 professional table templates
19
- - ⌨️ **Keyboard Shortcuts** — Ctrl+K (link), Ctrl+Shift+T (table), Ctrl+S (save)
20
-
21
- ---
22
-
23
- ## 📦 Installation
24
-
25
- ```bash
26
- npm install erl-mathtextx-editor
27
- ```
28
-
29
- ---
30
-
31
- ## 🚀 Quick Start
32
-
33
- ### Basic Usage
34
-
35
- ```tsx
36
- import { MathTextXEditor } from 'erl-mathtextx-editor'
37
- import 'erl-mathtextx-editor/styles'
38
-
39
- function App() {
40
- return (
41
- <MathTextXEditor
42
- onChange={(html) => console.log(html)}
43
- placeholder="Tulis soal di sini..."
44
- />
45
- )
46
- }
47
- ```
48
-
49
- ### With Save Handler
50
-
51
- ```tsx
52
- import { MathTextXEditor, getHTML } from 'erl-mathtextx-editor'
53
- import 'erl-mathtextx-editor/styles'
54
-
55
- function QuestionForm() {
56
- const handleSave = (html) => {
57
- // Send to your API
58
- fetch('/api/questions', {
59
- method: 'POST',
60
- headers: { 'Content-Type': 'application/json' },
61
- body: JSON.stringify({ content: html }),
62
- })
63
- }
64
-
65
- return (
66
- <MathTextXEditor
67
- onChange={(html) => console.log(html)}
68
- onSave={handleSave}
69
- placeholder="Tulis pertanyaan..."
70
- />
71
- )
72
- }
73
- ```
74
-
75
- ### Content Viewer (Read-Only)
76
-
77
- ```tsx
78
- import { ContentViewer } from 'erl-mathtextx-editor/viewer'
79
- import 'erl-mathtextx-editor/styles'
80
-
81
- function QuestionCard({ questionHtml }) {
82
- return (
83
- <div className="question-card">
84
- <h3>Soal 1</h3>
85
- <ContentViewer content={questionHtml} />
86
- </div>
87
- )
88
- }
89
- ```
90
-
91
- ---
92
-
93
- ## 📖 Documentation
94
-
95
- - [Quick Start Guide](https://github.com/erlangga/richtext-editor-research/blob/main/QUICK_START.md)
96
- - [Integration Tutorial](https://github.com/erlangga/richtext-editor-research/blob/main/INTEGRATION_TUTORIAL.md)
97
-
98
- ---
99
-
100
- ## 🛠️ Tech Stack
101
-
102
- - **UI Framework:** React 18+
103
- - **Editor Engine:** TipTap / ProseMirror
104
- - **Math Input:** MathLive (WYSIWYG math)
105
- - **Math Rendering:** KaTeX
106
- - **XSS Protection:** DOMPurify
107
-
108
- ---
109
-
110
- ## 📋 Props API
111
-
112
- ### MathTextXEditor
113
-
114
- | Prop | Type | Default | Description |
115
- |------|------|---------|-------------|
116
- | `content` | `string` | `''` | Initial HTML content |
117
- | `onChange` | `(html: string) => void` | — | Callback on content change |
118
- | `onSave` | `(html: string) => void` | | Callback on Ctrl+S |
119
- | `placeholder` | `string` | `'Tulis soal...'` | Placeholder text |
120
- | `minHeight` | `string` | `'200px'` | Minimum editor height |
121
- | `maxHeight` | `string` | | Maximum editor height |
122
- | `onImageUpload` | `(file: File) => Promise<string>` | | Custom image upload handler |
123
-
124
- ### ContentViewer
125
-
126
- | Prop | Type | Default | Description |
127
- |------|------|---------|-------------|
128
- | `content` | `string` | | HTML content to render (required) |
129
- | `className` | `string` | — | Additional CSS class |
130
-
131
- ---
132
-
133
- ## ⌨️ Keyboard Shortcuts
134
-
135
- | Shortcut | Action |
136
- |----------|--------|
137
- | `Ctrl+K` | Insert/edit link |
138
- | `Ctrl+Shift+T` | Insert table |
139
- | `Ctrl+S` | Save document |
140
- | `Ctrl+B` | Bold |
141
- | `Ctrl+I` | Italic |
142
- | `Ctrl+U` | Underline |
143
-
144
- ---
145
-
146
- ## 🎯 Example: Multiple Choice Question Form
147
-
148
- ```tsx
149
- import { useState } from 'react'
150
- import { MathTextXEditor } from 'erl-mathtextx-editor'
151
- import 'erl-mathtextx-editor/styles'
152
-
153
- export default function QuestionForm() {
154
- const [question, setQuestion] = useState('')
155
- const [options, setOptions] = useState([
156
- { id: 'A', content: '', isCorrect: false },
157
- { id: 'B', content: '', isCorrect: false },
158
- { id: 'C', content: '', isCorrect: false },
159
- { id: 'D', content: '', isCorrect: false },
160
- ])
161
-
162
- const handleSubmit = async () => {
163
- await fetch('/api/questions', {
164
- method: 'POST',
165
- headers: { 'Content-Type': 'application/json' },
166
- body: JSON.stringify({ question, options }),
167
- })
168
- }
169
-
170
- return (
171
- <div>
172
- <h2>Buat Soal Pilihan Ganda</h2>
173
-
174
- {/* Question */}
175
- <MathTextXEditor
176
- content={question}
177
- onChange={setQuestion}
178
- placeholder="Tulis pertanyaan..."
179
- minHeight="150px"
180
- />
181
-
182
- {/* Options */}
183
- {options.map((option) => (
184
- <div key={option.id}>
185
- <label>
186
- <input
187
- type="radio"
188
- name="correct"
189
- checked={option.isCorrect}
190
- onChange={() => {
191
- setOptions(prev =>
192
- prev.map(o => ({
193
- ...o,
194
- isCorrect: o.id === option.id
195
- }))
196
- )
197
- }}
198
- />
199
- Opsi {option.id}
200
- </label>
201
- <MathTextXEditor
202
- content={option.content}
203
- onChange={(html) => {
204
- setOptions(prev =>
205
- prev.map(o =>
206
- o.id === option.id ? { ...o, content: html } : o
207
- )
208
- )
209
- }}
210
- placeholder={`Jawaban ${option.id}...`}
211
- minHeight="80px"
212
- />
213
- </div>
214
- ))}
215
-
216
- <button onClick={handleSubmit}>Simpan Soal</button>
217
- </div>
218
- )
219
- }
220
- ```
221
-
222
- ---
223
-
224
- ## 🔗 Links
225
-
226
- - **Website:** [erl-mathtextx-editor](https://github.com/erlangga/richtext-editor-research)
227
- - **NPM:** [erl-mathtextx-editor](https://www.npmjs.com/package/erl-mathtextx-editor)
228
- - **Issues:** [Report Bug](https://github.com/erlangga/richtext-editor-research/issues)
229
-
230
- ---
231
-
232
- ## 📄 License
233
-
234
- [MIT](https://github.com/erlangga/richtext-editor-research/blob/main/LICENSE) © Erlangga Team
235
-
236
- ---
237
-
238
- ## 🎉 Ready to Use?
239
-
240
- ```bash
241
- npm install erl-mathtextx-editor
242
- ```
243
-
244
- Then import and use:
245
-
246
- ```tsx
247
- import { MathTextXEditor } from 'erl-mathtextx-editor'
248
- import 'erl-mathtextx-editor/styles'
249
- ```
250
-
251
- Happy coding! 🚀
1
+ # erl-mathtextx-editor
2
+
3
+ **Visual Math Editor Component — Zero LaTeX Required**
4
+
5
+ [![npm version](https://badge.fury.io/js/erl-mathtextx-editor.svg)](https://www.npmjs.com/package/erl-mathtextx-editor)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ Embeddable visual math editor widget untuk CMS dan platform edukasi. User tidak perlu tahu LaTeX — semua input matematika dilakukan secara visual.
9
+
10
+ ---
11
+
12
+ ## ✨ Fitur Utama
13
+
14
+ - 🎯 **Visual Math Keyboard** — Klik simbol, operator, template formula
15
+ - 📝 **Rich Text Editor** — Bold, italic, tables, lists, links, images
16
+ - 🧮 **100+ Formula Templates** — Algebra, calculus, trigonometry, chemistry
17
+ - 👁️ **Content Viewer** — Read-only renderer dengan KaTeX
18
+ - 🎨 **Table Editor** — 6 professional table templates
19
+ - ⌨️ **Keyboard Shortcuts** — Ctrl+K (link), Ctrl+Shift+T (table), Ctrl+S (save)
20
+
21
+ ---
22
+
23
+ ## 📦 Installation
24
+
25
+ ```bash
26
+ npm install erl-mathtextx-editor
27
+ ```
28
+
29
+ ---
30
+
31
+ ## 🚀 Quick Start
32
+
33
+ ### Basic Usage
34
+
35
+ ```tsx
36
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
37
+ import 'erl-mathtextx-editor/styles'
38
+
39
+ function App() {
40
+ return (
41
+ <MathTextXEditor
42
+ onChange={(html) => console.log(html)}
43
+ placeholder="Tulis soal di sini..."
44
+ />
45
+ )
46
+ }
47
+ ```
48
+
49
+ ### With Save Handler
50
+
51
+ ```tsx
52
+ import { MathTextXEditor, getHTML } from 'erl-mathtextx-editor'
53
+ import 'erl-mathtextx-editor/styles'
54
+
55
+ function QuestionForm() {
56
+ const handleSave = (html) => {
57
+ // Send to your API
58
+ fetch('/api/questions', {
59
+ method: 'POST',
60
+ headers: { 'Content-Type': 'application/json' },
61
+ body: JSON.stringify({ content: html }),
62
+ })
63
+ }
64
+
65
+ return (
66
+ <MathTextXEditor
67
+ onChange={(html) => console.log(html)}
68
+ onSave={handleSave}
69
+ placeholder="Tulis pertanyaan..."
70
+ />
71
+ )
72
+ }
73
+ ```
74
+
75
+ ### Content Viewer (Read-Only)
76
+
77
+ ```tsx
78
+ import { ContentViewer } from 'erl-mathtextx-editor/viewer'
79
+ import 'erl-mathtextx-editor/viewer/styles'
80
+
81
+ function QuestionCard({ questionHtml }) {
82
+ return (
83
+ <div className="question-card">
84
+ <h3>Soal 1</h3>
85
+ <ContentViewer content={questionHtml} />
86
+ </div>
87
+ )
88
+ }
89
+ ```
90
+
91
+ **Catatan:** Viewer memerlukan import CSS terpisah: `import 'erl-mathtextx-editor/viewer/styles'`
92
+
93
+ ---
94
+
95
+ ## 📖 Documentation
96
+
97
+ - [Quick Start Guide](https://github.com/erlangga/richtext-editor-research/blob/main/QUICK_START.md)
98
+ - [Integration Tutorial](https://github.com/erlangga/richtext-editor-research/blob/main/INTEGRATION_TUTORIAL.md)
99
+
100
+ ---
101
+
102
+ ## 🛠️ Tech Stack
103
+
104
+ - **UI Framework:** React 18+
105
+ - **Editor Engine:** TipTap / ProseMirror
106
+ - **Math Input:** MathLive (WYSIWYG math)
107
+ - **Math Rendering:** KaTeX
108
+ - **XSS Protection:** DOMPurify
109
+
110
+ ---
111
+
112
+ ## 📋 Props API
113
+
114
+ ### MathTextXEditor
115
+
116
+ | Prop | Type | Default | Description |
117
+ |------|------|---------|-------------|
118
+ | `content` | `string` | `''` | Initial HTML content |
119
+ | `onChange` | `(html: string) => void` | | Callback on content change |
120
+ | `onSave` | `(html: string) => void` | | Callback on Ctrl+S |
121
+ | `placeholder` | `string` | `'Tulis soal...'` | Placeholder text |
122
+ | `minHeight` | `string` | `'200px'` | Minimum editor height |
123
+ | `maxHeight` | `string` | — | Maximum editor height |
124
+ | `onImageUpload` | `(file: File) => Promise<string>` | — | Custom image upload handler |
125
+
126
+ ### ContentViewer
127
+
128
+ | Prop | Type | Default | Description |
129
+ |------|------|---------|-------------|
130
+ | `content` | `string` | — | HTML content to render (required) |
131
+ | `className` | `string` | — | Additional CSS class |
132
+
133
+ ---
134
+
135
+ ## ⌨️ Keyboard Shortcuts
136
+
137
+ | Shortcut | Action |
138
+ |----------|--------|
139
+ | `Ctrl+K` | Insert/edit link |
140
+ | `Ctrl+Shift+T` | Insert table |
141
+ | `Ctrl+S` | Save document |
142
+ | `Ctrl+B` | Bold |
143
+ | `Ctrl+I` | Italic |
144
+ | `Ctrl+U` | Underline |
145
+
146
+ ---
147
+
148
+ ## 🎯 Example: Multiple Choice Question Form
149
+
150
+ ```tsx
151
+ import { useState } from 'react'
152
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
153
+ import 'erl-mathtextx-editor/styles'
154
+
155
+ export default function QuestionForm() {
156
+ const [question, setQuestion] = useState('')
157
+ const [options, setOptions] = useState([
158
+ { id: 'A', content: '', isCorrect: false },
159
+ { id: 'B', content: '', isCorrect: false },
160
+ { id: 'C', content: '', isCorrect: false },
161
+ { id: 'D', content: '', isCorrect: false },
162
+ ])
163
+
164
+ const handleSubmit = async () => {
165
+ await fetch('/api/questions', {
166
+ method: 'POST',
167
+ headers: { 'Content-Type': 'application/json' },
168
+ body: JSON.stringify({ question, options }),
169
+ })
170
+ }
171
+
172
+ return (
173
+ <div>
174
+ <h2>Buat Soal Pilihan Ganda</h2>
175
+
176
+ {/* Question */}
177
+ <MathTextXEditor
178
+ content={question}
179
+ onChange={setQuestion}
180
+ placeholder="Tulis pertanyaan..."
181
+ minHeight="150px"
182
+ />
183
+
184
+ {/* Options */}
185
+ {options.map((option) => (
186
+ <div key={option.id}>
187
+ <label>
188
+ <input
189
+ type="radio"
190
+ name="correct"
191
+ checked={option.isCorrect}
192
+ onChange={() => {
193
+ setOptions(prev =>
194
+ prev.map(o => ({
195
+ ...o,
196
+ isCorrect: o.id === option.id
197
+ }))
198
+ )
199
+ }}
200
+ />
201
+ Opsi {option.id}
202
+ </label>
203
+ <MathTextXEditor
204
+ content={option.content}
205
+ onChange={(html) => {
206
+ setOptions(prev =>
207
+ prev.map(o =>
208
+ o.id === option.id ? { ...o, content: html } : o
209
+ )
210
+ )
211
+ }}
212
+ placeholder={`Jawaban ${option.id}...`}
213
+ minHeight="80px"
214
+ />
215
+ </div>
216
+ ))}
217
+
218
+ <button onClick={handleSubmit}>Simpan Soal</button>
219
+ </div>
220
+ )
221
+ }
222
+ ```
223
+
224
+ ---
225
+
226
+ ## 🔗 Links
227
+
228
+ - **Website:** [erl-mathtextx-editor](https://github.com/erlangga/richtext-editor-research)
229
+ - **NPM:** [erl-mathtextx-editor](https://www.npmjs.com/package/erl-mathtextx-editor)
230
+ - **Issues:** [Report Bug](https://github.com/erlangga/richtext-editor-research/issues)
231
+
232
+ ---
233
+
234
+ ## 📄 License
235
+
236
+ [MIT](https://github.com/erlangga/richtext-editor-research/blob/main/LICENSE) © Erlangga Team
237
+
238
+ ---
239
+
240
+ ## 🎉 Ready to Use?
241
+
242
+ ```bash
243
+ npm install erl-mathtextx-editor
244
+ ```
245
+
246
+ Then import and use:
247
+
248
+ ```tsx
249
+ import { MathTextXEditor } from 'erl-mathtextx-editor'
250
+ import 'erl-mathtextx-editor/styles'
251
+ ```
252
+
253
+ Happy coding! 🚀
@@ -1,6 +1,6 @@
1
1
  import { jsx as t, jsxs as l } from "react/jsx-runtime";
2
2
  import z, { useId as H, useRef as A, useCallback as b, useState as d, useEffect as V } from "react";
3
- import { u as B } from "./index-K6msjM1s.js";
3
+ import { u as B } from "./index-Ci20X1Rj.js";
4
4
  const M = z.memo(({
5
5
  isOpen: s,
6
6
  initialData: i = {},
@@ -1,6 +1,7 @@
1
- import { jsx as r } from "react/jsx-runtime";
1
+ import { jsx as n } from "react/jsx-runtime";
2
2
  import { useRef as o, useMemo as i, useEffect as m } from "react";
3
3
  import { p as s, k as l } from "./viewer-deps-CjbAqdti.js";
4
+ import "./viewer-styles.js";
4
5
  function c(t) {
5
6
  const e = t.getAttribute("data-latex") || t.getAttribute("latex");
6
7
  if (!e) return;
@@ -15,8 +16,8 @@ function c(t) {
15
16
  t.textContent = a ? `$$${e}$$` : `$${e}$`;
16
17
  }
17
18
  }
18
- function p({ content: t, className: e }) {
19
- const a = o(null), n = i(
19
+ function x({ content: t, className: e }) {
20
+ const a = o(null), r = i(
20
21
  () => s.sanitize(t, {
21
22
  ADD_TAGS: ["math-field", "span", "div"],
22
23
  ADD_ATTR: ["data-latex", "latex", "data-type", "data-mathml"],
@@ -29,15 +30,15 @@ function p({ content: t, className: e }) {
29
30
  a.current.querySelectorAll(
30
31
  '.mtx-math-inline, .mtx-math-block, [data-type="math-inline"], [data-type="math-block"]'
31
32
  ).forEach(c);
32
- }, [n]), /* @__PURE__ */ r(
33
+ }, [r]), /* @__PURE__ */ n(
33
34
  "div",
34
35
  {
35
36
  ref: a,
36
37
  className: `mtx-content-viewer${e ? ` ${e}` : ""}`,
37
- dangerouslySetInnerHTML: { __html: n }
38
+ dangerouslySetInnerHTML: { __html: r }
38
39
  }
39
40
  );
40
41
  }
41
42
  export {
42
- p as C
43
+ x as C
43
44
  };
@@ -1,6 +1,6 @@
1
1
  import { jsxs as l, Fragment as E, jsx as e } from "react/jsx-runtime";
2
2
  import { useId as I, useRef as P, useState as c, useCallback as n } from "react";
3
- import { u as W } from "./index-K6msjM1s.js";
3
+ import { u as W } from "./index-Ci20X1Rj.js";
4
4
  function O({
5
5
  isOpen: f,
6
6
  onClose: x,
@@ -1,6 +1,6 @@
1
1
  import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
2
  import N, { useId as v, useRef as f, useState as c, useEffect as g, useCallback as w } from "react";
3
- import { u as y } from "./index-K6msjM1s.js";
3
+ import { u as y } from "./index-Ci20X1Rj.js";
4
4
  const k = N.memo(({ isOpen: i, onInsert: d, onClose: m }) => {
5
5
  const b = v(), u = f(null), [l, n] = c(3), [a, s] = c(3), [o, h] = c(!0);
6
6
  g(() => {
@@ -1,6 +1,6 @@
1
1
  import { jsx as t, jsxs as a } from "react/jsx-runtime";
2
2
  import w, { useId as F, useRef as U, useState as r, useEffect as j, useCallback as d } from "react";
3
- import { u as E } from "./index-K6msjM1s.js";
3
+ import { u as E } from "./index-Ci20X1Rj.js";
4
4
  const P = w.memo(({
5
5
  isOpen: m,
6
6
  currentUrl: i = "",
@@ -1,6 +1,6 @@
1
1
  import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
2
  import o, { useId as c, useRef as n, useCallback as p } from "react";
3
- import { u as h } from "./index-K6msjM1s.js";
3
+ import { u as h } from "./index-Ci20X1Rj.js";
4
4
  const x = [
5
5
  { id: "plain", name: "Plain", description: "Simple table without styling", headerStyle: "none", borderStyle: "all", stripeRows: !1 },
6
6
  { id: "light", name: "Light", description: "Light gray header", headerStyle: "light", borderStyle: "all", stripeRows: !1 },