@passui/react 0.1.41 → 0.1.43

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.
@@ -1,3 +1,4 @@
1
+ export type LocaleSwitcherSize = "sm" | "md" | "lg";
1
2
  export type LocaleSwitcherProps<L extends string> = {
2
3
  locales: readonly L[];
3
4
  locale: L;
@@ -5,6 +6,8 @@ export type LocaleSwitcherProps<L extends string> = {
5
6
  /** localStorage key for where the person dragged it. */
6
7
  storageKey: string;
7
8
  label: string;
9
+ /** Trigger, header, list item and icon scale. @default "md" */
10
+ size?: LocaleSwitcherSize;
8
11
  };
9
12
  /** A draggable floating language picker that snaps to the nearest corner. */
10
- export declare function LocaleSwitcher<L extends string>({ locales, locale, onSelect, storageKey, label, }: LocaleSwitcherProps<L>): import("react").JSX.Element;
13
+ export declare function LocaleSwitcher<L extends string>({ locales, locale, onSelect, storageKey, label, size, }: LocaleSwitcherProps<L>): import("react").JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import { type HTMLMotionProps } from "motion/react";
2
2
  import { type ReactNode, type RefObject } from "react";
3
+ export type MorphSize = "sm" | "md" | "lg";
3
4
  export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
4
5
  /** Controls whether the overlay is presented. */
5
6
  open: boolean;
@@ -13,6 +14,8 @@ export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
13
14
  labelledBy?: string;
14
15
  /** Allow tapping the backdrop to close. Escape still calls onClose. @default true */
15
16
  dismissOnOutsidePress?: boolean;
17
+ /** Outer padding and panel max-width. @default "md" */
18
+ size?: MorphSize;
16
19
  className?: string;
17
20
  children?: ReactNode;
18
21
  }
@@ -1,3 +1,4 @@
1
+ export type WakeSliderSize = "sm" | "md" | "lg";
1
2
  export interface WakeSliderProps {
2
3
  label: string;
3
4
  value?: number;
@@ -8,5 +9,7 @@ export interface WakeSliderProps {
8
9
  disabled?: boolean;
9
10
  className?: string;
10
11
  formatValue?: (value: number) => string;
12
+ /** Bar height at rest and value text scale. @default "md" */
13
+ size?: WakeSliderSize;
11
14
  }
12
- export declare function WakeSlider({ label, value: controlledValue, defaultValue, onChange, disabled, className, formatValue, bars, wake, }: WakeSliderProps): import("react").JSX.Element;
15
+ export declare function WakeSlider({ label, value: controlledValue, defaultValue, onChange, disabled, className, formatValue, size, bars, wake, }: WakeSliderProps): import("react").JSX.Element;
@@ -46,6 +46,7 @@ export * from "./lib/haptics";
46
46
  export * from "./lib/clipboard";
47
47
  export * from "./lib/screen-edges";
48
48
  export * from "./motion/transitions";
49
+ export { AnimatePresence, motion } from "motion/react";
49
50
  export * from "./appearance";
50
51
  export * from "./components/date-picker/date-picker";
51
52
  export * from "./components/compact-date-picker/compact-date-picker";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@passui/react",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "Passui: native React controls, design tokens, presentation and appearance.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -6,6 +6,39 @@ import { useDismiss } from "../../hooks/use-dismiss"
6
6
  import { CheckIcon, GlobeIcon } from "../../icons/icons"
7
7
  import { Popover } from "../popover/popover"
8
8
 
9
+ export type LocaleSwitcherSize = "sm" | "md" | "lg"
10
+
11
+ const triggerSizes: Record<LocaleSwitcherSize, string> = {
12
+ sm: "glass-menu flex h-9 min-w-16 touch-none cursor-grab items-center justify-center gap-1.5 rounded-full px-3 text-caption2 font-semibold text-label active:cursor-grabbing",
13
+ md: "glass-menu flex h-12 min-w-20 touch-none cursor-grab items-center justify-center gap-2 rounded-full px-4 text-caption font-semibold text-label active:cursor-grabbing",
14
+ lg: "glass-menu flex h-14 min-w-24 touch-none cursor-grab items-center justify-center gap-2.5 rounded-full px-5 text-subheadline font-semibold text-label active:cursor-grabbing",
15
+ }
16
+ const iconSizes: Record<LocaleSwitcherSize, string> = {
17
+ sm: "size-4",
18
+ md: "size-5",
19
+ lg: "size-6",
20
+ }
21
+ const popoverWidths: Record<LocaleSwitcherSize, string> = {
22
+ sm: "w-44",
23
+ md: "w-52",
24
+ lg: "w-60",
25
+ }
26
+ const headerSizes: Record<LocaleSwitcherSize, string> = {
27
+ sm: "flex min-h-9 w-full items-center justify-center gap-1.5 rounded-4xl bg-fill/50 text-caption2 font-semibold text-label",
28
+ md: "flex min-h-12 w-full items-center justify-center gap-2 rounded-4xl bg-fill/50 text-caption font-semibold text-label",
29
+ lg: "flex min-h-14 w-full items-center justify-center gap-2.5 rounded-4xl bg-fill/50 text-subheadline font-semibold text-label",
30
+ }
31
+ const itemSizes: Record<LocaleSwitcherSize, string> = {
32
+ sm: "flex min-h-9 items-center justify-between gap-2 rounded-4xl px-3 py-2 text-left text-caption text-label outline-none",
33
+ md: "flex min-h-11 items-center justify-between gap-3 rounded-4xl px-3.5 py-2.5 text-left text-footnote text-label outline-none",
34
+ lg: "flex min-h-13 items-center justify-between gap-3 rounded-4xl px-4 py-3 text-left text-subheadline text-label outline-none",
35
+ }
36
+ const checkIconSizes: Record<LocaleSwitcherSize, string> = {
37
+ sm: "size-3.5 shrink-0",
38
+ md: "size-4 shrink-0",
39
+ lg: "size-5 shrink-0",
40
+ }
41
+
9
42
  type Side = "left" | "right"
10
43
  type Edge = "top" | "bottom"
11
44
 
@@ -58,6 +91,8 @@ export type LocaleSwitcherProps<L extends string> = {
58
91
  /** localStorage key for where the person dragged it. */
59
92
  storageKey: string
60
93
  label: string
94
+ /** Trigger, header, list item and icon scale. @default "md" */
95
+ size?: LocaleSwitcherSize
61
96
  }
62
97
 
63
98
  /** A draggable floating language picker that snaps to the nearest corner. */
@@ -67,6 +102,7 @@ export function LocaleSwitcher<L extends string>({
67
102
  onSelect,
68
103
  storageKey,
69
104
  label,
105
+ size = "md",
70
106
  }: LocaleSwitcherProps<L>) {
71
107
  const containerRef = useRef<HTMLDivElement>(null)
72
108
  const triggerRef = useRef<HTMLButtonElement>(null)
@@ -177,9 +213,9 @@ export function LocaleSwitcher<L extends string>({
177
213
  setOpen(true)
178
214
  }
179
215
  }}
180
- className="glass-menu flex h-12 min-w-20 touch-none cursor-grab items-center justify-center gap-2 rounded-full px-4 text-caption font-semibold text-label active:cursor-grabbing"
216
+ className={triggerSizes[size]}
181
217
  >
182
- <GlobeIcon aria-hidden="true" className="size-5" />
218
+ <GlobeIcon aria-hidden="true" className={iconSizes[size]} />
183
219
  {locale.toUpperCase()}
184
220
  </button>
185
221
  <Popover
@@ -189,7 +225,8 @@ export function LocaleSwitcher<L extends string>({
189
225
  role="menu"
190
226
  aria-label={label}
191
227
  className={cn(
192
- "top-0 flex w-52 max-w-[calc(100vw-2.5rem)] gap-2 p-2",
228
+ "top-0 flex max-w-[calc(100vw-2.5rem)] gap-2 p-2",
229
+ popoverWidths[size],
193
230
  anchor.side === "right" ? "right-0" : "left-0",
194
231
  growsUp ? "flex-col-reverse" : "flex-col"
195
232
  )}
@@ -201,9 +238,9 @@ export function LocaleSwitcher<L extends string>({
201
238
  setOpen(false)
202
239
  triggerRef.current?.focus()
203
240
  }}
204
- className="flex min-h-12 w-full items-center justify-center gap-2 rounded-4xl bg-fill/50 text-caption font-semibold text-label"
241
+ className={headerSizes[size]}
205
242
  >
206
- <GlobeIcon aria-hidden="true" className="size-5" />
243
+ <GlobeIcon aria-hidden="true" className={iconSizes[size]} />
207
244
  {locale.toUpperCase()}
208
245
  </button>
209
246
  <div className="flex flex-col gap-1">
@@ -224,14 +261,17 @@ export function LocaleSwitcher<L extends string>({
224
261
  }
225
262
  }}
226
263
  className={cn(
227
- "flex min-h-11 items-center justify-between gap-3 rounded-4xl px-3.5 py-2.5 text-left text-footnote text-label outline-none",
264
+ itemSizes[size],
228
265
  "hover:bg-fill/65 focus-visible:bg-fill/65 transition-colors",
229
266
  active && "bg-accent/12 font-medium"
230
267
  )}
231
268
  >
232
269
  {item.label}
233
270
  {active && (
234
- <CheckIcon aria-hidden="true" className="size-4 shrink-0" />
271
+ <CheckIcon
272
+ aria-hidden="true"
273
+ className={checkIconSizes[size]}
274
+ />
235
275
  )}
236
276
  </button>
237
277
  )
@@ -7,6 +7,19 @@ import { SheetPortal } from "../sheet/sheet-portal"
7
7
  import { cn } from "../../lib/class-names"
8
8
  import { contentCrossfade, fluidCollapse, fluidOvershoot } from "../../motion/transitions"
9
9
 
10
+ export type MorphSize = "sm" | "md" | "lg"
11
+
12
+ const outerPaddings: Record<MorphSize, string> = {
13
+ sm: "p-4",
14
+ md: "p-6",
15
+ lg: "p-8",
16
+ }
17
+ const panelMaxWidths: Record<MorphSize, string> = {
18
+ sm: "max-w-sm",
19
+ md: "max-w-[720px]",
20
+ lg: "max-w-3xl",
21
+ }
22
+
10
23
  export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
11
24
  /** Controls whether the overlay is presented. */
12
25
  open: boolean
@@ -20,6 +33,8 @@ export interface MorphProps extends Omit<HTMLMotionProps<"div">, "children"> {
20
33
  labelledBy?: string
21
34
  /** Allow tapping the backdrop to close. Escape still calls onClose. @default true */
22
35
  dismissOnOutsidePress?: boolean
36
+ /** Outer padding and panel max-width. @default "md" */
37
+ size?: MorphSize
23
38
  className?: string
24
39
  children?: ReactNode
25
40
  }
@@ -44,6 +59,7 @@ function OpenMorph({
44
59
  label,
45
60
  labelledBy,
46
61
  dismissOnOutsidePress = true,
62
+ size = "md",
47
63
  className,
48
64
  children,
49
65
  ...props
@@ -131,7 +147,12 @@ function OpenMorph({
131
147
 
132
148
  return (
133
149
  <SheetPortal>
134
- <div className="fixed inset-0 z-[75] flex items-center justify-center p-6">
150
+ <div
151
+ className={cn(
152
+ "fixed inset-0 z-[75] flex items-center justify-center",
153
+ outerPaddings[size]
154
+ )}
155
+ >
135
156
  <motion.div
136
157
  aria-hidden="true"
137
158
  className="absolute inset-0 bg-black/30 backdrop-blur-md"
@@ -150,7 +171,8 @@ function OpenMorph({
150
171
  tabIndex={-1}
151
172
  className={cn(
152
173
  "relative isolate flex w-full flex-col rounded-5xl outline-none",
153
- "max-h-[92%] max-w-[720px]",
174
+ "max-h-[92%]",
175
+ panelMaxWidths[size],
154
176
  settled && "overflow-hidden",
155
177
  className
156
178
  )}
@@ -24,7 +24,9 @@ import { cn } from "../../lib/class-names"
24
24
  changes, which is what the reading and the colours need, and
25
25
  the loop runs only while some bar still has energy. */
26
26
 
27
- const H = 20 // a bar at rest, px
27
+ const H: Record<WakeSliderSize, number> = { sm: 16, md: 20, lg: 26 } // a bar at rest, px
28
+
29
+ export type WakeSliderSize = "sm" | "md" | "lg"
28
30
 
29
31
  export interface WakeSliderProps {
30
32
  label: string
@@ -36,6 +38,8 @@ export interface WakeSliderProps {
36
38
  disabled?: boolean
37
39
  className?: string
38
40
  formatValue?: (value: number) => string
41
+ /** Bar height at rest and value text scale. @default "md" */
42
+ size?: WakeSliderSize
39
43
  }
40
44
 
41
45
  export function WakeSlider({
@@ -46,6 +50,7 @@ export function WakeSlider({
46
50
  disabled = false,
47
51
  className,
48
52
  formatValue,
53
+ size = "md",
49
54
  /* how many bars make the track */
50
55
  bars = 36,
51
56
  /* how high the wake rises, 0..100 */
@@ -144,7 +149,10 @@ export function WakeSlider({
144
149
  const lit = Math.round((value / 100) * (n - 1))
145
150
 
146
151
  return (
147
- <div className={cn("passui-wake-slider not-typeset", disabled && "opacity-40", className)}>
152
+ <div
153
+ className={cn("passui-wake-slider not-typeset", disabled && "opacity-40", className)}
154
+ data-size={size}
155
+ >
148
156
  <div
149
157
  ref={track}
150
158
  className="passui-wake-track"
@@ -156,7 +164,7 @@ export function WakeSlider({
156
164
  aria-valuemin={0}
157
165
  aria-valuemax={100}
158
166
  aria-valuenow={value}
159
- style={{ height: H * 2.6, "--wake-bars": n } as CSSProperties}
167
+ style={{ height: H[size] * 2.6, "--wake-bars": n } as CSSProperties}
160
168
  onPointerDown={(e) => {
161
169
  if (disabled || e.button !== 0) return
162
170
  e.currentTarget.focus()
@@ -225,7 +233,7 @@ export function WakeSlider({
225
233
  aria-hidden="true"
226
234
  className="passui-wake-bar"
227
235
  data-on={(value > 0 && i <= lit) || undefined}
228
- style={{ height: H }}
236
+ style={{ height: H[size] }}
229
237
  />
230
238
  ))}
231
239
  </div>
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ export * from "./lib/haptics"
62
62
  export * from "./lib/clipboard"
63
63
  export * from "./lib/screen-edges"
64
64
  export * from "./motion/transitions"
65
+ export { AnimatePresence, motion } from "motion/react"
65
66
  export * from "./appearance"
66
67
 
67
68
  export * from "./components/date-picker/date-picker"
@@ -189,6 +189,10 @@
189
189
  font-variant-numeric: tabular-nums;
190
190
  color: var(--color-label-secondary);
191
191
  }
192
+ .passui-wake-slider[data-size="sm"] { gap: 0.75rem; }
193
+ .passui-wake-slider[data-size="sm"] .passui-wake-value { font-size: var(--text-caption); }
194
+ .passui-wake-slider[data-size="lg"] { gap: 1.25rem; }
195
+ .passui-wake-slider[data-size="lg"] .passui-wake-value { font-size: var(--text-subheadline); }
192
196
  @media (prefers-reduced-motion: reduce) {
193
197
  .passui-wake-bar, .passui-search-pill { transition: none; }
194
198
  .passui-search-pill:active { scale: 1; }