sparkdesign 0.4.8 → 0.4.10
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/AGENT_COMPONENT_LIBRARY_QUICKREF.md +117 -0
- package/AI_README.md +7 -2
- package/README.md +4 -1
- package/cli/registry/AGENTS.md +1 -1
- package/cli/registry/agent-manifest.json +4040 -67
- package/cli/registry/basic/accordion.tsx +79 -0
- package/cli/registry/basic/badge.tsx +49 -0
- package/cli/registry/basic/button.tsx +19 -14
- package/cli/registry/basic/calendar.tsx +16 -16
- package/cli/registry/basic/collapsible-card.tsx +10 -1
- package/cli/registry/basic/combobox.tsx +11 -2
- package/cli/registry/basic/date-picker.tsx +3 -2
- package/cli/registry/basic/ellipsis-text.tsx +151 -0
- package/cli/registry/basic/form.tsx +186 -0
- package/cli/registry/basic/icon-button.tsx +12 -4
- package/cli/registry/basic/popover.tsx +19 -2
- package/cli/registry/basic/rating.tsx +161 -0
- package/cli/registry/basic/sidebar.tsx +665 -0
- package/cli/registry/basic/sonner.tsx +10 -10
- package/cli/registry/basic/stepper.tsx +163 -0
- package/cli/registry/basic/timeline.tsx +129 -0
- package/cli/registry/chat/chat-input/compound.tsx +1 -0
- package/cli/registry/chat/permission-card.tsx +1 -1
- package/cli/registry/chat/user-question/compound.tsx +2 -0
- package/cli/registry/meta.json +171 -13
- package/dist/registry/basic/accordion.d.ts +15 -0
- package/dist/registry/basic/badge.d.ts +23 -0
- package/dist/registry/basic/calendar.d.ts +1 -1
- package/dist/registry/basic/combobox.d.ts +2 -1
- package/dist/registry/basic/date-picker.d.ts +2 -2
- package/dist/registry/basic/ellipsis-text.d.ts +45 -0
- package/dist/registry/basic/form.d.ts +23 -0
- package/dist/registry/basic/icon-button.d.ts +15 -2
- package/dist/registry/basic/item.d.ts +1 -1
- package/dist/registry/basic/popover.d.ts +2 -0
- package/dist/registry/basic/rating.d.ts +31 -0
- package/dist/registry/basic/sidebar.d.ts +72 -0
- package/dist/registry/basic/stepper.d.ts +36 -0
- package/dist/registry/basic/tag.d.ts +1 -1
- package/dist/registry/basic/timeline.d.ts +34 -0
- package/dist/spark-design.cjs.js +27 -30
- package/dist/spark-design.es.js +6398 -5130
- package/dist/sparkdesign.css +1 -1
- package/dist/src/components/basic/Accordion/index.d.ts +13 -0
- package/dist/src/components/basic/Badge/index.d.ts +13 -0
- package/dist/src/components/basic/EllipsisText/index.d.ts +4 -36
- package/dist/src/components/basic/Form/index.d.ts +12 -0
- package/dist/src/components/basic/Rating/index.d.ts +13 -0
- package/dist/src/components/basic/Sidebar/index.d.ts +13 -0
- package/dist/src/components/basic/Stepper/index.d.ts +13 -0
- package/dist/src/components/basic/Timeline/index.d.ts +13 -0
- package/dist/src/components/index.d.ts +12 -4
- package/docs/agent/component-selection.md +104 -4
- package/docs/agent/prompt-recipes.md +167 -0
- package/docs/guides/agent-usage.md +213 -0
- package/docs/guides/system-operating-model.md +148 -0
- package/package.json +20 -3
- package/registry/agent-manifest.json +4040 -67
- package/cli/registry/basic/sheet.tsx +0 -18
- package/cli/registry/chat/user-question/UserQuestionCard.tsx +0 -198
- package/cli/registry/chat/user-question/UserQuestionFooter.tsx +0 -66
- package/cli/registry/chat/user-question/UserQuestionHeader.tsx +0 -64
- package/cli/registry/chat/user-question/useUserQuestionState.ts +0 -165
- package/dist/registry/basic/sheet.d.ts +0 -13
- package/dist/registry/chat/user-question/UserQuestionCard.d.ts +0 -36
- package/dist/registry/chat/user-question/UserQuestionFooter.d.ts +0 -24
- package/dist/registry/chat/user-question/UserQuestionHeader.d.ts +0 -26
- package/dist/registry/chat/user-question/useUserQuestionState.d.ts +0 -26
- package/dist/src/components/basic/CollapsibleSection/index.d.ts +0 -43
- package/dist/src/components/basic/Sheet/index.d.ts +0 -13
- package/dist/src/components/chat/Response/StreamingMarkdownBlock.d.ts +0 -12
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as LabelPrimitive from "@radix-ui/react-label"
|
|
5
|
+
import { Slot } from "@radix-ui/react-slot"
|
|
6
|
+
import {
|
|
7
|
+
Controller,
|
|
8
|
+
FormProvider,
|
|
9
|
+
useFormContext,
|
|
10
|
+
type ControllerProps,
|
|
11
|
+
type FieldPath,
|
|
12
|
+
type FieldValues,
|
|
13
|
+
} from "react-hook-form"
|
|
14
|
+
|
|
15
|
+
import { cn } from "@/lib/utils"
|
|
16
|
+
import { Label } from "./label"
|
|
17
|
+
|
|
18
|
+
const Form = FormProvider
|
|
19
|
+
|
|
20
|
+
type FormFieldContextValue<
|
|
21
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
22
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
23
|
+
> = {
|
|
24
|
+
name: TName
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const FormFieldContext = React.createContext<FormFieldContextValue>(
|
|
28
|
+
{} as FormFieldContextValue
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
function FormField<
|
|
32
|
+
TFieldValues extends FieldValues = FieldValues,
|
|
33
|
+
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
|
|
34
|
+
>({ ...props }: ControllerProps<TFieldValues, TName>) {
|
|
35
|
+
return (
|
|
36
|
+
<FormFieldContext.Provider value={{ name: props.name }}>
|
|
37
|
+
<Controller {...props} />
|
|
38
|
+
</FormFieldContext.Provider>
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
type FormItemContextValue = {
|
|
43
|
+
id: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const FormItemContext = React.createContext<FormItemContextValue>(
|
|
47
|
+
{} as FormItemContextValue
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
function useFormField() {
|
|
51
|
+
const fieldContext = React.useContext(FormFieldContext)
|
|
52
|
+
const itemContext = React.useContext(FormItemContext)
|
|
53
|
+
const { getFieldState, formState } = useFormContext()
|
|
54
|
+
|
|
55
|
+
if (!fieldContext.name) {
|
|
56
|
+
throw new Error("useFormField should be used within <FormField>")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const fieldState = getFieldState(fieldContext.name, formState)
|
|
60
|
+
|
|
61
|
+
const { id } = itemContext
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
id,
|
|
65
|
+
name: fieldContext.name,
|
|
66
|
+
formItemId: `${id}-form-item`,
|
|
67
|
+
formDescriptionId: `${id}-form-item-description`,
|
|
68
|
+
formMessageId: `${id}-form-item-message`,
|
|
69
|
+
...fieldState,
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const FormItem = React.forwardRef<
|
|
74
|
+
HTMLDivElement,
|
|
75
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
76
|
+
>(({ className, ...props }, ref) => {
|
|
77
|
+
const id = React.useId()
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<FormItemContext.Provider value={{ id }}>
|
|
81
|
+
<div
|
|
82
|
+
ref={ref}
|
|
83
|
+
data-slot="form-item"
|
|
84
|
+
className={cn("space-y-2", className)}
|
|
85
|
+
{...props}
|
|
86
|
+
/>
|
|
87
|
+
</FormItemContext.Provider>
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
FormItem.displayName = "FormItem"
|
|
91
|
+
|
|
92
|
+
const FormLabel = React.forwardRef<
|
|
93
|
+
React.ElementRef<typeof LabelPrimitive.Root>,
|
|
94
|
+
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
|
|
95
|
+
>(({ className, ...props }, ref) => {
|
|
96
|
+
const { error, formItemId } = useFormField()
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<Label
|
|
100
|
+
ref={ref}
|
|
101
|
+
data-slot="form-label"
|
|
102
|
+
data-error={!!error}
|
|
103
|
+
className={cn("data-[error=true]:text-error", className)}
|
|
104
|
+
htmlFor={formItemId}
|
|
105
|
+
{...props}
|
|
106
|
+
/>
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
FormLabel.displayName = "FormLabel"
|
|
110
|
+
|
|
111
|
+
const FormControl = React.forwardRef<
|
|
112
|
+
React.ElementRef<typeof Slot>,
|
|
113
|
+
React.ComponentPropsWithoutRef<typeof Slot>
|
|
114
|
+
>(({ ...props }, ref) => {
|
|
115
|
+
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<Slot
|
|
119
|
+
ref={ref}
|
|
120
|
+
data-slot="form-control"
|
|
121
|
+
id={formItemId}
|
|
122
|
+
aria-describedby={
|
|
123
|
+
!error
|
|
124
|
+
? `${formDescriptionId}`
|
|
125
|
+
: `${formDescriptionId} ${formMessageId}`
|
|
126
|
+
}
|
|
127
|
+
aria-invalid={!!error}
|
|
128
|
+
{...props}
|
|
129
|
+
/>
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
FormControl.displayName = "FormControl"
|
|
133
|
+
|
|
134
|
+
const FormDescription = React.forwardRef<
|
|
135
|
+
HTMLParagraphElement,
|
|
136
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
137
|
+
>(({ className, ...props }, ref) => {
|
|
138
|
+
const { formDescriptionId } = useFormField()
|
|
139
|
+
|
|
140
|
+
return (
|
|
141
|
+
<p
|
|
142
|
+
ref={ref}
|
|
143
|
+
id={formDescriptionId}
|
|
144
|
+
data-slot="form-description"
|
|
145
|
+
className={cn("text-sm text-text-tertiary", className)}
|
|
146
|
+
{...props}
|
|
147
|
+
/>
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
FormDescription.displayName = "FormDescription"
|
|
151
|
+
|
|
152
|
+
const FormMessage = React.forwardRef<
|
|
153
|
+
HTMLParagraphElement,
|
|
154
|
+
React.HTMLAttributes<HTMLParagraphElement>
|
|
155
|
+
>(({ className, children, ...props }, ref) => {
|
|
156
|
+
const { error, formMessageId } = useFormField()
|
|
157
|
+
const body = error ? String(error?.message ?? "") : children
|
|
158
|
+
|
|
159
|
+
if (!body) {
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<p
|
|
165
|
+
ref={ref}
|
|
166
|
+
id={formMessageId}
|
|
167
|
+
data-slot="form-message"
|
|
168
|
+
className={cn("text-sm font-medium text-error", className)}
|
|
169
|
+
{...props}
|
|
170
|
+
>
|
|
171
|
+
{body}
|
|
172
|
+
</p>
|
|
173
|
+
)
|
|
174
|
+
})
|
|
175
|
+
FormMessage.displayName = "FormMessage"
|
|
176
|
+
|
|
177
|
+
export {
|
|
178
|
+
useFormField,
|
|
179
|
+
Form,
|
|
180
|
+
FormItem,
|
|
181
|
+
FormLabel,
|
|
182
|
+
FormControl,
|
|
183
|
+
FormDescription,
|
|
184
|
+
FormMessage,
|
|
185
|
+
FormField,
|
|
186
|
+
}
|
|
@@ -5,7 +5,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
|
|
|
5
5
|
import { cn } from '@/lib/utils'
|
|
6
6
|
|
|
7
7
|
const iconButtonVariants = cva(
|
|
8
|
-
'flex-none shrink-0 inline-flex items-center justify-center box-border transition-[
|
|
8
|
+
'flex-none shrink-0 inline-flex items-center justify-center box-border transition-[color,background-color,border-color,box-shadow,transform] duration-200 active:scale-[0.98] focus:outline-none disabled:opacity-50 disabled:cursor-not-allowed cursor-pointer [&>*]:flex [&>*]:items-center [&>*]:justify-center [&>*]:size-full [&>*]:[&>svg]:block [&>*]:[&>svg]:leading-none',
|
|
9
9
|
{
|
|
10
10
|
variants: {
|
|
11
11
|
variant: {
|
|
@@ -34,9 +34,17 @@ const iconButtonVariants = cva(
|
|
|
34
34
|
}
|
|
35
35
|
)
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
type IconButtonAccessibleName =
|
|
38
|
+
| { 'aria-label': string; 'aria-labelledby'?: string; title?: string }
|
|
39
|
+
| { 'aria-label'?: string; 'aria-labelledby': string; title?: string }
|
|
40
|
+
| { 'aria-label'?: string; 'aria-labelledby'?: string; title: string }
|
|
41
|
+
|
|
42
|
+
export type IconButtonProps = Omit<
|
|
43
|
+
ButtonHTMLAttributes<HTMLButtonElement>,
|
|
44
|
+
'aria-label' | 'aria-labelledby' | 'title'
|
|
45
|
+
> &
|
|
46
|
+
VariantProps<typeof iconButtonVariants> &
|
|
47
|
+
IconButtonAccessibleName & {
|
|
40
48
|
icon: ReactNode
|
|
41
49
|
asChild?: boolean
|
|
42
50
|
}
|
|
@@ -14,6 +14,8 @@ export interface PopoverContentProps
|
|
|
14
14
|
/** When provided (e.g. from ThemeStyleProvider), used for portal wrapper */
|
|
15
15
|
dataStyle?: string
|
|
16
16
|
dataTheme?: string
|
|
17
|
+
/** Popover internal padding; keep default to avoid breaking existing usages */
|
|
18
|
+
contentPadding?: 'default' | 'compact' | 'none'
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const PopoverContent = React.forwardRef<
|
|
@@ -21,7 +23,16 @@ const PopoverContent = React.forwardRef<
|
|
|
21
23
|
PopoverContentProps
|
|
22
24
|
>(
|
|
23
25
|
(
|
|
24
|
-
{
|
|
26
|
+
{
|
|
27
|
+
className,
|
|
28
|
+
align = 'center',
|
|
29
|
+
sideOffset = 4,
|
|
30
|
+
contentPadding = 'default',
|
|
31
|
+
children,
|
|
32
|
+
dataStyle,
|
|
33
|
+
dataTheme,
|
|
34
|
+
...props
|
|
35
|
+
},
|
|
25
36
|
ref,
|
|
26
37
|
) => {
|
|
27
38
|
const fromDoc = getThemeFromDocument()
|
|
@@ -34,10 +45,16 @@ const PopoverContent = React.forwardRef<
|
|
|
34
45
|
<div style={{ display: 'contents' }} {...dataProps}>
|
|
35
46
|
<PopoverPrimitive.Content
|
|
36
47
|
ref={ref}
|
|
48
|
+
data-slot="popover-content"
|
|
37
49
|
align={align}
|
|
38
50
|
sideOffset={sideOffset}
|
|
39
51
|
className={cn(
|
|
40
|
-
'z-50 w-72 rounded-lg border border-border-tertiary bg-bg-container
|
|
52
|
+
'z-50 w-72 rounded-lg border border-border-tertiary bg-bg-container text-sm text-text shadow-md outline-none',
|
|
53
|
+
contentPadding === 'none'
|
|
54
|
+
? 'p-0'
|
|
55
|
+
: contentPadding === 'compact'
|
|
56
|
+
? 'p-1'
|
|
57
|
+
: 'p-4',
|
|
41
58
|
className,
|
|
42
59
|
)}
|
|
43
60
|
{...props}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
3
|
+
|
|
4
|
+
import { cn } from '@/lib/utils'
|
|
5
|
+
|
|
6
|
+
const ratingVariants = cva('inline-flex items-center gap-1', {
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
sm: '[--rating-size:1rem]',
|
|
10
|
+
md: '[--rating-size:1.25rem]',
|
|
11
|
+
lg: '[--rating-size:1.5rem]',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
size: 'md',
|
|
16
|
+
},
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
type RatingPrecision = 1 | 0.5
|
|
20
|
+
|
|
21
|
+
export interface RatingProps
|
|
22
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'>,
|
|
23
|
+
VariantProps<typeof ratingVariants> {
|
|
24
|
+
value?: number
|
|
25
|
+
defaultValue?: number
|
|
26
|
+
onValueChange?: (value: number) => void
|
|
27
|
+
max?: number
|
|
28
|
+
precision?: RatingPrecision
|
|
29
|
+
readOnly?: boolean
|
|
30
|
+
disabled?: boolean
|
|
31
|
+
showValue?: boolean
|
|
32
|
+
label?: string
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function clampRating(value: number, max: number, precision: RatingPrecision) {
|
|
36
|
+
const normalized = Math.max(0, Math.min(max, value))
|
|
37
|
+
return Math.round(normalized / precision) * precision
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function RatingStar({ className }: { className?: string }) {
|
|
41
|
+
return (
|
|
42
|
+
<svg
|
|
43
|
+
aria-hidden="true"
|
|
44
|
+
viewBox="0 0 20 20"
|
|
45
|
+
className={cn('size-[var(--rating-size)]', className)}
|
|
46
|
+
>
|
|
47
|
+
<path d="M10 1.7l2.3 4.7 5.2.8-3.8 3.7.9 5.2-4.6-2.5-4.6 2.5.9-5.2-3.8-3.7 5.2-.8L10 1.7z" />
|
|
48
|
+
</svg>
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const Rating = React.forwardRef<HTMLDivElement, RatingProps>(
|
|
53
|
+
(
|
|
54
|
+
{
|
|
55
|
+
className,
|
|
56
|
+
size,
|
|
57
|
+
value,
|
|
58
|
+
defaultValue = 0,
|
|
59
|
+
onValueChange,
|
|
60
|
+
max = 5,
|
|
61
|
+
precision = 1,
|
|
62
|
+
readOnly = false,
|
|
63
|
+
disabled = false,
|
|
64
|
+
showValue = false,
|
|
65
|
+
label = 'Rating',
|
|
66
|
+
...props
|
|
67
|
+
},
|
|
68
|
+
ref
|
|
69
|
+
) => {
|
|
70
|
+
const [internalValue, setInternalValue] = React.useState(defaultValue)
|
|
71
|
+
const currentValue = clampRating(value ?? internalValue, max, precision)
|
|
72
|
+
const interactive = !readOnly && !disabled
|
|
73
|
+
|
|
74
|
+
const commitValue = (nextValue: number) => {
|
|
75
|
+
const next = clampRating(nextValue, max, precision)
|
|
76
|
+
if (value == null) setInternalValue(next)
|
|
77
|
+
onValueChange?.(next)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<div
|
|
82
|
+
ref={ref}
|
|
83
|
+
role={interactive ? 'radiogroup' : 'img'}
|
|
84
|
+
aria-label={label}
|
|
85
|
+
aria-disabled={disabled || undefined}
|
|
86
|
+
aria-readonly={readOnly || undefined}
|
|
87
|
+
aria-valuenow={!interactive ? currentValue : undefined}
|
|
88
|
+
aria-valuemax={!interactive ? max : undefined}
|
|
89
|
+
data-slot="rating"
|
|
90
|
+
data-disabled={disabled || undefined}
|
|
91
|
+
className={cn(
|
|
92
|
+
ratingVariants({ size }),
|
|
93
|
+
disabled && 'cursor-not-allowed opacity-50',
|
|
94
|
+
className
|
|
95
|
+
)}
|
|
96
|
+
{...props}
|
|
97
|
+
>
|
|
98
|
+
{Array.from({ length: max }, (_, index) => {
|
|
99
|
+
const starValue = index + 1
|
|
100
|
+
const fillRatio = Math.max(0, Math.min(1, currentValue - index))
|
|
101
|
+
const halfValue = starValue - 0.5
|
|
102
|
+
return (
|
|
103
|
+
<span
|
|
104
|
+
key={starValue}
|
|
105
|
+
className="relative inline-flex size-[var(--rating-size)] shrink-0 text-border-tertiary"
|
|
106
|
+
>
|
|
107
|
+
<RatingStar className="fill-current" />
|
|
108
|
+
<span
|
|
109
|
+
aria-hidden="true"
|
|
110
|
+
className="absolute inset-0 overflow-hidden text-warning"
|
|
111
|
+
style={{ width: `${fillRatio * 100}%` }}
|
|
112
|
+
>
|
|
113
|
+
<RatingStar className="fill-current" />
|
|
114
|
+
</span>
|
|
115
|
+
{interactive ? (
|
|
116
|
+
precision === 0.5 ? (
|
|
117
|
+
<span className="absolute inset-0 grid grid-cols-2">
|
|
118
|
+
<button
|
|
119
|
+
type="button"
|
|
120
|
+
role="radio"
|
|
121
|
+
aria-label={`${halfValue} of ${max}`}
|
|
122
|
+
aria-checked={currentValue === halfValue}
|
|
123
|
+
className="cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-border"
|
|
124
|
+
onClick={() => commitValue(halfValue)}
|
|
125
|
+
/>
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
role="radio"
|
|
129
|
+
aria-label={`${starValue} of ${max}`}
|
|
130
|
+
aria-checked={currentValue === starValue}
|
|
131
|
+
className="cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-border"
|
|
132
|
+
onClick={() => commitValue(starValue)}
|
|
133
|
+
/>
|
|
134
|
+
</span>
|
|
135
|
+
) : (
|
|
136
|
+
<button
|
|
137
|
+
type="button"
|
|
138
|
+
role="radio"
|
|
139
|
+
aria-label={`${starValue} of ${max}`}
|
|
140
|
+
aria-checked={currentValue === starValue}
|
|
141
|
+
className="absolute inset-0 cursor-pointer focus:outline-none focus-visible:ring-2 focus-visible:ring-primary-border"
|
|
142
|
+
onClick={() => commitValue(starValue)}
|
|
143
|
+
/>
|
|
144
|
+
)
|
|
145
|
+
) : null}
|
|
146
|
+
</span>
|
|
147
|
+
)
|
|
148
|
+
})}
|
|
149
|
+
{showValue ? (
|
|
150
|
+
<span className="ml-1 text-sm font-medium text-text-secondary">
|
|
151
|
+
{currentValue.toFixed(precision === 0.5 && currentValue % 1 ? 1 : 0)}
|
|
152
|
+
</span>
|
|
153
|
+
) : null}
|
|
154
|
+
</div>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
Rating.displayName = 'Rating'
|
|
159
|
+
|
|
160
|
+
export { Rating, ratingVariants }
|
|
161
|
+
export type { RatingPrecision }
|