UNPKG

4.19 kBTypeScriptView Raw
1import React, { memo, forwardRef, type CSSProperties } from "react";
2import { Button } from "./Button";
3import { ButtonProps } from "./Button";
4import { symToStr } from "tsafe/symToStr";
5import { assert } from "tsafe/assert";
6import type { Equals } from "tsafe";
7import { fr } from "./fr";
8import { cx } from "./tools/cx";
9import { useAnalyticsId } from "./tools/useAnalyticsId";
10
11export type ButtonsGroupProps = ButtonsGroupProps.AlwaysStacked | ButtonsGroupProps.Inline;
12
13export namespace ButtonsGroupProps {
14 export type Common = {
15 id?: string;
16 className?: string;
17 buttonsSize?: ButtonProps["size"];
18 /** Default: left */
19 buttonsIconPosition?: ButtonProps.WithIcon["iconPosition"];
20 /* Default: "left", in vertical layout this has no effect */
21 alignment?: "left" | "center" | "right";
22 /** Default: false */
23 buttonsEquisized?: boolean;
24 buttons: [ButtonProps, ...ButtonProps[]];
25 style?: CSSProperties;
26 };
27
28 export type AlwaysStacked = Common & {
29 /**
30 * Default "never", it means that the button are
31 * stacked vertically regardless of the screed width
32 **/
33 inlineLayoutWhen?: "never";
34 isReverseOrder?: never;
35 };
36
37 export type Inline = Omit<Common, "alignment"> & {
38 /**
39 * Default "never", "never" means that the button are
40 * stacked vertically regardless of the screed width
41 **/
42 inlineLayoutWhen?: "always" | `${"sm" | "md" | "lg"} and up`;
43 /** Default: false */
44 isReverseOrder?: boolean;
45 /* Default: "left" */
46 alignment?: Common["alignment"] | "between";
47 };
48}
49
50/** @see <https://components.react-dsfr.codegouv.studio/?path=/docs/components-buttonsgroup> */
51export const ButtonsGroup = memo(
52 forwardRef<HTMLUListElement, ButtonsGroupProps>((props, ref) => {
53 const {
54 id: props_id,
55 className,
56 buttonsSize = "medium",
57 buttonsIconPosition = "left",
58 inlineLayoutWhen = "never",
59 alignment = "left",
60 buttonsEquisized = false,
61 isReverseOrder = false,
62 buttons,
63 style,
64 ...rest
65 } = props;
66
67 assert<Equals<keyof typeof rest, never>>();
68
69 const id = useAnalyticsId({
70 "defaultIdPrefix": "fr-btns-group",
71 "explicitlyProvidedId": props_id
72 });
73
74 const buttonsGroupClassName = cx(
75 fr.cx(
76 "fr-btns-group",
77 buttonsSize !== "medium" &&
78 `fr-btns-group--${(() => {
79 switch (buttonsSize) {
80 case "small":
81 return "sm";
82 case "large":
83 return "lg";
84 }
85 })()}`,
86 inlineLayoutWhen !== "never" &&
87 `fr-btns-group--inline${(() => {
88 switch (inlineLayoutWhen) {
89 case "always":
90 return "";
91 case "sm and up":
92 return "-sm";
93 case "md and up":
94 return "-md";
95 case "lg and up":
96 return "-lg";
97 }
98 })()}`,
99 buttonsEquisized && `fr-btns-group--equisized`,
100 `fr-btns-group--${alignment}`,
101 isReverseOrder && "fr-btns-group--inline-reverse",
102 `fr-btns-group--icon-${buttonsIconPosition}`
103 ),
104 className
105 );
106
107 return (
108 <ul id={id} className={buttonsGroupClassName} style={style} ref={ref} {...rest}>
109 {buttons.map((buttonProps, i) => (
110 <li key={i}>
111 <Button {...buttonProps} />
112 </li>
113 ))}
114 </ul>
115 );
116 })
117);
118
119ButtonsGroup.displayName = symToStr({ ButtonsGroup });
120
121export default ButtonsGroup;
122
\No newline at end of file