Skip to content

Commit

Permalink
Update modal popup design + update create branch modal
Browse files Browse the repository at this point in the history
  • Loading branch information
jaclarke committed Jul 30, 2024
1 parent c44c974 commit 65210aa
Show file tree
Hide file tree
Showing 15 changed files with 496 additions and 141 deletions.
50 changes: 36 additions & 14 deletions shared/common/hooks/useModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {createPortal} from "react-dom";
const modalContext = createContext<{
modal: JSX.Element | null;
openModal: (modal: JSX.Element | null, transition?: boolean) => () => void;
_modalOpen: boolean;
}>(null!);

export function ModalProvider({children}: PropsWithChildren<{}>) {
Expand Down Expand Up @@ -66,7 +67,11 @@ export function ModalProvider({children}: PropsWithChildren<{}>) {

return (
<modalContext.Provider
value={{modal: transitionState === false ? null : modal, openModal}}
value={{
modal: modal,
openModal,
_modalOpen: !!modal && transitionState !== false,
}}
>
{children}
<div
Expand All @@ -87,25 +92,42 @@ export function ModalProvider({children}: PropsWithChildren<{}>) {
);
}

export function useModal() {
return useContext(modalContext);
}

export function useCloseModal() {
const {openModal} = useContext(modalContext);
return () => openModal(null);
}

const modalPlaceholder = <></>;

export function useModal_v2(modal: JSX.Element) {
const {modal: placeholder, openModal} = useContext(modalContext);
export function useModal(): {
modal: JSX.Element | null;
openModal: (modal: JSX.Element | null, transition?: boolean) => () => void;
};
export function useModal(modal: JSX.Element): {
modal: JSX.Element | null;
openModal: (transition?: boolean) => () => void;
};
export function useModal(modal?: JSX.Element): {
modal: JSX.Element | null;
openModal:
| ((modal: JSX.Element | null, transition?: boolean) => () => void)
| ((transition?: boolean) => () => void);
} {
const ctx = useContext(modalContext);

if (!modal) {
return {
modal: ctx._modalOpen ? ctx.modal : null,
openModal: ctx.openModal,
};
}

return {
modal:
placeholder === modalPlaceholder
ctx.modal === modalPlaceholder
? createPortal(modal, document.getElementById("modal_target")!)
: null,
openModal: () => openModal(modalPlaceholder),
openModal: (transition?: boolean) =>
ctx.openModal(modalPlaceholder, transition),
};
}

export function useCloseModal() {
const {openModal} = useContext(modalContext);
return (transition?: boolean) => openModal(null, transition);
}
4 changes: 2 additions & 2 deletions shared/common/newui/button/button.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

&.primary {
// background: linear-gradient(180deg, #a565cd 0%, #9c56b4 100%);
background: #a565cd;
background: var(--buttonPrimaryBackground, #a565cd);
box-shadow: 0px 2px 4px 0px rgba(165, 100, 203, 0.25);
border: 0;
color: #fffffffa;
Expand All @@ -61,7 +61,7 @@
box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.16);

&.primary {
background: #6c4186;
background: var(--buttonPrimaryBackground, #6c4186);
box-shadow: 0px 2px 4px 0px rgba(73, 20, 104, 0.5);
border: 0;
color: #ffffffdb;
Expand Down
15 changes: 12 additions & 3 deletions shared/common/newui/button/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ export interface ButtonProps extends _BaseButtonProps {
onClick?: () => void;
}

export function Button({
function _Button({
className,
kind,
type,
children,
leftIcon,
rightIcon,
disabled,
loading,
...props
}: ButtonProps) {
}: ButtonProps & {type: "button" | "submit"}) {
return (
<button
type="button"
type={type}
className={cn(
styles.button,
{
Expand All @@ -48,6 +49,14 @@ export function Button({
);
}

export const Button = (props: ButtonProps) => (
<_Button {...props} type="button" />
);

export const SubmitButton = (props: Omit<ButtonProps, "onClick">) => (
<_Button {...props} type="submit" />
);

export interface LinkButtonProps extends _BaseButtonProps {
href: string;
target?: React.AnchorHTMLAttributes<HTMLAnchorElement>["target"];
Expand Down
5 changes: 5 additions & 0 deletions shared/common/newui/checkbox/checkbox.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@
}
}

&.disabled {
opacity: 0.5;
pointer-events: none;
}

@include darkTheme {
color: #ccc;

Expand Down
9 changes: 8 additions & 1 deletion shared/common/newui/checkbox/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ export interface CheckboxProps {
label?: string | JSX.Element;
checked: boolean;
onChange: (checked: boolean) => void;
disabled?: boolean;
}

export function Checkbox({
className,
label,
checked,
onChange,
disabled,
}: CheckboxProps) {
return (
<label className={cn(styles.checkbox, className)}>
<label
className={cn(styles.checkbox, className, {
[styles.disabled]: !!disabled,
})}
>
<input
type="checkbox"
checked={checked}
onChange={(e) => onChange(e.target.checked)}
disabled={disabled}
/>
<div className={styles.check} />
{label}
Expand Down
22 changes: 22 additions & 0 deletions shared/common/newui/fieldHeader/fieldHeader.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
.fieldHeader {
display: flex;
align-items: center;
gap: 4px;
font-size: 14px;
font-style: normal;
font-weight: 500;
line-height: 24px;
padding: 0 4px;
}

.optional {
opacity: 0.5;
margin-left: 6px;
font-weight: 450;
}

.headerNote {
color: var(--Grey50, #808080);
font-weight: 400;
margin-left: auto;
}
19 changes: 19 additions & 0 deletions shared/common/newui/fieldHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import styles from "./fieldHeader.module.scss";

export interface FieldHeaderProps {
label?: string | JSX.Element;
optional?: boolean;
headerNote?: string;
}

export function FieldHeader({label, optional, headerNote}: FieldHeaderProps) {
return (
<div className={styles.fieldHeader}>
{label}
{optional ? <span className={styles.optional}>(optional)</span> : null}
{headerNote ? (
<span className={styles.headerNote}>{headerNote}</span>
) : null}
</div>
);
}
2 changes: 2 additions & 0 deletions shared/common/newui/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
export * from "./button";
export * from "./checkbox";
export * from "./textInput";
export * from "./fieldHeader";
export * from "./select";
export * from "./icons";
export * from "./logos";
export * from "./infoTooltip";
export * from "./modal";
101 changes: 101 additions & 0 deletions shared/common/newui/modal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import {HTMLAttributes, PropsWithChildren} from "react";

import cn from "@edgedb/common/utils/classNames";

import styles from "./modal.module.scss";
import {CrossIcon, WarningIcon} from "../icons";

export interface ModalProps {
className?: string;
title: string;
subheading?: string;
onClose?: () => void;
noCloseOnOverlayClick?: boolean;
onSubmit?: () => void;
formError?: string | null;
footerButtons?: JSX.Element;
footerDetails?: JSX.Element;
footerExtra?: JSX.Element;
}

export function Modal({
className,
title,
subheading,
onClose,
noCloseOnOverlayClick,
onSubmit,
formError,
children,
footerDetails,
footerButtons,
footerExtra,
}: PropsWithChildren<ModalProps>) {
const El = onSubmit ? "form" : "div";

return (
<div
className={styles.modalOverlay}
onClick={
onClose && !noCloseOnOverlayClick
? (e) => {
if (e.target === e.currentTarget) {
onClose();
}
}
: undefined
}
>
<El
className={cn(styles.modal, className)}
onSubmit={
onSubmit
? (e) => {
e.preventDefault();
onSubmit();
}
: undefined
}
autoComplete="off"
>
<div className={styles.header}>
<div className={styles.headings}>
<div className={styles.title}>{title}</div>
{subheading ? (
<div className={styles.subheading}>{subheading}</div>
) : null}
</div>
<div
className={styles.closeButton}
onClick={onClose && (() => onClose())}
>
<CrossIcon />
</div>
</div>
{children}
{formError ? (
<div className={styles.formError}>
<WarningIcon />
<div>{formError}</div>
</div>
) : null}
{footerButtons || footerDetails ? (
<div className={styles.footer}>
{footerExtra}
<div className={styles.footerMain}>
<div className={styles.footerDetails}>{footerDetails}</div>
{footerButtons}
</div>
</div>
) : null}
</El>
</div>
);
}

export function ModalContent({
className,
...props
}: PropsWithChildren<HTMLAttributes<HTMLDivElement>>) {
return <div className={cn(styles.content, className)} {...props} />;
}
Loading

0 comments on commit 65210aa

Please sign in to comment.