diff --git a/packages/query-composer/src/assets/img/query_clear_hover.svg b/packages/query-composer/src/assets/img/query_clear_hover.svg
index 7a960481..9b45f8c5 100644
--- a/packages/query-composer/src/assets/img/query_clear_hover.svg
+++ b/packages/query-composer/src/assets/img/query_clear_hover.svg
@@ -1,8 +1,9 @@
-
\ No newline at end of file
+
diff --git a/packages/query-composer/src/components/ActionIcon/ActionIcon.tsx b/packages/query-composer/src/components/ActionIcon/ActionIcon.tsx
index 5cd8526f..e87f5c2d 100644
--- a/packages/query-composer/src/components/ActionIcon/ActionIcon.tsx
+++ b/packages/query-composer/src/components/ActionIcon/ActionIcon.tsx
@@ -86,6 +86,7 @@ interface ActionIconProps {
action: ActionIconName;
onClick?: () => void;
color?: ColorKey;
+ title?: string;
}
const iconMap: Record<
@@ -127,6 +128,7 @@ export const ActionIcon: React.FC = ({
action,
onClick,
color,
+ title,
}) => {
const sizeProps = {width: '22px', height: '22px'};
const otherProps = {
@@ -137,7 +139,7 @@ export const ActionIcon: React.FC = ({
const Icon = iconMap[action];
return (
-
+
);
diff --git a/packages/query-composer/src/components/DatePicker/DatePicker.tsx b/packages/query-composer/src/components/DatePicker/DatePicker.tsx
index a87e261a..0fe5100b 100644
--- a/packages/query-composer/src/components/DatePicker/DatePicker.tsx
+++ b/packages/query-composer/src/components/DatePicker/DatePicker.tsx
@@ -31,6 +31,7 @@ import {
} from '../CommonElements';
import {NumberInput} from '../NumberInput';
import {SelectDropdown} from '../SelectDropdown';
+import {COLORS} from '../../colors';
interface DatePickerProps {
value: Date;
@@ -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);
`
: `
@@ -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);
`
: `
diff --git a/packages/query-composer/src/components/ExploreQueryEditor/ExploreQueryEditor.tsx b/packages/query-composer/src/components/ExploreQueryEditor/ExploreQueryEditor.tsx
index 39bd5a57..52534e85 100644
--- a/packages/query-composer/src/components/ExploreQueryEditor/ExploreQueryEditor.tsx
+++ b/packages/query-composer/src/components/ExploreQueryEditor/ExploreQueryEditor.tsx
@@ -179,6 +179,7 @@ export const ExploreQueryEditor: React.FC = ({
action="remove"
onClick={clearQuery}
color={isQueryEmpty ? 'other' : 'dimension'}
+ title="Clear Query"
/>
= ({
{unsaved ? : ''}
{canRemove && (
-
+
= ({
};
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));
diff --git a/packages/query-composer/src/components/PillInput/PillInput.tsx b/packages/query-composer/src/components/PillInput/PillInput.tsx
index 2f2d4336..0d44bfc1 100644
--- a/packages/query-composer/src/components/PillInput/PillInput.tsx
+++ b/packages/query-composer/src/components/PillInput/PillInput.tsx
@@ -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';
@@ -56,47 +65,82 @@ export const PillInput: React.FC = ({
const [selectedPill, setSelectedPill] = useState(
undefined
);
+ const pillRefs = useRef>([]);
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(values.length);
+ return values.map(
+ (value, index): ReactElement => (
+ {
+ setSelectedPill(index);
+ event.stopPropagation();
+ }}
+ onDelete={() => deletePill(index)}
+ forwardRef={ref => {
+ pillRefs.current[index] = ref;
+ }}
+ >
+ {value}
+
+ )
+ );
+ }, [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) {
@@ -112,23 +156,12 @@ export const PillInput: React.FC = ({
return (
inp.current?.focus()}
isFocused={focused || selectedPill !== undefined}
ref={ref}
>
- {values.map((value, index) => (
- {
- setSelectedPill(index);
- event.stopPropagation();
- }}
- >
- {value}
-
- ))}
+ {pills}
= ({
);
};
+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 (
+
+ );
+};
+
const OuterInput = styled.div<{
isFocused: boolean;
}>`
@@ -196,7 +254,7 @@ const OuterInput = styled.div<{
${({isFocused}) => (isFocused ? `border-color: #4285F4;` : '')}
`;
-const Pill = styled.div<{
+const Pill = styled(PillInner)<{
isSelected: boolean;
}>`
${({isSelected}) => `
@@ -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;
@@ -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};
- }
- `;
- }}
-`;
diff --git a/packages/query-composer/src/core/query.ts b/packages/query-composer/src/core/query.ts
index 3fca6050..aa2374e1 100644
--- a/packages/query-composer/src/core/query.ts
+++ b/packages/query-composer/src/core/query.ts
@@ -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}`);