@colisweb/rescript-toolkit 5.38.2 → 5.40.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": "@colisweb/rescript-toolkit",
3
- "version": "5.38.2",
3
+ "version": "5.40.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "clean": "rescript clean",
@@ -21,6 +21,34 @@ module type Config = {
21
21
  module Make = (StateLenses: Config) => {
22
22
  include Reform.Make(StateLenses)
23
23
 
24
+ module RadioGroupEnum = {
25
+ @react.component
26
+ let make = (
27
+ type enum,
28
+ ~enum: module(Toolkit__Utils.Enum with type t = enum),
29
+ ~field,
30
+ ~elements,
31
+ ~variant=?,
32
+ ~inline=?,
33
+ ~containerClassName=?,
34
+ ) =>
35
+ <Field
36
+ field
37
+ render={({handleChange, error, value}) =>
38
+ <React.Fragment>
39
+ <Toolkit__Ui_RadioGroupEnum
40
+ enum
41
+ ?containerClassName
42
+ ?variant
43
+ defaultValue=?value
44
+ onChange={v => handleChange(v)}
45
+ elements
46
+ ?inline
47
+ />
48
+ <ErrorMessage ?error />
49
+ </React.Fragment>}
50
+ />
51
+ }
24
52
  module RadioGroup = {
25
53
  @react.component
26
54
  let make = (~field, ~elements, ~variant=?, ~inline=?, ~containerClassName=?) =>
@@ -510,6 +538,69 @@ module Make = (StateLenses: Config) => {
510
538
  />
511
539
  }
512
540
  }
541
+ module SelectEnum = {
542
+ @react.component
543
+ let make = (
544
+ type enum,
545
+ ~enum: module(Toolkit__Utils.Enum with type t = enum),
546
+ ~field,
547
+ ~label=?,
548
+ ~id,
549
+ ~name=?,
550
+ ~options: Toolkit__Ui_SelectEnum.options<enum>,
551
+ ~placeholder=?,
552
+ ~autoFocus=?,
553
+ ~disabled=?,
554
+ ~isOptional=?,
555
+ ~className=?,
556
+ ) => {
557
+ <Field
558
+ field
559
+ render={({handleChange, error, value, validate, state}) => {
560
+ let isInvalid = error->Option.isSome
561
+
562
+ let onBlur = _ => {
563
+ switch state {
564
+ | Pristine => ()
565
+ | _ => validate()
566
+ }
567
+ }
568
+
569
+ <React.Fragment>
570
+ {switch label {
571
+ | None => React.null
572
+ | Some(label) =>
573
+ <Toolkit__Ui_Label
574
+ htmlFor=id
575
+ optionalMessage={isOptional->Option.getWithDefault(false)
576
+ ? <FormattedMessage defaultMessage="(Optionnel)" />
577
+ : React.null}>
578
+ label
579
+ </Toolkit__Ui_Label>
580
+ }}
581
+ <Toolkit__Ui_SelectEnum
582
+ enum
583
+ options
584
+ ?name
585
+ ?placeholder
586
+ ?autoFocus
587
+ ?className
588
+ isDisabled=?{disabled}
589
+ onBlur
590
+ id
591
+ isInvalid
592
+ ?value
593
+ onChange={selectedValue => {
594
+ Js.log(selectedValue)
595
+ handleChange(selectedValue)
596
+ }}
597
+ />
598
+ <ErrorMessage ?error />
599
+ </React.Fragment>
600
+ }}
601
+ />
602
+ }
603
+ }
513
604
 
514
605
  module SearchSelect = {
515
606
  open! ReactSelect
@@ -247,6 +247,10 @@ module Make = (Config: RouterConfig) => {
247
247
  ~isSubLink=false,
248
248
  ~isActive=false,
249
249
  ~onLinkClick=() => (),
250
+ ~containerClassName="",
251
+ ~labelClassName="",
252
+ ~tooltipClassName="",
253
+ ~activeClassName="",
250
254
  ) =>
251
255
  switch link {
252
256
  | Custom(element) => element
@@ -255,7 +259,12 @@ module Make = (Config: RouterConfig) => {
255
259
  <a
256
260
  href=url
257
261
  target="_blank"
258
- className={cx([commonClassName, "py-3 sidenav-link", isSubLink ? "ml-3" : ""])}>
262
+ className={cx([
263
+ commonClassName,
264
+ "py-3 sidenav-link",
265
+ isSubLink ? "ml-3" : "",
266
+ containerClassName,
267
+ ])}>
259
268
  <span className="overflow-hidden flex">
260
269
  {config.icon->Option.mapWithDefault(React.null, icon =>
261
270
  <div className={cx(["text-neutral-800", isNavOpen ? "pl-2" : "px-2"])}> icon </div>
@@ -265,13 +274,17 @@ module Make = (Config: RouterConfig) => {
265
274
  "transition-all duration-200 ease-in-out absolute ml-12 w-40 transform top-1/2 -translate-y-1/2",
266
275
  isNavOpen ? "pl-1 opacity-100 delay-75" : "opacity-0 invisible",
267
276
  isSubLink ? "text-sm" : "",
277
+ labelClassName,
268
278
  ])}>
269
279
  label
270
280
  </span>
271
281
  {isNavOpen
272
282
  ? React.null
273
283
  : <div
274
- className="sidenav-link-tooltip absolute left-0 p-2 bg-neutral-700 text-white rounded transform top-1/2 -translate-y-1/2 transition-all duration-200 ease-in-out opacity-0 invisible ml-16 whitespace-nowrap">
284
+ className={cx([
285
+ "sidenav-link-tooltip absolute left-0 p-2 bg-neutral-700 text-white rounded transform top-1/2 -translate-y-1/2 transition-all duration-200 ease-in-out opacity-0 invisible ml-16 whitespace-nowrap",
286
+ tooltipClassName,
287
+ ])}>
275
288
  label
276
289
  </div>}
277
290
  </span>
@@ -288,9 +301,14 @@ module Make = (Config: RouterConfig) => {
288
301
  isSubLink ? "ml-3" : "",
289
302
  config.icon->Option.isNone ? "py-5" : "",
290
303
  "sidenav-link",
304
+ containerClassName,
291
305
  isActive && !disabledActiveLink ? "bg-primary-100/75 text-neutral-700" : "",
306
+ isActive && !disabledActiveLink ? activeClassName : "",
307
+ ])}
308
+ activeClassName={cx([
309
+ disabledActiveLink ? "" : "bg-primary-100/75 text-neutral-700",
310
+ disabledActiveLink ? "" : activeClassName,
292
311
  ])}
293
- activeClassName={disabledActiveLink ? "" : "bg-primary-100/75 text-neutral-700"}
294
312
  onClick={_ => onLinkClick()}>
295
313
  <span className="overflow-hidden flex">
296
314
  {config.icon->Option.mapWithDefault(React.null, icon =>
@@ -301,13 +319,17 @@ module Make = (Config: RouterConfig) => {
301
319
  "transition-all duration-200 ease-in-out absolute ml-12 w-40 transform top-1/2 -translate-y-1/2",
302
320
  isNavOpen ? "pl-1 opacity-100 delay-75" : "opacity-0 invisible",
303
321
  isSubLink ? "text-sm" : "",
322
+ labelClassName,
304
323
  ])}>
305
324
  label
306
325
  </span>
307
326
  {isNavOpen
308
327
  ? React.null
309
328
  : <div
310
- className="sidenav-link-tooltip absolute left-0 p-2 bg-neutral-700 text-white rounded transform top-1/2 -translate-y-1/2 transition-all duration-200 ease-in-out opacity-0 invisible ml-16 whitespace-nowrap">
329
+ className={cx([
330
+ "sidenav-link-tooltip absolute left-0 p-2 bg-neutral-700 text-white rounded transform top-1/2 -translate-y-1/2 transition-all duration-200 ease-in-out opacity-0 invisible ml-16 whitespace-nowrap",
331
+ tooltipClassName,
332
+ ])}>
311
333
  label
312
334
  </div>}
313
335
  </span>
@@ -345,6 +367,8 @@ module Make = (Config: RouterConfig) => {
345
367
  ~isNavOpen: bool,
346
368
  ~onLinkClick=() => (),
347
369
  ~openMenu,
370
+ ~iconClassName="",
371
+ ~labelClassName="",
348
372
  ) => {
349
373
  let (isOpen, setIsOpen) = React.useState(() => false)
350
374
  let currentRoute = useRoute()
@@ -387,13 +411,15 @@ module Make = (Config: RouterConfig) => {
387
411
  "flex items-center w-full sidenav-link",
388
412
  hasActiveSubRoute || relativeRoute->Option.isSome ? "bg-primary-100/75" : "",
389
413
  ])}>
390
- <span className={cx(["mr-2 text-neutral-800", isNavOpen ? "pl-2" : "px-2"])}>
414
+ <span
415
+ className={cx(["mr-2 text-neutral-800", isNavOpen ? "pl-2" : "px-2", iconClassName])}>
391
416
  groupInfo.icon
392
417
  </span>
393
418
  <span
394
419
  className={cx([
395
420
  "transition-all duration-200 ease-in-out absolute ml-12 w-40 transform top-1/2 -translate-y-1/2 text-left",
396
421
  isNavOpen ? "pl-1 opacity-100 delay-75" : "opacity-0 invisible",
422
+ labelClassName,
397
423
  ])}>
398
424
  groupInfo.label
399
425
  </span>
@@ -0,0 +1,116 @@
1
+ type size = [#xs | #sm | #md | #lg]
2
+ type variant =
3
+ | Default
4
+ | Filled
5
+
6
+ @react.component
7
+ let make = (
8
+ type enum,
9
+ ~enum: module(Toolkit__Utils.Enum with type t = enum),
10
+ ~value: enum,
11
+ ~children: option<React.element>=?,
12
+ ~disabled: option<bool>=?,
13
+ ~onChange=?,
14
+ ~name=?,
15
+ ~checked=?,
16
+ ~className="",
17
+ ~contentClassName="",
18
+ ~size: size=#sm,
19
+ ~variant: variant=Default,
20
+ ~icon: option<module(ReactIcons.Icon)>=?,
21
+ ) => {
22
+ <label
23
+ className={cx([
24
+ "flex items-center cw-radio relative",
25
+ switch variant {
26
+ | Default => "cw-radio--default"
27
+ | Filled => "self-start bg-neutral-100 font-medium px-2 py-1 rounded-full cw-radio--filled"
28
+ },
29
+ disabled->Option.getWithDefault(false)
30
+ ? "cursor-not-allowed opacity-75 text-gray-600"
31
+ : "cursor-pointer",
32
+ className,
33
+ ])}>
34
+ <input
35
+ type_="radio"
36
+ value={Toolkit__Utils.encodeEnumToString(value, enum)}
37
+ className="opacity-0 w-0 h-0 peer cw-radio-input"
38
+ onChange={event => {
39
+ let target = ReactEvent.Form.target(event)
40
+ let value = target["value"]
41
+
42
+ onChange->Option.forEach(fn =>
43
+ fn(Toolkit__Utils.decodeEnumFromString(value, enum)->Option.getExn)
44
+ )
45
+ }}
46
+ ?disabled
47
+ ?name
48
+ ?checked
49
+ />
50
+ {switch variant {
51
+ | Default => React.null
52
+ | Filled =>
53
+ <span
54
+ className="cw-radio-filled-bg peer-checked:bg-primary-700 w-full h-full absolute rounded-full left-0 top-0"
55
+ />
56
+ }}
57
+ <span
58
+ className={cx([
59
+ "bg-white flex-shrink-0 cw-radio-circle",
60
+ {
61
+ switch variant {
62
+ | Default => "border peer-checked:border-4 peer-checked:border-primary-700 peer-checked:text-primary-700"
63
+ | Filled => "border-2 peer-checked:border-primary-700 peer-checked:text-white"
64
+ }
65
+ },
66
+ "checkmark rounded-full mr-2 border-neutral-700 transform transition-all ease-in-out flex items-center justify-center",
67
+ switch size {
68
+ | #xs => "w-4 h-4"
69
+ | #sm => "w-6 h-6"
70
+ | #md => "w-8 h-8"
71
+ | #lg => "w-10 h-10"
72
+ },
73
+ ])}>
74
+ {switch variant {
75
+ | Default => React.null
76
+ | Filled =>
77
+ <span
78
+ className={cx([
79
+ "transform transition-all ease-in-out cw-radio-circle-content rounded-full",
80
+ switch size {
81
+ | #xs => "w-2 h-2"
82
+ | #sm => "w-3 h-3"
83
+ | #md => "w-4 h-4"
84
+ | #lg => "w-6 h-6"
85
+ },
86
+ ])}
87
+ />
88
+ }}
89
+ </span>
90
+ <span
91
+ className={cx([
92
+ "flex flex-row items-center gap-2",
93
+ {
94
+ switch variant {
95
+ | Default => ""
96
+ | Filled => "peer-checked:text-white relative pr-1"
97
+ }
98
+ },
99
+ contentClassName,
100
+ ])}>
101
+ {icon->Option.mapWithDefault(React.null, icon => {
102
+ let module(Icon) = icon
103
+
104
+ <Icon
105
+ size={switch size {
106
+ | #lg => 26
107
+ | #md => 24
108
+ | #sm => 20
109
+ | #xs => 16
110
+ }}
111
+ />
112
+ })}
113
+ {children->Option.getWithDefault(React.null)}
114
+ </span>
115
+ </label>
116
+ }
@@ -0,0 +1,47 @@
1
+ module Radio = Toolkit__Ui_RadioEnum
2
+
3
+ type element<'value> = {
4
+ label: React.element,
5
+ value: 'value,
6
+ disabled?: bool,
7
+ }
8
+
9
+ @react.component
10
+ let make = (
11
+ type enum,
12
+ ~enum: module(Toolkit__Utils.Enum with type t = enum),
13
+ ~onChange,
14
+ ~elements: array<element<enum>>,
15
+ ~variant=?,
16
+ ~defaultValue: option<enum>=?,
17
+ ~containerClassName="",
18
+ ~inline=false,
19
+ ) => {
20
+ let name = "radio-" ++ ReachUi_AutoId.use("")
21
+ let (value, setValue) = React.useState(() => defaultValue)
22
+
23
+ React.useEffect(() => {
24
+ onChange(value)
25
+ None
26
+ }, [value])
27
+
28
+ <div className={cx([inline ? "flex gap-3" : "", containerClassName])}>
29
+ {elements
30
+ ->Array.mapWithIndex((i, element) =>
31
+ <Radio
32
+ enum
33
+ ?variant
34
+ key={"radio-" ++ string_of_int(i)}
35
+ value=element.value
36
+ name
37
+ disabled=?element.disabled
38
+ checked={Some(element.value) === value}
39
+ onChange={value => {
40
+ setValue(_ => Some(value))
41
+ }}>
42
+ element.label
43
+ </Radio>
44
+ )
45
+ ->React.array}
46
+ </div>
47
+ }
@@ -0,0 +1,47 @@
1
+ type selectOption<'value> = {
2
+ label: string,
3
+ value: 'value,
4
+ disabled?: bool,
5
+ }
6
+
7
+ type options<'value> = array<selectOption<'value>>
8
+
9
+ @react.component
10
+ let make = (
11
+ type enum,
12
+ ~enum: module(Toolkit__Utils.Enum with type t = enum),
13
+ ~options: options<enum>,
14
+ ~onChange: option<enum> => unit,
15
+ ~onBlur=?,
16
+ ~placeholder=?,
17
+ ~defaultValue=?,
18
+ ~isDisabled=?,
19
+ ~isInvalid=?,
20
+ ~className="",
21
+ ~containerClassName="",
22
+ ~id=?,
23
+ ~value: option<enum>=?,
24
+ ~name=?,
25
+ ~autoFocus=?,
26
+ ) =>
27
+ <Toolkit__Ui_Select
28
+ ?id
29
+ ?name
30
+ containerClassName
31
+ className
32
+ ?autoFocus
33
+ ?onBlur
34
+ ?isDisabled
35
+ ?isInvalid
36
+ ?placeholder
37
+ value=?{value->Option.map(v => Toolkit__Utils.encodeEnumToString(v, enum))}
38
+ defaultValue=?{defaultValue->Option.map(v => v->Toolkit__Utils.encodeEnumToString(enum))}
39
+ options={options->Array.map((option): Toolkit__Ui_Select.selectOption => {
40
+ label: option.label,
41
+ value: option.value->Toolkit__Utils.encodeEnumToString(enum),
42
+ disabled: ?option.disabled,
43
+ })}
44
+ onChange={value => {
45
+ value->Toolkit__Utils.decodeEnumFromString(enum)->onChange
46
+ }}
47
+ />