Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/images/pc/editor/icon_bold.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_bold_selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_index.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_index_selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_italic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_italic_selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_quote.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_quote_selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_text.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_text_selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_textlink.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/pc/editor/icon_textlink_selected.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
252 changes: 214 additions & 38 deletions src/AlisEditor.vue
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
<template lang="html">
<div id="ALISEditor">
<InsertPopup :activeRoot="activeRoot" />
<InsertPopup
@deleteTargetAnchorNode="handleDeleteTargetAnchorNode"
@update="updateBlock"
:activeRoot="activeRoot"
:popUpPosition="popUpPosition"
:hoverPopupPosition="hoverPopupPosition"
:isHover="isHover"
:linked_url="linked_url"
:targetAnchorNode="targetAnchorNode"
:store="store"
/>
<template v-if="store.state.isInitialized">
<EditorToolbar
v-if="!config.preview"
@append="appendNewBlock(active, { type: $event })"
@upload="insertImageBlock(active, $event)"
:activeRoot="activeRoot || {}"
:isSaving="store.state.isSaving"
/>
<!--<EditorToolbar-->
<!--v-if="!config.preview"-->
<!--@append="appendNewBlock(active, { type: $event })"-->
<!--@upload="insertImageBlock(active, $event)"-->
<!--:activeRoot="activeRoot || {}"-->
<!--:isSaving="store.state.isSaving">-->
<!--</EditorToolbar>-->
<div
@keydown="handleKeydown(block.id, $event)"
@keydown.enter="handleKeydownEnter(block.id, $event)"
Expand All @@ -24,9 +34,11 @@
@active="setActive($event)"
@addimageuri="addImageURI(block.id, $event)"
@moveToNextBlock="moveToNextBlock(block.id)"
@addEvent="addEventAnchor"
:config="config"
:block="block"
:active="activeRoot && activeRoot.id === block.id"
:store="store"
/>
</div>
</template>
Expand Down Expand Up @@ -75,6 +87,17 @@ interface EditorState {
target: Node | HTMLElement | null
}
insertInitialPositionTrigger: boolean
popUpPosition: {
left: any
top: any
}
hoverPopupPosition: {
left: any
top: any
}
isHover: boolean
linked_url: string
targetAnchorNode: HTMLElement | null
}

interface DeviceKeyDownEventArgument {
Expand Down Expand Up @@ -105,7 +128,18 @@ export default Vue.extend({
posY: 0,
target: null
},
insertInitialPositionTrigger: this.isPressedEnterInTitle
insertInitialPositionTrigger: this.isPressedEnterInTitle,
popUpPosition: {
left: 0.0,
top: 0.0
},
hoverPopupPosition: {
left: 0.0,
top: 0.0
},
isHover: false,
linked_url: '',
targetAnchorNode: null
}
},
props: {
Expand All @@ -123,6 +157,7 @@ export default Vue.extend({
InsertButton
},
mounted() {
this.addEventAnchor()
this.beforeBlockSnapshot = JSON.stringify(this.store.state.blocks)
this.registerScheduledSave()

Expand Down Expand Up @@ -162,8 +197,13 @@ export default Vue.extend({
},
watch: {
isPressedEnterInTitle: function() {
this.appendNewBlockInitialPosition()
this.insertInitialPositionTrigger = !this.insertInitialPositionTrigger
if (this.store.state.blocks[0].type === "Paragraph") {
this.insertInitialPositionTrigger = !this.insertInitialPositionTrigger
browserSelection.selectContentEditableFirstCharFromBlock(this.store.state.blocks[0])
} else {
this.appendNewBlockInitialPosition()
this.insertInitialPositionTrigger = !this.insertInitialPositionTrigger
}
}
},
methods: {
Expand Down Expand Up @@ -215,15 +255,97 @@ export default Vue.extend({
const childId = findRootIdByBlockId(id, this.store.state.blocks)
if (!childId) return
const nowContent = findTreeContentById(childId, this.store.state.blocks)
console.log(nowContent)
if (!nowContent) return

if (isDesktop()) {
if (event.shiftKey) {
return
}
if (nowContent.type === BlockType.Image) {
return
}
// Enterを押した際に<div><br></div>の改行が生まれることを防ぐ処理
;(async () => {
const p = document.createElement('p')
const br = document.createElement('br')
p.appendChild(br)
const selection = window.getSelection() as any
await this.$nextTick()

// Blockquote内のセレクションの位置を見て最終文字の位置にキャレットが存在する場合はBlockquoteから抜ける処理
// ContentEditableがh2, h3タグの行末でEnterを入力すると<div><br></div>を生成してしまう仕様なのでそれに対応する処理
// blockquote内の最後のノードがdivの場合と途中のノードがdivの場合での場合分け
if (!!this.isDeterminedInBlockquote(selection.anchorNode)) {
const el = this.isDeterminedInBlockquote(selection.anchorNode)
const range = document.createRange()
const sel = window.getSelection()
el.childNodes.forEach((targetEl: any) => {
// Blockquote内最後にdivが発生する場合の回避処理
if (targetEl.nodeName === 'DIV' && targetEl.nextSibling === null) {
;(async () => {
el.parentNode.insertBefore(p, el.nextSibling)
el.removeChild(el.lastChild)
await this.$nextTick()
range.setStart(el.nextSibling, 0)
range.collapse(true)
sel.removeAllRanges()
sel.addRange(range)
})()
// Blockquote内の途中でdivが発生した際の回避処理
} else if (targetEl.nodeName === 'DIV' && targetEl.nextSibling !== null) {
console.log(targetEl)
;(async () => {
targetEl.parentNode.insertBefore(targetEl.firstChild, targetEl)
const br = targetEl.previousSibling
targetEl.parentNode.removeChild(targetEl)
await this.$nextTick()
range.setStart(br, 0)
range.collapse(true)
sel.removeAllRanges()
sel.addRange(range)
})()
}
})
if (el.textContent === '') {
el.parentNode.insertBefore(p, el.nextSibling)
el.parentNode.removeChild(el)
}
}

// h2, h3タグの最後尾でEnterを押した時に挿入されるdivタグをpタグに変更する処理
const nodeList = (document as any).querySelector(`[data-block-id="${(this as any).activeRoot.id}"] .target`)
.childNodes
for (let i = 0; i < nodeList.length; i++) {
if (nodeList[i].nodeName === 'DIV') {
await nodeList[i].parentNode.insertBefore(p, nodeList[i].nextSibling)
nodeList[i].parentNode.removeChild(nodeList[i])
}
}
})()
// this.desktopKeyDownEnter({ id, event, childId, nowContent })
}
if (isMobile()) {
this.mobileKeyDownEnter({ id, event, childId, nowContent })
}
},
isDeterminedInBlockquote(targetNode: any) {
const requireRecursivenodeNames = ['#text', 'A', 'H2', 'H3', 'I', 'B', 'DIV']
const processTerminateNodes = ['BLOCKQUOTE', 'P']
if (requireRecursivenodeNames.includes(targetNode.nodeName)) {
const parentNode = targetNode.parentNode
return (this as any).isDeterminedInBlockquote(parentNode)
} else if (processTerminateNodes.includes(targetNode.nodeName)) {
if (targetNode.nodeName === 'BLOCKQUOTE') {
if (targetNode.nextSibling === null || targetNode.nextSibling.nodeName !== 'BLOCKQUOTE') {
return targetNode
}
return false
}
return false
}
},
isLastCharInBlockquote() {},
desktopKeyDownEnter({ id, event, childId, nowContent }: DeviceKeyDownEventArgument) {
if (event.shiftKey) {
return
Expand All @@ -232,25 +354,25 @@ export default Vue.extend({
return
}
event.preventDefault()
requestAnimationFrame(() => {
const b = this.appendNewBlock(nowContent.id, createBlock(BlockType.Paragraph))
if (!b) {
return
}
requestAnimationFrame(() => {
this.active = b.id
browserSelection.selectContentEditableFirstCharFromBlock(b)
const isLink = isContentEditableBlock(nowContent) && regex.isValidEmbedString(nowContent.payload.body)
if (!isLink) return
this.updateBlock({
id: nowContent.id,
type: BlockType.Embed,
payload: {
src: sanitizer.sanitizeAllTags(nowContent.payload.body)
}
})
})
})
// requestAnimationFrame(() => {
// const b = this.appendNewBlock(nowContent.id, createBlock(BlockType.Paragraph))
// if (!b) {
// return
// }
// requestAnimationFrame(() => {
// this.active = b.id
// browserSelection.selectContentEditableFirstCharFromBlock(b)
// const isLink = isContentEditableBlock(nowContent) && regex.isValidEmbedString(nowContent.payload.body)
// if (!isLink) return
// this.updateBlock({
// id: nowContent.id,
// type: BlockType.Embed,
// payload: {
// src: sanitizer.sanitizeAllTags(nowContent.payload.body)
// }
// })
// })
// })
},
mobileKeyDownEnter({ id, event, childId, nowContent }: DeviceKeyDownEventArgument) {
if (isContentEditableBlock(nowContent)) {
Expand Down Expand Up @@ -413,15 +535,69 @@ export default Vue.extend({
return this.store.appendParagraphBlockInitialPosition(createBlock(BlockType.Paragraph, {}))
},
moveToNextBlock(id: string) {
console.log('要修正')
// 要修正
// ;(async () => {
// const block = this.appendNewBlock(id, createBlock(BlockType.Paragraph)) as any
// await this.$nextTick()
// this.active = block.id
// browserSelection.selectContentEditableFirstCharFromBlock(block)
// })()
const blocks = this.store.state.blocks
blocks.forEach((block, index) => {
if (block.id === id) {
if (blocks[index + 1] !== undefined && blocks[index + 1].type === 'Paragraph') {
this.active = blocks[index + 1].id
browserSelection.selectContentEditableFirstCharFromBlock(blocks[index + 1])
}
if (blocks[index + 1] === undefined || (blocks[index + 1] !== undefined && blocks[index + 1].type !== 'Paragraph')) {
console.log(block.id)
const newBlock = this.store.appendBlock(createBlock(BlockType.Paragraph), block) as Block
requestAnimationFrame(() => {
this.active = newBlock.id
browserSelection.selectContentEditableFirstCharFromBlock(newBlock)
})
}
}
})
},
addEventAnchor() {
const targetNodeList = document.querySelectorAll('.target')
for (let i = 0; i < targetNodeList.length; i++) {
const elements = targetNodeList[i].getElementsByTagName('a') as any
if (elements.length !== 0) {
const aCollectionArr = Object.keys(elements).map(function(key) {
return elements[key]
})
if (aCollectionArr.length !== 0) {
for (let n = 0; n < aCollectionArr.length; n++) {
aCollectionArr[n].addEventListener('mouseover', this.addHoverEvent)
aCollectionArr[n].addEventListener('mouseleave', this.deleteHover)
}
}
}
}
},
addHoverEvent(event: any) {
this.isHover = true
this.linked_url = event.target.href
this.targetAnchorNode = event.target
// elementの座標を取得し、windowの絶対座標をマウスオーバー時に出現するポップアップの座標としてセットする
const rect = event.srcElement.getBoundingClientRect()
const alisEditor = document.getElementById('ALISEditor')
const alisEditorRect = (alisEditor as any).getBoundingClientRect()
const absoluteUnderPopupRectLeft = rect.left + rect.width / 2 - alisEditorRect.left - 105
const absoluteUnderPopupRectTop = rect.top - alisEditorRect.top + 30
const absoluteUpperPopupRectLeft = rect.left + rect.width / 2 - alisEditorRect.left - 225
const absoluteUpperPopupRectTop = rect.top - alisEditorRect.top - 110
this.hoverPopupPosition.left = absoluteUnderPopupRectLeft
this.hoverPopupPosition.top = absoluteUnderPopupRectTop
this.popUpPosition.left = absoluteUpperPopupRectLeft
this.popUpPosition.top = absoluteUpperPopupRectTop
},
deleteHover() {
setTimeout(() => {
this.isHover = false
}, 1000)
},
handleDeleteTargetAnchorNode() {
this.targetAnchorNode = null
}
// handleClick(event: any) {
// const blocks = this.store.state.blocks
// }
}
})
</script>
Expand Down
12 changes: 9 additions & 3 deletions src/components/blocks/EditorBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@
<component
:is="`${block.type}Block`"
:block="block"
:store="store"
v-bind="config"
@input="handleInput"
@update="handleUpdate"
@delete="handleDelete"
@append="handleAppendBlock"
@addimageuri="handleAddImage"
@moveToNextBlock="moveToNextBlock"
@addEvent="addEvent"
/>
</template>
</div>
Expand Down Expand Up @@ -62,7 +64,8 @@ export default Vue.extend({
props: {
block: Object as () => Block,
active: Boolean,
config: configProps
config: configProps,
store: Object
},
data(): LocalState {
return {
Expand Down Expand Up @@ -109,8 +112,11 @@ export default Vue.extend({
}
return !sanitizer.sanitizeAllTags((block.payload || { body: '' }).body)
},
moveToNextBlock(event: any) {
this.$emit('moveToNextBlock', event)
moveToNextBlock() {
this.$emit('moveToNextBlock')
},
addEvent() {
this.$emit('addEvent')
}
}
})
Expand Down
9 changes: 3 additions & 6 deletions src/components/blocks/ImageBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
placeholder="説明文を入力"
:value="this.block.payload.caption"
@keydown.enter.prevent="handleEnter"
@input="handleInputCaption"></textarea>
@input="handleInputCaption"
></textarea>
</span>
<div class="image-uploading" v-if="isUploading">Uploading...</div>
<div class="image-toolbar" v-if="!isUploading">
Expand Down Expand Up @@ -58,11 +59,7 @@
</div>
</div>
</div>
<div
class="aliseditor--image preview"
:style="switchStyle"
v-else
>
<div class="aliseditor--image preview" :style="switchStyle" v-else>
<div class="preview-content">
<img :src="block.payload.src" /><br />
<p class="caption">{{ block.payload.caption }}</p>
Expand Down
Loading