@fanvue/ui 3.15.0 → 3.16.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/dist/cjs/components/AudioRecordButton/AudioRecordButton.cjs +54 -0
- package/dist/cjs/components/AudioRecordButton/AudioRecordButton.cjs.map +1 -0
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs +5 -5
- package/dist/cjs/components/AudioUpload/AudioUpload.cjs.map +1 -1
- package/dist/cjs/components/ChatInput/ChatInput.cjs +6 -11
- package/dist/cjs/components/ChatInput/ChatInput.cjs.map +1 -1
- package/dist/cjs/components/ChatMessage/ChatMessage.cjs +228 -0
- package/dist/cjs/components/ChatMessage/ChatMessage.cjs.map +1 -0
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs +160 -71
- package/dist/cjs/components/DropdownMenu/DropdownMenu.cjs.map +1 -1
- package/dist/cjs/components/Icons/DenseGridViewIcon.cjs +52 -0
- package/dist/cjs/components/Icons/DenseGridViewIcon.cjs.map +1 -0
- package/dist/cjs/components/MediaStatusIndicator/MediaStatusIndicator.cjs +66 -0
- package/dist/cjs/components/MediaStatusIndicator/MediaStatusIndicator.cjs.map +1 -0
- package/dist/cjs/components/Select/Select.cjs +61 -17
- package/dist/cjs/components/Select/Select.cjs.map +1 -1
- package/dist/cjs/components/SubscribeButton/SubscribeButton.cjs +53 -0
- package/dist/cjs/components/SubscribeButton/SubscribeButton.cjs.map +1 -0
- package/dist/cjs/index.cjs +10 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/components/AudioRecordButton/AudioRecordButton.mjs +37 -0
- package/dist/components/AudioRecordButton/AudioRecordButton.mjs.map +1 -0
- package/dist/components/AudioUpload/AudioUpload.mjs +5 -5
- package/dist/components/AudioUpload/AudioUpload.mjs.map +1 -1
- package/dist/components/ChatInput/ChatInput.mjs +6 -11
- package/dist/components/ChatInput/ChatInput.mjs.map +1 -1
- package/dist/components/ChatMessage/ChatMessage.mjs +211 -0
- package/dist/components/ChatMessage/ChatMessage.mjs.map +1 -0
- package/dist/components/DropdownMenu/DropdownMenu.mjs +161 -72
- package/dist/components/DropdownMenu/DropdownMenu.mjs.map +1 -1
- package/dist/components/Icons/DenseGridViewIcon.mjs +35 -0
- package/dist/components/Icons/DenseGridViewIcon.mjs.map +1 -0
- package/dist/components/MediaStatusIndicator/MediaStatusIndicator.mjs +49 -0
- package/dist/components/MediaStatusIndicator/MediaStatusIndicator.mjs.map +1 -0
- package/dist/components/Select/Select.mjs +61 -17
- package/dist/components/Select/Select.mjs.map +1 -1
- package/dist/components/SubscribeButton/SubscribeButton.mjs +36 -0
- package/dist/components/SubscribeButton/SubscribeButton.mjs.map +1 -0
- package/dist/index.d.ts +245 -4
- package/dist/index.mjs +10 -0
- package/dist/index.mjs.map +1 -1
- package/dist/styles/theme.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.mjs";
|
|
5
|
+
import { CloseIcon } from "../Icons/CloseIcon.mjs";
|
|
6
|
+
import { EyeOffIcon } from "../Icons/EyeOffIcon.mjs";
|
|
7
|
+
import { FlagIcon } from "../Icons/FlagIcon.mjs";
|
|
8
|
+
const STATUS_CONFIG = {
|
|
9
|
+
default: {
|
|
10
|
+
container: "bg-warning-surface text-warning-content",
|
|
11
|
+
icon: /* @__PURE__ */ jsx(FlagIcon, { size: 16, filled: true }),
|
|
12
|
+
label: "Flagged"
|
|
13
|
+
},
|
|
14
|
+
removed: {
|
|
15
|
+
container: "border border-error-secondary bg-error-negative-content text-content-always-white",
|
|
16
|
+
icon: /* @__PURE__ */ jsx(CloseIcon, { size: 16 }),
|
|
17
|
+
label: "Removed"
|
|
18
|
+
},
|
|
19
|
+
sensitive: {
|
|
20
|
+
container: "bg-buttons-overlay-default text-content-always-white",
|
|
21
|
+
icon: /* @__PURE__ */ jsx(EyeOffIcon, { size: 16, filled: true }),
|
|
22
|
+
label: "Sensitive content"
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const MediaStatusIndicator = React.forwardRef(
|
|
26
|
+
({ status = "default", label, className, ...props }, ref) => {
|
|
27
|
+
const config = STATUS_CONFIG[status];
|
|
28
|
+
return /* @__PURE__ */ jsx(
|
|
29
|
+
"span",
|
|
30
|
+
{
|
|
31
|
+
ref,
|
|
32
|
+
role: "img",
|
|
33
|
+
"aria-label": props["aria-label"] ?? label ?? config.label,
|
|
34
|
+
className: cn(
|
|
35
|
+
"inline-flex items-center justify-center rounded-full p-2 [&>svg]:size-4",
|
|
36
|
+
config.container,
|
|
37
|
+
className
|
|
38
|
+
),
|
|
39
|
+
...props,
|
|
40
|
+
children: config.icon
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
MediaStatusIndicator.displayName = "MediaStatusIndicator";
|
|
46
|
+
export {
|
|
47
|
+
MediaStatusIndicator
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=MediaStatusIndicator.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MediaStatusIndicator.mjs","sources":["../../../src/components/MediaStatusIndicator/MediaStatusIndicator.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { CloseIcon } from \"../Icons/CloseIcon\";\nimport { EyeOffIcon } from \"../Icons/EyeOffIcon\";\nimport { FlagIcon } from \"../Icons/FlagIcon\";\n\n/**\n * State represented by the indicator.\n * - `\"default\"`: media flagged / pending review (amber).\n * - `\"removed\"`: media removed for a policy violation (red).\n * - `\"sensitive\"`: sensitive content hidden behind an overlay (dark).\n */\nexport type MediaStatusIndicatorStatus = \"default\" | \"removed\" | \"sensitive\";\n\nexport interface MediaStatusIndicatorProps\n extends Omit<React.HTMLAttributes<HTMLSpanElement>, \"children\"> {\n /** Which media state to represent. @default \"default\" */\n status?: MediaStatusIndicatorStatus;\n /**\n * Accessible label announced by assistive tech. Defaults to a per-status\n * description; pass your own for localisation or extra context.\n */\n label?: string;\n}\n\ntype StatusConfig = { container: string; icon: React.ReactNode; label: string };\n\nconst STATUS_CONFIG: Record<MediaStatusIndicatorStatus, StatusConfig> = {\n default: {\n container: \"bg-warning-surface text-warning-content\",\n icon: <FlagIcon size={16} filled />,\n label: \"Flagged\",\n },\n removed: {\n container: \"border border-error-secondary bg-error-negative-content text-content-always-white\",\n icon: <CloseIcon size={16} />,\n label: \"Removed\",\n },\n sensitive: {\n container: \"bg-buttons-overlay-default text-content-always-white\",\n icon: <EyeOffIcon size={16} filled />,\n label: \"Sensitive content\",\n },\n};\n\n/**\n * A compact circular indicator that surfaces the moderation state of a piece of\n * media — flagged, removed, or sensitive. Typically overlaid on a thumbnail or\n * attachment.\n *\n * Renders as `role=\"img\"` with a descriptive `aria-label`; the inner glyph is\n * decorative.\n *\n * @example\n * ```tsx\n * <MediaStatusIndicator status=\"sensitive\" />\n * <MediaStatusIndicator status=\"removed\" label=\"Removed for violating guidelines\" />\n * ```\n */\nexport const MediaStatusIndicator = React.forwardRef<HTMLSpanElement, MediaStatusIndicatorProps>(\n ({ status = \"default\", label, className, ...props }, ref) => {\n const config = STATUS_CONFIG[status];\n return (\n <span\n ref={ref}\n role=\"img\"\n aria-label={props[\"aria-label\"] ?? label ?? config.label}\n className={cn(\n \"inline-flex items-center justify-center rounded-full p-2 [&>svg]:size-4\",\n config.container,\n className,\n )}\n {...props}\n >\n {config.icon}\n </span>\n );\n },\n);\n\nMediaStatusIndicator.displayName = \"MediaStatusIndicator\";\n"],"names":[],"mappings":";;;;;;;AA2BA,MAAM,gBAAkE;AAAA,EACtE,SAAS;AAAA,IACP,WAAW;AAAA,IACX,MAAM,oBAAC,UAAA,EAAS,MAAM,IAAI,QAAM,MAAC;AAAA,IACjC,OAAO;AAAA,EAAA;AAAA,EAET,SAAS;AAAA,IACP,WAAW;AAAA,IACX,MAAM,oBAAC,WAAA,EAAU,MAAM,GAAA,CAAI;AAAA,IAC3B,OAAO;AAAA,EAAA;AAAA,EAET,WAAW;AAAA,IACT,WAAW;AAAA,IACX,MAAM,oBAAC,YAAA,EAAW,MAAM,IAAI,QAAM,MAAC;AAAA,IACnC,OAAO;AAAA,EAAA;AAEX;AAgBO,MAAM,uBAAuB,MAAM;AAAA,EACxC,CAAC,EAAE,SAAS,WAAW,OAAO,WAAW,GAAG,MAAA,GAAS,QAAQ;AAC3D,UAAM,SAAS,cAAc,MAAM;AACnC,WACE;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,MAAK;AAAA,QACL,cAAY,MAAM,YAAY,KAAK,SAAS,OAAO;AAAA,QACnD,WAAW;AAAA,UACT;AAAA,UACA,OAAO;AAAA,UACP;AAAA,QAAA;AAAA,QAED,GAAG;AAAA,QAEH,UAAA,OAAO;AAAA,MAAA;AAAA,IAAA;AAAA,EAGd;AACF;AAEA,qBAAqB,cAAc;"}
|
|
@@ -78,7 +78,7 @@ const Select = React.forwardRef(
|
|
|
78
78
|
"aria-describedby": bottomText ? helperTextId : void 0,
|
|
79
79
|
"aria-invalid": error || void 0,
|
|
80
80
|
className: cn(
|
|
81
|
-
"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-inputs-inputs-primary outline-none
|
|
81
|
+
"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-inputs-inputs-primary outline-none focus-visible:shadow-focus-ring motion-safe:transition-colors",
|
|
82
82
|
TRIGGER_HEIGHT[size],
|
|
83
83
|
TRIGGER_PADDING_X[size],
|
|
84
84
|
TRIGGER_GAP[size],
|
|
@@ -153,22 +153,66 @@ const SelectContent = React.forwardRef(
|
|
|
153
153
|
) })
|
|
154
154
|
);
|
|
155
155
|
SelectContent.displayName = "SelectContent";
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
)
|
|
156
|
+
const ITEM_SIZE_CLASSES = {
|
|
157
|
+
"40": "min-h-10 py-2 typography-body-default-16px-regular",
|
|
158
|
+
"32": "min-h-8 py-[7px] typography-body-small-14px-regular"
|
|
159
|
+
};
|
|
160
|
+
const ITEM_DESCRIPTION_TYPOGRAPHY = {
|
|
161
|
+
"40": "typography-body-small-14px-regular",
|
|
162
|
+
"32": "typography-description-12px-regular"
|
|
163
|
+
};
|
|
164
|
+
function isRenderableNode(node) {
|
|
165
|
+
return node !== null && node !== void 0 && typeof node !== "boolean";
|
|
166
|
+
}
|
|
167
|
+
const SelectItem = React.forwardRef(({ className, children, size, leadingIcon, avatar, description, ...props }, ref) => {
|
|
168
|
+
const { size: triggerSize } = React.useContext(SelectContext);
|
|
169
|
+
const itemSize = size ?? (triggerSize === "32" ? "32" : "40");
|
|
170
|
+
const hasDescription = isRenderableNode(description);
|
|
171
|
+
const hasAvatar = isRenderableNode(avatar);
|
|
172
|
+
const hasLeadingIcon = isRenderableNode(leadingIcon);
|
|
173
|
+
return /* @__PURE__ */ jsxs(
|
|
174
|
+
SelectPrimitive.Item,
|
|
175
|
+
{
|
|
176
|
+
ref,
|
|
177
|
+
className: cn(
|
|
178
|
+
"group relative flex w-full cursor-pointer select-none gap-2 rounded-xs px-3 text-content-primary outline-none",
|
|
179
|
+
hasDescription ? "items-start" : "items-center",
|
|
180
|
+
ITEM_SIZE_CLASSES[itemSize],
|
|
181
|
+
hasAvatar && !hasDescription && itemSize === "32" && "py-1",
|
|
182
|
+
"focus:bg-neutral-alphas-50 data-highlighted:bg-neutral-alphas-50",
|
|
183
|
+
"data-disabled:pointer-events-none data-disabled:text-content-disabled",
|
|
184
|
+
className
|
|
185
|
+
),
|
|
186
|
+
...props,
|
|
187
|
+
children: [
|
|
188
|
+
hasAvatar ? /* @__PURE__ */ jsx("span", { className: "shrink-0", children: avatar }) : hasLeadingIcon && (hasDescription ? /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center pt-0.5 [&>svg]:size-4", children: leadingIcon }) : /* @__PURE__ */ jsx("span", { className: "flex shrink-0 items-center [&>svg]:size-4", children: leadingIcon })),
|
|
189
|
+
hasDescription ? /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
190
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }) }),
|
|
191
|
+
/* @__PURE__ */ jsx(
|
|
192
|
+
"span",
|
|
193
|
+
{
|
|
194
|
+
className: cn(
|
|
195
|
+
"truncate text-content-secondary group-data-[disabled]:text-content-disabled",
|
|
196
|
+
ITEM_DESCRIPTION_TYPOGRAPHY[itemSize]
|
|
197
|
+
),
|
|
198
|
+
children: description
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
] }) : /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }) }),
|
|
202
|
+
/* @__PURE__ */ jsx(
|
|
203
|
+
SelectPrimitive.ItemIndicator,
|
|
204
|
+
{
|
|
205
|
+
className: cn(
|
|
206
|
+
"ml-auto flex size-4 shrink-0 items-center justify-center",
|
|
207
|
+
hasDescription && "self-start"
|
|
208
|
+
),
|
|
209
|
+
children: /* @__PURE__ */ jsx(CheckIcon, { className: "size-4 text-content-primary group-data-[disabled]:text-content-disabled" })
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
);
|
|
215
|
+
});
|
|
172
216
|
SelectItem.displayName = "SelectItem";
|
|
173
217
|
const SelectGroup = SelectPrimitive.Group;
|
|
174
218
|
SelectGroup.displayName = "SelectGroup";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Select.mjs","sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Select field height in pixels. */\nexport type SelectSize = \"48\" | \"40\" | \"32\";\n\ntype SelectContextValue = {\n size: SelectSize;\n error: boolean;\n disabled?: boolean;\n};\n\nconst SelectContext = React.createContext<SelectContextValue>({\n size: \"48\",\n error: false,\n});\n\nconst TRIGGER_HEIGHT: Record<SelectSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst TRIGGER_PADDING_X: Record<SelectSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst TRIGGER_GAP: Record<SelectSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nconst TRIGGER_TYPOGRAPHY: Record<SelectSize, string> = {\n \"48\": \"typography-body-default-16px-regular\",\n \"40\": \"typography-body-default-16px-regular\",\n \"32\": \"typography-body-small-14px-regular\",\n};\n\nexport interface SelectProps extends Omit<SelectPrimitive.SelectProps, \"dir\"> {\n /** Label text displayed above the trigger. Also used as the accessible name. */\n label?: string;\n /** Accessible name applied directly to the trigger button when no visible `label` is provided. */\n \"aria-label\"?: string;\n /** ID of an external element that labels the trigger button. */\n \"aria-labelledby\"?: string;\n /** Helper text displayed below the trigger. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the select field in pixels. @default \"48\" */\n size?: SelectSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the trigger. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Placeholder shown when no value is selected. */\n placeholder?: string;\n /** Icon element displayed at the left side of the trigger. */\n leftIcon?: React.ReactNode;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Wraps the `className` of the outermost container div. */\n className?: string;\n /** HTML `id` applied to the trigger button. Auto-generated if omitted. */\n id?: string;\n}\n\n/**\n * A select field with optional label, helper/error text, and an icon slot,\n * built on Radix UI Select for full accessibility and keyboard navigation.\n *\n * Pair with {@link SelectContent} and {@link SelectItem} to provide options.\n *\n * @example\n * ```tsx\n * <Select label=\"Country\" placeholder=\"Select a country\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"uk\">United Kingdom</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nexport const Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Trigger>,\n SelectProps\n>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n className,\n id,\n disabled,\n children,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const triggerId = id ?? generatedId;\n const helperTextId = `${triggerId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <SelectContext.Provider value={{ size, error, disabled }}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={triggerId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n ref={ref}\n id={triggerId}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n \"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-inputs-inputs-primary outline-none motion-safe:transition-colors focus-visible:shadow-focus-ring\",\n TRIGGER_HEIGHT[size],\n TRIGGER_PADDING_X[size],\n TRIGGER_GAP[size],\n TRIGGER_TYPOGRAPHY[size],\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled &&\n !error &&\n \"hover:border-neutral-alphas-400 data-[state=open]:border-neutral-alphas-400\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n {leftIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\"\n data-testid=\"left-icon\"\n >\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate text-left text-content-primary [&>[data-placeholder]]:text-content-tertiary\">\n <SelectPrimitive.Value placeholder={placeholder} />\n </span>\n </div>\n\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n {children}\n </SelectPrimitive.Root>\n\n {bottomText && (\n <p\n id={helperTextId}\n className={cn(\n \"typography-description-12px-regular px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </SelectContext.Provider>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nexport interface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {}\n\n/**\n * The dropdown panel rendered inside a portal. Place {@link SelectItem} elements\n * (and optionally {@link SelectGroup} / {@link SelectLabel}) as children.\n */\nexport const SelectContent = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(\n (\n {\n className,\n children,\n position = \"popper\",\n sideOffset = 4,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n style,\n ...props\n },\n ref,\n ) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"relative w-max min-w-(--radix-select-trigger-width) max-w-(--radix-select-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-background-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"max-h-[var(--radix-select-content-available-height)] overflow-y-auto p-1\">\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n ),\n);\n\nSelectContent.displayName = \"SelectContent\";\n\nexport interface SelectItemProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {}\n\n/**\n * An individual option inside {@link SelectContent}.\n */\nexport const SelectItem = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Item>,\n SelectItemProps\n>(({ className, children, ...props }, ref) => (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"typography-body-default-16px-regular relative flex w-full cursor-pointer select-none items-center gap-2 rounded-xs py-2 pr-2 pl-3 text-content-primary outline-none\",\n \"focus:bg-neutral-alphas-100 data-disabled:pointer-events-none data-disabled:opacity-50\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n <SelectPrimitive.ItemIndicator className=\"ml-auto flex size-4 shrink-0 items-center justify-center\">\n <CheckIcon className=\"size-4 text-content-primary\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n));\n\nSelectItem.displayName = \"SelectItem\";\n\n/** Props for {@link SelectGroup}. */\nexport type SelectGroupProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;\n\n/**\n * Groups related {@link SelectItem} elements under a {@link SelectLabel}.\n */\nexport const SelectGroup = SelectPrimitive.Group;\nSelectGroup.displayName = \"SelectGroup\";\n\nexport interface SelectLabelProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> {}\n\n/**\n * A non-interactive label shown above a {@link SelectGroup}.\n */\nexport const SelectLabel = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\n \"typography-description-12px-semibold px-3 py-1.5 text-content-secondary\",\n className,\n )}\n {...props}\n />\n));\n\nSelectLabel.displayName = \"SelectLabel\";\n\nexport interface SelectSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> {}\n\n/** A horizontal rule that visually separates groups in {@link SelectContent}. */\nexport const SelectSeparator = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-neutral-alphas-200\", className)}\n {...props}\n />\n));\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Select.mjs","sources":["../../../src/components/Select/Select.tsx"],"sourcesContent":["import * as SelectPrimitive from \"@radix-ui/react-select\";\nimport * as React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { FLOATING_CONTENT_COLLISION_PADDING } from \"@/utils/floatingContentCollisionPadding\";\nimport { CheckIcon } from \"../Icons/CheckIcon\";\nimport { ChevronDownIcon } from \"../Icons/ChevronDownIcon\";\n\n/** Select field height in pixels. */\nexport type SelectSize = \"48\" | \"40\" | \"32\";\n\ntype SelectContextValue = {\n size: SelectSize;\n error: boolean;\n disabled?: boolean;\n};\n\nconst SelectContext = React.createContext<SelectContextValue>({\n size: \"48\",\n error: false,\n});\n\nconst TRIGGER_HEIGHT: Record<SelectSize, string> = {\n \"48\": \"h-12\",\n \"40\": \"h-10\",\n \"32\": \"h-8\",\n};\n\nconst TRIGGER_PADDING_X: Record<SelectSize, string> = {\n \"48\": \"px-4\",\n \"40\": \"px-4\",\n \"32\": \"px-3\",\n};\n\nconst TRIGGER_GAP: Record<SelectSize, string> = {\n \"48\": \"gap-3\",\n \"40\": \"gap-3\",\n \"32\": \"gap-2\",\n};\n\nconst TRIGGER_TYPOGRAPHY: Record<SelectSize, string> = {\n \"48\": \"typography-body-default-16px-regular\",\n \"40\": \"typography-body-default-16px-regular\",\n \"32\": \"typography-body-small-14px-regular\",\n};\n\nexport interface SelectProps extends Omit<SelectPrimitive.SelectProps, \"dir\"> {\n /** Label text displayed above the trigger. Also used as the accessible name. */\n label?: string;\n /** Accessible name applied directly to the trigger button when no visible `label` is provided. */\n \"aria-label\"?: string;\n /** ID of an external element that labels the trigger button. */\n \"aria-labelledby\"?: string;\n /** Helper text displayed below the trigger. Replaced by `errorMessage` when `error` is `true`. */\n helperText?: string;\n /** Height of the select field in pixels. @default \"48\" */\n size?: SelectSize;\n /** Whether the field is in an error state. @default false */\n error?: boolean;\n /** Error message displayed below the trigger. Shown instead of `helperText` when `error` is `true`. */\n errorMessage?: string;\n /** Placeholder shown when no value is selected. */\n placeholder?: string;\n /** Icon element displayed at the left side of the trigger. */\n leftIcon?: React.ReactNode;\n /** Whether the field stretches to fill its container width. @default false */\n fullWidth?: boolean;\n /** Wraps the `className` of the outermost container div. */\n className?: string;\n /** HTML `id` applied to the trigger button. Auto-generated if omitted. */\n id?: string;\n}\n\n/**\n * A select field with optional label, helper/error text, and an icon slot,\n * built on Radix UI Select for full accessibility and keyboard navigation.\n *\n * Pair with {@link SelectContent} and {@link SelectItem} to provide options.\n *\n * @example\n * ```tsx\n * <Select label=\"Country\" placeholder=\"Select a country\">\n * <SelectContent>\n * <SelectItem value=\"us\">United States</SelectItem>\n * <SelectItem value=\"uk\">United Kingdom</SelectItem>\n * </SelectContent>\n * </Select>\n * ```\n */\nexport const Select = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Trigger>,\n SelectProps\n>(\n (\n {\n label,\n helperText,\n size = \"48\",\n error = false,\n errorMessage,\n placeholder,\n leftIcon,\n fullWidth = false,\n className,\n id,\n disabled,\n children,\n \"aria-label\": ariaLabel,\n \"aria-labelledby\": ariaLabelledby,\n ...props\n },\n ref,\n ) => {\n const generatedId = React.useId();\n const triggerId = id ?? generatedId;\n const helperTextId = `${triggerId}-helper`;\n const bottomText = error && errorMessage ? errorMessage : helperText;\n\n return (\n <SelectContext.Provider value={{ size, error, disabled }}>\n <div\n className={cn(\"flex flex-col\", fullWidth && \"w-full\", className)}\n data-disabled={disabled ? \"\" : undefined}\n data-error={error ? \"\" : undefined}\n >\n {label && (\n <label\n htmlFor={triggerId}\n className=\"typography-description-12px-semibold pb-2 text-content-primary\"\n >\n {label}\n </label>\n )}\n\n <SelectPrimitive.Root disabled={disabled} {...props}>\n <SelectPrimitive.Trigger\n ref={ref}\n id={triggerId}\n aria-label={ariaLabel}\n aria-labelledby={ariaLabelledby}\n aria-describedby={bottomText ? helperTextId : undefined}\n aria-invalid={error || undefined}\n className={cn(\n \"flex w-full cursor-pointer items-center justify-between rounded-sm border bg-inputs-inputs-primary outline-none focus-visible:shadow-focus-ring motion-safe:transition-colors\",\n TRIGGER_HEIGHT[size],\n TRIGGER_PADDING_X[size],\n TRIGGER_GAP[size],\n TRIGGER_TYPOGRAPHY[size],\n error ? \"border-error-content\" : \"border-border-primary\",\n !disabled &&\n !error &&\n \"hover:border-neutral-alphas-400 data-[state=open]:border-neutral-alphas-400\",\n disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n <div className=\"flex min-w-0 items-center gap-2\">\n {leftIcon && (\n <span\n className=\"flex size-5 shrink-0 items-center justify-center text-content-secondary\"\n data-testid=\"left-icon\"\n >\n {leftIcon}\n </span>\n )}\n <span className=\"min-w-0 flex-1 truncate text-left text-content-primary [&>[data-placeholder]]:text-content-tertiary\">\n <SelectPrimitive.Value placeholder={placeholder} />\n </span>\n </div>\n\n <SelectPrimitive.Icon asChild>\n <ChevronDownIcon />\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n\n {children}\n </SelectPrimitive.Root>\n\n {bottomText && (\n <p\n id={helperTextId}\n className={cn(\n \"typography-description-12px-regular px-2 pt-1 pb-0.5\",\n error ? \"text-error-content\" : \"text-content-secondary\",\n )}\n >\n {bottomText}\n </p>\n )}\n </div>\n </SelectContext.Provider>\n );\n },\n);\n\nSelect.displayName = \"Select\";\n\nexport interface SelectContentProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content> {}\n\n/**\n * The dropdown panel rendered inside a portal. Place {@link SelectItem} elements\n * (and optionally {@link SelectGroup} / {@link SelectLabel}) as children.\n */\nexport const SelectContent = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Content>,\n SelectContentProps\n>(\n (\n {\n className,\n children,\n position = \"popper\",\n sideOffset = 4,\n collisionPadding = FLOATING_CONTENT_COLLISION_PADDING,\n style,\n ...props\n },\n ref,\n ) => (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n ref={ref}\n position={position}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n style={{ zIndex: \"var(--fanvue-ui-portal-z-index, 50)\", ...style }}\n className={cn(\n \"relative w-max min-w-(--radix-select-trigger-width) max-w-(--radix-select-content-available-width) overflow-hidden rounded-sm border border-neutral-alphas-200 bg-background-primary text-content-primary shadow-[0_4px_16px_rgba(0,0,0,0.10)]\",\n \"data-[state=closed]:animate-out data-[state=open]:animate-in\",\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=top]:slide-in-from-bottom-2\",\n className,\n )}\n {...props}\n >\n <SelectPrimitive.Viewport className=\"max-h-[var(--radix-select-content-available-height)] overflow-y-auto p-1\">\n {children}\n </SelectPrimitive.Viewport>\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n ),\n);\n\nSelectContent.displayName = \"SelectContent\";\n\n/** Dropdown row height in pixels, matching the V2 Menu Item spec. */\nexport type SelectItemSize = \"40\" | \"32\";\n\nconst ITEM_SIZE_CLASSES: Record<SelectItemSize, string> = {\n \"40\": \"min-h-10 py-2 typography-body-default-16px-regular\",\n \"32\": \"min-h-8 py-[7px] typography-body-small-14px-regular\",\n};\n\nconst ITEM_DESCRIPTION_TYPOGRAPHY: Record<SelectItemSize, string> = {\n \"40\": \"typography-body-small-14px-regular\",\n \"32\": \"typography-description-12px-regular\",\n};\n\nexport interface SelectItemProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {\n /** Row height. Defaults to the parent {@link Select} size (`48`/`40` → `40`, `32` → `32`). */\n size?: SelectItemSize;\n /** Icon (or other node) rendered before the label. Ignored when {@link SelectItemProps.avatar} is set. */\n leadingIcon?: React.ReactNode;\n /**\n * Leading avatar rendered in place of {@link SelectItemProps.leadingIcon}, for rows\n * representing a person or account. Pass an `Avatar` sized to `24`. Takes precedence over `leadingIcon`.\n */\n avatar?: React.ReactNode;\n /** Optional secondary text rendered on a second line below the label. */\n description?: React.ReactNode;\n}\n\n/**\n * Whether a `ReactNode` will actually render. Excludes `null`/`undefined` and\n * booleans so short-circuit props (e.g. `description={cond && \"…\"}`) don't flip\n * the item into a two-line/avatar layout when they resolve to `false`.\n */\nfunction isRenderableNode(node: React.ReactNode): boolean {\n return node !== null && node !== undefined && typeof node !== \"boolean\";\n}\n\n/**\n * An individual option inside {@link SelectContent}, following the V2 Menu Item spec.\n *\n * Supports a leading icon or avatar, an optional two-line layout via `description`,\n * and the standard hover / selected / disabled states. The selected row is marked\n * with a trailing check indicator.\n *\n * @example\n * ```tsx\n * <SelectItem value=\"jane\" avatar={<Avatar size={24} fallback=\"JD\" />} description=\"Product designer\">\n * Jane Doe\n * </SelectItem>\n * ```\n */\nexport const SelectItem = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Item>,\n SelectItemProps\n>(({ className, children, size, leadingIcon, avatar, description, ...props }, ref) => {\n const { size: triggerSize } = React.useContext(SelectContext);\n const itemSize: SelectItemSize = size ?? (triggerSize === \"32\" ? \"32\" : \"40\");\n const hasDescription = isRenderableNode(description);\n const hasAvatar = isRenderableNode(avatar);\n const hasLeadingIcon = isRenderableNode(leadingIcon);\n\n return (\n <SelectPrimitive.Item\n ref={ref}\n className={cn(\n \"group relative flex w-full cursor-pointer select-none gap-2 rounded-xs px-3 text-content-primary outline-none\",\n hasDescription ? \"items-start\" : \"items-center\",\n ITEM_SIZE_CLASSES[itemSize],\n hasAvatar && !hasDescription && itemSize === \"32\" && \"py-1\",\n \"focus:bg-neutral-alphas-50 data-highlighted:bg-neutral-alphas-50\",\n \"data-disabled:pointer-events-none data-disabled:text-content-disabled\",\n className,\n )}\n {...props}\n >\n {hasAvatar ? (\n <span className=\"shrink-0\">{avatar}</span>\n ) : (\n hasLeadingIcon &&\n (hasDescription ? (\n <span className=\"flex shrink-0 items-center pt-0.5 [&>svg]:size-4\">{leadingIcon}</span>\n ) : (\n <span className=\"flex shrink-0 items-center [&>svg]:size-4\">{leadingIcon}</span>\n ))\n )}\n\n {hasDescription ? (\n <span className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"truncate\">\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </span>\n <span\n className={cn(\n \"truncate text-content-secondary group-data-[disabled]:text-content-disabled\",\n ITEM_DESCRIPTION_TYPOGRAPHY[itemSize],\n )}\n >\n {description}\n </span>\n </span>\n ) : (\n <span className=\"min-w-0 flex-1 truncate\">\n <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>\n </span>\n )}\n\n <SelectPrimitive.ItemIndicator\n className={cn(\n \"ml-auto flex size-4 shrink-0 items-center justify-center\",\n hasDescription && \"self-start\",\n )}\n >\n <CheckIcon className=\"size-4 text-content-primary group-data-[disabled]:text-content-disabled\" />\n </SelectPrimitive.ItemIndicator>\n </SelectPrimitive.Item>\n );\n});\n\nSelectItem.displayName = \"SelectItem\";\n\n/** Props for {@link SelectGroup}. */\nexport type SelectGroupProps = React.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;\n\n/**\n * Groups related {@link SelectItem} elements under a {@link SelectLabel}.\n */\nexport const SelectGroup = SelectPrimitive.Group;\nSelectGroup.displayName = \"SelectGroup\";\n\nexport interface SelectLabelProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label> {}\n\n/**\n * A non-interactive label shown above a {@link SelectGroup}.\n */\nexport const SelectLabel = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Label>,\n SelectLabelProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Label\n ref={ref}\n className={cn(\n \"typography-description-12px-semibold px-3 py-1.5 text-content-secondary\",\n className,\n )}\n {...props}\n />\n));\n\nSelectLabel.displayName = \"SelectLabel\";\n\nexport interface SelectSeparatorProps\n extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator> {}\n\n/** A horizontal rule that visually separates groups in {@link SelectContent}. */\nexport const SelectSeparator = React.forwardRef<\n React.ComponentRef<typeof SelectPrimitive.Separator>,\n SelectSeparatorProps\n>(({ className, ...props }, ref) => (\n <SelectPrimitive.Separator\n ref={ref}\n className={cn(\"-mx-1 my-1 h-px bg-neutral-alphas-200\", className)}\n {...props}\n />\n));\n\nSelectSeparator.displayName = \"SelectSeparator\";\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs, jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.mjs";
|
|
5
|
+
import { Button } from "../Button/Button.mjs";
|
|
6
|
+
const OLD_PRICE_TYPOGRAPHY = {
|
|
7
|
+
"48": "typography-body-small-14px-regular",
|
|
8
|
+
"40": "typography-body-small-14px-regular",
|
|
9
|
+
"32": "typography-description-12px-regular"
|
|
10
|
+
};
|
|
11
|
+
function warnMissingAccessibleName(hasTextLabel, ariaLabel) {
|
|
12
|
+
if (process.env.NODE_ENV !== "production" && !hasTextLabel && !ariaLabel) {
|
|
13
|
+
console.warn(
|
|
14
|
+
"SubscribeButton: no accessible name could be derived from a string `children`. Pass an `aria-label` so the action and price are announced."
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const SubscribeButton = React.forwardRef(
|
|
19
|
+
({ variant = "primary", size = "48", price, discount, children = "Join now", ...props }, ref) => {
|
|
20
|
+
const labelText = typeof children === "string" ? children : void 0;
|
|
21
|
+
warnMissingAccessibleName(Boolean(labelText?.trim()), props["aria-label"]);
|
|
22
|
+
const accessibleName = props["aria-label"] ?? [labelText, price, discount ? `was ${discount}` : null].filter(Boolean).join(", ");
|
|
23
|
+
return /* @__PURE__ */ jsxs(Button, { ref, variant, size, ...props, "aria-label": accessibleName, children: [
|
|
24
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children }),
|
|
25
|
+
/* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-2", "aria-hidden": "true", children: [
|
|
26
|
+
discount && /* @__PURE__ */ jsx("span", { className: cn(OLD_PRICE_TYPOGRAPHY[size], "line-through"), children: discount }),
|
|
27
|
+
/* @__PURE__ */ jsx("span", { children: price })
|
|
28
|
+
] })
|
|
29
|
+
] });
|
|
30
|
+
}
|
|
31
|
+
);
|
|
32
|
+
SubscribeButton.displayName = "SubscribeButton";
|
|
33
|
+
export {
|
|
34
|
+
SubscribeButton
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=SubscribeButton.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SubscribeButton.mjs","sources":["../../../src/components/SubscribeButton/SubscribeButton.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cn } from \"../../utils/cn\";\nimport { Button, type ButtonProps } from \"../Button/Button\";\n\n/** Visual style variant of the subscribe button. Mirrors the V2 Subscribe Button \"Type\" set (`brand` is Figma's \"Upsell\"). */\nexport type SubscribeButtonVariant = \"primary\" | \"secondary\" | \"tertiary\" | \"outline\" | \"brand\";\n\n/** Height of the subscribe button in pixels. */\nexport type SubscribeButtonSize = \"48\" | \"40\" | \"32\";\n\nexport interface SubscribeButtonProps\n extends Omit<\n ButtonProps,\n \"variant\" | \"size\" | \"price\" | \"discount\" | \"leftIcon\" | \"rightIcon\" | \"asChild\"\n > {\n /** Visual style variant. `brand` is the Figma \"Upsell\" (green) type. @default \"primary\" */\n variant?: SubscribeButtonVariant;\n /** Height of the button in pixels. @default \"48\" */\n size?: SubscribeButtonSize;\n /** Current price shown on the trailing side (e.g. `\"$9.99/mo\"`). */\n price: string;\n /** Previous price rendered struck-through before {@link SubscribeButtonProps.price}. */\n discount?: string;\n /** Leading action label. @default \"Join now\" */\n children?: React.ReactNode;\n}\n\nconst OLD_PRICE_TYPOGRAPHY: Record<SubscribeButtonSize, string> = {\n \"48\": \"typography-body-small-14px-regular\",\n \"40\": \"typography-body-small-14px-regular\",\n \"32\": \"typography-description-12px-regular\",\n};\n\nfunction warnMissingAccessibleName(hasTextLabel: boolean, ariaLabel?: string) {\n if (process.env.NODE_ENV !== \"production\" && !hasTextLabel && !ariaLabel) {\n console.warn(\n \"SubscribeButton: no accessible name could be derived from a string `children`. Pass an `aria-label` so the action and price are announced.\",\n );\n }\n}\n\n/**\n * A subscription / purchase button pairing an action label with the current\n * price and an optional struck-through previous price. Built on {@link Button},\n * so it inherits the same variants, sizes, `negative` dark-surface treatment,\n * `fullWidth`, loading, and disabled behaviour.\n *\n * The accessible name defaults to the label plus pricing (e.g. `\"Join now, $9.99,\n * was $19.99\"`); pass `aria-label` to override.\n *\n * @example\n * ```tsx\n * <SubscribeButton price=\"$9.99/mo\" discount=\"$19.99\" variant=\"brand\" fullWidth>\n * Join now\n * </SubscribeButton>\n * ```\n */\nexport const SubscribeButton = React.forwardRef<HTMLButtonElement, SubscribeButtonProps>(\n ({ variant = \"primary\", size = \"48\", price, discount, children = \"Join now\", ...props }, ref) => {\n const labelText = typeof children === \"string\" ? children : undefined;\n warnMissingAccessibleName(Boolean(labelText?.trim()), props[\"aria-label\"]);\n\n const accessibleName =\n props[\"aria-label\"] ??\n [labelText, price, discount ? `was ${discount}` : null].filter(Boolean).join(\", \");\n\n return (\n <Button ref={ref} variant={variant} size={size} {...props} aria-label={accessibleName}>\n <span className=\"min-w-0 flex-1 truncate text-left\">{children}</span>\n <span className=\"flex shrink-0 items-center gap-2\" aria-hidden=\"true\">\n {discount && (\n <span className={cn(OLD_PRICE_TYPOGRAPHY[size], \"line-through\")}>{discount}</span>\n )}\n <span>{price}</span>\n </span>\n </Button>\n );\n },\n);\n\nSubscribeButton.displayName = \"SubscribeButton\";\n"],"names":[],"mappings":";;;;;AA2BA,MAAM,uBAA4D;AAAA,EAChE,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AACR;AAEA,SAAS,0BAA0B,cAAuB,WAAoB;AAC5E,MAAI,QAAQ,IAAI,aAAa,gBAAgB,CAAC,gBAAgB,CAAC,WAAW;AACxE,YAAQ;AAAA,MACN;AAAA,IAAA;AAAA,EAEJ;AACF;AAkBO,MAAM,kBAAkB,MAAM;AAAA,EACnC,CAAC,EAAE,UAAU,WAAW,OAAO,MAAM,OAAO,UAAU,WAAW,YAAY,GAAG,MAAA,GAAS,QAAQ;AAC/F,UAAM,YAAY,OAAO,aAAa,WAAW,WAAW;AAC5D,8BAA0B,QAAQ,WAAW,KAAA,CAAM,GAAG,MAAM,YAAY,CAAC;AAEzE,UAAM,iBACJ,MAAM,YAAY,KAClB,CAAC,WAAW,OAAO,WAAW,OAAO,QAAQ,KAAK,IAAI,EAAE,OAAO,OAAO,EAAE,KAAK,IAAI;AAEnF,WACE,qBAAC,UAAO,KAAU,SAAkB,MAAa,GAAG,OAAO,cAAY,gBACrE,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,qCAAqC,SAAA,CAAS;AAAA,MAC9D,qBAAC,QAAA,EAAK,WAAU,oCAAmC,eAAY,QAC5D,UAAA;AAAA,QAAA,YACC,oBAAC,UAAK,WAAW,GAAG,qBAAqB,IAAI,GAAG,cAAc,GAAI,UAAA,SAAA,CAAS;AAAA,QAE7E,oBAAC,UAAM,UAAA,MAAA,CAAM;AAAA,MAAA,EAAA,CACf;AAAA,IAAA,GACF;AAAA,EAEJ;AACF;AAEA,gBAAgB,cAAc;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -380,6 +380,37 @@ export declare interface AudioPlayerProps extends Omit<React_2.HTMLAttributes<HT
|
|
|
380
380
|
/** Height of the audio player row, in pixels. Matches both the Vault card overlay and the "Generated Audio" modal row in Figma — both use an identical 32px play button and 40px row. */
|
|
381
381
|
export declare type AudioPlayerSize = "40";
|
|
382
382
|
|
|
383
|
+
/**
|
|
384
|
+
* A circular icon button that toggles audio recording. It owns the record
|
|
385
|
+
* lifecycle so consumers flip a single `status` prop instead of swapping an
|
|
386
|
+
* {@link IconButton}'s icon and colour: `idle` shows the microphone to start,
|
|
387
|
+
* `recording` turns destructive with a stop glyph to end. Used by
|
|
388
|
+
* {@link AudioUpload} for its in-progress stop control.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```tsx
|
|
392
|
+
* <AudioRecordButton status={recording ? "recording" : "idle"} onClick={toggle} />
|
|
393
|
+
* ```
|
|
394
|
+
*/
|
|
395
|
+
export declare const AudioRecordButton: React_2.ForwardRefExoticComponent<AudioRecordButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
|
|
396
|
+
|
|
397
|
+
export declare interface AudioRecordButtonProps extends Omit<IconButtonProps, "icon" | "variant" | "aria-label"> {
|
|
398
|
+
/** Recording state; drives the icon and colour. @default "idle" */
|
|
399
|
+
status?: AudioRecordButtonStatus;
|
|
400
|
+
/**
|
|
401
|
+
* Accessible name. Falls back to a status-aware default ("Record" /
|
|
402
|
+
* "Stop recording") when omitted so screen readers announce the action.
|
|
403
|
+
*/
|
|
404
|
+
"aria-label"?: string;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Recording state of the button. Drives the icon and colour: `idle` shows the
|
|
409
|
+
* microphone to start a recording, `recording` shows the stop glyph with the
|
|
410
|
+
* destructive treatment to end it.
|
|
411
|
+
*/
|
|
412
|
+
export declare type AudioRecordButtonStatus = "idle" | "recording";
|
|
413
|
+
|
|
383
414
|
/**
|
|
384
415
|
* Audio file upload with drag-and-drop and optional in-browser recording.
|
|
385
416
|
* Supports file validation, multiple files, and real-time waveform visualization during recording.
|
|
@@ -1255,6 +1286,82 @@ export declare interface ChatInputSelectOption {
|
|
|
1255
1286
|
icon?: React_2.ReactNode;
|
|
1256
1287
|
}
|
|
1257
1288
|
|
|
1289
|
+
/**
|
|
1290
|
+
* A single chat message rendered as a bubble. Sender messages sit on the right
|
|
1291
|
+
* with a green bubble and a delivery tick; receiver messages sit on the left
|
|
1292
|
+
* with a grey bubble and an avatar. Supports plain text, a typing indicator, a
|
|
1293
|
+
* voice message with waveform, and a deleted-message placeholder.
|
|
1294
|
+
*
|
|
1295
|
+
* Text bubbles place the timestamp inline after short messages and drop it to
|
|
1296
|
+
* the bottom-right corner once the text wraps, so no variant switch is needed
|
|
1297
|
+
* for short versus long content.
|
|
1298
|
+
*
|
|
1299
|
+
* @example
|
|
1300
|
+
* ```tsx
|
|
1301
|
+
* <ChatMessage user="sender" message="On my way!" time="16:00" status="read" />
|
|
1302
|
+
* <ChatMessage user="receiver" message="See you soon" time="16:01" avatarSrc="/jane.jpg" online />
|
|
1303
|
+
* <ChatMessage user="receiver" variant="typing" avatarSrc="/jane.jpg" />
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
export declare const ChatMessage: React_2.ForwardRefExoticComponent<ChatMessageProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
1307
|
+
|
|
1308
|
+
export declare interface ChatMessageProps extends React_2.HTMLAttributes<HTMLDivElement> {
|
|
1309
|
+
/**
|
|
1310
|
+
* Who sent the message. `"sender"` is the current user (right-aligned, green
|
|
1311
|
+
* bubble, delivery tick). `"receiver"` is the other party (left-aligned, grey
|
|
1312
|
+
* bubble, with avatar). @default "receiver"
|
|
1313
|
+
*/
|
|
1314
|
+
user?: ChatMessageUser;
|
|
1315
|
+
/** The kind of content the message carries. @default "text" */
|
|
1316
|
+
variant?: ChatMessageVariant;
|
|
1317
|
+
/** Message body for the `"text"` variant. Keep it to inline content. */
|
|
1318
|
+
message?: React_2.ReactNode;
|
|
1319
|
+
/** Timestamp shown with the message, e.g. `"16:00"`. */
|
|
1320
|
+
time?: string;
|
|
1321
|
+
/**
|
|
1322
|
+
* Delivery status shown on sender messages (a double tick). Ignored for
|
|
1323
|
+
* receiver messages. `"read"` renders the tick in the read colour. @default "delivered"
|
|
1324
|
+
*/
|
|
1325
|
+
status?: ChatMessageStatus;
|
|
1326
|
+
/** Duration label for the `"audio"` variant, e.g. `"0:05"`. @default "0:00" */
|
|
1327
|
+
audioDuration?: string;
|
|
1328
|
+
/**
|
|
1329
|
+
* Relative bar heights (values `0`–`1`) for the `"audio"` waveform. Defaults
|
|
1330
|
+
* to a flat row of dots matching the unplayed design state.
|
|
1331
|
+
*/
|
|
1332
|
+
waveform?: number[];
|
|
1333
|
+
/** Whether the audio is playing (controlled). Pairs with {@link onPlayingChange}. */
|
|
1334
|
+
playing?: boolean;
|
|
1335
|
+
/** Initial playing state when uncontrolled. @default false */
|
|
1336
|
+
defaultPlaying?: boolean;
|
|
1337
|
+
/** Fired when the audio play/pause button is pressed, with the next playing state. */
|
|
1338
|
+
onPlayingChange?: (playing: boolean) => void;
|
|
1339
|
+
/** Render the receiver avatar. Reserves the avatar space when `false` so grouped bubbles stay aligned. @default true */
|
|
1340
|
+
showAvatar?: boolean;
|
|
1341
|
+
/** Avatar image URL for receiver messages. */
|
|
1342
|
+
avatarSrc?: string;
|
|
1343
|
+
/** Avatar alt text. @default "Avatar" */
|
|
1344
|
+
avatarAlt?: string;
|
|
1345
|
+
/** Avatar fallback (initials or icon) shown before the image loads. */
|
|
1346
|
+
avatarFallback?: React_2.ReactNode;
|
|
1347
|
+
/** Show the online indicator on the receiver avatar. @default false */
|
|
1348
|
+
online?: boolean;
|
|
1349
|
+
/** Accessible label for the typing indicator. @default "Typing" */
|
|
1350
|
+
typingLabel?: string;
|
|
1351
|
+
/** Text shown for the `"deleted"` variant. @default "Message deleted" */
|
|
1352
|
+
deletedLabel?: string;
|
|
1353
|
+
/** Accessible label for the audio play button. @default "Play" */
|
|
1354
|
+
playLabel?: string;
|
|
1355
|
+
/** Accessible label for the audio pause button. @default "Pause" */
|
|
1356
|
+
pauseLabel?: string;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
export declare type ChatMessageStatus = "delivered" | "read";
|
|
1360
|
+
|
|
1361
|
+
export declare type ChatMessageUser = "sender" | "receiver";
|
|
1362
|
+
|
|
1363
|
+
export declare type ChatMessageVariant = "text" | "typing" | "audio" | "deleted";
|
|
1364
|
+
|
|
1258
1365
|
/**
|
|
1259
1366
|
* A checkbox input with optional label and helper text. Supports checked,
|
|
1260
1367
|
* unchecked, and indeterminate states.
|
|
@@ -1779,6 +1886,20 @@ export declare interface CyclingTextProps extends Omit<React_2.HTMLAttributes<HT
|
|
|
1779
1886
|
/** How the wrapper should be sized to accommodate variable-length items. */
|
|
1780
1887
|
export declare type CyclingTextSizing = "longest" | "current";
|
|
1781
1888
|
|
|
1889
|
+
/**
|
|
1890
|
+
* Dense grid view icon (3×3 tiles) — the compact-grid counterpart of
|
|
1891
|
+
* {@link GridViewIcon}. Renders at sizes 16, 24, or 32 px.
|
|
1892
|
+
*
|
|
1893
|
+
* @example
|
|
1894
|
+
* ```tsx
|
|
1895
|
+
* <DenseGridViewIcon size={24} />
|
|
1896
|
+
* ```
|
|
1897
|
+
*/
|
|
1898
|
+
export declare const DenseGridViewIcon: React_2.ForwardRefExoticComponent<BaseIconProps & React_2.RefAttributes<SVGSVGElement>>;
|
|
1899
|
+
|
|
1900
|
+
/** Props for {@link DenseGridViewIcon}. See {@link BaseIconProps} for the shared shape. */
|
|
1901
|
+
export declare type DenseGridViewIconProps = BaseIconProps;
|
|
1902
|
+
|
|
1782
1903
|
/** Root component that manages open/close state for a dialog. */
|
|
1783
1904
|
export declare const Dialog: React_2.FC<DialogPrimitive.DialogProps>;
|
|
1784
1905
|
|
|
@@ -2221,7 +2342,7 @@ export declare type DrawerTriggerProps = React_2.ComponentPropsWithoutRef<typeof
|
|
|
2221
2342
|
export declare type DrawerVariant = "panel" | "sheet";
|
|
2222
2343
|
|
|
2223
2344
|
/** Root component that manages open/close state for a dropdown menu. */
|
|
2224
|
-
export declare function DropdownMenu({ open: openProp, defaultOpen, onOpenChange, children, ...props }: DropdownMenuProps): JSX.Element;
|
|
2345
|
+
export declare function DropdownMenu({ open: openProp, defaultOpen, onOpenChange, variant, children, ...props }: DropdownMenuProps): JSX.Element;
|
|
2225
2346
|
|
|
2226
2347
|
/**
|
|
2227
2348
|
* The positioned content panel rendered inside a portal.
|
|
@@ -2248,7 +2369,11 @@ export declare const DropdownMenuContent: React_2.ForwardRefExoticComponent<Drop
|
|
|
2248
2369
|
export declare interface DropdownMenuContentProps extends React_2.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content> {
|
|
2249
2370
|
}
|
|
2250
2371
|
|
|
2251
|
-
/**
|
|
2372
|
+
/**
|
|
2373
|
+
* Groups related menu items. Accepts an optional `DropdownMenuLabel`.
|
|
2374
|
+
*
|
|
2375
|
+
* Requires Radix menu context — not supported inside a `variant="sheet"` menu.
|
|
2376
|
+
*/
|
|
2252
2377
|
export declare const DropdownMenuGroup: React_2.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuGroupProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
2253
2378
|
|
|
2254
2379
|
/** Props for the {@link DropdownMenuGroup} component. */
|
|
@@ -2345,7 +2470,13 @@ export declare interface DropdownMenuItemProps extends React_2.ComponentPropsWit
|
|
|
2345
2470
|
* Takes precedence over `leadingIcon`.
|
|
2346
2471
|
*/
|
|
2347
2472
|
avatar?: React_2.ReactNode;
|
|
2348
|
-
/**
|
|
2473
|
+
/**
|
|
2474
|
+
* Icon (or other node) rendered after the label. When
|
|
2475
|
+
* {@link DropdownMenuItemProps.selected} is true and no `trailingIcon` is
|
|
2476
|
+
* given, the built-in selected check indicator renders in this slot
|
|
2477
|
+
* instead — pass a `trailingIcon` to use a custom selected indicator (e.g.
|
|
2478
|
+
* a themed tick) rather than the default one.
|
|
2479
|
+
*/
|
|
2349
2480
|
trailingIcon?: React_2.ReactNode;
|
|
2350
2481
|
/** Trailing count or number (e.g. an unread total) rendered before {@link DropdownMenuItemProps.trailingIcon}. */
|
|
2351
2482
|
count?: React_2.ReactNode;
|
|
@@ -2390,6 +2521,8 @@ export declare interface DropdownMenuLabelProps extends React_2.ComponentPropsWi
|
|
|
2390
2521
|
|
|
2391
2522
|
/** Props for the {@link DropdownMenu} root component. */
|
|
2392
2523
|
export declare interface DropdownMenuProps extends React_2.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Root> {
|
|
2524
|
+
/** How the menu presents its content. @default "menu" */
|
|
2525
|
+
variant?: DropdownMenuVariant;
|
|
2393
2526
|
}
|
|
2394
2527
|
|
|
2395
2528
|
/**
|
|
@@ -2403,6 +2536,8 @@ export declare interface DropdownMenuProps extends React_2.ComponentPropsWithout
|
|
|
2403
2536
|
* <DropdownMenuRadioItem value="oldest">Oldest first</DropdownMenuRadioItem>
|
|
2404
2537
|
* </DropdownMenuRadioGroup>
|
|
2405
2538
|
* ```
|
|
2539
|
+
*
|
|
2540
|
+
* Requires Radix menu context — not supported inside a `variant="sheet"` menu.
|
|
2406
2541
|
*/
|
|
2407
2542
|
export declare const DropdownMenuRadioGroup: React_2.ForwardRefExoticComponent<DropdownMenuPrimitive.DropdownMenuRadioGroupProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
2408
2543
|
|
|
@@ -2447,6 +2582,16 @@ export declare const DropdownMenuTrigger: React_2.ForwardRefExoticComponent<Omit
|
|
|
2447
2582
|
/** Props for the {@link DropdownMenuTrigger} component. */
|
|
2448
2583
|
export declare type DropdownMenuTriggerProps = React_2.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Trigger>;
|
|
2449
2584
|
|
|
2585
|
+
/**
|
|
2586
|
+
* How a {@link DropdownMenu} presents its content.
|
|
2587
|
+
* - `"menu"` (default) — a Radix-positioned panel anchored to the trigger.
|
|
2588
|
+
* - `"sheet"` — a bottom drawer (via {@link Drawer}), for mobile/touch viewports.
|
|
2589
|
+
*
|
|
2590
|
+
* The viewport decision belongs to the consumer (it owns the breakpoint
|
|
2591
|
+
* source of truth), so pass e.g. `variant={isDesktop ? "menu" : "sheet"}`.
|
|
2592
|
+
*/
|
|
2593
|
+
declare type DropdownMenuVariant = "menu" | "sheet";
|
|
2594
|
+
|
|
2450
2595
|
/**
|
|
2451
2596
|
* Edit icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
|
|
2452
2597
|
*
|
|
@@ -3326,6 +3471,40 @@ export declare const MassMessageIcon: React_2.ForwardRefExoticComponent<BaseIcon
|
|
|
3326
3471
|
/** Props for {@link MassMessageIcon}. See {@link BaseIconProps} for the shared shape. */
|
|
3327
3472
|
export declare type MassMessageIconProps = BaseIconProps;
|
|
3328
3473
|
|
|
3474
|
+
/**
|
|
3475
|
+
* A compact circular indicator that surfaces the moderation state of a piece of
|
|
3476
|
+
* media — flagged, removed, or sensitive. Typically overlaid on a thumbnail or
|
|
3477
|
+
* attachment.
|
|
3478
|
+
*
|
|
3479
|
+
* Renders as `role="img"` with a descriptive `aria-label`; the inner glyph is
|
|
3480
|
+
* decorative.
|
|
3481
|
+
*
|
|
3482
|
+
* @example
|
|
3483
|
+
* ```tsx
|
|
3484
|
+
* <MediaStatusIndicator status="sensitive" />
|
|
3485
|
+
* <MediaStatusIndicator status="removed" label="Removed for violating guidelines" />
|
|
3486
|
+
* ```
|
|
3487
|
+
*/
|
|
3488
|
+
export declare const MediaStatusIndicator: React_2.ForwardRefExoticComponent<MediaStatusIndicatorProps & React_2.RefAttributes<HTMLSpanElement>>;
|
|
3489
|
+
|
|
3490
|
+
export declare interface MediaStatusIndicatorProps extends Omit<React_2.HTMLAttributes<HTMLSpanElement>, "children"> {
|
|
3491
|
+
/** Which media state to represent. @default "default" */
|
|
3492
|
+
status?: MediaStatusIndicatorStatus;
|
|
3493
|
+
/**
|
|
3494
|
+
* Accessible label announced by assistive tech. Defaults to a per-status
|
|
3495
|
+
* description; pass your own for localisation or extra context.
|
|
3496
|
+
*/
|
|
3497
|
+
label?: string;
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
/**
|
|
3501
|
+
* State represented by the indicator.
|
|
3502
|
+
* - `"default"`: media flagged / pending review (amber).
|
|
3503
|
+
* - `"removed"`: media removed for a policy violation (red).
|
|
3504
|
+
* - `"sensitive"`: sensitive content hidden behind an overlay (dark).
|
|
3505
|
+
*/
|
|
3506
|
+
export declare type MediaStatusIndicatorStatus = "default" | "removed" | "sensitive";
|
|
3507
|
+
|
|
3329
3508
|
/**
|
|
3330
3509
|
* Megaphone icon. Renders at sizes 16, 24, or 32 px with outlined and filled variants.
|
|
3331
3510
|
*
|
|
@@ -4298,13 +4477,38 @@ export declare const SelectGroup: React_2.ForwardRefExoticComponent<SelectPrimit
|
|
|
4298
4477
|
export declare type SelectGroupProps = React_2.ComponentPropsWithoutRef<typeof SelectPrimitive.Group>;
|
|
4299
4478
|
|
|
4300
4479
|
/**
|
|
4301
|
-
* An individual option inside {@link SelectContent}.
|
|
4480
|
+
* An individual option inside {@link SelectContent}, following the V2 Menu Item spec.
|
|
4481
|
+
*
|
|
4482
|
+
* Supports a leading icon or avatar, an optional two-line layout via `description`,
|
|
4483
|
+
* and the standard hover / selected / disabled states. The selected row is marked
|
|
4484
|
+
* with a trailing check indicator.
|
|
4485
|
+
*
|
|
4486
|
+
* @example
|
|
4487
|
+
* ```tsx
|
|
4488
|
+
* <SelectItem value="jane" avatar={<Avatar size={24} fallback="JD" />} description="Product designer">
|
|
4489
|
+
* Jane Doe
|
|
4490
|
+
* </SelectItem>
|
|
4491
|
+
* ```
|
|
4302
4492
|
*/
|
|
4303
4493
|
export declare const SelectItem: React_2.ForwardRefExoticComponent<SelectItemProps & React_2.RefAttributes<HTMLDivElement>>;
|
|
4304
4494
|
|
|
4305
4495
|
export declare interface SelectItemProps extends React_2.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {
|
|
4496
|
+
/** Row height. Defaults to the parent {@link Select} size (`48`/`40` → `40`, `32` → `32`). */
|
|
4497
|
+
size?: SelectItemSize;
|
|
4498
|
+
/** Icon (or other node) rendered before the label. Ignored when {@link SelectItemProps.avatar} is set. */
|
|
4499
|
+
leadingIcon?: React_2.ReactNode;
|
|
4500
|
+
/**
|
|
4501
|
+
* Leading avatar rendered in place of {@link SelectItemProps.leadingIcon}, for rows
|
|
4502
|
+
* representing a person or account. Pass an `Avatar` sized to `24`. Takes precedence over `leadingIcon`.
|
|
4503
|
+
*/
|
|
4504
|
+
avatar?: React_2.ReactNode;
|
|
4505
|
+
/** Optional secondary text rendered on a second line below the label. */
|
|
4506
|
+
description?: React_2.ReactNode;
|
|
4306
4507
|
}
|
|
4307
4508
|
|
|
4509
|
+
/** Dropdown row height in pixels, matching the V2 Menu Item spec. */
|
|
4510
|
+
export declare type SelectItemSize = "40" | "32";
|
|
4511
|
+
|
|
4308
4512
|
/**
|
|
4309
4513
|
* A non-interactive label shown above a {@link SelectGroup}.
|
|
4310
4514
|
*/
|
|
@@ -4632,6 +4836,43 @@ export declare const StopIcon: React_2.ForwardRefExoticComponent<BaseIconProps &
|
|
|
4632
4836
|
/** Props for {@link StopIcon}. See {@link BaseIconProps} for the shared shape. */
|
|
4633
4837
|
export declare type StopIconProps = BaseIconProps;
|
|
4634
4838
|
|
|
4839
|
+
/**
|
|
4840
|
+
* A subscription / purchase button pairing an action label with the current
|
|
4841
|
+
* price and an optional struck-through previous price. Built on {@link Button},
|
|
4842
|
+
* so it inherits the same variants, sizes, `negative` dark-surface treatment,
|
|
4843
|
+
* `fullWidth`, loading, and disabled behaviour.
|
|
4844
|
+
*
|
|
4845
|
+
* The accessible name defaults to the label plus pricing (e.g. `"Join now, $9.99,
|
|
4846
|
+
* was $19.99"`); pass `aria-label` to override.
|
|
4847
|
+
*
|
|
4848
|
+
* @example
|
|
4849
|
+
* ```tsx
|
|
4850
|
+
* <SubscribeButton price="$9.99/mo" discount="$19.99" variant="brand" fullWidth>
|
|
4851
|
+
* Join now
|
|
4852
|
+
* </SubscribeButton>
|
|
4853
|
+
* ```
|
|
4854
|
+
*/
|
|
4855
|
+
export declare const SubscribeButton: React_2.ForwardRefExoticComponent<SubscribeButtonProps & React_2.RefAttributes<HTMLButtonElement>>;
|
|
4856
|
+
|
|
4857
|
+
export declare interface SubscribeButtonProps extends Omit<ButtonProps, "variant" | "size" | "price" | "discount" | "leftIcon" | "rightIcon" | "asChild"> {
|
|
4858
|
+
/** Visual style variant. `brand` is the Figma "Upsell" (green) type. @default "primary" */
|
|
4859
|
+
variant?: SubscribeButtonVariant;
|
|
4860
|
+
/** Height of the button in pixels. @default "48" */
|
|
4861
|
+
size?: SubscribeButtonSize;
|
|
4862
|
+
/** Current price shown on the trailing side (e.g. `"$9.99/mo"`). */
|
|
4863
|
+
price: string;
|
|
4864
|
+
/** Previous price rendered struck-through before {@link SubscribeButtonProps.price}. */
|
|
4865
|
+
discount?: string;
|
|
4866
|
+
/** Leading action label. @default "Join now" */
|
|
4867
|
+
children?: React_2.ReactNode;
|
|
4868
|
+
}
|
|
4869
|
+
|
|
4870
|
+
/** Height of the subscribe button in pixels. */
|
|
4871
|
+
export declare type SubscribeButtonSize = "48" | "40" | "32";
|
|
4872
|
+
|
|
4873
|
+
/** Visual style variant of the subscribe button. Mirrors the V2 Subscribe Button "Type" set (`brand` is Figma's "Upsell"). */
|
|
4874
|
+
export declare type SubscribeButtonVariant = "primary" | "secondary" | "tertiary" | "outline" | "brand";
|
|
4875
|
+
|
|
4635
4876
|
/** A checkmark inside a filled circle icon for success states (20 × 20). */
|
|
4636
4877
|
export declare const SuccessIcon: default_2.ForwardRefExoticComponent<default_2.SVGAttributes<SVGSVGElement> & {
|
|
4637
4878
|
className?: string;
|