Add modals on top other modal
This commit is contained in:
parent
9980b75ab5
commit
f2b5753f83
@ -76,3 +76,7 @@
|
||||
.modal > .clickableOverlay > .styledModal.aside > .styledIcon.modalVariantAside:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.styledModal.confirmModal {
|
||||
padding: 35px 40px 40px;
|
||||
}
|
@ -11,7 +11,7 @@
|
||||
@import "css/styledSelect.css";
|
||||
@import "css/styledButton.css";
|
||||
@import "css/styledInput.css";
|
||||
@import "css/styledModal.css";
|
||||
@import "css/app.css";
|
||||
@import "css/modal.css";
|
||||
@import "css/issue.css";
|
||||
@import "css/project.css";
|
||||
|
@ -3,7 +3,7 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::IssueStatus;
|
||||
|
||||
use crate::model::Page;
|
||||
use crate::model::{ModalType, Page};
|
||||
use crate::shared::styled_select::StyledSelectChange;
|
||||
|
||||
mod api;
|
||||
@ -23,6 +23,12 @@ pub type AvatarFilterActive = bool;
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FieldId {
|
||||
IssueTypeEditModalTop,
|
||||
CopyButtonLabel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FieldChange {
|
||||
LinkCopied(FieldId, bool),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -52,7 +58,9 @@ pub enum Msg {
|
||||
IssueUpdateResult(FetchObject<String>),
|
||||
|
||||
// modals
|
||||
PopModal,
|
||||
ModalOpened(ModalType),
|
||||
ModalDropped,
|
||||
ModalChanged(FieldChange),
|
||||
}
|
||||
|
||||
fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
|
16
jirs-client/src/modal/confirm_delete_issue.rs
Normal file
16
jirs-client/src/modal/confirm_delete_issue.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use seed::{prelude::*, *};
|
||||
|
||||
use crate::shared::styled_modal::StyledModal;
|
||||
use crate::shared::ToNode;
|
||||
use crate::{model, Msg};
|
||||
|
||||
pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
let handle_issue_delete = mouse_ev(Ev::Click, |_| Msg::NoOp);
|
||||
StyledModal::build()
|
||||
.title("Are you sure you want to delete this issue?")
|
||||
.message("Once you delete, it's gone for good.")
|
||||
.confirm_text("Delete issue")
|
||||
.on_confirm(handle_issue_delete)
|
||||
.build()
|
||||
.into_node()
|
||||
}
|
@ -2,12 +2,12 @@ use seed::{prelude::*, *};
|
||||
|
||||
use jirs_data::{Issue, IssueType};
|
||||
|
||||
use crate::model::{EditIssueModal, Model};
|
||||
use crate::model::{EditIssueModal, ModalType, Model};
|
||||
use crate::shared::styled_button::StyledButton;
|
||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||
use crate::shared::styled_select::{StyledSelect, Variant as SelectVariant};
|
||||
use crate::shared::ToNode;
|
||||
use crate::{FieldId, IssueId, Msg};
|
||||
use crate::{FieldChange, FieldId, IssueId, Msg};
|
||||
|
||||
#[derive(PartialOrd, PartialEq, Debug)]
|
||||
struct IssueTypeOption(IssueId, IssueType);
|
||||
@ -92,10 +92,13 @@ pub fn view(_model: &Model, issue: &Issue, modal: &EditIssueModal) -> Node<Msg>
|
||||
el.set_selection_range(0, 9999).unwrap();
|
||||
seed::html_document().exec_command("copy").unwrap();
|
||||
seed::body().remove_child(&el).unwrap();
|
||||
Msg::NoOp
|
||||
Msg::ModalChanged(FieldChange::LinkCopied(FieldId::CopyButtonLabel, true))
|
||||
});
|
||||
let close_handler = mouse_ev(Ev::Click, |_| Msg::ModalDropped);
|
||||
let delete_confirmation_handler = mouse_ev(Ev::Click, move |_| {
|
||||
Msg::ModalOpened(ModalType::DeleteIssueConfirm(issue_id))
|
||||
});
|
||||
|
||||
let close_handler = mouse_ev(Ev::Click, |_| Msg::PopModal);
|
||||
let copy_button = StyledButton::build()
|
||||
.empty()
|
||||
.icon(Icon::Link)
|
||||
@ -110,6 +113,7 @@ pub fn view(_model: &Model, issue: &Issue, modal: &EditIssueModal) -> Node<Msg>
|
||||
let delete_button = StyledButton::build()
|
||||
.empty()
|
||||
.icon(Icon::Trash.into_styled_builder().size(19).build())
|
||||
.on_click(delete_confirmation_handler)
|
||||
.build()
|
||||
.into_node();
|
||||
let close_button = StyledButton::build()
|
||||
|
@ -7,16 +7,29 @@ use crate::model::{EditIssueModal, ModalType, Page};
|
||||
use crate::shared::modal::{Modal, Variant as ModalVariant};
|
||||
use crate::shared::styled_select::StyledSelectChange;
|
||||
use crate::shared::{find_issue, ToNode};
|
||||
use crate::{model, FieldId, Msg};
|
||||
use crate::{model, FieldChange, FieldId, Msg};
|
||||
|
||||
mod confirm_delete_issue;
|
||||
mod issue_details;
|
||||
|
||||
pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||
match msg {
|
||||
Msg::PopModal => match model.modals.pop() {
|
||||
Msg::ModalDropped => match model.modals.pop() {
|
||||
_ => (),
|
||||
},
|
||||
|
||||
Msg::ModalChanged(FieldChange::LinkCopied(FieldId::CopyButtonLabel, true)) => {
|
||||
for modal in model.modals.iter_mut() {
|
||||
if let ModalType::EditIssue(_, edit) = modal {
|
||||
edit.link_copied = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Msg::ModalOpened(modal_type) => {
|
||||
model.modals.push(modal_type.clone());
|
||||
}
|
||||
|
||||
Msg::ChangePage(Page::EditIssue(issue_id)) => {
|
||||
let value = find_issue(model, *issue_id)
|
||||
.map(|issue| issue.issue_type.clone())
|
||||
@ -113,6 +126,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
||||
empty![]
|
||||
}
|
||||
}
|
||||
ModalType::DeleteIssueConfirm(_id) => confirm_delete_issue::view(model),
|
||||
_ => empty![],
|
||||
})
|
||||
.collect();
|
||||
|
@ -25,7 +25,6 @@ pub struct EditIssueModal {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum Page {
|
||||
Project,
|
||||
EditIssue(IssueId),
|
||||
|
@ -13,6 +13,7 @@ pub mod styled_avatar;
|
||||
pub mod styled_button;
|
||||
pub mod styled_icon;
|
||||
pub mod styled_input;
|
||||
pub mod styled_modal;
|
||||
pub mod styled_select;
|
||||
pub mod styled_tooltip;
|
||||
|
||||
|
@ -58,7 +58,7 @@ pub fn render(values: Modal) -> Node<Msg> {
|
||||
empty![]
|
||||
};
|
||||
|
||||
let close_handler = mouse_ev(Ev::Click, |_| Msg::PopModal);
|
||||
let close_handler = mouse_ev(Ev::Click, |_| Msg::ModalDropped);
|
||||
let body_handler = mouse_ev(Ev::Click, |ev| {
|
||||
ev.stop_propagation();
|
||||
Msg::NoOp
|
||||
|
119
jirs-client/src/shared/styled_modal.rs
Normal file
119
jirs-client/src/shared/styled_modal.rs
Normal file
@ -0,0 +1,119 @@
|
||||
use seed::EventHandler;
|
||||
use seed::{prelude::*, *};
|
||||
|
||||
use crate::shared::ToNode;
|
||||
use crate::Msg;
|
||||
|
||||
const TITLE: &str = "Warning";
|
||||
const MESSAGE: &str = "Are you sure you want to continue with this action?";
|
||||
const CONFIRM_TEXT: &str = "Confirm";
|
||||
const CANCEL_TEXT: &str = "Cancel";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Variant {
|
||||
Primary,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct StyledModal {
|
||||
pub variant: Variant,
|
||||
pub title: String,
|
||||
pub message: String,
|
||||
pub confirm_text: String,
|
||||
pub cancel_text: String,
|
||||
pub on_confirm: Option<EventHandler<Msg>>,
|
||||
}
|
||||
|
||||
impl StyledModal {
|
||||
pub fn build() -> StyledModalBuilder {
|
||||
StyledModalBuilder::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToNode for StyledModal {
|
||||
fn into_node(self) -> Node<Msg> {
|
||||
render(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct StyledModalBuilder {
|
||||
variant: Option<Variant>,
|
||||
title: Option<String>,
|
||||
message: Option<String>,
|
||||
confirm_text: Option<String>,
|
||||
cancel_text: Option<String>,
|
||||
on_confirm: Option<Option<EventHandler<Msg>>>,
|
||||
}
|
||||
|
||||
impl StyledModalBuilder {
|
||||
pub fn variant(mut self, variant: Variant) -> Self {
|
||||
self.variant = Some(variant);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn title<S>(mut self, title: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.title = Some(title.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn message<S>(mut self, message: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.message = Some(message.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn confirm_text<S>(mut self, confirm_text: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.confirm_text = Some(confirm_text.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cancel_text<S>(mut self, cancel_text: S) -> Self
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
self.cancel_text = Some(cancel_text.into());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_confirm(mut self, on_confirm: EventHandler<Msg>) -> Self {
|
||||
self.on_confirm = Some(Some(on_confirm));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> StyledModal {
|
||||
StyledModal {
|
||||
variant: self.variant.unwrap_or_else(|| Variant::Primary),
|
||||
title: self.title.unwrap_or_else(|| TITLE.to_string()),
|
||||
message: self.message.unwrap_or_else(|| MESSAGE.to_string()),
|
||||
confirm_text: self
|
||||
.confirm_text
|
||||
.unwrap_or_else(|| CONFIRM_TEXT.to_string()),
|
||||
cancel_text: self.cancel_text.unwrap_or_else(|| CANCEL_TEXT.to_string()),
|
||||
on_confirm: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render(values: StyledModal) -> Node<Msg> {
|
||||
let StyledModal {
|
||||
variant,
|
||||
title,
|
||||
message,
|
||||
confirm_text,
|
||||
cancel_text,
|
||||
on_confirm,
|
||||
} = values;
|
||||
div![
|
||||
attrs![At::Class => "modal"],
|
||||
div![attrs![At::Class => "styledModal"]]
|
||||
]
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { font } from 'shared/utils/styles';
|
||||
import Modal from 'shared/components/Modal';
|
||||
import Button from 'shared/components/Button';
|
||||
import { font } from '../../../shared/utils/styles';
|
||||
import Modal from '../../../shared/components/Modal';
|
||||
import Button from '../../../shared/components/Button';
|
||||
|
||||
export const StyledConfirmModal = styled(Modal)`
|
||||
padding: 35px 40px 40px;
|
||||
|
@ -2,8 +2,8 @@ import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react
|
||||
import ReactDOM from 'react-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
||||
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
|
||||
import useOnOutsideClick from '../../../shared/hooks/onOutsideClick';
|
||||
import useOnEscapeKeyDown from '../../../shared/hooks/onEscapeKeyDown';
|
||||
|
||||
import { ClickableOverlay, CloseIcon, ScrollOverlay, StyledModal } from './Styles';
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user