Skip to content

Commit 9fc92bf

Browse files
committed
finalise propmpts for saved filter managememnt
1 parent b5465bc commit 9fc92bf

File tree

12 files changed

+523
-161
lines changed

12 files changed

+523
-161
lines changed

vuu-ui/packages/vuu-filters/src/filter-provider/FilterProvider.tsx

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { FilterChangeHandler } from "@vuu-ui/vuu-filter-types";
2-
import { Prompt } from "@vuu-ui/vuu-ui-controls";
32
import {
43
createContext,
54
ReactElement,
@@ -10,10 +9,12 @@ import {
109
} from "react";
1110
import { FilterMenuActionHandler } from "../filter-pill/FilterMenu";
1211
import { FilterDescriptor } from "../saved-filters/useSavedFilterPanel";
13-
import { FilterNameForm } from "../saved-filters/FilterNameForm";
12+
import { FilterNamePrompt } from "../saved-filters/FilterNamePrompt";
13+
import { DeleteFilterPrompt } from "../saved-filters/DeleteFilterPrompt";
1414

1515
export interface FilterContextProps {
1616
activeFilter: FilterDescriptor | undefined;
17+
saveFilter: (filter: FilterDescriptor) => void;
1718
savedFilters?: FilterDescriptor[];
1819
onApplyFilter: FilterChangeHandler;
1920
onFilterMenuAction?: FilterMenuActionHandler;
@@ -27,6 +28,10 @@ export const FilterContext = createContext<FilterContextProps>({
2728
console.warn(
2829
"[FilterContext] onApplyFilter, no FilterProvider has been configured",
2930
),
31+
saveFilter: () =>
32+
console.warn(
33+
"[FilterContext] saveFilter, no FilterProvider has been configured",
34+
),
3035
setActiveFilter: () =>
3136
console.warn(
3237
"[FilterContext] setActiveFilter, no FilterProvider has been configured",
@@ -60,6 +65,12 @@ export const FilterProvider = ({
6065
[filterDescriptors],
6166
);
6267

68+
const deleteFilter = useCallback((filterId: string) => {
69+
setFilterDescriptors((filterDescriptors) =>
70+
filterDescriptors.filter(({ id }) => id !== filterId),
71+
);
72+
}, []);
73+
6374
const renameFilter = useCallback((filterId: string, filterName: string) => {
6475
setFilterDescriptors((currentFilterDescriptors) => {
6576
return currentFilterDescriptors.map<FilterDescriptor>((f) => {
@@ -81,32 +92,39 @@ export const FilterProvider = ({
8192
const PromptForFilterName = useCallback(
8293
({ filter, id }: FilterDescriptor) => {
8394
const originalFilterName = filter.name ?? "";
84-
let filterName = originalFilterName;
8595
setDialog(
86-
<Prompt
87-
onCancel={() => setDialog(null)}
88-
onConfirm={() => {
96+
<FilterNamePrompt
97+
filterName={filter.name}
98+
title="Rename filter"
99+
onClose={() => setDialog(null)}
100+
onConfirm={(name) => {
89101
setDialog(null);
90-
if (originalFilterName !== filterName) {
91-
renameFilter(id, filterName);
102+
if (originalFilterName !== name) {
103+
renameFilter(id, name);
92104
}
93105
}}
94-
onOpenChange={(open) => {
95-
if (!open) setDialog(null);
96-
}}
97-
open
98-
title="Rename Filter"
99-
>
100-
<FilterNameForm
101-
filterName={filter.name}
102-
onFilterNameChange={(value) => (filterName = value)}
103-
/>
104-
</Prompt>,
106+
/>,
105107
);
106108
},
107109
[renameFilter],
108110
);
109111

112+
const promptForConfirmationOfDelete = useCallback(
113+
(filterDescriptor: FilterDescriptor) => {
114+
setDialog(
115+
<DeleteFilterPrompt
116+
filterDescriptor={filterDescriptor}
117+
onConfirm={() => {
118+
setDialog(null);
119+
deleteFilter(filterDescriptor.id);
120+
}}
121+
onClose={() => setDialog(null)}
122+
/>,
123+
);
124+
},
125+
[],
126+
);
127+
110128
const handleFilterMenuAction = useCallback<FilterMenuActionHandler>(
111129
(filterId, actionType) => {
112130
const targetFilter = findFilter(filterId);
@@ -118,15 +136,31 @@ export const FilterProvider = ({
118136
console.log(`edit filter ${filterId}`);
119137
break;
120138
case "remove":
121-
console.log(`remove filter ${filterId}`);
139+
promptForConfirmationOfDelete(targetFilter);
122140
break;
123141
case "rename":
124142
return PromptForFilterName(targetFilter);
125143
}
126144
},
127-
[findFilter, PromptForFilterName],
145+
[findFilter, promptForConfirmationOfDelete, PromptForFilterName],
128146
);
129147

148+
const handleSaveFilter = useCallback((filterDescriptor: FilterDescriptor) => {
149+
setFilterDescriptors((filterDescriptors) => {
150+
if (filterDescriptor.active) {
151+
return filterDescriptors
152+
.map((filterDescriptor) =>
153+
filterDescriptor.active
154+
? { ...filterDescriptor, active: false }
155+
: filterDescriptor,
156+
)
157+
.concat(filterDescriptor);
158+
} else {
159+
return filterDescriptors.concat(filterDescriptor);
160+
}
161+
});
162+
}, []);
163+
130164
const setActiveFilter = useCallback(
131165
(filterId?: string) => {
132166
setFilterDescriptors((currentFilterDescriptors) => {
@@ -157,6 +191,7 @@ export const FilterProvider = ({
157191
activeFilter: filterDescriptors.find((f) => f.active),
158192
onApplyFilter: handleApplyFilter,
159193
onFilterMenuAction: handleFilterMenuAction,
194+
saveFilter: handleSaveFilter,
160195
savedFilters: filterDescriptors,
161196
setActiveFilter,
162197
}}
@@ -178,13 +213,15 @@ export function useSavedFilters() {
178213
onApplyFilter,
179214
onFilterMenuAction,
180215
savedFilters,
216+
saveFilter,
181217
setActiveFilter,
182218
} = useContext(FilterContext);
183219
return {
184220
activeFilter,
185221
onApplyFilter,
186222
onFilterMenuAction,
187223
savedFilters,
224+
saveFilter,
188225
setActiveFilter,
189226
};
190227
}

vuu-ui/packages/vuu-filters/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ export * from "./filter-utils";
1919
export * from "./FilterModel";
2020
export * from "./inline-filter";
2121
export * from "./quick-filters";
22-
export { FilterNameForm } from "./saved-filters/FilterNameForm";
2322
export { SavedFilterPanel } from "./saved-filters/SavedFilterPanel";
2423
export type {
2524
FilterClickHandler,
2625
FilterDescriptor,
2726
} from "./saved-filters/useSavedFilterPanel";
27+
export {
28+
FilterNamePrompt as SaveFilterConfirmPrompt,
29+
type FilterNamePromptProps as SaveFilterConfirmPromptProps,
30+
} from "./saved-filters/FilterNamePrompt";
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { Prompt, PromptProps } from "@vuu-ui/vuu-ui-controls";
2+
import cx from "clsx";
3+
import { HTMLAttributes } from "react";
4+
import { FilterDescriptor } from "./useSavedFilterPanel";
5+
6+
export interface DeleteFilterPromptProps
7+
extends Pick<PromptProps, "onConfirm" | "onClose" | "open">,
8+
HTMLAttributes<HTMLDivElement> {
9+
filterDescriptor: FilterDescriptor;
10+
}
11+
12+
export const DeleteFilterPrompt = ({
13+
children,
14+
className,
15+
filterDescriptor,
16+
onClose,
17+
onConfirm,
18+
open = true,
19+
...htmlAttributes
20+
}: DeleteFilterPromptProps) => {
21+
return (
22+
<Prompt
23+
{...htmlAttributes}
24+
className={cx("vuuDeleteFilterPrompt", className)}
25+
initialFocusedItem="confirm"
26+
onClose={onClose}
27+
onConfirm={onConfirm}
28+
open={open}
29+
title="Save Filter"
30+
>
31+
<span>{`Do you want to delete '${filterDescriptor.filter.name}' ?`}</span>
32+
{children}
33+
</Prompt>
34+
);
35+
};

vuu-ui/packages/vuu-filters/src/saved-filters/FilterNameForm.tsx

Lines changed: 0 additions & 49 deletions
This file was deleted.
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { FormField, FormFieldLabel } from "@salt-ds/core";
2+
import { Prompt, PromptProps, VuuInput } from "@vuu-ui/vuu-ui-controls";
3+
import { CommitHandler } from "@vuu-ui/vuu-utils";
4+
import {
5+
ChangeEventHandler,
6+
HTMLAttributes,
7+
useCallback,
8+
useEffect,
9+
useMemo,
10+
useRef,
11+
useState,
12+
} from "react";
13+
import cx from "clsx";
14+
15+
export interface FilterNamePromptProps
16+
extends Pick<PromptProps, "onClose" | "open" | "title">,
17+
Omit<HTMLAttributes<HTMLDivElement>, "title"> {
18+
filterName?: string;
19+
onConfirm: (filterName: string) => void;
20+
}
21+
22+
const isValidName = (name: unknown): name is string =>
23+
typeof name === "string" && name.trim().length > 0;
24+
25+
export const FilterNamePrompt = ({
26+
className,
27+
filterName = "",
28+
onClose,
29+
onConfirm,
30+
open = true,
31+
title,
32+
...htmlAttributes
33+
}: FilterNamePromptProps) => {
34+
const filterNameRef = useRef(filterName);
35+
const [isValid, setIsValid] = useState(filterName !== "");
36+
const confirmRef = useRef<HTMLButtonElement>(null);
37+
const inputRef = useRef<HTMLInputElement>(null);
38+
39+
const handleConfirm = useCallback(() => {
40+
onConfirm(filterNameRef.current);
41+
}, [onConfirm]);
42+
43+
const handleChange = useCallback<ChangeEventHandler<HTMLInputElement>>(
44+
(e) => {
45+
const value = e.target.value;
46+
filterNameRef.current = value;
47+
setIsValid(isValidName(value));
48+
},
49+
[],
50+
);
51+
52+
const handleCommit = useCallback<CommitHandler>(
53+
(e, value) => {
54+
if (isValidName(value)) {
55+
onConfirm(value);
56+
}
57+
},
58+
[onConfirm],
59+
);
60+
61+
const confirmButtonProps = useMemo(
62+
() => ({
63+
disabled: !isValid,
64+
ref: confirmRef,
65+
}),
66+
[isValid],
67+
);
68+
69+
useEffect(() => {
70+
setTimeout(() => {
71+
inputRef.current?.focus();
72+
}, 200);
73+
}, []);
74+
75+
return (
76+
<Prompt
77+
{...htmlAttributes}
78+
className={cx("vuuFilterNamePrompt", className)}
79+
confirmButtonProps={confirmButtonProps}
80+
onClose={onClose}
81+
onConfirm={handleConfirm}
82+
open={open}
83+
title={title}
84+
>
85+
<FormField>
86+
<FormFieldLabel>Filter name</FormFieldLabel>
87+
<VuuInput
88+
commitOnBlur={false}
89+
inputRef={inputRef}
90+
onChange={handleChange}
91+
onCommit={handleCommit}
92+
defaultValue={filterNameRef.current}
93+
placeholder="Please enter"
94+
/>
95+
</FormField>
96+
</Prompt>
97+
);
98+
};

0 commit comments

Comments
 (0)