Preact.res
1// Preact.res
2/* Below is a number of aliases to the common `Jsx` module */
3type element = Jsx.element
4
5type component<'props> = Jsx.component<'props>
6
7type ref<'value> = {mutable current: 'value}
8
9type componentLike<'props, 'return> = Jsx.componentLike<'props, 'return>
10@module("preact")
11external render: (Jsx.element, Dom.element) => unit = "render"
12
13@module("preact/jsx-runtime")
14external jsx: (component<'props>, 'props) => element = "jsx"
15
16@module("preact/jsx-runtime")
17external jsxKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsx"
18
19@module("preact/jsx-runtime")
20external jsxs: (component<'props>, 'props) => element = "jsxs"
21
22@module("preact/jsx-runtime")
23external jsxsKeyed: (component<'props>, 'props, ~key: string=?, @ignore unit) => element = "jsxs"
24
25/* These identity functions and static values below are optional, but lets
26you move things easily to the `element` type. The only required thing to
27define though is `array`, which the JSX transform will output. */
28external array: array<element> => element = "%identity"
29@val external null: element = "null"
30
31/* option for when you want to conditionally render Some(element) or null */
32let option = (elem: option<element>): element => {
33 switch elem {
34 | Some(elem) => elem
35 | None => null
36 }
37}
38
39external float: float => element = "%identity"
40external int: int => element = "%identity"
41external string: string => element = "%identity"
42
43module ClassList = {
44 @scope("classList") @send
45 external add: (Dom.element, string) => unit = "add"
46
47 @scope("classList") @send
48 external remove: (Dom.element, string) => unit = "remove"
49}
50
51/* These are needed for Fragment (<> </>) support */
52type fragmentProps = {children?: element}
53
54@module("preact/jsx-runtime") external jsxFragment: component<fragmentProps> = "Fragment"
55
56@module("preact/compat") external createPortal: (element, Dom.element) => unit = "createPortal"
57
58/* The Elements module is the equivalent to the ReactDOM module in React. This holds things relevant to _lowercase_ JSX elements. */
59module Elements = {
60 /* Here you can control what props lowercase JSX elements should have.
61 A base that the React JSX transform uses is provided via JsxDOM.domProps,
62 but you can make this anything. The editor tooling will support
63 autocompletion etc for your specific type. */
64 type props = JsxDOM.domProps
65
66 @module("preact/jsx-runtime")
67 external jsx: (string, props) => Jsx.element = "jsx"
68
69 @module("preact/jsx-runtime")
70 external div: (string, props) => Jsx.element = "jsx"
71
72 @module("preact/jsx-runtime")
73 external jsxKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsx"
74
75 @module("preact/jsx-runtime")
76 external jsxs: (string, props) => Jsx.element = "jsxs"
77
78 @module("preact/jsx-runtime")
79 external jsxsKeyed: (string, props, ~key: string=?, @ignore unit) => Jsx.element = "jsxs"
80
81 external someElement: element => option<element> = "%identity"
82}
83
84// @module("preact/hooks")
85// external render: (element, Dom.element) => unit = "render"
86
87/* HOOKS */
88
89/*
90 * Yeah, we know this api isn't great. tl;dr: useReducer instead.
91 * It's because useState can take functions or non-function values and treats
92 * them differently. Lazy initializer + callback which returns state is the
93 * only way to safely have any type of state and be able to update it correctly.
94 */
95@module("preact/hooks")
96external useState: (@uncurry (unit => 'state)) => ('state, ('state => 'state) => unit) = "useState"
97
98@module("preact/hooks")
99external useReducer: (@uncurry ('state, 'action) => 'state, 'state) => ('state, 'action => unit) =
100 "useReducer"
101
102@module("preact/hooks")
103external useReducerWithMapState: (
104 @uncurry ('state, 'action) => 'state,
105 'initialState,
106 @uncurry ('initialState => 'state),
107) => ('state, 'action => unit) = "useReducer"
108
109@module("preact/hooks")
110external useEffectOnEveryRender: (@uncurry (unit => option<unit => unit>)) => unit = "useEffect"
111@module("preact/hooks")
112external useEffect: (@uncurry (unit => option<unit => unit>), 'deps) => unit = "useEffect"
113@module("preact/hooks")
114external useEffect0: (@uncurry (unit => option<unit => unit>), @as(json`[]`) _) => unit =
115 "useEffect"
116@module("preact/hooks")
117external useEffect1: (@uncurry (unit => option<unit => unit>), array<'a>) => unit = "useEffect"
118@module("preact/hooks")
119external useEffect2: (@uncurry (unit => option<unit => unit>), ('a, 'b)) => unit = "useEffect"
120@module("preact/hooks")
121external useEffect3: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c)) => unit = "useEffect"
122@module("preact/hooks")
123external useEffect4: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c, 'd)) => unit =
124 "useEffect"
125@module("preact/hooks")
126external useEffect5: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c, 'd, 'e)) => unit =
127 "useEffect"
128@module("preact/hooks")
129external useEffect6: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c, 'd, 'e, 'f)) => unit =
130 "useEffect"
131@module("preact/hooks")
132external useEffect7: (
133 @uncurry (unit => option<unit => unit>),
134 ('a, 'b, 'c, 'd, 'e, 'f, 'g),
135) => unit = "useEffect"
136
137@module("preact/hooks")
138external useLayoutEffectOnEveryRender: (@uncurry (unit => option<unit => unit>)) => unit =
139 "useLayoutEffect"
140@module("preact/hooks")
141external useLayoutEffect: (@uncurry (unit => option<unit => unit>), 'deps) => unit =
142 "useLayoutEffect"
143@module("preact/hooks")
144external useLayoutEffect0: (@uncurry (unit => option<unit => unit>), @as(json`[]`) _) => unit =
145 "useLayoutEffect"
146@module("preact/hooks")
147external useLayoutEffect1: (@uncurry (unit => option<unit => unit>), array<'a>) => unit =
148 "useLayoutEffect"
149@module("preact/hooks")
150external useLayoutEffect2: (@uncurry (unit => option<unit => unit>), ('a, 'b)) => unit =
151 "useLayoutEffect"
152@module("preact/hooks")
153external useLayoutEffect3: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c)) => unit =
154 "useLayoutEffect"
155@module("preact/hooks")
156external useLayoutEffect4: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c, 'd)) => unit =
157 "useLayoutEffect"
158@module("preact/hooks")
159external useLayoutEffect5: (@uncurry (unit => option<unit => unit>), ('a, 'b, 'c, 'd, 'e)) => unit =
160 "useLayoutEffect"
161@module("preact/hooks")
162external useLayoutEffect6: (
163 @uncurry (unit => option<unit => unit>),
164 ('a, 'b, 'c, 'd, 'e, 'f),
165) => unit = "useLayoutEffect"
166@module("preact/hooks")
167external useLayoutEffect7: (
168 @uncurry (unit => option<unit => unit>),
169 ('a, 'b, 'c, 'd, 'e, 'f, 'g),
170) => unit = "useLayoutEffect"
171
172@module("preact/hooks")
173external useMemo: (@uncurry (unit => 'any), 'deps) => 'any = "useMemo"
174
175@module("preact/hooks")
176external useMemo0: (@uncurry (unit => 'any), @as(json`[]`) _) => 'any = "useMemo"
177
178@module("preact/hooks")
179external useMemo1: (@uncurry (unit => 'any), array<'a>) => 'any = "useMemo"
180
181@module("preact/hooks")
182external useMemo2: (@uncurry (unit => 'any), ('a, 'b)) => 'any = "useMemo"
183
184@module("preact/hooks")
185external useMemo3: (@uncurry (unit => 'any), ('a, 'b, 'c)) => 'any = "useMemo"
186
187@module("preact/hooks")
188external useMemo4: (@uncurry (unit => 'any), ('a, 'b, 'c, 'd)) => 'any = "useMemo"
189
190@module("preact/hooks")
191external useMemo5: (@uncurry (unit => 'any), ('a, 'b, 'c, 'd, 'e)) => 'any = "useMemo"
192
193@module("preact/hooks")
194external useMemo6: (@uncurry (unit => 'any), ('a, 'b, 'c, 'd, 'e, 'f)) => 'any = "useMemo"
195
196@module("preact/hooks")
197external useMemo7: (@uncurry (unit => 'any), ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'any = "useMemo"
198
199@module("preact/hooks")
200external useCallback: ('f, 'deps) => 'f = "useCallback"
201
202@module("preact/hooks")
203external useCallback0: ('f, @as(json`[]`) _) => 'f = "useCallback"
204
205@module("preact/hooks")
206external useCallback1: ('f, array<'a>) => 'f = "useCallback"
207
208@module("preact/hooks")
209external useCallback2: ('f, ('a, 'b)) => 'f = "useCallback"
210
211@module("preact/hooks")
212external useCallback3: ('f, ('a, 'b, 'c)) => 'f = "useCallback"
213
214@module("preact/hooks")
215external useCallback4: ('f, ('a, 'b, 'c, 'd)) => 'f = "useCallback"
216
217@module("preact/hooks")
218external useCallback5: ('f, ('a, 'b, 'c, 'd, 'e)) => 'f = "useCallback"
219
220@module("preact/hooks")
221external useCallback6: ('callback, ('a, 'b, 'c, 'd, 'e, 'f)) => 'callback = "useCallback"
222
223@module("preact/hooks")
224external useCallback7: ('callback, ('a, 'b, 'c, 'd, 'e, 'f, 'g)) => 'callback = "useCallback"
225
226// @module("preact/hooks")
227// external useContext: Context.t<'any> => 'any = "useContext"
228
229@module("preact/hooks") external useRef: 'value => ref<'value> = "useRef"
230
231module Signal = {
232 type t<'value>
233
234 @set external set: (t<'value>, 'value) => unit = "value"
235
236 @get external get: t<'value> => 'value = "value"
237 @send external peek: t<'value> => 'value = "peek"
238
239 @module("@preact/signals")
240 external make: 'value => t<'value> = "signal"
241
242 @module("@preact/signals")
243 external computed: (@uncurry (unit => 'value)) => t<'value> = "computed"
244
245 @module("@preact/signals")
246 external effect: (@uncurry (unit => unit)) => unit = "effect"
247
248 @module("@preact/signals")
249 external effectWithCleanup: (@uncurry (unit => option<unit => unit>)) => unit = "effect"
250
251 @module("@preact/signals")
252 external batch: (@uncurry (unit => unit)) => unit = "batch"
253
254 @module("@preact/signals-react")
255 external useSignal: 'value => t<'value> = "useSignal"
256
257 @module("@preact/signals-react")
258 external useComputed: (@uncurry (unit => 'value)) => t<'value> = "useComputed"
259
260 @module("@preact/signals-react/runtime")
261 external useSignals: unit => unit = "useSignals"
262}
263
264//////////////////////////////////////////////////////////
265// Utilities
266//////////////////////////////////////////////////////////
267
268// Helper to conditionally add classes to a base class string
269let cls = (base: string, kv: Dict.t<bool>): string => {
270 Dict.keysToArray(kv)->Array.reduce(base, (out, key) => {
271 if Dict.getUnsafe(kv, key) {
272 out ++ ` ${key}`
273 } else {
274 out
275 }
276 })
277}