@boyernick/standard-ui-react 0.1.1-canary.8 → 0.1.1-canary.9
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/package.json +1 -1
- package/src/command.tsx +15 -2
- package/src/date-picker.tsx +7 -2
- package/src/index.ts +7 -0
- package/src/kbd.tsx +43 -0
package/package.json
CHANGED
package/src/command.tsx
CHANGED
|
@@ -4,8 +4,10 @@ import { Dialog as BaseDialog } from "@base-ui/react/dialog"
|
|
|
4
4
|
import {
|
|
5
5
|
createContext,
|
|
6
6
|
useContext,
|
|
7
|
+
useEffect,
|
|
7
8
|
useId,
|
|
8
9
|
useMemo,
|
|
10
|
+
useRef,
|
|
9
11
|
type ButtonHTMLAttributes,
|
|
10
12
|
type ComponentProps,
|
|
11
13
|
type HTMLAttributes,
|
|
@@ -222,7 +224,7 @@ export const CommandContent = ({
|
|
|
222
224
|
}: CommandContentProps) => (
|
|
223
225
|
<div
|
|
224
226
|
className={cn(
|
|
225
|
-
"flex min-h-0 flex-1 flex-col gap-3 overflow-
|
|
227
|
+
"flex min-h-0 flex-1 flex-col gap-3 overflow-hidden px-4 pt-1 pb-5",
|
|
226
228
|
className,
|
|
227
229
|
)}
|
|
228
230
|
{...props}
|
|
@@ -276,7 +278,7 @@ export const CommandList = ({ className, ...props }: CommandListProps) => {
|
|
|
276
278
|
id={listboxId}
|
|
277
279
|
role="listbox"
|
|
278
280
|
className={cn(
|
|
279
|
-
"grid min-h-0 flex-1 content-start gap-0 overflow-
|
|
281
|
+
"grid min-h-0 flex-1 content-start gap-0 overflow-y-auto overscroll-contain py-0.5",
|
|
280
282
|
className,
|
|
281
283
|
)}
|
|
282
284
|
{...props}
|
|
@@ -294,6 +296,15 @@ export const CommandItem = ({
|
|
|
294
296
|
onClick,
|
|
295
297
|
...props
|
|
296
298
|
}: CommandItemProps) => {
|
|
299
|
+
const ref = useRef<HTMLAnchorElement & HTMLButtonElement>(null)
|
|
300
|
+
|
|
301
|
+
// Keyboard selection moves the highlight without moving focus, so nothing
|
|
302
|
+
// scrolls the list on its own. `nearest` only acts when the row is actually
|
|
303
|
+
// out of view, which leaves hover-driven selection alone.
|
|
304
|
+
useEffect(() => {
|
|
305
|
+
if (selected) ref.current?.scrollIntoView({ block: "nearest" })
|
|
306
|
+
}, [selected])
|
|
307
|
+
|
|
297
308
|
const itemClassName = cn(
|
|
298
309
|
"flex h-15 min-h-15 w-full cursor-pointer items-center justify-between gap-4 rounded-lg border border-transparent px-4 text-left text-sm font-medium text-fg-primary outline-none select-none",
|
|
299
310
|
motion.colors,
|
|
@@ -307,6 +318,7 @@ export const CommandItem = ({
|
|
|
307
318
|
<li role="presentation" className="m-0">
|
|
308
319
|
{href ? (
|
|
309
320
|
<a
|
|
321
|
+
ref={ref}
|
|
310
322
|
id={id}
|
|
311
323
|
href={disabled ? undefined : href}
|
|
312
324
|
role="option"
|
|
@@ -322,6 +334,7 @@ export const CommandItem = ({
|
|
|
322
334
|
</a>
|
|
323
335
|
) : (
|
|
324
336
|
<button
|
|
337
|
+
ref={ref}
|
|
325
338
|
id={id}
|
|
326
339
|
type="button"
|
|
327
340
|
role="option"
|
package/src/date-picker.tsx
CHANGED
|
@@ -62,11 +62,16 @@ const DatePickerTrigger = ({
|
|
|
62
62
|
render={
|
|
63
63
|
<Button
|
|
64
64
|
type="button"
|
|
65
|
-
|
|
65
|
+
// Default md height, so the trigger lines up with every other field
|
|
66
|
+
// in a form rather than sitting 4px short of them.
|
|
67
|
+
size="md"
|
|
66
68
|
variant="outline"
|
|
67
69
|
disabled={disabled}
|
|
68
70
|
prefix={<IconCalendar1 aria-hidden />}
|
|
69
|
-
|
|
71
|
+
// The outline button fills on hover, which is right for a button and
|
|
72
|
+
// wrong here: this reads as a field, and fields in the system keep
|
|
73
|
+
// their surface. The border and the popover carry the state.
|
|
74
|
+
className={cn("w-56 justify-start hover:bg-surface", className)}
|
|
70
75
|
/>
|
|
71
76
|
}
|
|
72
77
|
>
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export { Button, buttonVariants, type ButtonProps } from "./button";
|
|
2
2
|
export { Input, inputVariants, type InputProps } from "./input";
|
|
3
|
+
export {
|
|
4
|
+
Kbd,
|
|
5
|
+
KbdGroup,
|
|
6
|
+
kbdVariants,
|
|
7
|
+
type KbdGroupProps,
|
|
8
|
+
type KbdProps,
|
|
9
|
+
} from "./kbd";
|
|
3
10
|
export { Badge, badgeVariants, type BadgeProps } from "./badge";
|
|
4
11
|
export { Switch, type SwitchProps } from "./switch";
|
|
5
12
|
export { Checkbox, type CheckboxProps } from "./checkbox";
|
package/src/kbd.tsx
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
import type { ComponentProps } from "react"
|
|
3
|
+
import { cn } from "./lib/cn"
|
|
4
|
+
|
|
5
|
+
const kbdVariants = cva(
|
|
6
|
+
// The border is bottom-only on purpose: it reads as the lip of a physical
|
|
7
|
+
// key without boxing the glyph in on all four sides, which at 12px turns a
|
|
8
|
+
// keycap into a cramped badge.
|
|
9
|
+
"inline-flex shrink-0 items-center justify-center border-b px-1 font-sans text-xs whitespace-nowrap select-none",
|
|
10
|
+
{
|
|
11
|
+
variants: {
|
|
12
|
+
variant: {
|
|
13
|
+
default: "border-border-primary bg-background-tertiary text-fg-secondary",
|
|
14
|
+
// The default fill is a light grey that vanishes on a dark surface,
|
|
15
|
+
// so an inverted keycap lifts off the surface rather than tinting it.
|
|
16
|
+
inverted: "border-white/10 bg-white/10 text-fg-inverted",
|
|
17
|
+
},
|
|
18
|
+
size: {
|
|
19
|
+
sm: "h-4 min-w-3.5 rounded-2xs",
|
|
20
|
+
md: "h-5 min-w-4 rounded-xs",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultVariants: { variant: "default", size: "md" },
|
|
24
|
+
},
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
export type KbdProps = ComponentProps<"kbd"> & VariantProps<typeof kbdVariants>
|
|
28
|
+
export type KbdGroupProps = ComponentProps<"span">
|
|
29
|
+
|
|
30
|
+
/** A single keycap. Presentational — map "cmd" to ⌘ before it gets here. */
|
|
31
|
+
export const Kbd = ({ className, variant, size, ...props }: KbdProps) => (
|
|
32
|
+
<kbd className={cn(kbdVariants({ variant, size }), className)} {...props} />
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
/** A full shortcut. Keeps the caps on one line at a consistent gap. */
|
|
36
|
+
export const KbdGroup = ({ className, ...props }: KbdGroupProps) => (
|
|
37
|
+
<span
|
|
38
|
+
className={cn("inline-flex items-center gap-1 align-middle", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
/>
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
export { kbdVariants }
|