Line data Source code
1 : import * as React from 'react';
2 : import symbols from '@ankoh/dashql-svg-symbols';
3 :
4 : import * as styles from './session_connection_buttons.module.css';
5 :
6 : import { AnchorSide } from '../../ui/foundations/anchored_position.js';
7 : import { ButtonSize, ButtonVariant, IconButton } from '../../ui/foundations/button.js';
8 : import { ConnectionHealth, type ConnectionState } from '../../connections/connection_state.js';
9 : import { CONNECTION_HEALTH_COLORS, CONNECTION_HEALTH_NAMES } from '../../connections/connection_status.js';
10 : import { ConnectionSettingsOverlay } from '../../connections/connection_settings_overlay.js';
11 : import { connectionLabelText } from '../../connections/connection_label.js';
12 : import { useConnectionLabeler } from '../connection_labels.js';
13 : import { CONNECTOR_ICON_STYLE, useConnectorIcon } from '../../connections/connector_icon.js';
14 :
15 : interface Props {
16 : /// The connections that the scripts of the session and the next script run on
17 : connections: readonly ConnectionState[];
18 : /// The session asks for attention to its connections, e.g. when a blocked run button is hovered
19 : attention: boolean;
20 : }
21 :
22 : export interface SessionConnectionButtonsDependencies {
23 : ConnectionSettingsOverlay: typeof ConnectionSettingsOverlay;
24 : }
25 :
26 1 : const DEFAULT_DEPENDENCIES: SessionConnectionButtonsDependencies = {
27 1 : ConnectionSettingsOverlay,
28 1 : };
29 :
30 : /// The connections that the scripts of a session and its next script run on, a connector logo with a health dot each.
31 : ///
32 : /// The user sees which connection is not connected yet. A button opens the settings of its connection,
33 : /// nothing connects by itself.
34 1 : export function SessionConnectionButtons(props: Props & { dependencies?: SessionConnectionButtonsDependencies }): React.ReactElement | null {
35 1 : const connectorIcon = useConnectorIcon();
36 1 : const dependencies = props.dependencies ?? DEFAULT_DEPENDENCIES;
37 1 : const labeler = useConnectionLabeler();
38 1 : const [settingsConnectionId, setSettingsConnectionId] = React.useState<string | null>(null);
39 1 : const anchorRef = React.useRef<HTMLButtonElement | null>(null);
40 1 : if (props.connections.length === 0) return null;
41 1 : return (
42 1 : <div className={styles.buttons} role="group" aria-label="Connections of the session" data-testid="session-connections">
43 1 : {props.connections.map(connection => {
44 1 : const health = connection.connectionHealth;
45 1 : const label = `${connectionLabelText(labeler(connection))}: ${CONNECTION_HEALTH_NAMES[health]}`;
46 1 : return (
47 1 : <IconButton
48 1 : key={connection.connectionId}
49 1 : className={styles.button}
50 1 : variant={ButtonVariant.Invisible}
51 1 : size={ButtonSize.Small}
52 1 : aria-label={label}
53 1 : data-testid="session-connection"
54 1 : data-health={ConnectionHealth[health]}
55 1 : data-attention={props.attention && health !== ConnectionHealth.ONLINE ? '' : undefined}
56 1 : onClick={event => {
57 1 : anchorRef.current = event.currentTarget;
58 1 : setSettingsConnectionId(connection.connectionId);
59 1 : }}
60 : >
61 1 : <span className={styles.logo}>
62 1 : <svg width="16px" height="16px" aria-hidden="true">
63 1 : <use xlinkHref={`${symbols}#${connectorIcon(connection.connectorInfo)}`} style={CONNECTOR_ICON_STYLE} />
64 1 : </svg>
65 1 : <svg className={styles.health} width="10px" height="10px" viewBox="0 0 10 10" aria-hidden="true">
66 1 : <circle cx="5" cy="5" r="5" style={{ fill: 'var(--color_bg_card)' }} />
67 1 : <circle cx="5" cy="5" r="3.5" style={{ fill: CONNECTION_HEALTH_COLORS[health] }} />
68 1 : </svg>
69 1 : </span>
70 1 : </IconButton>
71 : );
72 1 : })}
73 : {/* Mounted only while open, the overlay subscribes to the connection */}
74 1 : {settingsConnectionId != null && (
75 1 : <dependencies.ConnectionSettingsOverlay
76 1 : connectionId={settingsConnectionId}
77 1 : isOpen
78 1 : onClose={() => setSettingsConnectionId(null)}
79 1 : anchorRef={anchorRef}
80 1 : side={AnchorSide.OutsideBottom}
81 1 : />
82 : )}
83 1 : </div>
84 : );
85 1 : }
|