@behindthemusictree/app-kit-ui 30.0.0 → 32.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-2HE2HDFA.js → chunk-QGGVPIXY.js} +546 -618
- package/dist/chunk-QGGVPIXY.js.map +1 -0
- package/dist/genre-tree/index.cjs +547 -625
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +37 -40
- package/dist/genre-tree/index.js +3 -7
- package/dist/index.cjs +547 -625
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -7
- package/package.json +6 -6
- package/dist/chunk-2HE2HDFA.js.map +0 -1
|
@@ -30,20 +30,19 @@ __export(genre_tree_exports, {
|
|
|
30
30
|
GenreTreeView: () => GenreTreeView,
|
|
31
31
|
GenreTreeViewSkeleton: () => import_genre_tree_view6.GenreTreeViewSkeleton,
|
|
32
32
|
GenreTreeWheelSkeleton: () => import_genre_tree_view6.GenreTreeWheelSkeleton,
|
|
33
|
-
PanelEdgeTab: () => PanelEdgeTab,
|
|
34
33
|
PlayerPanel: () => PlayerPanel,
|
|
35
34
|
Rating: () => Rating,
|
|
36
35
|
TrackItem: () => TrackItem,
|
|
37
36
|
TrackListSidebar: () => TrackListSidebar,
|
|
38
37
|
TrackPositionPlayPause: () => TrackPositionPlayPause,
|
|
39
|
-
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback
|
|
40
|
-
useLeftDetailPanelLayout: () => useLeftDetailPanelLayout
|
|
38
|
+
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback
|
|
41
39
|
});
|
|
42
40
|
module.exports = __toCommonJS(genre_tree_exports);
|
|
43
41
|
|
|
44
42
|
// src/genre-tree/GenreTreeView.tsx
|
|
45
|
-
var
|
|
43
|
+
var import_react16 = require("react");
|
|
46
44
|
var import_react_query3 = require("@tanstack/react-query");
|
|
45
|
+
var import_lucide_react11 = require("lucide-react");
|
|
47
46
|
var import_lucide_react12 = require("lucide-react");
|
|
48
47
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
49
48
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
@@ -51,6 +50,32 @@ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
51
50
|
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
52
51
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
53
52
|
|
|
53
|
+
// src/genre-tree/playlist-tree/size-metric.ts
|
|
54
|
+
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
55
|
+
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
56
|
+
if (metric === "songs") return genrePlaylist.tracksCount;
|
|
57
|
+
if (genrePlaylist.viewsCount === void 0) {
|
|
58
|
+
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
59
|
+
}
|
|
60
|
+
return genrePlaylist.viewsCount;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// src/genre-tree/playlist-tree/toTreeNodes.ts
|
|
64
|
+
function toTreeNodes(genrePlaylists, sizeMetric) {
|
|
65
|
+
return genrePlaylists.map((genrePlaylist) => ({
|
|
66
|
+
id: genrePlaylist.uuid,
|
|
67
|
+
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
68
|
+
name: genrePlaylist.name,
|
|
69
|
+
itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
|
|
70
|
+
actionable: Boolean(genrePlaylist.criteria),
|
|
71
|
+
detached: !genrePlaylist.criteria,
|
|
72
|
+
side: genrePlaylist.criteria?.side ?? void 0,
|
|
73
|
+
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
74
|
+
umbrella: genrePlaylist.isUmbrella,
|
|
75
|
+
scene: genrePlaylist.scene ?? void 0
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
55
80
|
var import_react3 = require("react");
|
|
56
81
|
var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
|
|
@@ -303,7 +328,6 @@ function useGenrePlaylistPlayback({
|
|
|
303
328
|
}
|
|
304
329
|
|
|
305
330
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
306
|
-
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
307
331
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
308
332
|
function GenrePlaylistTreePerRoot({
|
|
309
333
|
scope,
|
|
@@ -315,24 +339,19 @@ function GenrePlaylistTreePerRoot({
|
|
|
315
339
|
additionalActions,
|
|
316
340
|
onNodeClick,
|
|
317
341
|
onNodeHover,
|
|
318
|
-
renderInfoPanelBody,
|
|
319
342
|
selectedNodeId,
|
|
320
343
|
onSelectedNodeChange,
|
|
321
|
-
hideInfoPanelClose,
|
|
322
|
-
hideInfoPanelTitle,
|
|
323
344
|
readOnly = false,
|
|
324
|
-
showToolbar,
|
|
325
345
|
wheelZoom,
|
|
326
346
|
defaultViewport,
|
|
327
347
|
viewportRestore,
|
|
328
348
|
onViewportChange,
|
|
329
349
|
playback,
|
|
330
|
-
...
|
|
350
|
+
...hostPanel
|
|
331
351
|
}) {
|
|
332
352
|
const { isPlaying, setIsPlaying } = (0, import_player.usePlayer)();
|
|
333
353
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
|
|
334
354
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
335
|
-
const trackNoun = (0, import_track_noun.useTrackNoun)();
|
|
336
355
|
const { showPopup } = (0, import_popup.usePopup)();
|
|
337
356
|
const nodes = (0, import_react3.useMemo)(
|
|
338
357
|
() => genrePlaylistTreePerRoot.map((genrePlaylist) => ({
|
|
@@ -397,7 +416,6 @@ function GenrePlaylistTreePerRoot({
|
|
|
397
416
|
{
|
|
398
417
|
className: className2,
|
|
399
418
|
nodes,
|
|
400
|
-
itemCountLabel: `${(0, import_track_noun.capitalize)(trackNoun.singular)} count`,
|
|
401
419
|
rootColor: (0, import_genre_tree_view.getGenreTreeColor)(rootUuid),
|
|
402
420
|
playingNodeId,
|
|
403
421
|
playState: isPlaying ? "playing" : "paused",
|
|
@@ -408,18 +426,13 @@ function GenrePlaylistTreePerRoot({
|
|
|
408
426
|
additionalActions,
|
|
409
427
|
onNodeClick,
|
|
410
428
|
onNodeHover,
|
|
411
|
-
renderInfoPanelBody,
|
|
412
429
|
selectedNodeId,
|
|
413
430
|
onSelectedNodeChange,
|
|
414
|
-
hideInfoPanelClose,
|
|
415
|
-
hideInfoPanelTitle,
|
|
416
|
-
showToolbar,
|
|
417
431
|
wheelZoom,
|
|
418
432
|
defaultViewport,
|
|
419
433
|
viewportRestore,
|
|
420
434
|
onViewportChange,
|
|
421
|
-
...
|
|
422
|
-
readOnly
|
|
435
|
+
...hostPanel
|
|
423
436
|
}
|
|
424
437
|
);
|
|
425
438
|
}
|
|
@@ -430,19 +443,6 @@ var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
|
|
|
430
443
|
var import_player2 = require("@behindthemusictree/app-kit/player");
|
|
431
444
|
var import_popup2 = require("@behindthemusictree/app-kit/popup");
|
|
432
445
|
var import_transport2 = require("@behindthemusictree/app-kit/transport");
|
|
433
|
-
|
|
434
|
-
// src/genre-tree/playlist-tree/size-metric.ts
|
|
435
|
-
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
436
|
-
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
437
|
-
if (metric === "songs") return genrePlaylist.tracksCount;
|
|
438
|
-
if (genrePlaylist.viewsCount === void 0) {
|
|
439
|
-
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
440
|
-
}
|
|
441
|
-
return genrePlaylist.viewsCount;
|
|
442
|
-
};
|
|
443
|
-
|
|
444
|
-
// src/genre-tree/playlist-tree/TreeWheel.tsx
|
|
445
|
-
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
446
446
|
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
447
447
|
function GenrePlaylistTreeWheel({
|
|
448
448
|
scope,
|
|
@@ -453,14 +453,10 @@ function GenrePlaylistTreeWheel({
|
|
|
453
453
|
additionalActions,
|
|
454
454
|
onNodeClick,
|
|
455
455
|
onNodeHover,
|
|
456
|
-
renderInfoPanelBody,
|
|
457
456
|
selectedNodeId,
|
|
458
457
|
onSelectedNodeChange,
|
|
459
|
-
hideInfoPanelClose,
|
|
460
|
-
hideInfoPanelTitle,
|
|
461
458
|
readOnly = false,
|
|
462
459
|
allowWheelRotation,
|
|
463
|
-
showToolbar,
|
|
464
460
|
sizeMetric = "songs",
|
|
465
461
|
floatingControlsLeading,
|
|
466
462
|
busy,
|
|
@@ -468,12 +464,11 @@ function GenrePlaylistTreeWheel({
|
|
|
468
464
|
viewportRestore,
|
|
469
465
|
onViewportChange,
|
|
470
466
|
playback,
|
|
471
|
-
...
|
|
467
|
+
...hostPanel
|
|
472
468
|
}) {
|
|
473
469
|
const { isPlaying, setIsPlaying } = (0, import_player2.usePlayer)();
|
|
474
470
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
|
|
475
471
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
476
|
-
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
477
472
|
const { showPopup } = (0, import_popup2.usePopup)();
|
|
478
473
|
const nodes = (0, import_react4.useMemo)(
|
|
479
474
|
() => genrePlaylists.map((genrePlaylist) => ({
|
|
@@ -537,7 +532,6 @@ function GenrePlaylistTreeWheel({
|
|
|
537
532
|
{
|
|
538
533
|
className: className2,
|
|
539
534
|
nodes,
|
|
540
|
-
itemCountLabel: sizeMetric === "views" ? "Views" : `${(0, import_track_noun2.capitalize)(trackNoun.singular)} count`,
|
|
541
535
|
playingNodeId,
|
|
542
536
|
playState: isPlaying ? "playing" : "paused",
|
|
543
537
|
onPlayPause: handlePlayPause,
|
|
@@ -547,20 +541,15 @@ function GenrePlaylistTreeWheel({
|
|
|
547
541
|
additionalActions,
|
|
548
542
|
onNodeClick,
|
|
549
543
|
onNodeHover,
|
|
550
|
-
renderInfoPanelBody,
|
|
551
544
|
selectedNodeId,
|
|
552
545
|
onSelectedNodeChange,
|
|
553
|
-
hideInfoPanelClose,
|
|
554
|
-
hideInfoPanelTitle,
|
|
555
546
|
allowWheelRotation,
|
|
556
|
-
showToolbar,
|
|
557
547
|
floatingControlsLeading,
|
|
558
548
|
busy,
|
|
559
549
|
defaultViewport,
|
|
560
550
|
viewportRestore,
|
|
561
551
|
onViewportChange,
|
|
562
|
-
...
|
|
563
|
-
readOnly
|
|
552
|
+
...hostPanel
|
|
564
553
|
}
|
|
565
554
|
);
|
|
566
555
|
}
|
|
@@ -569,7 +558,6 @@ function GenrePlaylistTreeWheel({
|
|
|
569
558
|
var import_react5 = require("react");
|
|
570
559
|
var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
|
|
571
560
|
var import_player3 = require("@behindthemusictree/app-kit/player");
|
|
572
|
-
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
573
561
|
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
574
562
|
function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
575
563
|
const {
|
|
@@ -581,14 +569,10 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
581
569
|
additionalActions,
|
|
582
570
|
onNodeClick,
|
|
583
571
|
onNodeHover,
|
|
584
|
-
renderInfoPanelBody,
|
|
585
572
|
selectedNodeId,
|
|
586
573
|
onSelectedNodeChange,
|
|
587
|
-
hideInfoPanelClose,
|
|
588
|
-
hideInfoPanelTitle,
|
|
589
574
|
readOnly = false,
|
|
590
575
|
allowWheelRotation,
|
|
591
|
-
showToolbar,
|
|
592
576
|
sizeMetric = "songs",
|
|
593
577
|
floatingControlsLeading,
|
|
594
578
|
busy,
|
|
@@ -599,33 +583,14 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
599
583
|
playback
|
|
600
584
|
} = props;
|
|
601
585
|
const focus = props;
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
onExpandedChange: props.onExpandedChange,
|
|
606
|
-
collapsed: props.collapsed,
|
|
607
|
-
hideExpandButton: props.hideExpandButton,
|
|
608
|
-
overlayPanel: props.overlayPanel
|
|
586
|
+
const hostPanel = {
|
|
587
|
+
obscuredInset: props.obscuredInset,
|
|
588
|
+
onNodeStyleResolverChange: props.onNodeStyleResolverChange
|
|
609
589
|
};
|
|
610
590
|
const { isPlaying, setIsPlaying } = (0, import_player3.usePlayer)();
|
|
611
591
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
|
|
612
592
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
613
|
-
const
|
|
614
|
-
const nodes = (0, import_react5.useMemo)(
|
|
615
|
-
() => genrePlaylists.map((genrePlaylist) => ({
|
|
616
|
-
id: genrePlaylist.uuid,
|
|
617
|
-
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
618
|
-
name: genrePlaylist.name,
|
|
619
|
-
itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
|
|
620
|
-
actionable: Boolean(genrePlaylist.criteria),
|
|
621
|
-
detached: !genrePlaylist.criteria,
|
|
622
|
-
side: genrePlaylist.criteria?.side ?? void 0,
|
|
623
|
-
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
624
|
-
umbrella: genrePlaylist.isUmbrella,
|
|
625
|
-
scene: genrePlaylist.scene ?? void 0
|
|
626
|
-
})),
|
|
627
|
-
[genrePlaylists, sizeMetric]
|
|
628
|
-
);
|
|
593
|
+
const nodes = (0, import_react5.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
|
|
629
594
|
const handlePlayPause = (0, import_react5.useCallback)(
|
|
630
595
|
(nodeId) => {
|
|
631
596
|
const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
|
|
@@ -666,7 +631,6 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
666
631
|
const treeProps = {
|
|
667
632
|
className: className2,
|
|
668
633
|
nodes,
|
|
669
|
-
itemCountLabel: sizeMetric === "views" ? "Views" : `${(0, import_track_noun3.capitalize)(trackNoun.singular)} count`,
|
|
670
634
|
playingNodeId,
|
|
671
635
|
playState: isPlaying ? "playing" : "paused",
|
|
672
636
|
onPlayPause: handlePlayPause,
|
|
@@ -676,14 +640,9 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
676
640
|
additionalActions,
|
|
677
641
|
onNodeClick,
|
|
678
642
|
onNodeHover,
|
|
679
|
-
renderInfoPanelBody,
|
|
680
643
|
selectedNodeId,
|
|
681
644
|
onSelectedNodeChange,
|
|
682
|
-
|
|
683
|
-
hideInfoPanelTitle,
|
|
684
|
-
showToolbar,
|
|
685
|
-
...infoPanelCuration,
|
|
686
|
-
readOnly
|
|
645
|
+
...hostPanel
|
|
687
646
|
};
|
|
688
647
|
return outline ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
689
648
|
import_genre_tree_view3.GenreTreeWheelRadialPopCore,
|
|
@@ -803,7 +762,7 @@ function GenreSearch({
|
|
|
803
762
|
type: "button",
|
|
804
763
|
"aria-label": "Previous",
|
|
805
764
|
title: "Previous",
|
|
806
|
-
className: "genre-search-previous absolute left-2 top-1/2 z-10
|
|
765
|
+
className: "genre-search-previous absolute left-2 top-1/2 z-10 flex items-center justify-center p-1.5 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full -translate-y-1/2 hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
807
766
|
onClick: onPrevious,
|
|
808
767
|
children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react3.ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
809
768
|
}
|
|
@@ -825,7 +784,7 @@ function GenreSearch({
|
|
|
825
784
|
onPick: handlePick,
|
|
826
785
|
status,
|
|
827
786
|
loading: loading ? "Searching\u2026" : void 0,
|
|
828
|
-
inputClassName:
|
|
787
|
+
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md`,
|
|
829
788
|
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
830
789
|
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
831
790
|
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
@@ -1289,7 +1248,7 @@ function MusicBrainzRecordingLink({
|
|
|
1289
1248
|
|
|
1290
1249
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1291
1250
|
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1292
|
-
var
|
|
1251
|
+
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
1293
1252
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1294
1253
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1295
1254
|
var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree6.SongDetailedSchema).extend({
|
|
@@ -1323,7 +1282,7 @@ function GenrePlaylistTracks({
|
|
|
1323
1282
|
const { session, sessionRestored } = (0, import_auth.useSession)();
|
|
1324
1283
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1325
1284
|
const { showPopup } = (0, import_popup4.usePopup)();
|
|
1326
|
-
const trackNoun = (0,
|
|
1285
|
+
const trackNoun = (0, import_track_noun.useTrackNoun)();
|
|
1327
1286
|
const { uuid } = genrePlaylist;
|
|
1328
1287
|
const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
|
|
1329
1288
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
@@ -1344,9 +1303,8 @@ function GenrePlaylistTracks({
|
|
|
1344
1303
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1345
1304
|
enabled: scope !== "me" || sessionRestored && !!session?.accessToken
|
|
1346
1305
|
});
|
|
1347
|
-
const
|
|
1306
|
+
const [sentinel, setSentinel] = (0, import_react11.useState)(null);
|
|
1348
1307
|
(0, import_react11.useEffect)(() => {
|
|
1349
|
-
const sentinel = sentinelRef.current;
|
|
1350
1308
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1351
1309
|
const observer = new IntersectionObserver((entries) => {
|
|
1352
1310
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
@@ -1355,7 +1313,7 @@ function GenrePlaylistTracks({
|
|
|
1355
1313
|
});
|
|
1356
1314
|
observer.observe(sentinel);
|
|
1357
1315
|
return () => observer.disconnect();
|
|
1358
|
-
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
1316
|
+
}, [hasNextPage, isFetchingNextPage, fetchNextPage, sentinel]);
|
|
1359
1317
|
const pages = data?.pages ?? [];
|
|
1360
1318
|
const tracks = pages.flatMap((page) => page.tracks);
|
|
1361
1319
|
const total = pages[0]?.total ?? void 0;
|
|
@@ -1366,11 +1324,7 @@ function GenrePlaylistTracks({
|
|
|
1366
1324
|
showPopup(/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
|
|
1367
1325
|
});
|
|
1368
1326
|
};
|
|
1369
|
-
return /* @__PURE__ */ (0, import_jsx_runtime18.
|
|
1370
|
-
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: "gtv-info-panel-children-title", children: [
|
|
1371
|
-
(0, import_track_noun4.capitalize)(trackNoun.plural),
|
|
1372
|
-
total !== void 0 && ` (${total})`
|
|
1373
|
-
] }),
|
|
1327
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ui4.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { children: [
|
|
1374
1328
|
renderTracksToolbar?.(),
|
|
1375
1329
|
isPending ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_ui4.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("p", { role: "alert", children: [
|
|
1376
1330
|
"Couldn't load ",
|
|
@@ -1433,13 +1387,13 @@ function GenrePlaylistTracks({
|
|
|
1433
1387
|
track.uuid
|
|
1434
1388
|
);
|
|
1435
1389
|
}),
|
|
1436
|
-
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { ref:
|
|
1390
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1437
1391
|
] })
|
|
1438
|
-
] });
|
|
1392
|
+
] }) });
|
|
1439
1393
|
}
|
|
1440
1394
|
|
|
1441
1395
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1442
|
-
var
|
|
1396
|
+
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1443
1397
|
var import_si2 = require("react-icons/si");
|
|
1444
1398
|
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1445
1399
|
function GenreDetailExtras({
|
|
@@ -1457,7 +1411,7 @@ function GenreDetailExtras({
|
|
|
1457
1411
|
family,
|
|
1458
1412
|
...trackSlots
|
|
1459
1413
|
}) {
|
|
1460
|
-
const trackNoun = (0,
|
|
1414
|
+
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1461
1415
|
const { data: overview, isPending } = (0, import_genre_tree9.useFetchGenreOverview)(
|
|
1462
1416
|
genreUuid,
|
|
1463
1417
|
scope,
|
|
@@ -1466,10 +1420,7 @@ function GenreDetailExtras({
|
|
|
1466
1420
|
);
|
|
1467
1421
|
if (isPending) {
|
|
1468
1422
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
1469
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.
|
|
1470
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "gtv-info-panel-children-title", children: "Summary" }),
|
|
1471
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ui5.Skeleton, { className: "h-4 w-3/4" })
|
|
1472
|
-
] }),
|
|
1423
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ui5.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ui5.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1473
1424
|
family
|
|
1474
1425
|
] });
|
|
1475
1426
|
}
|
|
@@ -1477,50 +1428,38 @@ function GenreDetailExtras({
|
|
|
1477
1428
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1478
1429
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1479
1430
|
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
1480
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "
|
|
1481
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
] })
|
|
1510
|
-
] }),
|
|
1511
|
-
renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "gtv-info-panel-children text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: [
|
|
1512
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "gtv-info-panel-children-title", children: "Summary" }),
|
|
1513
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ClampedText, { className: "mt-1", children: summary ?? "\u2014" }, genreUuid)
|
|
1514
|
-
] }),
|
|
1431
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ui5.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1432
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1433
|
+
"a",
|
|
1434
|
+
{
|
|
1435
|
+
href: summarySourceUrl,
|
|
1436
|
+
target: "_blank",
|
|
1437
|
+
rel: "noopener noreferrer",
|
|
1438
|
+
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1439
|
+
children: [
|
|
1440
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1441
|
+
"Wikipedia"
|
|
1442
|
+
]
|
|
1443
|
+
}
|
|
1444
|
+
),
|
|
1445
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1446
|
+
"a",
|
|
1447
|
+
{
|
|
1448
|
+
href: wikidataUrl,
|
|
1449
|
+
target: "_blank",
|
|
1450
|
+
rel: "noopener noreferrer",
|
|
1451
|
+
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1452
|
+
children: [
|
|
1453
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1454
|
+
"Wikidata"
|
|
1455
|
+
]
|
|
1456
|
+
}
|
|
1457
|
+
)
|
|
1458
|
+
] }) }),
|
|
1459
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ui5.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1515
1460
|
family,
|
|
1516
1461
|
renderGenreDetailExtras?.(overview),
|
|
1517
|
-
renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && /* @__PURE__ */ (0, import_jsx_runtime19.
|
|
1518
|
-
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "gtv-info-panel-children-title", children: [
|
|
1519
|
-
"Essential ",
|
|
1520
|
-
trackNoun.plural
|
|
1521
|
-
] }),
|
|
1522
|
-
essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "mt-1 divide-y divide-[var(--color-border)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "mt-1", children: "\u2014" })
|
|
1523
|
-
] }),
|
|
1462
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_ui5.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1524
1463
|
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
|
|
1525
1464
|
GenrePlaylistTracks,
|
|
1526
1465
|
{
|
|
@@ -1591,66 +1530,10 @@ function GenreInfoPanelBody({
|
|
|
1591
1530
|
] });
|
|
1592
1531
|
}
|
|
1593
1532
|
|
|
1594
|
-
// src/genre-tree/PanelEdgeTab.tsx
|
|
1595
|
-
var import_lucide_react8 = require("lucide-react");
|
|
1596
|
-
var import_tailwind_merge = require("tailwind-merge");
|
|
1597
|
-
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1598
|
-
var buttonClassName = "flex h-12 w-full items-center justify-center p-0 text-[var(--color-text-muted)] hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)] focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--color-primary)]";
|
|
1599
|
-
function PanelEdgeTab({ side, edge, center, controls, className: className2 }) {
|
|
1600
|
-
if (!edge && !center) return null;
|
|
1601
|
-
const isLeft = side === "left";
|
|
1602
|
-
const EdgeIcon = isLeft ? import_lucide_react8.ChevronLeft : import_lucide_react8.ChevronRight;
|
|
1603
|
-
const CenterIcon = isLeft ? import_lucide_react8.ChevronRight : import_lucide_react8.ChevronLeft;
|
|
1604
|
-
const renderButton = (action, icon) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1605
|
-
"button",
|
|
1606
|
-
{
|
|
1607
|
-
type: "button",
|
|
1608
|
-
"aria-label": action.label,
|
|
1609
|
-
title: action.label,
|
|
1610
|
-
"aria-controls": controls,
|
|
1611
|
-
"aria-expanded": action.expanded,
|
|
1612
|
-
className: buttonClassName,
|
|
1613
|
-
onClick: action.onClick,
|
|
1614
|
-
children: icon
|
|
1615
|
-
}
|
|
1616
|
-
);
|
|
1617
|
-
const edgeButton = edge && renderButton(edge, /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(EdgeIcon, { "aria-hidden": "true", className: "h-5 w-5" }));
|
|
1618
|
-
const centerButton = center && renderButton(center, /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(CenterIcon, { "aria-hidden": "true", className: "h-5 w-5" }));
|
|
1619
|
-
const buttons = isLeft ? [edgeButton, centerButton] : [centerButton, edgeButton];
|
|
1620
|
-
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
1621
|
-
"div",
|
|
1622
|
-
{
|
|
1623
|
-
className: (0, import_tailwind_merge.twMerge)(
|
|
1624
|
-
"panel-edge-tab flex w-[var(--layout-edge-tab-w)] flex-col overflow-hidden bg-[var(--color-surface)] shadow-[0_1px_2px_rgba(60,64,67,0.3),0_2px_6px_2px_rgba(60,64,67,0.15)] [clip-path:inset(-0.5rem_-0.5rem_-0.5rem_0)]",
|
|
1625
|
-
isLeft ? "rounded-r-lg" : "rounded-l-lg [clip-path:inset(-0.5rem_0_-0.5rem_-0.5rem)]",
|
|
1626
|
-
className2
|
|
1627
|
-
),
|
|
1628
|
-
children: [
|
|
1629
|
-
buttons[0],
|
|
1630
|
-
buttons[0] && buttons[1] && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { role: "presentation", className: "h-px shrink-0 bg-[var(--color-border)]" }),
|
|
1631
|
-
buttons[1]
|
|
1632
|
-
]
|
|
1633
|
-
}
|
|
1634
|
-
);
|
|
1635
|
-
}
|
|
1636
|
-
|
|
1637
|
-
// src/genre-tree/useLeftDetailPanelLayout.ts
|
|
1638
|
-
var import_react13 = require("react");
|
|
1639
|
-
function useLeftDetailPanelLayout(isPanelShown, enabled = true) {
|
|
1640
|
-
(0, import_react13.useEffect)(() => {
|
|
1641
|
-
if (!enabled || isPanelShown) return void 0;
|
|
1642
|
-
const root = document.documentElement;
|
|
1643
|
-
root.style.setProperty("--layout-left-w", "0px");
|
|
1644
|
-
return () => {
|
|
1645
|
-
root.style.removeProperty("--layout-left-w");
|
|
1646
|
-
};
|
|
1647
|
-
}, [enabled, isPanelShown]);
|
|
1648
|
-
}
|
|
1649
|
-
|
|
1650
1533
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1651
|
-
var
|
|
1534
|
+
var import_react13 = require("react");
|
|
1652
1535
|
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1653
|
-
var
|
|
1536
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1654
1537
|
function ChangeParentPopup({
|
|
1655
1538
|
genrePlaylist,
|
|
1656
1539
|
genrePlaylists,
|
|
@@ -1660,7 +1543,7 @@ function ChangeParentPopup({
|
|
|
1660
1543
|
onParentChanged
|
|
1661
1544
|
}) {
|
|
1662
1545
|
const { mutate, isPending, formErrors } = (0, import_genre_tree10.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1663
|
-
const candidates = (0,
|
|
1546
|
+
const candidates = (0, import_react13.useMemo)(
|
|
1664
1547
|
() => (0, import_genre_tree10.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1665
1548
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1666
1549
|
);
|
|
@@ -1678,19 +1561,19 @@ function ChangeParentPopup({
|
|
|
1678
1561
|
}
|
|
1679
1562
|
);
|
|
1680
1563
|
};
|
|
1681
|
-
(0,
|
|
1564
|
+
(0, import_react13.useEffect)(() => {
|
|
1682
1565
|
const handleKeyDown = (event) => {
|
|
1683
1566
|
if (event.key === "Escape") onClose();
|
|
1684
1567
|
};
|
|
1685
1568
|
window.addEventListener("keydown", handleKeyDown);
|
|
1686
1569
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1687
1570
|
}, [onClose]);
|
|
1688
|
-
return /* @__PURE__ */ (0,
|
|
1689
|
-
/* @__PURE__ */ (0,
|
|
1571
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1572
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1690
1573
|
"Current parent: ",
|
|
1691
1574
|
genrePlaylist.parent?.name ?? "none"
|
|
1692
1575
|
] }),
|
|
1693
|
-
/* @__PURE__ */ (0,
|
|
1576
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1694
1577
|
GenreSearch,
|
|
1695
1578
|
{
|
|
1696
1579
|
genrePlaylists: candidates,
|
|
@@ -1698,7 +1581,7 @@ function ChangeParentPopup({
|
|
|
1698
1581
|
placeholder: "Search a new parent\u2026"
|
|
1699
1582
|
}
|
|
1700
1583
|
) }),
|
|
1701
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1584
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1702
1585
|
] });
|
|
1703
1586
|
}
|
|
1704
1587
|
|
|
@@ -1710,12 +1593,12 @@ var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
1710
1593
|
var import_ui7 = require("@behindthemusictree/ui");
|
|
1711
1594
|
|
|
1712
1595
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
1713
|
-
var
|
|
1596
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1714
1597
|
var import_si3 = require("react-icons/si");
|
|
1715
1598
|
var import_ui6 = require("@behindthemusictree/ui");
|
|
1716
|
-
var
|
|
1599
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1717
1600
|
var LINK_KINDS = {
|
|
1718
|
-
official: { label: "Official website", Icon:
|
|
1601
|
+
official: { label: "Official website", Icon: import_lucide_react8.Globe },
|
|
1719
1602
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
1720
1603
|
wikidata: { label: "Wikidata", Icon: import_si3.SiWikidata },
|
|
1721
1604
|
spotify: { label: "Spotify", Icon: import_si3.SiSpotify },
|
|
@@ -1723,7 +1606,7 @@ var LINK_KINDS = {
|
|
|
1723
1606
|
bandcamp: { label: "Bandcamp", Icon: import_si3.SiBandcamp },
|
|
1724
1607
|
soundcloud: { label: "SoundCloud", Icon: import_si3.SiSoundcloud },
|
|
1725
1608
|
discogs: { label: "Discogs", Icon: import_si3.SiDiscogs },
|
|
1726
|
-
social: { label: "Social", Icon:
|
|
1609
|
+
social: { label: "Social", Icon: import_lucide_react8.Share2 }
|
|
1727
1610
|
};
|
|
1728
1611
|
function socialLabel(url) {
|
|
1729
1612
|
try {
|
|
@@ -1750,7 +1633,7 @@ function artistLinks(artist) {
|
|
|
1750
1633
|
return links;
|
|
1751
1634
|
}
|
|
1752
1635
|
function ArtistLinks({ links }) {
|
|
1753
|
-
return /* @__PURE__ */ (0,
|
|
1636
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_ui6.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1754
1637
|
}
|
|
1755
1638
|
|
|
1756
1639
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1761,7 +1644,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1761
1644
|
}
|
|
1762
1645
|
|
|
1763
1646
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1764
|
-
var
|
|
1647
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1765
1648
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1766
1649
|
function ArtistHeader({ artist }) {
|
|
1767
1650
|
const fields = [];
|
|
@@ -1771,35 +1654,35 @@ function ArtistHeader({ artist }) {
|
|
|
1771
1654
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1772
1655
|
if (years) fields.push({ label: "Active", value: years });
|
|
1773
1656
|
const links = artistLinks(artist);
|
|
1774
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
1657
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(ArtistLinks, { links }) });
|
|
1775
1658
|
if (fields.length === 0) return null;
|
|
1776
|
-
return /* @__PURE__ */ (0,
|
|
1659
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui7.FieldList, { items: fields });
|
|
1777
1660
|
}
|
|
1778
1661
|
|
|
1779
1662
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1780
|
-
var
|
|
1663
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1781
1664
|
function ExternalLink({ href, children }) {
|
|
1782
|
-
return /* @__PURE__ */ (0,
|
|
1665
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1783
1666
|
}
|
|
1784
1667
|
function ArtistPhoto({ image, name }) {
|
|
1785
|
-
return /* @__PURE__ */ (0,
|
|
1786
|
-
/* @__PURE__ */ (0,
|
|
1787
|
-
/* @__PURE__ */ (0,
|
|
1668
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1669
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1670
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1788
1671
|
"Photo: ",
|
|
1789
|
-
/* @__PURE__ */ (0,
|
|
1672
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1790
1673
|
",",
|
|
1791
1674
|
" ",
|
|
1792
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
1675
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1793
1676
|
] })
|
|
1794
1677
|
] });
|
|
1795
1678
|
}
|
|
1796
1679
|
|
|
1797
1680
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1798
1681
|
var import_ui8 = require("@behindthemusictree/ui");
|
|
1799
|
-
var
|
|
1682
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1800
1683
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1801
1684
|
if (memberships.length === 0) return null;
|
|
1802
|
-
return /* @__PURE__ */ (0,
|
|
1685
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
1803
1686
|
const details = [
|
|
1804
1687
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1805
1688
|
...membership.roles
|
|
@@ -1807,9 +1690,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1807
1690
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1808
1691
|
return (
|
|
1809
1692
|
// A member can have several periods in the same group.
|
|
1810
|
-
/* @__PURE__ */ (0,
|
|
1811
|
-
/* @__PURE__ */ (0,
|
|
1812
|
-
detailsText && /* @__PURE__ */ (0,
|
|
1693
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1694
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1695
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1813
1696
|
" \u2014 ",
|
|
1814
1697
|
detailsText
|
|
1815
1698
|
] })
|
|
@@ -1820,9 +1703,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1820
1703
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1821
1704
|
if (memberships.length === 0) return null;
|
|
1822
1705
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1823
|
-
return /* @__PURE__ */ (0,
|
|
1824
|
-
/* @__PURE__ */ (0,
|
|
1825
|
-
/* @__PURE__ */ (0,
|
|
1706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_ui8.PanelSection, { title, children: [
|
|
1707
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1826
1709
|
MembershipList,
|
|
1827
1710
|
{
|
|
1828
1711
|
title: "Past",
|
|
@@ -1835,7 +1718,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1835
1718
|
|
|
1836
1719
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1837
1720
|
var import_ui9 = require("@behindthemusictree/ui");
|
|
1838
|
-
var
|
|
1721
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1839
1722
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1840
1723
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1841
1724
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1855,20 +1738,20 @@ function byYear(a, b) {
|
|
|
1855
1738
|
function ArtistDiscography({ albums }) {
|
|
1856
1739
|
if (albums.length === 0) return null;
|
|
1857
1740
|
const sorted = [...albums].sort(byYear);
|
|
1858
|
-
return /* @__PURE__ */ (0,
|
|
1741
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui9.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
1859
1742
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1860
1743
|
if (groupAlbums.length === 0) return null;
|
|
1861
|
-
return /* @__PURE__ */ (0,
|
|
1862
|
-
/* @__PURE__ */ (0,
|
|
1863
|
-
/* @__PURE__ */ (0,
|
|
1744
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_ui9.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1745
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1746
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1864
1747
|
] }, album.uuid)) }) }, group);
|
|
1865
1748
|
}) });
|
|
1866
1749
|
}
|
|
1867
1750
|
|
|
1868
1751
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1869
|
-
var
|
|
1752
|
+
var import_react14 = require("react");
|
|
1870
1753
|
var import_react_query2 = require("@tanstack/react-query");
|
|
1871
|
-
var
|
|
1754
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1872
1755
|
var import_ui10 = require("@behindthemusictree/ui");
|
|
1873
1756
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
1874
1757
|
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
@@ -1882,7 +1765,7 @@ var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
1882
1765
|
var import_fa3 = require("react-icons/fa");
|
|
1883
1766
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1884
1767
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
1885
|
-
var
|
|
1768
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1886
1769
|
function TrackPositionPlayPause({
|
|
1887
1770
|
position,
|
|
1888
1771
|
uuid,
|
|
@@ -1896,14 +1779,14 @@ function TrackPositionPlayPause({
|
|
|
1896
1779
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
1897
1780
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
1898
1781
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1899
|
-
return /* @__PURE__ */ (0,
|
|
1782
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1900
1783
|
"div",
|
|
1901
1784
|
{
|
|
1902
1785
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1903
1786
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1904
1787
|
onClick: handlePlayPauseClick,
|
|
1905
1788
|
children: [
|
|
1906
|
-
/* @__PURE__ */ (0,
|
|
1789
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1907
1790
|
"span",
|
|
1908
1791
|
{
|
|
1909
1792
|
className: "track-position-play-pause__bars",
|
|
@@ -1911,18 +1794,18 @@ function TrackPositionPlayPause({
|
|
|
1911
1794
|
role: "img",
|
|
1912
1795
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1913
1796
|
children: [
|
|
1914
|
-
/* @__PURE__ */ (0,
|
|
1915
|
-
/* @__PURE__ */ (0,
|
|
1916
|
-
/* @__PURE__ */ (0,
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
1798
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
1917
1800
|
]
|
|
1918
1801
|
}
|
|
1919
1802
|
) : position }),
|
|
1920
|
-
/* @__PURE__ */ (0,
|
|
1803
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1921
1804
|
"div",
|
|
1922
1805
|
{
|
|
1923
1806
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1924
1807
|
"aria-hidden": "true",
|
|
1925
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
1808
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_fa3.FaPlay, {})
|
|
1926
1809
|
}
|
|
1927
1810
|
)
|
|
1928
1811
|
]
|
|
@@ -1933,7 +1816,7 @@ function TrackPositionPlayPause({
|
|
|
1933
1816
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1934
1817
|
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1935
1818
|
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1936
|
-
var
|
|
1819
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1937
1820
|
function TrackItem({
|
|
1938
1821
|
track,
|
|
1939
1822
|
position,
|
|
@@ -1957,12 +1840,12 @@ function TrackItem({
|
|
|
1957
1840
|
loadTrackForPlayer(track.uuid);
|
|
1958
1841
|
}
|
|
1959
1842
|
};
|
|
1960
|
-
return /* @__PURE__ */ (0,
|
|
1843
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1961
1844
|
"div",
|
|
1962
1845
|
{
|
|
1963
1846
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1964
1847
|
children: [
|
|
1965
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
1848
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1966
1849
|
"div",
|
|
1967
1850
|
{
|
|
1968
1851
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1970,7 +1853,7 @@ function TrackItem({
|
|
|
1970
1853
|
"aria-disabled": "true",
|
|
1971
1854
|
children: position
|
|
1972
1855
|
}
|
|
1973
|
-
) : /* @__PURE__ */ (0,
|
|
1856
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1974
1857
|
TrackPositionPlayPause,
|
|
1975
1858
|
{
|
|
1976
1859
|
position,
|
|
@@ -1979,12 +1862,12 @@ function TrackItem({
|
|
|
1979
1862
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1980
1863
|
}
|
|
1981
1864
|
),
|
|
1982
|
-
/* @__PURE__ */ (0,
|
|
1983
|
-
/* @__PURE__ */ (0,
|
|
1984
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
1865
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1866
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree12.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1867
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree13.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1985
1868
|
] }),
|
|
1986
|
-
/* @__PURE__ */ (0,
|
|
1987
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
1869
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1870
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1988
1871
|
"div",
|
|
1989
1872
|
{
|
|
1990
1873
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1992,22 +1875,22 @@ function TrackItem({
|
|
|
1992
1875
|
children: renderDuration(track)
|
|
1993
1876
|
}
|
|
1994
1877
|
),
|
|
1995
|
-
renderActions && /* @__PURE__ */ (0,
|
|
1878
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1996
1879
|
]
|
|
1997
1880
|
}
|
|
1998
1881
|
);
|
|
1999
1882
|
}
|
|
2000
1883
|
|
|
2001
1884
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2002
|
-
var
|
|
1885
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
2003
1886
|
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema);
|
|
2004
1887
|
var SORTS = [
|
|
2005
1888
|
{ ordering: "title", label: "Title" },
|
|
2006
1889
|
{ ordering: "-albumYear", label: "Year" }
|
|
2007
1890
|
];
|
|
2008
1891
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2009
|
-
const [page, setPage] = (0,
|
|
2010
|
-
const [ordering, setOrdering] = (0,
|
|
1892
|
+
const [page, setPage] = (0, import_react14.useState)(1);
|
|
1893
|
+
const [ordering, setOrdering] = (0, import_react14.useState)("title");
|
|
2011
1894
|
const { fetch } = (0, import_transport7.useFetchWrapper)(getBackendBaseUrl);
|
|
2012
1895
|
const { playNewTrackListFromArtist } = (0, import_genre_tree14.useTrackList)();
|
|
2013
1896
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
@@ -2032,21 +1915,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2032
1915
|
event.stopPropagation();
|
|
2033
1916
|
void play(song.uuid);
|
|
2034
1917
|
};
|
|
2035
|
-
return /* @__PURE__ */ (0,
|
|
2036
|
-
/* @__PURE__ */ (0,
|
|
2037
|
-
/* @__PURE__ */ (0,
|
|
1918
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1919
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1920
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
2038
1921
|
"button",
|
|
2039
1922
|
{
|
|
2040
1923
|
type: "button",
|
|
2041
1924
|
onClick: () => void play(),
|
|
2042
1925
|
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",
|
|
2043
1926
|
children: [
|
|
2044
|
-
/* @__PURE__ */ (0,
|
|
1927
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react9.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2045
1928
|
"Play all"
|
|
2046
1929
|
]
|
|
2047
1930
|
}
|
|
2048
1931
|
),
|
|
2049
|
-
/* @__PURE__ */ (0,
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2050
1933
|
"button",
|
|
2051
1934
|
{
|
|
2052
1935
|
type: "button",
|
|
@@ -2061,7 +1944,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2061
1944
|
sort.ordering
|
|
2062
1945
|
)) })
|
|
2063
1946
|
] }),
|
|
2064
|
-
/* @__PURE__ */ (0,
|
|
1947
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
2065
1948
|
TrackItem,
|
|
2066
1949
|
{
|
|
2067
1950
|
track: song,
|
|
@@ -2070,12 +1953,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2070
1953
|
renderSongLink
|
|
2071
1954
|
}
|
|
2072
1955
|
) }, song.uuid)) }),
|
|
2073
|
-
/* @__PURE__ */ (0,
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2074
1957
|
] }) });
|
|
2075
1958
|
}
|
|
2076
1959
|
|
|
2077
1960
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2078
|
-
var
|
|
1961
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
2079
1962
|
function useArtistPanel({
|
|
2080
1963
|
uuid,
|
|
2081
1964
|
scope,
|
|
@@ -2096,26 +1979,26 @@ function useArtistPanel({
|
|
|
2096
1979
|
return {
|
|
2097
1980
|
...panel,
|
|
2098
1981
|
title: "Artist",
|
|
2099
|
-
content: /* @__PURE__ */ (0,
|
|
2100
|
-
/* @__PURE__ */ (0,
|
|
2101
|
-
/* @__PURE__ */ (0,
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
2103
|
-
/* @__PURE__ */ (0,
|
|
2104
|
-
/* @__PURE__ */ (0,
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
1982
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1983
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui11.Skeleton, { className: "h-3 w-16" }),
|
|
1984
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui11.Skeleton, { className: "h-4 w-32" }),
|
|
1985
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui11.Skeleton, { className: "h-3 w-16" }),
|
|
1986
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui11.Skeleton, { className: "h-4 w-24" }),
|
|
1987
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui11.Skeleton, { className: "h-16 mt-2.5" }),
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui11.Skeleton, { className: "h-16" })
|
|
2106
1989
|
] })
|
|
2107
1990
|
};
|
|
2108
1991
|
case "not-found":
|
|
2109
1992
|
return {
|
|
2110
1993
|
...panel,
|
|
2111
1994
|
title: "Artist not found",
|
|
2112
|
-
content: /* @__PURE__ */ (0,
|
|
1995
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2113
1996
|
};
|
|
2114
1997
|
case "error":
|
|
2115
1998
|
return {
|
|
2116
1999
|
...panel,
|
|
2117
2000
|
title: "Artist",
|
|
2118
|
-
content: /* @__PURE__ */ (0,
|
|
2001
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2119
2002
|
};
|
|
2120
2003
|
case "success": {
|
|
2121
2004
|
const { artist } = result;
|
|
@@ -2124,13 +2007,13 @@ function useArtistPanel({
|
|
|
2124
2007
|
return {
|
|
2125
2008
|
...panel,
|
|
2126
2009
|
title: artist.name,
|
|
2127
|
-
content: /* @__PURE__ */ (0,
|
|
2128
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2129
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2130
|
-
/* @__PURE__ */ (0,
|
|
2131
|
-
/* @__PURE__ */ (0,
|
|
2132
|
-
/* @__PURE__ */ (0,
|
|
2133
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2010
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
2011
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2012
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2013
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ArtistHeader, { artist }),
|
|
2014
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2016
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
2134
2017
|
ArtistSongs,
|
|
2135
2018
|
{
|
|
2136
2019
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2140,7 +2023,7 @@ function useArtistPanel({
|
|
|
2140
2023
|
}
|
|
2141
2024
|
)
|
|
2142
2025
|
] }),
|
|
2143
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2026
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2144
2027
|
};
|
|
2145
2028
|
}
|
|
2146
2029
|
}
|
|
@@ -2151,45 +2034,45 @@ var import_ui13 = require("@behindthemusictree/ui");
|
|
|
2151
2034
|
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2152
2035
|
|
|
2153
2036
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2154
|
-
var
|
|
2155
|
-
var
|
|
2037
|
+
var import_react15 = require("react");
|
|
2038
|
+
var import_lucide_react10 = require("lucide-react");
|
|
2156
2039
|
var import_ui12 = require("@behindthemusictree/ui");
|
|
2157
2040
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2158
2041
|
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2159
|
-
var
|
|
2042
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
2160
2043
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2161
2044
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2162
2045
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2163
2046
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2164
|
-
const [open, setOpen] = (0,
|
|
2047
|
+
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
2165
2048
|
const history = (0, import_genre_tree16.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2166
2049
|
let body = null;
|
|
2167
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2168
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2050
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui12.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2051
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2169
2052
|
else if (history.status === "success")
|
|
2170
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2171
|
-
/* @__PURE__ */ (0,
|
|
2172
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2053
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2054
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2055
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2173
2056
|
" ",
|
|
2174
2057
|
"\u2014 ",
|
|
2175
2058
|
entry.oldValue ?? "none",
|
|
2176
2059
|
" \u2192 ",
|
|
2177
2060
|
entry.newValue ?? "none"
|
|
2178
2061
|
] }) : null,
|
|
2179
|
-
/* @__PURE__ */ (0,
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2180
2063
|
entry.actorPseudo ?? "Anonymous",
|
|
2181
2064
|
" \xB7 ",
|
|
2182
2065
|
dateFormat.format(new Date(entry.createdOn))
|
|
2183
2066
|
] })
|
|
2184
2067
|
] }, entry.uuid)) });
|
|
2185
|
-
return /* @__PURE__ */ (0,
|
|
2068
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
2186
2069
|
"details",
|
|
2187
2070
|
{
|
|
2188
2071
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2189
2072
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2190
2073
|
children: [
|
|
2191
|
-
/* @__PURE__ */ (0,
|
|
2192
|
-
/* @__PURE__ */ (0,
|
|
2074
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2075
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "mt-1.5", children: body })
|
|
2193
2076
|
]
|
|
2194
2077
|
}
|
|
2195
2078
|
);
|
|
@@ -2215,13 +2098,13 @@ function SongPanelContent({
|
|
|
2215
2098
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2216
2099
|
fields.push({
|
|
2217
2100
|
label: "Genre",
|
|
2218
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2219
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2220
|
-
/* @__PURE__ */ (0,
|
|
2101
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { children: [
|
|
2102
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2221
2104
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2222
2105
|
});
|
|
2223
2106
|
} else {
|
|
2224
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2107
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2225
2108
|
}
|
|
2226
2109
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2227
2110
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2231,11 +2114,11 @@ function SongPanelContent({
|
|
|
2231
2114
|
const links = [];
|
|
2232
2115
|
if (musicBrainzUrl)
|
|
2233
2116
|
links.push(
|
|
2234
|
-
/* @__PURE__ */ (0,
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2235
2118
|
);
|
|
2236
2119
|
if (song.youtubeVideo)
|
|
2237
2120
|
links.push(
|
|
2238
|
-
/* @__PURE__ */ (0,
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
2239
2122
|
"a",
|
|
2240
2123
|
{
|
|
2241
2124
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2250,33 +2133,33 @@ function SongPanelContent({
|
|
|
2250
2133
|
if (links.length > 0)
|
|
2251
2134
|
fields.push({
|
|
2252
2135
|
label: "Links",
|
|
2253
|
-
value: /* @__PURE__ */ (0,
|
|
2136
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "flex gap-3", children: links })
|
|
2254
2137
|
});
|
|
2255
|
-
return /* @__PURE__ */ (0,
|
|
2256
|
-
/* @__PURE__ */ (0,
|
|
2257
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2138
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
2139
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2140
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(
|
|
2258
2141
|
"button",
|
|
2259
2142
|
{
|
|
2260
2143
|
type: "button",
|
|
2261
2144
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2262
2145
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2263
2146
|
children: [
|
|
2264
|
-
/* @__PURE__ */ (0,
|
|
2147
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2265
2148
|
isPlaying ? "Pause" : "Play"
|
|
2266
2149
|
]
|
|
2267
2150
|
}
|
|
2268
2151
|
),
|
|
2269
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2152
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2270
2153
|
] }) }),
|
|
2271
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2272
|
-
/* @__PURE__ */ (0,
|
|
2273
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2274
|
-
/* @__PURE__ */ (0,
|
|
2154
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_genre_tree16.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2155
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui12.FieldList, { items: fields }),
|
|
2156
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui12.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2157
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2275
2158
|
] });
|
|
2276
2159
|
}
|
|
2277
2160
|
|
|
2278
2161
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2279
|
-
var
|
|
2162
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
2280
2163
|
function useSongPanel({
|
|
2281
2164
|
uuid,
|
|
2282
2165
|
scope,
|
|
@@ -2298,24 +2181,24 @@ function useSongPanel({
|
|
|
2298
2181
|
return {
|
|
2299
2182
|
...panel,
|
|
2300
2183
|
title: "Song",
|
|
2301
|
-
content: /* @__PURE__ */ (0,
|
|
2302
|
-
/* @__PURE__ */ (0,
|
|
2303
|
-
/* @__PURE__ */ (0,
|
|
2304
|
-
/* @__PURE__ */ (0,
|
|
2305
|
-
/* @__PURE__ */ (0,
|
|
2184
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2185
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui13.Skeleton, { className: "h-3 w-16" }),
|
|
2186
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui13.Skeleton, { className: "h-4 w-32" }),
|
|
2187
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui13.Skeleton, { className: "h-3 w-16" }),
|
|
2188
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui13.Skeleton, { className: "h-4 w-24" })
|
|
2306
2189
|
] })
|
|
2307
2190
|
};
|
|
2308
2191
|
case "not-found":
|
|
2309
2192
|
return {
|
|
2310
2193
|
...panel,
|
|
2311
2194
|
title: "Song not found",
|
|
2312
|
-
content: /* @__PURE__ */ (0,
|
|
2195
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2313
2196
|
};
|
|
2314
2197
|
case "error":
|
|
2315
2198
|
return {
|
|
2316
2199
|
...panel,
|
|
2317
2200
|
title: "Song",
|
|
2318
|
-
content: /* @__PURE__ */ (0,
|
|
2201
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2319
2202
|
};
|
|
2320
2203
|
case "success": {
|
|
2321
2204
|
const { song } = result;
|
|
@@ -2323,7 +2206,7 @@ function useSongPanel({
|
|
|
2323
2206
|
return {
|
|
2324
2207
|
...panel,
|
|
2325
2208
|
title: song.title,
|
|
2326
|
-
content: /* @__PURE__ */ (0,
|
|
2209
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
2327
2210
|
SongPanelContent,
|
|
2328
2211
|
{
|
|
2329
2212
|
song,
|
|
@@ -2343,12 +2226,12 @@ function useSongPanel({
|
|
|
2343
2226
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2344
2227
|
var import_md = require("react-icons/md");
|
|
2345
2228
|
var import_ui14 = require("@behindthemusictree/ui");
|
|
2346
|
-
var
|
|
2229
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
2347
2230
|
function SizeMetricToggle({
|
|
2348
2231
|
value,
|
|
2349
2232
|
onChange
|
|
2350
2233
|
}) {
|
|
2351
|
-
return /* @__PURE__ */ (0,
|
|
2234
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
2352
2235
|
import_ui14.SegmentedControl,
|
|
2353
2236
|
{
|
|
2354
2237
|
variant: "floating",
|
|
@@ -2356,26 +2239,19 @@ function SizeMetricToggle({
|
|
|
2356
2239
|
value,
|
|
2357
2240
|
onChange,
|
|
2358
2241
|
options: [
|
|
2359
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2360
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2242
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2243
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2361
2244
|
]
|
|
2362
2245
|
}
|
|
2363
2246
|
);
|
|
2364
2247
|
}
|
|
2365
2248
|
|
|
2366
2249
|
// src/genre-tree/GenreTreeView.tsx
|
|
2367
|
-
var
|
|
2368
|
-
var PREVIOUS_BUTTON_CSS = `
|
|
2369
|
-
.genre-tree-view .search-overlay .gtv-info-panel-previous { display: none; }
|
|
2370
|
-
.genre-tree-view:has(.search-overlay .gtv-info-panel-previous) .genre-search input { padding-left: 2.75rem; }
|
|
2371
|
-
.genre-tree-view:has(.search-overlay .gtv-info-panel-previous) .genre-search-previous { display: flex; }
|
|
2372
|
-
@keyframes genre-tree-view-previous-in { from { opacity: 0; transform: translate(-0.5rem, -50%); } to { opacity: 1; transform: translate(0, -50%); } }
|
|
2373
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
2374
|
-
.genre-search-previous { animation: genre-tree-view-previous-in 200ms ease-out; }
|
|
2375
|
-
}
|
|
2376
|
-
`;
|
|
2250
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
2377
2251
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2378
|
-
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]
|
|
2252
|
+
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2253
|
+
var noop = () => {
|
|
2254
|
+
};
|
|
2379
2255
|
function GenreTreeView({
|
|
2380
2256
|
scope,
|
|
2381
2257
|
handleGenreCreationAction,
|
|
@@ -2429,21 +2305,21 @@ function GenreTreeView({
|
|
|
2429
2305
|
placement = "default",
|
|
2430
2306
|
...focus
|
|
2431
2307
|
}) {
|
|
2432
|
-
const getShareUrlRef = (0,
|
|
2308
|
+
const getShareUrlRef = (0, import_react16.useRef)(getShareUrl);
|
|
2433
2309
|
getShareUrlRef.current = getShareUrl;
|
|
2434
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2310
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react16.useState)(null);
|
|
2435
2311
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2436
2312
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2437
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2313
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react16.useState)(null);
|
|
2438
2314
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2439
2315
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2440
2316
|
const changeSong = (uuid) => {
|
|
2441
2317
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2442
2318
|
onSongUuidChange?.(uuid);
|
|
2443
2319
|
};
|
|
2444
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2320
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react16.useState)("songs");
|
|
2445
2321
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2446
|
-
const deferredSizeMetric = (0,
|
|
2322
|
+
const deferredSizeMetric = (0, import_react16.useDeferredValue)(sizeMetric);
|
|
2447
2323
|
const changeSizeMetric = (metric) => {
|
|
2448
2324
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2449
2325
|
onSizeMetricChange?.(metric);
|
|
@@ -2480,7 +2356,7 @@ function GenreTreeView({
|
|
|
2480
2356
|
artistUuid
|
|
2481
2357
|
});
|
|
2482
2358
|
const restoreRevision = viewportRestore?.revision;
|
|
2483
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2359
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react16.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2484
2360
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2485
2361
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2486
2362
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2496,7 +2372,7 @@ function GenreTreeView({
|
|
|
2496
2372
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2497
2373
|
const artistResult = (0, import_genre_tree19.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2498
2374
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2499
|
-
const filterArtist = (0,
|
|
2375
|
+
const filterArtist = (0, import_react16.useMemo)(
|
|
2500
2376
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2501
2377
|
[artistUuid, filterArtistName]
|
|
2502
2378
|
);
|
|
@@ -2511,14 +2387,14 @@ function GenreTreeView({
|
|
|
2511
2387
|
if (selectedNodeId !== null) clearSelection();
|
|
2512
2388
|
changeSong(uuid);
|
|
2513
2389
|
};
|
|
2514
|
-
const openSongRef = (0,
|
|
2390
|
+
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2515
2391
|
openSongRef.current = openSong;
|
|
2516
|
-
const resolvedRenderSongLink = (0,
|
|
2517
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2392
|
+
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2393
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2518
2394
|
[renderSongLink]
|
|
2519
2395
|
);
|
|
2520
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2521
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
2396
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
2397
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react16.useState)(null);
|
|
2522
2398
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2523
2399
|
const songBaseUrl = getBackendBaseUrl;
|
|
2524
2400
|
const artistPanel = useArtistPanel({
|
|
@@ -2526,7 +2402,7 @@ function GenreTreeView({
|
|
|
2526
2402
|
scope,
|
|
2527
2403
|
getBackendBaseUrl: artistBaseUrl,
|
|
2528
2404
|
getShareUrl: getShareUrlRef.current,
|
|
2529
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2405
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2530
2406
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2531
2407
|
onExpandedChange: (on) => {
|
|
2532
2408
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2535,7 +2411,7 @@ function GenreTreeView({
|
|
|
2535
2411
|
renderSongLink: resolvedRenderSongLink,
|
|
2536
2412
|
onClose: () => changeArtist(null)
|
|
2537
2413
|
});
|
|
2538
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2414
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2539
2415
|
const getGenrePath = (genre) => {
|
|
2540
2416
|
const path = [];
|
|
2541
2417
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2567,9 +2443,8 @@ function GenreTreeView({
|
|
|
2567
2443
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2568
2444
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2569
2445
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2570
|
-
const
|
|
2571
|
-
const [
|
|
2572
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react17.useState)(false);
|
|
2446
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react16.useState)(false);
|
|
2447
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react16.useState)(false);
|
|
2573
2448
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2574
2449
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2575
2450
|
const changeCollapsed = (next) => {
|
|
@@ -2580,55 +2455,56 @@ function GenreTreeView({
|
|
|
2580
2455
|
setUncontrolledExpanded(next);
|
|
2581
2456
|
onExpandedChange?.(next);
|
|
2582
2457
|
};
|
|
2583
|
-
(0,
|
|
2458
|
+
(0, import_react16.useEffect)(() => {
|
|
2584
2459
|
setUncontrolledCollapsed(false);
|
|
2585
2460
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2586
|
-
const
|
|
2587
|
-
useLeftDetailPanelLayout(!isDetailHidden, placement === "regions");
|
|
2588
|
-
const detailOverlay = placement === "regions" && activeOverlayPanel?.onExpandedChange ? activeOverlayPanel : void 0;
|
|
2589
|
-
const detailExpanded = detailOverlay ? !!detailOverlay.expanded : isExpanded;
|
|
2590
|
-
const onDetailExpandedChange = detailOverlay ? detailOverlay.onExpandedChange : changeExpanded;
|
|
2591
|
-
const infoPanelCuration = {
|
|
2592
|
-
renderDangerZone,
|
|
2593
|
-
expanded: placement === "regions" ? isExpanded : expanded,
|
|
2594
|
-
onExpandedChange: placement === "regions" ? changeExpanded : onExpandedChange,
|
|
2595
|
-
collapsed: placement === "regions" ? isCollapsed : void 0,
|
|
2596
|
-
hideExpandButton: placement === "regions" ? true : void 0,
|
|
2597
|
-
overlayPanel: detailOverlay ? { ...detailOverlay, collapsed: isCollapsed, hideExpandButton: true } : activeOverlayPanel
|
|
2598
|
-
};
|
|
2599
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react17.useState)(false);
|
|
2461
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react16.useState)(false);
|
|
2600
2462
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2601
2463
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2602
|
-
const
|
|
2603
|
-
const
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2464
|
+
const [resolveStyle, setResolveStyle] = (0, import_react16.useState)(null);
|
|
2465
|
+
const handleNodeStyleResolverChange = (0, import_react16.useCallback)(
|
|
2466
|
+
(resolver) => setResolveStyle(() => resolver),
|
|
2467
|
+
[]
|
|
2468
|
+
);
|
|
2469
|
+
const panelRef = (0, import_react16.useRef)(null);
|
|
2470
|
+
const obscuredInset = (0, import_ui15.useObscuredInset)(panelRef);
|
|
2471
|
+
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2472
|
+
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2473
|
+
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2474
|
+
const relations = (0, import_genre_tree_view4.useGenreNodeRelations)(
|
|
2475
|
+
treeNodes,
|
|
2476
|
+
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2477
|
+
resolveStyle
|
|
2478
|
+
);
|
|
2479
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react16.useState)({
|
|
2480
|
+
current: selectedNodeId,
|
|
2481
|
+
previous: null
|
|
2482
|
+
});
|
|
2483
|
+
if (selectionHistory.current !== selectedNodeId) {
|
|
2484
|
+
setSelectionHistory({
|
|
2485
|
+
current: selectedNodeId,
|
|
2486
|
+
previous: selectionHistory.current ?? selectionHistory.previous
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2490
|
+
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2615
2491
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
2616
|
-
const [searchText, setSearchText] = (0,
|
|
2492
|
+
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2617
2493
|
const artistSearchResult = (0, import_genre_tree18.useArtistSearch)(artistBaseUrl, artistSearch ? searchText : "");
|
|
2618
2494
|
const artistGroup = artistSearch ? {
|
|
2619
2495
|
key: "artists",
|
|
2620
2496
|
label: "Artists",
|
|
2621
2497
|
items: artistSearchResult.artists.map((artist) => ({
|
|
2622
2498
|
key: artist.uuid,
|
|
2623
|
-
content: /* @__PURE__ */ (0,
|
|
2624
|
-
/* @__PURE__ */ (0,
|
|
2499
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2500
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
2625
2501
|
artist.name,
|
|
2626
|
-
artist.disambiguation && /* @__PURE__ */ (0,
|
|
2502
|
+
artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
2627
2503
|
" \xB7 ",
|
|
2628
2504
|
artist.disambiguation
|
|
2629
2505
|
] })
|
|
2630
2506
|
] }),
|
|
2631
|
-
artist.matchedAlias && /* @__PURE__ */ (0,
|
|
2507
|
+
artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
2632
2508
|
"also known as ",
|
|
2633
2509
|
artist.matchedAlias
|
|
2634
2510
|
] })
|
|
@@ -2647,9 +2523,9 @@ function GenreTreeView({
|
|
|
2647
2523
|
label: "Songs",
|
|
2648
2524
|
items: songSearchResult.songs.map((song) => ({
|
|
2649
2525
|
key: song.uuid,
|
|
2650
|
-
content: /* @__PURE__ */ (0,
|
|
2651
|
-
/* @__PURE__ */ (0,
|
|
2652
|
-
/* @__PURE__ */ (0,
|
|
2526
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: (0, import_genre_tree18.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
2527
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
2528
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
2653
2529
|
] }),
|
|
2654
2530
|
onSelect: () => {
|
|
2655
2531
|
openSong(song.uuid);
|
|
@@ -2675,36 +2551,49 @@ function GenreTreeView({
|
|
|
2675
2551
|
const handleGenreSearchZoomIn = () => {
|
|
2676
2552
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2677
2553
|
};
|
|
2678
|
-
const zoomInRef = (0,
|
|
2554
|
+
const zoomInRef = (0, import_react16.useRef)(void 0);
|
|
2679
2555
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2680
|
-
const
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2556
|
+
const selectGenreFromPanel = (nodeId) => {
|
|
2557
|
+
const genrePlaylist = genrePlaylistByNodeId.get(nodeId);
|
|
2558
|
+
if (genrePlaylist) handleGenreSearchSelect(genrePlaylist);
|
|
2559
|
+
};
|
|
2560
|
+
const renderGenreBody = (relation) => {
|
|
2561
|
+
const { node } = relation;
|
|
2562
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2563
|
+
import_genre_tree_view4.GenreNodeFamily,
|
|
2564
|
+
{
|
|
2565
|
+
node,
|
|
2566
|
+
fill: relation.fill,
|
|
2567
|
+
textColor: relation.textColor,
|
|
2568
|
+
parentNode: relation.parentNode,
|
|
2569
|
+
childNodes: relation.childNodes,
|
|
2570
|
+
onSelectNode: selectGenreFromPanel
|
|
2571
|
+
}
|
|
2572
|
+
);
|
|
2573
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2574
|
+
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2575
|
+
const criteria = genrePlaylist?.criteria;
|
|
2576
|
+
if (!genrePlaylist || !criteria) return { primary: family, aside: ancestors };
|
|
2577
|
+
const genreUuid = criteria.uuid;
|
|
2578
|
+
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist.parent?.uuid);
|
|
2579
|
+
const canToggleSide = !readOnly && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2580
|
+
const side = criteria.side ?? "core";
|
|
2581
|
+
const shareTarget = {
|
|
2582
|
+
kind: "genre",
|
|
2583
|
+
genrePlaylistUuid: genrePlaylist.uuid,
|
|
2584
|
+
name: criteria.name,
|
|
2585
|
+
scope,
|
|
2586
|
+
artistUuid
|
|
2587
|
+
};
|
|
2588
|
+
const shareUrl = getShareUrlRef.current?.(shareTarget) ?? void 0;
|
|
2589
|
+
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find(
|
|
2590
|
+
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2591
|
+
) : void 0;
|
|
2592
|
+
return {
|
|
2593
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2594
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2705
2595
|
GenreInfoPanelBody,
|
|
2706
2596
|
{
|
|
2707
|
-
toolbar,
|
|
2708
2597
|
family,
|
|
2709
2598
|
genreUuid,
|
|
2710
2599
|
genrePlaylist,
|
|
@@ -2720,7 +2609,7 @@ function GenreTreeView({
|
|
|
2720
2609
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2721
2610
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2722
2611
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2723
|
-
/* @__PURE__ */ (0,
|
|
2612
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2724
2613
|
ChangeParentPopup,
|
|
2725
2614
|
{
|
|
2726
2615
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2751,49 +2640,65 @@ function GenreTreeView({
|
|
|
2751
2640
|
}
|
|
2752
2641
|
}
|
|
2753
2642
|
),
|
|
2754
|
-
|
|
2755
|
-
}
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
additionalActions,
|
|
2760
|
-
readOnly,
|
|
2761
|
-
handleGenreCreationAction,
|
|
2762
|
-
handleGenreRenameAction,
|
|
2763
|
-
showPopup,
|
|
2764
|
-
hidePopup,
|
|
2765
|
-
onParentChanged,
|
|
2766
|
-
visibleGenrePlaylists,
|
|
2767
|
-
updateGenre,
|
|
2768
|
-
playback,
|
|
2769
|
-
filterArtist,
|
|
2770
|
-
scope,
|
|
2771
|
-
getBackendBaseUrl,
|
|
2772
|
-
criteriaOverviewSchema,
|
|
2773
|
-
renderGenreDetailExtras,
|
|
2774
|
-
renderArtistLink,
|
|
2775
|
-
resolvedRenderSongLink,
|
|
2776
|
-
renderSummary,
|
|
2777
|
-
renderEssentialTracks,
|
|
2778
|
-
renderSongLeading,
|
|
2779
|
-
renderSongActions,
|
|
2780
|
-
renderTracksToolbar
|
|
2781
|
-
]
|
|
2782
|
-
);
|
|
2643
|
+
renderDangerZone?.(node)
|
|
2644
|
+
] }),
|
|
2645
|
+
aside: ancestors
|
|
2646
|
+
};
|
|
2647
|
+
};
|
|
2783
2648
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
2784
2649
|
const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
|
|
2785
|
-
|
|
2786
|
-
|
|
2650
|
+
let activePanel = null;
|
|
2651
|
+
if (overlayPanel) {
|
|
2652
|
+
activePanel = {
|
|
2653
|
+
title: overlayPanel.title,
|
|
2654
|
+
content: overlayPanel.content,
|
|
2655
|
+
aside: overlayPanel.extraContent,
|
|
2656
|
+
expanded: overlayPanel.expanded ?? false,
|
|
2657
|
+
onExpandedChange: overlayPanel.onExpandedChange,
|
|
2658
|
+
onClose: overlayPanel.onClose,
|
|
2659
|
+
compact: false
|
|
2660
|
+
};
|
|
2661
|
+
} else if (relations) {
|
|
2662
|
+
const { primary, aside } = renderGenreBody(relations);
|
|
2663
|
+
activePanel = {
|
|
2664
|
+
title: relations.node.name,
|
|
2665
|
+
content: primary,
|
|
2666
|
+
aside,
|
|
2667
|
+
expanded: isExpanded,
|
|
2668
|
+
onExpandedChange: changeExpanded,
|
|
2669
|
+
onClose: clearSelection,
|
|
2670
|
+
compact: true,
|
|
2671
|
+
headerAccent: { background: relations.fill, color: relations.textColor }
|
|
2672
|
+
};
|
|
2673
|
+
} else if (builtInPanel) {
|
|
2674
|
+
activePanel = {
|
|
2675
|
+
title: builtInPanel.title,
|
|
2676
|
+
content: builtInPanel.content,
|
|
2677
|
+
aside: builtInPanel.extraContent,
|
|
2678
|
+
expanded: builtInPanel.expanded,
|
|
2679
|
+
onExpandedChange: builtInPanel.onExpandedChange,
|
|
2680
|
+
onClose: builtInPanel.onClose,
|
|
2681
|
+
compact: true
|
|
2682
|
+
};
|
|
2683
|
+
}
|
|
2684
|
+
const isPanelShown = isTreeReady && activePanel !== null;
|
|
2685
|
+
const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
|
|
2686
|
+
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2687
|
+
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2688
|
+
} : void 0;
|
|
2689
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2690
|
+
import_ui15.IconButton,
|
|
2787
2691
|
{
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2692
|
+
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2693
|
+
style: activePanel.headerAccent && { color: "inherit" },
|
|
2694
|
+
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2695
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2791
2696
|
}
|
|
2792
2697
|
) : null;
|
|
2793
2698
|
const renderArtistFilter = () => {
|
|
2794
2699
|
if (isLoading || !filterArtist) return null;
|
|
2795
|
-
return /* @__PURE__ */ (0,
|
|
2796
|
-
/* @__PURE__ */ (0,
|
|
2700
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2701
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2797
2702
|
"button",
|
|
2798
2703
|
{
|
|
2799
2704
|
type: "button",
|
|
@@ -2805,7 +2710,7 @@ function GenreTreeView({
|
|
|
2805
2710
|
children: filterArtist.name
|
|
2806
2711
|
}
|
|
2807
2712
|
),
|
|
2808
|
-
/* @__PURE__ */ (0,
|
|
2713
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2809
2714
|
"button",
|
|
2810
2715
|
{
|
|
2811
2716
|
type: "button",
|
|
@@ -2817,22 +2722,22 @@ function GenreTreeView({
|
|
|
2817
2722
|
)
|
|
2818
2723
|
] });
|
|
2819
2724
|
};
|
|
2820
|
-
const actions = /* @__PURE__ */ (0,
|
|
2821
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
2725
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2726
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2822
2727
|
GenreSearch,
|
|
2823
2728
|
{
|
|
2824
2729
|
genrePlaylists: visibleGenrePlaylists,
|
|
2825
2730
|
onSelect: handleGenreSearchSelect,
|
|
2826
|
-
selectedName: selectedGenrePlaylist?.name ?? builtInPanel?.title
|
|
2731
|
+
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2827
2732
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2828
2733
|
onPrevious: handlePrevious,
|
|
2829
2734
|
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
2830
2735
|
extraGroups
|
|
2831
2736
|
}
|
|
2832
2737
|
),
|
|
2833
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2834
|
-
/* @__PURE__ */ (0,
|
|
2835
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
2738
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2739
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
2740
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2836
2741
|
import_ui15.SegmentedControl,
|
|
2837
2742
|
{
|
|
2838
2743
|
"aria-label": "Tree view mode",
|
|
@@ -2846,13 +2751,13 @@ function GenreTreeView({
|
|
|
2846
2751
|
]
|
|
2847
2752
|
}
|
|
2848
2753
|
),
|
|
2849
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
2754
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2850
2755
|
"div",
|
|
2851
2756
|
{
|
|
2852
2757
|
className: "flex items-center gap-1",
|
|
2853
2758
|
role: "group",
|
|
2854
2759
|
"aria-label": "Tree display options",
|
|
2855
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
2760
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2856
2761
|
import_ui15.Button,
|
|
2857
2762
|
{
|
|
2858
2763
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2863,52 +2768,44 @@ function GenreTreeView({
|
|
|
2863
2768
|
)
|
|
2864
2769
|
}
|
|
2865
2770
|
),
|
|
2866
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
2771
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2867
2772
|
import_ui15.IconTextButton,
|
|
2868
2773
|
{
|
|
2869
|
-
icon:
|
|
2774
|
+
icon: import_lucide_react11.Plus,
|
|
2870
2775
|
text: "Add root",
|
|
2871
2776
|
onClick: () => handleGenreCreationAction(null)
|
|
2872
2777
|
}
|
|
2873
2778
|
)
|
|
2874
2779
|
] })
|
|
2875
2780
|
] });
|
|
2876
|
-
return /* @__PURE__ */ (0,
|
|
2781
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
2877
2782
|
"div",
|
|
2878
2783
|
{
|
|
2879
|
-
|
|
2784
|
+
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2880
2785
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2881
2786
|
children: [
|
|
2882
|
-
/* @__PURE__ */ (0,
|
|
2883
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2787
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2884
2788
|
"div",
|
|
2885
2789
|
{
|
|
2886
2790
|
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"}`,
|
|
2887
2791
|
children: actions
|
|
2888
2792
|
}
|
|
2889
2793
|
),
|
|
2890
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2794
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2891
2795
|
"div",
|
|
2892
2796
|
{
|
|
2893
2797
|
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"}`,
|
|
2894
2798
|
children: renderArtistFilter()
|
|
2895
2799
|
}
|
|
2896
2800
|
),
|
|
2897
|
-
|
|
2898
|
-
"div",
|
|
2899
|
-
{
|
|
2900
|
-
className: `absolute top-1/2 z-30 -translate-y-1/2 max-md:hidden transition-[opacity,visibility] duration-150 ease-out ${isTreeReady ? "opacity-100" : "pointer-events-none invisible opacity-0"} ${detailExpanded ? "right-[calc(var(--layout-center-right)-var(--layout-edge-tab-w))]" : isCollapsed ? "left-0" : "left-[calc(var(--layout-gutter)+var(--layout-left-w))]"}`,
|
|
2901
|
-
children: detailTab
|
|
2902
|
-
}
|
|
2903
|
-
),
|
|
2904
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2905
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2906
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2801
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2802
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2803
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2907
2804
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2908
2805
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2909
2806
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2910
2807
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2911
|
-
/* @__PURE__ */ (0,
|
|
2808
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2912
2809
|
GenrePlaylistTreeWheel,
|
|
2913
2810
|
{
|
|
2914
2811
|
scope,
|
|
@@ -2917,29 +2814,26 @@ function GenreTreeView({
|
|
|
2917
2814
|
handleGenreRenameAction,
|
|
2918
2815
|
additionalActions,
|
|
2919
2816
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2920
|
-
hideInfoPanelClose: true,
|
|
2921
|
-
hideInfoPanelTitle: true,
|
|
2922
2817
|
onNodeHover: handleNodeHover,
|
|
2923
|
-
|
|
2818
|
+
...hostPanel,
|
|
2924
2819
|
playback,
|
|
2925
2820
|
selectedNodeId,
|
|
2926
2821
|
readOnly,
|
|
2927
2822
|
allowWheelRotation,
|
|
2928
2823
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2929
2824
|
busy,
|
|
2930
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2825
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2931
2826
|
floatingControlsLeading,
|
|
2932
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2827
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2933
2828
|
] }) : void 0,
|
|
2934
2829
|
defaultViewport,
|
|
2935
2830
|
viewportRestore,
|
|
2936
|
-
onViewportChange
|
|
2937
|
-
...infoPanelCuration
|
|
2831
|
+
onViewportChange
|
|
2938
2832
|
}
|
|
2939
2833
|
) }) })
|
|
2940
2834
|
) : viewMode === "pop-core" ? (
|
|
2941
2835
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2942
|
-
/* @__PURE__ */ (0,
|
|
2836
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2943
2837
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2944
2838
|
{
|
|
2945
2839
|
scope,
|
|
@@ -2948,28 +2842,25 @@ function GenreTreeView({
|
|
|
2948
2842
|
handleGenreRenameAction,
|
|
2949
2843
|
additionalActions,
|
|
2950
2844
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2951
|
-
hideInfoPanelClose: true,
|
|
2952
|
-
hideInfoPanelTitle: true,
|
|
2953
2845
|
onNodeHover: handleNodeHover,
|
|
2954
|
-
|
|
2846
|
+
...hostPanel,
|
|
2955
2847
|
playback,
|
|
2956
2848
|
selectedNodeId,
|
|
2957
2849
|
readOnly,
|
|
2958
2850
|
allowWheelRotation,
|
|
2959
2851
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2960
2852
|
busy,
|
|
2961
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2853
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2962
2854
|
floatingControlsLeading,
|
|
2963
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2855
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2964
2856
|
] }) : void 0,
|
|
2965
2857
|
defaultViewport,
|
|
2966
2858
|
viewportRestore,
|
|
2967
2859
|
onViewportChange,
|
|
2968
|
-
...infoPanelCuration,
|
|
2969
2860
|
...focus
|
|
2970
2861
|
}
|
|
2971
2862
|
) }) })
|
|
2972
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
2863
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2973
2864
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2974
2865
|
{
|
|
2975
2866
|
outline: true,
|
|
@@ -2979,22 +2870,19 @@ function GenreTreeView({
|
|
|
2979
2870
|
handleGenreRenameAction,
|
|
2980
2871
|
additionalActions,
|
|
2981
2872
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2982
|
-
hideInfoPanelClose: true,
|
|
2983
|
-
hideInfoPanelTitle: true,
|
|
2984
2873
|
onNodeHover: handleNodeHover,
|
|
2985
|
-
|
|
2874
|
+
...hostPanel,
|
|
2986
2875
|
playback,
|
|
2987
2876
|
selectedNodeId,
|
|
2988
|
-
readOnly
|
|
2989
|
-
...infoPanelCuration
|
|
2877
|
+
readOnly
|
|
2990
2878
|
}
|
|
2991
|
-
) }) : /* @__PURE__ */ (0,
|
|
2992
|
-
([uuid, genrePlaylistTreePerRoot]
|
|
2993
|
-
return /* @__PURE__ */ (0,
|
|
2879
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
2880
|
+
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2881
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2994
2882
|
"div",
|
|
2995
2883
|
{
|
|
2996
2884
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2997
|
-
children: /* @__PURE__ */ (0,
|
|
2885
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2998
2886
|
GenrePlaylistTreePerRoot,
|
|
2999
2887
|
{
|
|
3000
2888
|
scope,
|
|
@@ -3004,26 +2892,45 @@ function GenreTreeView({
|
|
|
3004
2892
|
handleGenreRenameAction,
|
|
3005
2893
|
additionalActions,
|
|
3006
2894
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
3007
|
-
hideInfoPanelClose: true,
|
|
3008
|
-
hideInfoPanelTitle: true,
|
|
3009
2895
|
onNodeHover: handleNodeHover,
|
|
3010
|
-
|
|
2896
|
+
...hostPanel,
|
|
3011
2897
|
playback,
|
|
3012
2898
|
selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
|
|
3013
2899
|
readOnly,
|
|
3014
2900
|
wheelZoom: "modifier",
|
|
3015
2901
|
defaultViewport: uuid === selectedGenrePlaylist?.root.uuid ? defaultViewport : void 0,
|
|
3016
2902
|
viewportRestore: uuid === selectedGenrePlaylist?.root.uuid && uuid === restoreRootUuid ? viewportRestore : void 0,
|
|
3017
|
-
onViewportChange: uuid === selectedGenrePlaylist?.root.uuid ? onViewportChange : void 0
|
|
3018
|
-
...infoPanelCuration,
|
|
3019
|
-
overlayPanel: rootIndex === 0 ? activeOverlayPanel : void 0
|
|
2903
|
+
onViewportChange: uuid === selectedGenrePlaylist?.root.uuid ? onViewportChange : void 0
|
|
3020
2904
|
}
|
|
3021
2905
|
) })
|
|
3022
2906
|
},
|
|
3023
2907
|
uuid
|
|
3024
2908
|
);
|
|
3025
2909
|
}
|
|
3026
|
-
) })
|
|
2910
|
+
) }),
|
|
2911
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2912
|
+
import_ui15.DetailPanel,
|
|
2913
|
+
{
|
|
2914
|
+
ref: panelRef,
|
|
2915
|
+
side: "left",
|
|
2916
|
+
title: activePanel?.title ?? "",
|
|
2917
|
+
hideTitle: activePanel?.compact,
|
|
2918
|
+
hideClose: activePanel?.compact,
|
|
2919
|
+
headerAccent: activePanel?.headerAccent,
|
|
2920
|
+
headerActions: expandButton,
|
|
2921
|
+
onClose: activePanel?.onClose ?? noop,
|
|
2922
|
+
collapsed: isCollapsed || !isTreeReady || !activePanel,
|
|
2923
|
+
expanded: activePanel?.expanded,
|
|
2924
|
+
onExpandedChange: activePanel?.onExpandedChange,
|
|
2925
|
+
aside: activePanel?.aside,
|
|
2926
|
+
edgeTab: detailTab,
|
|
2927
|
+
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2928
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2929
|
+
activePanel.content,
|
|
2930
|
+
activePanel.aside
|
|
2931
|
+
] }) : activePanel?.content
|
|
2932
|
+
}
|
|
2933
|
+
)
|
|
3027
2934
|
] }) })
|
|
3028
2935
|
]
|
|
3029
2936
|
}
|
|
@@ -3031,12 +2938,12 @@ function GenreTreeView({
|
|
|
3031
2938
|
}
|
|
3032
2939
|
|
|
3033
2940
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
3034
|
-
var
|
|
2941
|
+
var import_react17 = require("react");
|
|
3035
2942
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3036
2943
|
var import_ui16 = require("@behindthemusictree/ui");
|
|
2944
|
+
var import_lucide_react13 = require("lucide-react");
|
|
3037
2945
|
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3038
|
-
var
|
|
3039
|
-
var ITEM_COUNT_LABEL = "Songs";
|
|
2946
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
3040
2947
|
var RELATIVE_ITEM_COUNT = 1;
|
|
3041
2948
|
function toNode(genre, parentId) {
|
|
3042
2949
|
return {
|
|
@@ -3060,11 +2967,12 @@ function GenreDetailPanel({
|
|
|
3060
2967
|
collapsed,
|
|
3061
2968
|
expanded,
|
|
3062
2969
|
onExpandedChange,
|
|
2970
|
+
edgeTab,
|
|
3063
2971
|
extras
|
|
3064
2972
|
}) {
|
|
3065
2973
|
const { data: detail, isPending, isError } = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3066
2974
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3067
|
-
const view = (0,
|
|
2975
|
+
const view = (0, import_react17.useMemo)(() => {
|
|
3068
2976
|
if (!detail) return null;
|
|
3069
2977
|
const rootColor = (0, import_genre_tree_view5.getGenreTreeColor)(detail.root.wikidataId);
|
|
3070
2978
|
const relative = (genre, parentId) => ({
|
|
@@ -3072,7 +2980,7 @@ function GenreDetailPanel({
|
|
|
3072
2980
|
fill: (0, import_genre_tree_view5.tintSurface)(rootColor),
|
|
3073
2981
|
textColor: import_genre_tree_view5.TEXT_COLOR
|
|
3074
2982
|
});
|
|
3075
|
-
const
|
|
2983
|
+
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
3076
2984
|
return {
|
|
3077
2985
|
node: {
|
|
3078
2986
|
id: detail.uuid,
|
|
@@ -3084,57 +2992,72 @@ function GenreDetailPanel({
|
|
|
3084
2992
|
fill: rootColor,
|
|
3085
2993
|
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
3086
2994
|
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
3087
|
-
ancestorNodes:
|
|
3088
|
-
({ ascendant }, index) => relative(ascendant, index === 0 ? null :
|
|
2995
|
+
ancestorNodes: ancestors2.map(
|
|
2996
|
+
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
3089
2997
|
)
|
|
3090
2998
|
};
|
|
3091
2999
|
}, [detail]);
|
|
3092
3000
|
if (isError) {
|
|
3093
|
-
return /* @__PURE__ */ (0,
|
|
3001
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3094
3002
|
}
|
|
3095
3003
|
if (isPending || !detail || !view) {
|
|
3096
|
-
return /* @__PURE__ */ (0,
|
|
3097
|
-
/* @__PURE__ */ (0,
|
|
3098
|
-
/* @__PURE__ */ (0,
|
|
3099
|
-
/* @__PURE__ */ (0,
|
|
3004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3005
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui16.Skeleton, { className: "h-6 w-2/3" }),
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui16.Skeleton, { className: "h-4 w-full" }),
|
|
3007
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui16.Skeleton, { className: "h-4 w-3/4" })
|
|
3100
3008
|
] });
|
|
3101
3009
|
}
|
|
3102
|
-
const
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
{
|
|
3106
|
-
toolbar: sections.toolbar,
|
|
3107
|
-
family: sections.family,
|
|
3108
|
-
genreUuid: detail.uuid,
|
|
3109
|
-
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3110
|
-
wikidataId: detail.wikidataId,
|
|
3111
|
-
scope,
|
|
3112
|
-
getBackendBaseUrl,
|
|
3113
|
-
playback,
|
|
3114
|
-
band: { node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
3115
|
-
extras
|
|
3116
|
-
}
|
|
3117
|
-
),
|
|
3118
|
-
aside: null
|
|
3119
|
-
});
|
|
3120
|
-
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
3121
|
-
import_genre_tree_view5.InfoPanel,
|
|
3010
|
+
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3011
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
3012
|
+
import_genre_tree_view5.GenreNodeFamily,
|
|
3122
3013
|
{
|
|
3123
3014
|
node: view.node,
|
|
3124
3015
|
fill: view.fill,
|
|
3125
3016
|
textColor: import_genre_tree_view5.ACCENT_TEXT_COLOR,
|
|
3126
3017
|
parentNode: view.parentNode,
|
|
3127
3018
|
childNodes: view.childNodes,
|
|
3128
|
-
|
|
3019
|
+
onSelectNode: selectGenre
|
|
3020
|
+
}
|
|
3021
|
+
);
|
|
3022
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3023
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
3024
|
+
import_ui16.DetailPanel,
|
|
3025
|
+
{
|
|
3129
3026
|
side: "left",
|
|
3027
|
+
title: view.node.name,
|
|
3028
|
+
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3029
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
3030
|
+
import_ui16.IconButton,
|
|
3031
|
+
{
|
|
3032
|
+
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3033
|
+
style: { color: "inherit" },
|
|
3034
|
+
onClick: () => onExpandedChange(!expanded),
|
|
3035
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3036
|
+
}
|
|
3037
|
+
) : void 0,
|
|
3130
3038
|
onClose,
|
|
3131
|
-
|
|
3132
|
-
itemCountLabel: ITEM_COUNT_LABEL,
|
|
3133
|
-
renderInfoPanelBody: renderBody,
|
|
3039
|
+
collapsed,
|
|
3134
3040
|
expanded,
|
|
3135
3041
|
onExpandedChange,
|
|
3136
|
-
|
|
3137
|
-
|
|
3042
|
+
edgeTab,
|
|
3043
|
+
aside: ancestors,
|
|
3044
|
+
children: [
|
|
3045
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
3046
|
+
GenreInfoPanelBody,
|
|
3047
|
+
{
|
|
3048
|
+
family,
|
|
3049
|
+
genreUuid: detail.uuid,
|
|
3050
|
+
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3051
|
+
wikidataId: detail.wikidataId,
|
|
3052
|
+
scope,
|
|
3053
|
+
getBackendBaseUrl,
|
|
3054
|
+
playback,
|
|
3055
|
+
band: { node: view.node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
3056
|
+
extras
|
|
3057
|
+
}
|
|
3058
|
+
),
|
|
3059
|
+
!expanded && ancestors
|
|
3060
|
+
]
|
|
3138
3061
|
}
|
|
3139
3062
|
);
|
|
3140
3063
|
}
|
|
@@ -3143,13 +3066,13 @@ function GenreDetailPanel({
|
|
|
3143
3066
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3144
3067
|
|
|
3145
3068
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3146
|
-
var
|
|
3147
|
-
var
|
|
3069
|
+
var import_react18 = require("react");
|
|
3070
|
+
var import_lucide_react14 = require("lucide-react");
|
|
3148
3071
|
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3149
3072
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3150
|
-
var
|
|
3151
|
-
var
|
|
3152
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
3073
|
+
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3074
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
3075
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3153
3076
|
function TrackListSidebar({
|
|
3154
3077
|
className: className2 = "",
|
|
3155
3078
|
renderDuration,
|
|
@@ -3162,17 +3085,17 @@ function TrackListSidebar({
|
|
|
3162
3085
|
renderSongLink
|
|
3163
3086
|
}) {
|
|
3164
3087
|
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree21.useTrackList)();
|
|
3165
|
-
const trackNoun = (0,
|
|
3166
|
-
const trackListRef = (0,
|
|
3167
|
-
const previousOriginRef = (0,
|
|
3168
|
-
const handledAutoAdvanceRequestIdRef = (0,
|
|
3169
|
-
const shouldFollowPlaybackRef = (0,
|
|
3170
|
-
const programmaticScrollTopRef = (0,
|
|
3171
|
-
const userScrollIntentRef = (0,
|
|
3172
|
-
const userScrollIntentTimerRef = (0,
|
|
3173
|
-
const loadMoreSentinelRef = (0,
|
|
3088
|
+
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3089
|
+
const trackListRef = (0, import_react18.useRef)(null);
|
|
3090
|
+
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
3091
|
+
const handledAutoAdvanceRequestIdRef = (0, import_react18.useRef)(null);
|
|
3092
|
+
const shouldFollowPlaybackRef = (0, import_react18.useRef)(true);
|
|
3093
|
+
const programmaticScrollTopRef = (0, import_react18.useRef)(null);
|
|
3094
|
+
const userScrollIntentRef = (0, import_react18.useRef)(false);
|
|
3095
|
+
const userScrollIntentTimerRef = (0, import_react18.useRef)(null);
|
|
3096
|
+
const loadMoreSentinelRef = (0, import_react18.useRef)(null);
|
|
3174
3097
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
3175
|
-
(0,
|
|
3098
|
+
(0, import_react18.useEffect)(() => {
|
|
3176
3099
|
const sentinel = loadMoreSentinelRef.current;
|
|
3177
3100
|
if (!hasMore || !sentinel) return;
|
|
3178
3101
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -3192,11 +3115,11 @@ function TrackListSidebar({
|
|
|
3192
3115
|
programmaticScrollTopRef.current = null;
|
|
3193
3116
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
3194
3117
|
}, [trackList?.origin]);
|
|
3195
|
-
(0,
|
|
3118
|
+
(0, import_react18.useEffect)(() => {
|
|
3196
3119
|
const list = trackListRef.current;
|
|
3197
3120
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
3198
3121
|
}, [isCollapsed]);
|
|
3199
|
-
(0,
|
|
3122
|
+
(0, import_react18.useEffect)(
|
|
3200
3123
|
() => () => {
|
|
3201
3124
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
3202
3125
|
},
|
|
@@ -3250,25 +3173,25 @@ function TrackListSidebar({
|
|
|
3250
3173
|
}
|
|
3251
3174
|
};
|
|
3252
3175
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3253
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3176
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
3254
3177
|
"div",
|
|
3255
3178
|
{
|
|
3256
3179
|
"data-theme": "dark",
|
|
3257
3180
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3258
3181
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3259
3182
|
children: [
|
|
3260
|
-
/* @__PURE__ */ (0,
|
|
3261
|
-
/* @__PURE__ */ (0,
|
|
3262
|
-
/* @__PURE__ */ (0,
|
|
3263
|
-
/* @__PURE__ */ (0,
|
|
3264
|
-
/* @__PURE__ */ (0,
|
|
3183
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3184
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3185
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3186
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3187
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3265
3188
|
] }),
|
|
3266
|
-
/* @__PURE__ */ (0,
|
|
3189
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3267
3190
|
trackList.origin.type === import_genre_tree22.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree22.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3268
3191
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3269
3192
|
] })
|
|
3270
3193
|
] }),
|
|
3271
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3194
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
3272
3195
|
"button",
|
|
3273
3196
|
{
|
|
3274
3197
|
type: "button",
|
|
@@ -3277,11 +3200,11 @@ function TrackListSidebar({
|
|
|
3277
3200
|
"aria-expanded": !isCollapsed,
|
|
3278
3201
|
className: `ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-md hover:bg-[var(--color-surface-muted)] ${isCollapsed ? "text-[var(--color-text-muted)]" : "text-white"}`,
|
|
3279
3202
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3280
|
-
children: /* @__PURE__ */ (0,
|
|
3203
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react14.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3281
3204
|
}
|
|
3282
3205
|
)
|
|
3283
3206
|
] }),
|
|
3284
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3207
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
3285
3208
|
"ul",
|
|
3286
3209
|
{
|
|
3287
3210
|
ref: trackListRef,
|
|
@@ -3291,7 +3214,7 @@ function TrackListSidebar({
|
|
|
3291
3214
|
onPointerDown: markUserScrollIntent,
|
|
3292
3215
|
className: layout === "inline" ? "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0" : "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0",
|
|
3293
3216
|
children: [
|
|
3294
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3217
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
3295
3218
|
TrackItem,
|
|
3296
3219
|
{
|
|
3297
3220
|
track,
|
|
@@ -3302,7 +3225,7 @@ function TrackListSidebar({
|
|
|
3302
3225
|
renderSongLink
|
|
3303
3226
|
}
|
|
3304
3227
|
) }, track.uuid)),
|
|
3305
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3228
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3306
3229
|
]
|
|
3307
3230
|
}
|
|
3308
3231
|
)
|
|
@@ -3312,16 +3235,17 @@ function TrackListSidebar({
|
|
|
3312
3235
|
}
|
|
3313
3236
|
|
|
3314
3237
|
// src/genre-tree/PlayerPanel.tsx
|
|
3315
|
-
var
|
|
3316
|
-
var
|
|
3238
|
+
var import_react19 = require("react");
|
|
3239
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
3317
3240
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3318
3241
|
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3242
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
3319
3243
|
|
|
3320
3244
|
// src/genre-tree/PlayerControls.tsx
|
|
3321
|
-
var
|
|
3245
|
+
var import_lucide_react15 = require("lucide-react");
|
|
3322
3246
|
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3323
3247
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3324
|
-
var
|
|
3248
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
3325
3249
|
function PlayerControls() {
|
|
3326
3250
|
const {
|
|
3327
3251
|
duration,
|
|
@@ -3340,15 +3264,15 @@ function PlayerControls() {
|
|
|
3340
3264
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3341
3265
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3342
3266
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3343
|
-
return /* @__PURE__ */ (0,
|
|
3267
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
3344
3268
|
"section",
|
|
3345
3269
|
{
|
|
3346
3270
|
"aria-label": "Player controls",
|
|
3347
3271
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3348
3272
|
children: [
|
|
3349
|
-
/* @__PURE__ */ (0,
|
|
3350
|
-
/* @__PURE__ */ (0,
|
|
3351
|
-
/* @__PURE__ */ (0,
|
|
3273
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3274
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree23.formatTime)(safeCurrentTime) }),
|
|
3275
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3352
3276
|
"input",
|
|
3353
3277
|
{
|
|
3354
3278
|
"aria-label": "Seek",
|
|
@@ -3362,11 +3286,11 @@ function PlayerControls() {
|
|
|
3362
3286
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3363
3287
|
}
|
|
3364
3288
|
),
|
|
3365
|
-
/* @__PURE__ */ (0,
|
|
3289
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree23.formatTime)(safeDuration) })
|
|
3366
3290
|
] }),
|
|
3367
|
-
/* @__PURE__ */ (0,
|
|
3368
|
-
/* @__PURE__ */ (0,
|
|
3369
|
-
/* @__PURE__ */ (0,
|
|
3291
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3292
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3293
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3370
3294
|
"button",
|
|
3371
3295
|
{
|
|
3372
3296
|
type: "button",
|
|
@@ -3375,10 +3299,10 @@ function PlayerControls() {
|
|
|
3375
3299
|
disabled: !isReady || !hasPreviousTrack,
|
|
3376
3300
|
onClick: playPreviousTrack,
|
|
3377
3301
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
3378
|
-
children: /* @__PURE__ */ (0,
|
|
3302
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react15.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3379
3303
|
}
|
|
3380
3304
|
),
|
|
3381
|
-
/* @__PURE__ */ (0,
|
|
3305
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3382
3306
|
"button",
|
|
3383
3307
|
{
|
|
3384
3308
|
type: "button",
|
|
@@ -3387,10 +3311,10 @@ function PlayerControls() {
|
|
|
3387
3311
|
disabled: !isReady,
|
|
3388
3312
|
onClick: handlePlayPauseAction,
|
|
3389
3313
|
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white text-zinc-950 hover:bg-zinc-200 disabled:cursor-not-allowed disabled:opacity-40",
|
|
3390
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3314
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react15.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react15.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3391
3315
|
}
|
|
3392
3316
|
),
|
|
3393
|
-
/* @__PURE__ */ (0,
|
|
3317
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3394
3318
|
"button",
|
|
3395
3319
|
{
|
|
3396
3320
|
type: "button",
|
|
@@ -3399,14 +3323,14 @@ function PlayerControls() {
|
|
|
3399
3323
|
disabled: !isReady || !hasNextTrack,
|
|
3400
3324
|
onClick: playNextTrack,
|
|
3401
3325
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
3402
|
-
children: /* @__PURE__ */ (0,
|
|
3326
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react15.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3403
3327
|
}
|
|
3404
3328
|
)
|
|
3405
3329
|
] }),
|
|
3406
|
-
/* @__PURE__ */ (0,
|
|
3407
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3408
|
-
/* @__PURE__ */ (0,
|
|
3409
|
-
/* @__PURE__ */ (0,
|
|
3330
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3331
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react15.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react15.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3332
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3333
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3410
3334
|
"input",
|
|
3411
3335
|
{
|
|
3412
3336
|
"aria-label": "Volume",
|
|
@@ -3428,7 +3352,7 @@ function PlayerControls() {
|
|
|
3428
3352
|
}
|
|
3429
3353
|
|
|
3430
3354
|
// src/genre-tree/PlayerPanel.tsx
|
|
3431
|
-
var
|
|
3355
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
3432
3356
|
var PANEL_ID = "player-panel";
|
|
3433
3357
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3434
3358
|
function PlayerPanel({
|
|
@@ -3442,12 +3366,12 @@ function PlayerPanel({
|
|
|
3442
3366
|
}) {
|
|
3443
3367
|
const { playerTrackObject } = (0, import_player13.usePlayer)();
|
|
3444
3368
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree24.useTrackListSidebarVisibility)();
|
|
3445
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0,
|
|
3369
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3446
3370
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3447
|
-
(0,
|
|
3371
|
+
(0, import_react19.useEffect)(() => {
|
|
3448
3372
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3449
3373
|
}, [loadedTrackTitle]);
|
|
3450
|
-
const [uncontrolledExtended, setUncontrolledExtended] = (0,
|
|
3374
|
+
const [uncontrolledExtended, setUncontrolledExtended] = (0, import_react19.useState)(false);
|
|
3451
3375
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3452
3376
|
const setIsExtendRequested = (next) => {
|
|
3453
3377
|
setUncontrolledExtended(next);
|
|
@@ -3468,11 +3392,11 @@ function PlayerPanel({
|
|
|
3468
3392
|
onClick: () => setIsExtendRequested(true),
|
|
3469
3393
|
expanded: false
|
|
3470
3394
|
} : null;
|
|
3471
|
-
return /* @__PURE__ */ (0,
|
|
3395
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
3472
3396
|
"div",
|
|
3473
3397
|
{
|
|
3474
3398
|
id: PANEL_ID,
|
|
3475
|
-
className: (0,
|
|
3399
|
+
className: (0, import_tailwind_merge.twMerge)(
|
|
3476
3400
|
"fixed right-[var(--layout-gutter)] top-[var(--layout-band-top)] z-40 max-h-[calc(100vh-var(--layout-band-top)-3.5rem)]",
|
|
3477
3401
|
!isExtended && "w-[var(--layout-right-w)]",
|
|
3478
3402
|
isTrackListSidebarVisible && "h-[calc(100vh-var(--layout-band-top)-3.5rem)]",
|
|
@@ -3481,8 +3405,8 @@ function PlayerPanel({
|
|
|
3481
3405
|
),
|
|
3482
3406
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3483
3407
|
children: [
|
|
3484
|
-
/* @__PURE__ */ (0,
|
|
3485
|
-
PanelEdgeTab,
|
|
3408
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3409
|
+
import_ui17.PanelEdgeTab,
|
|
3486
3410
|
{
|
|
3487
3411
|
side: "right",
|
|
3488
3412
|
edge: tabEdge,
|
|
@@ -3491,30 +3415,30 @@ function PlayerPanel({
|
|
|
3491
3415
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3492
3416
|
}
|
|
3493
3417
|
),
|
|
3494
|
-
/* @__PURE__ */ (0,
|
|
3418
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
3495
3419
|
"div",
|
|
3496
3420
|
{
|
|
3497
|
-
className: (0,
|
|
3421
|
+
className: (0, import_tailwind_merge.twMerge)(
|
|
3498
3422
|
"flex h-full max-h-[inherit] overflow-hidden rounded-[var(--radius-panel,1rem)] bg-zinc-950 text-zinc-100 shadow-[0_1px_2px_rgba(0,0,0,0.5),0_2px_6px_2px_rgba(0,0,0,0.3)]",
|
|
3499
3423
|
isExtended ? "flex-row" : "flex-col"
|
|
3500
3424
|
),
|
|
3501
3425
|
children: [
|
|
3502
|
-
/* @__PURE__ */ (0,
|
|
3503
|
-
/* @__PURE__ */ (0,
|
|
3504
|
-
/* @__PURE__ */ (0,
|
|
3505
|
-
/* @__PURE__ */ (0,
|
|
3506
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3426
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3427
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3428
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3429
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3430
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: playerTrackObject.loadError })
|
|
3507
3431
|
] }),
|
|
3508
|
-
/* @__PURE__ */ (0,
|
|
3432
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(PlayerControls, {})
|
|
3509
3433
|
] }),
|
|
3510
|
-
/* @__PURE__ */ (0,
|
|
3434
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3511
3435
|
TrackListSidebar,
|
|
3512
3436
|
{
|
|
3513
3437
|
layout: "inline",
|
|
3514
3438
|
isCollapsed: !isTrackListSidebarVisible,
|
|
3515
3439
|
onExpand: toggleTrackListSidebar,
|
|
3516
3440
|
onMinimize: hideSongList,
|
|
3517
|
-
className: (0,
|
|
3441
|
+
className: (0, import_tailwind_merge.twMerge)(
|
|
3518
3442
|
"min-h-0 min-w-0 flex-1",
|
|
3519
3443
|
isExtended && "border-l border-zinc-800"
|
|
3520
3444
|
),
|
|
@@ -3534,11 +3458,11 @@ function PlayerPanel({
|
|
|
3534
3458
|
var import_uuid = require("uuid");
|
|
3535
3459
|
var import_ai = require("react-icons/ai");
|
|
3536
3460
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3537
|
-
var
|
|
3461
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
3538
3462
|
function Rating({ rating, handleChange }) {
|
|
3539
3463
|
const instanceId = (0, import_uuid.v4)();
|
|
3540
|
-
return /* @__PURE__ */ (0,
|
|
3541
|
-
/* @__PURE__ */ (0,
|
|
3464
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3465
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3542
3466
|
"input",
|
|
3543
3467
|
{
|
|
3544
3468
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3550,16 +3474,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3550
3474
|
onChange: handleChange
|
|
3551
3475
|
}
|
|
3552
3476
|
),
|
|
3553
|
-
/* @__PURE__ */ (0,
|
|
3554
|
-
/* @__PURE__ */ (0,
|
|
3477
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex", children: [
|
|
3478
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3555
3479
|
import_ai.AiOutlineStar,
|
|
3556
3480
|
{
|
|
3557
3481
|
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree25.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3558
3482
|
}
|
|
3559
3483
|
),
|
|
3560
|
-
/* @__PURE__ */ (0,
|
|
3484
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "flex-grow w-8" }),
|
|
3561
3485
|
" "
|
|
3562
|
-
] }) : /* @__PURE__ */ (0,
|
|
3486
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
3563
3487
|
import_ai.AiFillStar,
|
|
3564
3488
|
{
|
|
3565
3489
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3579,13 +3503,11 @@ function Rating({ rating, handleChange }) {
|
|
|
3579
3503
|
GenreTreeView,
|
|
3580
3504
|
GenreTreeViewSkeleton,
|
|
3581
3505
|
GenreTreeWheelSkeleton,
|
|
3582
|
-
PanelEdgeTab,
|
|
3583
3506
|
PlayerPanel,
|
|
3584
3507
|
Rating,
|
|
3585
3508
|
TrackItem,
|
|
3586
3509
|
TrackListSidebar,
|
|
3587
3510
|
TrackPositionPlayPause,
|
|
3588
|
-
useGenrePlaylistPlayback
|
|
3589
|
-
useLeftDetailPanelLayout
|
|
3511
|
+
useGenrePlaylistPlayback
|
|
3590
3512
|
});
|
|
3591
3513
|
//# sourceMappingURL=index.cjs.map
|