@wrnrlr/prelude 0.1.7 → 0.1.9
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/deno.json +1 -1
- package/package.json +1 -1
- package/readme.md +21 -0
- package/src/controlflow.ts +2 -2
- package/src/mod.ts +2 -2
- package/src/ui/accordion.ts +8 -0
- package/src/ui/button.ts +9 -0
- package/src/ui/canvas.ts +8 -0
- package/src/ui/date.ts +8 -0
- package/src/ui/dialog.ts +12 -0
- package/src/ui/filter.ts +8 -0
- package/src/ui/form.ts +7 -0
- package/src/ui/h.ts +48 -0
- package/src/ui/image.ts +5 -0
- package/src/ui/input.ts +49 -0
- package/src/ui/mod.ts +12 -0
- package/src/ui/multiselect.ts +42 -0
- package/src/ui/option.ts +1 -0
- package/src/ui/select.ts +28 -0
- package/src/ui/tab.ts +7 -0
- package/src/ui/table.ts +9 -0
- package/src/ui/upload.ts +7 -0
- package/www/playground.html +2 -1
- package/www/public/example/select.html +27 -0
- package/www/ui.html +49 -0
package/deno.json
CHANGED
package/package.json
CHANGED
package/readme.md
CHANGED
@@ -181,6 +181,27 @@ h(Router,[
|
|
181
181
|
])
|
182
182
|
```
|
183
183
|
|
184
|
+
## Developer Information
|
185
|
+
|
186
|
+
Only Deno is required to run the development server.
|
187
|
+
|
188
|
+
Install Dependencies
|
189
|
+
```sh
|
190
|
+
deno install
|
191
|
+
```
|
192
|
+
|
193
|
+
Start Development server
|
194
|
+
|
195
|
+
```sh
|
196
|
+
deno task dev
|
197
|
+
```
|
198
|
+
|
199
|
+
Run tests
|
200
|
+
|
201
|
+
```sh
|
202
|
+
deno task test
|
203
|
+
```
|
204
|
+
|
184
205
|
## Learn More
|
185
206
|
|
186
207
|
* [API Reference]()
|
package/src/controlflow.ts
CHANGED
@@ -12,7 +12,7 @@ export type ShowProps<T> = {
|
|
12
12
|
Show children if `when` prop is true, otherwise show `fallback`.
|
13
13
|
@group Components
|
14
14
|
*/
|
15
|
-
export function Show<T>(props:
|
15
|
+
export function Show<T>(props:any) {
|
16
16
|
const condition = memo(()=>props.when)
|
17
17
|
return memo(()=>{
|
18
18
|
const c = condition()
|
@@ -43,7 +43,7 @@ export type ListProps<T> = {
|
|
43
43
|
List
|
44
44
|
@group Components
|
45
45
|
*/
|
46
|
-
export function List<T>(props:
|
46
|
+
export function List<T>(props:any) {
|
47
47
|
const fallback = "fallback" in props && { fallback: () => props.fallback }
|
48
48
|
const list = props.each
|
49
49
|
const cb:any = (props.children as any)?.call ? props.children : (v:any)=>v
|
package/src/mod.ts
CHANGED
@@ -12,7 +12,7 @@ export {resource,makeAbortable,abortable} from './resource.js'
|
|
12
12
|
// export {Input,Table} from './components.js'
|
13
13
|
// export * from './canvas.js'
|
14
14
|
|
15
|
-
const r:Runtime
|
15
|
+
const r:Runtime = /*#__PURE__*/ (typeof window === 'object') ? runtime(window as any) : undefined as any
|
16
16
|
|
17
17
|
/** h
|
18
18
|
@example Element with a single child
|
@@ -29,7 +29,7 @@ h(Input,{onInput:e => {}})
|
|
29
29
|
```
|
30
30
|
@group Hyperscript
|
31
31
|
*/
|
32
|
-
const h:HyperScript
|
32
|
+
const h:HyperScript = /*#__PURE__*/ r ? hyperscript(r) : undefined
|
33
33
|
|
34
34
|
const render = /*#__PURE__*/ r?.render
|
35
35
|
|
package/src/ui/button.ts
ADDED
package/src/ui/canvas.ts
ADDED
package/src/ui/date.ts
ADDED
package/src/ui/dialog.ts
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
import { effect } from '../reactive.ts'
|
2
|
+
import { h } from './h.ts'
|
3
|
+
|
4
|
+
export function Dialog(props) {
|
5
|
+
return h('dialog', {
|
6
|
+
ref(r) {
|
7
|
+
r.addEventListener('cancel', ()=>props.show(false))
|
8
|
+
const show = props.modal ? ()=>r.showModal() : ()=>r.show()
|
9
|
+
effect(() => props.show() ? show() : r.close())
|
10
|
+
},
|
11
|
+
}, props.children)
|
12
|
+
}
|
package/src/ui/filter.ts
ADDED
package/src/ui/form.ts
ADDED
package/src/ui/h.ts
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
import {runtime,type Runtime} from '../runtime.ts'
|
2
|
+
import {hyperscript,type HyperScript} from '../hyperscript.ts'
|
3
|
+
|
4
|
+
let _fontMetricsCtx:undefined|CanvasRenderingContext2D
|
5
|
+
|
6
|
+
export function fontMetrics(font:string, text:string) {
|
7
|
+
if (!_fontMetricsCtx) _fontMetricsCtx = document.createElement('canvas').getContext('2d')
|
8
|
+
_fontMetricsCtx!.font = font;
|
9
|
+
return _fontMetricsCtx!.measureText(text)
|
10
|
+
}
|
11
|
+
|
12
|
+
export function renderEffect(fn:any):any {
|
13
|
+
return requestAnimationFrame(()=>effect(fn))
|
14
|
+
}
|
15
|
+
|
16
|
+
const r:Runtime|undefined = /*#__PURE__*/ (typeof window === 'object') ? runtime(window as any) : undefined
|
17
|
+
|
18
|
+
/** h
|
19
|
+
@example Element with a single child
|
20
|
+
```js
|
21
|
+
h('h1','Hello World!')
|
22
|
+
```
|
23
|
+
@example Element with multiple children
|
24
|
+
```js
|
25
|
+
h('p',['Hello ',h('em','World!')])
|
26
|
+
```
|
27
|
+
@example Component with event handler
|
28
|
+
```js
|
29
|
+
h(Input,{onInput:e => {}})
|
30
|
+
```
|
31
|
+
@group Hyperscript
|
32
|
+
*/
|
33
|
+
const h:HyperScript|undefined = /*#__PURE__*/ r ? hyperscript(r) : undefined
|
34
|
+
|
35
|
+
const render = /*#__PURE__*/ r?.render
|
36
|
+
|
37
|
+
// import {signal,wrap} from './reactive.ts'
|
38
|
+
|
39
|
+
// /**
|
40
|
+
// @group Utils
|
41
|
+
// */
|
42
|
+
// export function $(a:any,b:any):any {
|
43
|
+
// const t = typeof a
|
44
|
+
// if (t==='function') return wrap(a,b)
|
45
|
+
// else return signal(a,b)
|
46
|
+
// }
|
47
|
+
|
48
|
+
export {h,render}
|
package/src/ui/image.ts
ADDED
package/src/ui/input.ts
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import {h,fontMetrics,renderEffect} from './h.ts'
|
2
|
+
import {type Getter, type Setter} from '../reactive.ts'
|
3
|
+
|
4
|
+
export type InputProps = {
|
5
|
+
ref?: (r:HTMLElement)=>void,
|
6
|
+
id?: string,
|
7
|
+
autocomplete?: 'on'|'off',
|
8
|
+
autosize?: (r:HTMLElement, props:any)=>(r:HTMLElement)=>void,
|
9
|
+
class?: string,
|
10
|
+
disabled?: boolean,
|
11
|
+
name?: string,
|
12
|
+
placeholder?: string,
|
13
|
+
onInput?: (e:Event) => void,
|
14
|
+
value: (Getter<string>&Setter<string>)|(Getter<number>&Setter<number>),
|
15
|
+
style?: string,
|
16
|
+
type?: string,
|
17
|
+
}
|
18
|
+
|
19
|
+
export function Input(props: InputProps): any {
|
20
|
+
props.onInput = props.onInput || ((e:Event) => props.value(e?.target?.value))
|
21
|
+
props.autocomplete = props.autocomplete || 'off'
|
22
|
+
if (props.autosize) props.ref = (r)=>props.autosize(r,props)
|
23
|
+
return h('input', props)
|
24
|
+
}
|
25
|
+
|
26
|
+
const autosize = (r:HTMLElement, props:any) => {
|
27
|
+
const style = window.getComputedStyle(r)
|
28
|
+
renderEffect(() => {
|
29
|
+
const font = style.getPropertyValue('font')
|
30
|
+
const metrics = fontMetrics(font, props.value().toString())
|
31
|
+
r.style.width = metrics.width + 'px'
|
32
|
+
})
|
33
|
+
}
|
34
|
+
|
35
|
+
Input.prototype.autosize = autosize
|
36
|
+
|
37
|
+
// return h('input', {
|
38
|
+
// ref,
|
39
|
+
// id: props.id,
|
40
|
+
// autocomplete: props.autocomplete || 'off',
|
41
|
+
// class: props.class,
|
42
|
+
// disabled: props.disabled,
|
43
|
+
// onInput: (e:Event) => props.value(e?.target?.value),
|
44
|
+
// name: props.name,
|
45
|
+
// placeholder: props.placeholder,
|
46
|
+
// type: props.type,
|
47
|
+
// style: props.style,
|
48
|
+
// value: props.value,
|
49
|
+
// })
|
package/src/ui/mod.ts
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
export {Accordion, type AccordionProps} from './accordion.ts'
|
2
|
+
export {Button, type ButtonProps} from './button.ts'
|
3
|
+
export {Canvas} from './canvas.ts'
|
4
|
+
export {DateInput} from './date.ts'
|
5
|
+
export {Input, type InputProps} from './input.ts'
|
6
|
+
export {Form} from './form.ts'
|
7
|
+
export {Tab} from './tab.ts'
|
8
|
+
export {Filter, type FilterProps} from './filter.ts'
|
9
|
+
export {Multiselect, type MultiselectProps} from './multiselect.ts'
|
10
|
+
export {Select, type SelectProps} from './select.ts'
|
11
|
+
export {Table, type TableProps} from './table.ts'
|
12
|
+
export {Upload} from './upload.ts'
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import {h} from './h.ts'
|
2
|
+
import {signal} from "../reactive.ts"
|
3
|
+
import {Dialog} from './dialog.ts'
|
4
|
+
import {Button} from './button.ts'
|
5
|
+
|
6
|
+
export type MultiselectProps = {
|
7
|
+
key: string | ((item:unknown)=>string),
|
8
|
+
label: string | ((item:unknown)=>string),
|
9
|
+
options: string[] | (()=>string[]),
|
10
|
+
disabled: boolean | (()=>boolean),
|
11
|
+
value: string[] | ((value:string[])=>void),
|
12
|
+
}
|
13
|
+
|
14
|
+
export function Multiselect(props:MultiselectProps) {
|
15
|
+
const showDialog = signal(true)
|
16
|
+
const key = props.key || (f => f)
|
17
|
+
const label = props.label || (f => f)
|
18
|
+
const add = item => props.value([...props.value(), key(item)])
|
19
|
+
const remove = i => _ => props.value(items=>items.toSpliced(i,1))
|
20
|
+
const options = () => (props.options.call ? props.options() : props.options).filter(e=>!props.value().includes(e))
|
21
|
+
const value = (...args:any[]) => (!args.length) ? props.value() : (show(false), add(key(args[0])))
|
22
|
+
const disabled = () => ((props?.disabled?.call ? props.disabled() : props.disabled))
|
23
|
+
|
24
|
+
return h('span.multiselect', [
|
25
|
+
h(List, {each:()=>value}, (e,i) => h('span', [
|
26
|
+
h(Button, {disabled}, ()=>label(e())),
|
27
|
+
h(Button, {disabled, onClick:remove(i())})
|
28
|
+
])),
|
29
|
+
h('span', {style:'position:relative'}, [
|
30
|
+
h(Button, {
|
31
|
+
onClick: _ => showDialog(b=>!b),
|
32
|
+
class: () => showDialog() ? 'minus' : 'add',
|
33
|
+
disabled: () => disabled() || !options().length,
|
34
|
+
}, '+'),
|
35
|
+
h(Dialog, {show:()=>showDialog}, h(Options, {
|
36
|
+
options: () => options,
|
37
|
+
value: () => value,
|
38
|
+
label
|
39
|
+
}))
|
40
|
+
])
|
41
|
+
])
|
42
|
+
}
|
package/src/ui/option.ts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
import {h} from './h.ts'
|
package/src/ui/select.ts
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
import {signal} from '../reactive.ts'
|
2
|
+
import { h } from './h.ts'
|
3
|
+
import {Dialog} from './dialog.ts'
|
4
|
+
import {Button} from './button.ts'
|
5
|
+
|
6
|
+
export type SelectProps = {
|
7
|
+
label?: (value:any)=>string,
|
8
|
+
key?: (value:any)=>string,
|
9
|
+
options: any[] | (()=>any[]),
|
10
|
+
}
|
11
|
+
|
12
|
+
export function Select(props:SelectProps) {
|
13
|
+
const show = signal(false)
|
14
|
+
const label = props.label || (f => f)
|
15
|
+
const key = props.key || (f => f)
|
16
|
+
const placeholder = h('span.placeholder', props.placeholder)
|
17
|
+
const display = () => props?.value?.() ? label(props.value()) : placeholder
|
18
|
+
const value = (...args) => (!args.length) ? props.value() : (show(false), props.value(key(args[0])))
|
19
|
+
|
20
|
+
return h('span.select', {id: props.id, class:props.class, style:'position:relative'}, [
|
21
|
+
h(Button, {onClick:_ => show(b=>!b)}, display),
|
22
|
+
h(Dialog, {show:()=>show}, h(Options, {
|
23
|
+
options: props.options,
|
24
|
+
value: ()=>value,
|
25
|
+
label
|
26
|
+
}))
|
27
|
+
])
|
28
|
+
}
|
package/src/ui/tab.ts
ADDED
package/src/ui/table.ts
ADDED
package/src/ui/upload.ts
ADDED
package/www/playground.html
CHANGED
@@ -18,6 +18,7 @@ const playing = signal(true)
|
|
18
18
|
const examples = [
|
19
19
|
{name:'counter',title:'Counter'},
|
20
20
|
{name:'todo',title:'Todo App'},
|
21
|
+
{name:'select',title:'Select'},
|
21
22
|
// {name:'admin',title:'Admin'}
|
22
23
|
]
|
23
24
|
|
@@ -98,7 +99,7 @@ function SrcMenu(props) {
|
|
98
99
|
function Playground() {
|
99
100
|
const show = signal(true)
|
100
101
|
const src = signal('example')
|
101
|
-
const file = signal('
|
102
|
+
const file = signal('select')
|
102
103
|
const local = signal(examples)
|
103
104
|
const content = signal('')
|
104
105
|
const exs = ()=>examples
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Select</title>
|
3
|
+
|
4
|
+
<script type="module">
|
5
|
+
|
6
|
+
import {h,signal,render,memo,List} from 'https://esm.sh/@wrnrlr/prelude'
|
7
|
+
|
8
|
+
function Select(props) {
|
9
|
+
const state = memo(() => ({
|
10
|
+
options: props.options(),
|
11
|
+
index: signal(-1)
|
12
|
+
}))
|
13
|
+
return [
|
14
|
+
()=>state().index,
|
15
|
+
h(List, {each:()=>state().options}, (o,i) =>
|
16
|
+
h('button', {onClick:e=>{state().index(i())}}, o))
|
17
|
+
]
|
18
|
+
}
|
19
|
+
|
20
|
+
function App() {
|
21
|
+
const options = signal(['apple', 'banana', 'cherry'])
|
22
|
+
return h(Select, {options:()=>options})
|
23
|
+
}
|
24
|
+
|
25
|
+
render(App, document.body)
|
26
|
+
|
27
|
+
</script>
|
package/www/ui.html
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<title>Demo</title>
|
3
|
+
|
4
|
+
<script src="./accordion.ts"></script>
|
5
|
+
<script src="./button.ts"></script>
|
6
|
+
<script src="./canvas.ts"></script>
|
7
|
+
<script src="./date.ts"></script>
|
8
|
+
<script src="./editable.ts"></script>
|
9
|
+
<script src="./filter.ts"></script>
|
10
|
+
<script src="./multiselect.ts"></script>
|
11
|
+
<script src="./select.ts"></script>
|
12
|
+
<script src="./table.ts"></script>
|
13
|
+
|
14
|
+
<body>
|
15
|
+
<h1>UI</h1>
|
16
|
+
<div id="example"></div>
|
17
|
+
</body>
|
18
|
+
|
19
|
+
<script type="module">
|
20
|
+
function App() {}
|
21
|
+
|
22
|
+
function AccordionExample() {
|
23
|
+
return h('#accordion', h(Accordion))
|
24
|
+
}
|
25
|
+
function ButtonExample() {
|
26
|
+
return h('#button', h(Button))
|
27
|
+
}
|
28
|
+
function CanvasExample() {
|
29
|
+
return h('#canvas', h(Canvas))
|
30
|
+
}
|
31
|
+
function DateInputExample() {
|
32
|
+
return h('#date', h(DateInput))
|
33
|
+
}
|
34
|
+
function EditableExample() {
|
35
|
+
return h('#editable', h(Editbale))
|
36
|
+
}
|
37
|
+
function FilterExample() {
|
38
|
+
return h('#filter', h(Filter))
|
39
|
+
}
|
40
|
+
function MultiselectExample() {
|
41
|
+
return h('#multiselect', h(Multiselect))
|
42
|
+
}
|
43
|
+
function SelectExample() {
|
44
|
+
return h('#select', h(Select))
|
45
|
+
}
|
46
|
+
function TableExample() {
|
47
|
+
return h('#table', h(Table))
|
48
|
+
}
|
49
|
+
</script>
|