Fix controlling contenteditable div innerText

This commit is contained in:
josdejong 2020-05-06 10:47:48 +02:00
parent 2b73c6e6bf
commit e9116cc36c
2 changed files with 73 additions and 36 deletions

View File

@ -31,6 +31,7 @@
} }
} }
}, },
'': '',
'string': 'Hello World', 'string': 'Hello World',
'url': 'https://jsoneditoronline.org', 'url': 'https://jsoneditoronline.org',
'Lorem Ipsum': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.' 'Lorem Ipsum': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
@ -117,7 +118,7 @@
<div class="editor"> <div class="editor">
<JSONEditor <JSONEditor
json={json} bind:json={json}
onChange={handleChange} onChange={handleChange}
/> />
</div> </div>

View File

@ -21,6 +21,9 @@
let limit = DEFAULT_LIMIT let limit = DEFAULT_LIMIT
let domKey
let domValue
$: type = valueType (value) $: type = valueType (value)
let prevValue = undefined let prevValue = undefined
@ -49,13 +52,37 @@
: false : false
}) })
$: valueClass = classnames('value', type, { $: valueClass = getValueClass(value)
url: valueIsUrl,
empty: escapedValue.length === 0, $: if (domKey) {
search: searchResult if (document.activeElement !== domKey || escapedKey === '') {
? !!searchResult[SEARCH_VALUE] // synchronize the innerText of the editable div with the escaped value,
: false // but only when the domValue does not have focus else we will ruin
}) // the cursor position.
domKey.innerText = escapedKey
}
}
$: if (domValue) {
if (document.activeElement !== domValue || escapedValue === '') {
// synchronize the innerText of the editable div with the escaped value,
// but only when the domValue does not have focus else we will ruin
// the cursor position.
domValue.innerText = escapedValue
}
}
function getValueClass (value) {
const type = valueType (value)
return classnames('value', type, {
url: isUrl(value),
empty: escapedValue.length === 0,
search: searchResult
? !!searchResult[SEARCH_VALUE]
: false
})
}
function toggle () { function toggle () {
expanded = !expanded expanded = !expanded
@ -66,12 +93,22 @@
onChangeKey(newKey, key) onChangeKey(newKey, key)
} }
function handleKeyBlur () {
// make sure differences in escaped text like with new lines is updated
domKey.innerText = escapedValue
}
function handleValueInput (event) { function handleValueInput (event) {
const valueText = unescapeHTML(getInnerText(event.target)) const valueText = unescapeHTML(getInnerText(event.target))
const newValue = stringConvert(valueText) // TODO: implement support for type "string" const newValue = stringConvert(valueText) // TODO: implement support for type "string"
onChangeValue(newValue, key) onChangeValue(newValue, key)
} }
function handleValueBlur () {
// make sure differences in escaped text like with new lines is updated
domValue.innerText = escapedValue
}
function handleValueClick (event) { function handleValueClick (event) {
if (valueIsUrl && event.ctrlKey) { if (valueIsUrl && event.ctrlKey) {
event.preventDefault() event.preventDefault()
@ -152,9 +189,9 @@
contenteditable="true" contenteditable="true"
spellcheck="false" spellcheck="false"
on:input={handleKeyInput} on:input={handleKeyInput}
> on:blur={handleKeyBlur}
{escapedKey} bind:this={domKey}
</div> />
<div class="separator">:</div> <div class="separator">:</div>
{/if} {/if}
{#if expanded} {#if expanded}
@ -201,9 +238,9 @@
contenteditable="true" contenteditable="true"
spellcheck="false" spellcheck="false"
on:input={handleKeyInput} on:input={handleKeyInput}
> on:blur={handleKeyBlur}
{escapedKey} bind:this={domKey}
</div> />
<span class="separator">:</span> <span class="separator">:</span>
{/if} {/if}
{#if expanded} {#if expanded}
@ -238,9 +275,9 @@
contenteditable="true" contenteditable="true"
spellcheck="false" spellcheck="false"
on:input={handleKeyInput} on:input={handleKeyInput}
> on:blur={handleKeyBlur}
{escapedKey} bind:this={domKey}
</div> />
<span class="separator">:</span> <span class="separator">:</span>
{/if} {/if}
<div <div
@ -248,12 +285,12 @@
contenteditable="true" contenteditable="true"
spellcheck="false" spellcheck="false"
on:input={handleValueInput} on:input={handleValueInput}
on:blur={handleValueBlur}
on:click={handleValueClick} on:click={handleValueClick}
on:keydown={handleValueKeyDown} on:keydown={handleValueKeyDown}
bind:this={domValue}
title={valueIsUrl ? 'Ctrl+Click or Ctrl+Enter to open url in new window' : null} title={valueIsUrl ? 'Ctrl+Click or Ctrl+Enter to open url in new window' : null}
> />
{escapedValue}
</div>
</div> </div>
{/if} {/if}
</div> </div>
@ -386,24 +423,23 @@
} }
div.empty { div.empty {
border: 1px dotted lightgray; &:not(:focus) {
border-radius: 2px; outline: 1px dotted lightgray;
padding: 0 $input-padding; -moz-outline-radius: 2px;
line-height: 17px; }
}
&::after {
pointer-events: none;
color: lightgray;
}
div.empty::after, &.key::after {
div.empty::after { content: 'key';
pointer-events: none; }
color: lightgray;
}
div.key.empty::after { &.value::after {
content: 'key'; content: 'value';
} }
div.value.empty::after {
content: 'value';
} }
.key.search, .key.search,