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';
|
||||
|
||||
// external
|
||||
import {createApp, handler, connect} from './app.js'
|
||||
|
||||
// Components
|
||||
|
||||
31
index.html
31
index.html
@@ -341,6 +341,37 @@
|
||||
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 {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import {el} from './domutils.js'
|
||||
import {map_find} from '../utils.js'
|
||||
import {open_dir, create_file} from '../filesystem.js'
|
||||
import {examples} from '../examples.js'
|
||||
import {
|
||||
exec,
|
||||
get_state,
|
||||
@@ -219,16 +220,47 @@ export class Files {
|
||||
|
||||
on_click(e, file) {
|
||||
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)
|
||||
} else {
|
||||
// in examples mode, on click file we also change entrypoint for
|
||||
// simplicity
|
||||
exec('change_entrypoint', file.path)
|
||||
}
|
||||
} 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
|
||||
// simplicity
|
||||
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', {
|
||||
'class': 'statusbar_action',
|
||||
'class': 'statusbar_action open_app_window_button',
|
||||
href: 'javascript: void(0)',
|
||||
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',
|
||||
@@ -211,6 +216,8 @@ export class UI {
|
||||
}
|
||||
|
||||
open_app_window() {
|
||||
this.toggle_open_app_window_tooltip(false)
|
||||
localStorage.onboarding_open_app_window = true
|
||||
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)
|
||||
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) => {
|
||||
|
||||
@@ -6,17 +6,34 @@ const read_example = name => {
|
||||
return localStorage['examples_' + name]
|
||||
}
|
||||
|
||||
const list = [
|
||||
'github_api/index.js',
|
||||
'ethers/block_by_timestamp.js',
|
||||
'ethers/index.js',
|
||||
// TODO for html5 example, open run window or hint that it should be opened
|
||||
]
|
||||
.map(l => l.split('/'))
|
||||
export const examples = [
|
||||
{
|
||||
path: 'github_api',
|
||||
entrypoint: 'github_api/index.js',
|
||||
},
|
||||
{
|
||||
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 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 dirs = [...new Set(children
|
||||
.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 {
|
||||
kind: 'directory',
|
||||
name: 'examples',
|
||||
@@ -54,4 +71,3 @@ export const examples_promise = get_children([]).then(children => {
|
||||
children,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
49
src/index.js
49
src/index.js
@@ -5,7 +5,7 @@ import {
|
||||
close_dir,
|
||||
init_window_service_worker
|
||||
} from './filesystem.js'
|
||||
import {examples_promise} from './examples.js'
|
||||
import {examples, examples_dir_promise} from './examples.js'
|
||||
|
||||
const EXAMPLE = `function fib(n) {
|
||||
if(n == 0 || n == 1) {
|
||||
@@ -141,20 +141,10 @@ export const reload_app_window = state => {
|
||||
globalThis.app_window.location = get_html_url(state)
|
||||
}
|
||||
|
||||
|
||||
|
||||
const get_entrypoint_settings = () => {
|
||||
|
||||
const params = new URLSearchParams(window.location.search)
|
||||
|
||||
const entrypoint = null
|
||||
?? params.get('entrypoint')
|
||||
?? localStorage.entrypoint
|
||||
?? ''
|
||||
|
||||
return {
|
||||
current_module: params.get('entrypoint') ?? localStorage.current_module ?? '',
|
||||
entrypoint,
|
||||
current_module: localStorage.current_module ?? '',
|
||||
entrypoint: localStorage.entrypoint ?? '',
|
||||
html_file: localStorage.html_file ?? '',
|
||||
}
|
||||
}
|
||||
@@ -171,7 +161,7 @@ export const open_directory = () => {
|
||||
|
||||
export const close_directory = async () => {
|
||||
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)
|
||||
|
||||
const default_module = {'': localStorage.code || EXAMPLE}
|
||||
let initial_state
|
||||
let initial_state, entrypoint_settings
|
||||
const project_dir = await open_dir(false)
|
||||
let example
|
||||
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 = {
|
||||
project_dir: await examples_promise,
|
||||
project_dir: await examples_dir_promise,
|
||||
files: default_module,
|
||||
has_file_system_access: false,
|
||||
}
|
||||
} else {
|
||||
entrypoint_settings = get_entrypoint_settings()
|
||||
initial_state = {
|
||||
project_dir,
|
||||
files: default_module,
|
||||
@@ -206,8 +218,7 @@ export const init = async (container, _COMMANDS) => {
|
||||
...initial_state,
|
||||
on_deferred_call: (...args) => exec('on_deferred_call', ...args)
|
||||
},
|
||||
|
||||
get_entrypoint_settings(),
|
||||
entrypoint_settings,
|
||||
)
|
||||
|
||||
// Expose state for debugging
|
||||
@@ -216,7 +227,7 @@ export const init = async (container, _COMMANDS) => {
|
||||
// Expose for debugging
|
||||
globalThis.__ui = ui
|
||||
|
||||
render_initial_state(ui, state)
|
||||
render_initial_state(ui, state, example)
|
||||
|
||||
open_run_iframe(state)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user