akanjs 2.4.2-rc.2 → 3.0.0-alpha.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 (170) hide show
  1. package/base/baseEnv.ts +0 -6
  2. package/client/cn.ts +52 -0
  3. package/client/csrTypes.ts +2 -2
  4. package/client/frameConfig.ts +2 -2
  5. package/client/index.ts +1 -0
  6. package/client/types.ts +0 -4
  7. package/common/fileUpload.ts +1 -1
  8. package/common/index.ts +1 -0
  9. package/common/isThenable.ts +12 -0
  10. package/constant/purify.ts +9 -10
  11. package/fetch/client/fetchClient.ts +1 -1
  12. package/fetch/client/httpClient.ts +9 -5
  13. package/fetch/fetchType/sliceFetch.type.ts +1 -1
  14. package/package.json +4 -7
  15. package/server/akanApp.ts +2 -0
  16. package/server/systemPageDocument.tsx +12 -12
  17. package/signal/endpointInfo.ts +2 -1
  18. package/signal/slice.ts +1 -1
  19. package/signal/sliceInfo.ts +2 -1
  20. package/store/action.ts +18 -2
  21. package/store/baseSt.ts +2 -2
  22. package/store/state.ts +9 -1
  23. package/store/storeInstance.ts +2 -0
  24. package/test/sampleOf.ts +3 -3
  25. package/test/signalTestRuntime.ts +0 -2
  26. package/types/base/baseEnv.d.ts +0 -2
  27. package/types/client/cn.d.ts +12 -0
  28. package/types/client/index.d.ts +1 -0
  29. package/types/client/types.d.ts +0 -3
  30. package/types/common/fileUpload.d.ts +1 -1
  31. package/types/common/index.d.ts +1 -0
  32. package/types/common/isThenable.d.ts +9 -0
  33. package/types/constant/purify.d.ts +3 -2
  34. package/types/fetch/fetchType/sliceFetch.type.d.ts +1 -1
  35. package/types/signal/endpointInfo.d.ts +2 -2
  36. package/types/signal/slice.d.ts +1 -1
  37. package/types/signal/sliceInfo.d.ts +2 -2
  38. package/types/store/action.d.ts +1 -1
  39. package/types/store/state.d.ts +4 -2
  40. package/types/test/sampleOf.d.ts +2 -2
  41. package/types/ui/Badge.d.ts +6 -0
  42. package/types/ui/Button.d.ts +26 -6
  43. package/types/ui/Load/Units.d.ts +3 -1
  44. package/types/ui/RecentTime.d.ts +1 -1
  45. package/types/ui/Signal/Collapse.d.ts +12 -0
  46. package/types/ui/Signal/style.d.ts +5 -6
  47. package/types/ui/Switch.d.ts +14 -0
  48. package/types/ui/Tooltip.d.ts +11 -0
  49. package/types/ui/UiOverride/Provider.d.ts +6 -5
  50. package/types/ui/UiOverride/context.d.ts +22 -1
  51. package/types/ui/UiOverride/index.d.ts +2 -1
  52. package/types/ui/UiOverride/override.d.ts +7 -6
  53. package/types/ui/UiOverride/useUiRecipe.d.ts +8 -0
  54. package/types/ui/index.d.ts +5 -1
  55. package/types/ui/recipe/badgeRecipe.d.ts +13 -0
  56. package/types/ui/recipe/buttonRecipe.d.ts +15 -0
  57. package/types/ui/recipe/factory.d.ts +24 -0
  58. package/types/ui/recipe/index.d.ts +18 -0
  59. package/types/ui/recipe/inputRecipe.d.ts +13 -0
  60. package/types/ui/recipe.d.ts +1 -0
  61. package/ui/Badge.tsx +17 -0
  62. package/ui/BottomSheet.tsx +8 -10
  63. package/ui/Button.tsx +158 -48
  64. package/ui/Clipboard.tsx +3 -3
  65. package/ui/Constant/Doc.tsx +108 -65
  66. package/ui/Constant/Mermaid.tsx +9 -9
  67. package/ui/Data/CardList.tsx +3 -3
  68. package/ui/Data/Dashboard.tsx +22 -13
  69. package/ui/Data/Insight.tsx +6 -6
  70. package/ui/Data/Item.tsx +49 -34
  71. package/ui/Data/ListContainer.tsx +11 -7
  72. package/ui/Data/Pagination.tsx +2 -2
  73. package/ui/DatePicker.tsx +5 -5
  74. package/ui/Dialog/Content.tsx +2 -2
  75. package/ui/Dialog/Modal.tsx +12 -8
  76. package/ui/Dialog/Provider.tsx +2 -2
  77. package/ui/DragAction.tsx +2 -2
  78. package/ui/DraggableList.tsx +10 -5
  79. package/ui/Dropdown.tsx +33 -26
  80. package/ui/Empty.tsx +5 -5
  81. package/ui/Field.tsx +82 -70
  82. package/ui/Image.tsx +2 -2
  83. package/ui/Input.tsx +62 -60
  84. package/ui/Layout/BottomInset.tsx +2 -2
  85. package/ui/Layout/BottomTab.tsx +5 -5
  86. package/ui/Layout/Header.tsx +3 -3
  87. package/ui/Layout/LeftSider.tsx +6 -5
  88. package/ui/Layout/Navbar.tsx +2 -12
  89. package/ui/Layout/RightSider.tsx +10 -8
  90. package/ui/Layout/Sider.tsx +5 -4
  91. package/ui/Layout/Template.tsx +2 -2
  92. package/ui/Layout/TopInset.tsx +2 -2
  93. package/ui/Layout/Unit.tsx +2 -2
  94. package/ui/Layout/View.tsx +2 -2
  95. package/ui/Layout/Zone.tsx +2 -2
  96. package/ui/Link/Back.tsx +2 -2
  97. package/ui/Link/Close.tsx +2 -2
  98. package/ui/Link/CsrLink.tsx +6 -4
  99. package/ui/Link/Lang.tsx +2 -2
  100. package/ui/Link/SsrLink.tsx +6 -5
  101. package/ui/Load/Page.tsx +1 -1
  102. package/ui/Load/Pagination.tsx +2 -2
  103. package/ui/Load/Units.tsx +19 -4
  104. package/ui/Load/View.tsx +2 -2
  105. package/ui/Loading/Area.tsx +4 -2
  106. package/ui/Loading/Button.tsx +2 -2
  107. package/ui/Loading/Input.tsx +2 -2
  108. package/ui/Loading/ProgressBar.tsx +2 -2
  109. package/ui/Loading/Skeleton.tsx +6 -6
  110. package/ui/Loading/Spin.tsx +2 -2
  111. package/ui/Menu.tsx +29 -27
  112. package/ui/Model/Edit.tsx +3 -3
  113. package/ui/Model/EditModal.tsx +5 -5
  114. package/ui/Model/EditWrapper.tsx +2 -2
  115. package/ui/Model/New.tsx +3 -3
  116. package/ui/Model/NewWrapper_Client.tsx +2 -2
  117. package/ui/Model/Remove.tsx +5 -4
  118. package/ui/Model/RemoveWrapper.tsx +2 -2
  119. package/ui/Model/SureToRemove.tsx +7 -6
  120. package/ui/Model/View.tsx +2 -2
  121. package/ui/Model/ViewEditModal.tsx +20 -21
  122. package/ui/Model/ViewWrapper.tsx +2 -2
  123. package/ui/More.tsx +2 -2
  124. package/ui/ObjectId.tsx +9 -3
  125. package/ui/Pagination.tsx +14 -11
  126. package/ui/Popconfirm.tsx +20 -11
  127. package/ui/Radio.tsx +4 -4
  128. package/ui/RecentTime.tsx +30 -83
  129. package/ui/Refresh.tsx +1 -1
  130. package/ui/ScreenNavigator.tsx +2 -2
  131. package/ui/Select.tsx +21 -23
  132. package/ui/Signal/Arg.tsx +21 -18
  133. package/ui/Signal/Collapse.tsx +29 -0
  134. package/ui/Signal/Doc.tsx +27 -22
  135. package/ui/Signal/Listener.tsx +3 -3
  136. package/ui/Signal/Message.tsx +31 -17
  137. package/ui/Signal/Object.tsx +13 -8
  138. package/ui/Signal/PubSub.tsx +26 -16
  139. package/ui/Signal/Request.tsx +3 -2
  140. package/ui/Signal/Response.tsx +5 -4
  141. package/ui/Signal/RestApi.tsx +27 -17
  142. package/ui/Signal/style.ts +16 -16
  143. package/ui/Switch.tsx +62 -0
  144. package/ui/System/CSR.tsx +40 -35
  145. package/ui/System/Client.tsx +2 -2
  146. package/ui/System/DevModeToggle.tsx +10 -12
  147. package/ui/System/Messages.tsx +14 -16
  148. package/ui/System/Reconnect.tsx +7 -7
  149. package/ui/System/SSR.tsx +25 -21
  150. package/ui/System/SelectLanguage.tsx +24 -28
  151. package/ui/System/ThemeToggle.tsx +38 -45
  152. package/ui/Tab/Menu.tsx +20 -18
  153. package/ui/Tab/Panel.tsx +3 -3
  154. package/ui/Tab/Provider.tsx +2 -2
  155. package/ui/Table.tsx +11 -8
  156. package/ui/ToggleSelect.tsx +14 -18
  157. package/ui/Tooltip.tsx +55 -0
  158. package/ui/UiOverride/Provider.tsx +10 -6
  159. package/ui/UiOverride/context.ts +22 -2
  160. package/ui/UiOverride/index.ts +8 -1
  161. package/ui/UiOverride/override.ts +7 -6
  162. package/ui/UiOverride/useUiRecipe.ts +15 -0
  163. package/ui/Unauthorized.tsx +5 -5
  164. package/ui/index.ts +16 -0
  165. package/ui/recipe/badgeRecipe.ts +23 -0
  166. package/ui/recipe/buttonRecipe.ts +32 -0
  167. package/ui/recipe/factory.ts +32 -0
  168. package/ui/recipe/index.ts +18 -0
  169. package/ui/recipe/inputRecipe.ts +13 -0
  170. package/ui/styles.css +150 -21
package/ui/Input.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx, usePage } from "akanjs/client";
2
+ import { cn, usePage } from "akanjs/client";
3
3
  import { isEmail } from "akanjs/common";
4
4
  import React, {
5
5
  type ChangeEvent,
@@ -13,8 +13,12 @@ import React, {
13
13
  } from "react";
14
14
  import { AiOutlineEye, AiOutlineEyeInvisible } from "react-icons/ai";
15
15
 
16
+ import { inputRecipe } from "./recipe";
16
17
  import { createOverridable } from "./UiOverride";
17
18
 
19
+ const inputBase = inputRecipe();
20
+ const textareaBase = inputRecipe({ kind: "area" });
21
+
18
22
  export type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "onChange"> & {
19
23
  /** Visual input style. */
20
24
  inputStyleType?: "bordered" | "borderless" | "underline";
@@ -72,19 +76,21 @@ const DefaultInput = ({
72
76
  const statusClass =
73
77
  inputStyleType === "bordered"
74
78
  ? status === "error"
75
- ? "input-error"
79
+ ? "border-destructive"
76
80
  : !firstFocus && status === "warning"
77
- ? "input-warning"
81
+ ? "border-warning"
78
82
  : status === "success"
79
- ? "input-success"
83
+ ? "border-success"
80
84
  : ""
81
85
  : "";
82
- const inputType =
83
- inputStyleType === "bordered"
84
- ? "input"
85
- : inputStyleType === "borderless"
86
- ? "input-ghost"
87
- : "border-0 border-b rounded-none";
86
+ const inputType = cn(
87
+ inputBase,
88
+ inputStyleType === "borderless"
89
+ ? "border-transparent bg-transparent"
90
+ : inputStyleType === "underline"
91
+ ? "rounded-none border-0 border-b"
92
+ : "",
93
+ );
88
94
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
89
95
  if (onPressEnter && e.key === "Enter") onPressEnter(e.currentTarget.value, e);
90
96
  if (e.key === "Escape") {
@@ -100,8 +106,8 @@ const DefaultInput = ({
100
106
  }, [value]);
101
107
 
102
108
  return (
103
- <div className={clsx("relative isolate flex items-center", className)}>
104
- {icon ? <div className={clsx("flex items-center justify-center", iconClassName)}>{icon}</div> : null}
109
+ <div className={cn("relative isolate flex items-center", className)}>
110
+ {icon ? <div className={cn("flex items-center justify-center", iconClassName)}>{icon}</div> : null}
105
111
  <input
106
112
  {...rest}
107
113
  ref={inputRef}
@@ -116,15 +122,15 @@ const DefaultInput = ({
116
122
  if (firstFocus && value) setFirstFocus(false);
117
123
  }}
118
124
  onKeyDown={handleKeyDown}
119
- className={clsx(
120
- `b-5 text-base-content outline-hidden duration-300 focus:border-primary focus:outline-hidden ${icon && ""}`,
125
+ className={cn(
126
+ `b-5 text-foreground outline-hidden duration-300 focus:border-primary focus:outline-hidden ${icon && ""}`,
121
127
  inputType,
122
128
  inputClassName,
123
129
  )}
124
130
  />
125
131
  <div
126
132
  data-validate={!!validateResult}
127
- className="absolute -bottom-4 whitespace-nowrap text-error text-xs duration-300"
133
+ className="absolute -bottom-4 whitespace-nowrap text-destructive text-xs duration-300"
128
134
  >
129
135
  {invalidMessage}
130
136
  </div>
@@ -174,11 +180,11 @@ const DefaultTextArea = ({
174
180
  : validateResult;
175
181
  const statusClass =
176
182
  status === "error"
177
- ? "textarea-error"
183
+ ? "border-destructive"
178
184
  : !firstFocus && status === "warning"
179
- ? "textarea-warning"
185
+ ? "border-warning"
180
186
  : status === "success"
181
- ? "textarea-success"
187
+ ? "border-success"
182
188
  : "";
183
189
  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
184
190
  if (onPressEnter && e.key === "Enter") onPressEnter(e.currentTarget.value, e);
@@ -193,7 +199,7 @@ const DefaultTextArea = ({
193
199
  }, []);
194
200
 
195
201
  return (
196
- <div className={clsx("relative mb-5", className)}>
202
+ <div className={cn("relative mb-5", className)}>
197
203
  <textarea
198
204
  {...rest}
199
205
  ref={inputRef}
@@ -208,14 +214,17 @@ const DefaultTextArea = ({
208
214
  onBlur={(e) => {
209
215
  if (firstFocus && value) setFirstFocus(false);
210
216
  }}
211
- className={clsx(
212
- `textarea textarea-bordered resize-none text-base-content outline-hidden duration-300 focus:border-primary focus:outline-hidden`,
217
+ className={cn(
218
+ cn(
219
+ textareaBase,
220
+ "resize-none text-foreground outline-hidden duration-300 focus:border-primary focus:outline-hidden",
221
+ ),
213
222
  statusClass,
214
223
  inputClassName,
215
224
  )}
216
225
  />
217
226
  {invalidMessage ? (
218
- <div className="absolute -bottom-4 animate-fadeIn text-error text-xs">{invalidMessage}</div>
227
+ <div className="absolute -bottom-4 animate-fadeIn text-destructive text-xs">{invalidMessage}</div>
219
228
  ) : null}
220
229
  </div>
221
230
  );
@@ -264,11 +273,11 @@ const DefaultPassword = ({
264
273
  : validateResult;
265
274
  const statusClass =
266
275
  status === "error"
267
- ? "input-error"
276
+ ? "border-destructive"
268
277
  : !firstFocus && status === "warning"
269
- ? "input-warning"
278
+ ? "border-warning"
270
279
  : status === "success"
271
- ? "input-success"
280
+ ? "border-success"
272
281
  : "";
273
282
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
274
283
  if (onPressEnter && e.key === "Enter") onPressEnter(e.currentTarget.value, e);
@@ -283,10 +292,10 @@ const DefaultPassword = ({
283
292
  }
284
293
  }, []);
285
294
  return (
286
- <div className={clsx("relative isolate pb-2", className)}>
287
- <div className={clsx("relative flex items-center justify-between", inputWrapperClassName)}>
295
+ <div className={cn("relative isolate pb-2", className)}>
296
+ <div className={cn("relative flex items-center justify-between", inputWrapperClassName)}>
288
297
  {icon ? (
289
- <div className={clsx("absolute inset-y-0 left-4 z-10 flex items-center justify-center", iconClassName)}>
298
+ <div className={cn("absolute inset-y-0 left-4 z-10 flex items-center justify-center", iconClassName)}>
290
299
  {icon}
291
300
  </div>
292
301
  ) : null}
@@ -305,8 +314,8 @@ const DefaultPassword = ({
305
314
  }
306
315
  onChange?.(e.target.value, e);
307
316
  }}
308
- className={clsx(
309
- `input text-base-content duration-300 focus:border-primary focus:outline-hidden ${icon && "pl-12"}`,
317
+ className={cn(
318
+ cn(inputBase, `text-foreground duration-300 focus:border-primary focus:outline-hidden ${icon && "pl-12"}`),
310
319
  statusClass,
311
320
  inputClassName,
312
321
  )}
@@ -323,7 +332,7 @@ const DefaultPassword = ({
323
332
  </div>
324
333
  <div
325
334
  data-validate={!!invalidMessage.length}
326
- className="h-2 text-error text-xs duration-300 data-[validate=false]:opacity-0 data-[validate=true]:opacity-100"
335
+ className="h-2 text-destructive text-xs duration-300 data-[validate=false]:opacity-0 data-[validate=true]:opacity-100"
327
336
  >
328
337
  {invalidMessage}
329
338
  </div>
@@ -376,18 +385,13 @@ const DefaultEmail = ({
376
385
  : validateResult;
377
386
  const statusClass =
378
387
  status === "error"
379
- ? "input-error"
388
+ ? "border-destructive"
380
389
  : !firstFocus && status === "warning"
381
- ? "input-warning"
390
+ ? "border-warning"
382
391
  : status === "success"
383
- ? "input-success"
392
+ ? "border-success"
384
393
  : "";
385
- const inputType =
386
- inputStyleType === "bordered"
387
- ? "input-bordered"
388
- : inputStyleType === "borderless"
389
- ? "input"
390
- : "input-bordered rounded-none";
394
+ const inputType = cn(inputBase, inputStyleType === "underline" ? "rounded-none" : "");
391
395
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
392
396
  if (onPressEnter && e.key === "Enter") onPressEnter(e.currentTarget.value, e);
393
397
  if (e.key === "Escape") {
@@ -405,10 +409,10 @@ const DefaultEmail = ({
405
409
  }, []);
406
410
 
407
411
  return (
408
- <div className={clsx("relative isolate mb-5", className)}>
409
- <div className={clsx("flex items-center", inputWrapperClassName)}>
412
+ <div className={cn("relative isolate mb-5", className)}>
413
+ <div className={cn("flex items-center", inputWrapperClassName)}>
410
414
  {icon ? (
411
- <div className={clsx("absolute inset-y-0 left-4 z-10 flex items-center justify-center", iconClassName)}>
415
+ <div className={cn("absolute inset-y-0 left-4 z-10 flex items-center justify-center", iconClassName)}>
412
416
  {icon}
413
417
  </div>
414
418
  ) : null}
@@ -427,10 +431,8 @@ const DefaultEmail = ({
427
431
  }
428
432
  onChange?.(e.target.value, e);
429
433
  }}
430
- className={clsx(
431
- `input text-base-content outline-hidden duration-300 focus:border-primary focus:outline-hidden ${
432
- icon && "pl-12"
433
- }`,
434
+ className={cn(
435
+ `text-foreground outline-hidden duration-300 focus:border-primary focus:outline-hidden ${icon && "pl-12"}`,
434
436
  inputType,
435
437
  statusClass,
436
438
  inputClassName,
@@ -438,7 +440,7 @@ const DefaultEmail = ({
438
440
  />
439
441
  </div>
440
442
  {invalidMessage ? (
441
- <div className="absolute -bottom-4 animate-fadeIn text-error text-xs">{invalidMessage}</div>
443
+ <div className="absolute -bottom-4 animate-fadeIn text-destructive text-xs">{invalidMessage}</div>
442
444
  ) : null}
443
445
  </div>
444
446
  );
@@ -506,13 +508,13 @@ const DefaultNumber = ({
506
508
  const statusClass =
507
509
  validate !== undefined
508
510
  ? status === "error"
509
- ? "input-error"
511
+ ? "border-destructive"
510
512
  : !firstFocus && status === "warning"
511
- ? "input-warning"
513
+ ? "border-warning"
512
514
  : status === "success"
513
- ? "input-success"
514
- : "input"
515
- : "input";
515
+ ? "border-success"
516
+ : ""
517
+ : "";
516
518
 
517
519
  const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
518
520
  const numberValue = parseFloat(e.currentTarget.value.replace(/[^\d-]/g, ""));
@@ -573,10 +575,10 @@ const DefaultNumber = ({
573
575
  }, [value]);
574
576
 
575
577
  return (
576
- <div className={clsx("relative isolate", className)}>
577
- <div className={clsx("flex items-center", inputWrapperClassName)}>
578
+ <div className={cn("relative isolate", className)}>
579
+ <div className={cn("flex items-center", inputWrapperClassName)}>
578
580
  {icon ? (
579
- <div className={clsx("absolute inset-y-0 left-4 z-10 flex items-center justify-center", iconClassName)}>
581
+ <div className={cn("absolute inset-y-0 left-4 z-10 flex items-center justify-center", iconClassName)}>
580
582
  {icon}
581
583
  </div>
582
584
  ) : null}
@@ -594,8 +596,8 @@ const DefaultNumber = ({
594
596
  onChange(parser ? parseFloat(parsedValue) : parseFloat(e.target.value), e);
595
597
  if (cacheKey) sessionStorage.setItem(cacheKey, parsedValue);
596
598
  }}
597
- className={clsx(
598
- `input text-base-content duration-300 focus:border-primary focus:outline-hidden ${icon && "pl-12"}`,
599
+ className={cn(
600
+ cn(inputBase, `text-foreground duration-300 focus:border-primary focus:outline-hidden ${icon && "pl-12"}`),
599
601
  statusClass,
600
602
  inputClassName,
601
603
  )}
@@ -603,7 +605,7 @@ const DefaultNumber = ({
603
605
  </div>
604
606
 
605
607
  {invalidMessage ? (
606
- <div className="absolute -bottom-4 animate-fadeIn text-error text-xs">{invalidMessage}</div>
608
+ <div className="absolute -bottom-4 animate-fadeIn text-destructive text-xs">{invalidMessage}</div>
607
609
  ) : null}
608
610
  </div>
609
611
  );
@@ -621,7 +623,7 @@ const DefaultCheckbox = ({ checked, onChange, className, ...rest }: CheckboxProp
621
623
  {...rest}
622
624
  type="checkbox"
623
625
  checked={checked}
624
- className={clsx("checkbox", className)}
626
+ className={cn("checkbox", className)}
625
627
  onChange={(e) => {
626
628
  onChange(e.target.checked, e);
627
629
  }}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { getEnv } from "akanjs/base";
3
- import { clsx, DEFAULT_BOTTOM_INSET, debugFrame, type FrameSlotRegistration, usePathCtx } from "akanjs/client";
3
+ import { cn, DEFAULT_BOTTOM_INSET, debugFrame, type FrameSlotRegistration, usePathCtx } from "akanjs/client";
4
4
  import { type ReactNode, useLayoutEffect, useRef, useState } from "react";
5
5
 
6
6
  import { Portal } from "../Portal";
@@ -102,7 +102,7 @@ export const BottomInset = ({
102
102
  ref={contentRef}
103
103
  data-akan-frame-slot="bottomInset"
104
104
  data-akan-frame-role={frameRole}
105
- className={clsx("pointer-events-auto w-full", className)}
105
+ className={cn("pointer-events-auto w-full", className)}
106
106
  >
107
107
  {children}
108
108
  </div>
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx, usePage } from "akanjs/client";
2
+ import { cn, usePage } from "akanjs/client";
3
3
  import { useEffect, useState } from "react";
4
4
 
5
5
  import { Link } from "../Link";
@@ -47,8 +47,8 @@ export const BottomTab = ({ className, tabs, height = 64 }: BottomTabProps) => {
47
47
  frameSource="bottomTab"
48
48
  >
49
49
  <div
50
- className={clsx(
51
- `flex size-full items-center justify-around rounded-t-xl border border-base-200 border-b-0 bg-base-100`,
50
+ className={cn(
51
+ `flex size-full items-center justify-around rounded-t-xl border border-muted border-b-0 bg-background text-foreground`,
52
52
  className,
53
53
  )}
54
54
  >
@@ -61,10 +61,10 @@ export const BottomTab = ({ className, tabs, height = 64 }: BottomTabProps) => {
61
61
  isActiveTab(tab.href) ? "" : "opacity-60"
62
62
  }`}
63
63
  >
64
- <div className="indicator">
64
+ <div className="relative inline-flex w-max">
65
65
  {isActiveTab(tab.href) ? (tab.activeIcon ?? tab.icon) : tab.icon}
66
66
  {tab.notiCount && tab.notiCount > 0 ? (
67
- <div className="indicator-item flex size-2 items-center justify-center rounded-full bg-secondary text-[10px] text-base-100"></div>
67
+ <div className="absolute top-0 right-0 z-[1] flex size-2 translate-x-1/2 -translate-y-1/2 items-center justify-center rounded-full bg-secondary text-[10px] text-background"></div>
68
68
  ) : null}
69
69
  </div>
70
70
  <span>{tab.name}</span>
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx } from "akanjs/client";
2
+ import { cn } from "akanjs/client";
3
3
  import { type ReactNode, useEffect, useRef, useState } from "react";
4
4
 
5
5
  export interface HeaderProps {
@@ -25,10 +25,10 @@ export const Header = ({ className, type, children, height = 40 }: HeaderProps)
25
25
  }, []);
26
26
  return (
27
27
  <div
28
- className={clsx(
28
+ className={cn(
29
29
  `fixed top-0 z-[9] flex duration-300 ${
30
30
  !visible ? "md:-translate-y-full" : ""
31
- } w-full bg-base-100 shadow-sm backdrop-blur-lg`,
31
+ } w-full bg-background shadow-sm backdrop-blur-lg`,
32
32
  className,
33
33
  )}
34
34
  >
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { clsx } from "akanjs/client";
2
+ import { cn } from "akanjs/client";
3
3
  import type { ReactNode } from "react";
4
4
  import { BiX } from "react-icons/bi";
5
+ import { buttonRecipe } from "../Button";
5
6
 
6
7
  export interface LeftSiderProps {
7
8
  className?: string;
@@ -13,16 +14,16 @@ export interface LeftSiderProps {
13
14
  export const LeftSider = ({ className, children, open, width, onCancel }: LeftSiderProps) => {
14
15
  return (
15
16
  <div
16
- className={clsx(
17
- "absolute top-0 border-base-200 border-r bg-base-100 transition-all duration-150",
18
- { "translate-x-0": open, "translate-x-[-100%]": !open },
17
+ className={cn(
18
+ "absolute top-0 border-muted border-r bg-background transition-all duration-150",
19
+ open ? "translate-x-0" : "translate-x-[-100%]",
19
20
  className,
20
21
  )}
21
22
  style={{ width }}
22
23
  >
23
24
  {children}
24
25
  <button
25
- className="btn btn-ghost btn-square absolute top-0 right-0"
26
+ className={buttonRecipe({ variant: "ghost", size: "icon" }, "absolute top-0 right-0")}
26
27
  onClick={() => {
27
28
  onCancel();
28
29
  }}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { getEnv } from "akanjs/base";
3
- import { clsx, DEFAULT_TOP_INSET, debugFrame, type TransitionType, usePathCtx } from "akanjs/client";
3
+ import { cn, DEFAULT_TOP_INSET, debugFrame, type TransitionType, usePathCtx } from "akanjs/client";
4
4
  import { type ReactNode, useLayoutEffect } from "react";
5
5
  import { BiChevronLeft, BiX } from "react-icons/bi";
6
6
 
@@ -44,17 +44,7 @@ export const Navbar = ({ back = false, className, height, children, title, left,
44
44
  return (
45
45
  <>
46
46
  <Portal id={`topInsetContent${suffix}`}>
47
- <div
48
- className={clsx(
49
- "flex size-full items-center px-5",
50
- {
51
- "pl-14": back,
52
- },
53
- className,
54
- )}
55
- >
56
- {children}
57
- </div>
47
+ <div className={cn("flex size-full items-center px-5", back && "pl-14", className)}>{children}</div>
58
48
  </Portal>
59
49
  {back ? (
60
50
  <Portal id={`topLeftActionContent${suffix}`}>
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx } from "akanjs/client";
2
+ import { cn } from "akanjs/client";
3
3
  import type { ReactNode } from "react";
4
4
  import { AiOutlineClose } from "react-icons/ai";
5
5
 
@@ -14,9 +14,10 @@ export interface RightSiderProps {
14
14
  export const RightSider = ({ className, children, open, title, width, onCancel }: RightSiderProps) => {
15
15
  return (
16
16
  <div
17
- className={clsx(
18
- "group absolute top-0 right-0 overflow-y-auto border-base-200 border-l bg-base-100 pt-14 transition-all duration-150",
19
- { "translate-x-0": open, "translate-x-[100%]": !open },
17
+ className={cn(
18
+ "group absolute top-0 right-0 overflow-y-auto border-muted border-l bg-background pt-14 transition-all duration-150",
19
+ open && "translate-x-0",
20
+ !open && "translate-x-[100%]",
20
21
  className,
21
22
  )}
22
23
  style={{ width }}
@@ -24,10 +25,11 @@ export const RightSider = ({ className, children, open, title, width, onCancel }
24
25
  {children}
25
26
  <div className="absolute top-2 left-4 flex items-center gap-4 pt-2 text-xl">
26
27
  <div
27
- className={clsx("cursor-pointer border-base-200 bg-base-100 transition-all duration-150", {
28
- "opacity-100": open,
29
- "opacity-0": !open,
30
- })}
28
+ className={cn(
29
+ "cursor-pointer border-muted bg-background transition-all duration-150",
30
+ open && "opacity-100",
31
+ !open && "opacity-0",
32
+ )}
31
33
  onClick={() => {
32
34
  onCancel();
33
35
  }}
@@ -1,11 +1,12 @@
1
1
  "use client";
2
- import { clsx } from "akanjs/client";
2
+ import { cn } from "akanjs/client";
3
3
  import { st } from "akanjs/store";
4
4
  import { animated } from "akanjs/ui";
5
5
  import { type ReactNode, useEffect, useState } from "react";
6
6
  import { AiOutlineMenu } from "react-icons/ai";
7
7
  import { BiX } from "react-icons/bi";
8
8
  import { useSpring } from "react-spring";
9
+ import { buttonRecipe } from "../Button";
9
10
 
10
11
  export interface SiderProps {
11
12
  className?: string;
@@ -32,7 +33,7 @@ export const Sider = ({ className, bgClassName, children }: SiderProps) => {
32
33
  return (
33
34
  <>
34
35
  <button
35
- className="btn btn-ghost"
36
+ className={buttonRecipe({ variant: "ghost" })}
36
37
  onClick={() => {
37
38
  setIsOpen(true);
38
39
  }}
@@ -43,7 +44,7 @@ export const Sider = ({ className, bgClassName, children }: SiderProps) => {
43
44
  {isOpen ? (
44
45
  <animated.div
45
46
  style={overlayAnimation}
46
- className={clsx("fixed inset-0 z-40 h-screen w-screen", bgClassName)}
47
+ className={cn("fixed inset-0 z-40 h-screen w-screen", bgClassName)}
47
48
  onClick={() => {
48
49
  setIsOpen(false);
49
50
  }}
@@ -52,7 +53,7 @@ export const Sider = ({ className, bgClassName, children }: SiderProps) => {
52
53
 
53
54
  <animated.div
54
55
  style={siderAnimation}
55
- className={clsx("fixed top-0 left-0 z-50 h-full w-3/4 bg-base-200 p-4 text-base-content md:w-80", className)}
56
+ className={cn("fixed top-0 left-0 z-50 h-full w-3/4 bg-muted p-4 text-foreground md:w-80", className)}
56
57
  >
57
58
  <button
58
59
  className="absolute top-4 left-4 text-lg"
@@ -1,4 +1,4 @@
1
- import { clsx } from "akanjs/client";
1
+ import { cn } from "akanjs/client";
2
2
 
3
3
  export interface TemplateProps {
4
4
  /** Additional classes merged with the default vertical form/template spacing. */
@@ -7,5 +7,5 @@ export interface TemplateProps {
7
7
  children?: React.ReactNode;
8
8
  }
9
9
  export const Template = ({ className, children }: TemplateProps) => {
10
- return <div className={clsx("flex w-full flex-col gap-6 p-2", className)}>{children}</div>;
10
+ return <div className={cn("flex w-full flex-col gap-6 p-2", className)}>{children}</div>;
11
11
  };
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { getEnv } from "akanjs/base";
3
- import { clsx, DEFAULT_TOP_INSET, debugFrame, usePathCtx } from "akanjs/client";
3
+ import { cn, DEFAULT_TOP_INSET, debugFrame, usePathCtx } from "akanjs/client";
4
4
  import { type ReactNode, useLayoutEffect } from "react";
5
5
 
6
6
  import { Portal } from "../Portal";
@@ -34,7 +34,7 @@ export const TopInset = ({ className, children, estimatedHeight = DEFAULT_TOP_IN
34
34
 
35
35
  return (
36
36
  <Portal id={`topInsetContent${suffix}`}>
37
- <div data-akan-frame-slot="topInset" data-akan-frame-role="topChrome" className={clsx("size-full", className)}>
37
+ <div data-akan-frame-slot="topInset" data-akan-frame-role="topChrome" className={cn("size-full", className)}>
38
38
  {children}
39
39
  </div>
40
40
  </Portal>
@@ -1,4 +1,4 @@
1
- import { clsx } from "akanjs/client";
1
+ import { cn } from "akanjs/client";
2
2
 
3
3
  import { Link } from "../Link";
4
4
 
@@ -13,7 +13,7 @@ export interface UnitProps {
13
13
  export const Unit = ({ className, children, href }: UnitProps) => {
14
14
  return (
15
15
  <Link href={href}>
16
- <div className={clsx("flex w-full flex-col gap-2 p-4", { "cursor-pointer": !!href }, className)}>{children}</div>
16
+ <div className={cn("flex w-full flex-col gap-2 p-4", !!href && "cursor-pointer", className)}>{children}</div>
17
17
  </Link>
18
18
  );
19
19
  };
@@ -1,4 +1,4 @@
1
- import { clsx } from "akanjs/client";
1
+ import { cn } from "akanjs/client";
2
2
  import type { ReactNode } from "react";
3
3
 
4
4
  export interface ViewProps {
@@ -8,5 +8,5 @@ export interface ViewProps {
8
8
  children: ReactNode;
9
9
  }
10
10
  export const View = ({ className, children }: ViewProps) => {
11
- return <div className={clsx("flex size-full max-w-5xl flex-col gap-6 px-2", className)}>{children}</div>;
11
+ return <div className={cn("flex size-full max-w-5xl flex-col gap-6 px-2", className)}>{children}</div>;
12
12
  };
@@ -1,4 +1,4 @@
1
- import { clsx } from "akanjs/client";
1
+ import { cn } from "akanjs/client";
2
2
  import type { ReactNode } from "react";
3
3
 
4
4
  export interface ZoneProps {
@@ -6,5 +6,5 @@ export interface ZoneProps {
6
6
  children: ReactNode;
7
7
  }
8
8
  export const Zone = ({ className, children }: ZoneProps) => {
9
- return <div className={clsx("flex size-full max-w-5xl flex-col gap-6 px-2", className)}>{children}</div>;
9
+ return <div className={cn("flex size-full max-w-5xl flex-col gap-6 px-2", className)}>{children}</div>;
10
10
  };
package/ui/Link/Back.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx, router } from "akanjs/client";
2
+ import { cn, router } from "akanjs/client";
3
3
  import type { ReactNode } from "react";
4
4
 
5
5
  interface BackProps {
@@ -8,7 +8,7 @@ interface BackProps {
8
8
  }
9
9
  export default function Back({ className, children }: BackProps) {
10
10
  return (
11
- <div className={clsx("cursor-pointer", className)} onClick={() => router.back()}>
11
+ <div className={cn("cursor-pointer", className)} onClick={() => router.back()}>
12
12
  {children}
13
13
  </div>
14
14
  );
package/ui/Link/Close.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx } from "akanjs/client";
2
+ import { cn } from "akanjs/client";
3
3
  import type { ReactNode } from "react";
4
4
 
5
5
  interface CloseProps {
@@ -9,7 +9,7 @@ interface CloseProps {
9
9
  export default function Close({ className, children }: CloseProps) {
10
10
  return (
11
11
  <div
12
- className={clsx("cursor-pointer", className)}
12
+ className={cn("cursor-pointer", className)}
13
13
  onClick={() => {
14
14
  window.close();
15
15
  }}
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- import { clsx, getPathInfo, router, usePage, usePathCtx } from "akanjs/client";
3
+ import { cn, getPathInfo, router, usePage, usePathCtx } from "akanjs/client";
4
4
  import { loadCapacitorBrowser } from "akanjs/client/capacitor";
5
5
  import { st } from "akanjs/store";
6
6
 
@@ -23,9 +23,11 @@ export default function CsrLink({
23
23
  const { path, hash } = getPathInfo(href, lang, prefix ?? "");
24
24
  return (
25
25
  <a
26
- className={clsx("cursor-pointer", className, {
27
- [activeClassName ?? ""]: activeExact ? currentPath === path : currentPath.startsWith(path),
28
- })}
26
+ className={cn(
27
+ "cursor-pointer",
28
+ className,
29
+ (activeExact ? currentPath === path : currentPath.startsWith(path)) && (activeClassName ?? ""),
30
+ )}
29
31
  onClick={() => {
30
32
  const isExternal = href.startsWith("http") || href.startsWith("mailto:") || href.startsWith("tel:");
31
33
  const isHash = href.startsWith("#");
package/ui/Link/Lang.tsx CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { clsx, router } from "akanjs/client";
2
+ import { cn, router } from "akanjs/client";
3
3
  import type { ReactNode } from "react";
4
4
 
5
5
  interface LangProps {
@@ -9,7 +9,7 @@ interface LangProps {
9
9
  }
10
10
  export default function Lang({ className, lang, children }: LangProps) {
11
11
  return (
12
- <div className={clsx("cursor-pointer", className)} onClick={() => router.setLang(lang)}>
12
+ <div className={cn("cursor-pointer", className)} onClick={() => router.setLang(lang)}>
13
13
  {children}
14
14
  </div>
15
15
  );