basefn 1.0.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 (154) hide show
  1. package/README.md +104 -0
  2. package/package.json +82 -0
  3. package/rescript.json +32 -0
  4. package/src/Basefn.css +14 -0
  5. package/src/Basefn.res +105 -0
  6. package/src/Basefn.res.mjs +114 -0
  7. package/src/Basefn__Dom.res +9 -0
  8. package/src/Basefn__Dom.res.mjs +24 -0
  9. package/src/Basefn__Utils.res +15 -0
  10. package/src/Basefn__Utils.res.mjs +32 -0
  11. package/src/Demo.res +1417 -0
  12. package/src/Demo.res.mjs +2328 -0
  13. package/src/Eita.res.mjs +105 -0
  14. package/src/Eita__Accordion.res.mjs +77 -0
  15. package/src/Eita__Alert.res.mjs +81 -0
  16. package/src/Eita__AppLayout.res.mjs +100 -0
  17. package/src/Eita__Avatar.res.mjs +40 -0
  18. package/src/Eita__Badge.res.mjs +65 -0
  19. package/src/Eita__Breadcrumb.res.mjs +53 -0
  20. package/src/Eita__Button.res.mjs +47 -0
  21. package/src/Eita__Card.res.mjs +60 -0
  22. package/src/Eita__Checkbox.res.mjs +36 -0
  23. package/src/Eita__Dom.res.mjs +16 -0
  24. package/src/Eita__Drawer.res.mjs +112 -0
  25. package/src/Eita__Dropdown.res.mjs +96 -0
  26. package/src/Eita__Grid.res.mjs +24 -0
  27. package/src/Eita__Input.res.mjs +54 -0
  28. package/src/Eita__Kbd.res.mjs +42 -0
  29. package/src/Eita__Label.res.mjs +24 -0
  30. package/src/Eita__Modal.res.mjs +93 -0
  31. package/src/Eita__Progress.res.mjs +101 -0
  32. package/src/Eita__Radio.res.mjs +38 -0
  33. package/src/Eita__Select.res.mjs +40 -0
  34. package/src/Eita__Separator.res.mjs +70 -0
  35. package/src/Eita__Sidebar.res.mjs +103 -0
  36. package/src/Eita__Slider.res.mjs +89 -0
  37. package/src/Eita__Spinner.res.mjs +69 -0
  38. package/src/Eita__Stepper.res.mjs +114 -0
  39. package/src/Eita__Switch.res.mjs +84 -0
  40. package/src/Eita__Tabs.res.mjs +57 -0
  41. package/src/Eita__Textarea.res.mjs +39 -0
  42. package/src/Eita__Timeline.res.mjs +86 -0
  43. package/src/Eita__Toast.res.mjs +112 -0
  44. package/src/Eita__Tooltip.res.mjs +60 -0
  45. package/src/Eita__Topbar.res.mjs +96 -0
  46. package/src/Eita__Typography.res.mjs +183 -0
  47. package/src/Eita__Utils.res.mjs +32 -0
  48. package/src/Example.res +111 -0
  49. package/src/Example.res.mjs +176 -0
  50. package/src/components/Basefn__Accordion.css +70 -0
  51. package/src/components/Basefn__Accordion.res +79 -0
  52. package/src/components/Basefn__Accordion.res.mjs +77 -0
  53. package/src/components/Basefn__Alert.css +79 -0
  54. package/src/components/Basefn__Alert.res +68 -0
  55. package/src/components/Basefn__Alert.res.mjs +78 -0
  56. package/src/components/Basefn__AppLayout.css +100 -0
  57. package/src/components/Basefn__AppLayout.res +74 -0
  58. package/src/components/Basefn__AppLayout.res.mjs +100 -0
  59. package/src/components/Basefn__Avatar.css +25 -0
  60. package/src/components/Basefn__Avatar.res +23 -0
  61. package/src/components/Basefn__Avatar.res.mjs +40 -0
  62. package/src/components/Basefn__Badge.css +71 -0
  63. package/src/components/Basefn__Badge.res +43 -0
  64. package/src/components/Basefn__Badge.res.mjs +65 -0
  65. package/src/components/Basefn__Breadcrumb.css +36 -0
  66. package/src/components/Basefn__Breadcrumb.res +45 -0
  67. package/src/components/Basefn__Breadcrumb.res.mjs +53 -0
  68. package/src/components/Basefn__Button.css +83 -0
  69. package/src/components/Basefn__Button.res +32 -0
  70. package/src/components/Basefn__Button.res.mjs +54 -0
  71. package/src/components/Basefn__Card.css +50 -0
  72. package/src/components/Basefn__Card.res +45 -0
  73. package/src/components/Basefn__Card.res.mjs +60 -0
  74. package/src/components/Basefn__Checkbox.css +72 -0
  75. package/src/components/Basefn__Checkbox.res +25 -0
  76. package/src/components/Basefn__Checkbox.res.mjs +36 -0
  77. package/src/components/Basefn__Drawer.css +168 -0
  78. package/src/components/Basefn__Drawer.res +86 -0
  79. package/src/components/Basefn__Drawer.res.mjs +112 -0
  80. package/src/components/Basefn__Dropdown.css +76 -0
  81. package/src/components/Basefn__Dropdown.res +85 -0
  82. package/src/components/Basefn__Dropdown.res.mjs +96 -0
  83. package/src/components/Basefn__Grid.css +11 -0
  84. package/src/components/Basefn__Grid.res +296 -0
  85. package/src/components/Basefn__Grid.res.mjs +263 -0
  86. package/src/components/Basefn__Icon.css +12 -0
  87. package/src/components/Basefn__Icon.res +196 -0
  88. package/src/components/Basefn__Icon.res.mjs +183 -0
  89. package/src/components/Basefn__Input.css +44 -0
  90. package/src/components/Basefn__Input.res +48 -0
  91. package/src/components/Basefn__Input.res.mjs +63 -0
  92. package/src/components/Basefn__Kbd.css +65 -0
  93. package/src/components/Basefn__Kbd.res +27 -0
  94. package/src/components/Basefn__Kbd.res.mjs +42 -0
  95. package/src/components/Basefn__Label.css +22 -0
  96. package/src/components/Basefn__Label.res +18 -0
  97. package/src/components/Basefn__Label.res.mjs +24 -0
  98. package/src/components/Basefn__Modal.css +100 -0
  99. package/src/components/Basefn__Modal.res +74 -0
  100. package/src/components/Basefn__Modal.res.mjs +93 -0
  101. package/src/components/Basefn__Progress.css +69 -0
  102. package/src/components/Basefn__Progress.res +88 -0
  103. package/src/components/Basefn__Progress.res.mjs +101 -0
  104. package/src/components/Basefn__Radio.css +72 -0
  105. package/src/components/Basefn__Radio.res +35 -0
  106. package/src/components/Basefn__Radio.res.mjs +38 -0
  107. package/src/components/Basefn__Select.css +44 -0
  108. package/src/components/Basefn__Select.res +33 -0
  109. package/src/components/Basefn__Select.res.mjs +40 -0
  110. package/src/components/Basefn__Separator.css +85 -0
  111. package/src/components/Basefn__Separator.res +45 -0
  112. package/src/components/Basefn__Separator.res.mjs +70 -0
  113. package/src/components/Basefn__Sidebar.css +141 -0
  114. package/src/components/Basefn__Sidebar.res +95 -0
  115. package/src/components/Basefn__Sidebar.res.mjs +107 -0
  116. package/src/components/Basefn__Slider.css +97 -0
  117. package/src/components/Basefn__Slider.res +68 -0
  118. package/src/components/Basefn__Slider.res.mjs +89 -0
  119. package/src/components/Basefn__Spinner.css +63 -0
  120. package/src/components/Basefn__Spinner.res +44 -0
  121. package/src/components/Basefn__Spinner.res.mjs +69 -0
  122. package/src/components/Basefn__Stepper.css +141 -0
  123. package/src/components/Basefn__Stepper.res +86 -0
  124. package/src/components/Basefn__Stepper.res.mjs +114 -0
  125. package/src/components/Basefn__Switch.css +80 -0
  126. package/src/components/Basefn__Switch.res +62 -0
  127. package/src/components/Basefn__Switch.res.mjs +84 -0
  128. package/src/components/Basefn__Tabs.css +54 -0
  129. package/src/components/Basefn__Tabs.res +73 -0
  130. package/src/components/Basefn__Tabs.res.mjs +57 -0
  131. package/src/components/Basefn__Textarea.css +41 -0
  132. package/src/components/Basefn__Textarea.res +28 -0
  133. package/src/components/Basefn__Textarea.res.mjs +41 -0
  134. package/src/components/Basefn__ThemeToggle.css +5 -0
  135. package/src/components/Basefn__ThemeToggle.res +29 -0
  136. package/src/components/Basefn__ThemeToggle.res.mjs +49 -0
  137. package/src/components/Basefn__Timeline.css +144 -0
  138. package/src/components/Basefn__Timeline.res +70 -0
  139. package/src/components/Basefn__Timeline.res.mjs +86 -0
  140. package/src/components/Basefn__Toast.css +100 -0
  141. package/src/components/Basefn__Toast.res +92 -0
  142. package/src/components/Basefn__Toast.res.mjs +112 -0
  143. package/src/components/Basefn__Tooltip.css +84 -0
  144. package/src/components/Basefn__Tooltip.res +42 -0
  145. package/src/components/Basefn__Tooltip.res.mjs +60 -0
  146. package/src/components/Basefn__Topbar.css +130 -0
  147. package/src/components/Basefn__Topbar.res +92 -0
  148. package/src/components/Basefn__Topbar.res.mjs +91 -0
  149. package/src/components/Basefn__Typography.css +120 -0
  150. package/src/components/Basefn__Typography.res +96 -0
  151. package/src/components/Basefn__Typography.res.mjs +175 -0
  152. package/src/styles/Basefn__Theme.res +63 -0
  153. package/src/styles/Basefn__Theme.res.mjs +65 -0
  154. package/src/styles/variables.css +199 -0
@@ -0,0 +1,176 @@
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
+ import * as Basefn__Input from "./components/Basefn__Input.res.mjs";
6
+ import * as Basefn__Label from "./components/Basefn__Label.res.mjs";
7
+ import * as Basefn__Button from "./components/Basefn__Button.res.mjs";
8
+ import * as Basefn__Select from "./components/Basefn__Select.res.mjs";
9
+ import * as Basefn__Checkbox from "./components/Basefn__Checkbox.res.mjs";
10
+ import * as Basefn__Textarea from "./components/Basefn__Textarea.res.mjs";
11
+
12
+ function Example$ExampleForm(props) {
13
+ let name = Xote.Signal.make("", undefined, undefined);
14
+ let email = Xote.Signal.make("", undefined, undefined);
15
+ let message = Xote.Signal.make("", undefined, undefined);
16
+ let agreeToTerms = Xote.Signal.make(false, undefined, undefined);
17
+ let selectedOption = Xote.Signal.make("option1", undefined, undefined);
18
+ let isSubmitting = Xote.Signal.make(false, undefined, undefined);
19
+ let handleNameChange = evt => {
20
+ let target = evt.target;
21
+ Xote.Signal.set(name, target.value);
22
+ };
23
+ let handleEmailChange = evt => {
24
+ let target = evt.target;
25
+ Xote.Signal.set(email, target.value);
26
+ };
27
+ let handleMessageChange = evt => {
28
+ let target = evt.target;
29
+ Xote.Signal.set(message, target.value);
30
+ };
31
+ let handleCheckboxChange = _evt => Xote.Signal.update(agreeToTerms, prev => !prev);
32
+ let handleSubmit = _evt => {
33
+ Xote.Signal.set(isSubmitting, true);
34
+ console.log("Submitting form...");
35
+ console.log(`Name: ` + Xote.Signal.get(name));
36
+ console.log(`Email: ` + Xote.Signal.get(email));
37
+ console.log(`Message: ` + Xote.Signal.get(message));
38
+ setTimeout(() => {
39
+ Xote.Signal.set(isSubmitting, false);
40
+ }, 1000);
41
+ };
42
+ let selectOptions = Xote.Signal.make([
43
+ {
44
+ value: "option1",
45
+ label: "Option 1"
46
+ },
47
+ {
48
+ value: "option2",
49
+ label: "Option 2"
50
+ },
51
+ {
52
+ value: "option3",
53
+ label: "Option 3"
54
+ }
55
+ ], undefined, undefined);
56
+ return Xote__JSX.Elements.jsxs("div", {
57
+ style: "max-width: 50rem; margin: auto;",
58
+ children: Xote__JSX.array([
59
+ Xote__JSX.Elements.jsx("h1", {
60
+ children: Xote.Component.text("Contact Form Example")
61
+ }),
62
+ Xote__JSX.Elements.jsxs("div", {
63
+ style: "margin-bottom: 1rem;",
64
+ children: Xote__JSX.array([
65
+ Xote__JSX.jsx(Basefn__Label.make, {
66
+ text: "Name",
67
+ required: true
68
+ }),
69
+ Xote__JSX.jsx(Basefn__Input.make, {
70
+ value: {
71
+ TAG: "Reactive",
72
+ _0: name
73
+ },
74
+ onInput: handleNameChange,
75
+ type_: "Text",
76
+ placeholder: "Enter your name"
77
+ })
78
+ ])
79
+ }),
80
+ Xote__JSX.Elements.jsxs("div", {
81
+ style: "margin-bottom: 1rem;",
82
+ children: Xote__JSX.array([
83
+ Xote__JSX.jsx(Basefn__Label.make, {
84
+ text: "Email",
85
+ required: true
86
+ }),
87
+ Xote__JSX.jsx(Basefn__Input.make, {
88
+ value: {
89
+ TAG: "Reactive",
90
+ _0: email
91
+ },
92
+ onInput: handleEmailChange,
93
+ type_: "Email",
94
+ placeholder: "Enter your email"
95
+ })
96
+ ])
97
+ }),
98
+ Xote__JSX.Elements.jsxs("div", {
99
+ style: "margin-bottom: 1rem;",
100
+ children: Xote__JSX.array([
101
+ Xote__JSX.jsx(Basefn__Label.make, {
102
+ text: "Choose an option",
103
+ required: false
104
+ }),
105
+ Xote__JSX.jsx(Basefn__Select.make, {
106
+ value: selectedOption,
107
+ options: selectOptions
108
+ })
109
+ ])
110
+ }),
111
+ Xote__JSX.Elements.jsxs("div", {
112
+ style: "margin-bottom: 1rem;",
113
+ children: Xote__JSX.array([
114
+ Xote__JSX.jsx(Basefn__Label.make, {
115
+ text: "Message",
116
+ required: false
117
+ }),
118
+ Xote__JSX.jsx(Basefn__Textarea.make, {
119
+ value: {
120
+ TAG: "Reactive",
121
+ _0: message
122
+ },
123
+ onInput: handleMessageChange,
124
+ placeholder: "Enter your message"
125
+ })
126
+ ])
127
+ }),
128
+ Xote__JSX.Elements.jsx("div", {
129
+ style: "margin-bottom: 1rem;",
130
+ children: Xote__JSX.jsx(Basefn__Checkbox.make, {
131
+ checked: agreeToTerms,
132
+ onChange: handleCheckboxChange,
133
+ label: "I agree to the terms and conditions"
134
+ })
135
+ }),
136
+ Xote__JSX.Elements.jsxs("div", {
137
+ style: "display: flex; gap: 1rem;",
138
+ children: Xote__JSX.array([
139
+ Xote__JSX.jsx(Basefn__Button.make, {
140
+ children: Xote.Component.textSignal(() => {
141
+ if (Xote.Signal.get(isSubmitting)) {
142
+ return "Submitting...";
143
+ } else {
144
+ return "Submit";
145
+ }
146
+ }),
147
+ disabled: {
148
+ TAG: "Reactive",
149
+ _0: isSubmitting
150
+ },
151
+ onClick: handleSubmit,
152
+ variant: "Primary"
153
+ }),
154
+ Xote__JSX.jsx(Basefn__Button.make, {
155
+ label: {
156
+ TAG: "Static",
157
+ _0: "Cancel"
158
+ },
159
+ variant: "Ghost"
160
+ })
161
+ ])
162
+ })
163
+ ])
164
+ });
165
+ }
166
+
167
+ let ExampleForm = {
168
+ make: Example$ExampleForm
169
+ };
170
+
171
+ Xote.Component.mountById(Xote__JSX.jsx(Example$ExampleForm, {}), "root");
172
+
173
+ export {
174
+ ExampleForm,
175
+ }
176
+ /* Not a pure module */
@@ -0,0 +1,70 @@
1
+ .basefn-accordion {
2
+ width: 100%;
3
+ border: 1px solid #e5e7eb;
4
+ border-radius: 0.5rem;
5
+ overflow: hidden;
6
+ }
7
+
8
+ .basefn-accordion__item {
9
+ border-bottom: 1px solid #e5e7eb;
10
+ }
11
+
12
+ .basefn-accordion__item:last-child {
13
+ border-bottom: none;
14
+ }
15
+
16
+ .basefn-accordion__trigger {
17
+ width: 100%;
18
+ display: flex;
19
+ justify-content: space-between;
20
+ align-items: center;
21
+ padding: 1rem 1.25rem;
22
+ background: none;
23
+ border: none;
24
+ font-size: 0.9375rem;
25
+ font-weight: 500;
26
+ color: #1f2937;
27
+ cursor: pointer;
28
+ transition: background-color 0.2s;
29
+ text-align: left;
30
+ }
31
+
32
+ .basefn-accordion__trigger:hover {
33
+ background-color: #f9fafb;
34
+ }
35
+
36
+ .basefn-accordion__trigger:disabled {
37
+ opacity: 0.5;
38
+ cursor: not-allowed;
39
+ }
40
+
41
+ .basefn-accordion__icon {
42
+ font-size: 1.25rem;
43
+ transition: transform 0.2s;
44
+ color: #6b7280;
45
+ }
46
+
47
+ .basefn-accordion__icon--open {
48
+ transform: rotate(180deg);
49
+ }
50
+
51
+ .basefn-accordion__content {
52
+ overflow: hidden;
53
+ transition: max-height 0.3s ease-out, opacity 0.2s ease-out;
54
+ }
55
+
56
+ .basefn-accordion__content--collapsed {
57
+ max-height: 0;
58
+ opacity: 0;
59
+ }
60
+
61
+ .basefn-accordion__content--expanded {
62
+ max-height: 1000px;
63
+ opacity: 1;
64
+ }
65
+
66
+ .basefn-accordion__content-inner {
67
+ padding: 0 1.25rem 1rem 1.25rem;
68
+ color: #6b7280;
69
+ line-height: 1.6;
70
+ }
@@ -0,0 +1,79 @@
1
+ %%raw(`import './Basefn__Accordion.css'`)
2
+
3
+ open Xote
4
+
5
+ type accordionItem = {
6
+ value: string,
7
+ title: string,
8
+ content: Component.node,
9
+ disabled?: bool,
10
+ }
11
+
12
+ @jsx.component
13
+ let make = (
14
+ ~items: array<accordionItem>,
15
+ ~defaultOpen: option<array<string>>=?,
16
+ ~multiple: bool=false,
17
+ ) => {
18
+ let openItems = Signal.make(defaultOpen->Option.getOr([]))
19
+
20
+ let isOpen = (value: string) => {
21
+ Signal.get(openItems)->Array.includes(value)
22
+ }
23
+
24
+ let toggleItem = (value: string, disabled: option<bool>) => {
25
+ switch disabled {
26
+ | Some(true) => ()
27
+ | _ =>
28
+ Signal.update(openItems, current => {
29
+ if current->Array.includes(value) {
30
+ // Close item
31
+ current->Array.filter(item => item != value)
32
+ } // Open item
33
+ else if multiple {
34
+ current->Array.concat([value])
35
+ } else {
36
+ [value]
37
+ }
38
+ })
39
+ }
40
+ }
41
+
42
+ <div class="basefn-accordion">
43
+ {items
44
+ ->Array.map(item => {
45
+ let itemIsOpen = Computed.make(() => isOpen(item.value))
46
+
47
+ <div key={item.value} class="basefn-accordion__item">
48
+ <button
49
+ class="basefn-accordion__trigger"
50
+ onClick={_ => toggleItem(item.value, item.disabled)}
51
+ disabled={item.disabled->Option.getOr(false)}
52
+ >
53
+ <span> {Component.text(item.title)} </span>
54
+ <span
55
+ class={Computed.make(() => {
56
+ let baseClass = "basefn-accordion__icon"
57
+ let openClass = Signal.get(itemIsOpen) ? " basefn-accordion__icon--open" : ""
58
+ baseClass ++ openClass
59
+ })}
60
+ >
61
+ {Component.text("\u25bc")}
62
+ </span>
63
+ </button>
64
+ <div
65
+ class={Computed.make(() => {
66
+ let baseClass = "basefn-accordion__content"
67
+ let stateClass = Signal.get(itemIsOpen)
68
+ ? " basefn-accordion__content--expanded"
69
+ : " basefn-accordion__content--collapsed"
70
+ baseClass ++ stateClass
71
+ })}
72
+ >
73
+ <div class="basefn-accordion__content-inner"> {item.content} </div>
74
+ </div>
75
+ </div>
76
+ })
77
+ ->Component.fragment}
78
+ </div>
79
+ }
@@ -0,0 +1,77 @@
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
+ import * as Core__Option from "@rescript/core/src/Core__Option.res.mjs";
6
+
7
+ import './Basefn__Accordion.css'
8
+ ;
9
+
10
+ function Basefn__Accordion(props) {
11
+ let __multiple = props.multiple;
12
+ let multiple = __multiple !== undefined ? __multiple : false;
13
+ let openItems = Xote.Signal.make(Core__Option.getOr(props.defaultOpen, []), undefined, undefined);
14
+ return Xote__JSX.Elements.jsx("div", {
15
+ class: "basefn-accordion",
16
+ children: Xote.Component.fragment(props.items.map(item => {
17
+ let itemIsOpen = Xote.Computed.make(() => {
18
+ let value = item.value;
19
+ return Xote.Signal.get(openItems).includes(value);
20
+ }, undefined);
21
+ return Xote__JSX.Elements.jsxsKeyed("div", {
22
+ class: "basefn-accordion__item",
23
+ children: Xote__JSX.array([
24
+ Xote__JSX.Elements.jsxs("button", {
25
+ class: "basefn-accordion__trigger",
26
+ disabled: Core__Option.getOr(item.disabled, false),
27
+ onClick: param => {
28
+ let value = item.value;
29
+ let disabled = item.disabled;
30
+ if (disabled !== undefined && disabled) {
31
+ return;
32
+ }
33
+ Xote.Signal.update(openItems, current => {
34
+ if (current.includes(value)) {
35
+ return current.filter(item => item !== value);
36
+ } else if (multiple) {
37
+ return current.concat([value]);
38
+ } else {
39
+ return [value];
40
+ }
41
+ });
42
+ },
43
+ children: Xote__JSX.array([
44
+ Xote__JSX.Elements.jsx("span", {
45
+ children: Xote.Component.text(item.title)
46
+ }),
47
+ Xote__JSX.Elements.jsx("span", {
48
+ class: Xote.Computed.make(() => {
49
+ let openClass = Xote.Signal.get(itemIsOpen) ? " basefn-accordion__icon--open" : "";
50
+ return "basefn-accordion__icon" + openClass;
51
+ }, undefined),
52
+ children: Xote.Component.text("\u25bc")
53
+ })
54
+ ])
55
+ }),
56
+ Xote__JSX.Elements.jsx("div", {
57
+ class: Xote.Computed.make(() => {
58
+ let stateClass = Xote.Signal.get(itemIsOpen) ? " basefn-accordion__content--expanded" : " basefn-accordion__content--collapsed";
59
+ return "basefn-accordion__content" + stateClass;
60
+ }, undefined),
61
+ children: Xote__JSX.Elements.jsx("div", {
62
+ class: "basefn-accordion__content-inner",
63
+ children: item.content
64
+ })
65
+ })
66
+ ])
67
+ }, item.value, undefined);
68
+ }))
69
+ });
70
+ }
71
+
72
+ let make = Basefn__Accordion;
73
+
74
+ export {
75
+ make,
76
+ }
77
+ /* Not a pure module */
@@ -0,0 +1,79 @@
1
+ .basefn-alert {
2
+ position: relative;
3
+ display: flex;
4
+ align-items: flex-start;
5
+ justify-content: space-between;
6
+ padding: 1rem 1.25rem;
7
+ border-radius: 0.5rem;
8
+ border-left: 4px solid;
9
+ font-size: 0.875rem;
10
+ animation: basefn-alert-slide-in 0.3s ease-out;
11
+ }
12
+
13
+ .basefn-alert__content {
14
+ flex: 1;
15
+ }
16
+
17
+ .basefn-alert__title {
18
+ font-weight: 600;
19
+ margin-bottom: 0.25rem;
20
+ font-size: 1rem;
21
+ }
22
+
23
+ .basefn-alert__message {
24
+ line-height: 1.5;
25
+ }
26
+
27
+ .basefn-alert__dismiss {
28
+ background: none;
29
+ border: none;
30
+ font-size: 1.5rem;
31
+ line-height: 1;
32
+ cursor: pointer;
33
+ padding: 0;
34
+ margin-left: 1rem;
35
+ color: inherit;
36
+ opacity: 0.6;
37
+ transition: opacity 0.2s;
38
+ }
39
+
40
+ .basefn-alert__dismiss:hover {
41
+ opacity: 1;
42
+ }
43
+
44
+ /* Variants */
45
+ .basefn-alert--info {
46
+ background-color: var(--basefn-alert-info-bg);
47
+ border-left-color: var(--basefn-color-info);
48
+ color: var(--basefn-alert-info-text);
49
+ }
50
+
51
+ .basefn-alert--success {
52
+ background-color: var(--basefn-alert-success-bg);
53
+ border-left-color: var(--basefn-color-success);
54
+ color: var(--basefn-alert-success-text);
55
+ }
56
+
57
+ .basefn-alert--warning {
58
+ background-color: var(--basefn-alert-warning-bg);
59
+ border-left-color: var(--basefn-color-warning);
60
+ color: var(--basefn-alert-warning-text);
61
+ }
62
+
63
+ .basefn-alert--error {
64
+ background-color: var(--basefn-alert-error-bg);
65
+ border-left-color: var(--basefn-color-error);
66
+ color: var(--basefn-alert-error-text);
67
+ }
68
+
69
+ /* Animation */
70
+ @keyframes basefn-alert-slide-in {
71
+ from {
72
+ opacity: 0;
73
+ transform: translateY(-10px);
74
+ }
75
+ to {
76
+ opacity: 1;
77
+ transform: translateY(0);
78
+ }
79
+ }
@@ -0,0 +1,68 @@
1
+ %%raw(`import './Basefn__Alert.css'`)
2
+
3
+ open Xote
4
+
5
+ type variant = Info | Success | Warning | Error
6
+
7
+ let variantToString = (variant: variant) => {
8
+ switch variant {
9
+ | Info => "info"
10
+ | Success => "success"
11
+ | Warning => "warning"
12
+ | Error => "error"
13
+ }
14
+ }
15
+
16
+ @jsx.component
17
+ let make = (
18
+ ~title: option<string>=?,
19
+ ~message: Signal.t<string>,
20
+ ~variant: variant=Info,
21
+ ~dismissible: bool=false,
22
+ ~onDismiss: option<unit => unit>=?,
23
+ ) => {
24
+ let isVisible = Signal.make(true)
25
+
26
+ let handleDismiss = () => {
27
+ Signal.set(isVisible, false)
28
+ switch onDismiss {
29
+ | Some(callback) => callback()
30
+ | None => ()
31
+ }
32
+ }
33
+
34
+ let getClassName = () => {
35
+ let variantClass = "basefn-alert--" ++ variantToString(variant)
36
+ "basefn-alert " ++ variantClass
37
+ }
38
+
39
+ // Use computed signal to reactively determine which nodes to render
40
+ let content = Computed.make(() => {
41
+ if Signal.get(isVisible) {
42
+ [
43
+ <div class={getClassName()}>
44
+ <div class="basefn-alert__content">
45
+ {switch title {
46
+ | Some(titleText) =>
47
+ <div class="basefn-alert__title"> {Component.text(titleText)} </div>
48
+ | None => <> </>
49
+ }}
50
+ <div class="basefn-alert__message">
51
+ {Component.textSignal(() => Signal.get(message))}
52
+ </div>
53
+ </div>
54
+ {dismissible
55
+ ? <button class="basefn-alert__dismiss" onClick={_ => handleDismiss()}>
56
+ {Component.text("\u00d7")}
57
+ </button>
58
+ : <> </>}
59
+ </div>,
60
+ ]
61
+ } else {
62
+ []
63
+ }
64
+ })
65
+
66
+ // Use signalFragment to reactively render the content
67
+ Component.signalFragment(content)
68
+ }
@@ -0,0 +1,78 @@
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__Alert.css'
7
+ ;
8
+
9
+ function variantToString(variant) {
10
+ switch (variant) {
11
+ case "Info" :
12
+ return "info";
13
+ case "Success" :
14
+ return "success";
15
+ case "Warning" :
16
+ return "warning";
17
+ case "Error" :
18
+ return "error";
19
+ }
20
+ }
21
+
22
+ function Basefn__Alert(props) {
23
+ let onDismiss = props.onDismiss;
24
+ let __dismissible = props.dismissible;
25
+ let __variant = props.variant;
26
+ let message = props.message;
27
+ let title = props.title;
28
+ let variant = __variant !== undefined ? __variant : "Info";
29
+ let dismissible = __dismissible !== undefined ? __dismissible : false;
30
+ let isVisible = Xote.Signal.make(true, undefined, undefined);
31
+ let handleDismiss = () => {
32
+ Xote.Signal.set(isVisible, false);
33
+ if (onDismiss !== undefined) {
34
+ return onDismiss();
35
+ }
36
+ };
37
+ let getClassName = () => {
38
+ let variantClass = "basefn-alert--" + variantToString(variant);
39
+ return "basefn-alert " + variantClass;
40
+ };
41
+ return Xote.Component.signalFragment(Xote.Computed.make(() => {
42
+ if (Xote.Signal.get(isVisible)) {
43
+ return [Xote__JSX.Elements.jsxs("div", {
44
+ class: getClassName(),
45
+ children: Xote__JSX.array([
46
+ Xote__JSX.Elements.jsxs("div", {
47
+ class: "basefn-alert__content",
48
+ children: Xote__JSX.array([
49
+ title !== undefined ? Xote__JSX.Elements.jsx("div", {
50
+ class: "basefn-alert__title",
51
+ children: Xote.Component.text(title)
52
+ }) : Xote__JSX.jsx(Xote__JSX.jsxFragment, {}),
53
+ Xote__JSX.Elements.jsx("div", {
54
+ class: "basefn-alert__message",
55
+ children: Xote.Component.textSignal(() => Xote.Signal.get(message))
56
+ })
57
+ ])
58
+ }),
59
+ dismissible ? Xote__JSX.Elements.jsx("button", {
60
+ class: "basefn-alert__dismiss",
61
+ onClick: param => handleDismiss(),
62
+ children: Xote.Component.text("\u00d7")
63
+ }) : Xote__JSX.jsx(Xote__JSX.jsxFragment, {})
64
+ ])
65
+ })];
66
+ } else {
67
+ return [];
68
+ }
69
+ }, undefined));
70
+ }
71
+
72
+ let make = Basefn__Alert;
73
+
74
+ export {
75
+ variantToString,
76
+ make,
77
+ }
78
+ /* Not a pure module */