xote 4.2.0 → 4.3.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/README.md +5 -5
- package/dist/xote.cjs +1 -1
- package/dist/xote.mjs +523 -613
- package/dist/xote.umd.js +1 -1
- package/package.json +7 -9
- package/rescript.json +30 -0
- package/src/Xote.res +23 -0
- package/src/Xote.res.mjs +88 -0
- package/src/Xote__Component.res +495 -0
- package/src/Xote__Component.res.mjs +487 -0
- package/src/Xote__JSX.res +305 -0
- package/src/Xote__JSX.res.mjs +244 -0
- package/src/Xote__Route.res +62 -0
- package/src/Xote__Route.res.mjs +56 -0
- package/src/Xote__Router.res +130 -0
- package/src/Xote__Router.res.mjs +116 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
open Signals
|
|
2
|
+
module Component = Xote__Component
|
|
3
|
+
|
|
4
|
+
/* ReScript JSX transform type aliases */
|
|
5
|
+
type element = Component.node
|
|
6
|
+
|
|
7
|
+
type component<'props> = 'props => element
|
|
8
|
+
|
|
9
|
+
type componentLike<'props, 'return> = 'props => 'return
|
|
10
|
+
|
|
11
|
+
/* JSX functions for component creation */
|
|
12
|
+
let jsx = (component: component<'props>, props: 'props): element => component(props)
|
|
13
|
+
|
|
14
|
+
let jsxs = (component: component<'props>, props: 'props): element => component(props)
|
|
15
|
+
|
|
16
|
+
let jsxKeyed = (
|
|
17
|
+
component: component<'props>,
|
|
18
|
+
props: 'props,
|
|
19
|
+
~key: option<string>=?,
|
|
20
|
+
_: unit,
|
|
21
|
+
): element => {
|
|
22
|
+
let _ = key /* TODO: Implement key support for list reconciliation */
|
|
23
|
+
component(props)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let jsxsKeyed = (
|
|
27
|
+
component: component<'props>,
|
|
28
|
+
props: 'props,
|
|
29
|
+
~key: option<string>=?,
|
|
30
|
+
_: unit,
|
|
31
|
+
): element => {
|
|
32
|
+
let _ = key
|
|
33
|
+
component(props)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* Fragment support */
|
|
37
|
+
type fragmentProps = {children?: element}
|
|
38
|
+
|
|
39
|
+
let jsxFragment = (props: fragmentProps): element => {
|
|
40
|
+
switch props.children {
|
|
41
|
+
| Some(child) => child
|
|
42
|
+
| None => Component.fragment([])
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Element converters for JSX expressions */
|
|
47
|
+
let array = (children: array<element>): element => Component.fragment(children)
|
|
48
|
+
|
|
49
|
+
let null = (): element => Component.text("")
|
|
50
|
+
|
|
51
|
+
/* Elements module for lowercase HTML tags */
|
|
52
|
+
module Elements = {
|
|
53
|
+
/* Attribute value type that can be static, signal, or computed */
|
|
54
|
+
@unboxed
|
|
55
|
+
type rec attributeValue = Any('a): attributeValue
|
|
56
|
+
|
|
57
|
+
/* Automatic conversion from string to attributeValue */
|
|
58
|
+
external fromString: string => attributeValue = "%identity"
|
|
59
|
+
|
|
60
|
+
/* Helper to convert a signal to an attributeValue */
|
|
61
|
+
let signal = (s: Signals.Signal.t<string>): attributeValue => Any(s)
|
|
62
|
+
|
|
63
|
+
/* Helper to convert a computed function to an attributeValue */
|
|
64
|
+
let computed = (f: unit => string): attributeValue => Any(f)
|
|
65
|
+
|
|
66
|
+
/* Props type for HTML elements - supports common attributes and events */
|
|
67
|
+
type props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data> = {
|
|
68
|
+
/* Standard attributes - can be static strings or reactive values */
|
|
69
|
+
id?: 'id,
|
|
70
|
+
class?: 'class,
|
|
71
|
+
style?: 'style,
|
|
72
|
+
/* Input attributes */
|
|
73
|
+
@as("type") type_?: 'typ,
|
|
74
|
+
value?: 'value,
|
|
75
|
+
placeholder?: 'placeholder,
|
|
76
|
+
disabled?: bool,
|
|
77
|
+
checked?: bool,
|
|
78
|
+
/* Link attributes */
|
|
79
|
+
href?: 'href,
|
|
80
|
+
target?: 'target,
|
|
81
|
+
/* Data attributes */
|
|
82
|
+
data?: 'data,
|
|
83
|
+
/* Event handlers */
|
|
84
|
+
onClick?: Dom.event => unit,
|
|
85
|
+
onInput?: Dom.event => unit,
|
|
86
|
+
onChange?: Dom.event => unit,
|
|
87
|
+
onSubmit?: Dom.event => unit,
|
|
88
|
+
onFocus?: Dom.event => unit,
|
|
89
|
+
onBlur?: Dom.event => unit,
|
|
90
|
+
onKeyDown?: Dom.event => unit,
|
|
91
|
+
onKeyUp?: Dom.event => unit,
|
|
92
|
+
onMouseEnter?: Dom.event => unit,
|
|
93
|
+
onMouseLeave?: Dom.event => unit,
|
|
94
|
+
/* Children */
|
|
95
|
+
children?: element,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/* Helper to detect if a value is a signal (has an id property) */
|
|
99
|
+
@get external hasId: 'a => option<int> = "id"
|
|
100
|
+
|
|
101
|
+
/* Helper to convert any value to Component.attrValue */
|
|
102
|
+
let convertAttrValue = (key: string, value: 'a): (string, Component.attrValue) => {
|
|
103
|
+
// Check if it's a function (computed)
|
|
104
|
+
if typeof(value) == #function {
|
|
105
|
+
// It's a computed function
|
|
106
|
+
let f: unit => string = Obj.magic(value)
|
|
107
|
+
Component.computedAttr(key, f)
|
|
108
|
+
} else if typeof(value) == #object && hasId(value)->Option.isSome {
|
|
109
|
+
// It's a signal (has an id property)
|
|
110
|
+
let sig: Signal.t<string> = Obj.magic(value)
|
|
111
|
+
Component.signalAttr(key, sig)
|
|
112
|
+
} else {
|
|
113
|
+
// It's a static string
|
|
114
|
+
let s: string = Obj.magic(value)
|
|
115
|
+
Component.attr(key, s)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Convert props to attrs array */
|
|
120
|
+
let propsToAttrs = (
|
|
121
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
122
|
+
): array<(string, Component.attrValue)> => {
|
|
123
|
+
let attrs = []
|
|
124
|
+
|
|
125
|
+
switch props.id {
|
|
126
|
+
| Some(v) => attrs->Array.push(convertAttrValue("id", v))
|
|
127
|
+
| None => ()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
switch props.class {
|
|
131
|
+
| Some(v) => attrs->Array.push(convertAttrValue("class", v))
|
|
132
|
+
| None => ()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
switch props.style {
|
|
136
|
+
| Some(v) => attrs->Array.push(convertAttrValue("style", v))
|
|
137
|
+
| None => ()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
switch props.type_ {
|
|
141
|
+
| Some(v) => attrs->Array.push(convertAttrValue("type", v))
|
|
142
|
+
| None => ()
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
switch props.value {
|
|
146
|
+
| Some(v) => attrs->Array.push(convertAttrValue("value", v))
|
|
147
|
+
| None => ()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
switch props.placeholder {
|
|
151
|
+
| Some(v) => attrs->Array.push(convertAttrValue("placeholder", v))
|
|
152
|
+
| None => ()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
switch props.disabled {
|
|
156
|
+
| Some(true) => attrs->Array.push(Component.attr("disabled", "true"))
|
|
157
|
+
| _ => ()
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
switch props.checked {
|
|
161
|
+
| Some(true) => attrs->Array.push(Component.attr("checked", "true"))
|
|
162
|
+
| _ => ()
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
switch props.href {
|
|
166
|
+
| Some(v) => attrs->Array.push(convertAttrValue("href", v))
|
|
167
|
+
| None => ()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
switch props.target {
|
|
171
|
+
| Some(v) => attrs->Array.push(convertAttrValue("target", v))
|
|
172
|
+
| None => ()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
switch props.data {
|
|
176
|
+
| Some(_dataObj) => {
|
|
177
|
+
let _ = %raw(`
|
|
178
|
+
Object.entries(_dataObj).forEach(([key, value]) => {
|
|
179
|
+
attrs.push(convertAttrValue("data-" + key, value))
|
|
180
|
+
})
|
|
181
|
+
`)
|
|
182
|
+
}
|
|
183
|
+
| None => ()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
attrs
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Convert props to events array */
|
|
190
|
+
let propsToEvents = (
|
|
191
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
192
|
+
): array<(string, Dom.event => unit)> => {
|
|
193
|
+
let events = []
|
|
194
|
+
|
|
195
|
+
switch props.onClick {
|
|
196
|
+
| Some(handler) => events->Array.push(("click", handler))
|
|
197
|
+
| None => ()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
switch props.onInput {
|
|
201
|
+
| Some(handler) => events->Array.push(("input", handler))
|
|
202
|
+
| None => ()
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
switch props.onChange {
|
|
206
|
+
| Some(handler) => events->Array.push(("change", handler))
|
|
207
|
+
| None => ()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
switch props.onSubmit {
|
|
211
|
+
| Some(handler) => events->Array.push(("submit", handler))
|
|
212
|
+
| None => ()
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
switch props.onFocus {
|
|
216
|
+
| Some(handler) => events->Array.push(("focus", handler))
|
|
217
|
+
| None => ()
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
switch props.onBlur {
|
|
221
|
+
| Some(handler) => events->Array.push(("blur", handler))
|
|
222
|
+
| None => ()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
switch props.onKeyDown {
|
|
226
|
+
| Some(handler) => events->Array.push(("keydown", handler))
|
|
227
|
+
| None => ()
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
switch props.onKeyUp {
|
|
231
|
+
| Some(handler) => events->Array.push(("keyup", handler))
|
|
232
|
+
| None => ()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
switch props.onMouseEnter {
|
|
236
|
+
| Some(handler) => events->Array.push(("mouseenter", handler))
|
|
237
|
+
| None => ()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
switch props.onMouseLeave {
|
|
241
|
+
| Some(handler) => events->Array.push(("mouseleave", handler))
|
|
242
|
+
| None => ()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
events
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/* Extract children from props */
|
|
249
|
+
let getChildren = (
|
|
250
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
251
|
+
): array<element> => {
|
|
252
|
+
switch props.children {
|
|
253
|
+
| Some(Fragment(children)) => children
|
|
254
|
+
| Some(child) => [child]
|
|
255
|
+
| None => []
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* Create an element from a tag string and props */
|
|
260
|
+
let createElement = (
|
|
261
|
+
tag: string,
|
|
262
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
263
|
+
): element => {
|
|
264
|
+
Component.Element({
|
|
265
|
+
tag,
|
|
266
|
+
attrs: propsToAttrs(props),
|
|
267
|
+
events: propsToEvents(props),
|
|
268
|
+
children: getChildren(props),
|
|
269
|
+
})
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* JSX functions for HTML elements */
|
|
273
|
+
let jsx = (
|
|
274
|
+
tag: string,
|
|
275
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
276
|
+
): element => createElement(tag, props)
|
|
277
|
+
|
|
278
|
+
let jsxs = (
|
|
279
|
+
tag: string,
|
|
280
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
281
|
+
): element => createElement(tag, props)
|
|
282
|
+
|
|
283
|
+
let jsxKeyed = (
|
|
284
|
+
tag: string,
|
|
285
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
286
|
+
~key: option<string>=?,
|
|
287
|
+
_: unit,
|
|
288
|
+
): element => {
|
|
289
|
+
let _ = key
|
|
290
|
+
createElement(tag, props)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
let jsxsKeyed = (
|
|
294
|
+
tag: string,
|
|
295
|
+
props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
|
|
296
|
+
~key: option<string>=?,
|
|
297
|
+
_: unit,
|
|
298
|
+
): element => {
|
|
299
|
+
let _ = key
|
|
300
|
+
createElement(tag, props)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/* Element helper for ReScript JSX type checking */
|
|
304
|
+
external someElement: element => option<element> = "%identity"
|
|
305
|
+
}
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Core__Option from "@rescript/core/src/Core__Option.res.mjs";
|
|
4
|
+
import * as Xote__Component from "./Xote__Component.res.mjs";
|
|
5
|
+
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
6
|
+
|
|
7
|
+
function jsx(component, props) {
|
|
8
|
+
return component(props);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function jsxs(component, props) {
|
|
12
|
+
return component(props);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function jsxKeyed(component, props, key, param) {
|
|
16
|
+
return component(props);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function jsxsKeyed(component, props, key, param) {
|
|
20
|
+
return component(props);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function jsxFragment(props) {
|
|
24
|
+
let child = props.children;
|
|
25
|
+
if (child !== undefined) {
|
|
26
|
+
return child;
|
|
27
|
+
} else {
|
|
28
|
+
return Xote__Component.fragment([]);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let array = Xote__Component.fragment;
|
|
33
|
+
|
|
34
|
+
function $$null() {
|
|
35
|
+
return Xote__Component.text("");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function signal(s) {
|
|
39
|
+
return s;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function computed(f) {
|
|
43
|
+
return f;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function convertAttrValue(key, value) {
|
|
47
|
+
if (typeof value === "function") {
|
|
48
|
+
return Xote__Component.computedAttr(key, value);
|
|
49
|
+
} else if (typeof value === "object" && Core__Option.isSome(value.id)) {
|
|
50
|
+
return Xote__Component.signalAttr(key, value);
|
|
51
|
+
} else {
|
|
52
|
+
return Xote__Component.attr(key, value);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function propsToAttrs(props) {
|
|
57
|
+
let attrs = [];
|
|
58
|
+
let v = props.id;
|
|
59
|
+
if (v !== undefined) {
|
|
60
|
+
attrs.push(convertAttrValue("id", Primitive_option.valFromOption(v)));
|
|
61
|
+
}
|
|
62
|
+
let v$1 = props.class;
|
|
63
|
+
if (v$1 !== undefined) {
|
|
64
|
+
attrs.push(convertAttrValue("class", Primitive_option.valFromOption(v$1)));
|
|
65
|
+
}
|
|
66
|
+
let v$2 = props.style;
|
|
67
|
+
if (v$2 !== undefined) {
|
|
68
|
+
attrs.push(convertAttrValue("style", Primitive_option.valFromOption(v$2)));
|
|
69
|
+
}
|
|
70
|
+
let v$3 = props.type;
|
|
71
|
+
if (v$3 !== undefined) {
|
|
72
|
+
attrs.push(convertAttrValue("type", Primitive_option.valFromOption(v$3)));
|
|
73
|
+
}
|
|
74
|
+
let v$4 = props.value;
|
|
75
|
+
if (v$4 !== undefined) {
|
|
76
|
+
attrs.push(convertAttrValue("value", Primitive_option.valFromOption(v$4)));
|
|
77
|
+
}
|
|
78
|
+
let v$5 = props.placeholder;
|
|
79
|
+
if (v$5 !== undefined) {
|
|
80
|
+
attrs.push(convertAttrValue("placeholder", Primitive_option.valFromOption(v$5)));
|
|
81
|
+
}
|
|
82
|
+
let match = props.disabled;
|
|
83
|
+
if (match !== undefined && match) {
|
|
84
|
+
attrs.push(Xote__Component.attr("disabled", "true"));
|
|
85
|
+
}
|
|
86
|
+
let match$1 = props.checked;
|
|
87
|
+
if (match$1 !== undefined && match$1) {
|
|
88
|
+
attrs.push(Xote__Component.attr("checked", "true"));
|
|
89
|
+
}
|
|
90
|
+
let v$6 = props.href;
|
|
91
|
+
if (v$6 !== undefined) {
|
|
92
|
+
attrs.push(convertAttrValue("href", Primitive_option.valFromOption(v$6)));
|
|
93
|
+
}
|
|
94
|
+
let v$7 = props.target;
|
|
95
|
+
if (v$7 !== undefined) {
|
|
96
|
+
attrs.push(convertAttrValue("target", Primitive_option.valFromOption(v$7)));
|
|
97
|
+
}
|
|
98
|
+
let _dataObj = props.data;
|
|
99
|
+
if (_dataObj !== undefined) {
|
|
100
|
+
((Object.entries(_dataObj).forEach(([key, value]) => {
|
|
101
|
+
attrs.push(convertAttrValue("data-" + key, value))
|
|
102
|
+
})));
|
|
103
|
+
}
|
|
104
|
+
return attrs;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function propsToEvents(props) {
|
|
108
|
+
let events = [];
|
|
109
|
+
let handler = props.onClick;
|
|
110
|
+
if (handler !== undefined) {
|
|
111
|
+
events.push([
|
|
112
|
+
"click",
|
|
113
|
+
handler
|
|
114
|
+
]);
|
|
115
|
+
}
|
|
116
|
+
let handler$1 = props.onInput;
|
|
117
|
+
if (handler$1 !== undefined) {
|
|
118
|
+
events.push([
|
|
119
|
+
"input",
|
|
120
|
+
handler$1
|
|
121
|
+
]);
|
|
122
|
+
}
|
|
123
|
+
let handler$2 = props.onChange;
|
|
124
|
+
if (handler$2 !== undefined) {
|
|
125
|
+
events.push([
|
|
126
|
+
"change",
|
|
127
|
+
handler$2
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
let handler$3 = props.onSubmit;
|
|
131
|
+
if (handler$3 !== undefined) {
|
|
132
|
+
events.push([
|
|
133
|
+
"submit",
|
|
134
|
+
handler$3
|
|
135
|
+
]);
|
|
136
|
+
}
|
|
137
|
+
let handler$4 = props.onFocus;
|
|
138
|
+
if (handler$4 !== undefined) {
|
|
139
|
+
events.push([
|
|
140
|
+
"focus",
|
|
141
|
+
handler$4
|
|
142
|
+
]);
|
|
143
|
+
}
|
|
144
|
+
let handler$5 = props.onBlur;
|
|
145
|
+
if (handler$5 !== undefined) {
|
|
146
|
+
events.push([
|
|
147
|
+
"blur",
|
|
148
|
+
handler$5
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
let handler$6 = props.onKeyDown;
|
|
152
|
+
if (handler$6 !== undefined) {
|
|
153
|
+
events.push([
|
|
154
|
+
"keydown",
|
|
155
|
+
handler$6
|
|
156
|
+
]);
|
|
157
|
+
}
|
|
158
|
+
let handler$7 = props.onKeyUp;
|
|
159
|
+
if (handler$7 !== undefined) {
|
|
160
|
+
events.push([
|
|
161
|
+
"keyup",
|
|
162
|
+
handler$7
|
|
163
|
+
]);
|
|
164
|
+
}
|
|
165
|
+
let handler$8 = props.onMouseEnter;
|
|
166
|
+
if (handler$8 !== undefined) {
|
|
167
|
+
events.push([
|
|
168
|
+
"mouseenter",
|
|
169
|
+
handler$8
|
|
170
|
+
]);
|
|
171
|
+
}
|
|
172
|
+
let handler$9 = props.onMouseLeave;
|
|
173
|
+
if (handler$9 !== undefined) {
|
|
174
|
+
events.push([
|
|
175
|
+
"mouseleave",
|
|
176
|
+
handler$9
|
|
177
|
+
]);
|
|
178
|
+
}
|
|
179
|
+
return events;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function getChildren(props) {
|
|
183
|
+
let child = props.children;
|
|
184
|
+
if (child !== undefined) {
|
|
185
|
+
if (child.TAG === "Fragment") {
|
|
186
|
+
return child._0;
|
|
187
|
+
} else {
|
|
188
|
+
return [child];
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
return [];
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function createElement(tag, props) {
|
|
196
|
+
return {
|
|
197
|
+
TAG: "Element",
|
|
198
|
+
tag: tag,
|
|
199
|
+
attrs: propsToAttrs(props),
|
|
200
|
+
events: propsToEvents(props),
|
|
201
|
+
children: getChildren(props)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
let jsx$1 = createElement;
|
|
206
|
+
|
|
207
|
+
let jsxs$1 = createElement;
|
|
208
|
+
|
|
209
|
+
function jsxKeyed$1(tag, props, key, param) {
|
|
210
|
+
return createElement(tag, props);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function jsxsKeyed$1(tag, props, key, param) {
|
|
214
|
+
return createElement(tag, props);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
let Elements = {
|
|
218
|
+
signal: signal,
|
|
219
|
+
computed: computed,
|
|
220
|
+
convertAttrValue: convertAttrValue,
|
|
221
|
+
propsToAttrs: propsToAttrs,
|
|
222
|
+
propsToEvents: propsToEvents,
|
|
223
|
+
getChildren: getChildren,
|
|
224
|
+
createElement: createElement,
|
|
225
|
+
jsx: jsx$1,
|
|
226
|
+
jsxs: jsxs$1,
|
|
227
|
+
jsxKeyed: jsxKeyed$1,
|
|
228
|
+
jsxsKeyed: jsxsKeyed$1
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
let Component;
|
|
232
|
+
|
|
233
|
+
export {
|
|
234
|
+
Component,
|
|
235
|
+
jsx,
|
|
236
|
+
jsxs,
|
|
237
|
+
jsxKeyed,
|
|
238
|
+
jsxsKeyed,
|
|
239
|
+
jsxFragment,
|
|
240
|
+
array,
|
|
241
|
+
$$null,
|
|
242
|
+
Elements,
|
|
243
|
+
}
|
|
244
|
+
/* Xote__Component Not a pure module */
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Pure route matching logic (no signals, no DOM)
|
|
2
|
+
|
|
3
|
+
// Route parameter map
|
|
4
|
+
type params = Dict.t<string>
|
|
5
|
+
|
|
6
|
+
// Match result
|
|
7
|
+
type matchResult =
|
|
8
|
+
| Match(params)
|
|
9
|
+
| NoMatch
|
|
10
|
+
|
|
11
|
+
// Route segment - either static or dynamic parameter
|
|
12
|
+
type segment =
|
|
13
|
+
| Static(string)
|
|
14
|
+
| Param(string)
|
|
15
|
+
|
|
16
|
+
// Parse a route pattern like "/users/:id/posts/:postId"
|
|
17
|
+
// Returns array of segments, where dynamic segments are marked
|
|
18
|
+
let parsePattern = (pattern: string): array<segment> => {
|
|
19
|
+
pattern
|
|
20
|
+
->String.split("/")
|
|
21
|
+
->Array.filterMap(seg => {
|
|
22
|
+
if seg == "" {
|
|
23
|
+
None
|
|
24
|
+
} else if String.startsWith(seg, ":") {
|
|
25
|
+
Some(Param(String.sliceToEnd(seg, ~start=1)))
|
|
26
|
+
} else {
|
|
27
|
+
Some(Static(seg))
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Match a pathname against a parsed pattern
|
|
33
|
+
let matchPath = (pattern: array<segment>, pathname: string): matchResult => {
|
|
34
|
+
let pathSegments =
|
|
35
|
+
pathname
|
|
36
|
+
->String.split("/")
|
|
37
|
+
->Array.filter(s => s != "")
|
|
38
|
+
|
|
39
|
+
// Length must match
|
|
40
|
+
if Array.length(pattern) != Array.length(pathSegments) {
|
|
41
|
+
NoMatch
|
|
42
|
+
} else {
|
|
43
|
+
let params = Dict.make()
|
|
44
|
+
let matches = pattern->Array.everyWithIndex((seg, idx) => {
|
|
45
|
+
let pathSeg = pathSegments->Array.getUnsafe(idx)
|
|
46
|
+
switch seg {
|
|
47
|
+
| Static(expected) => pathSeg == expected
|
|
48
|
+
| Param(name) => {
|
|
49
|
+
params->Dict.set(name, pathSeg)
|
|
50
|
+
true
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
matches ? Match(params) : NoMatch
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Convenience: match a pattern string against pathname
|
|
60
|
+
let match = (pattern: string, pathname: string): matchResult => {
|
|
61
|
+
matchPath(parsePattern(pattern), pathname)
|
|
62
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Core__Array from "@rescript/core/src/Core__Array.res.mjs";
|
|
4
|
+
|
|
5
|
+
function parsePattern(pattern) {
|
|
6
|
+
return Core__Array.filterMap(pattern.split("/"), seg => {
|
|
7
|
+
if (seg === "") {
|
|
8
|
+
return;
|
|
9
|
+
} else if (seg.startsWith(":")) {
|
|
10
|
+
return {
|
|
11
|
+
TAG: "Param",
|
|
12
|
+
_0: seg.slice(1)
|
|
13
|
+
};
|
|
14
|
+
} else {
|
|
15
|
+
return {
|
|
16
|
+
TAG: "Static",
|
|
17
|
+
_0: seg
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function matchPath(pattern, pathname) {
|
|
24
|
+
let pathSegments = pathname.split("/").filter(s => s !== "");
|
|
25
|
+
if (pattern.length !== pathSegments.length) {
|
|
26
|
+
return "NoMatch";
|
|
27
|
+
}
|
|
28
|
+
let params = {};
|
|
29
|
+
let matches = pattern.every((seg, idx) => {
|
|
30
|
+
let pathSeg = pathSegments[idx];
|
|
31
|
+
if (seg.TAG === "Static") {
|
|
32
|
+
return pathSeg === seg._0;
|
|
33
|
+
}
|
|
34
|
+
params[seg._0] = pathSeg;
|
|
35
|
+
return true;
|
|
36
|
+
});
|
|
37
|
+
if (matches) {
|
|
38
|
+
return {
|
|
39
|
+
TAG: "Match",
|
|
40
|
+
_0: params
|
|
41
|
+
};
|
|
42
|
+
} else {
|
|
43
|
+
return "NoMatch";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function match(pattern, pathname) {
|
|
48
|
+
return matchPath(parsePattern(pattern), pathname);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export {
|
|
52
|
+
parsePattern,
|
|
53
|
+
matchPath,
|
|
54
|
+
match,
|
|
55
|
+
}
|
|
56
|
+
/* No side effect */
|