@nexub/react 4.2.0 → 4.4.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/accessibility.json +46 -2
- package/dist/components/Button/Button.d.ts +3 -0
- package/dist/components/Button/Button.js +6 -0
- package/dist/components/Card/Card.d.ts +3 -2
- package/dist/components/Card/Card.js +2 -2
- package/dist/components/Celebrate/Celebrate.d.ts +9 -0
- package/dist/components/Celebrate/Celebrate.js +31 -0
- package/dist/components/Celebrate/index.d.ts +2 -0
- package/dist/components/Celebrate/index.js +4 -0
- package/dist/components/FloatingInput/FloatingInput.d.ts +6 -0
- package/dist/components/FloatingInput/FloatingInput.js +30 -0
- package/dist/components/FloatingInput/index.d.ts +2 -0
- package/dist/components/FloatingInput/index.js +4 -0
- package/dist/components/ScrollProgress/ScrollProgress.d.ts +3 -0
- package/dist/components/ScrollProgress/ScrollProgress.js +8 -0
- package/dist/components/ScrollProgress/index.d.ts +2 -0
- package/dist/components/ScrollProgress/index.js +4 -0
- package/dist/components/SharedElement/SharedElement.d.ts +9 -0
- package/dist/components/SharedElement/SharedElement.js +21 -0
- package/dist/components/SharedElement/index.d.ts +2 -0
- package/dist/components/SharedElement/index.js +5 -0
- package/dist/hooks/usePointerSheen.js +2 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +9 -0
- package/dist/styles.css +1 -1
- package/package.json +6 -6
package/accessibility.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.
|
|
2
|
+
"version": "4.4.0",
|
|
3
3
|
"standard": "WCAG 2.2 Level AA",
|
|
4
4
|
"method": {
|
|
5
5
|
"automated": "axe-core rules tagged wcag2a, wcag2aa, wcag21a, wcag21aa and wcag22aa, run on every Storybook story in light and dark on every pull request; a new violation fails the build.",
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"slug": "aurora",
|
|
74
74
|
"name": "Aurora",
|
|
75
75
|
"package": "@nexub/react",
|
|
76
|
-
"automated": "
|
|
76
|
+
"automated": "pass",
|
|
77
77
|
"exceptions": [],
|
|
78
78
|
"keyboard": [],
|
|
79
79
|
"screenReader": [
|
|
@@ -194,6 +194,17 @@
|
|
|
194
194
|
"Prefer CardLink over wrapping the whole card in `<a>`: the link’s accessible name stays the title instead of every word on the card."
|
|
195
195
|
]
|
|
196
196
|
},
|
|
197
|
+
{
|
|
198
|
+
"slug": "celebrate",
|
|
199
|
+
"name": "Celebrate",
|
|
200
|
+
"package": "@nexub/react",
|
|
201
|
+
"automated": "pass",
|
|
202
|
+
"exceptions": [],
|
|
203
|
+
"keyboard": [],
|
|
204
|
+
"screenReader": [
|
|
205
|
+
"Decorative (`aria-hidden`). Announce the win in text, for example in a Toast or a status message."
|
|
206
|
+
]
|
|
207
|
+
},
|
|
197
208
|
{
|
|
198
209
|
"slug": "checkbox-card",
|
|
199
210
|
"name": "Checkbox Card",
|
|
@@ -371,6 +382,17 @@
|
|
|
371
382
|
"keyboard": [],
|
|
372
383
|
"screenReader": []
|
|
373
384
|
},
|
|
385
|
+
{
|
|
386
|
+
"slug": "floating-input",
|
|
387
|
+
"name": "Floating Input",
|
|
388
|
+
"package": "@nexub/react",
|
|
389
|
+
"automated": "pass",
|
|
390
|
+
"exceptions": [],
|
|
391
|
+
"keyboard": [],
|
|
392
|
+
"screenReader": [
|
|
393
|
+
"A real `<label>` tied to the input, so the name never disappears."
|
|
394
|
+
]
|
|
395
|
+
},
|
|
374
396
|
{
|
|
375
397
|
"slug": "form-field",
|
|
376
398
|
"name": "Form Field",
|
|
@@ -719,6 +741,17 @@
|
|
|
719
741
|
"keyboard": [],
|
|
720
742
|
"screenReader": []
|
|
721
743
|
},
|
|
744
|
+
{
|
|
745
|
+
"slug": "scroll-progress",
|
|
746
|
+
"name": "Scroll Progress",
|
|
747
|
+
"package": "@nexub/react",
|
|
748
|
+
"automated": "pass",
|
|
749
|
+
"exceptions": [],
|
|
750
|
+
"keyboard": [],
|
|
751
|
+
"screenReader": [
|
|
752
|
+
"Decorative (`aria-hidden`); hidden under reduced motion and where scroll-driven animations are not supported."
|
|
753
|
+
]
|
|
754
|
+
},
|
|
722
755
|
{
|
|
723
756
|
"slug": "search-input",
|
|
724
757
|
"name": "Search Input",
|
|
@@ -755,6 +788,17 @@
|
|
|
755
788
|
"keyboard": [],
|
|
756
789
|
"screenReader": []
|
|
757
790
|
},
|
|
791
|
+
{
|
|
792
|
+
"slug": "shared-element",
|
|
793
|
+
"name": "Shared Element",
|
|
794
|
+
"package": "@nexub/react",
|
|
795
|
+
"automated": "pass",
|
|
796
|
+
"exceptions": [],
|
|
797
|
+
"keyboard": [],
|
|
798
|
+
"screenReader": [
|
|
799
|
+
"Visual only: run the change in `startViewTransition()` and move focus to the new view as usual."
|
|
800
|
+
]
|
|
801
|
+
},
|
|
758
802
|
{
|
|
759
803
|
"slug": "sheet",
|
|
760
804
|
"name": "Sheet",
|
|
@@ -26,5 +26,8 @@ export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElemen
|
|
|
26
26
|
leadingIcon?: React.ReactNode;
|
|
27
27
|
trailingIcon?: React.ReactNode;
|
|
28
28
|
asChild?: boolean;
|
|
29
|
+
/** 4.3: the button drifts a few pixels toward the pointer on hover. For one key action per screen.
|
|
30
|
+
* Off for touch and reduced motion. */
|
|
31
|
+
magnetic?: boolean;
|
|
29
32
|
}
|
|
30
33
|
export declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
|
|
@@ -4,6 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import { Slot } from "@radix-ui/react-slot";
|
|
5
5
|
import { Loader2 } from "@nexub/icons";
|
|
6
6
|
import { cva } from "class-variance-authority";
|
|
7
|
+
import { usePointerSheen } from "../../hooks/usePointerSheen.js";
|
|
7
8
|
import { cn } from "../../utils/cn.js";
|
|
8
9
|
import { disabledContainer, disabledText, pressable } from "../../utils/control.js";
|
|
9
10
|
import { glassSurfaceClass } from "../../utils/glass.js";
|
|
@@ -81,19 +82,24 @@ const Button = React.forwardRef(
|
|
|
81
82
|
leadingIcon,
|
|
82
83
|
trailingIcon,
|
|
83
84
|
asChild = false,
|
|
85
|
+
magnetic = false,
|
|
84
86
|
disabled,
|
|
85
87
|
onClick,
|
|
88
|
+
onPointerMove,
|
|
86
89
|
children,
|
|
87
90
|
...props
|
|
88
91
|
}, ref) => {
|
|
89
92
|
const status = statusProp ?? (loading ? "loading" : "idle");
|
|
93
|
+
const handlePointerMove = usePointerSheen(magnetic, onPointerMove);
|
|
90
94
|
const sharedProps = {
|
|
91
95
|
className: cn(
|
|
92
96
|
buttonVariants({ variant, size, fullWidth }),
|
|
93
97
|
tone ? toneClasses[tone] : "",
|
|
94
98
|
!asChild && status === "error" && "nx-shake",
|
|
99
|
+
magnetic && "nx-magnetic",
|
|
95
100
|
className
|
|
96
101
|
),
|
|
102
|
+
onPointerMove: handlePointerMove,
|
|
97
103
|
"data-tone": tone,
|
|
98
104
|
// Styling hook for opt-in layers (vivid, marketing); no styles depend on it by default.
|
|
99
105
|
"data-variant": variant ?? "primary",
|
|
@@ -8,8 +8,9 @@ export declare const cardVariants: (props?: ({
|
|
|
8
8
|
export interface CardProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardVariants> {
|
|
9
9
|
tone?: ToneValue;
|
|
10
10
|
surface?: 'solid' | 'glass';
|
|
11
|
-
/** `sheen`: a soft light follows the pointer (
|
|
12
|
-
|
|
11
|
+
/** `sheen`: a soft light follows the pointer. `tilt` (4.3): the card also leans toward the pointer in 3D.
|
|
12
|
+
* Opt-in; off for touch and reduced motion. */
|
|
13
|
+
effect?: 'none' | 'sheen' | 'tilt';
|
|
13
14
|
/** Product accent for `variant="showcase"`: a coloured top edge and tint in marketing mode. Sets `data-product`. */
|
|
14
15
|
accent?: 'leap' | 'bookub' | 'payub' | 'founderub';
|
|
15
16
|
asChild?: boolean;
|
|
@@ -52,13 +52,13 @@ const Card = React.forwardRef(
|
|
|
52
52
|
({ className, variant = "flat", surface = "solid", effect = "none", tone, accent, asChild = false, onPointerMove, children, ...props }, ref) => {
|
|
53
53
|
const Comp = asChild ? Slot : "div";
|
|
54
54
|
const toneStyle = tone ? toneBorderMap[tone] : "";
|
|
55
|
-
const sheen = effect === "sheen";
|
|
55
|
+
const sheen = effect === "sheen" || effect === "tilt";
|
|
56
56
|
const handlePointerMove = usePointerSheen(sheen, onPointerMove);
|
|
57
57
|
return /* @__PURE__ */ jsx(
|
|
58
58
|
Comp,
|
|
59
59
|
{
|
|
60
60
|
ref,
|
|
61
|
-
className: cn(cardVariants({ variant, surface }), toneStyle, sheen && "nx-sheen", className),
|
|
61
|
+
className: cn(cardVariants({ variant, surface }), toneStyle, sheen && "nx-sheen", effect === "tilt" && "nx-tilt", className),
|
|
62
62
|
onPointerMove: handlePointerMove,
|
|
63
63
|
"data-tone": tone,
|
|
64
64
|
"data-variant": variant ?? "flat",
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface CelebrateProps {
|
|
3
|
+
/** Bursts each time this goes from false to true. */
|
|
4
|
+
active: boolean;
|
|
5
|
+
/** Number of pieces. @default 28 */
|
|
6
|
+
count?: number;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function Celebrate({ active, count, className }: CelebrateProps): React.JSX.Element | null;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { cn } from "../../utils/cn.js";
|
|
5
|
+
const COLORS = ["bg-chart-1", "bg-chart-2", "bg-chart-3", "bg-chart-4", "bg-chart-5", "bg-chart-6", "bg-chart-7", "bg-chart-8"];
|
|
6
|
+
function Celebrate({ active, count = 28, className }) {
|
|
7
|
+
const ref = React.useRef(null);
|
|
8
|
+
const [burst, setBurst] = React.useState(0);
|
|
9
|
+
const was = React.useRef(false);
|
|
10
|
+
React.useEffect(() => {
|
|
11
|
+
if (active && !was.current) setBurst((b) => b + 1);
|
|
12
|
+
was.current = active;
|
|
13
|
+
}, [active]);
|
|
14
|
+
React.useLayoutEffect(() => {
|
|
15
|
+
const el = ref.current;
|
|
16
|
+
if (!burst || !el) return;
|
|
17
|
+
for (const piece of Array.from(el.children)) {
|
|
18
|
+
const angle = Math.random() * Math.PI * 2;
|
|
19
|
+
const distance = 60 + Math.random() * 90;
|
|
20
|
+
piece.style.setProperty("--x", `${Math.cos(angle) * distance}px`);
|
|
21
|
+
piece.style.setProperty("--y", `${Math.sin(angle) * distance + 40}px`);
|
|
22
|
+
piece.style.setProperty("--r", `${Math.random() * 540 - 270}deg`);
|
|
23
|
+
piece.style.setProperty("--d", `${Math.random() * 90}ms`);
|
|
24
|
+
}
|
|
25
|
+
}, [burst]);
|
|
26
|
+
if (!burst) return null;
|
|
27
|
+
return /* @__PURE__ */ jsx("div", { ref, "aria-hidden": "true", className: cn("nx-celebrate", className), children: Array.from({ length: count }, (_, i) => /* @__PURE__ */ jsx("span", { className: COLORS[i % COLORS.length] }, i)) }, burst);
|
|
28
|
+
}
|
|
29
|
+
export {
|
|
30
|
+
Celebrate
|
|
31
|
+
};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface FloatingInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'placeholder'> {
|
|
3
|
+
/** Always visible: inside the field when empty, above the value otherwise. */
|
|
4
|
+
label: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const FloatingInput: React.ForwardRefExoticComponent<FloatingInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
const FloatingInput = React.forwardRef(
|
|
5
|
+
({ label, id, className, ...props }, ref) => {
|
|
6
|
+
const auto = React.useId();
|
|
7
|
+
const inputId = id ?? auto;
|
|
8
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("nx-float-field relative", className), children: [
|
|
9
|
+
/* @__PURE__ */ jsx(
|
|
10
|
+
"input",
|
|
11
|
+
{
|
|
12
|
+
ref,
|
|
13
|
+
id: inputId,
|
|
14
|
+
placeholder: " ",
|
|
15
|
+
className: cn(
|
|
16
|
+
"h-14 w-full rounded-shape-field border border-[var(--color-outline)] bg-[var(--color-surface)] px-4 pt-5 text-body-md text-[var(--color-on-surface)]",
|
|
17
|
+
"outline-none transition-[border-color,box-shadow] focus-visible:border-[var(--color-primary)] focus-visible:shadow-[0_0_0_1px_var(--color-primary)]",
|
|
18
|
+
"aria-[invalid=true]:border-[var(--color-error)] disabled:cursor-not-allowed disabled:border-[var(--color-outline-variant)] disabled:text-[var(--color-text-disabled)]"
|
|
19
|
+
),
|
|
20
|
+
...props
|
|
21
|
+
}
|
|
22
|
+
),
|
|
23
|
+
/* @__PURE__ */ jsx("label", { htmlFor: inputId, children: label })
|
|
24
|
+
] });
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
FloatingInput.displayName = "FloatingInput";
|
|
28
|
+
export {
|
|
29
|
+
FloatingInput
|
|
30
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { cn } from "../../utils/cn.js";
|
|
4
|
+
const ScrollProgress = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, "aria-hidden": "true", className: cn("nx-scroll-progress", className), ...props }));
|
|
5
|
+
ScrollProgress.displayName = "ScrollProgress";
|
|
6
|
+
export {
|
|
7
|
+
ScrollProgress
|
|
8
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
export interface SharedElementProps {
|
|
3
|
+
/** Same name on both sides of the transition, e.g. `avatar-${user.id}`. */
|
|
4
|
+
name: string;
|
|
5
|
+
children: React.ReactElement;
|
|
6
|
+
}
|
|
7
|
+
/** Turns any string into a valid <custom-ident> for view-transition-name. */
|
|
8
|
+
export declare const transitionName: (name: string) => string;
|
|
9
|
+
export declare function SharedElement({ name, children }: SharedElementProps): React.JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
5
|
+
const transitionName = (name) => `nx-${name.replace(/[^a-zA-Z0-9_-]/g, "-")}`;
|
|
6
|
+
function SharedElement({ name, children }) {
|
|
7
|
+
const ref = React.useRef(null);
|
|
8
|
+
React.useLayoutEffect(() => {
|
|
9
|
+
const el = ref.current;
|
|
10
|
+
if (!el) return;
|
|
11
|
+
el.style.setProperty("view-transition-name", transitionName(name));
|
|
12
|
+
return () => {
|
|
13
|
+
el.style.removeProperty("view-transition-name");
|
|
14
|
+
};
|
|
15
|
+
}, [name]);
|
|
16
|
+
return /* @__PURE__ */ jsx(Slot, { ref, children });
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
SharedElement,
|
|
20
|
+
transitionName
|
|
21
|
+
};
|
|
@@ -14,6 +14,8 @@ function usePointerSheen(enabled, onPointerMove) {
|
|
|
14
14
|
const r = el.getBoundingClientRect();
|
|
15
15
|
el.style.setProperty("--nx-mx", `${clientX - r.left}px`);
|
|
16
16
|
el.style.setProperty("--nx-my", `${clientY - r.top}px`);
|
|
17
|
+
el.style.setProperty("--nx-px", String(r.width ? (clientX - r.left) / r.width : 0.5));
|
|
18
|
+
el.style.setProperty("--nx-py", String(r.height ? (clientY - r.top) / r.height : 0.5));
|
|
17
19
|
});
|
|
18
20
|
},
|
|
19
21
|
[onPointerMove]
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,14 @@ export type { NumberInputProps } from './components/NumberInput/index.js';
|
|
|
36
36
|
export { MoneyInput } from './components/MoneyInput/index.js';
|
|
37
37
|
export type { MoneyInputProps } from './components/MoneyInput/index.js';
|
|
38
38
|
export { Amount } from './components/Amount/index.js';
|
|
39
|
+
export { SharedElement, transitionName } from './components/SharedElement/index.js';
|
|
40
|
+
export type { SharedElementProps } from './components/SharedElement/index.js';
|
|
41
|
+
export { ScrollProgress } from './components/ScrollProgress/index.js';
|
|
42
|
+
export type { ScrollProgressProps } from './components/ScrollProgress/index.js';
|
|
43
|
+
export { FloatingInput } from './components/FloatingInput/index.js';
|
|
44
|
+
export type { FloatingInputProps } from './components/FloatingInput/index.js';
|
|
45
|
+
export { Celebrate } from './components/Celebrate/index.js';
|
|
46
|
+
export type { CelebrateProps } from './components/Celebrate/index.js';
|
|
39
47
|
export { Aurora } from './components/Aurora/index.js';
|
|
40
48
|
export type { AuroraProps } from './components/Aurora/index.js';
|
|
41
49
|
export { BarChart, LineChart, DonutChart, Sparkline, ChartLegend, niceMax, niceDomain } from './components/Chart/index.js';
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,10 @@ import { Textarea, textareaVariants } from "./components/Textarea/index.js";
|
|
|
21
21
|
import { NumberInput } from "./components/NumberInput/index.js";
|
|
22
22
|
import { MoneyInput } from "./components/MoneyInput/index.js";
|
|
23
23
|
import { Amount } from "./components/Amount/index.js";
|
|
24
|
+
import { SharedElement, transitionName } from "./components/SharedElement/index.js";
|
|
25
|
+
import { ScrollProgress } from "./components/ScrollProgress/index.js";
|
|
26
|
+
import { FloatingInput } from "./components/FloatingInput/index.js";
|
|
27
|
+
import { Celebrate } from "./components/Celebrate/index.js";
|
|
24
28
|
import { Aurora } from "./components/Aurora/index.js";
|
|
25
29
|
import { BarChart, LineChart, DonutChart, Sparkline, ChartLegend, niceMax, niceDomain } from "./components/Chart/index.js";
|
|
26
30
|
import { NumberTicker } from "./components/NumberTicker/index.js";
|
|
@@ -131,6 +135,7 @@ export {
|
|
|
131
135
|
ButtonGroup,
|
|
132
136
|
Card,
|
|
133
137
|
CardLink,
|
|
138
|
+
Celebrate,
|
|
134
139
|
ChartLegend,
|
|
135
140
|
CheckList,
|
|
136
141
|
CheckListItem,
|
|
@@ -162,6 +167,7 @@ export {
|
|
|
162
167
|
Flag,
|
|
163
168
|
Flex,
|
|
164
169
|
Float,
|
|
170
|
+
FloatingInput,
|
|
165
171
|
FormError,
|
|
166
172
|
FormField,
|
|
167
173
|
GradientDivider,
|
|
@@ -202,11 +208,13 @@ export {
|
|
|
202
208
|
Reveal,
|
|
203
209
|
Scale,
|
|
204
210
|
ScrollArea,
|
|
211
|
+
ScrollProgress,
|
|
205
212
|
SearchInput,
|
|
206
213
|
Section,
|
|
207
214
|
SegmentedControl,
|
|
208
215
|
Select,
|
|
209
216
|
Separator,
|
|
217
|
+
SharedElement,
|
|
210
218
|
Sheet,
|
|
211
219
|
Sidebar,
|
|
212
220
|
Skeleton,
|
|
@@ -272,6 +280,7 @@ export {
|
|
|
272
280
|
textareaVariants,
|
|
273
281
|
toast,
|
|
274
282
|
toggleVariants,
|
|
283
|
+
transitionName,
|
|
275
284
|
useControllableState,
|
|
276
285
|
useDisclosure,
|
|
277
286
|
useIsomorphicLayoutEffect,
|