mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 21:14:28 -08:00
show dom elements in value explorer
This commit is contained in:
16
docs/examples/plot/index.js
Normal file
16
docs/examples/plot/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import _ from 'https://unpkg.com/lodash-es'
|
||||||
|
|
||||||
|
const url = 'https://api.github.com/search/repositories?q=stars:%3E1&sort=stars'
|
||||||
|
const resp = await fetch(url)
|
||||||
|
const repos = await resp.json()
|
||||||
|
const langs = _(repos.items)
|
||||||
|
.map(r => r.language)
|
||||||
|
.filter(l => l != null)
|
||||||
|
.countBy()
|
||||||
|
.toPairs()
|
||||||
|
.map(([language, count]) => ({language, count}))
|
||||||
|
|
||||||
|
import {barY} from "https://cdn.jsdelivr.net/npm/@observablehq/plot@0.6/+esm";
|
||||||
|
|
||||||
|
barY(langs, {x: "language", y: "count", sort: {x: "y", reverse: true}, fill: 'purple'})
|
||||||
|
.plot()
|
||||||
11
index.html
11
index.html
@@ -266,16 +266,23 @@
|
|||||||
|
|
||||||
/* value_explorer */
|
/* value_explorer */
|
||||||
|
|
||||||
.embed_value_explorer_container {
|
.embed_value_explorer_container.is_not_dom_el {
|
||||||
height: 0px;
|
height: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.embed_value_explorer_container.is_dom_el {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.embed_value_explorer_wrapper {
|
.embed_value_explorer_wrapper {
|
||||||
margin-left: 1em;
|
|
||||||
/* preserve wrapper from getting clicks for code line left to it */
|
/* preserve wrapper from getting clicks for code line left to it */
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.embed_value_explorer_container.is_not_dom_el .embed_value_explorer_wrapper {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.embed_value_explorer_content {
|
.embed_value_explorer_content {
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|||||||
@@ -169,10 +169,33 @@ export class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unembed_value_explorer() {
|
unembed_value_explorer() {
|
||||||
if(this.widget != null) {
|
if(this.widget == null) {
|
||||||
this.ace_editor.getSession().widgetManager.removeLineWidget(this.widget)
|
return
|
||||||
this.widget = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const session = this.ace_editor.getSession()
|
||||||
|
const widget_bottom = this.widget.el.getBoundingClientRect().bottom
|
||||||
|
session.widgetManager.removeLineWidget(this.widget)
|
||||||
|
|
||||||
|
if(this.widget.is_dom_el) {
|
||||||
|
/*
|
||||||
|
if cursor moves below widget, then ace editor first adjusts scroll,
|
||||||
|
and then widget gets remove, so scroll jerks. We have to set scroll
|
||||||
|
back
|
||||||
|
*/
|
||||||
|
// distance travelled by cursor
|
||||||
|
const distance = session.selection.getCursor().row - this.widget.row
|
||||||
|
if(distance > 0) {
|
||||||
|
const line_height = this.ace_editor.renderer.lineHeight
|
||||||
|
const scroll = widget_bottom - this.editor_container.getBoundingClientRect().bottom
|
||||||
|
if(scroll > 0) {
|
||||||
|
const scrollTop = session.getScrollTop()
|
||||||
|
session.setScrollTop(session.getScrollTop() - scroll - line_height*distance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.widget = null
|
||||||
}
|
}
|
||||||
|
|
||||||
update_value_explorer_margin() {
|
update_value_explorer_margin() {
|
||||||
@@ -203,10 +226,6 @@ export class Editor {
|
|||||||
this.unembed_value_explorer()
|
this.unembed_value_explorer()
|
||||||
|
|
||||||
const session = this.ace_editor.getSession()
|
const session = this.ace_editor.getSession()
|
||||||
const pos = session.doc.indexToPosition(index)
|
|
||||||
const row = pos.row
|
|
||||||
|
|
||||||
const line_height = this.ace_editor.renderer.lineHeight
|
|
||||||
|
|
||||||
let content
|
let content
|
||||||
const container = el('div', {'class': 'embed_value_explorer_container'},
|
const container = el('div', {'class': 'embed_value_explorer_container'},
|
||||||
@@ -214,7 +233,6 @@ export class Editor {
|
|||||||
content = el('div', {
|
content = el('div', {
|
||||||
// Ace editor cannot render widget before the first line. So we
|
// Ace editor cannot render widget before the first line. So we
|
||||||
// render in on the next line and apply translate
|
// render in on the next line and apply translate
|
||||||
'style': `transform: translate(0px, -${line_height}px)`,
|
|
||||||
'class': 'embed_value_explorer_content',
|
'class': 'embed_value_explorer_content',
|
||||||
tabindex: 0
|
tabindex: 0
|
||||||
})
|
})
|
||||||
@@ -242,37 +260,49 @@ export class Editor {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if(ok) {
|
let is_dom_el
|
||||||
const exp = new ValueExplorer({
|
|
||||||
container: content,
|
|
||||||
event_target: container,
|
|
||||||
on_escape: escape,
|
|
||||||
scroll_to_element: t => {
|
|
||||||
if(initial_scroll_top == null) {
|
|
||||||
initial_scroll_top = session.getScrollTop()
|
|
||||||
}
|
|
||||||
let scroll
|
|
||||||
const out_of_bottom = t.getBoundingClientRect().bottom - this.editor_container.getBoundingClientRect().bottom
|
|
||||||
if(out_of_bottom > 0) {
|
|
||||||
session.setScrollTop(session.getScrollTop() + out_of_bottom)
|
|
||||||
}
|
|
||||||
const out_of_top = this.editor_container.getBoundingClientRect().top - t.getBoundingClientRect().top
|
|
||||||
if(out_of_top > 0) {
|
|
||||||
session.setScrollTop(session.getScrollTop() - out_of_top)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
exp.render(value)
|
if(ok) {
|
||||||
|
if(value instanceof globalThis.app_window.Element && !value.isConnected) {
|
||||||
|
is_dom_el = true
|
||||||
|
content.appendChild(value)
|
||||||
|
} else {
|
||||||
|
is_dom_el = false
|
||||||
|
const exp = new ValueExplorer({
|
||||||
|
container: content,
|
||||||
|
event_target: container,
|
||||||
|
on_escape: escape,
|
||||||
|
scroll_to_element: t => {
|
||||||
|
if(initial_scroll_top == null) {
|
||||||
|
initial_scroll_top = session.getScrollTop()
|
||||||
|
}
|
||||||
|
let scroll
|
||||||
|
const out_of_bottom = t.getBoundingClientRect().bottom - this.editor_container.getBoundingClientRect().bottom
|
||||||
|
if(out_of_bottom > 0) {
|
||||||
|
session.setScrollTop(session.getScrollTop() + out_of_bottom)
|
||||||
|
}
|
||||||
|
const out_of_top = this.editor_container.getBoundingClientRect().top - t.getBoundingClientRect().top
|
||||||
|
if(out_of_top > 0) {
|
||||||
|
session.setScrollTop(session.getScrollTop() - out_of_top)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
exp.render(value)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
is_dom_el = false
|
||||||
content.appendChild(el('span', 'eval_error', stringify_for_header(error)))
|
content.appendChild(el('span', 'eval_error', stringify_for_header(error)))
|
||||||
}
|
}
|
||||||
|
|
||||||
const widget = this.widget = {
|
const widget = this.widget = {
|
||||||
row,
|
row: is_dom_el
|
||||||
|
? session.doc.indexToPosition(index + length).row
|
||||||
|
: session.doc.indexToPosition(index).row,
|
||||||
fixedWidth: true,
|
fixedWidth: true,
|
||||||
el: container,
|
el: container,
|
||||||
content,
|
content,
|
||||||
|
is_dom_el,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -282,13 +312,22 @@ export class Editor {
|
|||||||
session.widgetManager.attach(this.ace_editor);
|
session.widgetManager.attach(this.ace_editor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update_value_explorer_margin relies on getLastVisibleRow which can be
|
if(is_dom_el) {
|
||||||
// incorrect because it may be executed right after set_cursor_position
|
container.classList.add('is_dom_el')
|
||||||
// which is async in ace_editor. Use setTimeout
|
|
||||||
setTimeout(() => {
|
|
||||||
this.update_value_explorer_margin()
|
|
||||||
session.widgetManager.addLineWidget(widget)
|
session.widgetManager.addLineWidget(widget)
|
||||||
}, 0)
|
} else {
|
||||||
|
container.classList.add('is_not_dom_el')
|
||||||
|
const line_height = this.ace_editor.renderer.lineHeight
|
||||||
|
content.style.transform = `translate(0px, -${line_height}px)`
|
||||||
|
// update_value_explorer_margin relies on getLastVisibleRow which can be
|
||||||
|
// incorrect because it may be executed right after set_cursor_position
|
||||||
|
// which is async in ace_editor. Use setTimeout
|
||||||
|
setTimeout(() => {
|
||||||
|
this.update_value_explorer_margin()
|
||||||
|
session.widgetManager.addLineWidget(widget)
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
focus_value_explorer(return_to) {
|
focus_value_explorer(return_to) {
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ export const examples = [
|
|||||||
path: 'ethers',
|
path: 'ethers',
|
||||||
entrypoint: 'ethers/block_by_timestamp.js',
|
entrypoint: 'ethers/block_by_timestamp.js',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'plot',
|
||||||
|
entrypoint: 'plot/index.js',
|
||||||
|
},
|
||||||
].map(e => ({...e, entrypoint: e.entrypoint ?? e.path}))
|
].map(e => ({...e, entrypoint: e.entrypoint ?? e.path}))
|
||||||
|
|
||||||
const files_list = examples
|
const files_list = examples
|
||||||
|
|||||||
Reference in New Issue
Block a user