Skip to content

Commit b6f29b4

Browse files
committed
wip: modals
1 parent 283c5e5 commit b6f29b4

File tree

4 files changed

+230
-228
lines changed

4 files changed

+230
-228
lines changed

packages/webui/src/client/lib/ModalDialog.tsx

Lines changed: 104 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,24 @@ import FocusBounder from 'react-focus-bounder'
66
import { useTranslation } from 'react-i18next'
77

88
import ClassNames from 'classnames'
9-
// @ts-expect-error No types available
10-
import * as VelocityReact from 'velocity-react'
119
import { logger } from './logging'
1210
import * as _ from 'underscore'
1311
import { withTranslation } from 'react-i18next'
1412
import { Translated } from './ReactMeteorData/ReactMeteorData'
1513
import { EditAttribute, EditAttributeType, IEditAttributeBaseProps } from './EditAttribute'
1614
import { Settings } from '../lib/Settings'
1715

16+
import Modal from 'react-bootstrap/Modal'
17+
import Button from 'react-bootstrap/Button'
18+
1819
interface IModalDialogAttributes {
1920
show?: boolean
2021
title: string
2122
secondaryText?: string
2223
acceptText: string
2324
onAccept?: (e: SomeEvent, inputResult: ModalInputResult) => void
24-
onSecondary?: (e: SomeEvent, inputResult: ModalInputResult) => void
25-
onDiscard?: (e: SomeEvent, inputResult: ModalInputResult) => void
25+
onSecondary?: (e: SomeEvent | undefined, inputResult: ModalInputResult) => void
26+
onDiscard?: (e: SomeEvent | undefined, inputResult: ModalInputResult) => void
2627
inputs?: { [attribute: string]: ModalInput }
2728
warning?: boolean
2829
actions?: ModalAction[]
@@ -74,15 +75,15 @@ export function ModalDialog({
7475
onAccept?.(e, inputResult.current)
7576
}
7677

77-
function handleDiscard(e: SomeEvent) {
78+
function handleDiscard(e?: SomeEvent) {
7879
if (onDiscard) {
7980
onDiscard(e, inputResult.current)
8081
return
8182
}
8283
handleSecondary(e)
8384
}
8485

85-
function handleSecondary(e: SomeEvent) {
86+
function handleSecondary(e: SomeEvent | undefined) {
8687
onSecondary?.(e, inputResult.current)
8788
}
8889

@@ -101,16 +102,16 @@ export function ModalDialog({
101102
e.currentTarget.click()
102103
}
103104

104-
function onDialogKeyDown(e: React.KeyboardEvent<HTMLDialogElement>) {
105-
if (!(e.target instanceof HTMLDialogElement)) return
105+
function onDialogKeyDown(e: React.KeyboardEvent<HTMLDivElement>) {
106+
if (!(e.target instanceof HTMLDivElement)) return
106107
if (!isAcceptKey(e.code) && !isDismissKey(e.code)) return
107108

108109
e.preventDefault()
109110
e.stopPropagation()
110111
}
111112

112-
function onDialogKeyUp(e: React.KeyboardEvent<HTMLDialogElement>) {
113-
if (!(e.target instanceof HTMLDialogElement)) return
113+
function onDialogKeyUp(e: React.KeyboardEvent<HTMLDivElement>) {
114+
if (!(e.target instanceof HTMLDivElement)) return
114115
if (!isAcceptKey(e.code) && !isDismissKey(e.code)) return
115116
e.preventDefault()
116117
e.stopPropagation()
@@ -141,135 +142,106 @@ export function ModalDialog({
141142

142143
return (
143144
<Escape to="viewport">
144-
<VelocityReact.VelocityTransitionGroup
145-
enter={{ animation: 'fadeIn', easing: 'ease-out', duration: 250 }}
146-
runOnMount={true}
147-
>
148-
<div className="glass-pane">
149-
<FocusBounder>
150-
<div className="glass-pane-content">
151-
<VelocityReact.VelocityTransitionGroup
152-
enter={{
153-
animation: {
154-
translateY: [0, 100],
155-
opacity: [1, 0],
145+
<FocusBounder>
146+
<div onKeyDown={onDialogKeyDown} onKeyUp={onDialogKeyUp}>
147+
<Modal
148+
show={show}
149+
onHide={handleDiscard}
150+
onEscapeKeyDown={handleDiscard}
151+
backdrop="static"
152+
keyboard
153+
className={className}
154+
scrollable
155+
>
156+
<Modal.Header closeVariant="white" className={warning ? 'modal-header-warn' : 'modal-header-info'}>
157+
<Modal.Title className={'grid-buttons-right w-100'}>
158+
<h2>{title}</h2>
159+
160+
<button
161+
className="action-btn"
162+
onClick={handleDiscard}
163+
onKeyDown={preventClickOnEnter}
164+
onKeyUp={emulateClick}
165+
aria-label={t('Dismiss')}
166+
>
167+
<CoreIcons.NrkClose />
168+
</button>
169+
</Modal.Title>
170+
</Modal.Header>
171+
<Modal.Body>{children}</Modal.Body>
172+
<Modal.Footer>
173+
{inputs ? (
174+
<div className="title-box-inputs">
175+
{_.map(inputs, (input: ModalInput, attribute: string) => {
176+
return (
177+
<div className="title-box-input" key={attribute}>
178+
{input.text}
179+
<EditAttribute
180+
type={input.type}
181+
label={input.label}
182+
options={input.options}
183+
overrideDisplayValue={input.defaultValue}
184+
attribute={attribute}
185+
updateFunction={updateInput}
186+
/>
187+
</div>
188+
)
189+
})}
190+
</div>
191+
) : null}
192+
<div
193+
className={ClassNames(
194+
{
195+
'text-end': !secondaryText,
156196
},
157-
easing: 'spring',
158-
duration: 250,
159-
}}
160-
runOnMount={true}
197+
'modal-dialog-actions'
198+
)}
161199
>
162-
<dialog
163-
open={true}
164-
className={'border-box overlay-m ' + className || ''}
165-
role="alertdialog"
166-
onKeyUp={onDialogKeyUp}
167-
onKeyDown={onDialogKeyDown}
168-
>
169-
<div className={'flex-row ' + (warning ? 'warn' : 'info') + ' vertical-align-stretch tight-s'}>
170-
<div className="flex-col c12">
171-
<h2>{title}</h2>
172-
</div>
173-
<div className="flex-col horizontal-align-right vertical-align-middle">
174-
<p>
175-
<button
176-
className="action-btn"
177-
onClick={handleDiscard}
178-
onKeyDown={preventClickOnEnter}
179-
onKeyUp={emulateClick}
180-
aria-label={t('Dismiss')}
181-
>
182-
<CoreIcons.NrkClose />
183-
</button>
184-
</p>
185-
</div>
186-
</div>
187-
<div className="title-box-content">{children}</div>
188-
{inputs ? (
189-
<div className="title-box-inputs">
190-
{_.map(inputs, (input: ModalInput, attribute: string) => {
191-
return (
192-
<div className="title-box-input" key={attribute}>
193-
{input.text}
194-
<EditAttribute
195-
type={input.type}
196-
label={input.label}
197-
options={input.options}
198-
overrideDisplayValue={input.defaultValue}
199-
attribute={attribute}
200-
updateFunction={updateInput}
201-
/>
202-
</div>
203-
)
204-
})}
205-
</div>
206-
) : null}
207-
<div
208-
className={ClassNames(
209-
'mod',
210-
{
211-
'text-end': !secondaryText,
212-
},
213-
'modal-dialog-actions'
214-
)}
200+
{secondaryText && (
201+
<Button
202+
variant={discardAsPrimary ? (warning ? 'warn' : 'primary') : 'outline-secondary'}
203+
className={'discard-btn'}
204+
autoFocus={discardAsPrimary}
205+
onClick={handleSecondary}
206+
onKeyDown={preventClickOnEnter}
207+
onKeyUp={emulateClick}
215208
>
216-
{secondaryText && (
217-
<button
218-
className={ClassNames(
219-
'btn',
220-
discardAsPrimary ? 'btn-primary' : 'btn-secondary',
221-
'discard-btn',
222-
{ 'btn-warn': discardAsPrimary && warning }
223-
)}
224-
autoFocus={discardAsPrimary}
225-
onClick={handleSecondary}
226-
onKeyDown={preventClickOnEnter}
227-
onKeyUp={emulateClick}
228-
>
229-
{secondaryText}
230-
</button>
231-
)}
232-
{_.compact(
233-
_.map(actions || [], (action: ModalAction, i) => {
234-
if (!action) return null
235-
return (
236-
<button
237-
key={i}
238-
className={ClassNames(
239-
'btn right mrs',
240-
{
241-
'btn-secondary': !(action.classNames || '').match(/btn-/),
242-
},
243-
action.classNames
244-
)}
245-
onClick={(e) => handleAction(e, action.on)}
246-
onKeyDown={preventClickOnEnter}
247-
onKeyUp={emulateClick}
248-
>
249-
{action.label}
250-
</button>
251-
)
252-
})
253-
)}
254-
<button
255-
className={ClassNames('btn', !discardAsPrimary ? 'btn-primary' : 'btn-secondary', {
256-
right: secondaryText !== undefined,
257-
'btn-warn': !discardAsPrimary && warning,
258-
})}
259-
autoFocus={!discardAsPrimary}
260-
onClick={handleAccept}
209+
{secondaryText}
210+
</Button>
211+
)}
212+
{actions?.map((action, i) => {
213+
if (!action) return null
214+
215+
return (
216+
<Button
217+
key={i}
218+
variant={!(action.classNames || '').match(/btn-/) ? 'outline-secondary' : undefined}
219+
className={ClassNames('right mrs', action.classNames)}
220+
onClick={(e) => handleAction(e, action.on)}
261221
onKeyDown={preventClickOnEnter}
262222
onKeyUp={emulateClick}
263223
>
264-
{acceptText}
265-
</button>
266-
</div>
267-
</dialog>
268-
</VelocityReact.VelocityTransitionGroup>
269-
</div>
270-
</FocusBounder>
224+
{action.label}
225+
</Button>
226+
)
227+
})}
228+
<Button
229+
variant={!discardAsPrimary ? (warning ? 'warn' : 'primary') : 'outline-secondary'}
230+
className={ClassNames({
231+
right: secondaryText !== undefined,
232+
})}
233+
autoFocus={!discardAsPrimary}
234+
onClick={handleAccept}
235+
onKeyDown={preventClickOnEnter}
236+
onKeyUp={emulateClick}
237+
>
238+
{acceptText}
239+
</Button>
240+
</div>
241+
</Modal.Footer>
242+
</Modal>
271243
</div>
272-
</VelocityReact.VelocityTransitionGroup>
244+
</FocusBounder>
273245
</Escape>
274246
)
275247
}

packages/webui/src/client/styles/bootstrap-customise.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ $line-height-base: 1.2;
1414
$input-padding-y: 0.5rem;
1515

1616
$btn-disabled-opacity: 0.5;
17+
18+
$modal-header-padding: 0.5rem 1rem;
19+
$modal-md: 550px;

0 commit comments

Comments
 (0)