Line data Source code
1 : import * as React from 'react';
2 : import symbols from '@ankoh/dashql-svg-symbols';
3 :
4 : import * as ActionList from '../ui/foundations/action_list.js';
5 : import { Button, ButtonSize, ButtonVariant } from '../ui/foundations/button.js';
6 : import { ConnectionHealth, type ConnectionState } from './connection_state.js';
7 : import { CONNECTION_HEALTH_COLORS } from './connection_status.js';
8 : import { connectionLabelText, type ConnectionLabelParts } from './connection_label.js';
9 : import { ConnectionLabelView } from './connection_label_view.js';
10 : import type { ConnectionReplaceCandidates } from './connection_replace.js';
11 : import * as styles from './connection_replace_picker.module.css';
12 : import { CONNECTOR_ICON_STYLE, useConnectorIcon } from './connector_icon.js';
13 :
14 : interface Props {
15 : /// The connection that is replaced
16 : source: ConnectionState;
17 : /// The connections that can replace it
18 : candidates: ConnectionReplaceCandidates;
19 : /// Label a connection
20 : labelOf: (database: ConnectionState) => ConnectionLabelParts;
21 : /// Go back without replacing
22 : onCancel: () => void;
23 : /// Replace the connection with the picked one
24 : onConfirm: (targetId: string) => void;
25 : }
26 :
27 : /// Pick the connection that replaces a connection, the connections of the same connector first
28 1 : export const ConnectionReplacePicker: React.FC<Props> = ({ source, candidates, labelOf, onCancel, onConfirm }) => {
29 1 : const connectorIcon = useConnectorIcon();
30 1 : const [selected, setSelected] = React.useState<string | null>(null);
31 1 : const sourceLabel = connectionLabelText(labelOf(source));
32 1 : const renderItem = (connection: ConnectionState) => (
33 1 : <ActionList.ListItem
34 1 : key={connection.connectionId}
35 1 : data-testid="connection-replace-option"
36 1 : data-connection-id={connection.connectionId}
37 1 : selected={connection.connectionId === selected}
38 1 : onClick={() => setSelected(connection.connectionId)}
39 1 : onDoubleClick={() => onConfirm(connection.connectionId)}
40 : >
41 1 : <ActionList.Leading>
42 1 : <svg className={styles.connector_icon} width="16px" height="16px" aria-hidden="true">
43 1 : <use xlinkHref={`${symbols}#${connectorIcon(connection.connectorInfo)}`} style={CONNECTOR_ICON_STYLE} />
44 1 : </svg>
45 1 : </ActionList.Leading>
46 1 : <ActionList.ItemText>
47 1 : <ActionList.ItemTextTitle>
48 1 : <ConnectionLabelView parts={labelOf(connection)} />
49 1 : </ActionList.ItemTextTitle>
50 1 : </ActionList.ItemText>
51 1 : <ActionList.Trailing>
52 1 : <svg width="16px" height="16px" viewBox="0 0 16 16" aria-hidden="true">
53 1 : <circle cx="8" cy="8" r="4" style={{ fill: CONNECTION_HEALTH_COLORS[connection.connectionHealth] ?? CONNECTION_HEALTH_COLORS[ConnectionHealth.NOT_STARTED] }} />
54 1 : </svg>
55 1 : </ActionList.Trailing>
56 1 : </ActionList.ListItem>
57 : );
58 1 : return (
59 1 : <div className={styles.picker} role="dialog" aria-label={`Replace ${sourceLabel}`} data-testid="connection-replace-picker">
60 1 : <div className={styles.header}>
61 1 : <span className={styles.title}>Replace with</span>
62 1 : <span className={styles.subtitle}>{sourceLabel}</span>
63 1 : </div>
64 1 : <ActionList.List className={styles.list} aria-label={`Connections that replace ${sourceLabel}`}>
65 : {/* The list takes elements only, the fragments wrap the optional groups */}
66 1 : <>{candidates.sameConnector.length > 0 && (
67 1 : <>
68 1 : <ActionList.GroupHeading>{source.connectorInfo.names.displayShort}</ActionList.GroupHeading>
69 1 : {candidates.sameConnector.map(renderItem)}
70 1 : </>
71 1 : )}</>
72 1 : <>{candidates.otherConnectors.length > 0 && (
73 1 : <>
74 1 : <ActionList.GroupHeading>Other connectors</ActionList.GroupHeading>
75 1 : {candidates.otherConnectors.map(renderItem)}
76 1 : </>
77 1 : )}</>
78 1 : </ActionList.List>
79 1 : <div className={styles.footer}>
80 1 : <span className={styles.note}>The sessions move to the picked connection, then this one is removed.</span>
81 1 : <div className={styles.actions}>
82 1 : <Button size={ButtonSize.Small} onClick={onCancel} data-testid="connection-replace-cancel">Cancel</Button>
83 1 : <Button
84 1 : size={ButtonSize.Small}
85 1 : variant={ButtonVariant.Primary}
86 1 : disabled={selected == null}
87 1 : onClick={() => { if (selected != null) onConfirm(selected); }}
88 1 : data-testid="connection-replace-confirm"
89 1 : >
90 : Replace
91 1 : </Button>
92 1 : </div>
93 1 : </div>
94 1 : </div>
95 : );
96 1 : };
|