Skip to content

Commit

Permalink
Pill behavior tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
whscullin committed Nov 21, 2024
1 parent b2045d6 commit 0a53104
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 73 deletions.
15 changes: 8 additions & 7 deletions packages/query-composer/src/assets/img/query_clear_hover.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ interface ActionIconProps {
action: ActionIconName;
onClick?: () => void;
color?: ColorKey;
title?: string;
}

const iconMap: Record<
Expand Down Expand Up @@ -127,6 +128,7 @@ export const ActionIcon: React.FC<ActionIconProps> = ({
action,
onClick,
color,
title,
}) => {
const sizeProps = {width: '22px', height: '22px'};
const otherProps = {
Expand All @@ -137,7 +139,7 @@ export const ActionIcon: React.FC<ActionIconProps> = ({
const Icon = iconMap[action];

return (
<IconWrapper color={color} doHover={onClick !== undefined}>
<IconWrapper color={color} doHover={onClick !== undefined} title={title}>
<Icon {...props} />
</IconWrapper>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from '../CommonElements';
import {NumberInput} from '../NumberInput';
import {SelectDropdown} from '../SelectDropdown';
import {COLORS} from '../../colors';

interface DatePickerProps {
value: Date;
Expand Down Expand Up @@ -686,7 +687,7 @@ const WeekButton = styled.div<{
isSelected
? `
background-color: var(--malloy-composer-form-focusBackground, #f0f6ff);
color: var(--malloy-composer-dimension-strong);
color: ${COLORS.dimension.fillStrong};
border-color: var(--malloy-composer-focus, #c3d7f7);
`
: `
Expand Down Expand Up @@ -737,7 +738,7 @@ const Day = styled(Cell)<{
isSelected
? `
background-color: var(--malloy-composer-form-focusBackground, #f0f6ff);
color: var(--malloy-composer-dimension-strong);
color: ${COLORS.dimension.fillStrong};
border-color: var(--malloy-composer-focus, #c3d7f7);
`
: `
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ export const ExploreQueryEditor: React.FC<ExploreQueryEditorProps> = ({
action="remove"
onClick={clearQuery}
color={isQueryEmpty ? 'other' : 'dimension'}
title="Clear Query"
/>
<StyledRunIcon
width="80px"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export const FieldButton: React.FC<FieldButtonProps> = ({
{unsaved ? <UnsavedIndicator color={color} /> : ''}
</FrontPart>
{canRemove && (
<BackPart className="back">
<BackPart className="back" title="Remove">
<CloseIconStyled
color={color}
width="20px"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const FieldDetailPanel: React.FC<FieldDetailPanelProps> = ({
};

const ContextMenuDetail = styled(ContextMenuMain)`
border-radius: 4px;
padding: 20px;
font-family: var(--malloy-composer-font-family, sans-serif);
background-color: var(--malloy-composer-menu-background, rgb(248, 248, 248));
Expand Down
167 changes: 106 additions & 61 deletions packages/query-composer/src/components/PillInput/PillInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,18 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import * as React from 'react';
import {RefObject, useEffect, useRef, useState} from 'react';
import {
ReactElement,
ReactNode,
RefObject,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import styled from 'styled-components';
import {ColorKey, COLORS} from '../../colors';
import {COLORS} from '../../colors';
import CloseIcon from '../../assets/img/query_clear_hover.svg?react';
import {useClickOutside} from '../../hooks';

Expand Down Expand Up @@ -56,47 +65,82 @@ export const PillInput: React.FC<PillInputProps> = ({
const [selectedPill, setSelectedPill] = useState<number | undefined>(
undefined
);
const pillRefs = useRef<Array<HTMLDivElement | null>>([]);

const value = controlledValue || uncontrolledValue;
const setValue = setControlledValue || setUncontrolledValue;

useEffect(() => {
const handler = (event: KeyboardEvent) => {
if (event.key === 'Backspace') {
if (selectedPill !== undefined) {
const newValues = [...values];
newValues.splice(selectedPill, 1);
setValues(newValues);
if (selectedPill === 0) {
if (values.length === 1) {
setSelectedPill(undefined);
inp.current?.focus();
} else {
setSelectedPill(0);
}
} else {
setSelectedPill(selectedPill - 1);
}
}
} else if (event.key === 'ArrowRight') {
if (selectedPill === values.length - 1) {
console.info({selectedPill, activeElement: document.activeElement});

const deletePill = useCallback(
(idx: number) => {
const newValues = [...values];
newValues.splice(idx, 1);
setValues(newValues);
if (selectedPill === 0) {
if (values.length === 1) {
setSelectedPill(undefined);
inp.current?.focus();
} else if (selectedPill !== undefined) {
setSelectedPill(selectedPill + 1);
}
} else if (event.key === 'ArrowLeft') {
if (selectedPill !== undefined && selectedPill > 0) {
setSelectedPill(selectedPill - 1);
} else {
setSelectedPill(0);
}
} else {
setSelectedPill(idx - 1);
}
},
[selectedPill, setValues, values]
);

const pills = useMemo(() => {
pillRefs.current = new Array<HTMLDivElement>(values.length);
return values.map(
(value, index): ReactElement<PillProps> => (
<Pill
key={value}
isSelected={selectedPill === index}
onClick={event => {
setSelectedPill(index);
event.stopPropagation();
}}
onDelete={() => deletePill(index)}
forwardRef={ref => {
pillRefs.current[index] = ref;
}}
>
{value}
</Pill>
)
);
}, [deletePill, selectedPill, values]);

useEffect(() => {
if (selectedPill !== undefined) {
pillRefs.current[selectedPill]?.focus();
}
}, [pills, selectedPill]);

const onKeyUp = (event: React.KeyboardEvent) => {
console.info({event});

if (event.key === 'Backspace') {
if (selectedPill !== undefined) {
deletePill(selectedPill);
}
} else if (event.key === 'ArrowRight') {
if (selectedPill === values.length - 1) {
setSelectedPill(undefined);
inp.current?.focus();
} else if (selectedPill !== undefined) {
setSelectedPill(selectedPill + 1);
}
};
const {current} = ref;
current?.addEventListener('keyup', handler);
return () => current?.removeEventListener('keyup', handler);
});
} else if (event.key === 'ArrowLeft') {
if (selectedPill !== undefined && selectedPill > 0) {
setSelectedPill(selectedPill - 1);
}
} else {
inp.current?.focus();
}
};

const commitValue = () => {
if (value.length > 0) {
Expand All @@ -112,23 +156,12 @@ export const PillInput: React.FC<PillInputProps> = ({

return (
<OuterInput
onKeyUp={onKeyUp}
onClick={() => inp.current?.focus()}
isFocused={focused || selectedPill !== undefined}
ref={ref}
>
{values.map((value, index) => (
<Pill
key={index}
tabIndex={0}
isSelected={selectedPill === index}
onClick={event => {
setSelectedPill(index);
event.stopPropagation();
}}
>
{value}
</Pill>
))}
{pills}
<StyledInput
ref={inp}
type={type}
Expand Down Expand Up @@ -177,6 +210,31 @@ export const PillInput: React.FC<PillInputProps> = ({
);
};

interface PillProps {
onClick: (event: React.MouseEvent) => void;
onDelete: (event: React.MouseEvent) => void;
children: ReactNode;
className?: string;
forwardRef: (ref: HTMLDivElement | null) => void;
}

const PillInner = ({
children,
className,
forwardRef,
onClick,
onDelete,
}: PillProps) => {
return (
<div className={className} onClick={onClick} tabIndex={0} ref={forwardRef}>
{children}
<div title="Remove">
<CloseIcon width={16} height={16} onClick={onDelete} title="Remove" />
</div>
</div>
);
};

const OuterInput = styled.div<{
isFocused: boolean;
}>`
Expand All @@ -196,7 +254,7 @@ const OuterInput = styled.div<{
${({isFocused}) => (isFocused ? `border-color: #4285F4;` : '')}
`;

const Pill = styled.div<{
const Pill = styled(PillInner)<{
isSelected: boolean;
}>`
${({isSelected}) => `
Expand All @@ -209,7 +267,7 @@ const Pill = styled.div<{
`}
border-radius: 5px;
color: var(--malloy-composer-dimension-strong);
color: ${COLORS.dimension.fillStrong};
display: flex;
align-items: center;
gap: 5px;
Expand All @@ -230,16 +288,3 @@ const StyledInput = styled.input`
padding: 3.75px 7px;
flex-grow: 1;
`;

export const CloseIconStyled = styled(CloseIcon)<{
color: ColorKey;
}>`
cursor: pointer;
${({color}) => {
return `
.cross {
fill: ${COLORS[color].fillStrong};
}
`;
}}
`;
2 changes: 1 addition & 1 deletion packages/query-composer/src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ export class QueryBuilder extends SourceUtils {
this.query.name = definition.as || definition.name;
}

public replaceQuery(field: TurtleDef): void {
public replaceQuery(field: TurtleDef | NamedQuery): void {
const stage = this.query.pipeline[0];
if (!(stage.type === 'reduce' || stage.type === 'project')) {
throw new Error(`Unhandled stage type ${stage.type}`);
Expand Down

0 comments on commit 0a53104

Please sign in to comment.