Создание компонента Timeline с React


При работе над новой страницей своего веб-сайта я решил добавить Timeline, чтобы показать свои профессиональные достижения за последние годы. С помощью временной шкалы можно не только отследить собственные достижения, но и привлечь новых клиентов. 

На изображении выше показана временная шкала, которую мы будем создавать с помощью React! Для этого выполним следующие действия:

  • Создадим data
  • Создадим компонент TimelineItem
  • Создадим контейнер Timeline для размещения data, которые затем будут переданы в TimelineItem
  • Выполним стилизацию
  • Создание data

    Прежде чем создавать компоненты React, нужно разобраться в том, как будут выглядеть денные, чтобы распланировать структуру DOM.

    Для этого приложения Timeline понадобится массив объектов. Назовем этот массив: timelineData.

    Он выглядит следующим образом:

    [ { 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 } ];

    Свойства достаточно ясны.

    Затем создадим компонент TimelineItem. Он будет использовать данные из объекта выше:

    Компонент 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> );

    У нас есть следующие теги:

  • .timeline-item div — используется в качестве wrapper. Этот div обладает половиной родительской ширины (50%), а все остальные .timeline-item div размещаются с правой стороны с помощью селектора :nth-child(odd)
  • .timeline-item-content div — еще один wrapper (больше информации об этом в разделе стилизации)
  • .tag span — этот тег обладает пользовательским фоновым цветом с зависимости от категории
  • time/date и text
  • link — ее необходимо проверять, чтобы узнать о наличии ссылки (link) , поскольку она не всегда необходима
  • .circle span — этот тег используется для размещения круга в середине строки/панели
  • Примечание: Все будет понятно в разделах CSS иСтилизация, но для начала создадим компонент Timeline:

    Контейнер Timeline

    Этот компонент будет отображаться (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> );

    Как было сказано выше, timelineData — это массив объектов, содержащий всю необходимую информацию. В этом случае массив сохранен в файле и импортирован сюда, однако его вы можете взять его из своей базы данных или endpoint API.

    CSS

    Примечание: большинство wrappers будут контейнерами flexbox, поскольку так легче экспериментировать с их расположением.

    Начнем с CSS .timeline-container:

    .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%; }

    Используем селектор ::after для создания красной линии/полосы в середине .timeline-container. С помощью функции calc() линию можно разместить посередине путем вычисления половины ее размера (2px) из 50%. Это нужно сделать, поскольку по умолчанию свойство left размещает ее в соответствии с левым краем, а не посередине.

    Теперь перейдем к wrapper .timeline-item.

    Ниже показан пример их размещения относительно родителя (.timeline-container):

    Каждый следующий wrapper переходит вправо, а внутренний wrapper (.timeline-item-content) занимает меньше места  — пространство, заданное тегом p, который находится внутри (по большей части).

    Рассмотрим 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; }

    Смысл заключается в том, что мы используем селектор :nth-child(odd) и устанавливаем свойство align-self для flex-end, которое означает: “Перейти вправо настолько, насколько это возможно”!

    Поскольку эти wrappers 50% шириной, то два из них занимают всю ширину. Таким образом, для изменения стилизации с правой стороны нужно использовать этот подход.

    Переходим к 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); }

    Здесь происходит следующее:

  • Этот wrapper установил width и max-width, необходимые для наличия рамок. Это означает, что при наличии лишь нескольких слов, коробка должна быть минимум 400px шириной. Однако если текста много, она не должна занимать все пространство (50% от wrapper .timeline-item), а текст должен переходить на следующую строку -> по этой причине мы использовали второй wrapper: .timeline-item-content
  • Свойства text-align и align-items используются для перемещения внутренних элементов влево или вправо в зависимости от родителя
  • Маленькая стрелка, указывающая на середину линии, задается стилями, которые применяются к селектору ::after. По сути, это коробка с box-shadow, нанесенной на нее и повернутой на 45deg
  • Как было сказано выше, стилизация правой стороны осуществляется с помощью выбора родителя с селектором :nth-child(odd)
  • Переходим ко внутренним элементам:

    .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; }

    Примечания:

  • .tag имеет размещение absolute, поскольку он должен находится в левом (или правом) верхнем углу независимо от размера коробки
  • Нужно добавить небольшой значок после тега a для обозначения ссылки
  • Создаем .circle и размещаем его в верхней части средней линии/полосы прямо напротив стрелки
  • Почти все! Осталось только добавить 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; } }

    У нас есть два медиа-запроса:

    На маленьких экранах ноутбуков  — max-width: 1023px — .timeline-item-content должен проходить по всей ширине родителя, поскольку экран меньше, иначе изображение будет выглядеть сжатым

  • На телефонах  — max-width: 767px
    • установите .tag на полную ширину (width) (не забудьте вычесть 10px от 100% — поскольку его расположение left: 5px, поэтому удаляем вдвое больше от этой суммы)
    • разместите текст в центре и опустите его немного вниз от верхнего края
    • удалите значок на ссылке и добавьте подчеркивание  — оно выглядит лучше на мобильном устройстве ????

    Вот и все!

    Заключение

    Этот компонент находится на моей странице Timeline. Там можно увидеть его в действии!

    Счастливого программирования! ????


    Перевод статьи Florin Pop: How to create a Timeline Component with React


    Поделиться статьей:


    Вернуться к статьям

    Комментарии

      Ничего не найдено.