erl-mathtextx-editor 0.3.1 → 0.3.6
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/CHANGELOG.md +108 -0
- package/README.md +14 -1
- package/dist/{CellPropertiesDialogImpl-5bzEkaTt.js → CellPropertiesDialogImpl-NSTfG9SA.js} +1 -1
- package/dist/ContentViewer-LqSkiQjT.js +161 -0
- package/dist/ImageInsertDialog-DDxR_Cah.js +331 -0
- package/dist/{InsertTableDialogImpl-B2GUzYNY.js → InsertTableDialogImpl-DrW9QTDd.js} +1 -1
- package/dist/{LinkDialogImpl-CPjj0CkY.js → LinkDialogImpl-BXjhfEiE.js} +1 -1
- package/dist/MathTextXEditor.d.ts +1 -1
- package/dist/{TablePropertiesDialogImpl-BLbhyfA0.js → TablePropertiesDialogImpl-C2eqgZMa.js} +1 -1
- package/dist/{TableTemplatesDialogImpl-BQZTGw_Y.js → TableTemplatesDialogImpl-B2DJBmzm.js} +1 -1
- package/dist/assets/erl-mathtextx-editor.css +1 -1
- package/dist/assets/viewer.css +1 -1
- package/dist/components/UploadErrorDebugPanel.d.ts +6 -0
- package/dist/erl-mathtextx-editor.js +50 -28
- package/dist/erl-mathtextx-editor.umd.cjs +470 -200
- package/dist/export-deps-Tu2DJTcR.js +14815 -0
- package/dist/{index-CI4oNSUc.js → index-CvLezwrJ.js} +1 -1
- package/dist/{index-Bq8NKSwD.js → index-D1pCAIcb.js} +6239 -4221
- package/dist/index-D7oo0rd0.js +14235 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.es-UDhZ1Ncp.js +5632 -0
- package/dist/jszip.min-I9rjwvpP.js +2341 -0
- package/dist/math/MathInputModeContext.d.ts +7 -0
- package/dist/math/TemplatePanel.d.ts +4 -1
- package/dist/math/mathFieldFontStyles.d.ts +7 -0
- package/dist/math/mathFieldSpaceKey.d.ts +10 -0
- package/dist/{tiptap-K3rU-Wjn.js → tiptap-BiWGiMFj.js} +27 -27
- package/dist/toolbar/MainToolbar.d.ts +3 -1
- package/dist/toolbar/MathToolbar.d.ts +9 -2
- package/dist/types/index.d.ts +18 -5
- package/dist/utils/docxImporter.d.ts +1 -0
- package/dist/utils/docxOmmlPreprocess.d.ts +12 -0
- package/dist/utils/imageFileValidation.d.ts +14 -0
- package/dist/utils/isLatexContent.d.ts +5 -0
- package/dist/utils/mathMlToLatex.d.ts +4 -0
- package/dist/utils/microsoftMathPaste.d.ts +43 -0
- package/dist/utils/ommlToLatex.d.ts +11 -0
- package/dist/utils/reuploadImagesInHTML.d.ts +9 -0
- package/dist/utils/sanitizeConfig.d.ts +11 -0
- package/dist/utils/unicodeMathToLatex.d.ts +24 -0
- package/dist/utils/uploadError.d.ts +8 -0
- package/dist/{viewer-deps-BDYoL2Ts.js → viewer-deps-_HnWniSt.js} +5 -0
- package/dist/viewer.js +1 -1
- package/package.json +23 -10
- package/dist/ContentViewer-RijJ5nlJ.js +0 -45
- package/dist/ImageInsertDialog-Dcg6kdLb.js +0 -240
- package/dist/components/__tests__/WordCount.test.d.ts +0 -1
- package/dist/core/__tests__/serializer.test.d.ts +0 -1
- package/dist/index-elIws1oE.js +0 -16549
- package/dist/viewer-styles.d.ts +0 -1
- package/dist/viewer-styles.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,114 @@ All notable changes to `erl-mathtextx-editor` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.6] - 2026-07-21
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Word-like Math Conversions** — Unicode / LaTeX / Text mode toggles + Convert on the math toolbar
|
|
13
|
+
- `mathInputMode` / `onMathInputModeChange` props (default `latex`, persisted in `localStorage`)
|
|
14
|
+
- Paste interpretation is gated by mode (avoids mixing Unicode and LaTeX auto-detect)
|
|
15
|
+
- **Convert → Ke rumus** / **Ke teks LaTeX** for selection or active math node
|
|
16
|
+
- `MathInputModeContext` / `useMathInputMode` for NodeViews (math-field paste)
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **Word equation paste as image** — in **LaTeX** mode, when clipboard has OMML/MathML/LaTeX *and* an equation bitmap, prefer math node; strip Word equation preview images after math rewrite. In **Unicode** mode, Word equation snapshots paste as image
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **Pecahan & Akar tab** — expanded templates: fraction ops (+/−/×/÷), proportion, rationalization, nth roots, power–root forms, and more preset formulas (ABC, distance, Pythagoras)
|
|
25
|
+
- **Operator & Relasi tab** — added ⊆/⊇, ∄, ∝, ≪/≫, ∤/∣, ∖/∁, ⇒/⇔, gcd/lcm, inequality chains, and more logic/set symbols
|
|
26
|
+
- **Geometri & Trigonometri tab** — added cot/sec/csc, invers trig, sudut ganda & penjumlahan, luas/volume bangun, identitas trig, dan templat geometri (garis, busur, vektor)
|
|
27
|
+
- **Equation Editor modal UX** — equation-first layout (persamaan di atas, templat di bawah), collapsible “Templat & simbol” panel, compact format bar in equation header, floating tooltips
|
|
28
|
+
- **Arabic math text** — Adobe Naskh Medium font (`mt-font-arabic`), RTL direction, shadow-DOM injection for MathLive, KaTeX `\class` → `\htmlClass` in viewer
|
|
29
|
+
- **Equation editor space key** — Space still inserts `\,` in LaTeX; key-repeat and focus fixes prevent stuck typing
|
|
30
|
+
- **Image insert dialog UX** — choose file → preview + alt text → Sisipkan; primary action always visible; clearer empty-state guidance
|
|
31
|
+
|
|
32
|
+
## [0.3.5] - 2026-07-21
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- **OMML Word paste** — `<m:oMath>` / `<m:oMathPara>` convert to LaTeX math nodes (`ommlToLatex`)
|
|
37
|
+
- **DOCX equation import** — OMML inside `.docx` is preprocessed to math spans before mammoth
|
|
38
|
+
- **Default paste image re-upload** — when `onImageUpload` is set (and `onBeforePasteHTML` is not), pasted images are re-uploaded automatically (`reuploadImagesInHTML`)
|
|
39
|
+
- **Wired config props** — `toolbar`, `mathToolbar`, `templates` (education level), and `outputFormat` (`html` | `json`) now apply at runtime
|
|
40
|
+
- **Public export helpers** — `exportToImage`, `exportToPDF`, `exportToJSON` exported from package root
|
|
41
|
+
|
|
42
|
+
### Fixed
|
|
43
|
+
|
|
44
|
+
- **LaTeX control-word gluing** — Word Unicode paste no longer produces `\timesE` / `\partialB`; commands get a terminating space before letters (`\times E`, `\partial B`) so MathLive renders correctly (e.g. Faraday `∇×E=-∂B/∂t`)
|
|
45
|
+
|
|
46
|
+
### Notes
|
|
47
|
+
|
|
48
|
+
- OMML coverage is best-effort for common education structures (frac, sqrt, scripts, n-ary, delimiters, matrix)
|
|
49
|
+
- `onBeforePasteHTML` still overrides the default image re-upload when provided
|
|
50
|
+
|
|
51
|
+
## [0.3.4] - 2026-07-21
|
|
52
|
+
|
|
53
|
+
### Added
|
|
54
|
+
|
|
55
|
+
- **Microsoft Word math paste** — Unicode math characters (𝑥, 𝑎, ², ∫, …) and LaTeX from Word convert to `mathInline` / `mathBlock`
|
|
56
|
+
- `unicodeMathToLatex` / `isUnicodeMathContent` for Mathematical Alphanumeric Symbols, super/subscripts, operators
|
|
57
|
+
- `resolveMicrosoftMathPaste` priority: `text/latex` → MathML → LaTeX plain → Unicode math
|
|
58
|
+
- MathML → LaTeX via MathLive (`mathMlToLatex`)
|
|
59
|
+
- Office HTML path rewrites `<math>` / TeX annotations to `mtx-math-*` spans before OMML tags are stripped
|
|
60
|
+
- Linear UnicodeMath build-up: `1/2√2` → `\frac{1}{2}\sqrt{2}`, `√(a+b)`, `(a+b)/(c+d)`, ∛/∜
|
|
61
|
+
- Strips Word invisible operators (U+2061–2064, ZWSP, etc.)
|
|
62
|
+
- Unicode math text runs inside Office HTML spans also convert to math nodes
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- Supplementary-plane math letters detection now uses the Unicode `u` flag (`\u{1D400}`)
|
|
67
|
+
- Whitespace preserved before fractions (`= 1/2√2` → `= \frac{1}{2}\sqrt{2}`)
|
|
68
|
+
- Parentheses no longer collapse to braces outside frac/sqrt (`sin(x)` stays `sin(x)`)
|
|
69
|
+
|
|
70
|
+
### Notes
|
|
71
|
+
|
|
72
|
+
- Full OMML (`<m:oMath>`) conversion is not included yet; Word pastes that only expose OMML without Unicode/MathML/LaTeX may still lose structure
|
|
73
|
+
- Complex UnicodeMath linear build-up is best-effort (nested scripts / matrices still limited)
|
|
74
|
+
|
|
75
|
+
## [0.3.3] - 2026-07-21
|
|
76
|
+
|
|
77
|
+
### Fixed
|
|
78
|
+
|
|
79
|
+
- **Stale TipTap handlers** — `onChange`, `onSave`, `onImageUpload`, `onBeforePasteHTML`, drop/paste handlers now use refs so they stay current after re-renders
|
|
80
|
+
- **Reactive `editable` / `content`** — `editable` syncs via `setEditable`; external `content` updates apply without fighting local edits
|
|
81
|
+
- **Paste race** — async paste transforms use a generation counter so only the latest paste is inserted
|
|
82
|
+
- **Image object URL leaks** — revoke on upload failure, tab switch, and unmount
|
|
83
|
+
- **WebP validation** — magic-byte check requires RIFF + WEBP signature
|
|
84
|
+
- **Image URL insert** — only `http:` / `https:` schemes allowed
|
|
85
|
+
- **Drop/paste image validation** — same MIME/size/signature checks as the insert dialog
|
|
86
|
+
- **GraphComponent DOM accumulation** — clears previous plot before re-render
|
|
87
|
+
- **Image drag overlay** — removed on node unmount
|
|
88
|
+
- **Word count without spaces** — correct whitespace stripping
|
|
89
|
+
- **XSS allowlists** — ContentViewer, paste, and serializer share one sanitization config; `getHTML()` is sanitized on output
|
|
90
|
+
|
|
91
|
+
### Added
|
|
92
|
+
|
|
93
|
+
- Shared helpers: `isLatexContent`, `validateImageFile`, `isSafeImageUrl`
|
|
94
|
+
- Unit tests for LaTeX detection, upload errors, safe URLs, and word/character counting
|
|
95
|
+
|
|
96
|
+
## [0.3.2] - 2026-07-07
|
|
97
|
+
|
|
98
|
+
### Added
|
|
99
|
+
|
|
100
|
+
- **LaTeX Paste Support** — Paste LaTeX equations from clipboard into the editor
|
|
101
|
+
- Detects `text/latex` MIME type and plain-text LaTeX patterns (`\frac`, `\sqrt`, `$$`, etc.)
|
|
102
|
+
- Inserts as `mathInline` node when pasting in regular text
|
|
103
|
+
- Passes through to MathLive native paste when cursor is inside a math field
|
|
104
|
+
- `paste` event listener on `<math-field>` prevents ProseMirror from intercepting
|
|
105
|
+
|
|
106
|
+
- **Upload Error Debug** — Better diagnostics when `onImageUpload` fails
|
|
107
|
+
- `UploadError` class with structured `debug` payload
|
|
108
|
+
- Collapsible debug panel in Image Insert dialog and drag-and-drop error toast
|
|
109
|
+
- `logUploadError()`, `getUploadErrorDebug()`, `formatUploadErrorDebug()` utilities exported from package
|
|
110
|
+
|
|
111
|
+
### Fixed
|
|
112
|
+
|
|
113
|
+
- **Image Upload Error Handling** — Drag-and-drop upload no longer fails silently; shows user-visible error with debug details
|
|
114
|
+
- **Image Insert After Upload** — Image is still inserted even when dimension probing fails after a successful upload
|
|
115
|
+
|
|
8
116
|
## [0.3.0] - 2026-06-18
|
|
9
117
|
|
|
10
118
|
### Added
|
package/README.md
CHANGED
|
@@ -510,7 +510,20 @@ Klik kanan → Table Templates untuk memilih tema:
|
|
|
510
510
|
| `erl-mathtextx-editor` | `dist/erl-mathtextx-editor.js` |
|
|
511
511
|
| `erl-mathtextx-editor/styles` | `dist/assets/erl-mathtextx-editor.css` |
|
|
512
512
|
| `erl-mathtextx-editor/viewer` | `dist/viewer.js` |
|
|
513
|
-
| `erl-mathtextx-editor/viewer/styles` | `dist/viewer
|
|
513
|
+
| `erl-mathtextx-editor/viewer/styles` | `dist/assets/viewer.css` |
|
|
514
|
+
|
|
515
|
+
---
|
|
516
|
+
|
|
517
|
+
## 📤 Publish (maintainers)
|
|
518
|
+
|
|
519
|
+
```bash
|
|
520
|
+
cd packages/editor
|
|
521
|
+
npm run lint
|
|
522
|
+
npm run pack:check # dry-run tarball
|
|
523
|
+
npm publish # runs prepack → test + build
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
Published version on npm is independent of the monorepo demo (`file:` link). After publish, bump consumers to the new semver.
|
|
514
527
|
|
|
515
528
|
---
|
|
516
529
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as t, jsxs as l } from "react/jsx-runtime";
|
|
2
2
|
import V, { useId as B, useRef as M, useCallback as b, useState as d, useEffect as A } from "react";
|
|
3
|
-
import { u as $ } from "./index-
|
|
3
|
+
import { u as $ } from "./index-D1pCAIcb.js";
|
|
4
4
|
import { u as I } from "./useDraggable-DEoLIB5k.js";
|
|
5
5
|
const P = V.memo(({
|
|
6
6
|
isOpen: o,
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { jsx as o } from "react/jsx-runtime";
|
|
2
|
+
import { useRef as n, useMemo as s, useEffect as m } from "react";
|
|
3
|
+
import { p as i, k as l } from "./viewer-deps-_HnWniSt.js";
|
|
4
|
+
const d = [
|
|
5
|
+
"p",
|
|
6
|
+
"br",
|
|
7
|
+
"b",
|
|
8
|
+
"strong",
|
|
9
|
+
"i",
|
|
10
|
+
"em",
|
|
11
|
+
"u",
|
|
12
|
+
"s",
|
|
13
|
+
"sub",
|
|
14
|
+
"sup",
|
|
15
|
+
"h1",
|
|
16
|
+
"h2",
|
|
17
|
+
"h3",
|
|
18
|
+
"h4",
|
|
19
|
+
"h5",
|
|
20
|
+
"h6",
|
|
21
|
+
"ul",
|
|
22
|
+
"ol",
|
|
23
|
+
"li",
|
|
24
|
+
"table",
|
|
25
|
+
"thead",
|
|
26
|
+
"tbody",
|
|
27
|
+
"tfoot",
|
|
28
|
+
"tr",
|
|
29
|
+
"td",
|
|
30
|
+
"th",
|
|
31
|
+
"div",
|
|
32
|
+
"span",
|
|
33
|
+
"a",
|
|
34
|
+
"img",
|
|
35
|
+
"figure",
|
|
36
|
+
"figcaption",
|
|
37
|
+
"pre",
|
|
38
|
+
"code",
|
|
39
|
+
"blockquote",
|
|
40
|
+
"hr",
|
|
41
|
+
"wbr",
|
|
42
|
+
"col",
|
|
43
|
+
"colgroup",
|
|
44
|
+
"caption",
|
|
45
|
+
// MathLive / MathML (incoming content + viewer)
|
|
46
|
+
"math-field",
|
|
47
|
+
"math",
|
|
48
|
+
"mi",
|
|
49
|
+
"mo",
|
|
50
|
+
"mn",
|
|
51
|
+
"msup",
|
|
52
|
+
"msub",
|
|
53
|
+
"mrow",
|
|
54
|
+
"mfrac",
|
|
55
|
+
"msqrt",
|
|
56
|
+
"mroot",
|
|
57
|
+
"mover",
|
|
58
|
+
"munder",
|
|
59
|
+
"munderover",
|
|
60
|
+
"mspace",
|
|
61
|
+
"mstyle",
|
|
62
|
+
"mtext",
|
|
63
|
+
"menclose",
|
|
64
|
+
"mfenced",
|
|
65
|
+
"merror",
|
|
66
|
+
"mpadded",
|
|
67
|
+
"mphantom",
|
|
68
|
+
"mprescripts",
|
|
69
|
+
"none"
|
|
70
|
+
], c = [
|
|
71
|
+
"href",
|
|
72
|
+
"target",
|
|
73
|
+
"rel",
|
|
74
|
+
"title",
|
|
75
|
+
"src",
|
|
76
|
+
"alt",
|
|
77
|
+
"width",
|
|
78
|
+
"height",
|
|
79
|
+
"class",
|
|
80
|
+
"id",
|
|
81
|
+
"style",
|
|
82
|
+
"align",
|
|
83
|
+
"data-align",
|
|
84
|
+
"data-theme",
|
|
85
|
+
"data-colwidth",
|
|
86
|
+
"data-rowheight",
|
|
87
|
+
"data-latex",
|
|
88
|
+
"data-type",
|
|
89
|
+
"data-display",
|
|
90
|
+
"data-offset-x",
|
|
91
|
+
"data-offset-y",
|
|
92
|
+
"data-table-width",
|
|
93
|
+
"data-indent",
|
|
94
|
+
"data-mathml",
|
|
95
|
+
"lang",
|
|
96
|
+
"dir",
|
|
97
|
+
"hidden",
|
|
98
|
+
"value",
|
|
99
|
+
"virtual-keyboard-mode",
|
|
100
|
+
"math-virtual-keyboard-policy",
|
|
101
|
+
"smart-mode",
|
|
102
|
+
"smart-fence",
|
|
103
|
+
"smart-superscript",
|
|
104
|
+
// Table attributes
|
|
105
|
+
"colspan",
|
|
106
|
+
"rowspan",
|
|
107
|
+
"border",
|
|
108
|
+
"valign",
|
|
109
|
+
"cellspacing",
|
|
110
|
+
"cellpadding",
|
|
111
|
+
"summary",
|
|
112
|
+
"scope",
|
|
113
|
+
"abbr",
|
|
114
|
+
"axis"
|
|
115
|
+
], h = {
|
|
116
|
+
ALLOWED_TAGS: d,
|
|
117
|
+
ALLOWED_ATTR: c,
|
|
118
|
+
ALLOW_DATA_ATTR: !0
|
|
119
|
+
};
|
|
120
|
+
function u(t) {
|
|
121
|
+
if (t.querySelector(".katex")) return;
|
|
122
|
+
const e = t.getAttribute("data-latex") || t.getAttribute("latex");
|
|
123
|
+
if (!e) return;
|
|
124
|
+
const a = t.classList.contains("mtx-math-block");
|
|
125
|
+
try {
|
|
126
|
+
l.render(e, t, {
|
|
127
|
+
throwOnError: !1,
|
|
128
|
+
displayMode: a,
|
|
129
|
+
output: "htmlAndMathml",
|
|
130
|
+
trust: (r) => r.command === "\\htmlClass",
|
|
131
|
+
macros: {
|
|
132
|
+
"\\class": "\\htmlClass{#1}{#2}"
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
} catch {
|
|
136
|
+
t.textContent = a ? `$$${e}$$` : `$${e}$`;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function y({ content: t, className: e }) {
|
|
140
|
+
const a = n(null), r = s(
|
|
141
|
+
() => i.sanitize(t, { ...h }),
|
|
142
|
+
[t]
|
|
143
|
+
);
|
|
144
|
+
return m(() => {
|
|
145
|
+
if (!a.current) return;
|
|
146
|
+
a.current.querySelectorAll(
|
|
147
|
+
'.mtx-math-inline, .mtx-math-block, [data-type="math-inline"], [data-type="math-block"]'
|
|
148
|
+
).forEach(u);
|
|
149
|
+
}, [r]), /* @__PURE__ */ o(
|
|
150
|
+
"div",
|
|
151
|
+
{
|
|
152
|
+
ref: a,
|
|
153
|
+
className: `mtx-content-viewer${e ? ` ${e}` : ""}`,
|
|
154
|
+
dangerouslySetInnerHTML: { __html: r }
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
export {
|
|
159
|
+
y as C,
|
|
160
|
+
h as S
|
|
161
|
+
};
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { jsxs as l, Fragment as le, jsx as a } from "react/jsx-runtime";
|
|
2
|
+
import { useId as _, useRef as $, useState as m, useCallback as t, useEffect as ie } from "react";
|
|
3
|
+
import { u as te, v as re, l as ne, i as V, U as oe } from "./index-D1pCAIcb.js";
|
|
4
|
+
function de(u) {
|
|
5
|
+
return u < 1024 ? `${u} B` : u < 1024 * 1024 ? `${(u / 1024).toFixed(1)} KB` : `${(u / (1024 * 1024)).toFixed(1)} MB`;
|
|
6
|
+
}
|
|
7
|
+
function ue({
|
|
8
|
+
isOpen: u,
|
|
9
|
+
onClose: B,
|
|
10
|
+
onInsert: v,
|
|
11
|
+
onImageUpload: k
|
|
12
|
+
}) {
|
|
13
|
+
const x = _(), j = _(), E = _(), G = _(), N = _(), S = $(null), [o, J] = m(k ? "upload" : "url"), [d, D] = m(""), [g, F] = m(""), [q, U] = m(!1), [h, b] = m(!1), [f, p] = m(null), [r, M] = m(null), [P, n] = m(""), [T, w] = m(null), L = $(null), s = $(null), W = t(() => {
|
|
14
|
+
s.current && (URL.revokeObjectURL(s.current), s.current = null), D(""), F(""), p(null), M(null), n(""), w(null), b(!1), U(!1);
|
|
15
|
+
}, []), c = t(() => {
|
|
16
|
+
W(), B();
|
|
17
|
+
}, [B, W]);
|
|
18
|
+
te({ isOpen: u, dialogRef: S, onClose: c }), ie(() => () => {
|
|
19
|
+
s.current && (URL.revokeObjectURL(s.current), s.current = null);
|
|
20
|
+
}, []);
|
|
21
|
+
const y = t(() => {
|
|
22
|
+
s.current && (URL.revokeObjectURL(s.current), s.current = null), p(null), M(null);
|
|
23
|
+
}, []), A = t((e) => {
|
|
24
|
+
J(e), n(""), w(null), y(), D("");
|
|
25
|
+
}, [y]), R = t(
|
|
26
|
+
async (e) => {
|
|
27
|
+
const i = await re(e);
|
|
28
|
+
if (!i.valid) {
|
|
29
|
+
n(i.error);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
n(""), w(null), y();
|
|
33
|
+
const C = URL.createObjectURL(e);
|
|
34
|
+
if (s.current = C, p(C), M(e), !g.trim()) {
|
|
35
|
+
const I = e.name.replace(/\.[^.]+$/, "");
|
|
36
|
+
F(I);
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
[g, y]
|
|
40
|
+
), O = t(async () => {
|
|
41
|
+
if (!r) return;
|
|
42
|
+
if (k) {
|
|
43
|
+
b(!0);
|
|
44
|
+
try {
|
|
45
|
+
const i = await k(r);
|
|
46
|
+
if (!i)
|
|
47
|
+
throw new Error("Upload gagal: URL tidak valid");
|
|
48
|
+
v(i, g.trim() || r.name), c();
|
|
49
|
+
} catch (i) {
|
|
50
|
+
const C = i instanceof Error ? i.message : "Upload gagal", I = ne("Image insert upload failed", i);
|
|
51
|
+
n(`Upload gagal: ${C}`), w(I);
|
|
52
|
+
} finally {
|
|
53
|
+
b(!1);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
b(!0);
|
|
58
|
+
const e = new FileReader();
|
|
59
|
+
e.onload = () => {
|
|
60
|
+
const i = e.result;
|
|
61
|
+
v(i, g.trim() || r.name), c();
|
|
62
|
+
}, e.onerror = () => {
|
|
63
|
+
n("Gagal membaca file gambar"), b(!1);
|
|
64
|
+
}, e.readAsDataURL(r);
|
|
65
|
+
}, [r, k, v, g, c]), H = t(
|
|
66
|
+
(e) => {
|
|
67
|
+
const i = e.target.files?.[0];
|
|
68
|
+
i && R(i), e.target.value = "";
|
|
69
|
+
},
|
|
70
|
+
[R]
|
|
71
|
+
), Q = t((e) => {
|
|
72
|
+
e.stopPropagation(), L.current?.click();
|
|
73
|
+
}, []), X = t((e) => {
|
|
74
|
+
e.preventDefault(), e.stopPropagation(), U(!0);
|
|
75
|
+
}, []), Y = t((e) => {
|
|
76
|
+
e.preventDefault(), e.stopPropagation(), U(!1);
|
|
77
|
+
}, []), Z = t(
|
|
78
|
+
(e) => {
|
|
79
|
+
e.preventDefault(), e.stopPropagation(), U(!1);
|
|
80
|
+
const i = e.dataTransfer.files?.[0];
|
|
81
|
+
i && R(i);
|
|
82
|
+
},
|
|
83
|
+
[R]
|
|
84
|
+
), z = t(() => {
|
|
85
|
+
if (!d.trim()) {
|
|
86
|
+
n("Masukkan URL gambar");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!V(d)) {
|
|
90
|
+
n("URL tidak valid. Gunakan http:// atau https://");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
v(d.trim(), g.trim() || void 0), c();
|
|
94
|
+
}, [d, g, v, c]), ee = t(() => {
|
|
95
|
+
if (!d.trim()) {
|
|
96
|
+
p(null);
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (!V(d)) {
|
|
100
|
+
n("URL tidak valid. Gunakan http:// atau https://"), p(null);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
p(d.trim()), n("");
|
|
104
|
+
}, [d]), K = t(() => {
|
|
105
|
+
o === "upload" ? O() : z();
|
|
106
|
+
}, [o, O, z]), ae = o === "upload" ? !!r && !h : !!d.trim() && !h;
|
|
107
|
+
return u ? /* @__PURE__ */ l(le, { children: [
|
|
108
|
+
/* @__PURE__ */ a("div", { className: "mtx-dialog-overlay", onClick: c, "aria-hidden": "true" }),
|
|
109
|
+
/* @__PURE__ */ l(
|
|
110
|
+
"div",
|
|
111
|
+
{
|
|
112
|
+
className: "mtx-image-dialog",
|
|
113
|
+
ref: S,
|
|
114
|
+
role: "dialog",
|
|
115
|
+
"aria-modal": "true",
|
|
116
|
+
"aria-labelledby": x,
|
|
117
|
+
tabIndex: -1,
|
|
118
|
+
children: [
|
|
119
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__header", children: [
|
|
120
|
+
/* @__PURE__ */ a("h3", { id: x, children: "Sisipkan Gambar" }),
|
|
121
|
+
/* @__PURE__ */ a(
|
|
122
|
+
"button",
|
|
123
|
+
{
|
|
124
|
+
type: "button",
|
|
125
|
+
className: "mtx-image-dialog__close",
|
|
126
|
+
onClick: c,
|
|
127
|
+
"aria-label": "Tutup",
|
|
128
|
+
children: /* @__PURE__ */ a("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ a("path", { d: "M3 3l10 10M13 3L3 13", stroke: "currentColor", strokeWidth: "1.75", strokeLinecap: "round" }) })
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__tabs", role: "tablist", "aria-label": "Metode sisip gambar", children: [
|
|
133
|
+
/* @__PURE__ */ l(
|
|
134
|
+
"button",
|
|
135
|
+
{
|
|
136
|
+
type: "button",
|
|
137
|
+
id: j,
|
|
138
|
+
role: "tab",
|
|
139
|
+
"aria-selected": o === "upload",
|
|
140
|
+
"aria-controls": G,
|
|
141
|
+
className: `mtx-image-dialog__tab ${o === "upload" ? "is-active" : ""}`,
|
|
142
|
+
onClick: () => A("upload"),
|
|
143
|
+
children: [
|
|
144
|
+
/* @__PURE__ */ l("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
|
|
145
|
+
/* @__PURE__ */ a("path", { d: "M8 10V2M8 2L5 5M8 2l3 3", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
146
|
+
/* @__PURE__ */ a("path", { d: "M2 12v1.5A1.5 1.5 0 003.5 15h9a1.5 1.5 0 001.5-1.5V12", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
147
|
+
] }),
|
|
148
|
+
"Upload File"
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
),
|
|
152
|
+
/* @__PURE__ */ l(
|
|
153
|
+
"button",
|
|
154
|
+
{
|
|
155
|
+
type: "button",
|
|
156
|
+
id: E,
|
|
157
|
+
role: "tab",
|
|
158
|
+
"aria-selected": o === "url",
|
|
159
|
+
"aria-controls": N,
|
|
160
|
+
className: `mtx-image-dialog__tab ${o === "url" ? "is-active" : ""}`,
|
|
161
|
+
onClick: () => A("url"),
|
|
162
|
+
children: [
|
|
163
|
+
/* @__PURE__ */ l("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
|
|
164
|
+
/* @__PURE__ */ a("path", { d: "M6.5 9.5a3 3 0 004.24 0l2-2a3 3 0 00-4.24-4.24l-.88.88", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
|
|
165
|
+
/* @__PURE__ */ a("path", { d: "M9.5 6.5a3 3 0 00-4.24 0l-2 2a3 3 0 004.24 4.24l.88-.88", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
|
|
166
|
+
] }),
|
|
167
|
+
"URL"
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
)
|
|
171
|
+
] }),
|
|
172
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__body", children: [
|
|
173
|
+
o === "upload" && /* @__PURE__ */ a(
|
|
174
|
+
"div",
|
|
175
|
+
{
|
|
176
|
+
id: G,
|
|
177
|
+
role: "tabpanel",
|
|
178
|
+
"aria-labelledby": j,
|
|
179
|
+
className: "mtx-image-dialog__panel",
|
|
180
|
+
children: /* @__PURE__ */ l(
|
|
181
|
+
"div",
|
|
182
|
+
{
|
|
183
|
+
className: `mtx-image-dialog__dropzone ${q ? "is-dragging" : ""} ${f ? "has-preview" : ""}`,
|
|
184
|
+
onDragOver: X,
|
|
185
|
+
onDragLeave: Y,
|
|
186
|
+
onDrop: Z,
|
|
187
|
+
onClick: () => !h && L.current?.click(),
|
|
188
|
+
onKeyDown: (e) => {
|
|
189
|
+
(e.key === "Enter" || e.key === " ") && (e.preventDefault(), h || L.current?.click());
|
|
190
|
+
},
|
|
191
|
+
role: "button",
|
|
192
|
+
tabIndex: 0,
|
|
193
|
+
"aria-label": "Pilih file gambar atau seret dan lepas di sini",
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ a(
|
|
196
|
+
"input",
|
|
197
|
+
{
|
|
198
|
+
ref: L,
|
|
199
|
+
type: "file",
|
|
200
|
+
accept: "image/jpeg,image/png,image/gif,image/webp",
|
|
201
|
+
onChange: H,
|
|
202
|
+
className: "mtx-image-dialog__file-input",
|
|
203
|
+
"aria-hidden": "true",
|
|
204
|
+
tabIndex: -1
|
|
205
|
+
}
|
|
206
|
+
),
|
|
207
|
+
h ? /* @__PURE__ */ l("div", { className: "mtx-image-dialog__uploading", children: [
|
|
208
|
+
/* @__PURE__ */ a("div", { className: "mtx-image-dialog__spinner", "aria-hidden": "true" }),
|
|
209
|
+
/* @__PURE__ */ a("p", { children: "Mengupload gambar..." })
|
|
210
|
+
] }) : f && r ? /* @__PURE__ */ l("div", { className: "mtx-image-dialog__preview-card", children: [
|
|
211
|
+
/* @__PURE__ */ a("img", { src: f, alt: "", className: "mtx-image-dialog__preview-img" }),
|
|
212
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__preview-meta", children: [
|
|
213
|
+
/* @__PURE__ */ a("span", { className: "mtx-image-dialog__preview-name", title: r.name, children: r.name }),
|
|
214
|
+
/* @__PURE__ */ a("span", { className: "mtx-image-dialog__preview-size", children: de(r.size) })
|
|
215
|
+
] }),
|
|
216
|
+
/* @__PURE__ */ a(
|
|
217
|
+
"button",
|
|
218
|
+
{
|
|
219
|
+
type: "button",
|
|
220
|
+
className: "mtx-image-dialog__change-file",
|
|
221
|
+
onClick: Q,
|
|
222
|
+
children: "Ganti file"
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
] }) : /* @__PURE__ */ l("div", { className: "mtx-image-dialog__placeholder", children: [
|
|
226
|
+
/* @__PURE__ */ a("div", { className: "mtx-image-dialog__placeholder-icon", "aria-hidden": "true", children: /* @__PURE__ */ l("svg", { width: "40", height: "40", viewBox: "0 0 48 48", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
|
|
227
|
+
/* @__PURE__ */ a("rect", { x: "4", y: "8", width: "40", height: "32", rx: "4" }),
|
|
228
|
+
/* @__PURE__ */ a("circle", { cx: "16", cy: "20", r: "4" }),
|
|
229
|
+
/* @__PURE__ */ a("path", { d: "M4 36l10-10 6 6 8-10 16 14" })
|
|
230
|
+
] }) }),
|
|
231
|
+
/* @__PURE__ */ a("p", { className: "mtx-image-dialog__placeholder-title", children: "Klik untuk pilih file atau drag & drop" }),
|
|
232
|
+
/* @__PURE__ */ a("p", { className: "mtx-image-dialog__placeholder-hint", children: "JPG, PNG, GIF, WebP — maks. 10 MB" })
|
|
233
|
+
] })
|
|
234
|
+
]
|
|
235
|
+
}
|
|
236
|
+
)
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
o === "url" && /* @__PURE__ */ l(
|
|
240
|
+
"div",
|
|
241
|
+
{
|
|
242
|
+
id: N,
|
|
243
|
+
role: "tabpanel",
|
|
244
|
+
"aria-labelledby": E,
|
|
245
|
+
className: "mtx-image-dialog__panel",
|
|
246
|
+
children: [
|
|
247
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__url-form", children: [
|
|
248
|
+
/* @__PURE__ */ a("label", { htmlFor: `${N}-url`, children: "URL Gambar" }),
|
|
249
|
+
/* @__PURE__ */ a(
|
|
250
|
+
"input",
|
|
251
|
+
{
|
|
252
|
+
id: `${N}-url`,
|
|
253
|
+
type: "url",
|
|
254
|
+
value: d,
|
|
255
|
+
onChange: (e) => {
|
|
256
|
+
D(e.target.value), P && n("");
|
|
257
|
+
},
|
|
258
|
+
placeholder: "https://example.com/gambar.jpg",
|
|
259
|
+
onKeyDown: (e) => {
|
|
260
|
+
e.key === "Enter" && K();
|
|
261
|
+
},
|
|
262
|
+
onBlur: ee,
|
|
263
|
+
autoFocus: !0
|
|
264
|
+
}
|
|
265
|
+
)
|
|
266
|
+
] }),
|
|
267
|
+
f && o === "url" && /* @__PURE__ */ a("div", { className: "mtx-image-dialog__url-preview", children: /* @__PURE__ */ a(
|
|
268
|
+
"img",
|
|
269
|
+
{
|
|
270
|
+
src: f,
|
|
271
|
+
alt: "Pratinjau gambar dari URL",
|
|
272
|
+
onError: () => {
|
|
273
|
+
p(null), n("Gambar tidak dapat dimuat dari URL ini");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
) })
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
),
|
|
280
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__alt", children: [
|
|
281
|
+
/* @__PURE__ */ a("label", { htmlFor: `${x}-alt`, children: "Teks alternatif" }),
|
|
282
|
+
/* @__PURE__ */ a(
|
|
283
|
+
"input",
|
|
284
|
+
{
|
|
285
|
+
id: `${x}-alt`,
|
|
286
|
+
type: "text",
|
|
287
|
+
value: g,
|
|
288
|
+
onChange: (e) => F(e.target.value),
|
|
289
|
+
placeholder: "Deskripsi gambar untuk aksesibilitas (opsional)"
|
|
290
|
+
}
|
|
291
|
+
),
|
|
292
|
+
/* @__PURE__ */ a("p", { className: "mtx-image-dialog__alt-hint", children: "Membantu pembaca layar memahami isi gambar." })
|
|
293
|
+
] }),
|
|
294
|
+
P && /* @__PURE__ */ l("div", { className: "mtx-image-dialog__error", role: "alert", children: [
|
|
295
|
+
/* @__PURE__ */ a("div", { children: P }),
|
|
296
|
+
T && /* @__PURE__ */ a(oe, { debug: T })
|
|
297
|
+
] })
|
|
298
|
+
] }),
|
|
299
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__footer", children: [
|
|
300
|
+
/* @__PURE__ */ a("p", { className: "mtx-image-dialog__footer-hint", "aria-live": "polite", children: o === "upload" && !r && !h ? "Pilih file terlebih dahulu, lalu klik Sisipkan." : "" }),
|
|
301
|
+
/* @__PURE__ */ l("div", { className: "mtx-image-dialog__actions", children: [
|
|
302
|
+
/* @__PURE__ */ a(
|
|
303
|
+
"button",
|
|
304
|
+
{
|
|
305
|
+
type: "button",
|
|
306
|
+
className: "mtx-image-dialog__btn mtx-image-dialog__btn--cancel",
|
|
307
|
+
onClick: c,
|
|
308
|
+
disabled: h,
|
|
309
|
+
children: "Batal"
|
|
310
|
+
}
|
|
311
|
+
),
|
|
312
|
+
/* @__PURE__ */ a(
|
|
313
|
+
"button",
|
|
314
|
+
{
|
|
315
|
+
type: "button",
|
|
316
|
+
className: "mtx-image-dialog__btn mtx-image-dialog__btn--insert",
|
|
317
|
+
onClick: K,
|
|
318
|
+
disabled: !ae,
|
|
319
|
+
children: h ? "Mengupload..." : "Sisipkan"
|
|
320
|
+
}
|
|
321
|
+
)
|
|
322
|
+
] })
|
|
323
|
+
] })
|
|
324
|
+
]
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
] }) : null;
|
|
328
|
+
}
|
|
329
|
+
export {
|
|
330
|
+
ue as ImageInsertDialog
|
|
331
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as e, jsxs as t } from "react/jsx-runtime";
|
|
2
2
|
import v, { useId as g, useRef as y, useState as c, useEffect as k, useCallback as w } from "react";
|
|
3
|
-
import { u as C } from "./index-
|
|
3
|
+
import { u as C } from "./index-D1pCAIcb.js";
|
|
4
4
|
import { u as M } from "./useDraggable-DEoLIB5k.js";
|
|
5
5
|
const I = v.memo(({ isOpen: i, onInsert: d, onClose: m }) => {
|
|
6
6
|
const u = g(), b = y(null), [l, n] = c(3), [a, s] = c(3), [o, h] = c(!0), { position: p, handleMouseDown: N } = M(i);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as a, jsxs as e } from "react/jsx-runtime";
|
|
2
2
|
import S, { useId as T, useRef as _, useState as r, useEffect as w, useCallback as c } from "react";
|
|
3
|
-
import { u as F } from "./index-
|
|
3
|
+
import { u as F } from "./index-D1pCAIcb.js";
|
|
4
4
|
const I = S.memo(({
|
|
5
5
|
isOpen: s,
|
|
6
6
|
currentUrl: o = "",
|
|
@@ -5,4 +5,4 @@ import { MathTextXEditorProps } from './types';
|
|
|
5
5
|
* Drop-in replacement for CKEditor in solutest.id.
|
|
6
6
|
* Supports rich text formatting and visual math input without LaTeX.
|
|
7
7
|
*/
|
|
8
|
-
export declare function MathTextXEditor({ content, onChange, onSave, editable, placeholder, toolbarMode, toolbar
|
|
8
|
+
export declare function MathTextXEditor({ content, onChange, onSave, editable, placeholder, toolbarMode, toolbar, mathToolbar, templates, outputFormat, mathInputMode: mathInputModeProp, onMathInputModeChange, className, minHeight, maxHeight, autoFocus, onImageUpload, onBeforePasteHTML, tableStyle, }: MathTextXEditorProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/{TablePropertiesDialogImpl-BLbhyfA0.js → TablePropertiesDialogImpl-C2eqgZMa.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as t, jsxs as l } from "react/jsx-runtime";
|
|
2
2
|
import g, { useId as x, useRef as p, useState as f, useEffect as y, useCallback as h } from "react";
|
|
3
|
-
import { u as N } from "./index-
|
|
3
|
+
import { u as N } from "./index-D1pCAIcb.js";
|
|
4
4
|
import { u as k } from "./useDraggable-DEoLIB5k.js";
|
|
5
5
|
const z = g.memo(({
|
|
6
6
|
isOpen: a,
|