mirror of
https://github.com/spacebarchat/client.git
synced 2024-11-21 18:02:32 +01:00
animated modals
This commit is contained in:
parent
2dee50f9ce
commit
807a7dc8e3
@ -29,6 +29,7 @@
|
||||
"@types/node": "^16.18.28",
|
||||
"@types/react": "^18.2.6",
|
||||
"@types/react-dom": "^18.2.4",
|
||||
"framer-motion": "^10.16.1",
|
||||
"mobx": "^6.9.0",
|
||||
"mobx-react-lite": "^3.4.3",
|
||||
"react": "^18.2.0",
|
||||
|
@ -56,6 +56,9 @@ dependencies:
|
||||
'@types/react-dom':
|
||||
specifier: ^18.2.4
|
||||
version: 18.2.4
|
||||
framer-motion:
|
||||
specifier: ^10.16.1
|
||||
version: 10.16.1(react-dom@18.2.0)(react@18.2.0)
|
||||
mobx:
|
||||
specifier: ^6.9.0
|
||||
version: 6.9.0
|
||||
@ -1626,12 +1629,26 @@ packages:
|
||||
resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
|
||||
dev: false
|
||||
|
||||
/@emotion/is-prop-valid@0.8.8:
|
||||
resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@emotion/memoize': 0.7.4
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@emotion/is-prop-valid@1.2.1:
|
||||
resolution: {integrity: sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==}
|
||||
dependencies:
|
||||
'@emotion/memoize': 0.8.1
|
||||
dev: false
|
||||
|
||||
/@emotion/memoize@0.7.4:
|
||||
resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
/@emotion/memoize@0.8.1:
|
||||
resolution: {integrity: sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==}
|
||||
dev: false
|
||||
@ -6032,6 +6049,24 @@ packages:
|
||||
map-cache: 0.2.2
|
||||
dev: true
|
||||
|
||||
/framer-motion@10.16.1(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-K6TXr5mZtitC/dxQCBdg7xzdN0d5IAIrlaqCPKtIQVdzVPGC0qBuJKXggHX1vjnP5gPOFwB1KbCCTWcnFc3kWg==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
react:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
tslib: 2.5.0
|
||||
optionalDependencies:
|
||||
'@emotion/is-prop-valid': 0.8.8
|
||||
dev: false
|
||||
|
||||
/fresh@0.5.2:
|
||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
@ -4,14 +4,14 @@ import Icon from "../Icon";
|
||||
import CreateServerModal from "./CreateServerModal";
|
||||
import JoinServerModal from "./JoinServerModal";
|
||||
import {
|
||||
Modal,
|
||||
ModalActionItem,
|
||||
ModalCloseWrapper,
|
||||
ModalContainer,
|
||||
ModalHeaderText,
|
||||
ModalSubHeaderText,
|
||||
ModalWrapper,
|
||||
ModelContentContainer,
|
||||
} from "./ModalComponents";
|
||||
import { AnimatedModalProps } from "./ModalRenderer";
|
||||
|
||||
export const ModalHeader = styled.div`
|
||||
padding: 16px;
|
||||
@ -39,66 +39,58 @@ const JoinButton = styled(ModalActionItem)`
|
||||
}
|
||||
`;
|
||||
|
||||
function AddServerModal() {
|
||||
function AddServerModal(props: AnimatedModalProps) {
|
||||
const { openModal, closeModal } = useModals();
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalWrapper>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
<Modal {...props}>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Add a Guild</ModalHeaderText>
|
||||
<ModalSubHeaderText>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</ModalSubHeaderText>
|
||||
</ModalHeader>
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Add a Guild</ModalHeaderText>
|
||||
<ModalSubHeaderText>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</ModalSubHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
<CreateButton
|
||||
variant="filled"
|
||||
size="med"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
openModal(CreateServerModal);
|
||||
}}
|
||||
>
|
||||
Create a Guild
|
||||
</CreateButton>
|
||||
<ModelContentContainer>
|
||||
<CreateButton
|
||||
variant="filled"
|
||||
size="med"
|
||||
onClick={() => {
|
||||
openModal(CreateServerModal);
|
||||
}}
|
||||
>
|
||||
Create a Guild
|
||||
</CreateButton>
|
||||
|
||||
<JoinButton
|
||||
variant="outlined"
|
||||
size="med"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
openModal(JoinServerModal);
|
||||
}}
|
||||
>
|
||||
Join a Guild
|
||||
</JoinButton>
|
||||
</ModelContentContainer>
|
||||
</ModalWrapper>
|
||||
</ModalContainer>
|
||||
<JoinButton
|
||||
variant="outlined"
|
||||
size="med"
|
||||
onClick={() => {
|
||||
openModal(JoinServerModal);
|
||||
}}
|
||||
>
|
||||
Join a Guild
|
||||
</JoinButton>
|
||||
</ModelContentContainer>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -10,17 +10,16 @@ import { messageFromFieldError } from "../../utils/messageFromFieldError";
|
||||
import { Input, InputErrorText, InputLabel, InputWrapper, LabelWrapper } from "../AuthComponents";
|
||||
import { TextDivider } from "../Divider";
|
||||
import Icon from "../Icon";
|
||||
import AddServerModal from "./AddServerModal";
|
||||
import {
|
||||
Modal,
|
||||
ModalActionItem,
|
||||
ModalCloseWrapper,
|
||||
ModalContainer,
|
||||
ModalFooter,
|
||||
ModalHeaderText,
|
||||
ModalSubHeaderText,
|
||||
ModalWrapper,
|
||||
ModelContentContainer,
|
||||
} from "./ModalComponents";
|
||||
import { AnimatedModalProps } from "./ModalRenderer";
|
||||
|
||||
export const ModalHeader = styled.div`
|
||||
margin-bottom: 30px;
|
||||
@ -67,18 +66,14 @@ type FormValues = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
function CreateServerModal() {
|
||||
function CreateServerModal(props: AnimatedModalProps) {
|
||||
const app = useAppStore();
|
||||
const logger = useLogger("CreateServerModal");
|
||||
const { openModal, closeModal } = useModals();
|
||||
const { openModal, closeModal, closeAllModals } = useModals();
|
||||
const [selectedFile, setSelectedFile] = React.useState<File>();
|
||||
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@ -103,7 +98,7 @@ function CreateServerModal() {
|
||||
})
|
||||
.then((r) => {
|
||||
navigate(`/channels/${r.id}`);
|
||||
closeModal();
|
||||
closeAllModals();
|
||||
})
|
||||
.catch((r) => {
|
||||
if ("message" in r) {
|
||||
@ -138,117 +133,114 @@ function CreateServerModal() {
|
||||
});
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalWrapper>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
<Modal {...props}>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeAllModals}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Customize your guild</ModalHeaderText>
|
||||
<ModalSubHeaderText>
|
||||
Give your new guild a personality with a name and an icon. You can always change it later.
|
||||
</ModalSubHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
<UploadIcon>
|
||||
<IconContainer>
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="m 39.88 78.32 c 4.0666 0 8.0467 -0.648 11.8282 -1.9066 l -0.9101 -2.7331 c -3.4906 1.1606 -7.1626 1.7597 -10.921 1.7597 v 2.88 m 17.4528 -4.3056 c 3.5539 -1.8749 6.7824 -4.3142 9.5645 -7.2115 l -2.0765 -1.993 c -2.569 2.6755 -5.5526 4.9277 -8.833 6.6586 l 1.345 2.5459 m 13.4208 -11.9434 c 2.2752 -3.3091 4.0061 -6.9638 5.1178 -10.8346 l -2.7677 -0.7949 c -1.0253 3.5712 -2.6237 6.9437 -4.7232 9.9965 l 2.3731 1.633 m 6.2899 -16.5917 c 0.1814 -1.4918 0.2765 -2.9981 0.2794 -4.5158 c 0 -2.5805 -0.2448 -5.063 -0.7344 -7.4966 l -2.8224 0.5674 c 0.4522 2.2464 0.6797 4.5389 0.6797 6.9264 c -0.0029 1.3997 -0.0893 2.7936 -0.2592 4.1702 l 2.8598 0.3514 m -2.1254 -17.8243 c -1.4198 -3.7642 -3.4416 -7.2662 -5.976 -10.3824 l -2.2349 1.8173 c 2.3386 2.8771 4.2048 6.1114 5.5181 9.5818 l 2.6928 -1.0166 m -10.1923 -14.783 c -3.0038 -2.6669 -6.4195 -4.8384 -10.1146 -6.4195 l -1.1347 2.6467 c 3.4099 1.4602 6.5635 3.4646 9.337 5.927 l 1.9123 -2.1542 m -15.7334 -8.3117 c -2.9146 -0.7286 -5.927 -1.1059 -8.9827 -1.1174 c -1.0886 0 -2.065 0.0374 -3.0499 0.1123 l 0.2218 2.8714 c 0.9101 -0.0691 1.8115 -0.1037 2.8224 -0.1037 c 2.8195 0.0086 5.5987 0.3571 8.2915 1.031 l 0.6998 -2.7936 m -17.9654 -0.0634 c -3.9197 0.9504 -7.6406 2.5286 -11.0362 4.6627 l 1.5322 2.4394 c 3.1363 -1.9699 6.5693 -3.4243 10.1837 -4.3027 l -0.6797 -2.7994 m -15.889 8.2886 c -3.0154 2.6554 -5.5872 5.7888 -7.6118 9.2506 l 2.4883 1.4515 c 1.8691 -3.2026 4.2451 -6.0883 7.0301 -8.5421 l -1.9037 -2.16 m -10.1952 14.6189 c -1.4371 3.7238 -2.2723 7.6723 -2.4595 11.7274 l 2.8771 0.1325 c 0.1728 -3.744 0.9446 -7.3843 2.2694 -10.823 l -2.687 -1.0368 m -2.2464 17.8618 c 0.4694 4.0205 1.5811 7.9027 3.2832 11.52 l 2.6064 -1.224 c -1.5696 -3.3408 -2.5978 -6.9235 -3.0298 -10.633 l -2.8598 0.3341 m 6.2698 16.7443 c 2.2694 3.3149 5.0573 6.2467 8.2541 8.6688 l 1.7453 -2.2925 c -2.952 -2.2464 -5.5267 -4.9565 -7.6205 -8.015 l -2.376 1.6272 m 13.4352 11.9923 c 3.5424 1.872 7.3699 3.168 11.3558 3.8246 l 0.4666 -2.8426 c -3.6806 -0.6048 -7.2086 -1.8 -10.4774 -3.528 l -1.3478 2.5459 m 17.3376 4.3229 c 0.0691 0 0.0691 0 0.1382 0 v -2.88 c -0.0634 0 -0.0634 0 -0.1267 0 l -0.0115 2.88"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M40 29C37.794 29 36 30.794 36 33C36 35.207 37.794 37 40 37C42.206 37 44 35.207 44 33C44 30.795 42.206 29 40 29Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M48 26.001H46.07C45.402 26.001 44.777 25.667 44.406 25.111L43.594 23.891C43.223 23.335 42.598 23 41.93 23H38.07C37.402 23 36.777 23.335 36.406 23.89L35.594 25.11C35.223 25.667 34.598 26 33.93 26H32C30.895 26 30 26.896 30 28V39C30 40.104 30.895 41 32 41H48C49.104 41 50 40.104 50 39V28C50 26.897 49.104 26.001 48 26.001ZM40 39C36.691 39 34 36.309 34 33C34 29.692 36.691 27 40 27C43.309 27 46 29.692 46 33C46 36.31 43.309 39 40 39Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M24.6097 52.712V47.72H22.5457V52.736C22.5457 53.792 22.0777 54.404 21.1417 54.404C20.2177 54.404 19.7377 53.78 19.7377 52.712V47.72H17.6737V52.724C17.6737 55.04 19.0897 56.132 21.1177 56.132C23.1217 56.132 24.6097 55.016 24.6097 52.712ZM26.0314 56H28.0834V53.252H28.6114C30.6154 53.252 31.9474 52.292 31.9474 50.42C31.9474 48.62 30.7114 47.72 28.6954 47.72H26.0314V56ZM29.9554 50.456C29.9554 51.308 29.4514 51.704 28.5394 51.704H28.0594V49.268H28.5754C29.4874 49.268 29.9554 49.664 29.9554 50.456ZM37.8292 56L37.5532 54.224H35.0092V47.72H32.9572V56H37.8292ZM45.9558 51.848C45.9558 49.292 44.4078 47.564 42.0078 47.564C39.6078 47.564 38.0478 49.304 38.0478 51.872C38.0478 54.428 39.6078 56.156 41.9838 56.156C44.3958 56.156 45.9558 54.404 45.9558 51.848ZM43.8918 51.86C43.8918 53.504 43.1958 54.548 41.9958 54.548C40.8078 54.548 40.0998 53.504 40.0998 51.86C40.0998 50.216 40.8078 49.172 41.9958 49.172C43.1958 49.172 43.8918 50.216 43.8918 51.86ZM52.2916 56.084L54.3676 55.748L51.4876 47.684H49.2316L46.2556 56H48.2716L48.8236 54.284H51.6916L52.2916 56.084ZM50.2516 49.796L51.1756 52.676H49.3156L50.2516 49.796ZM62.5174 51.848C62.5174 49.388 61.0174 47.72 58.1374 47.72H55.2814V56H58.1854C60.9814 56 62.5174 54.308 62.5174 51.848ZM60.4534 51.86C60.4534 53.636 59.5414 54.404 58.0774 54.404H57.3334V49.316H58.0774C59.4814 49.316 60.4534 50.12 60.4534 51.86Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
<IconInput
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
name="icon"
|
||||
accept="image/*"
|
||||
onChange={onIconChange}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
<FileInput
|
||||
role="button"
|
||||
// disabled until I get the motiviation to not make it shit, I don't really want to use an invisible input
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
></FileInput>
|
||||
</IconContainer>
|
||||
</UploadIcon>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Customize your guild</ModalHeaderText>
|
||||
<ModalSubHeaderText>
|
||||
Give your new guild a personality with a name and an icon. You can always change it later.
|
||||
</ModalSubHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
<UploadIcon>
|
||||
<IconContainer>
|
||||
<svg width="80" height="80" viewBox="0 0 80 80" fill="none">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="m 39.88 78.32 c 4.0666 0 8.0467 -0.648 11.8282 -1.9066 l -0.9101 -2.7331 c -3.4906 1.1606 -7.1626 1.7597 -10.921 1.7597 v 2.88 m 17.4528 -4.3056 c 3.5539 -1.8749 6.7824 -4.3142 9.5645 -7.2115 l -2.0765 -1.993 c -2.569 2.6755 -5.5526 4.9277 -8.833 6.6586 l 1.345 2.5459 m 13.4208 -11.9434 c 2.2752 -3.3091 4.0061 -6.9638 5.1178 -10.8346 l -2.7677 -0.7949 c -1.0253 3.5712 -2.6237 6.9437 -4.7232 9.9965 l 2.3731 1.633 m 6.2899 -16.5917 c 0.1814 -1.4918 0.2765 -2.9981 0.2794 -4.5158 c 0 -2.5805 -0.2448 -5.063 -0.7344 -7.4966 l -2.8224 0.5674 c 0.4522 2.2464 0.6797 4.5389 0.6797 6.9264 c -0.0029 1.3997 -0.0893 2.7936 -0.2592 4.1702 l 2.8598 0.3514 m -2.1254 -17.8243 c -1.4198 -3.7642 -3.4416 -7.2662 -5.976 -10.3824 l -2.2349 1.8173 c 2.3386 2.8771 4.2048 6.1114 5.5181 9.5818 l 2.6928 -1.0166 m -10.1923 -14.783 c -3.0038 -2.6669 -6.4195 -4.8384 -10.1146 -6.4195 l -1.1347 2.6467 c 3.4099 1.4602 6.5635 3.4646 9.337 5.927 l 1.9123 -2.1542 m -15.7334 -8.3117 c -2.9146 -0.7286 -5.927 -1.1059 -8.9827 -1.1174 c -1.0886 0 -2.065 0.0374 -3.0499 0.1123 l 0.2218 2.8714 c 0.9101 -0.0691 1.8115 -0.1037 2.8224 -0.1037 c 2.8195 0.0086 5.5987 0.3571 8.2915 1.031 l 0.6998 -2.7936 m -17.9654 -0.0634 c -3.9197 0.9504 -7.6406 2.5286 -11.0362 4.6627 l 1.5322 2.4394 c 3.1363 -1.9699 6.5693 -3.4243 10.1837 -4.3027 l -0.6797 -2.7994 m -15.889 8.2886 c -3.0154 2.6554 -5.5872 5.7888 -7.6118 9.2506 l 2.4883 1.4515 c 1.8691 -3.2026 4.2451 -6.0883 7.0301 -8.5421 l -1.9037 -2.16 m -10.1952 14.6189 c -1.4371 3.7238 -2.2723 7.6723 -2.4595 11.7274 l 2.8771 0.1325 c 0.1728 -3.744 0.9446 -7.3843 2.2694 -10.823 l -2.687 -1.0368 m -2.2464 17.8618 c 0.4694 4.0205 1.5811 7.9027 3.2832 11.52 l 2.6064 -1.224 c -1.5696 -3.3408 -2.5978 -6.9235 -3.0298 -10.633 l -2.8598 0.3341 m 6.2698 16.7443 c 2.2694 3.3149 5.0573 6.2467 8.2541 8.6688 l 1.7453 -2.2925 c -2.952 -2.2464 -5.5267 -4.9565 -7.6205 -8.015 l -2.376 1.6272 m 13.4352 11.9923 c 3.5424 1.872 7.3699 3.168 11.3558 3.8246 l 0.4666 -2.8426 c -3.6806 -0.6048 -7.2086 -1.8 -10.4774 -3.528 l -1.3478 2.5459 m 17.3376 4.3229 c 0.0691 0 0.0691 0 0.1382 0 v -2.88 c -0.0634 0 -0.0634 0 -0.1267 0 l -0.0115 2.88"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M40 29C37.794 29 36 30.794 36 33C36 35.207 37.794 37 40 37C42.206 37 44 35.207 44 33C44 30.795 42.206 29 40 29Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M48 26.001H46.07C45.402 26.001 44.777 25.667 44.406 25.111L43.594 23.891C43.223 23.335 42.598 23 41.93 23H38.07C37.402 23 36.777 23.335 36.406 23.89L35.594 25.11C35.223 25.667 34.598 26 33.93 26H32C30.895 26 30 26.896 30 28V39C30 40.104 30.895 41 32 41H48C49.104 41 50 40.104 50 39V28C50 26.897 49.104 26.001 48 26.001ZM40 39C36.691 39 34 36.309 34 33C34 29.692 36.691 27 40 27C43.309 27 46 29.692 46 33C46 36.31 43.309 39 40 39Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
<path
|
||||
d="M24.6097 52.712V47.72H22.5457V52.736C22.5457 53.792 22.0777 54.404 21.1417 54.404C20.2177 54.404 19.7377 53.78 19.7377 52.712V47.72H17.6737V52.724C17.6737 55.04 19.0897 56.132 21.1177 56.132C23.1217 56.132 24.6097 55.016 24.6097 52.712ZM26.0314 56H28.0834V53.252H28.6114C30.6154 53.252 31.9474 52.292 31.9474 50.42C31.9474 48.62 30.7114 47.72 28.6954 47.72H26.0314V56ZM29.9554 50.456C29.9554 51.308 29.4514 51.704 28.5394 51.704H28.0594V49.268H28.5754C29.4874 49.268 29.9554 49.664 29.9554 50.456ZM37.8292 56L37.5532 54.224H35.0092V47.72H32.9572V56H37.8292ZM45.9558 51.848C45.9558 49.292 44.4078 47.564 42.0078 47.564C39.6078 47.564 38.0478 49.304 38.0478 51.872C38.0478 54.428 39.6078 56.156 41.9838 56.156C44.3958 56.156 45.9558 54.404 45.9558 51.848ZM43.8918 51.86C43.8918 53.504 43.1958 54.548 41.9958 54.548C40.8078 54.548 40.0998 53.504 40.0998 51.86C40.0998 50.216 40.8078 49.172 41.9958 49.172C43.1958 49.172 43.8918 50.216 43.8918 51.86ZM52.2916 56.084L54.3676 55.748L51.4876 47.684H49.2316L46.2556 56H48.2716L48.8236 54.284H51.6916L52.2916 56.084ZM50.2516 49.796L51.1756 52.676H49.3156L50.2516 49.796ZM62.5174 51.848C62.5174 49.388 61.0174 47.72 58.1374 47.72H55.2814V56H58.1854C60.9814 56 62.5174 54.308 62.5174 51.848ZM60.4534 51.86C60.4534 53.636 59.5414 54.404 58.0774 54.404H57.3334V49.316H58.0774C59.4814 49.316 60.4534 50.12 60.4534 51.86Z"
|
||||
fill="currentColor"
|
||||
></path>
|
||||
</svg>
|
||||
<IconInput
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
name="icon"
|
||||
accept="image/*"
|
||||
onChange={onIconChange}
|
||||
<form>
|
||||
<InputContainer>
|
||||
<LabelWrapper error={!!errors.name}>
|
||||
<InputLabel>Guild Name</InputLabel>
|
||||
{errors.name && (
|
||||
<InputErrorText>
|
||||
<>
|
||||
<TextDivider>-</TextDivider>
|
||||
{errors.name.message}
|
||||
</>
|
||||
</InputErrorText>
|
||||
)}
|
||||
</LabelWrapper>
|
||||
<InputWrapper>
|
||||
<Input
|
||||
autoFocus
|
||||
{...register("name", { required: true })}
|
||||
placeholder="Guild Name"
|
||||
error={!!errors.name}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<FileInput
|
||||
role="button"
|
||||
// disabled until I get the motiviation to not make it shit, I don't really want to use an invisible input
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
></FileInput>
|
||||
</IconContainer>
|
||||
</UploadIcon>
|
||||
</InputWrapper>
|
||||
</InputContainer>
|
||||
</form>
|
||||
</ModelContentContainer>
|
||||
|
||||
<form>
|
||||
<InputContainer>
|
||||
<LabelWrapper error={!!errors.name}>
|
||||
<InputLabel>Guild Name</InputLabel>
|
||||
{errors.name && (
|
||||
<InputErrorText>
|
||||
<>
|
||||
<TextDivider>-</TextDivider>
|
||||
{errors.name.message}
|
||||
</>
|
||||
</InputErrorText>
|
||||
)}
|
||||
</LabelWrapper>
|
||||
<InputWrapper>
|
||||
<Input
|
||||
autoFocus
|
||||
{...register("name", { required: true })}
|
||||
placeholder="Guild Name"
|
||||
error={!!errors.name}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</InputWrapper>
|
||||
</InputContainer>
|
||||
</form>
|
||||
</ModelContentContainer>
|
||||
<ModalFooter>
|
||||
<ModalActionItem variant="filled" size="med" onClick={onSubmit} disabled={isLoading}>
|
||||
Create
|
||||
</ModalActionItem>
|
||||
|
||||
<ModalFooter>
|
||||
<ModalActionItem variant="filled" size="med" onClick={onSubmit} disabled={isLoading}>
|
||||
Create
|
||||
</ModalActionItem>
|
||||
|
||||
<ModalActionItem
|
||||
variant="link"
|
||||
size="min"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
openModal(AddServerModal);
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</ModalActionItem>
|
||||
</ModalFooter>
|
||||
</ModalWrapper>
|
||||
</ModalContainer>
|
||||
<ModalActionItem
|
||||
variant="link"
|
||||
size="min"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</ModalActionItem>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,14 @@ import { useModals } from "@mattjennings/react-modal-stack";
|
||||
import styled from "styled-components";
|
||||
import Icon from "../Icon";
|
||||
import {
|
||||
Modal,
|
||||
ModalActionItem,
|
||||
ModalCloseWrapper,
|
||||
ModalContainer,
|
||||
ModalFooter,
|
||||
ModalHeaderText,
|
||||
ModalWrapper,
|
||||
ModelContentContainer,
|
||||
} from "./ModalComponents";
|
||||
import { AnimatedModalProps } from "./ModalRenderer";
|
||||
|
||||
export const ModalHeader = styled.div`
|
||||
padding: 16px;
|
||||
@ -25,52 +25,46 @@ const SubmitButton = styled(ModalActionItem)`
|
||||
}
|
||||
`;
|
||||
|
||||
function ForgotPasswordModal() {
|
||||
const { openModal, closeModal } = useModals();
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
function ForgotPasswordModal(props: AnimatedModalProps) {
|
||||
const { closeModal } = useModals();
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalWrapper>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
<Modal {...props}>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Instructions Sent</ModalHeaderText>
|
||||
</ModalHeader>
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Instructions Sent</ModalHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
We sent instructions to change your password to user@example.com, please check both your inbox and
|
||||
spam folder.
|
||||
</ModelContentContainer>
|
||||
<ModelContentContainer>
|
||||
We sent instructions to change your password to user@example.com, please check both your inbox and spam
|
||||
folder.
|
||||
</ModelContentContainer>
|
||||
|
||||
<ModalFooter>
|
||||
<SubmitButton variant="filled" size="med" onClick={closeModal}>
|
||||
Okay
|
||||
</SubmitButton>
|
||||
</ModalFooter>
|
||||
</ModalWrapper>
|
||||
</ModalContainer>
|
||||
<ModalFooter>
|
||||
<SubmitButton variant="filled" size="med" onClick={closeModal}>
|
||||
Okay
|
||||
</SubmitButton>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -11,15 +11,15 @@ import { TextDivider } from "../Divider";
|
||||
import Icon from "../Icon";
|
||||
import AddServerModal from "./AddServerModal";
|
||||
import {
|
||||
Modal,
|
||||
ModalActionItem,
|
||||
ModalCloseWrapper,
|
||||
ModalContainer,
|
||||
ModalFooter,
|
||||
ModalHeaderText,
|
||||
ModalSubHeaderText,
|
||||
ModalWrapper,
|
||||
ModelContentContainer,
|
||||
} from "./ModalComponents";
|
||||
import { AnimatedModalProps } from "./ModalRenderer";
|
||||
|
||||
export const ModalHeader = styled.div`
|
||||
padding: 16px;
|
||||
@ -34,16 +34,12 @@ type FormValues = {
|
||||
code: string;
|
||||
};
|
||||
|
||||
function JoinServerModal() {
|
||||
function JoinServerModal(props: AnimatedModalProps) {
|
||||
const logger = useLogger("JoinServerModal");
|
||||
const { openModal, closeModal } = useModals();
|
||||
const { openModal, closeAllModals } = useModals();
|
||||
const app = useAppStore();
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (!open) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@ -59,7 +55,7 @@ function JoinServerModal() {
|
||||
.post<never, { guild_id: string; channel_id: string }>(Routes.invite(code))
|
||||
.then((r) => {
|
||||
navigate(`/channels/${r.guild_id}/${r.channel_id}`);
|
||||
closeModal();
|
||||
closeAllModals();
|
||||
})
|
||||
.catch((r) => {
|
||||
if ("message" in r) {
|
||||
@ -94,81 +90,78 @@ function JoinServerModal() {
|
||||
});
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalWrapper>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
<Modal {...props}>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeAllModals}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Join a Guild</ModalHeaderText>
|
||||
<ModalSubHeaderText>Enter an invite below to join an existing guild.</ModalSubHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
<form>
|
||||
<InviteInputContainer>
|
||||
<LabelWrapper error={!!errors.code}>
|
||||
<InputLabel>Invite Link</InputLabel>
|
||||
|
||||
{errors.code && (
|
||||
<InputErrorText>
|
||||
<>
|
||||
<TextDivider>-</TextDivider>
|
||||
{errors.code.message}
|
||||
</>
|
||||
</InputErrorText>
|
||||
)}
|
||||
</LabelWrapper>
|
||||
<Input
|
||||
{...register("code", { required: true })}
|
||||
placeholder="https://app.spacebar.chat/invite/cool-guild"
|
||||
type="text"
|
||||
maxLength={9999}
|
||||
required
|
||||
error={!!errors.code}
|
||||
disabled={isLoading}
|
||||
autoFocus
|
||||
minLength={6}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
</InviteInputContainer>
|
||||
</form>
|
||||
</ModelContentContainer>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Join a Guild</ModalHeaderText>
|
||||
<ModalSubHeaderText>Enter an invite below to join an existing guild.</ModalSubHeaderText>
|
||||
</ModalHeader>
|
||||
<ModalFooter>
|
||||
<ModalActionItem variant="filled" size="med" onClick={onSubmit}>
|
||||
Join Guild
|
||||
</ModalActionItem>
|
||||
|
||||
<ModelContentContainer>
|
||||
<form>
|
||||
<InviteInputContainer>
|
||||
<LabelWrapper error={!!errors.code}>
|
||||
<InputLabel>Invite Link</InputLabel>
|
||||
|
||||
{errors.code && (
|
||||
<InputErrorText>
|
||||
<>
|
||||
<TextDivider>-</TextDivider>
|
||||
{errors.code.message}
|
||||
</>
|
||||
</InputErrorText>
|
||||
)}
|
||||
</LabelWrapper>
|
||||
<Input
|
||||
{...register("code", { required: true })}
|
||||
placeholder="https://app.spacebar.chat/invite/cool-guild"
|
||||
type="text"
|
||||
maxLength={9999}
|
||||
required
|
||||
error={!!errors.code}
|
||||
disabled={isLoading}
|
||||
autoFocus
|
||||
minLength={6}
|
||||
/>
|
||||
</InviteInputContainer>
|
||||
</form>
|
||||
</ModelContentContainer>
|
||||
|
||||
<ModalFooter>
|
||||
<ModalActionItem variant="filled" size="med" onClick={onSubmit}>
|
||||
Join Guild
|
||||
</ModalActionItem>
|
||||
|
||||
<ModalActionItem
|
||||
variant="link"
|
||||
size="min"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
openModal(AddServerModal);
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</ModalActionItem>
|
||||
</ModalFooter>
|
||||
</ModalWrapper>
|
||||
</ModalContainer>
|
||||
<ModalActionItem
|
||||
variant="link"
|
||||
size="min"
|
||||
onClick={() => {
|
||||
openModal(AddServerModal);
|
||||
}}
|
||||
>
|
||||
Back
|
||||
</ModalActionItem>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -6,14 +6,14 @@ import { useAppStore } from "../../stores/AppStore";
|
||||
import Guild from "../../stores/objects/Guild";
|
||||
import Icon from "../Icon";
|
||||
import {
|
||||
Modal,
|
||||
ModalActionItem,
|
||||
ModalCloseWrapper,
|
||||
ModalContainer,
|
||||
ModalFooter,
|
||||
ModalHeaderText,
|
||||
ModalWrapper,
|
||||
ModelContentContainer,
|
||||
} from "./ModalComponents";
|
||||
import { AnimatedModalProps } from "./ModalRenderer";
|
||||
|
||||
export const ModalHeader = styled.div`
|
||||
padding: 16px;
|
||||
@ -47,7 +47,7 @@ interface Props {
|
||||
guild: Guild;
|
||||
}
|
||||
|
||||
function LeaveServerModal({ guild }: Props) {
|
||||
function LeaveServerModal(props: Props & AnimatedModalProps) {
|
||||
const app = useAppStore();
|
||||
const { closeModal } = useModals();
|
||||
const navigate = useNavigate();
|
||||
@ -57,7 +57,7 @@ function LeaveServerModal({ guild }: Props) {
|
||||
}
|
||||
|
||||
const handleLeaveServer = () => {
|
||||
app.rest.delete(Routes.userGuild(guild.id)).finally(() => {
|
||||
app.rest.delete(Routes.userGuild(props.guild.id)).finally(() => {
|
||||
closeModal();
|
||||
// navigate to @me
|
||||
navigate("channels/@me");
|
||||
@ -65,68 +65,66 @@ function LeaveServerModal({ guild }: Props) {
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalWrapper>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Leave {guild.name}</ModalHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
<span>
|
||||
Are you sure you want to leave <b>{guild.name}</b>? You won't be able to rejoin this server
|
||||
unless you are re-invited.
|
||||
</span>
|
||||
</ModelContentContainer>
|
||||
|
||||
<ModalFooter
|
||||
<Modal {...props}>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<CancelButton
|
||||
variant="link"
|
||||
size="med"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</CancelButton>
|
||||
|
||||
<LeaveButton
|
||||
variant="outlined"
|
||||
size="med"
|
||||
onClick={handleLeaveServer}
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
backgroundColor: "var(--danger)",
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
>
|
||||
Leave
|
||||
</LeaveButton>
|
||||
</ModalFooter>
|
||||
</ModalWrapper>
|
||||
</ModalContainer>
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
|
||||
<ModalHeader>
|
||||
<ModalHeaderText>Leave {props.guild.name}</ModalHeaderText>
|
||||
</ModalHeader>
|
||||
|
||||
<ModelContentContainer>
|
||||
<span>
|
||||
Are you sure you want to leave <b>{props.guild.name}</b>? You won't be able to rejoin this server
|
||||
unless you are re-invited.
|
||||
</span>
|
||||
</ModelContentContainer>
|
||||
|
||||
<ModalFooter
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<CancelButton
|
||||
variant="link"
|
||||
size="med"
|
||||
onClick={() => {
|
||||
closeModal();
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</CancelButton>
|
||||
|
||||
<LeaveButton
|
||||
variant="outlined"
|
||||
size="med"
|
||||
onClick={handleLeaveServer}
|
||||
style={{
|
||||
backgroundColor: "var(--danger)",
|
||||
}}
|
||||
>
|
||||
Leave
|
||||
</LeaveButton>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { type StackedModalProps } from "@mattjennings/react-modal-stack";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
/**
|
||||
* Main container for all modals, handles the background overlay and positioning
|
||||
*/
|
||||
export const ModalContainer = styled.div`
|
||||
export const ModalBase = styled(motion.div)`
|
||||
z-index: 100;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -13,22 +16,22 @@ export const ModalContainer = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: black;
|
||||
opacity: 0.85;
|
||||
}
|
||||
// &::before {
|
||||
// content: "";
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// bottom: 0;
|
||||
// background-color: black;
|
||||
// opacity: 0.85;
|
||||
// }
|
||||
`;
|
||||
|
||||
/**
|
||||
* Wrapper for modal content, handles the sizing and positioning
|
||||
*/
|
||||
export const ModalWrapper = styled.div<{ full?: boolean }>`
|
||||
export const ModalWrapper = styled(motion.div)<{ full?: boolean }>`
|
||||
width: ${(props) => (props.full ? "100%" : "440px")};
|
||||
height: ${(props) => (props.full ? "100%" : "auto")};
|
||||
border-radius: 4px;
|
||||
@ -175,3 +178,35 @@ export const ModalFullContent = styled.div`
|
||||
align-items: flex-start;
|
||||
background-color: var(--background-primary);
|
||||
`;
|
||||
|
||||
interface ModalProps extends StackedModalProps {
|
||||
children: React.ReactNode;
|
||||
full?: boolean;
|
||||
}
|
||||
|
||||
export function Modal(props: ModalProps) {
|
||||
return (
|
||||
<AnimatePresence>
|
||||
{props.open && (
|
||||
<ModalBase
|
||||
variants={{
|
||||
show: {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
},
|
||||
hide: {
|
||||
opacity: 0,
|
||||
scale: 0,
|
||||
},
|
||||
}}
|
||||
initial="hide"
|
||||
animate="show"
|
||||
exit="hide"
|
||||
{...props}
|
||||
>
|
||||
<ModalWrapper full={props.full}>{props.children}</ModalWrapper>
|
||||
</ModalBase>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
}
|
||||
|
76
src/components/modals/ModalRenderer.tsx
Normal file
76
src/components/modals/ModalRenderer.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import { ModalStackValue } from "@mattjennings/react-modal-stack";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
import React from "react";
|
||||
|
||||
export type AnimatedModalProps = {
|
||||
open: boolean;
|
||||
};
|
||||
|
||||
function ModalRenderer({ stack }: ModalStackValue) {
|
||||
const [displayedStack, setDisplayedStack] = React.useState(stack);
|
||||
const [isOpen, setOpen] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log(stack.length, displayedStack.length);
|
||||
// we're opening the first modal, so update the stack right away
|
||||
if (stack.length === 1 && displayedStack.length === 0) {
|
||||
setOpen(true);
|
||||
setDisplayedStack(stack);
|
||||
}
|
||||
// stack updated, trigger a dismissal of the current modal
|
||||
else {
|
||||
setOpen(false);
|
||||
}
|
||||
}, [stack]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<AnimatePresence>
|
||||
{stack.length > 0 && (
|
||||
<motion.div
|
||||
style={{
|
||||
zIndex: 90,
|
||||
position: `fixed`,
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
background: "rgba(0,0,0,0.8)",
|
||||
}}
|
||||
variants={{
|
||||
show: { opacity: 1 },
|
||||
hide: { opacity: 0 },
|
||||
}}
|
||||
initial="hide"
|
||||
animate="show"
|
||||
exit="hide"
|
||||
/>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{displayedStack.map((modal, index) => (
|
||||
<modal.component
|
||||
key={index}
|
||||
open={index === displayedStack.length - 1 && isOpen}
|
||||
onAnimationComplete={() => {
|
||||
// set open state for next modal
|
||||
if (stack.length > 0) {
|
||||
setOpen(true);
|
||||
} else {
|
||||
setOpen(false);
|
||||
}
|
||||
|
||||
// update displayed stack
|
||||
// setTimeout is a hack to prevent a warning about updating state
|
||||
// in an unmounted component (I can't figure out why it happens, or why this fixes it)
|
||||
setTimeout(() => setDisplayedStack(stack));
|
||||
|
||||
modal.props?.onAnimationComplete?.();
|
||||
}}
|
||||
{...modal.props}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default ModalRenderer;
|
@ -1,39 +1,38 @@
|
||||
import { useModals } from "@mattjennings/react-modal-stack";
|
||||
import Icon from "../Icon";
|
||||
import { ModalCloseWrapper, ModalContainer, ModalFullContent, ModalFullSidebar, ModalWrapper } from "./ModalComponents";
|
||||
import { Modal, ModalCloseWrapper, ModalFullContent, ModalFullSidebar } from "./ModalComponents";
|
||||
import { AnimatedModalProps } from "./ModalRenderer";
|
||||
|
||||
function SettingsModal() {
|
||||
function SettingsModal(props: AnimatedModalProps) {
|
||||
const { closeModal } = useModals();
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalWrapper full>
|
||||
<ModalFullSidebar>Sidebar</ModalFullSidebar>
|
||||
<Modal full {...props}>
|
||||
<ModalFullSidebar>Sidebar</ModalFullSidebar>
|
||||
|
||||
<ModalFullContent>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
<ModalFullContent>
|
||||
<ModalCloseWrapper>
|
||||
<button
|
||||
onClick={closeModal}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
background: "none",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="mdiClose"
|
||||
size={1}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
color: "var(--text)",
|
||||
}}
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
Content
|
||||
</ModalFullContent>
|
||||
</ModalWrapper>
|
||||
</ModalContainer>
|
||||
/>
|
||||
</button>
|
||||
</ModalCloseWrapper>
|
||||
Content
|
||||
</ModalFullContent>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@ import { ModalStack } from "@mattjennings/react-modal-stack";
|
||||
import ReactDOM from "react-dom/client";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import App from "./App";
|
||||
import ModalRenderer from "./components/modals/ModalRenderer";
|
||||
import { ContextMenuContextProvider } from "./contexts/ContextMenuContext";
|
||||
import Theme from "./contexts/Theme";
|
||||
import "./index.css";
|
||||
@ -22,7 +23,7 @@ import "./index.css";
|
||||
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
|
||||
root.render(
|
||||
<BrowserRouter>
|
||||
<ModalStack>
|
||||
<ModalStack renderModals={ModalRenderer}>
|
||||
<ContextMenuContextProvider>
|
||||
<App />
|
||||
</ContextMenuContextProvider>
|
||||
|
Loading…
Reference in New Issue
Block a user