Line data Source code
1 : import * as React from 'react';
2 :
3 : import { Button, ButtonVariant } from '../../ui/foundations/button.js';
4 : import { Overlay, OverlaySize } from '../../ui/foundations/overlay.js';
5 : import type { ConnectionImportChoice, ConnectionImportPlanItem } from './session_connection_import.js';
6 : import { connectionParamsLabel } from '../../connections/connection_label.js';
7 :
8 : import * as styles from './session_import_conflict_dialog.module.css';
9 : import * as ownStyles from './session_connection_import_dialog.module.css';
10 :
11 : interface Props {
12 : /// The name of the imported session
13 : sessionName: string;
14 : /// The connections of the session that may consolidate with existing connections
15 : questions: ConnectionImportPlanItem[];
16 : /// Is the import running?
17 : busy: boolean;
18 : /// Import with the chosen connections, keyed by the connection id in the session
19 : onConfirm: (choices: Map<string, ConnectionImportChoice>) => void;
20 : /// Abort the import
21 : onCancel: () => void;
22 : }
23 :
24 : /// The value of a choice in the form
25 1 : function choiceValue(choice: ConnectionImportChoice): string {
26 1 : return choice.kind === 'link' ? `link:${choice.connectionId}` : 'separate';
27 1 : }
28 :
29 : /// Ask the user whether imported connections should use existing connections.
30 : /// Using an existing connection keeps settings in one place, the session then shares the connection
31 : /// with every other session that uses it.
32 1 : export function SessionConnectionImportDialog(props: Props): React.ReactElement {
33 1 : const titleId = React.useId();
34 1 : const descriptionId = React.useId();
35 1 : const dialogRef = React.useRef<HTMLElement>(null);
36 1 : const [choices, setChoices] = React.useState(() => new Map<string, ConnectionImportChoice>(props.questions.map(item => [
37 1 : item.ref.connectionId,
38 1 : item.decision.kind === 'ask' ? { kind: 'link', connectionId: item.decision.candidates[0].id } : { kind: 'separate' },
39 1 : ])));
40 1 : const cancel = React.useCallback(() => {
41 1 : if (!props.busy) props.onCancel();
42 1 : }, [props.busy, props.onCancel]);
43 :
44 1 : return (
45 1 : <Overlay centered width={OverlaySize.L} onEscape={cancel} onClickOutside={cancel} initialFocusRef={dialogRef}>
46 1 : <section
47 1 : ref={dialogRef}
48 1 : className={styles.dialog}
49 1 : role="dialog"
50 1 : aria-modal="true"
51 1 : aria-labelledby={titleId}
52 1 : aria-describedby={descriptionId}
53 1 : aria-busy={props.busy}
54 1 : tabIndex={-1}
55 : >
56 1 : <header className={styles.header}>
57 1 : <h2 id={titleId}>Use existing connections?</h2>
58 1 : </header>
59 1 : <div className={styles.body}>
60 1 : <p id={descriptionId} className={styles.description}>
61 1 : The session {props.sessionName} uses connections that look like connections you already have.
62 : Using an existing connection shares its settings with the sessions that already use it.
63 1 : </p>
64 1 : {props.questions.map(item => {
65 1 : if (item.decision.kind !== 'ask') return null;
66 1 : const current = choices.get(item.ref.connectionId);
67 1 : const name = `connection-${item.ref.connectionId}`;
68 1 : const options: Array<{ choice: ConnectionImportChoice; label: string }> = [
69 1 : ...item.decision.candidates.map(candidate => ({
70 1 : choice: { kind: 'link' as const, connectionId: candidate.id },
71 1 : label: `Use ${candidate.name ?? connectionParamsLabel(candidate.params)}`,
72 1 : })),
73 1 : { choice: { kind: 'separate' }, label: 'Keep as a new connection' },
74 1 : ];
75 1 : return (
76 1 : <fieldset key={item.ref.connectionId} className={ownStyles.connection} disabled={props.busy}>
77 1 : <legend className={ownStyles.connection_label}>
78 1 : {item.ref.name == null
79 1 : ? connectionParamsLabel(item.ref.params)
80 0 : : `${item.ref.name} ยท ${connectionParamsLabel(item.ref.params)}`}
81 1 : </legend>
82 1 : {options.map(option => (
83 1 : <label key={choiceValue(option.choice)} className={ownStyles.option}>
84 1 : <input
85 1 : type="radio"
86 1 : name={name}
87 1 : value={choiceValue(option.choice)}
88 1 : checked={current != null && choiceValue(current) === choiceValue(option.choice)}
89 1 : onChange={() => setChoices(prev => new Map(prev).set(item.ref.connectionId, option.choice))}
90 1 : />
91 1 : {option.label}
92 1 : </label>
93 1 : ))}
94 1 : </fieldset>
95 : );
96 1 : })}
97 1 : </div>
98 1 : <footer className={styles.actions}>
99 1 : <Button disabled={props.busy} onClick={cancel}>Cancel</Button>
100 1 : <Button variant={ButtonVariant.Primary} disabled={props.busy} onClick={() => props.onConfirm(choices)}>
101 : Import
102 1 : </Button>
103 1 : </footer>
104 1 : </section>
105 1 : </Overlay>
106 : );
107 1 : }
|