@behindthemusictree/app-kit-ui 34.1.0 → 36.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-RTXLUHJY.js} +469 -453
- package/dist/chunk-RTXLUHJY.js.map +1 -0
- package/dist/genre-tree/index.cjs +518 -503
- 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 +518 -503
- 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
package/dist/index.cjs
CHANGED
|
@@ -587,13 +587,13 @@ function AuthCallbackHandler({ renderStatus, ...options }) {
|
|
|
587
587
|
}
|
|
588
588
|
|
|
589
589
|
// src/genre-tree/GenreTreeView.tsx
|
|
590
|
-
var
|
|
590
|
+
var import_react18 = require("react");
|
|
591
591
|
var import_react_query3 = require("@tanstack/react-query");
|
|
592
|
-
var import_lucide_react15 = require("lucide-react");
|
|
593
592
|
var import_lucide_react16 = require("lucide-react");
|
|
594
|
-
var
|
|
595
|
-
var
|
|
596
|
-
var
|
|
593
|
+
var import_lucide_react17 = require("lucide-react");
|
|
594
|
+
var import_ui20 = require("@behindthemusictree/ui");
|
|
595
|
+
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
596
|
+
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
597
597
|
var import_popup6 = require("@behindthemusictree/app-kit/popup");
|
|
598
598
|
|
|
599
599
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1240,6 +1240,7 @@ function GenreTreeSearch({
|
|
|
1240
1240
|
artistSearch,
|
|
1241
1241
|
songSearch,
|
|
1242
1242
|
SearchGroups,
|
|
1243
|
+
onSelectGenre,
|
|
1243
1244
|
extraGroups = [],
|
|
1244
1245
|
onQueryChange,
|
|
1245
1246
|
onFocus,
|
|
@@ -1265,9 +1266,16 @@ function GenreTreeSearch({
|
|
|
1265
1266
|
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
1266
1267
|
}
|
|
1267
1268
|
);
|
|
1268
|
-
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
1269
|
+
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
|
|
1269
1270
|
}
|
|
1270
1271
|
|
|
1272
|
+
// src/genre-tree/GenreDetailPanel.tsx
|
|
1273
|
+
var import_react14 = require("react");
|
|
1274
|
+
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
1275
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1276
|
+
var import_lucide_react12 = require("lucide-react");
|
|
1277
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1278
|
+
|
|
1271
1279
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1272
1280
|
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1273
1281
|
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
@@ -1689,6 +1697,7 @@ var nextCursor = (next) => {
|
|
|
1689
1697
|
function GenrePlaylistTracks({
|
|
1690
1698
|
genreUuid,
|
|
1691
1699
|
genrePlaylist,
|
|
1700
|
+
genreName: selectedGenreName,
|
|
1692
1701
|
selection,
|
|
1693
1702
|
scope,
|
|
1694
1703
|
getBackendBaseUrl,
|
|
@@ -1704,14 +1713,18 @@ function GenrePlaylistTracks({
|
|
|
1704
1713
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1705
1714
|
const { showPopup } = (0, import_popup5.usePopup)();
|
|
1706
1715
|
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1707
|
-
const
|
|
1708
|
-
const
|
|
1716
|
+
const uuid = genrePlaylist?.uuid;
|
|
1717
|
+
const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
|
|
1718
|
+
const { fetchBound } = (0, import_transport5.usePlaybackFetch)(getBackendBaseUrl);
|
|
1709
1719
|
const playback = (0, import_transport5.usePlaybackContext)();
|
|
1710
1720
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1711
|
-
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] : []],
|
|
1721
|
+
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] : []],
|
|
1712
1722
|
queryFn: async ({ pageParam }) => {
|
|
1713
|
-
if (scope === "me")
|
|
1714
|
-
|
|
1723
|
+
if (scope === "me") {
|
|
1724
|
+
if (!uuid) throw new Error("Personal genre has no playlist");
|
|
1725
|
+
return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1726
|
+
}
|
|
1727
|
+
const response = await fetchBound(
|
|
1715
1728
|
import_genre_tree13.libraryEndpoints.reference.songs.list(),
|
|
1716
1729
|
true,
|
|
1717
1730
|
false,
|
|
@@ -1723,7 +1736,9 @@ function GenrePlaylistTracks({
|
|
|
1723
1736
|
},
|
|
1724
1737
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1725
1738
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1726
|
-
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1739
|
+
enabled: playback.ready && (scope !== "me" || uuid !== void 0 && sessionRestored && !!session?.accessToken),
|
|
1740
|
+
retry: (failureCount, error) => failureCount < 1 && error instanceof DOMException && error.name === "AbortError",
|
|
1741
|
+
retryDelay: 0
|
|
1727
1742
|
});
|
|
1728
1743
|
const [sentinel, setSentinel] = (0, import_react12.useState)(null);
|
|
1729
1744
|
(0, import_react12.useEffect)(() => {
|
|
@@ -1745,7 +1760,7 @@ function GenrePlaylistTracks({
|
|
|
1745
1760
|
ordering: "title",
|
|
1746
1761
|
genreUuid,
|
|
1747
1762
|
...selection && { selection }
|
|
1748
|
-
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name:
|
|
1763
|
+
}) : 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"));
|
|
1749
1764
|
started.catch((error) => {
|
|
1750
1765
|
console.error("Failed to play genre playlist:", error);
|
|
1751
1766
|
showPopup(/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
|
|
@@ -1753,13 +1768,13 @@ function GenrePlaylistTracks({
|
|
|
1753
1768
|
};
|
|
1754
1769
|
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { children: [
|
|
1755
1770
|
renderTracksToolbar?.(),
|
|
1756
|
-
isPending ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { role: "alert", children: [
|
|
1771
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("p", { role: "alert", children: [
|
|
1757
1772
|
"Couldn't load ",
|
|
1758
1773
|
trackNoun.plural,
|
|
1759
1774
|
".",
|
|
1760
1775
|
" ",
|
|
1761
1776
|
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1762
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${
|
|
1777
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime29.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: [
|
|
1763
1778
|
tracks.map((track, index) => {
|
|
1764
1779
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1765
1780
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
@@ -1826,6 +1841,7 @@ var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
|
1826
1841
|
function GenreDetailExtras({
|
|
1827
1842
|
genreUuid,
|
|
1828
1843
|
genrePlaylist,
|
|
1844
|
+
genreName,
|
|
1829
1845
|
selection,
|
|
1830
1846
|
wikidataId,
|
|
1831
1847
|
scope,
|
|
@@ -1893,6 +1909,7 @@ function GenreDetailExtras({
|
|
|
1893
1909
|
{
|
|
1894
1910
|
genreUuid,
|
|
1895
1911
|
genrePlaylist,
|
|
1912
|
+
genreName,
|
|
1896
1913
|
selection,
|
|
1897
1914
|
scope,
|
|
1898
1915
|
getBackendBaseUrl,
|
|
@@ -1934,6 +1951,7 @@ function GenreInfoPanelBody({
|
|
|
1934
1951
|
genreUuid,
|
|
1935
1952
|
genrePlaylist,
|
|
1936
1953
|
catalogue,
|
|
1954
|
+
countsContext,
|
|
1937
1955
|
wikidataId,
|
|
1938
1956
|
scope,
|
|
1939
1957
|
getBackendBaseUrl,
|
|
@@ -1942,7 +1960,7 @@ function GenreInfoPanelBody({
|
|
|
1942
1960
|
extras
|
|
1943
1961
|
}) {
|
|
1944
1962
|
const noun = (0, import_track_noun4.useTrackNoun)();
|
|
1945
|
-
const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist) : void 0;
|
|
1963
|
+
const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
1946
1964
|
const selection = summary ? (0, import_genre_tree18.defaultGenreMusicSelection)(summary) : void 0;
|
|
1947
1965
|
const describeCounts = (counts) => {
|
|
1948
1966
|
const scopes = counts.genreAndSubgenresTracksCount;
|
|
@@ -1950,17 +1968,19 @@ function GenreInfoPanelBody({
|
|
|
1950
1968
|
};
|
|
1951
1969
|
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1952
1970
|
toolbar,
|
|
1953
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1971
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(GenreActionBand, { ...band }),
|
|
1954
1972
|
summary && catalogue && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1955
1973
|
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { children: [
|
|
1956
1974
|
"Catalogue ",
|
|
1957
1975
|
noun.plural,
|
|
1976
|
+
countsContext ? ` (${countsContext})` : "",
|
|
1958
1977
|
": ",
|
|
1959
1978
|
describeCounts(catalogue)
|
|
1960
1979
|
] }),
|
|
1961
|
-
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { children: [
|
|
1980
|
+
genrePlaylist && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { children: [
|
|
1962
1981
|
"Playlist ",
|
|
1963
1982
|
noun.plural,
|
|
1983
|
+
countsContext ? ` (${countsContext})` : "",
|
|
1964
1984
|
": ",
|
|
1965
1985
|
describeCounts(summary)
|
|
1966
1986
|
] })
|
|
@@ -1973,6 +1993,7 @@ function GenreInfoPanelBody({
|
|
|
1973
1993
|
family,
|
|
1974
1994
|
genreUuid,
|
|
1975
1995
|
genrePlaylist,
|
|
1996
|
+
genreName: band.node.name,
|
|
1976
1997
|
selection,
|
|
1977
1998
|
wikidataId,
|
|
1978
1999
|
scope,
|
|
@@ -1982,10 +2003,160 @@ function GenreInfoPanelBody({
|
|
|
1982
2003
|
] });
|
|
1983
2004
|
}
|
|
1984
2005
|
|
|
1985
|
-
// src/genre-tree/
|
|
1986
|
-
var import_react14 = require("react");
|
|
1987
|
-
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2006
|
+
// src/genre-tree/GenreDetailPanel.tsx
|
|
1988
2007
|
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
2008
|
+
var RELATIVE_ITEM_COUNT = 1;
|
|
2009
|
+
function toNode(genre, parentId) {
|
|
2010
|
+
return {
|
|
2011
|
+
id: genre.uuid,
|
|
2012
|
+
parentId,
|
|
2013
|
+
name: genre.name,
|
|
2014
|
+
itemCount: RELATIVE_ITEM_COUNT,
|
|
2015
|
+
side: genre.side ?? void 0,
|
|
2016
|
+
wikidataId: genre.wikidataId
|
|
2017
|
+
};
|
|
2018
|
+
}
|
|
2019
|
+
function useGenreDetailContent({
|
|
2020
|
+
genreUuid,
|
|
2021
|
+
scope,
|
|
2022
|
+
getBackendBaseUrl,
|
|
2023
|
+
onSelectGenre,
|
|
2024
|
+
extras,
|
|
2025
|
+
band,
|
|
2026
|
+
playback: hostPlayback,
|
|
2027
|
+
trailing,
|
|
2028
|
+
genrePlaylist: hostPlaylist,
|
|
2029
|
+
expanded
|
|
2030
|
+
}) {
|
|
2031
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree19.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
2032
|
+
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
2033
|
+
const playback = hostPlayback ?? defaultPlayback;
|
|
2034
|
+
const view = (0, import_react14.useMemo)(() => {
|
|
2035
|
+
if (!detail) return null;
|
|
2036
|
+
const rootColor = (0, import_genre_tree_view4.getGenreTreeColor)(detail.root.wikidataId);
|
|
2037
|
+
const relative = (genre, parentId) => ({
|
|
2038
|
+
node: toNode(genre, parentId),
|
|
2039
|
+
fill: (0, import_genre_tree_view4.tintSurface)(rootColor),
|
|
2040
|
+
textColor: import_genre_tree_view4.TEXT_COLOR
|
|
2041
|
+
});
|
|
2042
|
+
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
2043
|
+
return {
|
|
2044
|
+
node: {
|
|
2045
|
+
id: detail.uuid,
|
|
2046
|
+
parentId: detail.parent?.uuid ?? null,
|
|
2047
|
+
name: detail.name,
|
|
2048
|
+
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,
|
|
2049
|
+
wikidataId: detail.wikidataId
|
|
2050
|
+
},
|
|
2051
|
+
fill: rootColor,
|
|
2052
|
+
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
2053
|
+
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
2054
|
+
ancestorNodes: ancestors2.map(
|
|
2055
|
+
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
2056
|
+
)
|
|
2057
|
+
};
|
|
2058
|
+
}, [detail, scope]);
|
|
2059
|
+
if (isError) return {
|
|
2060
|
+
title: "Genre unavailable",
|
|
2061
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
2062
|
+
};
|
|
2063
|
+
if (isPending || !detail || !view) return {
|
|
2064
|
+
title: "Loading genre\u2026",
|
|
2065
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2066
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.Skeleton, { className: "h-4 w-full" }),
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.Skeleton, { className: "h-4 w-3/4" })
|
|
2069
|
+
] })
|
|
2070
|
+
};
|
|
2071
|
+
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree19.GenreUuidSchema.parse(nodeId));
|
|
2072
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
2073
|
+
import_genre_tree_view4.GenreNodeFamily,
|
|
2074
|
+
{
|
|
2075
|
+
node: view.node,
|
|
2076
|
+
fill: view.fill,
|
|
2077
|
+
textColor: import_genre_tree_view4.ACCENT_TEXT_COLOR,
|
|
2078
|
+
parentNode: view.parentNode,
|
|
2079
|
+
childNodes: view.childNodes,
|
|
2080
|
+
onSelectNode: selectGenre
|
|
2081
|
+
}
|
|
2082
|
+
);
|
|
2083
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2084
|
+
return {
|
|
2085
|
+
title: detail.name,
|
|
2086
|
+
headerAccent: { background: view.fill, color: import_genre_tree_view4.ACCENT_TEXT_COLOR },
|
|
2087
|
+
aside: expanded ? ancestors : void 0,
|
|
2088
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
2089
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
2090
|
+
GenreInfoPanelBody,
|
|
2091
|
+
{
|
|
2092
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_jsx_runtime32.Fragment, { children: [
|
|
2093
|
+
family,
|
|
2094
|
+
!expanded && ancestors
|
|
2095
|
+
] }),
|
|
2096
|
+
genreUuid: detail.uuid,
|
|
2097
|
+
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 }),
|
|
2098
|
+
catalogue: detail.catalogue,
|
|
2099
|
+
countsContext: !hostPlaylist && extras?.artist ? "all artists" : void 0,
|
|
2100
|
+
wikidataId: detail.wikidataId,
|
|
2101
|
+
scope,
|
|
2102
|
+
getBackendBaseUrl,
|
|
2103
|
+
playback,
|
|
2104
|
+
band: { node: view.node, ...band, shareTarget: band?.shareTarget?.kind === "genre" ? { ...band.shareTarget, name: detail.name } : band?.shareTarget },
|
|
2105
|
+
extras
|
|
2106
|
+
}
|
|
2107
|
+
),
|
|
2108
|
+
trailing
|
|
2109
|
+
] })
|
|
2110
|
+
};
|
|
2111
|
+
}
|
|
2112
|
+
function GenreDetailPanel({
|
|
2113
|
+
genreUuid,
|
|
2114
|
+
scope,
|
|
2115
|
+
getBackendBaseUrl,
|
|
2116
|
+
onClose,
|
|
2117
|
+
onSelectGenre,
|
|
2118
|
+
onZoomIn,
|
|
2119
|
+
shareUrl,
|
|
2120
|
+
shareTarget,
|
|
2121
|
+
collapsed,
|
|
2122
|
+
expanded,
|
|
2123
|
+
onExpandedChange,
|
|
2124
|
+
edgeTab,
|
|
2125
|
+
extras,
|
|
2126
|
+
headerClassName
|
|
2127
|
+
}) {
|
|
2128
|
+
const panel = useGenreDetailContent({
|
|
2129
|
+
genreUuid,
|
|
2130
|
+
scope,
|
|
2131
|
+
getBackendBaseUrl,
|
|
2132
|
+
onSelectGenre,
|
|
2133
|
+
extras,
|
|
2134
|
+
expanded,
|
|
2135
|
+
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
2136
|
+
});
|
|
2137
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
2138
|
+
import_ui10.DetailPanel,
|
|
2139
|
+
{
|
|
2140
|
+
side: "left",
|
|
2141
|
+
title: panel.title,
|
|
2142
|
+
headerAccent: panel.headerAccent,
|
|
2143
|
+
onClose,
|
|
2144
|
+
collapsed,
|
|
2145
|
+
expanded,
|
|
2146
|
+
onExpandedChange,
|
|
2147
|
+
edgeTab,
|
|
2148
|
+
aside: panel.aside,
|
|
2149
|
+
headerClassName,
|
|
2150
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react12.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react12.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
2151
|
+
children: panel.content
|
|
2152
|
+
}
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
// src/genre-tree/ChangeParentPopup.tsx
|
|
2157
|
+
var import_react15 = require("react");
|
|
2158
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2159
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1989
2160
|
function ChangeParentPopup({
|
|
1990
2161
|
genrePlaylist,
|
|
1991
2162
|
genrePlaylists,
|
|
@@ -1994,9 +2165,9 @@ function ChangeParentPopup({
|
|
|
1994
2165
|
onClose,
|
|
1995
2166
|
onParentChanged
|
|
1996
2167
|
}) {
|
|
1997
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1998
|
-
const candidates = (0,
|
|
1999
|
-
() => (0,
|
|
2168
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree20.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2169
|
+
const candidates = (0, import_react15.useMemo)(
|
|
2170
|
+
() => (0, import_genre_tree20.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
2000
2171
|
[genrePlaylists, genrePlaylist.uuid]
|
|
2001
2172
|
);
|
|
2002
2173
|
const handleSelect = (newParent) => {
|
|
@@ -2013,19 +2184,19 @@ function ChangeParentPopup({
|
|
|
2013
2184
|
}
|
|
2014
2185
|
);
|
|
2015
2186
|
};
|
|
2016
|
-
(0,
|
|
2187
|
+
(0, import_react15.useEffect)(() => {
|
|
2017
2188
|
const handleKeyDown = (event) => {
|
|
2018
2189
|
if (event.key === "Escape") onClose();
|
|
2019
2190
|
};
|
|
2020
2191
|
window.addEventListener("keydown", handleKeyDown);
|
|
2021
2192
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
2022
2193
|
}, [onClose]);
|
|
2023
|
-
return /* @__PURE__ */ (0,
|
|
2024
|
-
/* @__PURE__ */ (0,
|
|
2194
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
2195
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
2025
2196
|
"Current parent: ",
|
|
2026
2197
|
genrePlaylist.parent?.name ?? "none"
|
|
2027
2198
|
] }),
|
|
2028
|
-
/* @__PURE__ */ (0,
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
2029
2200
|
GenreSearch,
|
|
2030
2201
|
{
|
|
2031
2202
|
genrePlaylists: candidates,
|
|
@@ -2033,24 +2204,24 @@ function ChangeParentPopup({
|
|
|
2033
2204
|
placeholder: "Search a new parent\u2026"
|
|
2034
2205
|
}
|
|
2035
2206
|
) }),
|
|
2036
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
2207
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
2037
2208
|
] });
|
|
2038
2209
|
}
|
|
2039
2210
|
|
|
2040
2211
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2041
|
-
var
|
|
2042
|
-
var
|
|
2212
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
2213
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2043
2214
|
|
|
2044
2215
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2045
|
-
var
|
|
2216
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
2046
2217
|
|
|
2047
2218
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
2048
|
-
var
|
|
2219
|
+
var import_lucide_react13 = require("lucide-react");
|
|
2049
2220
|
var import_si3 = require("react-icons/si");
|
|
2050
|
-
var
|
|
2051
|
-
var
|
|
2221
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
2222
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
2052
2223
|
var LINK_KINDS = {
|
|
2053
|
-
official: { label: "Official website", Icon:
|
|
2224
|
+
official: { label: "Official website", Icon: import_lucide_react13.Globe },
|
|
2054
2225
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
2055
2226
|
wikidata: { label: "Wikidata", Icon: import_si3.SiWikidata },
|
|
2056
2227
|
spotify: { label: "Spotify", Icon: import_si3.SiSpotify },
|
|
@@ -2058,7 +2229,7 @@ var LINK_KINDS = {
|
|
|
2058
2229
|
bandcamp: { label: "Bandcamp", Icon: import_si3.SiBandcamp },
|
|
2059
2230
|
soundcloud: { label: "SoundCloud", Icon: import_si3.SiSoundcloud },
|
|
2060
2231
|
discogs: { label: "Discogs", Icon: import_si3.SiDiscogs },
|
|
2061
|
-
social: { label: "Social", Icon:
|
|
2232
|
+
social: { label: "Social", Icon: import_lucide_react13.Share2 }
|
|
2062
2233
|
};
|
|
2063
2234
|
function socialLabel(url) {
|
|
2064
2235
|
try {
|
|
@@ -2085,7 +2256,7 @@ function artistLinks(artist) {
|
|
|
2085
2256
|
return links;
|
|
2086
2257
|
}
|
|
2087
2258
|
function ArtistLinks({ links }) {
|
|
2088
|
-
return /* @__PURE__ */ (0,
|
|
2259
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2089
2260
|
}
|
|
2090
2261
|
|
|
2091
2262
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2096,7 +2267,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2096
2267
|
}
|
|
2097
2268
|
|
|
2098
2269
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2099
|
-
var
|
|
2270
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2100
2271
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2101
2272
|
function ArtistHeader({ artist }) {
|
|
2102
2273
|
const fields = [];
|
|
@@ -2106,35 +2277,35 @@ function ArtistHeader({ artist }) {
|
|
|
2106
2277
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2107
2278
|
if (years) fields.push({ label: "Active", value: years });
|
|
2108
2279
|
const links = artistLinks(artist);
|
|
2109
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2280
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistLinks, { links }) });
|
|
2110
2281
|
if (fields.length === 0) return null;
|
|
2111
|
-
return /* @__PURE__ */ (0,
|
|
2282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.FieldList, { items: fields });
|
|
2112
2283
|
}
|
|
2113
2284
|
|
|
2114
2285
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2115
|
-
var
|
|
2286
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2116
2287
|
function ExternalLink({ href, children }) {
|
|
2117
|
-
return /* @__PURE__ */ (0,
|
|
2288
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
2118
2289
|
}
|
|
2119
2290
|
function ArtistPhoto({ image, name }) {
|
|
2120
|
-
return /* @__PURE__ */ (0,
|
|
2121
|
-
/* @__PURE__ */ (0,
|
|
2122
|
-
/* @__PURE__ */ (0,
|
|
2291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
2292
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2293
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
2123
2294
|
"Photo: ",
|
|
2124
|
-
/* @__PURE__ */ (0,
|
|
2295
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
2125
2296
|
",",
|
|
2126
2297
|
" ",
|
|
2127
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
2298
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
2128
2299
|
] })
|
|
2129
2300
|
] });
|
|
2130
2301
|
}
|
|
2131
2302
|
|
|
2132
2303
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2133
|
-
var
|
|
2134
|
-
var
|
|
2304
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
2305
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2135
2306
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2136
2307
|
if (memberships.length === 0) return null;
|
|
2137
|
-
return /* @__PURE__ */ (0,
|
|
2308
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui13.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2138
2309
|
const details = [
|
|
2139
2310
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2140
2311
|
...membership.roles
|
|
@@ -2142,9 +2313,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2142
2313
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
2143
2314
|
return (
|
|
2144
2315
|
// A member can have several periods in the same group.
|
|
2145
|
-
/* @__PURE__ */ (0,
|
|
2146
|
-
/* @__PURE__ */ (0,
|
|
2147
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2316
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2317
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2318
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2148
2319
|
" \u2014 ",
|
|
2149
2320
|
detailsText
|
|
2150
2321
|
] })
|
|
@@ -2155,9 +2326,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2155
2326
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2156
2327
|
if (memberships.length === 0) return null;
|
|
2157
2328
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2158
|
-
return /* @__PURE__ */ (0,
|
|
2159
|
-
/* @__PURE__ */ (0,
|
|
2160
|
-
/* @__PURE__ */ (0,
|
|
2329
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui13.PanelSection, { title, children: [
|
|
2330
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2331
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2161
2332
|
MembershipList,
|
|
2162
2333
|
{
|
|
2163
2334
|
title: "Past",
|
|
@@ -2169,8 +2340,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2169
2340
|
}
|
|
2170
2341
|
|
|
2171
2342
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2172
|
-
var
|
|
2173
|
-
var
|
|
2343
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2344
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2174
2345
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2175
2346
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2176
2347
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2190,34 +2361,34 @@ function byYear(a, b) {
|
|
|
2190
2361
|
function ArtistDiscography({ albums }) {
|
|
2191
2362
|
if (albums.length === 0) return null;
|
|
2192
2363
|
const sorted = [...albums].sort(byYear);
|
|
2193
|
-
return /* @__PURE__ */ (0,
|
|
2364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2194
2365
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2195
2366
|
if (groupAlbums.length === 0) return null;
|
|
2196
|
-
return /* @__PURE__ */ (0,
|
|
2197
|
-
/* @__PURE__ */ (0,
|
|
2198
|
-
/* @__PURE__ */ (0,
|
|
2367
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui14.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2368
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2199
2370
|
] }, album.uuid)) }) }, group);
|
|
2200
2371
|
}) });
|
|
2201
2372
|
}
|
|
2202
2373
|
|
|
2203
2374
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2204
|
-
var
|
|
2375
|
+
var import_react16 = require("react");
|
|
2205
2376
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2206
|
-
var
|
|
2207
|
-
var
|
|
2377
|
+
var import_lucide_react14 = require("lucide-react");
|
|
2378
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
2208
2379
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2209
|
-
var
|
|
2380
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2210
2381
|
var import_transport9 = require("@behindthemusictree/app-kit/transport");
|
|
2211
2382
|
|
|
2212
2383
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2213
2384
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2214
|
-
var
|
|
2385
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2215
2386
|
|
|
2216
2387
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
2217
2388
|
var import_fa5 = require("react-icons/fa");
|
|
2218
2389
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2219
2390
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2220
|
-
var
|
|
2391
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2221
2392
|
function TrackPositionPlayPause({
|
|
2222
2393
|
position,
|
|
2223
2394
|
uuid,
|
|
@@ -2231,14 +2402,14 @@ function TrackPositionPlayPause({
|
|
|
2231
2402
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
2232
2403
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
2233
2404
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2234
|
-
return /* @__PURE__ */ (0,
|
|
2405
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2235
2406
|
"div",
|
|
2236
2407
|
{
|
|
2237
2408
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2238
2409
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2239
2410
|
onClick: handlePlayPauseClick,
|
|
2240
2411
|
children: [
|
|
2241
|
-
/* @__PURE__ */ (0,
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2242
2413
|
"span",
|
|
2243
2414
|
{
|
|
2244
2415
|
className: "track-position-play-pause__bars",
|
|
@@ -2246,18 +2417,18 @@ function TrackPositionPlayPause({
|
|
|
2246
2417
|
role: "img",
|
|
2247
2418
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2248
2419
|
children: [
|
|
2249
|
-
/* @__PURE__ */ (0,
|
|
2250
|
-
/* @__PURE__ */ (0,
|
|
2251
|
-
/* @__PURE__ */ (0,
|
|
2420
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2421
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2422
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2252
2423
|
]
|
|
2253
2424
|
}
|
|
2254
2425
|
) : position }),
|
|
2255
|
-
/* @__PURE__ */ (0,
|
|
2426
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2256
2427
|
"div",
|
|
2257
2428
|
{
|
|
2258
2429
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2259
2430
|
"aria-hidden": "true",
|
|
2260
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2431
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_fa5.FaPlay, {})
|
|
2261
2432
|
}
|
|
2262
2433
|
)
|
|
2263
2434
|
]
|
|
@@ -2266,9 +2437,9 @@ function TrackPositionPlayPause({
|
|
|
2266
2437
|
}
|
|
2267
2438
|
|
|
2268
2439
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2269
|
-
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2270
2440
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2271
|
-
var
|
|
2441
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2442
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2272
2443
|
function TrackItem({
|
|
2273
2444
|
track,
|
|
2274
2445
|
position,
|
|
@@ -2278,11 +2449,11 @@ function TrackItem({
|
|
|
2278
2449
|
renderSongLink
|
|
2279
2450
|
}) {
|
|
2280
2451
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
2281
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2452
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree21.useTrackList)();
|
|
2282
2453
|
const scope = trackList?.origin?.scope ?? null;
|
|
2283
2454
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
2284
2455
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
2285
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2456
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree22.isSong)(track) && (0, import_genre_tree22.songUnplayableReason)(track) !== null;
|
|
2286
2457
|
const handlePlayPauseClick = (event) => {
|
|
2287
2458
|
event.stopPropagation();
|
|
2288
2459
|
if (isCurrentTrack) {
|
|
@@ -2292,12 +2463,12 @@ function TrackItem({
|
|
|
2292
2463
|
loadTrackForPlayer(track.uuid);
|
|
2293
2464
|
}
|
|
2294
2465
|
};
|
|
2295
|
-
return /* @__PURE__ */ (0,
|
|
2466
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2296
2467
|
"div",
|
|
2297
2468
|
{
|
|
2298
2469
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2299
2470
|
children: [
|
|
2300
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2471
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2301
2472
|
"div",
|
|
2302
2473
|
{
|
|
2303
2474
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2305,7 +2476,7 @@ function TrackItem({
|
|
|
2305
2476
|
"aria-disabled": "true",
|
|
2306
2477
|
children: position
|
|
2307
2478
|
}
|
|
2308
|
-
) : /* @__PURE__ */ (0,
|
|
2479
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2309
2480
|
TrackPositionPlayPause,
|
|
2310
2481
|
{
|
|
2311
2482
|
position,
|
|
@@ -2314,12 +2485,12 @@ function TrackItem({
|
|
|
2314
2485
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2315
2486
|
}
|
|
2316
2487
|
),
|
|
2317
|
-
/* @__PURE__ */ (0,
|
|
2318
|
-
/* @__PURE__ */ (0,
|
|
2319
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2489
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2490
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree23.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2320
2491
|
] }),
|
|
2321
|
-
/* @__PURE__ */ (0,
|
|
2322
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.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_runtime40.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2493
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2323
2494
|
"div",
|
|
2324
2495
|
{
|
|
2325
2496
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2327,28 +2498,28 @@ function TrackItem({
|
|
|
2327
2498
|
children: renderDuration(track)
|
|
2328
2499
|
}
|
|
2329
2500
|
),
|
|
2330
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2501
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2331
2502
|
]
|
|
2332
2503
|
}
|
|
2333
2504
|
);
|
|
2334
2505
|
}
|
|
2335
2506
|
|
|
2336
2507
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2337
|
-
var
|
|
2338
|
-
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(
|
|
2508
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2509
|
+
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree24.SongDetailedSchema);
|
|
2339
2510
|
var SORTS = [
|
|
2340
2511
|
{ ordering: "title", label: "Title" },
|
|
2341
2512
|
{ ordering: "-albumYear", label: "Year" }
|
|
2342
2513
|
];
|
|
2343
2514
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2344
|
-
const [page, setPage] = (0,
|
|
2345
|
-
const [ordering, setOrdering] = (0,
|
|
2346
|
-
const {
|
|
2347
|
-
const { playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
|
|
2515
|
+
const [page, setPage] = (0, import_react16.useState)(1);
|
|
2516
|
+
const [ordering, setOrdering] = (0, import_react16.useState)("title");
|
|
2517
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree24.useTrackList)();
|
|
2348
2518
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
2349
|
-
const { data, isPending, isError } = (0, import_transport9.
|
|
2519
|
+
const { data, isPending, isError } = (0, import_transport9.usePlaybackQuery)({
|
|
2520
|
+
getBackendBaseUrl,
|
|
2350
2521
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2351
|
-
queryFn: () =>
|
|
2522
|
+
queryFn: (fetchBound) => fetchBound(import_genre_tree24.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2352
2523
|
artist: artist.uuid,
|
|
2353
2524
|
ordering,
|
|
2354
2525
|
page
|
|
@@ -2363,25 +2534,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2363
2534
|
});
|
|
2364
2535
|
const handleRowClickCapture = (event, song) => {
|
|
2365
2536
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2366
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2537
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree24.songUnplayableReason)(song) !== null) return;
|
|
2367
2538
|
event.stopPropagation();
|
|
2368
2539
|
void play(song.uuid);
|
|
2369
2540
|
};
|
|
2370
|
-
return /* @__PURE__ */ (0,
|
|
2371
|
-
/* @__PURE__ */ (0,
|
|
2372
|
-
/* @__PURE__ */ (0,
|
|
2541
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2373
2544
|
"button",
|
|
2374
2545
|
{
|
|
2375
2546
|
type: "button",
|
|
2376
2547
|
onClick: () => void play(),
|
|
2377
2548
|
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",
|
|
2378
2549
|
children: [
|
|
2379
|
-
/* @__PURE__ */ (0,
|
|
2550
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2380
2551
|
"Play all"
|
|
2381
2552
|
]
|
|
2382
2553
|
}
|
|
2383
2554
|
),
|
|
2384
|
-
/* @__PURE__ */ (0,
|
|
2555
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2385
2556
|
"button",
|
|
2386
2557
|
{
|
|
2387
2558
|
type: "button",
|
|
@@ -2396,7 +2567,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2396
2567
|
sort.ordering
|
|
2397
2568
|
)) })
|
|
2398
2569
|
] }),
|
|
2399
|
-
/* @__PURE__ */ (0,
|
|
2570
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2400
2571
|
TrackItem,
|
|
2401
2572
|
{
|
|
2402
2573
|
track: song,
|
|
@@ -2405,12 +2576,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2405
2576
|
renderSongLink
|
|
2406
2577
|
}
|
|
2407
2578
|
) }, song.uuid)) }),
|
|
2408
|
-
/* @__PURE__ */ (0,
|
|
2579
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui15.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2409
2580
|
] }) });
|
|
2410
2581
|
}
|
|
2411
2582
|
|
|
2412
2583
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2413
|
-
var
|
|
2584
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2414
2585
|
function useArtistPanel({
|
|
2415
2586
|
uuid,
|
|
2416
2587
|
scope,
|
|
@@ -2422,7 +2593,7 @@ function useArtistPanel({
|
|
|
2422
2593
|
onExpandedChange,
|
|
2423
2594
|
onClose
|
|
2424
2595
|
}) {
|
|
2425
|
-
const result = (0,
|
|
2596
|
+
const result = (0, import_genre_tree25.useArtist)(uuid, getBackendBaseUrl);
|
|
2426
2597
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2427
2598
|
switch (result.status) {
|
|
2428
2599
|
case "idle":
|
|
@@ -2431,26 +2602,26 @@ function useArtistPanel({
|
|
|
2431
2602
|
return {
|
|
2432
2603
|
...panel,
|
|
2433
2604
|
title: "Artist",
|
|
2434
|
-
content: /* @__PURE__ */ (0,
|
|
2435
|
-
/* @__PURE__ */ (0,
|
|
2436
|
-
/* @__PURE__ */ (0,
|
|
2437
|
-
/* @__PURE__ */ (0,
|
|
2438
|
-
/* @__PURE__ */ (0,
|
|
2439
|
-
/* @__PURE__ */ (0,
|
|
2440
|
-
/* @__PURE__ */ (0,
|
|
2605
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2606
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-3 w-16" }),
|
|
2607
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-4 w-32" }),
|
|
2608
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-3 w-16" }),
|
|
2609
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-4 w-24" }),
|
|
2610
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2611
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui16.Skeleton, { className: "h-16" })
|
|
2441
2612
|
] })
|
|
2442
2613
|
};
|
|
2443
2614
|
case "not-found":
|
|
2444
2615
|
return {
|
|
2445
2616
|
...panel,
|
|
2446
2617
|
title: "Artist not found",
|
|
2447
|
-
content: /* @__PURE__ */ (0,
|
|
2618
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2448
2619
|
};
|
|
2449
2620
|
case "error":
|
|
2450
2621
|
return {
|
|
2451
2622
|
...panel,
|
|
2452
2623
|
title: "Artist",
|
|
2453
|
-
content: /* @__PURE__ */ (0,
|
|
2624
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2454
2625
|
};
|
|
2455
2626
|
case "success": {
|
|
2456
2627
|
const { artist } = result;
|
|
@@ -2459,13 +2630,13 @@ function useArtistPanel({
|
|
|
2459
2630
|
return {
|
|
2460
2631
|
...panel,
|
|
2461
2632
|
title: artist.name,
|
|
2462
|
-
content: /* @__PURE__ */ (0,
|
|
2463
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2464
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2465
|
-
/* @__PURE__ */ (0,
|
|
2466
|
-
/* @__PURE__ */ (0,
|
|
2467
|
-
/* @__PURE__ */ (0,
|
|
2468
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2633
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2634
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2635
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2636
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistHeader, { artist }),
|
|
2637
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2638
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2639
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2469
2640
|
ArtistSongs,
|
|
2470
2641
|
{
|
|
2471
2642
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2475,56 +2646,56 @@ function useArtistPanel({
|
|
|
2475
2646
|
}
|
|
2476
2647
|
)
|
|
2477
2648
|
] }),
|
|
2478
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2649
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2479
2650
|
};
|
|
2480
2651
|
}
|
|
2481
2652
|
}
|
|
2482
2653
|
}
|
|
2483
2654
|
|
|
2484
2655
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2485
|
-
var
|
|
2486
|
-
var
|
|
2656
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
2657
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2487
2658
|
|
|
2488
2659
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2489
|
-
var
|
|
2490
|
-
var
|
|
2491
|
-
var
|
|
2660
|
+
var import_react17 = require("react");
|
|
2661
|
+
var import_lucide_react15 = require("lucide-react");
|
|
2662
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2492
2663
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2493
|
-
var
|
|
2494
|
-
var
|
|
2664
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2665
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2495
2666
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2496
2667
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2497
2668
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2498
2669
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2499
|
-
const [open, setOpen] = (0,
|
|
2500
|
-
const history = (0,
|
|
2670
|
+
const [open, setOpen] = (0, import_react17.useState)(false);
|
|
2671
|
+
const history = (0, import_genre_tree26.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2501
2672
|
let body = null;
|
|
2502
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2503
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2673
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2674
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2504
2675
|
else if (history.status === "success")
|
|
2505
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2506
|
-
/* @__PURE__ */ (0,
|
|
2507
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2676
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2677
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2678
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2508
2679
|
" ",
|
|
2509
2680
|
"\u2014 ",
|
|
2510
2681
|
entry.oldValue ?? "none",
|
|
2511
2682
|
" \u2192 ",
|
|
2512
2683
|
entry.newValue ?? "none"
|
|
2513
2684
|
] }) : null,
|
|
2514
|
-
/* @__PURE__ */ (0,
|
|
2685
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2515
2686
|
entry.actorPseudo ?? "Anonymous",
|
|
2516
2687
|
" \xB7 ",
|
|
2517
2688
|
dateFormat.format(new Date(entry.createdOn))
|
|
2518
2689
|
] })
|
|
2519
2690
|
] }, entry.uuid)) });
|
|
2520
|
-
return /* @__PURE__ */ (0,
|
|
2691
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
2521
2692
|
"details",
|
|
2522
2693
|
{
|
|
2523
2694
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2524
2695
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2525
2696
|
children: [
|
|
2526
|
-
/* @__PURE__ */ (0,
|
|
2527
|
-
/* @__PURE__ */ (0,
|
|
2697
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2698
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "mt-1.5", children: body })
|
|
2528
2699
|
]
|
|
2529
2700
|
}
|
|
2530
2701
|
);
|
|
@@ -2538,25 +2709,25 @@ function SongPanelContent({
|
|
|
2538
2709
|
genrePath,
|
|
2539
2710
|
onSelectGenre
|
|
2540
2711
|
}) {
|
|
2541
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2712
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree26.useTrackList)();
|
|
2542
2713
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2543
|
-
const unplayableReason = (0,
|
|
2714
|
+
const unplayableReason = (0, import_genre_tree26.songUnplayableReason)(song);
|
|
2544
2715
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2545
2716
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2546
2717
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2547
|
-
const musicBrainzUrl = (0,
|
|
2718
|
+
const musicBrainzUrl = (0, import_genre_tree26.musicBrainzRecordingUrl)(song);
|
|
2548
2719
|
const fields = [];
|
|
2549
2720
|
if (song.genre) {
|
|
2550
2721
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2551
2722
|
fields.push({
|
|
2552
2723
|
label: "Genre",
|
|
2553
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2554
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2555
|
-
/* @__PURE__ */ (0,
|
|
2724
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
2725
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2726
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2556
2727
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2557
2728
|
});
|
|
2558
2729
|
} else {
|
|
2559
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2730
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2560
2731
|
}
|
|
2561
2732
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2562
2733
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2566,11 +2737,11 @@ function SongPanelContent({
|
|
|
2566
2737
|
const links = [];
|
|
2567
2738
|
if (musicBrainzUrl)
|
|
2568
2739
|
links.push(
|
|
2569
|
-
/* @__PURE__ */ (0,
|
|
2740
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2570
2741
|
);
|
|
2571
2742
|
if (song.youtubeVideo)
|
|
2572
2743
|
links.push(
|
|
2573
|
-
/* @__PURE__ */ (0,
|
|
2744
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2574
2745
|
"a",
|
|
2575
2746
|
{
|
|
2576
2747
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2585,33 +2756,33 @@ function SongPanelContent({
|
|
|
2585
2756
|
if (links.length > 0)
|
|
2586
2757
|
fields.push({
|
|
2587
2758
|
label: "Links",
|
|
2588
|
-
value: /* @__PURE__ */ (0,
|
|
2759
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex gap-3", children: links })
|
|
2589
2760
|
});
|
|
2590
|
-
return /* @__PURE__ */ (0,
|
|
2591
|
-
/* @__PURE__ */ (0,
|
|
2592
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2761
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
|
|
2762
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2763
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
2593
2764
|
"button",
|
|
2594
2765
|
{
|
|
2595
2766
|
type: "button",
|
|
2596
2767
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2597
2768
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2598
2769
|
children: [
|
|
2599
|
-
/* @__PURE__ */ (0,
|
|
2770
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.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_runtime43.jsx)(import_lucide_react15.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react15.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2600
2771
|
isPlaying ? "Pause" : "Play"
|
|
2601
2772
|
]
|
|
2602
2773
|
}
|
|
2603
2774
|
),
|
|
2604
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2775
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2605
2776
|
] }) }),
|
|
2606
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2607
|
-
/* @__PURE__ */ (0,
|
|
2608
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2609
|
-
/* @__PURE__ */ (0,
|
|
2777
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_genre_tree26.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2778
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.FieldList, { items: fields }),
|
|
2779
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui17.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2780
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2610
2781
|
] });
|
|
2611
2782
|
}
|
|
2612
2783
|
|
|
2613
2784
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2614
|
-
var
|
|
2785
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2615
2786
|
function useSongPanel({
|
|
2616
2787
|
uuid,
|
|
2617
2788
|
scope,
|
|
@@ -2624,7 +2795,7 @@ function useSongPanel({
|
|
|
2624
2795
|
onExpandedChange,
|
|
2625
2796
|
onClose
|
|
2626
2797
|
}) {
|
|
2627
|
-
const result = (0,
|
|
2798
|
+
const result = (0, import_genre_tree27.useSong)(uuid, getBackendBaseUrl);
|
|
2628
2799
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2629
2800
|
switch (result.status) {
|
|
2630
2801
|
case "idle":
|
|
@@ -2633,24 +2804,24 @@ function useSongPanel({
|
|
|
2633
2804
|
return {
|
|
2634
2805
|
...panel,
|
|
2635
2806
|
title: "Song",
|
|
2636
|
-
content: /* @__PURE__ */ (0,
|
|
2637
|
-
/* @__PURE__ */ (0,
|
|
2638
|
-
/* @__PURE__ */ (0,
|
|
2639
|
-
/* @__PURE__ */ (0,
|
|
2640
|
-
/* @__PURE__ */ (0,
|
|
2807
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.Skeleton, { className: "h-3 w-16" }),
|
|
2809
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.Skeleton, { className: "h-4 w-32" }),
|
|
2810
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.Skeleton, { className: "h-3 w-16" }),
|
|
2811
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui18.Skeleton, { className: "h-4 w-24" })
|
|
2641
2812
|
] })
|
|
2642
2813
|
};
|
|
2643
2814
|
case "not-found":
|
|
2644
2815
|
return {
|
|
2645
2816
|
...panel,
|
|
2646
2817
|
title: "Song not found",
|
|
2647
|
-
content: /* @__PURE__ */ (0,
|
|
2818
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2648
2819
|
};
|
|
2649
2820
|
case "error":
|
|
2650
2821
|
return {
|
|
2651
2822
|
...panel,
|
|
2652
2823
|
title: "Song",
|
|
2653
|
-
content: /* @__PURE__ */ (0,
|
|
2824
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2654
2825
|
};
|
|
2655
2826
|
case "success": {
|
|
2656
2827
|
const { song } = result;
|
|
@@ -2658,7 +2829,7 @@ function useSongPanel({
|
|
|
2658
2829
|
return {
|
|
2659
2830
|
...panel,
|
|
2660
2831
|
title: song.title,
|
|
2661
|
-
content: /* @__PURE__ */ (0,
|
|
2832
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2662
2833
|
SongPanelContent,
|
|
2663
2834
|
{
|
|
2664
2835
|
song,
|
|
@@ -2677,29 +2848,29 @@ function useSongPanel({
|
|
|
2677
2848
|
|
|
2678
2849
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2679
2850
|
var import_md2 = require("react-icons/md");
|
|
2680
|
-
var
|
|
2681
|
-
var
|
|
2851
|
+
var import_ui19 = require("@behindthemusictree/ui");
|
|
2852
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2682
2853
|
function SizeMetricToggle({
|
|
2683
2854
|
value,
|
|
2684
2855
|
onChange
|
|
2685
2856
|
}) {
|
|
2686
|
-
return /* @__PURE__ */ (0,
|
|
2687
|
-
|
|
2857
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2858
|
+
import_ui19.SegmentedControl,
|
|
2688
2859
|
{
|
|
2689
2860
|
variant: "floating",
|
|
2690
2861
|
"aria-label": "Size nodes by",
|
|
2691
2862
|
value,
|
|
2692
2863
|
onChange,
|
|
2693
2864
|
options: [
|
|
2694
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2695
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2865
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2866
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2696
2867
|
]
|
|
2697
2868
|
}
|
|
2698
2869
|
);
|
|
2699
2870
|
}
|
|
2700
2871
|
|
|
2701
2872
|
// src/genre-tree/GenreTreeView.tsx
|
|
2702
|
-
var
|
|
2873
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2703
2874
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2704
2875
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2705
2876
|
var noop = () => {
|
|
@@ -2712,8 +2883,8 @@ function GenreTreeView({
|
|
|
2712
2883
|
getBackendBaseUrl,
|
|
2713
2884
|
additionalActions,
|
|
2714
2885
|
viewMode: controlledViewMode,
|
|
2715
|
-
|
|
2716
|
-
|
|
2886
|
+
selectedGenreUuid: controlledSelectedGenreUuid,
|
|
2887
|
+
onSelectedGenreUuidChange,
|
|
2717
2888
|
defaultViewport,
|
|
2718
2889
|
viewportRestore,
|
|
2719
2890
|
onViewportChange,
|
|
@@ -2761,21 +2932,21 @@ function GenreTreeView({
|
|
|
2761
2932
|
placement = "default",
|
|
2762
2933
|
...focus
|
|
2763
2934
|
}) {
|
|
2764
|
-
const getShareUrlRef = (0,
|
|
2935
|
+
const getShareUrlRef = (0, import_react18.useRef)(getShareUrl);
|
|
2765
2936
|
getShareUrlRef.current = getShareUrl;
|
|
2766
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2937
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react18.useState)(null);
|
|
2767
2938
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2768
2939
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2769
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2940
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react18.useState)(null);
|
|
2770
2941
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2771
2942
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2772
2943
|
const changeSong = (uuid) => {
|
|
2773
2944
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2774
2945
|
onSongUuidChange?.(uuid);
|
|
2775
2946
|
};
|
|
2776
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2947
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react18.useState)("songs");
|
|
2777
2948
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2778
|
-
const deferredSizeMetric = (0,
|
|
2949
|
+
const deferredSizeMetric = (0, import_react18.useDeferredValue)(sizeMetric);
|
|
2779
2950
|
const changeSizeMetric = (metric) => {
|
|
2780
2951
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2781
2952
|
onSizeMetricChange?.(metric);
|
|
@@ -2786,10 +2957,14 @@ function GenreTreeView({
|
|
|
2786
2957
|
};
|
|
2787
2958
|
const {
|
|
2788
2959
|
isLoading,
|
|
2960
|
+
isError,
|
|
2961
|
+
refetch,
|
|
2789
2962
|
isFetching: isTreeFetching,
|
|
2790
2963
|
genrePlaylistByNodeId,
|
|
2791
2964
|
visibleGenrePlaylists,
|
|
2792
2965
|
groupedGenrePlaylistsByRoot,
|
|
2966
|
+
selectedGenreUuid,
|
|
2967
|
+
selectGenre,
|
|
2793
2968
|
selectedNodeId,
|
|
2794
2969
|
selectedGenrePlaylist,
|
|
2795
2970
|
selectNode,
|
|
@@ -2802,17 +2977,17 @@ function GenreTreeView({
|
|
|
2802
2977
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2803
2978
|
toggleWheelRotation,
|
|
2804
2979
|
onNodeHover: handleNodeHover
|
|
2805
|
-
} = (0,
|
|
2980
|
+
} = (0, import_genre_tree28.useGenreTreeViewState)({
|
|
2806
2981
|
scope,
|
|
2807
2982
|
getBackendBaseUrl,
|
|
2808
2983
|
viewMode: controlledViewMode,
|
|
2809
|
-
|
|
2810
|
-
|
|
2984
|
+
selectedGenreUuid: controlledSelectedGenreUuid,
|
|
2985
|
+
onSelectedGenreUuidChange,
|
|
2811
2986
|
criteriaOverviewSchema,
|
|
2812
2987
|
artistUuid
|
|
2813
2988
|
});
|
|
2814
2989
|
const restoreRevision = viewportRestore?.revision;
|
|
2815
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2990
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react18.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2816
2991
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2817
2992
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2818
2993
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2821,36 +2996,36 @@ function GenreTreeView({
|
|
|
2821
2996
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2822
2997
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2823
2998
|
}
|
|
2824
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2825
|
-
const { mutate: updateGenre } = (0,
|
|
2999
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree28.genreUpdateMutationKey }) > 0;
|
|
3000
|
+
const { mutate: updateGenre } = (0, import_genre_tree28.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2826
3001
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2827
3002
|
const isArtistFiltered = artistUuid !== null;
|
|
2828
3003
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2829
|
-
const artistResult = (0,
|
|
3004
|
+
const artistResult = (0, import_genre_tree28.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2830
3005
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2831
|
-
const filterArtist = (0,
|
|
3006
|
+
const filterArtist = (0, import_react18.useMemo)(
|
|
2832
3007
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2833
3008
|
[artistUuid, filterArtistName]
|
|
2834
3009
|
);
|
|
2835
3010
|
const hasSelectedGenreNode = selectedGenrePlaylist !== void 0 && visibleGenrePlaylists.some(({ uuid }) => uuid === selectedGenrePlaylist.uuid);
|
|
2836
3011
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: visibleGenrePlaylists, artist: filterArtist });
|
|
2837
3012
|
const openArtist = (uuid) => {
|
|
2838
|
-
if (
|
|
3013
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2839
3014
|
if (songUuid !== null) changeSong(null);
|
|
2840
3015
|
changeArtist(uuid);
|
|
2841
3016
|
};
|
|
2842
3017
|
const openSong = (uuid) => {
|
|
2843
|
-
if (
|
|
3018
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2844
3019
|
changeSong(uuid);
|
|
2845
3020
|
};
|
|
2846
|
-
const openSongRef = (0,
|
|
3021
|
+
const openSongRef = (0, import_react18.useRef)(openSong);
|
|
2847
3022
|
openSongRef.current = openSong;
|
|
2848
|
-
const resolvedRenderSongLink = (0,
|
|
2849
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
3023
|
+
const resolvedRenderSongLink = (0, import_react18.useCallback)(
|
|
3024
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2850
3025
|
[renderSongLink]
|
|
2851
3026
|
);
|
|
2852
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2853
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
3027
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react18.useState)(null);
|
|
3028
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react18.useState)(null);
|
|
2854
3029
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2855
3030
|
const songBaseUrl = getBackendBaseUrl;
|
|
2856
3031
|
const artistPanel = useArtistPanel({
|
|
@@ -2858,7 +3033,7 @@ function GenreTreeView({
|
|
|
2858
3033
|
scope,
|
|
2859
3034
|
getBackendBaseUrl: artistBaseUrl,
|
|
2860
3035
|
getShareUrl: getShareUrlRef.current,
|
|
2861
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
3036
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2862
3037
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2863
3038
|
onExpandedChange: (on) => {
|
|
2864
3039
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2867,7 +3042,7 @@ function GenreTreeView({
|
|
|
2867
3042
|
renderSongLink: resolvedRenderSongLink,
|
|
2868
3043
|
onClose: () => changeArtist(null)
|
|
2869
3044
|
});
|
|
2870
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
3045
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2871
3046
|
const getGenrePath = (genre) => {
|
|
2872
3047
|
const path = [];
|
|
2873
3048
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2885,9 +3060,9 @@ function GenreTreeView({
|
|
|
2885
3060
|
getShareUrl: getShareUrlRef.current,
|
|
2886
3061
|
renderArtistLink: resolvedRenderArtistLink,
|
|
2887
3062
|
getGenrePath,
|
|
2888
|
-
onSelectGenre: (
|
|
3063
|
+
onSelectGenre: (genrePlaylist2) => {
|
|
2889
3064
|
changeSong(null);
|
|
2890
|
-
selectGenrePlaylist(
|
|
3065
|
+
selectGenrePlaylist(genrePlaylist2);
|
|
2891
3066
|
},
|
|
2892
3067
|
expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
|
|
2893
3068
|
onExpandedChange: (on) => {
|
|
@@ -2896,11 +3071,11 @@ function GenreTreeView({
|
|
|
2896
3071
|
},
|
|
2897
3072
|
onClose: () => changeSong(null)
|
|
2898
3073
|
});
|
|
2899
|
-
const builtInSongPanel = overlayPanel ||
|
|
2900
|
-
const builtInArtistPanel = overlayPanel ||
|
|
3074
|
+
const builtInSongPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : songPanel;
|
|
3075
|
+
const builtInArtistPanel = overlayPanel || selectedGenreUuid !== null || builtInSongPanel ? void 0 : artistPanel;
|
|
2901
3076
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2902
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0,
|
|
2903
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
3077
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react18.useState)(false);
|
|
3078
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react18.useState)(false);
|
|
2904
3079
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2905
3080
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2906
3081
|
const changeCollapsed = (next) => {
|
|
@@ -2911,155 +3086,114 @@ function GenreTreeView({
|
|
|
2911
3086
|
setUncontrolledExpanded(next);
|
|
2912
3087
|
onExpandedChange?.(next);
|
|
2913
3088
|
};
|
|
2914
|
-
(0,
|
|
3089
|
+
(0, import_react18.useEffect)(() => {
|
|
2915
3090
|
setUncontrolledCollapsed(false);
|
|
2916
|
-
}, [
|
|
2917
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0,
|
|
3091
|
+
}, [selectedGenreUuid, artistUuid, songUuid]);
|
|
3092
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react18.useState)(false);
|
|
2918
3093
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2919
|
-
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2920
|
-
const [resolveStyle, setResolveStyle] = (0,
|
|
2921
|
-
const handleNodeStyleResolverChange = (0,
|
|
3094
|
+
const isTreeReady = !isLoading && !isError && (!usesHandoff || isWheelRevealed);
|
|
3095
|
+
const [resolveStyle, setResolveStyle] = (0, import_react18.useState)(null);
|
|
3096
|
+
const handleNodeStyleResolverChange = (0, import_react18.useCallback)(
|
|
2922
3097
|
(resolver) => setResolveStyle(() => resolver),
|
|
2923
3098
|
[]
|
|
2924
3099
|
);
|
|
2925
|
-
const panelRef = (0,
|
|
2926
|
-
const obscuredInset = (0,
|
|
3100
|
+
const panelRef = (0, import_react18.useRef)(null);
|
|
3101
|
+
const obscuredInset = (0, import_ui20.useObscuredInset)(panelRef);
|
|
2927
3102
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2928
3103
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2929
|
-
const treeNodes = (0,
|
|
2930
|
-
const relations = (0,
|
|
3104
|
+
const treeNodes = (0, import_react18.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
3105
|
+
const relations = (0, import_genre_tree_view5.useGenreNodeRelations)(
|
|
2931
3106
|
treeNodes,
|
|
2932
3107
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2933
3108
|
resolveStyle
|
|
2934
3109
|
);
|
|
2935
|
-
const [selectionHistory, setSelectionHistory] = (0,
|
|
2936
|
-
|
|
2937
|
-
previous:
|
|
2938
|
-
});
|
|
2939
|
-
if (selectionHistory.current !== selectedNodeId) {
|
|
2940
|
-
setSelectionHistory({
|
|
2941
|
-
current: selectedNodeId,
|
|
2942
|
-
previous: selectionHistory.current ?? selectionHistory.previous
|
|
2943
|
-
});
|
|
3110
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react18.useState)({ current: selectedGenreUuid, previous: null });
|
|
3111
|
+
if (selectionHistory.current !== selectedGenreUuid) {
|
|
3112
|
+
setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
2944
3113
|
}
|
|
2945
|
-
const
|
|
2946
|
-
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
3114
|
+
const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
|
|
2947
3115
|
const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
|
|
3116
|
+
const reopenPanel = () => {
|
|
3117
|
+
setUncontrolledCollapsed(false);
|
|
3118
|
+
onCollapsedChange?.(false);
|
|
3119
|
+
};
|
|
2948
3120
|
const handleSelectedNodeChange = (node) => {
|
|
3121
|
+
reopenPanel();
|
|
2949
3122
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2950
3123
|
selectNode(node);
|
|
2951
3124
|
};
|
|
2952
3125
|
const handleGenreSearchClear = clearSelection;
|
|
2953
|
-
const handleGenreSearchSelect = (
|
|
3126
|
+
const handleGenreSearchSelect = (genrePlaylist2) => {
|
|
3127
|
+
reopenPanel();
|
|
2954
3128
|
if (songUuid !== null) changeSong(null);
|
|
2955
|
-
selectGenrePlaylist(
|
|
3129
|
+
selectGenrePlaylist(genrePlaylist2);
|
|
2956
3130
|
};
|
|
2957
3131
|
const selectedUuid = selectedGenrePlaylist?.uuid;
|
|
2958
|
-
const selectedChildren = selectedUuid ? visibleGenrePlaylists.filter((
|
|
3132
|
+
const selectedChildren = selectedUuid ? visibleGenrePlaylists.filter((genrePlaylist2) => genrePlaylist2.parent?.uuid === selectedUuid) : [];
|
|
2959
3133
|
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;
|
|
2960
3134
|
const handleGenreSearchZoomIn = () => {
|
|
2961
3135
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2962
3136
|
};
|
|
2963
|
-
const zoomInRef = (0,
|
|
3137
|
+
const zoomInRef = (0, import_react18.useRef)(void 0);
|
|
2964
3138
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2965
|
-
const
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
const
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
kind: "genre",
|
|
2995
|
-
genrePlaylistUuid: genrePlaylist.uuid,
|
|
2996
|
-
name: criteria.name,
|
|
2997
|
-
scope,
|
|
2998
|
-
artistUuid
|
|
2999
|
-
};
|
|
3000
|
-
const shareUrl = getShareUrlRef.current?.(shareTarget) ?? void 0;
|
|
3001
|
-
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find(
|
|
3002
|
-
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
3003
|
-
) : void 0;
|
|
3004
|
-
return {
|
|
3005
|
-
primary: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
3006
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3007
|
-
GenreInfoPanelBody,
|
|
3139
|
+
const genrePlaylist = selectedGenrePlaylist;
|
|
3140
|
+
const criteria = genrePlaylist?.criteria;
|
|
3141
|
+
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist?.parent?.uuid);
|
|
3142
|
+
const canToggleSide = !readOnly && criteria !== void 0 && criteria !== null && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
3143
|
+
const side = criteria?.side ?? "core";
|
|
3144
|
+
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find((sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist?.uuid && sibling.criteria?.side === "pop") : void 0;
|
|
3145
|
+
const shareTarget = selectedGenreUuid ? { kind: "genre", genreUuid: selectedGenreUuid, name: criteria?.name ?? "Genre", scope, artistUuid } : void 0;
|
|
3146
|
+
const shareUrl = shareTarget ? getShareUrlRef.current?.(shareTarget) ?? void 0 : void 0;
|
|
3147
|
+
const genreContent = useGenreDetailContent({
|
|
3148
|
+
genreUuid: selectedGenreUuid,
|
|
3149
|
+
scope,
|
|
3150
|
+
getBackendBaseUrl,
|
|
3151
|
+
onSelectGenre: (uuid) => {
|
|
3152
|
+
reopenPanel();
|
|
3153
|
+
selectGenre(uuid);
|
|
3154
|
+
},
|
|
3155
|
+
expanded: isExpanded,
|
|
3156
|
+
playback,
|
|
3157
|
+
genrePlaylist: selectedGenrePlaylist,
|
|
3158
|
+
band: {
|
|
3159
|
+
...relations ? { node: relations.node } : {},
|
|
3160
|
+
shareTarget: shareUrl ? shareTarget : void 0,
|
|
3161
|
+
shareUrl,
|
|
3162
|
+
onZoomIn: zoomInRef.current,
|
|
3163
|
+
onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
|
|
3164
|
+
onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
|
|
3165
|
+
onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
|
|
3166
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3167
|
+
ChangeParentPopup,
|
|
3008
3168
|
{
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
ancestors
|
|
3012
|
-
] }),
|
|
3013
|
-
genreUuid,
|
|
3014
|
-
genrePlaylist,
|
|
3015
|
-
catalogue: genrePlaylist.catalogue,
|
|
3016
|
-
wikidataId: criteria.wikidataId,
|
|
3169
|
+
genrePlaylist: { ...genrePlaylist, criteria },
|
|
3170
|
+
genrePlaylists: visibleGenrePlaylists,
|
|
3017
3171
|
scope,
|
|
3018
3172
|
getBackendBaseUrl,
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
node,
|
|
3022
|
-
shareTarget: shareUrl ? shareTarget : void 0,
|
|
3023
|
-
shareUrl,
|
|
3024
|
-
onZoomIn: zoomInRef.current,
|
|
3025
|
-
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
3026
|
-
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
3027
|
-
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
3028
|
-
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3029
|
-
ChangeParentPopup,
|
|
3030
|
-
{
|
|
3031
|
-
genrePlaylist: { ...genrePlaylist, criteria },
|
|
3032
|
-
genrePlaylists: visibleGenrePlaylists,
|
|
3033
|
-
scope,
|
|
3034
|
-
getBackendBaseUrl,
|
|
3035
|
-
onClose: hidePopup,
|
|
3036
|
-
onParentChanged
|
|
3037
|
-
}
|
|
3038
|
-
)
|
|
3039
|
-
),
|
|
3040
|
-
side,
|
|
3041
|
-
onToggleSide: canToggleSide ? () => updateGenre({ uuid: genreUuid, data: { side: side === "pop" ? "core" : "pop" } }) : void 0,
|
|
3042
|
-
toggleSideDisabledReason: popSibling ? `${popSibling.name} is already the pop side of ${parentPlaylist?.name}` : void 0,
|
|
3043
|
-
additionalActions
|
|
3044
|
-
},
|
|
3045
|
-
extras: {
|
|
3046
|
-
criteriaOverviewSchema,
|
|
3047
|
-
renderGenreDetailExtras,
|
|
3048
|
-
renderArtistLink,
|
|
3049
|
-
renderSongLink: resolvedRenderSongLink,
|
|
3050
|
-
artist: filterArtist,
|
|
3051
|
-
renderSummary,
|
|
3052
|
-
renderEssentialTracks,
|
|
3053
|
-
renderSongLeading,
|
|
3054
|
-
renderSongActions,
|
|
3055
|
-
renderTracksToolbar
|
|
3056
|
-
}
|
|
3173
|
+
onClose: hidePopup,
|
|
3174
|
+
onParentChanged
|
|
3057
3175
|
}
|
|
3058
|
-
)
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3176
|
+
)
|
|
3177
|
+
),
|
|
3178
|
+
side,
|
|
3179
|
+
onToggleSide: canToggleSide && criteria ? () => updateGenre({ uuid: criteria.uuid, data: { side: side === "pop" ? "core" : "pop" } }) : void 0,
|
|
3180
|
+
toggleSideDisabledReason: popSibling ? `${popSibling.name} is already the pop side of ${parentPlaylist?.name}` : void 0,
|
|
3181
|
+
additionalActions
|
|
3182
|
+
},
|
|
3183
|
+
extras: {
|
|
3184
|
+
criteriaOverviewSchema,
|
|
3185
|
+
renderGenreDetailExtras,
|
|
3186
|
+
renderArtistLink,
|
|
3187
|
+
renderSongLink: resolvedRenderSongLink,
|
|
3188
|
+
artist: filterArtist,
|
|
3189
|
+
renderSummary,
|
|
3190
|
+
renderEssentialTracks,
|
|
3191
|
+
renderSongLeading,
|
|
3192
|
+
renderSongActions,
|
|
3193
|
+
renderTracksToolbar
|
|
3194
|
+
},
|
|
3195
|
+
trailing: relations ? renderDangerZone?.(relations.node) : void 0
|
|
3196
|
+
});
|
|
3063
3197
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
3064
3198
|
const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
|
|
3065
3199
|
let activePanel = null;
|
|
@@ -3073,16 +3207,16 @@ function GenreTreeView({
|
|
|
3073
3207
|
onClose: overlayPanel.onClose,
|
|
3074
3208
|
compact: false
|
|
3075
3209
|
};
|
|
3076
|
-
} else if (
|
|
3077
|
-
const { primary } = renderGenreBody(relations);
|
|
3210
|
+
} else if (selectedGenreUuid) {
|
|
3078
3211
|
activePanel = {
|
|
3079
|
-
title:
|
|
3080
|
-
content:
|
|
3212
|
+
title: genreContent.title,
|
|
3213
|
+
content: genreContent.content,
|
|
3214
|
+
aside: genreContent.aside,
|
|
3081
3215
|
expanded: isExpanded,
|
|
3082
3216
|
onExpandedChange: changeExpanded,
|
|
3083
3217
|
onClose: clearSelection,
|
|
3084
|
-
compact:
|
|
3085
|
-
headerAccent: { background: relations.fill, color: relations.textColor }
|
|
3218
|
+
compact: false,
|
|
3219
|
+
headerAccent: relations ? { background: relations.fill, color: relations.textColor } : genreContent.headerAccent
|
|
3086
3220
|
};
|
|
3087
3221
|
} else if (builtInPanel) {
|
|
3088
3222
|
activePanel = {
|
|
@@ -3095,36 +3229,36 @@ function GenreTreeView({
|
|
|
3095
3229
|
compact: true
|
|
3096
3230
|
};
|
|
3097
3231
|
}
|
|
3098
|
-
const isPanelShown =
|
|
3232
|
+
const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
|
|
3099
3233
|
const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
|
|
3100
3234
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
3101
3235
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3102
3236
|
} : void 0;
|
|
3103
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
3104
|
-
|
|
3237
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3238
|
+
import_ui20.IconButton,
|
|
3105
3239
|
{
|
|
3106
3240
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3107
3241
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
3108
3242
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
3109
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3243
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3110
3244
|
}
|
|
3111
3245
|
) : null;
|
|
3112
3246
|
const renderArtistFilter = () => {
|
|
3113
3247
|
if (isLoading || !filterArtist) return null;
|
|
3114
|
-
return /* @__PURE__ */ (0,
|
|
3115
|
-
/* @__PURE__ */ (0,
|
|
3248
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
3249
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3116
3250
|
"button",
|
|
3117
3251
|
{
|
|
3118
3252
|
type: "button",
|
|
3119
3253
|
className: "py-1 pl-3 pr-2 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] hover:underline",
|
|
3120
3254
|
"aria-label": `Show artist ${filterArtist.name}`,
|
|
3121
3255
|
onClick: () => {
|
|
3122
|
-
if (
|
|
3256
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
3123
3257
|
},
|
|
3124
3258
|
children: filterArtist.name
|
|
3125
3259
|
}
|
|
3126
3260
|
),
|
|
3127
|
-
/* @__PURE__ */ (0,
|
|
3261
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3128
3262
|
"button",
|
|
3129
3263
|
{
|
|
3130
3264
|
type: "button",
|
|
@@ -3136,8 +3270,8 @@ function GenreTreeView({
|
|
|
3136
3270
|
)
|
|
3137
3271
|
] });
|
|
3138
3272
|
};
|
|
3139
|
-
const actions = /* @__PURE__ */ (0,
|
|
3140
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
3273
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3274
|
+
!isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3141
3275
|
GenreTreeSearch,
|
|
3142
3276
|
{
|
|
3143
3277
|
artistBaseUrl,
|
|
@@ -3155,7 +3289,12 @@ function GenreTreeView({
|
|
|
3155
3289
|
SearchGroups,
|
|
3156
3290
|
genrePlaylists: visibleGenrePlaylists,
|
|
3157
3291
|
onSelect: handleGenreSearchSelect,
|
|
3158
|
-
|
|
3292
|
+
onSelectGenre: (uuid) => {
|
|
3293
|
+
reopenPanel();
|
|
3294
|
+
if (songUuid) changeSong(null);
|
|
3295
|
+
selectGenre(uuid);
|
|
3296
|
+
},
|
|
3297
|
+
selectedName: (selectedGenreUuid ? genreContent.title : null) ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
3159
3298
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
3160
3299
|
onPrevious: handlePrevious,
|
|
3161
3300
|
onQueryChange: onSearchQueryChange,
|
|
@@ -3163,10 +3302,10 @@ function GenreTreeView({
|
|
|
3163
3302
|
extraGroups: extraSearchGroups
|
|
3164
3303
|
}
|
|
3165
3304
|
),
|
|
3166
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3167
|
-
/* @__PURE__ */ (0,
|
|
3168
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
3169
|
-
|
|
3305
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3306
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.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: [
|
|
3307
|
+
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3308
|
+
import_ui20.SegmentedControl,
|
|
3170
3309
|
{
|
|
3171
3310
|
"aria-label": "Tree view mode",
|
|
3172
3311
|
value: viewMode,
|
|
@@ -3179,14 +3318,14 @@ function GenreTreeView({
|
|
|
3179
3318
|
]
|
|
3180
3319
|
}
|
|
3181
3320
|
),
|
|
3182
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3321
|
+
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3183
3322
|
"div",
|
|
3184
3323
|
{
|
|
3185
3324
|
className: "flex items-center gap-1",
|
|
3186
3325
|
role: "group",
|
|
3187
3326
|
"aria-label": "Tree display options",
|
|
3188
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3189
|
-
|
|
3327
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3328
|
+
import_ui20.Button,
|
|
3190
3329
|
{
|
|
3191
3330
|
variant: allowWheelRotation ? "default" : "outline",
|
|
3192
3331
|
size: "sm",
|
|
@@ -3196,44 +3335,48 @@ function GenreTreeView({
|
|
|
3196
3335
|
)
|
|
3197
3336
|
}
|
|
3198
3337
|
),
|
|
3199
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3200
|
-
|
|
3338
|
+
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3339
|
+
import_ui20.IconTextButton,
|
|
3201
3340
|
{
|
|
3202
|
-
icon:
|
|
3341
|
+
icon: import_lucide_react16.Plus,
|
|
3203
3342
|
text: "Add root",
|
|
3204
3343
|
onClick: () => handleGenreCreationAction(null)
|
|
3205
3344
|
}
|
|
3206
3345
|
)
|
|
3207
3346
|
] })
|
|
3208
3347
|
] });
|
|
3209
|
-
return /* @__PURE__ */ (0,
|
|
3348
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3210
3349
|
"div",
|
|
3211
3350
|
{
|
|
3212
3351
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3213
3352
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3214
3353
|
children: [
|
|
3215
|
-
/* @__PURE__ */ (0,
|
|
3354
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3216
3355
|
"div",
|
|
3217
3356
|
{
|
|
3218
3357
|
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"}`,
|
|
3219
3358
|
children: actions
|
|
3220
3359
|
}
|
|
3221
3360
|
),
|
|
3222
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3361
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3223
3362
|
"div",
|
|
3224
3363
|
{
|
|
3225
3364
|
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"}`,
|
|
3226
3365
|
children: renderArtistFilter()
|
|
3227
3366
|
}
|
|
3228
3367
|
),
|
|
3229
|
-
/* @__PURE__ */ (0,
|
|
3230
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3231
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3368
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3369
|
+
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3370
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
|
|
3371
|
+
"Couldn't load the genre tree.",
|
|
3372
|
+
" ",
|
|
3373
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
|
|
3374
|
+
] }) : viewMode === "wheel" ? (
|
|
3232
3375
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3233
3376
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3234
3377
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3235
3378
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3236
|
-
/* @__PURE__ */ (0,
|
|
3379
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3237
3380
|
GenrePlaylistTreeWheel,
|
|
3238
3381
|
{
|
|
3239
3382
|
scope,
|
|
@@ -3250,9 +3393,9 @@ function GenreTreeView({
|
|
|
3250
3393
|
allowWheelRotation,
|
|
3251
3394
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3252
3395
|
busy,
|
|
3253
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3396
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3254
3397
|
floatingControlsLeading,
|
|
3255
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3398
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3256
3399
|
] }) : void 0,
|
|
3257
3400
|
defaultViewport,
|
|
3258
3401
|
viewportRestore,
|
|
@@ -3261,7 +3404,7 @@ function GenreTreeView({
|
|
|
3261
3404
|
) }) })
|
|
3262
3405
|
) : viewMode === "pop-core" ? (
|
|
3263
3406
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3264
|
-
/* @__PURE__ */ (0,
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3265
3408
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3266
3409
|
{
|
|
3267
3410
|
scope,
|
|
@@ -3278,9 +3421,9 @@ function GenreTreeView({
|
|
|
3278
3421
|
allowWheelRotation,
|
|
3279
3422
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3280
3423
|
busy,
|
|
3281
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3424
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3282
3425
|
floatingControlsLeading,
|
|
3283
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3426
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3284
3427
|
] }) : void 0,
|
|
3285
3428
|
defaultViewport,
|
|
3286
3429
|
viewportRestore,
|
|
@@ -3288,7 +3431,7 @@ function GenreTreeView({
|
|
|
3288
3431
|
...focus
|
|
3289
3432
|
}
|
|
3290
3433
|
) }) })
|
|
3291
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3434
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3292
3435
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3293
3436
|
{
|
|
3294
3437
|
outline: true,
|
|
@@ -3304,13 +3447,13 @@ function GenreTreeView({
|
|
|
3304
3447
|
selectedNodeId,
|
|
3305
3448
|
readOnly
|
|
3306
3449
|
}
|
|
3307
|
-
) }) : /* @__PURE__ */ (0,
|
|
3450
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime46.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(
|
|
3308
3451
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3309
|
-
return /* @__PURE__ */ (0,
|
|
3452
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3310
3453
|
"div",
|
|
3311
3454
|
{
|
|
3312
3455
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3313
|
-
children: /* @__PURE__ */ (0,
|
|
3456
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3314
3457
|
GenrePlaylistTreePerRoot,
|
|
3315
3458
|
{
|
|
3316
3459
|
scope,
|
|
@@ -3336,8 +3479,8 @@ function GenreTreeView({
|
|
|
3336
3479
|
);
|
|
3337
3480
|
}
|
|
3338
3481
|
) }),
|
|
3339
|
-
/* @__PURE__ */ (0,
|
|
3340
|
-
|
|
3482
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3483
|
+
import_ui20.DetailPanel,
|
|
3341
3484
|
{
|
|
3342
3485
|
ref: panelRef,
|
|
3343
3486
|
side: "left",
|
|
@@ -3347,13 +3490,13 @@ function GenreTreeView({
|
|
|
3347
3490
|
headerAccent: activePanel?.headerAccent,
|
|
3348
3491
|
headerActions: expandButton,
|
|
3349
3492
|
onClose: activePanel?.onClose ?? noop,
|
|
3350
|
-
collapsed: isCollapsed || !
|
|
3493
|
+
collapsed: isCollapsed || !isPanelShown,
|
|
3351
3494
|
expanded: activePanel?.expanded,
|
|
3352
3495
|
onExpandedChange: activePanel?.onExpandedChange,
|
|
3353
3496
|
aside: activePanel?.aside,
|
|
3354
3497
|
edgeTab: detailTab,
|
|
3355
|
-
headerClassName:
|
|
3356
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3498
|
+
headerClassName: "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]",
|
|
3499
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3357
3500
|
activePanel.content,
|
|
3358
3501
|
activePanel.aside
|
|
3359
3502
|
] }) : activePanel?.content
|
|
@@ -3365,134 +3508,6 @@ function GenreTreeView({
|
|
|
3365
3508
|
);
|
|
3366
3509
|
}
|
|
3367
3510
|
|
|
3368
|
-
// src/genre-tree/GenreDetailPanel.tsx
|
|
3369
|
-
var import_react18 = require("react");
|
|
3370
|
-
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3371
|
-
var import_ui20 = require("@behindthemusictree/ui");
|
|
3372
|
-
var import_lucide_react17 = require("lucide-react");
|
|
3373
|
-
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3374
|
-
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
3375
|
-
var RELATIVE_ITEM_COUNT = 1;
|
|
3376
|
-
function toNode(genre, parentId) {
|
|
3377
|
-
return {
|
|
3378
|
-
id: genre.uuid,
|
|
3379
|
-
parentId,
|
|
3380
|
-
name: genre.name,
|
|
3381
|
-
itemCount: RELATIVE_ITEM_COUNT,
|
|
3382
|
-
side: genre.side ?? void 0,
|
|
3383
|
-
wikidataId: genre.wikidataId
|
|
3384
|
-
};
|
|
3385
|
-
}
|
|
3386
|
-
function GenreDetailPanel({
|
|
3387
|
-
genreUuid,
|
|
3388
|
-
scope,
|
|
3389
|
-
getBackendBaseUrl,
|
|
3390
|
-
onClose,
|
|
3391
|
-
onSelectGenre,
|
|
3392
|
-
onZoomIn,
|
|
3393
|
-
shareUrl,
|
|
3394
|
-
shareTarget,
|
|
3395
|
-
collapsed,
|
|
3396
|
-
expanded,
|
|
3397
|
-
onExpandedChange,
|
|
3398
|
-
edgeTab,
|
|
3399
|
-
extras
|
|
3400
|
-
}) {
|
|
3401
|
-
const { data: detail, isPending, isError } = (0, import_genre_tree28.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3402
|
-
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3403
|
-
const view = (0, import_react18.useMemo)(() => {
|
|
3404
|
-
if (!detail) return null;
|
|
3405
|
-
const rootColor = (0, import_genre_tree_view5.getGenreTreeColor)(detail.root.wikidataId);
|
|
3406
|
-
const relative = (genre, parentId) => ({
|
|
3407
|
-
node: toNode(genre, parentId),
|
|
3408
|
-
fill: (0, import_genre_tree_view5.tintSurface)(rootColor),
|
|
3409
|
-
textColor: import_genre_tree_view5.TEXT_COLOR
|
|
3410
|
-
});
|
|
3411
|
-
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
3412
|
-
return {
|
|
3413
|
-
node: {
|
|
3414
|
-
id: detail.uuid,
|
|
3415
|
-
parentId: detail.parent?.uuid ?? null,
|
|
3416
|
-
name: detail.name,
|
|
3417
|
-
itemCount: scope === "reference" ? (0, import_genre_tree28.genreMusicCount)(import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
|
|
3418
|
-
wikidataId: detail.wikidataId
|
|
3419
|
-
},
|
|
3420
|
-
fill: rootColor,
|
|
3421
|
-
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
3422
|
-
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
3423
|
-
ancestorNodes: ancestors2.map(
|
|
3424
|
-
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
3425
|
-
)
|
|
3426
|
-
};
|
|
3427
|
-
}, [detail, scope]);
|
|
3428
|
-
if (isError) {
|
|
3429
|
-
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3430
|
-
}
|
|
3431
|
-
if (isPending || !detail || !view) {
|
|
3432
|
-
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3433
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-6 w-2/3" }),
|
|
3434
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-4 w-full" }),
|
|
3435
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_ui20.Skeleton, { className: "h-4 w-3/4" })
|
|
3436
|
-
] });
|
|
3437
|
-
}
|
|
3438
|
-
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3439
|
-
const family = /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3440
|
-
import_genre_tree_view5.GenreNodeFamily,
|
|
3441
|
-
{
|
|
3442
|
-
node: view.node,
|
|
3443
|
-
fill: view.fill,
|
|
3444
|
-
textColor: import_genre_tree_view5.ACCENT_TEXT_COLOR,
|
|
3445
|
-
parentNode: view.parentNode,
|
|
3446
|
-
childNodes: view.childNodes,
|
|
3447
|
-
onSelectNode: selectGenre
|
|
3448
|
-
}
|
|
3449
|
-
);
|
|
3450
|
-
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3451
|
-
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3452
|
-
import_ui20.DetailPanel,
|
|
3453
|
-
{
|
|
3454
|
-
side: "left",
|
|
3455
|
-
title: view.node.name,
|
|
3456
|
-
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3457
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3458
|
-
import_ui20.IconButton,
|
|
3459
|
-
{
|
|
3460
|
-
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3461
|
-
style: { color: "inherit" },
|
|
3462
|
-
onClick: () => onExpandedChange(!expanded),
|
|
3463
|
-
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react17.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3464
|
-
}
|
|
3465
|
-
) : void 0,
|
|
3466
|
-
onClose,
|
|
3467
|
-
collapsed,
|
|
3468
|
-
expanded,
|
|
3469
|
-
onExpandedChange,
|
|
3470
|
-
edgeTab,
|
|
3471
|
-
children: [
|
|
3472
|
-
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3473
|
-
GenreInfoPanelBody,
|
|
3474
|
-
{
|
|
3475
|
-
family: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3476
|
-
family,
|
|
3477
|
-
ancestors
|
|
3478
|
-
] }),
|
|
3479
|
-
genreUuid: detail.uuid,
|
|
3480
|
-
genrePlaylist: scope === "reference" ? import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3481
|
-
catalogue: detail.catalogue,
|
|
3482
|
-
wikidataId: detail.wikidataId,
|
|
3483
|
-
scope,
|
|
3484
|
-
getBackendBaseUrl,
|
|
3485
|
-
playback,
|
|
3486
|
-
band: { node: view.node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
3487
|
-
extras
|
|
3488
|
-
}
|
|
3489
|
-
),
|
|
3490
|
-
!expanded && ancestors
|
|
3491
|
-
]
|
|
3492
|
-
}
|
|
3493
|
-
);
|
|
3494
|
-
}
|
|
3495
|
-
|
|
3496
3511
|
// src/genre-tree/index.ts
|
|
3497
3512
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3498
3513
|
|