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
package/src/Demo.res ADDED
@@ -0,0 +1,1417 @@
1
+ // Demo application showcasing basefn-UI components
2
+
3
+ %%raw(`import './styles/variables.css'`)
4
+ %%raw(`import './eita.css'`)
5
+
6
+ open Xote
7
+ open Basefn
8
+
9
+ @get external target: Dom.event => Dom.element = "target"
10
+ @set external setValue: (Dom.element, string) => unit = "value"
11
+ @get external key: Dom.event => string = "key"
12
+
13
+ @get external value: Dom.element => string = "value"
14
+ @send external preventDefault: Dom.event => unit = "preventDefault"
15
+
16
+ module Demo = {
17
+ @jsx.component
18
+ let make = () => {
19
+ // Form state using signals
20
+ let name = Signal.make("")
21
+ let email = Signal.make("")
22
+ let password = Signal.make("")
23
+ let message = Signal.make("")
24
+ let agreeToTerms = Signal.make(false)
25
+ let newsletter = Signal.make(false)
26
+ let selectedOption = Signal.make("option1")
27
+ let selectedColor = Signal.make("blue")
28
+ let isSubmitting = Signal.make(false)
29
+ let downloadProgress = Signal.make(65.0)
30
+
31
+ // Tier 3 component states
32
+ let isModalOpen = Signal.make(false)
33
+ let sliderValue = Signal.make(50.0)
34
+ let switchEnabled = Signal.make(false)
35
+ let darkModeSwitch = Signal.make(true)
36
+ let notificationsSwitch = Signal.make(false)
37
+ let toastVisible = Signal.make(false)
38
+
39
+ // Tier 4 component states
40
+ let currentStep = Signal.make(1)
41
+ let isDrawerOpen = Signal.make(false)
42
+
43
+ // Layout component states
44
+ let activeNavItem = Signal.make("home")
45
+ let sidebarCollapsed = Signal.make(false)
46
+
47
+ // Event handlers
48
+ let handleNameChange = evt => {
49
+ let target = Obj.magic(evt)["target"]
50
+ Signal.set(name, target["value"])
51
+ }
52
+
53
+ let handleEmailChange = evt => {
54
+ let target = Obj.magic(evt)["target"]
55
+ Signal.set(email, target["value"])
56
+ }
57
+
58
+ let handlePasswordChange = evt => {
59
+ let target = Obj.magic(evt)["target"]
60
+ Signal.set(password, target["value"])
61
+ }
62
+
63
+ let handleMessageChange = evt => {
64
+ let target = Obj.magic(evt)["target"]
65
+ Signal.set(message, target["value"])
66
+ }
67
+
68
+ let handleTermsChange = _evt => {
69
+ Signal.update(agreeToTerms, prev => !prev)
70
+ }
71
+
72
+ let handleNewsletterChange = _evt => {
73
+ Signal.update(newsletter, prev => !prev)
74
+ }
75
+
76
+ let handleColorChange = evt => {
77
+ let target = Obj.magic(evt)["target"]
78
+ Signal.set(selectedColor, target["value"])
79
+ }
80
+
81
+ let handleSubmit = _evt => {
82
+ Signal.set(isSubmitting, true)
83
+ Console.log("=== Form Submission ===")
84
+ Console.log(`Name: ${Signal.get(name)}`)
85
+ Console.log(`Email: ${Signal.get(email)}`)
86
+ Console.log(`Password: ${Signal.get(password)}`)
87
+ Console.log(`Message: ${Signal.get(message)}`)
88
+ Console.log(`Selected Option: ${Signal.get(selectedOption)}`)
89
+ Console.log(`Selected Color: ${Signal.get(selectedColor)}`)
90
+ Console.log(`Agree to Terms: ${Signal.get(agreeToTerms)->Bool.toString}`)
91
+ Console.log(`Newsletter: ${Signal.get(newsletter)->Bool.toString}`)
92
+
93
+ // Simulate API call
94
+ setTimeout(() => {
95
+ Signal.set(isSubmitting, false)
96
+ Console.log("Form submitted successfully!")
97
+ }, 2000)->ignore
98
+ }
99
+
100
+ let handleReset = _evt => {
101
+ Signal.set(name, "")
102
+ Signal.set(email, "")
103
+ Signal.set(password, "")
104
+ Signal.set(message, "")
105
+ Signal.set(agreeToTerms, false)
106
+ Signal.set(newsletter, false)
107
+ Signal.set(selectedOption, "option1")
108
+ Signal.set(selectedColor, "blue")
109
+ Console.log("Form reset")
110
+ }
111
+
112
+ let selectOptions: array<selectOption> = [
113
+ {value: "option1", label: "Web Development"},
114
+ {value: "option2", label: "Mobile Development"},
115
+ {value: "option3", label: "UI/UX Design"},
116
+ {value: "option4", label: "Other"},
117
+ ]
118
+ let selectOptionsSignal = Signal.make(selectOptions)
119
+
120
+ <>
121
+ {Component.textSignal(() => Signal.get(selectedOption))}
122
+ <h1> {Component.text("basefn-UI Component Library")} </h1>
123
+ <p style="color: #6b7280; margin-bottom: 2rem;">
124
+ {Component.text(
125
+ "A demonstration of all form components with both static and reactive values.",
126
+ )}
127
+ </p>
128
+
129
+ <Card style="margin-bottom: 2rem;">
130
+ <Grid>
131
+ <Avatar src="https://upload.wikimedia.org/wikipedia/commons/a/ad/Schopfkarakara.jpg" />
132
+ <div>
133
+ <Typography
134
+ text={ReactiveProp.Static("Crested Caracara")}
135
+ variant=Typography.Unstyled
136
+ class="bold"
137
+ />
138
+ <Typography text={ReactiveProp.Static("Bird of prey")} variant=Typography.Small />
139
+ </div>
140
+ </Grid>
141
+ <br />
142
+ <Grid>
143
+ <Avatar src="https://upload.wikimedia.org/wikipedia/commons/a/ad/Schopfkarakara.jpg" />
144
+ <div>
145
+ <Typography
146
+ text={ReactiveProp.Static("Crested Caracara")} variant=Typography.P class="bold"
147
+ />
148
+ </div>
149
+ </Grid>
150
+ <br />
151
+ <Grid>
152
+ <Avatar
153
+ src="https://upload.wikimedia.org/wikipedia/commons/a/ad/Schopfkarakara.jpg" size={Sm}
154
+ />
155
+ <div>
156
+ <Typography
157
+ text={ReactiveProp.Static("Crested Caracara")} variant=Typography.Unstyled
158
+ />
159
+ </div>
160
+ </Grid>
161
+ <br />
162
+ <Grid gap="1rem">
163
+ <Avatar
164
+ src="https://upload.wikimedia.org/wikipedia/commons/a/ad/Schopfkarakara.jpg" size={Lg}
165
+ />
166
+ <div>
167
+ <Typography text={ReactiveProp.Static("Crested Caracara")} variant=Typography.H4 />
168
+ <Typography
169
+ text={ReactiveProp.Static("Bird of prey")}
170
+ variant=Typography.H6
171
+ class="muted font-normal"
172
+ />
173
+ </div>
174
+ </Grid>
175
+ </Card>
176
+
177
+ <Card>
178
+ <Label text="Full Name" required={true} />
179
+ <Input
180
+ value={Reactive(name)}
181
+ onInput={handleNameChange}
182
+ type_={Input.Text}
183
+ placeholder="John Doe"
184
+ />
185
+ <br />
186
+ <Label text="Email Address" required={true} />
187
+ <Input
188
+ value={Reactive(email)}
189
+ onInput={handleEmailChange}
190
+ type_={Input.Email}
191
+ placeholder="john@example.com"
192
+ />
193
+ <br />
194
+ <Label text="Password" required={true} />
195
+ <Input
196
+ value={Reactive(password)}
197
+ onInput={handlePasswordChange}
198
+ type_={Input.Password}
199
+ placeholder="Enter a secure password"
200
+ />
201
+ </Card>
202
+ <br />
203
+ <Card>
204
+ <Label text="Area of Interest" required={false} />
205
+ <Select
206
+ value={selectedOption}
207
+ options={selectOptionsSignal}
208
+ onChange={e => {
209
+ let target = Obj.magic(e)["target"]
210
+ Signal.set(selectedOption, target["value"])
211
+ }}
212
+ />
213
+ <br />
214
+ <Label text="Favorite Color" required={false} />
215
+ <div style="display: flex; gap: 1rem; margin-top: 0.5rem;">
216
+ <Radio
217
+ checked={Computed.make(() => Signal.get(selectedColor) == "blue")}
218
+ onChange={handleColorChange}
219
+ value="blue"
220
+ label="Blue"
221
+ name="radio"
222
+ />
223
+ <Radio
224
+ checked={Computed.make(() => Signal.get(selectedColor) == "green")}
225
+ onChange={handleColorChange}
226
+ value="green"
227
+ label="Green"
228
+ name="radio"
229
+ />
230
+ <Radio
231
+ checked={Computed.make(() => Signal.get(selectedColor) == "red")}
232
+ onChange={handleColorChange}
233
+ value="red"
234
+ label="Red"
235
+ name="radio"
236
+ />
237
+ </div>
238
+ </Card>
239
+ <br />
240
+ <Card>
241
+ <Label text="Message" required={false} />
242
+ <Textarea
243
+ value={Reactive(message)}
244
+ onInput={handleMessageChange}
245
+ placeholder="Tell us more about yourself..."
246
+ />
247
+ </Card>
248
+ <br />
249
+ <Card>
250
+ <div style="margin-bottom: 1.5rem;">
251
+ <Checkbox
252
+ checked={agreeToTerms}
253
+ onChange={handleTermsChange}
254
+ label="I agree to the terms and conditions"
255
+ />
256
+ </div>
257
+
258
+ <div style="margin-bottom: 2rem;">
259
+ <Checkbox
260
+ checked={newsletter}
261
+ onChange={handleNewsletterChange}
262
+ label="Subscribe to our newsletter"
263
+ />
264
+ </div>
265
+
266
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
267
+ {Component.SignalFragment(
268
+ Computed.make(() => {
269
+ [
270
+ <Button
271
+ label={Signal.get(isSubmitting) ? Static("Submitting...") : Static("Submit Form")}
272
+ onClick={handleSubmit}
273
+ variant={Button.Primary}
274
+ disabled={isSubmitting->ReactiveProp.Reactive}
275
+ />,
276
+ ]
277
+ }),
278
+ )}
279
+ <Button
280
+ label={Static("reset")}
281
+ onClick={handleReset}
282
+ variant={Button.Secondary}
283
+ disabled={isSubmitting->ReactiveProp.Reactive}
284
+ />
285
+ <Button
286
+ label={Static("Cancel")}
287
+ variant={Button.Ghost}
288
+ disabled={isSubmitting->ReactiveProp.Reactive}
289
+ />
290
+ </div>
291
+ </Card>
292
+
293
+ <div style="margin-top: 2rem;">
294
+ <Typography text={ReactiveProp.Static("Alerts")} variant={Typography.H4} />
295
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
296
+ {Component.text("Display important messages with different severity levels.")}
297
+ </p>
298
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
299
+ <Alert
300
+ title="Information"
301
+ message={Signal.make("This is an informational alert message.")}
302
+ variant={Alert.Info}
303
+ />
304
+ <Alert
305
+ title="Success"
306
+ message={Signal.make("Your changes have been saved successfully!")}
307
+ variant={Alert.Success}
308
+ />
309
+ <Alert
310
+ title="Warning"
311
+ message={Signal.make("Please review your input before proceeding.")}
312
+ variant={Alert.Warning}
313
+ />
314
+ <Alert
315
+ title="Error"
316
+ message={Signal.make("An error occurred while processing your request.")}
317
+ variant={Alert.Error}
318
+ />
319
+ <Alert
320
+ message={Signal.make("This is a dismissible alert. Click the X to close it.")}
321
+ variant={Alert.Info}
322
+ dismissible={true}
323
+ />
324
+ </div>
325
+ </div>
326
+
327
+ <div style="margin-top: 2rem;">
328
+ <Typography text={ReactiveProp.Static("Progress")} variant={Typography.H4} />
329
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
330
+ {Component.text("Show progress indicators for ongoing operations.")}
331
+ </p>
332
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
333
+ <div>
334
+ <Progress value={Signal.make(25.0)} variant={Progress.Primary} showLabel={true} />
335
+ </div>
336
+ <div>
337
+ <Progress
338
+ value={Signal.make(50.0)} variant={Progress.Success} showLabel={true} label="Upload"
339
+ />
340
+ </div>
341
+ <div>
342
+ <Progress
343
+ value={Signal.make(75.0)}
344
+ variant={Progress.Warning}
345
+ showLabel={true}
346
+ label="Processing"
347
+ />
348
+ </div>
349
+ <div>
350
+ <Progress
351
+ value={Signal.make(100.0)}
352
+ variant={Progress.Success}
353
+ showLabel={true}
354
+ label="Complete"
355
+ />
356
+ </div>
357
+ <div>
358
+ <Progress
359
+ value={downloadProgress}
360
+ variant={Progress.Primary}
361
+ showLabel={true}
362
+ label="Dynamic Progress"
363
+ />
364
+ <Button
365
+ label={Static("Simulate Progress")}
366
+ onClick={_evt => {
367
+ Signal.set(downloadProgress, 0.0)
368
+ let intervalId = ref(None)
369
+ let id = setInterval(() => {
370
+ Signal.update(downloadProgress, prev => {
371
+ let next = prev +. 5.0
372
+ if next >= 100.0 {
373
+ switch intervalId.contents {
374
+ | Some(id) => clearInterval(id)
375
+ | None => ()
376
+ }
377
+ 100.0
378
+ } else {
379
+ next
380
+ }
381
+ })
382
+ }, 100)
383
+ intervalId := Some(id)
384
+ }}
385
+ variant={Button.Secondary}
386
+ />
387
+ </div>
388
+ <div>
389
+ <p style="color: #6b7280; margin-bottom: 0.5rem;">
390
+ {Component.text("Indeterminate progress:")}
391
+ </p>
392
+ <Progress value={Signal.make(0.0)} variant={Progress.Primary} indeterminate={true} />
393
+ </div>
394
+ </div>
395
+ </div>
396
+
397
+ <div style="margin-top: 2rem;">
398
+ <Typography text={ReactiveProp.Static("Tabs")} variant={Typography.H4} />
399
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
400
+ {Component.text("Organize content into tabbed sections.")}
401
+ </p>
402
+ <Tabs
403
+ tabs={[
404
+ {
405
+ value: "account",
406
+ label: "Account",
407
+ content: <div>
408
+ <Typography
409
+ text={ReactiveProp.Static("Account Settings")} variant={Typography.H5}
410
+ />
411
+ <p style="color: #6b7280; margin-top: 0.5rem;">
412
+ {Component.text(
413
+ "Manage your account settings and preferences here. You can update your profile information, change your password, and configure notification settings.",
414
+ )}
415
+ </p>
416
+ </div>,
417
+ },
418
+ {
419
+ value: "security",
420
+ label: "Security",
421
+ content: <div>
422
+ <Typography
423
+ text={ReactiveProp.Static("Security Settings")} variant={Typography.H5}
424
+ />
425
+ <p style="color: #6b7280; margin-top: 0.5rem;">
426
+ {Component.text(
427
+ "Configure your security preferences including two-factor authentication, active sessions, and security logs.",
428
+ )}
429
+ </p>
430
+ <div style="margin-top: 1rem;">
431
+ <Checkbox
432
+ checked={Signal.make(true)}
433
+ onChange={_ => ()}
434
+ label="Enable two-factor authentication"
435
+ />
436
+ </div>
437
+ </div>,
438
+ },
439
+ {
440
+ value: "notifications",
441
+ label: "Notifications",
442
+ content: <div>
443
+ <Typography
444
+ text={ReactiveProp.Static("Notification Preferences")} variant={Typography.H5}
445
+ />
446
+ <p style="color: #6b7280; margin-top: 0.5rem;">
447
+ {Component.text("Choose how you want to receive notifications.")}
448
+ </p>
449
+ <div style="display: flex; flex-direction: column; gap: 0.75rem; margin-top: 1rem;">
450
+ <Checkbox
451
+ checked={Signal.make(true)} onChange={_ => ()} label="Email notifications"
452
+ />
453
+ <Checkbox
454
+ checked={Signal.make(false)} onChange={_ => ()} label="SMS notifications"
455
+ />
456
+ <Checkbox
457
+ checked={Signal.make(true)} onChange={_ => ()} label="Push notifications"
458
+ />
459
+ </div>
460
+ </div>,
461
+ },
462
+ {
463
+ value: "billing",
464
+ label: "Billing",
465
+ content: <div>
466
+ <Typography
467
+ text={ReactiveProp.Static("Billing Information")} variant={Typography.H5}
468
+ />
469
+ <p style="color: #6b7280; margin-top: 0.5rem;">
470
+ {Component.text(
471
+ "View and manage your billing information, payment methods, and invoices.",
472
+ )}
473
+ </p>
474
+ </div>,
475
+ disabled: true,
476
+ },
477
+ ]}
478
+ />
479
+ </div>
480
+
481
+ <div style="margin-top: 2rem;">
482
+ <Typography text={ReactiveProp.Static("Accordion")} variant={Typography.H4} />
483
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
484
+ {Component.text("Collapsible content sections with expand/collapse functionality.")}
485
+ </p>
486
+ <Accordion
487
+ items={[
488
+ {
489
+ value: "faq1",
490
+ title: "What is basefn-UI?",
491
+ content: <p>
492
+ {Component.text(
493
+ "basefn-UI is a modern, reactive UI component library built with ReScript and Xote. It provides a comprehensive set of accessible and customizable components for building web applications.",
494
+ )}
495
+ </p>,
496
+ },
497
+ {
498
+ value: "faq2",
499
+ title: "How do I install basefn-UI?",
500
+ content: <div>
501
+ <p>
502
+ {Component.text(
503
+ "You can install basefn-UI via npm or yarn. Here's how to get started:",
504
+ )}
505
+ </p>
506
+ <br />
507
+ <Typography
508
+ text={ReactiveProp.Static("npm install basefn-ui")} variant={Typography.Code}
509
+ />
510
+ </div>,
511
+ },
512
+ {
513
+ value: "faq3",
514
+ title: "Is basefn-UI customizable?",
515
+ content: <p>
516
+ {Component.text(
517
+ "Yes! basefn-UI is fully customizable. You can override the default styles using CSS variables or by providing custom CSS classes. Each component accepts standard HTML attributes including className and style.",
518
+ )}
519
+ </p>,
520
+ },
521
+ {
522
+ value: "faq4",
523
+ title: "Does basefn-UI support TypeScript?",
524
+ content: <p>
525
+ {Component.text(
526
+ "basefn-UI is built with ReScript, which provides excellent type safety. While it doesn't directly use TypeScript, ReScript's type system is even more robust and catches errors at compile time.",
527
+ )}
528
+ </p>,
529
+ },
530
+ ]}
531
+ multiple={true}
532
+ defaultOpen={["faq1"]}
533
+ />
534
+ </div>
535
+
536
+ <div style="margin-top: 2rem;">
537
+ <Typography text={ReactiveProp.Static("Breadcrumb")} variant={Typography.H4} />
538
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
539
+ {Component.text("Navigation breadcrumbs to show the current page hierarchy.")}
540
+ </p>
541
+ <div style="display: flex; flex-direction: column; gap: 1.5rem;">
542
+ <div>
543
+ <p style="color: #6b7280; margin-bottom: 0.5rem; font-size: 0.875rem;">
544
+ {Component.text("Default separator:")}
545
+ </p>
546
+ <Breadcrumb
547
+ items={[
548
+ {label: "Home", href: Some("#"), onClick: None},
549
+ {label: "Products", href: Some("#"), onClick: None},
550
+ {label: "Electronics", href: Some("#"), onClick: None},
551
+ {label: "Laptops", href: None, onClick: None},
552
+ ]}
553
+ />
554
+ </div>
555
+ <div>
556
+ <p style="color: #6b7280; margin-bottom: 0.5rem; font-size: 0.875rem;">
557
+ {Component.text("Custom separator:")}
558
+ </p>
559
+ <Breadcrumb
560
+ items={[
561
+ {label: "Home", href: Some("#"), onClick: None},
562
+ {label: "Settings", href: Some("#"), onClick: None},
563
+ {label: "Account", href: None, onClick: None},
564
+ ]}
565
+ separator=">"
566
+ />
567
+ </div>
568
+ <div>
569
+ <p style="color: #6b7280; margin-bottom: 0.5rem; font-size: 0.875rem;">
570
+ {Component.text("With onClick handlers:")}
571
+ </p>
572
+ <Breadcrumb
573
+ items={[
574
+ {
575
+ label: "Dashboard",
576
+ href: None,
577
+ onClick: Some(() => Console.log("Navigate to Dashboard")),
578
+ },
579
+ {
580
+ label: "Users",
581
+ href: None,
582
+ onClick: Some(() => Console.log("Navigate to Users")),
583
+ },
584
+ {label: "Profile", href: None, onClick: None},
585
+ ]}
586
+ separator="\u2022"
587
+ />
588
+ </div>
589
+ </div>
590
+ </div>
591
+
592
+ <Separator orientation={Separator.Horizontal} variant={Separator.Solid} label={"Tier 3"} />
593
+
594
+ <div style="margin-top: 3rem;">
595
+ <Typography
596
+ text={ReactiveProp.Static("Interactive Components")}
597
+ variant={Typography.H2}
598
+ align={Typography.Left}
599
+ />
600
+ <Typography
601
+ text={ReactiveProp.Static("Explore the Tier 3 advanced interactive components below.")}
602
+ variant={Typography.Muted}
603
+ />
604
+ </div>
605
+
606
+ // Modal Section
607
+ <div style="margin-top: 2rem;">
608
+ <Typography text={ReactiveProp.Static("Modal / Dialog")} variant={Typography.H4} />
609
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
610
+ {Component.text("Display content in an overlay dialog.")}
611
+ </p>
612
+ <Button
613
+ label={Static("Open Modal")}
614
+ onClick={_ => Signal.set(isModalOpen, true)}
615
+ variant={Button.Primary}
616
+ />
617
+ <Modal
618
+ isOpen={isModalOpen}
619
+ onClose={() => Signal.set(isModalOpen, false)}
620
+ title="Example Modal"
621
+ size={Modal.Md}
622
+ footer={<div style="display: flex; gap: 0.5rem;">
623
+ <Button
624
+ label={Static("Cancel")}
625
+ onClick={_ => Signal.set(isModalOpen, false)}
626
+ variant={Button.Ghost}
627
+ />
628
+ <Button
629
+ label={Static("Confirm")}
630
+ onClick={_ => {
631
+ Console.log("Confirmed!")
632
+ Signal.set(isModalOpen, false)
633
+ }}
634
+ variant={Button.Primary}
635
+ />
636
+ </div>}
637
+ >
638
+ <p>
639
+ {Component.text(
640
+ "This is a modal dialog. You can include any content here. Click the backdrop or the close button to dismiss.",
641
+ )}
642
+ </p>
643
+ <p style="margin-top: 1rem;">
644
+ {Component.text("Modals are great for focused user interactions and confirmations.")}
645
+ </p>
646
+ </Modal>
647
+ </div>
648
+
649
+ // Switch Section
650
+ <div style="margin-top: 2rem;">
651
+ <Typography text={ReactiveProp.Static("Switch / Toggle")} variant={Typography.H4} />
652
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
653
+ {Component.text("Binary on/off switches for settings and preferences.")}
654
+ </p>
655
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
656
+ <Switch checked={switchEnabled} label="Enable feature" />
657
+ <Switch checked={darkModeSwitch} label="Dark mode" size={Switch.Lg} />
658
+ <Switch checked={notificationsSwitch} label="Push notifications" size={Switch.Sm} />
659
+ <Switch
660
+ checked={Signal.make(true)} label="Disabled switch" disabled={true} size={Switch.Md}
661
+ />
662
+ </div>
663
+ </div>
664
+
665
+ // Slider Section
666
+ <div style="margin-top: 2rem;">
667
+ <Typography text={ReactiveProp.Static("Slider")} variant={Typography.H4} />
668
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
669
+ {Component.text("Select a value from a range.")}
670
+ </p>
671
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
672
+ <Slider value={sliderValue} label="Volume" showValue={true} />
673
+ <Slider
674
+ value={Signal.make(25.0)}
675
+ label="Brightness"
676
+ min={0.0}
677
+ max={100.0}
678
+ step={5.0}
679
+ showValue={true}
680
+ />
681
+ <Slider
682
+ value={Signal.make(3.0)}
683
+ min={0.0}
684
+ max={5.0}
685
+ step={1.0}
686
+ label="Rating"
687
+ showValue={true}
688
+ markers={["0", "1", "2", "3", "4", "5"]}
689
+ />
690
+ </div>
691
+ </div>
692
+
693
+ // Tooltip Section
694
+ <div style="margin-top: 2rem;">
695
+ <Typography text={ReactiveProp.Static("Tooltip")} variant={Typography.H4} />
696
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
697
+ {Component.text("Show contextual information on hover.")}
698
+ </p>
699
+ <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
700
+ <Tooltip content="This appears on top" position={Tooltip.Top}>
701
+ <Button label={Static("Hover me )(top)")} variant={Button.Secondary} />
702
+ </Tooltip>
703
+ <Tooltip content="This appears on bottom" position={Tooltip.Bottom}>
704
+ <Button label={Static("Hover me )(bottom)")} variant={Button.Secondary} />
705
+ </Tooltip>
706
+ <Tooltip content="This appears on left" position={Tooltip.Left}>
707
+ <Button label={Static("Hover me )(left)")} variant={Button.Secondary} />
708
+ </Tooltip>
709
+ <Tooltip content="This appears on right" position={Tooltip.Right}>
710
+ <Button label={Static("Hover me )(right)")} variant={Button.Secondary} />
711
+ </Tooltip>
712
+ </div>
713
+ </div>
714
+
715
+ // Dropdown Section
716
+ <div style="margin-top: 2rem;">
717
+ <Typography text={ReactiveProp.Static("Dropdown Menu")} variant={Typography.H4} />
718
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
719
+ {Component.text("Contextual menu with actions.")}
720
+ </p>
721
+ <div style="display: flex; gap: 1rem;">
722
+ <Dropdown
723
+ trigger={<Button label={Static("Actions")} variant={Button.Secondary} />}
724
+ items={[
725
+ Dropdown.Item({
726
+ label: "Edit",
727
+ onClick: () => Console.log("Edit clicked"),
728
+ }),
729
+ Dropdown.Item({
730
+ label: "Duplicate",
731
+ onClick: () => Console.log("Duplicate clicked"),
732
+ }),
733
+ Dropdown.Separator,
734
+ Dropdown.Item({
735
+ label: "Archive",
736
+ onClick: () => Console.log("Archive clicked"),
737
+ }),
738
+ Dropdown.Item({
739
+ label: "Delete",
740
+ onClick: () => Console.log("Delete clicked"),
741
+ danger: true,
742
+ }),
743
+ ]}
744
+ />
745
+ <Dropdown
746
+ trigger={<Button label={Static("More options")} variant={Button.Ghost} />}
747
+ items={[
748
+ Dropdown.Item({
749
+ label: "Settings",
750
+ onClick: () => Console.log("Settings"),
751
+ }),
752
+ Dropdown.Item({
753
+ label: "Help",
754
+ onClick: () => Console.log("Help"),
755
+ }),
756
+ Dropdown.Separator,
757
+ Dropdown.Item({
758
+ label: "Disabled item",
759
+ onClick: () => Console.log("Should not fire"),
760
+ disabled: true,
761
+ }),
762
+ ]}
763
+ align=#right
764
+ />
765
+ </div>
766
+ </div>
767
+
768
+ // Toast Section
769
+ <div style="margin-top: 2rem;">
770
+ <Typography text={ReactiveProp.Static("Toast / Notification")} variant={Typography.H4} />
771
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
772
+ {Component.text("Temporary notification messages.")}
773
+ </p>
774
+ <div style="display: flex; gap: 0.75rem; flex-wrap: wrap;">
775
+ <Button
776
+ label={Static("Show Toast")}
777
+ onClick={_ => Signal.set(toastVisible, true)}
778
+ variant={Button.Primary}
779
+ />
780
+ </div>
781
+ <Toast
782
+ title="Success!"
783
+ message="Your changes have been saved successfully."
784
+ variant={Toast.Success}
785
+ isVisible={toastVisible}
786
+ onClose={() => Console.log("Toast closed")}
787
+ />
788
+ </div>
789
+
790
+ <Separator orientation={Separator.Horizontal} variant={Separator.Solid} label={"Tier 4"} />
791
+
792
+ <div style="margin-top: 3rem;">
793
+ <Typography
794
+ text={ReactiveProp.Static("Navigation & Layout")}
795
+ variant={Typography.H2}
796
+ align={Typography.Left}
797
+ />
798
+ <Typography
799
+ text={ReactiveProp.Static("Explore the Tier 4 navigation and layout components below.")}
800
+ variant={Typography.Muted}
801
+ />
802
+ </div>
803
+
804
+ // Stepper Section
805
+ <div style="margin-top: 2rem;">
806
+ <Typography text={ReactiveProp.Static("Stepper")} variant={Typography.H4} />
807
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
808
+ {Component.text("Multi-step process indicator with progress tracking.")}
809
+ </p>
810
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
811
+ <div>
812
+ <p style="color: #6b7280; margin-bottom: 1rem; font-size: 0.875rem;">
813
+ {Component.text("Horizontal stepper:")}
814
+ </p>
815
+ <Stepper
816
+ steps={[
817
+ {
818
+ title: "Account Info",
819
+ description: Some("Enter your details"),
820
+ status: Stepper.Completed,
821
+ },
822
+ {
823
+ title: "Verification",
824
+ description: Some("Verify your email"),
825
+ status: Stepper.Active,
826
+ },
827
+ {
828
+ title: "Preferences",
829
+ description: Some("Set your preferences"),
830
+ status: Stepper.Inactive,
831
+ },
832
+ {
833
+ title: "Complete",
834
+ description: Some("All done!"),
835
+ status: Stepper.Inactive,
836
+ },
837
+ ]}
838
+ currentStep={currentStep}
839
+ orientation={Stepper.Horizontal}
840
+ onStepClick={step => {
841
+ Signal.set(currentStep, step)
842
+ Console.log2("Step clicked:", step)
843
+ }}
844
+ />
845
+ </div>
846
+ <div>
847
+ <p style="color: #6b7280; margin-bottom: 1rem; font-size: 0.875rem;">
848
+ {Component.text("Vertical stepper:")}
849
+ </p>
850
+ <Stepper
851
+ steps={[
852
+ {
853
+ title: "Order Placed",
854
+ description: Some("Your order has been confirmed"),
855
+ status: Stepper.Completed,
856
+ },
857
+ {
858
+ title: "Processing",
859
+ description: Some("We are preparing your order"),
860
+ status: Stepper.Completed,
861
+ },
862
+ {
863
+ title: "Shipped",
864
+ description: Some("Your order is on the way"),
865
+ status: Stepper.Active,
866
+ },
867
+ {
868
+ title: "Delivered",
869
+ description: Some("Enjoy your purchase!"),
870
+ status: Stepper.Inactive,
871
+ },
872
+ ]}
873
+ currentStep={Signal.make(2)}
874
+ orientation={Stepper.Vertical}
875
+ />
876
+ </div>
877
+ </div>
878
+ </div>
879
+
880
+ // Drawer Section
881
+ <div style="margin-top: 2rem;">
882
+ <Typography text={ReactiveProp.Static("Drawer / Sidebar")} variant={Typography.H4} />
883
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
884
+ {Component.text("Slide-in panels for additional content.")}
885
+ </p>
886
+ <div style="display: flex; gap: 0.75rem; flex-wrap: wrap;">
887
+ <Button
888
+ label={Static("Open Drawer")}
889
+ onClick={_ => Signal.set(isDrawerOpen, true)}
890
+ variant={Button.Primary}
891
+ />
892
+ </div>
893
+ <Drawer
894
+ isOpen={isDrawerOpen}
895
+ onClose={() => Signal.set(isDrawerOpen, false)}
896
+ title="Drawer Panel"
897
+ position={Drawer.Right}
898
+ size={Drawer.Md}
899
+ footer={<div style="display: flex; gap: 0.5rem; justify-content: flex-end;">
900
+ <Button
901
+ label={Static("Cancel")}
902
+ onClick={_ => Signal.set(isDrawerOpen, false)}
903
+ variant={Button.Ghost}
904
+ />
905
+ <Button
906
+ label={Static("Save")}
907
+ onClick={_ => {
908
+ Console.log("Saved!")
909
+ Signal.set(isDrawerOpen, false)
910
+ }}
911
+ variant={Button.Primary}
912
+ />
913
+ </div>}
914
+ >
915
+ <div>
916
+ <Typography text={ReactiveProp.Static("Drawer Content")} variant={Typography.H5} />
917
+ <p style="margin-top: 1rem;">
918
+ {Component.text(
919
+ "This is a drawer panel. You can use it for navigation, forms, or any additional content that doesn't fit in the main view.",
920
+ )}
921
+ </p>
922
+ <div style="margin-top: 1.5rem;">
923
+ <Label text="Name" />
924
+ <Input value={Static("")} type_={Input.Text} placeholder="Enter your name" />
925
+ </div>
926
+ <div style="margin-top: 1rem;">
927
+ <Label text="Email" />
928
+ <Input value={Static("")} type_={Input.Email} placeholder="Enter your email" />
929
+ </div>
930
+ <div style="margin-top: 1rem;">
931
+ <Label text="Message" />
932
+ <Textarea value={ReactiveProp.Static("")} placeholder="Enter a message" />
933
+ </div>
934
+ </div>
935
+ </Drawer>
936
+ </div>
937
+
938
+ // Timeline Section
939
+ <div style="margin-top: 2rem;">
940
+ <Typography text={ReactiveProp.Static("Timeline")} variant={Typography.H4} />
941
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
942
+ {Component.text("Display chronological events in a visual timeline.")}
943
+ </p>
944
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
945
+ <div>
946
+ <p style="color: #6b7280; margin-bottom: 1rem; font-size: 0.875rem;">
947
+ {Component.text("Vertical timeline:")}
948
+ </p>
949
+ <Timeline
950
+ items={[
951
+ {
952
+ title: "Project Created",
953
+ timestamp: Some("2 hours ago"),
954
+ description: Some("Initial project setup and configuration"),
955
+ variant: Timeline.Success,
956
+ icon: Some("\u2713"),
957
+ },
958
+ {
959
+ title: "First Commit",
960
+ timestamp: Some("1 hour ago"),
961
+ description: Some("Added base components and styling"),
962
+ variant: Timeline.Success,
963
+ icon: Some("\u2713"),
964
+ },
965
+ {
966
+ title: "Code Review",
967
+ timestamp: Some("30 minutes ago"),
968
+ description: Some("Team reviewing the implementation"),
969
+ variant: Timeline.Primary,
970
+ icon: None,
971
+ },
972
+ {
973
+ title: "Deployment",
974
+ timestamp: Some("Pending"),
975
+ description: Some("Awaiting approval for production deployment"),
976
+ variant: Timeline.Default,
977
+ icon: None,
978
+ },
979
+ ]}
980
+ orientation={Timeline.Vertical}
981
+ />
982
+ </div>
983
+ <div>
984
+ <p style="color: #6b7280; margin-bottom: 1rem; font-size: 0.875rem;">
985
+ {Component.text("With different variants:")}
986
+ </p>
987
+ <Timeline
988
+ items={[
989
+ {
990
+ title: "Success Event",
991
+ timestamp: None,
992
+ description: Some("Operation completed successfully"),
993
+ variant: Timeline.Success,
994
+ icon: Some("\u2713"),
995
+ },
996
+ {
997
+ title: "Warning Event",
998
+ timestamp: None,
999
+ description: Some("Requires attention"),
1000
+ variant: Timeline.Warning,
1001
+ icon: Some("!"),
1002
+ },
1003
+ {
1004
+ title: "Error Event",
1005
+ timestamp: None,
1006
+ description: Some("Operation failed"),
1007
+ variant: Timeline.Error,
1008
+ icon: Some("\u00d7"),
1009
+ },
1010
+ ]}
1011
+ orientation={Timeline.Vertical}
1012
+ />
1013
+ </div>
1014
+ </div>
1015
+ </div>
1016
+
1017
+ <Separator
1018
+ orientation={Separator.Horizontal} variant={Separator.Solid} label={"App Layouts"}
1019
+ />
1020
+
1021
+ <div style="margin-top: 3rem;">
1022
+ <Typography
1023
+ text={ReactiveProp.Static("Application Layouts")}
1024
+ variant={Typography.H2}
1025
+ align={Typography.Left}
1026
+ />
1027
+ <Typography
1028
+ text={ReactiveProp.Static(
1029
+ "Complete application layout structures with sidebar and topbar combinations.",
1030
+ )}
1031
+ variant={Typography.Muted}
1032
+ />
1033
+ </div>
1034
+
1035
+ // Layout Examples Section
1036
+ <div style="margin-top: 2rem;">
1037
+ <Typography text={ReactiveProp.Static("Layout Variants")} variant={Typography.H4} />
1038
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
1039
+ {Component.text("Different application layout configurations.")}
1040
+ </p>
1041
+ <div style="display: flex; flex-direction: column; gap: 2rem;">
1042
+ // Sidebar Only Example
1043
+ <div>
1044
+ <Typography text={ReactiveProp.Static("Sidebar Only Layout")} variant={Typography.H5} />
1045
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0; font-size: 0.875rem;">
1046
+ {Component.text("Application with sidebar navigation")}
1047
+ </p>
1048
+ <div
1049
+ style="border: 2px solid #e5e7eb; border-radius: 0.5rem; overflow: hidden; height: 400px;"
1050
+ >
1051
+ <AppLayout
1052
+ sidebar={<Sidebar
1053
+ logo={Component.text("eita UI")}
1054
+ sections={[
1055
+ {
1056
+ title: Some("Main"),
1057
+ items: [
1058
+ {
1059
+ label: "Dashboard",
1060
+ icon: Some("\u2302"),
1061
+ active: Signal.get(activeNavItem) == "home",
1062
+ url: "/profile",
1063
+ },
1064
+ {
1065
+ label: "Analytics",
1066
+ icon: Some("\u{1F4CA}"),
1067
+ active: Signal.get(activeNavItem) == "analytics",
1068
+ url: "/profile",
1069
+ },
1070
+ ],
1071
+ },
1072
+ {
1073
+ title: Some("Settings"),
1074
+ items: [
1075
+ {
1076
+ label: "Profile",
1077
+ icon: Some("\u{1F464}"),
1078
+ active: Signal.get(activeNavItem) == "profile",
1079
+ url: "/profile",
1080
+ },
1081
+ {
1082
+ label: "Settings",
1083
+ icon: Some("\u2699"),
1084
+ active: Signal.get(activeNavItem) == "settings",
1085
+ url: "/profile",
1086
+ },
1087
+ ],
1088
+ },
1089
+ ]}
1090
+ size={Sidebar.Md}
1091
+ />}
1092
+ >
1093
+ <div style="padding: 2rem;">
1094
+ <Typography
1095
+ text={ReactiveProp.Static("Main Content Area")} variant={Typography.H3}
1096
+ />
1097
+ <p style="margin-top: 1rem;">
1098
+ {Component.text(
1099
+ "This is the main content area. The sidebar provides persistent navigation.",
1100
+ )}
1101
+ </p>
1102
+ </div>
1103
+ </AppLayout>
1104
+ </div>
1105
+ </div>
1106
+
1107
+ // Topbar Only Example
1108
+ <div>
1109
+ <Typography text={ReactiveProp.Static("Topbar Only Layout")} variant={Typography.H5} />
1110
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0; font-size: 0.875rem;">
1111
+ {Component.text("Application with top navigation bar")}
1112
+ </p>
1113
+ <div
1114
+ style="border: 2px solid #e5e7eb; border-radius: 0.5rem; overflow: hidden; height: 300px;"
1115
+ >
1116
+ <AppLayout
1117
+ topbar={<Topbar
1118
+ logo={Component.text("eita UI")}
1119
+ navItems={[
1120
+ {
1121
+ label: "Home",
1122
+ active: Signal.get(activeNavItem) == "home",
1123
+ onClick: () => Signal.set(activeNavItem, "home"),
1124
+ },
1125
+ {
1126
+ label: "Products",
1127
+ active: Signal.get(activeNavItem) == "products",
1128
+ onClick: () => Signal.set(activeNavItem, "products"),
1129
+ },
1130
+ {
1131
+ label: "About",
1132
+ active: Signal.get(activeNavItem) == "about",
1133
+ onClick: () => Signal.set(activeNavItem, "about"),
1134
+ },
1135
+ ]}
1136
+ rightContent={<div style="display: flex; gap: 0.5rem;">
1137
+ <Button label={Static("Sign In")} variant={Button.Ghost} />
1138
+ <Button label={Static("Sign Up")} variant={Button.Primary} />
1139
+ </div>}
1140
+ />}
1141
+ >
1142
+ <div style="padding: 2rem;">
1143
+ <Typography
1144
+ text={ReactiveProp.Static("Main Content Area")} variant={Typography.H3}
1145
+ />
1146
+ <p style="margin-top: 1rem;">
1147
+ {Component.text("This layout uses only a top navigation bar.")}
1148
+ </p>
1149
+ </div>
1150
+ </AppLayout>
1151
+ </div>
1152
+ </div>
1153
+
1154
+ // Sidebar + Topbar Example
1155
+ <div>
1156
+ <Typography
1157
+ text={ReactiveProp.Static("Sidebar + Topbar Layout")} variant={Typography.H5}
1158
+ />
1159
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0; font-size: 0.875rem;">
1160
+ {Component.text("Full application layout with both sidebar and topbar")}
1161
+ </p>
1162
+ <div
1163
+ style="border: 2px solid #e5e7eb; border-radius: 0.5rem; overflow: hidden; height: 500px;"
1164
+ >
1165
+ <AppLayout
1166
+ sidebar={<Sidebar
1167
+ logo={Component.text("basefn")}
1168
+ sections={[
1169
+ {
1170
+ title: Some("Navigation"),
1171
+ items: [
1172
+ {
1173
+ label: "Dashboard",
1174
+ icon: Some("\u2302"),
1175
+ active: true,
1176
+ url: "/",
1177
+ },
1178
+ {
1179
+ label: "Projects",
1180
+ icon: Some("\u{1F4C1}"),
1181
+ active: false,
1182
+ url: "/",
1183
+ },
1184
+ {
1185
+ label: "Tasks",
1186
+ icon: Some("\u2713"),
1187
+ active: false,
1188
+ url: "/",
1189
+ },
1190
+ ],
1191
+ },
1192
+ ]}
1193
+ size={Sidebar.Md}
1194
+ collapsed={Signal.get(sidebarCollapsed)}
1195
+ />}
1196
+ topbar={<Topbar
1197
+ onMenuClick={() => Signal.update(sidebarCollapsed, prev => !prev)}
1198
+ rightContent={<div style="display: flex; align-items: center; gap: 1rem;">
1199
+ <Badge label={Signal.make("3")} variant={Badge.Primary} />
1200
+ <Avatar src="https://ui-avatars.com/api/?name=John+Doe" size={Avatar.Sm} />
1201
+ </div>}
1202
+ />}
1203
+ sidebarSize={"md"}
1204
+ sidebarCollapsed={Signal.get(sidebarCollapsed)}
1205
+ >
1206
+ <div style="padding: 2rem;">
1207
+ <Typography text={ReactiveProp.Static("Dashboard")} variant={Typography.H3} />
1208
+ <p style="margin-top: 1rem;">
1209
+ {Component.text(
1210
+ "This is a complete application layout with both sidebar and topbar. Click the menu button in the topbar to toggle the sidebar.",
1211
+ )}
1212
+ </p>
1213
+ <div
1214
+ style="margin-top: 2rem; display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;"
1215
+ >
1216
+ <Card>
1217
+ <Typography
1218
+ text={ReactiveProp.Static("Total Users")} variant={Typography.H6}
1219
+ />
1220
+ <Typography
1221
+ text={ReactiveProp.Static("1,234")}
1222
+ variant={Typography.H2}
1223
+ class="text-primary"
1224
+ />
1225
+ </Card>
1226
+ <Card>
1227
+ <Typography
1228
+ text={ReactiveProp.Static("Active Projects")} variant={Typography.H6}
1229
+ />
1230
+ <Typography text={ReactiveProp.Static("45")} variant={Typography.H2} />
1231
+ </Card>
1232
+ <Card>
1233
+ <Typography
1234
+ text={ReactiveProp.Static("Completed Tasks")} variant={Typography.H6}
1235
+ />
1236
+ <Typography text={ReactiveProp.Static("892")} variant={Typography.H2} />
1237
+ </Card>
1238
+ </div>
1239
+ </div>
1240
+ </AppLayout>
1241
+ </div>
1242
+ </div>
1243
+ </div>
1244
+ </div>
1245
+
1246
+ <Separator
1247
+ orientation={Separator.Horizontal} variant={Separator.Dashed} label={"Foundational"}
1248
+ />
1249
+
1250
+ <div style="margin-top: 3rem;">
1251
+ <Typography
1252
+ text={ReactiveProp.Static("Foundation Components")}
1253
+ variant={Typography.H2}
1254
+ align={Typography.Left}
1255
+ />
1256
+ <Typography
1257
+ text={ReactiveProp.Static("Explore the Tier 1 foundation components below.")}
1258
+ variant={Typography.Muted}
1259
+ />
1260
+ </div>
1261
+
1262
+ // Badges Section
1263
+ <div style="margin-top: 2rem;">
1264
+ <Typography text={ReactiveProp.Static("Badges")} variant={Typography.H4} />
1265
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
1266
+ {Component.text("Display status indicators and labels with various styles.")}
1267
+ </p>
1268
+ <div style="display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center;">
1269
+ <Badge label={Signal.make("Default")} variant={Badge.Default} />
1270
+ <Badge label={Signal.make("Primary")} variant={Badge.Primary} />
1271
+ <Badge label={Signal.make("Secondary")} variant={Badge.Secondary} />
1272
+ <Badge label={Signal.make("Success")} variant={Badge.Success} />
1273
+ <Badge label={Signal.make("Warning")} variant={Badge.Warning} />
1274
+ <Badge label={Signal.make("Error")} variant={Badge.Error} />
1275
+ </div>
1276
+ <div
1277
+ style="display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; margin-top: 1rem;"
1278
+ >
1279
+ <Badge label={Signal.make("Small")} variant={Badge.Primary} size={Badge.Sm} />
1280
+ <Badge label={Signal.make("Medium")} variant={Badge.Primary} size={Badge.Md} />
1281
+ <Badge label={Signal.make("Large")} variant={Badge.Primary} size={Badge.Lg} />
1282
+ <Badge label={Signal.make("Online")} variant={Badge.Success} dot={true} />
1283
+ <Badge label={Signal.make("Away")} variant={Badge.Warning} dot={true} />
1284
+ </div>
1285
+ </div>
1286
+
1287
+ <Separator orientation={Separator.Horizontal} variant={Separator.Dashed} label={"Spinners"} />
1288
+
1289
+ // Spinners Section
1290
+ <div style="margin-top: 2rem;">
1291
+ <Typography text={ReactiveProp.Static("Spinners")} variant={Typography.H4} />
1292
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
1293
+ {Component.text("Loading indicators in different sizes and colors.")}
1294
+ </p>
1295
+ <div style="display: flex; gap: 2rem; flex-wrap: wrap; align-items: center;">
1296
+ <Spinner size={Spinner.Sm} variant={Spinner.Default} />
1297
+ <Spinner size={Spinner.Md} variant={Spinner.Primary} />
1298
+ <Spinner size={Spinner.Lg} variant={Spinner.Secondary} />
1299
+ <Spinner size={Spinner.Xl} variant={Spinner.Primary} />
1300
+ </div>
1301
+ <div style="display: flex; gap: 2rem; flex-wrap: wrap; margin-top: 1.5rem;">
1302
+ <Spinner size={Spinner.Md} variant={Spinner.Primary} label="Loading..." />
1303
+ <Spinner
1304
+ size={Spinner.Lg}
1305
+ variant={Spinner.Default}
1306
+ label={Signal.get(isSubmitting) ? "Submitting..." : "Ready"}
1307
+ />
1308
+ </div>
1309
+ </div>
1310
+
1311
+ <Separator orientation={Separator.Horizontal} variant={Separator.Dotted} />
1312
+
1313
+ // Keyboard Shortcuts Section
1314
+ <div style="margin-top: 2rem;">
1315
+ <Typography text={ReactiveProp.Static("Keyboard Shortcuts")} variant={Typography.H4} />
1316
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
1317
+ {Component.text("Display keyboard shortcuts in a visually appealing way.")}
1318
+ </p>
1319
+ <div style="display: flex; gap: 1.5rem; flex-wrap: wrap; align-items: center;">
1320
+ <div>
1321
+ <span style="color: #6b7280; margin-right: 0.5rem;"> {Component.text("Copy:")} </span>
1322
+ <Kbd keys={Signal.make(["Ctrl", "C"])} size={Kbd.Md} />
1323
+ </div>
1324
+ <div>
1325
+ <span style="color: #6b7280; margin-right: 0.5rem;"> {Component.text("Paste:")} </span>
1326
+ <Kbd keys={Signal.make(["Ctrl", "V"])} size={Kbd.Md} />
1327
+ </div>
1328
+ <div>
1329
+ <span style="color: #6b7280; margin-right: 0.5rem;"> {Component.text("Save:")} </span>
1330
+ <Kbd keys={Signal.make(["Ctrl", "S"])} size={Kbd.Md} />
1331
+ </div>
1332
+ <div>
1333
+ <span style="color: #6b7280; margin-right: 0.5rem;">
1334
+ {Component.text("Select All:")}
1335
+ </span>
1336
+ <Kbd keys={Signal.make(["Ctrl", "A"])} size={Kbd.Md} />
1337
+ </div>
1338
+ </div>
1339
+ <div style="margin-top: 1rem;">
1340
+ <Kbd keys={Signal.make(["Shift", "Alt", "F"])} size={Kbd.Sm} />
1341
+ <span style="color: #6b7280; margin-left: 0.5rem;">
1342
+ {Component.text("Format Document")}
1343
+ </span>
1344
+ </div>
1345
+ </div>
1346
+
1347
+ <Separator orientation={Separator.Horizontal} variant={Separator.Solid} />
1348
+
1349
+ // Typography Section
1350
+ <div style="margin-top: 2rem;">
1351
+ <Typography text={ReactiveProp.Static("Typography")} variant={Typography.H4} />
1352
+ <p style="color: #6b7280; margin: 0.5rem 0 1rem 0;">
1353
+ {Component.text("Consistent text styling across your application.")}
1354
+ </p>
1355
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
1356
+ <Typography text={ReactiveProp.Static("Heading 1")} variant={Typography.H1} />
1357
+ <Typography text={ReactiveProp.Static("Heading 2")} variant={Typography.H2} />
1358
+ <Typography text={ReactiveProp.Static("Heading 3")} variant={Typography.H3} />
1359
+ <Typography text={ReactiveProp.Static("Heading 4")} variant={Typography.H4} />
1360
+ <Typography text={ReactiveProp.Static("Heading 5")} variant={Typography.H5} />
1361
+ <Typography text={ReactiveProp.Static("Heading 6")} variant={Typography.H6} />
1362
+ <Separator orientation={Separator.Horizontal} variant={Separator.Dashed} />
1363
+ <Typography
1364
+ text={ReactiveProp.Static(
1365
+ "This is a regular paragraph with normal text styling and comfortable line height.",
1366
+ )}
1367
+ variant={Typography.P}
1368
+ />
1369
+ <Typography
1370
+ text={ReactiveProp.Static(
1371
+ "This is a lead paragraph that stands out with larger text and is perfect for introductions.",
1372
+ )}
1373
+ variant={Typography.Lead}
1374
+ />
1375
+ <Typography
1376
+ text={ReactiveProp.Static("This is small text, useful for captions and helper text.")}
1377
+ variant={Typography.Small}
1378
+ />
1379
+ <Typography
1380
+ text={ReactiveProp.Static("This is muted text with reduced emphasis.")}
1381
+ variant={Typography.Muted}
1382
+ />
1383
+ <Typography
1384
+ text={ReactiveProp.Static("const hello = 'world'")} variant={Typography.Code}
1385
+ />
1386
+ </div>
1387
+ </div>
1388
+
1389
+ <div
1390
+ style="margin-top: 3rem; padding: 1rem; background-color: #f3f4f6; border-radius: 0.5rem;"
1391
+ >
1392
+ <h3 style="margin-top: 0; color: #374151;"> {Component.text("Form State (Real-time)")} </h3>
1393
+ <pre
1394
+ style="background-color: #1f2937; color: #f9fafb; padding: 1rem; border-radius: 0.25rem; overflow-x: auto; font-size: 0.875rem;"
1395
+ >
1396
+ {Component.textSignal(() => {
1397
+ `Name: ${Signal.get(name)}
1398
+ Email: ${Signal.get(email)}
1399
+ Password: ${"*"->String.repeat(Signal.get(password)->String.length)}
1400
+ Interest: ${Signal.get(selectedOption)}
1401
+ Color: ${Signal.get(selectedColor)}
1402
+ Message: ${Signal.get(message)->String.slice(~start=0, ~end=50)}${Signal.get(
1403
+ message,
1404
+ )->String.length > 50
1405
+ ? "..."
1406
+ : ""}
1407
+ Terms: ${Signal.get(agreeToTerms)->Bool.toString}
1408
+ Newsletter: ${Signal.get(newsletter)->Bool.toString}`
1409
+ })}
1410
+ </pre>
1411
+ </div>
1412
+ </>
1413
+ }
1414
+ }
1415
+
1416
+ // Mount the demo application
1417
+ Component.mountById(<Demo />, "root")