@payglocal_ui/flux-ui 0.3.4 → 0.3.5
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/index.cjs +172 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +171 -81
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/alert.tsx +2 -1
- package/src/button-group.tsx +7 -5
- package/src/button.tsx +5 -4
- package/src/calendar.tsx +5 -2
- package/src/card.tsx +4 -1
- package/src/chart-templates.tsx +9 -7
- package/src/checkbox-select.tsx +9 -4
- package/src/checkbox.tsx +3 -1
- package/src/command.tsx +2 -1
- package/src/country-select.tsx +5 -2
- package/src/data-table.tsx +4 -2
- package/src/date-picker.tsx +9 -4
- package/src/dialog.tsx +3 -1
- package/src/drawer.tsx +2 -1
- package/src/dropdown-menu.tsx +5 -2
- package/src/elevation.ts +54 -0
- package/src/flag.tsx +3 -1
- package/src/icon-button.tsx +5 -4
- package/src/index.ts +1 -0
- package/src/inline-dialog.tsx +3 -1
- package/src/inline-edit.tsx +3 -1
- package/src/input-group.tsx +3 -1
- package/src/input.tsx +3 -1
- package/src/otp-input.tsx +3 -1
- package/src/pagination.tsx +2 -1
- package/src/popover.tsx +3 -1
- package/src/radio-group.tsx +3 -1
- package/src/section-message.tsx +3 -1
- package/src/select.tsx +5 -2
- package/src/single-select.tsx +7 -3
- package/src/skeleton.tsx +7 -1
- package/src/slider.tsx +5 -1
- package/src/sonner.tsx +2 -1
- package/src/spotlight.tsx +2 -1
- package/src/switch.tsx +3 -1
- package/src/textarea.tsx +3 -1
- package/src/time-picker.tsx +5 -2
- package/src/tooltip.tsx +3 -1
package/package.json
CHANGED
package/src/alert.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { X, Info, CheckCircle2, AlertTriangle, XCircle } from "lucide-react";
|
|
5
5
|
import { cn } from "./utils";
|
|
6
|
+
import { elevation } from "./elevation";
|
|
6
7
|
|
|
7
8
|
export type AlertVariant = "info" | "success" | "warning" | "error" | "neutral";
|
|
8
9
|
|
|
@@ -35,7 +36,7 @@ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
|
|
|
35
36
|
<div
|
|
36
37
|
ref={ref}
|
|
37
38
|
role="alert"
|
|
38
|
-
className={cn("relative flex gap-3 rounded-xl border p-4
|
|
39
|
+
className={cn("relative flex gap-3 rounded-xl border p-4", elevation.surface, variantStyles[variant], className)}
|
|
39
40
|
{...props}
|
|
40
41
|
>
|
|
41
42
|
<Icon className="mt-0.5 size-4 shrink-0" aria-hidden />
|
package/src/button-group.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { cn } from "./utils";
|
|
4
|
+
import { elevation } from "./elevation";
|
|
4
5
|
import { ChevronDown } from "lucide-react";
|
|
5
6
|
import {
|
|
6
7
|
forwardRef,
|
|
@@ -144,15 +145,15 @@ export const SplitButton = forwardRef<HTMLDivElement, SplitButtonProps>(
|
|
|
144
145
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
145
146
|
// Match variant styling
|
|
146
147
|
variant === "primary" &&
|
|
147
|
-
|
|
148
|
+
`bg-primary text-primary-foreground border border-primary ${elevation.control} hover:bg-[var(--primary-hover)] border-l border-l-primary-foreground/20`,
|
|
148
149
|
variant === "secondary" &&
|
|
149
|
-
|
|
150
|
+
`bg-muted text-foreground border border-border ${elevation.control} hover:bg-muted/85 dark:bg-muted/35 dark:hover:bg-muted/55 border-l-0`,
|
|
150
151
|
variant === "outline" &&
|
|
151
|
-
|
|
152
|
+
`bg-card text-foreground border border-border ${elevation.control} hover:bg-muted border-l-0`,
|
|
152
153
|
variant === "ghost" &&
|
|
153
154
|
"bg-transparent text-foreground border border-transparent hover:bg-muted border-l-0",
|
|
154
155
|
variant === "danger" &&
|
|
155
|
-
|
|
156
|
+
`bg-red-600 text-white border border-red-600 ${elevation.control} hover:bg-red-700 border-l border-l-white/20`,
|
|
156
157
|
// Size heights to match Button
|
|
157
158
|
size === "sm" && "h-9 min-h-9 rounded-lg",
|
|
158
159
|
size === "md" && "h-10 min-h-10 rounded-lg",
|
|
@@ -174,7 +175,8 @@ export const SplitButton = forwardRef<HTMLDivElement, SplitButtonProps>(
|
|
|
174
175
|
role="menu"
|
|
175
176
|
className={cn(
|
|
176
177
|
"absolute right-0 top-full mt-1 z-50",
|
|
177
|
-
"bg-card border border-border rounded-lg
|
|
178
|
+
"bg-card border border-border rounded-lg py-1 min-w-32",
|
|
179
|
+
elevation.popover
|
|
178
180
|
)}
|
|
179
181
|
>
|
|
180
182
|
{children}
|
package/src/button.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { cn } from "./utils";
|
|
4
|
+
import { elevation } from "./elevation";
|
|
4
5
|
import { Loader2 } from "lucide-react";
|
|
5
6
|
import { type ButtonHTMLAttributes, forwardRef } from "react";
|
|
6
7
|
|
|
@@ -14,15 +15,15 @@ export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
|
14
15
|
|
|
15
16
|
const variantClasses: Record<NonNullable<ButtonProps["variant"]>, string> = {
|
|
16
17
|
primary:
|
|
17
|
-
|
|
18
|
+
`bg-primary text-primary-foreground border border-primary ${elevation.control} hover:bg-[var(--primary-hover)]`,
|
|
18
19
|
secondary:
|
|
19
|
-
|
|
20
|
+
`bg-muted text-foreground border border-border ${elevation.control} hover:bg-muted/85 dark:bg-muted/35 dark:text-foreground dark:border-border dark:hover:bg-muted/55`,
|
|
20
21
|
ghost:
|
|
21
22
|
"bg-transparent text-foreground border border-transparent hover:bg-muted focus-visible:bg-muted/80 active:bg-muted/90",
|
|
22
23
|
danger:
|
|
23
|
-
|
|
24
|
+
`bg-red-600 text-white border border-red-600 ${elevation.control} hover:bg-red-700`,
|
|
24
25
|
outline:
|
|
25
|
-
|
|
26
|
+
`bg-card text-foreground border border-border ${elevation.control} hover:bg-muted`,
|
|
26
27
|
link:
|
|
27
28
|
"h-auto min-h-0 rounded-md border border-transparent bg-transparent px-2 py-2 text-[15px] font-medium text-primary shadow-none underline-offset-4 hover:bg-primary/5 hover:underline focus-visible:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35 disabled:pointer-events-none disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:no-underline",
|
|
28
29
|
};
|
package/src/calendar.tsx
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { Button } from "./button";
|
|
13
13
|
import type { ButtonProps } from "./button";
|
|
14
14
|
import { cn } from "./utils";
|
|
15
|
+
import { elevation } from "./elevation";
|
|
15
16
|
|
|
16
17
|
const cellSize = "[--cell-size:2rem]";
|
|
17
18
|
|
|
@@ -22,7 +23,8 @@ function navButtonClasses(variant: NonNullable<ButtonProps["variant"]>): string
|
|
|
22
23
|
case "outline":
|
|
23
24
|
return cn(
|
|
24
25
|
base,
|
|
25
|
-
|
|
26
|
+
elevation.control,
|
|
27
|
+
"border-border bg-card text-foreground hover:bg-muted"
|
|
26
28
|
);
|
|
27
29
|
case "ghost":
|
|
28
30
|
default:
|
|
@@ -100,7 +102,8 @@ function Calendar({
|
|
|
100
102
|
defaultClassNames.dropdowns
|
|
101
103
|
),
|
|
102
104
|
dropdown_root: cn(
|
|
103
|
-
"relative rounded-md border border-border bg-card
|
|
105
|
+
"relative rounded-md border border-border bg-card focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/35",
|
|
106
|
+
elevation.field,
|
|
104
107
|
defaultClassNames.dropdown_root
|
|
105
108
|
),
|
|
106
109
|
dropdown: cn("absolute inset-0 cursor-pointer opacity-0", defaultClassNames.dropdown),
|
package/src/card.tsx
CHANGED
|
@@ -2,8 +2,11 @@ import * as React from "react";
|
|
|
2
2
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
3
|
|
|
4
4
|
import { cn } from "./utils";
|
|
5
|
+
import { elevation } from "./elevation";
|
|
5
6
|
|
|
6
|
-
const cardVariants = cva(
|
|
7
|
+
const cardVariants = cva(
|
|
8
|
+
cn("flex flex-col rounded-xl border border-border bg-card text-card-foreground", elevation.surface),
|
|
9
|
+
{
|
|
7
10
|
variants: {
|
|
8
11
|
size: {
|
|
9
12
|
default: "gap-10 px-10 py-10",
|
package/src/chart-templates.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
Minus,
|
|
22
22
|
} from "lucide-react";
|
|
23
23
|
import { cn } from "./utils";
|
|
24
|
+
import { elevation } from "./elevation";
|
|
24
25
|
import { Button } from "./button";
|
|
25
26
|
import { Separator } from "./separator";
|
|
26
27
|
|
|
@@ -70,7 +71,8 @@ export function MetricSparklineCard({
|
|
|
70
71
|
return (
|
|
71
72
|
<div
|
|
72
73
|
className={cn(
|
|
73
|
-
"relative overflow-hidden rounded-xl border border-border bg-card p-5 text-card-foreground
|
|
74
|
+
"relative overflow-hidden rounded-xl border border-border bg-card p-5 text-card-foreground",
|
|
75
|
+
elevation.surface,
|
|
74
76
|
className
|
|
75
77
|
)}
|
|
76
78
|
>
|
|
@@ -167,7 +169,7 @@ export function DashboardAreaChartTemplate({
|
|
|
167
169
|
const areaGid = React.useId().replace(/:/g, "");
|
|
168
170
|
|
|
169
171
|
return (
|
|
170
|
-
<div className={cn("rounded-xl border border-border bg-card text-card-foreground
|
|
172
|
+
<div className={cn("rounded-xl border border-border bg-card text-card-foreground", elevation.surface, className)}>
|
|
171
173
|
<div className="flex flex-col gap-4 border-b border-border px-5 pt-4 pb-3 sm:flex-row sm:items-start sm:justify-between">
|
|
172
174
|
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
173
175
|
<div className="flex flex-wrap gap-1 rounded-lg border border-border bg-muted/30 p-0.5">
|
|
@@ -285,7 +287,7 @@ export function GroupedBarChartTemplate({
|
|
|
285
287
|
className,
|
|
286
288
|
}: GroupedBarChartTemplateProps) {
|
|
287
289
|
return (
|
|
288
|
-
<div className={cn("rounded-xl border border-border bg-card px-5 pt-4 pb-3 text-card-foreground
|
|
290
|
+
<div className={cn("rounded-xl border border-border bg-card px-5 pt-4 pb-3 text-card-foreground", elevation.surface, className)}>
|
|
289
291
|
<div className="mb-3 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
|
290
292
|
<div>
|
|
291
293
|
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
@@ -362,7 +364,7 @@ export function RankedBarListTemplate({
|
|
|
362
364
|
className,
|
|
363
365
|
}: RankedBarListTemplateProps) {
|
|
364
366
|
return (
|
|
365
|
-
<div className={cn("rounded-xl border border-border bg-card p-5 text-card-foreground
|
|
367
|
+
<div className={cn("rounded-xl border border-border bg-card p-5 text-card-foreground", elevation.surface, className)}>
|
|
366
368
|
<div className="mb-4 flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between">
|
|
367
369
|
<div>
|
|
368
370
|
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
@@ -422,7 +424,7 @@ export function CategoryBarChartTemplate({
|
|
|
422
424
|
const chartData = data.map((d) => ({ name: d.category, v: d.value }));
|
|
423
425
|
|
|
424
426
|
return (
|
|
425
|
-
<div className={cn("rounded-xl border border-border bg-card px-5 pt-4 pb-3 text-card-foreground
|
|
427
|
+
<div className={cn("rounded-xl border border-border bg-card px-5 pt-4 pb-3 text-card-foreground", elevation.surface, className)}>
|
|
426
428
|
<div className="mb-3">
|
|
427
429
|
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
428
430
|
{subtitle ? <p className="mt-0.5 text-xs text-muted-foreground">{subtitle}</p> : null}
|
|
@@ -486,7 +488,7 @@ export function MiniSparklineChartCard({
|
|
|
486
488
|
const hasCompare = data.some((d) => d.compare != null);
|
|
487
489
|
|
|
488
490
|
return (
|
|
489
|
-
<div className={cn("rounded-xl border border-border bg-card p-5 text-card-foreground
|
|
491
|
+
<div className={cn("rounded-xl border border-border bg-card p-5 text-card-foreground", elevation.surface, className)}>
|
|
490
492
|
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
491
493
|
<div className="mt-2 text-2xl font-semibold tabular-nums">{value}</div>
|
|
492
494
|
<div className="mt-2" style={{ height }}>
|
|
@@ -554,7 +556,7 @@ const toneCls = {
|
|
|
554
556
|
|
|
555
557
|
export function AttentionListTemplate({ title, items, className }: AttentionListTemplateProps) {
|
|
556
558
|
return (
|
|
557
|
-
<div className={cn("rounded-xl border border-border bg-card p-5 text-card-foreground
|
|
559
|
+
<div className={cn("rounded-xl border border-border bg-card p-5 text-card-foreground", elevation.surface, className)}>
|
|
558
560
|
<h3 className="text-sm font-semibold text-foreground">{title}</h3>
|
|
559
561
|
<ul className="mt-4 space-y-4">
|
|
560
562
|
{items.map((item) => (
|
package/src/checkbox-select.tsx
CHANGED
|
@@ -5,6 +5,7 @@ import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
|
5
5
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
6
6
|
import { Check, ChevronDown, Search } from "lucide-react";
|
|
7
7
|
import { cn } from "./utils";
|
|
8
|
+
import { elevation } from "./elevation";
|
|
8
9
|
import { filterOptions, type OptionFilter } from "./option-filter";
|
|
9
10
|
import { ScrollLockTakeover } from "./scroll-lock";
|
|
10
11
|
|
|
@@ -114,7 +115,8 @@ const CheckboxSelect = React.forwardRef<HTMLButtonElement, CheckboxSelectProps>(
|
|
|
114
115
|
aria-expanded={open}
|
|
115
116
|
aria-haspopup="listbox"
|
|
116
117
|
className={cn(
|
|
117
|
-
"flex h-11 w-full items-center justify-between gap-2.5 rounded-lg border border-border bg-card px-4 py-2 text-[15px]
|
|
118
|
+
"flex h-11 w-full items-center justify-between gap-2.5 rounded-lg border border-border bg-card px-4 py-2 text-[15px] outline-none",
|
|
119
|
+
elevation.field,
|
|
118
120
|
"transition-colors duration-pg-fast ease-pg-standard",
|
|
119
121
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35",
|
|
120
122
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
@@ -141,7 +143,8 @@ const CheckboxSelect = React.forwardRef<HTMLButtonElement, CheckboxSelectProps>(
|
|
|
141
143
|
sideOffset={6}
|
|
142
144
|
collisionPadding={8}
|
|
143
145
|
className={cn(
|
|
144
|
-
"z-[120] min-w-[var(--radix-popover-trigger-width)] w-full rounded-xl border border-border bg-popover text-popover-foreground
|
|
146
|
+
"z-[120] min-w-[var(--radix-popover-trigger-width)] w-full rounded-xl border border-border bg-popover text-popover-foreground outline-none p-1",
|
|
147
|
+
elevation.popover,
|
|
145
148
|
"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 transition-opacity duration-150"
|
|
146
149
|
)}
|
|
147
150
|
>
|
|
@@ -155,7 +158,8 @@ const CheckboxSelect = React.forwardRef<HTMLButtonElement, CheckboxSelectProps>(
|
|
|
155
158
|
placeholder={searchPlaceholder}
|
|
156
159
|
aria-label={searchPlaceholder}
|
|
157
160
|
className={cn(
|
|
158
|
-
"flex h-9 w-full rounded-md border border-border bg-card pl-8 pr-3 text-sm
|
|
161
|
+
"flex h-9 w-full rounded-md border border-border bg-card pl-8 pr-3 text-sm placeholder:text-muted-foreground",
|
|
162
|
+
elevation.field,
|
|
159
163
|
"transition-colors duration-pg-fast ease-pg-standard",
|
|
160
164
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35"
|
|
161
165
|
)}
|
|
@@ -222,7 +226,8 @@ const CheckboxSelect = React.forwardRef<HTMLButtonElement, CheckboxSelectProps>(
|
|
|
222
226
|
onCheckedChange={() => !option.disabled && handleToggle(option.value)}
|
|
223
227
|
onClick={(e) => e.stopPropagation()}
|
|
224
228
|
className={cn(
|
|
225
|
-
"peer h-4 w-4 shrink-0 rounded-md border border-border bg-card
|
|
229
|
+
"peer h-4 w-4 shrink-0 rounded-md border border-border bg-card",
|
|
230
|
+
elevation.field,
|
|
226
231
|
"transition-colors duration-pg-fast ease-pg-standard",
|
|
227
232
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35",
|
|
228
233
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
package/src/checkbox.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as CheckboxPrimitive from "@radix-ui/react-checkbox";
|
|
5
5
|
import { Check, Minus } from "lucide-react";
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
|
|
8
9
|
export type CheckboxSize = "sm" | "md" | "lg";
|
|
9
10
|
|
|
@@ -30,7 +31,8 @@ const Checkbox = React.forwardRef<
|
|
|
30
31
|
<CheckboxPrimitive.Root
|
|
31
32
|
ref={ref}
|
|
32
33
|
className={cn(
|
|
33
|
-
"peer shrink-0 border border-border bg-card
|
|
34
|
+
"peer shrink-0 border border-border bg-card transition-colors duration-pg-fast ease-pg-standard",
|
|
35
|
+
elevation.field,
|
|
34
36
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35",
|
|
35
37
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
36
38
|
"data-[state=checked]:bg-primary data-[state=checked]:border-primary",
|
package/src/command.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Search } from "lucide-react";
|
|
5
5
|
import { cn } from "./utils";
|
|
6
|
+
import { elevation } from "./elevation";
|
|
6
7
|
|
|
7
8
|
export interface CommandProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
8
9
|
export interface CommandInputProps extends React.InputHTMLAttributes<HTMLInputElement> {}
|
|
@@ -14,7 +15,7 @@ export interface CommandSeparatorProps extends React.HTMLAttributes<HTMLDivEleme
|
|
|
14
15
|
export interface CommandShortcutProps extends React.HTMLAttributes<HTMLSpanElement> {}
|
|
15
16
|
|
|
16
17
|
const Command = React.forwardRef<HTMLDivElement, CommandProps>(({ className, ...props }, ref) => (
|
|
17
|
-
<div ref={ref} className={cn("bg-card rounded-xl border border-border
|
|
18
|
+
<div ref={ref} className={cn("bg-card rounded-xl border border-border overflow-hidden", elevation.popover, className)} {...props} />
|
|
18
19
|
));
|
|
19
20
|
Command.displayName = "Command";
|
|
20
21
|
|
package/src/country-select.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
5
|
import { Check, ChevronDown, Search } from "lucide-react";
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
import { ScrollLockTakeover } from "./scroll-lock";
|
|
8
9
|
|
|
9
10
|
export interface Country {
|
|
@@ -107,7 +108,8 @@ const CountrySelect = React.forwardRef<HTMLButtonElement, CountrySelectProps>(
|
|
|
107
108
|
aria-expanded={open}
|
|
108
109
|
aria-haspopup="listbox"
|
|
109
110
|
className={cn(
|
|
110
|
-
"flex h-11 min-h-11 w-full items-center justify-between gap-2.5 rounded-lg border border-border bg-card px-4 text-[15px] text-foreground
|
|
111
|
+
"flex h-11 min-h-11 w-full items-center justify-between gap-2.5 rounded-lg border border-border bg-card px-4 text-[15px] text-foreground",
|
|
112
|
+
elevation.field,
|
|
111
113
|
"transition-colors duration-pg-fast ease-pg-standard",
|
|
112
114
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35",
|
|
113
115
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
@@ -150,7 +152,8 @@ const CountrySelect = React.forwardRef<HTMLButtonElement, CountrySelectProps>(
|
|
|
150
152
|
align="start"
|
|
151
153
|
sideOffset={6}
|
|
152
154
|
className={cn(
|
|
153
|
-
"z-[120] w-[var(--radix-popover-trigger-width)] min-w-[260px] rounded-xl border border-border bg-popover text-popover-foreground
|
|
155
|
+
"z-[120] w-[var(--radix-popover-trigger-width)] min-w-[260px] rounded-xl border border-border bg-popover text-popover-foreground outline-none",
|
|
156
|
+
elevation.popover,
|
|
154
157
|
"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 transition-opacity duration-150"
|
|
155
158
|
)}
|
|
156
159
|
onOpenAutoFocus={(e) => e.preventDefault()}
|
package/src/data-table.tsx
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { CSSProperties, ReactNode } from "react";
|
|
4
4
|
import { cn } from "./utils";
|
|
5
|
+
import { elevation } from "./elevation";
|
|
5
6
|
import { Shimmer } from "./skeleton";
|
|
6
7
|
import { EmptyState } from "./empty-state";
|
|
7
8
|
import {
|
|
@@ -1066,7 +1067,8 @@ export function DataTable<T>({
|
|
|
1066
1067
|
type="button"
|
|
1067
1068
|
onClick={() => rowCta?.onClick?.(row)}
|
|
1068
1069
|
className={cn(
|
|
1069
|
-
"inline-flex items-center font-medium text-foreground bg-card rounded-lg border border-border hover:border-muted-foreground/50 whitespace-nowrap
|
|
1070
|
+
"inline-flex items-center font-medium text-foreground bg-card rounded-lg border border-border hover:border-muted-foreground/50 whitespace-nowrap",
|
|
1071
|
+
elevation.control,
|
|
1070
1072
|
compact
|
|
1071
1073
|
? "px-2.5 py-1 text-[11px]"
|
|
1072
1074
|
: "px-3 py-1.5 text-[12px]"
|
|
@@ -1146,7 +1148,7 @@ const PAGE_BUTTON =
|
|
|
1146
1148
|
"w-7 h-7 rounded-md flex items-center justify-center text-[12px] font-medium tabular-nums transition-colors";
|
|
1147
1149
|
const PAGE_BUTTON_IDLE =
|
|
1148
1150
|
"text-muted-foreground hover:text-foreground hover:bg-muted disabled:opacity-30 disabled:cursor-not-allowed";
|
|
1149
|
-
const PAGE_BUTTON_ACTIVE =
|
|
1151
|
+
const PAGE_BUTTON_ACTIVE = `bg-primary text-primary-foreground ${elevation.control}`;
|
|
1150
1152
|
|
|
1151
1153
|
/**
|
|
1152
1154
|
* One footer for every pagination mode, so a cursor-paged grid and a
|
package/src/date-picker.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { RemoveScroll } from "react-remove-scroll";
|
|
|
6
6
|
import { ChevronLeft, ChevronRight, ChevronDown, CalendarDays } from "lucide-react";
|
|
7
7
|
import { AnimatePresence, motion } from "framer-motion";
|
|
8
8
|
import { cn } from "./utils";
|
|
9
|
+
import { elevation } from "./elevation";
|
|
9
10
|
import { ScrollLockTakeover } from "./scroll-lock";
|
|
10
11
|
|
|
11
12
|
/* ─── Constants ─────────────────────────────────────────────────────────── */
|
|
@@ -453,7 +454,7 @@ export function DatePicker({ value, onChange, placeholder = "Select date", class
|
|
|
453
454
|
<motion.div
|
|
454
455
|
initial={{ opacity: 0, y: -4 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -4 }}
|
|
455
456
|
transition={{ duration: 0.12 }}
|
|
456
|
-
className=
|
|
457
|
+
className={`absolute top-full left-0 z-[10000] mt-1 max-h-[220px] min-w-[130px] overflow-y-auto rounded-xl border border-border bg-popover py-1 ${elevation.popover}`}
|
|
457
458
|
>
|
|
458
459
|
{MONTHS.map((mn, mi) => (
|
|
459
460
|
<button type="button" key={mn} onClick={() => { setViewMonth(mi); setMonthMenu(false); }}
|
|
@@ -480,7 +481,7 @@ export function DatePicker({ value, onChange, placeholder = "Select date", class
|
|
|
480
481
|
<motion.div
|
|
481
482
|
initial={{ opacity: 0, y: -4 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -4 }}
|
|
482
483
|
transition={{ duration: 0.12 }}
|
|
483
|
-
className=
|
|
484
|
+
className={`absolute top-full left-0 z-[10000] mt-1 max-h-[200px] min-w-[90px] overflow-y-auto rounded-xl border border-border bg-popover py-1 ${elevation.popover}`}
|
|
484
485
|
>
|
|
485
486
|
{years.map(yr => (
|
|
486
487
|
<button type="button" key={yr} onClick={() => { setViewYear(yr); setYearMenu(false); }}
|
|
@@ -601,7 +602,10 @@ export function DatePicker({ value, onChange, placeholder = "Select date", class
|
|
|
601
602
|
animate={{ opacity: 1, scale: 1, y: 0 }}
|
|
602
603
|
exit={{ opacity: 0, scale: 0.97, y: -6 }}
|
|
603
604
|
transition={{ duration: 0.16, ease: [0.16, 1, 0.3, 1] }}
|
|
604
|
-
className=
|
|
605
|
+
className={cn(
|
|
606
|
+
"isolate rounded-2xl border border-border bg-popover text-popover-foreground select-none dark:shadow-black/40",
|
|
607
|
+
elevation.popover
|
|
608
|
+
)}
|
|
605
609
|
style={{
|
|
606
610
|
position: "fixed",
|
|
607
611
|
top: panelPos.top,
|
|
@@ -645,7 +649,8 @@ export function DatePicker({ value, onChange, placeholder = "Select date", class
|
|
|
645
649
|
type="button"
|
|
646
650
|
onClick={() => open ? closePanel() : openPanel()}
|
|
647
651
|
className={cn(
|
|
648
|
-
"flex h-12 min-h-12 w-full items-center gap-3 rounded-xl border border-border bg-card px-5 text-left text-[15px]
|
|
652
|
+
"flex h-12 min-h-12 w-full items-center gap-3 rounded-xl border border-border bg-card px-5 text-left text-[15px] transition-colors",
|
|
653
|
+
elevation.field,
|
|
649
654
|
open ? "border-ring ring-2 ring-ring/20" : "hover:border-muted-foreground/45",
|
|
650
655
|
)}
|
|
651
656
|
>
|
package/src/dialog.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import type { ComponentProps } from "react";
|
|
|
4
4
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
5
|
import { X } from "lucide-react";
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
|
|
8
9
|
const Dialog = DialogPrimitive.Root;
|
|
9
10
|
const DialogTrigger = DialogPrimitive.Trigger;
|
|
@@ -41,7 +42,8 @@ function DialogContent({
|
|
|
41
42
|
"fixed left-1/2 top-1/2 z-[101] w-[calc(100%-1.5rem)] max-w-[min(100%,26rem)] -translate-x-1/2 -translate-y-1/2",
|
|
42
43
|
// Default padding reserves space for the close button.
|
|
43
44
|
// Pass p-0 in className to opt out (manage padding per section).
|
|
44
|
-
"rounded-2xl border border-border bg-card p-6 pt-10 text-card-foreground
|
|
45
|
+
"rounded-2xl border border-border bg-card p-6 pt-10 text-card-foreground outline-none",
|
|
46
|
+
elevation.modal,
|
|
45
47
|
"max-h-[min(90vh,720px)] overflow-y-auto",
|
|
46
48
|
"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 data-[state=open]:scale-100 data-[state=closed]:scale-[0.98]",
|
|
47
49
|
"transition-[opacity,transform] duration-200 ease-out",
|
package/src/drawer.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
5
|
import { X } from "lucide-react";
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
|
|
8
9
|
type DrawerSide = "right" | "left" | "top" | "bottom";
|
|
9
10
|
const DrawerContext = React.createContext<{ side: DrawerSide }>({ side: "right" });
|
|
@@ -49,7 +50,7 @@ const DrawerContent = React.forwardRef<
|
|
|
49
50
|
<DrawerOverlay />
|
|
50
51
|
<DialogPrimitive.Content
|
|
51
52
|
ref={ref}
|
|
52
|
-
className={cn("fixed z-50 flex flex-col bg-card border-border
|
|
53
|
+
className={cn(elevation.drawer, "fixed z-50 flex flex-col bg-card border-border transition-all duration-pg-normal ease-pg-standard data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-200", sideClasses[side], className)}
|
|
53
54
|
{...props}
|
|
54
55
|
>
|
|
55
56
|
{children}
|
package/src/dropdown-menu.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
5
5
|
import { Check, ChevronRight, Circle } from "lucide-react";
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
|
|
8
9
|
const DropdownMenu = DropdownMenuPrimitive.Root;
|
|
9
10
|
const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
@@ -41,7 +42,8 @@ const DropdownMenuSubContent = React.forwardRef<
|
|
|
41
42
|
<DropdownMenuPrimitive.SubContent
|
|
42
43
|
ref={ref}
|
|
43
44
|
className={cn(
|
|
44
|
-
"z-[130] min-w-[8rem] overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground
|
|
45
|
+
"z-[130] min-w-[8rem] overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground",
|
|
46
|
+
elevation.popover,
|
|
45
47
|
"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 transition-opacity duration-150",
|
|
46
48
|
className
|
|
47
49
|
)}
|
|
@@ -59,7 +61,8 @@ const DropdownMenuContent = React.forwardRef<
|
|
|
59
61
|
ref={ref}
|
|
60
62
|
sideOffset={sideOffset}
|
|
61
63
|
className={cn(
|
|
62
|
-
"z-[130] min-w-[11rem] overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground
|
|
64
|
+
"z-[130] min-w-[11rem] overflow-hidden rounded-xl border border-border bg-popover p-1.5 text-popover-foreground",
|
|
65
|
+
elevation.popover,
|
|
63
66
|
"data-[state=open]:opacity-100 data-[state=closed]:opacity-0 transition-opacity duration-150",
|
|
64
67
|
className
|
|
65
68
|
)}
|
package/src/elevation.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Elevation — the one place flux decides what casts a shadow.
|
|
3
|
+
*
|
|
4
|
+
* Shadows used to be typed inline at ~60 call sites, which is how the library
|
|
5
|
+
* drifted: a Card, an Alert and a Skeleton card are the same kind of surface,
|
|
6
|
+
* but nothing held them to the same value, and "the default card shadow" was
|
|
7
|
+
* whatever the last person wrote. These constants are that default, named.
|
|
8
|
+
*
|
|
9
|
+
* They are plain Tailwind utility strings rather than CSS custom properties on
|
|
10
|
+
* purpose. flux ships no stylesheet — consuming apps compile its classes via
|
|
11
|
+
* `@source ".../flux-ui/src"` — so a `--shadow-*` token would have to be
|
|
12
|
+
* redeclared in every app's globals.css and would silently resolve to nothing
|
|
13
|
+
* in any app that forgot. A literal class here is scanned like any other and
|
|
14
|
+
* needs no app-side setup.
|
|
15
|
+
*
|
|
16
|
+
* Changing a value here restyles every component that uses it. That is the point.
|
|
17
|
+
*/
|
|
18
|
+
export const elevation = {
|
|
19
|
+
/**
|
|
20
|
+
* Flat surfaces that sit ON the page and are delineated by their border:
|
|
21
|
+
* Card, chart surfaces, DataTableCard, Alert, SectionMessage, Skeleton.
|
|
22
|
+
* This is the shadow on the mca-home revenue card, and the reference the
|
|
23
|
+
* rest of the library was pulled onto.
|
|
24
|
+
*/
|
|
25
|
+
surface: "shadow-sm",
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Button-shaped things: Button, IconButton, ButtonGroup segments, the
|
|
29
|
+
* pagination page buttons, a table's row CTA. Just enough lift to read as
|
|
30
|
+
* pressable. Ghost and link variants stay flat and do not use this.
|
|
31
|
+
*/
|
|
32
|
+
control: "shadow-sm",
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Anything you type into or pick from: Input, Textarea, every select
|
|
36
|
+
* trigger, the date and time pickers, OTP boxes, Checkbox, Radio.
|
|
37
|
+
*
|
|
38
|
+
* Deliberately flat. A field is a well, not a raised object, and a form of
|
|
39
|
+
* ten shadowed controls reads as clutter. Emitted as an explicit
|
|
40
|
+
* `shadow-none` rather than by omitting the class so that a caller passing
|
|
41
|
+
* `shadow-sm` through `className` still wins under tailwind-merge.
|
|
42
|
+
*/
|
|
43
|
+
field: "shadow-none",
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Things that genuinely float above the page, where depth is the signal that
|
|
47
|
+
* says "this is detached and dismissible". Kept heavier than `surface` on
|
|
48
|
+
* purpose — flattening these is what makes a dropdown look painted on.
|
|
49
|
+
*/
|
|
50
|
+
tooltip: "shadow-md",
|
|
51
|
+
popover: "shadow-lg",
|
|
52
|
+
drawer: "shadow-xl",
|
|
53
|
+
modal: "shadow-2xl",
|
|
54
|
+
} as const;
|
package/src/flag.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Info, CheckCircle2, AlertTriangle, XCircle, X } from "lucide-react";
|
|
5
5
|
import { cn } from "./utils";
|
|
6
|
+
import { elevation } from "./elevation";
|
|
6
7
|
|
|
7
8
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
8
9
|
|
|
@@ -150,7 +151,8 @@ const Flag = React.forwardRef<HTMLDivElement, FlagProps>(
|
|
|
150
151
|
onMouseEnter={handleMouseEnter}
|
|
151
152
|
onMouseLeave={handleMouseLeave}
|
|
152
153
|
className={cn(
|
|
153
|
-
"relative w-80 overflow-hidden rounded-xl border border-border bg-card
|
|
154
|
+
"relative w-80 overflow-hidden rounded-xl border border-border bg-card",
|
|
155
|
+
elevation.popover,
|
|
154
156
|
"border-l-4",
|
|
155
157
|
variantBorder[variant],
|
|
156
158
|
className
|
package/src/icon-button.tsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { cn } from "./utils";
|
|
4
|
+
import { elevation } from "./elevation";
|
|
4
5
|
import { Loader2 } from "lucide-react";
|
|
5
6
|
import { type ButtonHTMLAttributes, forwardRef } from "react";
|
|
6
7
|
|
|
@@ -14,15 +15,15 @@ export interface IconButtonProps extends ButtonHTMLAttributes<HTMLButtonElement>
|
|
|
14
15
|
|
|
15
16
|
const variantClasses: Record<NonNullable<IconButtonProps["variant"]>, string> = {
|
|
16
17
|
primary:
|
|
17
|
-
|
|
18
|
+
`bg-primary text-primary-foreground border border-primary ${elevation.control} hover:bg-[var(--primary-hover)]`,
|
|
18
19
|
secondary:
|
|
19
|
-
|
|
20
|
+
`bg-muted text-foreground border border-border ${elevation.control} hover:bg-muted/85 dark:bg-muted/35 dark:text-foreground dark:border-border dark:hover:bg-muted/55`,
|
|
20
21
|
ghost:
|
|
21
22
|
"bg-transparent text-foreground border border-transparent hover:bg-muted focus-visible:bg-muted/80 active:bg-muted/90",
|
|
22
23
|
outline:
|
|
23
|
-
|
|
24
|
+
`bg-card text-foreground border border-border ${elevation.control} hover:bg-muted`,
|
|
24
25
|
danger:
|
|
25
|
-
|
|
26
|
+
`bg-red-600 text-white border border-red-600 ${elevation.control} hover:bg-red-700`,
|
|
26
27
|
};
|
|
27
28
|
|
|
28
29
|
const sizeClasses: Record<NonNullable<IconButtonProps["size"]>, string> = {
|
package/src/index.ts
CHANGED
package/src/inline-dialog.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
5
5
|
import { X } from "lucide-react";
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
import { ScrollLockTakeover } from "./scroll-lock";
|
|
8
9
|
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
@@ -114,7 +115,8 @@ const InlineDialogContent = React.forwardRef<
|
|
|
114
115
|
className={cn(
|
|
115
116
|
// Base card
|
|
116
117
|
"relative z-[120] max-w-xs overflow-visible",
|
|
117
|
-
"rounded-xl border border-border bg-card p-4 text-foreground
|
|
118
|
+
"rounded-xl border border-border bg-card p-4 text-foreground",
|
|
119
|
+
elevation.popover,
|
|
118
120
|
// Animation
|
|
119
121
|
"data-[state=open]:opacity-100 data-[state=closed]:opacity-0",
|
|
120
122
|
"transition-opacity duration-pg-fast ease-pg-standard",
|
package/src/inline-edit.tsx
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import { Check, Pencil, X } from "lucide-react";
|
|
5
5
|
import { cn } from "./utils";
|
|
6
|
+
import { elevation } from "./elevation";
|
|
6
7
|
|
|
7
8
|
export interface InlineEditProps {
|
|
8
9
|
value: string;
|
|
@@ -93,7 +94,8 @@ const InlineEdit = React.forwardRef<HTMLDivElement, InlineEditProps>(
|
|
|
93
94
|
};
|
|
94
95
|
|
|
95
96
|
const sharedInputClass = cn(
|
|
96
|
-
"w-full rounded-md border border-border bg-card px-2 py-1 text-[15px] leading-tight text-foreground
|
|
97
|
+
"w-full rounded-md border border-border bg-card px-2 py-1 text-[15px] leading-tight text-foreground",
|
|
98
|
+
elevation.field,
|
|
97
99
|
"placeholder:text-muted-foreground",
|
|
98
100
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/35",
|
|
99
101
|
"transition-colors duration-pg-fast ease-pg-standard",
|
package/src/input-group.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
5
5
|
|
|
6
6
|
import { cn } from "./utils";
|
|
7
|
+
import { elevation } from "./elevation";
|
|
7
8
|
import { Input } from "./input";
|
|
8
9
|
import { Textarea } from "./textarea";
|
|
9
10
|
|
|
@@ -13,7 +14,8 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
13
14
|
data-slot="input-group"
|
|
14
15
|
role="group"
|
|
15
16
|
className={cn(
|
|
16
|
-
"group/input-group relative flex w-full items-stretch rounded-xl border border-border bg-card
|
|
17
|
+
"group/input-group relative flex w-full items-stretch rounded-xl border border-border bg-card outline-none transition-[color,box-shadow] dark:bg-card/50",
|
|
18
|
+
elevation.field,
|
|
17
19
|
"min-h-11 h-11 has-[>textarea]:h-auto",
|
|
18
20
|
"has-[>[data-align=inline-start]]:[&>input]:pl-2",
|
|
19
21
|
"has-[>[data-align=inline-end]]:[&>input]:pr-2",
|