@passui/react 0.1.39 → 0.1.40
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/types/src/components/code-block/code-block.d.ts +5 -2
- package/dist/types/src/components/one-time-code/one-time-code.d.ts +4 -1
- package/dist/types/src/components/popover/popover.d.ts +3 -0
- package/dist/types/src/components/search-list/search-list.d.ts +4 -1
- package/dist/types/src/components/sidebar/sidebar.d.ts +8 -3
- package/dist/types/src/components/slosh-slider/slosh-slider.d.ts +4 -1
- package/dist/types/src/components/tooltip/tooltip-host.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/code-block/code-block.tsx +7 -1
- package/src/components/one-time-code/one-time-code.tsx +22 -2
- package/src/components/popover/popover.tsx +15 -2
- package/src/components/search-list/search-list.tsx +27 -9
- package/src/components/sidebar/sidebar.tsx +38 -11
- package/src/components/slosh-slider/slosh-slider.tsx +6 -0
- package/src/components/tooltip/tooltip-host.tsx +14 -1
- package/src/styles/code-block.css +12 -0
- package/src/styles/motion-controls.css +8 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { type ComponentProps, type ReactNode } from "react";
|
|
2
|
-
export
|
|
2
|
+
export type CodeBlockSize = "sm" | "md" | "lg";
|
|
3
|
+
export interface CodeBlockProps extends Omit<ComponentProps<"div">, "children" | "size"> {
|
|
3
4
|
code: string;
|
|
4
5
|
/** Supply a highlighter's rendered pre/code element; source remains the fallback. */
|
|
5
6
|
children?: ReactNode;
|
|
6
7
|
/** Show a nonselectable line gutter. @default true */
|
|
7
8
|
lineNumbers?: boolean;
|
|
9
|
+
/** Code font size, padding and line gutter scale. @default "md" */
|
|
10
|
+
size?: CodeBlockSize;
|
|
8
11
|
}
|
|
9
12
|
/** Code presentation without shipping a highlighter or formatter to consumers. */
|
|
10
|
-
export declare function CodeBlock({ code, children, lineNumbers, className, ...props }: CodeBlockProps): import("react").JSX.Element;
|
|
13
|
+
export declare function CodeBlock({ code, children, lineNumbers, size, className, ...props }: CodeBlockProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type InputHTMLAttributes, type Ref } from "react";
|
|
2
2
|
export type OneTimeCodeStatus = "idle" | "verifying" | "success" | "error";
|
|
3
3
|
export type OneTimeCodeFormat = "numeric" | "alphanumeric";
|
|
4
|
+
export type OneTimeCodeSize = "sm" | "md" | "lg";
|
|
4
5
|
export interface OneTimeCodeProps extends Omit<InputHTMLAttributes<HTMLInputElement>, "value" | "defaultValue" | "onChange" | "type" | "maxLength" | "size"> {
|
|
5
6
|
label: string;
|
|
6
7
|
value: string;
|
|
@@ -16,8 +17,10 @@ export interface OneTimeCodeProps extends Omit<InputHTMLAttributes<HTMLInputElem
|
|
|
16
17
|
status?: OneTimeCodeStatus;
|
|
17
18
|
/** Localized verification feedback announced without reading decorative cells. */
|
|
18
19
|
statusLabel?: string;
|
|
20
|
+
/** Cell height, digit text scale and check icon size. @default "md" */
|
|
21
|
+
size?: OneTimeCodeSize;
|
|
19
22
|
}
|
|
20
23
|
/** Normalize common numeric keyboards and optional ASCII letters in one native field. */
|
|
21
24
|
export declare function normalizeCode(value: string, length: number, format?: OneTimeCodeFormat): string;
|
|
22
25
|
/** Six cells, one field: paste, autofill, selection and Backspace stay native. */
|
|
23
|
-
export declare function OneTimeCode({ label, value, onChange, onComplete, length, format, ref, status, statusLabel, className, disabled, id: suppliedId, ...props }: OneTimeCodeProps): import("react").JSX.Element;
|
|
26
|
+
export declare function OneTimeCode({ label, value, onChange, onComplete, length, format, ref, status, statusLabel, size, className, disabled, id: suppliedId, ...props }: OneTimeCodeProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type HTMLMotionProps } from "motion/react";
|
|
2
2
|
import { type ReactNode, type RefObject } from "react";
|
|
3
3
|
export type PopoverOrigin = "top-left" | "top-right";
|
|
4
|
+
export type PopoverSize = "sm" | "md";
|
|
4
5
|
export interface PopoverProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
5
6
|
open: boolean;
|
|
6
7
|
children?: ReactNode;
|
|
@@ -8,6 +9,8 @@ export interface PopoverProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
|
8
9
|
origin?: PopoverOrigin;
|
|
9
10
|
/** Trigger whose shape the popover uses during its opening and closing motion. */
|
|
10
11
|
anchorRef?: RefObject<HTMLElement | null>;
|
|
12
|
+
/** Surface corner radius. @default "md" */
|
|
13
|
+
size?: PopoverSize;
|
|
11
14
|
}
|
|
12
15
|
/**
|
|
13
16
|
* A glass surface that springs open from its anchor. Position it with
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { type ComponentProps } from "react";
|
|
2
|
-
export
|
|
2
|
+
export type SearchListSize = "sm" | "md" | "lg";
|
|
3
|
+
export declare function SearchList({ loop, open, size, onKeyDown, className, ...props }: ComponentProps<"div"> & {
|
|
3
4
|
loop?: boolean;
|
|
4
5
|
open?: boolean;
|
|
6
|
+
/** Field, item and heading density. @default "md" */
|
|
7
|
+
size?: SearchListSize;
|
|
5
8
|
}): import("react").JSX.Element;
|
|
6
9
|
export declare function SearchListField({ value, onValueChange, className, ...props }: Omit<ComponentProps<"input">, "value" | "onChange"> & {
|
|
7
10
|
value?: string;
|
|
@@ -12,8 +12,13 @@ export interface SidebarItemContentProps {
|
|
|
12
12
|
active?: boolean;
|
|
13
13
|
children: ReactNode;
|
|
14
14
|
}
|
|
15
|
-
export declare function SidebarSection({ title, children }: SidebarSectionProps
|
|
15
|
+
export declare function SidebarSection({ title, children, size, }: SidebarSectionProps & {
|
|
16
|
+
size?: SidebarSize;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
16
18
|
/** Classes for a sidebar row; apply to the app's router link. */
|
|
17
19
|
export type SidebarDensity = "regular" | "compact";
|
|
18
|
-
export
|
|
19
|
-
export declare function
|
|
20
|
+
export type SidebarSize = "sm" | "md" | "lg";
|
|
21
|
+
export declare function sidebarItemClass(active?: boolean, density?: SidebarDensity, size?: SidebarSize): string;
|
|
22
|
+
export declare function SidebarItemContent({ icon, count, active, size, children, }: SidebarItemContentProps & {
|
|
23
|
+
size?: SidebarSize;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type SloshSliderSize = "sm" | "md" | "lg";
|
|
1
2
|
export interface SloshSliderProps {
|
|
2
3
|
label: string;
|
|
3
4
|
value?: number;
|
|
@@ -11,8 +12,10 @@ export interface SloshSliderProps {
|
|
|
11
12
|
tilt?: number;
|
|
12
13
|
/** Track corner, from square to a pill, 0–20. @default 20 */
|
|
13
14
|
corner?: number;
|
|
15
|
+
/** Track height and knob scale. @default "md" */
|
|
16
|
+
size?: SloshSliderSize;
|
|
14
17
|
disabled?: boolean;
|
|
15
18
|
className?: string;
|
|
16
19
|
formatValue?: (value: number) => string;
|
|
17
20
|
}
|
|
18
|
-
export declare function SloshSlider({ label, value, defaultValue, onChange, viscosity, momentum, tilt, corner, disabled, className, formatValue, }: SloshSliderProps): import("react").JSX.Element;
|
|
21
|
+
export declare function SloshSlider({ label, value, defaultValue, onChange, viscosity, momentum, tilt, corner, size, disabled, className, formatValue, }: SloshSliderProps): import("react").JSX.Element;
|
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { Fragment, type ComponentProps, type ReactNode } from "react"
|
|
2
2
|
import { cn } from "../../lib/class-names"
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export type CodeBlockSize = "sm" | "md" | "lg"
|
|
5
|
+
|
|
6
|
+
export interface CodeBlockProps extends Omit<ComponentProps<"div">, "children" | "size"> {
|
|
5
7
|
code: string
|
|
6
8
|
/** Supply a highlighter's rendered pre/code element; source remains the fallback. */
|
|
7
9
|
children?: ReactNode
|
|
8
10
|
/** Show a nonselectable line gutter. @default true */
|
|
9
11
|
lineNumbers?: boolean
|
|
12
|
+
/** Code font size, padding and line gutter scale. @default "md" */
|
|
13
|
+
size?: CodeBlockSize
|
|
10
14
|
}
|
|
11
15
|
|
|
12
16
|
/** Code presentation without shipping a highlighter or formatter to consumers. */
|
|
@@ -14,6 +18,7 @@ export function CodeBlock({
|
|
|
14
18
|
code,
|
|
15
19
|
children,
|
|
16
20
|
lineNumbers = true,
|
|
21
|
+
size = "md",
|
|
17
22
|
className,
|
|
18
23
|
...props
|
|
19
24
|
}: CodeBlockProps) {
|
|
@@ -23,6 +28,7 @@ export function CodeBlock({
|
|
|
23
28
|
{...props}
|
|
24
29
|
className={cn("passui-code-block", className)}
|
|
25
30
|
data-line-numbers={lineNumbers || undefined}
|
|
31
|
+
data-size={size}
|
|
26
32
|
>
|
|
27
33
|
{children ?? (
|
|
28
34
|
<pre>
|
|
@@ -11,6 +11,18 @@ import { cn } from "../../lib/class-names"
|
|
|
11
11
|
|
|
12
12
|
export type OneTimeCodeStatus = "idle" | "verifying" | "success" | "error"
|
|
13
13
|
export type OneTimeCodeFormat = "numeric" | "alphanumeric"
|
|
14
|
+
export type OneTimeCodeSize = "sm" | "md" | "lg"
|
|
15
|
+
|
|
16
|
+
const checkSizes: Record<OneTimeCodeSize, number> = {
|
|
17
|
+
sm: 20,
|
|
18
|
+
md: 24,
|
|
19
|
+
lg: 28,
|
|
20
|
+
}
|
|
21
|
+
const statusText: Record<OneTimeCodeSize, string> = {
|
|
22
|
+
sm: "mt-1.5 text-caption2",
|
|
23
|
+
md: "mt-2 text-footnote",
|
|
24
|
+
lg: "mt-2.5 text-subheadline",
|
|
25
|
+
}
|
|
14
26
|
export interface OneTimeCodeProps
|
|
15
27
|
extends Omit<
|
|
16
28
|
InputHTMLAttributes<HTMLInputElement>,
|
|
@@ -30,6 +42,8 @@ export interface OneTimeCodeProps
|
|
|
30
42
|
status?: OneTimeCodeStatus
|
|
31
43
|
/** Localized verification feedback announced without reading decorative cells. */
|
|
32
44
|
statusLabel?: string
|
|
45
|
+
/** Cell height, digit text scale and check icon size. @default "md" */
|
|
46
|
+
size?: OneTimeCodeSize
|
|
33
47
|
}
|
|
34
48
|
|
|
35
49
|
/** Normalize common numeric keyboards and optional ASCII letters in one native field. */
|
|
@@ -60,6 +74,7 @@ export function OneTimeCode({
|
|
|
60
74
|
ref,
|
|
61
75
|
status = "idle",
|
|
62
76
|
statusLabel,
|
|
77
|
+
size = "md",
|
|
63
78
|
className,
|
|
64
79
|
disabled,
|
|
65
80
|
id: suppliedId,
|
|
@@ -85,6 +100,7 @@ export function OneTimeCode({
|
|
|
85
100
|
<div
|
|
86
101
|
className={cn("passui-one-time-code not-typeset", className)}
|
|
87
102
|
data-status={status}
|
|
103
|
+
data-size={size}
|
|
88
104
|
style={{ "--otp-length": count, "--otp-middle": (count - 1) / 2 } as CSSProperties}
|
|
89
105
|
>
|
|
90
106
|
<svg aria-hidden="true" width="0" height="0" className="absolute">
|
|
@@ -139,7 +155,11 @@ export function OneTimeCode({
|
|
|
139
155
|
</span>
|
|
140
156
|
))}
|
|
141
157
|
</div>
|
|
142
|
-
<CheckIcon
|
|
158
|
+
<CheckIcon
|
|
159
|
+
aria-hidden="true"
|
|
160
|
+
className="passui-otp-check"
|
|
161
|
+
size={checkSizes[size]}
|
|
162
|
+
/>
|
|
143
163
|
<input
|
|
144
164
|
{...props}
|
|
145
165
|
ref={(node) => {
|
|
@@ -186,7 +206,7 @@ export function OneTimeCode({
|
|
|
186
206
|
id={`${id}-status`}
|
|
187
207
|
role="status"
|
|
188
208
|
className={cn(
|
|
189
|
-
|
|
209
|
+
statusText[size],
|
|
190
210
|
status === "error" ? "text-danger" : "text-label-secondary"
|
|
191
211
|
)}
|
|
192
212
|
>
|
|
@@ -19,6 +19,12 @@ import {
|
|
|
19
19
|
import { fitPopover, type PopoverFit } from "./popover-fit"
|
|
20
20
|
|
|
21
21
|
export type PopoverOrigin = "top-left" | "top-right"
|
|
22
|
+
export type PopoverSize = "sm" | "md"
|
|
23
|
+
|
|
24
|
+
const surfaceRadius: Record<PopoverSize, string> = {
|
|
25
|
+
sm: "rounded-3xl",
|
|
26
|
+
md: "rounded-5xl",
|
|
27
|
+
}
|
|
22
28
|
|
|
23
29
|
export interface PopoverProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
24
30
|
open: boolean
|
|
@@ -27,6 +33,8 @@ export interface PopoverProps extends Omit<HTMLMotionProps<"div">, "children"> {
|
|
|
27
33
|
origin?: PopoverOrigin
|
|
28
34
|
/** Trigger whose shape the popover uses during its opening and closing motion. */
|
|
29
35
|
anchorRef?: RefObject<HTMLElement | null>
|
|
36
|
+
/** Surface corner radius. @default "md" */
|
|
37
|
+
size?: PopoverSize
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
/**
|
|
@@ -42,6 +50,7 @@ export function Popover({ open, ...props }: PopoverProps) {
|
|
|
42
50
|
function OpenPopover({
|
|
43
51
|
origin = "top-left",
|
|
44
52
|
anchorRef,
|
|
53
|
+
size = "md",
|
|
45
54
|
className,
|
|
46
55
|
style,
|
|
47
56
|
children,
|
|
@@ -259,7 +268,8 @@ function OpenPopover({
|
|
|
259
268
|
anchorRef
|
|
260
269
|
? "isolate [&>:not([data-popover-surface])]:opacity-[var(--popover-content-opacity)]"
|
|
261
270
|
: "glass-menu",
|
|
262
|
-
"absolute z-40
|
|
271
|
+
"absolute z-40 outline-none",
|
|
272
|
+
surfaceRadius[size],
|
|
263
273
|
className
|
|
264
274
|
)}
|
|
265
275
|
{...props}
|
|
@@ -272,7 +282,10 @@ function OpenPopover({
|
|
|
272
282
|
onAnimationComplete={() => {
|
|
273
283
|
if (reveal) setSettled(true)
|
|
274
284
|
}}
|
|
275
|
-
className=
|
|
285
|
+
className={cn(
|
|
286
|
+
"glass-menu pointer-events-none absolute top-0 left-0 -z-10",
|
|
287
|
+
surfaceRadius[size]
|
|
288
|
+
)}
|
|
276
289
|
style={{ width: "100%", height: "100%" }}
|
|
277
290
|
initial={reducedMotion || !reveal ? false : reveal}
|
|
278
291
|
animate={
|
|
@@ -10,6 +10,24 @@ import {
|
|
|
10
10
|
} from "react"
|
|
11
11
|
import { cn } from "../../lib/class-names"
|
|
12
12
|
|
|
13
|
+
export type SearchListSize = "sm" | "md" | "lg"
|
|
14
|
+
|
|
15
|
+
const fieldSizes: Record<SearchListSize, string> = {
|
|
16
|
+
sm: "min-h-8 w-full rounded-full border border-separator bg-fill/60 px-3 text-caption text-label outline-none focus-visible:ring-2 focus-visible:ring-accent-text/50",
|
|
17
|
+
md: "min-h-11 w-full rounded-full border border-separator bg-fill/60 px-4 text-subheadline text-label outline-none focus-visible:ring-2 focus-visible:ring-accent-text/50",
|
|
18
|
+
lg: "min-h-13 w-full rounded-full border border-separator bg-fill/60 px-5 text-body text-label outline-none focus-visible:ring-2 focus-visible:ring-accent-text/50",
|
|
19
|
+
}
|
|
20
|
+
const headingSizes: Record<SearchListSize, string> = {
|
|
21
|
+
sm: "px-1.5 py-1.5 text-caption2 font-medium text-label-tertiary",
|
|
22
|
+
md: "px-2 py-2 text-caption font-medium text-label-tertiary",
|
|
23
|
+
lg: "px-2.5 py-2.5 text-subheadline font-medium text-label-tertiary",
|
|
24
|
+
}
|
|
25
|
+
const itemSizes: Record<SearchListSize, string> = {
|
|
26
|
+
sm: "flex min-h-8 w-full items-center gap-1.5 rounded-md px-2 py-1 text-left text-footnote text-label hover:bg-fill aria-selected:bg-fill [@media(pointer:coarse)]:min-h-10",
|
|
27
|
+
md: "flex min-h-9 w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-left text-subheadline text-label hover:bg-fill aria-selected:bg-fill [@media(pointer:coarse)]:min-h-11",
|
|
28
|
+
lg: "flex min-h-11 w-full items-center gap-2.5 rounded-lg px-3 py-2 text-left text-body text-label hover:bg-fill aria-selected:bg-fill [@media(pointer:coarse)]:min-h-13",
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
type SearchState = {
|
|
14
32
|
query: string
|
|
15
33
|
setQuery: (query: string) => void
|
|
@@ -18,6 +36,7 @@ type SearchState = {
|
|
|
18
36
|
open: boolean
|
|
19
37
|
visible: number
|
|
20
38
|
register: (id: string, visible: boolean | null) => void
|
|
39
|
+
size: SearchListSize
|
|
21
40
|
}
|
|
22
41
|
const SearchContext = createContext<SearchState | null>(null)
|
|
23
42
|
function useSearch() {
|
|
@@ -29,12 +48,15 @@ function useSearch() {
|
|
|
29
48
|
export function SearchList({
|
|
30
49
|
loop = true,
|
|
31
50
|
open = true,
|
|
51
|
+
size = "md",
|
|
32
52
|
onKeyDown,
|
|
33
53
|
className,
|
|
34
54
|
...props
|
|
35
55
|
}: ComponentProps<"div"> & {
|
|
36
56
|
loop?: boolean
|
|
37
57
|
open?: boolean
|
|
58
|
+
/** Field, item and heading density. @default "md" */
|
|
59
|
+
size?: SearchListSize
|
|
38
60
|
}) {
|
|
39
61
|
const [query, setQuery] = useState("")
|
|
40
62
|
const updateQuery = useCallback((next: string) => {
|
|
@@ -73,6 +95,7 @@ export function SearchList({
|
|
|
73
95
|
open,
|
|
74
96
|
visible: Object.values(results).filter(Boolean).length,
|
|
75
97
|
register,
|
|
98
|
+
size,
|
|
76
99
|
}}
|
|
77
100
|
>
|
|
78
101
|
<div
|
|
@@ -148,10 +171,7 @@ export function SearchListField({
|
|
|
148
171
|
search.setQuery(event.target.value)
|
|
149
172
|
onValueChange?.(event.target.value)
|
|
150
173
|
}}
|
|
151
|
-
className={cn(
|
|
152
|
-
"min-h-11 w-full rounded-full border border-separator bg-fill/60 px-4 text-subheadline text-label outline-none focus-visible:ring-2 focus-visible:ring-accent-text/50",
|
|
153
|
-
className
|
|
154
|
-
)}
|
|
174
|
+
className={cn(fieldSizes[search.size], className)}
|
|
155
175
|
/>
|
|
156
176
|
)
|
|
157
177
|
}
|
|
@@ -179,6 +199,7 @@ export function SearchListGroup({
|
|
|
179
199
|
}: ComponentProps<"div"> & {
|
|
180
200
|
heading?: React.ReactNode
|
|
181
201
|
}) {
|
|
202
|
+
const search = useSearch()
|
|
182
203
|
const id = useId()
|
|
183
204
|
return (
|
|
184
205
|
// biome-ignore lint/a11y/useSemanticElements: Listbox option groups are not form fieldsets.
|
|
@@ -189,7 +210,7 @@ export function SearchListGroup({
|
|
|
189
210
|
aria-labelledby={heading ? id : undefined}
|
|
190
211
|
>
|
|
191
212
|
{heading && (
|
|
192
|
-
<div id={id} className=
|
|
213
|
+
<div id={id} className={headingSizes[search.size]}>
|
|
193
214
|
{heading}
|
|
194
215
|
</div>
|
|
195
216
|
)}
|
|
@@ -231,10 +252,7 @@ export function SearchListItem({
|
|
|
231
252
|
data-search-result=""
|
|
232
253
|
aria-selected={search.active === id}
|
|
233
254
|
data-selected={search.active === id ? "true" : undefined}
|
|
234
|
-
className={cn(
|
|
235
|
-
"flex min-h-9 w-full items-center gap-2 rounded-lg px-2.5 py-1.5 text-left text-subheadline text-label hover:bg-fill aria-selected:bg-fill [@media(pointer:coarse)]:min-h-11",
|
|
236
|
-
className
|
|
237
|
-
)}
|
|
255
|
+
className={cn(itemSizes[search.size], className)}
|
|
238
256
|
onMouseDown={(event) => {
|
|
239
257
|
props.onMouseDown?.(event)
|
|
240
258
|
if (!event.defaultPrevented) event.preventDefault()
|
|
@@ -16,14 +16,15 @@ export interface SidebarItemContentProps {
|
|
|
16
16
|
children: ReactNode
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export function SidebarSection({
|
|
19
|
+
export function SidebarSection({
|
|
20
|
+
title,
|
|
21
|
+
children,
|
|
22
|
+
size = "md",
|
|
23
|
+
}: SidebarSectionProps & { size?: SidebarSize }) {
|
|
20
24
|
const headingId = useId()
|
|
21
25
|
return (
|
|
22
26
|
<section aria-labelledby={headingId} className="flex flex-col gap-0.5">
|
|
23
|
-
<h2
|
|
24
|
-
id={headingId}
|
|
25
|
-
className="px-2.5 pb-1.5 text-caption font-semibold text-label-tertiary"
|
|
26
|
-
>
|
|
27
|
+
<h2 id={headingId} className={sidebarHeadingSizes[size]}>
|
|
27
28
|
{title}
|
|
28
29
|
</h2>
|
|
29
30
|
{children}
|
|
@@ -33,20 +34,46 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
|
|
|
33
34
|
|
|
34
35
|
/** Classes for a sidebar row; apply to the app's router link. */
|
|
35
36
|
export type SidebarDensity = "regular" | "compact"
|
|
37
|
+
export type SidebarSize = "sm" | "md" | "lg"
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
const sidebarItemSizes: Record<SidebarSize, string> = {
|
|
40
|
+
sm: "min-h-8 rounded-lg px-2 py-1 text-footnote [@media(pointer:coarse)]:min-h-10",
|
|
41
|
+
md: "rounded-2xl px-2.5 py-[7px] text-subheadline",
|
|
42
|
+
lg: "rounded-2xl px-3 py-2.5 text-body [@media(pointer:coarse)]:min-h-13",
|
|
43
|
+
}
|
|
44
|
+
const sidebarHeadingSizes: Record<SidebarSize, string> = {
|
|
45
|
+
sm: "px-2 pb-1 text-caption2 font-semibold text-label-tertiary",
|
|
46
|
+
md: "px-2.5 pb-1.5 text-caption font-semibold text-label-tertiary",
|
|
47
|
+
lg: "px-3 pb-2 text-subheadline font-semibold text-label-tertiary",
|
|
48
|
+
}
|
|
49
|
+
const sidebarCountSizes: Record<SidebarSize, string> = {
|
|
50
|
+
sm: "text-caption2 tabular-nums",
|
|
51
|
+
md: "text-footnote tabular-nums",
|
|
52
|
+
lg: "text-subheadline tabular-nums",
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function sidebarItemClass(
|
|
56
|
+
active?: boolean,
|
|
57
|
+
density: SidebarDensity = "regular",
|
|
58
|
+
size?: SidebarSize
|
|
59
|
+
): string {
|
|
60
|
+
const resolved = size ?? (density === "compact" ? "sm" : "md")
|
|
38
61
|
return cn(
|
|
39
62
|
"group flex w-full items-center gap-2.5 text-label no-underline transition-colors duration-fast focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-text",
|
|
40
|
-
|
|
41
|
-
? "min-h-8 rounded-lg px-2 py-1 text-footnote [@media(pointer:coarse)]:min-h-10"
|
|
42
|
-
: "rounded-2xl px-2.5 py-[7px] text-subheadline",
|
|
63
|
+
sidebarItemSizes[resolved],
|
|
43
64
|
active
|
|
44
65
|
? "bg-fill font-medium"
|
|
45
66
|
: "font-normal text-label-secondary hover:bg-fill/70 hover:text-label"
|
|
46
67
|
)
|
|
47
68
|
}
|
|
48
69
|
|
|
49
|
-
export function SidebarItemContent({
|
|
70
|
+
export function SidebarItemContent({
|
|
71
|
+
icon,
|
|
72
|
+
count,
|
|
73
|
+
active,
|
|
74
|
+
size = "md",
|
|
75
|
+
children,
|
|
76
|
+
}: SidebarItemContentProps & { size?: SidebarSize }) {
|
|
50
77
|
return (
|
|
51
78
|
<>
|
|
52
79
|
{icon && (
|
|
@@ -63,7 +90,7 @@ export function SidebarItemContent({ icon, count, active, children }: SidebarIte
|
|
|
63
90
|
{count !== undefined && (
|
|
64
91
|
<span
|
|
65
92
|
className={cn(
|
|
66
|
-
|
|
93
|
+
sidebarCountSizes[size],
|
|
67
94
|
active ? "text-accent-text" : "text-label-tertiary"
|
|
68
95
|
)}
|
|
69
96
|
>
|
|
@@ -5,6 +5,8 @@ import { cn } from "../../lib/class-names"
|
|
|
5
5
|
import { clampRange as clamp } from "../../lib/range-value"
|
|
6
6
|
import { stepLiquid } from "./liquid-step"
|
|
7
7
|
|
|
8
|
+
export type SloshSliderSize = "sm" | "md" | "lg"
|
|
9
|
+
|
|
8
10
|
export interface SloshSliderProps {
|
|
9
11
|
label: string
|
|
10
12
|
value?: number
|
|
@@ -18,6 +20,8 @@ export interface SloshSliderProps {
|
|
|
18
20
|
tilt?: number
|
|
19
21
|
/** Track corner, from square to a pill, 0–20. @default 20 */
|
|
20
22
|
corner?: number
|
|
23
|
+
/** Track height and knob scale. @default "md" */
|
|
24
|
+
size?: SloshSliderSize
|
|
21
25
|
disabled?: boolean
|
|
22
26
|
className?: string
|
|
23
27
|
formatValue?: (value: number) => string
|
|
@@ -44,6 +48,7 @@ export function SloshSlider({
|
|
|
44
48
|
momentum = 55,
|
|
45
49
|
tilt = 45,
|
|
46
50
|
corner = 20,
|
|
51
|
+
size = "md",
|
|
47
52
|
disabled = false,
|
|
48
53
|
className,
|
|
49
54
|
formatValue,
|
|
@@ -169,6 +174,7 @@ export function SloshSlider({
|
|
|
169
174
|
<div
|
|
170
175
|
className={cn("passui-slosh-slider not-typeset", disabled && "opacity-40", className)}
|
|
171
176
|
style={{ "--slosh-radius": `${clamp(corner, 0, 20)}px` } as CSSProperties}
|
|
177
|
+
data-size={size}
|
|
172
178
|
>
|
|
173
179
|
{/* Nothing but the track: the liquid is the value's motion, not a
|
|
174
180
|
second numeric readout that lags and overshoots beside it. */}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { AnimatePresence, motion, useReducedMotion } from "motion/react"
|
|
2
2
|
import { spring } from "../../motion/transitions"
|
|
3
3
|
import { useEffect, useId, useState } from "react"
|
|
4
|
+
import { cn } from "../../lib/class-names"
|
|
5
|
+
|
|
6
|
+
export type TooltipSize = "sm" | "md" | "lg"
|
|
7
|
+
|
|
8
|
+
const tooltipClasses: Record<TooltipSize, string> = {
|
|
9
|
+
sm: "pointer-events-none fixed z-[80] max-w-[200px] rounded-md bg-inverse px-2 py-1 text-center text-caption2 leading-tight font-medium text-on-inverse shadow-lg",
|
|
10
|
+
md: "pointer-events-none fixed z-[80] max-w-[240px] rounded-md bg-inverse px-2.5 py-1.5 text-center text-caption leading-tight font-medium text-on-inverse shadow-lg",
|
|
11
|
+
lg: "pointer-events-none fixed z-[80] max-w-[280px] rounded-md bg-inverse px-3 py-2 text-center text-footnote leading-tight font-medium text-on-inverse shadow-lg",
|
|
12
|
+
}
|
|
4
13
|
|
|
5
14
|
interface Tip {
|
|
6
15
|
text: string
|
|
@@ -8,6 +17,7 @@ interface Tip {
|
|
|
8
17
|
y: number
|
|
9
18
|
/** Shown above the element when there's no room below. */
|
|
10
19
|
above: boolean
|
|
20
|
+
size: TooltipSize
|
|
11
21
|
}
|
|
12
22
|
|
|
13
23
|
const DELAY = 450
|
|
@@ -45,11 +55,14 @@ export function TooltipHost() {
|
|
|
45
55
|
if (!text) return
|
|
46
56
|
const rect = element.getBoundingClientRect()
|
|
47
57
|
const above = rect.bottom + 40 > window.innerHeight
|
|
58
|
+
const sizeAttr = element.getAttribute("data-tooltip-size")
|
|
59
|
+
const size: TooltipSize = sizeAttr === "sm" || sizeAttr === "lg" ? sizeAttr : "md"
|
|
48
60
|
setTip({
|
|
49
61
|
text,
|
|
50
62
|
x: rect.left + rect.width / 2,
|
|
51
63
|
y: above ? rect.top - MARGIN : rect.bottom + MARGIN,
|
|
52
64
|
above,
|
|
65
|
+
size,
|
|
53
66
|
})
|
|
54
67
|
lastShown = Date.now()
|
|
55
68
|
}
|
|
@@ -121,7 +134,7 @@ export function TooltipHost() {
|
|
|
121
134
|
id={tooltipId}
|
|
122
135
|
key={tip.text + tip.x}
|
|
123
136
|
role="tooltip"
|
|
124
|
-
className=
|
|
137
|
+
className={cn(tooltipClasses[tip.size])}
|
|
125
138
|
style={{
|
|
126
139
|
left: Math.min(Math.max(tip.x, 128), window.innerWidth - 128),
|
|
127
140
|
top: tip.y,
|
|
@@ -24,6 +24,18 @@
|
|
|
24
24
|
user-select: none;
|
|
25
25
|
}
|
|
26
26
|
.passui-code-block:not([data-line-numbers]) pre { padding-inline-start: 1rem; }
|
|
27
|
+
.passui-code-block[data-size="sm"] pre {
|
|
28
|
+
padding-block: 0.75rem;
|
|
29
|
+
font-size: var(--text-caption);
|
|
30
|
+
line-height: 1.6;
|
|
31
|
+
}
|
|
32
|
+
.passui-code-block[data-size="sm"][data-line-numbers] .line::before { width: 2.5rem; margin-inline-end: 1rem; }
|
|
33
|
+
.passui-code-block[data-size="lg"] pre {
|
|
34
|
+
padding-block: 1.25rem;
|
|
35
|
+
font-size: var(--text-subheadline);
|
|
36
|
+
line-height: 1.8;
|
|
37
|
+
}
|
|
38
|
+
.passui-code-block[data-size="lg"][data-line-numbers] .line::before { width: 3.5rem; margin-inline-end: 1.5rem; }
|
|
27
39
|
.passui-code-block .shiki, .passui-code-block .shiki span { color: var(--shiki-light); background: transparent !important; }
|
|
28
40
|
@media (prefers-color-scheme: dark) {
|
|
29
41
|
.passui-code-block .shiki, .passui-code-block .shiki span { color: var(--shiki-dark); }
|
|
@@ -80,6 +80,10 @@
|
|
|
80
80
|
.passui-one-time-code:is([data-status="verifying"], [data-status="success"]) .passui-otp-capsule { opacity: 1; transform: translateX(-50%) scaleX(1); }
|
|
81
81
|
.passui-one-time-code:is([data-status="verifying"], [data-status="success"]) .passui-otp-digits { opacity: 0; }
|
|
82
82
|
.passui-otp-check { position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); color: var(--color-on-accent); opacity: 0; pointer-events: none; }
|
|
83
|
+
.passui-one-time-code[data-size="sm"] .passui-otp-row { height: 2.5rem; }
|
|
84
|
+
.passui-one-time-code[data-size="sm"] .passui-otp-digit { font-size: var(--text-title); }
|
|
85
|
+
.passui-one-time-code[data-size="lg"] .passui-otp-row { height: 3.5rem; }
|
|
86
|
+
.passui-one-time-code[data-size="lg"] .passui-otp-digit { font-size: var(--text-title2); }
|
|
83
87
|
.passui-one-time-code[data-status="success"] .passui-otp-check { opacity: 1; transition: opacity 160ms 240ms; }
|
|
84
88
|
/* A wrong code shivers, then drains right to left. Selecting the single
|
|
85
89
|
input afterward lets the next keystroke replace it without a trap. */
|
|
@@ -129,6 +133,10 @@
|
|
|
129
133
|
calc(100% - var(--slosh-lean, 0px)) 100%, 0 100%);
|
|
130
134
|
}
|
|
131
135
|
.passui-slosh-track:dir(rtl) .passui-slosh-fill { transform: scaleX(-1); }
|
|
136
|
+
.passui-slosh-slider[data-size="sm"] .passui-slosh-track { height: 2rem; }
|
|
137
|
+
.passui-slosh-slider[data-size="sm"] .passui-slosh-knob { top: 4px; bottom: 4px; }
|
|
138
|
+
.passui-slosh-slider[data-size="lg"] .passui-slosh-track { height: 3rem; }
|
|
139
|
+
.passui-slosh-slider[data-size="lg"] .passui-slosh-knob { top: 8px; bottom: 8px; width: 4px; margin-inline-start: -2px; }
|
|
132
140
|
.passui-slosh-knob {
|
|
133
141
|
position: absolute;
|
|
134
142
|
top: 6px;
|