From e3fdffde4aec72e7449879fc50241d3ae02146e5 Mon Sep 17 00:00:00 2001 From: Kim T Date: Sun, 22 Sep 2024 15:47:26 -0700 Subject: [PATCH] Project list page use shared components --- renderer/components/card.tsx | 31 ++-- renderer/components/filters.tsx | 2 +- renderer/components/list.tsx | 28 ++- renderer/components/tabs.tsx | 71 ++++++++ renderer/lib/project.ts | 34 ++-- renderer/pages/effects/[userId]/index.tsx | 2 +- renderer/pages/effects/index.tsx | 2 +- renderer/pages/instruments/[userId]/index.tsx | 2 +- renderer/pages/instruments/index.tsx | 2 +- renderer/pages/projects/index.tsx | 164 +++--------------- renderer/styles/components/tabs.module.css | 74 ++++++++ 11 files changed, 219 insertions(+), 193 deletions(-) create mode 100644 renderer/components/tabs.tsx create mode 100644 renderer/styles/components/tabs.module.css diff --git a/renderer/components/card.tsx b/renderer/components/card.tsx index 6a173c1..914eb77 100644 --- a/renderer/components/card.tsx +++ b/renderer/components/card.tsx @@ -1,22 +1,25 @@ import styles from '../styles/components/card.module.css'; import Link from 'next/link'; import { getBasePath } from '../lib/path'; -import { imageError } from '../lib/image'; import { pluginFileUrl } from '@studiorack/core'; type CardProps = { section: string; - plugin: any; - pluginIndex: number; + item: any; + index: number; }; -const Card = ({ section, plugin, pluginIndex }: CardProps) => ( - +const Card = ({ section, item, index }: CardProps) => ( +

- {plugin.name} v{plugin.version} + {item.name} v{item.version}

(
    Tags - {plugin.tags.map((tag: string, tagIndex: number) => ( -
  • + {item.tags.map((tag: string, tagIndex: number) => ( +
  • {tag} - {tagIndex !== plugin.tags.length - 1 ? ',' : ''} + {tagIndex !== item.tags.length - 1 ? ',' : ''}
  • ))}
- {plugin.files.image ? ( - {plugin.name} + {item.files.image ? ( + {item.name} ) : ( '' )} diff --git a/renderer/components/filters.tsx b/renderer/components/filters.tsx index cc7eb59..b44a2ef 100644 --- a/renderer/components/filters.tsx +++ b/renderer/components/filters.tsx @@ -10,7 +10,7 @@ type FiltersProps = { const Filters = ({ section }: FiltersProps) => { const router = useRouter(); - const search: string = router.query['search'] as string; + const search: string = (router.query['search'] as string) || ''; const onSearch = (event: ChangeEvent) => { const el: HTMLInputElement = event.target as HTMLInputElement; diff --git a/renderer/components/list.tsx b/renderer/components/list.tsx index e9453d1..5cd9192 100644 --- a/renderer/components/list.tsx +++ b/renderer/components/list.tsx @@ -1,28 +1,38 @@ import styles from '../styles/components/list.module.css'; -import { PluginVersion, PluginVersionLocal, ProjectVersion, ProjectVersionLocal } from '@studiorack/core'; +import { + PluginVersion, + PluginVersionLocal, + PluginTypes, + ProjectTypes, + ProjectVersion, + ProjectVersionLocal, +} from '@studiorack/core'; import Header from './header'; import Card from './card'; import Filters from './filters'; import Crumb from './crumb'; +import Tabs from './tabs'; type ListProps = { filters?: boolean; - plugins: PluginVersion[] | PluginVersionLocal[] | ProjectVersion[] | ProjectVersionLocal[]; + items: ListItem[]; type: string; + tabs?: PluginTypes | ProjectTypes; title: string; }; -const List = ({ filters = true, plugins, type, title }: ListProps) => ( +type ListItem = PluginVersion | PluginVersionLocal | ProjectVersion | ProjectVersionLocal; + +const List = ({ filters = true, items, type, tabs, title }: ListProps) => (
-
+
{filters ? : ''} + {tabs ? : ''}
- {plugins.map( - (plugin: PluginVersion | PluginVersionLocal | ProjectVersion | ProjectVersionLocal, pluginIndex: number) => ( - - ), - )} + {items.map((item: ListItem, index: number) => ( + + ))}
); diff --git a/renderer/components/tabs.tsx b/renderer/components/tabs.tsx new file mode 100644 index 0000000..2f3c5d6 --- /dev/null +++ b/renderer/components/tabs.tsx @@ -0,0 +1,71 @@ +import styles from '../styles/components/tabs.module.css'; +import { PluginType, PluginTypes, ProjectType, ProjectTypes } from '@studiorack/core'; +import { useRouter } from 'next/router'; +import { ChangeEvent } from 'react'; + +type TabsProps = { + items: PluginTypes | ProjectTypes; +}; + +type TabsItem = PluginType | ProjectType; +type TabsKey = keyof PluginTypes & keyof ProjectTypes; + +const Tabs = ({ items }: TabsProps) => { + const router = useRouter(); + let category: string = (router.query['category'] as string) || 'all'; + const search: string = (router.query['search'] as string) || ''; + + const isSelected = (path: string) => { + return category === path ? 'selected' : ''; + }; + + const onSearch = (event: ChangeEvent) => { + const el: HTMLInputElement = event.target as HTMLInputElement; + router.query['search'] = el.value ? el.value.toLowerCase() : ''; + router.push({ + pathname: router.pathname, + query: router.query, + }); + }; + + const selectCategory = (event: any) => { + category = (event.target as HTMLTextAreaElement).getAttribute('data-category') || ''; + router.query['category'] = category || ''; + router.push({ + pathname: router.pathname, + query: router.query, + }); + }; + + return ( +
+ +
+ ); +}; + +export default Tabs; diff --git a/renderer/lib/project.ts b/renderer/lib/project.ts index 67a7765..f5edc7c 100644 --- a/renderer/lib/project.ts +++ b/renderer/lib/project.ts @@ -1,23 +1,19 @@ -import { ProjectTypes, ProjectVersionLocal } from '@studiorack/core'; +import { ProjectTypes, ProjectVersion } from '@studiorack/core'; +import { NextRouter } from 'next/router'; -export function filterProjects( - category: string, - projects: ProjectVersionLocal[], - projectTypes: ProjectTypes, - query: string, -): ProjectVersionLocal[] { - console.log('filterProjects', category, projectTypes, query); - return projects.filter((project: ProjectVersionLocal) => { +export function filterProjects(projectTypes: ProjectTypes, projects: ProjectVersion[], router: NextRouter) { + const category: string = (router.query['category'] as string) || 'all'; + const search: string = router.query['search'] as string; + return projects.filter((project: ProjectVersion) => { + if (category !== 'all' && project.type?.ext !== projectTypes[category as keyof ProjectTypes]?.ext) return false; if ( - (category === 'all' || project.type?.ext === projectTypes[category as keyof ProjectTypes]?.ext) && - (project.author.toLowerCase().indexOf(query) !== -1 || - project.id?.toLowerCase().indexOf(query) !== -1 || - project.name.toLowerCase().indexOf(query) !== -1 || - project.description.toLowerCase().indexOf(query) !== -1 || - project.tags.includes(query)) - ) { - return project; - } - return false; + search && + project.id?.toLowerCase().indexOf(search.toLowerCase()) === -1 && + project.name?.toLowerCase().indexOf(search.toLowerCase()) === -1 && + project.description?.toLowerCase().indexOf(search.toLowerCase()) === -1 && + project.tags?.indexOf(search.toLowerCase()) === -1 + ) + return false; + return project; }); } diff --git a/renderer/pages/effects/[userId]/index.tsx b/renderer/pages/effects/[userId]/index.tsx index 9826ccc..9875fe2 100644 --- a/renderer/pages/effects/[userId]/index.tsx +++ b/renderer/pages/effects/[userId]/index.tsx @@ -34,7 +34,7 @@ class PluginList extends Component< {pageTitle(['Effects', this.state.userId])} - + ); } diff --git a/renderer/pages/effects/index.tsx b/renderer/pages/effects/index.tsx index 5e5956f..8741f33 100644 --- a/renderer/pages/effects/index.tsx +++ b/renderer/pages/effects/index.tsx @@ -22,7 +22,7 @@ const Effects = ({ plugins }: EffectsProps) => { {pageTitle(['Effects'])} - + ); }; diff --git a/renderer/pages/instruments/[userId]/index.tsx b/renderer/pages/instruments/[userId]/index.tsx index 411c282..1acb5af 100644 --- a/renderer/pages/instruments/[userId]/index.tsx +++ b/renderer/pages/instruments/[userId]/index.tsx @@ -34,7 +34,7 @@ class PluginList extends Component< {pageTitle(['Instruments', this.state.userId])} - + ); } diff --git a/renderer/pages/instruments/index.tsx b/renderer/pages/instruments/index.tsx index 4540bf2..153c1e5 100644 --- a/renderer/pages/instruments/index.tsx +++ b/renderer/pages/instruments/index.tsx @@ -22,7 +22,7 @@ const Instruments = ({ plugins }: InstrumentsProps) => { {pageTitle(['Instruments'])} - + ); }; diff --git a/renderer/pages/projects/index.tsx b/renderer/pages/projects/index.tsx index 5de82c8..3a88896 100644 --- a/renderer/pages/projects/index.tsx +++ b/renderer/pages/projects/index.tsx @@ -1,152 +1,31 @@ -import { Component, ChangeEvent } from 'react'; +import { configDefaults, ConfigList, projectsGetLocal, ProjectVersion, ProjectVersionLocal } from '@studiorack/core'; +import { useRouter } from 'next/router'; +import Layout from '../../components/layout'; import Head from 'next/head'; -import { withRouter, Router } from 'next/router'; -import Layout, { siteTitle } from '../../components/layout'; -import styles from '../../styles/plugins.module.css'; -import GridItem from '../../components/grid-item'; import { GetServerSideProps } from 'next'; -import { ProjectVersionLocal, ProjectTypes } from '@studiorack/core'; -import { projectsGetLocal } from '../../../node_modules/@studiorack/core/build/project'; -import { configDefaults } from '../../../node_modules/@studiorack/core/build/config-defaults'; +import { pageTitle } from '../../lib/utils'; +import List from '../../components/list'; import { filterProjects } from '../../lib/project'; -type ProjectListProps = { - category: string; - projectTypes: ProjectTypes; - projects: ProjectVersionLocal[]; - projectsFiltered: ProjectVersionLocal[]; - query: string; - router: Router; +type ProjectsProps = { + projects: ProjectVersion[]; }; -class ProjectList extends Component< - ProjectListProps, - { - category: string; - projectTypes: ProjectTypes; - projects: ProjectVersionLocal[]; - projectsFiltered: ProjectVersionLocal[]; - query: string; - router: Router; - } -> { - constructor(props: ProjectListProps) { - super(props); - const params = props.router.query; - const category = (params.category as string) || 'all'; - const projectTypes = configDefaults('appFolder', 'pluginFolder', 'presetFolder', 'projectFolder').projectTypes; - const projects = props.projects || []; - const query = (params.query as string) || ''; - this.state = { - category, - projectTypes, - projects, - projectsFiltered: filterProjects(category, projects, projectTypes, query), - query, - router: props.router, - }; - } - - componentDidUpdate(prevProps: any) { - const paramPrev = prevProps.router.query; - const params = this.props.router.query; - if (params.category !== paramPrev.category) { - this.setState({ category: params.category as string }, () => { - this.updateFilter(); - }); - } - if (params.query !== paramPrev.query) { - this.setState({ query: params.query as string }, () => { - this.updateFilter(); - }); - } - } - - updateFilter() { - this.setState({ - projectsFiltered: filterProjects( - this.state.category, - this.state.projects, - this.state.projectTypes, - this.state.query, - ), - }); - } - - updateUrl = (category: string, query: string) => { - this.state.router.push(`/projects?category=${category}&query=${query}`, undefined, { shallow: true }); - }; - - handleChange = (event: ChangeEvent) => { - const el = event.target as HTMLInputElement; - const query = el.value ? el.value.toLowerCase() : ''; - this.updateUrl(this.state.category, query); - }; - - isSelected = (path: string) => { - return this.state.category === path ? 'selected' : ''; - }; - - selectCategory = (event: React.MouseEvent): void => { - const category = (event.currentTarget as HTMLTextAreaElement).getAttribute('data-category') || ''; - this.updateUrl(category, this.state.query); - }; +const Projects = ({ projects }: ProjectsProps) => { + const router = useRouter(); + const projectTypes = configDefaults('appFolder', 'pluginFolder', 'presetFolder', 'projectFolder').projectTypes; + const projectsFiltered: ProjectVersion[] = filterProjects(projectTypes, projects, router); + return ( + + + {pageTitle(['Projects'])} + + + + ); +}; - render() { - return ( - - - {siteTitle} - -
-
-

- Projects ({this.state.projectsFiltered.length}) -

- -
-
- -
-
- {this.state.projectsFiltered.map((project: ProjectVersionLocal, projectIndex: number) => ( - - ))} -
-
-
- ); - } -} -export default withRouter(ProjectList); +export default Projects; export const getServerSideProps: GetServerSideProps = async () => { let projects = await projectsGetLocal(); @@ -156,7 +35,6 @@ export const getServerSideProps: GetServerSideProps = async () => { return { props: { projects, - projectsFiltered: projects, }, }; }; diff --git a/renderer/styles/components/tabs.module.css b/renderer/styles/components/tabs.module.css new file mode 100644 index 0000000..05fef14 --- /dev/null +++ b/renderer/styles/components/tabs.module.css @@ -0,0 +1,74 @@ +.tabs { + position: relative; +} + +.tabs:after { + background-image: linear-gradient(to right, rgba(255, 255, 255, 0), black 90%); + bottom: 0; + content: ''; + pointer-events: none; + position: absolute; + right: 0; + top: 0; + width: 10%; + z-index: 1; +} + +.tabsCategory { + display: flex; + list-style: none; + margin: 0 0 1rem; + overflow-y: auto; + padding: 0; +} + +.tabsCategory li { + display: inline-block; +} + +.tabsCategory a { + border: 0; + border-radius: 1rem; + cursor: pointer; + display: inline-block; + margin-right: 0.5rem; + padding: 0.5rem 0.75rem; + transition: background-color 0.25s ease-out; +} + +.tabsCategory a:hover { + background-color: #222; +} + +.tabsCategory :global(.selected) { + background-color: #fff; + color: #000; +} + +.tabsCategory :global(.selected):hover { + background-color: #fff; +} + +.tabsSearch { + background-color: #111; + border-radius: 0.25rem; + border: 0; + color: #999; + font-size: 1rem; + margin-bottom: 1rem; + padding: 0.5rem 1rem; + text-overflow: ellipsis; + width: 100%; +} + +.tabsSearch:focus { + outline: 1px solid #fff; +} + +@media (min-width: 48rem) { + .tabsSearch { + display: inline-block; + margin-right: 1rem; + max-width: 180px; + } +}