basefn 1.2.0 → 1.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.
Files changed (45) hide show
  1. package/package.json +3 -3
  2. package/src/Basefn.res +50 -0
  3. package/src/Basefn.res.mjs +40 -0
  4. package/src/Basefn__Utils.res.mjs +1 -1
  5. package/src/components/Basefn__AlertDialog.css +119 -0
  6. package/src/components/Basefn__AlertDialog.res +80 -0
  7. package/src/components/Basefn__AlertDialog.res.mjs +106 -0
  8. package/src/components/Basefn__AppLayout.css +10 -0
  9. package/src/components/Basefn__AspectRatio.css +16 -0
  10. package/src/components/Basefn__AspectRatio.res +26 -0
  11. package/src/components/Basefn__AspectRatio.res.mjs +33 -0
  12. package/src/components/Basefn__ButtonGroup.css +58 -0
  13. package/src/components/Basefn__ButtonGroup.res +33 -0
  14. package/src/components/Basefn__ButtonGroup.res.mjs +38 -0
  15. package/src/components/Basefn__Card.css +15 -0
  16. package/src/components/Basefn__ContextMenu.css +78 -0
  17. package/src/components/Basefn__ContextMenu.res +98 -0
  18. package/src/components/Basefn__ContextMenu.res.mjs +93 -0
  19. package/src/components/Basefn__Drawer.css +31 -0
  20. package/src/components/Basefn__Dropdown.res +3 -1
  21. package/src/components/Basefn__Dropdown.res.mjs +22 -43
  22. package/src/components/Basefn__HoverCard.css +93 -0
  23. package/src/components/Basefn__HoverCard.res +99 -0
  24. package/src/components/Basefn__HoverCard.res.mjs +109 -0
  25. package/src/components/Basefn__Modal.css +38 -0
  26. package/src/components/Basefn__Popover.css +98 -0
  27. package/src/components/Basefn__Popover.res +95 -0
  28. package/src/components/Basefn__Popover.res.mjs +107 -0
  29. package/src/components/Basefn__ScrollArea.css +76 -0
  30. package/src/components/Basefn__ScrollArea.res +60 -0
  31. package/src/components/Basefn__ScrollArea.res.mjs +63 -0
  32. package/src/components/Basefn__Skeleton.css +83 -0
  33. package/src/components/Basefn__Skeleton.res +55 -0
  34. package/src/components/Basefn__Skeleton.res.mjs +62 -0
  35. package/src/components/Basefn__Stepper.css +36 -0
  36. package/src/components/Basefn__Tabs.css +18 -0
  37. package/src/components/Basefn__Toggle.css +87 -0
  38. package/src/components/Basefn__Toggle.res +65 -0
  39. package/src/components/Basefn__Toggle.res.mjs +71 -0
  40. package/src/components/Basefn__ToggleGroup.css +110 -0
  41. package/src/components/Basefn__ToggleGroup.res +106 -0
  42. package/src/components/Basefn__ToggleGroup.res.mjs +89 -0
  43. package/src/components/Basefn__Topbar.css +44 -3
  44. package/src/components/Basefn__Topbar.res +1 -13
  45. package/src/components/Basefn__Topbar.res.mjs +1 -11
@@ -0,0 +1,83 @@
1
+ @import '../styles/variables.css';
2
+
3
+ .basefn-skeleton {
4
+ background-color: var(--basefn-bg-tertiary);
5
+ display: block;
6
+ }
7
+
8
+ /* Variants */
9
+ .basefn-skeleton--text {
10
+ height: 1em;
11
+ width: 100%;
12
+ border-radius: var(--basefn-radius-sm);
13
+ }
14
+
15
+ .basefn-skeleton--circular {
16
+ width: 2.5rem;
17
+ height: 2.5rem;
18
+ border-radius: var(--basefn-radius-full);
19
+ }
20
+
21
+ .basefn-skeleton--rectangular {
22
+ width: 100%;
23
+ height: 100px;
24
+ border-radius: var(--basefn-radius-md);
25
+ }
26
+
27
+ /* Animations */
28
+ @keyframes basefn-skeleton-pulse {
29
+ 0%, 100% {
30
+ opacity: 1;
31
+ }
32
+ 50% {
33
+ opacity: 0.5;
34
+ }
35
+ }
36
+
37
+ @keyframes basefn-skeleton-wave {
38
+ 0% {
39
+ transform: translateX(-100%);
40
+ }
41
+ 100% {
42
+ transform: translateX(100%);
43
+ }
44
+ }
45
+
46
+ .basefn-skeleton--pulse {
47
+ animation: basefn-skeleton-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
48
+ }
49
+
50
+ .basefn-skeleton--wave {
51
+ position: relative;
52
+ overflow: hidden;
53
+ }
54
+
55
+ .basefn-skeleton--wave::after {
56
+ content: '';
57
+ position: absolute;
58
+ top: 0;
59
+ left: 0;
60
+ right: 0;
61
+ bottom: 0;
62
+ background: linear-gradient(
63
+ 90deg,
64
+ transparent,
65
+ rgba(255, 255, 255, 0.3),
66
+ transparent
67
+ );
68
+ animation: basefn-skeleton-wave 1.5s infinite;
69
+ }
70
+
71
+ .basefn-skeleton--none {
72
+ animation: none;
73
+ }
74
+
75
+ /* Dark mode wave adjustment */
76
+ [data-theme="dark"] .basefn-skeleton--wave::after {
77
+ background: linear-gradient(
78
+ 90deg,
79
+ transparent,
80
+ rgba(255, 255, 255, 0.1),
81
+ transparent
82
+ );
83
+ }
@@ -0,0 +1,55 @@
1
+ %%raw(`import './Basefn__Skeleton.css'`)
2
+
3
+ type variant = Text | Circular | Rectangular
4
+
5
+ type animation = Pulse | Wave | None
6
+
7
+ let variantToString = (variant: variant) => {
8
+ switch variant {
9
+ | Text => "text"
10
+ | Circular => "circular"
11
+ | Rectangular => "rectangular"
12
+ }
13
+ }
14
+
15
+ let animationToString = (animation: animation) => {
16
+ switch animation {
17
+ | Pulse => "pulse"
18
+ | Wave => "wave"
19
+ | None => "none"
20
+ }
21
+ }
22
+
23
+ @jsx.component
24
+ let make = (
25
+ ~variant: variant=Rectangular,
26
+ ~animation: animation=Pulse,
27
+ ~width: option<string>=?,
28
+ ~height: option<string>=?,
29
+ ~className: option<string>=?,
30
+ ) => {
31
+ let getClassName = () => {
32
+ let baseClass = "basefn-skeleton"
33
+ let variantClass = " basefn-skeleton--" ++ variantToString(variant)
34
+ let animationClass = " basefn-skeleton--" ++ animationToString(animation)
35
+ let customClass = switch className {
36
+ | Some(c) => " " ++ c
37
+ | None => ""
38
+ }
39
+ baseClass ++ variantClass ++ animationClass ++ customClass
40
+ }
41
+
42
+ let getStyle = () => {
43
+ let widthStyle = switch width {
44
+ | Some(w) => "width: " ++ w ++ ";"
45
+ | None => ""
46
+ }
47
+ let heightStyle = switch height {
48
+ | Some(h) => "height: " ++ h ++ ";"
49
+ | None => ""
50
+ }
51
+ widthStyle ++ heightStyle
52
+ }
53
+
54
+ <div class={getClassName()} style={getStyle()} />
55
+ }
@@ -0,0 +1,62 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Xote__JSX from "xote/src/Xote__JSX.res.mjs";
4
+
5
+ import './Basefn__Skeleton.css'
6
+ ;
7
+
8
+ function variantToString(variant) {
9
+ switch (variant) {
10
+ case "Text" :
11
+ return "text";
12
+ case "Circular" :
13
+ return "circular";
14
+ case "Rectangular" :
15
+ return "rectangular";
16
+ }
17
+ }
18
+
19
+ function animationToString(animation) {
20
+ switch (animation) {
21
+ case "Pulse" :
22
+ return "pulse";
23
+ case "Wave" :
24
+ return "wave";
25
+ case "None" :
26
+ return "none";
27
+ }
28
+ }
29
+
30
+ function Basefn__Skeleton(props) {
31
+ let className = props.className;
32
+ let height = props.height;
33
+ let width = props.width;
34
+ let __animation = props.animation;
35
+ let __variant = props.variant;
36
+ let variant = __variant !== undefined ? __variant : "Rectangular";
37
+ let animation = __animation !== undefined ? __animation : "Pulse";
38
+ let getClassName = () => {
39
+ let variantClass = " basefn-skeleton--" + variantToString(variant);
40
+ let animationClass = " basefn-skeleton--" + animationToString(animation);
41
+ let customClass = className !== undefined ? " " + className : "";
42
+ return "basefn-skeleton" + variantClass + animationClass + customClass;
43
+ };
44
+ let getStyle = () => {
45
+ let widthStyle = width !== undefined ? "width: " + width + ";" : "";
46
+ let heightStyle = height !== undefined ? "height: " + height + ";" : "";
47
+ return widthStyle + heightStyle;
48
+ };
49
+ return Xote__JSX.Elements.jsx("div", {
50
+ class: getClassName(),
51
+ style: getStyle()
52
+ });
53
+ }
54
+
55
+ let make = Basefn__Skeleton;
56
+
57
+ export {
58
+ variantToString,
59
+ animationToString,
60
+ make,
61
+ }
62
+ /* Not a pure module */
@@ -139,3 +139,39 @@
139
139
  .basefn-stepper__step--clickable .basefn-stepper__step-indicator:hover {
140
140
  transform: scale(1.05);
141
141
  }
142
+
143
+ /* Mobile responsiveness */
144
+ @media (max-width: 768px) {
145
+ .basefn-stepper--horizontal {
146
+ flex-direction: column;
147
+ }
148
+
149
+ .basefn-stepper--horizontal .basefn-stepper__step {
150
+ flex-direction: row;
151
+ margin-bottom: 2rem;
152
+ }
153
+
154
+ .basefn-stepper--horizontal .basefn-stepper__step:last-child {
155
+ margin-bottom: 0;
156
+ }
157
+
158
+ .basefn-stepper--horizontal .basefn-stepper__step-header {
159
+ flex-direction: row;
160
+ text-align: left;
161
+ }
162
+
163
+ .basefn-stepper--horizontal .basefn-stepper__connector {
164
+ width: 2px;
165
+ height: auto;
166
+ left: 1.25rem;
167
+ top: 2.5rem;
168
+ bottom: -2rem;
169
+ transform: none;
170
+ }
171
+
172
+ .basefn-stepper__step-indicator {
173
+ width: 2rem;
174
+ height: 2rem;
175
+ font-size: 0.75rem;
176
+ }
177
+ }
@@ -52,3 +52,21 @@
52
52
  transform: translateY(0);
53
53
  }
54
54
  }
55
+
56
+ /* Mobile responsiveness */
57
+ @media (max-width: 768px) {
58
+ .basefn-tabs__list {
59
+ overflow-x: auto;
60
+ -webkit-overflow-scrolling: touch;
61
+ scrollbar-width: none;
62
+ }
63
+
64
+ .basefn-tabs__list::-webkit-scrollbar {
65
+ display: none;
66
+ }
67
+
68
+ .basefn-tabs__trigger {
69
+ padding: 0.625rem 1rem;
70
+ font-size: 0.8125rem;
71
+ }
72
+ }
@@ -0,0 +1,87 @@
1
+ @import '../styles/variables.css';
2
+
3
+ .basefn-toggle {
4
+ display: inline-flex;
5
+ align-items: center;
6
+ justify-content: center;
7
+ border: none;
8
+ cursor: pointer;
9
+ font-family: var(--basefn-font-family);
10
+ font-weight: var(--basefn-font-weight-medium);
11
+ transition: all var(--basefn-transition-fast);
12
+ border-radius: var(--basefn-radius-md);
13
+ background: transparent;
14
+ color: var(--basefn-text-secondary);
15
+ }
16
+
17
+ .basefn-toggle:focus-visible {
18
+ outline: none;
19
+ box-shadow: 0 0 0 var(--basefn-focus-ring-width) var(--basefn-focus-ring-color);
20
+ }
21
+
22
+ /* Sizes */
23
+ .basefn-toggle--sm {
24
+ height: var(--basefn-button-height-sm);
25
+ padding: 0 var(--basefn-spacing-sm);
26
+ font-size: var(--basefn-font-size-sm);
27
+ gap: var(--basefn-spacing-xs);
28
+ }
29
+
30
+ .basefn-toggle--md {
31
+ height: var(--basefn-button-height);
32
+ padding: 0 var(--basefn-spacing-md);
33
+ font-size: var(--basefn-font-size-base);
34
+ gap: var(--basefn-spacing-sm);
35
+ }
36
+
37
+ .basefn-toggle--lg {
38
+ height: var(--basefn-button-height-lg);
39
+ padding: 0 var(--basefn-spacing-lg);
40
+ font-size: var(--basefn-font-size-lg);
41
+ gap: var(--basefn-spacing-sm);
42
+ }
43
+
44
+ /* Default variant */
45
+ .basefn-toggle--default {
46
+ background: transparent;
47
+ }
48
+
49
+ .basefn-toggle--default:hover:not(.basefn-toggle--disabled) {
50
+ background: var(--basefn-bg-tertiary);
51
+ color: var(--basefn-text-primary);
52
+ }
53
+
54
+ .basefn-toggle--default.basefn-toggle--pressed {
55
+ background: var(--basefn-bg-tertiary);
56
+ color: var(--basefn-text-primary);
57
+ }
58
+
59
+ /* Outline variant */
60
+ .basefn-toggle--outline {
61
+ background: transparent;
62
+ border: var(--basefn-border-width) solid var(--basefn-border-primary);
63
+ }
64
+
65
+ .basefn-toggle--outline:hover:not(.basefn-toggle--disabled) {
66
+ background: var(--basefn-bg-tertiary);
67
+ border-color: var(--basefn-border-secondary);
68
+ color: var(--basefn-text-primary);
69
+ }
70
+
71
+ .basefn-toggle--outline.basefn-toggle--pressed {
72
+ background: var(--basefn-bg-tertiary);
73
+ border-color: var(--basefn-color-primary);
74
+ color: var(--basefn-text-primary);
75
+ }
76
+
77
+ /* Disabled state */
78
+ .basefn-toggle--disabled {
79
+ opacity: 0.5;
80
+ cursor: not-allowed;
81
+ }
82
+
83
+ /* Icons in toggle */
84
+ .basefn-toggle svg {
85
+ width: 1em;
86
+ height: 1em;
87
+ }
@@ -0,0 +1,65 @@
1
+ %%raw(`import './Basefn__Toggle.css'`)
2
+
3
+ open Xote
4
+
5
+ type variant = Default | Outline
6
+
7
+ type size = Sm | Md | Lg
8
+
9
+ let variantToString = (variant: variant) => {
10
+ switch variant {
11
+ | Default => "default"
12
+ | Outline => "outline"
13
+ }
14
+ }
15
+
16
+ let sizeToString = (size: size) => {
17
+ switch size {
18
+ | Sm => "sm"
19
+ | Md => "md"
20
+ | Lg => "lg"
21
+ }
22
+ }
23
+
24
+ @jsx.component
25
+ let make = (
26
+ ~pressed: Signal.t<bool>,
27
+ ~onPressedChange: option<bool => unit>=?,
28
+ ~variant: variant=Default,
29
+ ~size: size=Md,
30
+ ~disabled: bool=false,
31
+ ~className: option<string>=?,
32
+ ~children: Component.node,
33
+ ) => {
34
+ let handleClick = _ => {
35
+ if !disabled {
36
+ let newValue = !Signal.get(pressed)
37
+ Signal.set(pressed, newValue)
38
+ switch onPressedChange {
39
+ | Some(callback) => callback(newValue)
40
+ | None => ()
41
+ }
42
+ }
43
+ }
44
+
45
+ let computedClassName = Computed.make(() => {
46
+ let baseClass = "basefn-toggle"
47
+ let variantClass = " basefn-toggle--" ++ variantToString(variant)
48
+ let sizeClass = " basefn-toggle--" ++ sizeToString(size)
49
+ let pressedClass = Signal.get(pressed) ? " basefn-toggle--pressed" : ""
50
+ let disabledClass = disabled ? " basefn-toggle--disabled" : ""
51
+ let customClass = switch className {
52
+ | Some(c) => " " ++ c
53
+ | None => ""
54
+ }
55
+ baseClass ++ variantClass ++ sizeClass ++ pressedClass ++ disabledClass ++ customClass
56
+ })
57
+
58
+ <button
59
+ class={computedClassName}
60
+ onClick={handleClick}
61
+ disabled={disabled}
62
+ >
63
+ {children}
64
+ </button>
65
+ }
@@ -0,0 +1,71 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Xote from "xote/src/Xote.res.mjs";
4
+ import * as Xote__JSX from "xote/src/Xote__JSX.res.mjs";
5
+
6
+ import './Basefn__Toggle.css'
7
+ ;
8
+
9
+ function variantToString(variant) {
10
+ if (variant === "Default") {
11
+ return "default";
12
+ } else {
13
+ return "outline";
14
+ }
15
+ }
16
+
17
+ function sizeToString(size) {
18
+ switch (size) {
19
+ case "Sm" :
20
+ return "sm";
21
+ case "Md" :
22
+ return "md";
23
+ case "Lg" :
24
+ return "lg";
25
+ }
26
+ }
27
+
28
+ function Basefn__Toggle(props) {
29
+ let className = props.className;
30
+ let __disabled = props.disabled;
31
+ let __size = props.size;
32
+ let __variant = props.variant;
33
+ let onPressedChange = props.onPressedChange;
34
+ let pressed = props.pressed;
35
+ let variant = __variant !== undefined ? __variant : "Default";
36
+ let size = __size !== undefined ? __size : "Md";
37
+ let disabled = __disabled !== undefined ? __disabled : false;
38
+ let handleClick = param => {
39
+ if (disabled) {
40
+ return;
41
+ }
42
+ let newValue = !Xote.Signal.get(pressed);
43
+ Xote.Signal.set(pressed, newValue);
44
+ if (onPressedChange !== undefined) {
45
+ return onPressedChange(newValue);
46
+ }
47
+ };
48
+ let computedClassName = Xote.Computed.make(() => {
49
+ let variantClass = " basefn-toggle--" + variantToString(variant);
50
+ let sizeClass = " basefn-toggle--" + sizeToString(size);
51
+ let pressedClass = Xote.Signal.get(pressed) ? " basefn-toggle--pressed" : "";
52
+ let disabledClass = disabled ? " basefn-toggle--disabled" : "";
53
+ let customClass = className !== undefined ? " " + className : "";
54
+ return "basefn-toggle" + variantClass + sizeClass + pressedClass + disabledClass + customClass;
55
+ }, undefined);
56
+ return Xote__JSX.Elements.jsx("button", {
57
+ class: computedClassName,
58
+ disabled: disabled,
59
+ onClick: handleClick,
60
+ children: props.children
61
+ });
62
+ }
63
+
64
+ let make = Basefn__Toggle;
65
+
66
+ export {
67
+ variantToString,
68
+ sizeToString,
69
+ make,
70
+ }
71
+ /* Not a pure module */
@@ -0,0 +1,110 @@
1
+ @import '../styles/variables.css';
2
+
3
+ .basefn-toggle-group {
4
+ display: inline-flex;
5
+ gap: 0;
6
+ }
7
+
8
+ .basefn-toggle-group__item {
9
+ display: inline-flex;
10
+ align-items: center;
11
+ justify-content: center;
12
+ border: none;
13
+ cursor: pointer;
14
+ font-family: var(--basefn-font-family);
15
+ font-weight: var(--basefn-font-weight-medium);
16
+ transition: all var(--basefn-transition-fast);
17
+ background: transparent;
18
+ color: var(--basefn-text-secondary);
19
+ }
20
+
21
+ .basefn-toggle-group__item:focus-visible {
22
+ outline: none;
23
+ box-shadow: 0 0 0 var(--basefn-focus-ring-width) var(--basefn-focus-ring-color);
24
+ z-index: 1;
25
+ position: relative;
26
+ }
27
+
28
+ /* First item */
29
+ .basefn-toggle-group__item:first-child {
30
+ border-top-left-radius: var(--basefn-radius-md);
31
+ border-bottom-left-radius: var(--basefn-radius-md);
32
+ }
33
+
34
+ /* Last item */
35
+ .basefn-toggle-group__item:last-child {
36
+ border-top-right-radius: var(--basefn-radius-md);
37
+ border-bottom-right-radius: var(--basefn-radius-md);
38
+ }
39
+
40
+ /* Middle items */
41
+ .basefn-toggle-group__item:not(:first-child):not(:last-child) {
42
+ border-radius: 0;
43
+ }
44
+
45
+ /* Sizes */
46
+ .basefn-toggle-group__item--sm {
47
+ height: var(--basefn-button-height-sm);
48
+ padding: 0 var(--basefn-spacing-sm);
49
+ font-size: var(--basefn-font-size-sm);
50
+ }
51
+
52
+ .basefn-toggle-group__item--md {
53
+ height: var(--basefn-button-height);
54
+ padding: 0 var(--basefn-spacing-md);
55
+ font-size: var(--basefn-font-size-base);
56
+ }
57
+
58
+ .basefn-toggle-group__item--lg {
59
+ height: var(--basefn-button-height-lg);
60
+ padding: 0 var(--basefn-spacing-lg);
61
+ font-size: var(--basefn-font-size-lg);
62
+ }
63
+
64
+ /* Default variant */
65
+ .basefn-toggle-group__item--default {
66
+ background: var(--basefn-bg-secondary);
67
+ border: var(--basefn-border-width) solid var(--basefn-border-primary);
68
+ }
69
+
70
+ .basefn-toggle-group__item--default:not(:first-child) {
71
+ margin-left: -1px;
72
+ }
73
+
74
+ .basefn-toggle-group__item--default:hover:not(.basefn-toggle-group__item--disabled) {
75
+ background: var(--basefn-bg-tertiary);
76
+ color: var(--basefn-text-primary);
77
+ }
78
+
79
+ .basefn-toggle-group__item--default.basefn-toggle-group__item--pressed {
80
+ background: var(--basefn-bg-tertiary);
81
+ color: var(--basefn-text-primary);
82
+ }
83
+
84
+ /* Outline variant */
85
+ .basefn-toggle-group__item--outline {
86
+ background: transparent;
87
+ border: var(--basefn-border-width) solid var(--basefn-border-primary);
88
+ }
89
+
90
+ .basefn-toggle-group__item--outline:not(:first-child) {
91
+ margin-left: -1px;
92
+ }
93
+
94
+ .basefn-toggle-group__item--outline:hover:not(.basefn-toggle-group__item--disabled) {
95
+ background: var(--basefn-bg-tertiary);
96
+ border-color: var(--basefn-border-secondary);
97
+ color: var(--basefn-text-primary);
98
+ }
99
+
100
+ .basefn-toggle-group__item--outline.basefn-toggle-group__item--pressed {
101
+ background: var(--basefn-bg-tertiary);
102
+ border-color: var(--basefn-color-primary);
103
+ color: var(--basefn-text-primary);
104
+ }
105
+
106
+ /* Disabled state */
107
+ .basefn-toggle-group__item--disabled {
108
+ opacity: 0.5;
109
+ cursor: not-allowed;
110
+ }
@@ -0,0 +1,106 @@
1
+ %%raw(`import './Basefn__ToggleGroup.css'`)
2
+
3
+ open Xote
4
+
5
+ type selectionType = Single | Multiple
6
+
7
+ type toggleItem = {
8
+ value: string,
9
+ label: string,
10
+ disabled?: bool,
11
+ }
12
+
13
+ type variant = Default | Outline
14
+
15
+ type size = Sm | Md | Lg
16
+
17
+ let variantToString = (variant: variant) => {
18
+ switch variant {
19
+ | Default => "default"
20
+ | Outline => "outline"
21
+ }
22
+ }
23
+
24
+ let sizeToString = (size: size) => {
25
+ switch size {
26
+ | Sm => "sm"
27
+ | Md => "md"
28
+ | Lg => "lg"
29
+ }
30
+ }
31
+
32
+ @jsx.component
33
+ let make = (
34
+ ~type_: selectionType=Single,
35
+ ~value: Signal.t<array<string>>,
36
+ ~items: array<toggleItem>,
37
+ ~onValueChange: option<array<string> => unit>=?,
38
+ ~variant: variant=Default,
39
+ ~size: size=Md,
40
+ ~className: option<string>=?,
41
+ ) => {
42
+ let handleItemClick = (itemValue: string, itemDisabled: bool) => {
43
+ if !itemDisabled {
44
+ let currentValues = Signal.get(value)
45
+ let newValues = switch type_ {
46
+ | Single =>
47
+ if Array.includes(currentValues, itemValue) {
48
+ []
49
+ } else {
50
+ [itemValue]
51
+ }
52
+ | Multiple =>
53
+ if Array.includes(currentValues, itemValue) {
54
+ currentValues->Array.filter(v => v !== itemValue)
55
+ } else {
56
+ Array.concat(currentValues, [itemValue])
57
+ }
58
+ }
59
+ Signal.set(value, newValues)
60
+ switch onValueChange {
61
+ | Some(callback) => callback(newValues)
62
+ | None => ()
63
+ }
64
+ }
65
+ }
66
+
67
+ let getGroupClassName = () => {
68
+ let baseClass = "basefn-toggle-group"
69
+ let customClass = switch className {
70
+ | Some(c) => " " ++ c
71
+ | None => ""
72
+ }
73
+ baseClass ++ customClass
74
+ }
75
+
76
+ let getItemClassName = (itemValue: string, itemDisabled: bool) => {
77
+ Computed.make(() => {
78
+ let baseClass = "basefn-toggle-group__item"
79
+ let variantClass = " basefn-toggle-group__item--" ++ variantToString(variant)
80
+ let sizeClass = " basefn-toggle-group__item--" ++ sizeToString(size)
81
+ let currentValues = Signal.get(value)
82
+ let pressedClass = Array.includes(currentValues, itemValue) ? " basefn-toggle-group__item--pressed" : ""
83
+ let disabledClass = itemDisabled ? " basefn-toggle-group__item--disabled" : ""
84
+ baseClass ++ variantClass ++ sizeClass ++ pressedClass ++ disabledClass
85
+ })
86
+ }
87
+
88
+ <div class={getGroupClassName()} role="group">
89
+ {items
90
+ ->Array.map(item => {
91
+ let isDisabled = switch item.disabled {
92
+ | Some(d) => d
93
+ | None => false
94
+ }
95
+ <button
96
+ key={item.value}
97
+ class={getItemClassName(item.value, isDisabled)}
98
+ onClick={_ => handleItemClick(item.value, isDisabled)}
99
+ disabled={isDisabled}
100
+ >
101
+ {Component.text(item.label)}
102
+ </button>
103
+ })
104
+ ->Component.fragment}
105
+ </div>
106
+ }