mirror of
https://github.com/leporello-js/leporello-js
synced 2026-01-13 13:04:30 -08:00
link to example
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
import {h, render} from 'https://unpkg.com/preact?module';
|
import {h, render} from 'https://unpkg.com/preact?module';
|
||||||
|
|
||||||
// external
|
|
||||||
import {createApp, handler, connect} from './app.js'
|
import {createApp, handler, connect} from './app.js'
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
|
|||||||
31
index.html
31
index.html
@@ -341,6 +341,37 @@
|
|||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.open_app_window_button {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.open_app_window_tooltip {
|
||||||
|
padding: 1em;
|
||||||
|
position: absolute;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
bottom: 100%;
|
||||||
|
border: none;
|
||||||
|
font-size: 1.7em;
|
||||||
|
background-color: rgb(120 206 247);
|
||||||
|
border-radius: 21px;
|
||||||
|
transform: scale(0);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
}
|
||||||
|
.open_app_window_tooltip.on {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
.open_app_window_tooltip:after {
|
||||||
|
content: '';
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 20px solid transparent;
|
||||||
|
border-right: 20px solid transparent;
|
||||||
|
border-top: 20px solid rgb(120 206 247);
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%);
|
||||||
|
}
|
||||||
|
|
||||||
.options {
|
.options {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import {el} from './domutils.js'
|
import {el} from './domutils.js'
|
||||||
import {map_find} from '../utils.js'
|
import {map_find} from '../utils.js'
|
||||||
import {open_dir, create_file} from '../filesystem.js'
|
import {open_dir, create_file} from '../filesystem.js'
|
||||||
|
import {examples} from '../examples.js'
|
||||||
import {
|
import {
|
||||||
exec,
|
exec,
|
||||||
get_state,
|
get_state,
|
||||||
@@ -219,16 +220,47 @@ export class Files {
|
|||||||
|
|
||||||
on_click(e, file) {
|
on_click(e, file) {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
this.render_current_module(file.path)
|
|
||||||
|
|
||||||
if(file.kind != 'directory') {
|
|
||||||
if(get_state().has_file_system_access) {
|
if(get_state().has_file_system_access) {
|
||||||
|
if(file.kind != 'directory') {
|
||||||
exec('change_current_module', file.path)
|
exec('change_current_module', file.path)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
if(file.path == null) {
|
||||||
|
// root of examples dir, do nothing
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if(file.path == '') {
|
||||||
|
exec('change_entrypoint', '')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const find_node = n =>
|
||||||
|
n.path == file.path
|
||||||
|
||
|
||||||
|
n.children != null && n.children.find(find_node)
|
||||||
|
|
||||||
|
// find example dir
|
||||||
|
const example_dir = get_state().project_dir.children.find(
|
||||||
|
c => find_node(c) != null
|
||||||
|
)
|
||||||
|
|
||||||
// in examples mode, on click file we also change entrypoint for
|
// in examples mode, on click file we also change entrypoint for
|
||||||
// simplicity
|
// simplicity
|
||||||
exec('change_entrypoint', file.path)
|
const example = examples.find(e => e.path == example_dir.path)
|
||||||
|
exec('change_entrypoint', example.entrypoint)
|
||||||
|
if(example.with_app_window && !localStorage.onboarding_open_app_window) {
|
||||||
|
this.ui.toggle_open_app_window_tooltip(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that we call render_current_module AFTER exec('change_entrypoint'),
|
||||||
|
// because in case we clicked to example dir, entrypoint would be set, and
|
||||||
|
// rendered active, so file with entrypoint would be active and not dir we
|
||||||
|
// clicked, which is weird
|
||||||
|
this.render_current_module(file.path)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,11 +88,16 @@ export class UI {
|
|||||||
),
|
),
|
||||||
|
|
||||||
el('a', {
|
el('a', {
|
||||||
'class': 'statusbar_action',
|
'class': 'statusbar_action open_app_window_button',
|
||||||
href: 'javascript: void(0)',
|
href: 'javascript: void(0)',
|
||||||
click: this.open_app_window,
|
click: this.open_app_window,
|
||||||
},
|
},
|
||||||
'(Re)open app window (F7)'
|
'(Re)open app window (F7)',
|
||||||
|
this.open_app_window_tooltip = el('div', {
|
||||||
|
'class': 'open_app_window_tooltip',
|
||||||
|
},
|
||||||
|
'Click here to open app window'
|
||||||
|
)
|
||||||
),
|
),
|
||||||
|
|
||||||
this.options = el('div', 'options',
|
this.options = el('div', 'options',
|
||||||
@@ -211,6 +216,8 @@ export class UI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
open_app_window() {
|
open_app_window() {
|
||||||
|
this.toggle_open_app_window_tooltip(false)
|
||||||
|
localStorage.onboarding_open_app_window = true
|
||||||
open_app_window(get_state())
|
open_app_window(get_state())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,4 +357,8 @@ export class UI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggle_open_app_window_tooltip(on) {
|
||||||
|
this.open_app_window_tooltip.classList.toggle('on', on)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/effects.js
vendored
9
src/effects.js
vendored
@@ -127,9 +127,16 @@ const render_parse_result = (ui, state) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const render_initial_state = (ui, state) => {
|
export const render_initial_state = (ui, state, example) => {
|
||||||
ensure_session(ui, state)
|
ensure_session(ui, state)
|
||||||
ui.editor.switch_session(state.current_module)
|
ui.editor.switch_session(state.current_module)
|
||||||
|
if(
|
||||||
|
example != null
|
||||||
|
&& example.with_app_window
|
||||||
|
&& !localStorage.onboarding_open_app_window
|
||||||
|
) {
|
||||||
|
ui.toggle_open_app_window_tooltip(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const apply_side_effects = (prev, next, command, ui) => {
|
export const apply_side_effects = (prev, next, command, ui) => {
|
||||||
|
|||||||
@@ -6,17 +6,34 @@ const read_example = name => {
|
|||||||
return localStorage['examples_' + name]
|
return localStorage['examples_' + name]
|
||||||
}
|
}
|
||||||
|
|
||||||
const list = [
|
export const examples = [
|
||||||
'github_api/index.js',
|
{
|
||||||
'ethers/block_by_timestamp.js',
|
path: 'github_api',
|
||||||
'ethers/index.js',
|
entrypoint: 'github_api/index.js',
|
||||||
// TODO for html5 example, open run window or hint that it should be opened
|
},
|
||||||
]
|
{
|
||||||
.map(l => l.split('/'))
|
path: 'ethers',
|
||||||
|
entrypoint: 'ethers/block_by_timestamp.js',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'todos-preact',
|
||||||
|
entrypoint: 'todos-preact/index.js',
|
||||||
|
with_app_window: true,
|
||||||
|
files: [
|
||||||
|
'todos-preact/app.js',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
].map(e => ({...e, entrypoint: e.entrypoint ?? e.path}))
|
||||||
|
|
||||||
|
const files_list = examples
|
||||||
|
.map(e => {
|
||||||
|
return (e.files ?? []).concat([e.entrypoint])
|
||||||
|
})
|
||||||
|
.flat()
|
||||||
|
.map(l => l.split('/'))
|
||||||
|
|
||||||
const get_children = path => {
|
const get_children = path => {
|
||||||
const children = list.filter(l => path.every((elem, i) => elem == l[i] ))
|
const children = files_list.filter(l => path.every((elem, i) => elem == l[i] ))
|
||||||
const files = children.filter(c => c.length == path.length + 1)
|
const files = children.filter(c => c.length == path.length + 1)
|
||||||
const dirs = [...new Set(children
|
const dirs = [...new Set(children
|
||||||
.filter(c => c.length != path.length + 1)
|
.filter(c => c.length != path.length + 1)
|
||||||
@@ -46,7 +63,7 @@ const get_children = path => {
|
|||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const examples_promise = get_children([]).then(children => {
|
export const examples_dir_promise = get_children([]).then(children => {
|
||||||
return {
|
return {
|
||||||
kind: 'directory',
|
kind: 'directory',
|
||||||
name: 'examples',
|
name: 'examples',
|
||||||
@@ -54,4 +71,3 @@ export const examples_promise = get_children([]).then(children => {
|
|||||||
children,
|
children,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
49
src/index.js
49
src/index.js
@@ -5,7 +5,7 @@ import {
|
|||||||
close_dir,
|
close_dir,
|
||||||
init_window_service_worker
|
init_window_service_worker
|
||||||
} from './filesystem.js'
|
} from './filesystem.js'
|
||||||
import {examples_promise} from './examples.js'
|
import {examples, examples_dir_promise} from './examples.js'
|
||||||
|
|
||||||
const EXAMPLE = `function fib(n) {
|
const EXAMPLE = `function fib(n) {
|
||||||
if(n == 0 || n == 1) {
|
if(n == 0 || n == 1) {
|
||||||
@@ -141,20 +141,10 @@ export const reload_app_window = state => {
|
|||||||
globalThis.app_window.location = get_html_url(state)
|
globalThis.app_window.location = get_html_url(state)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const get_entrypoint_settings = () => {
|
const get_entrypoint_settings = () => {
|
||||||
|
|
||||||
const params = new URLSearchParams(window.location.search)
|
|
||||||
|
|
||||||
const entrypoint = null
|
|
||||||
?? params.get('entrypoint')
|
|
||||||
?? localStorage.entrypoint
|
|
||||||
?? ''
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
current_module: params.get('entrypoint') ?? localStorage.current_module ?? '',
|
current_module: localStorage.current_module ?? '',
|
||||||
entrypoint,
|
entrypoint: localStorage.entrypoint ?? '',
|
||||||
html_file: localStorage.html_file ?? '',
|
html_file: localStorage.html_file ?? '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,7 +161,7 @@ export const open_directory = () => {
|
|||||||
|
|
||||||
export const close_directory = async () => {
|
export const close_directory = async () => {
|
||||||
close_dir()
|
close_dir()
|
||||||
exec('load_dir', await examples_promise, false, get_entrypoint_settings())
|
exec('load_dir', await examples_dir_promise, false, get_entrypoint_settings())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -185,15 +175,37 @@ export const init = async (container, _COMMANDS) => {
|
|||||||
set_error_handler(window)
|
set_error_handler(window)
|
||||||
|
|
||||||
const default_module = {'': localStorage.code || EXAMPLE}
|
const default_module = {'': localStorage.code || EXAMPLE}
|
||||||
let initial_state
|
let initial_state, entrypoint_settings
|
||||||
const project_dir = await open_dir(false)
|
const project_dir = await open_dir(false)
|
||||||
|
let example
|
||||||
if(project_dir == null) {
|
if(project_dir == null) {
|
||||||
|
/*
|
||||||
|
extract example from URL params and delete it
|
||||||
|
*/
|
||||||
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
const example_path = params.get('example')
|
||||||
|
params.delete('example')
|
||||||
|
globalThis.history.replaceState(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
'/' + params.toString() + window.location.hash
|
||||||
|
)
|
||||||
|
|
||||||
|
example = examples.find(e => e.path == example_path)
|
||||||
|
entrypoint_settings = example == null
|
||||||
|
? get_entrypoint_settings()
|
||||||
|
: {
|
||||||
|
current_module: example.entrypoint,
|
||||||
|
entrypoint: example.entrypoint,
|
||||||
|
}
|
||||||
|
|
||||||
initial_state = {
|
initial_state = {
|
||||||
project_dir: await examples_promise,
|
project_dir: await examples_dir_promise,
|
||||||
files: default_module,
|
files: default_module,
|
||||||
has_file_system_access: false,
|
has_file_system_access: false,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
entrypoint_settings = get_entrypoint_settings()
|
||||||
initial_state = {
|
initial_state = {
|
||||||
project_dir,
|
project_dir,
|
||||||
files: default_module,
|
files: default_module,
|
||||||
@@ -206,8 +218,7 @@ export const init = async (container, _COMMANDS) => {
|
|||||||
...initial_state,
|
...initial_state,
|
||||||
on_deferred_call: (...args) => exec('on_deferred_call', ...args)
|
on_deferred_call: (...args) => exec('on_deferred_call', ...args)
|
||||||
},
|
},
|
||||||
|
entrypoint_settings,
|
||||||
get_entrypoint_settings(),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Expose state for debugging
|
// Expose state for debugging
|
||||||
@@ -216,7 +227,7 @@ export const init = async (container, _COMMANDS) => {
|
|||||||
// Expose for debugging
|
// Expose for debugging
|
||||||
globalThis.__ui = ui
|
globalThis.__ui = ui
|
||||||
|
|
||||||
render_initial_state(ui, state)
|
render_initial_state(ui, state, example)
|
||||||
|
|
||||||
open_run_iframe(state)
|
open_run_iframe(state)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user