xote 4.3.1 → 4.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xote",
3
- "version": "4.3.1",
3
+ "version": "4.4.0",
4
4
  "repository": {
5
5
  "url": "https://github.com/brnrdog/xote"
6
6
  },
package/src/Xote__JSX.res CHANGED
@@ -63,23 +63,76 @@ module Elements = {
63
63
  /* Helper to convert a computed function to an attributeValue */
64
64
  let computed = (f: unit => string): attributeValue => Any(f)
65
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 */
66
+ /* Props type for HTML elements - supports common attributes and events
67
+ * String-like attributes use polymorphic types to accept strings, signals, or computed functions
68
+ */
69
+ type props<
70
+ 'id,
71
+ 'class,
72
+ 'style,
73
+ 'typ,
74
+ 'name,
75
+ 'value,
76
+ 'placeholder,
77
+ 'min,
78
+ 'max,
79
+ 'step,
80
+ 'pattern,
81
+ 'autoComplete,
82
+ 'accept,
83
+ 'forAttr,
84
+ 'href,
85
+ 'target,
86
+ 'src,
87
+ 'alt,
88
+ 'width,
89
+ 'height,
90
+ 'role,
91
+ 'ariaLabel,
92
+ > = {
93
+ /* Standard attributes - can be static strings, signals, or computed values */
69
94
  id?: 'id,
70
95
  class?: 'class,
71
96
  style?: 'style,
72
- /* Input attributes */
97
+ /* Form/Input attributes */
73
98
  @as("type") type_?: 'typ,
99
+ name?: 'name,
74
100
  value?: 'value,
75
101
  placeholder?: 'placeholder,
76
102
  disabled?: bool,
77
103
  checked?: bool,
104
+ required?: bool,
105
+ readOnly?: bool,
106
+ maxLength?: int,
107
+ minLength?: int,
108
+ min?: 'min,
109
+ max?: 'max,
110
+ step?: 'step,
111
+ pattern?: 'pattern,
112
+ autoComplete?: 'autoComplete,
113
+ multiple?: bool,
114
+ accept?: 'accept,
115
+ rows?: int,
116
+ cols?: int,
117
+ /* Label attributes */
118
+ @as("for") for_?: 'forAttr,
78
119
  /* Link attributes */
79
120
  href?: 'href,
80
121
  target?: 'target,
122
+ /* Image attributes */
123
+ src?: 'src,
124
+ alt?: 'alt,
125
+ width?: 'width,
126
+ height?: 'height,
127
+ /* Accessibility attributes */
128
+ role?: 'role,
129
+ tabIndex?: int,
130
+ @as("aria-label") ariaLabel?: 'ariaLabel,
131
+ @as("aria-hidden") ariaHidden?: bool,
132
+ @as("aria-expanded") ariaExpanded?: bool,
133
+ @as("aria-selected") ariaSelected?: bool,
81
134
  /* Data attributes */
82
- data?: 'data,
135
+ data?: Obj.t,
83
136
  /* Event handlers */
84
137
  onClick?: Dom.event => unit,
85
138
  onInput?: Dom.event => unit,
@@ -118,10 +171,34 @@ module Elements = {
118
171
 
119
172
  /* Convert props to attrs array */
120
173
  let propsToAttrs = (
121
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
174
+ props: props<
175
+ 'id,
176
+ 'class,
177
+ 'style,
178
+ 'typ,
179
+ 'name,
180
+ 'value,
181
+ 'placeholder,
182
+ 'min,
183
+ 'max,
184
+ 'step,
185
+ 'pattern,
186
+ 'autoComplete,
187
+ 'accept,
188
+ 'forAttr,
189
+ 'href,
190
+ 'target,
191
+ 'src,
192
+ 'alt,
193
+ 'width,
194
+ 'height,
195
+ 'role,
196
+ 'ariaLabel,
197
+ >,
122
198
  ): array<(string, Component.attrValue)> => {
123
199
  let attrs = []
124
200
 
201
+ /* Standard attributes */
125
202
  switch props.id {
126
203
  | Some(v) => attrs->Array.push(convertAttrValue("id", v))
127
204
  | None => ()
@@ -137,11 +214,17 @@ module Elements = {
137
214
  | None => ()
138
215
  }
139
216
 
217
+ /* Form/Input attributes */
140
218
  switch props.type_ {
141
219
  | Some(v) => attrs->Array.push(convertAttrValue("type", v))
142
220
  | None => ()
143
221
  }
144
222
 
223
+ switch props.name {
224
+ | Some(v) => attrs->Array.push(convertAttrValue("name", v))
225
+ | None => ()
226
+ }
227
+
145
228
  switch props.value {
146
229
  | Some(v) => attrs->Array.push(convertAttrValue("value", v))
147
230
  | None => ()
@@ -162,6 +245,78 @@ module Elements = {
162
245
  | _ => ()
163
246
  }
164
247
 
248
+ switch props.required {
249
+ | Some(true) => attrs->Array.push(Component.attr("required", "true"))
250
+ | _ => ()
251
+ }
252
+
253
+ switch props.readOnly {
254
+ | Some(true) => attrs->Array.push(Component.attr("readonly", "true"))
255
+ | _ => ()
256
+ }
257
+
258
+ switch props.maxLength {
259
+ | Some(v) => attrs->Array.push(Component.attr("maxlength", Int.toString(v)))
260
+ | None => ()
261
+ }
262
+
263
+ switch props.minLength {
264
+ | Some(v) => attrs->Array.push(Component.attr("minlength", Int.toString(v)))
265
+ | None => ()
266
+ }
267
+
268
+ switch props.min {
269
+ | Some(v) => attrs->Array.push(convertAttrValue("min", v))
270
+ | None => ()
271
+ }
272
+
273
+ switch props.max {
274
+ | Some(v) => attrs->Array.push(convertAttrValue("max", v))
275
+ | None => ()
276
+ }
277
+
278
+ switch props.step {
279
+ | Some(v) => attrs->Array.push(convertAttrValue("step", v))
280
+ | None => ()
281
+ }
282
+
283
+ switch props.pattern {
284
+ | Some(v) => attrs->Array.push(convertAttrValue("pattern", v))
285
+ | None => ()
286
+ }
287
+
288
+ switch props.autoComplete {
289
+ | Some(v) => attrs->Array.push(convertAttrValue("autocomplete", v))
290
+ | None => ()
291
+ }
292
+
293
+ switch props.multiple {
294
+ | Some(true) => attrs->Array.push(Component.attr("multiple", "true"))
295
+ | _ => ()
296
+ }
297
+
298
+ switch props.accept {
299
+ | Some(v) => attrs->Array.push(convertAttrValue("accept", v))
300
+ | None => ()
301
+ }
302
+
303
+ switch props.rows {
304
+ | Some(v) => attrs->Array.push(Component.attr("rows", Int.toString(v)))
305
+ | None => ()
306
+ }
307
+
308
+ switch props.cols {
309
+ | Some(v) => attrs->Array.push(Component.attr("cols", Int.toString(v)))
310
+ | None => ()
311
+ }
312
+
313
+ /* Label attributes */
314
+ switch props.for_ {
315
+ | Some(v) => attrs->Array.push(convertAttrValue("for", v))
316
+ | None => ()
317
+ }
318
+
319
+ /* Link attributes */
165
320
  switch props.href {
166
321
  | Some(v) => attrs->Array.push(convertAttrValue("href", v))
167
322
  | None => ()
@@ -172,6 +327,62 @@ module Elements = {
172
327
  | None => ()
173
328
  }
174
329
 
330
+ /* Image attributes */
331
+ switch props.src {
332
+ | Some(v) => attrs->Array.push(convertAttrValue("src", v))
333
+ | None => ()
334
+ }
335
+
336
+ switch props.alt {
337
+ | Some(v) => attrs->Array.push(convertAttrValue("alt", v))
338
+ | None => ()
339
+ }
340
+
341
+ switch props.width {
342
+ | Some(v) => attrs->Array.push(convertAttrValue("width", v))
343
+ | None => ()
344
+ }
345
+
346
+ switch props.height {
347
+ | Some(v) => attrs->Array.push(convertAttrValue("height", v))
348
+ | None => ()
349
+ }
350
+
351
+ /* Accessibility attributes */
352
+ switch props.role {
353
+ | Some(v) => attrs->Array.push(convertAttrValue("role", v))
354
+ | None => ()
355
+ }
356
+
357
+ switch props.tabIndex {
358
+ | Some(v) => attrs->Array.push(Component.attr("tabindex", Int.toString(v)))
359
+ | None => ()
360
+ }
361
+
362
+ switch props.ariaLabel {
363
+ | Some(v) => attrs->Array.push(convertAttrValue("aria-label", v))
364
+ | None => ()
365
+ }
366
+
367
+ switch props.ariaHidden {
368
+ | Some(true) => attrs->Array.push(Component.attr("aria-hidden", "true"))
369
+ | Some(false) => attrs->Array.push(Component.attr("aria-hidden", "false"))
370
+ | None => ()
371
+ }
372
+
373
+ switch props.ariaExpanded {
374
+ | Some(true) => attrs->Array.push(Component.attr("aria-expanded", "true"))
375
+ | Some(false) => attrs->Array.push(Component.attr("aria-expanded", "false"))
376
+ | None => ()
377
+ }
378
+
379
+ switch props.ariaSelected {
380
+ | Some(true) => attrs->Array.push(Component.attr("aria-selected", "true"))
381
+ | Some(false) => attrs->Array.push(Component.attr("aria-selected", "false"))
382
+ | None => ()
383
+ }
384
+
385
+ /* Data attributes */
175
386
  switch props.data {
176
387
  | Some(_dataObj) => {
177
388
  let _ = %raw(`
@@ -188,7 +399,30 @@ module Elements = {
188
399
 
189
400
  /* Convert props to events array */
190
401
  let propsToEvents = (
191
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
402
+ props: props<
403
+ 'id,
404
+ 'class,
405
+ 'style,
406
+ 'typ,
407
+ 'name,
408
+ 'value,
409
+ 'placeholder,
410
+ 'min,
411
+ 'max,
412
+ 'step,
413
+ 'pattern,
414
+ 'autoComplete,
415
+ 'accept,
416
+ 'forAttr,
417
+ 'href,
418
+ 'target,
419
+ 'src,
420
+ 'alt,
421
+ 'width,
422
+ 'height,
423
+ 'role,
424
+ 'ariaLabel,
425
+ >,
192
426
  ): array<(string, Dom.event => unit)> => {
193
427
  let events = []
194
428
 
@@ -247,7 +481,30 @@ module Elements = {
247
481
 
248
482
  /* Extract children from props */
249
483
  let getChildren = (
250
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
484
+ props: props<
485
+ 'id,
486
+ 'class,
487
+ 'style,
488
+ 'typ,
489
+ 'name,
490
+ 'value,
491
+ 'placeholder,
492
+ 'min,
493
+ 'max,
494
+ 'step,
495
+ 'pattern,
496
+ 'autoComplete,
497
+ 'accept,
498
+ 'forAttr,
499
+ 'href,
500
+ 'target,
501
+ 'src,
502
+ 'alt,
503
+ 'width,
504
+ 'height,
505
+ 'role,
506
+ 'ariaLabel,
507
+ >,
251
508
  ): array<element> => {
252
509
  switch props.children {
253
510
  | Some(Fragment(children)) => children
@@ -259,7 +516,30 @@ module Elements = {
259
516
  /* Create an element from a tag string and props */
260
517
  let createElement = (
261
518
  tag: string,
262
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
519
+ props: props<
520
+ 'id,
521
+ 'class,
522
+ 'style,
523
+ 'typ,
524
+ 'name,
525
+ 'value,
526
+ 'placeholder,
527
+ 'min,
528
+ 'max,
529
+ 'step,
530
+ 'pattern,
531
+ 'autoComplete,
532
+ 'accept,
533
+ 'forAttr,
534
+ 'href,
535
+ 'target,
536
+ 'src,
537
+ 'alt,
538
+ 'width,
539
+ 'height,
540
+ 'role,
541
+ 'ariaLabel,
542
+ >,
263
543
  ): element => {
264
544
  Component.Element({
265
545
  tag,
@@ -272,17 +552,86 @@ module Elements = {
272
552
  /* JSX functions for HTML elements */
273
553
  let jsx = (
274
554
  tag: string,
275
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
555
+ props: props<
556
+ 'id,
557
+ 'class,
558
+ 'style,
559
+ 'typ,
560
+ 'name,
561
+ 'value,
562
+ 'placeholder,
563
+ 'min,
564
+ 'max,
565
+ 'step,
566
+ 'pattern,
567
+ 'autoComplete,
568
+ 'accept,
569
+ 'forAttr,
570
+ 'href,
571
+ 'target,
572
+ 'src,
573
+ 'alt,
574
+ 'width,
575
+ 'height,
576
+ 'role,
577
+ 'ariaLabel,
578
+ >,
276
579
  ): element => createElement(tag, props)
277
580
 
278
581
  let jsxs = (
279
582
  tag: string,
280
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
583
+ props: props<
584
+ 'id,
585
+ 'class,
586
+ 'style,
587
+ 'typ,
588
+ 'name,
589
+ 'value,
590
+ 'placeholder,
591
+ 'min,
592
+ 'max,
593
+ 'step,
594
+ 'pattern,
595
+ 'autoComplete,
596
+ 'accept,
597
+ 'forAttr,
598
+ 'href,
599
+ 'target,
600
+ 'src,
601
+ 'alt,
602
+ 'width,
603
+ 'height,
604
+ 'role,
605
+ 'ariaLabel,
606
+ >,
281
607
  ): element => createElement(tag, props)
282
608
 
283
609
  let jsxKeyed = (
284
610
  tag: string,
285
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
611
+ props: props<
612
+ 'id,
613
+ 'class,
614
+ 'style,
615
+ 'typ,
616
+ 'name,
617
+ 'value,
618
+ 'placeholder,
619
+ 'min,
620
+ 'max,
621
+ 'step,
622
+ 'pattern,
623
+ 'autoComplete,
624
+ 'accept,
625
+ 'forAttr,
626
+ 'href,
627
+ 'target,
628
+ 'src,
629
+ 'alt,
630
+ 'width,
631
+ 'height,
632
+ 'role,
633
+ 'ariaLabel,
634
+ >,
286
635
  ~key: option<string>=?,
287
636
  _: unit,
288
637
  ): element => {
@@ -292,7 +641,30 @@ module Elements = {
292
641
 
293
642
  let jsxsKeyed = (
294
643
  tag: string,
295
- props: props<'id, 'class, 'style, 'typ, 'value, 'placeholder, 'href, 'target, 'data>,
644
+ props: props<
645
+ 'id,
646
+ 'class,
647
+ 'style,
648
+ 'typ,
649
+ 'name,
650
+ 'value,
651
+ 'placeholder,
652
+ 'min,
653
+ 'max,
654
+ 'step,
655
+ 'pattern,
656
+ 'autoComplete,
657
+ 'accept,
658
+ 'forAttr,
659
+ 'href,
660
+ 'target,
661
+ 'src,
662
+ 'alt,
663
+ 'width,
664
+ 'height,
665
+ 'role,
666
+ 'ariaLabel,
667
+ >,
296
668
  ~key: option<string>=?,
297
669
  _: unit,
298
670
  ): element => {
@@ -71,13 +71,17 @@ function propsToAttrs(props) {
71
71
  if (v$3 !== undefined) {
72
72
  attrs.push(convertAttrValue("type", Primitive_option.valFromOption(v$3)));
73
73
  }
74
- let v$4 = props.value;
74
+ let v$4 = props.name;
75
75
  if (v$4 !== undefined) {
76
- attrs.push(convertAttrValue("value", Primitive_option.valFromOption(v$4)));
76
+ attrs.push(convertAttrValue("name", Primitive_option.valFromOption(v$4)));
77
77
  }
78
- let v$5 = props.placeholder;
78
+ let v$5 = props.value;
79
79
  if (v$5 !== undefined) {
80
- attrs.push(convertAttrValue("placeholder", Primitive_option.valFromOption(v$5)));
80
+ attrs.push(convertAttrValue("value", Primitive_option.valFromOption(v$5)));
81
+ }
82
+ let v$6 = props.placeholder;
83
+ if (v$6 !== undefined) {
84
+ attrs.push(convertAttrValue("placeholder", Primitive_option.valFromOption(v$6)));
81
85
  }
82
86
  let match = props.disabled;
83
87
  if (match !== undefined && match) {
@@ -87,13 +91,121 @@ function propsToAttrs(props) {
87
91
  if (match$1 !== undefined && match$1) {
88
92
  attrs.push(Xote__Component.attr("checked", "true"));
89
93
  }
90
- let v$6 = props.href;
91
- if (v$6 !== undefined) {
92
- attrs.push(convertAttrValue("href", Primitive_option.valFromOption(v$6)));
94
+ let match$2 = props.required;
95
+ if (match$2 !== undefined && match$2) {
96
+ attrs.push(Xote__Component.attr("required", "true"));
93
97
  }
94
- let v$7 = props.target;
98
+ let match$3 = props.readOnly;
99
+ if (match$3 !== undefined && match$3) {
100
+ attrs.push(Xote__Component.attr("readonly", "true"));
101
+ }
102
+ let v$7 = props.maxLength;
95
103
  if (v$7 !== undefined) {
96
- attrs.push(convertAttrValue("target", Primitive_option.valFromOption(v$7)));
104
+ attrs.push(Xote__Component.attr("maxlength", v$7.toString()));
105
+ }
106
+ let v$8 = props.minLength;
107
+ if (v$8 !== undefined) {
108
+ attrs.push(Xote__Component.attr("minlength", v$8.toString()));
109
+ }
110
+ let v$9 = props.min;
111
+ if (v$9 !== undefined) {
112
+ attrs.push(convertAttrValue("min", Primitive_option.valFromOption(v$9)));
113
+ }
114
+ let v$10 = props.max;
115
+ if (v$10 !== undefined) {
116
+ attrs.push(convertAttrValue("max", Primitive_option.valFromOption(v$10)));
117
+ }
118
+ let v$11 = props.step;
119
+ if (v$11 !== undefined) {
120
+ attrs.push(convertAttrValue("step", Primitive_option.valFromOption(v$11)));
121
+ }
122
+ let v$12 = props.pattern;
123
+ if (v$12 !== undefined) {
124
+ attrs.push(convertAttrValue("pattern", Primitive_option.valFromOption(v$12)));
125
+ }
126
+ let v$13 = props.autoComplete;
127
+ if (v$13 !== undefined) {
128
+ attrs.push(convertAttrValue("autocomplete", Primitive_option.valFromOption(v$13)));
129
+ }
130
+ let match$4 = props.multiple;
131
+ if (match$4 !== undefined && match$4) {
132
+ attrs.push(Xote__Component.attr("multiple", "true"));
133
+ }
134
+ let v$14 = props.accept;
135
+ if (v$14 !== undefined) {
136
+ attrs.push(convertAttrValue("accept", Primitive_option.valFromOption(v$14)));
137
+ }
138
+ let v$15 = props.rows;
139
+ if (v$15 !== undefined) {
140
+ attrs.push(Xote__Component.attr("rows", v$15.toString()));
141
+ }
142
+ let v$16 = props.cols;
143
+ if (v$16 !== undefined) {
144
+ attrs.push(Xote__Component.attr("cols", v$16.toString()));
145
+ }
146
+ let v$17 = props.for;
147
+ if (v$17 !== undefined) {
148
+ attrs.push(convertAttrValue("for", Primitive_option.valFromOption(v$17)));
149
+ }
150
+ let v$18 = props.href;
151
+ if (v$18 !== undefined) {
152
+ attrs.push(convertAttrValue("href", Primitive_option.valFromOption(v$18)));
153
+ }
154
+ let v$19 = props.target;
155
+ if (v$19 !== undefined) {
156
+ attrs.push(convertAttrValue("target", Primitive_option.valFromOption(v$19)));
157
+ }
158
+ let v$20 = props.src;
159
+ if (v$20 !== undefined) {
160
+ attrs.push(convertAttrValue("src", Primitive_option.valFromOption(v$20)));
161
+ }
162
+ let v$21 = props.alt;
163
+ if (v$21 !== undefined) {
164
+ attrs.push(convertAttrValue("alt", Primitive_option.valFromOption(v$21)));
165
+ }
166
+ let v$22 = props.width;
167
+ if (v$22 !== undefined) {
168
+ attrs.push(convertAttrValue("width", Primitive_option.valFromOption(v$22)));
169
+ }
170
+ let v$23 = props.height;
171
+ if (v$23 !== undefined) {
172
+ attrs.push(convertAttrValue("height", Primitive_option.valFromOption(v$23)));
173
+ }
174
+ let v$24 = props.role;
175
+ if (v$24 !== undefined) {
176
+ attrs.push(convertAttrValue("role", Primitive_option.valFromOption(v$24)));
177
+ }
178
+ let v$25 = props.tabIndex;
179
+ if (v$25 !== undefined) {
180
+ attrs.push(Xote__Component.attr("tabindex", v$25.toString()));
181
+ }
182
+ let v$26 = props["aria-label"];
183
+ if (v$26 !== undefined) {
184
+ attrs.push(convertAttrValue("aria-label", Primitive_option.valFromOption(v$26)));
185
+ }
186
+ let match$5 = props["aria-hidden"];
187
+ if (match$5 !== undefined) {
188
+ if (match$5) {
189
+ attrs.push(Xote__Component.attr("aria-hidden", "true"));
190
+ } else {
191
+ attrs.push(Xote__Component.attr("aria-hidden", "false"));
192
+ }
193
+ }
194
+ let match$6 = props["aria-expanded"];
195
+ if (match$6 !== undefined) {
196
+ if (match$6) {
197
+ attrs.push(Xote__Component.attr("aria-expanded", "true"));
198
+ } else {
199
+ attrs.push(Xote__Component.attr("aria-expanded", "false"));
200
+ }
201
+ }
202
+ let match$7 = props["aria-selected"];
203
+ if (match$7 !== undefined) {
204
+ if (match$7) {
205
+ attrs.push(Xote__Component.attr("aria-selected", "true"));
206
+ } else {
207
+ attrs.push(Xote__Component.attr("aria-selected", "false"));
208
+ }
97
209
  }
98
210
  let _dataObj = props.data;
99
211
  if (_dataObj !== undefined) {