From e5f478218e48f68ae578f77be53886601d200ae0 Mon Sep 17 00:00:00 2001 From: jeffvli Date: Sun, 15 Jan 2023 21:57:44 -0800 Subject: [PATCH] Forward playlist query filters --- .../components/playlist-query-builder.tsx | 698 +++++++++--------- 1 file changed, 361 insertions(+), 337 deletions(-) diff --git a/src/renderer/features/playlists/components/playlist-query-builder.tsx b/src/renderer/features/playlists/components/playlist-query-builder.tsx index 2c06345d..1f3d95ef 100644 --- a/src/renderer/features/playlists/components/playlist-query-builder.tsx +++ b/src/renderer/features/playlists/components/playlist-query-builder.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { forwardRef, Ref, useImperativeHandle, useState } from 'react'; import { Group } from '@mantine/core'; import { useForm } from '@mantine/form'; import clone from 'lodash/clone'; @@ -20,7 +20,7 @@ import { } from '/@/renderer/features/playlists/utils'; import { QueryBuilderGroup, QueryBuilderRule } from '/@/renderer/types'; import { RiMore2Fill, RiSaveLine } from 'react-icons/ri'; -import { SongListSort, SortOrder } from '/@/renderer/api/types'; +import { SongListSort } from '/@/renderer/api/types'; import { NDSongQueryBooleanOperators, NDSongQueryDateOperators, @@ -43,17 +43,17 @@ type DeleteArgs = { interface PlaylistQueryBuilderProps { isSaving?: boolean; limit?: number; - onSave: ( + onSave?: ( parsedFilter: any, extraFilters: { limit?: number; sortBy?: string; sortOrder?: string }, ) => void; - onSaveAs: ( + onSaveAs?: ( parsedFilter: any, extraFilters: { limit?: number; sortBy?: string; sortOrder?: string }, ) => void; query: any; sortBy: SongListSort; - sortOrder: SortOrder; + sortOrder: 'asc' | 'desc'; } const DEFAULT_QUERY = { @@ -70,381 +70,405 @@ const DEFAULT_QUERY = { uniqueId: nanoid(), }; -export const PlaylistQueryBuilder = ({ - sortOrder, - sortBy, - limit, - isSaving, - query, - onSave, - onSaveAs, -}: PlaylistQueryBuilderProps) => { - const [filters, setFilters] = useState( - query ? convertNDQueryToQueryGroup(query) : DEFAULT_QUERY, - ); +export type PlaylistQueryBuilderRef = { + getFilters: () => { + extraFilters: { + limit?: number; + sortBy?: string; + sortOrder?: string; + }; + filters: QueryBuilderGroup; + }; +}; - const extraFiltersForm = useForm({ - initialValues: { - limit, - sortBy, - sortOrder, - }, - }); +export const PlaylistQueryBuilder = forwardRef( + ( + { sortOrder, sortBy, limit, isSaving, query, onSave, onSaveAs }: PlaylistQueryBuilderProps, + ref: Ref, + ) => { + const [filters, setFilters] = useState( + query ? convertNDQueryToQueryGroup(query) : DEFAULT_QUERY, + ); - const handleResetFilters = () => { - if (query) { - setFilters(convertNDQueryToQueryGroup(query)); - } else { + const extraFiltersForm = useForm({ + initialValues: { + limit, + sortBy, + sortOrder, + }, + }); + + useImperativeHandle(ref, () => ({ + getFilters: () => ({ + extraFilters: extraFiltersForm.values, + filters, + }), + })); + + const handleResetFilters = () => { + if (query) { + setFilters(convertNDQueryToQueryGroup(query)); + } else { + setFilters(DEFAULT_QUERY); + } + }; + + const handleClearFilters = () => { setFilters(DEFAULT_QUERY); - } - }; + }; - const handleClearFilters = () => { - setFilters(DEFAULT_QUERY); - }; + const setFilterHandler = (newFilters: QueryBuilderGroup) => { + setFilters(newFilters); + }; - const setFilterHandler = (newFilters: QueryBuilderGroup) => { - setFilters(newFilters); - }; + const handleSave = () => { + onSave?.(convertQueryGroupToNDQuery(filters), extraFiltersForm.values); + }; - const handleSave = () => { - onSave(convertQueryGroupToNDQuery(filters), extraFiltersForm.values); - }; + const handleSaveAs = () => { + onSaveAs?.(convertQueryGroupToNDQuery(filters), extraFiltersForm.values); + }; - const handleSaveAs = () => { - onSaveAs(convertQueryGroupToNDQuery(filters), extraFiltersForm.values); - }; + const handleAddRuleGroup = (args: AddArgs) => { + const { level, groupIndex } = args; + const filtersCopy = clone(filters); - const handleAddRuleGroup = (args: AddArgs) => { - const { level, groupIndex } = args; - const filtersCopy = clone(filters); + const getPath = (level: number) => { + if (level === 0) return 'group'; - const getPath = (level: number) => { - if (level === 0) return 'group'; + const str = []; + for (const index of groupIndex) { + str.push(`group[${index}]`); + } + + return `${str.join('.')}.group`; + }; + + const path = getPath(level); + const updatedFilters = setWith( + filtersCopy, + path, + [ + ...get(filtersCopy, path), + { + group: [], + rules: [ + { + field: '', + operator: '', + uniqueId: nanoid(), + value: '', + }, + ], + type: 'any', + uniqueId: nanoid(), + }, + ], + clone, + ); + + setFilterHandler(updatedFilters); + }; + + const handleDeleteRuleGroup = (args: DeleteArgs) => { + const { uniqueId, level, groupIndex } = args; + const filtersCopy = clone(filters); + + const getPath = (level: number) => { + if (level === 0) return 'group'; + + const str = []; + for (let i = 0; i < groupIndex.length; i += 1) { + if (i !== groupIndex.length - 1) { + str.push(`group[${groupIndex[i]}]`); + } else { + str.push(`group`); + } + } + + return `${str.join('.')}`; + }; + + const path = getPath(level); + + const updatedFilters = setWith( + filtersCopy, + path, + [ + ...get(filtersCopy, path).filter( + (group: QueryBuilderGroup) => group.uniqueId !== uniqueId, + ), + ], + clone, + ); + + setFilterHandler(updatedFilters); + }; + + const getRulePath = (level: number, groupIndex: number[]) => { + if (level === 0) return 'rules'; const str = []; for (const index of groupIndex) { str.push(`group[${index}]`); } - return `${str.join('.')}.group`; + return `${str.join('.')}.rules`; }; - const path = getPath(level); - const updatedFilters = setWith( - filtersCopy, - path, - [ - ...get(filtersCopy, path), - { - group: [], - rules: [ - { - field: '', - operator: '', - uniqueId: nanoid(), - value: '', - }, - ], - type: 'any', - uniqueId: nanoid(), - }, - ], - clone, - ); + const handleAddRule = (args: AddArgs) => { + const { level, groupIndex } = args; + const filtersCopy = clone(filters); - setFilterHandler(updatedFilters); - }; + const path = getRulePath(level, groupIndex); + const updatedFilters = setWith( + filtersCopy, + path, + [ + ...get(filtersCopy, path), + { + field: '', + operator: '', + uniqueId: nanoid(), + value: null, + }, + ], + clone, + ); - const handleDeleteRuleGroup = (args: DeleteArgs) => { - const { uniqueId, level, groupIndex } = args; - const filtersCopy = clone(filters); + setFilterHandler(updatedFilters); + }; - const getPath = (level: number) => { - if (level === 0) return 'group'; + const handleDeleteRule = (args: DeleteArgs) => { + const { uniqueId, level, groupIndex } = args; + const filtersCopy = clone(filters); - const str = []; - for (let i = 0; i < groupIndex.length; i += 1) { - if (i !== groupIndex.length - 1) { + const path = getRulePath(level, groupIndex); + const updatedFilters = setWith( + filtersCopy, + path, + get(filtersCopy, path).filter((rule: QueryBuilderRule) => rule.uniqueId !== uniqueId), + clone, + ); + + setFilterHandler(updatedFilters); + }; + + const handleChangeField = (args: any) => { + const { uniqueId, level, groupIndex, value } = args; + const filtersCopy = clone(filters); + + const path = getRulePath(level, groupIndex); + const updatedFilters = setWith( + filtersCopy, + path, + get(filtersCopy, path).map((rule: QueryBuilderGroup) => { + if (rule.uniqueId !== uniqueId) return rule; + return { + ...rule, + field: value, + operator: '', + value: '', + }; + }), + clone, + ); + + setFilterHandler(updatedFilters); + }; + + const handleChangeType = (args: any) => { + const { level, groupIndex, value } = args; + + const filtersCopy = clone(filters); + + if (level === 0) { + return setFilterHandler({ ...filtersCopy, type: value }); + } + + const getTypePath = () => { + const str = []; + for (let i = 0; i < groupIndex.length; i += 1) { str.push(`group[${groupIndex[i]}]`); - } else { - str.push(`group`); } - } - return `${str.join('.')}`; - }; + return `${str.join('.')}`; + }; - const path = getPath(level); - - const updatedFilters = setWith( - filtersCopy, - path, - [...get(filtersCopy, path).filter((group: QueryBuilderGroup) => group.uniqueId !== uniqueId)], - clone, - ); - - setFilterHandler(updatedFilters); - }; - - const getRulePath = (level: number, groupIndex: number[]) => { - if (level === 0) return 'rules'; - - const str = []; - for (const index of groupIndex) { - str.push(`group[${index}]`); - } - - return `${str.join('.')}.rules`; - }; - - const handleAddRule = (args: AddArgs) => { - const { level, groupIndex } = args; - const filtersCopy = clone(filters); - - const path = getRulePath(level, groupIndex); - const updatedFilters = setWith( - filtersCopy, - path, - [ - ...get(filtersCopy, path), + const path = getTypePath(); + const updatedFilters = setWith( + filtersCopy, + path, { - field: '', - operator: '', - uniqueId: nanoid(), - value: null, + ...get(filtersCopy, path), + type: value, }, - ], - clone, - ); + clone, + ); - setFilterHandler(updatedFilters); - }; - - const handleDeleteRule = (args: DeleteArgs) => { - const { uniqueId, level, groupIndex } = args; - const filtersCopy = clone(filters); - - const path = getRulePath(level, groupIndex); - const updatedFilters = setWith( - filtersCopy, - path, - get(filtersCopy, path).filter((rule: QueryBuilderRule) => rule.uniqueId !== uniqueId), - clone, - ); - - setFilterHandler(updatedFilters); - }; - - const handleChangeField = (args: any) => { - const { uniqueId, level, groupIndex, value } = args; - const filtersCopy = clone(filters); - - const path = getRulePath(level, groupIndex); - const updatedFilters = setWith( - filtersCopy, - path, - get(filtersCopy, path).map((rule: QueryBuilderGroup) => { - if (rule.uniqueId !== uniqueId) return rule; - return { - ...rule, - field: value, - operator: '', - value: '', - }; - }), - clone, - ); - - setFilterHandler(updatedFilters); - }; - - const handleChangeType = (args: any) => { - const { level, groupIndex, value } = args; - - const filtersCopy = clone(filters); - - if (level === 0) { - return setFilterHandler({ ...filtersCopy, type: value }); - } - - const getTypePath = () => { - const str = []; - for (let i = 0; i < groupIndex.length; i += 1) { - str.push(`group[${groupIndex[i]}]`); - } - - return `${str.join('.')}`; + return setFilterHandler(updatedFilters); }; - const path = getTypePath(); - const updatedFilters = setWith( - filtersCopy, - path, - { - ...get(filtersCopy, path), - type: value, - }, - clone, - ); + const handleChangeOperator = (args: any) => { + const { uniqueId, level, groupIndex, value } = args; + const filtersCopy = clone(filters); - return setFilterHandler(updatedFilters); - }; + const path = getRulePath(level, groupIndex); + const updatedFilters = setWith( + filtersCopy, + path, + get(filtersCopy, path).map((rule: QueryBuilderRule) => { + if (rule.uniqueId !== uniqueId) return rule; + return { + ...rule, + operator: value, + }; + }), + clone, + ); - const handleChangeOperator = (args: any) => { - const { uniqueId, level, groupIndex, value } = args; - const filtersCopy = clone(filters); + setFilterHandler(updatedFilters); + }; - const path = getRulePath(level, groupIndex); - const updatedFilters = setWith( - filtersCopy, - path, - get(filtersCopy, path).map((rule: QueryBuilderRule) => { - if (rule.uniqueId !== uniqueId) return rule; - return { - ...rule, - operator: value, - }; - }), - clone, - ); + const handleChangeValue = (args: any) => { + const { uniqueId, level, groupIndex, value } = args; + const filtersCopy = clone(filters); - setFilterHandler(updatedFilters); - }; + const path = getRulePath(level, groupIndex); + const updatedFilters = setWith( + filtersCopy, + path, + get(filtersCopy, path).map((rule: QueryBuilderRule) => { + if (rule.uniqueId !== uniqueId) return rule; + return { + ...rule, + value, + }; + }), + clone, + ); - const handleChangeValue = (args: any) => { - const { uniqueId, level, groupIndex, value } = args; - const filtersCopy = clone(filters); + setFilterHandler(updatedFilters); + }; - const path = getRulePath(level, groupIndex); - const updatedFilters = setWith( - filtersCopy, - path, - get(filtersCopy, path).map((rule: QueryBuilderRule) => { - if (rule.uniqueId !== uniqueId) return rule; - return { - ...rule, - value, - }; - }), - clone, - ); + const sortOptions = [ + { label: 'Random', type: 'string', value: 'random' }, + ...NDSongQueryFields, + ]; - setFilterHandler(updatedFilters); - }; - - const sortOptions = [{ label: 'Random', type: 'string', value: 'random' }, ...NDSongQueryFields]; - - return ( - - - - - + + + - - - - - - - + + + + {onSave && onSaveAs && ( + - - - - } - onClick={handleSave} - > - Save and replace - - - + + + + + + + } + onClick={handleSave} + > + Save and replace + + + + + )} - - - ); -}; + + ); + }, +);