@hauke5/dialog
v1.2.10
Published
React Dialog component
Downloads
329
Maintainers
Readme
Dialog Component
A NextJS component package for simplifying and standardizing the use of user dialog in NextJS apps. Supported features include:
- movable modal dialog box
- fully typed interface, with typing support in the dialog result for accessing items and buttons by name.
- configurable side effects: allow changes to an items value effect the values of other items or the the disabledness of buttons
Installation
Either 1) Use npx
to run an example of the dialog component in a NextJS app:
$ npx hauke5/dialogexample
This will start a next development server. Point a browser to http://localhost:3001/dialog to run the app.
Or 2) use git clone
to install the component along with a usage example app in NextJS:
$ git clone https://github.com/Hauke5/DialogExample.git
This will install the required typescript components and support libraries, along with the usage example in app/dialog/DialogExample.tsx
To run the example manually, follow the git clone
command with:
cd DialogExample # change into the newly created folder
npm i # install dependencies
npm run dev # run the development server
Again, point a browser to http://localhost:3001/dialog to run the app.
Usage
Defining a Dialog
A dialog is defined by the DialogConfig
structure, consisting of
- a leading title to explain the purpose of the dialog to the user
- an optional description, accepting a ReactNode
- an optional list of dialog items, each consisting of a
DialogItemConfig
structure - and a list of buttons to perform actions.
If not provided by the DialogConfig
, A Cancel
button will be automatically added by the component.
A simplest version of a dialog configuration might look like this:
{
title: 'Simple Dialog Example:',
buttons:[
{ id:'OK'},
]
}
The ids
for items and buttons are by default used as their labels. Alternatively, an optional label
field can be specified:
{
title: 'Example:',
items:[
{ id:'ValidItem', type:'number', initial: 0, label:'Enter Valid Number'}},
],
buttons:[
{ id:'OkButton', label:'OK'}},
]
}
Integrating Dialogs into Components
Add a <Dialog>
component at the end of React nodes in your component. It takes a callback function through which it provides an open
function that can be stored in a Ref
. <Dialog>
internally uses the html <dialog>
tag, so stays invisible until opened, which happens below in runDialog
when it is triggered by clicking on the Open Dialog
button.
To open the dialog in code, call the open
function provided by <Dialog>
with the dialog content structure and await the result. It provides
- the
actionName
, i.e. id of the button pressed (or, in some cases, an ID for some predefined dialog action such as double-clicking a file in a file selector field) - an
items
object containing each of the item IDs, providing- the
type
of the item's value - the current
value
of the item, - the
isDefault
status:false
if the user has directly manipulated the item, andtrue
if the value is either theinitial
value or the result of asideEffect
.
- the
function Component() {
const openDialog = useRef<OpenDialog>()
return <div>
<button onClick={runDialog}>Open Dialog</button>
<Dialog open={open=>openDialog.current=open} />
</div>
async function runDialog() {
if (openDialog.current) {
// open the dialog with `dialogConfog` as content
const result = await openDialog.current({
title: 'Example:',
items:[
{ id:'ValidItem', type:'number', initial: 0, label:'Enter Valid Number'}},
],
buttons:[
{ id:'OkButton', label:'OK'}},
]
})
// process the result action and item values
if (result.actionName==='Ok') {
const numberItem = result.items.ValidItem
const number = numberItem.value as number
const isDefault = numberItem.isDefault
...
}
}
}
}
Defining Side effects
Side effects are actions performed on the elements of a dialog box while the user manipulates them. For example, the disabled state of a button or the initial value of a dialog element can be made to change depending on the value of one or more other elements.
Two types of side effects are available:
Disabling Buttons
Add a disable
function to a button configuration to tell the <Dialog>
whether the button should be active or disabled. The function will receive the same items
object as is returned when the dialog closes
The following example disables the OkButton
if the ValidItem
value is 0
...
buttons:[
{id:'OkButton', disable:(items) items.ValidItem.value===0},
]
...
Item SideEffects
Add a sideEffect
callback to an item configuration to have user-changes to the item value trigger updates to other items.
The callback will receive the current value
of the item, as well as a reference to the set of all items.
In the example below, a sideEffect will be called each time the ValidItem
number field changes. It will change the TextItem
field, if it hasn't been explicitely set by the user, to read invalid number
if the number value is 0
, and valid number
otherwise.
{
title: 'Example:',
elements:[
{ id:'ValidItem', type:'number', initial: 0, label:'Enter Valid Number:',
sideEffect:(value, items)=>items.TextItem.isDefault
? {TextItem: value!==0? 'valid number' : 'invalid number' }
: {}
}},
{ id:'TextItem', type:'text', initial: 'invalid', label:'Number Comment:'}},
],
buttons:[
{ id:'OkButton', label:'OK'}},
]
}
Complex Example:
{
title: 'Dialog Example With Side-Effects:',
description: <div style={{borderBottom:"1px solid gray"}}>
A <b>complex</b> dialog:
<ul className={styles.instructions}>
<li><b>Hint:</b>You can move the box by dragging it in the title field.</li>
<li>Side effects within the dialog:</li>
<ul className={styles.instructions}>
<li><b>Ok Disabled</b>: Enter a number other than <code>0</code> in the the <code>Number</code> field to enable the <code>Ok</code> button.</li>
<li><b>Field auto-values</b>:Changing the <code>Number</code> field automatically sets the <code>Text</code> field
to <code>valid</code> or <code>invalid</code> depending on the <code>Number</code> field value,
unless the user has manually changed it before</li>
</ul>
<li>Click the <code>Ok</code> button when available.
The left box below will reflect the values from the dialog.</li>
</ul>
</div>,
items:[
{id:'Date', type:'date', initial: new Date() },
{id:'Number', type:'number', initial: 0, label:'Enter Valid Number:', sideEffect:(value:number, items) =>
items.Text.isDefault
? {Text:(value !== 0)? 'valid number' : 'invalid number' }
: {}
},
{id:'Text', type:'text', initial: 'invalid', list:textFieldHistory},
{id:'Checkbox', type:'boolean',initial: false},
{id:'List', type:'select', initial: 'two', list:['one', 'two', 'three']},
],
buttons:[
{id:'Ok', disable:(values) => values.Number.value===0},
]
}