Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions apps/spaces/src/components/modals/CreateQuizModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ export const CreateQuizModal: FunctionComponent<Props> = ({

const [title, handleTitle] = useState('')

const handleClose = () => {
setIsModalOpen(false);
handleTitle("");
};

return (
<Modal
isOpen={isModalOpen}
Expand All @@ -26,19 +31,16 @@ export const CreateQuizModal: FunctionComponent<Props> = ({
onPrimaryClick={() => {
onCreate(title)
setIsModalOpen(false);
handleTitle('')
}}
onSecondaryClick={() => {
setIsModalOpen(false)
handleTitle('')
}}
onSecondaryClick={handleClose}
onClose={handleClose}
>
<FormContent>
<TextInput
label="Quiz name"
value={title}
onChange={(e) => handleTitle(e.target.value)}
/>
<FormContent>
<TextInput
label="Quiz name"
value={title}
onChange={(e) => handleTitle(e.target.value)}
/>
</FormContent>
</Modal>
)
Expand Down
26 changes: 19 additions & 7 deletions packages/shira-ui/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React, { useEffect } from 'react';
import styled from 'styled-components';
import { Button } from '../Button';
import { SubHeading2 } from '../Typography';
import { useEscapeClose, useEnterSubmit } from '../../hooks';

export interface ModalProps {
isOpen: boolean;
Expand All @@ -17,6 +18,8 @@ export interface ModalProps {
onLeftClick?: () => void
leftButtonText?: string
className?: string;

onClose?: () => void;
}

export enum ModalType {
Expand All @@ -29,7 +32,6 @@ const modalTypeColors = {
'primary': '#849D29'
}


export const Modal: React.FC<ModalProps> = ({
isOpen,
title,
Expand All @@ -43,7 +45,8 @@ export const Modal: React.FC<ModalProps> = ({
onLeftClick,
leftButtonText,
className,
type = 'primary'
type = 'primary',
onClose
}) => {

useEffect(() => {
Expand All @@ -58,14 +61,24 @@ export const Modal: React.FC<ModalProps> = ({
};
}, [isOpen]);

useEscapeClose({
when: isOpen,
onClose: onClose ?? onSecondaryClick ?? (() => { }),
});

useEnterSubmit({
when: isOpen,
onEnter: onPrimaryClick,
});

if (!isOpen) return null;

return (
<>
<Overlay>
<ModalContainer className={className}>
<Header>
{ titleIcon }
{titleIcon}
<SubHeading2>{title}</SubHeading2>
</Header>

Expand All @@ -75,8 +88,8 @@ export const Modal: React.FC<ModalProps> = ({

<Footer>
<div>
{ onLeftClick && (
<Button
{onLeftClick && (
<Button
text={leftButtonText}
type='primary'
color={modalTypeColors[ModalType.Danger]}
Expand All @@ -85,7 +98,7 @@ export const Modal: React.FC<ModalProps> = ({
)}
</div>
<div>
{ onSecondaryClick && (
{onSecondaryClick && (
<Button
text={secondaryButtonText}
type="outline"
Expand All @@ -107,7 +120,6 @@ export const Modal: React.FC<ModalProps> = ({
);
};


const Overlay = styled.div`
position: fixed;
top: 0;
Expand Down
2 changes: 2 additions & 0 deletions packages/shira-ui/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useEscapeClose';
export * from './useEnterSubmit';
70 changes: 70 additions & 0 deletions packages/shira-ui/src/hooks/useEnterSubmit.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { useEffect } from "react";

type Options = {
when: boolean;
onEnter: () => void;
target?: Window | Document | HTMLElement | null;
preventDefault?: boolean;
stopPropagation?: boolean;
ignoreWhenTextarea?: boolean;
ignoreWithModifier?: boolean;
};

export function useEnterSubmit({
when,
onEnter,
target,
preventDefault = true,
stopPropagation = false,
ignoreWhenTextarea = true,
ignoreWithModifier = true,
}: Options) {
useEffect(() => {
if (!when) return;

const t =
target ??
(typeof window !== "undefined"
? window
: typeof document !== "undefined"
? document
: null);

if (!t) return;

const handler = (e: KeyboardEvent) => {
if (e.isComposing) return;
if (ignoreWithModifier && (e.altKey || e.ctrlKey || e.metaKey)) return;
if (e.repeat) return;

const keyIsEnter =
e.key === "Enter" || e.code === "Enter" || e.key === "NumpadEnter";

if (!keyIsEnter) return;

const active = (document && document.activeElement) as HTMLElement | null;
if (
ignoreWhenTextarea &&
active &&
active.tagName === "TEXTAREA"
) {
return;
}

if (preventDefault) e.preventDefault();
if (stopPropagation) e.stopPropagation();
onEnter();
};

t.addEventListener("keydown", handler);
return () => t.removeEventListener("keydown", handler);
}, [
when,
onEnter,
target,
preventDefault,
stopPropagation,
ignoreWhenTextarea,
ignoreWithModifier,
]);
}
32 changes: 32 additions & 0 deletions packages/shira-ui/src/hooks/useEscapeClose.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect } from "react";

type UseEscapeCloseOptions = {
when: boolean;
onClose: () => void;
target?: Document | HTMLElement | Window | null;
stopPropagation?: boolean;
};

export function useEscapeClose({
when,
onClose,
target,
stopPropagation = true,
}: UseEscapeCloseOptions) {
useEffect(() => {
if (!when) return;

const t = target ?? (typeof document !== "undefined" ? document : null);
if (!t) return;

const onKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape" || e.key === "Esc") {
if (stopPropagation) e.stopPropagation();
onClose();
}
};

t.addEventListener("keydown", onKeyDown);
return () => t.removeEventListener("keydown", onKeyDown);
}, [when, onClose, target, stopPropagation]);
}
1 change: 1 addition & 0 deletions packages/shira-ui/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// shira-ui/src/index.ts
export * from './components'
export * from './theme'
export * from './hooks';
export { default as styled, createGlobalStyle } from 'styled-components';