@saas-ui/react 2.11.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 (232) hide show
  1. package/CHANGELOG.md +7 -154
  2. package/dist/index.cjs +8461 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +26 -0
  5. package/dist/index.d.ts +25 -7
  6. package/dist/index.js +8415 -35
  7. package/dist/index.js.map +1 -1
  8. package/package.json +24 -21
  9. package/src/components/accordion.tsx +47 -0
  10. package/src/components/action-bar.tsx +40 -0
  11. package/src/components/alert.tsx +51 -0
  12. package/src/components/app-shell/app-shell.recipe.ts +52 -0
  13. package/src/components/app-shell/app-shell.stories.tsx +51 -0
  14. package/src/components/app-shell/app-shell.tsx +94 -0
  15. package/src/components/app-shell/index.ts +3 -0
  16. package/src/components/avatar.tsx +74 -0
  17. package/src/components/blockquote.tsx +31 -0
  18. package/src/components/breadcrumbs/breadcrumb.stories.tsx +17 -0
  19. package/src/components/breadcrumbs/breadcrumb.tsx +36 -0
  20. package/src/components/breadcrumbs/index.ts +1 -0
  21. package/src/components/breadcrumbs/namespace.ts +8 -0
  22. package/src/components/button/button.recipe.ts +182 -0
  23. package/src/components/button/button.stories.tsx +99 -0
  24. package/src/components/button/button.tsx +55 -0
  25. package/src/components/button/index.ts +2 -0
  26. package/src/components/checkbox/checkbox.tsx +26 -0
  27. package/src/components/checkbox/index.ts +2 -0
  28. package/src/components/checkbox-card.tsx +57 -0
  29. package/src/components/checkbox.tsx +25 -0
  30. package/src/components/clipboard.tsx +107 -0
  31. package/src/components/close-button/close-button.stories.tsx +12 -0
  32. package/src/components/close-button/close-button.tsx +18 -0
  33. package/src/components/close-button/index.ts +2 -0
  34. package/src/components/color-mode.tsx +65 -0
  35. package/src/components/command/command.recipe.ts +17 -0
  36. package/src/components/command/command.stories.tsx +47 -0
  37. package/src/components/command/command.tsx +50 -0
  38. package/src/components/command/index.ts +1 -0
  39. package/src/components/data-list.tsx +37 -0
  40. package/src/components/dialog/dialog.tsx +66 -0
  41. package/src/components/dialog/index.ts +1 -0
  42. package/src/components/dialog/namespace.ts +18 -0
  43. package/src/components/drawer/drawer.tsx +56 -0
  44. package/src/components/drawer/index.ts +3 -0
  45. package/src/components/drawer/namespace.ts +19 -0
  46. package/src/components/empty-state.tsx +34 -0
  47. package/src/components/field.tsx +33 -0
  48. package/src/components/file-button.tsx +166 -0
  49. package/src/components/grid-list/grid-list.recipe.ts +113 -0
  50. package/src/components/hover-card.tsx +35 -0
  51. package/src/components/icon-badge/icon-badge.recipe.ts +57 -0
  52. package/src/components/icon-badge/icon-badge.stories.tsx +38 -0
  53. package/src/components/icon-badge/icon-badge.tsx +59 -0
  54. package/src/components/icon-badge/index.ts +2 -0
  55. package/src/components/icons/create-icon.tsx +41 -0
  56. package/src/components/icons/icons.tsx +121 -0
  57. package/src/components/icons/index.ts +1 -0
  58. package/src/components/input-group/index.ts +1 -0
  59. package/src/components/input-group/input-group.tsx +46 -0
  60. package/src/components/link/index.ts +2 -0
  61. package/src/components/link/link.stories.tsx +17 -0
  62. package/src/components/link/link.test.tsx +33 -0
  63. package/src/components/link/link.tsx +27 -0
  64. package/src/components/link-button.tsx +12 -0
  65. package/src/components/loading-overlay/index.ts +1 -0
  66. package/src/components/loading-overlay/loading-overlay.recipe.ts +61 -0
  67. package/src/components/loading-overlay/loading-overlay.stories.tsx +68 -0
  68. package/src/components/loading-overlay/loading-overlay.tsx +54 -0
  69. package/src/components/loading-overlay/namespace.ts +7 -0
  70. package/src/components/menu.tsx +108 -0
  71. package/src/components/native-select.tsx +57 -0
  72. package/src/components/navbar/index.ts +1 -0
  73. package/src/components/navbar/namespace.ts +9 -0
  74. package/src/components/navbar/navbar.recipe.ts +109 -0
  75. package/src/components/navbar/navbar.stories.tsx +435 -0
  76. package/src/components/navbar/navbar.test.tsx +49 -0
  77. package/src/components/navbar/navbar.tsx +39 -0
  78. package/src/components/number-input/index.ts +2 -0
  79. package/src/components/number-input/number-input.tsx +41 -0
  80. package/src/components/pagination.tsx +207 -0
  81. package/src/components/password-input/index.ts +2 -0
  82. package/src/components/password-input/password-input.tsx +98 -0
  83. package/src/components/persona/index.ts +2 -0
  84. package/src/components/persona/namespace.ts +18 -0
  85. package/src/components/persona/persona-primitive.tsx +220 -0
  86. package/src/components/persona/persona.recipe.ts +94 -0
  87. package/src/components/persona/persona.stories.tsx +101 -0
  88. package/src/components/persona/persona.tsx +143 -0
  89. package/src/components/pin-input/index.ts +2 -0
  90. package/src/components/pin-input/pin-input.tsx +36 -0
  91. package/src/components/popover.tsx +58 -0
  92. package/src/components/progress-circle.tsx +37 -0
  93. package/src/components/progress.tsx +40 -0
  94. package/src/components/prose.tsx +264 -0
  95. package/src/components/provider.tsx +12 -0
  96. package/src/components/radio/index.ts +2 -0
  97. package/src/components/radio/radio.tsx +27 -0
  98. package/src/components/radio-card.tsx +57 -0
  99. package/src/components/radio.tsx +24 -0
  100. package/src/components/rating.tsx +27 -0
  101. package/src/components/search-input/index.ts +2 -0
  102. package/src/components/search-input/search-input.stories.tsx +63 -0
  103. package/src/components/search-input/search-input.tsx +134 -0
  104. package/src/components/segmented-control.tsx +47 -0
  105. package/src/components/select/index.ts +1 -0
  106. package/src/components/select/namespace.ts +18 -0
  107. package/src/components/select/select.tsx +135 -0
  108. package/src/components/sidebar/index.ts +7 -0
  109. package/src/components/sidebar/namespace.ts +27 -0
  110. package/src/components/sidebar/sidebar-item.recipe.ts +65 -0
  111. package/src/components/sidebar/sidebar.recipe.ts +237 -0
  112. package/src/components/sidebar/sidebar.stories.tsx +903 -0
  113. package/src/components/sidebar/sidebar.tsx +204 -0
  114. package/src/components/skeleton.tsx +44 -0
  115. package/src/components/slider.tsx +53 -0
  116. package/src/components/spinner/index.ts +2 -0
  117. package/src/components/spinner/spinner.stories.tsx +19 -0
  118. package/src/components/spinner/spinner.tsx +21 -0
  119. package/src/components/stat.tsx +75 -0
  120. package/src/components/status.tsx +29 -0
  121. package/src/components/stepper-input.tsx +49 -0
  122. package/src/components/steps/index.ts +1 -0
  123. package/src/components/steps/namespace.ts +16 -0
  124. package/src/components/steps/steps.tsx +82 -0
  125. package/src/components/switch/index.ts +3 -0
  126. package/src/components/switch/switch.tsx +39 -0
  127. package/src/components/tag.tsx +39 -0
  128. package/src/components/timeline.tsx +17 -0
  129. package/src/components/toaster.tsx +43 -0
  130. package/src/components/toggle-tip.tsx +62 -0
  131. package/src/components/tooltip.tsx +46 -0
  132. package/src/index.ts +6 -7
  133. package/src/preset.ts +9 -0
  134. package/src/provider/index.ts +4 -0
  135. package/src/provider/sui-provider.tsx +34 -0
  136. package/src/provider/use-link.test.tsx +60 -0
  137. package/src/provider/use-link.tsx +13 -0
  138. package/src/theme/animation-styles.ts +53 -0
  139. package/src/theme/breakpoints.ts +11 -0
  140. package/src/theme/conditions.ts +26 -0
  141. package/src/theme/fluid-font-sizes.ts +65 -0
  142. package/src/theme/global-css.ts +94 -0
  143. package/src/theme/index.ts +72 -0
  144. package/src/theme/layer-styles.ts +116 -0
  145. package/src/theme/recipes/chakra/accordion.ts +145 -0
  146. package/src/theme/recipes/chakra/action-bar.ts +62 -0
  147. package/src/theme/recipes/chakra/alert.ts +157 -0
  148. package/src/theme/recipes/chakra/avatar.ts +141 -0
  149. package/src/theme/recipes/chakra/badge.ts +67 -0
  150. package/src/theme/recipes/chakra/blockquote.ts +83 -0
  151. package/src/theme/recipes/chakra/breadcrumb.ts +94 -0
  152. package/src/theme/recipes/chakra/card.ts +99 -0
  153. package/src/theme/recipes/chakra/checkbox-card.ts +212 -0
  154. package/src/theme/recipes/chakra/checkbox.ts +70 -0
  155. package/src/theme/recipes/chakra/checkmark.ts +83 -0
  156. package/src/theme/recipes/chakra/code.ts +17 -0
  157. package/src/theme/recipes/chakra/collapsible.ts +20 -0
  158. package/src/theme/recipes/chakra/container.ts +26 -0
  159. package/src/theme/recipes/chakra/data-list.ts +80 -0
  160. package/src/theme/recipes/chakra/dialog.ts +225 -0
  161. package/src/theme/recipes/chakra/drawer.ts +201 -0
  162. package/src/theme/recipes/chakra/editable.ts +88 -0
  163. package/src/theme/recipes/chakra/empty-state.ts +88 -0
  164. package/src/theme/recipes/chakra/field.ts +68 -0
  165. package/src/theme/recipes/chakra/fieldset.ts +62 -0
  166. package/src/theme/recipes/chakra/file-upload.ts +96 -0
  167. package/src/theme/recipes/chakra/heading.ts +27 -0
  168. package/src/theme/recipes/chakra/hover-card.ts +68 -0
  169. package/src/theme/recipes/chakra/icon.ts +30 -0
  170. package/src/theme/recipes/chakra/input-addon.ts +40 -0
  171. package/src/theme/recipes/chakra/input.ts +96 -0
  172. package/src/theme/recipes/chakra/kbd.ts +60 -0
  173. package/src/theme/recipes/chakra/link.ts +37 -0
  174. package/src/theme/recipes/chakra/list.ts +67 -0
  175. package/src/theme/recipes/chakra/mark.ts +27 -0
  176. package/src/theme/recipes/chakra/menu.ts +124 -0
  177. package/src/theme/recipes/chakra/native-select.ts +140 -0
  178. package/src/theme/recipes/chakra/number-input.ts +115 -0
  179. package/src/theme/recipes/chakra/pin-input.ts +27 -0
  180. package/src/theme/recipes/chakra/popover.ts +86 -0
  181. package/src/theme/recipes/chakra/progress-circle.ts +94 -0
  182. package/src/theme/recipes/chakra/progress.ts +127 -0
  183. package/src/theme/recipes/chakra/radio-card.ts +220 -0
  184. package/src/theme/recipes/chakra/radio-group.ts +72 -0
  185. package/src/theme/recipes/chakra/radiomark.ts +107 -0
  186. package/src/theme/recipes/chakra/rating-group.ts +94 -0
  187. package/src/theme/recipes/chakra/segment-group.ts +117 -0
  188. package/src/theme/recipes/chakra/select.ts +282 -0
  189. package/src/theme/recipes/chakra/separator.ts +51 -0
  190. package/src/theme/recipes/chakra/skeleton.ts +53 -0
  191. package/src/theme/recipes/chakra/skip-nav-link.ts +34 -0
  192. package/src/theme/recipes/chakra/slider.ts +178 -0
  193. package/src/theme/recipes/chakra/spinner.ts +32 -0
  194. package/src/theme/recipes/chakra/stat.ts +79 -0
  195. package/src/theme/recipes/chakra/status.ts +48 -0
  196. package/src/theme/recipes/chakra/steps.ts +218 -0
  197. package/src/theme/recipes/chakra/switch.ts +167 -0
  198. package/src/theme/recipes/chakra/table.ts +172 -0
  199. package/src/theme/recipes/chakra/tabs.ts +280 -0
  200. package/src/theme/recipes/chakra/tag.ts +131 -0
  201. package/src/theme/recipes/chakra/textarea.ts +88 -0
  202. package/src/theme/recipes/chakra/timeline.ts +138 -0
  203. package/src/theme/recipes/chakra/toast.ts +96 -0
  204. package/src/theme/recipes/chakra/tooltip.ts +40 -0
  205. package/src/theme/recipes.ts +46 -0
  206. package/src/theme/semantic-tokens/colors.ts +403 -0
  207. package/src/theme/semantic-tokens/radii.ts +7 -0
  208. package/src/theme/semantic-tokens/shadows.ts +52 -0
  209. package/src/theme/slot-recipes.ts +104 -0
  210. package/src/theme/text-styles.ts +39 -0
  211. package/src/theme/tokens/animations.ts +8 -0
  212. package/src/theme/tokens/aspect-ratios.ts +10 -0
  213. package/src/theme/tokens/blurs.ts +12 -0
  214. package/src/theme/tokens/borders.ts +9 -0
  215. package/src/theme/tokens/colors.ts +177 -0
  216. package/src/theme/tokens/cursor.ts +12 -0
  217. package/src/theme/tokens/durations.ts +11 -0
  218. package/src/theme/tokens/easings.ts +10 -0
  219. package/src/theme/tokens/font-sizes.ts +20 -0
  220. package/src/theme/tokens/font-weights.ts +13 -0
  221. package/src/theme/tokens/fonts.ts +15 -0
  222. package/src/theme/tokens/keyframes.ts +173 -0
  223. package/src/theme/tokens/letter-spacing.ts +9 -0
  224. package/src/theme/tokens/line-heights.ts +19 -0
  225. package/src/theme/tokens/radius.ts +18 -0
  226. package/src/theme/tokens/sizes.ts +71 -0
  227. package/src/theme/tokens/spacing.ts +38 -0
  228. package/src/theme/tokens/z-indices.ts +34 -0
  229. package/src/theme/utils.ts +46 -0
  230. package/dist/index.d.mts +0 -8
  231. package/dist/index.mjs +0 -11
  232. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,57 @@
1
+ import { defineRecipe } from '@chakra-ui/react'
2
+
3
+ export const iconBadgeRecipe = defineRecipe({
4
+ className: 'sui-icon-badge',
5
+ base: {
6
+ display: 'inline-flex',
7
+ alignItems: 'center',
8
+ justifyContent: 'center',
9
+ },
10
+ variants: {
11
+ variant: {
12
+ outline: {
13
+ borderWidth: '1px',
14
+ borderColor: 'colorPalette.subtle',
15
+ color: 'colorPalette.fg',
16
+ },
17
+ solid: {
18
+ bg: 'colorPalette.solid',
19
+ color: 'white',
20
+ },
21
+ subtle: {
22
+ bg: 'colorPalette.subtle',
23
+ color: 'colorPalette.fg',
24
+ },
25
+ },
26
+ size: {
27
+ sm: {
28
+ borderRadius: 'sm',
29
+ fontSize: '0.9em',
30
+ w: 6,
31
+ h: 6,
32
+ },
33
+ md: {
34
+ borderRadius: 'md',
35
+ fontSize: '1.1em',
36
+ w: 8,
37
+ h: 8,
38
+ },
39
+ lg: {
40
+ borderRadius: 'md',
41
+ fontSize: '1.3em',
42
+ w: 10,
43
+ h: 10,
44
+ },
45
+ xl: {
46
+ borderRadius: 'md',
47
+ fontSize: '1.5em',
48
+ w: 12,
49
+ h: 12,
50
+ },
51
+ },
52
+ },
53
+ defaultVariants: {
54
+ variant: 'outline',
55
+ size: 'md',
56
+ },
57
+ })
@@ -0,0 +1,38 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+ import { LuUser } from 'react-icons/lu'
3
+
4
+ import { IconBadge } from './index.ts'
5
+
6
+ export default {
7
+ title: 'Components/IconBadge',
8
+ component: IconBadge,
9
+ } as Meta
10
+
11
+ type Story = StoryObj<typeof IconBadge>
12
+
13
+ export const Default: Story = {
14
+ args: {
15
+ icon: <LuUser />,
16
+ },
17
+ }
18
+
19
+ export const SolidVariant: Story = {
20
+ args: {
21
+ variant: 'solid',
22
+ icon: <LuUser />,
23
+ },
24
+ }
25
+
26
+ export const SubtleVariant: Story = {
27
+ args: {
28
+ variant: 'subtle',
29
+ icon: <LuUser />,
30
+ },
31
+ }
32
+
33
+ export const Rounded: Story = {
34
+ args: {
35
+ borderRadius: 'full',
36
+ icon: <LuUser />,
37
+ },
38
+ }
@@ -0,0 +1,59 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cloneElement, isValidElement } from 'react'
5
+
6
+ import {
7
+ type HTMLChakraProps,
8
+ RecipeProps,
9
+ chakra,
10
+ useRecipe,
11
+ } from '@chakra-ui/react'
12
+ import { cx } from '@saas-ui/core/utils'
13
+
14
+ ////////////////////////////////////////////////////////////////////////////////////
15
+
16
+ export interface IconBadgeProps
17
+ extends HTMLChakraProps<'div'>,
18
+ RecipeProps<'iconBadge'> {
19
+ /**
20
+ * The icon to display
21
+ */
22
+ icon: React.ReactNode
23
+
24
+ /**
25
+ * A11y: A label that describes the icon
26
+ */
27
+ 'aria-label'?: string
28
+ }
29
+
30
+ export const IconBadge = React.forwardRef<HTMLDivElement, IconBadgeProps>(
31
+ (props, ref) => {
32
+ const { icon, children, ...rest } = props
33
+ const recipe = useRecipe({ key: 'iconBadge', recipe: props.recipe })
34
+ const [variantProps, localProps] = recipe.splitVariantProps(rest)
35
+ const styles = recipe(variantProps)
36
+
37
+ /**
38
+ * Passing the icon as prop or children should work
39
+ */
40
+ const element = icon || children
41
+ const _children = isValidElement(element)
42
+ ? cloneElement(element as any, {
43
+ 'aria-hidden': true,
44
+ focusable: false,
45
+ })
46
+ : null
47
+
48
+ return (
49
+ <chakra.div
50
+ ref={ref}
51
+ {...localProps}
52
+ css={[styles, props.css]}
53
+ className={cx(recipe.className, props.className)}
54
+ >
55
+ {_children}
56
+ </chakra.div>
57
+ )
58
+ },
59
+ )
@@ -0,0 +1,2 @@
1
+ export { IconBadge } from './icon-badge'
2
+ export type { IconBadgeProps } from './icon-badge'
@@ -0,0 +1,41 @@
1
+ import type { IconProps } from '@chakra-ui/react'
2
+ import { createIcon as _createIcon } from '@chakra-ui/react'
3
+
4
+ interface CreateIconOptions {
5
+ /**
6
+ * The icon `svg` viewBox
7
+ * @default "0 0 24 24"
8
+ */
9
+ viewBox?: string
10
+ /**
11
+ * The `svg` path or group element
12
+ * @type React.ReactElement | React.ReactElement[]
13
+ */
14
+ path?: React.ReactElement | React.ReactElement[]
15
+ /**
16
+ * If the `svg` has a single path, simply copy the path's `d` attribute
17
+ */
18
+ d?: string
19
+ /**
20
+ * The display name useful in the dev tools
21
+ */
22
+ displayName?: string
23
+ /**
24
+ * Default props automatically passed to the component; overwriteable
25
+ */
26
+ defaultProps?: IconProps
27
+ }
28
+
29
+ export const createIcon = (props: CreateIconOptions) => {
30
+ return _createIcon({
31
+ viewBox: '0 0 24 24',
32
+ defaultProps: {
33
+ fill: 'none',
34
+ stroke: 'currentColor',
35
+ strokeWidth: '2',
36
+ strokeLinecap: 'round',
37
+ strokeLinejoin: 'round',
38
+ },
39
+ ...props,
40
+ })
41
+ }
@@ -0,0 +1,121 @@
1
+ import * as React from 'react'
2
+
3
+ import { createIcon } from './create-icon'
4
+
5
+ export const ChevronUpIcon = createIcon({
6
+ displayName: 'ChevronUpIcon',
7
+ path: <polyline points="18 15 12 9 6 15"></polyline>,
8
+ })
9
+
10
+ export const ChevronDownIcon = createIcon({
11
+ displayName: 'ChevronDownIcon',
12
+ path: <polyline points="6 9 12 15 18 9"></polyline>,
13
+ })
14
+
15
+ export const ChevronLeftIcon = createIcon({
16
+ displayName: 'ChevronLeftIcon',
17
+ path: <polyline points="15 18 9 12 15 6"></polyline>,
18
+ })
19
+
20
+ export const ChevronRightIcon = createIcon({
21
+ displayName: 'ChevronRightIcon',
22
+ path: <polyline points="9 18 15 12 9 6"></polyline>,
23
+ })
24
+
25
+ export const HamburgerIcon = createIcon({
26
+ displayName: 'ChevronDownIcon',
27
+ path: (
28
+ <g fill="none">
29
+ <line x1="3" y1="12" x2="21" y2="12"></line>
30
+ <line x1="3" y1="6" x2="21" y2="6"></line>
31
+ <line x1="3" y1="18" x2="21" y2="18"></line>
32
+ </g>
33
+ ),
34
+ })
35
+
36
+ export const CloseIcon = createIcon({
37
+ displayName: 'CloseIcon',
38
+ path: (
39
+ <g>
40
+ <line x1="18" y1="6" x2="6" y2="18"></line>
41
+ <line x1="6" y1="6" x2="18" y2="18"></line>
42
+ </g>
43
+ ),
44
+ })
45
+
46
+ export const FilterIcon = createIcon({
47
+ displayName: 'FilterIcon',
48
+ path: (
49
+ <polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"></polygon>
50
+ ),
51
+ })
52
+
53
+ export const CalendarIcon = createIcon({
54
+ displayName: 'CalendarIcon',
55
+ path: (
56
+ <g>
57
+ <rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
58
+ <line x1="16" y1="2" x2="16" y2="6"></line>
59
+ <line x1="8" y1="2" x2="8" y2="6"></line>
60
+ <line x1="3" y1="10" x2="21" y2="10"></line>
61
+ </g>
62
+ ),
63
+ })
64
+
65
+ export const PlusIcon = createIcon({
66
+ displayName: 'PlusIcon',
67
+ path: (
68
+ <g>
69
+ <line x1="12" y1="5" x2="12" y2="19"></line>
70
+ <line x1="5" y1="12" x2="19" y2="12"></line>
71
+ </g>
72
+ ),
73
+ })
74
+
75
+ export const MinusIcon = createIcon({
76
+ displayName: 'MinusIcon',
77
+ path: (
78
+ <g>
79
+ <line x1="5" y1="12" x2="19" y2="12"></line>
80
+ </g>
81
+ ),
82
+ })
83
+
84
+ export const ViewOffIcon = createIcon({
85
+ displayName: 'ViewOffIcon',
86
+ path: (
87
+ <g>
88
+ <path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path>
89
+ <line x1="1" y1="1" x2="23" y2="23"></line>
90
+ </g>
91
+ ),
92
+ })
93
+
94
+ export const ViewIcon = createIcon({
95
+ displayName: 'ViewOffIcon',
96
+ path: (
97
+ <g>
98
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path>
99
+ <circle cx="12" cy="12" r="3"></circle>
100
+ </g>
101
+ ),
102
+ })
103
+
104
+ export const SearchIcon = createIcon({
105
+ displayName: 'SearchIcon',
106
+ path: (
107
+ <g>
108
+ <circle cx="11" cy="11" r="8"></circle>
109
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
110
+ </g>
111
+ ),
112
+ })
113
+
114
+ export const CheckIcon = createIcon({
115
+ displayName: 'CheckIcon',
116
+ path: (
117
+ <g>
118
+ <polyline points="20 6 9 17 4 12"></polyline>
119
+ </g>
120
+ ),
121
+ })
@@ -0,0 +1 @@
1
+ export * from './icons.tsx'
@@ -0,0 +1 @@
1
+ export * from './input-group'
@@ -0,0 +1,46 @@
1
+ import { cloneElement, forwardRef, isValidElement } from 'react'
2
+
3
+ import type { BoxProps, InputElementProps } from '@chakra-ui/react'
4
+ import { Group, InputElement } from '@chakra-ui/react'
5
+
6
+ export interface InputGroupProps extends BoxProps {
7
+ startElementProps?: InputElementProps
8
+ endElementProps?: InputElementProps
9
+ startElement?: React.ReactNode
10
+ endElement?: React.ReactNode
11
+ children: React.ReactNode
12
+ }
13
+
14
+ export const InputGroup = forwardRef<HTMLDivElement, InputGroupProps>(
15
+ function InputGroup(props, ref) {
16
+ const {
17
+ startElement,
18
+ startElementProps,
19
+ endElement,
20
+ endElementProps,
21
+ children,
22
+ ...rest
23
+ } = props
24
+
25
+ return (
26
+ <Group ref={ref} display="flex" {...rest}>
27
+ {startElement && (
28
+ <InputElement pointerEvents="none" {...startElementProps}>
29
+ {startElement}
30
+ </InputElement>
31
+ )}
32
+ {isValidElement(children) &&
33
+ cloneElement(children, {
34
+ ...(startElement && { ps: 'calc(var(--input-height) - 6px)' }),
35
+ ...(endElement && { pe: 'calc(var(--input-height) - 6px)' }),
36
+ ...children.props,
37
+ })}
38
+ {endElement && (
39
+ <InputElement placement="end" {...endElementProps}>
40
+ {endElement}
41
+ </InputElement>
42
+ )}
43
+ </Group>
44
+ )
45
+ },
46
+ )
@@ -0,0 +1,2 @@
1
+ export { Link } from './link'
2
+ export type { LinkProps } from './link'
@@ -0,0 +1,17 @@
1
+ import { Meta, StoryObj } from '@storybook/react'
2
+
3
+ import { Link } from './'
4
+
5
+ export default {
6
+ title: 'Components/Link',
7
+ component: Link,
8
+ } as Meta
9
+
10
+ type Story = StoryObj<typeof Link>
11
+
12
+ export const Default: Story = {
13
+ args: {
14
+ href: '#',
15
+ children: 'Link',
16
+ },
17
+ }
@@ -0,0 +1,33 @@
1
+ import * as React from 'react'
2
+
3
+ import { render } from '@saas-ui/test-utils'
4
+
5
+ import { SaasProvider } from '../provider'
6
+
7
+ import { Link } from './link'
8
+
9
+ interface LinkComponentProps {
10
+ href: string
11
+ children: React.ReactNode
12
+ }
13
+
14
+ const LinkComponent: React.FC<LinkComponentProps> = (props) => {
15
+ const { children, href, ...rest } = props
16
+ return (
17
+ <a href={href} {...rest} className="saas-ui-link">
18
+ {children}
19
+ </a>
20
+ )
21
+ }
22
+
23
+ const renderLink = (ui: React.ReactNode) => {
24
+ return render(<SaasProvider linkComponent={LinkComponent}>{ui}</SaasProvider>)
25
+ }
26
+
27
+ test('should render the link', async () => {
28
+ const { getByText } = renderLink(<Link>Test</Link>)
29
+
30
+ const link = getByText('Test')
31
+
32
+ expect(link).toHaveClass('saas-ui-link')
33
+ })
@@ -0,0 +1,27 @@
1
+ import React from 'react'
2
+
3
+ import { Link as ChakraLink, LinkProps } from '@chakra-ui/react'
4
+
5
+ import { useLink } from '../provider'
6
+
7
+ export type { LinkProps }
8
+
9
+ /**
10
+ * Chakra UI `Link` component wrapped in a router specific link component.
11
+ * Falls back to a plain `Link` if no Saas UI context is available or no `linkComponent` is configured
12
+ * The router link component can be configured in `SaasProvider`.
13
+ * @see https://saas-ui.dev/docs/core/getting-started
14
+ */
15
+ export const Link = React.forwardRef<HTMLAnchorElement, LinkProps>(
16
+ (props, ref) => {
17
+ const LinkComponent = useLink()
18
+
19
+ return (
20
+ <ChakraLink asChild>
21
+ <LinkComponent ref={ref} {...props} />
22
+ </ChakraLink>
23
+ )
24
+ },
25
+ )
26
+
27
+ Link.displayName = 'Link'
@@ -0,0 +1,12 @@
1
+ "use client"
2
+
3
+ import type { HTMLChakraProps, RecipeProps } from "@chakra-ui/react"
4
+ import { createRecipeContext } from "@chakra-ui/react"
5
+
6
+ export interface LinkButtonProps
7
+ extends HTMLChakraProps<"a", RecipeProps<"button">> {}
8
+
9
+ const { withContext } = createRecipeContext({ key: "button" })
10
+
11
+ // Replace "a" with your framework's link component
12
+ export const LinkButton = withContext<HTMLAnchorElement, LinkButtonProps>("a")
@@ -0,0 +1 @@
1
+ export * as LoadingOverlay from './namespace.ts'
@@ -0,0 +1,61 @@
1
+ import { defineSlotRecipe } from '@chakra-ui/react'
2
+
3
+ export const loadingOverlaySlotRecipe = defineSlotRecipe({
4
+ className: 'sui-loading-overlay',
5
+ slots: ['root', 'text'],
6
+ base: {
7
+ root: {
8
+ display: 'flex',
9
+ flexDirection: 'column',
10
+ alignItems: 'center',
11
+ justifyContent: 'center',
12
+ p: 4,
13
+ transitionProperty: 'opacity',
14
+ transitionDuration: 'slower',
15
+ _open: {
16
+ opacity: 1,
17
+ },
18
+ _closed: {
19
+ opacity: 0,
20
+ },
21
+ },
22
+ },
23
+ variants: {
24
+ variant: {
25
+ fill: {
26
+ root: {
27
+ flex: 1,
28
+ height: '100%',
29
+ bg: {
30
+ base: 'whiteAlpha.400',
31
+ _dark: 'blackAlpha.400',
32
+ },
33
+ },
34
+ },
35
+ fullscreen: {
36
+ root: {
37
+ position: 'fixed',
38
+ inset: 0,
39
+ zIndex: 'modal',
40
+ bg: {
41
+ base: 'white',
42
+ _dark: 'gray.800',
43
+ },
44
+ },
45
+ },
46
+ overlay: {
47
+ root: {
48
+ position: 'absolute',
49
+ inset: 0,
50
+ bg: {
51
+ base: 'whiteAlpha.300',
52
+ _dark: 'blackAlpha.300',
53
+ },
54
+ },
55
+ },
56
+ },
57
+ },
58
+ defaultVariants: {
59
+ variant: 'fill',
60
+ },
61
+ })
@@ -0,0 +1,68 @@
1
+ import * as React from 'react'
2
+
3
+ import { Button, Card, Container, Text } from '@chakra-ui/react'
4
+
5
+ import { LoadingOverlay } from './index.ts'
6
+
7
+ export default {
8
+ title: 'Components/LoadingOverlay',
9
+ decorators: [
10
+ (Story: any) => (
11
+ <Container mt="40px">
12
+ <Story />
13
+ </Container>
14
+ ),
15
+ ],
16
+ }
17
+
18
+ export const Basic = () => (
19
+ <Card.Root width="400px" height="200px" overflow="hidden">
20
+ <LoadingOverlay.Root>
21
+ <LoadingOverlay.Spinner />
22
+ </LoadingOverlay.Root>
23
+ </Card.Root>
24
+ )
25
+
26
+ export const Overlay = () => (
27
+ <Card.Root width="400px" height="200px" pos="relative" p="4">
28
+ <Text>This content will be overlayed.</Text>
29
+ <LoadingOverlay.Root variant="overlay">
30
+ <LoadingOverlay.Spinner />
31
+ </LoadingOverlay.Root>
32
+ </Card.Root>
33
+ )
34
+
35
+ export const Fullscreen = () => (
36
+ <LoadingOverlay.Root variant="fullscreen">
37
+ <LoadingOverlay.Spinner />
38
+ </LoadingOverlay.Root>
39
+ )
40
+
41
+ export const WithText = () => (
42
+ <Card.Root width="400px" height="200px">
43
+ <LoadingOverlay.Root borderRadius="md">
44
+ <LoadingOverlay.Text>Loading...</LoadingOverlay.Text>
45
+ </LoadingOverlay.Root>
46
+ </Card.Root>
47
+ )
48
+
49
+ export const ExitAnimation = () => {
50
+ const [loading, setLoading] = React.useState(false)
51
+
52
+ React.useEffect(() => {
53
+ if (loading) {
54
+ setTimeout(() => {
55
+ setLoading(false)
56
+ }, 4000)
57
+ }
58
+ }, [loading])
59
+
60
+ return (
61
+ <Card.Root width="400px" height="200px" pos="relative" p="4">
62
+ <Button onClick={() => setLoading(true)}>Show loader</Button>
63
+ <LoadingOverlay.Root variant="overlay" loading={loading}>
64
+ <LoadingOverlay.Spinner />
65
+ </LoadingOverlay.Root>
66
+ </Card.Root>
67
+ )
68
+ }
@@ -0,0 +1,54 @@
1
+ import React from 'react'
2
+
3
+ import {
4
+ Presence,
5
+ type PresenceBaseProps,
6
+ splitPresenceProps,
7
+ } from '@ark-ui/react/presence'
8
+ import {
9
+ HTMLChakraProps,
10
+ SlotRecipeProps,
11
+ chakra,
12
+ createSlotRecipeContext,
13
+ } from '@chakra-ui/react'
14
+
15
+ import { Spinner } from '#components/spinner'
16
+
17
+ const { useStyles, withContext, withProvider } = createSlotRecipeContext({
18
+ key: 'loadingOverlay',
19
+ })
20
+
21
+ export const useLoadingOverlayStyles = useStyles
22
+
23
+ export interface LoadingOverlayProps
24
+ extends HTMLChakraProps<'div'>,
25
+ SlotRecipeProps<'loadingOverlay'>,
26
+ PresenceBaseProps {
27
+ /**
28
+ * Show or hide the LoadingOverlay.
29
+ * @default true
30
+ */
31
+ loading?: boolean
32
+ }
33
+
34
+ const LoadingOverlay: React.FC<LoadingOverlayProps> = (props) => {
35
+ const { children, loading = true, ...rest } = props
36
+
37
+ const [presenceProps, rootProps] = splitPresenceProps(rest)
38
+
39
+ return (
40
+ <Presence present={loading} {...presenceProps} asChild>
41
+ <chakra.div {...rootProps}>{children}</chakra.div>
42
+ </Presence>
43
+ )
44
+ }
45
+
46
+ export const LoadingOverlayRoot = withProvider(LoadingOverlay, 'root')
47
+
48
+ LoadingOverlayRoot.displayName = 'LoadingOverlay'
49
+
50
+ export const LoadingOverlaySpinner = Spinner
51
+
52
+ export interface LoadingTextProps extends HTMLChakraProps<'p'> {}
53
+
54
+ export const LoadingOverlayText = withContext('p', 'text')
@@ -0,0 +1,7 @@
1
+ import {
2
+ LoadingOverlayRoot as Root,
3
+ LoadingOverlaySpinner as Spinner,
4
+ LoadingOverlayText as Text,
5
+ } from './loading-overlay.tsx'
6
+
7
+ export { Root, Spinner, Text }