@behindthemusictree/app-kit-ui 34.1.0 → 36.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-JP7PID7L.js → chunk-RTXLUHJY.js} +469 -453
- package/dist/chunk-RTXLUHJY.js.map +1 -0
- package/dist/genre-tree/index.cjs +518 -503
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +20 -16
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +518 -503
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-JP7PID7L.js.map +0 -1
|
@@ -5,14 +5,12 @@ import {
|
|
|
5
5
|
} from "./chunk-DKQQLLMY.js";
|
|
6
6
|
|
|
7
7
|
// src/genre-tree/GenreTreeView.tsx
|
|
8
|
-
import { useCallback as useCallback6, useDeferredValue as useDeferredValue2, useEffect as useEffect5, useMemo as
|
|
8
|
+
import { useCallback as useCallback6, useDeferredValue as useDeferredValue2, useEffect as useEffect5, useMemo as useMemo8, useRef as useRef3, useState as useState9 } from "react";
|
|
9
9
|
import { useIsMutating } from "@tanstack/react-query";
|
|
10
10
|
import { Plus as Plus2 } from "lucide-react";
|
|
11
|
-
import { Maximize2, Minimize2 } from "lucide-react";
|
|
12
|
-
import { IconTextButton, IconButton, Button, SegmentedControl as SegmentedControl2, DetailPanel, useObscuredInset } from "@behindthemusictree/ui";
|
|
11
|
+
import { Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
|
|
12
|
+
import { IconTextButton, IconButton as IconButton2, Button, SegmentedControl as SegmentedControl2, DetailPanel as DetailPanel2, useObscuredInset } from "@behindthemusictree/ui";
|
|
13
13
|
import {
|
|
14
|
-
GenreNodeAncestors,
|
|
15
|
-
GenreNodeFamily,
|
|
16
14
|
GenreTreeWheelSkeleton,
|
|
17
15
|
GenreTreeViewSkeleton,
|
|
18
16
|
useGenreNodeRelations
|
|
@@ -683,6 +681,7 @@ function GenreTreeSearch({
|
|
|
683
681
|
artistSearch,
|
|
684
682
|
songSearch,
|
|
685
683
|
SearchGroups,
|
|
684
|
+
onSelectGenre,
|
|
686
685
|
extraGroups = [],
|
|
687
686
|
onQueryChange,
|
|
688
687
|
onFocus,
|
|
@@ -708,9 +707,30 @@ function GenreTreeSearch({
|
|
|
708
707
|
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
709
708
|
}
|
|
710
709
|
);
|
|
711
|
-
return SearchGroups ? /* @__PURE__ */ jsx9(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
710
|
+
return SearchGroups ? /* @__PURE__ */ jsx9(SearchGroups, { query: deferredQuery, focused, onSelectGenre, children: render }) : render([]);
|
|
712
711
|
}
|
|
713
712
|
|
|
713
|
+
// src/genre-tree/GenreDetailPanel.tsx
|
|
714
|
+
import { useMemo as useMemo6 } from "react";
|
|
715
|
+
import {
|
|
716
|
+
ACCENT_TEXT_COLOR,
|
|
717
|
+
GenreNodeAncestors,
|
|
718
|
+
GenreNodeFamily,
|
|
719
|
+
getGenreTreeColor as getGenreTreeColor2,
|
|
720
|
+
TEXT_COLOR,
|
|
721
|
+
tintSurface
|
|
722
|
+
} from "@behindthemusictree/genre-tree-view";
|
|
723
|
+
import { DetailPanel, IconButton, Skeleton as Skeleton3 } from "@behindthemusictree/ui";
|
|
724
|
+
import { Maximize2, Minimize2 } from "lucide-react";
|
|
725
|
+
import {
|
|
726
|
+
useFetchGenreDetail,
|
|
727
|
+
genreMusicCount as genreMusicCount2,
|
|
728
|
+
GenrePlaylistSummarySchema,
|
|
729
|
+
GenreCatalogueCountsSchema as GenreCatalogueCountsSchema3,
|
|
730
|
+
CriteriaMinimumSchema,
|
|
731
|
+
GenreUuidSchema
|
|
732
|
+
} from "@behindthemusictree/app-kit/genre-tree";
|
|
733
|
+
|
|
714
734
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
715
735
|
import {
|
|
716
736
|
genreMusicCount,
|
|
@@ -1076,7 +1096,7 @@ import { z } from "zod";
|
|
|
1076
1096
|
import { FaPlay, FaPause } from "react-icons/fa";
|
|
1077
1097
|
import { PanelSection, RingLoader, Skeleton } from "@behindthemusictree/ui";
|
|
1078
1098
|
import { useSession } from "@behindthemusictree/app-kit/auth";
|
|
1079
|
-
import { usePlaybackContext,
|
|
1099
|
+
import { usePlaybackContext, usePlaybackFetch } from "@behindthemusictree/app-kit/transport";
|
|
1080
1100
|
import { PaginatedResponseSchema } from "@behindthemusictree/app-kit/transport";
|
|
1081
1101
|
import { parseWithLog } from "@behindthemusictree/app-kit/transport";
|
|
1082
1102
|
import { usePlayer as usePlayer4 } from "@behindthemusictree/app-kit/player";
|
|
@@ -1136,6 +1156,7 @@ var nextCursor = (next) => {
|
|
|
1136
1156
|
function GenrePlaylistTracks({
|
|
1137
1157
|
genreUuid,
|
|
1138
1158
|
genrePlaylist,
|
|
1159
|
+
genreName: selectedGenreName,
|
|
1139
1160
|
selection,
|
|
1140
1161
|
scope,
|
|
1141
1162
|
getBackendBaseUrl,
|
|
@@ -1151,14 +1172,18 @@ function GenrePlaylistTracks({
|
|
|
1151
1172
|
const { playerTrackObject, playState, handlePlayPauseAction } = usePlayer4();
|
|
1152
1173
|
const { showPopup } = usePopup4();
|
|
1153
1174
|
const trackNoun = useTrackNoun();
|
|
1154
|
-
const
|
|
1155
|
-
const
|
|
1175
|
+
const uuid = genrePlaylist?.uuid;
|
|
1176
|
+
const genreName = selectedGenreName ?? genrePlaylist?.name ?? "Genre";
|
|
1177
|
+
const { fetchBound } = usePlaybackFetch(getBackendBaseUrl);
|
|
1156
1178
|
const playback = usePlaybackContext();
|
|
1157
1179
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({
|
|
1158
|
-
queryKey: [...scope === "me" ? genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1180
|
+
queryKey: [...scope === "me" ? uuid ? genrePlaylistQueryKeys.me.tracks(uuid) : ["missing-personal-genre-playlist", genreUuid] : artist ? [...libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1159
1181
|
queryFn: async ({ pageParam }) => {
|
|
1160
|
-
if (scope === "me")
|
|
1161
|
-
|
|
1182
|
+
if (scope === "me") {
|
|
1183
|
+
if (!uuid) throw new Error("Personal genre has no playlist");
|
|
1184
|
+
return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1185
|
+
}
|
|
1186
|
+
const response = await fetchBound(
|
|
1162
1187
|
libraryEndpoints.reference.songs.list(),
|
|
1163
1188
|
true,
|
|
1164
1189
|
false,
|
|
@@ -1170,7 +1195,9 @@ function GenrePlaylistTracks({
|
|
|
1170
1195
|
},
|
|
1171
1196
|
initialPageParam: scope === "me" ? 1 : "",
|
|
1172
1197
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1173
|
-
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1198
|
+
enabled: playback.ready && (scope !== "me" || uuid !== void 0 && sessionRestored && !!session?.accessToken),
|
|
1199
|
+
retry: (failureCount, error) => failureCount < 1 && error instanceof DOMException && error.name === "AbortError",
|
|
1200
|
+
retryDelay: 0
|
|
1174
1201
|
});
|
|
1175
1202
|
const [sentinel, setSentinel] = useState6(null);
|
|
1176
1203
|
useEffect3(() => {
|
|
@@ -1192,7 +1219,7 @@ function GenrePlaylistTracks({
|
|
|
1192
1219
|
ordering: "title",
|
|
1193
1220
|
genreUuid,
|
|
1194
1221
|
...selection && { selection }
|
|
1195
|
-
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name:
|
|
1222
|
+
}) : 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"));
|
|
1196
1223
|
started.catch((error) => {
|
|
1197
1224
|
console.error("Failed to play genre playlist:", error);
|
|
1198
1225
|
showPopup(/* @__PURE__ */ jsx17(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
|
|
@@ -1200,13 +1227,13 @@ function GenrePlaylistTracks({
|
|
|
1200
1227
|
};
|
|
1201
1228
|
return /* @__PURE__ */ jsx17(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
|
|
1202
1229
|
renderTracksToolbar?.(),
|
|
1203
|
-
isPending ? /* @__PURE__ */ jsx17(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
|
|
1230
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ jsx17(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
|
|
1204
1231
|
"Couldn't load ",
|
|
1205
1232
|
trackNoun.plural,
|
|
1206
1233
|
".",
|
|
1207
1234
|
" ",
|
|
1208
1235
|
/* @__PURE__ */ jsx17("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1209
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${
|
|
1236
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("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: [
|
|
1210
1237
|
tracks.map((track, index) => {
|
|
1211
1238
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1212
1239
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
@@ -1273,6 +1300,7 @@ import { Fragment as Fragment8, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-r
|
|
|
1273
1300
|
function GenreDetailExtras({
|
|
1274
1301
|
genreUuid,
|
|
1275
1302
|
genrePlaylist,
|
|
1303
|
+
genreName,
|
|
1276
1304
|
selection,
|
|
1277
1305
|
wikidataId,
|
|
1278
1306
|
scope,
|
|
@@ -1340,6 +1368,7 @@ function GenreDetailExtras({
|
|
|
1340
1368
|
{
|
|
1341
1369
|
genreUuid,
|
|
1342
1370
|
genrePlaylist,
|
|
1371
|
+
genreName,
|
|
1343
1372
|
selection,
|
|
1344
1373
|
scope,
|
|
1345
1374
|
getBackendBaseUrl,
|
|
@@ -1381,6 +1410,7 @@ function GenreInfoPanelBody({
|
|
|
1381
1410
|
genreUuid,
|
|
1382
1411
|
genrePlaylist,
|
|
1383
1412
|
catalogue,
|
|
1413
|
+
countsContext,
|
|
1384
1414
|
wikidataId,
|
|
1385
1415
|
scope,
|
|
1386
1416
|
getBackendBaseUrl,
|
|
@@ -1389,7 +1419,7 @@ function GenreInfoPanelBody({
|
|
|
1389
1419
|
extras
|
|
1390
1420
|
}) {
|
|
1391
1421
|
const noun = useTrackNoun3();
|
|
1392
|
-
const summary = scope === "reference" ? GenreCatalogueCountsSchema2.parse(genrePlaylist) : void 0;
|
|
1422
|
+
const summary = scope === "reference" ? GenreCatalogueCountsSchema2.parse(genrePlaylist ?? catalogue) : void 0;
|
|
1393
1423
|
const selection = summary ? defaultGenreMusicSelection2(summary) : void 0;
|
|
1394
1424
|
const describeCounts = (counts) => {
|
|
1395
1425
|
const scopes = counts.genreAndSubgenresTracksCount;
|
|
@@ -1397,17 +1427,19 @@ function GenreInfoPanelBody({
|
|
|
1397
1427
|
};
|
|
1398
1428
|
return /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1399
1429
|
toolbar,
|
|
1400
|
-
/* @__PURE__ */ jsx19(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1430
|
+
genrePlaylist ? /* @__PURE__ */ jsx19(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ jsx19(GenreActionBand, { ...band }),
|
|
1401
1431
|
summary && catalogue && /* @__PURE__ */ jsxs14("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1402
1432
|
/* @__PURE__ */ jsxs14("p", { children: [
|
|
1403
1433
|
"Catalogue ",
|
|
1404
1434
|
noun.plural,
|
|
1435
|
+
countsContext ? ` (${countsContext})` : "",
|
|
1405
1436
|
": ",
|
|
1406
1437
|
describeCounts(catalogue)
|
|
1407
1438
|
] }),
|
|
1408
|
-
/* @__PURE__ */ jsxs14("p", { children: [
|
|
1439
|
+
genrePlaylist && /* @__PURE__ */ jsxs14("p", { children: [
|
|
1409
1440
|
"Playlist ",
|
|
1410
1441
|
noun.plural,
|
|
1442
|
+
countsContext ? ` (${countsContext})` : "",
|
|
1411
1443
|
": ",
|
|
1412
1444
|
describeCounts(summary)
|
|
1413
1445
|
] })
|
|
@@ -1420,6 +1452,7 @@ function GenreInfoPanelBody({
|
|
|
1420
1452
|
family,
|
|
1421
1453
|
genreUuid,
|
|
1422
1454
|
genrePlaylist,
|
|
1455
|
+
genreName: band.node.name,
|
|
1423
1456
|
selection,
|
|
1424
1457
|
wikidataId,
|
|
1425
1458
|
scope,
|
|
@@ -1429,13 +1462,163 @@ function GenreInfoPanelBody({
|
|
|
1429
1462
|
] });
|
|
1430
1463
|
}
|
|
1431
1464
|
|
|
1465
|
+
// src/genre-tree/GenreDetailPanel.tsx
|
|
1466
|
+
import { Fragment as Fragment10, jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1467
|
+
var RELATIVE_ITEM_COUNT = 1;
|
|
1468
|
+
function toNode(genre, parentId) {
|
|
1469
|
+
return {
|
|
1470
|
+
id: genre.uuid,
|
|
1471
|
+
parentId,
|
|
1472
|
+
name: genre.name,
|
|
1473
|
+
itemCount: RELATIVE_ITEM_COUNT,
|
|
1474
|
+
side: genre.side ?? void 0,
|
|
1475
|
+
wikidataId: genre.wikidataId
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
function useGenreDetailContent({
|
|
1479
|
+
genreUuid,
|
|
1480
|
+
scope,
|
|
1481
|
+
getBackendBaseUrl,
|
|
1482
|
+
onSelectGenre,
|
|
1483
|
+
extras,
|
|
1484
|
+
band,
|
|
1485
|
+
playback: hostPlayback,
|
|
1486
|
+
trailing,
|
|
1487
|
+
genrePlaylist: hostPlaylist,
|
|
1488
|
+
expanded
|
|
1489
|
+
}) {
|
|
1490
|
+
const { data: detail, isPending, isError } = useFetchGenreDetail(genreUuid, scope, getBackendBaseUrl);
|
|
1491
|
+
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
1492
|
+
const playback = hostPlayback ?? defaultPlayback;
|
|
1493
|
+
const view = useMemo6(() => {
|
|
1494
|
+
if (!detail) return null;
|
|
1495
|
+
const rootColor = getGenreTreeColor2(detail.root.wikidataId);
|
|
1496
|
+
const relative = (genre, parentId) => ({
|
|
1497
|
+
node: toNode(genre, parentId),
|
|
1498
|
+
fill: tintSurface(rootColor),
|
|
1499
|
+
textColor: TEXT_COLOR
|
|
1500
|
+
});
|
|
1501
|
+
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
1502
|
+
return {
|
|
1503
|
+
node: {
|
|
1504
|
+
id: detail.uuid,
|
|
1505
|
+
parentId: detail.parent?.uuid ?? null,
|
|
1506
|
+
name: detail.name,
|
|
1507
|
+
itemCount: scope === "reference" ? genreMusicCount2(detail.criteriaPlaylist ? GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : GenreCatalogueCountsSchema3.parse(detail.catalogue)) : detail.tracksCount,
|
|
1508
|
+
wikidataId: detail.wikidataId
|
|
1509
|
+
},
|
|
1510
|
+
fill: rootColor,
|
|
1511
|
+
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
1512
|
+
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
1513
|
+
ancestorNodes: ancestors2.map(
|
|
1514
|
+
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
1515
|
+
)
|
|
1516
|
+
};
|
|
1517
|
+
}, [detail, scope]);
|
|
1518
|
+
if (isError) return {
|
|
1519
|
+
title: "Genre unavailable",
|
|
1520
|
+
content: /* @__PURE__ */ jsx20("p", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
1521
|
+
};
|
|
1522
|
+
if (isPending || !detail || !view) return {
|
|
1523
|
+
title: "Loading genre\u2026",
|
|
1524
|
+
content: /* @__PURE__ */ jsxs15("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
1525
|
+
/* @__PURE__ */ jsx20(Skeleton3, { className: "h-6 w-2/3" }),
|
|
1526
|
+
/* @__PURE__ */ jsx20(Skeleton3, { className: "h-4 w-full" }),
|
|
1527
|
+
/* @__PURE__ */ jsx20(Skeleton3, { className: "h-4 w-3/4" })
|
|
1528
|
+
] })
|
|
1529
|
+
};
|
|
1530
|
+
const selectGenre = (nodeId) => onSelectGenre(GenreUuidSchema.parse(nodeId));
|
|
1531
|
+
const family = /* @__PURE__ */ jsx20(
|
|
1532
|
+
GenreNodeFamily,
|
|
1533
|
+
{
|
|
1534
|
+
node: view.node,
|
|
1535
|
+
fill: view.fill,
|
|
1536
|
+
textColor: ACCENT_TEXT_COLOR,
|
|
1537
|
+
parentNode: view.parentNode,
|
|
1538
|
+
childNodes: view.childNodes,
|
|
1539
|
+
onSelectNode: selectGenre
|
|
1540
|
+
}
|
|
1541
|
+
);
|
|
1542
|
+
const ancestors = /* @__PURE__ */ jsx20(GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
1543
|
+
return {
|
|
1544
|
+
title: detail.name,
|
|
1545
|
+
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
1546
|
+
aside: expanded ? ancestors : void 0,
|
|
1547
|
+
content: /* @__PURE__ */ jsxs15(Fragment10, { children: [
|
|
1548
|
+
/* @__PURE__ */ jsx20(
|
|
1549
|
+
GenreInfoPanelBody,
|
|
1550
|
+
{
|
|
1551
|
+
family: /* @__PURE__ */ jsxs15(Fragment10, { children: [
|
|
1552
|
+
family,
|
|
1553
|
+
!expanded && ancestors
|
|
1554
|
+
] }),
|
|
1555
|
+
genreUuid: detail.uuid,
|
|
1556
|
+
genrePlaylist: hostPlaylist ?? (detail.criteriaPlaylist === null ? null : scope === "reference" ? { ...GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist), criteria: CriteriaMinimumSchema.parse(detail) } : { ...detail.criteriaPlaylist, criteria: CriteriaMinimumSchema.parse(detail), tracksCount: detail.tracksCount }),
|
|
1557
|
+
catalogue: detail.catalogue,
|
|
1558
|
+
countsContext: !hostPlaylist && extras?.artist ? "all artists" : void 0,
|
|
1559
|
+
wikidataId: detail.wikidataId,
|
|
1560
|
+
scope,
|
|
1561
|
+
getBackendBaseUrl,
|
|
1562
|
+
playback,
|
|
1563
|
+
band: { node: view.node, ...band, shareTarget: band?.shareTarget?.kind === "genre" ? { ...band.shareTarget, name: detail.name } : band?.shareTarget },
|
|
1564
|
+
extras
|
|
1565
|
+
}
|
|
1566
|
+
),
|
|
1567
|
+
trailing
|
|
1568
|
+
] })
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
function GenreDetailPanel({
|
|
1572
|
+
genreUuid,
|
|
1573
|
+
scope,
|
|
1574
|
+
getBackendBaseUrl,
|
|
1575
|
+
onClose,
|
|
1576
|
+
onSelectGenre,
|
|
1577
|
+
onZoomIn,
|
|
1578
|
+
shareUrl,
|
|
1579
|
+
shareTarget,
|
|
1580
|
+
collapsed,
|
|
1581
|
+
expanded,
|
|
1582
|
+
onExpandedChange,
|
|
1583
|
+
edgeTab,
|
|
1584
|
+
extras,
|
|
1585
|
+
headerClassName
|
|
1586
|
+
}) {
|
|
1587
|
+
const panel = useGenreDetailContent({
|
|
1588
|
+
genreUuid,
|
|
1589
|
+
scope,
|
|
1590
|
+
getBackendBaseUrl,
|
|
1591
|
+
onSelectGenre,
|
|
1592
|
+
extras,
|
|
1593
|
+
expanded,
|
|
1594
|
+
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
1595
|
+
});
|
|
1596
|
+
return /* @__PURE__ */ jsx20(
|
|
1597
|
+
DetailPanel,
|
|
1598
|
+
{
|
|
1599
|
+
side: "left",
|
|
1600
|
+
title: panel.title,
|
|
1601
|
+
headerAccent: panel.headerAccent,
|
|
1602
|
+
onClose,
|
|
1603
|
+
collapsed,
|
|
1604
|
+
expanded,
|
|
1605
|
+
onExpandedChange,
|
|
1606
|
+
edgeTab,
|
|
1607
|
+
aside: panel.aside,
|
|
1608
|
+
headerClassName,
|
|
1609
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ jsx20(IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ jsx20(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx20(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
1610
|
+
children: panel.content
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1432
1615
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1433
|
-
import { useEffect as useEffect4, useMemo as
|
|
1616
|
+
import { useEffect as useEffect4, useMemo as useMemo7 } from "react";
|
|
1434
1617
|
import {
|
|
1435
1618
|
getReparentCandidates,
|
|
1436
1619
|
useUpdateGenre
|
|
1437
1620
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1438
|
-
import { jsx as
|
|
1621
|
+
import { jsx as jsx21, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1439
1622
|
function ChangeParentPopup({
|
|
1440
1623
|
genrePlaylist,
|
|
1441
1624
|
genrePlaylists,
|
|
@@ -1445,7 +1628,7 @@ function ChangeParentPopup({
|
|
|
1445
1628
|
onParentChanged
|
|
1446
1629
|
}) {
|
|
1447
1630
|
const { mutate, isPending, formErrors } = useUpdateGenre(scope, getBackendBaseUrl);
|
|
1448
|
-
const candidates =
|
|
1631
|
+
const candidates = useMemo7(
|
|
1449
1632
|
() => getReparentCandidates(genrePlaylists, genrePlaylist.uuid),
|
|
1450
1633
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1451
1634
|
);
|
|
@@ -1470,12 +1653,12 @@ function ChangeParentPopup({
|
|
|
1470
1653
|
window.addEventListener("keydown", handleKeyDown);
|
|
1471
1654
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1472
1655
|
}, [onClose]);
|
|
1473
|
-
return /* @__PURE__ */
|
|
1474
|
-
/* @__PURE__ */
|
|
1656
|
+
return /* @__PURE__ */ jsxs16(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1657
|
+
/* @__PURE__ */ jsxs16("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1475
1658
|
"Current parent: ",
|
|
1476
1659
|
genrePlaylist.parent?.name ?? "none"
|
|
1477
1660
|
] }),
|
|
1478
|
-
/* @__PURE__ */
|
|
1661
|
+
/* @__PURE__ */ jsx21("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx21(
|
|
1479
1662
|
GenreSearch,
|
|
1480
1663
|
{
|
|
1481
1664
|
genrePlaylists: candidates,
|
|
@@ -1483,12 +1666,12 @@ function ChangeParentPopup({
|
|
|
1483
1666
|
placeholder: "Search a new parent\u2026"
|
|
1484
1667
|
}
|
|
1485
1668
|
) }),
|
|
1486
|
-
formErrors.length > 0 && /* @__PURE__ */
|
|
1669
|
+
formErrors.length > 0 && /* @__PURE__ */ jsx21("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1487
1670
|
] });
|
|
1488
1671
|
}
|
|
1489
1672
|
|
|
1490
1673
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1491
|
-
import { Skeleton as
|
|
1674
|
+
import { Skeleton as Skeleton5 } from "@behindthemusictree/ui";
|
|
1492
1675
|
import { useArtist } from "@behindthemusictree/app-kit/genre-tree";
|
|
1493
1676
|
|
|
1494
1677
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
@@ -1507,7 +1690,7 @@ import {
|
|
|
1507
1690
|
SiYoutube
|
|
1508
1691
|
} from "react-icons/si";
|
|
1509
1692
|
import { Chip } from "@behindthemusictree/ui";
|
|
1510
|
-
import { jsx as
|
|
1693
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1511
1694
|
var LINK_KINDS = {
|
|
1512
1695
|
official: { label: "Official website", Icon: Globe },
|
|
1513
1696
|
wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
|
|
@@ -1544,7 +1727,7 @@ function artistLinks(artist) {
|
|
|
1544
1727
|
return links;
|
|
1545
1728
|
}
|
|
1546
1729
|
function ArtistLinks({ links }) {
|
|
1547
|
-
return /* @__PURE__ */
|
|
1730
|
+
return /* @__PURE__ */ jsx22("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx22("li", { children: /* @__PURE__ */ jsx22(Chip, { href: url, icon: /* @__PURE__ */ jsx22(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1548
1731
|
}
|
|
1549
1732
|
|
|
1550
1733
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1555,7 +1738,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1555
1738
|
}
|
|
1556
1739
|
|
|
1557
1740
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1558
|
-
import { jsx as
|
|
1741
|
+
import { jsx as jsx23 } from "react/jsx-runtime";
|
|
1559
1742
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1560
1743
|
function ArtistHeader({ artist }) {
|
|
1561
1744
|
const fields = [];
|
|
@@ -1565,35 +1748,35 @@ function ArtistHeader({ artist }) {
|
|
|
1565
1748
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1566
1749
|
if (years) fields.push({ label: "Active", value: years });
|
|
1567
1750
|
const links = artistLinks(artist);
|
|
1568
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */
|
|
1751
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx23(ArtistLinks, { links }) });
|
|
1569
1752
|
if (fields.length === 0) return null;
|
|
1570
|
-
return /* @__PURE__ */
|
|
1753
|
+
return /* @__PURE__ */ jsx23(FieldList, { items: fields });
|
|
1571
1754
|
}
|
|
1572
1755
|
|
|
1573
1756
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1574
|
-
import { jsx as
|
|
1757
|
+
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1575
1758
|
function ExternalLink({ href, children }) {
|
|
1576
|
-
return /* @__PURE__ */
|
|
1759
|
+
return /* @__PURE__ */ jsx24("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1577
1760
|
}
|
|
1578
1761
|
function ArtistPhoto({ image, name }) {
|
|
1579
|
-
return /* @__PURE__ */
|
|
1580
|
-
/* @__PURE__ */
|
|
1581
|
-
/* @__PURE__ */
|
|
1762
|
+
return /* @__PURE__ */ jsxs17("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1763
|
+
/* @__PURE__ */ jsx24("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1764
|
+
/* @__PURE__ */ jsxs17("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1582
1765
|
"Photo: ",
|
|
1583
|
-
/* @__PURE__ */
|
|
1766
|
+
/* @__PURE__ */ jsx24(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1584
1767
|
",",
|
|
1585
1768
|
" ",
|
|
1586
|
-
image.licenseUrl ? /* @__PURE__ */
|
|
1769
|
+
image.licenseUrl ? /* @__PURE__ */ jsx24(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1587
1770
|
] })
|
|
1588
1771
|
] });
|
|
1589
1772
|
}
|
|
1590
1773
|
|
|
1591
1774
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1592
1775
|
import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
|
|
1593
|
-
import { jsx as
|
|
1776
|
+
import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1594
1777
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1595
1778
|
if (memberships.length === 0) return null;
|
|
1596
|
-
return /* @__PURE__ */
|
|
1779
|
+
return /* @__PURE__ */ jsx25(PanelGroup, { title, children: /* @__PURE__ */ jsx25("ul", { children: memberships.map((membership, index) => {
|
|
1597
1780
|
const details = [
|
|
1598
1781
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1599
1782
|
...membership.roles
|
|
@@ -1601,9 +1784,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1601
1784
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1602
1785
|
return (
|
|
1603
1786
|
// A member can have several periods in the same group.
|
|
1604
|
-
/* @__PURE__ */
|
|
1605
|
-
/* @__PURE__ */
|
|
1606
|
-
detailsText && /* @__PURE__ */
|
|
1787
|
+
/* @__PURE__ */ jsxs18("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1788
|
+
/* @__PURE__ */ jsx25("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1789
|
+
detailsText && /* @__PURE__ */ jsxs18("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1607
1790
|
" \u2014 ",
|
|
1608
1791
|
detailsText
|
|
1609
1792
|
] })
|
|
@@ -1614,9 +1797,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1614
1797
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1615
1798
|
if (memberships.length === 0) return null;
|
|
1616
1799
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1617
|
-
return /* @__PURE__ */
|
|
1618
|
-
/* @__PURE__ */
|
|
1619
|
-
/* @__PURE__ */
|
|
1800
|
+
return /* @__PURE__ */ jsxs18(PanelSection3, { title, children: [
|
|
1801
|
+
/* @__PURE__ */ jsx25(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1802
|
+
/* @__PURE__ */ jsx25(
|
|
1620
1803
|
MembershipList,
|
|
1621
1804
|
{
|
|
1622
1805
|
title: "Past",
|
|
@@ -1629,7 +1812,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1629
1812
|
|
|
1630
1813
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1631
1814
|
import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
|
|
1632
|
-
import { jsx as
|
|
1815
|
+
import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1633
1816
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1634
1817
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1635
1818
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1649,12 +1832,12 @@ function byYear(a, b) {
|
|
|
1649
1832
|
function ArtistDiscography({ albums }) {
|
|
1650
1833
|
if (albums.length === 0) return null;
|
|
1651
1834
|
const sorted = [...albums].sort(byYear);
|
|
1652
|
-
return /* @__PURE__ */
|
|
1835
|
+
return /* @__PURE__ */ jsx26(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
|
|
1653
1836
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1654
1837
|
if (groupAlbums.length === 0) return null;
|
|
1655
|
-
return /* @__PURE__ */
|
|
1656
|
-
/* @__PURE__ */
|
|
1657
|
-
/* @__PURE__ */
|
|
1838
|
+
return /* @__PURE__ */ jsx26(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx26("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs19("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1839
|
+
/* @__PURE__ */ jsx26("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1840
|
+
/* @__PURE__ */ jsx26("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1658
1841
|
] }, album.uuid)) }) }, group);
|
|
1659
1842
|
}) });
|
|
1660
1843
|
}
|
|
@@ -1663,7 +1846,7 @@ function ArtistDiscography({ albums }) {
|
|
|
1663
1846
|
import { useState as useState7 } from "react";
|
|
1664
1847
|
import { keepPreviousData } from "@tanstack/react-query";
|
|
1665
1848
|
import { Play as Play2 } from "lucide-react";
|
|
1666
|
-
import { Pagination, PanelSection as PanelSection5, Skeleton as
|
|
1849
|
+
import { Pagination, PanelSection as PanelSection5, Skeleton as Skeleton4 } from "@behindthemusictree/ui";
|
|
1667
1850
|
import { usePlayer as usePlayer8 } from "@behindthemusictree/app-kit/player";
|
|
1668
1851
|
import {
|
|
1669
1852
|
SongDetailedSchema as SongDetailedSchema2,
|
|
@@ -1671,7 +1854,7 @@ import {
|
|
|
1671
1854
|
songUnplayableReason as songUnplayableReason3,
|
|
1672
1855
|
useTrackList as useTrackList4
|
|
1673
1856
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1674
|
-
import { PaginatedResponseSchema as PaginatedResponseSchema2,
|
|
1857
|
+
import { PaginatedResponseSchema as PaginatedResponseSchema2, usePlaybackQuery } from "@behindthemusictree/app-kit/transport";
|
|
1675
1858
|
|
|
1676
1859
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1677
1860
|
import { usePlayer as usePlayer7 } from "@behindthemusictree/app-kit/player";
|
|
@@ -1681,7 +1864,7 @@ import { useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre
|
|
|
1681
1864
|
import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
|
|
1682
1865
|
import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
|
|
1683
1866
|
import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
|
|
1684
|
-
import { jsx as
|
|
1867
|
+
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1685
1868
|
function TrackPositionPlayPause({
|
|
1686
1869
|
position,
|
|
1687
1870
|
uuid,
|
|
@@ -1695,14 +1878,14 @@ function TrackPositionPlayPause({
|
|
|
1695
1878
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PLAYING;
|
|
1696
1879
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PAUSED;
|
|
1697
1880
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1698
|
-
return /* @__PURE__ */
|
|
1881
|
+
return /* @__PURE__ */ jsxs20(
|
|
1699
1882
|
"div",
|
|
1700
1883
|
{
|
|
1701
1884
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1702
1885
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1703
1886
|
onClick: handlePlayPauseClick,
|
|
1704
1887
|
children: [
|
|
1705
|
-
/* @__PURE__ */
|
|
1888
|
+
/* @__PURE__ */ jsx27("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx27("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs20(
|
|
1706
1889
|
"span",
|
|
1707
1890
|
{
|
|
1708
1891
|
className: "track-position-play-pause__bars",
|
|
@@ -1710,18 +1893,18 @@ function TrackPositionPlayPause({
|
|
|
1710
1893
|
role: "img",
|
|
1711
1894
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1712
1895
|
children: [
|
|
1713
|
-
/* @__PURE__ */
|
|
1714
|
-
/* @__PURE__ */
|
|
1715
|
-
/* @__PURE__ */
|
|
1896
|
+
/* @__PURE__ */ jsx27("span", { className: "track-position-play-pause__bar" }),
|
|
1897
|
+
/* @__PURE__ */ jsx27("span", { className: "track-position-play-pause__bar" }),
|
|
1898
|
+
/* @__PURE__ */ jsx27("span", { className: "track-position-play-pause__bar" })
|
|
1716
1899
|
]
|
|
1717
1900
|
}
|
|
1718
1901
|
) : position }),
|
|
1719
|
-
/* @__PURE__ */
|
|
1902
|
+
/* @__PURE__ */ jsx27(
|
|
1720
1903
|
"div",
|
|
1721
1904
|
{
|
|
1722
1905
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1723
1906
|
"aria-hidden": "true",
|
|
1724
|
-
children: isAwaitingPlayback ? /* @__PURE__ */
|
|
1907
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ jsx27("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx27(FaPause2, {}) : /* @__PURE__ */ jsx27(FaPlay2, {})
|
|
1725
1908
|
}
|
|
1726
1909
|
)
|
|
1727
1910
|
]
|
|
@@ -1732,7 +1915,7 @@ function TrackPositionPlayPause({
|
|
|
1732
1915
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1733
1916
|
import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1734
1917
|
import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1735
|
-
import { jsx as
|
|
1918
|
+
import { jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1736
1919
|
function TrackItem({
|
|
1737
1920
|
track,
|
|
1738
1921
|
position,
|
|
@@ -1756,12 +1939,12 @@ function TrackItem({
|
|
|
1756
1939
|
loadTrackForPlayer(track.uuid);
|
|
1757
1940
|
}
|
|
1758
1941
|
};
|
|
1759
|
-
return /* @__PURE__ */
|
|
1942
|
+
return /* @__PURE__ */ jsxs21(
|
|
1760
1943
|
"div",
|
|
1761
1944
|
{
|
|
1762
1945
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1763
1946
|
children: [
|
|
1764
|
-
isUnplayable ? /* @__PURE__ */
|
|
1947
|
+
isUnplayable ? /* @__PURE__ */ jsx28(
|
|
1765
1948
|
"div",
|
|
1766
1949
|
{
|
|
1767
1950
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1769,7 +1952,7 @@ function TrackItem({
|
|
|
1769
1952
|
"aria-disabled": "true",
|
|
1770
1953
|
children: position
|
|
1771
1954
|
}
|
|
1772
|
-
) : /* @__PURE__ */
|
|
1955
|
+
) : /* @__PURE__ */ jsx28(
|
|
1773
1956
|
TrackPositionPlayPause,
|
|
1774
1957
|
{
|
|
1775
1958
|
position,
|
|
@@ -1778,12 +1961,12 @@ function TrackItem({
|
|
|
1778
1961
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1779
1962
|
}
|
|
1780
1963
|
),
|
|
1781
|
-
/* @__PURE__ */
|
|
1782
|
-
/* @__PURE__ */
|
|
1783
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */
|
|
1964
|
+
/* @__PURE__ */ jsxs21("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1965
|
+
/* @__PURE__ */ jsx28("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx28("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx28(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1966
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx28("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx28(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1784
1967
|
] }),
|
|
1785
|
-
/* @__PURE__ */
|
|
1786
|
-
renderDuration && /* @__PURE__ */
|
|
1968
|
+
/* @__PURE__ */ jsx28("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx28("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1969
|
+
renderDuration && /* @__PURE__ */ jsx28(
|
|
1787
1970
|
"div",
|
|
1788
1971
|
{
|
|
1789
1972
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1791,14 +1974,14 @@ function TrackItem({
|
|
|
1791
1974
|
children: renderDuration(track)
|
|
1792
1975
|
}
|
|
1793
1976
|
),
|
|
1794
|
-
renderActions && /* @__PURE__ */
|
|
1977
|
+
renderActions && /* @__PURE__ */ jsx28("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1795
1978
|
]
|
|
1796
1979
|
}
|
|
1797
1980
|
);
|
|
1798
1981
|
}
|
|
1799
1982
|
|
|
1800
1983
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1801
|
-
import { Fragment as
|
|
1984
|
+
import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1802
1985
|
var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
|
|
1803
1986
|
var SORTS = [
|
|
1804
1987
|
{ ordering: "title", label: "Title" },
|
|
@@ -1807,12 +1990,12 @@ var SORTS = [
|
|
|
1807
1990
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
1808
1991
|
const [page, setPage] = useState7(1);
|
|
1809
1992
|
const [ordering, setOrdering] = useState7("title");
|
|
1810
|
-
const { fetch } = useFetchWrapper2(getBackendBaseUrl);
|
|
1811
1993
|
const { playNewTrackListFromArtist } = useTrackList4();
|
|
1812
1994
|
const { playerTrackObject } = usePlayer8();
|
|
1813
|
-
const { data, isPending, isError } =
|
|
1995
|
+
const { data, isPending, isError } = usePlaybackQuery({
|
|
1996
|
+
getBackendBaseUrl,
|
|
1814
1997
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
1815
|
-
queryFn: () =>
|
|
1998
|
+
queryFn: (fetchBound) => fetchBound(libraryEndpoints2.reference.songs.list(), true, false, void 0, {
|
|
1816
1999
|
artist: artist.uuid,
|
|
1817
2000
|
ordering,
|
|
1818
2001
|
page
|
|
@@ -1831,21 +2014,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1831
2014
|
event.stopPropagation();
|
|
1832
2015
|
void play(song.uuid);
|
|
1833
2016
|
};
|
|
1834
|
-
return /* @__PURE__ */
|
|
1835
|
-
/* @__PURE__ */
|
|
1836
|
-
/* @__PURE__ */
|
|
2017
|
+
return /* @__PURE__ */ jsx29(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx29(Skeleton4, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx29("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx29("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
2018
|
+
/* @__PURE__ */ jsxs22("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2019
|
+
/* @__PURE__ */ jsxs22(
|
|
1837
2020
|
"button",
|
|
1838
2021
|
{
|
|
1839
2022
|
type: "button",
|
|
1840
2023
|
onClick: () => void play(),
|
|
1841
2024
|
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",
|
|
1842
2025
|
children: [
|
|
1843
|
-
/* @__PURE__ */
|
|
2026
|
+
/* @__PURE__ */ jsx29(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1844
2027
|
"Play all"
|
|
1845
2028
|
]
|
|
1846
2029
|
}
|
|
1847
2030
|
),
|
|
1848
|
-
/* @__PURE__ */
|
|
2031
|
+
/* @__PURE__ */ jsx29("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx29(
|
|
1849
2032
|
"button",
|
|
1850
2033
|
{
|
|
1851
2034
|
type: "button",
|
|
@@ -1860,7 +2043,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1860
2043
|
sort.ordering
|
|
1861
2044
|
)) })
|
|
1862
2045
|
] }),
|
|
1863
|
-
/* @__PURE__ */
|
|
2046
|
+
/* @__PURE__ */ jsx29("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx29("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx29(
|
|
1864
2047
|
TrackItem,
|
|
1865
2048
|
{
|
|
1866
2049
|
track: song,
|
|
@@ -1869,12 +2052,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1869
2052
|
renderSongLink
|
|
1870
2053
|
}
|
|
1871
2054
|
) }, song.uuid)) }),
|
|
1872
|
-
/* @__PURE__ */
|
|
2055
|
+
/* @__PURE__ */ jsx29(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1873
2056
|
] }) });
|
|
1874
2057
|
}
|
|
1875
2058
|
|
|
1876
2059
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1877
|
-
import { Fragment as
|
|
2060
|
+
import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1878
2061
|
function useArtistPanel({
|
|
1879
2062
|
uuid,
|
|
1880
2063
|
scope,
|
|
@@ -1895,26 +2078,26 @@ function useArtistPanel({
|
|
|
1895
2078
|
return {
|
|
1896
2079
|
...panel,
|
|
1897
2080
|
title: "Artist",
|
|
1898
|
-
content: /* @__PURE__ */
|
|
1899
|
-
/* @__PURE__ */
|
|
1900
|
-
/* @__PURE__ */
|
|
1901
|
-
/* @__PURE__ */
|
|
1902
|
-
/* @__PURE__ */
|
|
1903
|
-
/* @__PURE__ */
|
|
1904
|
-
/* @__PURE__ */
|
|
2081
|
+
content: /* @__PURE__ */ jsxs23("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2082
|
+
/* @__PURE__ */ jsx30(Skeleton5, { className: "h-3 w-16" }),
|
|
2083
|
+
/* @__PURE__ */ jsx30(Skeleton5, { className: "h-4 w-32" }),
|
|
2084
|
+
/* @__PURE__ */ jsx30(Skeleton5, { className: "h-3 w-16" }),
|
|
2085
|
+
/* @__PURE__ */ jsx30(Skeleton5, { className: "h-4 w-24" }),
|
|
2086
|
+
/* @__PURE__ */ jsx30(Skeleton5, { className: "h-16 mt-2.5" }),
|
|
2087
|
+
/* @__PURE__ */ jsx30(Skeleton5, { className: "h-16" })
|
|
1905
2088
|
] })
|
|
1906
2089
|
};
|
|
1907
2090
|
case "not-found":
|
|
1908
2091
|
return {
|
|
1909
2092
|
...panel,
|
|
1910
2093
|
title: "Artist not found",
|
|
1911
|
-
content: /* @__PURE__ */
|
|
2094
|
+
content: /* @__PURE__ */ jsx30("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1912
2095
|
};
|
|
1913
2096
|
case "error":
|
|
1914
2097
|
return {
|
|
1915
2098
|
...panel,
|
|
1916
2099
|
title: "Artist",
|
|
1917
|
-
content: /* @__PURE__ */
|
|
2100
|
+
content: /* @__PURE__ */ jsx30("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
1918
2101
|
};
|
|
1919
2102
|
case "success": {
|
|
1920
2103
|
const { artist } = result;
|
|
@@ -1923,13 +2106,13 @@ function useArtistPanel({
|
|
|
1923
2106
|
return {
|
|
1924
2107
|
...panel,
|
|
1925
2108
|
title: artist.name,
|
|
1926
|
-
content: /* @__PURE__ */
|
|
1927
|
-
shareUrl && /* @__PURE__ */
|
|
1928
|
-
artist.image && /* @__PURE__ */
|
|
1929
|
-
/* @__PURE__ */
|
|
1930
|
-
/* @__PURE__ */
|
|
1931
|
-
/* @__PURE__ */
|
|
1932
|
-
artist.tracksCount > 0 && /* @__PURE__ */
|
|
2109
|
+
content: /* @__PURE__ */ jsxs23(Fragment12, { children: [
|
|
2110
|
+
shareUrl && /* @__PURE__ */ jsx30("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsx30("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ jsx30(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2111
|
+
artist.image && /* @__PURE__ */ jsx30(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2112
|
+
/* @__PURE__ */ jsx30(ArtistHeader, { artist }),
|
|
2113
|
+
/* @__PURE__ */ jsx30(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2114
|
+
/* @__PURE__ */ jsx30(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2115
|
+
artist.tracksCount > 0 && /* @__PURE__ */ jsx30(
|
|
1933
2116
|
ArtistSongs,
|
|
1934
2117
|
{
|
|
1935
2118
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -1939,14 +2122,14 @@ function useArtistPanel({
|
|
|
1939
2122
|
}
|
|
1940
2123
|
)
|
|
1941
2124
|
] }),
|
|
1942
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */
|
|
2125
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx30(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
1943
2126
|
};
|
|
1944
2127
|
}
|
|
1945
2128
|
}
|
|
1946
2129
|
}
|
|
1947
2130
|
|
|
1948
2131
|
// src/genre-tree/song/useSongPanel.tsx
|
|
1949
|
-
import { Skeleton as
|
|
2132
|
+
import { Skeleton as Skeleton7 } from "@behindthemusictree/ui";
|
|
1950
2133
|
import {
|
|
1951
2134
|
useSong
|
|
1952
2135
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
@@ -1954,7 +2137,7 @@ import {
|
|
|
1954
2137
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
1955
2138
|
import { useState as useState8 } from "react";
|
|
1956
2139
|
import { Pause as Pause2, Play as Play3 } from "lucide-react";
|
|
1957
|
-
import { FieldList as FieldList2, PanelSection as PanelSection6, Skeleton as
|
|
2140
|
+
import { FieldList as FieldList2, PanelSection as PanelSection6, Skeleton as Skeleton6 } from "@behindthemusictree/ui";
|
|
1958
2141
|
import { PlayStates as PlayStates3, usePlayer as usePlayer9 } from "@behindthemusictree/app-kit/player";
|
|
1959
2142
|
import {
|
|
1960
2143
|
ArtistNames as ArtistNames3,
|
|
@@ -1963,7 +2146,7 @@ import {
|
|
|
1963
2146
|
useSongHistory,
|
|
1964
2147
|
useTrackList as useTrackList5
|
|
1965
2148
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1966
|
-
import { Fragment as
|
|
2149
|
+
import { Fragment as Fragment13, jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1967
2150
|
var numberFormat = new Intl.NumberFormat("en");
|
|
1968
2151
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
1969
2152
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -1971,32 +2154,32 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
1971
2154
|
const [open, setOpen] = useState8(false);
|
|
1972
2155
|
const history = useSongHistory(uuid, getBackendBaseUrl, open);
|
|
1973
2156
|
let body = null;
|
|
1974
|
-
if (history.status === "loading") body = /* @__PURE__ */
|
|
1975
|
-
else if (history.status === "error") body = /* @__PURE__ */
|
|
2157
|
+
if (history.status === "loading") body = /* @__PURE__ */ jsx31(Skeleton6, { className: "h-10", "aria-busy": "true" });
|
|
2158
|
+
else if (history.status === "error") body = /* @__PURE__ */ jsx31("p", { className: "text-red-600", children: "Could not load the history." });
|
|
1976
2159
|
else if (history.status === "success")
|
|
1977
|
-
body = history.entries.length === 0 ? /* @__PURE__ */
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */
|
|
2160
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ jsx31("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx31("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs24("li", { className: "py-1.5 text-sm", children: [
|
|
2161
|
+
/* @__PURE__ */ jsx31("span", { className: "font-medium", children: entry.action }),
|
|
2162
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs24("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1980
2163
|
" ",
|
|
1981
2164
|
"\u2014 ",
|
|
1982
2165
|
entry.oldValue ?? "none",
|
|
1983
2166
|
" \u2192 ",
|
|
1984
2167
|
entry.newValue ?? "none"
|
|
1985
2168
|
] }) : null,
|
|
1986
|
-
/* @__PURE__ */
|
|
2169
|
+
/* @__PURE__ */ jsxs24("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
1987
2170
|
entry.actorPseudo ?? "Anonymous",
|
|
1988
2171
|
" \xB7 ",
|
|
1989
2172
|
dateFormat.format(new Date(entry.createdOn))
|
|
1990
2173
|
] })
|
|
1991
2174
|
] }, entry.uuid)) });
|
|
1992
|
-
return /* @__PURE__ */
|
|
2175
|
+
return /* @__PURE__ */ jsxs24(
|
|
1993
2176
|
"details",
|
|
1994
2177
|
{
|
|
1995
2178
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
1996
2179
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
1997
2180
|
children: [
|
|
1998
|
-
/* @__PURE__ */
|
|
1999
|
-
/* @__PURE__ */
|
|
2181
|
+
/* @__PURE__ */ jsx31("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2182
|
+
/* @__PURE__ */ jsx31("div", { className: "mt-1.5", children: body })
|
|
2000
2183
|
]
|
|
2001
2184
|
}
|
|
2002
2185
|
);
|
|
@@ -2022,13 +2205,13 @@ function SongPanelContent({
|
|
|
2022
2205
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2023
2206
|
fields.push({
|
|
2024
2207
|
label: "Genre",
|
|
2025
|
-
value: path ? /* @__PURE__ */
|
|
2026
|
-
index > 0 && /* @__PURE__ */
|
|
2027
|
-
/* @__PURE__ */
|
|
2208
|
+
value: path ? /* @__PURE__ */ jsx31("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs24("span", { children: [
|
|
2209
|
+
index > 0 && /* @__PURE__ */ jsx31("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2210
|
+
/* @__PURE__ */ jsx31("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2028
2211
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2029
2212
|
});
|
|
2030
2213
|
} else {
|
|
2031
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */
|
|
2214
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ jsx31("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2032
2215
|
}
|
|
2033
2216
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2034
2217
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2038,11 +2221,11 @@ function SongPanelContent({
|
|
|
2038
2221
|
const links = [];
|
|
2039
2222
|
if (musicBrainzUrl)
|
|
2040
2223
|
links.push(
|
|
2041
|
-
/* @__PURE__ */
|
|
2224
|
+
/* @__PURE__ */ jsx31("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2042
2225
|
);
|
|
2043
2226
|
if (song.youtubeVideo)
|
|
2044
2227
|
links.push(
|
|
2045
|
-
/* @__PURE__ */
|
|
2228
|
+
/* @__PURE__ */ jsx31(
|
|
2046
2229
|
"a",
|
|
2047
2230
|
{
|
|
2048
2231
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2057,33 +2240,33 @@ function SongPanelContent({
|
|
|
2057
2240
|
if (links.length > 0)
|
|
2058
2241
|
fields.push({
|
|
2059
2242
|
label: "Links",
|
|
2060
|
-
value: /* @__PURE__ */
|
|
2243
|
+
value: /* @__PURE__ */ jsx31("span", { className: "flex gap-3", children: links })
|
|
2061
2244
|
});
|
|
2062
|
-
return /* @__PURE__ */
|
|
2063
|
-
/* @__PURE__ */
|
|
2064
|
-
unplayableReason ? /* @__PURE__ */
|
|
2245
|
+
return /* @__PURE__ */ jsxs24(Fragment13, { children: [
|
|
2246
|
+
/* @__PURE__ */ jsx31("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxs24("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2247
|
+
unplayableReason ? /* @__PURE__ */ jsx31("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs24(
|
|
2065
2248
|
"button",
|
|
2066
2249
|
{
|
|
2067
2250
|
type: "button",
|
|
2068
2251
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2069
2252
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2070
2253
|
children: [
|
|
2071
|
-
/* @__PURE__ */
|
|
2254
|
+
/* @__PURE__ */ jsx31("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__ */ jsx31(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx31(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2072
2255
|
isPlaying ? "Pause" : "Play"
|
|
2073
2256
|
]
|
|
2074
2257
|
}
|
|
2075
2258
|
),
|
|
2076
|
-
shareUrl && /* @__PURE__ */
|
|
2259
|
+
shareUrl && /* @__PURE__ */ jsx31(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2077
2260
|
] }) }),
|
|
2078
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */
|
|
2079
|
-
/* @__PURE__ */
|
|
2080
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */
|
|
2081
|
-
/* @__PURE__ */
|
|
2261
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx31("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx31(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
|
|
2262
|
+
/* @__PURE__ */ jsx31(FieldList2, { items: fields }),
|
|
2263
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx31(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx31("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx31("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2264
|
+
/* @__PURE__ */ jsx31(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2082
2265
|
] });
|
|
2083
2266
|
}
|
|
2084
2267
|
|
|
2085
2268
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2086
|
-
import { jsx as
|
|
2269
|
+
import { jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2087
2270
|
function useSongPanel({
|
|
2088
2271
|
uuid,
|
|
2089
2272
|
scope,
|
|
@@ -2105,24 +2288,24 @@ function useSongPanel({
|
|
|
2105
2288
|
return {
|
|
2106
2289
|
...panel,
|
|
2107
2290
|
title: "Song",
|
|
2108
|
-
content: /* @__PURE__ */
|
|
2109
|
-
/* @__PURE__ */
|
|
2110
|
-
/* @__PURE__ */
|
|
2111
|
-
/* @__PURE__ */
|
|
2112
|
-
/* @__PURE__ */
|
|
2291
|
+
content: /* @__PURE__ */ jsxs25("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2292
|
+
/* @__PURE__ */ jsx32(Skeleton7, { className: "h-3 w-16" }),
|
|
2293
|
+
/* @__PURE__ */ jsx32(Skeleton7, { className: "h-4 w-32" }),
|
|
2294
|
+
/* @__PURE__ */ jsx32(Skeleton7, { className: "h-3 w-16" }),
|
|
2295
|
+
/* @__PURE__ */ jsx32(Skeleton7, { className: "h-4 w-24" })
|
|
2113
2296
|
] })
|
|
2114
2297
|
};
|
|
2115
2298
|
case "not-found":
|
|
2116
2299
|
return {
|
|
2117
2300
|
...panel,
|
|
2118
2301
|
title: "Song not found",
|
|
2119
|
-
content: /* @__PURE__ */
|
|
2302
|
+
content: /* @__PURE__ */ jsx32("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2120
2303
|
};
|
|
2121
2304
|
case "error":
|
|
2122
2305
|
return {
|
|
2123
2306
|
...panel,
|
|
2124
2307
|
title: "Song",
|
|
2125
|
-
content: /* @__PURE__ */
|
|
2308
|
+
content: /* @__PURE__ */ jsx32("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2126
2309
|
};
|
|
2127
2310
|
case "success": {
|
|
2128
2311
|
const { song } = result;
|
|
@@ -2130,7 +2313,7 @@ function useSongPanel({
|
|
|
2130
2313
|
return {
|
|
2131
2314
|
...panel,
|
|
2132
2315
|
title: song.title,
|
|
2133
|
-
content: /* @__PURE__ */
|
|
2316
|
+
content: /* @__PURE__ */ jsx32(
|
|
2134
2317
|
SongPanelContent,
|
|
2135
2318
|
{
|
|
2136
2319
|
song,
|
|
@@ -2150,12 +2333,12 @@ function useSongPanel({
|
|
|
2150
2333
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2151
2334
|
import { MdMusicNote, MdVisibility } from "react-icons/md";
|
|
2152
2335
|
import { SegmentedControl } from "@behindthemusictree/ui";
|
|
2153
|
-
import { jsx as
|
|
2336
|
+
import { jsx as jsx33 } from "react/jsx-runtime";
|
|
2154
2337
|
function SizeMetricToggle({
|
|
2155
2338
|
value,
|
|
2156
2339
|
onChange
|
|
2157
2340
|
}) {
|
|
2158
|
-
return /* @__PURE__ */
|
|
2341
|
+
return /* @__PURE__ */ jsx33(
|
|
2159
2342
|
SegmentedControl,
|
|
2160
2343
|
{
|
|
2161
2344
|
variant: "floating",
|
|
@@ -2163,15 +2346,15 @@ function SizeMetricToggle({
|
|
|
2163
2346
|
value,
|
|
2164
2347
|
onChange,
|
|
2165
2348
|
options: [
|
|
2166
|
-
{ value: "songs", label: /* @__PURE__ */
|
|
2167
|
-
{ value: "views", label: /* @__PURE__ */
|
|
2349
|
+
{ value: "songs", label: /* @__PURE__ */ jsx33(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2350
|
+
{ value: "views", label: /* @__PURE__ */ jsx33(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2168
2351
|
]
|
|
2169
2352
|
}
|
|
2170
2353
|
);
|
|
2171
2354
|
}
|
|
2172
2355
|
|
|
2173
2356
|
// src/genre-tree/GenreTreeView.tsx
|
|
2174
|
-
import { Fragment as
|
|
2357
|
+
import { Fragment as Fragment14, jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2175
2358
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2176
2359
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2177
2360
|
var noop = () => {
|
|
@@ -2184,8 +2367,8 @@ function GenreTreeView({
|
|
|
2184
2367
|
getBackendBaseUrl,
|
|
2185
2368
|
additionalActions,
|
|
2186
2369
|
viewMode: controlledViewMode,
|
|
2187
|
-
|
|
2188
|
-
|
|
2370
|
+
selectedGenreUuid: controlledSelectedGenreUuid,
|
|
2371
|
+
onSelectedGenreUuidChange,
|
|
2189
2372
|
defaultViewport,
|
|
2190
2373
|
viewportRestore,
|
|
2191
2374
|
onViewportChange,
|
|
@@ -2258,10 +2441,14 @@ function GenreTreeView({
|
|
|
2258
2441
|
};
|
|
2259
2442
|
const {
|
|
2260
2443
|
isLoading,
|
|
2444
|
+
isError,
|
|
2445
|
+
refetch,
|
|
2261
2446
|
isFetching: isTreeFetching,
|
|
2262
2447
|
genrePlaylistByNodeId,
|
|
2263
2448
|
visibleGenrePlaylists,
|
|
2264
2449
|
groupedGenrePlaylistsByRoot,
|
|
2450
|
+
selectedGenreUuid,
|
|
2451
|
+
selectGenre,
|
|
2265
2452
|
selectedNodeId,
|
|
2266
2453
|
selectedGenrePlaylist,
|
|
2267
2454
|
selectNode,
|
|
@@ -2278,8 +2465,8 @@ function GenreTreeView({
|
|
|
2278
2465
|
scope,
|
|
2279
2466
|
getBackendBaseUrl,
|
|
2280
2467
|
viewMode: controlledViewMode,
|
|
2281
|
-
|
|
2282
|
-
|
|
2468
|
+
selectedGenreUuid: controlledSelectedGenreUuid,
|
|
2469
|
+
onSelectedGenreUuidChange,
|
|
2283
2470
|
criteriaOverviewSchema,
|
|
2284
2471
|
artistUuid
|
|
2285
2472
|
});
|
|
@@ -2300,25 +2487,25 @@ function GenreTreeView({
|
|
|
2300
2487
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2301
2488
|
const artistResult = useArtist2(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2302
2489
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2303
|
-
const filterArtist =
|
|
2490
|
+
const filterArtist = useMemo8(
|
|
2304
2491
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2305
2492
|
[artistUuid, filterArtistName]
|
|
2306
2493
|
);
|
|
2307
2494
|
const hasSelectedGenreNode = selectedGenrePlaylist !== void 0 && visibleGenrePlaylists.some(({ uuid }) => uuid === selectedGenrePlaylist.uuid);
|
|
2308
2495
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: visibleGenrePlaylists, artist: filterArtist });
|
|
2309
2496
|
const openArtist = (uuid) => {
|
|
2310
|
-
if (
|
|
2497
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2311
2498
|
if (songUuid !== null) changeSong(null);
|
|
2312
2499
|
changeArtist(uuid);
|
|
2313
2500
|
};
|
|
2314
2501
|
const openSong = (uuid) => {
|
|
2315
|
-
if (
|
|
2502
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2316
2503
|
changeSong(uuid);
|
|
2317
2504
|
};
|
|
2318
2505
|
const openSongRef = useRef3(openSong);
|
|
2319
2506
|
openSongRef.current = openSong;
|
|
2320
2507
|
const resolvedRenderSongLink = useCallback6(
|
|
2321
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */
|
|
2508
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx34("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2322
2509
|
[renderSongLink]
|
|
2323
2510
|
);
|
|
2324
2511
|
const [expandedSongUuid, setExpandedSongUuid] = useState9(null);
|
|
@@ -2330,7 +2517,7 @@ function GenreTreeView({
|
|
|
2330
2517
|
scope,
|
|
2331
2518
|
getBackendBaseUrl: artistBaseUrl,
|
|
2332
2519
|
getShareUrl: getShareUrlRef.current,
|
|
2333
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2520
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx34("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2334
2521
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2335
2522
|
onExpandedChange: (on) => {
|
|
2336
2523
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2339,7 +2526,7 @@ function GenreTreeView({
|
|
|
2339
2526
|
renderSongLink: resolvedRenderSongLink,
|
|
2340
2527
|
onClose: () => changeArtist(null)
|
|
2341
2528
|
});
|
|
2342
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2529
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx34("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2343
2530
|
const getGenrePath = (genre) => {
|
|
2344
2531
|
const path = [];
|
|
2345
2532
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2357,9 +2544,9 @@ function GenreTreeView({
|
|
|
2357
2544
|
getShareUrl: getShareUrlRef.current,
|
|
2358
2545
|
renderArtistLink: resolvedRenderArtistLink,
|
|
2359
2546
|
getGenrePath,
|
|
2360
|
-
onSelectGenre: (
|
|
2547
|
+
onSelectGenre: (genrePlaylist2) => {
|
|
2361
2548
|
changeSong(null);
|
|
2362
|
-
selectGenrePlaylist(
|
|
2549
|
+
selectGenrePlaylist(genrePlaylist2);
|
|
2363
2550
|
},
|
|
2364
2551
|
expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
|
|
2365
2552
|
onExpandedChange: (on) => {
|
|
@@ -2368,8 +2555,8 @@ function GenreTreeView({
|
|
|
2368
2555
|
},
|
|
2369
2556
|
onClose: () => changeSong(null)
|
|
2370
2557
|
});
|
|
2371
|
-
const builtInSongPanel = overlayPanel ||
|
|
2372
|
-
const builtInArtistPanel = overlayPanel ||
|
|
2558
|
+
const builtInSongPanel = overlayPanel || selectedGenreUuid !== null ? void 0 : songPanel;
|
|
2559
|
+
const builtInArtistPanel = overlayPanel || selectedGenreUuid !== null || builtInSongPanel ? void 0 : artistPanel;
|
|
2373
2560
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2374
2561
|
const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState9(false);
|
|
2375
2562
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState9(false);
|
|
@@ -2385,10 +2572,10 @@ function GenreTreeView({
|
|
|
2385
2572
|
};
|
|
2386
2573
|
useEffect5(() => {
|
|
2387
2574
|
setUncontrolledCollapsed(false);
|
|
2388
|
-
}, [
|
|
2575
|
+
}, [selectedGenreUuid, artistUuid, songUuid]);
|
|
2389
2576
|
const [isWheelRevealed, setIsWheelRevealed] = useState9(false);
|
|
2390
2577
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2391
|
-
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2578
|
+
const isTreeReady = !isLoading && !isError && (!usesHandoff || isWheelRevealed);
|
|
2392
2579
|
const [resolveStyle, setResolveStyle] = useState9(null);
|
|
2393
2580
|
const handleNodeStyleResolverChange = useCallback6(
|
|
2394
2581
|
(resolver) => setResolveStyle(() => resolver),
|
|
@@ -2398,140 +2585,99 @@ function GenreTreeView({
|
|
|
2398
2585
|
const obscuredInset = useObscuredInset(panelRef);
|
|
2399
2586
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2400
2587
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2401
|
-
const treeNodes =
|
|
2588
|
+
const treeNodes = useMemo8(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2402
2589
|
const relations = useGenreNodeRelations(
|
|
2403
2590
|
treeNodes,
|
|
2404
2591
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2405
2592
|
resolveStyle
|
|
2406
2593
|
);
|
|
2407
|
-
const [selectionHistory, setSelectionHistory] = useState9({
|
|
2408
|
-
|
|
2409
|
-
previous:
|
|
2410
|
-
});
|
|
2411
|
-
if (selectionHistory.current !== selectedNodeId) {
|
|
2412
|
-
setSelectionHistory({
|
|
2413
|
-
current: selectedNodeId,
|
|
2414
|
-
previous: selectionHistory.current ?? selectionHistory.previous
|
|
2415
|
-
});
|
|
2594
|
+
const [selectionHistory, setSelectionHistory] = useState9({ current: selectedGenreUuid, previous: null });
|
|
2595
|
+
if (selectionHistory.current !== selectedGenreUuid) {
|
|
2596
|
+
setSelectionHistory({ current: selectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
2416
2597
|
}
|
|
2417
|
-
const
|
|
2418
|
-
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2598
|
+
const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
|
|
2419
2599
|
const { showPopup, hidePopup } = usePopup5();
|
|
2600
|
+
const reopenPanel = () => {
|
|
2601
|
+
setUncontrolledCollapsed(false);
|
|
2602
|
+
onCollapsedChange?.(false);
|
|
2603
|
+
};
|
|
2420
2604
|
const handleSelectedNodeChange = (node) => {
|
|
2605
|
+
reopenPanel();
|
|
2421
2606
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2422
2607
|
selectNode(node);
|
|
2423
2608
|
};
|
|
2424
2609
|
const handleGenreSearchClear = clearSelection;
|
|
2425
|
-
const handleGenreSearchSelect = (
|
|
2610
|
+
const handleGenreSearchSelect = (genrePlaylist2) => {
|
|
2611
|
+
reopenPanel();
|
|
2426
2612
|
if (songUuid !== null) changeSong(null);
|
|
2427
|
-
selectGenrePlaylist(
|
|
2613
|
+
selectGenrePlaylist(genrePlaylist2);
|
|
2428
2614
|
};
|
|
2429
2615
|
const selectedUuid = selectedGenrePlaylist?.uuid;
|
|
2430
|
-
const selectedChildren = selectedUuid ? visibleGenrePlaylists.filter((
|
|
2616
|
+
const selectedChildren = selectedUuid ? visibleGenrePlaylists.filter((genrePlaylist2) => genrePlaylist2.parent?.uuid === selectedUuid) : [];
|
|
2431
2617
|
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;
|
|
2432
2618
|
const handleGenreSearchZoomIn = () => {
|
|
2433
2619
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2434
2620
|
};
|
|
2435
2621
|
const zoomInRef = useRef3(void 0);
|
|
2436
2622
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2437
|
-
const
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
const
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
kind: "genre",
|
|
2467
|
-
genrePlaylistUuid: genrePlaylist.uuid,
|
|
2468
|
-
name: criteria.name,
|
|
2469
|
-
scope,
|
|
2470
|
-
artistUuid
|
|
2471
|
-
};
|
|
2472
|
-
const shareUrl = getShareUrlRef.current?.(shareTarget) ?? void 0;
|
|
2473
|
-
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find(
|
|
2474
|
-
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2475
|
-
) : void 0;
|
|
2476
|
-
return {
|
|
2477
|
-
primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2478
|
-
/* @__PURE__ */ jsx33(
|
|
2479
|
-
GenreInfoPanelBody,
|
|
2623
|
+
const genrePlaylist = selectedGenrePlaylist;
|
|
2624
|
+
const criteria = genrePlaylist?.criteria;
|
|
2625
|
+
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist?.parent?.uuid);
|
|
2626
|
+
const canToggleSide = !readOnly && criteria !== void 0 && criteria !== null && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2627
|
+
const side = criteria?.side ?? "core";
|
|
2628
|
+
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find((sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist?.uuid && sibling.criteria?.side === "pop") : void 0;
|
|
2629
|
+
const shareTarget = selectedGenreUuid ? { kind: "genre", genreUuid: selectedGenreUuid, name: criteria?.name ?? "Genre", scope, artistUuid } : void 0;
|
|
2630
|
+
const shareUrl = shareTarget ? getShareUrlRef.current?.(shareTarget) ?? void 0 : void 0;
|
|
2631
|
+
const genreContent = useGenreDetailContent({
|
|
2632
|
+
genreUuid: selectedGenreUuid,
|
|
2633
|
+
scope,
|
|
2634
|
+
getBackendBaseUrl,
|
|
2635
|
+
onSelectGenre: (uuid) => {
|
|
2636
|
+
reopenPanel();
|
|
2637
|
+
selectGenre(uuid);
|
|
2638
|
+
},
|
|
2639
|
+
expanded: isExpanded,
|
|
2640
|
+
playback,
|
|
2641
|
+
genrePlaylist: selectedGenrePlaylist,
|
|
2642
|
+
band: {
|
|
2643
|
+
...relations ? { node: relations.node } : {},
|
|
2644
|
+
shareTarget: shareUrl ? shareTarget : void 0,
|
|
2645
|
+
shareUrl,
|
|
2646
|
+
onZoomIn: zoomInRef.current,
|
|
2647
|
+
onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2648
|
+
onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2649
|
+
onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
|
|
2650
|
+
/* @__PURE__ */ jsx34(
|
|
2651
|
+
ChangeParentPopup,
|
|
2480
2652
|
{
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
ancestors
|
|
2484
|
-
] }),
|
|
2485
|
-
genreUuid,
|
|
2486
|
-
genrePlaylist,
|
|
2487
|
-
catalogue: genrePlaylist.catalogue,
|
|
2488
|
-
wikidataId: criteria.wikidataId,
|
|
2653
|
+
genrePlaylist: { ...genrePlaylist, criteria },
|
|
2654
|
+
genrePlaylists: visibleGenrePlaylists,
|
|
2489
2655
|
scope,
|
|
2490
2656
|
getBackendBaseUrl,
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
node,
|
|
2494
|
-
shareTarget: shareUrl ? shareTarget : void 0,
|
|
2495
|
-
shareUrl,
|
|
2496
|
-
onZoomIn: zoomInRef.current,
|
|
2497
|
-
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2498
|
-
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2499
|
-
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2500
|
-
/* @__PURE__ */ jsx33(
|
|
2501
|
-
ChangeParentPopup,
|
|
2502
|
-
{
|
|
2503
|
-
genrePlaylist: { ...genrePlaylist, criteria },
|
|
2504
|
-
genrePlaylists: visibleGenrePlaylists,
|
|
2505
|
-
scope,
|
|
2506
|
-
getBackendBaseUrl,
|
|
2507
|
-
onClose: hidePopup,
|
|
2508
|
-
onParentChanged
|
|
2509
|
-
}
|
|
2510
|
-
)
|
|
2511
|
-
),
|
|
2512
|
-
side,
|
|
2513
|
-
onToggleSide: canToggleSide ? () => updateGenre({ uuid: genreUuid, data: { side: side === "pop" ? "core" : "pop" } }) : void 0,
|
|
2514
|
-
toggleSideDisabledReason: popSibling ? `${popSibling.name} is already the pop side of ${parentPlaylist?.name}` : void 0,
|
|
2515
|
-
additionalActions
|
|
2516
|
-
},
|
|
2517
|
-
extras: {
|
|
2518
|
-
criteriaOverviewSchema,
|
|
2519
|
-
renderGenreDetailExtras,
|
|
2520
|
-
renderArtistLink,
|
|
2521
|
-
renderSongLink: resolvedRenderSongLink,
|
|
2522
|
-
artist: filterArtist,
|
|
2523
|
-
renderSummary,
|
|
2524
|
-
renderEssentialTracks,
|
|
2525
|
-
renderSongLeading,
|
|
2526
|
-
renderSongActions,
|
|
2527
|
-
renderTracksToolbar
|
|
2528
|
-
}
|
|
2657
|
+
onClose: hidePopup,
|
|
2658
|
+
onParentChanged
|
|
2529
2659
|
}
|
|
2530
|
-
)
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2660
|
+
)
|
|
2661
|
+
),
|
|
2662
|
+
side,
|
|
2663
|
+
onToggleSide: canToggleSide && criteria ? () => updateGenre({ uuid: criteria.uuid, data: { side: side === "pop" ? "core" : "pop" } }) : void 0,
|
|
2664
|
+
toggleSideDisabledReason: popSibling ? `${popSibling.name} is already the pop side of ${parentPlaylist?.name}` : void 0,
|
|
2665
|
+
additionalActions
|
|
2666
|
+
},
|
|
2667
|
+
extras: {
|
|
2668
|
+
criteriaOverviewSchema,
|
|
2669
|
+
renderGenreDetailExtras,
|
|
2670
|
+
renderArtistLink,
|
|
2671
|
+
renderSongLink: resolvedRenderSongLink,
|
|
2672
|
+
artist: filterArtist,
|
|
2673
|
+
renderSummary,
|
|
2674
|
+
renderEssentialTracks,
|
|
2675
|
+
renderSongLeading,
|
|
2676
|
+
renderSongActions,
|
|
2677
|
+
renderTracksToolbar
|
|
2678
|
+
},
|
|
2679
|
+
trailing: relations ? renderDangerZone?.(relations.node) : void 0
|
|
2680
|
+
});
|
|
2535
2681
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
2536
2682
|
const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
|
|
2537
2683
|
let activePanel = null;
|
|
@@ -2545,16 +2691,16 @@ function GenreTreeView({
|
|
|
2545
2691
|
onClose: overlayPanel.onClose,
|
|
2546
2692
|
compact: false
|
|
2547
2693
|
};
|
|
2548
|
-
} else if (
|
|
2549
|
-
const { primary } = renderGenreBody(relations);
|
|
2694
|
+
} else if (selectedGenreUuid) {
|
|
2550
2695
|
activePanel = {
|
|
2551
|
-
title:
|
|
2552
|
-
content:
|
|
2696
|
+
title: genreContent.title,
|
|
2697
|
+
content: genreContent.content,
|
|
2698
|
+
aside: genreContent.aside,
|
|
2553
2699
|
expanded: isExpanded,
|
|
2554
2700
|
onExpandedChange: changeExpanded,
|
|
2555
2701
|
onClose: clearSelection,
|
|
2556
|
-
compact:
|
|
2557
|
-
headerAccent: { background: relations.fill, color: relations.textColor }
|
|
2702
|
+
compact: false,
|
|
2703
|
+
headerAccent: relations ? { background: relations.fill, color: relations.textColor } : genreContent.headerAccent
|
|
2558
2704
|
};
|
|
2559
2705
|
} else if (builtInPanel) {
|
|
2560
2706
|
activePanel = {
|
|
@@ -2567,36 +2713,36 @@ function GenreTreeView({
|
|
|
2567
2713
|
compact: true
|
|
2568
2714
|
};
|
|
2569
2715
|
}
|
|
2570
|
-
const isPanelShown =
|
|
2716
|
+
const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
|
|
2571
2717
|
const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
|
|
2572
2718
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2573
2719
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2574
2720
|
} : void 0;
|
|
2575
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */
|
|
2576
|
-
|
|
2721
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx34(
|
|
2722
|
+
IconButton2,
|
|
2577
2723
|
{
|
|
2578
2724
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2579
2725
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2580
2726
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2581
|
-
children: activePanel.expanded ? /* @__PURE__ */
|
|
2727
|
+
children: activePanel.expanded ? /* @__PURE__ */ jsx34(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx34(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2582
2728
|
}
|
|
2583
2729
|
) : null;
|
|
2584
2730
|
const renderArtistFilter = () => {
|
|
2585
2731
|
if (isLoading || !filterArtist) return null;
|
|
2586
|
-
return /* @__PURE__ */
|
|
2587
|
-
/* @__PURE__ */
|
|
2732
|
+
return /* @__PURE__ */ jsxs26("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2733
|
+
/* @__PURE__ */ jsx34(
|
|
2588
2734
|
"button",
|
|
2589
2735
|
{
|
|
2590
2736
|
type: "button",
|
|
2591
2737
|
className: "py-1 pl-3 pr-2 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] hover:underline",
|
|
2592
2738
|
"aria-label": `Show artist ${filterArtist.name}`,
|
|
2593
2739
|
onClick: () => {
|
|
2594
|
-
if (
|
|
2740
|
+
if (selectedGenreUuid !== null) clearSelection();
|
|
2595
2741
|
},
|
|
2596
2742
|
children: filterArtist.name
|
|
2597
2743
|
}
|
|
2598
2744
|
),
|
|
2599
|
-
/* @__PURE__ */
|
|
2745
|
+
/* @__PURE__ */ jsx34(
|
|
2600
2746
|
"button",
|
|
2601
2747
|
{
|
|
2602
2748
|
type: "button",
|
|
@@ -2608,8 +2754,8 @@ function GenreTreeView({
|
|
|
2608
2754
|
)
|
|
2609
2755
|
] });
|
|
2610
2756
|
};
|
|
2611
|
-
const actions = /* @__PURE__ */
|
|
2612
|
-
!isLoading && /* @__PURE__ */
|
|
2757
|
+
const actions = /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
2758
|
+
!isLoading && !isError && /* @__PURE__ */ jsx34(
|
|
2613
2759
|
GenreTreeSearch,
|
|
2614
2760
|
{
|
|
2615
2761
|
artistBaseUrl,
|
|
@@ -2627,7 +2773,12 @@ function GenreTreeView({
|
|
|
2627
2773
|
SearchGroups,
|
|
2628
2774
|
genrePlaylists: visibleGenrePlaylists,
|
|
2629
2775
|
onSelect: handleGenreSearchSelect,
|
|
2630
|
-
|
|
2776
|
+
onSelectGenre: (uuid) => {
|
|
2777
|
+
reopenPanel();
|
|
2778
|
+
if (songUuid) changeSong(null);
|
|
2779
|
+
selectGenre(uuid);
|
|
2780
|
+
},
|
|
2781
|
+
selectedName: (selectedGenreUuid ? genreContent.title : null) ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2631
2782
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2632
2783
|
onPrevious: handlePrevious,
|
|
2633
2784
|
onQueryChange: onSearchQueryChange,
|
|
@@ -2635,9 +2786,9 @@ function GenreTreeView({
|
|
|
2635
2786
|
extraGroups: extraSearchGroups
|
|
2636
2787
|
}
|
|
2637
2788
|
),
|
|
2638
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2639
|
-
/* @__PURE__ */
|
|
2640
|
-
!isLoading && !isControlled && /* @__PURE__ */
|
|
2789
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ jsx34("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2790
|
+
/* @__PURE__ */ jsxs26("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: [
|
|
2791
|
+
!isLoading && !isError && !isControlled && /* @__PURE__ */ jsx34(
|
|
2641
2792
|
SegmentedControl2,
|
|
2642
2793
|
{
|
|
2643
2794
|
"aria-label": "Tree view mode",
|
|
@@ -2651,13 +2802,13 @@ function GenreTreeView({
|
|
|
2651
2802
|
]
|
|
2652
2803
|
}
|
|
2653
2804
|
),
|
|
2654
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */
|
|
2805
|
+
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ jsx34(
|
|
2655
2806
|
"div",
|
|
2656
2807
|
{
|
|
2657
2808
|
className: "flex items-center gap-1",
|
|
2658
2809
|
role: "group",
|
|
2659
2810
|
"aria-label": "Tree display options",
|
|
2660
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */
|
|
2811
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx34(
|
|
2661
2812
|
Button,
|
|
2662
2813
|
{
|
|
2663
2814
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2668,7 +2819,7 @@ function GenreTreeView({
|
|
|
2668
2819
|
)
|
|
2669
2820
|
}
|
|
2670
2821
|
),
|
|
2671
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */
|
|
2822
|
+
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ jsx34(
|
|
2672
2823
|
IconTextButton,
|
|
2673
2824
|
{
|
|
2674
2825
|
icon: Plus2,
|
|
@@ -2678,34 +2829,38 @@ function GenreTreeView({
|
|
|
2678
2829
|
)
|
|
2679
2830
|
] })
|
|
2680
2831
|
] });
|
|
2681
|
-
return /* @__PURE__ */
|
|
2832
|
+
return /* @__PURE__ */ jsxs26(
|
|
2682
2833
|
"div",
|
|
2683
2834
|
{
|
|
2684
2835
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2685
2836
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2686
2837
|
children: [
|
|
2687
|
-
/* @__PURE__ */
|
|
2838
|
+
/* @__PURE__ */ jsx34(
|
|
2688
2839
|
"div",
|
|
2689
2840
|
{
|
|
2690
2841
|
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"}`,
|
|
2691
2842
|
children: actions
|
|
2692
2843
|
}
|
|
2693
2844
|
),
|
|
2694
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2845
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ jsx34(
|
|
2695
2846
|
"div",
|
|
2696
2847
|
{
|
|
2697
2848
|
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"}`,
|
|
2698
2849
|
children: renderArtistFilter()
|
|
2699
2850
|
}
|
|
2700
2851
|
),
|
|
2701
|
-
/* @__PURE__ */
|
|
2702
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */
|
|
2703
|
-
isLoading ? /* @__PURE__ */
|
|
2852
|
+
/* @__PURE__ */ jsx34("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs26("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2853
|
+
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx34("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2854
|
+
isLoading ? /* @__PURE__ */ jsx34(GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ jsxs26("p", { role: "alert", className: "m-auto text-center", children: [
|
|
2855
|
+
"Couldn't load the genre tree.",
|
|
2856
|
+
" ",
|
|
2857
|
+
/* @__PURE__ */ jsx34("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
|
|
2858
|
+
] }) : viewMode === "wheel" ? (
|
|
2704
2859
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2705
2860
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2706
2861
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2707
2862
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2708
|
-
/* @__PURE__ */
|
|
2863
|
+
/* @__PURE__ */ jsx34("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx34(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx34(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx34(
|
|
2709
2864
|
GenrePlaylistTreeWheel,
|
|
2710
2865
|
{
|
|
2711
2866
|
scope,
|
|
@@ -2722,9 +2877,9 @@ function GenreTreeView({
|
|
|
2722
2877
|
allowWheelRotation,
|
|
2723
2878
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2724
2879
|
busy,
|
|
2725
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2880
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
|
|
2726
2881
|
floatingControlsLeading,
|
|
2727
|
-
canSizeByViews && /* @__PURE__ */
|
|
2882
|
+
canSizeByViews && /* @__PURE__ */ jsx34(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2728
2883
|
] }) : void 0,
|
|
2729
2884
|
defaultViewport,
|
|
2730
2885
|
viewportRestore,
|
|
@@ -2733,7 +2888,7 @@ function GenreTreeView({
|
|
|
2733
2888
|
) }) })
|
|
2734
2889
|
) : viewMode === "pop-core" ? (
|
|
2735
2890
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2736
|
-
/* @__PURE__ */
|
|
2891
|
+
/* @__PURE__ */ jsx34("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx34(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx34(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx34(
|
|
2737
2892
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2738
2893
|
{
|
|
2739
2894
|
scope,
|
|
@@ -2750,9 +2905,9 @@ function GenreTreeView({
|
|
|
2750
2905
|
allowWheelRotation,
|
|
2751
2906
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2752
2907
|
busy,
|
|
2753
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2908
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs26("div", { className: "flex items-center gap-2", children: [
|
|
2754
2909
|
floatingControlsLeading,
|
|
2755
|
-
canSizeByViews && /* @__PURE__ */
|
|
2910
|
+
canSizeByViews && /* @__PURE__ */ jsx34(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2756
2911
|
] }) : void 0,
|
|
2757
2912
|
defaultViewport,
|
|
2758
2913
|
viewportRestore,
|
|
@@ -2760,7 +2915,7 @@ function GenreTreeView({
|
|
|
2760
2915
|
...focus
|
|
2761
2916
|
}
|
|
2762
2917
|
) }) })
|
|
2763
|
-
) : viewMode === "outline" ? /* @__PURE__ */
|
|
2918
|
+
) : viewMode === "outline" ? /* @__PURE__ */ jsx34("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx34(
|
|
2764
2919
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2765
2920
|
{
|
|
2766
2921
|
outline: true,
|
|
@@ -2776,13 +2931,13 @@ function GenreTreeView({
|
|
|
2776
2931
|
selectedNodeId,
|
|
2777
2932
|
readOnly
|
|
2778
2933
|
}
|
|
2779
|
-
) }) : /* @__PURE__ */
|
|
2934
|
+
) }) : /* @__PURE__ */ jsx34("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(
|
|
2780
2935
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2781
|
-
return /* @__PURE__ */
|
|
2936
|
+
return /* @__PURE__ */ jsx34(
|
|
2782
2937
|
"div",
|
|
2783
2938
|
{
|
|
2784
2939
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2785
|
-
children: /* @__PURE__ */
|
|
2940
|
+
children: /* @__PURE__ */ jsx34("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx34(
|
|
2786
2941
|
GenrePlaylistTreePerRoot,
|
|
2787
2942
|
{
|
|
2788
2943
|
scope,
|
|
@@ -2808,8 +2963,8 @@ function GenreTreeView({
|
|
|
2808
2963
|
);
|
|
2809
2964
|
}
|
|
2810
2965
|
) }),
|
|
2811
|
-
/* @__PURE__ */
|
|
2812
|
-
|
|
2966
|
+
/* @__PURE__ */ jsx34(
|
|
2967
|
+
DetailPanel2,
|
|
2813
2968
|
{
|
|
2814
2969
|
ref: panelRef,
|
|
2815
2970
|
side: "left",
|
|
@@ -2819,13 +2974,13 @@ function GenreTreeView({
|
|
|
2819
2974
|
headerAccent: activePanel?.headerAccent,
|
|
2820
2975
|
headerActions: expandButton,
|
|
2821
2976
|
onClose: activePanel?.onClose ?? noop,
|
|
2822
|
-
collapsed: isCollapsed || !
|
|
2977
|
+
collapsed: isCollapsed || !isPanelShown,
|
|
2823
2978
|
expanded: activePanel?.expanded,
|
|
2824
2979
|
onExpandedChange: activePanel?.onExpandedChange,
|
|
2825
2980
|
aside: activePanel?.aside,
|
|
2826
2981
|
edgeTab: detailTab,
|
|
2827
|
-
headerClassName:
|
|
2828
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */
|
|
2982
|
+
headerClassName: "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]",
|
|
2983
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
2829
2984
|
activePanel.content,
|
|
2830
2985
|
activePanel.aside
|
|
2831
2986
|
] }) : activePanel?.content
|
|
@@ -2837,145 +2992,6 @@ function GenreTreeView({
|
|
|
2837
2992
|
);
|
|
2838
2993
|
}
|
|
2839
2994
|
|
|
2840
|
-
// src/genre-tree/GenreDetailPanel.tsx
|
|
2841
|
-
import { useMemo as useMemo8 } from "react";
|
|
2842
|
-
import {
|
|
2843
|
-
ACCENT_TEXT_COLOR,
|
|
2844
|
-
GenreNodeAncestors as GenreNodeAncestors2,
|
|
2845
|
-
GenreNodeFamily as GenreNodeFamily2,
|
|
2846
|
-
getGenreTreeColor as getGenreTreeColor2,
|
|
2847
|
-
TEXT_COLOR,
|
|
2848
|
-
tintSurface
|
|
2849
|
-
} from "@behindthemusictree/genre-tree-view";
|
|
2850
|
-
import { DetailPanel as DetailPanel2, IconButton as IconButton2, Skeleton as Skeleton7 } from "@behindthemusictree/ui";
|
|
2851
|
-
import { Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
|
|
2852
|
-
import {
|
|
2853
|
-
useFetchGenreDetail,
|
|
2854
|
-
genreMusicCount as genreMusicCount2,
|
|
2855
|
-
GenrePlaylistSummarySchema
|
|
2856
|
-
} from "@behindthemusictree/app-kit/genre-tree";
|
|
2857
|
-
import { Fragment as Fragment14, jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2858
|
-
var RELATIVE_ITEM_COUNT = 1;
|
|
2859
|
-
function toNode(genre, parentId) {
|
|
2860
|
-
return {
|
|
2861
|
-
id: genre.uuid,
|
|
2862
|
-
parentId,
|
|
2863
|
-
name: genre.name,
|
|
2864
|
-
itemCount: RELATIVE_ITEM_COUNT,
|
|
2865
|
-
side: genre.side ?? void 0,
|
|
2866
|
-
wikidataId: genre.wikidataId
|
|
2867
|
-
};
|
|
2868
|
-
}
|
|
2869
|
-
function GenreDetailPanel({
|
|
2870
|
-
genreUuid,
|
|
2871
|
-
scope,
|
|
2872
|
-
getBackendBaseUrl,
|
|
2873
|
-
onClose,
|
|
2874
|
-
onSelectGenre,
|
|
2875
|
-
onZoomIn,
|
|
2876
|
-
shareUrl,
|
|
2877
|
-
shareTarget,
|
|
2878
|
-
collapsed,
|
|
2879
|
-
expanded,
|
|
2880
|
-
onExpandedChange,
|
|
2881
|
-
edgeTab,
|
|
2882
|
-
extras
|
|
2883
|
-
}) {
|
|
2884
|
-
const { data: detail, isPending, isError } = useFetchGenreDetail(genreUuid, scope, getBackendBaseUrl);
|
|
2885
|
-
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
2886
|
-
const view = useMemo8(() => {
|
|
2887
|
-
if (!detail) return null;
|
|
2888
|
-
const rootColor = getGenreTreeColor2(detail.root.wikidataId);
|
|
2889
|
-
const relative = (genre, parentId) => ({
|
|
2890
|
-
node: toNode(genre, parentId),
|
|
2891
|
-
fill: tintSurface(rootColor),
|
|
2892
|
-
textColor: TEXT_COLOR
|
|
2893
|
-
});
|
|
2894
|
-
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
2895
|
-
return {
|
|
2896
|
-
node: {
|
|
2897
|
-
id: detail.uuid,
|
|
2898
|
-
parentId: detail.parent?.uuid ?? null,
|
|
2899
|
-
name: detail.name,
|
|
2900
|
-
itemCount: scope === "reference" ? genreMusicCount2(GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
|
|
2901
|
-
wikidataId: detail.wikidataId
|
|
2902
|
-
},
|
|
2903
|
-
fill: rootColor,
|
|
2904
|
-
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
2905
|
-
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
2906
|
-
ancestorNodes: ancestors2.map(
|
|
2907
|
-
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
2908
|
-
)
|
|
2909
|
-
};
|
|
2910
|
-
}, [detail, scope]);
|
|
2911
|
-
if (isError) {
|
|
2912
|
-
return /* @__PURE__ */ jsx34("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
2913
|
-
}
|
|
2914
|
-
if (isPending || !detail || !view) {
|
|
2915
|
-
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2916
|
-
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-6 w-2/3" }),
|
|
2917
|
-
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-4 w-full" }),
|
|
2918
|
-
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-4 w-3/4" })
|
|
2919
|
-
] });
|
|
2920
|
-
}
|
|
2921
|
-
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
2922
|
-
const family = /* @__PURE__ */ jsx34(
|
|
2923
|
-
GenreNodeFamily2,
|
|
2924
|
-
{
|
|
2925
|
-
node: view.node,
|
|
2926
|
-
fill: view.fill,
|
|
2927
|
-
textColor: ACCENT_TEXT_COLOR,
|
|
2928
|
-
parentNode: view.parentNode,
|
|
2929
|
-
childNodes: view.childNodes,
|
|
2930
|
-
onSelectNode: selectGenre
|
|
2931
|
-
}
|
|
2932
|
-
);
|
|
2933
|
-
const ancestors = /* @__PURE__ */ jsx34(GenreNodeAncestors2, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2934
|
-
return /* @__PURE__ */ jsxs26(
|
|
2935
|
-
DetailPanel2,
|
|
2936
|
-
{
|
|
2937
|
-
side: "left",
|
|
2938
|
-
title: view.node.name,
|
|
2939
|
-
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
2940
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ jsx34(
|
|
2941
|
-
IconButton2,
|
|
2942
|
-
{
|
|
2943
|
-
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
2944
|
-
style: { color: "inherit" },
|
|
2945
|
-
onClick: () => onExpandedChange(!expanded),
|
|
2946
|
-
children: expanded ? /* @__PURE__ */ jsx34(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx34(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2947
|
-
}
|
|
2948
|
-
) : void 0,
|
|
2949
|
-
onClose,
|
|
2950
|
-
collapsed,
|
|
2951
|
-
expanded,
|
|
2952
|
-
onExpandedChange,
|
|
2953
|
-
edgeTab,
|
|
2954
|
-
children: [
|
|
2955
|
-
/* @__PURE__ */ jsx34(
|
|
2956
|
-
GenreInfoPanelBody,
|
|
2957
|
-
{
|
|
2958
|
-
family: /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
2959
|
-
family,
|
|
2960
|
-
ancestors
|
|
2961
|
-
] }),
|
|
2962
|
-
genreUuid: detail.uuid,
|
|
2963
|
-
genrePlaylist: scope === "reference" ? GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
2964
|
-
catalogue: detail.catalogue,
|
|
2965
|
-
wikidataId: detail.wikidataId,
|
|
2966
|
-
scope,
|
|
2967
|
-
getBackendBaseUrl,
|
|
2968
|
-
playback,
|
|
2969
|
-
band: { node: view.node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
2970
|
-
extras
|
|
2971
|
-
}
|
|
2972
|
-
),
|
|
2973
|
-
!expanded && ancestors
|
|
2974
|
-
]
|
|
2975
|
-
}
|
|
2976
|
-
);
|
|
2977
|
-
}
|
|
2978
|
-
|
|
2979
2995
|
// src/genre-tree/index.ts
|
|
2980
2996
|
import {
|
|
2981
2997
|
GenreTreeSkeleton,
|
|
@@ -3433,10 +3449,10 @@ export {
|
|
|
3433
3449
|
GenreSearch,
|
|
3434
3450
|
GenreNodeActionBand,
|
|
3435
3451
|
GenreInfoPanelBody,
|
|
3452
|
+
GenreDetailPanel,
|
|
3436
3453
|
TrackPositionPlayPause,
|
|
3437
3454
|
TrackItem,
|
|
3438
3455
|
GenreTreeView,
|
|
3439
|
-
GenreDetailPanel,
|
|
3440
3456
|
TrackListSidebar,
|
|
3441
3457
|
PlayerPanel,
|
|
3442
3458
|
Rating,
|
|
@@ -3445,4 +3461,4 @@ export {
|
|
|
3445
3461
|
GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
|
|
3446
3462
|
GenreTreeOutlineSkeleton
|
|
3447
3463
|
};
|
|
3448
|
-
//# sourceMappingURL=chunk-
|
|
3464
|
+
//# sourceMappingURL=chunk-RTXLUHJY.js.map
|