@passui/react 0.1.44 → 0.1.46

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.
Files changed (140) hide show
  1. package/README.md +57 -46
  2. package/dist/types/src/components/accordion/accordion.d.ts +1 -1
  3. package/dist/types/src/components/breadcrumb/breadcrumb.d.ts +1 -1
  4. package/dist/types/src/components/inline-disclosure/inline-disclosure.d.ts +1 -1
  5. package/dist/types/src/components/popover/anchored-surface.d.ts +5 -1
  6. package/dist/types/src/components/popover/index.d.ts +41 -0
  7. package/dist/types/src/components/popover/popover-content.d.ts +32 -0
  8. package/dist/types/src/components/popover/popover-root.d.ts +22 -0
  9. package/dist/types/src/components/popover/popover-trigger.d.ts +23 -0
  10. package/dist/types/src/components/stepper/rolling-digit.d.ts +1 -1
  11. package/dist/types/src/components/table/table.d.ts +1 -1
  12. package/dist/types/src/components/view-controller/view-controller.d.ts +2 -2
  13. package/dist/types/src/index.d.ts +1 -1
  14. package/dist/types/src/machines/popover.machine.d.ts +30 -0
  15. package/package.json +307 -307
  16. package/src/appearance/index.ts +2 -2
  17. package/src/appearance/platform.tsx +76 -60
  18. package/src/appearance/theme.tsx +409 -377
  19. package/src/components/accordion/accordion.tsx +58 -49
  20. package/src/components/ambient/ambient.tsx +25 -25
  21. package/src/components/ambient/index.ts +1 -1
  22. package/src/components/aspect-ratio/aspect-ratio.tsx +21 -16
  23. package/src/components/breadcrumb/breadcrumb.tsx +63 -57
  24. package/src/components/button/button-styles.ts +39 -32
  25. package/src/components/button/button.tsx +91 -84
  26. package/src/components/button/icon-button.tsx +27 -25
  27. package/src/components/button/pending-spinner.tsx +8 -8
  28. package/src/components/button/use-pending-indicator.ts +11 -11
  29. package/src/components/chip/chip.tsx +35 -30
  30. package/src/components/code-block/code-block.tsx +40 -39
  31. package/src/components/combo-box/combo-box.tsx +5 -5
  32. package/src/components/command/command.tsx +124 -94
  33. package/src/components/compact-date-picker/calendar-month.ts +75 -53
  34. package/src/components/compact-date-picker/compact-date-picker.tsx +308 -289
  35. package/src/components/compact-time-picker/compact-time-picker.tsx +288 -281
  36. package/src/components/compact-time-picker/time-value.ts +32 -29
  37. package/src/components/content-unavailable-view/content-unavailable-view.tsx +64 -48
  38. package/src/components/content-view/content-view.tsx +168 -159
  39. package/src/components/content-view/index.ts +1 -1
  40. package/src/components/context-menu/conceal-menu-source.ts +25 -25
  41. package/src/components/context-menu/context-menu-host.tsx +439 -398
  42. package/src/components/context-menu/context-menu-store.ts +125 -123
  43. package/src/components/context-menu/menu-navigation.ts +25 -18
  44. package/src/components/context-menu/menu-position.ts +33 -29
  45. package/src/components/context-menu/menu-reveal.ts +32 -32
  46. package/src/components/context-menu/use-context-menu.ts +63 -56
  47. package/src/components/conversation/conversation.tsx +265 -263
  48. package/src/components/conversation/index.ts +1 -1
  49. package/src/components/date-picker/date-picker.tsx +95 -94
  50. package/src/components/date-picker/date-value.ts +20 -10
  51. package/src/components/dialog/dialog-host.tsx +105 -103
  52. package/src/components/dialog/dialog-store.ts +37 -36
  53. package/src/components/dialog/dialog.tsx +192 -179
  54. package/src/components/dialog/index.ts +1 -1
  55. package/src/components/disclosure-group/disclosure-group.tsx +93 -86
  56. package/src/components/divider/divider.tsx +24 -22
  57. package/src/components/inline-disclosure/inline-disclosure.tsx +40 -36
  58. package/src/components/keyboard-shortcut/keyboard-shortcut.tsx +23 -19
  59. package/src/components/labeled-content/labeled-content.tsx +68 -68
  60. package/src/components/locale-switcher/index.ts +1 -1
  61. package/src/components/locale-switcher/locale-switcher.tsx +279 -251
  62. package/src/components/menu/menu.tsx +75 -58
  63. package/src/components/morph/index.ts +2 -2
  64. package/src/components/morph/morph.tsx +227 -201
  65. package/src/components/morph/use-morph-disclosure.ts +19 -19
  66. package/src/components/one-time-code/one-time-code.tsx +216 -200
  67. package/src/components/picker/picker-trigger.ts +16 -13
  68. package/src/components/picker/picker.tsx +371 -345
  69. package/src/components/popover/anchored-surface.tsx +253 -229
  70. package/src/components/popover/index.ts +50 -0
  71. package/src/components/popover/popover-content.tsx +334 -0
  72. package/src/components/popover/popover-fit.ts +58 -53
  73. package/src/components/popover/popover-root.tsx +97 -0
  74. package/src/components/popover/popover-trigger.tsx +71 -0
  75. package/src/components/profile-image/profile-image.tsx +80 -64
  76. package/src/components/progress-view/progress-view.tsx +61 -59
  77. package/src/components/progress-view/use-liquid-progress.ts +38 -38
  78. package/src/components/radio-group/radio-group.tsx +71 -68
  79. package/src/components/search-field/expanding-search-field.tsx +191 -177
  80. package/src/components/search-field/search-field.tsx +59 -50
  81. package/src/components/search-list/search-list.tsx +246 -236
  82. package/src/components/section/section.tsx +58 -58
  83. package/src/components/secure-field/secure-field.tsx +3 -3
  84. package/src/components/segmented-control/segmented-control.tsx +136 -136
  85. package/src/components/sheet/modal-environment.ts +71 -64
  86. package/src/components/sheet/sheet-portal.tsx +58 -53
  87. package/src/components/sheet/sheet.tsx +146 -141
  88. package/src/components/sheet/use-sheet-focus.ts +97 -82
  89. package/src/components/sidebar/sidebar.tsx +78 -78
  90. package/src/components/skeleton/skeleton.tsx +30 -21
  91. package/src/components/slider/slider.tsx +113 -104
  92. package/src/components/slosh-slider/liquid-step.ts +32 -31
  93. package/src/components/slosh-slider/slosh-slider.tsx +265 -228
  94. package/src/components/stat-tile/index.ts +1 -1
  95. package/src/components/stat-tile/stat-tile.tsx +79 -68
  96. package/src/components/status-indicator/status-indicator.tsx +40 -37
  97. package/src/components/stepper/rolling-digit.tsx +82 -68
  98. package/src/components/stepper/stepper.tsx +122 -113
  99. package/src/components/swatch-picker/swatch-picker.tsx +120 -120
  100. package/src/components/tab-bar/tab-bar.tsx +149 -148
  101. package/src/components/tab-view/tab-view.tsx +140 -137
  102. package/src/components/table/table.tsx +117 -98
  103. package/src/components/text-editor/text-editor.tsx +34 -29
  104. package/src/components/text-field/text-field.tsx +182 -180
  105. package/src/components/toast/toast-store.ts +127 -115
  106. package/src/components/toast/toaster.tsx +361 -347
  107. package/src/components/toggle/toggle.tsx +297 -282
  108. package/src/components/tooltip/tooltip-host.tsx +157 -150
  109. package/src/components/view-controller/index.ts +1 -1
  110. package/src/components/view-controller/view-controller.tsx +64 -53
  111. package/src/components/wake-slider/wake-slider.tsx +220 -205
  112. package/src/hooks/scroll/minimize-decision.ts +11 -7
  113. package/src/hooks/use-anchored-disclosure.ts +19 -19
  114. package/src/hooks/use-anchored-placement.ts +29 -25
  115. package/src/hooks/use-dismiss.ts +33 -29
  116. package/src/hooks/use-floating-dismiss.ts +24 -22
  117. package/src/hooks/use-media-query.ts +67 -60
  118. package/src/hooks/use-minimize-on-scroll.ts +69 -60
  119. package/src/hooks/use-visual-viewport.ts +63 -63
  120. package/src/hooks/viewport/types.ts +16 -16
  121. package/src/hooks/viewport/viewport-metrics.ts +16 -10
  122. package/src/icons/icons.tsx +440 -328
  123. package/src/index.ts +91 -91
  124. package/src/lib/anchored-placement.ts +70 -65
  125. package/src/lib/class-names.ts +14 -13
  126. package/src/lib/clipboard.ts +8 -7
  127. package/src/lib/date.ts +61 -60
  128. package/src/lib/floating-layers.ts +39 -24
  129. package/src/lib/haptics.ts +10 -10
  130. package/src/lib/language.ts +9 -9
  131. package/src/lib/range-value.ts +27 -21
  132. package/src/lib/screen-edges.ts +33 -30
  133. package/src/lib/shapes.ts +38 -34
  134. package/src/machines/popover.machine.ts +51 -0
  135. package/src/motion/transitions.ts +46 -43
  136. package/src/styles/code-block.css +69 -43
  137. package/src/styles/motion-controls.css +393 -191
  138. package/src/tokens.ts +1 -1
  139. package/dist/types/src/components/popover/popover.d.ts +0 -21
  140. package/src/components/popover/popover.tsx +0 -322
@@ -1,79 +1,90 @@
1
- import type { ReactNode } from "react"
2
- import { cn } from "../../lib/class-names"
1
+ import type { ReactNode } from "react";
2
+ import { cn } from "../../lib/class-names";
3
3
 
4
- export type StatTileTone = "critical"
5
- export type StatTileSize = "sm" | "md" | "lg"
4
+ export type StatTileTone = "critical";
5
+ export type StatTileSize = "sm" | "md" | "lg";
6
6
 
7
- const tileSizes: Record<StatTileSize, { padding: string; valueText: string; labelText: string }> = {
8
- sm: { padding: "p-3", valueText: "text-title3", labelText: "text-caption" },
9
- md: { padding: "p-4", valueText: "text-title1", labelText: "text-footnote" },
10
- lg: { padding: "p-5", valueText: "text-title", labelText: "text-subheadline" },
11
- }
7
+ const tileSizes: Record<
8
+ StatTileSize,
9
+ { padding: string; valueText: string; labelText: string }
10
+ > = {
11
+ sm: { padding: "p-3", valueText: "text-title3", labelText: "text-caption" },
12
+ md: { padding: "p-4", valueText: "text-title1", labelText: "text-footnote" },
13
+ lg: {
14
+ padding: "p-5",
15
+ valueText: "text-title",
16
+ labelText: "text-subheadline",
17
+ },
18
+ };
12
19
 
13
20
  export interface StatTileProps {
14
- label: string
15
- value: number
16
- /** A quiet line under the number, like "3 signed in now". */
17
- caption?: ReactNode
18
- /** Turns the number red once it's above zero. */
19
- tone?: StatTileTone
20
- /** Makes the tile a toggle, e.g. a filter. */
21
- onClick?: () => void
22
- active?: boolean
23
- /** Custom classes for the tile container. */
24
- className?: string
25
- /** Tile padding and number scale. @default "md" */
26
- size?: StatTileSize
21
+ label: string;
22
+ value: number;
23
+ /** A quiet line under the number, like "3 signed in now". */
24
+ caption?: ReactNode;
25
+ /** Turns the number red once it's above zero. */
26
+ tone?: StatTileTone;
27
+ /** Makes the tile a toggle, e.g. a filter. */
28
+ onClick?: () => void;
29
+ active?: boolean;
30
+ /** Custom classes for the tile container. */
31
+ className?: string;
32
+ /** Tile padding and number scale. @default "md" */
33
+ size?: StatTileSize;
27
34
  }
28
35
 
29
36
  /** One number with its label, optionally a toggle. */
30
37
  export function StatTile({
31
- label,
32
- value,
33
- caption,
34
- tone,
35
- onClick,
36
- active = false,
37
- className,
38
- size = "md",
38
+ label,
39
+ value,
40
+ caption,
41
+ tone,
42
+ onClick,
43
+ active = false,
44
+ className,
45
+ size = "md",
39
46
  }: StatTileProps) {
40
- const dims = tileSizes[size]
41
- const body = (
42
- <>
43
- <span className={cn(dims.labelText, "text-label-secondary")}>{label}</span>
44
- <span
45
- className={cn(
46
- "tabular-nums font-semibold",
47
- dims.valueText,
48
- tone === "critical" && value > 0 && "text-danger"
49
- )}
50
- >
51
- {value.toLocaleString("en-US")}
52
- </span>
53
- {caption && (
54
- <span className={cn(dims.labelText, "text-label-tertiary")}>{caption}</span>
55
- )}
56
- </>
57
- )
58
- const tileClass = cn(
59
- "flex min-w-0 flex-col gap-2 rounded-2xl border border-separator/35 bg-elevated text-left shadow-xs",
60
- dims.padding,
61
- className
62
- )
47
+ const dims = tileSizes[size];
48
+ const body = (
49
+ <>
50
+ <span className={cn(dims.labelText, "text-label-secondary")}>
51
+ {label}
52
+ </span>
53
+ <span
54
+ className={cn(
55
+ "font-semibold tabular-nums",
56
+ dims.valueText,
57
+ tone === "critical" && value > 0 && "text-danger",
58
+ )}
59
+ >
60
+ {value.toLocaleString("en-US")}
61
+ </span>
62
+ {caption && (
63
+ <span className={cn(dims.labelText, "text-label-tertiary")}>
64
+ {caption}
65
+ </span>
66
+ )}
67
+ </>
68
+ );
69
+ const tileClass = cn(
70
+ "border-separator/35 bg-elevated flex min-w-0 flex-col gap-2 rounded-2xl border text-left shadow-xs",
71
+ dims.padding,
72
+ className,
73
+ );
63
74
 
64
- if (!onClick) return <div className={tileClass}>{body}</div>
65
- return (
66
- <button
67
- type="button"
68
- aria-pressed={active}
69
- onClick={onClick}
70
- className={cn(
71
- tileClass,
72
- "ring-1 ring-transparent transition-colors hover:bg-fill/60",
73
- active && "bg-fill/70 ring-label/15"
74
- )}
75
- >
76
- {body}
77
- </button>
78
- )
75
+ if (!onClick) return <div className={tileClass}>{body}</div>;
76
+ return (
77
+ <button
78
+ type="button"
79
+ aria-pressed={active}
80
+ onClick={onClick}
81
+ className={cn(
82
+ tileClass,
83
+ "hover:bg-fill/60 ring-1 ring-transparent transition-colors",
84
+ active && "bg-fill/70 ring-label/15",
85
+ )}
86
+ >
87
+ {body}
88
+ </button>
89
+ );
79
90
  }
@@ -1,51 +1,54 @@
1
- import type { HTMLAttributes, ReactNode } from "react"
2
- import { cn } from "../../lib/class-names"
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ import { cn } from "../../lib/class-names";
3
3
 
4
4
  const dots = {
5
- saved: "bg-success",
6
- syncing: "bg-warning animate-pulse",
7
- offline: "bg-label-tertiary",
8
- } as const
5
+ saved: "bg-success",
6
+ syncing: "bg-warning animate-pulse",
7
+ offline: "bg-label-tertiary",
8
+ } as const;
9
9
 
10
- export type StatusIndicatorState = keyof typeof dots
11
- export type StatusIndicatorSize = "sm" | "md"
10
+ export type StatusIndicatorState = keyof typeof dots;
11
+ export type StatusIndicatorSize = "sm" | "md";
12
12
 
13
13
  const dotSizes: Record<StatusIndicatorSize, string> = {
14
- sm: "size-[6px]",
15
- md: "size-[7px]",
16
- }
14
+ sm: "size-[6px]",
15
+ md: "size-[7px]",
16
+ };
17
17
 
18
18
  const textSizes: Record<StatusIndicatorSize, string> = {
19
- sm: "text-caption",
20
- md: "text-footnote",
21
- }
19
+ sm: "text-caption",
20
+ md: "text-footnote",
21
+ };
22
22
 
23
23
  export interface StatusIndicatorProps extends HTMLAttributes<HTMLSpanElement> {
24
- /** Visual treatment of the status dot. */
25
- state: StatusIndicatorState
26
- children: ReactNode
27
- /** Dot diameter and text scale. @default "md" */
28
- size?: StatusIndicatorSize
24
+ /** Visual treatment of the status dot. */
25
+ state: StatusIndicatorState;
26
+ children: ReactNode;
27
+ /** Dot diameter and text scale. @default "md" */
28
+ size?: StatusIndicatorSize;
29
29
  }
30
30
 
31
31
  export function StatusIndicator({
32
- state,
33
- children,
34
- size = "md",
35
- className,
36
- ...props
32
+ state,
33
+ children,
34
+ size = "md",
35
+ className,
36
+ ...props
37
37
  }: StatusIndicatorProps) {
38
- return (
39
- <span
40
- className={cn(
41
- "flex items-center gap-1.5 text-label-tertiary",
42
- textSizes[size],
43
- className
44
- )}
45
- {...props}
46
- >
47
- <span aria-hidden="true" className={cn(dotSizes[size], "rounded-full", dots[state])} />
48
- {children}
49
- </span>
50
- )
38
+ return (
39
+ <span
40
+ className={cn(
41
+ "text-label-tertiary flex items-center gap-1.5",
42
+ textSizes[size],
43
+ className,
44
+ )}
45
+ {...props}
46
+ >
47
+ <span
48
+ aria-hidden="true"
49
+ className={cn(dotSizes[size], "rounded-full", dots[state])}
50
+ />
51
+ {children}
52
+ </span>
53
+ );
51
54
  }
@@ -1,77 +1,91 @@
1
- import { useEffect, useRef } from "react"
2
- import { useMediaQuery } from "../../hooks/use-media-query"
1
+ import { useEffect, useRef } from "react";
2
+ import { useMediaQuery } from "../../hooks/use-media-query";
3
3
 
4
4
  /** Counts at a place retain carries: the tens column of 1284 targets 128. */
5
5
  export function digitAt(count: number) {
6
- return ((Math.floor(count) % 10) + 10) % 10
6
+ return ((Math.floor(count) % 10) + 10) % 10;
7
7
  }
8
8
 
9
- export function advanceDigit(position: number, velocity: number, target: number, frames: number) {
10
- const nextVelocity = (velocity + (target - position) * 0.16 * frames) * 0.7 ** frames
11
- return { position: position + nextVelocity * frames, velocity: nextVelocity }
9
+ export function advanceDigit(
10
+ position: number,
11
+ velocity: number,
12
+ target: number,
13
+ frames: number,
14
+ ) {
15
+ const nextVelocity =
16
+ (velocity + (target - position) * 0.16 * frames) * 0.7 ** frames;
17
+ return { position: position + nextVelocity * frames, velocity: nextVelocity };
12
18
  }
13
19
 
14
20
  /** Three recycled cells keep the strip continuous through either side of zero. */
15
- export function RollingDigit({ count, place }: { count: number; place: number }) {
16
- const strip = useRef<HTMLSpanElement>(null)
17
- const state = useRef({ position: count, velocity: 0 })
18
- const reduced = useMediaQuery("(prefers-reduced-motion: reduce)")
19
- useEffect(() => {
20
- let frame = 0
21
- let previous = 0
22
- let due = 0
23
- const paint = (position: number, velocity: number) => {
24
- const node = strip.current
25
- if (!node) return
26
- const base = Math.floor(position)
27
- for (let index = 0; index < node.children.length; index++) {
28
- node.children[index]!.textContent = String(digitAt(base + index - 1))
29
- }
30
- node.style.transform = `translateY(${(-(position - base + 1) * 100) / 3}%)`
31
- node.style.filter = `blur(${Math.min(2, Math.abs(velocity) * 1.4)}px)`
32
- }
33
- if (reduced) {
34
- state.current = { position: count, velocity: 0 }
35
- paint(count, 0)
36
- return
37
- }
38
- const tick = (now: number) => {
39
- if (!due) due = now + place * 28
40
- const frames = previous ? Math.min((now - previous) / (1000 / 60), 2) : 1
41
- previous = now
42
- if (now >= due) {
43
- state.current = advanceDigit(
44
- state.current.position,
45
- state.current.velocity,
46
- count,
47
- frames
48
- )
49
- if (
50
- Math.abs(count - state.current.position) < 0.002 &&
51
- Math.abs(state.current.velocity) < 0.002
52
- ) {
53
- state.current = { position: count, velocity: 0 }
54
- paint(count, 0)
55
- return
56
- }
57
- paint(state.current.position, state.current.velocity)
58
- }
59
- frame = requestAnimationFrame(tick)
60
- }
61
- frame = requestAnimationFrame(tick)
62
- return () => cancelAnimationFrame(frame)
63
- }, [count, place, reduced])
64
- return (
65
- <span className="relative inline-block h-[1.5em] w-[1ch] overflow-hidden align-middle">
66
- <span
67
- ref={strip}
68
- className="absolute inset-x-0 top-0 flex flex-col leading-[1.5em]"
69
- style={{ transform: "translateY(-33.333333%)" }}
70
- >
71
- <span>{digitAt(count - 1)}</span>
72
- <span>{digitAt(count)}</span>
73
- <span>{digitAt(count + 1)}</span>
74
- </span>
75
- </span>
76
- )
21
+ export function RollingDigit({
22
+ count,
23
+ place,
24
+ }: {
25
+ count: number;
26
+ place: number;
27
+ }) {
28
+ const strip = useRef<HTMLSpanElement>(null);
29
+ const state = useRef({ position: count, velocity: 0 });
30
+ const reduced = useMediaQuery("(prefers-reduced-motion: reduce)");
31
+ useEffect(() => {
32
+ let frame = 0;
33
+ let previous = 0;
34
+ let due = 0;
35
+ const paint = (position: number, velocity: number) => {
36
+ const node = strip.current;
37
+ if (!node) return;
38
+ const base = Math.floor(position);
39
+ for (let index = 0; index < node.children.length; index++) {
40
+ (node.children[index] as HTMLElement).textContent = String(
41
+ digitAt(base + index - 1),
42
+ );
43
+ }
44
+ node.style.transform = `translateY(${(-(position - base + 1) * 100) / 3}%)`;
45
+ node.style.filter = `blur(${Math.min(2, Math.abs(velocity) * 1.4)}px)`;
46
+ };
47
+ if (reduced) {
48
+ state.current = { position: count, velocity: 0 };
49
+ paint(count, 0);
50
+ return;
51
+ }
52
+ const tick = (now: number) => {
53
+ if (!due) due = now + place * 28;
54
+ const frames = previous ? Math.min((now - previous) / (1000 / 60), 2) : 1;
55
+ previous = now;
56
+ if (now >= due) {
57
+ state.current = advanceDigit(
58
+ state.current.position,
59
+ state.current.velocity,
60
+ count,
61
+ frames,
62
+ );
63
+ if (
64
+ Math.abs(count - state.current.position) < 0.002 &&
65
+ Math.abs(state.current.velocity) < 0.002
66
+ ) {
67
+ state.current = { position: count, velocity: 0 };
68
+ paint(count, 0);
69
+ return;
70
+ }
71
+ paint(state.current.position, state.current.velocity);
72
+ }
73
+ frame = requestAnimationFrame(tick);
74
+ };
75
+ frame = requestAnimationFrame(tick);
76
+ return () => cancelAnimationFrame(frame);
77
+ }, [count, place, reduced]);
78
+ return (
79
+ <span className="relative inline-block h-[1.5em] w-[1ch] overflow-hidden align-middle">
80
+ <span
81
+ ref={strip}
82
+ className="absolute inset-x-0 top-0 flex flex-col leading-[1.5em]"
83
+ style={{ transform: "translateY(-33.333333%)" }}
84
+ >
85
+ <span>{digitAt(count - 1)}</span>
86
+ <span>{digitAt(count)}</span>
87
+ <span>{digitAt(count + 1)}</span>
88
+ </span>
89
+ </span>
90
+ );
77
91
  }