A design system in a box. hip-ui.tngl.io/docs/introduction
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

command

+732 -124
+4 -3
README.md
··· 39 39 40 40 #### react-aria wrappers 41 41 42 + - [ ] Dialog 43 + 42 44 - [ ] File Trigger 43 45 - [ ] File Drop zone 44 46 - [ ] Segmented Control ··· 55 57 - [ ] Date Picker 56 58 - [ ] Range Date Picker 57 59 58 - - [ ] Dialog 59 60 - [ ] Alert Dialog 60 - - [ ] Command 61 61 - [ ] Drawer 62 62 - [ ] Sheet 63 63 ··· 81 81 - [ ] Toolbar 82 82 - [ ] Toast 83 83 84 + - [x] Command 85 + - [x] Tree 84 86 - [x] Combobox 85 87 - [x] Number Field 86 88 - [x] Color Field ··· 107 109 - [x] Toggle 108 110 - [x] Toggle Group 109 111 - [x] Tooltip 110 - - [x] Tree 111 112 - [x] Typography
+1
apps/example/package.json
··· 9 9 "start": "waku start" 10 10 }, 11 11 "dependencies": { 12 + "@react-stately/utils": "^3.10.8", 12 13 "@react-types/overlays": "^3.9.2", 13 14 "@repo/typescript-config": "workspace:*", 14 15 "@stylexjs/stylex": "^0.16.1",
+43 -4
apps/example/src/components/KitchenSink.tsx
··· 11 11 ArrowLeft, 12 12 ArrowRight, 13 13 ArrowUp, 14 + AxeIcon, 14 15 Check, 16 + Copy, 15 17 CpuIcon, 16 18 Ellipsis, 17 19 File, ··· 20 22 Image, 21 23 Pin, 22 24 Plus, 25 + Scissors, 23 26 Search, 24 27 Star, 25 28 } from "lucide-react"; ··· 59 62 import { ToggleButton } from "./toggle-button"; 60 63 import { ToggleButtonGroup } from "./toggle-button-group"; 61 64 import { 65 + CommandMenu, 66 + CommandMenuItem, 67 + CommandMenuSection, 68 + CommandMenuSectionHeader, 69 + CommandMenuSeparator, 70 + } from "./command-menu"; 71 + import { 62 72 Menu, 63 73 MenuItem, 64 74 MenuSection, ··· 76 86 import { NumberField } from "./number-field"; 77 87 import { ComboBox, ComboBoxItem } from "./combobox"; 78 88 import { Tree, TreeItem } from "./tree"; 89 + import { DialogTrigger } from "react-aria-components"; 79 90 80 91 const styles = stylex.create({ 81 92 subCard: { ··· 618 629 ); 619 630 } 620 631 632 + function CommandMenuExample() { 633 + return ( 634 + <Flex direction="column" gap="4"> 635 + <CommandMenu> 636 + <CommandMenuItem prefix={<AxeIcon />}>New</CommandMenuItem> 637 + <CommandMenuSection> 638 + <CommandMenuSectionHeader>Edit</CommandMenuSectionHeader> 639 + <CommandMenuItem 640 + prefix={<Copy />} 641 + onAction={() => console.log("Copy")} 642 + > 643 + Copy 644 + </CommandMenuItem> 645 + <CommandMenuItem prefix={<Plus />}>Paste</CommandMenuItem> 646 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 647 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 648 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 649 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 650 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 651 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 652 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 653 + <CommandMenuItem prefix={<Scissors />}>Cut</CommandMenuItem> 654 + </CommandMenuSection> 655 + </CommandMenu> 656 + </Flex> 657 + ); 658 + } 659 + 621 660 export function KitchenSink() { 622 661 return ( 623 662 <Flex ··· 625 664 gap="10" 626 665 style={[gray.bg, gray.text, styles.container]} 627 666 > 628 - <TitleCard title="Tree"> 629 - <TreeExample /> 630 - </TitleCard> 631 - <MenuExample /> 667 + <CommandMenuExample /> 632 668 </Flex> 633 669 ); 634 670 return ( ··· 637 673 gap="10" 638 674 style={[gray.bg, gray.text, styles.container]} 639 675 > 676 + <TitleCard title="Tree"> 677 + <TreeExample /> 678 + </TitleCard> 640 679 <SignUpForm /> 641 680 <PaymentMethod /> 642 681 <TitleCard title="Buttons">
+1 -1
apps/example/src/components/button/index.tsx
··· 19 19 children, 20 20 style, 21 21 variant = "primary", 22 - size = "md", 22 + size, 23 23 ...props 24 24 }: ButtonProps) => { 25 25 const buttonStyles = useButtonStyles({ variant, size });
+1 -1
apps/example/src/components/checkbox/index.tsx
··· 68 68 description, 69 69 errorMessage, 70 70 children, 71 - size = "md", 71 + size, 72 72 style, 73 73 ...props 74 74 }: CheckboxGroupProps) {
+1 -1
apps/example/src/components/color-field/index.tsx
··· 29 29 description, 30 30 errorMessage, 31 31 style, 32 - size = "md", 32 + size, 33 33 prefix, 34 34 suffix, 35 35 placeholder,
+3 -1
apps/example/src/components/combobox/index.tsx
··· 32 32 import { IconButton } from "../icon-button"; 33 33 import { SmallBody } from "../typography"; 34 34 import { spacing } from "../theme/spacing.stylex"; 35 + import { use } from "react"; 35 36 36 37 const styles = stylex.create({ 37 38 matchWidth: { ··· 81 82 children, 82 83 items, 83 84 style, 84 - size = "md", 85 + size: sizeProp, 85 86 shouldCloseOnInteractOutside, 86 87 shouldFlip, 87 88 shouldUpdatePosition, ··· 92 93 renderEmptyState, 93 94 ...props 94 95 }: ComboBoxProps<T>) { 96 + const size = sizeProp || use(SizeContext); 95 97 const inputStyles = useInputStyles({ size }); 96 98 const popoverStyles = usePopoverStyles(); 97 99
+184
apps/example/src/components/command-menu/index.tsx
··· 1 + import { 2 + InputProps, 3 + Modal, 4 + Dialog, 5 + Autocomplete, 6 + Menu, 7 + useFilter, 8 + ModalOverlay, 9 + AutocompleteProps as AriaAutocompleteProps, 10 + } from "react-aria-components"; 11 + import { useControlledState } from "@react-stately/utils"; 12 + import { SearchField } from "../search-field"; 13 + import { OverlayTriggerProps } from "react-stately"; 14 + import { 15 + MenuItem, 16 + MenuItemProps, 17 + MenuSection, 18 + MenuSectionHeader, 19 + MenuSectionHeaderProps, 20 + MenuSectionProps, 21 + MenuSeparator, 22 + MenuSeparatorProps, 23 + } from "../menu"; 24 + import { useEffect, useEffectEvent } from "react"; 25 + import * as stylex from "@stylexjs/stylex"; 26 + import { shadow } from "../theme/shadow.stylex"; 27 + import { radius } from "../theme/radius.stylex"; 28 + import { gray } from "../theme/semantic-color.stylex"; 29 + import { animations } from "../theme/animations.stylex"; 30 + import { spacing } from "../theme/spacing.stylex"; 31 + import { SizeContext } from "../context"; 32 + import { Separator } from "../separator"; 33 + 34 + const styles = stylex.create({ 35 + overlay: { 36 + backgroundColor: "rgba(0, 0, 0, 0.5)", 37 + height: "var(--page-height)", 38 + left: 0, 39 + position: "absolute", 40 + top: 0, 41 + width: "100vw", 42 + zIndex: 100, 43 + 44 + transitionProperty: "opacity", 45 + transitionDuration: { 46 + ":is([data-exiting])": "100ms", 47 + }, 48 + transitionTimingFunction: "ease-in-out", 49 + opacity: { 50 + default: 1, 51 + ":is([data-exiting])": 0, 52 + }, 53 + animationName: { 54 + ":is([data-entering])": animations.fadeIn, 55 + }, 56 + animationTimingFunction: "ease-in", 57 + animationDuration: "200ms", 58 + }, 59 + modal: { 60 + borderRadius: radius["lg"], 61 + boxShadow: shadow["lg"], 62 + display: "flex", 63 + flexDirection: "column", 64 + left: "50%", 65 + maxHeight: "calc(var(--visual-viewport-height) * 0.8)", 66 + outline: "none", 67 + position: "fixed", 68 + top: "calc(var(--visual-viewport-height) / 2)", 69 + translate: "-50% -50%", 70 + width: 400, 71 + 72 + animationDuration: { ":is([data-entering])": "300ms" }, 73 + animationName: { ":is([data-entering])": animations.zoomIn }, 74 + animationTimingFunction: { 75 + ":is([data-entering])": "cubic-bezier(0.175, 0.885, 0.32, 1.275)", 76 + }, 77 + }, 78 + dialog: { 79 + display: "flex", 80 + flexDirection: "column", 81 + flexGrow: 1, 82 + minHeight: 0, 83 + outline: "none", 84 + }, 85 + menu: { 86 + flexGrow: 1, 87 + marginLeft: `calc(${spacing["0.5"]} * -1)`, 88 + marginRight: `calc(${spacing["0.5"]} * -1)`, 89 + minHeight: 0, 90 + overflowY: "auto", 91 + paddingBottom: spacing["2"], 92 + paddingLeft: spacing["3"], 93 + paddingRight: spacing["3"], 94 + paddingTop: spacing["2"], 95 + }, 96 + searchField: { 97 + paddingBottom: spacing["3"], 98 + paddingLeft: spacing["3"], 99 + paddingRight: spacing["3"], 100 + paddingTop: spacing["3"], 101 + }, 102 + }); 103 + 104 + export interface CommandMenuProps<T extends object> 105 + extends OverlayTriggerProps, 106 + Pick<InputProps, "placeholder">, 107 + AriaAutocompleteProps<T> { 108 + children: React.ReactNode; 109 + } 110 + 111 + export function CommandMenu<T extends object>({ 112 + defaultOpen, 113 + isOpen: isOpenProp, 114 + onOpenChange, 115 + filter, 116 + placeholder = "Search commands", 117 + children, 118 + defaultInputValue, 119 + disableAutoFocusFirst, 120 + disableVirtualFocus, 121 + inputValue, 122 + onInputChange, 123 + }: CommandMenuProps<T>) { 124 + const defaultFilter = useFilter({ sensitivity: "base" }); 125 + const [isOpen, setIsOpen] = useControlledState( 126 + isOpenProp, 127 + defaultOpen ?? false, 128 + onOpenChange 129 + ); 130 + const onClose = useEffectEvent(() => setIsOpen(false)); 131 + 132 + useEffect(() => { 133 + function handleKeyDown(event: KeyboardEvent) { 134 + if (event.metaKey && event.key === "k") { 135 + setIsOpen(true); 136 + } 137 + } 138 + 139 + window.addEventListener("keydown", handleKeyDown); 140 + 141 + return () => window.removeEventListener("keydown", handleKeyDown); 142 + }, [setIsOpen]); 143 + 144 + return ( 145 + <SizeContext.Provider value="lg"> 146 + <ModalOverlay 147 + isDismissable 148 + isOpen={isOpen} 149 + onOpenChange={setIsOpen} 150 + {...stylex.props(styles.overlay)} 151 + > 152 + <Modal {...stylex.props(styles.modal, gray.bg, gray.text, gray.border)}> 153 + <Dialog {...stylex.props(styles.dialog)}> 154 + <Autocomplete 155 + filter={filter ?? defaultFilter.contains} 156 + defaultInputValue={defaultInputValue} 157 + disableAutoFocusFirst={disableAutoFocusFirst} 158 + disableVirtualFocus={disableVirtualFocus} 159 + inputValue={inputValue} 160 + onInputChange={onInputChange} 161 + > 162 + <div {...stylex.props(styles.searchField)}> 163 + <SearchField placeholder={placeholder} autoFocus /> 164 + </div> 165 + <Separator /> 166 + <Menu {...stylex.props(styles.menu)} onAction={onClose}> 167 + {children} 168 + </Menu> 169 + </Autocomplete> 170 + </Dialog> 171 + </Modal> 172 + </ModalOverlay> 173 + </SizeContext.Provider> 174 + ); 175 + } 176 + 177 + export type CommandMenuItemProps = MenuItemProps; 178 + export const CommandMenuItem = MenuItem; 179 + export type CommandMenuSectionHeaderProps = MenuSectionHeaderProps; 180 + export const CommandMenuSectionHeader = MenuSectionHeader; 181 + export type CommandMenuSectionProps<T extends object> = MenuSectionProps<T>; 182 + export const CommandMenuSection = MenuSection; 183 + export type CommandMenuSeparatorProps = MenuSeparatorProps; 184 + export const CommandMenuSeparator = MenuSeparator;
+2 -1
apps/example/src/components/context-menu/index.tsx
··· 140 140 141 141 export function ContextMenu<T extends object>({ 142 142 trigger, 143 - size = "md", 143 + size: sizeProp, 144 144 defaultOpen, 145 145 isOpen, 146 146 onOpenChange, ··· 151 151 ...props 152 152 }: ContextMenuProps<T>) { 153 153 const popoverStyles = usePopoverStyles(); 154 + const size = sizeProp || use(SizeContext); 154 155 155 156 return ( 156 157 <SizeContext.Provider value={size}>
+1 -1
apps/example/src/components/date-field/index.tsx
··· 29 29 description, 30 30 errorMessage, 31 31 style, 32 - size = "md", 32 + size, 33 33 prefix, 34 34 suffix, 35 35 ...props
+5 -1
apps/example/src/components/icon-button/index.tsx
··· 7 7 import { Button } from "../button"; 8 8 import { spacing } from "../theme/spacing.stylex"; 9 9 import { ButtonVariant, Size } from "../types"; 10 + import { use } from "react"; 11 + import { SizeContext } from "../context"; 10 12 11 13 const styles = stylex.create({ 12 14 sm: { ··· 32 34 33 35 export const IconButton = ({ 34 36 children, 35 - size = "md", 37 + size: sizeProp, 36 38 label, 37 39 style, 38 40 ...props 39 41 }: IconButtonProps) => { 42 + const size = sizeProp || use(SizeContext); 43 + 40 44 return ( 41 45 <Tooltip text={label}> 42 46 <Button size={size} style={[styles[size], style]} {...props}>
+8 -2
apps/example/src/components/label/index.tsx
··· 9 9 import { fontSize, fontWeight, lineHeight } from "../theme/typography.stylex"; 10 10 import { gray } from "../theme/semantic-color.stylex"; 11 11 import { Size } from "../types"; 12 + import { use } from "react"; 13 + import { SizeContext } from "../context"; 12 14 13 15 const styles = stylex.create({ 14 16 label: { ··· 43 45 size?: Size; 44 46 } 45 47 46 - export function Label({ style, size = "md", ...props }: LabelProps) { 48 + export function Label({ style, size: sizeProp, ...props }: LabelProps) { 49 + const size = sizeProp || use(SizeContext); 50 + 47 51 return ( 48 52 <AriaLabel 49 53 {...props} ··· 60 64 61 65 export function Description({ 62 66 style, 63 - size = "md", 67 + size: sizeProp, 64 68 ...props 65 69 }: DescriptionProps) { 70 + const size = sizeProp || use(SizeContext); 71 + 66 72 return ( 67 73 <Text 68 74 slot="description"
+4 -3
apps/example/src/components/listbox/index.tsx
··· 14 14 import { typeramp } from "../theme/typography.stylex"; 15 15 import { Size } from "../types"; 16 16 import { SizeContext } from "../context"; 17 - import { use, useContext } from "react"; 17 + import { use } from "react"; 18 18 import { Separator } from "../separator"; 19 19 import { gray } from "../theme/semantic-color.stylex"; 20 20 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; ··· 60 60 style, 61 61 ...props 62 62 }: ListBoxProps<T>) { 63 - const size = sizeProp || use(SizeContext) || "md"; 63 + const size = sizeProp || use(SizeContext); 64 64 65 65 return ( 66 66 <SizeContext.Provider value={size}> ··· 144 144 style, 145 145 ...props 146 146 }: ListBoxSectionHeaderProps) { 147 - const size = useContext(SizeContext); 147 + const size = use(SizeContext); 148 + 148 149 return ( 149 150 <Header 150 151 {...props}
+25 -3
apps/example/src/components/menu/index.tsx
··· 24 24 import { Check, ChevronRight } from "lucide-react"; 25 25 import { usePopoverStyles } from "../theme/usePopoverStyles"; 26 26 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 27 + import { use } from "react"; 27 28 28 29 export interface MenuProps<T extends object> 29 30 extends Omit<MenuTriggerProps, "trigger" | "children">, ··· 43 44 44 45 export function Menu<T extends object>({ 45 46 trigger, 46 - size = "md", 47 + size: sizeProp, 47 48 defaultOpen, 48 49 isOpen, 49 50 onOpenChange, ··· 54 55 ...props 55 56 }: MenuProps<T>) { 56 57 const popoverStyles = usePopoverStyles(); 58 + const size = sizeProp || use(SizeContext); 57 59 58 60 return ( 59 61 <SizeContext.Provider value={size}> ··· 138 140 extends Omit<AriaMenuItemProps, "style" | "className" | "children"> { 139 141 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 140 142 children: React.ReactNode; 143 + prefix?: React.ReactNode; 144 + suffix?: React.ReactNode; 141 145 } 142 146 143 - export function MenuItem({ style, children, ...props }: MenuItemProps) { 147 + export function MenuItem({ 148 + style, 149 + children, 150 + prefix, 151 + suffix, 152 + ...props 153 + }: MenuItemProps) { 144 154 const menuItemStyles = useListBoxItemStyles(); 145 155 146 156 return ( 147 - <AriaMenuItem {...props} {...stylex.props(menuItemStyles.wrapper, style)}> 157 + <AriaMenuItem 158 + {...props} 159 + textValue={ 160 + props.textValue || (typeof children === "string" ? children : undefined) 161 + } 162 + {...stylex.props(menuItemStyles.wrapper, style)} 163 + > 148 164 {({ isSelected, hasSubmenu }) => ( 149 165 <div {...stylex.props(menuItemStyles.inner)}> 166 + {prefix && ( 167 + <div {...stylex.props(menuItemStyles.addon)}>{prefix}</div> 168 + )} 150 169 <div {...stylex.props(menuItemStyles.label)}>{children}</div> 170 + {suffix && ( 171 + <div {...stylex.props(menuItemStyles.addon)}>{suffix}</div> 172 + )} 151 173 {isSelected && ( 152 174 <div {...stylex.props(menuItemStyles.addon)}> 153 175 <Check size={16} {...stylex.props(menuItemStyles.check)} />
+1 -1
apps/example/src/components/number-field/index.tsx
··· 67 67 description, 68 68 errorMessage, 69 69 style, 70 - size = "md", 70 + size, 71 71 prefix, 72 72 suffix, 73 73 placeholder,
+1 -1
apps/example/src/components/radio/index.tsx
··· 98 98 description, 99 99 errorMessage, 100 100 children, 101 - size = "md", 101 + size, 102 102 style, 103 103 ...props 104 104 }: RadioGroupProps) {
+2 -2
apps/example/src/components/search-field/index.tsx
··· 47 47 description, 48 48 errorMessage, 49 49 style, 50 - size = "md", 50 + size, 51 51 prefix = <SearchIcon />, 52 52 suffix, 53 53 placeholder, ··· 61 61 {({ isEmpty }) => { 62 62 return ( 63 63 <> 64 - <Label size={size}>{label}</Label> 64 + {label && <Label size={size}>{label}</Label>} 65 65 <div 66 66 {...stylex.props(inputStyles.wrapper, styles.wrapper)} 67 67 onClick={() => inputRef.current?.focus()}
+3 -1
apps/example/src/components/select/index.tsx
··· 28 28 import { SizeContext } from "../context"; 29 29 import { useInputStyles } from "../theme/useInputStyles"; 30 30 import { usePopoverStyles } from "../theme/usePopoverStyles"; 31 + import { use } from "react"; 31 32 32 33 const styles = stylex.create({ 33 34 matchWidth: { ··· 66 67 children, 67 68 items, 68 69 style, 69 - size = "md", 70 + size: sizeProp, 70 71 shouldCloseOnInteractOutside, 71 72 shouldFlip, 72 73 shouldUpdatePosition, ··· 76 77 suffix, 77 78 ...props 78 79 }: SelectProps<T, M>) { 80 + const size = sizeProp || use(SizeContext); 79 81 const inputStyles = useInputStyles({ size }); 80 82 const popoverStyles = usePopoverStyles(); 81 83
+4 -2
apps/example/src/components/text-area/index.tsx
··· 14 14 import { radius } from "../theme/radius.stylex"; 15 15 import { lineHeight, fontSize, fontFamily } from "../theme/typography.stylex"; 16 16 import { slate } from "../theme/colors.stylex"; 17 - import { useRef } from "react"; 17 + import { use, useRef } from "react"; 18 18 import { Size } from "../types"; 19 + import { SizeContext } from "../context"; 19 20 20 21 const styles = stylex.create({ 21 22 wrapper: { ··· 127 128 description, 128 129 errorMessage, 129 130 style, 130 - size = "md", 131 + size: sizeProp, 131 132 prefix, 132 133 suffix, 133 134 placeholder, ··· 135 136 ...props 136 137 }: TextAreaProps) { 137 138 const textAreaRef = useRef<HTMLTextAreaElement>(null); 139 + const size = sizeProp || use(SizeContext); 138 140 139 141 return ( 140 142 <AriaTextField {...props} {...stylex.props(styles.wrapper, style)}>
+1 -1
apps/example/src/components/text-field/index.tsx
··· 64 64 description, 65 65 errorMessage, 66 66 style, 67 - size = "md", 67 + size, 68 68 prefix, 69 69 suffix, 70 70 placeholder,
+24
apps/example/src/components/theme/animations.stylex.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + 3 + const fadeIn = stylex.keyframes({ 4 + from: { 5 + opacity: 0, 6 + }, 7 + to: { 8 + opacity: 1, 9 + }, 10 + }); 11 + 12 + const zoomIn = stylex.keyframes({ 13 + from: { 14 + transform: "scale(0.8)", 15 + }, 16 + to: { 17 + transform: "scale(1)", 18 + }, 19 + }); 20 + 21 + export const animations = stylex.defineVars({ 22 + fadeIn, 23 + zoomIn, 24 + });
+3 -1
apps/example/src/components/theme/useButtonStyles.ts
··· 16 16 import { use } from "react"; 17 17 import { Size, ButtonVariant } from "../types"; 18 18 import { ButtonGroupContext } from "../button/context"; 19 + import { SizeContext } from "../context"; 19 20 20 21 const styles = stylex.create({ 21 22 shadow: { ··· 131 132 132 133 export const useButtonStyles = ({ 133 134 variant = "primary", 134 - size = "md", 135 + size: sizeProp, 135 136 }: { 136 137 variant?: ButtonVariant; 137 138 size?: Size; 138 139 }) => { 140 + const size = sizeProp || use(SizeContext); 139 141 const group = use(ButtonGroupContext); 140 142 141 143 return [
+7 -3
apps/example/src/components/theme/useInputStyles.ts
··· 5 5 import { lineHeight, fontSize } from "./typography.stylex"; 6 6 import { slate } from "./colors.stylex"; 7 7 import { Size } from "../types"; 8 + import { use } from "react"; 9 + import { SizeContext } from "../context"; 8 10 9 11 const styles = stylex.create({ 10 12 field: { ··· 93 95 }, 94 96 lgInput: { 95 97 fontSize: fontSize["base"], 96 - paddingLeft: spacing["3"], 97 - paddingRight: spacing["3"], 98 + paddingLeft: spacing["1"], 99 + paddingRight: spacing["2"], 98 100 }, 99 101 }); 100 102 101 - export function useInputStyles({ size = "md" }: { size?: Size }) { 103 + export function useInputStyles({ size: sizeProp }: { size?: Size }) { 104 + const size = sizeProp || use(SizeContext); 105 + 102 106 return { 103 107 field: [styles.field], 104 108 wrapper: [styles.inputWrapper, gray.bgUi, gray.text, styles[size]],
+12 -4
apps/example/src/components/theme/useListBoxItemStyles.ts
··· 49 49 }, 50 50 display: "flex", 51 51 flexGrow: 1, 52 - gap: spacing["2"], 52 + gap: spacing["3"], 53 53 paddingBottom: spacing["2"], 54 - paddingLeft: spacing["2"], 55 - paddingRight: spacing["2"], 54 + paddingLeft: spacing["3"], 55 + paddingRight: spacing["3"], 56 56 paddingTop: spacing["2"], 57 57 transitionDuration: "100ms", 58 58 transitionProperty: "background-color", ··· 60 60 }, 61 61 smItemInner: { 62 62 fontSize: fontSize["xs"], 63 + gap: spacing["2"], 63 64 lineHeight: lineHeight["xs"], 64 65 paddingBottom: spacing["1"], 65 66 paddingTop: spacing["1"], 67 + }, 68 + lgItemInner: { 69 + fontSize: fontSize["base"], 66 70 }, 67 71 check: { 68 72 color: plum[9], ··· 94 98 95 99 return { 96 100 wrapper: [typeramp.label, styles.item, styles[size]], 97 - inner: [styles.itemInner, size === "sm" && styles.smItemInner], 101 + inner: [ 102 + styles.itemInner, 103 + size === "sm" && styles.smItemInner, 104 + size === "lg" && styles.lgItemInner, 105 + ], 98 106 label: styles.label, 99 107 addon: styles.addon, 100 108 check: styles.check,
+1 -1
apps/example/src/components/time-field/index.tsx
··· 29 29 description, 30 30 errorMessage, 31 31 style, 32 - size = "md", 32 + size, 33 33 prefix, 34 34 suffix, 35 35 ...props
+4 -2
apps/example/src/components/toggle-button/index.tsx
··· 4 4 5 5 import { spacing } from "../theme/spacing.stylex"; 6 6 import { plum, slate } from "../theme/colors.stylex"; 7 - import { Children } from "react"; 7 + import { Children, use } from "react"; 8 8 import { useButtonStyles } from "../theme/useButtonStyles"; 9 9 import { ButtonVariant, Size } from "../types"; 10 + import { SizeContext } from "../context"; 10 11 11 12 const styles = stylex.create({ 12 13 primarySelected: { ··· 99 100 export function ToggleButton({ 100 101 style, 101 102 variant = "primary", 102 - size = "md", 103 + size: sizeProp, 103 104 children, 104 105 ...props 105 106 }: ToggleButtonProps) { 107 + const size = sizeProp || use(SizeContext); 106 108 const buttonStyles = useButtonStyles({ variant, size }); 107 109 const toggleButtonStyles = (isSelected?: boolean) => 108 110 stylex.props(
+9 -6
apps/example/src/components/tree/index.tsx
··· 16 16 import { spacing } from "../theme/spacing.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { radius } from "../theme/radius.stylex"; 19 + import { use } from "react"; 19 20 20 21 const styles = stylex.create({ 21 22 wrapper: { ··· 54 55 marginTop: `calc(${spacing["2"]} * -1)`, 55 56 }, 56 57 dragButtonWrapper: { 58 + left: 0, 57 59 opacity: { 58 60 default: 0, 59 61 ":hover": 1, 60 62 ":is([data-react-aria-pressable=true]:hover:not([data-disabled]) *)": 1, 61 63 }, 62 - transitionDuration: "100ms", 63 - transitionProperty: "opacity", 64 - transitionTimingFunction: "ease-in-out", 65 64 position: "absolute", 66 65 top: "50%", 67 - left: 0, 68 66 transform: "translate(-100%, -50%)", 67 + transitionDuration: "100ms", 68 + transitionProperty: "opacity", 69 + transitionTimingFunction: "ease-in-out", 69 70 }, 70 71 dragButton: { 72 + alignItems: "center", 71 73 borderRadius: radius["sm"], 72 74 display: "flex", 73 75 justifyContent: "center", 74 - alignItems: "center", 75 76 76 77 height: spacing["8"], 77 78 width: spacing["8"], ··· 188 189 189 190 export function Tree<T extends object>({ 190 191 style, 191 - size = "md", 192 + size: sizeProp, 192 193 ...props 193 194 }: TreeProps<T>) { 195 + const size = sizeProp || use(SizeContext); 196 + 194 197 return ( 195 198 <SizeContext.Provider value={size}> 196 199 <AriaTree {...props} {...stylex.props(style)} />
+1 -1
packages/eslint-config/package.json
··· 22 22 "typescript-eslint": "^8.40.0" 23 23 }, 24 24 "dependencies": { 25 - "@stylexjs/eslint-plugin": "^0.16.1" 25 + "@stylexjs/eslint-plugin": "^0.16.2" 26 26 } 27 27 }
+2 -1
packages/hip-ui/package.json
··· 25 25 }, 26 26 "dependencies": { 27 27 "@inkjs/ui": "^2.0.0", 28 + "@react-stately/utils": "^3.10.8", 28 29 "@react-types/overlays": "^3.9.2", 29 - "@stylexjs/stylex": "^0.16.1", 30 + "@stylexjs/stylex": "^0.16.2", 30 31 "command-line-application": "^0.10.1", 31 32 "ink": "^6.3.1", 32 33 "lilconfig": "^3.1.3",
+2
packages/hip-ui/src/cli/install.tsx
··· 37 37 import { numberFieldConfig } from "../components/number-field/number-field-config.js"; 38 38 import { comboboxConfig } from "../components/combobox/combobox-config.js"; 39 39 import { treeConfig } from "../components/tree/tree-config.js"; 40 + import { commandMenuConfig } from "../components/command-menu/command-menu-config.js"; 40 41 41 42 const __dirname = path.dirname(new URL(import.meta.url).pathname); 42 43 ··· 69 70 numberFieldConfig, 70 71 comboboxConfig, 71 72 treeConfig, 73 + commandMenuConfig, 72 74 ]; 73 75 74 76 function StringSetting({
+1 -1
packages/hip-ui/src/components/button/index.tsx
··· 19 19 children, 20 20 style, 21 21 variant = "primary", 22 - size = "md", 22 + size, 23 23 ...props 24 24 }: ButtonProps) => { 25 25 const buttonStyles = useButtonStyles({ variant, size });
+1 -1
packages/hip-ui/src/components/checkbox/index.tsx
··· 68 68 description, 69 69 errorMessage, 70 70 children, 71 - size = "md", 71 + size, 72 72 style, 73 73 ...props 74 74 }: CheckboxGroupProps) {
+2 -2
packages/hip-ui/src/components/color-field/index.tsx
··· 29 29 description, 30 30 errorMessage, 31 31 style, 32 - size = "md", 32 + size, 33 33 prefix, 34 34 suffix, 35 35 placeholder, ··· 40 40 41 41 return ( 42 42 <AriaColorField {...props} {...stylex.props(inputStyles.field, style)}> 43 - <Label size={size}>{label}</Label> 43 + {label && <Label size={size}>{label}</Label>} 44 44 <div 45 45 {...stylex.props(inputStyles.wrapper)} 46 46 onClick={() => inputRef.current?.focus()}
+3 -1
packages/hip-ui/src/components/combobox/index.tsx
··· 32 32 import { IconButton } from "../icon-button"; 33 33 import { SmallBody } from "../typography"; 34 34 import { spacing } from "../theme/spacing.stylex"; 35 + import { use } from "react"; 35 36 36 37 const styles = stylex.create({ 37 38 matchWidth: { ··· 81 82 children, 82 83 items, 83 84 style, 84 - size = "md", 85 + size: sizeProp, 85 86 shouldCloseOnInteractOutside, 86 87 shouldFlip, 87 88 shouldUpdatePosition, ··· 92 93 renderEmptyState, 93 94 ...props 94 95 }: ComboBoxProps<T>) { 96 + const size = sizeProp || use(SizeContext); 95 97 const inputStyles = useInputStyles({ size }); 96 98 const popoverStyles = usePopoverStyles(); 97 99
+17
packages/hip-ui/src/components/command-menu/command-menu-config.ts
··· 1 + import { ComponentConfig } from "../../types"; 2 + 3 + export const commandMenuConfig: ComponentConfig = { 4 + name: "command-menu", 5 + filepath: "./index.tsx", 6 + hipDependencies: [ 7 + "../context.ts", 8 + "../theme/usePopoverStyles.ts", 9 + "../theme/useListBoxItemStyles.ts", 10 + "../theme/animations.stylex.tsx", 11 + ], 12 + dependencies: { 13 + "lucide-react": "^0.545.0", 14 + "@react-stately/utils": "^3.10.8", 15 + "react-stately": "^3.42.0", 16 + }, 17 + };
+188
packages/hip-ui/src/components/command-menu/index.tsx
··· 1 + import { 2 + InputProps, 3 + Modal, 4 + Dialog, 5 + Autocomplete, 6 + Menu, 7 + useFilter, 8 + ModalOverlay, 9 + AutocompleteProps as AriaAutocompleteProps, 10 + } from "react-aria-components"; 11 + import { useControlledState } from "@react-stately/utils"; 12 + import { SearchField } from "../search-field"; 13 + import { OverlayTriggerProps } from "react-stately"; 14 + import { 15 + MenuItem, 16 + MenuItemProps, 17 + MenuSection, 18 + MenuSectionHeader, 19 + MenuSectionHeaderProps, 20 + MenuSectionProps, 21 + MenuSeparator, 22 + MenuSeparatorProps, 23 + } from "../menu"; 24 + import { useEffect, useEffectEvent } from "react"; 25 + import * as stylex from "@stylexjs/stylex"; 26 + import { shadow } from "../theme/shadow.stylex"; 27 + import { radius } from "../theme/radius.stylex"; 28 + import { gray } from "../theme/semantic-color.stylex"; 29 + import { animations } from "../theme/animations.stylex"; 30 + import { spacing } from "../theme/spacing.stylex"; 31 + import { SizeContext } from "../context"; 32 + import { Separator } from "../separator"; 33 + 34 + const styles = stylex.create({ 35 + overlay: { 36 + backgroundColor: "rgba(0, 0, 0, 0.5)", 37 + height: "var(--page-height)", 38 + left: 0, 39 + position: "absolute", 40 + top: 0, 41 + width: "100vw", 42 + zIndex: 100, 43 + 44 + animationDuration: "200ms", 45 + animationName: { 46 + ":is([data-entering])": animations.fadeIn, 47 + }, 48 + animationTimingFunction: "ease-in", 49 + opacity: { 50 + default: 1, 51 + ":is([data-exiting])": 0, 52 + }, 53 + transitionDuration: { 54 + ":is([data-exiting])": "100ms", 55 + }, 56 + transitionProperty: "opacity", 57 + transitionTimingFunction: "ease-in-out", 58 + }, 59 + modal: { 60 + borderRadius: radius["lg"], 61 + boxShadow: shadow["lg"], 62 + display: "flex", 63 + flexDirection: "column", 64 + left: "50%", 65 + maxHeight: "calc(var(--visual-viewport-height) * 0.8)", 66 + outline: "none", 67 + position: "fixed", 68 + top: "calc(var(--visual-viewport-height) / 2)", 69 + translate: "-50% -50%", 70 + width: 400, 71 + 72 + animationDuration: { ":is([data-entering])": "300ms" }, 73 + animationName: { ":is([data-entering])": animations.zoomIn }, 74 + animationTimingFunction: { 75 + ":is([data-entering])": "cubic-bezier(0.175, 0.885, 0.32, 1.275)", 76 + }, 77 + }, 78 + dialog: { 79 + display: "flex", 80 + flexDirection: "column", 81 + flexGrow: 1, 82 + minHeight: 0, 83 + outline: "none", 84 + }, 85 + menu: { 86 + flexGrow: 1, 87 + marginLeft: `calc(${spacing["0.5"]} * -1)`, 88 + marginRight: `calc(${spacing["0.5"]} * -1)`, 89 + minHeight: 0, 90 + overflowY: "auto", 91 + paddingBottom: spacing["2"], 92 + paddingLeft: spacing["3"], 93 + paddingRight: spacing["3"], 94 + paddingTop: spacing["2"], 95 + }, 96 + searchField: { 97 + paddingBottom: spacing["3"], 98 + paddingLeft: spacing["3"], 99 + paddingRight: spacing["3"], 100 + paddingTop: spacing["3"], 101 + }, 102 + }); 103 + 104 + export interface CommandMenuProps<T extends object> 105 + extends OverlayTriggerProps, 106 + Pick<InputProps, "placeholder">, 107 + AriaAutocompleteProps<T> { 108 + children: React.ReactNode; 109 + disableGlobalShortcut?: boolean; 110 + } 111 + 112 + export function CommandMenu<T extends object>({ 113 + defaultOpen, 114 + isOpen: isOpenProp, 115 + onOpenChange, 116 + filter, 117 + placeholder = "Search commands", 118 + children, 119 + defaultInputValue, 120 + disableAutoFocusFirst, 121 + disableVirtualFocus, 122 + inputValue, 123 + onInputChange, 124 + disableGlobalShortcut = false, 125 + }: CommandMenuProps<T>) { 126 + const defaultFilter = useFilter({ sensitivity: "base" }); 127 + const [isOpen, setIsOpen] = useControlledState( 128 + isOpenProp, 129 + defaultOpen ?? false, 130 + onOpenChange 131 + ); 132 + const onClose = useEffectEvent(() => setIsOpen(false)); 133 + 134 + useEffect(() => { 135 + if (disableGlobalShortcut) return; 136 + 137 + function handleKeyDown(event: KeyboardEvent) { 138 + if (event.metaKey && event.key === "k") { 139 + setIsOpen(true); 140 + } 141 + } 142 + 143 + window.addEventListener("keydown", handleKeyDown); 144 + 145 + return () => window.removeEventListener("keydown", handleKeyDown); 146 + }, [setIsOpen, disableGlobalShortcut]); 147 + 148 + return ( 149 + <SizeContext.Provider value="lg"> 150 + <ModalOverlay 151 + isDismissable 152 + isOpen={isOpen} 153 + onOpenChange={setIsOpen} 154 + {...stylex.props(styles.overlay)} 155 + > 156 + <Modal {...stylex.props(styles.modal, gray.bg, gray.text, gray.border)}> 157 + <Dialog {...stylex.props(styles.dialog)}> 158 + <Autocomplete 159 + filter={filter ?? defaultFilter.contains} 160 + defaultInputValue={defaultInputValue} 161 + disableAutoFocusFirst={disableAutoFocusFirst} 162 + disableVirtualFocus={disableVirtualFocus} 163 + inputValue={inputValue} 164 + onInputChange={onInputChange} 165 + > 166 + <div {...stylex.props(styles.searchField)}> 167 + <SearchField placeholder={placeholder} autoFocus /> 168 + </div> 169 + <Separator /> 170 + <Menu {...stylex.props(styles.menu)} onAction={onClose}> 171 + {children} 172 + </Menu> 173 + </Autocomplete> 174 + </Dialog> 175 + </Modal> 176 + </ModalOverlay> 177 + </SizeContext.Provider> 178 + ); 179 + } 180 + 181 + export type CommandMenuItemProps = MenuItemProps; 182 + export const CommandMenuItem = MenuItem; 183 + export type CommandMenuSectionHeaderProps = MenuSectionHeaderProps; 184 + export const CommandMenuSectionHeader = MenuSectionHeader; 185 + export type CommandMenuSectionProps<T extends object> = MenuSectionProps<T>; 186 + export const CommandMenuSection = MenuSection; 187 + export type CommandMenuSeparatorProps = MenuSeparatorProps; 188 + export const CommandMenuSeparator = MenuSeparator;
+2 -1
packages/hip-ui/src/components/context-menu/index.tsx
··· 140 140 141 141 export function ContextMenu<T extends object>({ 142 142 trigger, 143 - size = "md", 143 + size: sizeProp, 144 144 defaultOpen, 145 145 isOpen, 146 146 onOpenChange, ··· 151 151 ...props 152 152 }: ContextMenuProps<T>) { 153 153 const popoverStyles = usePopoverStyles(); 154 + const size = sizeProp || use(SizeContext); 154 155 155 156 return ( 156 157 <SizeContext.Provider value={size}>
+2 -2
packages/hip-ui/src/components/date-field/index.tsx
··· 29 29 description, 30 30 errorMessage, 31 31 style, 32 - size = "md", 32 + size, 33 33 prefix, 34 34 suffix, 35 35 ...props ··· 39 39 40 40 return ( 41 41 <AriaDateField {...props} {...stylex.props(inputStyles.field, style)}> 42 - <Label size={size}>{label}</Label> 42 + {label && <Label size={size}>{label}</Label>} 43 43 <div 44 44 {...stylex.props(inputStyles.wrapper)} 45 45 onClick={() => inputRef.current?.focus()}
+5 -1
packages/hip-ui/src/components/icon-button/index.tsx
··· 7 7 import { Button } from "../button"; 8 8 import { spacing } from "../theme/spacing.stylex"; 9 9 import { ButtonVariant, Size } from "../types"; 10 + import { use } from "react"; 11 + import { SizeContext } from "../context"; 10 12 11 13 const styles = stylex.create({ 12 14 sm: { ··· 32 34 33 35 export const IconButton = ({ 34 36 children, 35 - size = "md", 37 + size: sizeProp, 36 38 label, 37 39 style, 38 40 ...props 39 41 }: IconButtonProps) => { 42 + const size = sizeProp || use(SizeContext); 43 + 40 44 return ( 41 45 <Tooltip text={label}> 42 46 <Button size={size} style={[styles[size], style]} {...props}>
+8 -2
packages/hip-ui/src/components/label/index.tsx
··· 9 9 import { fontSize, fontWeight, lineHeight } from "../theme/typography.stylex"; 10 10 import { gray } from "../theme/semantic-color.stylex"; 11 11 import { Size } from "../types"; 12 + import { use } from "react"; 13 + import { SizeContext } from "../context"; 12 14 13 15 const styles = stylex.create({ 14 16 label: { ··· 43 45 size?: Size; 44 46 } 45 47 46 - export function Label({ style, size = "md", ...props }: LabelProps) { 48 + export function Label({ style, size: sizeProp, ...props }: LabelProps) { 49 + const size = sizeProp || use(SizeContext); 50 + 47 51 return ( 48 52 <AriaLabel 49 53 {...props} ··· 60 64 61 65 export function Description({ 62 66 style, 63 - size = "md", 67 + size: sizeProp, 64 68 ...props 65 69 }: DescriptionProps) { 70 + const size = sizeProp || use(SizeContext); 71 + 66 72 return ( 67 73 <Text 68 74 slot="description"
+4 -3
packages/hip-ui/src/components/listbox/index.tsx
··· 14 14 import { typeramp } from "../theme/typography.stylex"; 15 15 import { Size } from "../types"; 16 16 import { SizeContext } from "../context"; 17 - import { use, useContext } from "react"; 17 + import { use } from "react"; 18 18 import { Separator } from "../separator"; 19 19 import { gray } from "../theme/semantic-color.stylex"; 20 20 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; ··· 60 60 style, 61 61 ...props 62 62 }: ListBoxProps<T>) { 63 - const size = sizeProp || use(SizeContext) || "md"; 63 + const size = sizeProp || use(SizeContext); 64 64 65 65 return ( 66 66 <SizeContext.Provider value={size}> ··· 144 144 style, 145 145 ...props 146 146 }: ListBoxSectionHeaderProps) { 147 - const size = useContext(SizeContext); 147 + const size = use(SizeContext); 148 + 148 149 return ( 149 150 <Header 150 151 {...props}
+25 -3
packages/hip-ui/src/components/menu/index.tsx
··· 24 24 import { Check, ChevronRight } from "lucide-react"; 25 25 import { usePopoverStyles } from "../theme/usePopoverStyles"; 26 26 import { useListBoxItemStyles } from "../theme/useListBoxItemStyles"; 27 + import { use } from "react"; 27 28 28 29 export interface MenuProps<T extends object> 29 30 extends Omit<MenuTriggerProps, "trigger" | "children">, ··· 43 44 44 45 export function Menu<T extends object>({ 45 46 trigger, 46 - size = "md", 47 + size: sizeProp, 47 48 defaultOpen, 48 49 isOpen, 49 50 onOpenChange, ··· 54 55 ...props 55 56 }: MenuProps<T>) { 56 57 const popoverStyles = usePopoverStyles(); 58 + const size = sizeProp || use(SizeContext); 57 59 58 60 return ( 59 61 <SizeContext.Provider value={size}> ··· 138 140 extends Omit<AriaMenuItemProps, "style" | "className" | "children"> { 139 141 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 140 142 children: React.ReactNode; 143 + prefix?: React.ReactNode; 144 + suffix?: React.ReactNode; 141 145 } 142 146 143 - export function MenuItem({ style, children, ...props }: MenuItemProps) { 147 + export function MenuItem({ 148 + style, 149 + children, 150 + prefix, 151 + suffix, 152 + ...props 153 + }: MenuItemProps) { 144 154 const menuItemStyles = useListBoxItemStyles(); 145 155 146 156 return ( 147 - <AriaMenuItem {...props} {...stylex.props(menuItemStyles.wrapper, style)}> 157 + <AriaMenuItem 158 + {...props} 159 + textValue={ 160 + props.textValue || (typeof children === "string" ? children : undefined) 161 + } 162 + {...stylex.props(menuItemStyles.wrapper, style)} 163 + > 148 164 {({ isSelected, hasSubmenu }) => ( 149 165 <div {...stylex.props(menuItemStyles.inner)}> 166 + {prefix && ( 167 + <div {...stylex.props(menuItemStyles.addon)}>{prefix}</div> 168 + )} 150 169 <div {...stylex.props(menuItemStyles.label)}>{children}</div> 170 + {suffix && ( 171 + <div {...stylex.props(menuItemStyles.addon)}>{suffix}</div> 172 + )} 151 173 {isSelected && ( 152 174 <div {...stylex.props(menuItemStyles.addon)}> 153 175 <Check size={16} {...stylex.props(menuItemStyles.check)} />
+1 -1
packages/hip-ui/src/components/number-field/index.tsx
··· 67 67 description, 68 68 errorMessage, 69 69 style, 70 - size = "md", 70 + size, 71 71 prefix, 72 72 suffix, 73 73 placeholder,
+1 -1
packages/hip-ui/src/components/radio/index.tsx
··· 98 98 description, 99 99 errorMessage, 100 100 children, 101 - size = "md", 101 + size, 102 102 style, 103 103 ...props 104 104 }: RadioGroupProps) {
+2 -2
packages/hip-ui/src/components/search-field/index.tsx
··· 47 47 description, 48 48 errorMessage, 49 49 style, 50 - size = "md", 50 + size, 51 51 prefix = <SearchIcon />, 52 52 suffix, 53 53 placeholder, ··· 61 61 {({ isEmpty }) => { 62 62 return ( 63 63 <> 64 - <Label size={size}>{label}</Label> 64 + {label && <Label size={size}>{label}</Label>} 65 65 <div 66 66 {...stylex.props(inputStyles.wrapper, styles.wrapper)} 67 67 onClick={() => inputRef.current?.focus()}
+3 -1
packages/hip-ui/src/components/select/index.tsx
··· 28 28 import { SizeContext } from "../context"; 29 29 import { useInputStyles } from "../theme/useInputStyles"; 30 30 import { usePopoverStyles } from "../theme/usePopoverStyles"; 31 + import { use } from "react"; 31 32 32 33 const styles = stylex.create({ 33 34 matchWidth: { ··· 66 67 children, 67 68 items, 68 69 style, 69 - size = "md", 70 + size: sizeProp, 70 71 shouldCloseOnInteractOutside, 71 72 shouldFlip, 72 73 shouldUpdatePosition, ··· 76 77 suffix, 77 78 ...props 78 79 }: SelectProps<T, M>) { 80 + const size = sizeProp || use(SizeContext); 79 81 const inputStyles = useInputStyles({ size }); 80 82 const popoverStyles = usePopoverStyles(); 81 83
+5 -3
packages/hip-ui/src/components/text-area/index.tsx
··· 14 14 import { radius } from "../theme/radius.stylex"; 15 15 import { lineHeight, fontSize, fontFamily } from "../theme/typography.stylex"; 16 16 import { slate } from "../theme/colors.stylex"; 17 - import { useRef } from "react"; 17 + import { use, useRef } from "react"; 18 18 import { Size } from "../types"; 19 + import { SizeContext } from "../context"; 19 20 20 21 const styles = stylex.create({ 21 22 wrapper: { ··· 127 128 description, 128 129 errorMessage, 129 130 style, 130 - size = "md", 131 + size: sizeProp, 131 132 prefix, 132 133 suffix, 133 134 placeholder, ··· 135 136 ...props 136 137 }: TextAreaProps) { 137 138 const textAreaRef = useRef<HTMLTextAreaElement>(null); 139 + const size = sizeProp || use(SizeContext); 138 140 139 141 return ( 140 142 <AriaTextField {...props} {...stylex.props(styles.wrapper, style)}> 141 - <Label size={size}>{label}</Label> 143 + {label && <Label size={size}>{label}</Label>} 142 144 <div 143 145 {...stylex.props(styles.inputWrapper, gray.bgUi, gray.text)} 144 146 onClick={() => textAreaRef.current?.focus()}
+2 -2
packages/hip-ui/src/components/text-field/index.tsx
··· 64 64 description, 65 65 errorMessage, 66 66 style, 67 - size = "md", 67 + size, 68 68 prefix, 69 69 suffix, 70 70 placeholder, ··· 83 83 type={type} 84 84 {...stylex.props(inputStyles.field, style)} 85 85 > 86 - <Label size={size}>{label}</Label> 86 + {label && <Label size={size}>{label}</Label>} 87 87 <div 88 88 {...stylex.props(inputStyles.wrapper)} 89 89 onClick={() => inputRef.current?.focus()}
+24
packages/hip-ui/src/components/theme/animations.stylex.tsx
··· 1 + import * as stylex from "@stylexjs/stylex"; 2 + 3 + const fadeIn = stylex.keyframes({ 4 + from: { 5 + opacity: 0, 6 + }, 7 + to: { 8 + opacity: 1, 9 + }, 10 + }); 11 + 12 + const zoomIn = stylex.keyframes({ 13 + from: { 14 + transform: "scale(0.8)", 15 + }, 16 + to: { 17 + transform: "scale(1)", 18 + }, 19 + }); 20 + 21 + export const animations = stylex.defineVars({ 22 + fadeIn, 23 + zoomIn, 24 + });
+3 -1
packages/hip-ui/src/components/theme/useButtonStyles.ts
··· 16 16 import { use } from "react"; 17 17 import { Size, ButtonVariant } from "../types"; 18 18 import { ButtonGroupContext } from "../button/context"; 19 + import { SizeContext } from "../context"; 19 20 20 21 const styles = stylex.create({ 21 22 shadow: { ··· 131 132 132 133 export const useButtonStyles = ({ 133 134 variant = "primary", 134 - size = "md", 135 + size: sizeProp, 135 136 }: { 136 137 variant?: ButtonVariant; 137 138 size?: Size; 138 139 }) => { 140 + const size = sizeProp || use(SizeContext); 139 141 const group = use(ButtonGroupContext); 140 142 141 143 return [
+7 -3
packages/hip-ui/src/components/theme/useInputStyles.ts
··· 5 5 import { lineHeight, fontSize } from "./typography.stylex"; 6 6 import { slate } from "./colors.stylex"; 7 7 import { Size } from "../types"; 8 + import { use } from "react"; 9 + import { SizeContext } from "../context"; 8 10 9 11 const styles = stylex.create({ 10 12 field: { ··· 93 95 }, 94 96 lgInput: { 95 97 fontSize: fontSize["base"], 96 - paddingLeft: spacing["3"], 97 - paddingRight: spacing["3"], 98 + paddingLeft: spacing["1"], 99 + paddingRight: spacing["2"], 98 100 }, 99 101 }); 100 102 101 - export function useInputStyles({ size = "md" }: { size?: Size }) { 103 + export function useInputStyles({ size: sizeProp }: { size?: Size }) { 104 + const size = sizeProp || use(SizeContext); 105 + 102 106 return { 103 107 field: [styles.field], 104 108 wrapper: [styles.inputWrapper, gray.bgUi, gray.text, styles[size]],
+12 -4
packages/hip-ui/src/components/theme/useListBoxItemStyles.ts
··· 49 49 }, 50 50 display: "flex", 51 51 flexGrow: 1, 52 - gap: spacing["2"], 52 + gap: spacing["3"], 53 53 paddingBottom: spacing["2"], 54 - paddingLeft: spacing["2"], 55 - paddingRight: spacing["2"], 54 + paddingLeft: spacing["3"], 55 + paddingRight: spacing["3"], 56 56 paddingTop: spacing["2"], 57 57 transitionDuration: "100ms", 58 58 transitionProperty: "background-color", ··· 60 60 }, 61 61 smItemInner: { 62 62 fontSize: fontSize["xs"], 63 + gap: spacing["2"], 63 64 lineHeight: lineHeight["xs"], 64 65 paddingBottom: spacing["1"], 65 66 paddingTop: spacing["1"], 67 + }, 68 + lgItemInner: { 69 + fontSize: fontSize["base"], 66 70 }, 67 71 check: { 68 72 color: plum[9], ··· 94 98 95 99 return { 96 100 wrapper: [typeramp.label, styles.item, styles[size]], 97 - inner: [styles.itemInner, size === "sm" && styles.smItemInner], 101 + inner: [ 102 + styles.itemInner, 103 + size === "sm" && styles.smItemInner, 104 + size === "lg" && styles.lgItemInner, 105 + ], 98 106 label: styles.label, 99 107 addon: styles.addon, 100 108 check: styles.check,
+2 -2
packages/hip-ui/src/components/time-field/index.tsx
··· 29 29 description, 30 30 errorMessage, 31 31 style, 32 - size = "md", 32 + size, 33 33 prefix, 34 34 suffix, 35 35 ...props ··· 39 39 40 40 return ( 41 41 <AriaTimeField {...props} {...stylex.props(inputStyles.field, style)}> 42 - <Label size={size}>{label}</Label> 42 + {label && <Label size={size}>{label}</Label>} 43 43 <div 44 44 {...stylex.props(inputStyles.wrapper)} 45 45 onClick={() => inputRef.current?.focus()}
+4 -2
packages/hip-ui/src/components/toggle-button copy/index.tsx
··· 4 4 5 5 import { spacing } from "../theme/spacing.stylex"; 6 6 import { plum, slate } from "../theme/colors.stylex"; 7 - import { Children } from "react"; 7 + import { Children, use } from "react"; 8 8 import { useButtonStyles } from "../theme/useButtonStyles"; 9 + import { SizeContext } from "../context"; 9 10 10 11 const styles = stylex.create({ 11 12 primarySelected: { ··· 98 99 export function ToggleButton({ 99 100 style, 100 101 variant = "primary", 101 - size = "md", 102 + size: sizeProp, 102 103 children, 103 104 ...props 104 105 }: ToggleButtonProps) { 106 + const size = sizeProp || use(SizeContext); 105 107 const buttonStyles = useButtonStyles({ variant, size }); 106 108 const toggleButtonStyles = (isSelected?: boolean) => 107 109 stylex.props(
+1
packages/hip-ui/src/components/toggle-button-group/index.tsx
··· 31 31 borderTopRightRadius: { ":is(:not(:last-child)) *": "0" }, 32 32 }, 33 33 verticalContents: { 34 + // eslint-disable-next-line @stylexjs/valid-styles 34 35 borderBottomLeftWidth: { ":is(:not(:first-child)) *": "0" }, 35 36 borderBottomRightRadius: { ":is(:not(:last-child)) *": "0" }, 36 37 borderBottomWidth: { ":is(:not(:last-child)) *": "0" },
+4 -2
packages/hip-ui/src/components/toggle-button/index.tsx
··· 4 4 5 5 import { spacing } from "../theme/spacing.stylex"; 6 6 import { plum, slate } from "../theme/colors.stylex"; 7 - import { Children } from "react"; 7 + import { Children, use } from "react"; 8 8 import { useButtonStyles } from "../theme/useButtonStyles"; 9 9 import { ButtonVariant, Size } from "../types"; 10 + import { SizeContext } from "../context"; 10 11 11 12 const styles = stylex.create({ 12 13 primarySelected: { ··· 99 100 export function ToggleButton({ 100 101 style, 101 102 variant = "primary", 102 - size = "md", 103 + size: sizeProp, 103 104 children, 104 105 ...props 105 106 }: ToggleButtonProps) { 107 + const size = sizeProp || use(SizeContext); 106 108 const buttonStyles = useButtonStyles({ variant, size }); 107 109 const toggleButtonStyles = (isSelected?: boolean) => 108 110 stylex.props(
+9 -6
packages/hip-ui/src/components/tree/index.tsx
··· 16 16 import { spacing } from "../theme/spacing.stylex"; 17 17 import { gray } from "../theme/semantic-color.stylex"; 18 18 import { radius } from "../theme/radius.stylex"; 19 + import { use } from "react"; 19 20 20 21 const styles = stylex.create({ 21 22 wrapper: { ··· 54 55 marginTop: `calc(${spacing["2"]} * -1)`, 55 56 }, 56 57 dragButtonWrapper: { 58 + left: 0, 57 59 opacity: { 58 60 default: 0, 59 61 ":hover": 1, 60 62 ":is([data-react-aria-pressable=true]:hover:not([data-disabled]) *)": 1, 61 63 }, 62 - transitionDuration: "100ms", 63 - transitionProperty: "opacity", 64 64 position: "absolute", 65 - transitionTimingFunction: "ease-in-out", 66 - left: 0, 67 65 top: "50%", 68 66 transform: "translate(-100%, -50%)", 67 + transitionDuration: "100ms", 68 + transitionProperty: "opacity", 69 + transitionTimingFunction: "ease-in-out", 69 70 }, 70 71 dragButton: { 72 + alignItems: "center", 71 73 borderRadius: radius["sm"], 72 74 display: "flex", 73 - alignItems: "center", 74 75 justifyContent: "center", 75 76 76 77 height: spacing["8"], ··· 188 189 189 190 export function Tree<T extends object>({ 190 191 style, 191 - size = "md", 192 + size: sizeProp, 192 193 ...props 193 194 }: TreeProps<T>) { 195 + const size = sizeProp || use(SizeContext); 196 + 194 197 return ( 195 198 <SizeContext.Provider value={size}> 196 199 <AriaTree {...props} {...stylex.props(style)} />
+34 -28
pnpm-lock.yaml
··· 35 35 36 36 apps/example: 37 37 dependencies: 38 + '@react-stately/utils': 39 + specifier: ^3.10.8 40 + version: 3.10.8(react@19.2.0) 38 41 '@react-types/overlays': 39 42 specifier: ^3.9.2 40 43 version: 3.9.2(react@19.2.0) ··· 43 46 version: link:../../packages/typescript-config 44 47 '@stylexjs/stylex': 45 48 specifier: ^0.16.1 46 - version: 0.16.1 49 + version: 0.16.2 47 50 hip-ui: 48 51 specifier: workspace:* 49 52 version: link:../../packages/hip-ui ··· 70 73 version: 3.42.0(react@19.2.0) 71 74 unplugin-stylex: 72 75 specifier: ^0.5.5 73 - version: 0.5.5(@stylexjs/stylex@0.16.1)(rollup@4.52.4)(vitest@3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)) 76 + version: 0.5.5(@stylexjs/stylex@0.16.2)(rollup@4.52.4)(vitest@3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)) 74 77 waku: 75 78 specifier: 0.26.1 76 79 version: 0.26.1(@swc/helpers@0.5.17)(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(react-dom@19.2.0(react@19.2.0))(react-server-dom-webpack@19.1.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(webpack@5.102.1))(react@19.2.0)(terser@5.44.0) ··· 88 91 packages/eslint-config: 89 92 dependencies: 90 93 '@stylexjs/eslint-plugin': 91 - specifier: ^0.16.1 92 - version: 0.16.1 94 + specifier: ^0.16.2 95 + version: 0.16.2 93 96 devDependencies: 94 97 '@eslint/js': 95 98 specifier: ^9.34.0 ··· 130 133 '@inkjs/ui': 131 134 specifier: ^2.0.0 132 135 version: 2.0.0(ink@6.3.1(@types/react@19.2.2)(react@19.2.0)) 136 + '@react-stately/utils': 137 + specifier: ^3.10.8 138 + version: 3.10.8(react@19.2.0) 133 139 '@react-types/overlays': 134 140 specifier: ^3.9.2 135 141 version: 3.9.2(react@19.2.0) 136 142 '@stylexjs/stylex': 137 - specifier: ^0.16.1 138 - version: 0.16.1 143 + specifier: ^0.16.2 144 + version: 0.16.2 139 145 command-line-application: 140 146 specifier: ^0.10.1 141 147 version: 0.10.1 ··· 1316 1322 cpu: [x64] 1317 1323 os: [win32] 1318 1324 1319 - '@stylexjs/babel-plugin@0.16.1': 1320 - resolution: {integrity: sha512-IdoS++ERG3IBI3H2yQwI/ZWfW6MKhMeEinPfdKJplVIGTCsNHJGzLu26gxR0EctoO1hblxme3+HR/Qo2PXdLIA==} 1325 + '@stylexjs/babel-plugin@0.16.2': 1326 + resolution: {integrity: sha512-rnxEaAwZvg7WLjsKn1hYbTYXvRFRX6qisp+9JLrPY5cyzskh19k8sp+HiO8m2vTEvoScq1SC19fFzuiSXIRKbQ==} 1321 1327 1322 - '@stylexjs/eslint-plugin@0.16.1': 1323 - resolution: {integrity: sha512-SWmJMJG/dl2UjOvbzebjmw5ZZqGuwU48g4RK3ruRCTv3BLlp9tGbTzzlpzCB3qJTiBh7VJ4SezpEmjq07iwszA==} 1328 + '@stylexjs/eslint-plugin@0.16.2': 1329 + resolution: {integrity: sha512-7rAS8NjiPjKluOA2XInpyGimmVRiXzxYSCSf66P5ofjgluFXQUBdteojN6jvssiP25L5wwIN/su5nBXwHxhjeQ==} 1324 1330 1325 - '@stylexjs/stylex@0.16.1': 1326 - resolution: {integrity: sha512-5VAlC+42ds5pengRTtsj5r9qnSRL8INjYP/I+PrD5j1OtzDzYROU1Sw3GLTOWA8r+P4LT7+QB0j+ihBOYVesTA==} 1331 + '@stylexjs/stylex@0.16.2': 1332 + resolution: {integrity: sha512-nOInNL+F70hsI9fwxCTO8qnzhF8bQTvU4CeeqjAb7BtvAAryey6zBHJCJ7u94P8ES8suB9NOaPFr4DIxmr319Q==} 1327 1333 1328 1334 '@swc/core-darwin-arm64@1.13.5': 1329 1335 resolution: {integrity: sha512-lKNv7SujeXvKn16gvQqUQI5DdyY8v7xcoO3k06/FJbHJS90zEwZdQiMNRiqpYw/orU543tPaWgz7cIYWhbopiQ==} ··· 1720 1726 resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 1721 1727 engines: {node: '>=12'} 1722 1728 1723 - ast-v8-to-istanbul@0.3.5: 1724 - resolution: {integrity: sha512-9SdXjNheSiE8bALAQCQQuT6fgQaoxJh7IRYrRGZ8/9nv8WhJeC1aXAwN8TbaOssGOukUvyvnkgD9+Yuykvl1aA==} 1729 + ast-v8-to-istanbul@0.3.7: 1730 + resolution: {integrity: sha512-kr1Hy6YRZBkGQSb6puP+D6FQ59Cx4m0siYhAxygMCAgadiWQ6oxAxQXHOMvJx67SJ63jRoVIIg5eXzUbbct1ww==} 1725 1731 1726 1732 async-function@1.0.0: 1727 1733 resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} ··· 3079 3085 stackback@0.0.2: 3080 3086 resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 3081 3087 3082 - std-env@3.9.0: 3083 - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} 3088 + std-env@3.10.0: 3089 + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} 3084 3090 3085 3091 stop-iteration-iterator@1.1.0: 3086 3092 resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} ··· 5033 5039 '@rollup/rollup-win32-x64-msvc@4.52.4': 5034 5040 optional: true 5035 5041 5036 - '@stylexjs/babel-plugin@0.16.1': 5042 + '@stylexjs/babel-plugin@0.16.2': 5037 5043 dependencies: 5038 5044 '@babel/core': 7.28.4 5039 5045 '@babel/helper-module-imports': 7.27.1 5040 5046 '@babel/traverse': 7.28.4 5041 5047 '@babel/types': 7.28.4 5042 5048 '@dual-bundle/import-meta-resolve': 4.2.1 5043 - '@stylexjs/stylex': 0.16.1 5049 + '@stylexjs/stylex': 0.16.2 5044 5050 postcss-value-parser: 4.2.0 5045 5051 transitivePeerDependencies: 5046 5052 - supports-color 5047 5053 5048 - '@stylexjs/eslint-plugin@0.16.1': 5054 + '@stylexjs/eslint-plugin@0.16.2': 5049 5055 dependencies: 5050 5056 css-shorthand-expand: 1.2.0 5051 5057 micromatch: 4.0.8 5052 5058 postcss-value-parser: 4.2.0 5053 5059 5054 - '@stylexjs/stylex@0.16.1': 5060 + '@stylexjs/stylex@0.16.2': 5055 5061 dependencies: 5056 5062 css-mediaquery: 0.1.2 5057 5063 invariant: 2.2.4 ··· 5297 5303 dependencies: 5298 5304 '@ampproject/remapping': 2.3.0 5299 5305 '@bcoe/v8-coverage': 1.0.2 5300 - ast-v8-to-istanbul: 0.3.5 5306 + ast-v8-to-istanbul: 0.3.7 5301 5307 debug: 4.4.1 5302 5308 istanbul-lib-coverage: 3.2.2 5303 5309 istanbul-lib-report: 3.0.1 ··· 5305 5311 istanbul-reports: 3.2.0 5306 5312 magic-string: 0.30.19 5307 5313 magicast: 0.3.5 5308 - std-env: 3.9.0 5314 + std-env: 3.10.0 5309 5315 test-exclude: 7.0.1 5310 5316 tinyrainbow: 2.0.0 5311 5317 vitest: 3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0) ··· 5554 5560 5555 5561 assertion-error@2.0.1: {} 5556 5562 5557 - ast-v8-to-istanbul@0.3.5: 5563 + ast-v8-to-istanbul@0.3.7: 5558 5564 dependencies: 5559 5565 '@jridgewell/trace-mapping': 0.3.31 5560 5566 estree-walker: 3.0.3 ··· 7134 7140 7135 7141 stackback@0.0.2: {} 7136 7142 7137 - std-env@3.9.0: {} 7143 + std-env@3.10.0: {} 7138 7144 7139 7145 stop-iteration-iterator@1.1.0: 7140 7146 dependencies: ··· 7387 7393 7388 7394 undici-types@7.14.0: {} 7389 7395 7390 - unplugin-stylex@0.5.5(@stylexjs/stylex@0.16.1)(rollup@4.52.4)(vitest@3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)): 7396 + unplugin-stylex@0.5.5(@stylexjs/stylex@0.16.2)(rollup@4.52.4)(vitest@3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)): 7391 7397 dependencies: 7392 7398 '@babel/core': 7.28.4 7393 7399 '@babel/plugin-syntax-flow': 7.27.1(@babel/core@7.28.4) 7394 7400 '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) 7395 7401 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) 7396 7402 '@rollup/pluginutils': 5.3.0(rollup@4.52.4) 7397 - '@stylexjs/babel-plugin': 0.16.1 7398 - '@stylexjs/stylex': 0.16.1 7403 + '@stylexjs/babel-plugin': 0.16.2 7404 + '@stylexjs/stylex': 0.16.2 7399 7405 '@types/node': 24.7.2 7400 7406 '@vitest/coverage-v8': 3.2.4(vitest@3.2.4(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)) 7401 7407 unplugin: 1.16.1 ··· 7480 7486 magic-string: 0.30.19 7481 7487 pathe: 2.0.3 7482 7488 picomatch: 4.0.3 7483 - std-env: 3.9.0 7489 + std-env: 3.10.0 7484 7490 tinybench: 2.9.0 7485 7491 tinyexec: 0.3.2 7486 7492 tinyglobby: 0.2.15