react创建组件
These days I’ve been working on a new page for my website. I wanted to have a Timeline to showcase some of my professional accomplishments over the years.
这些天来,我一直在为网站创建新页面。 我希望有一个时间表来展示我多年来的一些专业成就。
I did it for a couple of reasons:
我这样做有两个原因:
Nevertheless… Let’s build something now!
不过……让我们现在建立一些东西!
In the picture above you can see what we’re going to build today using React! Before we start let’s break down the steps we need to take:
在上图中,您可以看到我们今天将使用React构建什么! 在开始之前,让我们分解一下我们需要采取的步骤:
Create the data
that we'll need
创建我们需要的data
Create the TimelineItem
component - each individual timeline entry
创建TimelineItem
组件-每个单独的时间线条目
Create a Timeline
container - it will take the data
and pass it down to the TimelineItem
s
创建一个Timeline
容器-将获取data
并将其传递给TimelineItem
Before we move to actually create the React components we need to know exactly how the data is going to look so we can plan out the DOM structure.
在实际创建React组件之前,我们需要确切地知道数据的外观,以便我们可以规划DOM结构。
For this Timeline app we’re going to need an array of objects. We will call this array: timelineData
.
对于此时间轴应用程序,我们将需要一个对象数组 。 我们将其称为数组: timelineData
。
Let’s see how it might look:
让我们看一下它的外观:
[
{
text: 'Wrote my first blog post ever on Medium',
date: 'March 03 2017',
category: {
tag: 'medium',
color: '#018f69'
},
link: {
url:
'https://medium.com/@popflorin1705/javascript-coding-challenge-1-6d9c712963d2',
text: 'Read more'
}
},
{
// Another object with data
}
];
The properties are pretty straightforward, right? I used similar data to what I have on my timeline page, so we can say that this is production ready! ?
属性非常简单,对吧? 我使用了与时间表页面上相似的数据,因此可以说这已经可以投入生产了! ?
Next, we’ll build the TimelineItem
component. This will use the data from the object above:
接下来,我们将构建TimelineItem
组件。 这将使用上面对象中的数据:
const TimelineItem = ({ data }) => (
<div className="timeline-item">
<div className="timeline-item-content">
<span className="tag" style={{ background: data.category.color }}>
{data.category.tag}
</span>
<time>{data.date}</time>
<p>{data.text}</p>
{data.link && (
<a
href={data.link.url}
target="_blank"
rel="noopener noreferrer"
>
{data.link.text}
</a>
)}
<span className="circle" />
</div>
</div>
);
We have the following tags:
我们有以下标签:
.timeline-item
div - used as a wrapper. This div will have half the width of its parent's width (50%
) and every other .timeline-item
div will be placed to the right side using the :nth-child(odd)
selector
.timeline-item
用作包装器。 这个div的宽度是其父级宽度的一半( 50%
),并且每个其他.timeline-item
div将使用:nth-child(odd)
选择器放置在右侧
.timeline-item-content
div - another wrapper (more on why we need this in the styling section)
.timeline-item-content
另一个包装器(有关在样式部分中为什么需要此包装的更多信息)
.tag
span - this tag will have a custom background color depending on the category
.tag
此标签将具有自定义背景颜色,具体取决于类别
the time
/date
and the text
time
/ date
和text
link
- we will need to check this to see if a link
is provided because we might not always want to have one
link
-我们将需要检查此link
以查看是否提供了link
,因为我们可能并不总是希望拥有一个链接
.circle
span - this tag will be used to place a circle on the middle line/bar
.circle
此标签将用于在中线/条上放置一个圆
Note: Everything will make much more sense when we get to the CSS/styling part, but before that let’s create the Timeline
component:
注意 :当我们进入CSS / styleling部分时,一切都会变得更加有意义,但是在此之前,让我们创建Timeline
组件:
This component will basically map
over the array and for each object it will create a TimelineItem
component. We also add a small check to make sure that there is at least one element in the array:
该组件基本上将map
到数组,并为每个对象创建一个TimelineItem
组件。 我们还添加了一个小检查,以确保数组中至少有一个元素:
import timelineData from '_path_to_file_';
const Timeline = () =>
timelineData.length > 0 && (
<div className="timeline-container">
{timelineData.map((data, idx) => (
<TimelineItem data={data} key={idx} />
))}
</div>
);
As mentioned above, the timelineData
is the array of objects containing all the required information. In my case I stored this array in a file and I imported it here, but you can take this from your own database or from an API endpoint, it's up to you.
如上所述, timelineData
是包含所有必需信息的对象数组。 就我而言,我将此数组存储在文件中,然后将其导入此处,但是您可以从自己的数据库或API端点获取它,这取决于您。
Note: most of the wrappers will be flexbox
containers because we can play around easier with their positioning.
注意:大多数包装器都是flexbox
容器,因为我们可以更轻松地定位它们。
Let's start with the .timeline-container
CSS:
让我们从.timeline-container
CSS开始:
.timeline-container {
display: flex;
flex-direction: column;
position: relative;
margin: 40px 0;
}
.timeline-container::after {
background-color: #e17b77;
content: '';
position: absolute;
left: calc(50% - 2px);
width: 4px;
height: 100%;
}
We’re using the ::after
selector to create that red line/bar in the middle of the .timeline-container
. Using the calc()
function we can position the line exactly in the middle by subtracting half of its size (2px
) from 50%
. We need to do this because by default the left
property positions it according to the left edge of an element and not the middle.
我们使用::after
选择器在.timeline-container
的中间创建该红线/条。 使用calc()
函数,我们可以通过从50%
减去其大小的一半( 2px
)来将其精确定位在中间。 我们需要这样做,因为默认情况下, left
属性是根据元素的左边缘而不是中间位置来定位它的。
Now, let’s move to the .timeline-item
wrapper.
现在,让我们转到.timeline-item
包装器。
Below you can see an example of how these are positioned within their parent (the .timeline-container
). For demonstration purposes I added a border to highlight these wrappers:
在下面,您可以看到一个示例如何将它们放置在其父对象( .timeline-container
)中。 出于演示目的,我添加了边框以突出显示这些包装:
As you can see, every other wrapper goes to the right, and the inner wrapper (the .timeline-item-content
) is taking less space — space given by the p
tag which is inside it (mostly).
如您所见,其他所有包装器都向右移动 ,而内部包装器( .timeline-item-content
)占用的空间更少-由内部的p
标签提供的空间(主要是)。
Let's see the CSS for this:
让我们来看一下CSS:
.timeline-item {
display: flex;
justify-content: flex-end;
padding-right: 30px;
position: relative;
margin: 10px 0;
width: 50%;
}
.timeline-item:nth-child(odd) {
align-self: flex-end;
justify-content: flex-start;
padding-left: 30px;
padding-right: 0;
}
The key to this is that we use the :nth-child(odd)
selector and we set the align-self
property to flex-end
which means:“Go to the right as much as you can”!
关键在于我们使用:nth-child(odd)
选择器,并将align-self
属性设置为flex-end
,这意味着:“尽可能向右移动”!
Because these wrappers are 50%
in width, you can see that two of them take up the whole width. From now on, every time we want to style differently something in the right side, we'll have to use this approach.
由于这些包装纸的宽度为50%
,因此您可以看到其中两个包装纸占据了整个宽度。 从现在开始,每次我们要在右侧样式不同时,都必须使用此方法。
Next, the .timeline-item-content
wrapper:
接下来, .timeline-item-content
包装器:
.timeline-item-content {
box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
border-radius: 5px;
background-color: #fff;
display: flex;
flex-direction: column;
align-items: flex-end;
padding: 15px;
position: relative;
width: 400px;
max-width: 70%;
text-align: right;
}
.timeline-item-content::after {
content: ' ';
background-color: #fff;
box-shadow: 1px -1px 1px rgba(0, 0, 0, 0.2);
position: absolute;
right: -7.5px;
top: calc(50% - 7.5px);
transform: rotate(45deg);
width: 15px;
height: 15px;
}
.timeline-item:nth-child(odd) .timeline-item-content {
text-align: left;
align-items: flex-start;
}
.timeline-item:nth-child(odd) .timeline-item-content::after {
right: auto;
left: -7.5px;
box-shadow: -1px 1px 1px rgba(0, 0, 0, 0.2);
}
We have a few things going on:
我们正在进行一些事情:
This wrapper has a fixed width
and also a max-width
. This is because we want it to have some boundaries, meaning that if there are only a few words, we want the box to be at least 400px
wide, but if there is a lot of text, it shouldn't take up the full space (the 50%
from the .timeline-item
wrapper) but the text should move on to the next line -> this is the reason we used this second wrapper: .timeline-item-cont
ent
该包装器具有固定的width
和max-width
。 这是因为我们希望它具有一定的边界,这意味着如果只有几个单词,我们希望该框至少为400px
宽,但是如果有很多文本,则不应占用全部空间( .timeline-item
包装器中的50%
),但文本应移至下一行->这就是我们使用第二个wrappe r: .timeline-item-cont
的原因r: .timeline-item-cont
ent
The text-align
and align-items
properties are used to push the inner elements to the left or to the right, depending on the parent
text-align
和align-items
属性用于将内部元素向左或向右推,具体取决于父元素
The small arrow that points to the middle line is given by the styles applied on the ::after
selector. Basically it is a box with a box-shadow
applied on it that is rotated 45deg
指向中间行的小箭头由::after
选择器上应用的样式给出。 基本上,这是一个有box-shadow
的box-shadow
,它旋转了45deg
As mentioned above, we style the right side by selecting the parent with the :nth-child(odd)
selector
如上所述,我们使用:nth-child(odd)
选择器选择父:nth-child(odd)
来设置右边的样式
Next up, all the inner elements:
接下来,所有内部元素:
.timeline-item-content .tag {
color: #fff;
font-size: 12px;
font-weight: bold;
top: 5px;
left: 5px;
letter-spacing: 1px;
padding: 5px;
position: absolute;
text-transform: uppercase;
}
.timeline-item:nth-child(odd) .timeline-item-content .tag {
left: auto;
right: 5px;
}
.timeline-item-content time {
color: #777;
font-size: 12px;
font-weight: bold;
}
.timeline-item-content p {
font-size: 16px;
line-height: 24px;
margin: 15px 0;
max-width: 250px;
}
.timeline-item-content a {
font-size: 14px;
font-weight: bold;
}
.timeline-item-content a::after {
content: ' ►';
font-size: 12px;
}
.timeline-item-content .circle {
background-color: #fff;
border: 3px solid #e17b77;
border-radius: 50%;
position: absolute;
top: calc(50% - 10px);
right: -40px;
width: 20px;
height: 20px;
z-index: 100;
}
.timeline-item:nth-child(odd) .timeline-item-content .circle {
right: auto;
left: -40px;
}
Few things to note here:
这里要注意的几件事:
As you might have guessed, the .tag
is positioned absolute
because we want to keep it in the top left (or right) corner no matter what size is the box
正如您可能已经猜到的那样, .tag
的位置是absolute
因为无论框的大小如何,我们都希望将其保留在左上角(或右上角)
We want to add a small caret after the a
tag to highlight that it is a link
我们希望后添加一个小尖号a
标签来强调,这是一个链接
We create a .circle
and position it on top of the middle line/bar directly in front of the arrow
我们创建一个.circle
并将其放置在箭头正前方的中间线/条的顶部
We’re almost done! ? The only thing that’s left to do is to add the CSS to make everything responsive across all screen sizes:
我们快完成了! ? 剩下要做的唯一一件事情就是添加CSS,以使所有内容在所有屏幕尺寸上都能响应:
@media only screen and (max-width: 1023px) {
.timeline-item-content {
max-width: 100%;
}
}
@media only screen and (max-width: 767px) {
.timeline-item-content,
.timeline-item:nth-child(odd) .timeline-item-content {
padding: 15px 10px;
text-align: center;
align-items: center;
}
.timeline-item-content .tag {
width: calc(100% - 10px);
text-align: center;
}
.timeline-item-content time {
margin-top: 20px;
}
.timeline-item-content a {
text-decoration: underline;
}
.timeline-item-content a::after {
display: none;
}
}
We have two media queries:
我们有两个媒体查询:
On small laptop screen sizes — max-width: 1023px
— we want to allow the .timeline-item-content
to go across the entire width of its parent because the screen is smaller and otherwise it would look squeezed
在小型笔记本电脑屏幕上( max-width: 1023px
,我们希望允许.timeline-item-content
遍及其父级的整个宽度,因为屏幕较小,否则看起来会受到挤压
On phones — max-width: 767px
在手机上- max-width: 767px
set the .tag
to be full width
(and for that we don't need to forget to subtract 10px
from the total of 100%
- this is because we have it positioned at left: 5px
, so we remove double of this amount)
将.tag
设置为全width
(为此,我们不必忘记从100%
的总和中减去10px
这是因为我们将其放置在left: 5px
,因此我们删除了该数量的两倍)
Aaaand… We’re done!
Aaaand…我们完成了!
As I mentioned, this component is on my Timeline page. Check it out to see it in action! ?
如前所述,该组件位于“ 时间轴”页面上。 检查一下,看看它的作用! ?
If there is something that you didn’t understand from this article, make sure you contact me and I’ll be happy to answer your questions!
如果您对本文不了解,请确保与我联系,我们将很乐意回答您的问题!
Happy Coding! ?
编码愉快! ?
Originally published at www.florin-pop.com.
最初在www.florin-pop.com上发布。
翻译自: https://www.freecodecamp.org/news/how-to-create-a-timeline-component-with-react-1b216f23d3d4/
react创建组件