|
| 1 | +import LinkTo from '@storybook/addon-links/react'; |
| 2 | +import { Meta } from '@storybook/addon-docs'; |
| 3 | +import { TextPositionVariations, TextAlignmentVariations, FlexSkeleton, IconMediaObject } from './code-snippets'; |
| 4 | +import { Example } from '../../templates'; |
| 5 | + |
| 6 | +<Meta title="Concepts/Recipes/Media Object" /> |
| 7 | + |
| 8 | +--- |
| 9 | + |
| 10 | +# Media Object recipe |
| 11 | + |
| 12 | +### **Overview** |
| 13 | + |
| 14 | +A Media Object, is a pattern commonly used to display media content and a description of that content. |
| 15 | +A typical example of this patter is displaying an Avatar of a person (the media content) next to their name (the description). Fluent UI's <LinkTo kind="components-persona--default">Persona</LinkTo> is an example of this pattern. |
| 16 | + |
| 17 | +This recipe focuses on other uses of this patterns and will show you how to create a Media Object to display images, icons or any other media content you want to add. |
| 18 | + |
| 19 | +### **Ingredients** |
| 20 | + |
| 21 | +- <LinkTo kind="concepts-developer-icons-icons--page">@fluentui/react-icons</LinkTo> |
| 22 | +- <LinkTo kind="components-image--default">@fluentui/react-image</LinkTo> |
| 23 | +- <LinkTo kind="components-text--default">@fluentui/react-text</LinkTo> |
| 24 | + |
| 25 | +> **\*\*Note:\*\*** This recipe is meant to provide a way to use custom media. If you want to use |
| 26 | +> `Avatar` or `PresenceBadge` as media, please refer to our <LinkTo kind="components-persona--default">Persona component</LinkTo>. |
| 27 | +
|
| 28 | +## **Steps** |
| 29 | + |
| 30 | +There are two ways to build a Media Object, you can use a grid layout or a flex layout. We will cover both in this |
| 31 | +recipe. There are upsides and downsides to both, but the main difference is that grid layout uses less DOM than the |
| 32 | +flex option. |
| 33 | + |
| 34 | +### **Flex Layout** |
| 35 | + |
| 36 | +The main idea behind the flex layout is to have a container and two children, as shown below. The parent div will |
| 37 | +contain the media and another div (text div) that will contain the text content. |
| 38 | + |
| 39 | +<TemplateExample centered> |
| 40 | + <FlexSkeleton /> |
| 41 | +</TemplateExample> |
| 42 | + |
| 43 | +To achieve this layout, we will follow these steps: |
| 44 | + |
| 45 | +#### **Step 1: Creating our parent and text container styles.** |
| 46 | + |
| 47 | +As seen above, we will first need to create our parent div and our text containers. These containers will need to have |
| 48 | +the `display: flex` css property and a `row` direction for the parent and a `column` direction for the text container, as shown in the `makeStyles` call below: |
| 49 | + |
| 50 | +```jsx |
| 51 | +const useStyles = makeStyles({ |
| 52 | + parent: { |
| 53 | + display: 'flex', |
| 54 | + flexDirection: 'row', |
| 55 | + }, |
| 56 | + textContainer: { |
| 57 | + display: 'flex', |
| 58 | + flexDirection: 'column', |
| 59 | + }, |
| 60 | +}); |
| 61 | +``` |
| 62 | + |
| 63 | +#### **Step 2: Putting everything together.** |
| 64 | + |
| 65 | +After we have our styles, we just have to put our styles to work! Since we are using flex box we don't need to worry about dynamically placing items. Do take into account that using this approach will be heavier on the DOM than the grid approach. |
| 66 | + |
| 67 | +```jsx |
| 68 | +import { Text, makeStyles } from '@fluentui/react-components'; |
| 69 | + |
| 70 | +// Our makeStyles call from above |
| 71 | + |
| 72 | +const MediaObject: React.FC<{ text?: string }> = ({ children, text }) => { |
| 73 | + const styles = useStyles(); |
| 74 | + |
| 75 | + return ( |
| 76 | + <div className={styles.parent}> |
| 77 | + {children} |
| 78 | + <div className={styles.textContainer}> |
| 79 | + <Text>{text}</Text> |
| 80 | + </div> |
| 81 | + </div> |
| 82 | + ); |
| 83 | +}; |
| 84 | +``` |
| 85 | + |
| 86 | +After these steps you should be able to build something like the example below. |
| 87 | + |
| 88 | +<TemplateExample centered> |
| 89 | + <IconMediaObject /> |
| 90 | +</TemplateExample> |
| 91 | + |
| 92 | +### **Grid Layout** |
| 93 | + |
| 94 | +When using a grid for Media Object, we only need a parent div and we can pass the items directly into it. |
| 95 | +As you can tell, the grid layout has one less layer of DOM compared to the flex layout. This is especially important |
| 96 | +when the component is going to be used repeatedly in a list or the media or/and text have many layers themselves. |
| 97 | + |
| 98 | +To get started, we will follow these steps: |
| 99 | + |
| 100 | +#### **Step 1: Creating our grid.** |
| 101 | + |
| 102 | +To get the initial layout, we need to add these css properties to our parent div: |
| 103 | + |
| 104 | +- `display: grid`: This will create our grid layout and allow us to use the grid properties. |
| 105 | +- `grid-auto-flow: column`: This will make the grid flow as a column and allow our media to be on the left and our text on the right. |
| 106 | + |
| 107 | +Resulting in a `makeStyles` call like this: |
| 108 | + |
| 109 | +```jsx |
| 110 | +const useStyles = makeStyles({ |
| 111 | + parent: { |
| 112 | + display: 'grid', |
| 113 | + gridAutoFlow: 'column', |
| 114 | + }, |
| 115 | +}); |
| 116 | +``` |
| 117 | + |
| 118 | +#### **Step 2: Adding our media styles.** |
| 119 | + |
| 120 | +Our next step is to add our media styles. Unlike the flex layout, we will need to add some css properties to our media because we are using `grid-auto-flow: column`. To make our media take all the rows on the left, we will need to make our media span the number of rows the text will take. If we have 4 lines of text, we will need to add `grid-row-start: span 4`. While this could be done dynamically using line names, this is the simplest way to achieve a media object with grid. This will give us the following `makeStyles` call: |
| 121 | + |
| 122 | +```jsx |
| 123 | +const useStyles = makeStyles({ |
| 124 | + // parent styles |
| 125 | + media: { |
| 126 | + gridRowStart: 'span 4', |
| 127 | + }, |
| 128 | +}); |
| 129 | +``` |
| 130 | + |
| 131 | +#### **Step 3: Putting everything together.** |
| 132 | + |
| 133 | +After these steps we should be able to add our media and text, and get the desired layout shown above. We should have a component that looks like this: |
| 134 | + |
| 135 | +```jsx |
| 136 | +import { Text, makeStyles } from '@fluentui/react-components'; |
| 137 | + |
| 138 | +// Our makeStyles call from above |
| 139 | + |
| 140 | +const MediaObject: React.FC<{ text?: string }> = ({ children, text }) => { |
| 141 | + const styles = useStyles(); |
| 142 | + |
| 143 | + return ( |
| 144 | + <div className={styles.parent}> |
| 145 | + <div className={styles.media}>{children}</div> |
| 146 | + <Text className={styles.text}>{text}</Text> |
| 147 | + </div> |
| 148 | + ); |
| 149 | +}; |
| 150 | +``` |
| 151 | + |
| 152 | +#### **Step 3 (_optional_): Dynamically place media and text.** |
| 153 | + |
| 154 | +If you want to dynamically place your media and text, you can use `grid-template-columns` to achieve this. For the basic layout, we need to add the css property `grid-template-columns: max-content [middle] auto`. This will create two columns and a line name called middle. The max-content column will be the media which will help only use the space needed for the media and the auto column will be the text so we can wrap it when there is not enough space in our container. This will give us the following `makeStyles` call: |
| 155 | + |
| 156 | +```jsx |
| 157 | +const useStyles = makeStyles({ |
| 158 | + parent: { |
| 159 | + display: 'grid', |
| 160 | + gridTemplateColumns: 'max-content [middle] auto', |
| 161 | + }, |
| 162 | +}); |
| 163 | +``` |
| 164 | + |
| 165 | +#### **Step 4 (_optional_): Let your media and text know where to start and end.** |
| 166 | + |
| 167 | +Now that we have our template columns, we need to let our text know where to start and our media where to end. The media must end at the middle line and the text must start at the middle line. This has the pro of not having to know how many rows of text we will have, but the downside is that we have to let the media and text know where to start or end. We should a `makeStyles` call like this: |
| 168 | + |
| 169 | +```jsx |
| 170 | +const useStyles = makeStyles({ |
| 171 | + // parent styles |
| 172 | + media: { |
| 173 | + gridColumnEnd: 'middle', |
| 174 | + }, |
| 175 | + text: { |
| 176 | + gridColumnStart: 'middle', |
| 177 | + }, |
| 178 | +}); |
| 179 | +``` |
| 180 | + |
| 181 | +#### **Step 5 (_optional_): Putting everything together.** |
| 182 | + |
| 183 | +After our styles are ready to be used, we can put everything together and get the desired layout shown above. We should have a component that looks like this: |
| 184 | + |
| 185 | +```jsx |
| 186 | +import { Text, makeStyles } from '@fluentui/react-components'; |
| 187 | + |
| 188 | +// Our makeStyles call from above |
| 189 | + |
| 190 | +const MediaObject: React.FC<{ text?: string }> = ({ children, text }) => { |
| 191 | + const styles = useStyles(); |
| 192 | + |
| 193 | + return ( |
| 194 | + <div className={styles.parent}> |
| 195 | + <div className={styles.media}>{children}</div> |
| 196 | + <Text className={styles.text}>{text}</Text> |
| 197 | + </div> |
| 198 | + ); |
| 199 | +}; |
| 200 | +``` |
| 201 | + |
| 202 | +## **Variants** |
| 203 | + |
| 204 | +There are a few common variants that you might want to use when building a Media Object. These are some of them: |
| 205 | + |
| 206 | +- ### **Text Alignment Variations** |
| 207 | + |
| 208 | +There are text alignment variations that might be useful when building an application. These can be ones below where the |
| 209 | +first one is after the media, the second one is below, and the last one is before. |
| 210 | + |
| 211 | +<TemplateExample centered> |
| 212 | + <TextPositionVariations /> |
| 213 | +</TemplateExample> |
| 214 | + |
| 215 | +- ### **Text Vertical Alignment Variations** |
| 216 | + |
| 217 | +There are also vertical alignment variations, these include start and center as seen below. |
| 218 | + |
| 219 | +<TemplateExample centered> |
| 220 | + <TextAlignmentVariations /> |
| 221 | +</TemplateExample> |
| 222 | + |
| 223 | +## **Best practices** |
| 224 | + |
| 225 | +- The higher up the text line, the more important it is. You should not apply higher weights to the lines underneath. |
| 226 | +- When using `grid-template-columns` make sure the DOM makes sense and not differ from how the grid is placing them. Do |
| 227 | + not have your DOM be `<media> <text>` when the rendered result looks like `<text> <media>`. |
0 commit comments