diff --git a/package.json b/package.json index ec6c62fa..8f1de115 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "publishConfig": { "registry": "https://registry.npmjs.org" }, - "version": "1.0.111", + "version": "1.0.112", "description": "Formation is a comprehensive component library powered by React, Styled Components, and CSS variables for creating apps and websites that demand responsive, unified cross-platform experiences.", "resolutions": { "string-width": "^4", diff --git a/src/components/Dialog/Dialog.stories.tsx b/src/components/Dialog/Dialog.stories.tsx index 49b56270..b887e363 100644 --- a/src/components/Dialog/Dialog.stories.tsx +++ b/src/components/Dialog/Dialog.stories.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { ComponentStory, ComponentMeta } from '@storybook/react' -import { Button, Gap, Dialog, DialogProvider, Break, dialogController } from '../../internal' +import { Button, Gap, Dialog, DialogProvider, useDialog } from '../../internal' export default { title: 'Input/Dialog', @@ -8,87 +8,47 @@ export default { decorators: [(Story) => ( + )], } as ComponentMeta -const Template: ComponentStory = (args: any) => { - const [response, setResponse] = useState('') - return ( - - -

{response !== null ? response.toString() : ''}

- -
- ) -} -export const Alert = Template.bind({}) -Alert.args = { - label: 'Open Alert Dialog', - message: 'To create visuals with cameras, please give AVsync.LIVE permission to access your camera. Go to chrome://settings/content/camera', - mode: 'alert' -} - -export const Confirm = Template.bind({}) -Confirm.args = { - label: 'Open Confirm Dialog', - message: 'Are you sure you want to delete this Scene?', - mode: 'confirm' -} - -export const Prompt = Template.bind({}) -Prompt.args = { - label: 'Open Prompt Dialog', - message: 'Choose a title for your blog post', - placeholder: 'Title', // Add a placeholder for the prompt input - mode: 'prompt' -} - -const AllDialogsTemplate: ComponentStory = () => { - const [confirmResponse, setConfirmResponse] = useState('') - const [promptResponse, setPromptResponse] = useState('') +const UseDialogTemplate: ComponentStory = () => { + const { openDialog } = useDialog() + const [response, setResponse] = useState('') return ( - - +

{response !== null ? `Alert response: ${response}` : ''}

- -

{confirmResponse !== null ? confirmResponse.toString() : ''}

+

{response !== null ? `Confirm response: ${response}` : ''}

- - - -

{promptResponse !== null ? promptResponse.toString() : ''}

- +

{response !== null ? `Prompt response: ${response}` : ''}

) } -export const AllDialogs = AllDialogsTemplate.bind({}) \ No newline at end of file +export const UseDialog = UseDialogTemplate.bind({}) \ No newline at end of file diff --git a/src/components/Dialog/Dialog.tsx b/src/components/Dialog/Dialog.tsx index 46922e89..09707072 100644 --- a/src/components/Dialog/Dialog.tsx +++ b/src/components/Dialog/Dialog.tsx @@ -1,9 +1,7 @@ -import React, { useState, useEffect, useRef, createContext, useContext } from 'react' +import React, { useState, createContext, useContext, useRef, useEffect } from 'react' import styled, { css, keyframes } from 'styled-components' import { Gap, Button, TextInput, Break, Fit } from '../../internal' -import { dialogController } from './DialogController' - interface DialogConfig { mode: 'alert' | 'confirm' | 'prompt' message: string @@ -26,62 +24,22 @@ const DialogContext = createContext({ export const useDialog = () => useContext(DialogContext) -interface DialogProviderProps { - children: React.ReactNode -} +export const DialogProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [isOpen, setIsOpen] = useState(false) + const [config, setConfig] = useState() -/** - * `Dialog` is a customizable dialog component for creating alert, confirm, and prompt dialogs. - * It provides an interactive way to display information and collect user input in a web application. - * This component supports three modes: 'alert' for simple messages, 'confirm' for yes/no decisions, - * and 'prompt' for user input. The dialog appears with an animation and can be configured with a - * message, placeholder text for prompts, and a callback function to handle user responses. - * - * The dialog also features an outside click detection to provide a shaking effect, enhancing the - * user experience by drawing attention to the dialog when users click outside of it. - * - * @component - * @param {function} useDialog - Custom hook to manage dialog state and configuration. - * @param {function} useState - React useState hook for managing internal state. - * @param {function} useEffect - React useEffect hook for handling side effects. - * @param {function} useRef - React useRef hook for referencing DOM elements. - * - * @example - * // To create and use a confirm dialog with a custom message and callback function - * const { showDialog, hideDialog } = useDialog(); - * showDialog({ - * mode: 'confirm', - * message: 'Are you sure?', - * callback: (response) => console.log(`User response: ${response}`) - * }); - * - * @example - * // To create and use a prompt dialog for user input - * const { showDialog } = useDialog(); - * showDialog({ - * mode: 'prompt', - * message: 'Enter your name:', - * placeholder: 'Name', - * callback: (input) => console.log(`User input: ${input}`) - * }); - */ -export const DialogProvider: React.FC = ({ children }) => { - const [dialogState, setDialogState] = useState({ - isOpen: false, - openDialog: (config) => { - setDialogState(prevState => ({ ...prevState, isOpen: true, config })) - }, - closeDialog: () => { - setDialogState(prevState => ({ ...prevState, isOpen: false, config: undefined })) - } - }) + const openDialog = (config: DialogConfig) => { + setConfig(config) + setIsOpen(true) + } - // Set the actual functions in the DialogController - dialogController.setOpenDialogFunction(dialogState.openDialog) - dialogController.setCloseDialogFunction(dialogState.closeDialog) + const closeDialog = () => { + setIsOpen(false) + setConfig(undefined) + } return ( - + {children} ) @@ -112,71 +70,7 @@ export const Dialog = () => { const handleClose = (value: boolean | string | null) => { closeDialog() - if (config && config.callback) { - config.callback(value) - } - } - - useEffect(() => { - const blurFocusableElements = () => { - const focusableElements = document.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])') - focusableElements.forEach((element: any) => { - if (typeof element.blur === 'function') { - element.blur() - } - }) - } - - if (isOpen && config?.mode !== 'prompt') { - blurFocusableElements() - } - }, [isOpen, config]) - - useEffect(() => { - const handleKeyDown = (event: KeyboardEvent) => { - if (event.key === 'Enter' && isOpen && config?.mode !== 'prompt') { - event.preventDefault() - switch (config?.mode) { - case 'confirm': - handleClose(true) - break - case 'alert': - handleClose(null) - break - default: - break - } - } - } - - document.addEventListener('keydown', handleKeyDown) - - return () => { - document.removeEventListener('keydown', handleKeyDown) - } - }, [isOpen, config, handleClose]) - - const renderButtons = () => { - switch (config?.mode) { - case 'alert': - return - case 'confirm': - return ( - <> - - - - ) - case 'prompt': - return ( - <> - - - - ) - default: - return null - } + config?.callback?.(value) } useEffect(() => { @@ -205,7 +99,21 @@ export const Dialog = () => { ) } - {renderButtons()} + {config && ( + config.mode === 'alert' ? : + config.mode === 'confirm' ? ( + <> + + + + ) : + config.mode === 'prompt' ? ( + <> + + + + ) : null + )} diff --git a/src/components/Dialog/DialogController.ts b/src/components/Dialog/DialogController.ts deleted file mode 100644 index dc07bd05..00000000 --- a/src/components/Dialog/DialogController.ts +++ /dev/null @@ -1,27 +0,0 @@ -interface DialogConfig { - mode: 'alert' | 'confirm' | 'prompt' - message: string - callback?: (value: boolean | string | null) => void - placeholder?: string -} - -type OpenDialogFunctionType = (config: DialogConfig) => void -type CloseDialogFunctionType = () => void - -let openDialogFunction: OpenDialogFunctionType = () => {} -let closeDialogFunction: CloseDialogFunctionType = () => {} - -export const dialogController = { - setOpenDialogFunction(fn: OpenDialogFunctionType) { - openDialogFunction = fn - }, - setCloseDialogFunction(fn: CloseDialogFunctionType) { - closeDialogFunction = fn - }, - openDialog(config: DialogConfig) { - openDialogFunction(config) - }, - closeDialog() { - closeDialogFunction() - }, -} \ No newline at end of file diff --git a/src/components/NavSpaces/NavSpaces.stories.tsx b/src/components/NavSpaces/NavSpaces.stories.tsx index 3d49cc54..92eddd1d 100644 --- a/src/components/NavSpaces/NavSpaces.stories.tsx +++ b/src/components/NavSpaces/NavSpaces.stories.tsx @@ -311,7 +311,7 @@ const Template: ComponentStory = args => { export const Positions = Template.bind({}) Positions.args = { label: 'Position title', - sidebarWidth: '320px', + sidebarWidth: '380px', secondaryTopNav: [ { title: 'People', diff --git a/src/index.ts b/src/index.ts index 80dec710..cac5552c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -86,7 +86,6 @@ export { QRCode } from './components/QRCode/QRCode' export { Small } from './components/Small/Small' export { TextArea } from './components/TextArea/TextArea' export { Dialog, DialogProvider, useDialog } from './components/Dialog/Dialog' -export { dialogController } from './components/Dialog/DialogController' export { DropCorners } from './components/DragAndDrop/DropCorners' import { Link } from './components/Link/Link' diff --git a/src/internal.ts b/src/internal.ts index 6f38c5df..77cbff8c 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -127,7 +127,6 @@ export { ProgressTimer } from './components/ProgressTimer/ProgressTimer' export { Control } from './components/Control/Control' export { MultiSelect } from './components/MultiSelect/MultiSelect' export { Dialog, DialogProvider, useDialog } from './components/Dialog/Dialog' -export { dialogController } from './components/Dialog/DialogController' // Tissue (2) export { Navigation } from './components/Navigation/Navigation'