This repository has been archived by the owner on Oct 31, 2021. It is now read-only.
/
DialogBox.tsx
130 lines (110 loc) 路 3.62 KB
/
DialogBox.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import {JSXInternal} from '/preact/src/jsx';
import {singleton} from '@utils/preact-singleton';
import {cn} from '@utils/preact-utils';
import {uid} from '@utils/uid';
import {Component, h} from 'preact';
import styles from './DialogBox.module.scss';
export type DialogButtonSeverity = 'success' | 'warning' | 'error';
export type DialogButton = {
type?: DialogButtonSeverity;
icon?: string;
text: string;
};
export type Dialog = {
icon: string;
title: string;
description?: string;
buttons?: Array<DialogButton>;
};
type InternalDialogItem = {
dialog: Dialog;
resolve: (res: number) => void;
removing: boolean;
};
type Props = unknown;
type State = {
dialogs: Array<InternalDialogItem>;
};
export const DialogBox = singleton(class extends Component<Props, State> {
readonly state = {
dialogs: []
};
private get firstItem(): InternalDialogItem | null {
const {dialogs} = this.state;
return dialogs.length ? dialogs[0] : null;
}
private resolve(index: number) {
return () => {
const item = this.firstItem;
if (item) {
item.resolve(index);
item.removing = true;
this.setState({
dialogs: [
item,
...this.state.dialogs.slice(1)
]
});
// Wait until fade-out animation is over
setTimeout(() => {
this.setState({
dialogs: this.state.dialogs.slice(1)
});
}, 300);
}
};
}
public open(dialog: Dialog): Promise<number> {
return new Promise<number>(resolve => {
this.setState(props => ({
dialogs: [
...props.dialogs, {
dialog,
resolve,
removing: false
}
]
}));
});
}
render() {
const item = this.firstItem;
let dialog: JSXInternal.Element | string = '';
if (item) {
const {title, description, buttons} = item.dialog;
const describedby = uid('aria');
const labelledby = uid('aria');
dialog = <div role="dialog"
aria-labelledby={labelledby}
aria-describedby={describedby}
className={cn(styles.dialog, {
[styles.closing]: item.removing
})}>
<header>
<bc-icon name={item.dialog.icon}/>
<p id={labelledby}>{title}</p>
</header>
<p id={describedby}>{description}</p>
{buttons ?
<div className={styles.buttonBar}>
{buttons.map((v, i) => (
<button key={i}
onClick={this.resolve(i)}
data-severity={v.type || 'success'}>
{v.icon ? <bc-icon name={v.icon}/> : ''}
<span>{v.text}</span>
</button>
))}
</div> : ''
}
</div>;
}
return (
<div className={cn(styles.dialogBox, {
[styles.open]: this.state.dialogs.length > 0
})} role="dialog">
{dialog}
</div>
);
}
});