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