Skip to content

Commit ea9ee4b

Browse files
authored
Merge pull request #17 from discussjs/dev
feat(submit): 移除预览功能,让评论框实时显示输入结果
2 parents f998189 + 17d7d27 commit ea9ee4b

File tree

6 files changed

+78
-82
lines changed

6 files changed

+78
-82
lines changed

.eslintrc.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ extends: eslint:recommended
66
parserOptions:
77
ecmaVersion: latest
88
sourceType: module
9+
910
plugins: [svelte3]
1011

11-
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }]
12+
overrides: [{ files: ['**/*.svelte'], processor: 'svelte3/svelte3' }]
13+
1214
rules:
1315
# off 或 0 - 关闭规则
1416
# warn 或 1 - 开启规则, 使用警告 程序不会退出

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "discuss",
3-
"version": "1.1.5",
3+
"version": "1.1.6",
44
"description": "一款简单,安全,免费的评论系统 | A simple, safe, free comment system",
55
"main": "index.js",
66
"unpkg": "dist/discuss.js",

src/client/lib/emot.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ export default (emotCDN) => {
278278
},
279279
[`<img src=${items['鼓掌']}>`]: {
280280
type: 'image',
281-
items: items
281+
items
282282
}
283283
}
284284
}

src/client/view/global.svelte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
#Discuss .D-comment-emot {
102102
width: 32px;
103103
height: auto;
104+
margin: -1px 1px 0;
104105
vertical-align: middle;
105106
}
106107

src/client/view/submit.svelte

Lines changed: 71 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,10 @@
7171
site: { value: '', is: true },
7272
content: { value: '', is: false }
7373
}
74-
$: contentHTML = ''
7574
76-
onMount(async () => {
75+
onMount(() => {
7776
initInfo()
78-
await getEmot()
77+
getEmot()
7978
})
8079
8180
afterUpdate(() => {
@@ -103,6 +102,7 @@
103102
} else {
104103
emotMaps = emot
105104
}
105+
getEmotAll()
106106
}
107107
108108
function getEmotAll() {
@@ -118,22 +118,6 @@
118118
console.log(error)
119119
}
120120
}
121-
function ParseEmot() {
122-
getEmotAll()
123-
let content = metas.content.value
124-
const emots = []
125-
content.replace(/\[(.*?)\]/g, ($0, $1) => {
126-
emots.push($1)
127-
})
128-
129-
for (const emot of emots) {
130-
const link = emotAll[emot]
131-
if (!link) continue
132-
const img = `<img class='D-comment-emot' src='${link}' alt='${emot}'/>`
133-
content = content.replace(`[${emot}]`, img)
134-
}
135-
contentHTML = content
136-
}
137121
138122
function SaveInfo() {
139123
for (const [k, v] of Object.entries(metas)) {
@@ -142,51 +126,69 @@
142126
localStorage.discuss = JSON.stringify(storage)
143127
}
144128
145-
$: isOnPreview = metas.content.value.length
146-
let isPreview = false
147-
function Preview() {
148-
if (!isPreview) return
149-
ParseEmot()
150-
}
151-
function onPreview() {
152-
isPreview = !isPreview
153-
Preview()
154-
}
155-
156129
function onInput() {
157130
SaveInfo()
158-
Preview()
159131
MetasChange()
160132
}
133+
134+
let lastEditRange
135+
function getCursor() {
136+
const sel = window.getSelection()
137+
if (sel.rangeCount < 0) {
138+
lastEditRange = document.createRange()
139+
return
140+
}
141+
142+
lastEditRange = sel.getRangeAt(0)
143+
}
144+
161145
/**
162146
* @param {String} key 表情名(描述)
163147
* @param {String} value 表情值(内容或地址)
164148
* @param {String} type 表情类型(text or image)
165149
*/
166150
let textareaDOM
151+
// eslint-disable-next-line max-statements
167152
function onClickEmot(key, value, type) {
168-
const cObj = metas.content
169-
let content = cObj.value
153+
textareaDOM.focus()
154+
const sel = window.getSelection()
155+
if (lastEditRange) {
156+
// 清除所有光标并添加最后光标编辑的状态
157+
sel.removeAllRanges()
158+
sel.addRange(lastEditRange)
159+
}
170160
171-
// 获取输入框光标位置
172-
let cursorStart = textareaDOM.selectionStart
173-
let cursorEnd = textareaDOM.selectionEnd
174-
const Start = content.substring(0, cursorStart)
175-
const Ent = content.substring(cursorEnd)
161+
let emojiEl
176162
177-
if (type === textStr) cObj.value = `${Start}${value}${Ent}`
178-
else cObj.value = `${Start}[${key}]${Ent}`
163+
if (type === textStr) {
164+
emojiEl = document.createTextNode(value)
165+
} else {
166+
emojiEl = document.createElement('img')
167+
emojiEl.src = emotAll[key]
168+
emojiEl.className = 'D-comment-emot'
169+
emojiEl.alt = key
170+
}
179171
180-
textareaDOM.focus()
181-
const contentLen = content.length
182-
cursorStart = contentLen
183-
cursorEnd = contentLen
184-
// 重新解析表情
185-
ParseEmot()
186-
// 重新保存
172+
// 不存在光标,则获取光标,并将光标移动至最后
173+
if (!lastEditRange) {
174+
getCursor()
175+
lastEditRange.selectNodeContents(textareaDOM)
176+
lastEditRange.collapse(false)
177+
sel.removeAllRanges()
178+
sel.addRange(lastEditRange)
179+
}
180+
181+
// 如果光标没有重叠,则代表光标选择了一部分内容,需要删除光标再插入表情
182+
if (!lastEditRange.collapsed) lastEditRange.deleteContents()
183+
184+
lastEditRange.insertNode(emojiEl)
185+
186+
// 让光标保持在插入表情的后面, true 表示保持在前面
187+
lastEditRange.collapse(false)
188+
189+
metas.content.value = textareaDOM.innerHTML
190+
// 保存
187191
SaveInfo()
188-
// 由于这是Svelte的特性,引用类型需要重新给自身赋值才会触发双向绑定
189-
metas = metas
190192
}
191193
192194
function MetasChange() {
@@ -217,13 +219,12 @@
217219
async function onSend() {
218220
try {
219221
if (!isSend && !isLegal) return
220-
ParseEmot()
221222
const comment = {
222223
type: 'COMMIT_COMMENT',
223224
nick: metas.nick.value,
224225
mail: metas.mail.value,
225226
site: metas.site.value,
226-
content: contentHTML,
227+
content: metas.content.value,
227228
path: D.path,
228229
pid,
229230
rid
@@ -247,7 +248,6 @@
247248
dispatch('submitComment', { comment: result.data, pid })
248249
metas.content.value = ''
249250
SaveInfo()
250-
isPreview = false
251251
}
252252
} catch (error) {
253253
// eslint-disable-next-line no-console
@@ -271,14 +271,21 @@
271271
on:input={onInput}
272272
/>
273273
{/each}
274-
<textarea
274+
<div
275275
name={contentStr}
276276
class="D-input-content {metas.content.is ? '' : 'D-error'}"
277-
bind:value={metas.content.value}
278277
placeholder={D.ph}
279-
on:input={onInput}
278+
on:input={function () {
279+
metas.content.value = this.innerHTML
280+
onInput()
281+
}}
282+
on:click={getCursor}
283+
on:keyup={getCursor}
280284
bind:this={textareaDOM}
285+
bind:innerHTML={metas.content.value}
286+
contenteditable
281287
/>
288+
282289
{#if wordLimitContent}
283290
<span class="D-text-number">
284291
{metas.content.value.length}
@@ -309,9 +316,7 @@
309316
>
310317
{/if}
311318

312-
<button on:click={onPreview} class="D-cancel D-btn D-btn-main {!isOnPreview && 'D-disabled'}"
313-
>{translate('preview')}</button
314-
><button class="D-send D-btn D-btn-main" on:click={onSend} disabled={isSend || !isLegal}>
319+
<button class="D-send D-btn D-btn-main" on:click={onSend} disabled={isSend || !isLegal}>
315320
{#if isSend && isLegal}
316321
<Loading />
317322
{:else}
@@ -320,9 +325,6 @@
320325
</button>
321326
</div>
322327
</div>
323-
{#if isPreview}
324-
<div class="D-preview">{@html contentHTML}</div>
325-
{/if}
326328
{#if isEmot}
327329
<div class="D-emot">
328330
{#each Object.entries(emotMaps) as [emotKey, emotValue], index}
@@ -332,7 +334,7 @@
332334
{#if emotValue.type === 'text'}
333335
<span title={iKey}>{iValue}</span>
334336
{:else}
335-
<img src={D.imgLoading} d-src={iValue} alt={iKey} title={iKey} />
337+
<img class="D-comment-emot" src={D.imgLoading} d-src={iValue} alt={iKey} title={iKey} />
336338
{/if}
337339
</li>
338340
{/each}
@@ -395,15 +397,22 @@
395397
transition: all 0.5s;
396398
}
397399
400+
.D-input-content:empty::before {
401+
content: attr(placeholder);
402+
color: #666;
403+
}
398404
.D-input-content {
399405
margin: 10px 0 0;
406+
padding: 6px;
400407
resize: vertical;
401408
width: 100%;
402409
min-height: 140px;
403410
max-height: 400px;
404411
outline: none;
405412
font-family: inherit;
406413
transition: none;
414+
overflow-y: auto;
415+
letter-spacing: 1px;
407416
}
408417
409418
.D-text-number {
@@ -500,11 +509,6 @@
500509
cursor: pointer;
501510
transition: 0.3s;
502511
503-
img {
504-
width: 32px;
505-
height: auto;
506-
}
507-
508512
&:hover {
509513
background: var(--D-Low-Color);
510514
box-shadow: 0 2px 2px 0 rgb(0 0 0 / 14%), 0 3px 1px -2px rgb(0 0 0 / 20%), 0 1px 5px 0 rgb(0 0 0 / 12%);
@@ -539,17 +543,6 @@
539543
background: var(--D-Low-Color);
540544
}
541545
542-
/* preview */
543-
544-
.D-preview {
545-
padding: 10px;
546-
overflow-x: auto;
547-
min-height: 1.375rem /* 22/16 */;
548-
margin: 10px 0;
549-
border: 1px solid #dcdfe6;
550-
border-radius: 4px;
551-
}
552-
553546
@media screen and (max-width: 500px) {
554547
.D-input {
555548
display: flex;

0 commit comments

Comments
 (0)