sunny-html-editor 1.0.3 → 1.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.
Files changed (3) hide show
  1. package/README.md +47 -104
  2. package/html-editor.js +168 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -4,90 +4,64 @@
4
4
 
5
5
  ## 特徴
6
6
 
7
- - **自己完結型** - 基本機能は外部依存なし
7
+ - **自己完結型** - ツールバー・コンテキストメニューを自動生成
8
+ - **遅延読み込み** - Excel/PDF出力用ライブラリは使用時に自動読み込み
8
9
  - **WYSIWYG編集** - 見たまま編集、右クリックで要素追加
9
10
  - **キーボード操作** - ↑↓で要素間移動、Shift/Ctrl+Enterで項目追加
10
11
  - **多彩なエクスポート** - Markdown、HTML、Excel、PDF
11
12
  - **サイドバーナビ** - 目次自動生成
12
13
 
13
- ## 外部ライブラリ依存
14
-
15
- ### 必須
16
-
17
- なし(基本機能のみ使用する場合)
18
-
19
- ### オプション(Excel/PDF出力に必要)
20
-
21
- | ライブラリ | バージョン | 用途 | CDN |
22
- |-----------|----------|------|-----|
23
- | ExcelJS | 4.3.0+ | Excel出力 | https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.3.0/exceljs.min.js |
24
- | jsPDF | 2.5.1+ | PDF出力 | https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js |
25
- | html2canvas | 1.4.1+ | PDF出力(HTML→Canvas変換) | https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js |
14
+ ## インストール
26
15
 
27
- Excel/PDF出力を使う場合は、html-editor.jsより**前に**読み込んでください:
16
+ ### CDN(推奨)
28
17
 
29
18
  ```html
30
- <!-- 外部ライブラリ(Excel/PDF出力に必要) -->
31
- <script src="https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.3.0/exceljs.min.js"></script>
32
- <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
33
- <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
34
-
35
- <!-- HTML Editor -->
36
- <link rel="stylesheet" href="html-editor.css">
37
- <script src="html-editor.js"></script>
19
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sunny-html-editor/html-editor.css">
20
+ <script src="https://cdn.jsdelivr.net/npm/sunny-html-editor/html-editor.js"></script>
38
21
  ```
39
22
 
40
- ## インストール
41
-
42
23
  ### npm
43
24
 
44
25
  ```bash
45
26
  npm install sunny-html-editor
46
27
  ```
47
28
 
48
- ### CDN
49
-
50
- ```html
51
- <link rel="stylesheet" href="https://unpkg.com/sunny-html-editor/html-editor.css">
52
- <script src="https://unpkg.com/sunny-html-editor/html-editor.js"></script>
53
- ```
54
-
55
- ### ダウンロード
56
-
57
- `html-editor.css` と `html-editor.js` をダウンロードして読み込み。
58
-
59
29
  ## 使い方
60
30
 
61
- ### 基本
31
+ `id="editorContainer"` を持つコンテナを用意するだけ。ツールバーとコンテキストメニューは自動生成されます。
62
32
 
63
33
  ```html
64
34
  <!DOCTYPE html>
65
- <html>
35
+ <html lang="ja">
66
36
  <head>
67
- <link rel="stylesheet" href="html-editor.css">
37
+ <meta charset="UTF-8">
38
+ <title>ドキュメントタイトル</title>
39
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sunny-html-editor/html-editor.css">
68
40
  </head>
69
41
  <body>
70
- <div id="content">
71
- <h1>タイトル</h1>
72
- <p>既存のHTMLコンテンツ</p>
73
- </div>
74
-
75
- <script src="html-editor.js"></script>
76
- <script>
77
- HtmlEditor.init('#content');
78
- </script>
42
+ <div class="layout">
43
+ <main class="main">
44
+ <div class="container" id="editorContainer">
45
+ <h1>タイトル</h1>
46
+ <h2 id="section1">見出し</h2>
47
+ <p>本文...</p>
48
+ </div>
49
+ </main>
50
+ </div>
51
+ <script src="https://cdn.jsdelivr.net/npm/sunny-html-editor/html-editor.js"></script>
79
52
  </body>
80
53
  </html>
81
54
  ```
82
55
 
83
- ### オプション
56
+ ## 外部ライブラリ
84
57
 
85
- ```javascript
86
- HtmlEditor.init('#content', {
87
- sidebar: true, // サイドバー表示(デフォルト: true)
88
- toolbar: true // ツールバー表示(デフォルト: true)
89
- });
90
- ```
58
+ Excel/PDF出力に必要なライブラリは**使用時に自動で読み込まれます**。事前の読み込みは不要です。
59
+
60
+ | ライブラリ | 用途 |
61
+ |-----------|------|
62
+ | ExcelJS | Excel出力 |
63
+ | jsPDF | PDF出力 |
64
+ | html2canvas | PDF出力(HTML→Canvas変換) |
91
65
 
92
66
  ## キーボードショートカット
93
67
 
@@ -113,64 +87,33 @@ HtmlEditor.init('#content', {
113
87
 
114
88
  テーブル上では行/列の追加・削除も可能。
115
89
 
116
- ## エクスポート
90
+ ## ツールバーボタン
117
91
 
118
- ### Markdown
119
-
120
- ```javascript
121
- // ツールバーのⓂ️ボタン、または Ctrl+M
122
- // クリップボードにコピーされます
123
- ```
92
+ | ボタン | 機能 |
93
+ |--------|------|
94
+ | ↔️ | サイドバー表示/非表示 |
95
+ | 📝 | 編集モード切替 |
96
+ | Ⓜ️ | Markdownコピー |
97
+ | 🌐 | HTMLコピー |
98
+ | 💾 | HTML保存(ダウンロード) |
99
+ | 📊 | Excel出力 |
100
+ | 📕 | PDF出力 |
124
101
 
125
- ### HTML
102
+ ## バージョン履歴
126
103
 
127
- ```javascript
128
- // ツールバーの🌐ボタン
129
- // クリップボードにコピーされます
130
- ```
131
-
132
- ### HTML保存
133
-
134
- ```javascript
135
- // ツールバーの💾ボタン
136
- // HTMLファイルとしてダウンロード
137
- ```
138
-
139
- ### Excel / PDF
140
-
141
- 上記の外部ライブラリが読み込まれている必要があります。
142
- 読み込まれていない場合、ボタンを押すとアラートが表示されます。
143
-
144
- ## API
145
-
146
- ### HtmlEditor.init(selector, options)
147
-
148
- エディタを初期化します。
149
-
150
- - `selector` - コンテナ要素のセレクタまたはDOM要素
151
- - `options` - オプションオブジェクト
152
-
153
- ### 返り値
154
-
155
- HtmlEditorインスタンスを返します。
156
-
157
- ```javascript
158
- var editor = HtmlEditor.init('#content');
159
-
160
- // 編集モード切替
161
- editor._action_editMode();
162
-
163
- // サイドバー更新
164
- editor._updateSidebar();
165
- ```
104
+ - **1.1.0** - ツールバー・コンテキストメニュー自動生成、外部ライブラリ遅延読み込み
105
+ - **1.0.3** - nullチェック強化
106
+ - **1.0.2** - DOMContentLoaded自動初期化
107
+ - **1.0.1** - メディアクエリ削除
108
+ - **1.0.0** - 初回リリース
166
109
 
167
110
  ## ブラウザ対応
168
111
 
169
- - Chrome (推奨)
112
+ - Chrome(推奨)
170
113
  - Firefox
171
114
  - Safari
172
115
  - Edge
173
116
 
174
117
  ## ライセンス
175
118
 
176
- MIT
119
+ MIT License - OnMax Group
package/html-editor.js CHANGED
@@ -1,11 +1,142 @@
1
1
  /**
2
2
  * Sunny HTML Editor - 軽量WYSIWYGエディタ
3
- * @version 1.0.3
3
+ * @version 1.1.0
4
4
  */
5
+
6
+ // ========== HTML生成関数 ==========
7
+ function getToolbarHtml() {
8
+ return '<div class="toolbar">' +
9
+ '<button id="toggleSidebar" title="サイドメニュー表示/非表示 (Ctrl+B)">↔️</button>' +
10
+ '<button id="editMode" title="編集モード (Ctrl+E)">📝</button>' +
11
+ '<button id="exportMd" title="Markdownコピー (Ctrl+M)">Ⓜ️</button>' +
12
+ '<button id="exportHtml" title="HTMLコピー">🌐</button>' +
13
+ '<button id="saveHtml" title="HTML保存">💾</button>' +
14
+ '<button id="exportExcel" title="Excel出力">📊</button>' +
15
+ '<button id="exportPdf" title="PDF出力">📕</button>' +
16
+ '</div>';
17
+ }
18
+
19
+ function getContextMenusHtml() {
20
+ // テーブル用
21
+ var tableMenu = '<div class="context-menu" id="contextMenu">' +
22
+ '<div class="context-menu-item" data-action="addRowBelow">+ 行を追加</div>' +
23
+ '<div class="context-menu-item" data-action="addColRight">+ 列を追加</div>' +
24
+ '<div class="context-menu-divider"></div>' +
25
+ '<div class="context-menu-item delete" data-action="deleteRow">🗑 行を削除</div>' +
26
+ '<div class="context-menu-item delete" data-action="deleteCol">🗑 列を削除</div>' +
27
+ '<div class="context-menu-item delete" data-action="deleteTable">🗑 テーブルを削除</div>' +
28
+ '<div class="context-menu-divider thick"></div>' +
29
+ '<div class="context-menu-item" data-action="addH2">+ 大見出し</div>' +
30
+ '<div class="context-menu-item" data-action="addH3">+ 中見出し</div>' +
31
+ '<div class="context-menu-item" data-action="addH4">+ 小見出し</div>' +
32
+ '<div class="context-menu-divider"></div>' +
33
+ '<div class="context-menu-item" data-action="addTableBelow">+ テーブル</div>' +
34
+ '<div class="context-menu-divider"></div>' +
35
+ '<div class="context-menu-item" data-action="addParagraph">+ 本文</div>' +
36
+ '<div class="context-menu-divider"></div>' +
37
+ '<div class="context-menu-item" data-action="addUl">+ 箇条書き</div>' +
38
+ '<div class="context-menu-item" data-action="addOl">+ 番号リスト</div>' +
39
+ '<div class="context-menu-divider"></div>' +
40
+ '<div class="context-menu-item" data-action="addPre">+ コードブロック</div>' +
41
+ '<div class="context-menu-item" data-action="addBlockquote">+ 引用</div>' +
42
+ '</div>';
43
+
44
+ // 共通メニュー項目
45
+ var commonItems =
46
+ '<div class="context-menu-item" data-action="addH2">+ 大見出し</div>' +
47
+ '<div class="context-menu-item" data-action="addH3">+ 中見出し</div>' +
48
+ '<div class="context-menu-item" data-action="addH4">+ 小見出し</div>' +
49
+ '<div class="context-menu-divider"></div>' +
50
+ '<div class="context-menu-item" data-action="addTable">+ テーブル</div>' +
51
+ '<div class="context-menu-divider"></div>' +
52
+ '<div class="context-menu-item" data-action="addParagraph">+ 本文</div>' +
53
+ '<div class="context-menu-divider"></div>' +
54
+ '<div class="context-menu-item" data-action="addUl">+ 箇条書き</div>' +
55
+ '<div class="context-menu-item" data-action="addOl">+ 番号リスト</div>' +
56
+ '<div class="context-menu-divider"></div>' +
57
+ '<div class="context-menu-item" data-action="addPre">+ コードブロック</div>' +
58
+ '<div class="context-menu-item" data-action="addBlockquote">+ 引用</div>';
59
+
60
+ // h1用
61
+ var h1Menu = '<div class="context-menu" id="h1ContextMenu">' + commonItems + '</div>';
62
+
63
+ // 見出し用
64
+ var headingMenu = '<div class="context-menu" id="headingContextMenu">' + commonItems +
65
+ '<div class="context-menu-divider"></div>' +
66
+ '<div class="context-menu-item delete" data-action="deleteSection">🗑 セクションを削除</div>' +
67
+ '</div>';
68
+
69
+ // 段落用
70
+ var paragraphMenu = '<div class="context-menu" id="paragraphContextMenu">' + commonItems +
71
+ '<div class="context-menu-divider"></div>' +
72
+ '<div class="context-menu-item delete" data-action="deleteParagraph">🗑 本文を削除</div>' +
73
+ '</div>';
74
+
75
+ // リスト用
76
+ var listMenu = '<div class="context-menu" id="listContextMenu">' + commonItems +
77
+ '<div class="context-menu-divider"></div>' +
78
+ '<div class="context-menu-item delete" data-action="deleteList">🗑 リストを削除</div>' +
79
+ '</div>';
80
+
81
+ // コードブロック用
82
+ var preMenu = '<div class="context-menu" id="preContextMenu">' + commonItems +
83
+ '<div class="context-menu-divider"></div>' +
84
+ '<div class="context-menu-item delete" data-action="deletePre">🗑 コードブロックを削除</div>' +
85
+ '</div>';
86
+
87
+ // 引用用
88
+ var blockquoteMenu = '<div class="context-menu" id="blockquoteContextMenu">' + commonItems +
89
+ '<div class="context-menu-divider"></div>' +
90
+ '<div class="context-menu-item delete" data-action="deleteBlockquote">🗑 引用を削除</div>' +
91
+ '</div>';
92
+
93
+ return tableMenu + h1Menu + headingMenu + paragraphMenu + listMenu + preMenu + blockquoteMenu;
94
+ }
95
+
96
+ // ========== 外部ライブラリ遅延読み込み ==========
97
+ var libraryPromises = {};
98
+
99
+ function loadScript(src) {
100
+ if (libraryPromises[src]) return libraryPromises[src];
101
+
102
+ libraryPromises[src] = new Promise(function(resolve, reject) {
103
+ var script = document.createElement('script');
104
+ script.src = src;
105
+ script.onload = resolve;
106
+ script.onerror = reject;
107
+ document.head.appendChild(script);
108
+ });
109
+
110
+ return libraryPromises[src];
111
+ }
112
+
113
+ async function ensurePdfLibraries() {
114
+ if (!window.jspdf) {
115
+ await loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js');
116
+ }
117
+ if (!window.html2canvas) {
118
+ await loadScript('https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js');
119
+ }
120
+ }
121
+
122
+ async function ensureExcelLibrary() {
123
+ if (!window.ExcelJS) {
124
+ await loadScript('https://cdnjs.cloudflare.com/ajax/libs/exceljs/4.3.0/exceljs.min.js');
125
+ }
126
+ }
127
+
128
+ // ========== 初期化 ==========
5
129
  document.addEventListener('DOMContentLoaded', function() {
6
130
  var container = document.getElementById('editorContainer');
7
- if (!container) return; // editorContainerがなければ何もしない
131
+ if (!container) return;
132
+
133
+ // ツールバー挿入
134
+ container.insertAdjacentHTML('afterbegin', getToolbarHtml());
8
135
 
136
+ // コンテキストメニュー挿入(bodyの末尾に配置)
137
+ document.body.insertAdjacentHTML('beforeend', getContextMenusHtml());
138
+
139
+ // ボタン参照取得
9
140
  var editModeBtn = document.getElementById('editMode');
10
141
  var toggleSidebarBtn = document.getElementById('toggleSidebar');
11
142
  var saveHtmlBtn = document.getElementById('saveHtml');
@@ -47,7 +178,25 @@ document.addEventListener('DOMContentLoaded', function() {
47
178
  var wasEditMode = isEditMode;
48
179
  if (isEditMode && editModeBtn) editModeBtn.click();
49
180
 
50
- var html = '<!DOCTYPE html>\n' + document.documentElement.outerHTML;
181
+ // ツールバーとコンテキストメニューを一時的に非表示
182
+ var toolbar = container.querySelector('.toolbar');
183
+ var contextMenus = document.querySelectorAll('.context-menu');
184
+
185
+ if (toolbar) toolbar.style.display = 'none';
186
+ contextMenus.forEach(function(menu) { menu.style.display = 'none'; });
187
+
188
+ // HTML取得(ツールバーとコンテキストメニューを除外したクリーンなHTML)
189
+ var docClone = document.documentElement.cloneNode(true);
190
+ var cloneToolbar = docClone.querySelector('.toolbar');
191
+ var cloneMenus = docClone.querySelectorAll('.context-menu');
192
+ if (cloneToolbar) cloneToolbar.remove();
193
+ cloneMenus.forEach(function(menu) { menu.remove(); });
194
+
195
+ var html = '<!DOCTYPE html>\n' + docClone.outerHTML;
196
+
197
+ // 表示を復元
198
+ if (toolbar) toolbar.style.display = '';
199
+ contextMenus.forEach(function(menu) { menu.style.display = ''; });
51
200
 
52
201
  if (wasEditMode && editModeBtn) editModeBtn.click();
53
202
 
@@ -104,7 +253,7 @@ document.addEventListener('DOMContentLoaded', function() {
104
253
  var toolbar = content.querySelector('.toolbar');
105
254
  if (toolbar) toolbar.remove();
106
255
 
107
- // 右クリックメニューを除去
256
+ // 右クリックメニューを除去(念のため)
108
257
  content.querySelectorAll('.context-menu').forEach(function(menu) {
109
258
  menu.remove();
110
259
  });
@@ -130,41 +279,23 @@ document.addEventListener('DOMContentLoaded', function() {
130
279
  var h1 = container.querySelector('h1');
131
280
  var title = h1 ? h1.textContent.trim() : 'Document';
132
281
 
133
- // CSSを取得(サイドバー、リサイザー、ツールバー、右クリックメニュー関連を除外)
134
- var styleEl = document.querySelector('style');
135
- var css = '';
136
- if (styleEl) {
137
- css = styleEl.textContent
138
- .replace(/\.sidebar[\s\S]*?\}\n/g, '')
139
- .replace(/\.sidebar [^{]*\{[\s\S]*?\}\n/g, '')
140
- .replace(/\.resizer[\s\S]*?\}\n/g, '')
141
- .replace(/\.toolbar[\s\S]*?\}\n/g, '')
142
- .replace(/\.toolbar [^{]*\{[\s\S]*?\}\n/g, '')
143
- .replace(/\.context-menu[\s\S]*?\}\n/g, '')
144
- .replace(/\.context-menu[^{]*\{[\s\S]*?\}\n/g, '')
145
- .replace(/\.edit-mode[\s\S]*?\}\n/g, '')
146
- .replace(/\.edit-mode [^{]*\{[\s\S]*?\}\n/g, '')
147
- .replace(/@media[^{]*\{[\s\S]*?\}\s*\}/g, '')
148
- .replace(/\n\s*\n/g, '\n')
149
- .trim();
150
- }
151
-
152
- // 完全なHTML構造(CSS含む、ラッパー構造含む)
282
+ // 完全なHTML構造(外部CSS/JS参照付き)
153
283
  var html = '<!DOCTYPE html>\n' +
154
284
  '<html lang="ja">\n' +
155
285
  '<head>\n' +
156
- ' <meta charset="UTF-8">\n' +
157
- ' <title>' + title + '</title>\n' +
158
- '<style>\n' + css + '\n</style>\n' +
286
+ '<meta charset="UTF-8">\n' +
287
+ '<title>' + title + '</title>\n' +
288
+ '<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sunny-html-editor/html-editor.css">\n' +
159
289
  '</head>\n' +
160
290
  '<body>\n' +
161
291
  '<div class="layout">\n' +
162
- ' <main class="main">\n' +
163
- ' <div class="container">\n' +
292
+ '<main class="main">\n' +
293
+ '<div class="container" id="editorContainer">\n' +
164
294
  innerHtml + '\n' +
165
- ' </div>\n' +
166
- ' </main>\n' +
167
295
  '</div>\n' +
296
+ '</main>\n' +
297
+ '</div>\n' +
298
+ '<script src="https://cdn.jsdelivr.net/npm/sunny-html-editor/html-editor.js"><\/script>\n' +
168
299
  '</body>\n' +
169
300
  '</html>';
170
301
 
@@ -1371,6 +1502,9 @@ document.addEventListener('DOMContentLoaded', function() {
1371
1502
  exportPdfBtn.textContent = '...';
1372
1503
 
1373
1504
  try {
1505
+ // ライブラリ遅延読み込み
1506
+ await ensurePdfLibraries();
1507
+
1374
1508
  // jsPDFインスタンス作成
1375
1509
  var { jsPDF } = window.jspdf;
1376
1510
  var pdf = new jsPDF({
@@ -1573,6 +1707,9 @@ document.addEventListener('DOMContentLoaded', function() {
1573
1707
  });
1574
1708
 
1575
1709
  async function convertToExcel() {
1710
+ // ライブラリ遅延読み込み
1711
+ await ensureExcelLibrary();
1712
+
1576
1713
  var content = container.querySelector('.content');
1577
1714
  if (!content) content = container;
1578
1715
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sunny-html-editor",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "軽量WYSIWYGエディタ - 既存のHTMLに編集機能を後付けで追加",
5
5
  "main": "html-editor.js",
6
6
  "files": [