From 57546af1e792d06c790cd8e0857721f5dac29d39 Mon Sep 17 00:00:00 2001 From: Edouard Wautier <4435185+Duncid@users.noreply.github.com> Date: Mon, 13 Nov 2023 10:51:33 +0100 Subject: [PATCH] New modal (#2493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Some love for the modals * Polish * Bumping Sparkle version * Cleaning config * Cleaning config --------- Co-authored-by: édouard wautier --- front/tailwind.config.js | 9 ++ sparkle/package-lock.json | 4 +- sparkle/package.json | 2 +- sparkle/src/components/Modal.tsx | 186 +++++++++++--------------- sparkle/src/stories/Modal.stories.tsx | 94 +++++++------ sparkle/tailwind.config.js | 9 ++ 6 files changed, 147 insertions(+), 157 deletions(-) diff --git a/front/tailwind.config.js b/front/tailwind.config.js index 614f0499ef90..fa3e81e437db 100644 --- a/front/tailwind.config.js +++ b/front/tailwind.config.js @@ -8,6 +8,15 @@ module.exports = { objektiv: ["'objektiv-mk1'", "sans-serif"], }, extend: { + scale: { + 99: ".99", + }, + dropShadow: { + md: "0 4px 3px rgba(0, 0, 0, 0.25)", + lg: "0 10px 15px rgba(0, 0, 0, 0.16)", + xl: "0 20px 25px rgba(0, 0, 0, 0.14)", + "2xl": "0 25px 50px rgba(0, 0, 0, 0.12)", + }, zIndex: { 60: "60", }, diff --git a/sparkle/package-lock.json b/sparkle/package-lock.json index e17473f876cd..842019bb416d 100644 --- a/sparkle/package-lock.json +++ b/sparkle/package-lock.json @@ -1,12 +1,12 @@ { "name": "@dust-tt/sparkle", - "version": "0.2.36", + "version": "0.2.37", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@dust-tt/sparkle", - "version": "0.2.36", + "version": "0.2.37", "license": "ISC", "dependencies": { "@headlessui/react": "^1.7.17" diff --git a/sparkle/package.json b/sparkle/package.json index 3ae5bfbf943b..08de2acd6383 100644 --- a/sparkle/package.json +++ b/sparkle/package.json @@ -1,6 +1,6 @@ { "name": "@dust-tt/sparkle", - "version": "0.2.36", + "version": "0.2.37", "scripts": { "build": "rm -rf dist && rollup -c", "build:with-tw-base": "rollup -c --environment INCLUDE_TW_BASE:true", diff --git a/sparkle/src/components/Modal.tsx b/sparkle/src/components/Modal.tsx index 001218847f5f..85c3d8aba029 100644 --- a/sparkle/src/components/Modal.tsx +++ b/sparkle/src/components/Modal.tsx @@ -1,16 +1,64 @@ import { Dialog, Transition } from "@headlessui/react"; import React, { Fragment } from "react"; -import { assertNever, classNames } from "@sparkle/lib/utils"; +import { classNames } from "@sparkle/lib/utils"; import { BarHeader, BarHeaderButtonBarProps } from "./BarHeader"; import { Button, ButtonProps } from "./Button"; -const RIGHT_SIDE_MODAL_WIDTH = { - normal: "sm:s-w-[448px]", - wide: "sm:s-w-[50rem]", - "ultra-wide": "sm:s-w-[80rem]", -} as const; +export enum ModalType { + Dialogue = "dialogue", + FullScreen = "full-screen", + Side = "side", +} + +const variantToType = { + "full-screen": ModalType.FullScreen, + "side-md": ModalType.Side, + dialogue: ModalType.Dialogue, + "side-sm": ModalType.Side, +}; +const variantSize = { + "full-screen": "", + "side-md": "sm:s-w-[50rem]", + dialogue: "", + "side-sm": "sm:s-w-[448px]", +}; + +const modalStyles = { + [ModalType.Side]: { + containerClasses: "s-flex s-h-full s-p-0 s-justify-end sm:s-p-2", + panelClasses: + "s-m-0 s-h-full s-max-h-full s-w-full s-max-w-full s-r-0 sm:s-rounded-xl s-drop-shadow-xl s-border s-border-structure-100", + transitionEnterFrom: "s-opacity-0 -s-translate-x-20 s-scale-99", + transitionEnterTo: "s-opacity-100 s-translate-x-0 s-scale-100", + transitionLeaveFrom: "s-opacity-100 s-translate-x-0 s-scale-100", + transitionLeaveTo: "s-opacity-0 s-translate-x-20 s-scale-99", + innerContainerClasses: "s-h-full s-overflow-y-auto", + }, + [ModalType.FullScreen]: { + containerClasses: + "s-flex s-items-center s-justify-center s-h-full s-p-0 s-drop-shadow-xl", + panelClasses: "s-m-0 s-h-full s-max-h-full s-w-full s-max-w-full s-r-0", + transitionEnterFrom: "s-opacity-0 s-translate-y-4 s-scale-95 s-rounded-xl", + transitionEnterTo: "s-opacity-100 s-translate-y-0 s-scale-100 s-rounded-0", + transitionLeaveFrom: + "s-opacity-100 s-translate-y-0 s-scale-100 s-rounded-0", + transitionLeaveTo: "s-opacity-0 s-translate-y-4 s-scale-95 s-rounded-xl", + innerContainerClasses: "s-h-full s-overflow-y-auto", + }, + [ModalType.Dialogue]: { + containerClasses: + "s-flex s-items-center s-justify-center s-min-h-full s-p-4", + panelClasses: + "s-w-full sm:s-w-[448px] overflow-hidden s-drop-shadow-2xl s-rounded-xl s-border s-border-structure-100", + transitionEnterFrom: "s-opacity-0 s-translate-y-4 s-scale-95", + transitionEnterTo: "s-opacity-100 s-translate-y-0 s-scale-100", + transitionLeaveFrom: "s-opacity-100 s-translate-y-0 s-scale-100", + transitionLeaveTo: "s-opacity-0 s-translate-y-4 s-scale-95", + innerContainerClasses: "", + }, +}; type ModalProps = { isOpen: boolean; @@ -23,15 +71,10 @@ type ModalProps = { isSaving?: boolean; savingLabel?: string; title?: string; -} & ( - | { - type: "right-side"; - width?: keyof typeof RIGHT_SIDE_MODAL_WIDTH; - } - | { - type: "full-screen" | "default"; - } -); + variant?: "full-screen" | "side-sm" | "side-md" | "dialogue"; +}; + +const getModalClasses = (type: ModalType) => modalStyles[type] || {}; export function Modal({ isOpen, @@ -44,8 +87,9 @@ export function Modal({ isSaving, savingLabel, title, - ...props + variant = "side-sm", }: ModalProps) { + const type = variantToType[variant]; const buttonBarProps: BarHeaderButtonBarProps = hasChanged ? { variant: "validate", @@ -60,117 +104,47 @@ export function Modal({ onClose: onClose, }; - const justifyClass = (() => { - switch (props.type) { - case "right-side": - return "s-justify-end"; - - case "full-screen": - case "default": - return "s-justify-center"; - - default: - throw assertNever(props); - } - })(); - - const outerContainerClasses = (() => { - switch (props.type) { - case "right-side": - case "full-screen": - return "s-h-full s-p-0"; - - case "default": - return "s-min-h-full s-p-4"; - - default: - throw assertNever(props); - } - })(); - - const transitionEnterLeaveClasses = (() => { - switch (props.type) { - case "right-side": - return "s-translate-x-full"; - - case "full-screen": - case "default": - return "s-translate-y-4 sm:s-translate-y-0 sm:s-scale-95"; - - default: - throw assertNever(props); - } - })(); - - const panelClasses = (() => { - switch (props.type) { - case "right-side": - return classNames( - "s-m-0 s-h-full s-max-h-full s-w-full s-max-w-full", - RIGHT_SIDE_MODAL_WIDTH[props.width || "normal"] - ); - - case "full-screen": - return "s-m-0 s-h-full s-max-h-full s-w-full s-max-w-full"; - - case "default": - return "s-max-w-2xl s-rounded-lg s-shadow-xl lg:s-w-1/2"; - - default: - throw assertNever(props); - } - })(); - - const innerContainerClasses = (() => { - switch (props.type) { - case "right-side": - case "full-screen": - return "s-h-full s-overflow-y-auto"; - - case "default": - return ""; - - default: - throw assertNever(props); - } - })(); + const { + containerClasses, + transitionEnterFrom, + transitionLeaveFrom, + transitionEnterTo, + transitionLeaveTo, + panelClasses, + innerContainerClasses, + } = getModalClasses(type); return ( -
+
-
+
{ const [inputValue, setInputValue] = useState("initial value"); const [isRightSideWideModalOpen, setIsRightSideWideModalOpen] = useState(false); - const [isRightSideUltraWideModalOpen, setIsRightSideUltraWideModalOpen] = - useState(false); return ( { onClose={() => setIsOpenNoActionNoChange(false)} hasChanged={false} title="Modal title" - type="default" + variant="dialogue" >
I'm the modal content
setIsRightSideModalOpen(false)} - type="right-side" + variant="side-sm" title="Modal title" hasChanged={inputValue !== "initial value"} > @@ -67,7 +65,7 @@ export const ModalExample = () => { }} saveLabel="Save (custom name possible)" hasChanged={true} - type="default" + variant="dialogue" >
I'm the modal content
@@ -75,7 +73,7 @@ export const ModalExample = () => { isOpen={isFullScreenModalOpen} onClose={() => setIsFullScreenModalOpen(false)} hasChanged={true} - type="full-screen" + variant="full-screen" title="Modal title" >
I'm the modal content
@@ -84,7 +82,7 @@ export const ModalExample = () => { isOpen={isFullScreenModalOverflowOpen} onClose={() => setIsFullScreenModalOverflowOpen(false)} hasChanged={true} - type="full-screen" + variant="full-screen" title="Modal title" >
@@ -104,54 +102,54 @@ export const ModalExample = () => { isOpen={isRightSideWideModalOpen} onClose={() => setIsRightSideWideModalOpen(false)} hasChanged={false} - type="right-side" + variant="side-md" title="Modal title" - width="wide" >
I'm the modal content, and I am wide
- - setIsRightSideUltraWideModalOpen(false)} - hasChanged={false} - type="right-side" - title="Modal title" - width="ultra-wide" - > -
- I'm the modal content, and I am ultra-wide +
+ I'm the modal content +
+
+ I'm the modal content +
+
+ I'm the modal content +
+
+ I'm the modal content
-
); }; diff --git a/sparkle/tailwind.config.js b/sparkle/tailwind.config.js index b6d84cffa254..5fe5bdb30e02 100644 --- a/sparkle/tailwind.config.js +++ b/sparkle/tailwind.config.js @@ -26,6 +26,15 @@ module.exports = { objektiv: ["'objektiv-mk1'", "sans-serif"], }, extend: { + scale: { + 99: ".99", + }, + dropShadow: { + md: "0 4px 3px rgba(0, 0, 0, 0.25)", + lg: "0 10px 15px rgba(0, 0, 0, 0.16)", + xl: "0 20px 25px rgba(0, 0, 0, 0.14)", + "2xl": "0 25px 50px rgba(0, 0, 0, 0.12)", + }, keyframes: { "move-square": { "0%": {