@typed/ui 0.1.5 → 0.3.0
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/Anchor/package.json +6 -0
- package/Link/package.json +6 -0
- package/dist/cjs/Anchor.js +39 -48
- package/dist/cjs/Anchor.js.map +1 -1
- package/dist/cjs/Link.js +27 -17
- package/dist/cjs/Link.js.map +1 -1
- package/dist/cjs/index.js +22 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/internal/addEventListener.js +19 -0
- package/dist/cjs/internal/addEventListener.js.map +1 -0
- package/dist/cjs/useClickAway.js +43 -0
- package/dist/cjs/useClickAway.js.map +1 -0
- package/dist/cjs/usePagination.js +68 -0
- package/dist/cjs/usePagination.js.map +1 -0
- package/dist/dts/Anchor.d.ts +8 -7
- package/dist/dts/Anchor.d.ts.map +1 -1
- package/dist/dts/Link.d.ts +2 -2
- package/dist/dts/Link.d.ts.map +1 -1
- package/dist/dts/index.d.ts +8 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/dts/internal/addEventListener.d.ts +6 -0
- package/dist/dts/internal/addEventListener.d.ts.map +1 -0
- package/dist/dts/internal/dom-properties.d.ts +36 -36
- package/dist/dts/internal/dom-properties.d.ts.map +1 -1
- package/dist/dts/useClickAway.d.ts +17 -0
- package/dist/dts/useClickAway.d.ts.map +1 -0
- package/dist/dts/usePagination.d.ts +41 -0
- package/dist/dts/usePagination.d.ts.map +1 -0
- package/dist/esm/Anchor.js +21 -45
- package/dist/esm/Anchor.js.map +1 -1
- package/dist/esm/Link.js +12 -11
- package/dist/esm/Link.js.map +1 -1
- package/dist/esm/index.js +29 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/internal/addEventListener.js +11 -0
- package/dist/esm/internal/addEventListener.js.map +1 -0
- package/dist/esm/useClickAway.js +36 -0
- package/dist/esm/useClickAway.js.map +1 -0
- package/dist/esm/usePagination.js +59 -0
- package/dist/esm/usePagination.js.map +1 -0
- package/package.json +29 -13
- package/src/Anchor.ts +46 -54
- package/src/Link.ts +27 -18
- package/src/index.ts +32 -0
- package/src/internal/addEventListener.ts +22 -0
- package/src/internal/dom-properties.ts +36 -36
- package/src/useClickAway.ts +64 -0
- package/src/usePagination.ts +122 -0
- package/useClickAway/package.json +6 -0
- package/usePagination/package.json +6 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typed/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -10,18 +10,18 @@
|
|
|
10
10
|
"sideEffects": [],
|
|
11
11
|
"author": "Typed contributors",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@effect/platform": "^0.
|
|
14
|
-
"@effect/schema": "0.
|
|
15
|
-
"csstype": "^3.1.
|
|
16
|
-
"effect": "2.0.
|
|
17
|
-
"@typed/context": "0.
|
|
18
|
-
"@typed/dom": "
|
|
19
|
-
"@typed/environment": "0.
|
|
20
|
-
"@typed/fx": "1.
|
|
21
|
-
"@typed/navigation": "0.
|
|
22
|
-
"@typed/route": "
|
|
23
|
-
"@typed/router": "0.
|
|
24
|
-
"@typed/template": "0.
|
|
13
|
+
"@effect/platform": "^0.41.0",
|
|
14
|
+
"@effect/schema": "0.59.1",
|
|
15
|
+
"csstype": "^3.1.3",
|
|
16
|
+
"effect": "^2.0.3",
|
|
17
|
+
"@typed/context": "0.21.0",
|
|
18
|
+
"@typed/dom": "9.0.0",
|
|
19
|
+
"@typed/environment": "0.3.0",
|
|
20
|
+
"@typed/fx": "1.20.0",
|
|
21
|
+
"@typed/navigation": "0.8.0",
|
|
22
|
+
"@typed/route": "2.0.0",
|
|
23
|
+
"@typed/router": "0.21.0",
|
|
24
|
+
"@typed/template": "0.3.0"
|
|
25
25
|
},
|
|
26
26
|
"main": "./dist/cjs/index.js",
|
|
27
27
|
"module": "./dist/esm/index.js",
|
|
@@ -42,6 +42,16 @@
|
|
|
42
42
|
"types": "./dist/dts/Link.d.ts",
|
|
43
43
|
"import": "./dist/esm/Link.js",
|
|
44
44
|
"default": "./dist/cjs/Link.js"
|
|
45
|
+
},
|
|
46
|
+
"./useClickAway": {
|
|
47
|
+
"types": "./dist/dts/useClickAway.d.ts",
|
|
48
|
+
"import": "./dist/esm/useClickAway.js",
|
|
49
|
+
"default": "./dist/cjs/useClickAway.js"
|
|
50
|
+
},
|
|
51
|
+
"./usePagination": {
|
|
52
|
+
"types": "./dist/dts/usePagination.d.ts",
|
|
53
|
+
"import": "./dist/esm/usePagination.js",
|
|
54
|
+
"default": "./dist/cjs/usePagination.js"
|
|
45
55
|
}
|
|
46
56
|
},
|
|
47
57
|
"typesVersions": {
|
|
@@ -51,6 +61,12 @@
|
|
|
51
61
|
],
|
|
52
62
|
"Link": [
|
|
53
63
|
"./dist/dts/Link.d.ts"
|
|
64
|
+
],
|
|
65
|
+
"useClickAway": [
|
|
66
|
+
"./dist/dts/useClickAway.d.ts"
|
|
67
|
+
],
|
|
68
|
+
"usePagination": [
|
|
69
|
+
"./dist/dts/usePagination.d.ts"
|
|
54
70
|
]
|
|
55
71
|
}
|
|
56
72
|
}
|
package/src/Anchor.ts
CHANGED
|
@@ -9,11 +9,11 @@ import type { DefaultEventMap, ElementSource } from "@typed/template/ElementSour
|
|
|
9
9
|
import * as EventHandler from "@typed/template/EventHandler"
|
|
10
10
|
import type { Placeholder } from "@typed/template/Placeholder"
|
|
11
11
|
import type { Renderable } from "@typed/template/Renderable"
|
|
12
|
-
import type {
|
|
13
|
-
import { html } from "@typed/template/RenderTemplate"
|
|
12
|
+
import type { RenderEvent } from "@typed/template/RenderEvent"
|
|
13
|
+
import { html, type RenderTemplate } from "@typed/template/RenderTemplate"
|
|
14
14
|
import type { Rendered } from "@typed/wire"
|
|
15
15
|
import type { ReadonlyRecord, Scope } from "effect"
|
|
16
|
-
import
|
|
16
|
+
import * as Effect from "effect/Effect"
|
|
17
17
|
import { uncapitalize } from "effect/String"
|
|
18
18
|
import type { HTMLAnchorElementProperties } from "./internal/dom-properties.js"
|
|
19
19
|
|
|
@@ -22,14 +22,14 @@ import type { HTMLAnchorElementProperties } from "./internal/dom-properties.js"
|
|
|
22
22
|
*/
|
|
23
23
|
export type AnchorProps =
|
|
24
24
|
& {
|
|
25
|
-
readonly [K in keyof HTMLAnchorElementProperties]
|
|
25
|
+
readonly [K in keyof HTMLAnchorElementProperties]?:
|
|
26
26
|
| HTMLAnchorElementProperties[K]
|
|
27
27
|
| Placeholder.Any<HTMLAnchorElementProperties[K]>
|
|
28
|
-
|
|
|
28
|
+
| undefined
|
|
29
29
|
}
|
|
30
30
|
& {
|
|
31
|
-
readonly ref?: (ref: ElementSource<HTMLAnchorElement>) => Effect.Effect<any, any, any>
|
|
32
|
-
readonly data?: Placeholder.Any<ReadonlyRecord.ReadonlyRecord<any>>
|
|
31
|
+
readonly ref?: ((ref: ElementSource<HTMLAnchorElement>) => Effect.Effect<any, any, any>) | undefined
|
|
32
|
+
readonly data?: Placeholder.Any<ReadonlyRecord.ReadonlyRecord<any>> | undefined
|
|
33
33
|
}
|
|
34
34
|
& EventHandlerProps<HTMLAnchorElement>
|
|
35
35
|
|
|
@@ -58,60 +58,52 @@ export function Anchor<
|
|
|
58
58
|
>(
|
|
59
59
|
props: Props,
|
|
60
60
|
...children: Children
|
|
61
|
-
):
|
|
62
|
-
Placeholder.Context<Props[keyof Props] | ReturnOf<Props["ref"]> | Children[number]>,
|
|
61
|
+
): Fx.Fx<
|
|
62
|
+
RenderTemplate | Scope.Scope | Placeholder.Context<Props[keyof Props] | ReturnOf<Props["ref"]> | Children[number]>,
|
|
63
63
|
Placeholder.Error<Props[keyof Props] | ReturnOf<Props["ref"]> | Children[number]>,
|
|
64
|
-
|
|
64
|
+
RenderEvent
|
|
65
65
|
> {
|
|
66
|
-
const
|
|
66
|
+
const {
|
|
67
|
+
data,
|
|
68
|
+
hash,
|
|
69
|
+
host,
|
|
70
|
+
hostname,
|
|
71
|
+
href,
|
|
72
|
+
hreflang,
|
|
73
|
+
pathname,
|
|
74
|
+
port,
|
|
75
|
+
protocol,
|
|
76
|
+
ref,
|
|
77
|
+
scrollLeft,
|
|
78
|
+
scrollTop,
|
|
79
|
+
search,
|
|
80
|
+
...rest
|
|
81
|
+
} = props
|
|
82
|
+
|
|
83
|
+
const refDirective = Directive.ref(({ value }) =>
|
|
67
84
|
Effect.gen(function*(_) {
|
|
68
|
-
yield* _(addEventListeners(props,
|
|
85
|
+
yield* _(addEventListeners(props, value))
|
|
69
86
|
|
|
70
|
-
if (
|
|
71
|
-
yield* _(
|
|
87
|
+
if (ref) {
|
|
88
|
+
yield* _(ref(value as any))
|
|
72
89
|
}
|
|
73
90
|
})
|
|
74
91
|
)
|
|
75
92
|
return html`<a
|
|
76
|
-
ref="${
|
|
77
|
-
.
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
.
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
draggable="${props.draggable}"
|
|
91
|
-
.hash="${props.hash}"
|
|
92
|
-
.host="${props.host}"
|
|
93
|
-
.hostname="${props.hostname}"
|
|
94
|
-
.href="${props.href}"
|
|
95
|
-
hreflang="${props.hreflang}"
|
|
96
|
-
id="${props.id}"
|
|
97
|
-
id="${props.id}"
|
|
98
|
-
lang="${props.lang}"
|
|
99
|
-
Methods="${props.Methods}"
|
|
100
|
-
name="${props.name}"
|
|
101
|
-
.pathname="${props.pathname}"
|
|
102
|
-
.port="${props.port}"
|
|
103
|
-
.protocol="${props.protocol}"
|
|
104
|
-
rel="${props.rel}"
|
|
105
|
-
rev="${props.rev}"
|
|
106
|
-
.search="${props.search}"
|
|
107
|
-
shape="${props.shape}"
|
|
108
|
-
slot="${props.slot}"
|
|
109
|
-
tabindex="${props.tabIndex}"
|
|
110
|
-
target="${props.target}"
|
|
111
|
-
text="${props.text}"
|
|
112
|
-
title="${props.title}"
|
|
113
|
-
type="${props.type}"
|
|
114
|
-
urn="${props.urn}"
|
|
93
|
+
ref="${refDirective}"
|
|
94
|
+
.props="${rest}"
|
|
95
|
+
.data="${data}"
|
|
96
|
+
.scrollLeft="${scrollLeft}"
|
|
97
|
+
.scrollTop="${scrollTop}"
|
|
98
|
+
.hash="${hash}"
|
|
99
|
+
.host="${host}"
|
|
100
|
+
.hostname="${hostname}"
|
|
101
|
+
.href="${href}"
|
|
102
|
+
.hreflang="${hreflang}"
|
|
103
|
+
.pathname="${pathname}"
|
|
104
|
+
.port="${port}"
|
|
105
|
+
.protocol="${protocol}"
|
|
106
|
+
.search="${search}"
|
|
115
107
|
>${children}</a>` as any
|
|
116
108
|
}
|
|
117
109
|
|
|
@@ -123,7 +115,7 @@ export function addEventListeners<Props extends EventHandlerProps<any>, T extend
|
|
|
123
115
|
ref: ElementSource<T>
|
|
124
116
|
): Effect.Effect<Scope.Scope | GetEventHandlersContext<Props>, never, void> {
|
|
125
117
|
return Fx.forkScoped(
|
|
126
|
-
Fx.
|
|
118
|
+
Fx.mergeAll(
|
|
127
119
|
getEventHandlers(props).map(([type, handler]: any) => addEventListener(ref, type, handler))
|
|
128
120
|
)
|
|
129
121
|
) as any
|
package/src/Link.ts
CHANGED
|
@@ -12,7 +12,8 @@ import { Placeholder } from "@typed/template/Placeholder"
|
|
|
12
12
|
import type { Renderable } from "@typed/template/Renderable"
|
|
13
13
|
import type { RenderEvent } from "@typed/template/RenderEvent"
|
|
14
14
|
import type { RenderTemplate } from "@typed/template/RenderTemplate"
|
|
15
|
-
import
|
|
15
|
+
import * as Effect from "effect/Effect"
|
|
16
|
+
import type * as Scope from "effect/Scope"
|
|
16
17
|
import { Anchor, type AnchorProps, getEventHandler } from "./Anchor.js"
|
|
17
18
|
|
|
18
19
|
/**
|
|
@@ -30,36 +31,36 @@ export type LinkProps = Omit<AnchorProps, keyof URL> & {
|
|
|
30
31
|
* @since 1.0.0
|
|
31
32
|
*/
|
|
32
33
|
export function Link<Props extends LinkProps, Children extends ReadonlyArray<Renderable<any, any>> = readonly []>(
|
|
33
|
-
props: Props,
|
|
34
|
+
{ onClick, ref, relative, replace, state, to, ...props }: Props,
|
|
34
35
|
...children: Children
|
|
35
36
|
): Fx.Fx<
|
|
36
37
|
| Navigation.Navigation
|
|
37
38
|
| CurrentRoute
|
|
38
39
|
| RenderTemplate
|
|
39
40
|
| Scope.Scope
|
|
40
|
-
|
|
|
41
|
-
|
|
|
42
|
-
Placeholder.Error<Props[keyof Props] | Children[number]>,
|
|
41
|
+
| Placeholder.Context<Props[keyof Props] | Children[number]>
|
|
42
|
+
| Fx.Context<Props[keyof Props] | Children[number]>,
|
|
43
|
+
Placeholder.Error<Props[keyof Props] | Children[number]> | Fx.Error<Props[keyof Props] | Children[number]>,
|
|
43
44
|
RenderEvent
|
|
44
45
|
> {
|
|
45
46
|
return Fx.gen(function*(_) {
|
|
46
|
-
const onClickHandler = getEventHandler(
|
|
47
|
-
const
|
|
48
|
-
Placeholder.asRef<Placeholder.Context<Props["to"]>, Placeholder.Error<Props["to"]>, string>(
|
|
47
|
+
const onClickHandler = getEventHandler(onClick)
|
|
48
|
+
const toRef = yield* _(
|
|
49
|
+
Placeholder.asRef<Placeholder.Context<Props["to"]>, Placeholder.Error<Props["to"]>, string>(to)
|
|
49
50
|
)
|
|
50
|
-
const
|
|
51
|
+
const relativeRef = yield* _(
|
|
51
52
|
Placeholder.asRef<Placeholder.Context<Props["relative"]>, Placeholder.Error<Props["relative"]>, boolean>(
|
|
52
|
-
|
|
53
|
+
relative ?? true
|
|
53
54
|
)
|
|
54
55
|
)
|
|
55
|
-
const
|
|
56
|
+
const replaceRef = yield* _(
|
|
56
57
|
Placeholder.asRef<Placeholder.Context<Props["replace"]>, Placeholder.Error<Props["replace"]>, boolean>(
|
|
57
|
-
|
|
58
|
+
replace ?? false
|
|
58
59
|
)
|
|
59
60
|
)
|
|
60
|
-
const
|
|
61
|
+
const stateRef = yield* _(
|
|
61
62
|
Placeholder.asRef<Placeholder.Context<Props["state"]>, Placeholder.Error<Props["state"]>, unknown>(
|
|
62
|
-
|
|
63
|
+
state as Placeholder.Any<unknown>
|
|
63
64
|
)
|
|
64
65
|
)
|
|
65
66
|
const reloadDocument = yield* _(
|
|
@@ -72,10 +73,13 @@ export function Link<Props extends LinkProps, Children extends ReadonlyArray<Ren
|
|
|
72
73
|
)
|
|
73
74
|
)
|
|
74
75
|
|
|
75
|
-
const href = RefSubject.
|
|
76
|
+
const href = RefSubject.mapEffect(
|
|
77
|
+
RefSubject.tuple([relativeRef, toRef]),
|
|
78
|
+
([rel, to]) => rel ? makeHref(to) : Effect.succeed(to)
|
|
79
|
+
)
|
|
76
80
|
|
|
77
81
|
const navigate = Effect.gen(function*(_) {
|
|
78
|
-
const current = yield* _(Effect.all({ replace, state, reloadDocument }))
|
|
82
|
+
const current = yield* _(Effect.all({ replace: replaceRef, state: stateRef, reloadDocument }))
|
|
79
83
|
const url = yield* _(href)
|
|
80
84
|
|
|
81
85
|
yield* _(Navigation.navigate(url, {
|
|
@@ -88,7 +92,7 @@ export function Link<Props extends LinkProps, Children extends ReadonlyArray<Ren
|
|
|
88
92
|
}
|
|
89
93
|
})
|
|
90
94
|
|
|
91
|
-
const
|
|
95
|
+
const onClickEventHandler = EventHandler.preventDefault(
|
|
92
96
|
(ev: EventWithCurrentTarget<HTMLAnchorElement, MouseEvent>) =>
|
|
93
97
|
Effect.gen(function*(_) {
|
|
94
98
|
if (onClickHandler) {
|
|
@@ -99,6 +103,11 @@ export function Link<Props extends LinkProps, Children extends ReadonlyArray<Ren
|
|
|
99
103
|
onClickHandler?.options
|
|
100
104
|
)
|
|
101
105
|
|
|
102
|
-
|
|
106
|
+
const allProps = { ...props, ref, href, state: stateRef, onClick: onClickEventHandler }
|
|
107
|
+
|
|
108
|
+
return Anchor(
|
|
109
|
+
allProps as any as AnchorProps,
|
|
110
|
+
...children
|
|
111
|
+
)
|
|
103
112
|
})
|
|
104
113
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// TODO:
|
|
6
|
+
// Form + Fields/FieldGroups
|
|
7
|
+
// Masking
|
|
8
|
+
// Accordion
|
|
9
|
+
// Button
|
|
10
|
+
// Select
|
|
11
|
+
// MultiSelect
|
|
12
|
+
// Drag and drop
|
|
13
|
+
// Popovers
|
|
14
|
+
// Portals
|
|
15
|
+
// Menu
|
|
16
|
+
// Trees
|
|
17
|
+
// Modals
|
|
18
|
+
// Search/Autocomplete
|
|
19
|
+
// Tables
|
|
20
|
+
// Editable Tables
|
|
21
|
+
// File uploads
|
|
22
|
+
// Focus management
|
|
23
|
+
// Toasts
|
|
24
|
+
// Virtualization
|
|
25
|
+
// Optimistic UI helpers??
|
|
26
|
+
|
|
5
27
|
/**
|
|
6
28
|
* @since 1.0.0
|
|
7
29
|
*/
|
|
@@ -10,3 +32,13 @@ export * from "./Anchor.js"
|
|
|
10
32
|
* @since 1.0.0
|
|
11
33
|
*/
|
|
12
34
|
export * from "./Link.js"
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @since 1.0.0
|
|
38
|
+
*/
|
|
39
|
+
export * from "./useClickAway.js"
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @since 1.0.0
|
|
43
|
+
*/
|
|
44
|
+
export * from "./usePagination.js"
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { EventWithCurrentTarget } from "@typed/dom/EventTarget"
|
|
2
|
+
import * as Fx from "@typed/fx/Fx"
|
|
3
|
+
import type { DefaultEventMap } from "@typed/template/ElementSource"
|
|
4
|
+
import type { Scope } from "effect"
|
|
5
|
+
import * as Effect from "effect/Effect"
|
|
6
|
+
|
|
7
|
+
export function addEventListeners<T extends EventTarget, Events extends ReadonlyArray<keyof DefaultEventMap<T>>>(
|
|
8
|
+
target: T,
|
|
9
|
+
...events: Events
|
|
10
|
+
) {
|
|
11
|
+
return Fx.withEmitter<never, EventWithCurrentTarget<T, DefaultEventMap<T>[Events[number]]>, Scope.Scope>(
|
|
12
|
+
(emitter) => {
|
|
13
|
+
events.forEach((event) => target.addEventListener(event as string, emitter.succeed as any))
|
|
14
|
+
|
|
15
|
+
return Effect.addFinalizer(() =>
|
|
16
|
+
Effect.sync(() => {
|
|
17
|
+
events.forEach((event) => target.addEventListener(event as string, emitter.succeed as any))
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -1,50 +1,50 @@
|
|
|
1
1
|
export type ElementProperties = {
|
|
2
|
-
className?: string
|
|
3
|
-
id?: string
|
|
4
|
-
scrollLeft?: number
|
|
5
|
-
scrollTop?: number
|
|
6
|
-
slot?: string
|
|
2
|
+
className?: string | undefined
|
|
3
|
+
id?: string | undefined
|
|
4
|
+
scrollLeft?: number | undefined
|
|
5
|
+
scrollTop?: number | undefined
|
|
6
|
+
slot?: string | undefined
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export type HTMLElementProperties =
|
|
10
10
|
& ElementProperties
|
|
11
11
|
& {
|
|
12
|
-
accessKey?: string
|
|
13
|
-
contentEditable?: string
|
|
14
|
-
dir?: string
|
|
15
|
-
draggable?: boolean
|
|
16
|
-
hidden?: boolean
|
|
17
|
-
hideFocus?: boolean
|
|
18
|
-
lang?: string
|
|
19
|
-
spellcheck?: boolean
|
|
20
|
-
tabIndex?: boolean
|
|
21
|
-
title?: string
|
|
12
|
+
accessKey?: string | undefined
|
|
13
|
+
contentEditable?: string | undefined
|
|
14
|
+
dir?: string | undefined
|
|
15
|
+
draggable?: boolean | undefined
|
|
16
|
+
hidden?: boolean | undefined
|
|
17
|
+
hideFocus?: boolean | undefined
|
|
18
|
+
lang?: string | undefined
|
|
19
|
+
spellcheck?: boolean | undefined
|
|
20
|
+
tabIndex?: boolean | undefined
|
|
21
|
+
title?: string | undefined
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export type HTMLAnchorElementProperties =
|
|
25
25
|
& HTMLElementProperties
|
|
26
26
|
& {
|
|
27
|
-
Methods?: string
|
|
28
|
-
charset?: string
|
|
29
|
-
coords?: string
|
|
30
|
-
download?: string
|
|
31
|
-
hash?: string
|
|
32
|
-
host?: string
|
|
33
|
-
hostname?: string
|
|
34
|
-
href?: string
|
|
35
|
-
hreflang?: string
|
|
36
|
-
name?: string
|
|
37
|
-
pathname?: string
|
|
38
|
-
port?: string
|
|
39
|
-
protocol?: string
|
|
40
|
-
rel?: string
|
|
41
|
-
rev?: string
|
|
42
|
-
search?: string
|
|
43
|
-
shape?: string
|
|
44
|
-
target?: string
|
|
45
|
-
text?: string
|
|
46
|
-
type?: string
|
|
47
|
-
urn?: string
|
|
27
|
+
Methods?: string | undefined
|
|
28
|
+
charset?: string | undefined
|
|
29
|
+
coords?: string | undefined
|
|
30
|
+
download?: string | undefined
|
|
31
|
+
hash?: string | undefined
|
|
32
|
+
host?: string | undefined
|
|
33
|
+
hostname?: string | undefined
|
|
34
|
+
href?: string | undefined
|
|
35
|
+
hreflang?: string | undefined
|
|
36
|
+
name?: string | undefined
|
|
37
|
+
pathname?: string | undefined
|
|
38
|
+
port?: string | undefined
|
|
39
|
+
protocol?: string | undefined
|
|
40
|
+
rel?: string | undefined
|
|
41
|
+
rev?: string | undefined
|
|
42
|
+
search?: string | undefined
|
|
43
|
+
shape?: string | undefined
|
|
44
|
+
target?: string | undefined
|
|
45
|
+
text?: string | undefined
|
|
46
|
+
type?: string | undefined
|
|
47
|
+
urn?: string | undefined
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
export type HTMLAppletElementProperties =
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { Document } from "@typed/dom/Document"
|
|
6
|
+
import type { EventWithCurrentTarget } from "@typed/dom/EventTarget"
|
|
7
|
+
import * as Fx from "@typed/fx/Fx"
|
|
8
|
+
import type * as ElementRef from "@typed/template/ElementRef"
|
|
9
|
+
import { getElements } from "@typed/template/ElementSource"
|
|
10
|
+
import type { Rendered } from "@typed/wire"
|
|
11
|
+
import type { Effect, Fiber, Scope } from "effect"
|
|
12
|
+
import * as Option from "effect/Option"
|
|
13
|
+
import { addEventListeners } from "./internal/addEventListener"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
*/
|
|
18
|
+
export function useClickAway<Refs extends ReadonlyArray<ElementRef.ElementRef<any>>, R2>(
|
|
19
|
+
refs: Refs,
|
|
20
|
+
f: (event: EventWithCurrentTarget<Document, MouseEvent | TouchEvent>) => Effect.Effect<R2, never, unknown>
|
|
21
|
+
): Effect.Effect<Document | Scope.Scope | R2, never, Fiber.RuntimeFiber<never, void>> {
|
|
22
|
+
return Fx.forkScoped(onClickAway(refs, f))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @since 1.0.0
|
|
27
|
+
*/
|
|
28
|
+
export function onClickAway<Refs extends ReadonlyArray<ElementRef.ElementRef<any>>, R2, E2, B>(
|
|
29
|
+
refs: Refs,
|
|
30
|
+
f: (event: EventWithCurrentTarget<Document, MouseEvent | TouchEvent>) => Effect.Effect<R2, E2, B>
|
|
31
|
+
): Fx.Fx<Document | R2 | Scope.Scope, E2, B> {
|
|
32
|
+
return Fx.fromFxEffect(Document.with((document) => {
|
|
33
|
+
const events = addEventListeners(document, "click", "touchend", "contextmenu")
|
|
34
|
+
const elements = Fx.map(
|
|
35
|
+
Fx.tuple(refs as ReadonlyArray<Fx.Fx<Scope.Scope, never, Rendered>>),
|
|
36
|
+
(els) => els.flatMap(getElements)
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return Fx.mapEffect(
|
|
40
|
+
Fx.compact(Fx.snapshot(events, elements, containsRefs)),
|
|
41
|
+
f
|
|
42
|
+
)
|
|
43
|
+
}))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const containsRefs = (
|
|
47
|
+
event: EventWithCurrentTarget<Document, MouseEvent | TouchEvent>,
|
|
48
|
+
refs: ReadonlyArray<Element>
|
|
49
|
+
): Option.Option<EventWithCurrentTarget<Document, MouseEvent | TouchEvent>> => {
|
|
50
|
+
const target = event.target
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
target === null ||
|
|
54
|
+
refs.some(
|
|
55
|
+
(c) =>
|
|
56
|
+
c === target ||
|
|
57
|
+
c.contains(target as Element)
|
|
58
|
+
)
|
|
59
|
+
) {
|
|
60
|
+
return Option.none()
|
|
61
|
+
} else {
|
|
62
|
+
return Option.some(event)
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as RefSubject from "@typed/fx/RefSubject"
|
|
6
|
+
import type { Scope } from "effect"
|
|
7
|
+
import * as Effect from "effect/Effect"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @since 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
export type PaginationOptions = {
|
|
13
|
+
readonly initialPage?: number // 0
|
|
14
|
+
readonly initialPageSize?: number // 10
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @since 1.0.0
|
|
19
|
+
*/
|
|
20
|
+
export interface Pagination<E, A> {
|
|
21
|
+
readonly page: RefSubject.Computed<never, never, number>
|
|
22
|
+
readonly pageSize: RefSubject.Computed<never, never, number>
|
|
23
|
+
readonly canGoBack: RefSubject.Computed<never, never, boolean>
|
|
24
|
+
readonly canGoForward: RefSubject.Computed<never, E, boolean>
|
|
25
|
+
readonly paginated: RefSubject.Computed<never, E, ReadonlyArray<A>>
|
|
26
|
+
readonly viewing: RefSubject.Computed<never, E, Viewing>
|
|
27
|
+
|
|
28
|
+
readonly goBack: Effect.Effect<never, never, number>
|
|
29
|
+
readonly goForward: Effect.Effect<never, E, number>
|
|
30
|
+
readonly goToStart: Effect.Effect<never, never, number>
|
|
31
|
+
readonly goToEnd: Effect.Effect<never, E, number>
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @since 1.0.0
|
|
36
|
+
*/
|
|
37
|
+
export interface Viewing {
|
|
38
|
+
readonly from: number
|
|
39
|
+
readonly to: number
|
|
40
|
+
readonly total: number
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @since 1.0.0
|
|
45
|
+
*/
|
|
46
|
+
export function usePagination<R, E, A>(
|
|
47
|
+
items: RefSubject.Computed<R, E, ReadonlyArray<A>>,
|
|
48
|
+
options: PaginationOptions = {}
|
|
49
|
+
): Effect.Effect<R | Scope.Scope, never, Pagination<E, A>> {
|
|
50
|
+
return Effect.gen(function*(_) {
|
|
51
|
+
const ctx = yield* _(Effect.context<R>())
|
|
52
|
+
const page: RefSubject.RefSubject<never, never, number> = yield* _(RefSubject.of(options.initialPage ?? 0))
|
|
53
|
+
const pageSize: RefSubject.RefSubject<never, never, number> = yield* _(RefSubject.of(options.initialPageSize ?? 10))
|
|
54
|
+
const canGoBack: RefSubject.Computed<never, never, boolean> = RefSubject.map(page, (x) => x > 0)
|
|
55
|
+
const combined: RefSubject.Computed<never, E, readonly [number, number, ReadonlyArray<A>]> = RefSubject.provide(
|
|
56
|
+
RefSubject.tuple([page, pageSize, items] as const),
|
|
57
|
+
ctx
|
|
58
|
+
)
|
|
59
|
+
const canGoForward: RefSubject.Computed<never, E, boolean> = RefSubject.map(
|
|
60
|
+
combined,
|
|
61
|
+
([page, pageSize, results]) => page < Math.ceil(results.length / pageSize - 1)
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
const getTotalPages: Effect.Effect<never, E, number> = Effect.provide(
|
|
65
|
+
Effect.gen(function*($) {
|
|
66
|
+
const currentPageSize = yield* $(pageSize)
|
|
67
|
+
const results = yield* $(items)
|
|
68
|
+
|
|
69
|
+
return Math.ceil(results.length / currentPageSize - 1)
|
|
70
|
+
}),
|
|
71
|
+
ctx
|
|
72
|
+
)
|
|
73
|
+
const goBack: Effect.Effect<never, never, number> = RefSubject.update(page, (x) => Math.max(x - 1, 0))
|
|
74
|
+
const goForward: Effect.Effect<never, E, number> = RefSubject.updateEffect(
|
|
75
|
+
page,
|
|
76
|
+
(currentPage) =>
|
|
77
|
+
Effect.gen(function*($) {
|
|
78
|
+
const totalPages = yield* $(getTotalPages)
|
|
79
|
+
const nextPage = Math.min(currentPage + 1, totalPages)
|
|
80
|
+
|
|
81
|
+
return nextPage
|
|
82
|
+
})
|
|
83
|
+
)
|
|
84
|
+
const goToStart: Effect.Effect<never, never, number> = RefSubject.set(page, 0)
|
|
85
|
+
const goToEnd: Effect.Effect<never, E, number> = RefSubject.updateEffect(page, () => getTotalPages)
|
|
86
|
+
|
|
87
|
+
const paginated: RefSubject.Computed<never, E, ReadonlyArray<A>> = RefSubject.map(
|
|
88
|
+
combined,
|
|
89
|
+
([page, pageSize, results]) => {
|
|
90
|
+
const start = page * pageSize
|
|
91
|
+
const end = start + pageSize
|
|
92
|
+
|
|
93
|
+
return results.slice(start, end)
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
const viewing: RefSubject.Computed<never, E, Viewing> = RefSubject.map(combined, ([page, pageSize, results]) => {
|
|
98
|
+
const start = page * pageSize
|
|
99
|
+
const end = start + pageSize
|
|
100
|
+
const total = results.length
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
from: start + 1,
|
|
104
|
+
to: Math.min(end, total),
|
|
105
|
+
total
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
page,
|
|
111
|
+
pageSize,
|
|
112
|
+
canGoBack,
|
|
113
|
+
canGoForward,
|
|
114
|
+
goBack,
|
|
115
|
+
goForward,
|
|
116
|
+
goToStart,
|
|
117
|
+
goToEnd,
|
|
118
|
+
paginated,
|
|
119
|
+
viewing
|
|
120
|
+
} as const
|
|
121
|
+
})
|
|
122
|
+
}
|