@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
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,6 @@ __export(src_exports, {
|
|
|
36
36
|
GenreTreeWheelSkeleton: () => import_genre_tree_view6.GenreTreeWheelSkeleton,
|
|
37
37
|
InternalErrorPopup: () => InternalErrorPopup,
|
|
38
38
|
NetworkErrorPopup: () => NetworkErrorPopup,
|
|
39
|
-
PanelEdgeTab: () => PanelEdgeTab,
|
|
40
39
|
PlayerPanel: () => PlayerPanel,
|
|
41
40
|
PopupButtons: () => PopupButtons,
|
|
42
41
|
PopupTitle: () => PopupTitle,
|
|
@@ -47,8 +46,7 @@ __export(src_exports, {
|
|
|
47
46
|
TrackListSidebar: () => TrackListSidebar,
|
|
48
47
|
TrackPositionPlayPause: () => TrackPositionPlayPause,
|
|
49
48
|
TrackUploadPopup: () => TrackUploadPopup,
|
|
50
|
-
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback
|
|
51
|
-
useLeftDetailPanelLayout: () => useLeftDetailPanelLayout
|
|
49
|
+
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback
|
|
52
50
|
});
|
|
53
51
|
module.exports = __toCommonJS(src_exports);
|
|
54
52
|
|
|
@@ -580,8 +578,9 @@ function AuthCallbackHandler({ renderStatus, ...options }) {
|
|
|
580
578
|
}
|
|
581
579
|
|
|
582
580
|
// src/genre-tree/GenreTreeView.tsx
|
|
583
|
-
var
|
|
581
|
+
var import_react16 = require("react");
|
|
584
582
|
var import_react_query3 = require("@tanstack/react-query");
|
|
583
|
+
var import_lucide_react15 = require("lucide-react");
|
|
585
584
|
var import_lucide_react16 = require("lucide-react");
|
|
586
585
|
var import_ui18 = require("@behindthemusictree/ui");
|
|
587
586
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
@@ -589,6 +588,32 @@ var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
589
588
|
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
590
589
|
var import_popup6 = require("@behindthemusictree/app-kit/popup");
|
|
591
590
|
|
|
591
|
+
// src/genre-tree/playlist-tree/size-metric.ts
|
|
592
|
+
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
593
|
+
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
594
|
+
if (metric === "songs") return genrePlaylist.tracksCount;
|
|
595
|
+
if (genrePlaylist.viewsCount === void 0) {
|
|
596
|
+
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
597
|
+
}
|
|
598
|
+
return genrePlaylist.viewsCount;
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/genre-tree/playlist-tree/toTreeNodes.ts
|
|
602
|
+
function toTreeNodes(genrePlaylists, sizeMetric) {
|
|
603
|
+
return genrePlaylists.map((genrePlaylist) => ({
|
|
604
|
+
id: genrePlaylist.uuid,
|
|
605
|
+
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
606
|
+
name: genrePlaylist.name,
|
|
607
|
+
itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
|
|
608
|
+
actionable: Boolean(genrePlaylist.criteria),
|
|
609
|
+
detached: !genrePlaylist.criteria,
|
|
610
|
+
side: genrePlaylist.criteria?.side ?? void 0,
|
|
611
|
+
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
612
|
+
umbrella: genrePlaylist.isUmbrella,
|
|
613
|
+
scene: genrePlaylist.scene ?? void 0
|
|
614
|
+
}));
|
|
615
|
+
}
|
|
616
|
+
|
|
592
617
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
593
618
|
var import_react4 = require("react");
|
|
594
619
|
var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
|
|
@@ -625,7 +650,6 @@ function useGenrePlaylistPlayback({
|
|
|
625
650
|
}
|
|
626
651
|
|
|
627
652
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
628
|
-
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
629
653
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
630
654
|
function GenrePlaylistTreePerRoot({
|
|
631
655
|
scope,
|
|
@@ -637,24 +661,19 @@ function GenrePlaylistTreePerRoot({
|
|
|
637
661
|
additionalActions,
|
|
638
662
|
onNodeClick,
|
|
639
663
|
onNodeHover,
|
|
640
|
-
renderInfoPanelBody,
|
|
641
664
|
selectedNodeId,
|
|
642
665
|
onSelectedNodeChange,
|
|
643
|
-
hideInfoPanelClose,
|
|
644
|
-
hideInfoPanelTitle,
|
|
645
666
|
readOnly = false,
|
|
646
|
-
showToolbar,
|
|
647
667
|
wheelZoom,
|
|
648
668
|
defaultViewport,
|
|
649
669
|
viewportRestore,
|
|
650
670
|
onViewportChange,
|
|
651
671
|
playback,
|
|
652
|
-
...
|
|
672
|
+
...hostPanel
|
|
653
673
|
}) {
|
|
654
674
|
const { isPlaying, setIsPlaying } = (0, import_player.usePlayer)();
|
|
655
675
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
|
|
656
676
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
657
|
-
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
658
677
|
const { showPopup } = (0, import_popup2.usePopup)();
|
|
659
678
|
const nodes = (0, import_react4.useMemo)(
|
|
660
679
|
() => genrePlaylistTreePerRoot.map((genrePlaylist) => ({
|
|
@@ -719,7 +738,6 @@ function GenrePlaylistTreePerRoot({
|
|
|
719
738
|
{
|
|
720
739
|
className: className2,
|
|
721
740
|
nodes,
|
|
722
|
-
itemCountLabel: `${(0, import_track_noun2.capitalize)(trackNoun.singular)} count`,
|
|
723
741
|
rootColor: (0, import_genre_tree_view.getGenreTreeColor)(rootUuid),
|
|
724
742
|
playingNodeId,
|
|
725
743
|
playState: isPlaying ? "playing" : "paused",
|
|
@@ -730,18 +748,13 @@ function GenrePlaylistTreePerRoot({
|
|
|
730
748
|
additionalActions,
|
|
731
749
|
onNodeClick,
|
|
732
750
|
onNodeHover,
|
|
733
|
-
renderInfoPanelBody,
|
|
734
751
|
selectedNodeId,
|
|
735
752
|
onSelectedNodeChange,
|
|
736
|
-
hideInfoPanelClose,
|
|
737
|
-
hideInfoPanelTitle,
|
|
738
|
-
showToolbar,
|
|
739
753
|
wheelZoom,
|
|
740
754
|
defaultViewport,
|
|
741
755
|
viewportRestore,
|
|
742
756
|
onViewportChange,
|
|
743
|
-
...
|
|
744
|
-
readOnly
|
|
757
|
+
...hostPanel
|
|
745
758
|
}
|
|
746
759
|
);
|
|
747
760
|
}
|
|
@@ -752,19 +765,6 @@ var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
|
|
|
752
765
|
var import_player2 = require("@behindthemusictree/app-kit/player");
|
|
753
766
|
var import_popup3 = require("@behindthemusictree/app-kit/popup");
|
|
754
767
|
var import_transport3 = require("@behindthemusictree/app-kit/transport");
|
|
755
|
-
|
|
756
|
-
// src/genre-tree/playlist-tree/size-metric.ts
|
|
757
|
-
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
758
|
-
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
759
|
-
if (metric === "songs") return genrePlaylist.tracksCount;
|
|
760
|
-
if (genrePlaylist.viewsCount === void 0) {
|
|
761
|
-
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
762
|
-
}
|
|
763
|
-
return genrePlaylist.viewsCount;
|
|
764
|
-
};
|
|
765
|
-
|
|
766
|
-
// src/genre-tree/playlist-tree/TreeWheel.tsx
|
|
767
|
-
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
768
768
|
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
769
769
|
function GenrePlaylistTreeWheel({
|
|
770
770
|
scope,
|
|
@@ -775,14 +775,10 @@ function GenrePlaylistTreeWheel({
|
|
|
775
775
|
additionalActions,
|
|
776
776
|
onNodeClick,
|
|
777
777
|
onNodeHover,
|
|
778
|
-
renderInfoPanelBody,
|
|
779
778
|
selectedNodeId,
|
|
780
779
|
onSelectedNodeChange,
|
|
781
|
-
hideInfoPanelClose,
|
|
782
|
-
hideInfoPanelTitle,
|
|
783
780
|
readOnly = false,
|
|
784
781
|
allowWheelRotation,
|
|
785
|
-
showToolbar,
|
|
786
782
|
sizeMetric = "songs",
|
|
787
783
|
floatingControlsLeading,
|
|
788
784
|
busy,
|
|
@@ -790,12 +786,11 @@ function GenrePlaylistTreeWheel({
|
|
|
790
786
|
viewportRestore,
|
|
791
787
|
onViewportChange,
|
|
792
788
|
playback,
|
|
793
|
-
...
|
|
789
|
+
...hostPanel
|
|
794
790
|
}) {
|
|
795
791
|
const { isPlaying, setIsPlaying } = (0, import_player2.usePlayer)();
|
|
796
792
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
|
|
797
793
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
798
|
-
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
799
794
|
const { showPopup } = (0, import_popup3.usePopup)();
|
|
800
795
|
const nodes = (0, import_react5.useMemo)(
|
|
801
796
|
() => genrePlaylists.map((genrePlaylist) => ({
|
|
@@ -859,7 +854,6 @@ function GenrePlaylistTreeWheel({
|
|
|
859
854
|
{
|
|
860
855
|
className: className2,
|
|
861
856
|
nodes,
|
|
862
|
-
itemCountLabel: sizeMetric === "views" ? "Views" : `${(0, import_track_noun3.capitalize)(trackNoun.singular)} count`,
|
|
863
857
|
playingNodeId,
|
|
864
858
|
playState: isPlaying ? "playing" : "paused",
|
|
865
859
|
onPlayPause: handlePlayPause,
|
|
@@ -869,20 +863,15 @@ function GenrePlaylistTreeWheel({
|
|
|
869
863
|
additionalActions,
|
|
870
864
|
onNodeClick,
|
|
871
865
|
onNodeHover,
|
|
872
|
-
renderInfoPanelBody,
|
|
873
866
|
selectedNodeId,
|
|
874
867
|
onSelectedNodeChange,
|
|
875
|
-
hideInfoPanelClose,
|
|
876
|
-
hideInfoPanelTitle,
|
|
877
868
|
allowWheelRotation,
|
|
878
|
-
showToolbar,
|
|
879
869
|
floatingControlsLeading,
|
|
880
870
|
busy,
|
|
881
871
|
defaultViewport,
|
|
882
872
|
viewportRestore,
|
|
883
873
|
onViewportChange,
|
|
884
|
-
...
|
|
885
|
-
readOnly
|
|
874
|
+
...hostPanel
|
|
886
875
|
}
|
|
887
876
|
);
|
|
888
877
|
}
|
|
@@ -891,7 +880,6 @@ function GenrePlaylistTreeWheel({
|
|
|
891
880
|
var import_react6 = require("react");
|
|
892
881
|
var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
|
|
893
882
|
var import_player3 = require("@behindthemusictree/app-kit/player");
|
|
894
|
-
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
895
883
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
896
884
|
function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
897
885
|
const {
|
|
@@ -903,14 +891,10 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
903
891
|
additionalActions,
|
|
904
892
|
onNodeClick,
|
|
905
893
|
onNodeHover,
|
|
906
|
-
renderInfoPanelBody,
|
|
907
894
|
selectedNodeId,
|
|
908
895
|
onSelectedNodeChange,
|
|
909
|
-
hideInfoPanelClose,
|
|
910
|
-
hideInfoPanelTitle,
|
|
911
896
|
readOnly = false,
|
|
912
897
|
allowWheelRotation,
|
|
913
|
-
showToolbar,
|
|
914
898
|
sizeMetric = "songs",
|
|
915
899
|
floatingControlsLeading,
|
|
916
900
|
busy,
|
|
@@ -921,33 +905,14 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
921
905
|
playback
|
|
922
906
|
} = props;
|
|
923
907
|
const focus = props;
|
|
924
|
-
const
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
onExpandedChange: props.onExpandedChange,
|
|
928
|
-
collapsed: props.collapsed,
|
|
929
|
-
hideExpandButton: props.hideExpandButton,
|
|
930
|
-
overlayPanel: props.overlayPanel
|
|
908
|
+
const hostPanel = {
|
|
909
|
+
obscuredInset: props.obscuredInset,
|
|
910
|
+
onNodeStyleResolverChange: props.onNodeStyleResolverChange
|
|
931
911
|
};
|
|
932
912
|
const { isPlaying, setIsPlaying } = (0, import_player3.usePlayer)();
|
|
933
913
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
|
|
934
914
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
935
|
-
const
|
|
936
|
-
const nodes = (0, import_react6.useMemo)(
|
|
937
|
-
() => genrePlaylists.map((genrePlaylist) => ({
|
|
938
|
-
id: genrePlaylist.uuid,
|
|
939
|
-
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
940
|
-
name: genrePlaylist.name,
|
|
941
|
-
itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
|
|
942
|
-
actionable: Boolean(genrePlaylist.criteria),
|
|
943
|
-
detached: !genrePlaylist.criteria,
|
|
944
|
-
side: genrePlaylist.criteria?.side ?? void 0,
|
|
945
|
-
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
946
|
-
umbrella: genrePlaylist.isUmbrella,
|
|
947
|
-
scene: genrePlaylist.scene ?? void 0
|
|
948
|
-
})),
|
|
949
|
-
[genrePlaylists, sizeMetric]
|
|
950
|
-
);
|
|
915
|
+
const nodes = (0, import_react6.useMemo)(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
|
|
951
916
|
const handlePlayPause = (0, import_react6.useCallback)(
|
|
952
917
|
(nodeId) => {
|
|
953
918
|
const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
|
|
@@ -988,7 +953,6 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
988
953
|
const treeProps = {
|
|
989
954
|
className: className2,
|
|
990
955
|
nodes,
|
|
991
|
-
itemCountLabel: sizeMetric === "views" ? "Views" : `${(0, import_track_noun4.capitalize)(trackNoun.singular)} count`,
|
|
992
956
|
playingNodeId,
|
|
993
957
|
playState: isPlaying ? "playing" : "paused",
|
|
994
958
|
onPlayPause: handlePlayPause,
|
|
@@ -998,14 +962,9 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
998
962
|
additionalActions,
|
|
999
963
|
onNodeClick,
|
|
1000
964
|
onNodeHover,
|
|
1001
|
-
renderInfoPanelBody,
|
|
1002
965
|
selectedNodeId,
|
|
1003
966
|
onSelectedNodeChange,
|
|
1004
|
-
|
|
1005
|
-
hideInfoPanelTitle,
|
|
1006
|
-
showToolbar,
|
|
1007
|
-
...infoPanelCuration,
|
|
1008
|
-
readOnly
|
|
967
|
+
...hostPanel
|
|
1009
968
|
};
|
|
1010
969
|
return outline ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_genre_tree_view3.GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
1011
970
|
import_genre_tree_view3.GenreTreeWheelRadialPopCore,
|
|
@@ -1125,7 +1084,7 @@ function GenreSearch({
|
|
|
1125
1084
|
type: "button",
|
|
1126
1085
|
"aria-label": "Previous",
|
|
1127
1086
|
title: "Previous",
|
|
1128
|
-
className: "genre-search-previous absolute left-2 top-1/2 z-10
|
|
1087
|
+
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)]",
|
|
1129
1088
|
onClick: onPrevious,
|
|
1130
1089
|
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1131
1090
|
}
|
|
@@ -1147,7 +1106,7 @@ function GenreSearch({
|
|
|
1147
1106
|
onPick: handlePick,
|
|
1148
1107
|
status,
|
|
1149
1108
|
loading: loading ? "Searching\u2026" : void 0,
|
|
1150
|
-
inputClassName:
|
|
1109
|
+
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`,
|
|
1151
1110
|
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
1152
1111
|
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
1153
1112
|
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
@@ -1584,7 +1543,7 @@ function MusicBrainzRecordingLink({
|
|
|
1584
1543
|
|
|
1585
1544
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1586
1545
|
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1587
|
-
var
|
|
1546
|
+
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1588
1547
|
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1589
1548
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1590
1549
|
var SongPageSchema = (0, import_transport5.PaginatedResponseSchema)(import_genre_tree6.SongDetailedSchema).extend({
|
|
@@ -1618,7 +1577,7 @@ function GenrePlaylistTracks({
|
|
|
1618
1577
|
const { session, sessionRestored } = (0, import_auth2.useSession)();
|
|
1619
1578
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1620
1579
|
const { showPopup } = (0, import_popup5.usePopup)();
|
|
1621
|
-
const trackNoun = (0,
|
|
1580
|
+
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1622
1581
|
const { uuid } = genrePlaylist;
|
|
1623
1582
|
const { fetch } = (0, import_transport4.useFetchWrapper)(getBackendBaseUrl);
|
|
1624
1583
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
@@ -1639,9 +1598,8 @@ function GenrePlaylistTracks({
|
|
|
1639
1598
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1640
1599
|
enabled: scope !== "me" || sessionRestored && !!session?.accessToken
|
|
1641
1600
|
});
|
|
1642
|
-
const
|
|
1601
|
+
const [sentinel, setSentinel] = (0, import_react11.useState)(null);
|
|
1643
1602
|
(0, import_react11.useEffect)(() => {
|
|
1644
|
-
const sentinel = sentinelRef.current;
|
|
1645
1603
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1646
1604
|
const observer = new IntersectionObserver((entries) => {
|
|
1647
1605
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
@@ -1650,7 +1608,7 @@ function GenrePlaylistTracks({
|
|
|
1650
1608
|
});
|
|
1651
1609
|
observer.observe(sentinel);
|
|
1652
1610
|
return () => observer.disconnect();
|
|
1653
|
-
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
|
1611
|
+
}, [hasNextPage, isFetchingNextPage, fetchNextPage, sentinel]);
|
|
1654
1612
|
const pages = data?.pages ?? [];
|
|
1655
1613
|
const tracks = pages.flatMap((page) => page.tracks);
|
|
1656
1614
|
const total = pages[0]?.total ?? void 0;
|
|
@@ -1661,11 +1619,7 @@ function GenrePlaylistTracks({
|
|
|
1661
1619
|
showPopup(/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(InternalErrorPopup, { errorCode: import_transport7.ErrorCode.CLIENT_UNKNOWN }));
|
|
1662
1620
|
});
|
|
1663
1621
|
};
|
|
1664
|
-
return /* @__PURE__ */ (0, import_jsx_runtime24.
|
|
1665
|
-
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("span", { className: "gtv-info-panel-children-title", children: [
|
|
1666
|
-
(0, import_track_noun5.capitalize)(trackNoun.plural),
|
|
1667
|
-
total !== void 0 && ` (${total})`
|
|
1668
|
-
] }),
|
|
1622
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui7.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { children: [
|
|
1669
1623
|
renderTracksToolbar?.(),
|
|
1670
1624
|
isPending ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui7.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("p", { role: "alert", children: [
|
|
1671
1625
|
"Couldn't load ",
|
|
@@ -1728,13 +1682,13 @@ function GenrePlaylistTracks({
|
|
|
1728
1682
|
track.uuid
|
|
1729
1683
|
);
|
|
1730
1684
|
}),
|
|
1731
|
-
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { ref:
|
|
1685
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1732
1686
|
] })
|
|
1733
|
-
] });
|
|
1687
|
+
] }) });
|
|
1734
1688
|
}
|
|
1735
1689
|
|
|
1736
1690
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1737
|
-
var
|
|
1691
|
+
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
1738
1692
|
var import_si2 = require("react-icons/si");
|
|
1739
1693
|
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1740
1694
|
function GenreDetailExtras({
|
|
@@ -1752,7 +1706,7 @@ function GenreDetailExtras({
|
|
|
1752
1706
|
family,
|
|
1753
1707
|
...trackSlots
|
|
1754
1708
|
}) {
|
|
1755
|
-
const trackNoun = (0,
|
|
1709
|
+
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
1756
1710
|
const { data: overview, isPending } = (0, import_genre_tree9.useFetchGenreOverview)(
|
|
1757
1711
|
genreUuid,
|
|
1758
1712
|
scope,
|
|
@@ -1761,10 +1715,7 @@ function GenreDetailExtras({
|
|
|
1761
1715
|
);
|
|
1762
1716
|
if (isPending) {
|
|
1763
1717
|
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
1764
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.
|
|
1765
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "gtv-info-panel-children-title", children: "Summary" }),
|
|
1766
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4" })
|
|
1767
|
-
] }),
|
|
1718
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1768
1719
|
family
|
|
1769
1720
|
] });
|
|
1770
1721
|
}
|
|
@@ -1772,50 +1723,38 @@ function GenreDetailExtras({
|
|
|
1772
1723
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1773
1724
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1774
1725
|
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
1775
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "
|
|
1776
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
] })
|
|
1805
|
-
] }),
|
|
1806
|
-
renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "gtv-info-panel-children text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: [
|
|
1807
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "gtv-info-panel-children-title", children: "Summary" }),
|
|
1808
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ClampedText, { className: "mt-1", children: summary ?? "\u2014" }, genreUuid)
|
|
1809
|
-
] }),
|
|
1726
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1727
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1728
|
+
"a",
|
|
1729
|
+
{
|
|
1730
|
+
href: summarySourceUrl,
|
|
1731
|
+
target: "_blank",
|
|
1732
|
+
rel: "noopener noreferrer",
|
|
1733
|
+
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1734
|
+
children: [
|
|
1735
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1736
|
+
"Wikipedia"
|
|
1737
|
+
]
|
|
1738
|
+
}
|
|
1739
|
+
),
|
|
1740
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1741
|
+
"a",
|
|
1742
|
+
{
|
|
1743
|
+
href: wikidataUrl,
|
|
1744
|
+
target: "_blank",
|
|
1745
|
+
rel: "noopener noreferrer",
|
|
1746
|
+
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1747
|
+
children: [
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1749
|
+
"Wikidata"
|
|
1750
|
+
]
|
|
1751
|
+
}
|
|
1752
|
+
)
|
|
1753
|
+
] }) }),
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1810
1755
|
family,
|
|
1811
1756
|
renderGenreDetailExtras?.(overview),
|
|
1812
|
-
renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && /* @__PURE__ */ (0, import_jsx_runtime25.
|
|
1813
|
-
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("span", { className: "gtv-info-panel-children-title", children: [
|
|
1814
|
-
"Essential ",
|
|
1815
|
-
trackNoun.plural
|
|
1816
|
-
] }),
|
|
1817
|
-
essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { className: "mt-1 divide-y divide-[var(--color-border)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "mt-1", children: "\u2014" })
|
|
1818
|
-
] }),
|
|
1757
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_ui8.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1819
1758
|
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1820
1759
|
GenrePlaylistTracks,
|
|
1821
1760
|
{
|
|
@@ -1886,66 +1825,10 @@ function GenreInfoPanelBody({
|
|
|
1886
1825
|
] });
|
|
1887
1826
|
}
|
|
1888
1827
|
|
|
1889
|
-
// src/genre-tree/PanelEdgeTab.tsx
|
|
1890
|
-
var import_lucide_react12 = require("lucide-react");
|
|
1891
|
-
var import_tailwind_merge = require("tailwind-merge");
|
|
1892
|
-
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1893
|
-
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)]";
|
|
1894
|
-
function PanelEdgeTab({ side, edge, center, controls, className: className2 }) {
|
|
1895
|
-
if (!edge && !center) return null;
|
|
1896
|
-
const isLeft = side === "left";
|
|
1897
|
-
const EdgeIcon = isLeft ? import_lucide_react12.ChevronLeft : import_lucide_react12.ChevronRight;
|
|
1898
|
-
const CenterIcon = isLeft ? import_lucide_react12.ChevronRight : import_lucide_react12.ChevronLeft;
|
|
1899
|
-
const renderButton = (action, icon) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1900
|
-
"button",
|
|
1901
|
-
{
|
|
1902
|
-
type: "button",
|
|
1903
|
-
"aria-label": action.label,
|
|
1904
|
-
title: action.label,
|
|
1905
|
-
"aria-controls": controls,
|
|
1906
|
-
"aria-expanded": action.expanded,
|
|
1907
|
-
className: buttonClassName,
|
|
1908
|
-
onClick: action.onClick,
|
|
1909
|
-
children: icon
|
|
1910
|
-
}
|
|
1911
|
-
);
|
|
1912
|
-
const edgeButton = edge && renderButton(edge, /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(EdgeIcon, { "aria-hidden": "true", className: "h-5 w-5" }));
|
|
1913
|
-
const centerButton = center && renderButton(center, /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(CenterIcon, { "aria-hidden": "true", className: "h-5 w-5" }));
|
|
1914
|
-
const buttons = isLeft ? [edgeButton, centerButton] : [centerButton, edgeButton];
|
|
1915
|
-
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
|
|
1916
|
-
"div",
|
|
1917
|
-
{
|
|
1918
|
-
className: (0, import_tailwind_merge.twMerge)(
|
|
1919
|
-
"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)]",
|
|
1920
|
-
isLeft ? "rounded-r-lg" : "rounded-l-lg [clip-path:inset(-0.5rem_0_-0.5rem_-0.5rem)]",
|
|
1921
|
-
className2
|
|
1922
|
-
),
|
|
1923
|
-
children: [
|
|
1924
|
-
buttons[0],
|
|
1925
|
-
buttons[0] && buttons[1] && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { role: "presentation", className: "h-px shrink-0 bg-[var(--color-border)]" }),
|
|
1926
|
-
buttons[1]
|
|
1927
|
-
]
|
|
1928
|
-
}
|
|
1929
|
-
);
|
|
1930
|
-
}
|
|
1931
|
-
|
|
1932
|
-
// src/genre-tree/useLeftDetailPanelLayout.ts
|
|
1933
|
-
var import_react13 = require("react");
|
|
1934
|
-
function useLeftDetailPanelLayout(isPanelShown, enabled = true) {
|
|
1935
|
-
(0, import_react13.useEffect)(() => {
|
|
1936
|
-
if (!enabled || isPanelShown) return void 0;
|
|
1937
|
-
const root = document.documentElement;
|
|
1938
|
-
root.style.setProperty("--layout-left-w", "0px");
|
|
1939
|
-
return () => {
|
|
1940
|
-
root.style.removeProperty("--layout-left-w");
|
|
1941
|
-
};
|
|
1942
|
-
}, [enabled, isPanelShown]);
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
1828
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1946
|
-
var
|
|
1829
|
+
var import_react13 = require("react");
|
|
1947
1830
|
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1948
|
-
var
|
|
1831
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1949
1832
|
function ChangeParentPopup({
|
|
1950
1833
|
genrePlaylist,
|
|
1951
1834
|
genrePlaylists,
|
|
@@ -1955,7 +1838,7 @@ function ChangeParentPopup({
|
|
|
1955
1838
|
onParentChanged
|
|
1956
1839
|
}) {
|
|
1957
1840
|
const { mutate, isPending, formErrors } = (0, import_genre_tree10.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1958
|
-
const candidates = (0,
|
|
1841
|
+
const candidates = (0, import_react13.useMemo)(
|
|
1959
1842
|
() => (0, import_genre_tree10.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1960
1843
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1961
1844
|
);
|
|
@@ -1973,19 +1856,19 @@ function ChangeParentPopup({
|
|
|
1973
1856
|
}
|
|
1974
1857
|
);
|
|
1975
1858
|
};
|
|
1976
|
-
(0,
|
|
1859
|
+
(0, import_react13.useEffect)(() => {
|
|
1977
1860
|
const handleKeyDown = (event) => {
|
|
1978
1861
|
if (event.key === "Escape") onClose();
|
|
1979
1862
|
};
|
|
1980
1863
|
window.addEventListener("keydown", handleKeyDown);
|
|
1981
1864
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1982
1865
|
}, [onClose]);
|
|
1983
|
-
return /* @__PURE__ */ (0,
|
|
1984
|
-
/* @__PURE__ */ (0,
|
|
1866
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1867
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1985
1868
|
"Current parent: ",
|
|
1986
1869
|
genrePlaylist.parent?.name ?? "none"
|
|
1987
1870
|
] }),
|
|
1988
|
-
/* @__PURE__ */ (0,
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1989
1872
|
GenreSearch,
|
|
1990
1873
|
{
|
|
1991
1874
|
genrePlaylists: candidates,
|
|
@@ -1993,7 +1876,7 @@ function ChangeParentPopup({
|
|
|
1993
1876
|
placeholder: "Search a new parent\u2026"
|
|
1994
1877
|
}
|
|
1995
1878
|
) }),
|
|
1996
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1879
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1997
1880
|
] });
|
|
1998
1881
|
}
|
|
1999
1882
|
|
|
@@ -2005,12 +1888,12 @@ var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
2005
1888
|
var import_ui10 = require("@behindthemusictree/ui");
|
|
2006
1889
|
|
|
2007
1890
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
2008
|
-
var
|
|
1891
|
+
var import_lucide_react12 = require("lucide-react");
|
|
2009
1892
|
var import_si3 = require("react-icons/si");
|
|
2010
1893
|
var import_ui9 = require("@behindthemusictree/ui");
|
|
2011
|
-
var
|
|
1894
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
2012
1895
|
var LINK_KINDS = {
|
|
2013
|
-
official: { label: "Official website", Icon:
|
|
1896
|
+
official: { label: "Official website", Icon: import_lucide_react12.Globe },
|
|
2014
1897
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
2015
1898
|
wikidata: { label: "Wikidata", Icon: import_si3.SiWikidata },
|
|
2016
1899
|
spotify: { label: "Spotify", Icon: import_si3.SiSpotify },
|
|
@@ -2018,7 +1901,7 @@ var LINK_KINDS = {
|
|
|
2018
1901
|
bandcamp: { label: "Bandcamp", Icon: import_si3.SiBandcamp },
|
|
2019
1902
|
soundcloud: { label: "SoundCloud", Icon: import_si3.SiSoundcloud },
|
|
2020
1903
|
discogs: { label: "Discogs", Icon: import_si3.SiDiscogs },
|
|
2021
|
-
social: { label: "Social", Icon:
|
|
1904
|
+
social: { label: "Social", Icon: import_lucide_react12.Share2 }
|
|
2022
1905
|
};
|
|
2023
1906
|
function socialLabel(url) {
|
|
2024
1907
|
try {
|
|
@@ -2045,7 +1928,7 @@ function artistLinks(artist) {
|
|
|
2045
1928
|
return links;
|
|
2046
1929
|
}
|
|
2047
1930
|
function ArtistLinks({ links }) {
|
|
2048
|
-
return /* @__PURE__ */ (0,
|
|
1931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui9.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2049
1932
|
}
|
|
2050
1933
|
|
|
2051
1934
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2056,7 +1939,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2056
1939
|
}
|
|
2057
1940
|
|
|
2058
1941
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2059
|
-
var
|
|
1942
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
2060
1943
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2061
1944
|
function ArtistHeader({ artist }) {
|
|
2062
1945
|
const fields = [];
|
|
@@ -2066,35 +1949,35 @@ function ArtistHeader({ artist }) {
|
|
|
2066
1949
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2067
1950
|
if (years) fields.push({ label: "Active", value: years });
|
|
2068
1951
|
const links = artistLinks(artist);
|
|
2069
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
1952
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ArtistLinks, { links }) });
|
|
2070
1953
|
if (fields.length === 0) return null;
|
|
2071
|
-
return /* @__PURE__ */ (0,
|
|
1954
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui10.FieldList, { items: fields });
|
|
2072
1955
|
}
|
|
2073
1956
|
|
|
2074
1957
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2075
|
-
var
|
|
1958
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
2076
1959
|
function ExternalLink({ href, children }) {
|
|
2077
|
-
return /* @__PURE__ */ (0,
|
|
1960
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
2078
1961
|
}
|
|
2079
1962
|
function ArtistPhoto({ image, name }) {
|
|
2080
|
-
return /* @__PURE__ */ (0,
|
|
2081
|
-
/* @__PURE__ */ (0,
|
|
2082
|
-
/* @__PURE__ */ (0,
|
|
1963
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1964
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1965
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
2083
1966
|
"Photo: ",
|
|
2084
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
2085
1968
|
",",
|
|
2086
1969
|
" ",
|
|
2087
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
1970
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
2088
1971
|
] })
|
|
2089
1972
|
] });
|
|
2090
1973
|
}
|
|
2091
1974
|
|
|
2092
1975
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2093
1976
|
var import_ui11 = require("@behindthemusictree/ui");
|
|
2094
|
-
var
|
|
1977
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
2095
1978
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2096
1979
|
if (memberships.length === 0) return null;
|
|
2097
|
-
return /* @__PURE__ */ (0,
|
|
1980
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui11.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2098
1981
|
const details = [
|
|
2099
1982
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2100
1983
|
...membership.roles
|
|
@@ -2102,9 +1985,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2102
1985
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
2103
1986
|
return (
|
|
2104
1987
|
// A member can have several periods in the same group.
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
2106
|
-
/* @__PURE__ */ (0,
|
|
2107
|
-
detailsText && /* @__PURE__ */ (0,
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1990
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2108
1991
|
" \u2014 ",
|
|
2109
1992
|
detailsText
|
|
2110
1993
|
] })
|
|
@@ -2115,9 +1998,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2115
1998
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2116
1999
|
if (memberships.length === 0) return null;
|
|
2117
2000
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2118
|
-
return /* @__PURE__ */ (0,
|
|
2119
|
-
/* @__PURE__ */ (0,
|
|
2120
|
-
/* @__PURE__ */ (0,
|
|
2001
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_ui11.PanelSection, { title, children: [
|
|
2002
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
2121
2004
|
MembershipList,
|
|
2122
2005
|
{
|
|
2123
2006
|
title: "Past",
|
|
@@ -2130,7 +2013,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2130
2013
|
|
|
2131
2014
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2132
2015
|
var import_ui12 = require("@behindthemusictree/ui");
|
|
2133
|
-
var
|
|
2016
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
2134
2017
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2135
2018
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2136
2019
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2150,20 +2033,20 @@ function byYear(a, b) {
|
|
|
2150
2033
|
function ArtistDiscography({ albums }) {
|
|
2151
2034
|
if (albums.length === 0) return null;
|
|
2152
2035
|
const sorted = [...albums].sort(byYear);
|
|
2153
|
-
return /* @__PURE__ */ (0,
|
|
2036
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui12.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2154
2037
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2155
2038
|
if (groupAlbums.length === 0) return null;
|
|
2156
|
-
return /* @__PURE__ */ (0,
|
|
2157
|
-
/* @__PURE__ */ (0,
|
|
2158
|
-
/* @__PURE__ */ (0,
|
|
2039
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui12.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2040
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2041
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2159
2042
|
] }, album.uuid)) }) }, group);
|
|
2160
2043
|
}) });
|
|
2161
2044
|
}
|
|
2162
2045
|
|
|
2163
2046
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2164
|
-
var
|
|
2047
|
+
var import_react14 = require("react");
|
|
2165
2048
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2166
|
-
var
|
|
2049
|
+
var import_lucide_react13 = require("lucide-react");
|
|
2167
2050
|
var import_ui13 = require("@behindthemusictree/ui");
|
|
2168
2051
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2169
2052
|
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
@@ -2177,7 +2060,7 @@ var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
2177
2060
|
var import_fa5 = require("react-icons/fa");
|
|
2178
2061
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2179
2062
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2180
|
-
var
|
|
2063
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
2181
2064
|
function TrackPositionPlayPause({
|
|
2182
2065
|
position,
|
|
2183
2066
|
uuid,
|
|
@@ -2191,14 +2074,14 @@ function TrackPositionPlayPause({
|
|
|
2191
2074
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
2192
2075
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
2193
2076
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2194
|
-
return /* @__PURE__ */ (0,
|
|
2077
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
2195
2078
|
"div",
|
|
2196
2079
|
{
|
|
2197
2080
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2198
2081
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2199
2082
|
onClick: handlePlayPauseClick,
|
|
2200
2083
|
children: [
|
|
2201
|
-
/* @__PURE__ */ (0,
|
|
2084
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
2202
2085
|
"span",
|
|
2203
2086
|
{
|
|
2204
2087
|
className: "track-position-play-pause__bars",
|
|
@@ -2206,18 +2089,18 @@ function TrackPositionPlayPause({
|
|
|
2206
2089
|
role: "img",
|
|
2207
2090
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2208
2091
|
children: [
|
|
2209
|
-
/* @__PURE__ */ (0,
|
|
2210
|
-
/* @__PURE__ */ (0,
|
|
2211
|
-
/* @__PURE__ */ (0,
|
|
2092
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2212
2095
|
]
|
|
2213
2096
|
}
|
|
2214
2097
|
) : position }),
|
|
2215
|
-
/* @__PURE__ */ (0,
|
|
2098
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
2216
2099
|
"div",
|
|
2217
2100
|
{
|
|
2218
2101
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2219
2102
|
"aria-hidden": "true",
|
|
2220
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2103
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_fa5.FaPlay, {})
|
|
2221
2104
|
}
|
|
2222
2105
|
)
|
|
2223
2106
|
]
|
|
@@ -2228,7 +2111,7 @@ function TrackPositionPlayPause({
|
|
|
2228
2111
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2229
2112
|
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2230
2113
|
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2231
|
-
var
|
|
2114
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
2232
2115
|
function TrackItem({
|
|
2233
2116
|
track,
|
|
2234
2117
|
position,
|
|
@@ -2252,12 +2135,12 @@ function TrackItem({
|
|
|
2252
2135
|
loadTrackForPlayer(track.uuid);
|
|
2253
2136
|
}
|
|
2254
2137
|
};
|
|
2255
|
-
return /* @__PURE__ */ (0,
|
|
2138
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
2256
2139
|
"div",
|
|
2257
2140
|
{
|
|
2258
2141
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2259
2142
|
children: [
|
|
2260
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2143
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2261
2144
|
"div",
|
|
2262
2145
|
{
|
|
2263
2146
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2265,7 +2148,7 @@ function TrackItem({
|
|
|
2265
2148
|
"aria-disabled": "true",
|
|
2266
2149
|
children: position
|
|
2267
2150
|
}
|
|
2268
|
-
) : /* @__PURE__ */ (0,
|
|
2151
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2269
2152
|
TrackPositionPlayPause,
|
|
2270
2153
|
{
|
|
2271
2154
|
position,
|
|
@@ -2274,12 +2157,12 @@ function TrackItem({
|
|
|
2274
2157
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2275
2158
|
}
|
|
2276
2159
|
),
|
|
2277
|
-
/* @__PURE__ */ (0,
|
|
2278
|
-
/* @__PURE__ */ (0,
|
|
2279
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2160
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2161
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime34.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_runtime34.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2162
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_genre_tree13.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2280
2163
|
] }),
|
|
2281
|
-
/* @__PURE__ */ (0,
|
|
2282
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2164
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.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_runtime34.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2165
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2283
2166
|
"div",
|
|
2284
2167
|
{
|
|
2285
2168
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2287,22 +2170,22 @@ function TrackItem({
|
|
|
2287
2170
|
children: renderDuration(track)
|
|
2288
2171
|
}
|
|
2289
2172
|
),
|
|
2290
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2173
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2291
2174
|
]
|
|
2292
2175
|
}
|
|
2293
2176
|
);
|
|
2294
2177
|
}
|
|
2295
2178
|
|
|
2296
2179
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2297
|
-
var
|
|
2180
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2298
2181
|
var SongPageSchema2 = (0, import_transport8.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema);
|
|
2299
2182
|
var SORTS = [
|
|
2300
2183
|
{ ordering: "title", label: "Title" },
|
|
2301
2184
|
{ ordering: "-albumYear", label: "Year" }
|
|
2302
2185
|
];
|
|
2303
2186
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
2304
|
-
const [page, setPage] = (0,
|
|
2305
|
-
const [ordering, setOrdering] = (0,
|
|
2187
|
+
const [page, setPage] = (0, import_react14.useState)(1);
|
|
2188
|
+
const [ordering, setOrdering] = (0, import_react14.useState)("title");
|
|
2306
2189
|
const { fetch } = (0, import_transport8.useFetchWrapper)(getBackendBaseUrl);
|
|
2307
2190
|
const { playNewTrackListFromArtist } = (0, import_genre_tree14.useTrackList)();
|
|
2308
2191
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
@@ -2327,21 +2210,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2327
2210
|
event.stopPropagation();
|
|
2328
2211
|
void play(song.uuid);
|
|
2329
2212
|
};
|
|
2330
|
-
return /* @__PURE__ */ (0,
|
|
2331
|
-
/* @__PURE__ */ (0,
|
|
2332
|
-
/* @__PURE__ */ (0,
|
|
2213
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
2214
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2215
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2333
2216
|
"button",
|
|
2334
2217
|
{
|
|
2335
2218
|
type: "button",
|
|
2336
2219
|
onClick: () => void play(),
|
|
2337
2220
|
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",
|
|
2338
2221
|
children: [
|
|
2339
|
-
/* @__PURE__ */ (0,
|
|
2222
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2340
2223
|
"Play all"
|
|
2341
2224
|
]
|
|
2342
2225
|
}
|
|
2343
2226
|
),
|
|
2344
|
-
/* @__PURE__ */ (0,
|
|
2227
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2345
2228
|
"button",
|
|
2346
2229
|
{
|
|
2347
2230
|
type: "button",
|
|
@@ -2356,7 +2239,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2356
2239
|
sort.ordering
|
|
2357
2240
|
)) })
|
|
2358
2241
|
] }),
|
|
2359
|
-
/* @__PURE__ */ (0,
|
|
2242
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2360
2243
|
TrackItem,
|
|
2361
2244
|
{
|
|
2362
2245
|
track: song,
|
|
@@ -2365,12 +2248,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2365
2248
|
renderSongLink
|
|
2366
2249
|
}
|
|
2367
2250
|
) }, song.uuid)) }),
|
|
2368
|
-
/* @__PURE__ */ (0,
|
|
2251
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2369
2252
|
] }) });
|
|
2370
2253
|
}
|
|
2371
2254
|
|
|
2372
2255
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2373
|
-
var
|
|
2256
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2374
2257
|
function useArtistPanel({
|
|
2375
2258
|
uuid,
|
|
2376
2259
|
scope,
|
|
@@ -2391,26 +2274,26 @@ function useArtistPanel({
|
|
|
2391
2274
|
return {
|
|
2392
2275
|
...panel,
|
|
2393
2276
|
title: "Artist",
|
|
2394
|
-
content: /* @__PURE__ */ (0,
|
|
2395
|
-
/* @__PURE__ */ (0,
|
|
2396
|
-
/* @__PURE__ */ (0,
|
|
2397
|
-
/* @__PURE__ */ (0,
|
|
2398
|
-
/* @__PURE__ */ (0,
|
|
2399
|
-
/* @__PURE__ */ (0,
|
|
2400
|
-
/* @__PURE__ */ (0,
|
|
2277
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2278
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-3 w-16" }),
|
|
2279
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-4 w-32" }),
|
|
2280
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-3 w-16" }),
|
|
2281
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-4 w-24" }),
|
|
2282
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2283
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui14.Skeleton, { className: "h-16" })
|
|
2401
2284
|
] })
|
|
2402
2285
|
};
|
|
2403
2286
|
case "not-found":
|
|
2404
2287
|
return {
|
|
2405
2288
|
...panel,
|
|
2406
2289
|
title: "Artist not found",
|
|
2407
|
-
content: /* @__PURE__ */ (0,
|
|
2290
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2408
2291
|
};
|
|
2409
2292
|
case "error":
|
|
2410
2293
|
return {
|
|
2411
2294
|
...panel,
|
|
2412
2295
|
title: "Artist",
|
|
2413
|
-
content: /* @__PURE__ */ (0,
|
|
2296
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2414
2297
|
};
|
|
2415
2298
|
case "success": {
|
|
2416
2299
|
const { artist } = result;
|
|
@@ -2419,13 +2302,13 @@ function useArtistPanel({
|
|
|
2419
2302
|
return {
|
|
2420
2303
|
...panel,
|
|
2421
2304
|
title: artist.name,
|
|
2422
|
-
content: /* @__PURE__ */ (0,
|
|
2423
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2424
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2425
|
-
/* @__PURE__ */ (0,
|
|
2426
|
-
/* @__PURE__ */ (0,
|
|
2427
|
-
/* @__PURE__ */ (0,
|
|
2428
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2305
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
2306
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2307
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2308
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistHeader, { artist }),
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2311
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2429
2312
|
ArtistSongs,
|
|
2430
2313
|
{
|
|
2431
2314
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2435,7 +2318,7 @@ function useArtistPanel({
|
|
|
2435
2318
|
}
|
|
2436
2319
|
)
|
|
2437
2320
|
] }),
|
|
2438
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2321
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2439
2322
|
};
|
|
2440
2323
|
}
|
|
2441
2324
|
}
|
|
@@ -2446,45 +2329,45 @@ var import_ui16 = require("@behindthemusictree/ui");
|
|
|
2446
2329
|
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2447
2330
|
|
|
2448
2331
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2449
|
-
var
|
|
2450
|
-
var
|
|
2332
|
+
var import_react15 = require("react");
|
|
2333
|
+
var import_lucide_react14 = require("lucide-react");
|
|
2451
2334
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
2452
2335
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2453
2336
|
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2454
|
-
var
|
|
2337
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2455
2338
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2456
2339
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2457
2340
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2458
2341
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2459
|
-
const [open, setOpen] = (0,
|
|
2342
|
+
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
2460
2343
|
const history = (0, import_genre_tree16.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2461
2344
|
let body = null;
|
|
2462
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2463
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2345
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2346
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2464
2347
|
else if (history.status === "success")
|
|
2465
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2466
|
-
/* @__PURE__ */ (0,
|
|
2467
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2348
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2349
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2350
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2468
2351
|
" ",
|
|
2469
2352
|
"\u2014 ",
|
|
2470
2353
|
entry.oldValue ?? "none",
|
|
2471
2354
|
" \u2192 ",
|
|
2472
2355
|
entry.newValue ?? "none"
|
|
2473
2356
|
] }) : null,
|
|
2474
|
-
/* @__PURE__ */ (0,
|
|
2357
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2475
2358
|
entry.actorPseudo ?? "Anonymous",
|
|
2476
2359
|
" \xB7 ",
|
|
2477
2360
|
dateFormat.format(new Date(entry.createdOn))
|
|
2478
2361
|
] })
|
|
2479
2362
|
] }, entry.uuid)) });
|
|
2480
|
-
return /* @__PURE__ */ (0,
|
|
2363
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2481
2364
|
"details",
|
|
2482
2365
|
{
|
|
2483
2366
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2484
2367
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2485
2368
|
children: [
|
|
2486
|
-
/* @__PURE__ */ (0,
|
|
2487
|
-
/* @__PURE__ */ (0,
|
|
2369
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2370
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "mt-1.5", children: body })
|
|
2488
2371
|
]
|
|
2489
2372
|
}
|
|
2490
2373
|
);
|
|
@@ -2510,13 +2393,13 @@ function SongPanelContent({
|
|
|
2510
2393
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2511
2394
|
fields.push({
|
|
2512
2395
|
label: "Genre",
|
|
2513
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2514
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2515
|
-
/* @__PURE__ */ (0,
|
|
2396
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { children: [
|
|
2397
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2398
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2516
2399
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2517
2400
|
});
|
|
2518
2401
|
} else {
|
|
2519
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2402
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2520
2403
|
}
|
|
2521
2404
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2522
2405
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2526,11 +2409,11 @@ function SongPanelContent({
|
|
|
2526
2409
|
const links = [];
|
|
2527
2410
|
if (musicBrainzUrl)
|
|
2528
2411
|
links.push(
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2530
2413
|
);
|
|
2531
2414
|
if (song.youtubeVideo)
|
|
2532
2415
|
links.push(
|
|
2533
|
-
/* @__PURE__ */ (0,
|
|
2416
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2534
2417
|
"a",
|
|
2535
2418
|
{
|
|
2536
2419
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2545,33 +2428,33 @@ function SongPanelContent({
|
|
|
2545
2428
|
if (links.length > 0)
|
|
2546
2429
|
fields.push({
|
|
2547
2430
|
label: "Links",
|
|
2548
|
-
value: /* @__PURE__ */ (0,
|
|
2431
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex gap-3", children: links })
|
|
2549
2432
|
});
|
|
2550
|
-
return /* @__PURE__ */ (0,
|
|
2551
|
-
/* @__PURE__ */ (0,
|
|
2552
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2433
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { children: [
|
|
2434
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2435
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2553
2436
|
"button",
|
|
2554
2437
|
{
|
|
2555
2438
|
type: "button",
|
|
2556
2439
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2557
2440
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2558
2441
|
children: [
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2442
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.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_runtime37.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2560
2443
|
isPlaying ? "Pause" : "Play"
|
|
2561
2444
|
]
|
|
2562
2445
|
}
|
|
2563
2446
|
),
|
|
2564
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2447
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2565
2448
|
] }) }),
|
|
2566
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2567
|
-
/* @__PURE__ */ (0,
|
|
2568
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2569
|
-
/* @__PURE__ */ (0,
|
|
2449
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_genre_tree16.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2450
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.FieldList, { items: fields }),
|
|
2451
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui15.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2452
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2570
2453
|
] });
|
|
2571
2454
|
}
|
|
2572
2455
|
|
|
2573
2456
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2574
|
-
var
|
|
2457
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2575
2458
|
function useSongPanel({
|
|
2576
2459
|
uuid,
|
|
2577
2460
|
scope,
|
|
@@ -2593,24 +2476,24 @@ function useSongPanel({
|
|
|
2593
2476
|
return {
|
|
2594
2477
|
...panel,
|
|
2595
2478
|
title: "Song",
|
|
2596
|
-
content: /* @__PURE__ */ (0,
|
|
2597
|
-
/* @__PURE__ */ (0,
|
|
2598
|
-
/* @__PURE__ */ (0,
|
|
2599
|
-
/* @__PURE__ */ (0,
|
|
2600
|
-
/* @__PURE__ */ (0,
|
|
2479
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2480
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui16.Skeleton, { className: "h-3 w-16" }),
|
|
2481
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui16.Skeleton, { className: "h-4 w-32" }),
|
|
2482
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui16.Skeleton, { className: "h-3 w-16" }),
|
|
2483
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui16.Skeleton, { className: "h-4 w-24" })
|
|
2601
2484
|
] })
|
|
2602
2485
|
};
|
|
2603
2486
|
case "not-found":
|
|
2604
2487
|
return {
|
|
2605
2488
|
...panel,
|
|
2606
2489
|
title: "Song not found",
|
|
2607
|
-
content: /* @__PURE__ */ (0,
|
|
2490
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2608
2491
|
};
|
|
2609
2492
|
case "error":
|
|
2610
2493
|
return {
|
|
2611
2494
|
...panel,
|
|
2612
2495
|
title: "Song",
|
|
2613
|
-
content: /* @__PURE__ */ (0,
|
|
2496
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2614
2497
|
};
|
|
2615
2498
|
case "success": {
|
|
2616
2499
|
const { song } = result;
|
|
@@ -2618,7 +2501,7 @@ function useSongPanel({
|
|
|
2618
2501
|
return {
|
|
2619
2502
|
...panel,
|
|
2620
2503
|
title: song.title,
|
|
2621
|
-
content: /* @__PURE__ */ (0,
|
|
2504
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2622
2505
|
SongPanelContent,
|
|
2623
2506
|
{
|
|
2624
2507
|
song,
|
|
@@ -2638,12 +2521,12 @@ function useSongPanel({
|
|
|
2638
2521
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2639
2522
|
var import_md2 = require("react-icons/md");
|
|
2640
2523
|
var import_ui17 = require("@behindthemusictree/ui");
|
|
2641
|
-
var
|
|
2524
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2642
2525
|
function SizeMetricToggle({
|
|
2643
2526
|
value,
|
|
2644
2527
|
onChange
|
|
2645
2528
|
}) {
|
|
2646
|
-
return /* @__PURE__ */ (0,
|
|
2529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2647
2530
|
import_ui17.SegmentedControl,
|
|
2648
2531
|
{
|
|
2649
2532
|
variant: "floating",
|
|
@@ -2651,26 +2534,19 @@ function SizeMetricToggle({
|
|
|
2651
2534
|
value,
|
|
2652
2535
|
onChange,
|
|
2653
2536
|
options: [
|
|
2654
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2655
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2537
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2538
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2656
2539
|
]
|
|
2657
2540
|
}
|
|
2658
2541
|
);
|
|
2659
2542
|
}
|
|
2660
2543
|
|
|
2661
2544
|
// src/genre-tree/GenreTreeView.tsx
|
|
2662
|
-
var
|
|
2663
|
-
var PREVIOUS_BUTTON_CSS = `
|
|
2664
|
-
.genre-tree-view .search-overlay .gtv-info-panel-previous { display: none; }
|
|
2665
|
-
.genre-tree-view:has(.search-overlay .gtv-info-panel-previous) .genre-search input { padding-left: 2.75rem; }
|
|
2666
|
-
.genre-tree-view:has(.search-overlay .gtv-info-panel-previous) .genre-search-previous { display: flex; }
|
|
2667
|
-
@keyframes genre-tree-view-previous-in { from { opacity: 0; transform: translate(-0.5rem, -50%); } to { opacity: 1; transform: translate(0, -50%); } }
|
|
2668
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
2669
|
-
.genre-search-previous { animation: genre-tree-view-previous-in 200ms ease-out; }
|
|
2670
|
-
}
|
|
2671
|
-
`;
|
|
2545
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2672
2546
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2673
|
-
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]
|
|
2547
|
+
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2548
|
+
var noop = () => {
|
|
2549
|
+
};
|
|
2674
2550
|
function GenreTreeView({
|
|
2675
2551
|
scope,
|
|
2676
2552
|
handleGenreCreationAction,
|
|
@@ -2724,21 +2600,21 @@ function GenreTreeView({
|
|
|
2724
2600
|
placement = "default",
|
|
2725
2601
|
...focus
|
|
2726
2602
|
}) {
|
|
2727
|
-
const getShareUrlRef = (0,
|
|
2603
|
+
const getShareUrlRef = (0, import_react16.useRef)(getShareUrl);
|
|
2728
2604
|
getShareUrlRef.current = getShareUrl;
|
|
2729
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2605
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react16.useState)(null);
|
|
2730
2606
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2731
2607
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2732
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2608
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react16.useState)(null);
|
|
2733
2609
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2734
2610
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2735
2611
|
const changeSong = (uuid) => {
|
|
2736
2612
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2737
2613
|
onSongUuidChange?.(uuid);
|
|
2738
2614
|
};
|
|
2739
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2615
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react16.useState)("songs");
|
|
2740
2616
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2741
|
-
const deferredSizeMetric = (0,
|
|
2617
|
+
const deferredSizeMetric = (0, import_react16.useDeferredValue)(sizeMetric);
|
|
2742
2618
|
const changeSizeMetric = (metric) => {
|
|
2743
2619
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2744
2620
|
onSizeMetricChange?.(metric);
|
|
@@ -2775,7 +2651,7 @@ function GenreTreeView({
|
|
|
2775
2651
|
artistUuid
|
|
2776
2652
|
});
|
|
2777
2653
|
const restoreRevision = viewportRestore?.revision;
|
|
2778
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2654
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react16.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2779
2655
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2780
2656
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2781
2657
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2791,7 +2667,7 @@ function GenreTreeView({
|
|
|
2791
2667
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2792
2668
|
const artistResult = (0, import_genre_tree19.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2793
2669
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2794
|
-
const filterArtist = (0,
|
|
2670
|
+
const filterArtist = (0, import_react16.useMemo)(
|
|
2795
2671
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2796
2672
|
[artistUuid, filterArtistName]
|
|
2797
2673
|
);
|
|
@@ -2806,14 +2682,14 @@ function GenreTreeView({
|
|
|
2806
2682
|
if (selectedNodeId !== null) clearSelection();
|
|
2807
2683
|
changeSong(uuid);
|
|
2808
2684
|
};
|
|
2809
|
-
const openSongRef = (0,
|
|
2685
|
+
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2810
2686
|
openSongRef.current = openSong;
|
|
2811
|
-
const resolvedRenderSongLink = (0,
|
|
2812
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2687
|
+
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2688
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2813
2689
|
[renderSongLink]
|
|
2814
2690
|
);
|
|
2815
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2816
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
2691
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
2692
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react16.useState)(null);
|
|
2817
2693
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2818
2694
|
const songBaseUrl = getBackendBaseUrl;
|
|
2819
2695
|
const artistPanel = useArtistPanel({
|
|
@@ -2821,7 +2697,7 @@ function GenreTreeView({
|
|
|
2821
2697
|
scope,
|
|
2822
2698
|
getBackendBaseUrl: artistBaseUrl,
|
|
2823
2699
|
getShareUrl: getShareUrlRef.current,
|
|
2824
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2700
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2825
2701
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2826
2702
|
onExpandedChange: (on) => {
|
|
2827
2703
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2830,7 +2706,7 @@ function GenreTreeView({
|
|
|
2830
2706
|
renderSongLink: resolvedRenderSongLink,
|
|
2831
2707
|
onClose: () => changeArtist(null)
|
|
2832
2708
|
});
|
|
2833
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2709
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2834
2710
|
const getGenrePath = (genre) => {
|
|
2835
2711
|
const path = [];
|
|
2836
2712
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2862,9 +2738,8 @@ function GenreTreeView({
|
|
|
2862
2738
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2863
2739
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2864
2740
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2865
|
-
const
|
|
2866
|
-
const [
|
|
2867
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react17.useState)(false);
|
|
2741
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react16.useState)(false);
|
|
2742
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react16.useState)(false);
|
|
2868
2743
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2869
2744
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2870
2745
|
const changeCollapsed = (next) => {
|
|
@@ -2875,55 +2750,56 @@ function GenreTreeView({
|
|
|
2875
2750
|
setUncontrolledExpanded(next);
|
|
2876
2751
|
onExpandedChange?.(next);
|
|
2877
2752
|
};
|
|
2878
|
-
(0,
|
|
2753
|
+
(0, import_react16.useEffect)(() => {
|
|
2879
2754
|
setUncontrolledCollapsed(false);
|
|
2880
2755
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2881
|
-
const
|
|
2882
|
-
useLeftDetailPanelLayout(!isDetailHidden, placement === "regions");
|
|
2883
|
-
const detailOverlay = placement === "regions" && activeOverlayPanel?.onExpandedChange ? activeOverlayPanel : void 0;
|
|
2884
|
-
const detailExpanded = detailOverlay ? !!detailOverlay.expanded : isExpanded;
|
|
2885
|
-
const onDetailExpandedChange = detailOverlay ? detailOverlay.onExpandedChange : changeExpanded;
|
|
2886
|
-
const infoPanelCuration = {
|
|
2887
|
-
renderDangerZone,
|
|
2888
|
-
expanded: placement === "regions" ? isExpanded : expanded,
|
|
2889
|
-
onExpandedChange: placement === "regions" ? changeExpanded : onExpandedChange,
|
|
2890
|
-
collapsed: placement === "regions" ? isCollapsed : void 0,
|
|
2891
|
-
hideExpandButton: placement === "regions" ? true : void 0,
|
|
2892
|
-
overlayPanel: detailOverlay ? { ...detailOverlay, collapsed: isCollapsed, hideExpandButton: true } : activeOverlayPanel
|
|
2893
|
-
};
|
|
2894
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react17.useState)(false);
|
|
2756
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react16.useState)(false);
|
|
2895
2757
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2896
2758
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2897
|
-
const
|
|
2898
|
-
const
|
|
2899
|
-
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2759
|
+
const [resolveStyle, setResolveStyle] = (0, import_react16.useState)(null);
|
|
2760
|
+
const handleNodeStyleResolverChange = (0, import_react16.useCallback)(
|
|
2761
|
+
(resolver) => setResolveStyle(() => resolver),
|
|
2762
|
+
[]
|
|
2763
|
+
);
|
|
2764
|
+
const panelRef = (0, import_react16.useRef)(null);
|
|
2765
|
+
const obscuredInset = (0, import_ui18.useObscuredInset)(panelRef);
|
|
2766
|
+
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2767
|
+
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2768
|
+
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2769
|
+
const relations = (0, import_genre_tree_view4.useGenreNodeRelations)(
|
|
2770
|
+
treeNodes,
|
|
2771
|
+
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2772
|
+
resolveStyle
|
|
2773
|
+
);
|
|
2774
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react16.useState)({
|
|
2775
|
+
current: selectedNodeId,
|
|
2776
|
+
previous: null
|
|
2777
|
+
});
|
|
2778
|
+
if (selectionHistory.current !== selectedNodeId) {
|
|
2779
|
+
setSelectionHistory({
|
|
2780
|
+
current: selectedNodeId,
|
|
2781
|
+
previous: selectionHistory.current ?? selectionHistory.previous
|
|
2782
|
+
});
|
|
2783
|
+
}
|
|
2784
|
+
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2785
|
+
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2910
2786
|
const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
|
|
2911
|
-
const [searchText, setSearchText] = (0,
|
|
2787
|
+
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2912
2788
|
const artistSearchResult = (0, import_genre_tree18.useArtistSearch)(artistBaseUrl, artistSearch ? searchText : "");
|
|
2913
2789
|
const artistGroup = artistSearch ? {
|
|
2914
2790
|
key: "artists",
|
|
2915
2791
|
label: "Artists",
|
|
2916
2792
|
items: artistSearchResult.artists.map((artist) => ({
|
|
2917
2793
|
key: artist.uuid,
|
|
2918
|
-
content: /* @__PURE__ */ (0,
|
|
2919
|
-
/* @__PURE__ */ (0,
|
|
2794
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
2795
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
2920
2796
|
artist.name,
|
|
2921
|
-
artist.disambiguation && /* @__PURE__ */ (0,
|
|
2797
|
+
artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
2922
2798
|
" \xB7 ",
|
|
2923
2799
|
artist.disambiguation
|
|
2924
2800
|
] })
|
|
2925
2801
|
] }),
|
|
2926
|
-
artist.matchedAlias && /* @__PURE__ */ (0,
|
|
2802
|
+
artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
2927
2803
|
"also known as ",
|
|
2928
2804
|
artist.matchedAlias
|
|
2929
2805
|
] })
|
|
@@ -2942,9 +2818,9 @@ function GenreTreeView({
|
|
|
2942
2818
|
label: "Songs",
|
|
2943
2819
|
items: songSearchResult.songs.map((song) => ({
|
|
2944
2820
|
key: song.uuid,
|
|
2945
|
-
content: /* @__PURE__ */ (0,
|
|
2946
|
-
/* @__PURE__ */ (0,
|
|
2947
|
-
/* @__PURE__ */ (0,
|
|
2821
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: (0, import_genre_tree18.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
2822
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
2823
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.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 ") })
|
|
2948
2824
|
] }),
|
|
2949
2825
|
onSelect: () => {
|
|
2950
2826
|
openSong(song.uuid);
|
|
@@ -2970,36 +2846,49 @@ function GenreTreeView({
|
|
|
2970
2846
|
const handleGenreSearchZoomIn = () => {
|
|
2971
2847
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2972
2848
|
};
|
|
2973
|
-
const zoomInRef = (0,
|
|
2849
|
+
const zoomInRef = (0, import_react16.useRef)(void 0);
|
|
2974
2850
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2975
|
-
const
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
2851
|
+
const selectGenreFromPanel = (nodeId) => {
|
|
2852
|
+
const genrePlaylist = genrePlaylistByNodeId.get(nodeId);
|
|
2853
|
+
if (genrePlaylist) handleGenreSearchSelect(genrePlaylist);
|
|
2854
|
+
};
|
|
2855
|
+
const renderGenreBody = (relation) => {
|
|
2856
|
+
const { node } = relation;
|
|
2857
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2858
|
+
import_genre_tree_view4.GenreNodeFamily,
|
|
2859
|
+
{
|
|
2860
|
+
node,
|
|
2861
|
+
fill: relation.fill,
|
|
2862
|
+
textColor: relation.textColor,
|
|
2863
|
+
parentNode: relation.parentNode,
|
|
2864
|
+
childNodes: relation.childNodes,
|
|
2865
|
+
onSelectNode: selectGenreFromPanel
|
|
2866
|
+
}
|
|
2867
|
+
);
|
|
2868
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2869
|
+
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2870
|
+
const criteria = genrePlaylist?.criteria;
|
|
2871
|
+
if (!genrePlaylist || !criteria) return { primary: family, aside: ancestors };
|
|
2872
|
+
const genreUuid = criteria.uuid;
|
|
2873
|
+
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist.parent?.uuid);
|
|
2874
|
+
const canToggleSide = !readOnly && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2875
|
+
const side = criteria.side ?? "core";
|
|
2876
|
+
const shareTarget = {
|
|
2877
|
+
kind: "genre",
|
|
2878
|
+
genrePlaylistUuid: genrePlaylist.uuid,
|
|
2879
|
+
name: criteria.name,
|
|
2880
|
+
scope,
|
|
2881
|
+
artistUuid
|
|
2882
|
+
};
|
|
2883
|
+
const shareUrl = getShareUrlRef.current?.(shareTarget) ?? void 0;
|
|
2884
|
+
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find(
|
|
2885
|
+
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2886
|
+
) : void 0;
|
|
2887
|
+
return {
|
|
2888
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
2889
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3000
2890
|
GenreInfoPanelBody,
|
|
3001
2891
|
{
|
|
3002
|
-
toolbar,
|
|
3003
2892
|
family,
|
|
3004
2893
|
genreUuid,
|
|
3005
2894
|
genrePlaylist,
|
|
@@ -3015,7 +2904,7 @@ function GenreTreeView({
|
|
|
3015
2904
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
3016
2905
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
3017
2906
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
3018
|
-
/* @__PURE__ */ (0,
|
|
2907
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3019
2908
|
ChangeParentPopup,
|
|
3020
2909
|
{
|
|
3021
2910
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -3046,49 +2935,65 @@ function GenreTreeView({
|
|
|
3046
2935
|
}
|
|
3047
2936
|
}
|
|
3048
2937
|
),
|
|
3049
|
-
|
|
3050
|
-
}
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
additionalActions,
|
|
3055
|
-
readOnly,
|
|
3056
|
-
handleGenreCreationAction,
|
|
3057
|
-
handleGenreRenameAction,
|
|
3058
|
-
showPopup,
|
|
3059
|
-
hidePopup,
|
|
3060
|
-
onParentChanged,
|
|
3061
|
-
visibleGenrePlaylists,
|
|
3062
|
-
updateGenre,
|
|
3063
|
-
playback,
|
|
3064
|
-
filterArtist,
|
|
3065
|
-
scope,
|
|
3066
|
-
getBackendBaseUrl,
|
|
3067
|
-
criteriaOverviewSchema,
|
|
3068
|
-
renderGenreDetailExtras,
|
|
3069
|
-
renderArtistLink,
|
|
3070
|
-
resolvedRenderSongLink,
|
|
3071
|
-
renderSummary,
|
|
3072
|
-
renderEssentialTracks,
|
|
3073
|
-
renderSongLeading,
|
|
3074
|
-
renderSongActions,
|
|
3075
|
-
renderTracksToolbar
|
|
3076
|
-
]
|
|
3077
|
-
);
|
|
2938
|
+
renderDangerZone?.(node)
|
|
2939
|
+
] }),
|
|
2940
|
+
aside: ancestors
|
|
2941
|
+
};
|
|
2942
|
+
};
|
|
3078
2943
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
3079
2944
|
const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
|
|
3080
|
-
|
|
3081
|
-
|
|
2945
|
+
let activePanel = null;
|
|
2946
|
+
if (overlayPanel) {
|
|
2947
|
+
activePanel = {
|
|
2948
|
+
title: overlayPanel.title,
|
|
2949
|
+
content: overlayPanel.content,
|
|
2950
|
+
aside: overlayPanel.extraContent,
|
|
2951
|
+
expanded: overlayPanel.expanded ?? false,
|
|
2952
|
+
onExpandedChange: overlayPanel.onExpandedChange,
|
|
2953
|
+
onClose: overlayPanel.onClose,
|
|
2954
|
+
compact: false
|
|
2955
|
+
};
|
|
2956
|
+
} else if (relations) {
|
|
2957
|
+
const { primary, aside } = renderGenreBody(relations);
|
|
2958
|
+
activePanel = {
|
|
2959
|
+
title: relations.node.name,
|
|
2960
|
+
content: primary,
|
|
2961
|
+
aside,
|
|
2962
|
+
expanded: isExpanded,
|
|
2963
|
+
onExpandedChange: changeExpanded,
|
|
2964
|
+
onClose: clearSelection,
|
|
2965
|
+
compact: true,
|
|
2966
|
+
headerAccent: { background: relations.fill, color: relations.textColor }
|
|
2967
|
+
};
|
|
2968
|
+
} else if (builtInPanel) {
|
|
2969
|
+
activePanel = {
|
|
2970
|
+
title: builtInPanel.title,
|
|
2971
|
+
content: builtInPanel.content,
|
|
2972
|
+
aside: builtInPanel.extraContent,
|
|
2973
|
+
expanded: builtInPanel.expanded,
|
|
2974
|
+
onExpandedChange: builtInPanel.onExpandedChange,
|
|
2975
|
+
onClose: builtInPanel.onClose,
|
|
2976
|
+
compact: true
|
|
2977
|
+
};
|
|
2978
|
+
}
|
|
2979
|
+
const isPanelShown = isTreeReady && activePanel !== null;
|
|
2980
|
+
const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
|
|
2981
|
+
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2982
|
+
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2983
|
+
} : void 0;
|
|
2984
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2985
|
+
import_ui18.IconButton,
|
|
3082
2986
|
{
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
2987
|
+
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2988
|
+
style: activePanel.headerAccent && { color: "inherit" },
|
|
2989
|
+
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2990
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react16.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react16.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3086
2991
|
}
|
|
3087
2992
|
) : null;
|
|
3088
2993
|
const renderArtistFilter = () => {
|
|
3089
2994
|
if (isLoading || !filterArtist) return null;
|
|
3090
|
-
return /* @__PURE__ */ (0,
|
|
3091
|
-
/* @__PURE__ */ (0,
|
|
2995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2996
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3092
2997
|
"button",
|
|
3093
2998
|
{
|
|
3094
2999
|
type: "button",
|
|
@@ -3100,7 +3005,7 @@ function GenreTreeView({
|
|
|
3100
3005
|
children: filterArtist.name
|
|
3101
3006
|
}
|
|
3102
3007
|
),
|
|
3103
|
-
/* @__PURE__ */ (0,
|
|
3008
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3104
3009
|
"button",
|
|
3105
3010
|
{
|
|
3106
3011
|
type: "button",
|
|
@@ -3112,22 +3017,22 @@ function GenreTreeView({
|
|
|
3112
3017
|
)
|
|
3113
3018
|
] });
|
|
3114
3019
|
};
|
|
3115
|
-
const actions = /* @__PURE__ */ (0,
|
|
3116
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
3020
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
3021
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3117
3022
|
GenreSearch,
|
|
3118
3023
|
{
|
|
3119
3024
|
genrePlaylists: visibleGenrePlaylists,
|
|
3120
3025
|
onSelect: handleGenreSearchSelect,
|
|
3121
|
-
selectedName: selectedGenrePlaylist?.name ?? builtInPanel?.title
|
|
3026
|
+
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
3122
3027
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
3123
3028
|
onPrevious: handlePrevious,
|
|
3124
3029
|
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
3125
3030
|
extraGroups
|
|
3126
3031
|
}
|
|
3127
3032
|
),
|
|
3128
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3129
|
-
/* @__PURE__ */ (0,
|
|
3130
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
3033
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3034
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.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: [
|
|
3035
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3131
3036
|
import_ui18.SegmentedControl,
|
|
3132
3037
|
{
|
|
3133
3038
|
"aria-label": "Tree view mode",
|
|
@@ -3141,13 +3046,13 @@ function GenreTreeView({
|
|
|
3141
3046
|
]
|
|
3142
3047
|
}
|
|
3143
3048
|
),
|
|
3144
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3049
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3145
3050
|
"div",
|
|
3146
3051
|
{
|
|
3147
3052
|
className: "flex items-center gap-1",
|
|
3148
3053
|
role: "group",
|
|
3149
3054
|
"aria-label": "Tree display options",
|
|
3150
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3055
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3151
3056
|
import_ui18.Button,
|
|
3152
3057
|
{
|
|
3153
3058
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -3158,52 +3063,44 @@ function GenreTreeView({
|
|
|
3158
3063
|
)
|
|
3159
3064
|
}
|
|
3160
3065
|
),
|
|
3161
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3066
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3162
3067
|
import_ui18.IconTextButton,
|
|
3163
3068
|
{
|
|
3164
|
-
icon:
|
|
3069
|
+
icon: import_lucide_react15.Plus,
|
|
3165
3070
|
text: "Add root",
|
|
3166
3071
|
onClick: () => handleGenreCreationAction(null)
|
|
3167
3072
|
}
|
|
3168
3073
|
)
|
|
3169
3074
|
] })
|
|
3170
3075
|
] });
|
|
3171
|
-
return /* @__PURE__ */ (0,
|
|
3076
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3172
3077
|
"div",
|
|
3173
3078
|
{
|
|
3174
|
-
|
|
3079
|
+
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3175
3080
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3176
3081
|
children: [
|
|
3177
|
-
/* @__PURE__ */ (0,
|
|
3178
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3082
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3179
3083
|
"div",
|
|
3180
3084
|
{
|
|
3181
3085
|
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"}`,
|
|
3182
3086
|
children: actions
|
|
3183
3087
|
}
|
|
3184
3088
|
),
|
|
3185
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3089
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3186
3090
|
"div",
|
|
3187
3091
|
{
|
|
3188
3092
|
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"}`,
|
|
3189
3093
|
children: renderArtistFilter()
|
|
3190
3094
|
}
|
|
3191
3095
|
),
|
|
3192
|
-
|
|
3193
|
-
"div",
|
|
3194
|
-
{
|
|
3195
|
-
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))]"}`,
|
|
3196
|
-
children: detailTab
|
|
3197
|
-
}
|
|
3198
|
-
),
|
|
3199
|
-
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3200
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3201
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
3096
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3097
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3098
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
3202
3099
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3203
3100
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3204
3101
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3205
3102
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3206
|
-
/* @__PURE__ */ (0,
|
|
3103
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3207
3104
|
GenrePlaylistTreeWheel,
|
|
3208
3105
|
{
|
|
3209
3106
|
scope,
|
|
@@ -3212,29 +3109,26 @@ function GenreTreeView({
|
|
|
3212
3109
|
handleGenreRenameAction,
|
|
3213
3110
|
additionalActions,
|
|
3214
3111
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
3215
|
-
hideInfoPanelClose: true,
|
|
3216
|
-
hideInfoPanelTitle: true,
|
|
3217
3112
|
onNodeHover: handleNodeHover,
|
|
3218
|
-
|
|
3113
|
+
...hostPanel,
|
|
3219
3114
|
playback,
|
|
3220
3115
|
selectedNodeId,
|
|
3221
3116
|
readOnly,
|
|
3222
3117
|
allowWheelRotation,
|
|
3223
3118
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3224
3119
|
busy,
|
|
3225
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3120
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3226
3121
|
floatingControlsLeading,
|
|
3227
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3122
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3228
3123
|
] }) : void 0,
|
|
3229
3124
|
defaultViewport,
|
|
3230
3125
|
viewportRestore,
|
|
3231
|
-
onViewportChange
|
|
3232
|
-
...infoPanelCuration
|
|
3126
|
+
onViewportChange
|
|
3233
3127
|
}
|
|
3234
3128
|
) }) })
|
|
3235
3129
|
) : viewMode === "pop-core" ? (
|
|
3236
3130
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3237
|
-
/* @__PURE__ */ (0,
|
|
3131
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3238
3132
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3239
3133
|
{
|
|
3240
3134
|
scope,
|
|
@@ -3243,28 +3137,25 @@ function GenreTreeView({
|
|
|
3243
3137
|
handleGenreRenameAction,
|
|
3244
3138
|
additionalActions,
|
|
3245
3139
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
3246
|
-
hideInfoPanelClose: true,
|
|
3247
|
-
hideInfoPanelTitle: true,
|
|
3248
3140
|
onNodeHover: handleNodeHover,
|
|
3249
|
-
|
|
3141
|
+
...hostPanel,
|
|
3250
3142
|
playback,
|
|
3251
3143
|
selectedNodeId,
|
|
3252
3144
|
readOnly,
|
|
3253
3145
|
allowWheelRotation,
|
|
3254
3146
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3255
3147
|
busy,
|
|
3256
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3148
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3257
3149
|
floatingControlsLeading,
|
|
3258
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3150
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3259
3151
|
] }) : void 0,
|
|
3260
3152
|
defaultViewport,
|
|
3261
3153
|
viewportRestore,
|
|
3262
3154
|
onViewportChange,
|
|
3263
|
-
...infoPanelCuration,
|
|
3264
3155
|
...focus
|
|
3265
3156
|
}
|
|
3266
3157
|
) }) })
|
|
3267
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3158
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3268
3159
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3269
3160
|
{
|
|
3270
3161
|
outline: true,
|
|
@@ -3274,22 +3165,19 @@ function GenreTreeView({
|
|
|
3274
3165
|
handleGenreRenameAction,
|
|
3275
3166
|
additionalActions,
|
|
3276
3167
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
3277
|
-
hideInfoPanelClose: true,
|
|
3278
|
-
hideInfoPanelTitle: true,
|
|
3279
3168
|
onNodeHover: handleNodeHover,
|
|
3280
|
-
|
|
3169
|
+
...hostPanel,
|
|
3281
3170
|
playback,
|
|
3282
3171
|
selectedNodeId,
|
|
3283
|
-
readOnly
|
|
3284
|
-
...infoPanelCuration
|
|
3172
|
+
readOnly
|
|
3285
3173
|
}
|
|
3286
|
-
) }) : /* @__PURE__ */ (0,
|
|
3287
|
-
([uuid, genrePlaylistTreePerRoot]
|
|
3288
|
-
return /* @__PURE__ */ (0,
|
|
3174
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime40.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(
|
|
3175
|
+
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3289
3177
|
"div",
|
|
3290
3178
|
{
|
|
3291
3179
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3292
|
-
children: /* @__PURE__ */ (0,
|
|
3180
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3293
3181
|
GenrePlaylistTreePerRoot,
|
|
3294
3182
|
{
|
|
3295
3183
|
scope,
|
|
@@ -3299,26 +3187,45 @@ function GenreTreeView({
|
|
|
3299
3187
|
handleGenreRenameAction,
|
|
3300
3188
|
additionalActions,
|
|
3301
3189
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
3302
|
-
hideInfoPanelClose: true,
|
|
3303
|
-
hideInfoPanelTitle: true,
|
|
3304
3190
|
onNodeHover: handleNodeHover,
|
|
3305
|
-
|
|
3191
|
+
...hostPanel,
|
|
3306
3192
|
playback,
|
|
3307
3193
|
selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
|
|
3308
3194
|
readOnly,
|
|
3309
3195
|
wheelZoom: "modifier",
|
|
3310
3196
|
defaultViewport: uuid === selectedGenrePlaylist?.root.uuid ? defaultViewport : void 0,
|
|
3311
3197
|
viewportRestore: uuid === selectedGenrePlaylist?.root.uuid && uuid === restoreRootUuid ? viewportRestore : void 0,
|
|
3312
|
-
onViewportChange: uuid === selectedGenrePlaylist?.root.uuid ? onViewportChange : void 0
|
|
3313
|
-
...infoPanelCuration,
|
|
3314
|
-
overlayPanel: rootIndex === 0 ? activeOverlayPanel : void 0
|
|
3198
|
+
onViewportChange: uuid === selectedGenrePlaylist?.root.uuid ? onViewportChange : void 0
|
|
3315
3199
|
}
|
|
3316
3200
|
) })
|
|
3317
3201
|
},
|
|
3318
3202
|
uuid
|
|
3319
3203
|
);
|
|
3320
3204
|
}
|
|
3321
|
-
) })
|
|
3205
|
+
) }),
|
|
3206
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3207
|
+
import_ui18.DetailPanel,
|
|
3208
|
+
{
|
|
3209
|
+
ref: panelRef,
|
|
3210
|
+
side: "left",
|
|
3211
|
+
title: activePanel?.title ?? "",
|
|
3212
|
+
hideTitle: activePanel?.compact,
|
|
3213
|
+
hideClose: activePanel?.compact,
|
|
3214
|
+
headerAccent: activePanel?.headerAccent,
|
|
3215
|
+
headerActions: expandButton,
|
|
3216
|
+
onClose: activePanel?.onClose ?? noop,
|
|
3217
|
+
collapsed: isCollapsed || !isTreeReady || !activePanel,
|
|
3218
|
+
expanded: activePanel?.expanded,
|
|
3219
|
+
onExpandedChange: activePanel?.onExpandedChange,
|
|
3220
|
+
aside: activePanel?.aside,
|
|
3221
|
+
edgeTab: detailTab,
|
|
3222
|
+
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
3223
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
3224
|
+
activePanel.content,
|
|
3225
|
+
activePanel.aside
|
|
3226
|
+
] }) : activePanel?.content
|
|
3227
|
+
}
|
|
3228
|
+
)
|
|
3322
3229
|
] }) })
|
|
3323
3230
|
]
|
|
3324
3231
|
}
|
|
@@ -3326,12 +3233,12 @@ function GenreTreeView({
|
|
|
3326
3233
|
}
|
|
3327
3234
|
|
|
3328
3235
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
3329
|
-
var
|
|
3236
|
+
var import_react17 = require("react");
|
|
3330
3237
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3331
3238
|
var import_ui19 = require("@behindthemusictree/ui");
|
|
3239
|
+
var import_lucide_react17 = require("lucide-react");
|
|
3332
3240
|
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3333
|
-
var
|
|
3334
|
-
var ITEM_COUNT_LABEL = "Songs";
|
|
3241
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
3335
3242
|
var RELATIVE_ITEM_COUNT = 1;
|
|
3336
3243
|
function toNode(genre, parentId) {
|
|
3337
3244
|
return {
|
|
@@ -3355,11 +3262,12 @@ function GenreDetailPanel({
|
|
|
3355
3262
|
collapsed,
|
|
3356
3263
|
expanded,
|
|
3357
3264
|
onExpandedChange,
|
|
3265
|
+
edgeTab,
|
|
3358
3266
|
extras
|
|
3359
3267
|
}) {
|
|
3360
3268
|
const { data: detail, isPending, isError } = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3361
3269
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3362
|
-
const view = (0,
|
|
3270
|
+
const view = (0, import_react17.useMemo)(() => {
|
|
3363
3271
|
if (!detail) return null;
|
|
3364
3272
|
const rootColor = (0, import_genre_tree_view5.getGenreTreeColor)(detail.root.wikidataId);
|
|
3365
3273
|
const relative = (genre, parentId) => ({
|
|
@@ -3367,7 +3275,7 @@ function GenreDetailPanel({
|
|
|
3367
3275
|
fill: (0, import_genre_tree_view5.tintSurface)(rootColor),
|
|
3368
3276
|
textColor: import_genre_tree_view5.TEXT_COLOR
|
|
3369
3277
|
});
|
|
3370
|
-
const
|
|
3278
|
+
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
3371
3279
|
return {
|
|
3372
3280
|
node: {
|
|
3373
3281
|
id: detail.uuid,
|
|
@@ -3379,57 +3287,72 @@ function GenreDetailPanel({
|
|
|
3379
3287
|
fill: rootColor,
|
|
3380
3288
|
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
3381
3289
|
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
3382
|
-
ancestorNodes:
|
|
3383
|
-
({ ascendant }, index) => relative(ascendant, index === 0 ? null :
|
|
3290
|
+
ancestorNodes: ancestors2.map(
|
|
3291
|
+
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
3384
3292
|
)
|
|
3385
3293
|
};
|
|
3386
3294
|
}, [detail]);
|
|
3387
3295
|
if (isError) {
|
|
3388
|
-
return /* @__PURE__ */ (0,
|
|
3296
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3389
3297
|
}
|
|
3390
3298
|
if (isPending || !detail || !view) {
|
|
3391
|
-
return /* @__PURE__ */ (0,
|
|
3392
|
-
/* @__PURE__ */ (0,
|
|
3393
|
-
/* @__PURE__ */ (0,
|
|
3394
|
-
/* @__PURE__ */ (0,
|
|
3299
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3300
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui19.Skeleton, { className: "h-6 w-2/3" }),
|
|
3301
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui19.Skeleton, { className: "h-4 w-full" }),
|
|
3302
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui19.Skeleton, { className: "h-4 w-3/4" })
|
|
3395
3303
|
] });
|
|
3396
3304
|
}
|
|
3397
|
-
const
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
{
|
|
3401
|
-
toolbar: sections.toolbar,
|
|
3402
|
-
family: sections.family,
|
|
3403
|
-
genreUuid: detail.uuid,
|
|
3404
|
-
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3405
|
-
wikidataId: detail.wikidataId,
|
|
3406
|
-
scope,
|
|
3407
|
-
getBackendBaseUrl,
|
|
3408
|
-
playback,
|
|
3409
|
-
band: { node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
3410
|
-
extras
|
|
3411
|
-
}
|
|
3412
|
-
),
|
|
3413
|
-
aside: null
|
|
3414
|
-
});
|
|
3415
|
-
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3416
|
-
import_genre_tree_view5.InfoPanel,
|
|
3305
|
+
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3306
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3307
|
+
import_genre_tree_view5.GenreNodeFamily,
|
|
3417
3308
|
{
|
|
3418
3309
|
node: view.node,
|
|
3419
3310
|
fill: view.fill,
|
|
3420
3311
|
textColor: import_genre_tree_view5.ACCENT_TEXT_COLOR,
|
|
3421
3312
|
parentNode: view.parentNode,
|
|
3422
3313
|
childNodes: view.childNodes,
|
|
3423
|
-
|
|
3314
|
+
onSelectNode: selectGenre
|
|
3315
|
+
}
|
|
3316
|
+
);
|
|
3317
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3318
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
3319
|
+
import_ui19.DetailPanel,
|
|
3320
|
+
{
|
|
3424
3321
|
side: "left",
|
|
3322
|
+
title: view.node.name,
|
|
3323
|
+
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3324
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3325
|
+
import_ui19.IconButton,
|
|
3326
|
+
{
|
|
3327
|
+
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3328
|
+
style: { color: "inherit" },
|
|
3329
|
+
onClick: () => onExpandedChange(!expanded),
|
|
3330
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react17.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react17.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3331
|
+
}
|
|
3332
|
+
) : void 0,
|
|
3425
3333
|
onClose,
|
|
3426
|
-
|
|
3427
|
-
itemCountLabel: ITEM_COUNT_LABEL,
|
|
3428
|
-
renderInfoPanelBody: renderBody,
|
|
3334
|
+
collapsed,
|
|
3429
3335
|
expanded,
|
|
3430
3336
|
onExpandedChange,
|
|
3431
|
-
|
|
3432
|
-
|
|
3337
|
+
edgeTab,
|
|
3338
|
+
aside: ancestors,
|
|
3339
|
+
children: [
|
|
3340
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3341
|
+
GenreInfoPanelBody,
|
|
3342
|
+
{
|
|
3343
|
+
family,
|
|
3344
|
+
genreUuid: detail.uuid,
|
|
3345
|
+
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3346
|
+
wikidataId: detail.wikidataId,
|
|
3347
|
+
scope,
|
|
3348
|
+
getBackendBaseUrl,
|
|
3349
|
+
playback,
|
|
3350
|
+
band: { node: view.node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
3351
|
+
extras
|
|
3352
|
+
}
|
|
3353
|
+
),
|
|
3354
|
+
!expanded && ancestors
|
|
3355
|
+
]
|
|
3433
3356
|
}
|
|
3434
3357
|
);
|
|
3435
3358
|
}
|
|
@@ -3438,13 +3361,13 @@ function GenreDetailPanel({
|
|
|
3438
3361
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3439
3362
|
|
|
3440
3363
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3441
|
-
var
|
|
3442
|
-
var
|
|
3364
|
+
var import_react18 = require("react");
|
|
3365
|
+
var import_lucide_react18 = require("lucide-react");
|
|
3443
3366
|
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3444
3367
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3445
|
-
var
|
|
3446
|
-
var
|
|
3447
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
3368
|
+
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
3369
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
3370
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3448
3371
|
function TrackListSidebar({
|
|
3449
3372
|
className: className2 = "",
|
|
3450
3373
|
renderDuration,
|
|
@@ -3457,17 +3380,17 @@ function TrackListSidebar({
|
|
|
3457
3380
|
renderSongLink
|
|
3458
3381
|
}) {
|
|
3459
3382
|
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree21.useTrackList)();
|
|
3460
|
-
const trackNoun = (0,
|
|
3461
|
-
const trackListRef = (0,
|
|
3462
|
-
const previousOriginRef = (0,
|
|
3463
|
-
const handledAutoAdvanceRequestIdRef = (0,
|
|
3464
|
-
const shouldFollowPlaybackRef = (0,
|
|
3465
|
-
const programmaticScrollTopRef = (0,
|
|
3466
|
-
const userScrollIntentRef = (0,
|
|
3467
|
-
const userScrollIntentTimerRef = (0,
|
|
3468
|
-
const loadMoreSentinelRef = (0,
|
|
3383
|
+
const trackNoun = (0, import_track_noun4.useTrackNoun)();
|
|
3384
|
+
const trackListRef = (0, import_react18.useRef)(null);
|
|
3385
|
+
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
3386
|
+
const handledAutoAdvanceRequestIdRef = (0, import_react18.useRef)(null);
|
|
3387
|
+
const shouldFollowPlaybackRef = (0, import_react18.useRef)(true);
|
|
3388
|
+
const programmaticScrollTopRef = (0, import_react18.useRef)(null);
|
|
3389
|
+
const userScrollIntentRef = (0, import_react18.useRef)(false);
|
|
3390
|
+
const userScrollIntentTimerRef = (0, import_react18.useRef)(null);
|
|
3391
|
+
const loadMoreSentinelRef = (0, import_react18.useRef)(null);
|
|
3469
3392
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
3470
|
-
(0,
|
|
3393
|
+
(0, import_react18.useEffect)(() => {
|
|
3471
3394
|
const sentinel = loadMoreSentinelRef.current;
|
|
3472
3395
|
if (!hasMore || !sentinel) return;
|
|
3473
3396
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -3487,11 +3410,11 @@ function TrackListSidebar({
|
|
|
3487
3410
|
programmaticScrollTopRef.current = null;
|
|
3488
3411
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
3489
3412
|
}, [trackList?.origin]);
|
|
3490
|
-
(0,
|
|
3413
|
+
(0, import_react18.useEffect)(() => {
|
|
3491
3414
|
const list = trackListRef.current;
|
|
3492
3415
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
3493
3416
|
}, [isCollapsed]);
|
|
3494
|
-
(0,
|
|
3417
|
+
(0, import_react18.useEffect)(
|
|
3495
3418
|
() => () => {
|
|
3496
3419
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
3497
3420
|
},
|
|
@@ -3545,25 +3468,25 @@ function TrackListSidebar({
|
|
|
3545
3468
|
}
|
|
3546
3469
|
};
|
|
3547
3470
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3548
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3471
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3549
3472
|
"div",
|
|
3550
3473
|
{
|
|
3551
3474
|
"data-theme": "dark",
|
|
3552
3475
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3553
3476
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3554
3477
|
children: [
|
|
3555
|
-
/* @__PURE__ */ (0,
|
|
3556
|
-
/* @__PURE__ */ (0,
|
|
3557
|
-
/* @__PURE__ */ (0,
|
|
3558
|
-
/* @__PURE__ */ (0,
|
|
3559
|
-
/* @__PURE__ */ (0,
|
|
3478
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3479
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3480
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3481
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3482
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.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_runtime42.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3560
3483
|
] }),
|
|
3561
|
-
/* @__PURE__ */ (0,
|
|
3484
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3562
3485
|
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 `,
|
|
3563
3486
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3564
3487
|
] })
|
|
3565
3488
|
] }),
|
|
3566
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3489
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3567
3490
|
"button",
|
|
3568
3491
|
{
|
|
3569
3492
|
type: "button",
|
|
@@ -3572,11 +3495,11 @@ function TrackListSidebar({
|
|
|
3572
3495
|
"aria-expanded": !isCollapsed,
|
|
3573
3496
|
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"}`,
|
|
3574
3497
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3575
|
-
children: /* @__PURE__ */ (0,
|
|
3498
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react18.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3576
3499
|
}
|
|
3577
3500
|
)
|
|
3578
3501
|
] }),
|
|
3579
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3502
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3580
3503
|
"ul",
|
|
3581
3504
|
{
|
|
3582
3505
|
ref: trackListRef,
|
|
@@ -3586,7 +3509,7 @@ function TrackListSidebar({
|
|
|
3586
3509
|
onPointerDown: markUserScrollIntent,
|
|
3587
3510
|
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",
|
|
3588
3511
|
children: [
|
|
3589
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3512
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3590
3513
|
TrackItem,
|
|
3591
3514
|
{
|
|
3592
3515
|
track,
|
|
@@ -3597,7 +3520,7 @@ function TrackListSidebar({
|
|
|
3597
3520
|
renderSongLink
|
|
3598
3521
|
}
|
|
3599
3522
|
) }, track.uuid)),
|
|
3600
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3523
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3601
3524
|
]
|
|
3602
3525
|
}
|
|
3603
3526
|
)
|
|
@@ -3607,16 +3530,17 @@ function TrackListSidebar({
|
|
|
3607
3530
|
}
|
|
3608
3531
|
|
|
3609
3532
|
// src/genre-tree/PlayerPanel.tsx
|
|
3610
|
-
var
|
|
3611
|
-
var
|
|
3533
|
+
var import_react19 = require("react");
|
|
3534
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
3612
3535
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3613
3536
|
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3537
|
+
var import_ui20 = require("@behindthemusictree/ui");
|
|
3614
3538
|
|
|
3615
3539
|
// src/genre-tree/PlayerControls.tsx
|
|
3616
|
-
var
|
|
3540
|
+
var import_lucide_react19 = require("lucide-react");
|
|
3617
3541
|
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3618
3542
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3619
|
-
var
|
|
3543
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
3620
3544
|
function PlayerControls() {
|
|
3621
3545
|
const {
|
|
3622
3546
|
duration,
|
|
@@ -3635,15 +3559,15 @@ function PlayerControls() {
|
|
|
3635
3559
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3636
3560
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3637
3561
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3638
|
-
return /* @__PURE__ */ (0,
|
|
3562
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
3639
3563
|
"section",
|
|
3640
3564
|
{
|
|
3641
3565
|
"aria-label": "Player controls",
|
|
3642
3566
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3643
3567
|
children: [
|
|
3644
|
-
/* @__PURE__ */ (0,
|
|
3645
|
-
/* @__PURE__ */ (0,
|
|
3646
|
-
/* @__PURE__ */ (0,
|
|
3568
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3569
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree23.formatTime)(safeCurrentTime) }),
|
|
3570
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3647
3571
|
"input",
|
|
3648
3572
|
{
|
|
3649
3573
|
"aria-label": "Seek",
|
|
@@ -3657,11 +3581,11 @@ function PlayerControls() {
|
|
|
3657
3581
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3658
3582
|
}
|
|
3659
3583
|
),
|
|
3660
|
-
/* @__PURE__ */ (0,
|
|
3584
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree23.formatTime)(safeDuration) })
|
|
3661
3585
|
] }),
|
|
3662
|
-
/* @__PURE__ */ (0,
|
|
3663
|
-
/* @__PURE__ */ (0,
|
|
3664
|
-
/* @__PURE__ */ (0,
|
|
3586
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3587
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3588
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3665
3589
|
"button",
|
|
3666
3590
|
{
|
|
3667
3591
|
type: "button",
|
|
@@ -3670,10 +3594,10 @@ function PlayerControls() {
|
|
|
3670
3594
|
disabled: !isReady || !hasPreviousTrack,
|
|
3671
3595
|
onClick: playPreviousTrack,
|
|
3672
3596
|
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",
|
|
3673
|
-
children: /* @__PURE__ */ (0,
|
|
3597
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react19.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3674
3598
|
}
|
|
3675
3599
|
),
|
|
3676
|
-
/* @__PURE__ */ (0,
|
|
3600
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3677
3601
|
"button",
|
|
3678
3602
|
{
|
|
3679
3603
|
type: "button",
|
|
@@ -3682,10 +3606,10 @@ function PlayerControls() {
|
|
|
3682
3606
|
disabled: !isReady,
|
|
3683
3607
|
onClick: handlePlayPauseAction,
|
|
3684
3608
|
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",
|
|
3685
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3609
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react19.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react19.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3686
3610
|
}
|
|
3687
3611
|
),
|
|
3688
|
-
/* @__PURE__ */ (0,
|
|
3612
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3689
3613
|
"button",
|
|
3690
3614
|
{
|
|
3691
3615
|
type: "button",
|
|
@@ -3694,14 +3618,14 @@ function PlayerControls() {
|
|
|
3694
3618
|
disabled: !isReady || !hasNextTrack,
|
|
3695
3619
|
onClick: playNextTrack,
|
|
3696
3620
|
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",
|
|
3697
|
-
children: /* @__PURE__ */ (0,
|
|
3621
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react19.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3698
3622
|
}
|
|
3699
3623
|
)
|
|
3700
3624
|
] }),
|
|
3701
|
-
/* @__PURE__ */ (0,
|
|
3702
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3703
|
-
/* @__PURE__ */ (0,
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3625
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.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: [
|
|
3626
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react19.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react19.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3627
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3628
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3705
3629
|
"input",
|
|
3706
3630
|
{
|
|
3707
3631
|
"aria-label": "Volume",
|
|
@@ -3723,7 +3647,7 @@ function PlayerControls() {
|
|
|
3723
3647
|
}
|
|
3724
3648
|
|
|
3725
3649
|
// src/genre-tree/PlayerPanel.tsx
|
|
3726
|
-
var
|
|
3650
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
3727
3651
|
var PANEL_ID = "player-panel";
|
|
3728
3652
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3729
3653
|
function PlayerPanel({
|
|
@@ -3737,12 +3661,12 @@ function PlayerPanel({
|
|
|
3737
3661
|
}) {
|
|
3738
3662
|
const { playerTrackObject } = (0, import_player13.usePlayer)();
|
|
3739
3663
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree24.useTrackListSidebarVisibility)();
|
|
3740
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0,
|
|
3664
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3741
3665
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3742
|
-
(0,
|
|
3666
|
+
(0, import_react19.useEffect)(() => {
|
|
3743
3667
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3744
3668
|
}, [loadedTrackTitle]);
|
|
3745
|
-
const [uncontrolledExtended, setUncontrolledExtended] = (0,
|
|
3669
|
+
const [uncontrolledExtended, setUncontrolledExtended] = (0, import_react19.useState)(false);
|
|
3746
3670
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3747
3671
|
const setIsExtendRequested = (next) => {
|
|
3748
3672
|
setUncontrolledExtended(next);
|
|
@@ -3763,11 +3687,11 @@ function PlayerPanel({
|
|
|
3763
3687
|
onClick: () => setIsExtendRequested(true),
|
|
3764
3688
|
expanded: false
|
|
3765
3689
|
} : null;
|
|
3766
|
-
return /* @__PURE__ */ (0,
|
|
3690
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
3767
3691
|
"div",
|
|
3768
3692
|
{
|
|
3769
3693
|
id: PANEL_ID,
|
|
3770
|
-
className: (0,
|
|
3694
|
+
className: (0, import_tailwind_merge.twMerge)(
|
|
3771
3695
|
"fixed right-[var(--layout-gutter)] top-[var(--layout-band-top)] z-40 max-h-[calc(100vh-var(--layout-band-top)-3.5rem)]",
|
|
3772
3696
|
!isExtended && "w-[var(--layout-right-w)]",
|
|
3773
3697
|
isTrackListSidebarVisible && "h-[calc(100vh-var(--layout-band-top)-3.5rem)]",
|
|
@@ -3776,8 +3700,8 @@ function PlayerPanel({
|
|
|
3776
3700
|
),
|
|
3777
3701
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3778
3702
|
children: [
|
|
3779
|
-
/* @__PURE__ */ (0,
|
|
3780
|
-
PanelEdgeTab,
|
|
3703
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3704
|
+
import_ui20.PanelEdgeTab,
|
|
3781
3705
|
{
|
|
3782
3706
|
side: "right",
|
|
3783
3707
|
edge: tabEdge,
|
|
@@ -3786,30 +3710,30 @@ function PlayerPanel({
|
|
|
3786
3710
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3787
3711
|
}
|
|
3788
3712
|
),
|
|
3789
|
-
/* @__PURE__ */ (0,
|
|
3713
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
3790
3714
|
"div",
|
|
3791
3715
|
{
|
|
3792
|
-
className: (0,
|
|
3716
|
+
className: (0, import_tailwind_merge.twMerge)(
|
|
3793
3717
|
"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)]",
|
|
3794
3718
|
isExtended ? "flex-row" : "flex-col"
|
|
3795
3719
|
),
|
|
3796
3720
|
children: [
|
|
3797
|
-
/* @__PURE__ */ (0,
|
|
3798
|
-
/* @__PURE__ */ (0,
|
|
3799
|
-
/* @__PURE__ */ (0,
|
|
3800
|
-
/* @__PURE__ */ (0,
|
|
3801
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3721
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3722
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3724
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3725
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: playerTrackObject.loadError })
|
|
3802
3726
|
] }),
|
|
3803
|
-
/* @__PURE__ */ (0,
|
|
3727
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PlayerControls, {})
|
|
3804
3728
|
] }),
|
|
3805
|
-
/* @__PURE__ */ (0,
|
|
3729
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3806
3730
|
TrackListSidebar,
|
|
3807
3731
|
{
|
|
3808
3732
|
layout: "inline",
|
|
3809
3733
|
isCollapsed: !isTrackListSidebarVisible,
|
|
3810
3734
|
onExpand: toggleTrackListSidebar,
|
|
3811
3735
|
onMinimize: hideSongList,
|
|
3812
|
-
className: (0,
|
|
3736
|
+
className: (0, import_tailwind_merge.twMerge)(
|
|
3813
3737
|
"min-h-0 min-w-0 flex-1",
|
|
3814
3738
|
isExtended && "border-l border-zinc-800"
|
|
3815
3739
|
),
|
|
@@ -3829,11 +3753,11 @@ function PlayerPanel({
|
|
|
3829
3753
|
var import_uuid = require("uuid");
|
|
3830
3754
|
var import_ai = require("react-icons/ai");
|
|
3831
3755
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3832
|
-
var
|
|
3756
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
3833
3757
|
function Rating({ rating, handleChange }) {
|
|
3834
3758
|
const instanceId = (0, import_uuid.v4)();
|
|
3835
|
-
return /* @__PURE__ */ (0,
|
|
3836
|
-
/* @__PURE__ */ (0,
|
|
3759
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3760
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3837
3761
|
"input",
|
|
3838
3762
|
{
|
|
3839
3763
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3845,16 +3769,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3845
3769
|
onChange: handleChange
|
|
3846
3770
|
}
|
|
3847
3771
|
),
|
|
3848
|
-
/* @__PURE__ */ (0,
|
|
3849
|
-
/* @__PURE__ */ (0,
|
|
3772
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex", children: [
|
|
3773
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3850
3774
|
import_ai.AiOutlineStar,
|
|
3851
3775
|
{
|
|
3852
3776
|
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)]"}`
|
|
3853
3777
|
}
|
|
3854
3778
|
),
|
|
3855
|
-
/* @__PURE__ */ (0,
|
|
3779
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "flex-grow w-8" }),
|
|
3856
3780
|
" "
|
|
3857
|
-
] }) : /* @__PURE__ */ (0,
|
|
3781
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3858
3782
|
import_ai.AiFillStar,
|
|
3859
3783
|
{
|
|
3860
3784
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3880,7 +3804,6 @@ function Rating({ rating, handleChange }) {
|
|
|
3880
3804
|
GenreTreeWheelSkeleton,
|
|
3881
3805
|
InternalErrorPopup,
|
|
3882
3806
|
NetworkErrorPopup,
|
|
3883
|
-
PanelEdgeTab,
|
|
3884
3807
|
PlayerPanel,
|
|
3885
3808
|
PopupButtons,
|
|
3886
3809
|
PopupTitle,
|
|
@@ -3891,7 +3814,6 @@ function Rating({ rating, handleChange }) {
|
|
|
3891
3814
|
TrackListSidebar,
|
|
3892
3815
|
TrackPositionPlayPause,
|
|
3893
3816
|
TrackUploadPopup,
|
|
3894
|
-
useGenrePlaylistPlayback
|
|
3895
|
-
useLeftDetailPanelLayout
|
|
3817
|
+
useGenrePlaylistPlayback
|
|
3896
3818
|
});
|
|
3897
3819
|
//# sourceMappingURL=index.cjs.map
|