1 | import React, { memo, forwardRef, type ReactNode, type CSSProperties } from "react";
|
2 | import { symToStr } from "tsafe/symToStr";
|
3 | import { assert } from "tsafe/assert";
|
4 | import type { Equals } from "tsafe";
|
5 | import type { FrClassName } from "./fr/generatedFromCss/classNames";
|
6 | import { cx } from "./tools/cx";
|
7 | import { fr } from "./fr";
|
8 | import { useAnalyticsId } from "./tools/useAnalyticsId";
|
9 |
|
10 | export type QuoteProps = {
|
11 | id?: string;
|
12 | className?: string;
|
13 | text: ReactNode;
|
14 | author?: ReactNode;
|
15 | source?: ReactNode;
|
16 | sourceUrl?: string;
|
17 | imageUrl?: string;
|
18 | size?: "medium" | "large" | "xlarge";
|
19 | accentColor?: QuoteProps.AccentColor;
|
20 | classes?: Partial<Record<"root" | "author" | "source" | "image" | "imageTag" | "text", string>>;
|
21 | style?: CSSProperties;
|
22 | };
|
23 |
|
24 | export namespace QuoteProps {
|
25 | type ExtractAccentColor<FrClassName> = FrClassName extends `fr-quote--${infer AccentColor}`
|
26 | ? AccentColor
|
27 | : never;
|
28 |
|
29 | export type AccentColor = ExtractAccentColor<FrClassName>;
|
30 | }
|
31 |
|
32 |
|
33 | export const Quote = memo(
|
34 | forwardRef<HTMLDivElement, QuoteProps>((props, ref) => {
|
35 | const {
|
36 | id: id_props,
|
37 | className,
|
38 | text,
|
39 | author,
|
40 | source,
|
41 | sourceUrl,
|
42 | imageUrl,
|
43 | size = "xlarge",
|
44 | accentColor,
|
45 | classes = {},
|
46 | style,
|
47 | ...rest
|
48 | } = props;
|
49 |
|
50 | assert<Equals<keyof typeof rest, never>>();
|
51 |
|
52 | const id = useAnalyticsId({
|
53 | "defaultIdPrefix": "fr-quote",
|
54 | "explicitlyProvidedId": id_props
|
55 | });
|
56 |
|
57 | return (
|
58 | <figure
|
59 | id={id}
|
60 | className={cx(
|
61 | fr.cx("fr-quote"),
|
62 | imageUrl && fr.cx("fr-quote--column"),
|
63 | accentColor && `fr-quote--${accentColor}`,
|
64 | classes.root,
|
65 | className
|
66 | )}
|
67 | style={style}
|
68 | ref={ref}
|
69 | >
|
70 | <blockquote cite={sourceUrl}>
|
71 | <p
|
72 | className={cx(
|
73 | size === "large" && fr.cx("fr-text--lg"),
|
74 | size === "medium" && fr.cx("fr-text--md"),
|
75 | classes.text
|
76 | )}
|
77 | >
|
78 | « {text} »
|
79 | </p>
|
80 | </blockquote>
|
81 | <figcaption>
|
82 | {author !== undefined && (
|
83 | <p className={cx(fr.cx("fr-quote__author"), classes.author)}>{author}</p>
|
84 | )}
|
85 | {source !== undefined && (
|
86 | <ul className={cx(fr.cx("fr-quote__source"), classes.source)}>{source}</ul>
|
87 | )}
|
88 | {imageUrl !== undefined && (
|
89 | <div className={cx("fr-quote__image", classes.image)}>
|
90 | <img
|
91 | src={imageUrl}
|
92 | className={cx(fr.cx("fr-responsive-img"), classes.imageTag)}
|
93 | alt=""
|
94 | />
|
95 | </div>
|
96 | )}
|
97 | </figcaption>
|
98 | </figure>
|
99 | );
|
100 | })
|
101 | );
|
102 |
|
103 | Quote.displayName = symToStr({ Quote });
|
104 |
|
105 | export default Quote;
|
106 |
|
\ | No newline at end of file |