@typed/ui 0.2.0 → 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/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/internal/addEventListener.js +1 -1
- package/dist/cjs/internal/addEventListener.js.map +1 -1
- package/dist/cjs/useClickAway.js +6 -6
- package/dist/cjs/useClickAway.js.map +1 -1
- package/dist/cjs/usePagination.js +13 -14
- package/dist/cjs/usePagination.js.map +1 -1
- 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/internal/addEventListener.d.ts +2 -1
- package/dist/dts/internal/addEventListener.d.ts.map +1 -1
- 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 +2 -3
- package/dist/dts/useClickAway.d.ts.map +1 -1
- package/dist/dts/usePagination.d.ts +9 -9
- package/dist/dts/usePagination.d.ts.map +1 -1
- 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/internal/addEventListener.js +1 -1
- package/dist/esm/internal/addEventListener.js.map +1 -1
- package/dist/esm/useClickAway.js +4 -4
- package/dist/esm/useClickAway.js.map +1 -1
- package/dist/esm/usePagination.js +10 -11
- package/dist/esm/usePagination.js.map +1 -1
- package/package.json +12 -12
- package/src/Anchor.ts +46 -54
- package/src/Link.ts +27 -18
- package/src/internal/addEventListener.ts +11 -8
- package/src/internal/dom-properties.ts +36 -36
- package/src/useClickAway.ts +22 -20
- package/src/usePagination.ts +39 -36
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
|
}
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import type { EventWithCurrentTarget } from "@typed/dom/EventTarget"
|
|
2
2
|
import * as Fx from "@typed/fx/Fx"
|
|
3
3
|
import type { DefaultEventMap } from "@typed/template/ElementSource"
|
|
4
|
+
import type { Scope } from "effect"
|
|
4
5
|
import * as Effect from "effect/Effect"
|
|
5
6
|
|
|
6
7
|
export function addEventListeners<T extends EventTarget, Events extends ReadonlyArray<keyof DefaultEventMap<T>>>(
|
|
7
8
|
target: T,
|
|
8
9
|
...events: Events
|
|
9
10
|
) {
|
|
10
|
-
return Fx.
|
|
11
|
-
|
|
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))
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
return Effect.addFinalizer(() =>
|
|
16
|
+
Effect.sync(() => {
|
|
17
|
+
events.forEach((event) => target.addEventListener(event as string, emitter.succeed as any))
|
|
18
|
+
})
|
|
19
|
+
)
|
|
20
|
+
}
|
|
21
|
+
)
|
|
19
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 =
|
package/src/useClickAway.ts
CHANGED
|
@@ -8,8 +8,8 @@ import * as Fx from "@typed/fx/Fx"
|
|
|
8
8
|
import type * as ElementRef from "@typed/template/ElementRef"
|
|
9
9
|
import { getElements } from "@typed/template/ElementSource"
|
|
10
10
|
import type { Rendered } from "@typed/wire"
|
|
11
|
-
import type { Fiber, Scope } from "effect"
|
|
12
|
-
import
|
|
11
|
+
import type { Effect, Fiber, Scope } from "effect"
|
|
12
|
+
import * as Option from "effect/Option"
|
|
13
13
|
import { addEventListeners } from "./internal/addEventListener"
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -28,11 +28,11 @@ export function useClickAway<Refs extends ReadonlyArray<ElementRef.ElementRef<an
|
|
|
28
28
|
export function onClickAway<Refs extends ReadonlyArray<ElementRef.ElementRef<any>>, R2, E2, B>(
|
|
29
29
|
refs: Refs,
|
|
30
30
|
f: (event: EventWithCurrentTarget<Document, MouseEvent | TouchEvent>) => Effect.Effect<R2, E2, B>
|
|
31
|
-
): Fx.Fx<Document | R2, E2, B> {
|
|
31
|
+
): Fx.Fx<Document | R2 | Scope.Scope, E2, B> {
|
|
32
32
|
return Fx.fromFxEffect(Document.with((document) => {
|
|
33
33
|
const events = addEventListeners(document, "click", "touchend", "contextmenu")
|
|
34
34
|
const elements = Fx.map(
|
|
35
|
-
Fx.
|
|
35
|
+
Fx.tuple(refs as ReadonlyArray<Fx.Fx<Scope.Scope, never, Rendered>>),
|
|
36
36
|
(els) => els.flatMap(getElements)
|
|
37
37
|
)
|
|
38
38
|
|
|
@@ -43,20 +43,22 @@ export function onClickAway<Refs extends ReadonlyArray<ElementRef.ElementRef<any
|
|
|
43
43
|
}))
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const containsRefs = (
|
|
47
|
-
|
|
48
|
-
|
|
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
|
|
49
51
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
+
}
|
package/src/usePagination.ts
CHANGED
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import * as Computed from "@typed/fx/Computed"
|
|
6
5
|
import * as RefSubject from "@typed/fx/RefSubject"
|
|
7
6
|
import type { Scope } from "effect"
|
|
8
|
-
import
|
|
7
|
+
import * as Effect from "effect/Effect"
|
|
9
8
|
|
|
10
9
|
/**
|
|
11
10
|
* @since 1.0.0
|
|
@@ -19,12 +18,12 @@ export type PaginationOptions = {
|
|
|
19
18
|
* @since 1.0.0
|
|
20
19
|
*/
|
|
21
20
|
export interface Pagination<E, A> {
|
|
22
|
-
readonly page:
|
|
23
|
-
readonly pageSize:
|
|
24
|
-
readonly canGoBack:
|
|
25
|
-
readonly canGoForward:
|
|
26
|
-
readonly paginated:
|
|
27
|
-
readonly viewing:
|
|
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>
|
|
28
27
|
|
|
29
28
|
readonly goBack: Effect.Effect<never, never, number>
|
|
30
29
|
readonly goForward: Effect.Effect<never, E, number>
|
|
@@ -45,19 +44,20 @@ export interface Viewing {
|
|
|
45
44
|
* @since 1.0.0
|
|
46
45
|
*/
|
|
47
46
|
export function usePagination<R, E, A>(
|
|
48
|
-
items:
|
|
47
|
+
items: RefSubject.Computed<R, E, ReadonlyArray<A>>,
|
|
49
48
|
options: PaginationOptions = {}
|
|
50
49
|
): Effect.Effect<R | Scope.Scope, never, Pagination<E, A>> {
|
|
51
50
|
return Effect.gen(function*(_) {
|
|
52
51
|
const ctx = yield* _(Effect.context<R>())
|
|
53
52
|
const page: RefSubject.RefSubject<never, never, number> = yield* _(RefSubject.of(options.initialPage ?? 0))
|
|
54
53
|
const pageSize: RefSubject.RefSubject<never, never, number> = yield* _(RefSubject.of(options.initialPageSize ?? 10))
|
|
55
|
-
const canGoBack:
|
|
56
|
-
const combined:
|
|
57
|
-
|
|
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),
|
|
58
57
|
ctx
|
|
59
58
|
)
|
|
60
|
-
const canGoForward:
|
|
59
|
+
const canGoForward: RefSubject.Computed<never, E, boolean> = RefSubject.map(
|
|
60
|
+
combined,
|
|
61
61
|
([page, pageSize, results]) => page < Math.ceil(results.length / pageSize - 1)
|
|
62
62
|
)
|
|
63
63
|
|
|
@@ -70,39 +70,42 @@ export function usePagination<R, E, A>(
|
|
|
70
70
|
}),
|
|
71
71
|
ctx
|
|
72
72
|
)
|
|
73
|
-
const goBack: Effect.Effect<never, never, number> =
|
|
74
|
-
const goForward: Effect.Effect<never, E, number> =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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)
|
|
78
80
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
+
return nextPage
|
|
82
|
+
})
|
|
81
83
|
)
|
|
82
|
-
const goToStart: Effect.Effect<never, never, number> =
|
|
83
|
-
const goToEnd: Effect.Effect<never, E, number> =
|
|
84
|
-
|
|
85
|
-
const paginated: Computed.Computed<never, E, ReadonlyArray<A>> = combined.map(([page, pageSize, results]) => {
|
|
86
|
-
const start = page * pageSize
|
|
87
|
-
const end = start + pageSize
|
|
88
|
-
|
|
89
|
-
return results.slice(start, end)
|
|
90
|
-
})
|
|
84
|
+
const goToStart: Effect.Effect<never, never, number> = RefSubject.set(page, 0)
|
|
85
|
+
const goToEnd: Effect.Effect<never, E, number> = RefSubject.updateEffect(page, () => getTotalPages)
|
|
91
86
|
|
|
92
|
-
const
|
|
87
|
+
const paginated: RefSubject.Computed<never, E, ReadonlyArray<A>> = RefSubject.map(
|
|
88
|
+
combined,
|
|
93
89
|
([page, pageSize, results]) => {
|
|
94
90
|
const start = page * pageSize
|
|
95
91
|
const end = start + pageSize
|
|
96
|
-
const total = results.length
|
|
97
92
|
|
|
98
|
-
return
|
|
99
|
-
from: start + 1,
|
|
100
|
-
to: Math.min(end, total),
|
|
101
|
-
total
|
|
102
|
-
}
|
|
93
|
+
return results.slice(start, end)
|
|
103
94
|
}
|
|
104
95
|
)
|
|
105
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
|
+
|
|
106
109
|
return {
|
|
107
110
|
page,
|
|
108
111
|
pageSize,
|