├── .gitignore
├── LICENSE
├── README.ja.md
├── README.md
├── TODO
├── babel.config.js
├── dist
├── css
│ └── style.css
├── favicon.ico
├── index.html
└── js
│ ├── block-editor-vue.js
│ ├── development.js
│ └── production.js
├── package-lock.json
├── package.json
├── public
├── css
│ └── style.css
├── favicon.ico
├── index.html
└── js
│ ├── development.js
│ └── production.js
├── screenshot.png
├── src
├── App.vue
├── components
│ ├── Column.vue
│ ├── Heading.vue
│ ├── Html.vue
│ ├── List.vue
│ ├── Paragraph.vue
│ ├── Table.vue
│ ├── icons
│ │ ├── IconAlignCenter.vue
│ │ ├── IconAlignLeft.vue
│ │ ├── IconAlignRight.vue
│ │ ├── IconArrowDown.vue
│ │ ├── IconArrowUp.vue
│ │ ├── IconBold.vue
│ │ ├── IconCheck.vue
│ │ ├── IconClipboard.vue
│ │ ├── IconClose.vue
│ │ ├── IconColumn.vue
│ │ ├── IconCssClass.vue
│ │ ├── IconDelete.vue
│ │ ├── IconEllipsisH.vue
│ │ ├── IconEllipsisV.vue
│ │ ├── IconEraser.vue
│ │ ├── IconGrid.vue
│ │ ├── IconHeading.vue
│ │ ├── IconHtml.vue
│ │ ├── IconImage.vue
│ │ ├── IconInputClear.vue
│ │ ├── IconLink.vue
│ │ ├── IconList.vue
│ │ ├── IconMovedown.vue
│ │ ├── IconMoveup.vue
│ │ ├── IconParagraph.vue
│ │ ├── IconPlus.vue
│ │ ├── IconPlusBig.vue
│ │ ├── IconRedo.vue
│ │ ├── IconReplicate.vue
│ │ ├── IconTable.vue
│ │ ├── IconTableMenu.vue
│ │ ├── IconUndo.vue
│ │ └── IconUnlink.vue
│ └── sub
│ │ ├── ActionMenu.vue
│ │ ├── AddMenu.vue
│ │ ├── BrokenImage.vue
│ │ ├── Checkbox.vue
│ │ ├── ClassInput.vue
│ │ ├── ColumnEdit.vue
│ │ ├── ComponentBase.vue
│ │ ├── HeadingLevelEdit.vue
│ │ ├── IconBase.vue
│ │ ├── ImageEdit.vue
│ │ ├── ImageList.vue
│ │ ├── ItemHeader.vue
│ │ ├── ItemMixin.vue
│ │ ├── ListTypeEdit.vue
│ │ ├── PlainText.vue
│ │ ├── RangeInput.vue
│ │ ├── Separator.vue
│ │ ├── SwitchInput.vue
│ │ ├── TableCell.vue
│ │ ├── TableCellMenu.vue
│ │ ├── TableColMenu.vue
│ │ ├── TableColResizeMixin.vue
│ │ ├── TableManagerMixin.vue
│ │ ├── TableRowMenu.vue
│ │ ├── TableSizeEdit.vue
│ │ ├── TableSortMixin.vue
│ │ ├── TextInput.vue
│ │ └── VisualText.vue
├── i18n
│ ├── en.json
│ ├── ja.json
│ └── load.js
├── main.js
├── scripts
│ ├── DOM.js
│ ├── DragItemUtil.js
│ ├── ItemBase.js
│ ├── LoadComponents.js
│ └── Util.js
└── styles
│ ├── animation.scss
│ ├── components.scss
│ ├── reset.scss
│ ├── v-tooltip.scss
│ └── valiables.scss
└── vue.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 |
4 | # local env files
5 | .env.local
6 | .env.*.local
7 |
8 | # Log files
9 | npm-debug.log*
10 | yarn-debug.log*
11 | yarn-error.log*
12 |
13 | # Editor directories and files
14 | .idea
15 | .vscode
16 | *.suo
17 | *.ntvs*
18 | *.njsproj
19 | *.sln
20 | *.sw*
21 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright 2019 takitakit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/TODO:
--------------------------------------------------------------------------------
1 | * 足らないアイコン
2 | * CKFinder、Froalaなどのアップローダとの連携
3 | * テーブル列の移動の際のボーダーがずれる
4 | * preset setをitemsで設定したときに、preset setとして画面に表示されない
5 | * 非minifyファイルの生成
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/dist/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | padding: 0;
3 | margin: 0;
4 | font-size: 16px;
5 | position: relative;
6 | }
7 | .content {
8 | width: 100%;
9 | position: relative;
10 | }
11 | div[id^="jsFrame"] {
12 | z-index: 10000;
13 | }
14 | #preview {
15 | width: 100%;
16 | height: 100%;
17 | box-sizing: border-box;
18 | overflow: auto;
19 | padding: 10px;
20 | font-size: 1rem;
21 | color:#555;
22 |
23 | }
24 | @media screen and (max-width:465px) {
25 | /* hide preview on small display */
26 | div[id^="jsFrame"] {
27 | display: none;
28 | }
29 | }
30 |
31 | /* プレビュー用スタイル */
32 | .preview-body .ve > * {
33 | box-sizing: border-box;
34 | width: 100%;
35 | box-sizing: border-box;
36 | }
37 |
38 | /* Heading */
39 | .preview-body h1 {
40 | font-size: 2.0rem;
41 | font-weight: bold;
42 | margin: .5rem 0px;
43 | }
44 | .preview-body h2 {
45 | font-size: 1.8rem;
46 | font-weight: bold;
47 | margin: .5rem 0px;
48 | }
49 | .preview-body h3 {
50 | font-size: 1.5rem;
51 | font-weight: bold;
52 | margin: .5rem 0px;
53 | }
54 | .preview-body h4 {
55 | font-size: 1.4rem;
56 | font-weight: bold;
57 | margin: .5rem 0px;
58 | }
59 | .preview-body h5 {
60 | font-size: 1.2rem;
61 | font-weight: bold;
62 | margin: .5rem 0px;
63 | }
64 | .preview-body h6 {
65 | font-size: 1.1rem;
66 | font-weight: bold;
67 | margin: .5rem 0px;
68 | }
69 |
70 | /* Paragraph */
71 | .preview-body .paragraph-wrap {
72 | margin: 1rem 0;
73 | }
74 | .preview-body .paragraph-wrap::after {
75 | content: "";
76 | display: block;
77 | clear: both;
78 | }
79 | .preview-body .paragraph-wrap .images {
80 | box-sizing: border-box;
81 | position: relative;
82 | }
83 | .preview-body .paragraph-wrap .images .image {
84 | position: relative;
85 | font-size: 0;
86 | margin: .5rem 0 0 0;
87 | }
88 | .preview-body .paragraph-wrap .images .image .caption {
89 | position: absolute;
90 | bottom: 0;
91 | left: 0;
92 | font-size: .8rem;
93 | width: 100%;
94 | background-color: rgba(0,0,0,.3);
95 | color: #fff;
96 | box-sizing: border-box;
97 | padding: 3px 5px;
98 | }
99 | .preview-body .paragraph-wrap .images .image {
100 | width: 100%;
101 | }
102 | .preview-body .paragraph-wrap .images .image img {
103 | width: 100%;
104 | }
105 | .preview-body .paragraph-wrap .images .image::last-child {
106 | margin: 0;
107 | }
108 |
109 | .preview-body .paragraph-wrap.align-left .images {
110 | margin: 0 .5rem 0 0;
111 | float: left;
112 | width: 40%;
113 | }
114 |
115 | .preview-body .paragraph-wrap.align-right .images {
116 | margin: 0 0 0 .5rem;
117 | float: right;
118 | width: 40%;
119 | }
120 | .preview-body .paragraph-wrap.align-center .images {
121 | margin: 0 auto;
122 | width: 50%;
123 | max-width: 80%;
124 | }
125 |
126 | /* List */
127 | .preview-body ol {
128 | padding: 0 0 0 1rem;
129 | }
130 | .preview-body ul {
131 | padding: 0 0 0 1rem;
132 | list-style-type: disc;
133 | }
134 |
135 | /* Table */
136 | .preview-body table {
137 | border-collapse: collapse;
138 | border: 1px solid #aaa;
139 | table-layout: fixed;
140 | }
141 | .preview-body table th,
142 | .preview-body table td {
143 | border: 1px solid #aaa;
144 | padding: 3px;
145 | }
146 | .preview-body table th {
147 | background-color: #ddd;
148 | color: #444;
149 | }
150 |
151 | /* Column */
152 | .preview-body .column-wrap {
153 | margin: 1rem 0;
154 | display: flex;
155 | flex-wrap: nowrap;
156 | border: 1px solid #ccc;
157 | }
158 | .preview-body .column-wrap .column-item {
159 | padding: .5rem;
160 | flex: 1;
161 | }
162 | .preview-body .column-wrap .paragraph-wrap.align-left .images,
163 | .preview-body .column-wrap .paragraph-wrap.align-right .images {
164 | width: 60%;
165 | }
166 |
167 | /* Visual Editor */
168 | .preview-body .ve-bold,
169 | .BEV-block-editor .ve-bold {
170 | font-weight: bold;
171 | }
172 | .preview-body .ve-highlight,
173 | .BEV-block-editor .ve-highlight {
174 | background-color: yellow;
175 | }
176 | .preview-body .ve-underline,
177 | .BEV-block-editor .ve-underline {
178 | text-decoration: underline;
179 | }
180 | .preview-body .ve-red,
181 | .BEV-block-editor .ve-red {
182 | color: red;
183 | }
184 | .preview-body .ve-blue,
185 | .BEV-block-editor .ve-blue {
186 | color: blue;
187 | }
188 | .preview-body .ve-green,
189 | .BEV-block-editor .ve-green {
190 | color: green;
191 | }
--------------------------------------------------------------------------------
/dist/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/takitakit/block-editor-vue/73b10634ec9dab2bab2cc9714515f8e84a014034/dist/favicon.ico
--------------------------------------------------------------------------------
/dist/index.html:
--------------------------------------------------------------------------------
1 |
block-editor-vue
--------------------------------------------------------------------------------
/dist/js/development.js:
--------------------------------------------------------------------------------
1 | let options
2 | const merge_cell = { content: 'merged cell', header: true, rowspan: 3, colspan: 3 };
3 | const merge_cell2 = { content: 'merged cell2', header: true, rowspan: 1, colspan: 2 };
4 | const items = [
5 | {
6 | name: 'Heading',
7 | className: 'title',
8 | level: 'h2',
9 | content: 'hoge'
10 | },
11 | {
12 | name: 'Table',
13 | rows: [
14 | {
15 | cells: [
16 | { content: 'C1-1', header: true },
17 | { content: 'C1-2', header: true },
18 | { content: 'C1-3', header: true },
19 | merge_cell2,
20 | { dummy: true, content: '', ref: merge_cell2 }
21 | ]
22 | },
23 | {
24 | cells: [
25 | { content: 'C2-1', header: false },
26 | merge_cell,
27 | { content: '', dummy: true, ref: merge_cell },
28 | { content: '', dummy: true, ref: merge_cell },
29 | { content: 'C2-5' }
30 | ]
31 | },
32 | {
33 | cells: [
34 | { content: 'C3-1', header: false },
35 | { content: '', dummy: true, ref: merge_cell },
36 | { content: '', dummy: true, ref: merge_cell },
37 | { content: '', dummy: true, ref: merge_cell },
38 | { content: 'C3-5' }
39 | ]
40 | },
41 | {
42 | cells: [
43 | { content: 'C4-1', header: false },
44 | { content: '', dummy: true, ref: merge_cell },
45 | { content: '', dummy: true, ref: merge_cell },
46 | { content: '', dummy: true, ref: merge_cell },
47 | { content: 'C4-5' }
48 | ]
49 | },
50 | {
51 | cells: [
52 | { content: 'C5-1', header: false },
53 | { content: 'C5-2', header: false },
54 | { content: 'C5-3', header: false },
55 | { content: 'C5-4', header: false },
56 | { content: 'C5-5', header: false }
57 | ]
58 | }
59 | ],
60 | colgroup: [
61 | { width: null },
62 | { width: null },
63 | { width: null },
64 | { width: null },
65 | { width: null },
66 | ]
67 | },
68 | {
69 | name: 'List',
70 | // type: 'unordered',
71 | className: 'test',
72 | rows: [
73 | { content: 'リスト1
太字テスト' },
74 | { content: 'リスト2' },
75 | { content: 'リスト3' },
76 | { content: 'リスト4' }
77 | ]
78 | },
79 | {
80 | name: 'Paragraph',
81 | imageAlign: 'left',
82 | className: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest',
83 | images: [
84 | { src: 'https://placehold.jp/3d4070/ffffff/150x150.png', caption: 'caption1' },
85 | { src: 'https://placehold.jp/c122cc/ffffff/300x200.png', caption: 'caption2' },
86 | { src: 'https://placehold.jp/22cca4/ffffff/200x300.png', caption: 'caption3' }
87 | ],
88 | content: 'externalリンク文章が入ります。internalリンク太字文章が入ります。
文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。'
89 | },
90 | {
91 | name: 'Paragraph',
92 | imageAlign: 'left',
93 | className: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest',
94 | images: [
95 | { src: 'https://placeholds.jp/3d4070/ffffff/150x150.png', caption: 'caption1' },
96 | { src: 'https://placehold.jp/c122cc/ffffff/300x200.png', caption: 'caption2' },
97 | { src: 'https://placehold.jp/22cca4/ffffff/200x300.png', caption: 'caption3' }
98 | ],
99 | content: '文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。'
100 | },
101 | {
102 | name: 'Html',
103 | content: 'hoge
'
104 | },
105 | {
106 | name: 'Column',
107 | className: 'aaaa',
108 | columns: [
109 | {
110 | items: [
111 | {
112 | name: 'Paragraph',
113 | imageAlign: 'left',
114 | className: 'testtesttesttesttesttesttesttest',
115 | images: [],
116 | content: ''
117 | },
118 | {
119 | name: 'Paragraph',
120 | imageAlign: 'right',
121 | className: null,
122 | images: [],
123 | content: ''
124 | }
125 | ]
126 | },
127 | {
128 | items: [
129 | { name: 'Html' }
130 | ]
131 | },
132 | {
133 | items: [
134 | {
135 | name: 'Paragraph',
136 | imageAlign: 'left',
137 | className: null,
138 | images: [],
139 | content: ''
140 | }
141 | ]
142 | }
143 | ]
144 | },
145 | {
146 | name: 'Column',
147 | columns: [
148 | { items: [] }, { items: [] }
149 | ]
150 | },
151 | {
152 | name: 'Paragraph',
153 | imageAlign: 'left',
154 | className: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest',
155 | images: [],
156 | content: ''
157 | }
158 | ];
159 |
160 | options = {
161 | locale: 'ja',
162 | items: items,
163 | // enabledItemNames: ['Html', 'Table'],
164 | // enabledItemNamesInColumn: ['Html'],
165 | loadItemsFromInputTag: true,
166 | allowHistories: true,
167 | allowStyledText: true,
168 | allowCssClass: true,
169 | allowFileBrowser: true,
170 | // itemOrder: [ // メニュー上のアイテムの表示順序
171 | // 'Heading', 'Image', 'List', 'Table', 'Column', 'Html', 'Paragraph'
172 | // ],
173 | FileBrowser: {
174 | url: 'http://localhost:8888/elfinder/',
175 | width: '80%',
176 | height: '80%',
177 | resizable: true,
178 | },
179 | // cssClasses: [
180 | // 'class1', 'class2',
181 | // {class3: 'クラス3'}, {'test-class': 'テストクラス'}
182 | // ],
183 | styledTextClasses: [
184 | 'bold', 'link',
185 | { red: '赤字' }, { blue: '青字' }, { green: '緑字' },
186 | { underline: '下線' }, { highlight: 'ハイライト' }
187 | ],
188 | Paragraph: {
189 | allowImages: true,
190 | defaultImageAlign: 'right',
191 | dispName: 'テキスト',
192 | presets: [
193 | { className: 'important', dispName: '重要' },
194 | { className: 'normal', dispName: '通常' },
195 | { imageAlign: 'center', dispName: '中央揃え画像' }
196 | ],
197 | },
198 | List: {
199 | maxRows: 10,
200 | allowStyledText: true,
201 | presets: [
202 | { className: 'link', type: 'unordered', dispName: 'リンクリスト' },
203 | { type: 'unordered', dispName: '通常リスト' },
204 | { type: 'ordered', dispName: '箇条書きリスト' }
205 | ]
206 | },
207 | Table: {
208 | maxRow: 10, // テーブルの最大行数
209 | maxCol: 10, // テーブルの最大列数
210 | minRow: 1, // テーブルの最小行数
211 | minCol: 2, // テーブルの最小列数
212 | presets: [
213 | { className: 'product', dispName: '商品テーブル' },
214 | { dispName: '通常テーブル' }
215 | ]
216 | },
217 | Heading: {
218 | levels: ['h2', 'h3', 'h4', 'h5', 'h6'],
219 | // levels: ['h3'],
220 | levelNames: { h2: '超大見出し', h3: '大見出し', h4: '中見出し', h5: '小見出し', h6: '超小見出し' },
221 | defaultLevel: 'h3',
222 | presets: [
223 | { className: 'title', level: 'h2', dispName: 'タイトル' },
224 | { className: 'sub-title', level: 'h3', dispName: 'サブタイトル' }
225 | ]
226 | },
227 | Column: {
228 | allowChangeNumColumn: true, // カラム数の変更許可
229 | defaultNumColumn: 2, // デフォルトのカラム数
230 | maxColumn: 5, // 段組の最大数
231 | minColumn: 2, // 段組の最小数
232 | presets: [
233 | { className: 'special', dispName: '特集カラム' },
234 | { className: 'normal', dispName: '通常カラム' }
235 | ]
236 | },
237 | onLoad: function (html) {
238 | updatePreview(html);
239 | },
240 | onUpdate: function (html) {
241 | updatePreview(html);
242 | }
243 | };
244 | const instance = new BlockEditor('#app1', options);
245 |
246 | // Update preview
247 | var previewBody;
248 | function updatePreview(html) {
249 | if (previewBody) {
250 | previewBody.innerHTML = html;
251 | } else {
252 | launchPreview(html);
253 | previewBody = document.querySelector('#preview');
254 | }
255 | }
256 |
257 | // Preview window
258 | function launchPreview (html) {
259 | var jsFrame = new JSFrame({
260 | horizontalAlign: 'left',
261 | verticalAlign: 'top',
262 | });
263 | var frame = jsFrame.create({
264 | title: 'Preview',
265 | left: 200, top: 200, width: 600, height: 400, minWidth: 300, minHeight: 200,
266 | appearanceName: 'material',
267 | appearanceParam: {
268 | border: {
269 | shadow: '0px 0px 6px -3px rgba(0,0,0,0.42)',
270 | width: 1,
271 | color: 'rgba(0,0,0,.15)',
272 | radius: 4,
273 | },
274 | titleBar: {
275 | background: 'rgba(97, 104, 111, 1.0)'
276 | }
277 | },
278 | style: {
279 | backgroundColor: 'rgba(255,255,255,0.88)',
280 | overflow: 'auto'
281 | },
282 | html: '' + html + '
'
283 | });
284 | frame.show();
285 |
286 | frame.setControl({
287 | maximizeButton: 'maximizeButton',
288 | demaximizeButton: 'restoreButton',
289 | minimizeButton: 'minimizeButton',
290 | deminimizeButton: 'deminimizeButton',
291 | animation: true,
292 | animationDuration: 200,
293 | });
294 | }
295 |
--------------------------------------------------------------------------------
/dist/js/production.js:
--------------------------------------------------------------------------------
1 | var options
2 | var tbl_head_row = {
3 | header: true,
4 | content: 'Table content',
5 | colspan: 3
6 | }
7 | options = {
8 | items: [
9 | {
10 | name: 'Heading',
11 | level: 'h1',
12 | content: 'Extra-big heading'
13 | },
14 | {
15 | name: 'Heading',
16 | level: 'h2',
17 | content: 'Big heading'
18 | },
19 | {
20 | name: 'Heading',
21 | level: 'h3',
22 | content: 'Medium heading'
23 | },
24 | {
25 | name: 'Heading',
26 | level: 'h4',
27 | content: 'Small heading'
28 | },
29 | {
30 | name: 'Heading',
31 | level: 'h5',
32 | content: 'Extra-small heading'
33 | },
34 | {
35 | name: 'Heading',
36 | level: 'h6',
37 | content: 'Minimum heading'
38 | },
39 | {
40 | name: 'Paragraph',
41 | content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a',
42 | },
43 | {
44 | name: 'Paragraph',
45 | imageAlign: 'left',
46 | images: [
47 | { src: 'https://placehold.jp/3d4070/ffffff/600x400.png?text=Dummy', 'caption': 'Dummy caption' }
48 | ],
49 | content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a',
50 | },
51 | {
52 | name: 'List',
53 | type: 'unordered',
54 | rows: [
55 | { content: 'This is the text of the list' },
56 | { content: 'This is the text of the list' },
57 | { content: 'This is the text of the list' }
58 | ]
59 | },
60 | {
61 | name: 'List',
62 | type: 'ordered',
63 | rows: [
64 | { content: 'This is the text of the list' },
65 | { content: 'This is the text of the list' },
66 | { content: 'This is the text of the list' }
67 | ]
68 | },
69 | {
70 | name: 'Table',
71 | rows: [
72 | {
73 | cells: [
74 | tbl_head_row,
75 | { dummy: true, ref: tbl_head_row },
76 | { dummy: true, ref: tbl_head_row }
77 | ]
78 | },
79 | {
80 | cells: [
81 | {
82 | header: true,
83 | content: 'header cell'
84 | },
85 | {
86 | content: 'cell'
87 | },
88 | {
89 | content: 'cell'
90 | }
91 | ]
92 | },
93 | {
94 | cells: [
95 | {
96 | header: true,
97 | content: 'header cell'
98 | },
99 | {
100 | content: 'cell'
101 | },
102 | {
103 | content: 'cell'
104 | }
105 | ]
106 | }
107 | ]
108 | },
109 | {
110 | name: 'Column',
111 | columns: [
112 | {
113 | items: [
114 | {
115 | name: 'Heading',
116 | level: 'h3',
117 | content: 'Heading in a column'
118 | },
119 | {
120 | name: 'Paragraph',
121 | images: [
122 | { src: 'https://placehold.jp/3d4070/ffffff/400x300.png?text=Dummy', caption: 'Image caption' }
123 | ],
124 | content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo'
125 | }
126 | ]
127 | },
128 | {
129 | items: [
130 | {
131 | name: 'List',
132 | type: 'unoradered',
133 | rows: [
134 | { content: 'This is the text of the list in a column' },
135 | { content: 'This is the text of the list in a column' },
136 | { content: 'This is the text of the list in a column' }
137 | ]
138 | }
139 | ]
140 | }
141 | ]
142 | },
143 | {
144 | name: 'Html',
145 | content: 'Here is a raw html
'
146 | }
147 | ],
148 | loadItemsFromInputTag: false,
149 | allowStyledText: true,
150 | styledTextClasses: ['bold','link','red','green','blue'],
151 | onLoad: function (html) {
152 | updatePreview(html);
153 | },
154 | onUpdate: function (html) {
155 | updatePreview(html);
156 | }
157 | };
158 | new BlockEditor('#app1', options);
159 |
160 | // Update preview
161 | var previewBody;
162 | function updatePreview(html) {
163 | if (previewBody) {
164 | previewBody.innerHTML = html;
165 | } else {
166 | launchPreview(html);
167 | previewBody = document.querySelector('#preview');
168 | }
169 | }
170 |
171 | // Preview window
172 | function launchPreview(html) {
173 | var jsFrame = new JSFrame({
174 | horizontalAlign: 'left',
175 | verticalAlign: 'top',
176 | });
177 | var frame = jsFrame.create({
178 | title: 'Preview',
179 | left: 200, top: 200, width: 600, height: 400, minWidth: 300, minHeight: 200,
180 | appearanceName: 'material',
181 | appearanceParam: {
182 | border: {
183 | shadow: '0px 0px 6px -3px rgba(0,0,0,0.42)',
184 | width: 1,
185 | color: 'rgba(0,0,0,.15)',
186 | radius: 4,
187 | },
188 | titleBar: {
189 | background: 'rgba(97, 104, 111, 1.0)'
190 | }
191 | },
192 | style: {
193 | backgroundColor: 'rgba(255,255,255,0.88)',
194 | overflow: 'auto'
195 | },
196 | html: '' + html + '
'
197 | });
198 | frame.show();
199 |
200 | frame.setControl({
201 | maximizeButton: 'maximizeButton',
202 | demaximizeButton: 'restoreButton',
203 | minimizeButton: 'minimizeButton',
204 | deminimizeButton: 'deminimizeButton',
205 | animation: true,
206 | animationDuration: 200,
207 | });
208 | }
209 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "block-editor-vue",
3 | "version": "1.0.3",
4 | "private": false,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "build": "vue-cli-service build",
8 | "lint": "vue-cli-service lint"
9 | },
10 | "dependencies": {
11 | "lodash": "^4.17.11",
12 | "quill": "^1.3.6",
13 | "terser": "^3.17.0",
14 | "v-tooltip": "^2.0.2",
15 | "vue": "^2.6.10",
16 | "vue-i18n": "^8.11.2",
17 | "vuex": "^3.1.0"
18 | },
19 | "devDependencies": {
20 | "@vue/cli-plugin-babel": "^3.7.0",
21 | "@vue/cli-plugin-eslint": "^3.7.0",
22 | "@vue/cli-service": "^3.7.0",
23 | "babel-eslint": "^10.0.1",
24 | "eslint": "^5.16.0",
25 | "eslint-plugin-vue": "^5.2.2",
26 | "node-sass": "^4.12.0",
27 | "sass-loader": "^7.1.0",
28 | "vue-template-compiler": "^2.6.10"
29 | },
30 | "eslintConfig": {
31 | "root": true,
32 | "env": {
33 | "node": true
34 | },
35 | "extends": [
36 | "plugin:vue/essential",
37 | "eslint:recommended"
38 | ],
39 | "rules": {
40 | "no-unused-vars": 0,
41 | "no-constant-condition": 0
42 | },
43 | "parserOptions": {
44 | "parser": "babel-eslint"
45 | }
46 | },
47 | "postcss": {
48 | "plugins": {
49 | "autoprefixer": {}
50 | }
51 | },
52 | "browserslist": [
53 | "> 1%",
54 | "last 2 versions",
55 | "not ie <= 8"
56 | ]
57 | }
58 |
--------------------------------------------------------------------------------
/public/css/style.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | padding: 0;
3 | margin: 0;
4 | font-size: 16px;
5 | position: relative;
6 | }
7 | .content {
8 | width: 100%;
9 | position: relative;
10 | }
11 | div[id^="jsFrame"] {
12 | z-index: 10000;
13 | }
14 | #preview {
15 | width: 100%;
16 | height: 100%;
17 | box-sizing: border-box;
18 | overflow: auto;
19 | padding: 10px;
20 | font-size: 1rem;
21 | color:#555;
22 |
23 | }
24 | @media screen and (max-width:465px) {
25 | /* hide preview on small display */
26 | div[id^="jsFrame"] {
27 | display: none;
28 | }
29 | }
30 |
31 | /* プレビュー用スタイル */
32 | .preview-body .ve > * {
33 | box-sizing: border-box;
34 | width: 100%;
35 | box-sizing: border-box;
36 | }
37 |
38 | /* Heading */
39 | .preview-body h1 {
40 | font-size: 2.0rem;
41 | font-weight: bold;
42 | margin: .5rem 0px;
43 | }
44 | .preview-body h2 {
45 | font-size: 1.8rem;
46 | font-weight: bold;
47 | margin: .5rem 0px;
48 | }
49 | .preview-body h3 {
50 | font-size: 1.5rem;
51 | font-weight: bold;
52 | margin: .5rem 0px;
53 | }
54 | .preview-body h4 {
55 | font-size: 1.4rem;
56 | font-weight: bold;
57 | margin: .5rem 0px;
58 | }
59 | .preview-body h5 {
60 | font-size: 1.2rem;
61 | font-weight: bold;
62 | margin: .5rem 0px;
63 | }
64 | .preview-body h6 {
65 | font-size: 1.1rem;
66 | font-weight: bold;
67 | margin: .5rem 0px;
68 | }
69 |
70 | /* Paragraph */
71 | .preview-body .paragraph-wrap {
72 | margin: 1rem 0;
73 | }
74 | .preview-body .paragraph-wrap::after {
75 | content: "";
76 | display: block;
77 | clear: both;
78 | }
79 | .preview-body .paragraph-wrap .images {
80 | box-sizing: border-box;
81 | position: relative;
82 | }
83 | .preview-body .paragraph-wrap .images .image {
84 | position: relative;
85 | font-size: 0;
86 | margin: .5rem 0 0 0;
87 | }
88 | .preview-body .paragraph-wrap .images .image .caption {
89 | position: absolute;
90 | bottom: 0;
91 | left: 0;
92 | font-size: .8rem;
93 | width: 100%;
94 | background-color: rgba(0,0,0,.3);
95 | color: #fff;
96 | box-sizing: border-box;
97 | padding: 3px 5px;
98 | }
99 | .preview-body .paragraph-wrap .images .image {
100 | width: 100%;
101 | }
102 | .preview-body .paragraph-wrap .images .image img {
103 | width: 100%;
104 | }
105 | .preview-body .paragraph-wrap .images .image::last-child {
106 | margin: 0;
107 | }
108 |
109 | .preview-body .paragraph-wrap.align-left .images {
110 | margin: 0 .5rem 0 0;
111 | float: left;
112 | width: 40%;
113 | }
114 |
115 | .preview-body .paragraph-wrap.align-right .images {
116 | margin: 0 0 0 .5rem;
117 | float: right;
118 | width: 40%;
119 | }
120 | .preview-body .paragraph-wrap.align-center .images {
121 | margin: 0 auto;
122 | width: 50%;
123 | max-width: 80%;
124 | }
125 |
126 | /* List */
127 | .preview-body ol {
128 | padding: 0 0 0 1rem;
129 | }
130 | .preview-body ul {
131 | padding: 0 0 0 1rem;
132 | list-style-type: disc;
133 | }
134 |
135 | /* Table */
136 | .preview-body table {
137 | border-collapse: collapse;
138 | border: 1px solid #aaa;
139 | table-layout: fixed;
140 | }
141 | .preview-body table th,
142 | .preview-body table td {
143 | border: 1px solid #aaa;
144 | padding: 3px;
145 | }
146 | .preview-body table th {
147 | background-color: #ddd;
148 | color: #444;
149 | }
150 |
151 | /* Column */
152 | .preview-body .column-wrap {
153 | margin: 1rem 0;
154 | display: flex;
155 | flex-wrap: nowrap;
156 | border: 1px solid #ccc;
157 | }
158 | .preview-body .column-wrap .column-item {
159 | padding: .5rem;
160 | flex: 1;
161 | }
162 | .preview-body .column-wrap .paragraph-wrap.align-left .images,
163 | .preview-body .column-wrap .paragraph-wrap.align-right .images {
164 | width: 60%;
165 | }
166 |
167 | /* Visual Editor */
168 | .preview-body .ve-bold,
169 | .BEV-block-editor .ve-bold {
170 | font-weight: bold;
171 | }
172 | .preview-body .ve-highlight,
173 | .BEV-block-editor .ve-highlight {
174 | background-color: yellow;
175 | }
176 | .preview-body .ve-underline,
177 | .BEV-block-editor .ve-underline {
178 | text-decoration: underline;
179 | }
180 | .preview-body .ve-red,
181 | .BEV-block-editor .ve-red {
182 | color: red;
183 | }
184 | .preview-body .ve-blue,
185 | .BEV-block-editor .ve-blue {
186 | color: blue;
187 | }
188 | .preview-body .ve-green,
189 | .BEV-block-editor .ve-green {
190 | color: green;
191 | }
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/takitakit/block-editor-vue/73b10634ec9dab2bab2cc9714515f8e84a014034/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
10 |
11 | block-editor-vue
12 |
13 |
14 |
15 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/public/js/development.js:
--------------------------------------------------------------------------------
1 | let options
2 | const merge_cell = { content: 'merged cell', header: true, rowspan: 3, colspan: 3 };
3 | const merge_cell2 = { content: 'merged cell2', header: true, rowspan: 1, colspan: 2 };
4 | const items = [
5 | {
6 | name: 'Heading',
7 | className: 'title',
8 | level: 'h2',
9 | content: 'hoge'
10 | },
11 | {
12 | name: 'Table',
13 | rows: [
14 | {
15 | cells: [
16 | { content: 'C1-1', header: true },
17 | { content: 'C1-2', header: true },
18 | { content: 'C1-3', header: true },
19 | merge_cell2,
20 | { dummy: true, content: '', ref: merge_cell2 }
21 | ]
22 | },
23 | {
24 | cells: [
25 | { content: 'C2-1', header: false },
26 | merge_cell,
27 | { content: '', dummy: true, ref: merge_cell },
28 | { content: '', dummy: true, ref: merge_cell },
29 | { content: 'C2-5' }
30 | ]
31 | },
32 | {
33 | cells: [
34 | { content: 'C3-1', header: false },
35 | { content: '', dummy: true, ref: merge_cell },
36 | { content: '', dummy: true, ref: merge_cell },
37 | { content: '', dummy: true, ref: merge_cell },
38 | { content: 'C3-5' }
39 | ]
40 | },
41 | {
42 | cells: [
43 | { content: 'C4-1', header: false },
44 | { content: '', dummy: true, ref: merge_cell },
45 | { content: '', dummy: true, ref: merge_cell },
46 | { content: '', dummy: true, ref: merge_cell },
47 | { content: 'C4-5' }
48 | ]
49 | },
50 | {
51 | cells: [
52 | { content: 'C5-1', header: false },
53 | { content: 'C5-2', header: false },
54 | { content: 'C5-3', header: false },
55 | { content: 'C5-4', header: false },
56 | { content: 'C5-5', header: false }
57 | ]
58 | }
59 | ],
60 | colgroup: [
61 | { width: null },
62 | { width: null },
63 | { width: null },
64 | { width: null },
65 | { width: null },
66 | ]
67 | },
68 | {
69 | name: 'List',
70 | // type: 'unordered',
71 | className: 'test',
72 | rows: [
73 | { content: 'リスト1
太字テスト' },
74 | { content: 'リスト2' },
75 | { content: 'リスト3' },
76 | { content: 'リスト4' }
77 | ]
78 | },
79 | {
80 | name: 'Paragraph',
81 | imageAlign: 'left',
82 | className: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest',
83 | images: [
84 | { src: 'https://placehold.jp/3d4070/ffffff/150x150.png', caption: 'caption1' },
85 | { src: 'https://placehold.jp/c122cc/ffffff/300x200.png', caption: 'caption2' },
86 | { src: 'https://placehold.jp/22cca4/ffffff/200x300.png', caption: 'caption3' }
87 | ],
88 | content: 'externalリンク文章が入ります。internalリンク太字文章が入ります。
文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。'
89 | },
90 | {
91 | name: 'Paragraph',
92 | imageAlign: 'left',
93 | className: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest',
94 | images: [
95 | { src: 'https://placeholds.jp/3d4070/ffffff/150x150.png', caption: 'caption1' },
96 | { src: 'https://placehold.jp/c122cc/ffffff/300x200.png', caption: 'caption2' },
97 | { src: 'https://placehold.jp/22cca4/ffffff/200x300.png', caption: 'caption3' }
98 | ],
99 | content: '文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。文章が入ります。'
100 | },
101 | {
102 | name: 'Html',
103 | content: 'hoge
'
104 | },
105 | {
106 | name: 'Column',
107 | className: 'aaaa',
108 | columns: [
109 | {
110 | items: [
111 | {
112 | name: 'Paragraph',
113 | imageAlign: 'left',
114 | className: 'testtesttesttesttesttesttesttest',
115 | images: [],
116 | content: ''
117 | },
118 | {
119 | name: 'Paragraph',
120 | imageAlign: 'right',
121 | className: null,
122 | images: [],
123 | content: ''
124 | }
125 | ]
126 | },
127 | {
128 | items: [
129 | { name: 'Html' }
130 | ]
131 | },
132 | {
133 | items: [
134 | {
135 | name: 'Paragraph',
136 | imageAlign: 'left',
137 | className: null,
138 | images: [],
139 | content: ''
140 | }
141 | ]
142 | }
143 | ]
144 | },
145 | {
146 | name: 'Column',
147 | columns: [
148 | { items: [] }, { items: [] }
149 | ]
150 | },
151 | {
152 | name: 'Paragraph',
153 | imageAlign: 'left',
154 | className: 'testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttest',
155 | images: [],
156 | content: ''
157 | }
158 | ];
159 |
160 | options = {
161 | locale: 'ja',
162 | items: items,
163 | // enabledItemNames: ['Html', 'Table'],
164 | // enabledItemNamesInColumn: ['Html'],
165 | loadItemsFromInputTag: true,
166 | allowHistories: true,
167 | allowStyledText: true,
168 | allowCssClass: true,
169 | allowFileBrowser: true,
170 | // itemOrder: [ // メニュー上のアイテムの表示順序
171 | // 'Heading', 'Image', 'List', 'Table', 'Column', 'Html', 'Paragraph'
172 | // ],
173 | FileBrowser: {
174 | url: 'http://localhost:8888/elfinder/',
175 | width: '80%',
176 | height: '80%',
177 | resizable: true,
178 | },
179 | // cssClasses: [
180 | // 'class1', 'class2',
181 | // {class3: 'クラス3'}, {'test-class': 'テストクラス'}
182 | // ],
183 | styledTextClasses: [
184 | 'bold', 'link',
185 | { red: '赤字' }, { blue: '青字' }, { green: '緑字' },
186 | { underline: '下線' }, { highlight: 'ハイライト' }
187 | ],
188 | Paragraph: {
189 | allowImages: true,
190 | defaultImageAlign: 'right',
191 | dispName: 'テキスト',
192 | presets: [
193 | { className: 'important', dispName: '重要' },
194 | { className: 'normal', dispName: '通常' },
195 | { imageAlign: 'center', dispName: '中央揃え画像' }
196 | ],
197 | },
198 | List: {
199 | maxRows: 10,
200 | allowStyledText: true,
201 | presets: [
202 | { className: 'link', type: 'unordered', dispName: 'リンクリスト' },
203 | { type: 'unordered', dispName: '通常リスト' },
204 | { type: 'ordered', dispName: '箇条書きリスト' }
205 | ]
206 | },
207 | Table: {
208 | maxRow: 10, // テーブルの最大行数
209 | maxCol: 10, // テーブルの最大列数
210 | minRow: 1, // テーブルの最小行数
211 | minCol: 2, // テーブルの最小列数
212 | presets: [
213 | { className: 'product', dispName: '商品テーブル' },
214 | { dispName: '通常テーブル' }
215 | ]
216 | },
217 | Heading: {
218 | levels: ['h2', 'h3', 'h4', 'h5', 'h6'],
219 | // levels: ['h3'],
220 | levelNames: { h2: '超大見出し', h3: '大見出し', h4: '中見出し', h5: '小見出し', h6: '超小見出し' },
221 | defaultLevel: 'h3',
222 | presets: [
223 | { className: 'title', level: 'h2', dispName: 'タイトル' },
224 | { className: 'sub-title', level: 'h3', dispName: 'サブタイトル' }
225 | ]
226 | },
227 | Column: {
228 | allowChangeNumColumn: true, // カラム数の変更許可
229 | defaultNumColumn: 2, // デフォルトのカラム数
230 | maxColumn: 5, // 段組の最大数
231 | minColumn: 2, // 段組の最小数
232 | presets: [
233 | { className: 'special', dispName: '特集カラム' },
234 | { className: 'normal', dispName: '通常カラム' }
235 | ]
236 | },
237 | onLoad: function (html) {
238 | updatePreview(html);
239 | },
240 | onUpdate: function (html) {
241 | updatePreview(html);
242 | }
243 | };
244 | const instance = new BlockEditor('#app1', options);
245 |
246 | // Update preview
247 | var previewBody;
248 | function updatePreview(html) {
249 | if (previewBody) {
250 | previewBody.innerHTML = html;
251 | } else {
252 | launchPreview(html);
253 | previewBody = document.querySelector('#preview');
254 | }
255 | }
256 |
257 | // Preview window
258 | function launchPreview (html) {
259 | var jsFrame = new JSFrame({
260 | horizontalAlign: 'left',
261 | verticalAlign: 'top',
262 | });
263 | var frame = jsFrame.create({
264 | title: 'Preview',
265 | left: 200, top: 200, width: 600, height: 400, minWidth: 300, minHeight: 200,
266 | appearanceName: 'material',
267 | appearanceParam: {
268 | border: {
269 | shadow: '0px 0px 6px -3px rgba(0,0,0,0.42)',
270 | width: 1,
271 | color: 'rgba(0,0,0,.15)',
272 | radius: 4,
273 | },
274 | titleBar: {
275 | background: 'rgba(97, 104, 111, 1.0)'
276 | }
277 | },
278 | style: {
279 | backgroundColor: 'rgba(255,255,255,0.88)',
280 | overflow: 'auto'
281 | },
282 | html: '' + html + '
'
283 | });
284 | frame.show();
285 |
286 | frame.setControl({
287 | maximizeButton: 'maximizeButton',
288 | demaximizeButton: 'restoreButton',
289 | minimizeButton: 'minimizeButton',
290 | deminimizeButton: 'deminimizeButton',
291 | animation: true,
292 | animationDuration: 200,
293 | });
294 | }
295 |
--------------------------------------------------------------------------------
/public/js/production.js:
--------------------------------------------------------------------------------
1 | var options
2 | var tbl_head_row = {
3 | header: true,
4 | content: 'Table content',
5 | colspan: 3
6 | }
7 | options = {
8 | items: [
9 | {
10 | name: 'Heading',
11 | level: 'h1',
12 | content: 'Extra-big heading'
13 | },
14 | {
15 | name: 'Heading',
16 | level: 'h2',
17 | content: 'Big heading'
18 | },
19 | {
20 | name: 'Heading',
21 | level: 'h3',
22 | content: 'Medium heading'
23 | },
24 | {
25 | name: 'Heading',
26 | level: 'h4',
27 | content: 'Small heading'
28 | },
29 | {
30 | name: 'Heading',
31 | level: 'h5',
32 | content: 'Extra-small heading'
33 | },
34 | {
35 | name: 'Heading',
36 | level: 'h6',
37 | content: 'Minimum heading'
38 | },
39 | {
40 | name: 'Paragraph',
41 | content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a',
42 | },
43 | {
44 | name: 'Paragraph',
45 | imageAlign: 'left',
46 | images: [
47 | { src: 'https://placehold.jp/3d4070/ffffff/600x400.png?text=Dummy', 'caption': 'Dummy caption' }
48 | ],
49 | content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a',
50 | },
51 | {
52 | name: 'List',
53 | type: 'unordered',
54 | rows: [
55 | { content: 'This is the text of the list' },
56 | { content: 'This is the text of the list' },
57 | { content: 'This is the text of the list' }
58 | ]
59 | },
60 | {
61 | name: 'List',
62 | type: 'ordered',
63 | rows: [
64 | { content: 'This is the text of the list' },
65 | { content: 'This is the text of the list' },
66 | { content: 'This is the text of the list' }
67 | ]
68 | },
69 | {
70 | name: 'Table',
71 | rows: [
72 | {
73 | cells: [
74 | tbl_head_row,
75 | { dummy: true, ref: tbl_head_row },
76 | { dummy: true, ref: tbl_head_row }
77 | ]
78 | },
79 | {
80 | cells: [
81 | {
82 | header: true,
83 | content: 'header cell'
84 | },
85 | {
86 | content: 'cell'
87 | },
88 | {
89 | content: 'cell'
90 | }
91 | ]
92 | },
93 | {
94 | cells: [
95 | {
96 | header: true,
97 | content: 'header cell'
98 | },
99 | {
100 | content: 'cell'
101 | },
102 | {
103 | content: 'cell'
104 | }
105 | ]
106 | }
107 | ]
108 | },
109 | {
110 | name: 'Column',
111 | columns: [
112 | {
113 | items: [
114 | {
115 | name: 'Heading',
116 | level: 'h3',
117 | content: 'Heading in a column'
118 | },
119 | {
120 | name: 'Paragraph',
121 | images: [
122 | { src: 'https://placehold.jp/3d4070/ffffff/400x300.png?text=Dummy', caption: 'Image caption' }
123 | ],
124 | content: 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo'
125 | }
126 | ]
127 | },
128 | {
129 | items: [
130 | {
131 | name: 'List',
132 | type: 'unoradered',
133 | rows: [
134 | { content: 'This is the text of the list in a column' },
135 | { content: 'This is the text of the list in a column' },
136 | { content: 'This is the text of the list in a column' }
137 | ]
138 | }
139 | ]
140 | }
141 | ]
142 | },
143 | {
144 | name: 'Html',
145 | content: 'Here is a raw html
'
146 | }
147 | ],
148 | loadItemsFromInputTag: false,
149 | allowStyledText: true,
150 | styledTextClasses: ['bold','link','red','green','blue'],
151 | onLoad: function (html) {
152 | updatePreview(html);
153 | },
154 | onUpdate: function (html) {
155 | updatePreview(html);
156 | }
157 | };
158 | new BlockEditor('#app1', options);
159 |
160 | // Update preview
161 | var previewBody;
162 | function updatePreview(html) {
163 | if (previewBody) {
164 | previewBody.innerHTML = html;
165 | } else {
166 | launchPreview(html);
167 | previewBody = document.querySelector('#preview');
168 | }
169 | }
170 |
171 | // Preview window
172 | function launchPreview(html) {
173 | var jsFrame = new JSFrame({
174 | horizontalAlign: 'left',
175 | verticalAlign: 'top',
176 | });
177 | var frame = jsFrame.create({
178 | title: 'Preview',
179 | left: 200, top: 200, width: 600, height: 400, minWidth: 300, minHeight: 200,
180 | appearanceName: 'material',
181 | appearanceParam: {
182 | border: {
183 | shadow: '0px 0px 6px -3px rgba(0,0,0,0.42)',
184 | width: 1,
185 | color: 'rgba(0,0,0,.15)',
186 | radius: 4,
187 | },
188 | titleBar: {
189 | background: 'rgba(97, 104, 111, 1.0)'
190 | }
191 | },
192 | style: {
193 | backgroundColor: 'rgba(255,255,255,0.88)',
194 | overflow: 'auto'
195 | },
196 | html: '' + html + '
'
197 | });
198 | frame.show();
199 |
200 | frame.setControl({
201 | maximizeButton: 'maximizeButton',
202 | demaximizeButton: 'restoreButton',
203 | minimizeButton: 'minimizeButton',
204 | deminimizeButton: 'deminimizeButton',
205 | animation: true,
206 | animationDuration: 200,
207 | });
208 | }
209 |
--------------------------------------------------------------------------------
/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/takitakit/block-editor-vue/73b10634ec9dab2bab2cc9714515f8e84a014034/screenshot.png
--------------------------------------------------------------------------------
/src/components/Heading.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
24 |
32 |
36 |
45 |
46 |
47 |
49 |
57 |
58 |
59 |
61 |
69 |
70 |
71 |
72 |
73 |
74 |
273 |
274 |
--------------------------------------------------------------------------------
/src/components/Html.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
22 |
27 |
31 |
40 |
41 |
42 |
44 |
52 |
53 |
54 |
55 |
56 |
200 |
201 |
237 |
238 |
--------------------------------------------------------------------------------
/src/components/icons/IconAlignCenter.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/icons/IconAlignLeft.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/icons/IconAlignRight.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/src/components/icons/IconArrowDown.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconArrowUp.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconBold.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/icons/IconCheck.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconClipboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconClose.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/icons/IconColumn.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconCssClass.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconDelete.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconEllipsisH.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/icons/IconEllipsisV.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/icons/IconEraser.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/icons/IconGrid.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconHeading.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconHtml.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconInputClear.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/icons/IconLink.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconList.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconMovedown.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/icons/IconMoveup.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/icons/IconParagraph.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconPlus.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/icons/IconPlusBig.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
--------------------------------------------------------------------------------
/src/components/icons/IconRedo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconReplicate.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconTableMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/src/components/icons/IconUndo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/src/components/icons/IconUnlink.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
--------------------------------------------------------------------------------
/src/components/sub/ActionMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
34 |
35 |
36 |
58 |
59 |
--------------------------------------------------------------------------------
/src/components/sub/AddMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
32 |
55 |
56 |
--------------------------------------------------------------------------------
/src/components/sub/BrokenImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{$t('common.brokenImage')}}
6 |
7 |
8 |
9 |
20 |
48 |
--------------------------------------------------------------------------------
/src/components/sub/Checkbox.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
11 |
12 |
13 |
14 |
25 |
26 |
--------------------------------------------------------------------------------
/src/components/sub/ClassInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
48 |
49 |
50 |
93 |
94 |
--------------------------------------------------------------------------------
/src/components/sub/ColumnEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
34 |
35 |
36 |
59 |
60 |
--------------------------------------------------------------------------------
/src/components/sub/HeadingLevelEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/sub/IconBase.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
26 |
27 |
--------------------------------------------------------------------------------
/src/components/sub/ImageList.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
9 | {{$tc('ImageList.images', images.length, {n: images.length > 9 ? '+9': images.length })}}
10 |
11 |
12 |
18 |
19 |
22 |
23 |
![]()
30 |
31 |
32 |
33 |
34 |
43 |
--------------------------------------------------------------------------------
/src/components/sub/ListTypeEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
49 |
50 |
--------------------------------------------------------------------------------
/src/components/sub/PlainText.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
196 |
219 |
--------------------------------------------------------------------------------
/src/components/sub/RangeInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
12 |
23 |
{{value}}
24 |
25 |
26 |
27 |
46 |
47 |
--------------------------------------------------------------------------------
/src/components/sub/Separator.vue:
--------------------------------------------------------------------------------
1 |
2 |
15 |
16 |
21 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
64 |
65 |
--------------------------------------------------------------------------------
/src/components/sub/SwitchInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
--------------------------------------------------------------------------------
/src/components/sub/TableCell.vue:
--------------------------------------------------------------------------------
1 |
2 | |
13 |
14 |
15 |
59 |
60 |
--------------------------------------------------------------------------------
/src/components/sub/TableCellMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
22 |
23 |
24 |
40 |
41 |
--------------------------------------------------------------------------------
/src/components/sub/TableColMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
41 |
42 |
--------------------------------------------------------------------------------
/src/components/sub/TableColResizeMixin.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/sub/TableRowMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
41 |
42 |
--------------------------------------------------------------------------------
/src/components/sub/TableSizeEdit.vue:
--------------------------------------------------------------------------------
1 |
2 |
46 |
47 |
48 |
115 |
116 |
--------------------------------------------------------------------------------
/src/components/sub/TableSortMixin.vue:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/sub/TextInput.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
42 |
43 |
--------------------------------------------------------------------------------
/src/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "common": {
3 | "Table": "Table",
4 | "Column": "Column",
5 | "Heading": "Heading",
6 | "List": "List",
7 | "Image": "Image",
8 | "Html": "HTML",
9 | "Paragraph": "Paragraph",
10 | "cssClassName": "CSS Class",
11 | "moveUpItem": "Move up",
12 | "moveDownItem": "Move down",
13 | "sortItem": "Sort to drag",
14 | "addItem": "Insert item",
15 | "actionForBlock": "Action for block",
16 | "addBlock": "Add a block AS",
17 | "undo": "Undo",
18 | "redo": "Redo",
19 | "selectClass": "Select class",
20 | "copyHtml": "Copy HTML",
21 | "copyHtmlAlert": {
22 | "success": "Copied HTML to clipboard",
23 | "fail": "Failed copy HTML to clipboard"
24 | },
25 | "columnNestAlert": "Column item can't be nested",
26 | "notAllowedItemInColumn": "This item is not allowed in column",
27 | "replicateItem": "Replicate",
28 | "deleteItem": "Delete",
29 | "apply": "Apply",
30 | "cancel": "Cancel",
31 | "ok": "OK",
32 | "save": "Save",
33 | "delete": "Delete",
34 | "setCssClass": "Set CSS class",
35 | "imageCaptionPlaceholder": "Caption",
36 | "imageFromSrc": "Edit url",
37 | "imageFromUploader": "Select",
38 | "InputImageUrl": "Input image URL",
39 | "openInNewWindow": "Open in a new window",
40 | "images": "No Images | {n} Image | {n} Images",
41 | "link": "link",
42 | "url": "URL",
43 | "unlink": "unlink",
44 | "bold": "bold",
45 | "brokenImage": "Broken image",
46 | "clickToAddItem": "Click here to add a item",
47 | "test": "hoge"
48 | },
49 | "VisualText": {
50 | "bold": "Bold",
51 | "link": "Link",
52 | "unlink": "Unlink",
53 | "remove": "Remove format"
54 | },
55 | "Paragraph": {
56 | "leftAlign": "left-align",
57 | "rightAlign": "rignt-align",
58 | "centerAlign": "center-align",
59 | "editImages": "Add/Edit Images"
60 | },
61 | "Heading": {
62 | "changeLevel": "Change heading level"
63 | },
64 | "Column": {
65 | "confirmMakeColumnSmaller": "Because a number less than the current number of columns was specified, the column will be delete.",
66 | "changeColumnNum": "Change number of columns",
67 | "columnNumber": "Column #"
68 | },
69 | "List": {
70 | "orderedList": "Ordered list",
71 | "unorderedList": "Unordered list",
72 | "changeType": "Change list type",
73 | "reachedMaxRow": "The number of rows has reached the maximum({n} rows) and can not be added anymore."
74 | },
75 | "Table": {
76 | "changeColRow": "Change number of cols/rows",
77 | "numberOfColumns": "columns",
78 | "numberOfRows": "rows",
79 | "rowRangeError": "The number of rows in the table can be set in the range of {min}-{max}.",
80 | "colRangeError": "The number of columns in the table can be set in the range of {min}-{max}.",
81 | "rowEmptyError": "The number of rows in the table has not been set.",
82 | "colEmptyError": "The number of columns in the table has not been set.",
83 | "decreaseConfirm": "Data is lost because it is smaller than the current input area. Is it OK?",
84 | "reachedMaxRow": "The number of rows has reached the maximum value and can not be added any more.",
85 | "reachedMaxCol": "The number of columns has reached the maximum value and can not be added any more.",
86 | "reachedMinRow": "The number of rows has reached the minimum value and can not be deleted any more.",
87 | "reachedMinCol": "The number of columns has reached the minimum value and can not be deleted any more.",
88 | "resetColWidth": "Reset width of all columns",
89 | "cannotMoveRow": "You can not move a row if the source or destination location contains merged cells.",
90 | "cannotMoveCol": "You can not move a column if the source or destination location contains merged cells.",
91 | "addRowAbove": "Add a row above",
92 | "addRowBelow": "Add a row below",
93 | "headerizeRow": "Turn a row into a table header",
94 | "deheaderizeRow": "Return a row into a table data",
95 | "clearRow": "Clear the contents of a row",
96 | "delRow": "Delete a row",
97 | "addColLeft": "Add a column to the left",
98 | "addColRight": "Add a column to the right",
99 | "headerizeCol": "Turn a column into a table header",
100 | "deheaderizeCol": "Return a column into a table data",
101 | "clearCol": "Clear the contents of a column",
102 | "delCol": "Delete a column",
103 | "cellMenu": "Cell Menu",
104 | "rowMenu": "Row Menu",
105 | "colMenu": "Column Menu",
106 | "mergeCell": "Merge cells",
107 | "divideCell": "Divide cells",
108 | "headerizeCell": "Trun cells into a table header",
109 | "deheaderizeCell": "Return cells into a table data",
110 | "clearCell": "Clear the contents of cells"
111 | },
112 | "ImageEdit": {
113 | "inputUrl": "Input URL",
114 | "openFileBrowser": "Open a file browser",
115 | "captionPlaceholder": "Caption",
116 | "urlPlaceholder": "URL",
117 | "cancelInputAlert": "Discards the input contents.Is it OK?"
118 | },
119 | "ImageList": {
120 | "images": "No Images | {n} Image | {n} Images"
121 | }
122 | }
--------------------------------------------------------------------------------
/src/i18n/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "common": {
3 | "Table": "表組み",
4 | "Column": "カラム",
5 | "Heading": "見出し",
6 | "List": "リスト",
7 | "Image": "画像",
8 | "Html": "HTML",
9 | "Paragraph": "段落",
10 | "cssClassName": "CSSクラス",
11 | "moveUpItem": "上に移動",
12 | "moveDownItem": "下に移動",
13 | "sortItem": "ドラッグして並び替え",
14 | "addItem": "項目の挿入",
15 | "actionForBlock": "ブロックの設定",
16 | "addBlock": "ブロックの追加",
17 | "undo": "アンドゥ",
18 | "redo": "リドゥ",
19 | "selectClass": "クラスを選択",
20 | "copyHtml": "HTMLをコピー",
21 | "copyHtmlAlert": {
22 | "success": "HTMLをクリップボードにコピーしました",
23 | "fail": "コピーに失敗しました"
24 | },
25 | "columnNestAlert": "カラムを入れ子にすることはできません",
26 | "notAllowedItemInColumn": "カラム内で許可されていないアイテムです",
27 | "replicateItem": "複製",
28 | "deleteItem": "削除",
29 | "apply": "適用",
30 | "cancel": "キャンセル",
31 | "ok": "OK",
32 | "save": "保存",
33 | "delete": "削除",
34 | "setCssClass": "CSSクラスの設定",
35 | "imageCaptionPlaceholder": "キャプション",
36 | "imageFromSrc": "URLを編集",
37 | "imageFromUploader": "選択",
38 | "InputImageUrl": "画像のURLを入力",
39 | "openInNewWindow": "別画面で開く",
40 | "images": "画像なし | {n} 画像 | {n} 画像",
41 | "link": "リンク",
42 | "url": "URL",
43 | "unlink": "リンク解除",
44 | "bold": "太字",
45 | "brokenImage": "Broken image",
46 | "clickToAddItem": "クリックして要素を追加して下さい",
47 | "test": "ほげ"
48 | },
49 | "VisualText": {
50 | "bold": "太字",
51 | "link": "リンク",
52 | "unlink": "リンク解除",
53 | "remove": "リセット"
54 | },
55 | "Paragraph": {
56 | "leftAlign": "左寄せ",
57 | "rightAlign": "右寄せ",
58 | "centerAlign": "中央寄せ",
59 | "editImages": "画像の追加・編集"
60 | },
61 | "Heading": {
62 | "changeLevel": "見出しレベルの変更"
63 | },
64 | "Column": {
65 | "confirmMakeColumnSmaller": "現在のカラム数よりも小さい数が指定されたため、カラムは削除されます",
66 | "changeColumnNum": "カラム数の変更",
67 | "columnNumber": "カラム No."
68 | },
69 | "List": {
70 | "orderedList": "番号リスト",
71 | "unorderedList": "リスト",
72 | "changeType": "リスト種類の変更",
73 | "reachedMaxRow": "行数が最大数({n}行)に達しているため、これ以上追加できません"
74 | },
75 | "Table": {
76 | "changeColRow": "行/列数の変更",
77 | "numberOfColumns": "列数",
78 | "numberOfRows": "行数",
79 | "rowRangeError": "テーブルの行数は {min}-{max} の範囲で設定可能です",
80 | "colRangeError": "テーブルの列数は {min}-{max} の範囲で設定可能です",
81 | "rowEmptyError": "テーブルの行数が設定されていません",
82 | "colEmptyError": "テーブルの行数が設定されていません",
83 | "decreaseConfirm": "現在の入力エリアよりも縮小するため、データが失われます。よろしいですか?",
84 | "reachedMaxRow": "これ以上行を追加できません",
85 | "reachedMaxCol": "これ以上列を追加できません",
86 | "reachedMinRow": "これ以上行を削除できません",
87 | "reachedMinCol": "これ以上列を削除できません",
88 | "cannotMoveRow": "移動元か、移動先に結合されたセルが含まれている場合は行を移動できません。",
89 | "cannotMoveCol": "移動元か、移動先に結合されたセルが含まれている場合は列を移動できません。",
90 | "addRowAbove": "一つ上に行を追加",
91 | "addRowBelow": "一つ下に行を追加",
92 | "resetColWidth": "列幅を全てリセット",
93 | "headerizeRow": "行をヘッダに変換",
94 | "deheaderizeRow": "行のヘッダを解除",
95 | "clearRow": "行の入力をクリア",
96 | "delRow": "行を削除",
97 | "addColLeft": "左に列を追加",
98 | "addColRight": "右に列を追加",
99 | "headerizeCol": "列をヘッダに変換",
100 | "deheaderizeCol": "列のヘッダを解除",
101 | "clearCol": "列の入力をクリア",
102 | "delCol": "列を削除",
103 | "cellMenu": "選択されたセルの操作",
104 | "rowMenu": "選択された行の操作",
105 | "colMenu": "選択された列の操作",
106 | "mergeCell": "セルを結合",
107 | "divideCell": "セルを分割",
108 | "headerizeCell": "セルをヘッダに変換",
109 | "deheaderizeCell": "セルのヘッダを解除",
110 | "clearCell": "セルの入力をクリア"
111 | },
112 | "ImageEdit": {
113 | "inputUrl": "URLを入力",
114 | "openFileBrowser": "ファイルブラウザを開く",
115 | "captionPlaceholder": "キャプション",
116 | "urlPlaceholder": "URL",
117 | "cancelInputAlert": "入力した内容を破棄します。よろしいですか?"
118 | },
119 | "ImageList": {
120 | "images": "画像なし | {n} 画像 | {n} 画像"
121 | }
122 | }
--------------------------------------------------------------------------------
/src/i18n/load.js:
--------------------------------------------------------------------------------
1 | // i18nメッセージのロード処理
2 | const i18n_msg = {}
3 | const requires = require.context(
4 | './',
5 | false,
6 | /.json$/
7 | )
8 | requires.keys().forEach(file => {
9 | const config = requires(file)
10 | let m
11 | if ((m = file.match(/([^\/\.]+)\.json$/)) !== null) {
12 | let locale = m[1]
13 | i18n_msg[locale] = config
14 | }
15 | })
16 | export default i18n_msg;
--------------------------------------------------------------------------------
/src/scripts/DOM.js:
--------------------------------------------------------------------------------
1 | export default class DOM {
2 | // 指定されたセレクタにマッチするかどうか
3 | static matches (node, selector) {
4 | return (node.matches || node.msMatchesSelector).call(node, selector)
5 | }
6 |
7 | // 指定したセレクタに一致する最も近い先祖要素を返す
8 | static closest (node, selector) {
9 | return (node.closest || function(_selector) {
10 | do {
11 | if ((node.matches || node.msMatchesSelector).call(node, _selector)) {
12 | return node
13 | }
14 | node = node.parentElement || node.parentNode
15 | } while (node !== null && node.nodeType === 1)
16 | return null
17 | }).call(node, selector)
18 | }
19 | // 指定したセレクタに一致する要素内のうち、何番目の要素かを返す
20 | static index (node, selector, base_node) {
21 | base_node = typeof base_node==='undefined' ? document : base_node
22 | let nodes = base_node.querySelectorAll(selector)
23 | let index = false
24 | for (let i=0; i= nodes.length) return null
48 | return nodes[index]
49 | }
50 | }
--------------------------------------------------------------------------------
/src/scripts/DragItemUtil.js:
--------------------------------------------------------------------------------
1 | import DOM from '@/scripts/DOM.js'
2 |
3 | // ドラッグ&ドロップのライブラリクラス
4 | export default class DragItemUtil {
5 | constructor (options) {
6 | // ドラッグ対象のDOM要素(実際にはcloneした要素を表示に使う)
7 | const target = options.targetElement
8 | this.dragTargetElement = target.cloneNode(true)
9 |
10 | const wrap = document.createElement('div')
11 | wrap.appendChild(this.dragTargetElement)
12 | this.wrapElement = wrap
13 | this.wrapElement.style.position = 'absolute'
14 | this.wrapElement.style.top = '0px'
15 | this.wrapElement.style.left = '0px'
16 | this.wrapElement.style.pointerEvents = 'none'
17 | this.wrapElement.style.boxSizing = 'border-box'
18 | this.wrapElement.style.transition = 'opacity .2s'
19 | this.wrapElement.style.zIndex = '1000'
20 | this.wrapElement.style.cursor = 'grabbing'
21 | this.wrapElement.opacity = 0
22 |
23 | this.dragStartEvent = options.dragStartEvent
24 |
25 | // ドラッグイメージを包含するDOM要素
26 | this.parentElement = options.parentElement
27 |
28 | // ドラッグ対象の要素の位置を記録
29 | this.srcPosition = DOM.position(target, this.parentElement)
30 | this.startPosition = {x: options.dragStartEvent.pageX, y: options.dragStartEvent.pageY}
31 |
32 | if (options.draggingClass) {
33 | // クラスの指定があれば、ドラッグ対象の要素にクラスを設定する
34 | setTimeout(()=>{
35 | this.dragTargetElement.classList.add(options.draggingClass)
36 | },0 )
37 | }
38 |
39 | // ドラッグイメージのサイズ、位置を調整して初期化する
40 | this.dragTargetElement.style.width = target.offsetWidth + 'px'
41 | this.dragTargetElement.style.height = target.offsetHeight + 'px'
42 | this.parentElement.appendChild(this.wrapElement)
43 | this.dragover(this.dragStartEvent)
44 |
45 | // ドラッグ時の表示制御のためのダミー画像
46 | const dummy = document.createElement('div')
47 | dummy.style.position = 'absolute'
48 | dummy.style.opacity = '0'
49 | dummy.style.width = '1px'
50 | dummy.style.height = '1px'
51 | dummy.style.pointerEvents = 'none'
52 | this.parentElement.appendChild(dummy)
53 | this.dragStartEvent.dataTransfer.setDragImage(dummy, 0, 0)
54 |
55 | this.dragoverFunc = ev => {
56 | this.dragover(ev)
57 | }
58 | document.addEventListener('dragover', this.dragoverFunc)
59 | }
60 |
61 | // ドラッグオーバー処理
62 | dragover (ev) {
63 | const dx = ev.pageX - this.startPosition.x
64 | const dy = ev.pageY - this.startPosition.y
65 | const x = this.srcPosition.x + dx
66 | const y = this.srcPosition.y + dy
67 | this.wrapElement.style.transform = `translate(${x}px, ${y}px)`
68 | }
69 |
70 | // 破棄時の処理
71 | destroy () {
72 | this.wrapElement.style.opacity = 1
73 | this.wrapElement.style.transition = 'opacity .2s'
74 | this.wrapElement.style.opacity = 0
75 | setTimeout(()=>{
76 | this.wrapElement.remove()
77 | },200)
78 | document.removeEventListener('dragover', this.dragoverFunc)
79 | }
80 | }
--------------------------------------------------------------------------------
/src/scripts/ItemBase.js:
--------------------------------------------------------------------------------
1 | // アイテムデータ処理クラスの基底クラス
2 | export default class ItemBase {
3 |
4 | constructor (options) {
5 | this.options = options
6 | this.baseIndentLevel = 0
7 | this.name
8 | }
9 |
10 | // 設定取得
11 | getConfig (key) {
12 | if (typeof this.options[this.name] !== 'undefined' && typeof this.options[this.name][key] !== 'undefined') {
13 | return this.options[this.name][key]
14 | } else if (typeof this.options[key] !== 'undefined') {
15 | return this.options[key]
16 | } else {
17 | return null
18 | }
19 | }
20 |
21 | // 渡されたHTMLElementが、このコンポーネントで処理可能か判断する
22 | matches () { return false }
23 |
24 | // 渡されたHTMLElementから、コンポーネントで扱えるデータに変換する
25 | getItem (element) { return null }
26 |
27 | // アイテムデータをHTMLに変換する
28 | getHtml(item, base_ind) {
29 | if (typeof base_ind !== 'undefined') {
30 | this.baseIndentLevel = base_ind
31 | }
32 | return null
33 | }
34 |
35 | // アイテムデータから、
36 | // 定義セット情報を取得する
37 | getPreset (item) {
38 | const conf = this.getConfig('presets')
39 | if (!conf) return null
40 |
41 | let matched_def = null
42 | conf.some(def => {
43 | let key_num = 0, matched_num = 0
44 | for (let key in def) {
45 | if (key === 'dispName') continue
46 | if (def[key] === item[key]) matched_num++
47 | key_num++
48 | }
49 | if (matched_num === key_num) { // 一致する定義セットが見つかった
50 | matched_def = def
51 | return true
52 | }
53 | })
54 | return matched_def
55 | }
56 |
57 | // 空のデータを返す
58 | getEmptyItem() { return null }
59 |
60 | // インデントを返す
61 | _indent (level) {
62 | let indent = this.options['outputIndent'] // インデント
63 | return indent.repeat(this.baseIndentLevel + level)
64 | }
65 | }
--------------------------------------------------------------------------------
/src/scripts/LoadComponents.js:
--------------------------------------------------------------------------------
1 | const components = {}
2 | const item_classes = {}
3 | const requires = require.context(
4 | '@/components', false, /.vue$/
5 | )
6 | requires.keys().forEach(file => {
7 | let m
8 | if ((m = file.match(/^\.\/(.+?)\.vue$/))!==null) {
9 | const name = m[1]
10 | const config = requires(file)
11 | const component = config.default || config
12 | const item_class = config.Item
13 |
14 | components[name] = component
15 | item_classes[name] = item_class
16 | }
17 | })
18 |
19 | export {components, item_classes}
--------------------------------------------------------------------------------
/src/scripts/Util.js:
--------------------------------------------------------------------------------
1 | import mergeWith from 'lodash/mergeWith'
2 | import cloneDeep from 'lodash/cloneDeep'
3 |
4 | export default class Util {
5 | static generateID(len, type) {
6 | if (typeof len === 'undefined') len = 8
7 | if (typeof type === 'undefined') type = 'alphanumeric'
8 |
9 | let chars
10 | if (type === 'alphanumeric') chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
11 | else if (type === 'numeric') chars = '0123456789'
12 |
13 | const clen = chars.length
14 | let r = ""
15 | for (let i = 0; i < len; i++) {
16 | r += chars[Math.floor(Math.random() * clen)]
17 | }
18 | return r
19 | }
20 |
21 | // WYSIWYGエディタで入力されたHTMLを整形する
22 | static arrangeEditableHtml(html) {
23 | if (!html) return ''
24 | //
の形式はに変換
25 | html = html.replace(/
]+)?>
]+)?(\s*\/)?><\/p>/ig, '
')
26 | // DIV および Pタグの開始タグは改行に変換
27 | html = html.replace(/(|)/ig, '
')
28 | // DIV および Pタグの終了タグは除去
29 | html = html.replace(/(<\/\s*div\s*>|<\/\s*p\s*>)/ig, '')
30 | // 先頭のBRタグは除去
31 | html = html.replace(/^(
]+)?(\s*\/)?>)+/i, '')
32 | return html
33 | }
34 |
35 | // データをクリップボードにコピーする
36 | static copyToClipboard(data) {
37 | let elm = document.createElement('div')
38 | elm.appendChild(document.createElement('pre')).textContent = data
39 |
40 | elm.style.position = 'fixed'
41 | elm.style.left = '-100%'
42 |
43 | document.body.appendChild(elm)
44 | document.getSelection().selectAllChildren(elm)
45 |
46 | let result = document.execCommand('copy')
47 | document.body.removeChild(elm)
48 | return result
49 | }
50 |
51 | // サイズ表記からバイト数への変換
52 | static sizeToByte(size_str) {
53 | const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
54 | size_str = size_str.toUpperCase()
55 | const reg = new RegExp('^(([1-9]\\d*|0)(\\.\\d+)?)\\s*(' + units.join('|') + '?)$')
56 |
57 | if (size_str.match(reg)) {
58 | const size = RegExp.$1
59 | const unit = RegExp.$4
60 | const pos = units.indexOf(unit)
61 | return Math.floor(size * Math.pow(1024, pos))
62 | }
63 | return null
64 | }
65 |
66 | // タッチデバイス判定
67 | static isTouchDevice() {
68 | return ('ontouchstart' in document) && ('orientation' in window)
69 | }
70 |
71 | // オブジェクトのディープコピー
72 | static deepCopy(obj) {
73 | return cloneDeep(obj)
74 | }
75 | // オブジェクトのディープマージ
76 | static deepMerge(target, src) {
77 | return mergeWith(target, src, (obj, src)=>{
78 | if (Array.isArray(obj)) {
79 | return src
80 | }
81 | })
82 | }
83 | }
84 |
85 | export class Rect {
86 | constructor(x, y, w, h) {
87 | this.x = x
88 | this.y = y
89 | this.width = w
90 | this.height = h
91 | }
92 | }
--------------------------------------------------------------------------------
/src/styles/animation.scss:
--------------------------------------------------------------------------------
1 | // ブロック要素のグループアニメーション
2 | .BEV-item-anim {
3 | transition: all .3s;
4 | width: 100%;
5 | box-sizing: border-box;
6 | }
7 | .BEV-item-enter {
8 | opacity: 0;
9 | transform: translateX(-30px);
10 | }
11 | .BEV-item-enter-to, .BEV-item-leave {
12 | opacity: 1;
13 | transform: translateX(0);
14 | }
15 | .BEV-item-leave-to {
16 | opacity: 0;
17 | transform: translateX(0);
18 | }
19 | .BEV-item-leave-active {
20 | position: absolute;
21 | }
22 |
23 | // ポップアップメニュー アニメーション
24 | .popup-enter-active, .popup-leave-active {
25 | transition: opacity .2s;
26 | }
27 | .popup-enter, .popup-leave-to /* .fade-leave-active below version 2.1.8 */ {
28 | opacity: 0;
29 | }
30 |
31 | // メニュー系のフェードアニメーション
32 | .fade-enter-active, .fade-leave-active {
33 | transition: opacity .2s;
34 | }
35 | .fade-enter, .fade-leave-to {
36 | opacity: 0;
37 | }
38 |
39 | // カラムのアイテムなし<-->アイテムありのアニメーション
40 | .column-no-items-enter-active, .column-no-items-leave-active {
41 | transition: opacity .3s;
42 | }
43 | .column-no-items-enter, .column-no-items-leave-to {
44 | opacity: 0;
45 | }
46 | .column-no-items-leave-active {
47 | position: absolute!important;
48 | }
49 |
50 | // ポップアップメニューの移動アニメーション
51 | .popup-leave-move {
52 | transition: all .2s;
53 | transform: translate(-20px, 0);
54 | }
55 |
56 | .popup-move-enter-active, .popup-move-leave-active {
57 | transition: opacity .2s, transform .2s, padding .2s;
58 | }
59 | .popup-move-enter {
60 | opacity: 0;
61 | transform: translate(20px, 0);
62 | }
63 | .popup-move-leave-to {
64 | opacity: 0;
65 | }
66 |
--------------------------------------------------------------------------------
/src/styles/components.scss:
--------------------------------------------------------------------------------
1 | .BEV-item {
2 | position: relative;
3 | opacity: 1;
4 | }
5 |
6 |
--------------------------------------------------------------------------------
/src/styles/reset.scss:
--------------------------------------------------------------------------------
1 | // リセット
2 | /deep/ {
3 | div,
4 | dl,
5 | dt,
6 | dd,
7 | ul,
8 | ol,
9 | li,
10 | h1,
11 | h2,
12 | h3,
13 | h4,
14 | h5,
15 | h6,
16 | pre,
17 | code,
18 | form,
19 | fieldset,
20 | legend,
21 | input,
22 | textarea,
23 | p,
24 | blockquote,
25 | th,
26 | td,
27 | label {
28 | margin:0;
29 | padding:0;
30 | }
31 | table {
32 | border-collapse:collapse;
33 | border-spacing:0;
34 | }
35 | fieldset,
36 | img {
37 | border:0;
38 | }
39 |
40 | address,
41 | caption,
42 | cite,
43 | code,
44 | dfn,
45 | em,
46 | strong,
47 | th,
48 | var {
49 | font-style:normal;
50 | font-weight:normal;
51 | }
52 | ol,
53 | ul {
54 | list-style:none;
55 | }
56 | caption,
57 | th {
58 | text-align:left;
59 | }
60 | h1,
61 | h2,
62 | h3,
63 | h4,
64 | h5,
65 | h6 {
66 | font-size:100%;
67 | font-weight:normal;
68 | }
69 | q:before,
70 | q:after {
71 | content:'';
72 | }
73 | abbr,
74 | acronym {
75 | border:0;
76 | font-variant:normal;
77 | }
78 | sup {
79 | vertical-align:text-top;
80 | }
81 | sub {
82 | vertical-align:text-bottom;
83 | }
84 | input,
85 | textarea,
86 | select {
87 | font-family:inherit;
88 | font-size:inherit;
89 | font-weight:inherit;
90 | }
91 |
92 | input,
93 | textarea,
94 | select {
95 | *font-size:100%;
96 | }
97 |
98 | legend {
99 | color:#000;
100 | }
101 | }
--------------------------------------------------------------------------------
/src/styles/v-tooltip.scss:
--------------------------------------------------------------------------------
1 | $tooltip-bg-color: rgba(0,0,0,.7);
2 | .BEV-tooltip {
3 | display: block !important;
4 | z-index: 10000;
5 |
6 | .BEV-tooltip-inner {
7 | background: $tooltip-bg-color;
8 | color: white;
9 | border-radius: 3px;
10 | padding: 3px 8px 3px;
11 | font-size: .8em;
12 | }
13 |
14 | .BEV-tooltip-arrow {
15 | width: 0;
16 | height: 0;
17 | border-style: solid;
18 | position: absolute;
19 | margin: 5px;
20 | border-color: $tooltip-bg-color;
21 | z-index: 1;
22 | }
23 |
24 | &[x-placement^="top"] {
25 | margin-bottom: 5px;
26 |
27 | .BEV-tooltip-arrow {
28 | border-width: 5px 5px 0 5px;
29 | border-left-color: transparent !important;
30 | border-right-color: transparent !important;
31 | border-bottom-color: transparent !important;
32 | bottom: -5px;
33 | left: calc(50% - 5px);
34 | margin-top: 0;
35 | margin-bottom: 0;
36 | }
37 | }
38 |
39 | &[x-placement^="bottom"] {
40 | margin-top: 5px;
41 |
42 | .BEV-tooltip-arrow {
43 | border-width: 0 5px 5px 5px;
44 | border-left-color: transparent !important;
45 | border-right-color: transparent !important;
46 | border-top-color: transparent !important;
47 | top: -5px;
48 | left: calc(50% - 5px);
49 | margin-top: 0;
50 | margin-bottom: 0;
51 | }
52 | }
53 |
54 | &[x-placement^="right"] {
55 | margin-left: 5px;
56 |
57 | .BEV-tooltip-arrow {
58 | border-width: 5px 5px 5px 0;
59 | border-left-color: transparent !important;
60 | border-top-color: transparent !important;
61 | border-bottom-color: transparent !important;
62 | left: -5px;
63 | top: calc(50% - 5px);
64 | margin-left: 0;
65 | margin-right: 0;
66 | }
67 | }
68 |
69 | &[x-placement^="left"] {
70 | margin-right: 5px;
71 |
72 | .BEV-tooltip-arrow {
73 | border-width: 5px 0 5px 5px;
74 | border-top-color: transparent !important;
75 | border-right-color: transparent !important;
76 | border-bottom-color: transparent !important;
77 | right: -5px;
78 | top: calc(50% - 5px);
79 | margin-left: 0;
80 | margin-right: 0;
81 | }
82 | }
83 |
84 |
85 | &[aria-hidden='true'] {
86 | visibility: hidden;
87 | opacity: 0;
88 | transition: opacity .15s, visibility .15s;
89 | }
90 |
91 | &[aria-hidden='false'] {
92 | visibility: visible;
93 | opacity: 1;
94 | transition: opacity .15s;
95 | }
96 | }
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | publicPath: './',
3 | outputDir: './dist',
4 | runtimeCompiler: true,
5 |
6 | configureWebpack: {
7 | output: {
8 | filename: 'js/block-editor-vue.js'
9 | },
10 | optimization: {
11 | splitChunks: false
12 | }
13 | },
14 |
15 | filenameHashing: false,
16 | baseUrl: undefined,
17 | assetsDir: undefined,
18 | productionSourceMap: false,
19 | parallel: undefined,
20 |
21 | css: {
22 | extract: false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------