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