@wrnrlr/prelude 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +16 -0
- package/LICENSE +1 -0
- package/deno.json +23 -0
- package/example/counter.html +24 -0
- package/example/greeting.html +25 -0
- package/example/paint.html +22 -0
- package/example/show.html +18 -0
- package/example/todo.html +70 -0
- package/index.html +230 -0
- package/package.json +12 -0
- package/presets.css +284 -0
- package/public/banner.svg +6 -0
- package/public/logo.svg +5 -0
- package/readme.md +86 -0
- package/src/canvas.js +114 -0
- package/src/components.js +20 -0
- package/src/constants.ts +515 -0
- package/src/controlflow.js +163 -0
- package/src/hyperscript.ts +237 -0
- package/src/mod.ts +45 -0
- package/src/reactive.ts +359 -0
- package/src/runtime.ts +434 -0
- package/test/hyperscript.js +102 -0
- package/test/reactive.js +98 -0
- package/test/runtime.js +7 -0
- package/typedoc.jsonc +31 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
name: Publish
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- main
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
publish:
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
permissions:
|
12
|
+
contents: read
|
13
|
+
id-token: write # The OIDC ID token is used for authentication with JSR.
|
14
|
+
steps:
|
15
|
+
- uses: actions/checkout@v4
|
16
|
+
- run: npx jsr publish
|
package/LICENSE
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
All that is not forbidden is allowed, and nothing more is implied.
|
package/deno.json
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
{
|
2
|
+
"name": "@wrnrlr/prelude",
|
3
|
+
"version": "0.1.0",
|
4
|
+
"exports": "./src/mod.ts",
|
5
|
+
"compilerOptions": {
|
6
|
+
"strict": true,
|
7
|
+
"checkJs": false,
|
8
|
+
"noImplicitThis": false,
|
9
|
+
"lib": ["dom","dom.iterable","dom.asynciterable","deno.ns"]
|
10
|
+
},
|
11
|
+
"imports": {
|
12
|
+
"@std/assert": "jsr:@std/assert@^1.0.0",
|
13
|
+
"@std/testing": "jsr:@std/testing@^0.225.3",
|
14
|
+
"typedoc": "npm:typedoc",
|
15
|
+
"vite": "npm:vite@^5.4.2",
|
16
|
+
"typedoc-plugin-markdown": "npm:typedoc-plugin-markdown"
|
17
|
+
},
|
18
|
+
"tasks": {
|
19
|
+
"dev": "deno run -A npm:vite@^5.4.2",
|
20
|
+
"test": "deno test -A --unstable-sloppy-imports ./test/*.[jt]s",
|
21
|
+
"docs": "deno run -A npm:typedoc "
|
22
|
+
}
|
23
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Counter</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,render} from '../src/mod.ts'
|
7
|
+
|
8
|
+
function Input(props) {
|
9
|
+
const onInput = e => props.value(parseInt(e.target.value))
|
10
|
+
return h('input',{type:'number',value:props.value,onInput})
|
11
|
+
}
|
12
|
+
|
13
|
+
function App() {
|
14
|
+
const count = signal(1)
|
15
|
+
return [
|
16
|
+
h('button',{onClick(e){count(i=>i-1)}},'-'),
|
17
|
+
h(Input,{value:()=>count}),
|
18
|
+
h('button',{onClick(e){count(i=>i+1)}},'+')
|
19
|
+
]
|
20
|
+
}
|
21
|
+
|
22
|
+
render(App, document.body)
|
23
|
+
|
24
|
+
</script>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Counter</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,effect,render} from '../src/mod.ts'
|
7
|
+
|
8
|
+
function Input(props) {
|
9
|
+
const onInput = e => props.value(e.target.value)
|
10
|
+
return h('input',{value:props.value,onInput})
|
11
|
+
}
|
12
|
+
|
13
|
+
function Greeting(props) {
|
14
|
+
return h("div", {}, ["Hello ",props.name])
|
15
|
+
}
|
16
|
+
|
17
|
+
function App() {
|
18
|
+
const name = signal('Alice')
|
19
|
+
return h('h1','a')
|
20
|
+
}
|
21
|
+
|
22
|
+
const name = signal('Alice')
|
23
|
+
render(h('h1','a'),document.body)
|
24
|
+
|
25
|
+
</script>
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Counter</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,render,Canvas,Stroke,Line} from '../src/mod.ts'
|
7
|
+
|
8
|
+
function App() {
|
9
|
+
const count = signal(1)
|
10
|
+
return h(Canvas, h(Line, [0, 0, 200, 200]))
|
11
|
+
}
|
12
|
+
|
13
|
+
render(App, document.body)
|
14
|
+
|
15
|
+
</script>
|
16
|
+
|
17
|
+
<style>
|
18
|
+
body {
|
19
|
+
width:100%; height:100%;
|
20
|
+
background-color: grey;
|
21
|
+
}
|
22
|
+
</style>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Counter</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,render,Show} from '../src/mod.ts'
|
7
|
+
|
8
|
+
function App(props) {
|
9
|
+
const toggle = signal(true)
|
10
|
+
return [
|
11
|
+
h('button',{onClick:e=>toggle(v=>!v)},'toggle'),
|
12
|
+
h(Show,{when:toggle,fallback:'Ho'},'Hey')
|
13
|
+
]
|
14
|
+
}
|
15
|
+
|
16
|
+
render(App,document.body)
|
17
|
+
|
18
|
+
</script>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Todo</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,effect,batch,wrap,render,List} from '../src/mod.ts'
|
7
|
+
|
8
|
+
const data = [
|
9
|
+
{description:'Buy groceries',done:false},
|
10
|
+
{description:'Clean car',done:false},
|
11
|
+
{description:'File taxes',done:true},
|
12
|
+
]
|
13
|
+
|
14
|
+
function Input(props) {
|
15
|
+
return h('input',{
|
16
|
+
value:props.value,
|
17
|
+
onInput:e => props.value(e.target.value),
|
18
|
+
tabindex:props.tabindex
|
19
|
+
})
|
20
|
+
}
|
21
|
+
|
22
|
+
function Checkbox(props) {
|
23
|
+
return h('input',{
|
24
|
+
type:'checkbox',
|
25
|
+
checked:props.value,
|
26
|
+
onInput:e => props.value(e.target.checked)
|
27
|
+
})
|
28
|
+
}
|
29
|
+
|
30
|
+
function TodoItem(props) {
|
31
|
+
const description = wrap(props.value,'description')
|
32
|
+
const done = wrap(props.value,'done')
|
33
|
+
return h('li', [
|
34
|
+
h(Checkbox,{value:()=>done}),
|
35
|
+
h(Input,{value:()=>description}),
|
36
|
+
h('button',{onClick:props.onDelete},'\u2715')
|
37
|
+
])
|
38
|
+
}
|
39
|
+
|
40
|
+
function App() {
|
41
|
+
const name = signal('')
|
42
|
+
const todos = signal(data)
|
43
|
+
function onSubmit(e) {
|
44
|
+
e.preventDefault()
|
45
|
+
todos(v => v.toSpliced(v.length, 0, {description:name(), done:false}))
|
46
|
+
name('')
|
47
|
+
}
|
48
|
+
const remove = i => e => todos(v => v.toSpliced(i(),1))
|
49
|
+
return [
|
50
|
+
h('h3','Todo'),
|
51
|
+
h('ul',[h(List, {each:()=>todos}, (value,i) =>
|
52
|
+
h(TodoItem, {
|
53
|
+
value:()=>value,
|
54
|
+
onDelete:_=>todos(v => v.toSpliced(i(),1))})
|
55
|
+
)]),
|
56
|
+
h('form',{onSubmit},[h(Input,{value:()=>name,tabindex:1})]),
|
57
|
+
// h('pre.hello',()=>'todos: '+JSON.stringify(todos(),undefined,2))
|
58
|
+
]
|
59
|
+
}
|
60
|
+
|
61
|
+
render(App, document.body)
|
62
|
+
|
63
|
+
</script>
|
64
|
+
|
65
|
+
<style>
|
66
|
+
ul {margin:0;padding:0}
|
67
|
+
ul,li {list-style-type: none}
|
68
|
+
input {border-width: 0 0 1px 0; margin:0.25em}
|
69
|
+
li>button {border:none; background:none}
|
70
|
+
</style>
|
package/index.html
ADDED
@@ -0,0 +1,230 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<title>Counter</title>
|
4
|
+
<link href="https://unpkg.com/prismjs@1.29.0/themes/prism.css" rel="stylesheet" />
|
5
|
+
<script src="https://unpkg.com/prismjs@1.29.0/components/prism-core.min.js"></script>
|
6
|
+
<script src="https://unpkg.com/prismjs@1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
7
|
+
</head>
|
8
|
+
|
9
|
+
<nav style="display: flex; justify-content: space-between; padding: 0.25rem 0.5rem; align-items: center;">
|
10
|
+
<div>
|
11
|
+
<img src="./banner.svg" alt="PreludeJS Logo" style="height: 3rem;">
|
12
|
+
</div>
|
13
|
+
<div style="display: flex; gap: 0.75rem;">
|
14
|
+
<a href="/docs/index.html">Get Started</a>
|
15
|
+
<a href="/docs/modules.html">API</a>
|
16
|
+
<a href="#examples">Examples</a>
|
17
|
+
<a href="https://github.com/wrnrlr/prelude">Github</a>
|
18
|
+
</div>
|
19
|
+
</nav>
|
20
|
+
|
21
|
+
<section class="jumbo">
|
22
|
+
<img class="logo" src="./logo.svg" alt="PreludeJS Logo" style="width: 300px">
|
23
|
+
<div style="width: 900px">
|
24
|
+
<h1>A <em>signal</em> based frontend library with fine-grained reactivity</h1>
|
25
|
+
<h2>Prelude lets you develop web application in a familiar component-based functional style</h2>
|
26
|
+
<p></p>
|
27
|
+
<div style="display: flex; gap: 0.75rem;">
|
28
|
+
<a href="/docs/index.html">Learn More</a>
|
29
|
+
<a href="/docs/modules.html">API Reference</a>
|
30
|
+
</div>
|
31
|
+
</div>
|
32
|
+
</section>
|
33
|
+
|
34
|
+
<section>
|
35
|
+
<div style="width: 900px">
|
36
|
+
<h1></h1>
|
37
|
+
<p>Prelude works directly in the browser using just vanilla JavaScript
|
38
|
+
but for more complex applications it can also be writen in TypeScript and used together with a bundler like Vite.</p>
|
39
|
+
<pre><code class="language-js">
|
40
|
+
import {h, signal, render} from 'https://esm.sh/wrnrlr/prelude'
|
41
|
+
|
42
|
+
const count = signal(1)
|
43
|
+
|
44
|
+
function Counter(props) {
|
45
|
+
const onClick = e => props.count(i => i+1)
|
46
|
+
return [
|
47
|
+
props.value,
|
48
|
+
h('button', {value:props.value, onClick})
|
49
|
+
]
|
50
|
+
}
|
51
|
+
|
52
|
+
render(h(Counter, {value:signal})), document.body)
|
53
|
+
</code></pre>
|
54
|
+
<p>Try out this example for yourself <a href="http://codepen.io">here!</a></p>
|
55
|
+
</div>
|
56
|
+
</section>
|
57
|
+
|
58
|
+
<section>
|
59
|
+
<div class="wide wrapper">
|
60
|
+
<h3>Signals with two-way data-binding</h3>
|
61
|
+
<h2></h2>
|
62
|
+
<p>Signals are nothing more then a function that allows you to both get and set a value.</p>
|
63
|
+
<pre><code class="language-javascript">
|
64
|
+
const a = signal(1)
|
65
|
+
a() // 1
|
66
|
+
a(2) // 2
|
67
|
+
</code></pre>
|
68
|
+
<p>Signals can be combined with other functions </p>
|
69
|
+
<h3>Hyperscript: A DSL for HTML</h3>
|
70
|
+
<p>Simple tags components</p>
|
71
|
+
<pre><code class="language-javascript">
|
72
|
+
h('h1', 'Hello World!')
|
73
|
+
</code></pre>
|
74
|
+
<p>Higher order components</p>
|
75
|
+
<pre><code class="language-javascript">
|
76
|
+
function Greet(props) {
|
77
|
+
return h('h1', ['Hello', props.children])
|
78
|
+
}
|
79
|
+
|
80
|
+
h(Greet, 'Bob')
|
81
|
+
</code></pre>
|
82
|
+
</div>
|
83
|
+
</section>
|
84
|
+
|
85
|
+
<section id="examples" style="padding: 2rem; display: flex; flex-direction: column; justify-content: center;">
|
86
|
+
<div style="width: 900px">
|
87
|
+
<h1>Check out our examples yourself</h1>
|
88
|
+
<h2>See how easy it is to developer a variaty of different web applications with Prelude.</h2>
|
89
|
+
</div>
|
90
|
+
<ul style="display: flex; gap: 1.5rem; list-style: none">
|
91
|
+
<li>
|
92
|
+
<a href="./example/counter.html" class="card">
|
93
|
+
<div class="title">Counter</div>
|
94
|
+
<iframe class="example" title="Counter" src="./example/counter.html" scrolling="no"></iframe>
|
95
|
+
</a>
|
96
|
+
</li>
|
97
|
+
<li>
|
98
|
+
<a href="./example/todo.html" class="card">
|
99
|
+
<div class="title">Todo</div>
|
100
|
+
<iframe class="example" title="Todo" src="./example/todo.html" scrolling="no"></iframe>
|
101
|
+
</a>
|
102
|
+
</li>
|
103
|
+
<li>
|
104
|
+
<a href="./example/form.html" class="card">
|
105
|
+
<div class="title">Form</div>
|
106
|
+
<iframe class="example" title="Form" src="./example/form.html" scrolling="no"></iframe>
|
107
|
+
</a>
|
108
|
+
</li>
|
109
|
+
</ul>
|
110
|
+
<div style="width: 900px">
|
111
|
+
<p><a href="">All Examples</a></p>
|
112
|
+
</div>
|
113
|
+
</section>
|
114
|
+
|
115
|
+
<section>
|
116
|
+
<div style="width: 900px">
|
117
|
+
<h1>Welcome to our community!</h1>
|
118
|
+
<h2>Free and Open Source Software</h2>
|
119
|
+
<ul>
|
120
|
+
<li>Only 10kb minified js, small bundle size for a lot of power</li>
|
121
|
+
<li>Typescript support</li>
|
122
|
+
<li>Fully documented</li>
|
123
|
+
<li>Extensivaly tested</li>
|
124
|
+
<li>No seperate build process requires, works directly in the browser</li>
|
125
|
+
</ul>
|
126
|
+
</div>
|
127
|
+
</section>
|
128
|
+
|
129
|
+
<section style="display:flex; flex-direction:row; padding: 2rem; background-color: white">
|
130
|
+
<div style="display: flex; flex-direction:column; flex-grow: 2; align-self: stretch; justify-content: space-between">
|
131
|
+
<div style="display: flex; gap: 0.75rem">
|
132
|
+
<a href="/">Home</a>
|
133
|
+
<a href="/docs/index.html">Quick Guide</a>
|
134
|
+
<a href="/docs/modules.html">API Reference</a>
|
135
|
+
</div>
|
136
|
+
<div style="display: flex; gap: 0.75rem">
|
137
|
+
<a href="https://github.com/wrnrlr/prelude">Github</a>
|
138
|
+
<a href="https://npm.com/@wrnrlr/prelude">NPM</a>
|
139
|
+
<a href="https://jsr.io/@wrnrlr/prelude">JSR</a>
|
140
|
+
</div>
|
141
|
+
<div>Some Right Reserved © 2024</div>
|
142
|
+
</div>
|
143
|
+
<img class="emblem" src="./logo.svg" style="width: 100px">
|
144
|
+
</section>
|
145
|
+
|
146
|
+
<style >
|
147
|
+
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Warang+Citi&display=swap');
|
148
|
+
@import url('/presets.css')
|
149
|
+
|
150
|
+
html {
|
151
|
+
font-size: 20px;
|
152
|
+
font-family: "Noto Sans Warang Citi", sans-serif;
|
153
|
+
font-weight: 400;
|
154
|
+
font-style: normal;
|
155
|
+
margin: 0;
|
156
|
+
display: flex;
|
157
|
+
width: 100%;
|
158
|
+
height: 100%
|
159
|
+
}
|
160
|
+
|
161
|
+
body {
|
162
|
+
margin: 0;
|
163
|
+
flex-grow: 2;
|
164
|
+
width: 100%;
|
165
|
+
height: 100%;
|
166
|
+
}
|
167
|
+
|
168
|
+
a { color: dodgerblue; }
|
169
|
+
a:visited { color: dodgerblue; }
|
170
|
+
|
171
|
+
h1 { font-size: 2.5rem; color: var(--slate-900); font-weight: bolder }
|
172
|
+
h2 { font-size: 2rem; color: var(--slate-700); line-height: 1.5 }
|
173
|
+
|
174
|
+
section {
|
175
|
+
padding: 2rem;
|
176
|
+
display: flex;
|
177
|
+
align-items: center;
|
178
|
+
justify-content: center;
|
179
|
+
flex-direction: column;
|
180
|
+
}
|
181
|
+
section:nth-child(even) { background-color: var(--neutral-100); }
|
182
|
+
section:nth-child(odd) { background-color: var(--neutral-200); }
|
183
|
+
|
184
|
+
section > .wrapper { width: 700px }
|
185
|
+
section > .wide.wrapper { width: 900px }
|
186
|
+
|
187
|
+
/* Top Navigation */
|
188
|
+
|
189
|
+
iframe.example {
|
190
|
+
zoom: 1.0;
|
191
|
+
width: 100%;
|
192
|
+
height:100%;
|
193
|
+
border: none;
|
194
|
+
overflow: hidden;
|
195
|
+
background-color: var(--neutral-50)
|
196
|
+
}
|
197
|
+
|
198
|
+
/* iframe.example > a {
|
199
|
+
position: absolute;
|
200
|
+
top: 0;
|
201
|
+
left: 0;
|
202
|
+
width: 100%;
|
203
|
+
height: 100%;
|
204
|
+
background: rgba(0.3, 1, 0, 0.0);
|
205
|
+
cursor: pointer;
|
206
|
+
} */
|
207
|
+
|
208
|
+
.card {
|
209
|
+
width: 20rem;
|
210
|
+
height: 16rem;
|
211
|
+
display: flex;
|
212
|
+
flex-direction: column;
|
213
|
+
/* border: 1px solid var(--neutral-400); */
|
214
|
+
/* border-radius: 0.5em 0.5em 0 0; */
|
215
|
+
}
|
216
|
+
|
217
|
+
.card > .title {
|
218
|
+
font-weight: bold;
|
219
|
+
color: var(--gray-700);
|
220
|
+
padding: 0.5em;
|
221
|
+
background-color: var(--gray-300);
|
222
|
+
border-radius: 0.5em 0.5em 0 0;
|
223
|
+
/* border-bottom: 1px solid var(--gray-400); */
|
224
|
+
}
|
225
|
+
|
226
|
+
a.card {
|
227
|
+
text-decoration: none;
|
228
|
+
}
|
229
|
+
|
230
|
+
</style>
|
package/package.json
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
{
|
2
|
+
"name": "@wrnrlr/prelude",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"author": "Werner Laurensse",
|
5
|
+
"description": "A signal based frontend library with fine-grained reactivity",
|
6
|
+
"main": "./src/mod.ts",
|
7
|
+
"directories": {
|
8
|
+
"example": "example",
|
9
|
+
"test": "test"
|
10
|
+
},
|
11
|
+
"scripts": {}
|
12
|
+
}
|