@behindthemusictree/app-kit-ui 34.1.0 → 35.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JP7PID7L.js → chunk-YC3WBUVO.js} +453 -445
- package/dist/chunk-YC3WBUVO.js.map +1 -0
- package/dist/genre-tree/index.cjs +505 -498
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +20 -16
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +505 -498
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-JP7PID7L.js.map +0 -1
|
@@ -46,13 +46,13 @@ __export(genre_tree_exports, {
|
|
|
46
46
|
module.exports = __toCommonJS(genre_tree_exports);
|
|
47
47
|
|
|
48
48
|
// src/genre-tree/GenreTreeView.tsx
|
|
49
|
-
var
|
|
49
|
+
var import_react18 = require("react");
|
|
50
50
|
var import_react_query3 = require("@tanstack/react-query");
|
|
51
|
-
var import_lucide_react11 = require("lucide-react");
|
|
52
51
|
var import_lucide_react12 = require("lucide-react");
|
|
53
|
-
var
|
|
54
|
-
var
|
|
55
|
-
var
|
|
52
|
+
var import_lucide_react13 = require("lucide-react");
|
|
53
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
54
|
+
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
55
|
+
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
56
56
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
57
57
|
|
|
58
58
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -915,6 +915,7 @@ function GenreTreeSearch({
|
|
|
915
915
|
artistSearch,
|
|
916
916
|
songSearch,
|
|
917
917
|
SearchGroups,
|
|
918
|
+
onSelectGenre,
|
|
918
919
|
extraGroups = [],
|
|
919
920
|
onQueryChange,
|
|
920
921
|
onFocus,
|
|
@@ -940,9 +941,16 @@ function GenreTreeSearch({
|
|
|
940
941
|
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
941
942
|
}
|
|
942
943
|
);
|
|
943
|
-
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
944
|
+
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
|
|
944
945
|
}
|
|
945
946
|
|
|
947
|
+
// src/genre-tree/GenreDetailPanel.tsx
|
|
948
|
+
var import_react14 = require("react");
|
|
949
|
+
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
950
|
+
var import_ui7 = require("@behindthemusictree/ui");
|
|
951
|
+
var import_lucide_react8 = require("lucide-react");
|
|
952
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
953
|
+
|
|
946
954
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
947
955
|
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
948
956
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
@@ -1391,6 +1399,7 @@ var nextCursor = (next) => {
|
|
|
1391
1399
|
function GenrePlaylistTracks({
|
|
1392
1400
|
genreUuid,
|
|
1393
1401
|
genrePlaylist,
|
|
1402
|
+
genreName: selectedGenreName,
|
|
1394
1403
|
selection,
|
|
1395
1404
|
scope,
|
|
1396
1405
|
getBackendBaseUrl,
|
|
@@ -1406,13 +1415,17 @@ function GenrePlaylistTracks({
|
|
|
1406
1415
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1407
1416
|
const { showPopup } = (0, import_popup4.usePopup)();
|
|
1408
1417
|
const trackNoun = (0, import_track_noun.useTrackNoun)();
|
|
1409
|
-
const
|
|
1418
|
+
const uuid = genrePlaylist?.uuid;
|
|
1419
|
+
const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
|
|
1410
1420
|
const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
|
|
1411
1421
|
const playback = (0, import_transport3.usePlaybackContext)();
|
|
1412
1422
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1413
|
-
queryKey: [...scope === "me" ? import_genre_tree12.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1423
|
+
queryKey: [...scope === "me" ? uuid ? import_genre_tree12.genrePlaylistQueryKeys.me.tracks(uuid) : ["missing-personal-genre-playlist", genreUuid] : artist ? [...import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1414
1424
|
queryFn: async ({ pageParam }) => {
|
|
1415
|
-
if (scope === "me")
|
|
1425
|
+
if (scope === "me") {
|
|
1426
|
+
if (!uuid) throw new Error("Personal genre has no playlist");
|
|
1427
|
+
return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1428
|
+
}
|
|
1416
1429
|
const response = await fetch(
|
|
1417
1430
|
import_genre_tree13.libraryEndpoints.reference.songs.list(),
|
|
1418
1431
|
true,
|
|
@@ -1425,7 +1438,7 @@ function GenrePlaylistTracks({
|
|
|
1425
1438
|
},
|
|
1426
1439
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1427
1440
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1428
|
-
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1441
|
+
enabled: playback.ready && (scope !== "me" || uuid !== void 0 && sessionRestored && !!session?.accessToken)
|
|
1429
1442
|
});
|
|
1430
1443
|
const [sentinel, setSentinel] = (0, import_react12.useState)(null);
|
|
1431
1444
|
(0, import_react12.useEffect)(() => {
|
|
@@ -1447,7 +1460,7 @@ function GenrePlaylistTracks({
|
|
|
1447
1460
|
ordering: "title",
|
|
1448
1461
|
genreUuid,
|
|
1449
1462
|
...selection && { selection }
|
|
1450
|
-
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name:
|
|
1463
|
+
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genreName }, scope, { startSongUuid, ...selection && { selection } }) : genrePlaylist ? playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid }) : Promise.reject(new Error("Personal genre has no playlist"));
|
|
1451
1464
|
started.catch((error) => {
|
|
1452
1465
|
console.error("Failed to play genre playlist:", error);
|
|
1453
1466
|
showPopup(/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
|
|
@@ -1455,13 +1468,13 @@ function GenrePlaylistTracks({
|
|
|
1455
1468
|
};
|
|
1456
1469
|
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui5.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { children: [
|
|
1457
1470
|
renderTracksToolbar?.(),
|
|
1458
|
-
isPending ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui5.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { role: "alert", children: [
|
|
1471
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui5.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { role: "alert", children: [
|
|
1459
1472
|
"Couldn't load ",
|
|
1460
1473
|
trackNoun.plural,
|
|
1461
1474
|
".",
|
|
1462
1475
|
" ",
|
|
1463
1476
|
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1464
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${
|
|
1477
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
|
|
1465
1478
|
tracks.map((track, index) => {
|
|
1466
1479
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1467
1480
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
@@ -1528,6 +1541,7 @@ var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
|
1528
1541
|
function GenreDetailExtras({
|
|
1529
1542
|
genreUuid,
|
|
1530
1543
|
genrePlaylist,
|
|
1544
|
+
genreName,
|
|
1531
1545
|
selection,
|
|
1532
1546
|
wikidataId,
|
|
1533
1547
|
scope,
|
|
@@ -1595,6 +1609,7 @@ function GenreDetailExtras({
|
|
|
1595
1609
|
{
|
|
1596
1610
|
genreUuid,
|
|
1597
1611
|
genrePlaylist,
|
|
1612
|
+
genreName,
|
|
1598
1613
|
selection,
|
|
1599
1614
|
scope,
|
|
1600
1615
|
getBackendBaseUrl,
|
|
@@ -1636,6 +1651,7 @@ function GenreInfoPanelBody({
|
|
|
1636
1651
|
genreUuid,
|
|
1637
1652
|
genrePlaylist,
|
|
1638
1653
|
catalogue,
|
|
1654
|
+
countsContext,
|
|
1639
1655
|
wikidataId,
|
|
1640
1656
|
scope,
|
|
1641
1657
|
getBackendBaseUrl,
|
|
@@ -1644,7 +1660,7 @@ function GenreInfoPanelBody({
|
|
|
1644
1660
|
extras
|
|
1645
1661
|
}) {
|
|
1646
1662
|
const noun = (0, import_track_noun3.useTrackNoun)();
|
|
1647
|
-
const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist) : void 0;
|
|
1663
|
+
const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
1648
1664
|
const selection = summary ? (0, import_genre_tree18.defaultGenreMusicSelection)(summary) : void 0;
|
|
1649
1665
|
const describeCounts = (counts) => {
|
|
1650
1666
|
const scopes = counts.genreAndSubgenresTracksCount;
|
|
@@ -1652,17 +1668,19 @@ function GenreInfoPanelBody({
|
|
|
1652
1668
|
};
|
|
1653
1669
|
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
1654
1670
|
toolbar,
|
|
1655
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1671
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(GenreActionBand, { ...band }),
|
|
1656
1672
|
summary && catalogue && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1657
1673
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { children: [
|
|
1658
1674
|
"Catalogue ",
|
|
1659
1675
|
noun.plural,
|
|
1676
|
+
countsContext ? ` (${countsContext})` : "",
|
|
1660
1677
|
": ",
|
|
1661
1678
|
describeCounts(catalogue)
|
|
1662
1679
|
] }),
|
|
1663
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { children: [
|
|
1680
|
+
genrePlaylist && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { children: [
|
|
1664
1681
|
"Playlist ",
|
|
1665
1682
|
noun.plural,
|
|
1683
|
+
countsContext ? ` (${countsContext})` : "",
|
|
1666
1684
|
": ",
|
|
1667
1685
|
describeCounts(summary)
|
|
1668
1686
|
] })
|
|
@@ -1675,6 +1693,7 @@ function GenreInfoPanelBody({
|
|
|
1675
1693
|
family,
|
|
1676
1694
|
genreUuid,
|
|
1677
1695
|
genrePlaylist,
|
|
1696
|
+
genreName: band.node.name,
|
|
1678
1697
|
selection,
|
|
1679
1698
|
wikidataId,
|
|
1680
1699
|
scope,
|
|
@@ -1684,10 +1703,160 @@ function GenreInfoPanelBody({
|
|
|
1684
1703
|
] });
|
|
1685
1704
|
}
|
|
1686
1705
|
|
|
1687
|
-
// src/genre-tree/
|
|
1688
|
-
var import_react14 = require("react");
|
|
1689
|
-
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1706
|
+
// src/genre-tree/GenreDetailPanel.tsx
|
|
1690
1707
|
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1708
|
+
var RELATIVE_ITEM_COUNT = 1;
|
|
1709
|
+
function toNode(genre, parentId) {
|
|
1710
|
+
return {
|
|
1711
|
+
id: genre.uuid,
|
|
1712
|
+
parentId,
|
|
1713
|
+
name: genre.name,
|
|
1714
|
+
itemCount: RELATIVE_ITEM_COUNT,
|
|
1715
|
+
side: genre.side ?? void 0,
|
|
1716
|
+
wikidataId: genre.wikidataId
|
|
1717
|
+
};
|
|
1718
|
+
}
|
|
1719
|
+
function useGenreDetailContent({
|
|
1720
|
+
genreUuid,
|
|
1721
|
+
scope,
|
|
1722
|
+
getBackendBaseUrl,
|
|
1723
|
+
onSelectGenre,
|
|
1724
|
+
extras,
|
|
1725
|
+
band,
|
|
1726
|
+
playback: hostPlayback,
|
|
1727
|
+
trailing,
|
|
1728
|
+
genrePlaylist: hostPlaylist,
|
|
1729
|
+
expanded
|
|
1730
|
+
}) {
|
|
1731
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree19.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
1732
|
+
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
1733
|
+
const playback = hostPlayback ?? defaultPlayback;
|
|
1734
|
+
const view = (0, import_react14.useMemo)(() => {
|
|
1735
|
+
if (!detail) return null;
|
|
1736
|
+
const rootColor = (0, import_genre_tree_view4.getGenreTreeColor)(detail.root.wikidataId);
|
|
1737
|
+
const relative = (genre, parentId) => ({
|
|
1738
|
+
node: toNode(genre, parentId),
|
|
1739
|
+
fill: (0, import_genre_tree_view4.tintSurface)(rootColor),
|
|
1740
|
+
textColor: import_genre_tree_view4.TEXT_COLOR
|
|
1741
|
+
});
|
|
1742
|
+
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
1743
|
+
return {
|
|
1744
|
+
node: {
|
|
1745
|
+
id: detail.uuid,
|
|
1746
|
+
parentId: detail.parent?.uuid ?? null,
|
|
1747
|
+
name: detail.name,
|
|
1748
|
+
itemCount: scope === "reference" ? (0, import_genre_tree19.genreMusicCount)(detail.criteriaPlaylist ? import_genre_tree19.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : import_genre_tree19.GenreCatalogueCountsSchema.parse(detail.catalogue)) : detail.tracksCount,
|
|
1749
|
+
wikidataId: detail.wikidataId
|
|
1750
|
+
},
|
|
1751
|
+
fill: rootColor,
|
|
1752
|
+
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
1753
|
+
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
1754
|
+
ancestorNodes: ancestors2.map(
|
|
1755
|
+
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
1756
|
+
)
|
|
1757
|
+
};
|
|
1758
|
+
}, [detail, scope]);
|
|
1759
|
+
if (isError) return {
|
|
1760
|
+
title: "Genre unavailable",
|
|
1761
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
1762
|
+
};
|
|
1763
|
+
if (isPending || !detail || !view) return {
|
|
1764
|
+
title: "Loading genre\u2026",
|
|
1765
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
1766
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui7.Skeleton, { className: "h-6 w-2/3" }),
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui7.Skeleton, { className: "h-4 w-full" }),
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui7.Skeleton, { className: "h-4 w-3/4" })
|
|
1769
|
+
] })
|
|
1770
|
+
};
|
|
1771
|
+
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree19.GenreUuidSchema.parse(nodeId));
|
|
1772
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1773
|
+
import_genre_tree_view4.GenreNodeFamily,
|
|
1774
|
+
{
|
|
1775
|
+
node: view.node,
|
|
1776
|
+
fill: view.fill,
|
|
1777
|
+
textColor: import_genre_tree_view4.ACCENT_TEXT_COLOR,
|
|
1778
|
+
parentNode: view.parentNode,
|
|
1779
|
+
childNodes: view.childNodes,
|
|
1780
|
+
onSelectNode: selectGenre
|
|
1781
|
+
}
|
|
1782
|
+
);
|
|
1783
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
1784
|
+
return {
|
|
1785
|
+
title: detail.name,
|
|
1786
|
+
headerAccent: { background: view.fill, color: import_genre_tree_view4.ACCENT_TEXT_COLOR },
|
|
1787
|
+
aside: expanded ? ancestors : void 0,
|
|
1788
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1789
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1790
|
+
GenreInfoPanelBody,
|
|
1791
|
+
{
|
|
1792
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1793
|
+
family,
|
|
1794
|
+
!expanded && ancestors
|
|
1795
|
+
] }),
|
|
1796
|
+
genreUuid: detail.uuid,
|
|
1797
|
+
genrePlaylist: hostPlaylist ?? (detail.criteriaPlaylist === null ? null : scope === "reference" ? { ...import_genre_tree19.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist), criteria: import_genre_tree19.CriteriaMinimumSchema.parse(detail) } : { ...detail.criteriaPlaylist, criteria: import_genre_tree19.CriteriaMinimumSchema.parse(detail), tracksCount: detail.tracksCount }),
|
|
1798
|
+
catalogue: detail.catalogue,
|
|
1799
|
+
countsContext: !hostPlaylist && extras?.artist ? "all artists" : void 0,
|
|
1800
|
+
wikidataId: detail.wikidataId,
|
|
1801
|
+
scope,
|
|
1802
|
+
getBackendBaseUrl,
|
|
1803
|
+
playback,
|
|
1804
|
+
band: { node: view.node, ...band, shareTarget: band?.shareTarget?.kind === "genre" ? { ...band.shareTarget, name: detail.name } : band?.shareTarget },
|
|
1805
|
+
extras
|
|
1806
|
+
}
|
|
1807
|
+
),
|
|
1808
|
+
trailing
|
|
1809
|
+
] })
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
function GenreDetailPanel({
|
|
1813
|
+
genreUuid,
|
|
1814
|
+
scope,
|
|
1815
|
+
getBackendBaseUrl,
|
|
1816
|
+
onClose,
|
|
1817
|
+
onSelectGenre,
|
|
1818
|
+
onZoomIn,
|
|
1819
|
+
shareUrl,
|
|
1820
|
+
shareTarget,
|
|
1821
|
+
collapsed,
|
|
1822
|
+
expanded,
|
|
1823
|
+
onExpandedChange,
|
|
1824
|
+
edgeTab,
|
|
1825
|
+
extras,
|
|
1826
|
+
headerClassName
|
|
1827
|
+
}) {
|
|
1828
|
+
const panel = useGenreDetailContent({
|
|
1829
|
+
genreUuid,
|
|
1830
|
+
scope,
|
|
1831
|
+
getBackendBaseUrl,
|
|
1832
|
+
onSelectGenre,
|
|
1833
|
+
extras,
|
|
1834
|
+
expanded,
|
|
1835
|
+
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
1836
|
+
});
|
|
1837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1838
|
+
import_ui7.DetailPanel,
|
|
1839
|
+
{
|
|
1840
|
+
side: "left",
|
|
1841
|
+
title: panel.title,
|
|
1842
|
+
headerAccent: panel.headerAccent,
|
|
1843
|
+
onClose,
|
|
1844
|
+
collapsed,
|
|
1845
|
+
expanded,
|
|
1846
|
+
onExpandedChange,
|
|
1847
|
+
edgeTab,
|
|
1848
|
+
aside: panel.aside,
|
|
1849
|
+
headerClassName,
|
|
1850
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui7.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react8.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_lucide_react8.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
1851
|
+
children: panel.content
|
|
1852
|
+
}
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// src/genre-tree/ChangeParentPopup.tsx
|
|
1857
|
+
var import_react15 = require("react");
|
|
1858
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1859
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1691
1860
|
function ChangeParentPopup({
|
|
1692
1861
|
genrePlaylist,
|
|
1693
1862
|
genrePlaylists,
|
|
@@ -1696,9 +1865,9 @@ function ChangeParentPopup({
|
|
|
1696
1865
|
onClose,
|
|
1697
1866
|
onParentChanged
|
|
1698
1867
|
}) {
|
|
1699
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1700
|
-
const candidates = (0,
|
|
1701
|
-
() => (0,
|
|
1868
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree20.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1869
|
+
const candidates = (0, import_react15.useMemo)(
|
|
1870
|
+
() => (0, import_genre_tree20.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1702
1871
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1703
1872
|
);
|
|
1704
1873
|
const handleSelect = (newParent) => {
|
|
@@ -1715,19 +1884,19 @@ function ChangeParentPopup({
|
|
|
1715
1884
|
}
|
|
1716
1885
|
);
|
|
1717
1886
|
};
|
|
1718
|
-
(0,
|
|
1887
|
+
(0, import_react15.useEffect)(() => {
|
|
1719
1888
|
const handleKeyDown = (event) => {
|
|
1720
1889
|
if (event.key === "Escape") onClose();
|
|
1721
1890
|
};
|
|
1722
1891
|
window.addEventListener("keydown", handleKeyDown);
|
|
1723
1892
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1724
1893
|
}, [onClose]);
|
|
1725
|
-
return /* @__PURE__ */ (0,
|
|
1726
|
-
/* @__PURE__ */ (0,
|
|
1894
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1895
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1727
1896
|
"Current parent: ",
|
|
1728
1897
|
genrePlaylist.parent?.name ?? "none"
|
|
1729
1898
|
] }),
|
|
1730
|
-
/* @__PURE__ */ (0,
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1731
1900
|
GenreSearch,
|
|
1732
1901
|
{
|
|
1733
1902
|
genrePlaylists: candidates,
|
|
@@ -1735,24 +1904,24 @@ function ChangeParentPopup({
|
|
|
1735
1904
|
placeholder: "Search a new parent\u2026"
|
|
1736
1905
|
}
|
|
1737
1906
|
) }),
|
|
1738
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1907
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1739
1908
|
] });
|
|
1740
1909
|
}
|
|
1741
1910
|
|
|
1742
1911
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1743
|
-
var
|
|
1744
|
-
var
|
|
1912
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
1913
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1745
1914
|
|
|
1746
1915
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1747
|
-
var
|
|
1916
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1748
1917
|
|
|
1749
1918
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
1750
|
-
var
|
|
1919
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1751
1920
|
var import_si3 = require("react-icons/si");
|
|
1752
|
-
var
|
|
1753
|
-
var
|
|
1921
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1922
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1754
1923
|
var LINK_KINDS = {
|
|
1755
|
-
official: { label: "Official website", Icon:
|
|
1924
|
+
official: { label: "Official website", Icon: import_lucide_react9.Globe },
|
|
1756
1925
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
1757
1926
|
wikidata: { label: "Wikidata", Icon: import_si3.SiWikidata },
|
|
1758
1927
|
spotify: { label: "Spotify", Icon: import_si3.SiSpotify },
|
|
@@ -1760,7 +1929,7 @@ var LINK_KINDS = {
|
|
|
1760
1929
|
bandcamp: { label: "Bandcamp", Icon: import_si3.SiBandcamp },
|
|
1761
1930
|
soundcloud: { label: "SoundCloud", Icon: import_si3.SiSoundcloud },
|
|
1762
1931
|
discogs: { label: "Discogs", Icon: import_si3.SiDiscogs },
|
|
1763
|
-
social: { label: "Social", Icon:
|
|
1932
|
+
social: { label: "Social", Icon: import_lucide_react9.Share2 }
|
|
1764
1933
|
};
|
|
1765
1934
|
function socialLabel(url) {
|
|
1766
1935
|
try {
|
|
@@ -1787,7 +1956,7 @@ function artistLinks(artist) {
|
|
|
1787
1956
|
return links;
|
|
1788
1957
|
}
|
|
1789
1958
|
function ArtistLinks({ links }) {
|
|
1790
|
-
return /* @__PURE__ */ (0,
|
|
1959
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1791
1960
|
}
|
|
1792
1961
|
|
|
1793
1962
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1798,7 +1967,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1798
1967
|
}
|
|
1799
1968
|
|
|
1800
1969
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1801
|
-
var
|
|
1970
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1802
1971
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1803
1972
|
function ArtistHeader({ artist }) {
|
|
1804
1973
|
const fields = [];
|
|
@@ -1808,35 +1977,35 @@ function ArtistHeader({ artist }) {
|
|
|
1808
1977
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1809
1978
|
if (years) fields.push({ label: "Active", value: years });
|
|
1810
1979
|
const links = artistLinks(artist);
|
|
1811
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
1980
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ArtistLinks, { links }) });
|
|
1812
1981
|
if (fields.length === 0) return null;
|
|
1813
|
-
return /* @__PURE__ */ (0,
|
|
1982
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.FieldList, { items: fields });
|
|
1814
1983
|
}
|
|
1815
1984
|
|
|
1816
1985
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1817
|
-
var
|
|
1986
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1818
1987
|
function ExternalLink({ href, children }) {
|
|
1819
|
-
return /* @__PURE__ */ (0,
|
|
1988
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1820
1989
|
}
|
|
1821
1990
|
function ArtistPhoto({ image, name }) {
|
|
1822
|
-
return /* @__PURE__ */ (0,
|
|
1823
|
-
/* @__PURE__ */ (0,
|
|
1824
|
-
/* @__PURE__ */ (0,
|
|
1991
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1992
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1993
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1825
1994
|
"Photo: ",
|
|
1826
|
-
/* @__PURE__ */ (0,
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1827
1996
|
",",
|
|
1828
1997
|
" ",
|
|
1829
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
1998
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1830
1999
|
] })
|
|
1831
2000
|
] });
|
|
1832
2001
|
}
|
|
1833
2002
|
|
|
1834
2003
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1835
|
-
var
|
|
1836
|
-
var
|
|
2004
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
2005
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1837
2006
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1838
2007
|
if (memberships.length === 0) return null;
|
|
1839
|
-
return /* @__PURE__ */ (0,
|
|
2008
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
1840
2009
|
const details = [
|
|
1841
2010
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1842
2011
|
...membership.roles
|
|
@@ -1844,9 +2013,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1844
2013
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1845
2014
|
return (
|
|
1846
2015
|
// A member can have several periods in the same group.
|
|
1847
|
-
/* @__PURE__ */ (0,
|
|
1848
|
-
/* @__PURE__ */ (0,
|
|
1849
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2016
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2017
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2018
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1850
2019
|
" \u2014 ",
|
|
1851
2020
|
detailsText
|
|
1852
2021
|
] })
|
|
@@ -1857,9 +2026,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1857
2026
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1858
2027
|
if (memberships.length === 0) return null;
|
|
1859
2028
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1860
|
-
return /* @__PURE__ */ (0,
|
|
1861
|
-
/* @__PURE__ */ (0,
|
|
1862
|
-
/* @__PURE__ */ (0,
|
|
2029
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_ui10.PanelSection, { title, children: [
|
|
2030
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2031
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1863
2032
|
MembershipList,
|
|
1864
2033
|
{
|
|
1865
2034
|
title: "Past",
|
|
@@ -1871,8 +2040,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1871
2040
|
}
|
|
1872
2041
|
|
|
1873
2042
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1874
|
-
var
|
|
1875
|
-
var
|
|
2043
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
2044
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1876
2045
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1877
2046
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1878
2047
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1892,34 +2061,34 @@ function byYear(a, b) {
|
|
|
1892
2061
|
function ArtistDiscography({ albums }) {
|
|
1893
2062
|
if (albums.length === 0) return null;
|
|
1894
2063
|
const sorted = [...albums].sort(byYear);
|
|
1895
|
-
return /* @__PURE__ */ (0,
|
|
2064
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
1896
2065
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1897
2066
|
if (groupAlbums.length === 0) return null;
|
|
1898
|
-
return /* @__PURE__ */ (0,
|
|
1899
|
-
/* @__PURE__ */ (0,
|
|
1900
|
-
/* @__PURE__ */ (0,
|
|
2067
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui11.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2069
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1901
2070
|
] }, album.uuid)) }) }, group);
|
|
1902
2071
|
}) });
|
|
1903
2072
|
}
|
|
1904
2073
|
|
|
1905
2074
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1906
|
-
var
|
|
2075
|
+
var import_react16 = require("react");
|
|
1907
2076
|
var import_react_query2 = require("@tanstack/react-query");
|
|
1908
|
-
var
|
|
1909
|
-
var
|
|
2077
|
+
var import_lucide_react10 = require("lucide-react");
|
|
2078
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
1910
2079
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
1911
|
-
var
|
|
2080
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1912
2081
|
var import_transport7 = require("@behindthemusictree/app-kit/transport");
|
|
1913
2082
|
|
|
1914
2083
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1915
2084
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
1916
|
-
var
|
|
2085
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1917
2086
|
|
|
1918
2087
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
1919
2088
|
var import_fa3 = require("react-icons/fa");
|
|
1920
2089
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1921
2090
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
1922
|
-
var
|
|
2091
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1923
2092
|
function TrackPositionPlayPause({
|
|
1924
2093
|
position,
|
|
1925
2094
|
uuid,
|
|
@@ -1933,14 +2102,14 @@ function TrackPositionPlayPause({
|
|
|
1933
2102
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
1934
2103
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
1935
2104
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1936
|
-
return /* @__PURE__ */ (0,
|
|
2105
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1937
2106
|
"div",
|
|
1938
2107
|
{
|
|
1939
2108
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1940
2109
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1941
2110
|
onClick: handlePlayPauseClick,
|
|
1942
2111
|
children: [
|
|
1943
|
-
/* @__PURE__ */ (0,
|
|
2112
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1944
2113
|
"span",
|
|
1945
2114
|
{
|
|
1946
2115
|
className: "track-position-play-pause__bars",
|
|
@@ -1948,18 +2117,18 @@ function TrackPositionPlayPause({
|
|
|
1948
2117
|
role: "img",
|
|
1949
2118
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1950
2119
|
children: [
|
|
1951
|
-
/* @__PURE__ */ (0,
|
|
1952
|
-
/* @__PURE__ */ (0,
|
|
1953
|
-
/* @__PURE__ */ (0,
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
1954
2123
|
]
|
|
1955
2124
|
}
|
|
1956
2125
|
) : position }),
|
|
1957
|
-
/* @__PURE__ */ (0,
|
|
2126
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1958
2127
|
"div",
|
|
1959
2128
|
{
|
|
1960
2129
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1961
2130
|
"aria-hidden": "true",
|
|
1962
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2131
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa3.FaPlay, {})
|
|
1963
2132
|
}
|
|
1964
2133
|
)
|
|
1965
2134
|
]
|
|
@@ -1968,9 +2137,9 @@ function TrackPositionPlayPause({
|
|
|
1968
2137
|
}
|
|
1969
2138
|
|
|
1970
2139
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1971
|
-
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1972
2140
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1973
|
-
var
|
|
2141
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2142
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1974
2143
|
function TrackItem({
|
|
1975
2144
|
track,
|
|
1976
2145
|
position,
|
|
@@ -1980,11 +2149,11 @@ function TrackItem({
|
|
|
1980
2149
|
renderSongLink
|
|
1981
2150
|
}) {
|
|
1982
2151
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
1983
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2152
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree21.useTrackList)();
|
|
1984
2153
|
const scope = trackList?.origin?.scope ?? null;
|
|
1985
2154
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
1986
2155
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
1987
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2156
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree22.isSong)(track) && (0, import_genre_tree22.songUnplayableReason)(track) !== null;
|
|
1988
2157
|
const handlePlayPauseClick = (event) => {
|
|
1989
2158
|
event.stopPropagation();
|
|
1990
2159
|
if (isCurrentTrack) {
|
|
@@ -1994,12 +2163,12 @@ function TrackItem({
|
|
|
1994
2163
|
loadTrackForPlayer(track.uuid);
|
|
1995
2164
|
}
|
|
1996
2165
|
};
|
|
1997
|
-
return /* @__PURE__ */ (0,
|
|
2166
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
1998
2167
|
"div",
|
|
1999
2168
|
{
|
|
2000
2169
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2001
2170
|
children: [
|
|
2002
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2171
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2003
2172
|
"div",
|
|
2004
2173
|
{
|
|
2005
2174
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2007,7 +2176,7 @@ function TrackItem({
|
|
|
2007
2176
|
"aria-disabled": "true",
|
|
2008
2177
|
children: position
|
|
2009
2178
|
}
|
|
2010
|
-
) : /* @__PURE__ */ (0,
|
|
2179
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2011
2180
|
TrackPositionPlayPause,
|
|
2012
2181
|
{
|
|
2013
2182
|
position,
|
|
@@ -2016,12 +2185,12 @@ function TrackItem({
|
|
|
2016
2185
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2017
2186
|
}
|
|
2018
2187
|
),
|
|
2019
|
-
/* @__PURE__ */ (0,
|
|
2020
|
-
/* @__PURE__ */ (0,
|
|
2021
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2188
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2189
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree22.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2190
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree23.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2022
2191
|
] }),
|
|
2023
|
-
/* @__PURE__ */ (0,
|
|
2024
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2192
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2193
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2025
2194
|
"div",
|
|
2026
2195
|
{
|
|
2027
2196
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2029,28 +2198,28 @@ function TrackItem({
|
|
|
2029
2198
|
children: renderDuration(track)
|
|
2030
2199
|
}
|
|
2031
2200
|
),
|
|
2032
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2201
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2033
2202
|
]
|
|
2034
2203
|
}
|
|
2035
2204
|
);
|
|
2036
2205
|
}
|
|
2037
2206
|
|
|
2038
2207
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2039
|
-
var
|
|
2040
|
-
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(
|
|
2208
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2209
|
+
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree24.SongDetailedSchema);
|
|
2041
2210
|
var SORTS = [
|
|
2042
2211
|
{ ordering: "title", label: "Title" },
|
|
2043
2212
|
{ ordering: "-albumYear", label: "Year" }
|
|
2044
2213
|
];
|
|
2045
2214
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2046
|
-
const [page, setPage] = (0,
|
|
2047
|
-
const [ordering, setOrdering] = (0,
|
|
2215
|
+
const [page, setPage] = (0, import_react16.useState)(1);
|
|
2216
|
+
const [ordering, setOrdering] = (0, import_react16.useState)("title");
|
|
2048
2217
|
const { fetch } = (0, import_transport7.useFetchWrapper)(getBackendBaseUrl);
|
|
2049
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2218
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree24.useTrackList)();
|
|
2050
2219
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
2051
2220
|
const { data, isPending, isError } = (0, import_transport7.useQueryWithParse)({
|
|
2052
2221
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2053
|
-
queryFn: () => fetch(
|
|
2222
|
+
queryFn: () => fetch(import_genre_tree24.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2054
2223
|
artist: artist.uuid,
|
|
2055
2224
|
ordering,
|
|
2056
2225
|
page
|
|
@@ -2065,25 +2234,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2065
2234
|
});
|
|
2066
2235
|
const handleRowClickCapture = (event, song) => {
|
|
2067
2236
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2068
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2237
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree24.songUnplayableReason)(song) !== null) return;
|
|
2069
2238
|
event.stopPropagation();
|
|
2070
2239
|
void play(song.uuid);
|
|
2071
2240
|
};
|
|
2072
|
-
return /* @__PURE__ */ (0,
|
|
2073
|
-
/* @__PURE__ */ (0,
|
|
2074
|
-
/* @__PURE__ */ (0,
|
|
2241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
2242
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2075
2244
|
"button",
|
|
2076
2245
|
{
|
|
2077
2246
|
type: "button",
|
|
2078
2247
|
onClick: () => void play(),
|
|
2079
2248
|
className: "flex w-fit items-center gap-1.5 px-3 py-1 text-xs font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
|
|
2080
2249
|
children: [
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
2250
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react10.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2082
2251
|
"Play all"
|
|
2083
2252
|
]
|
|
2084
2253
|
}
|
|
2085
2254
|
),
|
|
2086
|
-
/* @__PURE__ */ (0,
|
|
2255
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2087
2256
|
"button",
|
|
2088
2257
|
{
|
|
2089
2258
|
type: "button",
|
|
@@ -2098,7 +2267,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2098
2267
|
sort.ordering
|
|
2099
2268
|
)) })
|
|
2100
2269
|
] }),
|
|
2101
|
-
/* @__PURE__ */ (0,
|
|
2270
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2102
2271
|
TrackItem,
|
|
2103
2272
|
{
|
|
2104
2273
|
track: song,
|
|
@@ -2107,12 +2276,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2107
2276
|
renderSongLink
|
|
2108
2277
|
}
|
|
2109
2278
|
) }, song.uuid)) }),
|
|
2110
|
-
/* @__PURE__ */ (0,
|
|
2279
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2111
2280
|
] }) });
|
|
2112
2281
|
}
|
|
2113
2282
|
|
|
2114
2283
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2115
|
-
var
|
|
2284
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2116
2285
|
function useArtistPanel({
|
|
2117
2286
|
uuid,
|
|
2118
2287
|
scope,
|
|
@@ -2124,7 +2293,7 @@ function useArtistPanel({
|
|
|
2124
2293
|
onExpandedChange,
|
|
2125
2294
|
onClose
|
|
2126
2295
|
}) {
|
|
2127
|
-
const result = (0,
|
|
2296
|
+
const result = (0, import_genre_tree25.useArtist)(uuid, getBackendBaseUrl);
|
|
2128
2297
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2129
2298
|
switch (result.status) {
|
|
2130
2299
|
case "idle":
|
|
@@ -2133,26 +2302,26 @@ function useArtistPanel({
|
|
|
2133
2302
|
return {
|
|
2134
2303
|
...panel,
|
|
2135
2304
|
title: "Artist",
|
|
2136
|
-
content: /* @__PURE__ */ (0,
|
|
2137
|
-
/* @__PURE__ */ (0,
|
|
2138
|
-
/* @__PURE__ */ (0,
|
|
2139
|
-
/* @__PURE__ */ (0,
|
|
2140
|
-
/* @__PURE__ */ (0,
|
|
2141
|
-
/* @__PURE__ */ (0,
|
|
2142
|
-
/* @__PURE__ */ (0,
|
|
2305
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2306
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-3 w-16" }),
|
|
2307
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-32" }),
|
|
2308
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-3 w-16" }),
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-4 w-24" }),
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2311
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-16" })
|
|
2143
2312
|
] })
|
|
2144
2313
|
};
|
|
2145
2314
|
case "not-found":
|
|
2146
2315
|
return {
|
|
2147
2316
|
...panel,
|
|
2148
2317
|
title: "Artist not found",
|
|
2149
|
-
content: /* @__PURE__ */ (0,
|
|
2318
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2150
2319
|
};
|
|
2151
2320
|
case "error":
|
|
2152
2321
|
return {
|
|
2153
2322
|
...panel,
|
|
2154
2323
|
title: "Artist",
|
|
2155
|
-
content: /* @__PURE__ */ (0,
|
|
2324
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2156
2325
|
};
|
|
2157
2326
|
case "success": {
|
|
2158
2327
|
const { artist } = result;
|
|
@@ -2161,13 +2330,13 @@ function useArtistPanel({
|
|
|
2161
2330
|
return {
|
|
2162
2331
|
...panel,
|
|
2163
2332
|
title: artist.name,
|
|
2164
|
-
content: /* @__PURE__ */ (0,
|
|
2165
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2166
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2167
|
-
/* @__PURE__ */ (0,
|
|
2168
|
-
/* @__PURE__ */ (0,
|
|
2169
|
-
/* @__PURE__ */ (0,
|
|
2170
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2333
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
2334
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2335
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2336
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistHeader, { artist }),
|
|
2337
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2338
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2339
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2171
2340
|
ArtistSongs,
|
|
2172
2341
|
{
|
|
2173
2342
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2177,56 +2346,56 @@ function useArtistPanel({
|
|
|
2177
2346
|
}
|
|
2178
2347
|
)
|
|
2179
2348
|
] }),
|
|
2180
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2349
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2181
2350
|
};
|
|
2182
2351
|
}
|
|
2183
2352
|
}
|
|
2184
2353
|
}
|
|
2185
2354
|
|
|
2186
2355
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2187
|
-
var
|
|
2188
|
-
var
|
|
2356
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
2357
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2189
2358
|
|
|
2190
2359
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2191
|
-
var
|
|
2192
|
-
var
|
|
2193
|
-
var
|
|
2360
|
+
var import_react17 = require("react");
|
|
2361
|
+
var import_lucide_react11 = require("lucide-react");
|
|
2362
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2194
2363
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2195
|
-
var
|
|
2196
|
-
var
|
|
2364
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2365
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2197
2366
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2198
2367
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2199
2368
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2200
2369
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2201
|
-
const [open, setOpen] = (0,
|
|
2202
|
-
const history = (0,
|
|
2370
|
+
const [open, setOpen] = (0, import_react17.useState)(false);
|
|
2371
|
+
const history = (0, import_genre_tree26.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2203
2372
|
let body = null;
|
|
2204
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2205
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2373
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2374
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2206
2375
|
else if (history.status === "success")
|
|
2207
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2208
|
-
/* @__PURE__ */ (0,
|
|
2209
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2376
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2377
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2378
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2210
2379
|
" ",
|
|
2211
2380
|
"\u2014 ",
|
|
2212
2381
|
entry.oldValue ?? "none",
|
|
2213
2382
|
" \u2192 ",
|
|
2214
2383
|
entry.newValue ?? "none"
|
|
2215
2384
|
] }) : null,
|
|
2216
|
-
/* @__PURE__ */ (0,
|
|
2385
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2217
2386
|
entry.actorPseudo ?? "Anonymous",
|
|
2218
2387
|
" \xB7 ",
|
|
2219
2388
|
dateFormat.format(new Date(entry.createdOn))
|
|
2220
2389
|
] })
|
|
2221
2390
|
] }, entry.uuid)) });
|
|
2222
|
-
return /* @__PURE__ */ (0,
|
|
2391
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2223
2392
|
"details",
|
|
2224
2393
|
{
|
|
2225
2394
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2226
2395
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2227
2396
|
children: [
|
|
2228
|
-
/* @__PURE__ */ (0,
|
|
2229
|
-
/* @__PURE__ */ (0,
|
|
2397
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2398
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "mt-1.5", children: body })
|
|
2230
2399
|
]
|
|
2231
2400
|
}
|
|
2232
2401
|
);
|
|
@@ -2240,25 +2409,25 @@ function SongPanelContent({
|
|
|
2240
2409
|
genrePath,
|
|
2241
2410
|
onSelectGenre
|
|
2242
2411
|
}) {
|
|
2243
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2412
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree26.useTrackList)();
|
|
2244
2413
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2245
|
-
const unplayableReason = (0,
|
|
2414
|
+
const unplayableReason = (0, import_genre_tree26.songUnplayableReason)(song);
|
|
2246
2415
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2247
2416
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2248
2417
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2249
|
-
const musicBrainzUrl = (0,
|
|
2418
|
+
const musicBrainzUrl = (0, import_genre_tree26.musicBrainzRecordingUrl)(song);
|
|
2250
2419
|
const fields = [];
|
|
2251
2420
|
if (song.genre) {
|
|
2252
2421
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2253
2422
|
fields.push({
|
|
2254
2423
|
label: "Genre",
|
|
2255
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2256
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2257
|
-
/* @__PURE__ */ (0,
|
|
2424
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { children: [
|
|
2425
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2426
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2258
2427
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2259
2428
|
});
|
|
2260
2429
|
} else {
|
|
2261
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2430
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2262
2431
|
}
|
|
2263
2432
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2264
2433
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2268,11 +2437,11 @@ function SongPanelContent({
|
|
|
2268
2437
|
const links = [];
|
|
2269
2438
|
if (musicBrainzUrl)
|
|
2270
2439
|
links.push(
|
|
2271
|
-
/* @__PURE__ */ (0,
|
|
2440
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2272
2441
|
);
|
|
2273
2442
|
if (song.youtubeVideo)
|
|
2274
2443
|
links.push(
|
|
2275
|
-
/* @__PURE__ */ (0,
|
|
2444
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2276
2445
|
"a",
|
|
2277
2446
|
{
|
|
2278
2447
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2287,33 +2456,33 @@ function SongPanelContent({
|
|
|
2287
2456
|
if (links.length > 0)
|
|
2288
2457
|
fields.push({
|
|
2289
2458
|
label: "Links",
|
|
2290
|
-
value: /* @__PURE__ */ (0,
|
|
2459
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex gap-3", children: links })
|
|
2291
2460
|
});
|
|
2292
|
-
return /* @__PURE__ */ (0,
|
|
2293
|
-
/* @__PURE__ */ (0,
|
|
2294
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2461
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
2462
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2463
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2295
2464
|
"button",
|
|
2296
2465
|
{
|
|
2297
2466
|
type: "button",
|
|
2298
2467
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2299
2468
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2300
2469
|
children: [
|
|
2301
|
-
/* @__PURE__ */ (0,
|
|
2470
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react11.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react11.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2302
2471
|
isPlaying ? "Pause" : "Play"
|
|
2303
2472
|
]
|
|
2304
2473
|
}
|
|
2305
2474
|
),
|
|
2306
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2475
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2307
2476
|
] }) }),
|
|
2308
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2309
|
-
/* @__PURE__ */ (0,
|
|
2310
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2311
|
-
/* @__PURE__ */ (0,
|
|
2477
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_genre_tree26.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2478
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.FieldList, { items: fields }),
|
|
2479
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2480
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2312
2481
|
] });
|
|
2313
2482
|
}
|
|
2314
2483
|
|
|
2315
2484
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2316
|
-
var
|
|
2485
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2317
2486
|
function useSongPanel({
|
|
2318
2487
|
uuid,
|
|
2319
2488
|
scope,
|
|
@@ -2326,7 +2495,7 @@ function useSongPanel({
|
|
|
2326
2495
|
onExpandedChange,
|
|
2327
2496
|
onClose
|
|
2328
2497
|
}) {
|
|
2329
|
-
const result = (0,
|
|
2498
|
+
const result = (0, import_genre_tree27.useSong)(uuid, getBackendBaseUrl);
|
|
2330
2499
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2331
2500
|
switch (result.status) {
|
|
2332
2501
|
case "idle":
|
|
@@ -2335,24 +2504,24 @@ function useSongPanel({
|
|
|
2335
2504
|
return {
|
|
2336
2505
|
...panel,
|
|
2337
2506
|
title: "Song",
|
|
2338
|
-
content: /* @__PURE__ */ (0,
|
|
2339
|
-
/* @__PURE__ */ (0,
|
|
2340
|
-
/* @__PURE__ */ (0,
|
|
2341
|
-
/* @__PURE__ */ (0,
|
|
2342
|
-
/* @__PURE__ */ (0,
|
|
2507
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2509
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.Skeleton, { className: "h-4 w-32" }),
|
|
2510
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2511
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.Skeleton, { className: "h-4 w-24" })
|
|
2343
2512
|
] })
|
|
2344
2513
|
};
|
|
2345
2514
|
case "not-found":
|
|
2346
2515
|
return {
|
|
2347
2516
|
...panel,
|
|
2348
2517
|
title: "Song not found",
|
|
2349
|
-
content: /* @__PURE__ */ (0,
|
|
2518
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2350
2519
|
};
|
|
2351
2520
|
case "error":
|
|
2352
2521
|
return {
|
|
2353
2522
|
...panel,
|
|
2354
2523
|
title: "Song",
|
|
2355
|
-
content: /* @__PURE__ */ (0,
|
|
2524
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2356
2525
|
};
|
|
2357
2526
|
case "success": {
|
|
2358
2527
|
const { song } = result;
|
|
@@ -2360,7 +2529,7 @@ function useSongPanel({
|
|
|
2360
2529
|
return {
|
|
2361
2530
|
...panel,
|
|
2362
2531
|
title: song.title,
|
|
2363
|
-
content: /* @__PURE__ */ (0,
|
|
2532
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2364
2533
|
SongPanelContent,
|
|
2365
2534
|
{
|
|
2366
2535
|
song,
|
|
@@ -2379,29 +2548,29 @@ function useSongPanel({
|
|
|
2379
2548
|
|
|
2380
2549
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2381
2550
|
var import_md = require("react-icons/md");
|
|
2382
|
-
var
|
|
2383
|
-
var
|
|
2551
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
2552
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2384
2553
|
function SizeMetricToggle({
|
|
2385
2554
|
value,
|
|
2386
2555
|
onChange
|
|
2387
2556
|
}) {
|
|
2388
|
-
return /* @__PURE__ */ (0,
|
|
2389
|
-
|
|
2557
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2558
|
+
import_ui16.SegmentedControl,
|
|
2390
2559
|
{
|
|
2391
2560
|
variant: "floating",
|
|
2392
2561
|
"aria-label": "Size nodes by",
|
|
2393
2562
|
value,
|
|
2394
2563
|
onChange,
|
|
2395
2564
|
options: [
|
|
2396
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2397
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2565
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2566
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2398
2567
|
]
|
|
2399
2568
|
}
|
|
2400
2569
|
);
|
|
2401
2570
|
}
|
|
2402
2571
|
|
|
2403
2572
|
// src/genre-tree/GenreTreeView.tsx
|
|
2404
|
-
var
|
|
2573
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2405
2574
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2406
2575
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2407
2576
|
var noop = () => {
|
|
@@ -2414,8 +2583,8 @@ function GenreTreeView({
|
|
|
2414
2583
|
getBackendBaseUrl,
|
|
2415
2584
|
additionalActions,
|
|
2416
2585
|
viewMode: controlledViewMode,
|
|
2417
|
-
|
|
2418
|
-
|
|
2586
|
+
selectedGenreUuid: controlledSelectedGenreUuid,
|
|
2587
|
+
onSelectedGenreUuidChange,
|
|
2419
2588
|
defaultViewport,
|
|
2420
2589
|
viewportRestore,
|
|
2421
2590
|
onViewportChange,
|
|
@@ -2463,21 +2632,21 @@ function GenreTreeView({
|
|
|
2463
2632
|
placement = "default",
|
|
2464
2633
|
...focus
|
|
2465
2634
|
}) {
|
|
2466
|
-
const getShareUrlRef = (0,
|
|
2635
|
+
const getShareUrlRef = (0, import_react18.useRef)(getShareUrl);
|
|
2467
2636
|
getShareUrlRef.current = getShareUrl;
|
|
2468
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2637
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react18.useState)(null);
|
|
2469
2638
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2470
2639
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2471
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2640
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react18.useState)(null);
|
|
2472
2641
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2473
2642
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2474
2643
|
const changeSong = (uuid) => {
|
|
2475
2644
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2476
2645
|
onSongUuidChange?.(uuid);
|
|
2477
2646
|
};
|
|
2478
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2647
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react18.useState)("songs");
|
|
2479
2648
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2480
|
-
const deferredSizeMetric = (0,
|
|
2649
|
+
const deferredSizeMetric = (0, import_react18.useDeferredValue)(sizeMetric);
|
|
2481
2650
|
const changeSizeMetric = (metric) => {
|
|
2482
2651
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2483
2652
|
onSizeMetricChange?.(metric);
|
|
@@ -2492,6 +2661,8 @@ function GenreTreeView({
|
|
|
2492
2661
|
genrePlaylistByNodeId,
|
|
2493
2662
|
visibleGenrePlaylists,
|
|
2494
2663
|
groupedGenrePlaylistsByRoot,
|
|
2664
|
+
selectedGenreUuid,
|
|
2665
|
+
selectGenre,
|
|
2495
2666
|
selectedNodeId,
|
|
2496
2667
|
selectedGenrePlaylist,
|
|
2497
2668
|
selectNode,
|
|
@@ -2504,17 +2675,17 @@ function GenreTreeView({
|
|
|
2504
2675
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2505
2676
|
toggleWheelRotation,
|
|
2506
2677
|
onNodeHover: handleNodeHover
|
|
2507
|
-
} = (0,
|
|
2678
|
+
} = (0, import_genre_tree28.useGenreTreeViewState)({
|
|
2508
2679
|
scope,
|
|
2509
2680
|
getBackendBaseUrl,
|
|
2510
2681
|
viewMode: controlledViewMode,
|
|
2511
|
-
|
|
2512
|
-
|
|
2682
|
+
selectedGenreUuid: controlledSelectedGenreUuid,
|
|
2683
|
+
onSelectedGenreUuidChange,
|
|
2513
2684
|
criteriaOverviewSchema,
|
|
2514
2685
|
artistUuid
|
|
2515
2686
|
});
|
|
2516
2687
|
const restoreRevision = viewportRestore?.revision;
|
|
2517
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2688
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react18.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2518
2689
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2519
2690
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2520
2691
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2523,36 +2694,36 @@ function GenreTreeView({
|
|
|
2523
2694
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2524
2695
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2525
2696
|
}
|
|
2526
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2527
|
-
const { mutate: updateGenre } = (0,
|
|
2697
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree28.genreUpdateMutationKey }) > 0;
|
|
2698
|
+
const { mutate: updateGenre } = (0, import_genre_tree28.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2528
2699
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2529
2700
|
const isArtistFiltered = artistUuid !== null;
|
|
2530
2701
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2531
|
-
const artistResult = (0,
|
|
2702
|
+
const artistResult = (0, import_genre_tree28.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2532
2703
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2533
|
-
const filterArtist = (0,
|
|
2704
|
+
const filterArtist = (0, import_react18.useMemo)(
|
|
2534
2705
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2535
2706
|
[artistUuid, filterArtistName]
|
|
2536
2707
|
);
|
|
2537
2708
|
const hasSelectedGenreNode = selectedGenrePlaylist !== void 0 && visibleGenrePlaylists.some(({ uuid }) => uuid === selectedGenrePlaylist.uuid);
|
|
2538
2709
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: visibleGenrePlaylists, artist: filterArtist });
|
|
2539
2710
|
const openArtist = (uuid) => {
|
|
2540
|
-
if (
|
|
2711
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2541
2712
|
if (songUuid !== null) changeSong(null);
|
|
2542
2713
|
changeArtist(uuid);
|
|
2543
2714
|
};
|
|
2544
2715
|
const openSong = (uuid) => {
|
|
2545
|
-
if (
|
|
2716
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2546
2717
|
changeSong(uuid);
|
|
2547
2718
|
};
|
|
2548
|
-
const openSongRef = (0,
|
|
2719
|
+
const openSongRef = (0, import_react18.useRef)(openSong);
|
|
2549
2720
|
openSongRef.current = openSong;
|
|
2550
|
-
const resolvedRenderSongLink = (0,
|
|
2551
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2721
|
+
const resolvedRenderSongLink = (0, import_react18.useCallback)(
|
|
2722
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2552
2723
|
[renderSongLink]
|
|
2553
2724
|
);
|
|
2554
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2555
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
2725
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react18.useState)(null);
|
|
2726
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react18.useState)(null);
|
|
2556
2727
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2557
2728
|
const songBaseUrl = getBackendBaseUrl;
|
|
2558
2729
|
const artistPanel = useArtistPanel({
|
|
@@ -2560,7 +2731,7 @@ function GenreTreeView({
|
|
|
2560
2731
|
scope,
|
|
2561
2732
|
getBackendBaseUrl: artistBaseUrl,
|
|
2562
2733
|
getShareUrl: getShareUrlRef.current,
|
|
2563
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2734
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2564
2735
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2565
2736
|
onExpandedChange: (on) => {
|
|
2566
2737
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2569,7 +2740,7 @@ function GenreTreeView({
|
|
|
2569
2740
|
renderSongLink: resolvedRenderSongLink,
|
|
2570
2741
|
onClose: () => changeArtist(null)
|
|
2571
2742
|
});
|
|
2572
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2743
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2573
2744
|
const getGenrePath = (genre) => {
|
|
2574
2745
|
const path = [];
|
|
2575
2746
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2587,9 +2758,9 @@ function GenreTreeView({
|
|
|
2587
2758
|
getShareUrl: getShareUrlRef.current,
|
|
2588
2759
|
renderArtistLink: resolvedRenderArtistLink,
|
|
2589
2760
|
getGenrePath,
|
|
2590
|
-
onSelectGenre: (
|
|
2761
|
+
onSelectGenre: (genrePlaylist2) => {
|
|
2591
2762
|
changeSong(null);
|
|
2592
|
-
selectGenrePlaylist(
|
|
2763
|
+
selectGenrePlaylist(genrePlaylist2);
|
|
2593
2764
|
},
|
|
2594
2765
|
expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
|
|
2595
2766
|
onExpandedChange: (on) => {
|
|
@@ -2598,11 +2769,11 @@ function GenreTreeView({
|
|
|
2598
2769
|
},
|
|
2599
2770
|
onClose: () => changeSong(null)
|
|
2600
2771
|
});
|
|
2601
|
-
const builtInSongPanel = overlayPanel ||
|
|
2602
|
-
const builtInArtistPanel = overlayPanel ||
|
|
2772
|
+
const builtInSongPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : songPanel;
|
|
2773
|
+
const builtInArtistPanel = overlayPanel || selectedGenreUuid !== null || builtInSongPanel ? void 0 : artistPanel;
|
|
2603
2774
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2604
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0,
|
|
2605
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
2775
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react18.useState)(false);
|
|
2776
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react18.useState)(false);
|
|
2606
2777
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2607
2778
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2608
2779
|
const changeCollapsed = (next) => {
|
|
@@ -2613,155 +2784,114 @@ function GenreTreeView({
|
|
|
2613
2784
|
setUncontrolledExpanded(next);
|
|
2614
2785
|
onExpandedChange?.(next);
|
|
2615
2786
|
};
|
|
2616
|
-
(0,
|
|
2787
|
+
(0, import_react18.useEffect)(() => {
|
|
2617
2788
|
setUncontrolledCollapsed(false);
|
|
2618
|
-
}, [
|
|
2619
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0,
|
|
2789
|
+
}, [selectedGenreUuid, artistUuid, songUuid]);
|
|
2790
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react18.useState)(false);
|
|
2620
2791
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2621
2792
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2622
|
-
const [resolveStyle, setResolveStyle] = (0,
|
|
2623
|
-
const handleNodeStyleResolverChange = (0,
|
|
2793
|
+
const [resolveStyle, setResolveStyle] = (0, import_react18.useState)(null);
|
|
2794
|
+
const handleNodeStyleResolverChange = (0, import_react18.useCallback)(
|
|
2624
2795
|
(resolver) => setResolveStyle(() => resolver),
|
|
2625
2796
|
[]
|
|
2626
2797
|
);
|
|
2627
|
-
const panelRef = (0,
|
|
2628
|
-
const obscuredInset = (0,
|
|
2798
|
+
const panelRef = (0, import_react18.useRef)(null);
|
|
2799
|
+
const obscuredInset = (0, import_ui17.useObscuredInset)(panelRef);
|
|
2629
2800
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2630
2801
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2631
|
-
const treeNodes = (0,
|
|
2632
|
-
const relations = (0,
|
|
2802
|
+
const treeNodes = (0, import_react18.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2803
|
+
const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
|
|
2633
2804
|
treeNodes,
|
|
2634
2805
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2635
2806
|
resolveStyle
|
|
2636
2807
|
);
|
|
2637
|
-
const [selectionHistory, setSelectionHistory] = (0,
|
|
2638
|
-
|
|
2639
|
-
previous:
|
|
2640
|
-
});
|
|
2641
|
-
if (selectionHistory.current !== selectedNodeId) {
|
|
2642
|
-
setSelectionHistory({
|
|
2643
|
-
current: selectedNodeId,
|
|
2644
|
-
previous: selectionHistory.current ?? selectionHistory.previous
|
|
2645
|
-
});
|
|
2808
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react18.useState)({ current: selectedGenreUuid, previous: null });
|
|
2809
|
+
if (selectionHistory.current !== selectedGenreUuid) {
|
|
2810
|
+
setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
2646
2811
|
}
|
|
2647
|
-
const
|
|
2648
|
-
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2812
|
+
const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
|
|
2649
2813
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
2814
|
+
const reopenPanel = () => {
|
|
2815
|
+
setUncontrolledCollapsed(false);
|
|
2816
|
+
onCollapsedChange?.(false);
|
|
2817
|
+
};
|
|
2650
2818
|
const handleSelectedNodeChange = (node) => {
|
|
2819
|
+
reopenPanel();
|
|
2651
2820
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2652
2821
|
selectNode(node);
|
|
2653
2822
|
};
|
|
2654
2823
|
const handleGenreSearchClear = clearSelection;
|
|
2655
|
-
const handleGenreSearchSelect = (
|
|
2824
|
+
const handleGenreSearchSelect = (genrePlaylist2) => {
|
|
2825
|
+
reopenPanel();
|
|
2656
2826
|
if (songUuid !== null) changeSong(null);
|
|
2657
|
-
selectGenrePlaylist(
|
|
2827
|
+
selectGenrePlaylist(genrePlaylist2);
|
|
2658
2828
|
};
|
|
2659
2829
|
const selectedUuid = selectedGenrePlaylist?.uuid;
|
|
2660
|
-
const selectedChildren = selectedUuid ? visibleGenrePlaylists.filter((
|
|
2830
|
+
const selectedChildren = selectedUuid ? visibleGenrePlaylists.filter((genrePlaylist2) => genrePlaylist2.parent?.uuid === selectedUuid) : [];
|
|
2661
2831
|
const canZoomIn = viewMode === "pop-core" && focus.onFocusGenreChange !== void 0 && selectedUuid !== void 0 && focus.focusGenreUuid !== selectedUuid && selectedChildren.length > 0 && selectedChildren.filter((child) => child.criteria?.side === "pop").length <= 1;
|
|
2662
2832
|
const handleGenreSearchZoomIn = () => {
|
|
2663
2833
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2664
2834
|
};
|
|
2665
|
-
const zoomInRef = (0,
|
|
2835
|
+
const zoomInRef = (0, import_react18.useRef)(void 0);
|
|
2666
2836
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2667
|
-
const
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
const
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
kind: "genre",
|
|
2697
|
-
genrePlaylistUuid: genrePlaylist.uuid,
|
|
2698
|
-
name: criteria.name,
|
|
2699
|
-
scope,
|
|
2700
|
-
artistUuid
|
|
2701
|
-
};
|
|
2702
|
-
const shareUrl = getShareUrlRef.current?.(shareTarget) ?? void 0;
|
|
2703
|
-
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find(
|
|
2704
|
-
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2705
|
-
) : void 0;
|
|
2706
|
-
return {
|
|
2707
|
-
primary: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2708
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2709
|
-
GenreInfoPanelBody,
|
|
2837
|
+
const genrePlaylist = selectedGenrePlaylist;
|
|
2838
|
+
const criteria = genrePlaylist?.criteria;
|
|
2839
|
+
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist?.parent?.uuid);
|
|
2840
|
+
const canToggleSide = !readOnly && criteria !== void 0 && criteria !== null && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2841
|
+
const side = criteria?.side ?? "core";
|
|
2842
|
+
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find((sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist?.uuid && sibling.criteria?.side === "pop") : void 0;
|
|
2843
|
+
const shareTarget = selectedGenreUuid ? { kind: "genre", genreUuid: selectedGenreUuid, name: criteria?.name ?? "Genre", scope, artistUuid } : void 0;
|
|
2844
|
+
const shareUrl = shareTarget ? getShareUrlRef.current?.(shareTarget) ?? void 0 : void 0;
|
|
2845
|
+
const genreContent = useGenreDetailContent({
|
|
2846
|
+
genreUuid: selectedGenreUuid,
|
|
2847
|
+
scope,
|
|
2848
|
+
getBackendBaseUrl,
|
|
2849
|
+
onSelectGenre: (uuid) => {
|
|
2850
|
+
reopenPanel();
|
|
2851
|
+
selectGenre(uuid);
|
|
2852
|
+
},
|
|
2853
|
+
expanded: isExpanded,
|
|
2854
|
+
playback,
|
|
2855
|
+
genrePlaylist: selectedGenrePlaylist,
|
|
2856
|
+
band: {
|
|
2857
|
+
...relations ? { node: relations.node } : {},
|
|
2858
|
+
shareTarget: shareUrl ? shareTarget : void 0,
|
|
2859
|
+
shareUrl,
|
|
2860
|
+
onZoomIn: zoomInRef.current,
|
|
2861
|
+
onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2862
|
+
onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2863
|
+
onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
|
|
2864
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2865
|
+
ChangeParentPopup,
|
|
2710
2866
|
{
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
ancestors
|
|
2714
|
-
] }),
|
|
2715
|
-
genreUuid,
|
|
2716
|
-
genrePlaylist,
|
|
2717
|
-
catalogue: genrePlaylist.catalogue,
|
|
2718
|
-
wikidataId: criteria.wikidataId,
|
|
2867
|
+
genrePlaylist: { ...genrePlaylist, criteria },
|
|
2868
|
+
genrePlaylists: visibleGenrePlaylists,
|
|
2719
2869
|
scope,
|
|
2720
2870
|
getBackendBaseUrl,
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
node,
|
|
2724
|
-
shareTarget: shareUrl ? shareTarget : void 0,
|
|
2725
|
-
shareUrl,
|
|
2726
|
-
onZoomIn: zoomInRef.current,
|
|
2727
|
-
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2728
|
-
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2729
|
-
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2730
|
-
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2731
|
-
ChangeParentPopup,
|
|
2732
|
-
{
|
|
2733
|
-
genrePlaylist: { ...genrePlaylist, criteria },
|
|
2734
|
-
genrePlaylists: visibleGenrePlaylists,
|
|
2735
|
-
scope,
|
|
2736
|
-
getBackendBaseUrl,
|
|
2737
|
-
onClose: hidePopup,
|
|
2738
|
-
onParentChanged
|
|
2739
|
-
}
|
|
2740
|
-
)
|
|
2741
|
-
),
|
|
2742
|
-
side,
|
|
2743
|
-
onToggleSide: canToggleSide ? () => updateGenre({ uuid: genreUuid, data: { side: side === "pop" ? "core" : "pop" } }) : void 0,
|
|
2744
|
-
toggleSideDisabledReason: popSibling ? `${popSibling.name} is already the pop side of ${parentPlaylist?.name}` : void 0,
|
|
2745
|
-
additionalActions
|
|
2746
|
-
},
|
|
2747
|
-
extras: {
|
|
2748
|
-
criteriaOverviewSchema,
|
|
2749
|
-
renderGenreDetailExtras,
|
|
2750
|
-
renderArtistLink,
|
|
2751
|
-
renderSongLink: resolvedRenderSongLink,
|
|
2752
|
-
artist: filterArtist,
|
|
2753
|
-
renderSummary,
|
|
2754
|
-
renderEssentialTracks,
|
|
2755
|
-
renderSongLeading,
|
|
2756
|
-
renderSongActions,
|
|
2757
|
-
renderTracksToolbar
|
|
2758
|
-
}
|
|
2871
|
+
onClose: hidePopup,
|
|
2872
|
+
onParentChanged
|
|
2759
2873
|
}
|
|
2760
|
-
)
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2874
|
+
)
|
|
2875
|
+
),
|
|
2876
|
+
side,
|
|
2877
|
+
onToggleSide: canToggleSide && criteria ? () => updateGenre({ uuid: criteria.uuid, data: { side: side === "pop" ? "core" : "pop" } }) : void 0,
|
|
2878
|
+
toggleSideDisabledReason: popSibling ? `${popSibling.name} is already the pop side of ${parentPlaylist?.name}` : void 0,
|
|
2879
|
+
additionalActions
|
|
2880
|
+
},
|
|
2881
|
+
extras: {
|
|
2882
|
+
criteriaOverviewSchema,
|
|
2883
|
+
renderGenreDetailExtras,
|
|
2884
|
+
renderArtistLink,
|
|
2885
|
+
renderSongLink: resolvedRenderSongLink,
|
|
2886
|
+
artist: filterArtist,
|
|
2887
|
+
renderSummary,
|
|
2888
|
+
renderEssentialTracks,
|
|
2889
|
+
renderSongLeading,
|
|
2890
|
+
renderSongActions,
|
|
2891
|
+
renderTracksToolbar
|
|
2892
|
+
},
|
|
2893
|
+
trailing: relations ? renderDangerZone?.(relations.node) : void 0
|
|
2894
|
+
});
|
|
2765
2895
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
2766
2896
|
const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
|
|
2767
2897
|
let activePanel = null;
|
|
@@ -2775,16 +2905,16 @@ function GenreTreeView({
|
|
|
2775
2905
|
onClose: overlayPanel.onClose,
|
|
2776
2906
|
compact: false
|
|
2777
2907
|
};
|
|
2778
|
-
} else if (
|
|
2779
|
-
const { primary } = renderGenreBody(relations);
|
|
2908
|
+
} else if (selectedGenreUuid) {
|
|
2780
2909
|
activePanel = {
|
|
2781
|
-
title:
|
|
2782
|
-
content:
|
|
2910
|
+
title: genreContent.title,
|
|
2911
|
+
content: genreContent.content,
|
|
2912
|
+
aside: genreContent.aside,
|
|
2783
2913
|
expanded: isExpanded,
|
|
2784
2914
|
onExpandedChange: changeExpanded,
|
|
2785
2915
|
onClose: clearSelection,
|
|
2786
|
-
compact:
|
|
2787
|
-
headerAccent: { background: relations.fill, color: relations.textColor }
|
|
2916
|
+
compact: false,
|
|
2917
|
+
headerAccent: relations ? { background: relations.fill, color: relations.textColor } : genreContent.headerAccent
|
|
2788
2918
|
};
|
|
2789
2919
|
} else if (builtInPanel) {
|
|
2790
2920
|
activePanel = {
|
|
@@ -2797,36 +2927,36 @@ function GenreTreeView({
|
|
|
2797
2927
|
compact: true
|
|
2798
2928
|
};
|
|
2799
2929
|
}
|
|
2800
|
-
const isPanelShown =
|
|
2930
|
+
const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
|
|
2801
2931
|
const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
|
|
2802
2932
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2803
2933
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2804
2934
|
} : void 0;
|
|
2805
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2806
|
-
|
|
2935
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2936
|
+
import_ui17.IconButton,
|
|
2807
2937
|
{
|
|
2808
2938
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2809
2939
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2810
2940
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2811
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
2941
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2812
2942
|
}
|
|
2813
2943
|
) : null;
|
|
2814
2944
|
const renderArtistFilter = () => {
|
|
2815
2945
|
if (isLoading || !filterArtist) return null;
|
|
2816
|
-
return /* @__PURE__ */ (0,
|
|
2817
|
-
/* @__PURE__ */ (0,
|
|
2946
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2947
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2818
2948
|
"button",
|
|
2819
2949
|
{
|
|
2820
2950
|
type: "button",
|
|
2821
2951
|
className: "py-1 pl-3 pr-2 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] hover:underline",
|
|
2822
2952
|
"aria-label": `Show artist ${filterArtist.name}`,
|
|
2823
2953
|
onClick: () => {
|
|
2824
|
-
if (
|
|
2954
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2825
2955
|
},
|
|
2826
2956
|
children: filterArtist.name
|
|
2827
2957
|
}
|
|
2828
2958
|
),
|
|
2829
|
-
/* @__PURE__ */ (0,
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2830
2960
|
"button",
|
|
2831
2961
|
{
|
|
2832
2962
|
type: "button",
|
|
@@ -2838,8 +2968,8 @@ function GenreTreeView({
|
|
|
2838
2968
|
)
|
|
2839
2969
|
] });
|
|
2840
2970
|
};
|
|
2841
|
-
const actions = /* @__PURE__ */ (0,
|
|
2842
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
2971
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
2972
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2843
2973
|
GenreTreeSearch,
|
|
2844
2974
|
{
|
|
2845
2975
|
artistBaseUrl,
|
|
@@ -2857,7 +2987,12 @@ function GenreTreeView({
|
|
|
2857
2987
|
SearchGroups,
|
|
2858
2988
|
genrePlaylists: visibleGenrePlaylists,
|
|
2859
2989
|
onSelect: handleGenreSearchSelect,
|
|
2860
|
-
|
|
2990
|
+
onSelectGenre: (uuid) => {
|
|
2991
|
+
reopenPanel();
|
|
2992
|
+
if (songUuid) changeSong(null);
|
|
2993
|
+
selectGenre(uuid);
|
|
2994
|
+
},
|
|
2995
|
+
selectedName: (selectedGenreUuid ? genreContent.title : null) ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2861
2996
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2862
2997
|
onPrevious: handlePrevious,
|
|
2863
2998
|
onQueryChange: onSearchQueryChange,
|
|
@@ -2865,10 +3000,10 @@ function GenreTreeView({
|
|
|
2865
3000
|
extraGroups: extraSearchGroups
|
|
2866
3001
|
}
|
|
2867
3002
|
),
|
|
2868
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2869
|
-
/* @__PURE__ */ (0,
|
|
2870
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
2871
|
-
|
|
3003
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3004
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
3005
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3006
|
+
import_ui17.SegmentedControl,
|
|
2872
3007
|
{
|
|
2873
3008
|
"aria-label": "Tree view mode",
|
|
2874
3009
|
value: viewMode,
|
|
@@ -2881,14 +3016,14 @@ function GenreTreeView({
|
|
|
2881
3016
|
]
|
|
2882
3017
|
}
|
|
2883
3018
|
),
|
|
2884
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3019
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2885
3020
|
"div",
|
|
2886
3021
|
{
|
|
2887
3022
|
className: "flex items-center gap-1",
|
|
2888
3023
|
role: "group",
|
|
2889
3024
|
"aria-label": "Tree display options",
|
|
2890
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
2891
|
-
|
|
3025
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3026
|
+
import_ui17.Button,
|
|
2892
3027
|
{
|
|
2893
3028
|
variant: allowWheelRotation ? "default" : "outline",
|
|
2894
3029
|
size: "sm",
|
|
@@ -2898,44 +3033,44 @@ function GenreTreeView({
|
|
|
2898
3033
|
)
|
|
2899
3034
|
}
|
|
2900
3035
|
),
|
|
2901
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
2902
|
-
|
|
3036
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3037
|
+
import_ui17.IconTextButton,
|
|
2903
3038
|
{
|
|
2904
|
-
icon:
|
|
3039
|
+
icon: import_lucide_react12.Plus,
|
|
2905
3040
|
text: "Add root",
|
|
2906
3041
|
onClick: () => handleGenreCreationAction(null)
|
|
2907
3042
|
}
|
|
2908
3043
|
)
|
|
2909
3044
|
] })
|
|
2910
3045
|
] });
|
|
2911
|
-
return /* @__PURE__ */ (0,
|
|
3046
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2912
3047
|
"div",
|
|
2913
3048
|
{
|
|
2914
3049
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2915
3050
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2916
3051
|
children: [
|
|
2917
|
-
/* @__PURE__ */ (0,
|
|
3052
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2918
3053
|
"div",
|
|
2919
3054
|
{
|
|
2920
3055
|
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
|
|
2921
3056
|
children: actions
|
|
2922
3057
|
}
|
|
2923
3058
|
),
|
|
2924
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3059
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2925
3060
|
"div",
|
|
2926
3061
|
{
|
|
2927
3062
|
className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "top-5"}`,
|
|
2928
3063
|
children: renderArtistFilter()
|
|
2929
3064
|
}
|
|
2930
3065
|
),
|
|
2931
|
-
/* @__PURE__ */ (0,
|
|
2932
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
2933
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3066
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3067
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3068
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2934
3069
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2935
3070
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2936
3071
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2937
3072
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2938
|
-
/* @__PURE__ */ (0,
|
|
3073
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2939
3074
|
GenrePlaylistTreeWheel,
|
|
2940
3075
|
{
|
|
2941
3076
|
scope,
|
|
@@ -2952,9 +3087,9 @@ function GenreTreeView({
|
|
|
2952
3087
|
allowWheelRotation,
|
|
2953
3088
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2954
3089
|
busy,
|
|
2955
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3090
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2956
3091
|
floatingControlsLeading,
|
|
2957
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3092
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2958
3093
|
] }) : void 0,
|
|
2959
3094
|
defaultViewport,
|
|
2960
3095
|
viewportRestore,
|
|
@@ -2963,7 +3098,7 @@ function GenreTreeView({
|
|
|
2963
3098
|
) }) })
|
|
2964
3099
|
) : viewMode === "pop-core" ? (
|
|
2965
3100
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2966
|
-
/* @__PURE__ */ (0,
|
|
3101
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2967
3102
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2968
3103
|
{
|
|
2969
3104
|
scope,
|
|
@@ -2980,9 +3115,9 @@ function GenreTreeView({
|
|
|
2980
3115
|
allowWheelRotation,
|
|
2981
3116
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2982
3117
|
busy,
|
|
2983
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3118
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2984
3119
|
floatingControlsLeading,
|
|
2985
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3120
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2986
3121
|
] }) : void 0,
|
|
2987
3122
|
defaultViewport,
|
|
2988
3123
|
viewportRestore,
|
|
@@ -2990,7 +3125,7 @@ function GenreTreeView({
|
|
|
2990
3125
|
...focus
|
|
2991
3126
|
}
|
|
2992
3127
|
) }) })
|
|
2993
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3128
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2994
3129
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2995
3130
|
{
|
|
2996
3131
|
outline: true,
|
|
@@ -3006,13 +3141,13 @@ function GenreTreeView({
|
|
|
3006
3141
|
selectedNodeId,
|
|
3007
3142
|
readOnly
|
|
3008
3143
|
}
|
|
3009
|
-
) }) : /* @__PURE__ */ (0,
|
|
3144
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
3010
3145
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3011
|
-
return /* @__PURE__ */ (0,
|
|
3146
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3012
3147
|
"div",
|
|
3013
3148
|
{
|
|
3014
3149
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3015
|
-
children: /* @__PURE__ */ (0,
|
|
3150
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3016
3151
|
GenrePlaylistTreePerRoot,
|
|
3017
3152
|
{
|
|
3018
3153
|
scope,
|
|
@@ -3038,8 +3173,8 @@ function GenreTreeView({
|
|
|
3038
3173
|
);
|
|
3039
3174
|
}
|
|
3040
3175
|
) }),
|
|
3041
|
-
/* @__PURE__ */ (0,
|
|
3042
|
-
|
|
3176
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3177
|
+
import_ui17.DetailPanel,
|
|
3043
3178
|
{
|
|
3044
3179
|
ref: panelRef,
|
|
3045
3180
|
side: "left",
|
|
@@ -3049,13 +3184,13 @@ function GenreTreeView({
|
|
|
3049
3184
|
headerAccent: activePanel?.headerAccent,
|
|
3050
3185
|
headerActions: expandButton,
|
|
3051
3186
|
onClose: activePanel?.onClose ?? noop,
|
|
3052
|
-
collapsed: isCollapsed || !
|
|
3187
|
+
collapsed: isCollapsed || !isPanelShown,
|
|
3053
3188
|
expanded: activePanel?.expanded,
|
|
3054
3189
|
onExpandedChange: activePanel?.onExpandedChange,
|
|
3055
3190
|
aside: activePanel?.aside,
|
|
3056
3191
|
edgeTab: detailTab,
|
|
3057
|
-
headerClassName:
|
|
3058
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3192
|
+
headerClassName: "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]",
|
|
3193
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
3059
3194
|
activePanel.content,
|
|
3060
3195
|
activePanel.aside
|
|
3061
3196
|
] }) : activePanel?.content
|
|
@@ -3067,134 +3202,6 @@ function GenreTreeView({
|
|
|
3067
3202
|
);
|
|
3068
3203
|
}
|
|
3069
3204
|
|
|
3070
|
-
// src/genre-tree/GenreDetailPanel.tsx
|
|
3071
|
-
var import_react18 = require("react");
|
|
3072
|
-
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3073
|
-
var import_ui17 = require("@behindthemusictree/ui");
|
|
3074
|
-
var import_lucide_react13 = require("lucide-react");
|
|
3075
|
-
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3076
|
-
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3077
|
-
var RELATIVE_ITEM_COUNT = 1;
|
|
3078
|
-
function toNode(genre, parentId) {
|
|
3079
|
-
return {
|
|
3080
|
-
id: genre.uuid,
|
|
3081
|
-
parentId,
|
|
3082
|
-
name: genre.name,
|
|
3083
|
-
itemCount: RELATIVE_ITEM_COUNT,
|
|
3084
|
-
side: genre.side ?? void 0,
|
|
3085
|
-
wikidataId: genre.wikidataId
|
|
3086
|
-
};
|
|
3087
|
-
}
|
|
3088
|
-
function GenreDetailPanel({
|
|
3089
|
-
genreUuid,
|
|
3090
|
-
scope,
|
|
3091
|
-
getBackendBaseUrl,
|
|
3092
|
-
onClose,
|
|
3093
|
-
onSelectGenre,
|
|
3094
|
-
onZoomIn,
|
|
3095
|
-
shareUrl,
|
|
3096
|
-
shareTarget,
|
|
3097
|
-
collapsed,
|
|
3098
|
-
expanded,
|
|
3099
|
-
onExpandedChange,
|
|
3100
|
-
edgeTab,
|
|
3101
|
-
extras
|
|
3102
|
-
}) {
|
|
3103
|
-
const { data: detail, isPending, isError } = (0, import_genre_tree28.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3104
|
-
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3105
|
-
const view = (0, import_react18.useMemo)(() => {
|
|
3106
|
-
if (!detail) return null;
|
|
3107
|
-
const rootColor = (0, import_genre_tree_view5.getGenreTreeColor)(detail.root.wikidataId);
|
|
3108
|
-
const relative = (genre, parentId) => ({
|
|
3109
|
-
node: toNode(genre, parentId),
|
|
3110
|
-
fill: (0, import_genre_tree_view5.tintSurface)(rootColor),
|
|
3111
|
-
textColor: import_genre_tree_view5.TEXT_COLOR
|
|
3112
|
-
});
|
|
3113
|
-
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
3114
|
-
return {
|
|
3115
|
-
node: {
|
|
3116
|
-
id: detail.uuid,
|
|
3117
|
-
parentId: detail.parent?.uuid ?? null,
|
|
3118
|
-
name: detail.name,
|
|
3119
|
-
itemCount: scope === "reference" ? (0, import_genre_tree28.genreMusicCount)(import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
|
|
3120
|
-
wikidataId: detail.wikidataId
|
|
3121
|
-
},
|
|
3122
|
-
fill: rootColor,
|
|
3123
|
-
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
3124
|
-
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
3125
|
-
ancestorNodes: ancestors2.map(
|
|
3126
|
-
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
3127
|
-
)
|
|
3128
|
-
};
|
|
3129
|
-
}, [detail, scope]);
|
|
3130
|
-
if (isError) {
|
|
3131
|
-
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3132
|
-
}
|
|
3133
|
-
if (isPending || !detail || !view) {
|
|
3134
|
-
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3135
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-6 w-2/3" }),
|
|
3136
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-4 w-full" }),
|
|
3137
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-4 w-3/4" })
|
|
3138
|
-
] });
|
|
3139
|
-
}
|
|
3140
|
-
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3141
|
-
const family = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3142
|
-
import_genre_tree_view5.GenreNodeFamily,
|
|
3143
|
-
{
|
|
3144
|
-
node: view.node,
|
|
3145
|
-
fill: view.fill,
|
|
3146
|
-
textColor: import_genre_tree_view5.ACCENT_TEXT_COLOR,
|
|
3147
|
-
parentNode: view.parentNode,
|
|
3148
|
-
childNodes: view.childNodes,
|
|
3149
|
-
onSelectNode: selectGenre
|
|
3150
|
-
}
|
|
3151
|
-
);
|
|
3152
|
-
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3153
|
-
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3154
|
-
import_ui17.DetailPanel,
|
|
3155
|
-
{
|
|
3156
|
-
side: "left",
|
|
3157
|
-
title: view.node.name,
|
|
3158
|
-
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3159
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3160
|
-
import_ui17.IconButton,
|
|
3161
|
-
{
|
|
3162
|
-
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3163
|
-
style: { color: "inherit" },
|
|
3164
|
-
onClick: () => onExpandedChange(!expanded),
|
|
3165
|
-
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3166
|
-
}
|
|
3167
|
-
) : void 0,
|
|
3168
|
-
onClose,
|
|
3169
|
-
collapsed,
|
|
3170
|
-
expanded,
|
|
3171
|
-
onExpandedChange,
|
|
3172
|
-
edgeTab,
|
|
3173
|
-
children: [
|
|
3174
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3175
|
-
GenreInfoPanelBody,
|
|
3176
|
-
{
|
|
3177
|
-
family: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
3178
|
-
family,
|
|
3179
|
-
ancestors
|
|
3180
|
-
] }),
|
|
3181
|
-
genreUuid: detail.uuid,
|
|
3182
|
-
genrePlaylist: scope === "reference" ? import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3183
|
-
catalogue: detail.catalogue,
|
|
3184
|
-
wikidataId: detail.wikidataId,
|
|
3185
|
-
scope,
|
|
3186
|
-
getBackendBaseUrl,
|
|
3187
|
-
playback,
|
|
3188
|
-
band: { node: view.node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
3189
|
-
extras
|
|
3190
|
-
}
|
|
3191
|
-
),
|
|
3192
|
-
!expanded && ancestors
|
|
3193
|
-
]
|
|
3194
|
-
}
|
|
3195
|
-
);
|
|
3196
|
-
}
|
|
3197
|
-
|
|
3198
3205
|
// src/genre-tree/index.ts
|
|
3199
3206
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3200
3207
|
|