Skip to content

Commit

Permalink
[app] feat: library view resizable pane layout
Browse files Browse the repository at this point in the history
  • Loading branch information
adidahiya committed Nov 2, 2023
1 parent e28b85b commit 606f4a5
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 31 deletions.
3 changes: 2 additions & 1 deletion packages/music-library-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"date-fns": "^2.30.0",
"electron-squirrel-startup": "^1.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"react-resizable-panels": "^0.0.55"
},
"devDependencies": {
"@blueprintjs/node-build-scripts": "^8.1.0",
Expand Down
18 changes: 15 additions & 3 deletions packages/music-library-app/src/ui/libraryView.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { MusicLibraryPlist } from "@adahiya/music-library-tools-lib";
import { Button, Card, Divider, H5, NonIdealState } from "@blueprintjs/core";
import { Button, Card, H5, NonIdealState } from "@blueprintjs/core";
import { format } from "date-fns";
import type { IpcRendererEvent } from "electron";
import { useCallback, useEffect, useRef, useState } from "react";
import { PanelGroup, Panel } from "react-resizable-panels";

import type { ContextBridgeApi } from "../contextBridgeApi";
import { AUTO_LOAD_LIBRARY, DEBUG } from "../common/constants";
import type { LoadedSwinsianLibraryEventPayload } from "../events";

import PlaylistTable from "./playlistTable";
import styles from "./libraryView.module.scss";
import TrackTable from "./trackTable";
import ResizeHandle from "./resizeHandle";

declare global {
interface Window {
Expand All @@ -19,7 +22,7 @@ declare global {

type LibraryState = "none" | "loading" | "loaded" | "error";

export default function () {
export default function LibraryView() {
const [libraryState, setLibraryState] = useState<LibraryState>("none");
const [library, setLibrary] = useState<MusicLibraryPlist | undefined>(undefined);

Expand Down Expand Up @@ -96,10 +99,19 @@ function Library(props: { library: MusicLibraryPlist }) {
)}
<p># playlists: {formatStatNumber(props.library.Playlists.length)}</p>
</div>
<PlaylistTable headerHeight={headerHeight} library={props.library} />
<PanelGroup direction="horizontal">
<Panel defaultSize={20} minSize={20}>
<PlaylistTable headerHeight={headerHeight} library={props.library} />
</Panel>
<ResizeHandle />
<Panel minSize={30}>
<TrackTable />
</Panel>
</PanelGroup>
</div>
);
}
LibraryView.displayName = "LibraryView";

function getMasterPlaylist(library: MusicLibraryPlist) {
return library.Playlists.find((playlist) => playlist.Master);
Expand Down
70 changes: 43 additions & 27 deletions packages/music-library-app/src/ui/playlistTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@ import classNames from "classnames";
export interface LibraryTableProps {
headerHeight: number;
library: MusicLibraryPlist;
/** @default false */
showItemCounts?: boolean;
/** @default false */
showHeader?: boolean;
/** @default false */
showFooter?: boolean;
}

interface PlaylistRow {
def: PlaylistDefinition;
children: PlaylistRow[];
}

export default function (props: LibraryTableProps) {
export default function PlaylistTable(props: LibraryTableProps) {
// TODO: cleanup
// const playlistsByPersistentId = useMemo<Record<string, PlaylistDefinition>>(
// () =>
Expand Down Expand Up @@ -111,15 +117,20 @@ export default function (props: LibraryTableProps) {
cell: (info) => <i>{info.getValue()}</i>,
header: () => <span># tracks</span>,
footer: (info) => info.column.id,
enableHiding: true,
}),
];

const [expanded, setExpanded] = useState<ExpandedState>({});
const [columnVisibility, setColumnVisibility] = useState({
numberOfTracks: props.showItemCounts ?? false,
});

const table = useReactTable({
data: playlistRows,
columns,
state: {
columnVisibility,
expanded,
},
onExpandedChange: setExpanded,
Expand Down Expand Up @@ -154,11 +165,13 @@ export default function (props: LibraryTableProps) {

return (
<div className={styles.container}>
<div className={styles.header}>
<HTMLTable compact={true}>
<thead>{headerRows}</thead>
</HTMLTable>
</div>
{props.showHeader && (
<div className={styles.header}>
<HTMLTable compact={true}>
<thead>{headerRows}</thead>
</HTMLTable>
</div>
)}
<div
className={styles.body}
style={{ maxHeight: `calc(100vh - ${props.headerHeight + 50 + 75}px)` }}
Expand Down Expand Up @@ -188,27 +201,30 @@ export default function (props: LibraryTableProps) {
</tbody>
</HTMLTable>
</div>
<div className={styles.footer}>
<HTMLTable className={styles.footer} compact={true}>
<thead>{headerRows}</thead>
<tfoot>
{table.getFooterGroups().map((footerGroup) => (
<tr key={footerGroup.id}>
{footerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.footer,
header.getContext(),
)}
</th>
))}
</tr>
))}
</tfoot>
</HTMLTable>
</div>
{props.showFooter && (
<div className={styles.footer}>
<HTMLTable className={styles.footer} compact={true}>
<thead>{headerRows}</thead>
<tfoot>
{table.getFooterGroups().map((footerGroup) => (
<tr key={footerGroup.id}>
{footerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.footer,
header.getContext(),
)}
</th>
))}
</tr>
))}
</tfoot>
</HTMLTable>
</div>
)}
</div>
);
}
PlaylistTable.displayName = "PlaylistTable";
30 changes: 30 additions & 0 deletions packages/music-library-app/src/ui/resizeHandle.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.ResizeHandleOuter {
flex: 0 0 1.5em;
position: relative;
outline: none;

--background-color: transparent;
}

.ResizeHandleOuter[data-resize-handle-active] {
--background-color: var(--color-solid-resize-bar-handle);
}

.ResizeHandleInner {
position: absolute;
top: 0.25em;
bottom: 0.25em;
left: 0.25em;
right: 0.25em;
border-radius: 0.25em;
background-color: var(--background-color);
transition: background-color 0.2s linear;
}

.Icon {
width: 1em;
height: 1em;
position: absolute;
left: calc(50% - 0.5rem);
top: calc(50% - 0.5rem);
}
19 changes: 19 additions & 0 deletions packages/music-library-app/src/ui/resizeHandle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { PanelResizeHandle } from "react-resizable-panels";

import styles from "./resizeHandle.module.scss";

export default function ResizeHandle({ className = "", id }: { className?: string; id?: string }) {
return (
<PanelResizeHandle className={[styles.ResizeHandleOuter, className].join(" ")} id={id}>
<div className={styles.ResizeHandleInner}>
<svg className={styles.Icon} viewBox="0 0 24 24">
<path
fill="currentColor"
d="M8,18H11V15H2V13H22V15H13V18H16L12,22L8,18M12,2L8,6H11V9H2V11H22V9H13V6H16L12,2Z"
/>
</svg>
</div>
</PanelResizeHandle>
);
}
ResizeHandle.displayName = "ResizeHandle";
11 changes: 11 additions & 0 deletions packages/music-library-app/src/ui/trackTable.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@use "@blueprintjs/core/lib/scss/variables.scss" as bp;

.container {
height: 100%;
display: flex;
flex-direction: column;

:global(.#{bp.$ns}-non-ideal-state) {
flex-grow: 1;
}
}
12 changes: 12 additions & 0 deletions packages/music-library-app/src/ui/trackTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { NonIdealState } from "@blueprintjs/core";

import styles from "./trackTable.module.scss";

export default function TrackTable() {
return (
<div className={styles.container}>
<NonIdealState title="Track table" description="(unimplemented)" />
</div>
);
}
TrackTable.displayName = "TrackTable";
11 changes: 11 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ __metadata:
electron-squirrel-startup: "npm:^1.0.0"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
react-resizable-panels: "npm:^0.0.55"
sass: "npm:^1.69.5"
typescript: "npm:^5.2.2"
languageName: unknown
Expand Down Expand Up @@ -8501,6 +8502,16 @@ __metadata:
languageName: node
linkType: hard

"react-resizable-panels@npm:^0.0.55":
version: 0.0.55
resolution: "react-resizable-panels@npm:0.0.55"
peerDependencies:
react: ^16.14.0 || ^17.0.0 || ^18.0.0
react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0
checksum: 9b83287d2c962ff87cddae85b5233aaf61c0ff3ac30e29fcc51ac1a0ae5ea45fe78092ecf79b2e4a9dd898ca95c43dd698295b72e737d9275daac85c1ae59339
languageName: node
linkType: hard

"react-transition-group@npm:^4.4.5":
version: 4.4.5
resolution: "react-transition-group@npm:4.4.5"
Expand Down

0 comments on commit 606f4a5

Please sign in to comment.