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.

add button group variant and colorswatch

+466 -207
+2 -1
README.md
··· 43 43 44 44 - [ ] Color Area 45 45 - [ ] Color Picker 46 - - [ ] Color Swatch 47 46 - [ ] Color Swatch Picker 48 47 - [ ] Color Wheel 49 48 ··· 74 73 - [ ] Toolbar 75 74 - [ ] Toast 76 75 76 + - [x] Color Swatch 77 + - [x] Aspect Ratio 77 78 - [x] Switch 78 79 - [x] Grid 79 80 - [x] Badge
+11 -2
apps/docs/src/components/button-group/index.tsx
··· 1 1 "use client"; 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 + import { useMemo } from "react"; 4 5 import { Group, GroupProps } from "react-aria-components"; 5 6 6 7 import { ButtonGroupContext } from "../button/context"; 8 + import { ButtonGroupVariant } from "../theme/types"; 7 9 8 10 const styles = stylex.create({ 9 11 group: { ··· 22 24 extends Omit<GroupProps, "className" | "style"> { 23 25 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 24 26 orientation?: "horizontal" | "vertical"; 27 + variant?: ButtonGroupVariant; 25 28 } 26 29 27 30 export const ButtonGroup = ({ 28 31 children, 29 32 style, 30 33 orientation = "horizontal", 34 + variant = "grouped", 31 35 ...props 32 36 }: ButtonGroupProps) => { 37 + const contextValue = useMemo( 38 + () => ({ orientation, variant }), 39 + [orientation, variant], 40 + ); 41 + 33 42 return ( 34 - <ButtonGroupContext value={orientation}> 43 + <ButtonGroupContext value={contextValue}> 35 44 <Group 36 45 {...stylex.props( 37 46 styles.group, 38 47 orientation === "horizontal" && styles.horizontal, 39 48 orientation === "vertical" && styles.vertical, 40 - style 49 + style, 41 50 )} 42 51 {...props} 43 52 >
+5 -1
apps/docs/src/components/button/context.ts
··· 1 1 import { createContext } from "react"; 2 2 3 3 export const ButtonGroupContext = createContext< 4 - undefined | "vertical" | "horizontal" 4 + | { 5 + orientation: "vertical" | "horizontal"; 6 + variant: "grouped" | "separate"; 7 + } 8 + | undefined 5 9 >(undefined);
+61
apps/docs/src/components/color-swatch/index.tsx
··· 1 + import type { ColorSwatchProps as AriaColorSwatchProps } from "react-aria-components"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { use } from "react"; 5 + import { ColorSwatch as AriaColorSwatch } from "react-aria-components"; 6 + 7 + import { SizeContext } from "../context"; 8 + import { slate } from "../theme/colors.stylex"; 9 + import { radius } from "../theme/radius.stylex"; 10 + import { spacing } from "../theme/spacing.stylex"; 11 + import { Size } from "../theme/types"; 12 + 13 + const styles = stylex.create({ 14 + swatch: { 15 + borderColor: slate.border2, 16 + borderStyle: "solid", 17 + borderWidth: 1, 18 + boxSizing: "border-box", 19 + }, 20 + sm: { 21 + borderRadius: radius.sm, 22 + height: spacing["4"], 23 + width: spacing["4"], 24 + }, 25 + md: { 26 + borderRadius: radius.md, 27 + height: spacing["6"], 28 + width: spacing["6"], 29 + }, 30 + lg: { 31 + borderRadius: radius.lg, 32 + height: spacing["8"], 33 + width: spacing["8"], 34 + }, 35 + }); 36 + 37 + export interface ColorSwatchProps 38 + extends Omit<AriaColorSwatchProps, "children" | "style" | "className"> { 39 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 40 + children?: React.ReactNode; 41 + size?: Size; 42 + } 43 + 44 + export function ColorSwatch({ 45 + style, 46 + size: sizeProp, 47 + ...props 48 + }: ColorSwatchProps) { 49 + const size = sizeProp || use(SizeContext); 50 + 51 + return ( 52 + <AriaColorSwatch 53 + {...props} 54 + {...stylex.props(styles.swatch, styles[size], style)} 55 + style={({ color }) => ({ 56 + background: `linear-gradient(${color.toString()}, ${color.toString()}), 57 + repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px`, 58 + })} 59 + /> 60 + ); 61 + }
+1
apps/docs/src/components/theme/types.ts
··· 12 12 | "critical" 13 13 | "critical-outline"; 14 14 export type InputVariant = "primary" | "secondary" | "tertiary"; 15 + export type ButtonGroupVariant = "grouped" | "separate";
+27 -8
apps/docs/src/components/theme/useButtonStyles.ts
··· 112 112 borderTopLeftRadius: { ":not(:first-child)": 0 }, 113 113 borderTopRightRadius: { ":not(:last-child)": 0 }, 114 114 }, 115 - secondaryGroupHorizontal: { 115 + secondaryGroupedHorizontal: { 116 116 borderRightColor: { ":not(:last-child)": slate.border2 }, 117 117 }, 118 118 groupVertical: { ··· 122 122 borderTopRightRadius: { ":not(:first-child)": 0 }, 123 123 borderTopWidth: { ":not(:first-child)": 0 }, 124 124 }, 125 - secondaryGroupVertical: { 125 + secondaryGroupedVertical: { 126 126 borderBottomColor: { ":not(:last-child)": slate.border2 }, 127 127 }, 128 + separate: { 129 + flexBasis: 130 + "calc((1 / var(--items-per-row)) * (100% - (var(--toggle-button-group-gap) * (var(--items-per-row) - 1))))", 131 + flexGrow: 1, 132 + flexShrink: 1, 133 + }, 128 134 }); 129 135 130 136 export const useButtonStyles = ({ ··· 139 145 140 146 return [ 141 147 styles.base, 142 - group === "horizontal" && styles.groupHorizontal, 143 - group === "vertical" && styles.groupVertical, 148 + group?.orientation === "horizontal" && 149 + group.variant === "grouped" && 150 + styles.groupHorizontal, 151 + group?.orientation === "vertical" && 152 + group.variant === "grouped" && 153 + styles.groupVertical, 144 154 variant === "primary" && [ 145 155 primary.bgAction, 146 156 primary.borderInteractive, ··· 151 161 gray.bgUi, 152 162 styles.secondary, 153 163 gray.text, 154 - group === "horizontal" && styles.secondaryGroupHorizontal, 155 - group === "vertical" && styles.secondaryGroupVertical, 164 + group?.orientation === "horizontal" && 165 + group.variant === "grouped" && 166 + styles.secondaryGroupedHorizontal, 167 + group?.orientation === "vertical" && 168 + group.variant === "grouped" && 169 + styles.secondaryGroupedVertical, 156 170 ], 157 171 variant === "tertiary" && [ 158 172 gray.bgGhost, 159 173 styles.tertiary, 160 174 gray.text, 161 - group === "horizontal" && styles.secondaryGroupHorizontal, 162 - group === "vertical" && styles.secondaryGroupVertical, 175 + group?.orientation === "horizontal" && 176 + group.variant === "grouped" && 177 + styles.secondaryGroupedHorizontal, 178 + group?.orientation === "vertical" && 179 + group.variant === "grouped" && 180 + styles.secondaryGroupedVertical, 163 181 ], 164 182 variant === "outline" && [ 165 183 gray.borderInteractive, ··· 182 200 size === "sm" && styles.small, 183 201 size === "md" && styles.medium, 184 202 size === "lg" && styles.large, 203 + group?.variant === "separate" && styles.separate, 185 204 ]; 186 205 };
+1
apps/docs/src/components/theme/useInputStyles.ts
··· 117 117 styles.inputWrapper, 118 118 variant === "primary" && [gray.bgUi, styles.primary], 119 119 variant === "secondary" && [gray.bgUi], 120 + variant === "tertiary" && [gray.bgGhost], 120 121 gray.text, 121 122 styles[size], 122 123 ],
+55 -24
apps/docs/src/components/toggle-button-group/index.tsx
··· 1 1 "use client"; 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 - import { use } from "react"; 4 + import { use, useMemo } from "react"; 5 5 import { 6 6 ToggleButtonGroupProps as AriaToggleButtonGroupProps, 7 7 ToggleButtonGroup as AriaToggleButtonGroup, 8 8 } from "react-aria-components"; 9 9 10 10 import { ButtonGroupContext } from "../button/context"; 11 + import { spacing } from "../theme/spacing.stylex"; 11 12 12 13 const styles = stylex.create({ 13 14 group: { ··· 23 24 contents: { 24 25 display: "contents", 25 26 }, 26 - horizontalContents: { 27 + horizontalGroupedContents: { 27 28 borderBottomLeftRadius: { ":is(:not(:first-child)) *": "0" }, 28 29 borderBottomRightRadius: { ":is(:not(:last-child)) *": "0" }, 29 30 borderLeftWidth: { ":is(:not(:first-child)) *": "0" }, ··· 31 32 borderTopLeftRadius: { ":is(:not(:first-child)) *": "0" }, 32 33 borderTopRightRadius: { ":is(:not(:last-child)) *": "0" }, 33 34 }, 34 - verticalContents: { 35 + verticalGroupedContents: { 35 36 // eslint-disable-next-line @stylexjs/valid-styles 36 37 borderBottomLeftWidth: { ":is(:not(:first-child)) *": "0" }, 37 38 borderBottomRightRadius: { ":is(:not(:last-child)) *": "0" }, ··· 40 41 borderTopRightRadius: { ":is(:not(:first-child)) *": "0" }, 41 42 borderTopWidth: { ":is(:not(:first-child)) *": "0" }, 42 43 }, 44 + separate: (itemsPerRow?: number) => ({ 45 + "--items-per-row": itemsPerRow, 46 + "--toggle-button-group-gap": spacing["2"], 47 + flexWrap: "wrap", 48 + gap: "var(--toggle-button-group-gap)", 49 + }), 43 50 }); 44 51 45 - interface ToggleButtonGroupProps 52 + interface ToggleButtonGroupBaseProps 46 53 extends Omit<AriaToggleButtonGroupProps, "className" | "style" | "children"> { 47 54 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 48 55 orientation?: "horizontal" | "vertical"; 49 56 children?: React.ReactNode; 50 57 } 51 58 59 + interface ToggleButtonGroupSeparateProps extends ToggleButtonGroupBaseProps { 60 + itemsPerRow: number; 61 + variant: "separate"; 62 + } 63 + 64 + interface ToggleButtonGroupGroupedProps extends ToggleButtonGroupBaseProps { 65 + variant: "grouped"; 66 + itemsPerRow?: never; 67 + } 68 + 69 + type ToggleButtonGroupProps = 70 + | ToggleButtonGroupSeparateProps 71 + | ToggleButtonGroupGroupedProps; 72 + 52 73 export const ToggleButtonGroup = ({ 53 74 children, 54 75 style, 55 76 orientation: orientationProp = "horizontal", 77 + variant = "grouped", 78 + itemsPerRow, 56 79 ...props 57 80 }: ToggleButtonGroupProps) => { 58 81 const groupOrientation = use(ButtonGroupContext); 59 - const isInGroup = groupOrientation !== undefined; 60 - const orientation = groupOrientation || orientationProp; 82 + const isInGroup = groupOrientation?.orientation !== undefined; 83 + const orientation = groupOrientation?.orientation || orientationProp; 84 + 85 + let stylesToApply = []; 86 + 87 + if (isInGroup && variant === "grouped") { 88 + stylesToApply = [ 89 + styles.contents, 90 + orientation === "horizontal" && styles.horizontalGroupedContents, 91 + orientation === "vertical" && styles.verticalGroupedContents, 92 + ]; 93 + } else if (isInGroup && variant === "separate") { 94 + stylesToApply = [styles.contents]; 95 + } else { 96 + stylesToApply = [ 97 + styles.group, 98 + orientation === "horizontal" && styles.horizontal, 99 + orientation === "vertical" && styles.vertical, 100 + variant === "separate" && styles.separate(itemsPerRow), 101 + ]; 102 + } 103 + 104 + const contextValue = useMemo( 105 + () => ({ orientation, variant }), 106 + [orientation, variant], 107 + ); 61 108 62 109 return ( 63 - <ButtonGroupContext value={orientation}> 64 - <AriaToggleButtonGroup 65 - {...stylex.props( 66 - isInGroup 67 - ? [ 68 - styles.contents, 69 - orientation === "horizontal" && styles.horizontalContents, 70 - orientation === "vertical" && styles.verticalContents, 71 - ] 72 - : [ 73 - styles.group, 74 - orientation === "horizontal" && styles.horizontal, 75 - orientation === "vertical" && styles.vertical, 76 - ], 77 - style, 78 - )} 79 - {...props} 80 - > 110 + <ButtonGroupContext value={contextValue}> 111 + <AriaToggleButtonGroup {...stylex.props(stylesToApply, style)} {...props}> 81 112 {children} 82 113 </AriaToggleButtonGroup> 83 114 </ButtonGroupContext>
+135
apps/docs/src/routes/_docs.ecommerce-app.tsx
··· 14 14 import { Text } from "@/components/typography/text"; 15 15 import { Separator } from "@/components/separator"; 16 16 import { Select, SelectItem } from "@/components/select"; 17 + import { ToggleButtonGroup } from "@/components/toggle-button-group"; 18 + import { ToggleButton } from "@/components/toggle-button"; 19 + import { ColorSwatch } from "@/components/color-swatch"; 17 20 18 21 const styles = stylex.create({ 19 22 grow: { ··· 104 107 ); 105 108 } 106 109 110 + function ProductOptionsCard() { 111 + return ( 112 + <Card size="sm"> 113 + <CardBody> 114 + <Flex direction="column" gap="5"> 115 + <Flex direction="column" gap="2"> 116 + <Text weight="semibold">Delivery</Text> 117 + <ToggleButtonGroup 118 + variant="separate" 119 + itemsPerRow={2} 120 + selectionMode="single" 121 + > 122 + <ToggleButton id="tomorrow" variant="secondary"> 123 + Tomorrow 124 + </ToggleButton> 125 + <ToggleButton id="within-3-days" variant="secondary"> 126 + Within 3 days 127 + </ToggleButton> 128 + </ToggleButtonGroup> 129 + </Flex> 130 + <Flex direction="column" gap="2"> 131 + <Text weight="semibold">Size</Text> 132 + <ToggleButtonGroup 133 + variant="separate" 134 + itemsPerRow={5} 135 + selectionMode="single" 136 + > 137 + <ToggleButton id="5.5" variant="secondary"> 138 + 5.5 139 + </ToggleButton> 140 + <ToggleButton id="6" variant="secondary"> 141 + 6 142 + </ToggleButton> 143 + <ToggleButton id="6.5" variant="secondary"> 144 + 6.5 145 + </ToggleButton> 146 + <ToggleButton id="7" variant="secondary"> 147 + 7 148 + </ToggleButton> 149 + <ToggleButton id="7.5" variant="secondary"> 150 + 7.5 151 + </ToggleButton> 152 + <ToggleButton id="8" variant="secondary"> 153 + 8 154 + </ToggleButton> 155 + <ToggleButton id="8.5" variant="secondary"> 156 + 8.5 157 + </ToggleButton> 158 + <ToggleButton id="9" variant="secondary"> 159 + 9 160 + </ToggleButton> 161 + <ToggleButton id="9.5" variant="secondary"> 162 + 9.5 163 + </ToggleButton> 164 + <ToggleButton id="10" variant="secondary"> 165 + 10 166 + </ToggleButton> 167 + </ToggleButtonGroup> 168 + </Flex> 169 + <Flex direction="column" gap="2"> 170 + <Text weight="semibold">Material</Text> 171 + <ToggleButtonGroup 172 + variant="separate" 173 + itemsPerRow={5} 174 + selectionMode="single" 175 + > 176 + <ToggleButton id="leather" variant="secondary"> 177 + Leather 178 + </ToggleButton> 179 + <ToggleButton id="suede" variant="secondary"> 180 + Suede 181 + </ToggleButton> 182 + <ToggleButton id="mesh" variant="secondary"> 183 + Mesh 184 + </ToggleButton> 185 + <ToggleButton id="canvas" variant="secondary"> 186 + Canvas 187 + </ToggleButton> 188 + </ToggleButtonGroup> 189 + </Flex> 190 + <Flex direction="column" gap="2"> 191 + <Text weight="semibold">Color</Text> 192 + <ToggleButtonGroup 193 + variant="separate" 194 + itemsPerRow={3} 195 + selectionMode="single" 196 + > 197 + <ToggleButton id="white" variant="secondary"> 198 + <ColorSwatch color="#fff" size="sm" /> 199 + White 200 + </ToggleButton> 201 + <ToggleButton id="grey" variant="secondary"> 202 + <ColorSwatch color="#808080" size="sm" /> 203 + Grey 204 + </ToggleButton> 205 + <ToggleButton id="black" variant="secondary"> 206 + <ColorSwatch color="#000" size="sm" /> 207 + Black 208 + </ToggleButton> 209 + <ToggleButton id="red" variant="secondary"> 210 + <ColorSwatch color="#f00" size="sm" /> 211 + Red 212 + </ToggleButton> 213 + <ToggleButton id="pink" variant="secondary"> 214 + <ColorSwatch color="#f0f" size="sm" /> 215 + Pink 216 + </ToggleButton> 217 + <ToggleButton id="violet" variant="secondary"> 218 + <ColorSwatch color="#800080" size="sm" /> 219 + Violet 220 + </ToggleButton> 221 + <ToggleButton id="blue" variant="secondary"> 222 + <ColorSwatch color="#00f" size="sm" /> 223 + Blue 224 + </ToggleButton> 225 + <ToggleButton id="green" variant="secondary"> 226 + <ColorSwatch color="#0f0" size="sm" /> 227 + Green 228 + </ToggleButton> 229 + <ToggleButton id="beige" variant="secondary"> 230 + <ColorSwatch color="#f5f5dc" size="sm" /> 231 + Beige 232 + </ToggleButton> 233 + </ToggleButtonGroup> 234 + </Flex> 235 + </Flex> 236 + </CardBody> 237 + </Card> 238 + ); 239 + } 240 + 107 241 function RouteComponent() { 108 242 return ( 109 243 <Flex gap="4"> 110 244 <Flex direction="column" gap="4" style={styles.skinny}> 111 245 <SmallProductCard /> 112 246 <SmallProductCardWithBuying /> 247 + <ProductOptionsCard /> 113 248 </Flex> 114 249 <Flex direction="column" gap="4" style={styles.skinny}></Flex> 115 250 <Flex direction="column" gap="4" style={styles.skinny}></Flex>
+2
packages/hip-ui/src/cli/install.tsx
··· 17 17 import { cardConfig } from "../components/card/card-config.js"; 18 18 import { checkboxConfig } from "../components/checkbox/checkbox-config.js"; 19 19 import { colorFieldConfig } from "../components/color-field/color-field-config.js"; 20 + import { colorSwatchConfig } from "../components/color-swatch/link-config.js"; 20 21 import { comboboxConfig } from "../components/combobox/combobox-config.js"; 21 22 import { commandMenuConfig } from "../components/command-menu/command-menu-config.js"; 22 23 import { contextMenuConfig } from "../components/context-menu/context-menu-config.js"; ··· 86 87 gridConfig, 87 88 switchConfig, 88 89 aspectRatioConfig, 90 + colorSwatchConfig, 89 91 ]; 90 92 91 93 function StringSetting({
+11 -2
packages/hip-ui/src/components/button-group/index.tsx
··· 1 1 "use client"; 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 + import { useMemo } from "react"; 4 5 import { Group, GroupProps } from "react-aria-components"; 5 6 6 7 import { ButtonGroupContext } from "../button/context"; 8 + import { ButtonGroupVariant } from "../theme/types"; 7 9 8 10 const styles = stylex.create({ 9 11 group: { ··· 22 24 extends Omit<GroupProps, "className" | "style"> { 23 25 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 24 26 orientation?: "horizontal" | "vertical"; 27 + variant?: ButtonGroupVariant; 25 28 } 26 29 27 30 export const ButtonGroup = ({ 28 31 children, 29 32 style, 30 33 orientation = "horizontal", 34 + variant = "grouped", 31 35 ...props 32 36 }: ButtonGroupProps) => { 37 + const contextValue = useMemo( 38 + () => ({ orientation, variant }), 39 + [orientation, variant], 40 + ); 41 + 33 42 return ( 34 - <ButtonGroupContext value={orientation}> 43 + <ButtonGroupContext value={contextValue}> 35 44 <Group 36 45 {...stylex.props( 37 46 styles.group, 38 47 orientation === "horizontal" && styles.horizontal, 39 48 orientation === "vertical" && styles.vertical, 40 - style 49 + style, 41 50 )} 42 51 {...props} 43 52 >
+5 -1
packages/hip-ui/src/components/button/context.ts
··· 1 1 import { createContext } from "react"; 2 2 3 3 export const ButtonGroupContext = createContext< 4 - undefined | "vertical" | "horizontal" 4 + | { 5 + orientation: "vertical" | "horizontal"; 6 + variant: "grouped" | "separate"; 7 + } 8 + | undefined 5 9 >(undefined);
+61
packages/hip-ui/src/components/color-swatch/index.tsx
··· 1 + import type { ColorSwatchProps as AriaColorSwatchProps } from "react-aria-components"; 2 + 3 + import * as stylex from "@stylexjs/stylex"; 4 + import { use } from "react"; 5 + import { ColorSwatch as AriaColorSwatch } from "react-aria-components"; 6 + 7 + import { SizeContext } from "../context"; 8 + import { slate } from "../theme/colors.stylex"; 9 + import { radius } from "../theme/radius.stylex"; 10 + import { spacing } from "../theme/spacing.stylex"; 11 + import { Size } from "../theme/types"; 12 + 13 + const styles = stylex.create({ 14 + swatch: { 15 + borderColor: slate.border2, 16 + borderStyle: "solid", 17 + borderWidth: 1, 18 + boxSizing: "border-box", 19 + }, 20 + sm: { 21 + borderRadius: radius.sm, 22 + height: spacing["4"], 23 + width: spacing["4"], 24 + }, 25 + md: { 26 + borderRadius: radius.md, 27 + height: spacing["6"], 28 + width: spacing["6"], 29 + }, 30 + lg: { 31 + borderRadius: radius.lg, 32 + height: spacing["8"], 33 + width: spacing["8"], 34 + }, 35 + }); 36 + 37 + export interface ColorSwatchProps 38 + extends Omit<AriaColorSwatchProps, "children" | "style" | "className"> { 39 + style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 40 + children?: React.ReactNode; 41 + size?: Size; 42 + } 43 + 44 + export function ColorSwatch({ 45 + style, 46 + size: sizeProp, 47 + ...props 48 + }: ColorSwatchProps) { 49 + const size = sizeProp || use(SizeContext); 50 + 51 + return ( 52 + <AriaColorSwatch 53 + {...props} 54 + {...stylex.props(styles.swatch, styles[size], style)} 55 + style={({ color }) => ({ 56 + background: `linear-gradient(${color.toString()}, ${color.toString()}), 57 + repeating-conic-gradient(#CCC 0% 25%, white 0% 50%) 50% / 16px 16px`, 58 + })} 59 + /> 60 + ); 61 + }
+1
packages/hip-ui/src/components/theme/types.ts
··· 12 12 | "critical" 13 13 | "critical-outline"; 14 14 export type InputVariant = "primary" | "secondary" | "tertiary"; 15 + export type ButtonGroupVariant = "grouped" | "separate";
+27 -8
packages/hip-ui/src/components/theme/useButtonStyles.ts
··· 112 112 borderTopLeftRadius: { ":not(:first-child)": 0 }, 113 113 borderTopRightRadius: { ":not(:last-child)": 0 }, 114 114 }, 115 - secondaryGroupHorizontal: { 115 + secondaryGroupedHorizontal: { 116 116 borderRightColor: { ":not(:last-child)": slate.border2 }, 117 117 }, 118 118 groupVertical: { ··· 122 122 borderTopRightRadius: { ":not(:first-child)": 0 }, 123 123 borderTopWidth: { ":not(:first-child)": 0 }, 124 124 }, 125 - secondaryGroupVertical: { 125 + secondaryGroupedVertical: { 126 126 borderBottomColor: { ":not(:last-child)": slate.border2 }, 127 127 }, 128 + separate: { 129 + flexBasis: 130 + "calc((1 / var(--items-per-row)) * (100% - (var(--toggle-button-group-gap) * (var(--items-per-row) - 1))))", 131 + flexGrow: 1, 132 + flexShrink: 1, 133 + }, 128 134 }); 129 135 130 136 export const useButtonStyles = ({ ··· 139 145 140 146 return [ 141 147 styles.base, 142 - group === "horizontal" && styles.groupHorizontal, 143 - group === "vertical" && styles.groupVertical, 148 + group?.orientation === "horizontal" && 149 + group.variant === "grouped" && 150 + styles.groupHorizontal, 151 + group?.orientation === "vertical" && 152 + group.variant === "grouped" && 153 + styles.groupVertical, 144 154 variant === "primary" && [ 145 155 primary.bgAction, 146 156 primary.borderInteractive, ··· 151 161 gray.bgUi, 152 162 styles.secondary, 153 163 gray.text, 154 - group === "horizontal" && styles.secondaryGroupHorizontal, 155 - group === "vertical" && styles.secondaryGroupVertical, 164 + group?.orientation === "horizontal" && 165 + group.variant === "grouped" && 166 + styles.secondaryGroupedHorizontal, 167 + group?.orientation === "vertical" && 168 + group.variant === "grouped" && 169 + styles.secondaryGroupedVertical, 156 170 ], 157 171 variant === "tertiary" && [ 158 172 gray.bgGhost, 159 173 styles.tertiary, 160 174 gray.text, 161 - group === "horizontal" && styles.secondaryGroupHorizontal, 162 - group === "vertical" && styles.secondaryGroupVertical, 175 + group?.orientation === "horizontal" && 176 + group.variant === "grouped" && 177 + styles.secondaryGroupedHorizontal, 178 + group?.orientation === "vertical" && 179 + group.variant === "grouped" && 180 + styles.secondaryGroupedVertical, 163 181 ], 164 182 variant === "outline" && [ 165 183 gray.borderInteractive, ··· 182 200 size === "sm" && styles.small, 183 201 size === "md" && styles.medium, 184 202 size === "lg" && styles.large, 203 + group?.variant === "separate" && styles.separate, 185 204 ]; 186 205 };
-136
packages/hip-ui/src/components/toggle-button copy/index.tsx
··· 1 - import * as stylex from "@stylexjs/stylex"; 2 - import { Children, use } from "react"; 3 - import { 4 - ToggleButtonProps as AriaToggleButtonProps, 5 - ToggleButton as AriaToggleButton, 6 - } from "react-aria-components"; 7 - 8 - import { SizeContext } from "../context"; 9 - import { plum, slate } from "../theme/colors.stylex"; 10 - import { spacing } from "../theme/spacing.stylex"; 11 - import { useButtonStyles } from "../theme/useButtonStyles"; 12 - 13 - const styles = stylex.create({ 14 - primarySelected: { 15 - backgroundColor: { 16 - default: plum.solid1, 17 - ":hover": plum.solid2, 18 - ":active": plum.text1, 19 - }, 20 - color: "light-dark(white, black)", 21 - }, 22 - secondarySelected: { 23 - backgroundColor: { 24 - default: slate.border1, 25 - ":hover": slate.border2, 26 - ":active": slate.border3, 27 - }, 28 - borderColor: { 29 - default: slate.border1, 30 - ":hover": slate.border2, 31 - ":active": slate.border3, 32 - }, 33 - }, 34 - tertiarySelected: { 35 - backgroundColor: { 36 - default: slate.border1, 37 - ":hover": slate.border2, 38 - ":active": slate.border3, 39 - }, 40 - borderColor: { 41 - default: slate.border1, 42 - ":hover": slate.border2, 43 - ":active": slate.border3, 44 - }, 45 - }, 46 - outlineSelected: { 47 - backgroundColor: { 48 - default: slate.border1, 49 - ":hover": slate.border2, 50 - ":active": slate.border3, 51 - }, 52 - borderColor: { 53 - default: slate.border1, 54 - ":hover": slate.border2, 55 - ":active": slate.border3, 56 - }, 57 - }, 58 - sm: { 59 - paddingLeft: { 60 - ":has(> * + *, > *:not(svg):only-child)": spacing["2"], 61 - }, 62 - paddingRight: { 63 - ":has(> * + *, > *:not(svg):only-child)": spacing["2"], 64 - }, 65 - width: { 66 - ":has(svg:only-child)": spacing["7"], 67 - }, 68 - }, 69 - md: { 70 - paddingLeft: { 71 - ":has(> * + *, > *:not(svg):only-child)": spacing["3"], 72 - }, 73 - paddingRight: { 74 - ":has(> * + *, > *:not(svg):only-child)": spacing["3"], 75 - }, 76 - width: { 77 - ":has(svg:only-child)": spacing["8"], 78 - }, 79 - }, 80 - lg: { 81 - paddingLeft: { 82 - ":has(> * + *, > *:not(svg):only-child)": spacing["4"], 83 - }, 84 - paddingRight: { 85 - ":has(> * + *, > *:not(svg):only-child)": spacing["4"], 86 - }, 87 - width: { 88 - ":has(svg:only-child)": spacing["10"], 89 - }, 90 - }, 91 - }); 92 - 93 - export interface ToggleButtonProps 94 - extends Omit<AriaToggleButtonProps, "style" | "className" | "children"> { 95 - style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 96 - variant?: "primary" | "secondary" | "tertiary" | "outline"; 97 - size?: "sm" | "md" | "lg"; 98 - children?: React.ReactNode; 99 - } 100 - 101 - export function ToggleButton({ 102 - style, 103 - variant = "primary", 104 - size: sizeProp, 105 - children, 106 - ...props 107 - }: ToggleButtonProps) { 108 - const size = sizeProp || use(SizeContext); 109 - const buttonStyles = useButtonStyles({ variant, size }); 110 - const toggleButtonStyles = (isSelected?: boolean) => 111 - stylex.props( 112 - buttonStyles, 113 - styles[size], 114 - isSelected ? styles[`${variant}Selected`] : undefined, 115 - style, 116 - ); 117 - 118 - return ( 119 - <AriaToggleButton 120 - {...props} 121 - {...toggleButtonStyles()} 122 - className={({ isSelected }) => 123 - toggleButtonStyles(isSelected).className || "" 124 - } 125 - > 126 - {/* eslint-disable-next-line @eslint-react/no-children-map */} 127 - {Children.map(children, (child, index) => 128 - typeof child === "string" ? ( 129 - <span key={`${child}-${index.toString()}`}>{child}</span> 130 - ) : ( 131 - child 132 - ), 133 - )} 134 - </AriaToggleButton> 135 - ); 136 - }
+55 -24
packages/hip-ui/src/components/toggle-button-group/index.tsx
··· 1 1 "use client"; 2 2 3 3 import * as stylex from "@stylexjs/stylex"; 4 - import { use } from "react"; 4 + import { use, useMemo } from "react"; 5 5 import { 6 6 ToggleButtonGroupProps as AriaToggleButtonGroupProps, 7 7 ToggleButtonGroup as AriaToggleButtonGroup, 8 8 } from "react-aria-components"; 9 9 10 10 import { ButtonGroupContext } from "../button/context"; 11 + import { spacing } from "../theme/spacing.stylex"; 11 12 12 13 const styles = stylex.create({ 13 14 group: { ··· 23 24 contents: { 24 25 display: "contents", 25 26 }, 26 - horizontalContents: { 27 + horizontalGroupedContents: { 27 28 borderBottomLeftRadius: { ":is(:not(:first-child)) *": "0" }, 28 29 borderBottomRightRadius: { ":is(:not(:last-child)) *": "0" }, 29 30 borderLeftWidth: { ":is(:not(:first-child)) *": "0" }, ··· 31 32 borderTopLeftRadius: { ":is(:not(:first-child)) *": "0" }, 32 33 borderTopRightRadius: { ":is(:not(:last-child)) *": "0" }, 33 34 }, 34 - verticalContents: { 35 + verticalGroupedContents: { 35 36 // eslint-disable-next-line @stylexjs/valid-styles 36 37 borderBottomLeftWidth: { ":is(:not(:first-child)) *": "0" }, 37 38 borderBottomRightRadius: { ":is(:not(:last-child)) *": "0" }, ··· 40 41 borderTopRightRadius: { ":is(:not(:first-child)) *": "0" }, 41 42 borderTopWidth: { ":is(:not(:first-child)) *": "0" }, 42 43 }, 44 + separate: (itemsPerRow?: number) => ({ 45 + "--items-per-row": itemsPerRow, 46 + "--toggle-button-group-gap": spacing["2"], 47 + flexWrap: "wrap", 48 + gap: "var(--toggle-button-group-gap)", 49 + }), 43 50 }); 44 51 45 - interface ToggleButtonGroupProps 52 + interface ToggleButtonGroupBaseProps 46 53 extends Omit<AriaToggleButtonGroupProps, "className" | "style" | "children"> { 47 54 style?: stylex.StyleXStyles | stylex.StyleXStyles[]; 48 55 orientation?: "horizontal" | "vertical"; 49 56 children?: React.ReactNode; 50 57 } 51 58 59 + interface ToggleButtonGroupSeparateProps extends ToggleButtonGroupBaseProps { 60 + itemsPerRow: number; 61 + variant: "separate"; 62 + } 63 + 64 + interface ToggleButtonGroupGroupedProps extends ToggleButtonGroupBaseProps { 65 + variant: "grouped"; 66 + itemsPerRow?: never; 67 + } 68 + 69 + type ToggleButtonGroupProps = 70 + | ToggleButtonGroupSeparateProps 71 + | ToggleButtonGroupGroupedProps; 72 + 52 73 export const ToggleButtonGroup = ({ 53 74 children, 54 75 style, 55 76 orientation: orientationProp = "horizontal", 77 + variant = "grouped", 78 + itemsPerRow, 56 79 ...props 57 80 }: ToggleButtonGroupProps) => { 58 81 const groupOrientation = use(ButtonGroupContext); 59 - const isInGroup = groupOrientation !== undefined; 60 - const orientation = groupOrientation || orientationProp; 82 + const isInGroup = groupOrientation?.orientation !== undefined; 83 + const orientation = groupOrientation?.orientation || orientationProp; 84 + 85 + let stylesToApply = []; 86 + 87 + if (isInGroup && variant === "grouped") { 88 + stylesToApply = [ 89 + styles.contents, 90 + orientation === "horizontal" && styles.horizontalGroupedContents, 91 + orientation === "vertical" && styles.verticalGroupedContents, 92 + ]; 93 + } else if (isInGroup && variant === "separate") { 94 + stylesToApply = [styles.contents]; 95 + } else { 96 + stylesToApply = [ 97 + styles.group, 98 + orientation === "horizontal" && styles.horizontal, 99 + orientation === "vertical" && styles.vertical, 100 + variant === "separate" && styles.separate(itemsPerRow), 101 + ]; 102 + } 103 + 104 + const contextValue = useMemo( 105 + () => ({ orientation, variant }), 106 + [orientation, variant], 107 + ); 61 108 62 109 return ( 63 - <ButtonGroupContext value={orientation}> 64 - <AriaToggleButtonGroup 65 - {...stylex.props( 66 - isInGroup 67 - ? [ 68 - styles.contents, 69 - orientation === "horizontal" && styles.horizontalContents, 70 - orientation === "vertical" && styles.verticalContents, 71 - ] 72 - : [ 73 - styles.group, 74 - orientation === "horizontal" && styles.horizontal, 75 - orientation === "vertical" && styles.vertical, 76 - ], 77 - style, 78 - )} 79 - {...props} 80 - > 110 + <ButtonGroupContext value={contextValue}> 111 + <AriaToggleButtonGroup {...stylex.props(stylesToApply, style)} {...props}> 81 112 {children} 82 113 </AriaToggleButtonGroup> 83 114 </ButtonGroupContext>