Skip to content

Commit 1283a34

Browse files
authored
chore: DH-1469 Move ACL Editor Components (#1501)
Copied common ACL Editor components to Community. resolves #1469
1 parent b43b5b2 commit 1283a34

14 files changed

Lines changed: 408 additions & 0 deletions

package-lock.json

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/components/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"build:sass": "sass --embed-sources --load-path=../../node_modules ./src:./dist ./scss/BaseStyleSheet.scss:./css/BaseStyleSheet.css"
2424
},
2525
"dependencies": {
26+
"@adobe/react-spectrum": "^3.29.0",
2627
"@deephaven/icons": "file:../icons",
2728
"@deephaven/log": "file:../log",
2829
"@deephaven/react-hooks": "file:../react-hooks",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { ActionBar } from '@adobe/react-spectrum';
2+
import type { StyleProps } from '@react-types/shared';
3+
import commonStyles from './SpectrumComponent.module.scss';
4+
5+
export interface BulkActionBarProps {
6+
styleProps?: StyleProps;
7+
selectedItemCount: 'all' | number;
8+
onClearSelection: () => void;
9+
}
10+
11+
export function BulkActionBar({
12+
styleProps,
13+
selectedItemCount,
14+
onClearSelection,
15+
}: BulkActionBarProps) {
16+
return (
17+
<ActionBar
18+
UNSAFE_className={commonStyles.spectrumActionBar}
19+
// eslint-disable-next-line react/jsx-props-no-spreading
20+
{...styleProps}
21+
isEmphasized
22+
selectedItemCount={selectedItemCount}
23+
onClearSelection={onClearSelection}
24+
>
25+
{/* */}
26+
</ActionBar>
27+
);
28+
}
29+
30+
export default BulkActionBar;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
2+
import { Key, useCallback } from 'react';
3+
import { ComboBox, Item, SpectrumComboBoxProps } from '@adobe/react-spectrum';
4+
import type { FocusableRef } from '@react-types/shared';
5+
import type { ReactSpectrumComponent } from '@deephaven/react-hooks';
6+
import TextWithTooltip from './TextWithTooltip';
7+
8+
export interface SearchableComboboxProps<TItem, TKey extends Key>
9+
extends Omit<
10+
SpectrumComboBoxProps<TItem>,
11+
'children' | 'menuTrigger' | 'onSelectionChange'
12+
> {
13+
getItemDisplayText: (item: TItem | null | undefined) => string | null;
14+
getKey: (item: TItem | null | undefined) => TKey | null;
15+
scrollRef: React.RefObject<ReactSpectrumComponent<HTMLElement>>;
16+
onSelectionChange: (key: TKey | null) => void;
17+
}
18+
19+
export function SearchableCombobox<TItem, TKey extends Key>({
20+
scrollRef,
21+
getItemDisplayText,
22+
getKey,
23+
...props
24+
}: SearchableComboboxProps<TItem, TKey>) {
25+
const renderItem = useCallback(
26+
item => {
27+
const key = getKey(item);
28+
const displayText = getItemDisplayText(item);
29+
30+
return (
31+
<Item key={key} textValue={displayText ?? String(key)}>
32+
<TextWithTooltip text={displayText} />
33+
</Item>
34+
);
35+
},
36+
[getItemDisplayText, getKey]
37+
);
38+
39+
return (
40+
<ComboBox
41+
{...props}
42+
// The `ref`prop type defined by React Spectrum is incorrect here
43+
ref={scrollRef as unknown as FocusableRef<HTMLElement>}
44+
menuTrigger="focus"
45+
// Type assertion is necessary since <ComboBox> types don't recognize the
46+
// generic key arg
47+
onSelectionChange={props.onSelectionChange as (key: Key | null) => void}
48+
>
49+
{renderItem}
50+
</ComboBox>
51+
);
52+
}
53+
54+
export default SearchableCombobox;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* The Spectrum ActionBar hard codes a max-width: 960px for the content area.
3+
* Overriding so the bar fills the same width as its associated list component.
4+
*/
5+
.spectrumActionBar {
6+
[class^='react-spectrum-ActionBar-bar'] {
7+
max-width: none;
8+
}
9+
}
10+
11+
.spectrumDialogComponentHeading {
12+
font-weight: normal;
13+
overflow-wrap: break-word;
14+
}
15+
16+
.spectrumEllipsis {
17+
overflow: hidden;
18+
text-overflow: ellipsis;
19+
white-space: nowrap;
20+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useEffect, useState } from 'react';
2+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3+
import {
4+
Content,
5+
Heading,
6+
Icon,
7+
IllustratedMessage,
8+
} from '@adobe/react-spectrum';
9+
import { vsEmptyWindow } from '@deephaven/icons';
10+
11+
const DEFAULT_DELAY_MS = 500;
12+
13+
export interface TableViewEmptyStateProps {
14+
heading: string;
15+
content?: string;
16+
delayMs?: number;
17+
}
18+
19+
export function TableViewEmptyState({
20+
heading,
21+
content,
22+
delayMs = DEFAULT_DELAY_MS,
23+
}: TableViewEmptyStateProps) {
24+
const [show, setShow] = useState(false);
25+
26+
// Spectrum `TableView` will render the result of `renderEmptyState` prop
27+
// immediately, and it will be hidden once data loads. In cases where the data
28+
// load is quick, there is a jarring flicker, and the empty data message is
29+
// not helpful. This delay will avoid showing the message if data loads within
30+
// the `delayMs`.
31+
useEffect(() => {
32+
window.setTimeout(() => {
33+
setShow(true);
34+
}, delayMs);
35+
}, [delayMs]);
36+
37+
return show ? (
38+
<IllustratedMessage>
39+
<Icon size="XXL">
40+
<FontAwesomeIcon icon={vsEmptyWindow} />
41+
</Icon>
42+
<Heading>{heading}</Heading>
43+
{content == null ? null : <Content>{content}</Content>}
44+
</IllustratedMessage>
45+
) : null;
46+
}
47+
48+
export default TableViewEmptyState;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useMemo } from 'react';
2+
import { Text } from '@adobe/react-spectrum';
3+
import stylesCommon from './SpectrumComponent.module.scss';
4+
import { PopperOptions, Tooltip } from './popper';
5+
6+
export interface TextWithTooltipProps {
7+
text?: string | null;
8+
placement?: PopperOptions['placement'];
9+
}
10+
11+
export function TextWithTooltip({
12+
text,
13+
placement = 'top-start',
14+
}: TextWithTooltipProps) {
15+
const options = useMemo(() => ({ placement }), [placement]);
16+
17+
return (
18+
<>
19+
<Text UNSAFE_className={stylesCommon.spectrumEllipsis}>
20+
{text ?? (
21+
/* &nbsp; so that height doesn't collapse when empty */
22+
<>&nbsp;</>
23+
)}
24+
</Text>
25+
{text == null || text === '' ? null : (
26+
<Tooltip options={options}>{text}</Tooltip>
27+
)}
28+
</>
29+
);
30+
}
31+
32+
export default TextWithTooltip;
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ReactElement, ReactNode, useCallback } from 'react';
2+
import type { SpectrumLabelableProps } from '@react-types/shared';
3+
import { vsTrash } from '@deephaven/icons';
4+
import { ACTION_ICON_HEIGHT } from '@deephaven/utils';
5+
import { ActionButtonDialogTrigger, ConfirmationDialog } from '../dialogs';
6+
7+
export interface ConfirmActionButtonProps {
8+
ariaLabel: string;
9+
heading: ReactNode;
10+
confirmationButtonLabel: string;
11+
children:
12+
| ReactElement<SpectrumLabelableProps>
13+
| ReactElement<SpectrumLabelableProps>[];
14+
isHidden?: boolean;
15+
onConfirm: () => void;
16+
}
17+
18+
export function ConfirmActionButton({
19+
ariaLabel,
20+
heading,
21+
confirmationButtonLabel,
22+
isHidden,
23+
children,
24+
onConfirm,
25+
}: ConfirmActionButtonProps) {
26+
const renderDialog = useCallback(
27+
close => (
28+
<ConfirmationDialog
29+
heading={heading}
30+
confirmationButtonLabel={confirmationButtonLabel}
31+
onCancel={close}
32+
onConfirm={() => {
33+
close();
34+
onConfirm();
35+
}}
36+
>
37+
{children}
38+
</ConfirmationDialog>
39+
),
40+
[children, confirmationButtonLabel, heading, onConfirm]
41+
);
42+
43+
return (
44+
<ActionButtonDialogTrigger
45+
ariaLabel={ariaLabel}
46+
icon={vsTrash}
47+
isHidden={isHidden}
48+
isQuiet
49+
height={ACTION_ICON_HEIGHT}
50+
>
51+
{renderDialog}
52+
</ActionButtonDialogTrigger>
53+
);
54+
}
55+
56+
export default ConfirmActionButton;
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
2+
import {
3+
ActionButton,
4+
Icon,
5+
SpectrumActionButtonProps,
6+
} from '@adobe/react-spectrum';
7+
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
8+
import type { IconProp } from '@fortawesome/fontawesome-svg-core';
9+
import { ACTION_ICON_HEIGHT } from '@deephaven/utils';
10+
11+
export interface IconActionButtonProps
12+
extends Omit<SpectrumActionButtonProps, 'aria-label' | 'isQuiet' | 'height'> {
13+
icon: IconProp;
14+
label: string;
15+
}
16+
17+
export function IconActionButton({
18+
icon,
19+
label,
20+
...props
21+
}: IconActionButtonProps) {
22+
return (
23+
<ActionButton
24+
{...props}
25+
aria-label={label}
26+
isQuiet
27+
height={ACTION_ICON_HEIGHT}
28+
>
29+
<Icon>
30+
<FontAwesomeIcon icon={icon} />
31+
</Icon>
32+
</ActionButton>
33+
);
34+
}
35+
IconActionButton.displayName = 'IconActionButton';
36+
37+
export default IconActionButton;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './ConfirmActionButton';
2+
export * from './IconActionButton';

0 commit comments

Comments
 (0)