@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.
- package/CHANGELOG.md +7 -154
- package/dist/index.cjs +8461 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +26 -0
- package/dist/index.d.ts +25 -7
- package/dist/index.js +8415 -35
- package/dist/index.js.map +1 -1
- package/package.json +24 -21
- package/src/components/accordion.tsx +47 -0
- package/src/components/action-bar.tsx +40 -0
- package/src/components/alert.tsx +51 -0
- package/src/components/app-shell/app-shell.recipe.ts +52 -0
- package/src/components/app-shell/app-shell.stories.tsx +51 -0
- package/src/components/app-shell/app-shell.tsx +94 -0
- package/src/components/app-shell/index.ts +3 -0
- package/src/components/avatar.tsx +74 -0
- package/src/components/blockquote.tsx +31 -0
- package/src/components/breadcrumbs/breadcrumb.stories.tsx +17 -0
- package/src/components/breadcrumbs/breadcrumb.tsx +36 -0
- package/src/components/breadcrumbs/index.ts +1 -0
- package/src/components/breadcrumbs/namespace.ts +8 -0
- package/src/components/button/button.recipe.ts +182 -0
- package/src/components/button/button.stories.tsx +99 -0
- package/src/components/button/button.tsx +55 -0
- package/src/components/button/index.ts +2 -0
- package/src/components/checkbox/checkbox.tsx +26 -0
- package/src/components/checkbox/index.ts +2 -0
- package/src/components/checkbox-card.tsx +57 -0
- package/src/components/checkbox.tsx +25 -0
- package/src/components/clipboard.tsx +107 -0
- package/src/components/close-button/close-button.stories.tsx +12 -0
- package/src/components/close-button/close-button.tsx +18 -0
- package/src/components/close-button/index.ts +2 -0
- package/src/components/color-mode.tsx +65 -0
- package/src/components/command/command.recipe.ts +17 -0
- package/src/components/command/command.stories.tsx +47 -0
- package/src/components/command/command.tsx +50 -0
- package/src/components/command/index.ts +1 -0
- package/src/components/data-list.tsx +37 -0
- package/src/components/dialog/dialog.tsx +66 -0
- package/src/components/dialog/index.ts +1 -0
- package/src/components/dialog/namespace.ts +18 -0
- package/src/components/drawer/drawer.tsx +56 -0
- package/src/components/drawer/index.ts +3 -0
- package/src/components/drawer/namespace.ts +19 -0
- package/src/components/empty-state.tsx +34 -0
- package/src/components/field.tsx +33 -0
- package/src/components/file-button.tsx +166 -0
- package/src/components/grid-list/grid-list.recipe.ts +113 -0
- package/src/components/hover-card.tsx +35 -0
- package/src/components/icon-badge/icon-badge.recipe.ts +57 -0
- package/src/components/icon-badge/icon-badge.stories.tsx +38 -0
- package/src/components/icon-badge/icon-badge.tsx +59 -0
- package/src/components/icon-badge/index.ts +2 -0
- package/src/components/icons/create-icon.tsx +41 -0
- package/src/components/icons/icons.tsx +121 -0
- package/src/components/icons/index.ts +1 -0
- package/src/components/input-group/index.ts +1 -0
- package/src/components/input-group/input-group.tsx +46 -0
- package/src/components/link/index.ts +2 -0
- package/src/components/link/link.stories.tsx +17 -0
- package/src/components/link/link.test.tsx +33 -0
- package/src/components/link/link.tsx +27 -0
- package/src/components/link-button.tsx +12 -0
- package/src/components/loading-overlay/index.ts +1 -0
- package/src/components/loading-overlay/loading-overlay.recipe.ts +61 -0
- package/src/components/loading-overlay/loading-overlay.stories.tsx +68 -0
- package/src/components/loading-overlay/loading-overlay.tsx +54 -0
- package/src/components/loading-overlay/namespace.ts +7 -0
- package/src/components/menu.tsx +108 -0
- package/src/components/native-select.tsx +57 -0
- package/src/components/navbar/index.ts +1 -0
- package/src/components/navbar/namespace.ts +9 -0
- package/src/components/navbar/navbar.recipe.ts +109 -0
- package/src/components/navbar/navbar.stories.tsx +435 -0
- package/src/components/navbar/navbar.test.tsx +49 -0
- package/src/components/navbar/navbar.tsx +39 -0
- package/src/components/number-input/index.ts +2 -0
- package/src/components/number-input/number-input.tsx +41 -0
- package/src/components/pagination.tsx +207 -0
- package/src/components/password-input/index.ts +2 -0
- package/src/components/password-input/password-input.tsx +98 -0
- package/src/components/persona/index.ts +2 -0
- package/src/components/persona/namespace.ts +18 -0
- package/src/components/persona/persona-primitive.tsx +220 -0
- package/src/components/persona/persona.recipe.ts +94 -0
- package/src/components/persona/persona.stories.tsx +101 -0
- package/src/components/persona/persona.tsx +143 -0
- package/src/components/pin-input/index.ts +2 -0
- package/src/components/pin-input/pin-input.tsx +36 -0
- package/src/components/popover.tsx +58 -0
- package/src/components/progress-circle.tsx +37 -0
- package/src/components/progress.tsx +40 -0
- package/src/components/prose.tsx +264 -0
- package/src/components/provider.tsx +12 -0
- package/src/components/radio/index.ts +2 -0
- package/src/components/radio/radio.tsx +27 -0
- package/src/components/radio-card.tsx +57 -0
- package/src/components/radio.tsx +24 -0
- package/src/components/rating.tsx +27 -0
- package/src/components/search-input/index.ts +2 -0
- package/src/components/search-input/search-input.stories.tsx +63 -0
- package/src/components/search-input/search-input.tsx +134 -0
- package/src/components/segmented-control.tsx +47 -0
- package/src/components/select/index.ts +1 -0
- package/src/components/select/namespace.ts +18 -0
- package/src/components/select/select.tsx +135 -0
- package/src/components/sidebar/index.ts +7 -0
- package/src/components/sidebar/namespace.ts +27 -0
- package/src/components/sidebar/sidebar-item.recipe.ts +65 -0
- package/src/components/sidebar/sidebar.recipe.ts +237 -0
- package/src/components/sidebar/sidebar.stories.tsx +903 -0
- package/src/components/sidebar/sidebar.tsx +204 -0
- package/src/components/skeleton.tsx +44 -0
- package/src/components/slider.tsx +53 -0
- package/src/components/spinner/index.ts +2 -0
- package/src/components/spinner/spinner.stories.tsx +19 -0
- package/src/components/spinner/spinner.tsx +21 -0
- package/src/components/stat.tsx +75 -0
- package/src/components/status.tsx +29 -0
- package/src/components/stepper-input.tsx +49 -0
- package/src/components/steps/index.ts +1 -0
- package/src/components/steps/namespace.ts +16 -0
- package/src/components/steps/steps.tsx +82 -0
- package/src/components/switch/index.ts +3 -0
- package/src/components/switch/switch.tsx +39 -0
- package/src/components/tag.tsx +39 -0
- package/src/components/timeline.tsx +17 -0
- package/src/components/toaster.tsx +43 -0
- package/src/components/toggle-tip.tsx +62 -0
- package/src/components/tooltip.tsx +46 -0
- package/src/index.ts +6 -7
- package/src/preset.ts +9 -0
- package/src/provider/index.ts +4 -0
- package/src/provider/sui-provider.tsx +34 -0
- package/src/provider/use-link.test.tsx +60 -0
- package/src/provider/use-link.tsx +13 -0
- package/src/theme/animation-styles.ts +53 -0
- package/src/theme/breakpoints.ts +11 -0
- package/src/theme/conditions.ts +26 -0
- package/src/theme/fluid-font-sizes.ts +65 -0
- package/src/theme/global-css.ts +94 -0
- package/src/theme/index.ts +72 -0
- package/src/theme/layer-styles.ts +116 -0
- package/src/theme/recipes/chakra/accordion.ts +145 -0
- package/src/theme/recipes/chakra/action-bar.ts +62 -0
- package/src/theme/recipes/chakra/alert.ts +157 -0
- package/src/theme/recipes/chakra/avatar.ts +141 -0
- package/src/theme/recipes/chakra/badge.ts +67 -0
- package/src/theme/recipes/chakra/blockquote.ts +83 -0
- package/src/theme/recipes/chakra/breadcrumb.ts +94 -0
- package/src/theme/recipes/chakra/card.ts +99 -0
- package/src/theme/recipes/chakra/checkbox-card.ts +212 -0
- package/src/theme/recipes/chakra/checkbox.ts +70 -0
- package/src/theme/recipes/chakra/checkmark.ts +83 -0
- package/src/theme/recipes/chakra/code.ts +17 -0
- package/src/theme/recipes/chakra/collapsible.ts +20 -0
- package/src/theme/recipes/chakra/container.ts +26 -0
- package/src/theme/recipes/chakra/data-list.ts +80 -0
- package/src/theme/recipes/chakra/dialog.ts +225 -0
- package/src/theme/recipes/chakra/drawer.ts +201 -0
- package/src/theme/recipes/chakra/editable.ts +88 -0
- package/src/theme/recipes/chakra/empty-state.ts +88 -0
- package/src/theme/recipes/chakra/field.ts +68 -0
- package/src/theme/recipes/chakra/fieldset.ts +62 -0
- package/src/theme/recipes/chakra/file-upload.ts +96 -0
- package/src/theme/recipes/chakra/heading.ts +27 -0
- package/src/theme/recipes/chakra/hover-card.ts +68 -0
- package/src/theme/recipes/chakra/icon.ts +30 -0
- package/src/theme/recipes/chakra/input-addon.ts +40 -0
- package/src/theme/recipes/chakra/input.ts +96 -0
- package/src/theme/recipes/chakra/kbd.ts +60 -0
- package/src/theme/recipes/chakra/link.ts +37 -0
- package/src/theme/recipes/chakra/list.ts +67 -0
- package/src/theme/recipes/chakra/mark.ts +27 -0
- package/src/theme/recipes/chakra/menu.ts +124 -0
- package/src/theme/recipes/chakra/native-select.ts +140 -0
- package/src/theme/recipes/chakra/number-input.ts +115 -0
- package/src/theme/recipes/chakra/pin-input.ts +27 -0
- package/src/theme/recipes/chakra/popover.ts +86 -0
- package/src/theme/recipes/chakra/progress-circle.ts +94 -0
- package/src/theme/recipes/chakra/progress.ts +127 -0
- package/src/theme/recipes/chakra/radio-card.ts +220 -0
- package/src/theme/recipes/chakra/radio-group.ts +72 -0
- package/src/theme/recipes/chakra/radiomark.ts +107 -0
- package/src/theme/recipes/chakra/rating-group.ts +94 -0
- package/src/theme/recipes/chakra/segment-group.ts +117 -0
- package/src/theme/recipes/chakra/select.ts +282 -0
- package/src/theme/recipes/chakra/separator.ts +51 -0
- package/src/theme/recipes/chakra/skeleton.ts +53 -0
- package/src/theme/recipes/chakra/skip-nav-link.ts +34 -0
- package/src/theme/recipes/chakra/slider.ts +178 -0
- package/src/theme/recipes/chakra/spinner.ts +32 -0
- package/src/theme/recipes/chakra/stat.ts +79 -0
- package/src/theme/recipes/chakra/status.ts +48 -0
- package/src/theme/recipes/chakra/steps.ts +218 -0
- package/src/theme/recipes/chakra/switch.ts +167 -0
- package/src/theme/recipes/chakra/table.ts +172 -0
- package/src/theme/recipes/chakra/tabs.ts +280 -0
- package/src/theme/recipes/chakra/tag.ts +131 -0
- package/src/theme/recipes/chakra/textarea.ts +88 -0
- package/src/theme/recipes/chakra/timeline.ts +138 -0
- package/src/theme/recipes/chakra/toast.ts +96 -0
- package/src/theme/recipes/chakra/tooltip.ts +40 -0
- package/src/theme/recipes.ts +46 -0
- package/src/theme/semantic-tokens/colors.ts +403 -0
- package/src/theme/semantic-tokens/radii.ts +7 -0
- package/src/theme/semantic-tokens/shadows.ts +52 -0
- package/src/theme/slot-recipes.ts +104 -0
- package/src/theme/text-styles.ts +39 -0
- package/src/theme/tokens/animations.ts +8 -0
- package/src/theme/tokens/aspect-ratios.ts +10 -0
- package/src/theme/tokens/blurs.ts +12 -0
- package/src/theme/tokens/borders.ts +9 -0
- package/src/theme/tokens/colors.ts +177 -0
- package/src/theme/tokens/cursor.ts +12 -0
- package/src/theme/tokens/durations.ts +11 -0
- package/src/theme/tokens/easings.ts +10 -0
- package/src/theme/tokens/font-sizes.ts +20 -0
- package/src/theme/tokens/font-weights.ts +13 -0
- package/src/theme/tokens/fonts.ts +15 -0
- package/src/theme/tokens/keyframes.ts +173 -0
- package/src/theme/tokens/letter-spacing.ts +9 -0
- package/src/theme/tokens/line-heights.ts +19 -0
- package/src/theme/tokens/radius.ts +18 -0
- package/src/theme/tokens/sizes.ts +71 -0
- package/src/theme/tokens/spacing.ts +38 -0
- package/src/theme/tokens/z-indices.ts +34 -0
- package/src/theme/utils.ts +46 -0
- package/dist/index.d.mts +0 -8
- package/dist/index.mjs +0 -11
- package/dist/index.mjs.map +0 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import type { ButtonProps, TextProps } from "@chakra-ui/react"
|
|
4
|
+
import {
|
|
5
|
+
Button,
|
|
6
|
+
Pagination as ChakraPagination,
|
|
7
|
+
IconButton,
|
|
8
|
+
Text,
|
|
9
|
+
createContext,
|
|
10
|
+
usePaginationContext,
|
|
11
|
+
} from "@chakra-ui/react"
|
|
12
|
+
import { forwardRef, useMemo } from "react"
|
|
13
|
+
import {
|
|
14
|
+
HiChevronLeft,
|
|
15
|
+
HiChevronRight,
|
|
16
|
+
HiMiniEllipsisHorizontal,
|
|
17
|
+
} from "react-icons/hi2"
|
|
18
|
+
import { LinkButton } from "./link-button"
|
|
19
|
+
|
|
20
|
+
interface ButtonVariantMap {
|
|
21
|
+
current: ButtonProps["variant"]
|
|
22
|
+
default: ButtonProps["variant"]
|
|
23
|
+
ellipsis: ButtonProps["variant"]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type PaginationVariant = "outline" | "solid" | "subtle"
|
|
27
|
+
|
|
28
|
+
interface ButtonVariantContext {
|
|
29
|
+
size: ButtonProps["size"]
|
|
30
|
+
variantMap: ButtonVariantMap
|
|
31
|
+
getHref?: (page: number) => string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const [RootPropsProvider, useRootProps] = createContext<ButtonVariantContext>({
|
|
35
|
+
name: "RootPropsProvider",
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export interface PaginationRootProps
|
|
39
|
+
extends Omit<ChakraPagination.RootProps, "type"> {
|
|
40
|
+
size?: ButtonProps["size"]
|
|
41
|
+
variant?: PaginationVariant
|
|
42
|
+
getHref?: (page: number) => string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const variantMap: Record<PaginationVariant, ButtonVariantMap> = {
|
|
46
|
+
outline: { default: "ghost", ellipsis: "plain", current: "outline" },
|
|
47
|
+
solid: { default: "outline", ellipsis: "outline", current: "solid" },
|
|
48
|
+
subtle: { default: "ghost", ellipsis: "plain", current: "subtle" },
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const PaginationRoot = forwardRef<HTMLDivElement, PaginationRootProps>(
|
|
52
|
+
function PaginationRoot(props, ref) {
|
|
53
|
+
const { size = "sm", variant = "outline", getHref, ...rest } = props
|
|
54
|
+
return (
|
|
55
|
+
<RootPropsProvider
|
|
56
|
+
value={{ size, variantMap: variantMap[variant], getHref }}
|
|
57
|
+
>
|
|
58
|
+
<ChakraPagination.Root
|
|
59
|
+
ref={ref}
|
|
60
|
+
type={getHref ? "link" : "button"}
|
|
61
|
+
{...rest}
|
|
62
|
+
/>
|
|
63
|
+
</RootPropsProvider>
|
|
64
|
+
)
|
|
65
|
+
},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
export const PaginationEllipsis = forwardRef<
|
|
69
|
+
HTMLDivElement,
|
|
70
|
+
ChakraPagination.EllipsisProps
|
|
71
|
+
>(function PaginationEllipsis(props, ref) {
|
|
72
|
+
const { size, variantMap } = useRootProps()
|
|
73
|
+
return (
|
|
74
|
+
<ChakraPagination.Ellipsis ref={ref} {...props} asChild>
|
|
75
|
+
<Button as="span" variant={variantMap.ellipsis} size={size}>
|
|
76
|
+
<HiMiniEllipsisHorizontal />
|
|
77
|
+
</Button>
|
|
78
|
+
</ChakraPagination.Ellipsis>
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
export const PaginationItem = forwardRef<
|
|
83
|
+
HTMLButtonElement,
|
|
84
|
+
ChakraPagination.ItemProps
|
|
85
|
+
>(function PaginationItem(props, ref) {
|
|
86
|
+
const { page } = usePaginationContext()
|
|
87
|
+
const { size, variantMap, getHref } = useRootProps()
|
|
88
|
+
|
|
89
|
+
const current = page === props.value
|
|
90
|
+
const variant = current ? variantMap.current : variantMap.default
|
|
91
|
+
|
|
92
|
+
if (getHref) {
|
|
93
|
+
return (
|
|
94
|
+
<LinkButton href={getHref(props.value)} variant={variant} size={size}>
|
|
95
|
+
{props.value}
|
|
96
|
+
</LinkButton>
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<ChakraPagination.Item ref={ref} {...props} asChild>
|
|
102
|
+
<Button variant={variant} size={size}>
|
|
103
|
+
{props.value}
|
|
104
|
+
</Button>
|
|
105
|
+
</ChakraPagination.Item>
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
export const PaginationPrevTrigger = forwardRef<
|
|
110
|
+
HTMLButtonElement,
|
|
111
|
+
ChakraPagination.PrevTriggerProps
|
|
112
|
+
>(function PaginationPrevTrigger(props, ref) {
|
|
113
|
+
const { size, variantMap, getHref } = useRootProps()
|
|
114
|
+
const { previousPage } = usePaginationContext()
|
|
115
|
+
|
|
116
|
+
if (getHref) {
|
|
117
|
+
return (
|
|
118
|
+
<LinkButton
|
|
119
|
+
href={previousPage != null ? getHref(previousPage) : undefined}
|
|
120
|
+
variant={variantMap.default}
|
|
121
|
+
size={size}
|
|
122
|
+
>
|
|
123
|
+
<HiChevronLeft />
|
|
124
|
+
</LinkButton>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<ChakraPagination.PrevTrigger ref={ref} asChild {...props}>
|
|
130
|
+
<IconButton variant={variantMap.default} size={size}>
|
|
131
|
+
<HiChevronLeft />
|
|
132
|
+
</IconButton>
|
|
133
|
+
</ChakraPagination.PrevTrigger>
|
|
134
|
+
)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
export const PaginationNextTrigger = forwardRef<
|
|
138
|
+
HTMLButtonElement,
|
|
139
|
+
ChakraPagination.NextTriggerProps
|
|
140
|
+
>(function PaginationNextTrigger(props, ref) {
|
|
141
|
+
const { size, variantMap, getHref } = useRootProps()
|
|
142
|
+
const { nextPage } = usePaginationContext()
|
|
143
|
+
|
|
144
|
+
if (getHref) {
|
|
145
|
+
return (
|
|
146
|
+
<LinkButton
|
|
147
|
+
href={nextPage != null ? getHref(nextPage) : undefined}
|
|
148
|
+
variant={variantMap.default}
|
|
149
|
+
size={size}
|
|
150
|
+
>
|
|
151
|
+
<HiChevronRight />
|
|
152
|
+
</LinkButton>
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<ChakraPagination.NextTrigger ref={ref} asChild {...props}>
|
|
158
|
+
<IconButton variant={variantMap.default} size={size}>
|
|
159
|
+
<HiChevronRight />
|
|
160
|
+
</IconButton>
|
|
161
|
+
</ChakraPagination.NextTrigger>
|
|
162
|
+
)
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
export const PaginationItems = (props: React.HTMLAttributes<HTMLElement>) => {
|
|
166
|
+
return (
|
|
167
|
+
<ChakraPagination.Context>
|
|
168
|
+
{({ pages }) =>
|
|
169
|
+
pages.map((page, index) => {
|
|
170
|
+
return page.type === "ellipsis" ? (
|
|
171
|
+
<PaginationEllipsis key={index} index={index} {...props} />
|
|
172
|
+
) : (
|
|
173
|
+
<PaginationItem
|
|
174
|
+
key={index}
|
|
175
|
+
type="page"
|
|
176
|
+
value={page.value}
|
|
177
|
+
{...props}
|
|
178
|
+
/>
|
|
179
|
+
)
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
</ChakraPagination.Context>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface PageTextProps extends TextProps {
|
|
187
|
+
format?: "short" | "compact" | "long"
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const PaginationPageText = forwardRef<
|
|
191
|
+
HTMLParagraphElement,
|
|
192
|
+
PageTextProps
|
|
193
|
+
>(function PaginationPageText(props, ref) {
|
|
194
|
+
const { format = "compact", ...rest } = props
|
|
195
|
+
const { page, pages, pageRange, count } = usePaginationContext()
|
|
196
|
+
const content = useMemo(() => {
|
|
197
|
+
if (format === "short") return `${page} / ${pages.length}`
|
|
198
|
+
if (format === "compact") return `${page} of ${pages.length}`
|
|
199
|
+
return `${pageRange.start + 1} - ${pageRange.end} of ${count}`
|
|
200
|
+
}, [format, page, pages.length, pageRange, count])
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<Text fontWeight="medium" ref={ref} {...rest}>
|
|
204
|
+
{content}
|
|
205
|
+
</Text>
|
|
206
|
+
)
|
|
207
|
+
})
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { forwardRef, useRef } from 'react'
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
ButtonProps,
|
|
7
|
+
GroupProps,
|
|
8
|
+
InputProps,
|
|
9
|
+
StackProps,
|
|
10
|
+
} from '@chakra-ui/react'
|
|
11
|
+
import {
|
|
12
|
+
IconButton,
|
|
13
|
+
Input,
|
|
14
|
+
mergeRefs,
|
|
15
|
+
useControllableState,
|
|
16
|
+
} from '@chakra-ui/react'
|
|
17
|
+
import { LuEye, LuEyeOff } from 'react-icons/lu'
|
|
18
|
+
|
|
19
|
+
import { InputGroup } from '../input-group'
|
|
20
|
+
|
|
21
|
+
export interface PasswordInputProps
|
|
22
|
+
extends InputProps,
|
|
23
|
+
PasswordVisibilityProps {
|
|
24
|
+
rootProps?: GroupProps
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const PasswordInput = forwardRef<HTMLInputElement, PasswordInputProps>(
|
|
28
|
+
function PasswordInput(props, ref) {
|
|
29
|
+
const {
|
|
30
|
+
rootProps,
|
|
31
|
+
defaultVisible,
|
|
32
|
+
visible: visibleProp,
|
|
33
|
+
onVisibleChange,
|
|
34
|
+
visibilityIcon = { on: <LuEye />, off: <LuEyeOff /> },
|
|
35
|
+
...rest
|
|
36
|
+
} = props
|
|
37
|
+
|
|
38
|
+
const [visible, setVisible] = useControllableState({
|
|
39
|
+
value: visibleProp,
|
|
40
|
+
defaultValue: defaultVisible || false,
|
|
41
|
+
onChange: onVisibleChange,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<InputGroup
|
|
48
|
+
width="full"
|
|
49
|
+
endElement={
|
|
50
|
+
<VisibilityTrigger
|
|
51
|
+
disabled={rest.disabled}
|
|
52
|
+
onPointerDown={(e) => {
|
|
53
|
+
if (rest.disabled) return
|
|
54
|
+
if (e.button !== 0) return
|
|
55
|
+
e.preventDefault()
|
|
56
|
+
setVisible(!visible)
|
|
57
|
+
}}
|
|
58
|
+
>
|
|
59
|
+
{visible ? visibilityIcon.off : visibilityIcon.on}
|
|
60
|
+
</VisibilityTrigger>
|
|
61
|
+
}
|
|
62
|
+
{...rootProps}
|
|
63
|
+
>
|
|
64
|
+
<Input
|
|
65
|
+
{...rest}
|
|
66
|
+
ref={mergeRefs(ref, inputRef)}
|
|
67
|
+
type={visible ? 'text' : 'password'}
|
|
68
|
+
/>
|
|
69
|
+
</InputGroup>
|
|
70
|
+
)
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
export interface PasswordVisibilityProps {
|
|
75
|
+
defaultVisible?: boolean
|
|
76
|
+
visible?: boolean
|
|
77
|
+
onVisibleChange?: (visible: boolean) => void
|
|
78
|
+
visibilityIcon?: { on: React.ReactNode; off: React.ReactNode }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const VisibilityTrigger = forwardRef<HTMLButtonElement, ButtonProps>(
|
|
82
|
+
function VisibilityTrigger(props, ref) {
|
|
83
|
+
return (
|
|
84
|
+
<IconButton
|
|
85
|
+
tabIndex={-1}
|
|
86
|
+
ref={ref}
|
|
87
|
+
me="-2"
|
|
88
|
+
aspectRatio="square"
|
|
89
|
+
size="sm"
|
|
90
|
+
variant="ghost"
|
|
91
|
+
colorPalette="gray"
|
|
92
|
+
height="calc(100% - {spacing.2})"
|
|
93
|
+
aria-label="Toggle password visibility"
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
PersonaRoot as Root,
|
|
3
|
+
PersonaAvatar as Avatar,
|
|
4
|
+
PersonaPresenceBadge as PresenceBadge,
|
|
5
|
+
PersonaDetails as Details,
|
|
6
|
+
PersonaLabel as Label,
|
|
7
|
+
PersonaSecondaryLabel as SecondaryLabel,
|
|
8
|
+
PersonaTertiaryLabel as TertiaryLabel,
|
|
9
|
+
} from './persona-primitive.tsx'
|
|
10
|
+
|
|
11
|
+
export type {
|
|
12
|
+
PersonaRootProps as RootProps,
|
|
13
|
+
PersonaAvatarProps as AvatarProps,
|
|
14
|
+
PersonaPresenceBadgeProps as PresenceBadgeProps,
|
|
15
|
+
PersonaDetailsProps as DetailsProps,
|
|
16
|
+
PersonaLabelProps as LabelProps,
|
|
17
|
+
Presence,
|
|
18
|
+
} from './persona-primitive.tsx'
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import React, { forwardRef } from 'react'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Avatar,
|
|
5
|
+
type AvatarRootProps,
|
|
6
|
+
HTMLChakraProps,
|
|
7
|
+
type ImageProps,
|
|
8
|
+
SlotRecipeProps,
|
|
9
|
+
chakra,
|
|
10
|
+
createSlotRecipeContext,
|
|
11
|
+
} from '@chakra-ui/react'
|
|
12
|
+
import { dataAttr } from '@saas-ui/core/utils'
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
useStyles: usePersonaStyles,
|
|
16
|
+
withProvider,
|
|
17
|
+
withContext,
|
|
18
|
+
} = createSlotRecipeContext({
|
|
19
|
+
key: 'persona',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export { usePersonaStyles }
|
|
23
|
+
|
|
24
|
+
export type Presence = 'online' | 'offline' | 'busy' | 'dnd' | 'away'
|
|
25
|
+
|
|
26
|
+
interface PresenceConfig {
|
|
27
|
+
label: string
|
|
28
|
+
color: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type PresenceOptions<P extends string = Presence> = Record<
|
|
32
|
+
P,
|
|
33
|
+
PresenceConfig
|
|
34
|
+
>
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The presence configuration object.
|
|
38
|
+
*
|
|
39
|
+
* Default presence values: online, offline, busy, dnd, away
|
|
40
|
+
*
|
|
41
|
+
* You can overwrite colors in the theme semantic tokens.
|
|
42
|
+
* theme.semanticTokens.colors['presence.online'] = 'cyan.500'
|
|
43
|
+
*
|
|
44
|
+
* Or add a custom presence value
|
|
45
|
+
* theme.semanticTokens.colors['presence.vacay'] = 'blue.500'
|
|
46
|
+
*
|
|
47
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
48
|
+
*/
|
|
49
|
+
export const defaultPresenceOptions: PresenceOptions = {
|
|
50
|
+
online: {
|
|
51
|
+
label: 'Online',
|
|
52
|
+
color: 'presence.online',
|
|
53
|
+
},
|
|
54
|
+
offline: {
|
|
55
|
+
label: 'Offline',
|
|
56
|
+
color: 'presence.offline',
|
|
57
|
+
},
|
|
58
|
+
busy: {
|
|
59
|
+
label: 'Busy',
|
|
60
|
+
color: 'presence.busy',
|
|
61
|
+
},
|
|
62
|
+
dnd: {
|
|
63
|
+
label: 'Do-not-disturb',
|
|
64
|
+
color: 'presence.dnd',
|
|
65
|
+
},
|
|
66
|
+
away: {
|
|
67
|
+
label: 'Away',
|
|
68
|
+
color: 'presence.away',
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface PersonaRootProps
|
|
73
|
+
extends HTMLChakraProps<'div'>,
|
|
74
|
+
SlotRecipeProps<'persona'> {
|
|
75
|
+
/**
|
|
76
|
+
* Indicates that a person is out of office. Changes the presence badge style.
|
|
77
|
+
*/
|
|
78
|
+
outOfOffice?: boolean
|
|
79
|
+
/**
|
|
80
|
+
* The presence status of the person
|
|
81
|
+
*/
|
|
82
|
+
presence?: Presence
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The root component that provides context and styles.
|
|
87
|
+
*
|
|
88
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
89
|
+
*/
|
|
90
|
+
export const PersonaRoot = withProvider<HTMLDivElement, PersonaRootProps>(
|
|
91
|
+
forwardRef((props, ref) => {
|
|
92
|
+
const { outOfOffice, presence, ...rest } = props
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<chakra.div
|
|
96
|
+
ref={ref}
|
|
97
|
+
{...rest}
|
|
98
|
+
data-out-of-office={dataAttr(outOfOffice)}
|
|
99
|
+
data-presence={presence}
|
|
100
|
+
css={[
|
|
101
|
+
presence
|
|
102
|
+
? {
|
|
103
|
+
'--persona-presence': `colors.presence.${presence}`,
|
|
104
|
+
}
|
|
105
|
+
: undefined,
|
|
106
|
+
rest.css,
|
|
107
|
+
]}
|
|
108
|
+
/>
|
|
109
|
+
)
|
|
110
|
+
}),
|
|
111
|
+
'root',
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
interface PersonaAvatarOptions {
|
|
115
|
+
/**
|
|
116
|
+
* The name of the person in the avatar.
|
|
117
|
+
*
|
|
118
|
+
* - if `src` has loaded, the name will be used as the `alt` attribute of the `img`
|
|
119
|
+
* - If `src` is not loaded, the name will be used to create the initials
|
|
120
|
+
*/
|
|
121
|
+
name?: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface PersonaAvatarProps
|
|
125
|
+
extends PersonaAvatarOptions,
|
|
126
|
+
AvatarRootProps {
|
|
127
|
+
src?: string
|
|
128
|
+
srcSet?: string
|
|
129
|
+
loading?: ImageProps['loading']
|
|
130
|
+
icon?: React.ReactElement
|
|
131
|
+
fallback?: React.ReactNode
|
|
132
|
+
getInitials?: (name?: string | null) => string | null
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* An avatar with optional status badge.
|
|
137
|
+
*
|
|
138
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
139
|
+
*/
|
|
140
|
+
export const PersonaAvatar = forwardRef<HTMLDivElement, PersonaAvatarProps>(
|
|
141
|
+
(props, ref) => {
|
|
142
|
+
const {
|
|
143
|
+
name,
|
|
144
|
+
getInitials = (name?: string | null) => name?.[0],
|
|
145
|
+
icon,
|
|
146
|
+
loading,
|
|
147
|
+
onError,
|
|
148
|
+
src,
|
|
149
|
+
srcSet,
|
|
150
|
+
children,
|
|
151
|
+
...rest
|
|
152
|
+
} = props
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<Avatar.Root ref={ref} {...rest}>
|
|
156
|
+
<Avatar.Fallback>{getInitials(name)}</Avatar.Fallback>
|
|
157
|
+
<Avatar.Image
|
|
158
|
+
src={src}
|
|
159
|
+
srcSet={srcSet}
|
|
160
|
+
loading={loading}
|
|
161
|
+
onError={onError}
|
|
162
|
+
/>
|
|
163
|
+
{children}
|
|
164
|
+
</Avatar.Root>
|
|
165
|
+
)
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
export interface PersonaPresenceBadgeProps extends HTMLChakraProps<'span'> {}
|
|
170
|
+
|
|
171
|
+
export const PersonaPresenceBadge = withContext<
|
|
172
|
+
HTMLSpanElement,
|
|
173
|
+
PersonaPresenceBadgeProps
|
|
174
|
+
>('span', 'presence')
|
|
175
|
+
|
|
176
|
+
export interface PersonaDetailsProps extends HTMLChakraProps<'div'> {}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Wrapper component for the labels.
|
|
180
|
+
*
|
|
181
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
182
|
+
*/
|
|
183
|
+
export const PersonaDetails = withContext<HTMLDivElement, PersonaDetailsProps>(
|
|
184
|
+
'div',
|
|
185
|
+
'details',
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
export interface PersonaLabelProps extends HTMLChakraProps<'span'> {}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* The main label, usually a name.
|
|
192
|
+
*
|
|
193
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
194
|
+
*/
|
|
195
|
+
export const PersonaLabel = withContext<HTMLSpanElement, PersonaLabelProps>(
|
|
196
|
+
'span',
|
|
197
|
+
'label',
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
PersonaLabel.displayName = 'PersonaLabel'
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* The secondary label, usually the role of a person.
|
|
204
|
+
*
|
|
205
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
206
|
+
*/
|
|
207
|
+
export const PersonaSecondaryLabel = withContext<
|
|
208
|
+
HTMLSpanElement,
|
|
209
|
+
PersonaLabelProps
|
|
210
|
+
>('span', 'secondaryLabel')
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The tertiary label, typically a status message.
|
|
214
|
+
*
|
|
215
|
+
* @see Docs https://saas-ui.dev/docs/components/data-display/persona
|
|
216
|
+
*/
|
|
217
|
+
export const PersonaTertiaryLabel = withContext<
|
|
218
|
+
HTMLSpanElement,
|
|
219
|
+
PersonaLabelProps
|
|
220
|
+
>('span', 'tertiaryLabel')
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { defineSlotRecipe, defineStyle } from '@chakra-ui/react'
|
|
2
|
+
|
|
3
|
+
const baseStyleLabel = defineStyle({
|
|
4
|
+
overflow: 'hidden',
|
|
5
|
+
whiteSpace: 'nowrap',
|
|
6
|
+
textOverflow: 'ellipsis',
|
|
7
|
+
minW: 0,
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
export const personaSlotRecipe = defineSlotRecipe({
|
|
11
|
+
className: 'sui-persona',
|
|
12
|
+
slots: [
|
|
13
|
+
'root',
|
|
14
|
+
'avatar',
|
|
15
|
+
'presence',
|
|
16
|
+
'details',
|
|
17
|
+
'label',
|
|
18
|
+
'secondaryLabel',
|
|
19
|
+
'tertiaryLabel',
|
|
20
|
+
],
|
|
21
|
+
base: {
|
|
22
|
+
root: {
|
|
23
|
+
display: 'flex',
|
|
24
|
+
flexDirection: 'row',
|
|
25
|
+
alignItems: 'center',
|
|
26
|
+
},
|
|
27
|
+
presence: {
|
|
28
|
+
display: 'flex',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
justifyContent: 'center',
|
|
31
|
+
position: 'absolute',
|
|
32
|
+
bottom: 0,
|
|
33
|
+
right: 0,
|
|
34
|
+
boxSize: '1em',
|
|
35
|
+
transform: 'translate(15%, 15%)',
|
|
36
|
+
borderWidth: '0.15em',
|
|
37
|
+
borderRadius: '50%',
|
|
38
|
+
borderColor: 'bg.panel',
|
|
39
|
+
bg: 'var(--persona-presence)',
|
|
40
|
+
},
|
|
41
|
+
details: {
|
|
42
|
+
display: 'flex',
|
|
43
|
+
flexDirection: 'column',
|
|
44
|
+
minW: 0,
|
|
45
|
+
lineHeight: 'short',
|
|
46
|
+
},
|
|
47
|
+
label: baseStyleLabel,
|
|
48
|
+
secondaryLabel: {
|
|
49
|
+
...baseStyleLabel,
|
|
50
|
+
color: 'fg.muted',
|
|
51
|
+
},
|
|
52
|
+
tertiaryLabel: {
|
|
53
|
+
...baseStyleLabel,
|
|
54
|
+
color: 'fg.muted',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
variants: {
|
|
58
|
+
size: {
|
|
59
|
+
xs: {
|
|
60
|
+
details: { ms: 2 },
|
|
61
|
+
label: { fontSize: 'xs' },
|
|
62
|
+
secondaryLabel: { display: 'none' },
|
|
63
|
+
tertiaryLabel: { display: 'none' },
|
|
64
|
+
},
|
|
65
|
+
sm: {
|
|
66
|
+
details: { ms: 2 },
|
|
67
|
+
label: { fontSize: 'sm' },
|
|
68
|
+
secondaryLabel: { fontSize: 'xs' },
|
|
69
|
+
tertiaryLabel: { display: 'none' },
|
|
70
|
+
},
|
|
71
|
+
md: {
|
|
72
|
+
details: { ms: 2 },
|
|
73
|
+
label: { fontSize: 'sm' },
|
|
74
|
+
secondaryLabel: { fontSize: 'xs' },
|
|
75
|
+
tertiaryLabel: { display: 'none' },
|
|
76
|
+
},
|
|
77
|
+
lg: {
|
|
78
|
+
details: { ms: 3 },
|
|
79
|
+
label: { fontSize: 'md' },
|
|
80
|
+
secondaryLabel: { fontSize: 'sm' },
|
|
81
|
+
tertiaryLabel: { fontSize: 'sm' },
|
|
82
|
+
},
|
|
83
|
+
xl: {
|
|
84
|
+
details: { ms: 4 },
|
|
85
|
+
label: { fontSize: 'lg' },
|
|
86
|
+
secondaryLabel: { fontSize: 'md' },
|
|
87
|
+
tertiaryLabel: { fontSize: 'md' },
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
defaultVariants: {
|
|
92
|
+
size: 'md',
|
|
93
|
+
},
|
|
94
|
+
})
|