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 {
|
.modal > .clickableOverlay > .styledModal.aside > .styledIcon.modalVariantAside:hover {
|
||||||
color: var(--primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.styledModal.confirmModal {
|
||||||
|
padding: 35px 40px 40px;
|
||||||
|
}
|
@ -11,7 +11,7 @@
|
|||||||
@import "css/styledSelect.css";
|
@import "css/styledSelect.css";
|
||||||
@import "css/styledButton.css";
|
@import "css/styledButton.css";
|
||||||
@import "css/styledInput.css";
|
@import "css/styledInput.css";
|
||||||
|
@import "css/styledModal.css";
|
||||||
@import "css/app.css";
|
@import "css/app.css";
|
||||||
@import "css/modal.css";
|
|
||||||
@import "css/issue.css";
|
@import "css/issue.css";
|
||||||
@import "css/project.css";
|
@import "css/project.css";
|
||||||
|
@ -3,7 +3,7 @@ use seed::{prelude::*, *};
|
|||||||
|
|
||||||
use jirs_data::IssueStatus;
|
use jirs_data::IssueStatus;
|
||||||
|
|
||||||
use crate::model::Page;
|
use crate::model::{ModalType, Page};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
@ -23,6 +23,12 @@ pub type AvatarFilterActive = bool;
|
|||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum FieldId {
|
pub enum FieldId {
|
||||||
IssueTypeEditModalTop,
|
IssueTypeEditModalTop,
|
||||||
|
CopyButtonLabel,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum FieldChange {
|
||||||
|
LinkCopied(FieldId, bool),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -52,7 +58,9 @@ pub enum Msg {
|
|||||||
IssueUpdateResult(FetchObject<String>),
|
IssueUpdateResult(FetchObject<String>),
|
||||||
|
|
||||||
// modals
|
// modals
|
||||||
PopModal,
|
ModalOpened(ModalType),
|
||||||
|
ModalDropped,
|
||||||
|
ModalChanged(FieldChange),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(msg: Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
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 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_button::StyledButton;
|
||||||
use crate::shared::styled_icon::{Icon, StyledIcon};
|
use crate::shared::styled_icon::{Icon, StyledIcon};
|
||||||
use crate::shared::styled_select::{StyledSelect, Variant as SelectVariant};
|
use crate::shared::styled_select::{StyledSelect, Variant as SelectVariant};
|
||||||
use crate::shared::ToNode;
|
use crate::shared::ToNode;
|
||||||
use crate::{FieldId, IssueId, Msg};
|
use crate::{FieldChange, FieldId, IssueId, Msg};
|
||||||
|
|
||||||
#[derive(PartialOrd, PartialEq, Debug)]
|
#[derive(PartialOrd, PartialEq, Debug)]
|
||||||
struct IssueTypeOption(IssueId, IssueType);
|
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();
|
el.set_selection_range(0, 9999).unwrap();
|
||||||
seed::html_document().exec_command("copy").unwrap();
|
seed::html_document().exec_command("copy").unwrap();
|
||||||
seed::body().remove_child(&el).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()
|
let copy_button = StyledButton::build()
|
||||||
.empty()
|
.empty()
|
||||||
.icon(Icon::Link)
|
.icon(Icon::Link)
|
||||||
@ -110,6 +113,7 @@ pub fn view(_model: &Model, issue: &Issue, modal: &EditIssueModal) -> Node<Msg>
|
|||||||
let delete_button = StyledButton::build()
|
let delete_button = StyledButton::build()
|
||||||
.empty()
|
.empty()
|
||||||
.icon(Icon::Trash.into_styled_builder().size(19).build())
|
.icon(Icon::Trash.into_styled_builder().size(19).build())
|
||||||
|
.on_click(delete_confirmation_handler)
|
||||||
.build()
|
.build()
|
||||||
.into_node();
|
.into_node();
|
||||||
let close_button = StyledButton::build()
|
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::modal::{Modal, Variant as ModalVariant};
|
||||||
use crate::shared::styled_select::StyledSelectChange;
|
use crate::shared::styled_select::StyledSelectChange;
|
||||||
use crate::shared::{find_issue, ToNode};
|
use crate::shared::{find_issue, ToNode};
|
||||||
use crate::{model, FieldId, Msg};
|
use crate::{model, FieldChange, FieldId, Msg};
|
||||||
|
|
||||||
|
mod confirm_delete_issue;
|
||||||
mod issue_details;
|
mod issue_details;
|
||||||
|
|
||||||
pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
pub fn update(msg: &Msg, model: &mut model::Model, orders: &mut impl Orders<Msg>) {
|
||||||
match 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)) => {
|
Msg::ChangePage(Page::EditIssue(issue_id)) => {
|
||||||
let value = find_issue(model, *issue_id)
|
let value = find_issue(model, *issue_id)
|
||||||
.map(|issue| issue.issue_type.clone())
|
.map(|issue| issue.issue_type.clone())
|
||||||
@ -113,6 +126,7 @@ pub fn view(model: &model::Model) -> Node<Msg> {
|
|||||||
empty![]
|
empty![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ModalType::DeleteIssueConfirm(_id) => confirm_delete_issue::view(model),
|
||||||
_ => empty![],
|
_ => empty![],
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -25,7 +25,6 @@ pub struct EditIssueModal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialOrd, PartialEq)]
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum Page {
|
pub enum Page {
|
||||||
Project,
|
Project,
|
||||||
EditIssue(IssueId),
|
EditIssue(IssueId),
|
||||||
|
@ -13,6 +13,7 @@ pub mod styled_avatar;
|
|||||||
pub mod styled_button;
|
pub mod styled_button;
|
||||||
pub mod styled_icon;
|
pub mod styled_icon;
|
||||||
pub mod styled_input;
|
pub mod styled_input;
|
||||||
|
pub mod styled_modal;
|
||||||
pub mod styled_select;
|
pub mod styled_select;
|
||||||
pub mod styled_tooltip;
|
pub mod styled_tooltip;
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ pub fn render(values: Modal) -> Node<Msg> {
|
|||||||
empty![]
|
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| {
|
let body_handler = mouse_ev(Ev::Click, |ev| {
|
||||||
ev.stop_propagation();
|
ev.stop_propagation();
|
||||||
Msg::NoOp
|
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 styled from 'styled-components';
|
||||||
|
|
||||||
import { font } from 'shared/utils/styles';
|
import { font } from '../../../shared/utils/styles';
|
||||||
import Modal from 'shared/components/Modal';
|
import Modal from '../../../shared/components/Modal';
|
||||||
import Button from 'shared/components/Button';
|
import Button from '../../../shared/components/Button';
|
||||||
|
|
||||||
export const StyledConfirmModal = styled(Modal)`
|
export const StyledConfirmModal = styled(Modal)`
|
||||||
padding: 35px 40px 40px;
|
padding: 35px 40px 40px;
|
||||||
|
@ -1,19 +1,19 @@
|
|||||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
|
import useOnOutsideClick from '../../../shared/hooks/onOutsideClick';
|
||||||
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
|
import useOnEscapeKeyDown from '../../../shared/hooks/onEscapeKeyDown';
|
||||||
|
|
||||||
import { ClickableOverlay, CloseIcon, ScrollOverlay, StyledModal } from './Styles';
|
import { ClickableOverlay, CloseIcon, ScrollOverlay, StyledModal } from './Styles';
|
||||||
|
|
||||||
const Modal = ({
|
const Modal = ({
|
||||||
className,
|
className,
|
||||||
testid,
|
testid,
|
||||||
variant,
|
variant,
|
||||||
width,
|
width,
|
||||||
withCloseIcon,
|
withCloseIcon,
|
||||||
isOpen: propsIsOpen,
|
isOpen: propsIsOpen,
|
||||||
onClose: tellParentToClose,
|
onClose: tellParentToClose,
|
||||||
renderLink,
|
renderLink,
|
||||||
renderContent,
|
renderContent,
|
||||||
|
Loading…
Reference in New Issue
Block a user