Стек всплывающих уведомлений снизу вверх — css/actjs

Я делаю всплывающее уведомление и пытаюсь реализовать параметры позиции.

Моя проблема в том, что когда я помещаю всплывающее уведомление внизу, а затем добавляю другое, новое появляется в исходной позиции, заменяя первое, а первое перемещается вниз, в конечном итоге за пределы экрана.

Я пытаюсь добиться того, чтобы второй тост заменил первый, но чтобы первый затем отображался ВЫШЕ, поэтому они складываются вверх.

Я сделал минимальный пример на песочница кода

Вот код компонента reactjs, где Toasts в широком смысле означает отдельные Toast компоненты:

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [toastState, setToastState] = useState([]);
  const handleClick = (pos) => {
    const ts = [...toastState];
    ts.push({ id: ts.length, pos: pos });
    setToastState(ts);
  };

  const Toast = ({ id, pos }) => {
    return <div className={`toast ${pos}`}>{id}</div>;
  };
  const Toasts = () => {
    const toasts = [...toastState];
    const toastComps = toasts.reverse().map((t, i) => {
      return <Toast key={i} id={t.id} pos={t.pos} />;
    });
    return <div>{toastComps}</div>;
  };

  return (
    <div className="App">
      <button
        onClick={() => {
          handleClick("bottom");
        }}
      >
        Bottom
      </button>
      <Toasts className="toasts" />
    </div>
  );
}

И вот css:

.toasts {
  min-width: 100vw;
  z-index: 9999;
  position: fixed;
  top: 0px;
  left: 0px;
  margin: 0px;
  padding: 0px;
  pointer-events: none;
}

.toast {
  position: relative;
  min-height: 50px;
  min-width: 20rem;
  margin: 5px;
  padding: 5px;
  border: 1px solid black;
  border-radius: 5px;
}

.bottom {
  bottom: -80vh;
}

Любые предложения о том, как добиться укладки этих компонентов тоста снизу вверх?

Я попробовал использовать flexbox, например, добавив следующие свойства в класс контейнера toasts:

display: flex;
flex-direction: column-reverse;
flex-wrap: wrap-reverse;

Но безрезультатно.

Я также пробовал решение js, вычисляя значение для bottom на основе индекса тоста:

const [bot, setBot] = useState(0);

useEffect(() => {
    setBot(index ? 80 - (15*index) : 0);
}, [])

//then as an attribute to the `toast`div, after setting className for the position...
style={position.indexOf('bottom') > -1 && bot
? {bottom: `-${bot}vh`} : {}}

Это становится ближе к тому, что мне нужно, но когда тосты истекают или удаляются, оставшиеся остаются высоко на экране - так что на самом деле весь список вытягивается вверх по экрану, а не складывается вверх, как предполагалось.

3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
Современные подходы к организации и работе с CSS в проекте
Современные подходы к организации и работе с CSS в проекте
Любой, кто писал CSS в течение некоторого времени, знает о сложностях, которые с этим связаны, и о том, насколько это может быть болезненно.
Как использовать псевдокласс :hover в Tailwind только тогда, когда это действительно необходимо (при наличии курсора)
Как использовать псевдокласс :hover в Tailwind только тогда, когда это действительно необходимо (при наличии курсора)
Я люблю Tailwind. Раньше я относился к нему с подозрением, но как только я попробовал его использовать, я был поражен тем, сколько времени он мне...
Именование классов CSS: Конвенция именования BEM
Именование классов CSS: Конвенция именования BEM
Сопровождаемость кода, сама по себе, является пульсирующим эффектом нескольких факторов. Когда часть программного обеспечения читабельна, ясна,...
0
0
30
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вот упрощенное решение.

import { useState } from "react";
import "./styles.css";

export default function App() {
  const [toasts, setToasts] = useState([]);

  const btnClicked = () => {
    console.log("btnClicked");
    setToasts([...toasts, { id: toasts.length }]);
  };

  return (
    <div className="App">
      <button onClick={btnClicked}>Bottom</button>
      <div className="toasts">
        {toasts
          .map((toast) => <div className="toast">{toast.id}</div>)
          .reverse()}
      </div>
    </div>
  );
}

и CSS:

.App {
  font-family: sans-serif;
  text-align: center;
}

.toasts {
  position: fixed;
  min-width: 100vw;
  z-index: 9999;
  position: fixed;
  bottom: 0px;
  left: 0px;
  margin: 0px;
  padding: 0px;
  pointer-events: none;
  overflow: hidden;
}

.toast {
  position: relative;
  min-height: 50px;
  min-width: 10rem;
  max-width: 10rem;
  margin: 5px;
  padding: 5px;
  border: 1px solid black;
  border-radius: 5px;
}

.bottom {
  bottom: -80vh;
}

Вы можете увидеть это в действии здесь: КодПесочница

Другие вопросы по теме