@behindthemusictree/app-kit-ui 33.3.0 → 34.1.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-QMELESFB.js → chunk-JP7PID7L.js} +413 -335
- package/dist/chunk-JP7PID7L.js.map +1 -0
- package/dist/genre-tree/index.cjs +572 -503
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +38 -31
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +567 -498
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-QMELESFB.js.map +0 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-DKQQLLMY.js";
|
|
6
6
|
|
|
7
7
|
// src/genre-tree/GenreTreeView.tsx
|
|
8
|
-
import { useCallback as useCallback6, useDeferredValue, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef3, useState as
|
|
8
|
+
import { useCallback as useCallback6, useDeferredValue as useDeferredValue2, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef3, useState as useState9 } from "react";
|
|
9
9
|
import { useIsMutating } from "@tanstack/react-query";
|
|
10
10
|
import { Plus as Plus2 } from "lucide-react";
|
|
11
11
|
import { Maximize2, Minimize2 } from "lucide-react";
|
|
@@ -21,9 +21,10 @@ import { useGenreTreeViewState, useArtist as useArtist2, useUpdateGenre as useUp
|
|
|
21
21
|
import { usePopup as usePopup5 } from "@behindthemusictree/app-kit/popup";
|
|
22
22
|
|
|
23
23
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
24
|
+
import { genrePlaylistTrackCount } from "@behindthemusictree/app-kit/genre-tree";
|
|
24
25
|
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
25
26
|
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
26
|
-
if (metric === "songs") return genrePlaylist
|
|
27
|
+
if (metric === "songs") return genrePlaylistTrackCount(genrePlaylist);
|
|
27
28
|
if (genrePlaylist.viewsCount === void 0) {
|
|
28
29
|
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
29
30
|
}
|
|
@@ -47,6 +48,7 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
51
|
+
import { genrePlaylistTrackCount as genrePlaylistTrackCount2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
50
52
|
import { useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
51
53
|
import {
|
|
52
54
|
GenreTree,
|
|
@@ -60,30 +62,32 @@ import { ErrorCode } from "@behindthemusictree/app-kit/transport";
|
|
|
60
62
|
import { useCallback, useMemo } from "react";
|
|
61
63
|
import {
|
|
62
64
|
TrackListOriginType,
|
|
63
|
-
useTrackList
|
|
65
|
+
useTrackList,
|
|
66
|
+
GenreCatalogueCountsSchema,
|
|
67
|
+
defaultGenreMusicSelection,
|
|
68
|
+
genreMusicSelectionKey
|
|
64
69
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
65
70
|
function useGenrePlaylistPlayback({
|
|
66
71
|
scope,
|
|
67
72
|
genrePlaylists,
|
|
68
73
|
artist = null
|
|
69
74
|
}) {
|
|
70
|
-
const { trackList, playNewTrackListFromGenrePlaylist
|
|
75
|
+
const { trackList, playNewTrackListFromGenrePlaylist } = useTrackList();
|
|
71
76
|
const origin = trackList?.origin;
|
|
72
77
|
const playingNodeId = useMemo(() => {
|
|
73
|
-
if (!origin) return null;
|
|
74
|
-
|
|
75
|
-
if (
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
);
|
|
78
|
+
if (!origin || origin.type !== TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
|
|
79
|
+
const playlistOrigin = origin;
|
|
80
|
+
if (playlistOrigin.artistUuid !== artist?.uuid) return null;
|
|
81
|
+
const playlist = genrePlaylists.find((item) => item.uuid === origin.uuid) ?? playlistOrigin.criteriaPlaylist;
|
|
82
|
+
const expected = scope === "reference" && playlist && !("criteria" in playlist && playlist.criteria === null) ? defaultGenreMusicSelection(GenreCatalogueCountsSchema.parse(playlist)) : void 0;
|
|
83
|
+
if (playlistOrigin.selection && expected && genreMusicSelectionKey(playlistOrigin.selection).join() !== genreMusicSelectionKey(expected).join()) return null;
|
|
84
|
+
return origin.uuid;
|
|
85
|
+
}, [origin, artist, genrePlaylists, scope]);
|
|
86
|
+
const play = useCallback(async (playlist) => {
|
|
87
|
+
if (artist && !playlist.criteria) return;
|
|
88
|
+
const selection = scope === "reference" && playlist.criteria !== null ? defaultGenreMusicSelection(GenreCatalogueCountsSchema.parse(playlist)) : void 0;
|
|
89
|
+
return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
|
|
90
|
+
}, [artist, scope, playNewTrackListFromGenrePlaylist]);
|
|
87
91
|
return useMemo(() => ({ playingNodeId, play }), [playingNodeId, play]);
|
|
88
92
|
}
|
|
89
93
|
|
|
@@ -118,7 +122,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
118
122
|
id: genrePlaylist.uuid,
|
|
119
123
|
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
120
124
|
name: genrePlaylist.name,
|
|
121
|
-
itemCount: genrePlaylist
|
|
125
|
+
itemCount: genrePlaylistTrackCount2(genrePlaylist),
|
|
122
126
|
actionable: Boolean(genrePlaylist.criteria),
|
|
123
127
|
side: genrePlaylist.criteria?.side ?? void 0,
|
|
124
128
|
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
@@ -134,7 +138,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
134
138
|
setIsPlaying(!isPlaying);
|
|
135
139
|
return;
|
|
136
140
|
}
|
|
137
|
-
if (genrePlaylist
|
|
141
|
+
if (genrePlaylistTrackCount2(genrePlaylist) === 0) {
|
|
138
142
|
return;
|
|
139
143
|
}
|
|
140
144
|
play(genrePlaylist).catch((error) => {
|
|
@@ -198,6 +202,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
198
202
|
}
|
|
199
203
|
|
|
200
204
|
// src/genre-tree/playlist-tree/TreeWheel.tsx
|
|
205
|
+
import { genrePlaylistTrackCount as genrePlaylistTrackCount3 } from "@behindthemusictree/app-kit/genre-tree";
|
|
201
206
|
import { useCallback as useCallback3, useMemo as useMemo3 } from "react";
|
|
202
207
|
import {
|
|
203
208
|
GenreTreeWheel
|
|
@@ -252,7 +257,7 @@ function GenrePlaylistTreeWheel({
|
|
|
252
257
|
setIsPlaying(!isPlaying);
|
|
253
258
|
return;
|
|
254
259
|
}
|
|
255
|
-
if (genrePlaylist
|
|
260
|
+
if (genrePlaylistTrackCount3(genrePlaylist) === 0) {
|
|
256
261
|
return;
|
|
257
262
|
}
|
|
258
263
|
play(genrePlaylist).catch((error) => {
|
|
@@ -317,6 +322,7 @@ function GenrePlaylistTreeWheel({
|
|
|
317
322
|
}
|
|
318
323
|
|
|
319
324
|
// src/genre-tree/playlist-tree/TreeWheelRadialPopCore.tsx
|
|
325
|
+
import { genrePlaylistTrackCount as genrePlaylistTrackCount4 } from "@behindthemusictree/app-kit/genre-tree";
|
|
320
326
|
import { useCallback as useCallback4, useMemo as useMemo4 } from "react";
|
|
321
327
|
import {
|
|
322
328
|
GenreTreeOutline,
|
|
@@ -364,7 +370,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
364
370
|
setIsPlaying(!isPlaying);
|
|
365
371
|
return;
|
|
366
372
|
}
|
|
367
|
-
if (genrePlaylist
|
|
373
|
+
if (genrePlaylistTrackCount4(genrePlaylist) === 0) {
|
|
368
374
|
return;
|
|
369
375
|
}
|
|
370
376
|
play(genrePlaylist).catch((error) => {
|
|
@@ -459,6 +465,9 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
459
465
|
] });
|
|
460
466
|
}
|
|
461
467
|
|
|
468
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
469
|
+
import { useDeferredValue, useState as useState3 } from "react";
|
|
470
|
+
|
|
462
471
|
// src/genre-tree/GenreSearch.tsx
|
|
463
472
|
import { ArrowLeft } from "lucide-react";
|
|
464
473
|
import { SEARCH_LEADING_BUTTON_CLASS } from "@behindthemusictree/ui";
|
|
@@ -664,7 +673,51 @@ function GenreSearch({
|
|
|
664
673
|
) });
|
|
665
674
|
}
|
|
666
675
|
|
|
676
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
677
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
678
|
+
function GenreTreeSearch({
|
|
679
|
+
artistBaseUrl,
|
|
680
|
+
songBaseUrl,
|
|
681
|
+
onArtistSelect,
|
|
682
|
+
onSongSelect,
|
|
683
|
+
artistSearch,
|
|
684
|
+
songSearch,
|
|
685
|
+
SearchGroups,
|
|
686
|
+
extraGroups = [],
|
|
687
|
+
onQueryChange,
|
|
688
|
+
onFocus,
|
|
689
|
+
...props
|
|
690
|
+
}) {
|
|
691
|
+
const [query, setQuery] = useState3("");
|
|
692
|
+
const [focused, setFocused] = useState3(false);
|
|
693
|
+
const deferredQuery = useDeferredValue(query);
|
|
694
|
+
const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
|
|
695
|
+
const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
|
|
696
|
+
const render = (hostGroups) => /* @__PURE__ */ jsx9(
|
|
697
|
+
GenreSearch,
|
|
698
|
+
{
|
|
699
|
+
...props,
|
|
700
|
+
onQueryChange: (text) => {
|
|
701
|
+
setQuery(text);
|
|
702
|
+
onQueryChange?.(text);
|
|
703
|
+
},
|
|
704
|
+
onFocus: () => {
|
|
705
|
+
setFocused(true);
|
|
706
|
+
onFocus?.();
|
|
707
|
+
},
|
|
708
|
+
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
return SearchGroups ? /* @__PURE__ */ jsx9(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
712
|
+
}
|
|
713
|
+
|
|
667
714
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
715
|
+
import {
|
|
716
|
+
genreMusicCount,
|
|
717
|
+
defaultGenreMusicSelection as defaultGenreMusicSelection2,
|
|
718
|
+
GenreCatalogueCountsSchema as GenreCatalogueCountsSchema2
|
|
719
|
+
} from "@behindthemusictree/app-kit/genre-tree";
|
|
720
|
+
import { useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
|
|
668
721
|
import { usePlayer as usePlayer5 } from "@behindthemusictree/app-kit/player";
|
|
669
722
|
|
|
670
723
|
// src/genre-tree/GenreActionBand.tsx
|
|
@@ -675,18 +728,18 @@ import { Share2 as Share22 } from "lucide-react";
|
|
|
675
728
|
import { usePopup as usePopup3 } from "@behindthemusictree/app-kit/popup";
|
|
676
729
|
|
|
677
730
|
// src/genre-tree/SharePopup.tsx
|
|
678
|
-
import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo5, useState as
|
|
731
|
+
import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo5, useState as useState4 } from "react";
|
|
679
732
|
import { Link2, Share2 } from "lucide-react";
|
|
680
733
|
import { FaWhatsapp } from "react-icons/fa";
|
|
681
734
|
import { SiGmail } from "react-icons/si";
|
|
682
735
|
|
|
683
736
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
684
|
-
import { Fragment as Fragment4, jsx as
|
|
737
|
+
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
685
738
|
var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-xs text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
|
|
686
739
|
function Contents({ icon, label }) {
|
|
687
740
|
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
688
|
-
/* @__PURE__ */
|
|
689
|
-
/* @__PURE__ */
|
|
741
|
+
/* @__PURE__ */ jsx10("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
|
|
742
|
+
/* @__PURE__ */ jsx10("span", { className: "max-w-full truncate", children: label })
|
|
690
743
|
] });
|
|
691
744
|
}
|
|
692
745
|
function ShareDestinationButton({
|
|
@@ -698,29 +751,29 @@ function ShareDestinationButton({
|
|
|
698
751
|
ariaLabel
|
|
699
752
|
}) {
|
|
700
753
|
if (href) {
|
|
701
|
-
return /* @__PURE__ */
|
|
754
|
+
return /* @__PURE__ */ jsx10("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
|
|
702
755
|
}
|
|
703
|
-
return /* @__PURE__ */
|
|
756
|
+
return /* @__PURE__ */ jsx10("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
|
|
704
757
|
}
|
|
705
758
|
|
|
706
759
|
// src/genre-tree/SharePreviewCard.tsx
|
|
707
760
|
import { Music, Music2, UserRound } from "lucide-react";
|
|
708
|
-
import { jsx as
|
|
761
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
709
762
|
function SharePreviewCard({ target, url }) {
|
|
710
763
|
const Icon = { genre: Music2, artist: UserRound, song: Music }[target.kind];
|
|
711
764
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
712
|
-
return /* @__PURE__ */
|
|
713
|
-
/* @__PURE__ */
|
|
765
|
+
return /* @__PURE__ */ jsx11("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
766
|
+
/* @__PURE__ */ jsx11("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx11(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
714
767
|
/* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
715
|
-
/* @__PURE__ */
|
|
716
|
-
/* @__PURE__ */
|
|
717
|
-
/* @__PURE__ */
|
|
768
|
+
/* @__PURE__ */ jsx11("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
769
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
770
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
718
771
|
] })
|
|
719
772
|
] }) });
|
|
720
773
|
}
|
|
721
774
|
|
|
722
775
|
// src/genre-tree/SharePopup.tsx
|
|
723
|
-
import { Fragment as Fragment5, jsx as
|
|
776
|
+
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
724
777
|
function resolveAbsoluteUrl(url) {
|
|
725
778
|
try {
|
|
726
779
|
const parsed = new URL(url, window.location.href);
|
|
@@ -753,11 +806,11 @@ async function copyText(text) {
|
|
|
753
806
|
}
|
|
754
807
|
}
|
|
755
808
|
function SharePopup({ target, url, onClose }) {
|
|
756
|
-
const [absoluteUrl, setAbsoluteUrl] =
|
|
757
|
-
const [copyStatus, setCopyStatus] =
|
|
758
|
-
const [canUseNativeShare, setCanUseNativeShare] =
|
|
759
|
-
const [shareError, setShareError] =
|
|
760
|
-
const [toastId, setToastId] =
|
|
809
|
+
const [absoluteUrl, setAbsoluteUrl] = useState4(url);
|
|
810
|
+
const [copyStatus, setCopyStatus] = useState4("idle");
|
|
811
|
+
const [canUseNativeShare, setCanUseNativeShare] = useState4(false);
|
|
812
|
+
const [shareError, setShareError] = useState4(null);
|
|
813
|
+
const [toastId, setToastId] = useState4(0);
|
|
761
814
|
useEffect2(() => {
|
|
762
815
|
setAbsoluteUrl(resolveAbsoluteUrl(url) ?? "");
|
|
763
816
|
setCopyStatus("idle");
|
|
@@ -796,7 +849,7 @@ function SharePopup({ target, url, onClose }) {
|
|
|
796
849
|
}
|
|
797
850
|
};
|
|
798
851
|
return /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
799
|
-
/* @__PURE__ */
|
|
852
|
+
/* @__PURE__ */ jsx12(
|
|
800
853
|
BasePopup_default,
|
|
801
854
|
{
|
|
802
855
|
title: "Share",
|
|
@@ -812,64 +865,64 @@ function SharePopup({ target, url, onClose }) {
|
|
|
812
865
|
closeButtonClassName: "!h-10 !w-10 !rounded-none !border-0 !bg-transparent !p-0 !text-[var(--color-text)] hover:!bg-transparent hover:!text-[var(--color-text)]",
|
|
813
866
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
814
867
|
children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-4", children: [
|
|
815
|
-
/* @__PURE__ */
|
|
868
|
+
/* @__PURE__ */ jsx12(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
816
869
|
/* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
817
|
-
/* @__PURE__ */
|
|
870
|
+
/* @__PURE__ */ jsx12(
|
|
818
871
|
ShareDestinationButton,
|
|
819
872
|
{
|
|
820
873
|
label: "Copy Link",
|
|
821
|
-
icon: /* @__PURE__ */
|
|
874
|
+
icon: /* @__PURE__ */ jsx12(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
822
875
|
onClick: () => void handleCopy(),
|
|
823
876
|
ariaLabel: "Copy link",
|
|
824
877
|
disabled: !absoluteUrl
|
|
825
878
|
}
|
|
826
879
|
),
|
|
827
|
-
/* @__PURE__ */
|
|
880
|
+
/* @__PURE__ */ jsx12(
|
|
828
881
|
ShareDestinationButton,
|
|
829
882
|
{
|
|
830
883
|
label: "WhatsApp",
|
|
831
|
-
icon: /* @__PURE__ */
|
|
884
|
+
icon: /* @__PURE__ */ jsx12(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
832
885
|
href: destinationUrls?.whatsapp,
|
|
833
886
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
834
887
|
disabled: !absoluteUrl
|
|
835
888
|
}
|
|
836
889
|
),
|
|
837
|
-
/* @__PURE__ */
|
|
890
|
+
/* @__PURE__ */ jsx12(
|
|
838
891
|
ShareDestinationButton,
|
|
839
892
|
{
|
|
840
893
|
label: "Gmail",
|
|
841
|
-
icon: /* @__PURE__ */
|
|
894
|
+
icon: /* @__PURE__ */ jsx12(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
842
895
|
href: destinationUrls?.gmail,
|
|
843
896
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
844
897
|
disabled: !absoluteUrl
|
|
845
898
|
}
|
|
846
899
|
),
|
|
847
|
-
canUseNativeShare && /* @__PURE__ */
|
|
900
|
+
canUseNativeShare && /* @__PURE__ */ jsx12(
|
|
848
901
|
ShareDestinationButton,
|
|
849
902
|
{
|
|
850
903
|
label: "More",
|
|
851
|
-
icon: /* @__PURE__ */
|
|
904
|
+
icon: /* @__PURE__ */ jsx12(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
852
905
|
onClick: () => void handleNativeShare(),
|
|
853
906
|
ariaLabel: "More sharing options",
|
|
854
907
|
disabled: !absoluteUrl
|
|
855
908
|
}
|
|
856
909
|
)
|
|
857
910
|
] }),
|
|
858
|
-
copyStatus === "failed" && /* @__PURE__ */
|
|
859
|
-
shareError && /* @__PURE__ */
|
|
911
|
+
copyStatus === "failed" && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
912
|
+
shareError && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
860
913
|
] })
|
|
861
914
|
}
|
|
862
915
|
),
|
|
863
|
-
toastId > 0 && /* @__PURE__ */
|
|
916
|
+
toastId > 0 && /* @__PURE__ */ jsx12(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
864
917
|
] });
|
|
865
918
|
}
|
|
866
919
|
|
|
867
920
|
// src/genre-tree/ShareActionButton.tsx
|
|
868
|
-
import { jsx as
|
|
921
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
869
922
|
function ShareActionButton({ target, url }) {
|
|
870
923
|
const { showPopup, hidePopup } = usePopup3();
|
|
871
924
|
const openSharePopup = () => {
|
|
872
|
-
showPopup(/* @__PURE__ */
|
|
925
|
+
showPopup(/* @__PURE__ */ jsx13(SharePopup, { target, url, onClose: hidePopup }));
|
|
873
926
|
};
|
|
874
927
|
return /* @__PURE__ */ jsxs9(
|
|
875
928
|
"button",
|
|
@@ -880,7 +933,7 @@ function ShareActionButton({ target, url }) {
|
|
|
880
933
|
"aria-label": `Share ${target.name}`,
|
|
881
934
|
title: `Share ${target.name}`,
|
|
882
935
|
children: [
|
|
883
|
-
/* @__PURE__ */
|
|
936
|
+
/* @__PURE__ */ jsx13("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: /* @__PURE__ */ jsx13(Share22, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
884
937
|
"Share"
|
|
885
938
|
]
|
|
886
939
|
}
|
|
@@ -888,7 +941,7 @@ function ShareActionButton({ target, url }) {
|
|
|
888
941
|
}
|
|
889
942
|
|
|
890
943
|
// src/genre-tree/GenreActionBand.tsx
|
|
891
|
-
import { jsx as
|
|
944
|
+
import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
892
945
|
function GenreActionBand({
|
|
893
946
|
node,
|
|
894
947
|
onPlayPause,
|
|
@@ -910,7 +963,7 @@ function GenreActionBand({
|
|
|
910
963
|
items.push({
|
|
911
964
|
key: "play-pause",
|
|
912
965
|
label: isPlaying ? "Pause" : "Play",
|
|
913
|
-
icon: isPlaying ? /* @__PURE__ */
|
|
966
|
+
icon: isPlaying ? /* @__PURE__ */ jsx14(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx14(Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
914
967
|
onClick: onPlayPause
|
|
915
968
|
});
|
|
916
969
|
}
|
|
@@ -918,24 +971,24 @@ function GenreActionBand({
|
|
|
918
971
|
items.push({
|
|
919
972
|
key: "zoom-in",
|
|
920
973
|
label: "Focus",
|
|
921
|
-
icon: /* @__PURE__ */
|
|
974
|
+
icon: /* @__PURE__ */ jsx14(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
922
975
|
onClick: onZoomIn
|
|
923
976
|
});
|
|
924
977
|
}
|
|
925
978
|
if (onAddChild) {
|
|
926
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */
|
|
979
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx14(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
927
980
|
}
|
|
928
981
|
if (onRename) {
|
|
929
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */
|
|
982
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx14(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
930
983
|
}
|
|
931
984
|
if (onChangeParent) {
|
|
932
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */
|
|
985
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx14(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
933
986
|
}
|
|
934
987
|
if (onToggleSide) {
|
|
935
988
|
adminItems.push({
|
|
936
989
|
key: "toggle-side",
|
|
937
990
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
938
|
-
icon: /* @__PURE__ */
|
|
991
|
+
icon: /* @__PURE__ */ jsx14(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
939
992
|
onClick: onToggleSide,
|
|
940
993
|
disabled: !!toggleSideDisabledReason,
|
|
941
994
|
title: toggleSideDisabledReason
|
|
@@ -960,7 +1013,7 @@ function GenreActionBand({
|
|
|
960
1013
|
onClick: item.onClick,
|
|
961
1014
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none disabled:opacity-50",
|
|
962
1015
|
children: [
|
|
963
|
-
/* @__PURE__ */
|
|
1016
|
+
/* @__PURE__ */ jsx14("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)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
|
|
964
1017
|
item.label
|
|
965
1018
|
]
|
|
966
1019
|
},
|
|
@@ -969,7 +1022,7 @@ function GenreActionBand({
|
|
|
969
1022
|
return /* @__PURE__ */ jsxs10("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
970
1023
|
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
971
1024
|
renderItems(items),
|
|
972
|
-
shareTarget && shareUrl && /* @__PURE__ */
|
|
1025
|
+
shareTarget && shareUrl && /* @__PURE__ */ jsx14(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
973
1026
|
] }),
|
|
974
1027
|
adminItems.length > 0 && /* @__PURE__ */ jsxs10(
|
|
975
1028
|
"section",
|
|
@@ -977,8 +1030,8 @@ function GenreActionBand({
|
|
|
977
1030
|
"aria-label": "Admin actions",
|
|
978
1031
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
979
1032
|
children: [
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
1033
|
+
/* @__PURE__ */ jsx14("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1034
|
+
/* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
982
1035
|
]
|
|
983
1036
|
}
|
|
984
1037
|
)
|
|
@@ -990,20 +1043,20 @@ import { PanelSection as PanelSection2, Skeleton as Skeleton2 } from "@behindthe
|
|
|
990
1043
|
import { useFetchGenreOverview } from "@behindthemusictree/app-kit/genre-tree";
|
|
991
1044
|
|
|
992
1045
|
// src/genre-tree/ClampedText.tsx
|
|
993
|
-
import { useLayoutEffect, useRef as useRef2, useState as
|
|
994
|
-
import { Fragment as Fragment6, jsx as
|
|
1046
|
+
import { useLayoutEffect, useRef as useRef2, useState as useState5 } from "react";
|
|
1047
|
+
import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
995
1048
|
function ClampedText({ children, className: className2 = "" }) {
|
|
996
1049
|
const ref = useRef2(null);
|
|
997
|
-
const [expanded, setExpanded] =
|
|
998
|
-
const [overflows, setOverflows] =
|
|
1050
|
+
const [expanded, setExpanded] = useState5(false);
|
|
1051
|
+
const [overflows, setOverflows] = useState5(false);
|
|
999
1052
|
useLayoutEffect(() => {
|
|
1000
1053
|
const element = ref.current;
|
|
1001
1054
|
if (!element || expanded) return;
|
|
1002
1055
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1003
1056
|
}, [children, expanded]);
|
|
1004
1057
|
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1005
|
-
/* @__PURE__ */
|
|
1006
|
-
(overflows || expanded) && /* @__PURE__ */
|
|
1058
|
+
/* @__PURE__ */ jsx15("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1059
|
+
(overflows || expanded) && /* @__PURE__ */ jsx15(
|
|
1007
1060
|
"button",
|
|
1008
1061
|
{
|
|
1009
1062
|
type: "button",
|
|
@@ -1017,7 +1070,7 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1017
1070
|
}
|
|
1018
1071
|
|
|
1019
1072
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1020
|
-
import { useEffect as useEffect3, useState as
|
|
1073
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
1021
1074
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
1022
1075
|
import { z } from "zod";
|
|
1023
1076
|
import { FaPlay, FaPause } from "react-icons/fa";
|
|
@@ -1030,6 +1083,7 @@ import { usePlayer as usePlayer4 } from "@behindthemusictree/app-kit/player";
|
|
|
1030
1083
|
import { PlayStates } from "@behindthemusictree/app-kit/player";
|
|
1031
1084
|
import { usePopup as usePopup4 } from "@behindthemusictree/app-kit/popup";
|
|
1032
1085
|
import { ErrorCode as ErrorCode3 } from "@behindthemusictree/app-kit/transport";
|
|
1086
|
+
import { genreMusicQueryParams } from "@behindthemusictree/app-kit/genre-tree";
|
|
1033
1087
|
import { genrePlaylistQueryKeys } from "@behindthemusictree/app-kit/genre-tree";
|
|
1034
1088
|
import { libraryEndpoints, libraryQueryKeys } from "@behindthemusictree/app-kit/genre-tree";
|
|
1035
1089
|
import { isSong, songUnplayableReason, SongDetailedSchema } from "@behindthemusictree/app-kit/genre-tree";
|
|
@@ -1037,15 +1091,15 @@ import { useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre
|
|
|
1037
1091
|
|
|
1038
1092
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1039
1093
|
import { musicBrainzRecordingUrl } from "@behindthemusictree/app-kit/genre-tree";
|
|
1040
|
-
import { Fragment as Fragment7, jsx as
|
|
1094
|
+
import { Fragment as Fragment7, jsx as jsx16 } from "react/jsx-runtime";
|
|
1041
1095
|
function MusicBrainzRecordingLink({
|
|
1042
1096
|
song,
|
|
1043
1097
|
children,
|
|
1044
1098
|
className: className2 = ""
|
|
1045
1099
|
}) {
|
|
1046
1100
|
const url = musicBrainzRecordingUrl(song);
|
|
1047
|
-
if (!url) return /* @__PURE__ */
|
|
1048
|
-
return /* @__PURE__ */
|
|
1101
|
+
if (!url) return /* @__PURE__ */ jsx16(Fragment7, { children });
|
|
1102
|
+
return /* @__PURE__ */ jsx16(
|
|
1049
1103
|
"a",
|
|
1050
1104
|
{
|
|
1051
1105
|
href: url,
|
|
@@ -1062,7 +1116,7 @@ function MusicBrainzRecordingLink({
|
|
|
1062
1116
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1063
1117
|
import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
|
|
1064
1118
|
import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
|
|
1065
|
-
import { jsx as
|
|
1119
|
+
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1066
1120
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1067
1121
|
var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
|
|
1068
1122
|
overallTotal: z.number().nullable(),
|
|
@@ -1082,6 +1136,7 @@ var nextCursor = (next) => {
|
|
|
1082
1136
|
function GenrePlaylistTracks({
|
|
1083
1137
|
genreUuid,
|
|
1084
1138
|
genrePlaylist,
|
|
1139
|
+
selection,
|
|
1085
1140
|
scope,
|
|
1086
1141
|
getBackendBaseUrl,
|
|
1087
1142
|
renderArtistLink,
|
|
@@ -1091,7 +1146,7 @@ function GenrePlaylistTracks({
|
|
|
1091
1146
|
renderTracksToolbar,
|
|
1092
1147
|
artist = null
|
|
1093
1148
|
}) {
|
|
1094
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = useTrackList2();
|
|
1149
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = useTrackList2();
|
|
1095
1150
|
const { session, sessionRestored } = useSession();
|
|
1096
1151
|
const { playerTrackObject, playState, handlePlayPauseAction } = usePlayer4();
|
|
1097
1152
|
const { showPopup } = usePopup4();
|
|
@@ -1100,7 +1155,7 @@ function GenrePlaylistTracks({
|
|
|
1100
1155
|
const { fetch } = useFetchWrapper(getBackendBaseUrl);
|
|
1101
1156
|
const playback = usePlaybackContext();
|
|
1102
1157
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({
|
|
1103
|
-
queryKey: [...scope === "me" ? genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
|
|
1158
|
+
queryKey: [...scope === "me" ? genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1104
1159
|
queryFn: async ({ pageParam }) => {
|
|
1105
1160
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1106
1161
|
const response = await fetch(
|
|
@@ -1108,7 +1163,7 @@ function GenrePlaylistTracks({
|
|
|
1108
1163
|
true,
|
|
1109
1164
|
false,
|
|
1110
1165
|
{},
|
|
1111
|
-
{ genre: genreUuid, ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1166
|
+
{ genre: genreUuid, ...genreMusicQueryParams(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1112
1167
|
);
|
|
1113
1168
|
const parsed = parseWithLog(SongPageSchema, response, "GenrePlaylistTracks");
|
|
1114
1169
|
return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
|
|
@@ -1117,7 +1172,7 @@ function GenrePlaylistTracks({
|
|
|
1117
1172
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1118
1173
|
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1119
1174
|
});
|
|
1120
|
-
const [sentinel, setSentinel] =
|
|
1175
|
+
const [sentinel, setSentinel] = useState6(null);
|
|
1121
1176
|
useEffect3(() => {
|
|
1122
1177
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1123
1178
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -1132,30 +1187,35 @@ function GenrePlaylistTracks({
|
|
|
1132
1187
|
const tracks = pages.flatMap((page) => page.tracks);
|
|
1133
1188
|
const total = pages[0]?.total ?? void 0;
|
|
1134
1189
|
const play = (startSongUuid) => {
|
|
1135
|
-
const started = artist ? playNewTrackListFromArtist(artist, scope, {
|
|
1190
|
+
const started = artist ? playNewTrackListFromArtist(artist, scope, {
|
|
1191
|
+
startSongUuid,
|
|
1192
|
+
ordering: "title",
|
|
1193
|
+
genreUuid,
|
|
1194
|
+
...selection && { selection }
|
|
1195
|
+
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genrePlaylist.name }, scope, { startSongUuid, ...selection && { selection } }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1136
1196
|
started.catch((error) => {
|
|
1137
1197
|
console.error("Failed to play genre playlist:", error);
|
|
1138
|
-
showPopup(/* @__PURE__ */
|
|
1198
|
+
showPopup(/* @__PURE__ */ jsx17(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
|
|
1139
1199
|
});
|
|
1140
1200
|
};
|
|
1141
|
-
return /* @__PURE__ */
|
|
1201
|
+
return /* @__PURE__ */ jsx17(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
|
|
1142
1202
|
renderTracksToolbar?.(),
|
|
1143
|
-
isPending ? /* @__PURE__ */
|
|
1203
|
+
isPending ? /* @__PURE__ */ jsx17(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
|
|
1144
1204
|
"Couldn't load ",
|
|
1145
1205
|
trackNoun.plural,
|
|
1146
1206
|
".",
|
|
1147
1207
|
" ",
|
|
1148
|
-
/* @__PURE__ */
|
|
1149
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */
|
|
1208
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1209
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genrePlaylist.name} ${trackNoun.plural}`, children: [
|
|
1150
1210
|
tracks.map((track, index) => {
|
|
1151
1211
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1152
1212
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1153
1213
|
const isPlaying = isCurrent && playState === PlayStates.PLAYING;
|
|
1154
1214
|
const text = /* @__PURE__ */ jsxs12("span", { className: TEXT_CELL, children: [
|
|
1155
|
-
/* @__PURE__ */
|
|
1156
|
-
track.artists?.length ? /* @__PURE__ */
|
|
1157
|
-
/* @__PURE__ */
|
|
1158
|
-
track.album && /* @__PURE__ */
|
|
1215
|
+
/* @__PURE__ */ jsx17("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : isSong(track) ? /* @__PURE__ */ jsx17(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1216
|
+
track.artists?.length ? /* @__PURE__ */ jsx17("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1217
|
+
/* @__PURE__ */ jsx17("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx17(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1218
|
+
track.album && /* @__PURE__ */ jsx17("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1159
1219
|
] });
|
|
1160
1220
|
const leading = renderSongLeading?.(track, index);
|
|
1161
1221
|
const actions = renderSongActions?.(track);
|
|
@@ -1163,7 +1223,7 @@ function GenrePlaylistTracks({
|
|
|
1163
1223
|
if (isUnplayable) {
|
|
1164
1224
|
return /* @__PURE__ */ jsxs12("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1165
1225
|
leading,
|
|
1166
|
-
/* @__PURE__ */
|
|
1226
|
+
/* @__PURE__ */ jsx17(
|
|
1167
1227
|
"button",
|
|
1168
1228
|
{
|
|
1169
1229
|
type: "button",
|
|
@@ -1171,7 +1231,7 @@ function GenrePlaylistTracks({
|
|
|
1171
1231
|
"aria-disabled": "true",
|
|
1172
1232
|
"aria-label": label,
|
|
1173
1233
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1174
|
-
children: /* @__PURE__ */
|
|
1234
|
+
children: /* @__PURE__ */ jsx17(FaPlay, { size: 10 })
|
|
1175
1235
|
}
|
|
1176
1236
|
),
|
|
1177
1237
|
text,
|
|
@@ -1184,14 +1244,14 @@ function GenrePlaylistTracks({
|
|
|
1184
1244
|
className: `group flex min-h-8 items-center gap-1 rounded px-1 hover:bg-[var(--color-neutral-100)] ${isCurrent ? "font-medium text-[var(--color-primary)]" : ""}`,
|
|
1185
1245
|
children: [
|
|
1186
1246
|
leading,
|
|
1187
|
-
/* @__PURE__ */
|
|
1247
|
+
/* @__PURE__ */ jsx17(
|
|
1188
1248
|
"button",
|
|
1189
1249
|
{
|
|
1190
1250
|
type: "button",
|
|
1191
1251
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1192
1252
|
className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1193
1253
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1194
|
-
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */
|
|
1254
|
+
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx17(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx17(FaPause, { size: 10 }) : /* @__PURE__ */ jsx17(FaPlay, { size: 10 })
|
|
1195
1255
|
}
|
|
1196
1256
|
),
|
|
1197
1257
|
text,
|
|
@@ -1201,7 +1261,7 @@ function GenrePlaylistTracks({
|
|
|
1201
1261
|
track.uuid
|
|
1202
1262
|
);
|
|
1203
1263
|
}),
|
|
1204
|
-
hasNextPage && /* @__PURE__ */
|
|
1264
|
+
hasNextPage && /* @__PURE__ */ jsx17("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1205
1265
|
] })
|
|
1206
1266
|
] }) });
|
|
1207
1267
|
}
|
|
@@ -1209,10 +1269,11 @@ function GenrePlaylistTracks({
|
|
|
1209
1269
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1210
1270
|
import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
|
|
1211
1271
|
import { SiWikidata, SiWikipedia } from "react-icons/si";
|
|
1212
|
-
import { Fragment as Fragment8, jsx as
|
|
1272
|
+
import { Fragment as Fragment8, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1213
1273
|
function GenreDetailExtras({
|
|
1214
1274
|
genreUuid,
|
|
1215
1275
|
genrePlaylist,
|
|
1276
|
+
selection,
|
|
1216
1277
|
wikidataId,
|
|
1217
1278
|
scope,
|
|
1218
1279
|
getBackendBaseUrl,
|
|
@@ -1234,15 +1295,15 @@ function GenreDetailExtras({
|
|
|
1234
1295
|
);
|
|
1235
1296
|
if (isPending) {
|
|
1236
1297
|
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1237
|
-
/* @__PURE__ */
|
|
1298
|
+
/* @__PURE__ */ jsx18(PanelSection2, { title: "Summary", children: /* @__PURE__ */ jsx18("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ jsx18(Skeleton2, { className: "h-4 w-3/4" }) }) }),
|
|
1238
1299
|
family
|
|
1239
1300
|
] });
|
|
1240
1301
|
}
|
|
1241
|
-
if (!overview) return /* @__PURE__ */
|
|
1302
|
+
if (!overview) return /* @__PURE__ */ jsx18(Fragment8, { children: family });
|
|
1242
1303
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1243
1304
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1244
1305
|
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1245
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */
|
|
1306
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx18(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1246
1307
|
summarySourceUrl && /* @__PURE__ */ jsxs13(
|
|
1247
1308
|
"a",
|
|
1248
1309
|
{
|
|
@@ -1251,7 +1312,7 @@ function GenreDetailExtras({
|
|
|
1251
1312
|
rel: "noopener noreferrer",
|
|
1252
1313
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1253
1314
|
children: [
|
|
1254
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsx18(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1255
1316
|
"Wikipedia"
|
|
1256
1317
|
]
|
|
1257
1318
|
}
|
|
@@ -1264,21 +1325,22 @@ function GenreDetailExtras({
|
|
|
1264
1325
|
rel: "noopener noreferrer",
|
|
1265
1326
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1266
1327
|
children: [
|
|
1267
|
-
/* @__PURE__ */
|
|
1328
|
+
/* @__PURE__ */ jsx18(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1268
1329
|
"Wikidata"
|
|
1269
1330
|
]
|
|
1270
1331
|
}
|
|
1271
1332
|
)
|
|
1272
1333
|
] }) }),
|
|
1273
|
-
/* @__PURE__ */
|
|
1334
|
+
/* @__PURE__ */ jsx18(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx18(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1274
1335
|
family,
|
|
1275
1336
|
renderGenreDetailExtras?.(overview),
|
|
1276
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */
|
|
1277
|
-
/* @__PURE__ */
|
|
1337
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx18(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx18("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx18("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx18("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1338
|
+
/* @__PURE__ */ jsx18(
|
|
1278
1339
|
GenrePlaylistTracks,
|
|
1279
1340
|
{
|
|
1280
1341
|
genreUuid,
|
|
1281
1342
|
genrePlaylist,
|
|
1343
|
+
selection,
|
|
1282
1344
|
scope,
|
|
1283
1345
|
getBackendBaseUrl,
|
|
1284
1346
|
renderArtistLink,
|
|
@@ -1290,7 +1352,7 @@ function GenreDetailExtras({
|
|
|
1290
1352
|
}
|
|
1291
1353
|
|
|
1292
1354
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1293
|
-
import { Fragment as Fragment9, jsx as
|
|
1355
|
+
import { Fragment as Fragment9, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1294
1356
|
import { createElement } from "react";
|
|
1295
1357
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1296
1358
|
const { isPlaying, setIsPlaying } = usePlayer5();
|
|
@@ -1304,11 +1366,11 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1304
1366
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1305
1367
|
});
|
|
1306
1368
|
};
|
|
1307
|
-
return /* @__PURE__ */
|
|
1369
|
+
return /* @__PURE__ */ jsx19(
|
|
1308
1370
|
GenreActionBand,
|
|
1309
1371
|
{
|
|
1310
1372
|
...bandProps,
|
|
1311
|
-
onPlayPause: genrePlaylist.tracksCount > 0 ? handlePlayPause : void 0,
|
|
1373
|
+
onPlayPause: ("genreOnlyTracksCount" in genrePlaylist ? genreMusicCount(genrePlaylist) : genrePlaylist.tracksCount) > 0 ? handlePlayPause : void 0,
|
|
1312
1374
|
isPlaying: isCurrentTrackList && isPlaying
|
|
1313
1375
|
}
|
|
1314
1376
|
);
|
|
@@ -1318,6 +1380,7 @@ function GenreInfoPanelBody({
|
|
|
1318
1380
|
family,
|
|
1319
1381
|
genreUuid,
|
|
1320
1382
|
genrePlaylist,
|
|
1383
|
+
catalogue,
|
|
1321
1384
|
wikidataId,
|
|
1322
1385
|
scope,
|
|
1323
1386
|
getBackendBaseUrl,
|
|
@@ -1325,9 +1388,30 @@ function GenreInfoPanelBody({
|
|
|
1325
1388
|
band,
|
|
1326
1389
|
extras
|
|
1327
1390
|
}) {
|
|
1391
|
+
const noun = useTrackNoun3();
|
|
1392
|
+
const summary = scope === "reference" ? GenreCatalogueCountsSchema2.parse(genrePlaylist) : void 0;
|
|
1393
|
+
const selection = summary ? defaultGenreMusicSelection2(summary) : void 0;
|
|
1394
|
+
const describeCounts = (counts) => {
|
|
1395
|
+
const scopes = counts.genreAndSubgenresTracksCount;
|
|
1396
|
+
return "canonical" in scopes ? `${counts.genreOnlyTracksCount} this genre only \xB7 ${scopes.canonical} with canonical subgenres \xB7 ${scopes.regional} with regional subgenres \xB7 ${scopes.all} with all subgenres` : `${counts.genreOnlyTracksCount} this genre only \xB7 ${scopes.all} with subgenres`;
|
|
1397
|
+
};
|
|
1328
1398
|
return /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1329
1399
|
toolbar,
|
|
1330
|
-
/* @__PURE__ */
|
|
1400
|
+
/* @__PURE__ */ jsx19(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1401
|
+
summary && catalogue && /* @__PURE__ */ jsxs14("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1402
|
+
/* @__PURE__ */ jsxs14("p", { children: [
|
|
1403
|
+
"Catalogue ",
|
|
1404
|
+
noun.plural,
|
|
1405
|
+
": ",
|
|
1406
|
+
describeCounts(catalogue)
|
|
1407
|
+
] }),
|
|
1408
|
+
/* @__PURE__ */ jsxs14("p", { children: [
|
|
1409
|
+
"Playlist ",
|
|
1410
|
+
noun.plural,
|
|
1411
|
+
": ",
|
|
1412
|
+
describeCounts(summary)
|
|
1413
|
+
] })
|
|
1414
|
+
] }),
|
|
1331
1415
|
/* @__PURE__ */ createElement(
|
|
1332
1416
|
GenreDetailExtras,
|
|
1333
1417
|
{
|
|
@@ -1336,6 +1420,7 @@ function GenreInfoPanelBody({
|
|
|
1336
1420
|
family,
|
|
1337
1421
|
genreUuid,
|
|
1338
1422
|
genrePlaylist,
|
|
1423
|
+
selection,
|
|
1339
1424
|
wikidataId,
|
|
1340
1425
|
scope,
|
|
1341
1426
|
getBackendBaseUrl
|
|
@@ -1350,7 +1435,7 @@ import {
|
|
|
1350
1435
|
getReparentCandidates,
|
|
1351
1436
|
useUpdateGenre
|
|
1352
1437
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1353
|
-
import { jsx as
|
|
1438
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1354
1439
|
function ChangeParentPopup({
|
|
1355
1440
|
genrePlaylist,
|
|
1356
1441
|
genrePlaylists,
|
|
@@ -1390,7 +1475,7 @@ function ChangeParentPopup({
|
|
|
1390
1475
|
"Current parent: ",
|
|
1391
1476
|
genrePlaylist.parent?.name ?? "none"
|
|
1392
1477
|
] }),
|
|
1393
|
-
/* @__PURE__ */
|
|
1478
|
+
/* @__PURE__ */ jsx20("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx20(
|
|
1394
1479
|
GenreSearch,
|
|
1395
1480
|
{
|
|
1396
1481
|
genrePlaylists: candidates,
|
|
@@ -1398,7 +1483,7 @@ function ChangeParentPopup({
|
|
|
1398
1483
|
placeholder: "Search a new parent\u2026"
|
|
1399
1484
|
}
|
|
1400
1485
|
) }),
|
|
1401
|
-
formErrors.length > 0 && /* @__PURE__ */
|
|
1486
|
+
formErrors.length > 0 && /* @__PURE__ */ jsx20("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1402
1487
|
] });
|
|
1403
1488
|
}
|
|
1404
1489
|
|
|
@@ -1422,7 +1507,7 @@ import {
|
|
|
1422
1507
|
SiYoutube
|
|
1423
1508
|
} from "react-icons/si";
|
|
1424
1509
|
import { Chip } from "@behindthemusictree/ui";
|
|
1425
|
-
import { jsx as
|
|
1510
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1426
1511
|
var LINK_KINDS = {
|
|
1427
1512
|
official: { label: "Official website", Icon: Globe },
|
|
1428
1513
|
wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
|
|
@@ -1459,7 +1544,7 @@ function artistLinks(artist) {
|
|
|
1459
1544
|
return links;
|
|
1460
1545
|
}
|
|
1461
1546
|
function ArtistLinks({ links }) {
|
|
1462
|
-
return /* @__PURE__ */
|
|
1547
|
+
return /* @__PURE__ */ jsx21("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx21("li", { children: /* @__PURE__ */ jsx21(Chip, { href: url, icon: /* @__PURE__ */ jsx21(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1463
1548
|
}
|
|
1464
1549
|
|
|
1465
1550
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1470,7 +1555,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1470
1555
|
}
|
|
1471
1556
|
|
|
1472
1557
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1473
|
-
import { jsx as
|
|
1558
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1474
1559
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1475
1560
|
function ArtistHeader({ artist }) {
|
|
1476
1561
|
const fields = [];
|
|
@@ -1480,35 +1565,35 @@ function ArtistHeader({ artist }) {
|
|
|
1480
1565
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1481
1566
|
if (years) fields.push({ label: "Active", value: years });
|
|
1482
1567
|
const links = artistLinks(artist);
|
|
1483
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */
|
|
1568
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx22(ArtistLinks, { links }) });
|
|
1484
1569
|
if (fields.length === 0) return null;
|
|
1485
|
-
return /* @__PURE__ */
|
|
1570
|
+
return /* @__PURE__ */ jsx22(FieldList, { items: fields });
|
|
1486
1571
|
}
|
|
1487
1572
|
|
|
1488
1573
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1489
|
-
import { jsx as
|
|
1574
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1490
1575
|
function ExternalLink({ href, children }) {
|
|
1491
|
-
return /* @__PURE__ */
|
|
1576
|
+
return /* @__PURE__ */ jsx23("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1492
1577
|
}
|
|
1493
1578
|
function ArtistPhoto({ image, name }) {
|
|
1494
1579
|
return /* @__PURE__ */ jsxs16("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1495
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ jsx23("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1496
1581
|
/* @__PURE__ */ jsxs16("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1497
1582
|
"Photo: ",
|
|
1498
|
-
/* @__PURE__ */
|
|
1583
|
+
/* @__PURE__ */ jsx23(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1499
1584
|
",",
|
|
1500
1585
|
" ",
|
|
1501
|
-
image.licenseUrl ? /* @__PURE__ */
|
|
1586
|
+
image.licenseUrl ? /* @__PURE__ */ jsx23(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1502
1587
|
] })
|
|
1503
1588
|
] });
|
|
1504
1589
|
}
|
|
1505
1590
|
|
|
1506
1591
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1507
1592
|
import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
|
|
1508
|
-
import { jsx as
|
|
1593
|
+
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1509
1594
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1510
1595
|
if (memberships.length === 0) return null;
|
|
1511
|
-
return /* @__PURE__ */
|
|
1596
|
+
return /* @__PURE__ */ jsx24(PanelGroup, { title, children: /* @__PURE__ */ jsx24("ul", { children: memberships.map((membership, index) => {
|
|
1512
1597
|
const details = [
|
|
1513
1598
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1514
1599
|
...membership.roles
|
|
@@ -1517,7 +1602,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1517
1602
|
return (
|
|
1518
1603
|
// A member can have several periods in the same group.
|
|
1519
1604
|
/* @__PURE__ */ jsxs17("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1520
|
-
/* @__PURE__ */
|
|
1605
|
+
/* @__PURE__ */ jsx24("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1521
1606
|
detailsText && /* @__PURE__ */ jsxs17("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1522
1607
|
" \u2014 ",
|
|
1523
1608
|
detailsText
|
|
@@ -1530,8 +1615,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1530
1615
|
if (memberships.length === 0) return null;
|
|
1531
1616
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1532
1617
|
return /* @__PURE__ */ jsxs17(PanelSection3, { title, children: [
|
|
1533
|
-
/* @__PURE__ */
|
|
1534
|
-
/* @__PURE__ */
|
|
1618
|
+
/* @__PURE__ */ jsx24(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1619
|
+
/* @__PURE__ */ jsx24(
|
|
1535
1620
|
MembershipList,
|
|
1536
1621
|
{
|
|
1537
1622
|
title: "Past",
|
|
@@ -1544,7 +1629,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1544
1629
|
|
|
1545
1630
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1546
1631
|
import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
|
|
1547
|
-
import { jsx as
|
|
1632
|
+
import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1548
1633
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1549
1634
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1550
1635
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1564,18 +1649,18 @@ function byYear(a, b) {
|
|
|
1564
1649
|
function ArtistDiscography({ albums }) {
|
|
1565
1650
|
if (albums.length === 0) return null;
|
|
1566
1651
|
const sorted = [...albums].sort(byYear);
|
|
1567
|
-
return /* @__PURE__ */
|
|
1652
|
+
return /* @__PURE__ */ jsx25(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
|
|
1568
1653
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1569
1654
|
if (groupAlbums.length === 0) return null;
|
|
1570
|
-
return /* @__PURE__ */
|
|
1571
|
-
/* @__PURE__ */
|
|
1572
|
-
/* @__PURE__ */
|
|
1655
|
+
return /* @__PURE__ */ jsx25(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx25("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs18("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1656
|
+
/* @__PURE__ */ jsx25("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1657
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1573
1658
|
] }, album.uuid)) }) }, group);
|
|
1574
1659
|
}) });
|
|
1575
1660
|
}
|
|
1576
1661
|
|
|
1577
1662
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1578
|
-
import { useState as
|
|
1663
|
+
import { useState as useState7 } from "react";
|
|
1579
1664
|
import { keepPreviousData } from "@tanstack/react-query";
|
|
1580
1665
|
import { Play as Play2 } from "lucide-react";
|
|
1581
1666
|
import { Pagination, PanelSection as PanelSection5, Skeleton as Skeleton3 } from "@behindthemusictree/ui";
|
|
@@ -1596,7 +1681,7 @@ import { useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre
|
|
|
1596
1681
|
import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
|
|
1597
1682
|
import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
|
|
1598
1683
|
import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
|
|
1599
|
-
import { jsx as
|
|
1684
|
+
import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1600
1685
|
function TrackPositionPlayPause({
|
|
1601
1686
|
position,
|
|
1602
1687
|
uuid,
|
|
@@ -1617,7 +1702,7 @@ function TrackPositionPlayPause({
|
|
|
1617
1702
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1618
1703
|
onClick: handlePlayPauseClick,
|
|
1619
1704
|
children: [
|
|
1620
|
-
/* @__PURE__ */
|
|
1705
|
+
/* @__PURE__ */ jsx26("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs19(
|
|
1621
1706
|
"span",
|
|
1622
1707
|
{
|
|
1623
1708
|
className: "track-position-play-pause__bars",
|
|
@@ -1625,18 +1710,18 @@ function TrackPositionPlayPause({
|
|
|
1625
1710
|
role: "img",
|
|
1626
1711
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1627
1712
|
children: [
|
|
1628
|
-
/* @__PURE__ */
|
|
1629
|
-
/* @__PURE__ */
|
|
1630
|
-
/* @__PURE__ */
|
|
1713
|
+
/* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__bar" }),
|
|
1714
|
+
/* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__bar" }),
|
|
1715
|
+
/* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__bar" })
|
|
1631
1716
|
]
|
|
1632
1717
|
}
|
|
1633
1718
|
) : position }),
|
|
1634
|
-
/* @__PURE__ */
|
|
1719
|
+
/* @__PURE__ */ jsx26(
|
|
1635
1720
|
"div",
|
|
1636
1721
|
{
|
|
1637
1722
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1638
1723
|
"aria-hidden": "true",
|
|
1639
|
-
children: isAwaitingPlayback ? /* @__PURE__ */
|
|
1724
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx26(FaPause2, {}) : /* @__PURE__ */ jsx26(FaPlay2, {})
|
|
1640
1725
|
}
|
|
1641
1726
|
)
|
|
1642
1727
|
]
|
|
@@ -1647,7 +1732,7 @@ function TrackPositionPlayPause({
|
|
|
1647
1732
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1648
1733
|
import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1649
1734
|
import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1650
|
-
import { jsx as
|
|
1735
|
+
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1651
1736
|
function TrackItem({
|
|
1652
1737
|
track,
|
|
1653
1738
|
position,
|
|
@@ -1676,7 +1761,7 @@ function TrackItem({
|
|
|
1676
1761
|
{
|
|
1677
1762
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1678
1763
|
children: [
|
|
1679
|
-
isUnplayable ? /* @__PURE__ */
|
|
1764
|
+
isUnplayable ? /* @__PURE__ */ jsx27(
|
|
1680
1765
|
"div",
|
|
1681
1766
|
{
|
|
1682
1767
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1684,7 +1769,7 @@ function TrackItem({
|
|
|
1684
1769
|
"aria-disabled": "true",
|
|
1685
1770
|
children: position
|
|
1686
1771
|
}
|
|
1687
|
-
) : /* @__PURE__ */
|
|
1772
|
+
) : /* @__PURE__ */ jsx27(
|
|
1688
1773
|
TrackPositionPlayPause,
|
|
1689
1774
|
{
|
|
1690
1775
|
position,
|
|
@@ -1694,11 +1779,11 @@ function TrackItem({
|
|
|
1694
1779
|
}
|
|
1695
1780
|
),
|
|
1696
1781
|
/* @__PURE__ */ jsxs20("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1697
|
-
/* @__PURE__ */
|
|
1698
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */
|
|
1782
|
+
/* @__PURE__ */ jsx27("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx27("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx27(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1783
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx27("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx27(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1699
1784
|
] }),
|
|
1700
|
-
/* @__PURE__ */
|
|
1701
|
-
renderDuration && /* @__PURE__ */
|
|
1785
|
+
/* @__PURE__ */ jsx27("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx27("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1786
|
+
renderDuration && /* @__PURE__ */ jsx27(
|
|
1702
1787
|
"div",
|
|
1703
1788
|
{
|
|
1704
1789
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1706,22 +1791,22 @@ function TrackItem({
|
|
|
1706
1791
|
children: renderDuration(track)
|
|
1707
1792
|
}
|
|
1708
1793
|
),
|
|
1709
|
-
renderActions && /* @__PURE__ */
|
|
1794
|
+
renderActions && /* @__PURE__ */ jsx27("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1710
1795
|
]
|
|
1711
1796
|
}
|
|
1712
1797
|
);
|
|
1713
1798
|
}
|
|
1714
1799
|
|
|
1715
1800
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1716
|
-
import { Fragment as Fragment10, jsx as
|
|
1801
|
+
import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1717
1802
|
var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
|
|
1718
1803
|
var SORTS = [
|
|
1719
1804
|
{ ordering: "title", label: "Title" },
|
|
1720
1805
|
{ ordering: "-albumYear", label: "Year" }
|
|
1721
1806
|
];
|
|
1722
1807
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
1723
|
-
const [page, setPage] =
|
|
1724
|
-
const [ordering, setOrdering] =
|
|
1808
|
+
const [page, setPage] = useState7(1);
|
|
1809
|
+
const [ordering, setOrdering] = useState7("title");
|
|
1725
1810
|
const { fetch } = useFetchWrapper2(getBackendBaseUrl);
|
|
1726
1811
|
const { playNewTrackListFromArtist } = useTrackList4();
|
|
1727
1812
|
const { playerTrackObject } = usePlayer8();
|
|
@@ -1746,7 +1831,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1746
1831
|
event.stopPropagation();
|
|
1747
1832
|
void play(song.uuid);
|
|
1748
1833
|
};
|
|
1749
|
-
return /* @__PURE__ */
|
|
1834
|
+
return /* @__PURE__ */ jsx28(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx28(Skeleton3, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx28("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx28("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs21(Fragment10, { children: [
|
|
1750
1835
|
/* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1751
1836
|
/* @__PURE__ */ jsxs21(
|
|
1752
1837
|
"button",
|
|
@@ -1755,12 +1840,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1755
1840
|
onClick: () => void play(),
|
|
1756
1841
|
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",
|
|
1757
1842
|
children: [
|
|
1758
|
-
/* @__PURE__ */
|
|
1843
|
+
/* @__PURE__ */ jsx28(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1759
1844
|
"Play all"
|
|
1760
1845
|
]
|
|
1761
1846
|
}
|
|
1762
1847
|
),
|
|
1763
|
-
/* @__PURE__ */
|
|
1848
|
+
/* @__PURE__ */ jsx28("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx28(
|
|
1764
1849
|
"button",
|
|
1765
1850
|
{
|
|
1766
1851
|
type: "button",
|
|
@@ -1775,7 +1860,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1775
1860
|
sort.ordering
|
|
1776
1861
|
)) })
|
|
1777
1862
|
] }),
|
|
1778
|
-
/* @__PURE__ */
|
|
1863
|
+
/* @__PURE__ */ jsx28("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx28("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx28(
|
|
1779
1864
|
TrackItem,
|
|
1780
1865
|
{
|
|
1781
1866
|
track: song,
|
|
@@ -1784,12 +1869,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1784
1869
|
renderSongLink
|
|
1785
1870
|
}
|
|
1786
1871
|
) }, song.uuid)) }),
|
|
1787
|
-
/* @__PURE__ */
|
|
1872
|
+
/* @__PURE__ */ jsx28(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1788
1873
|
] }) });
|
|
1789
1874
|
}
|
|
1790
1875
|
|
|
1791
1876
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1792
|
-
import { Fragment as Fragment11, jsx as
|
|
1877
|
+
import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1793
1878
|
function useArtistPanel({
|
|
1794
1879
|
uuid,
|
|
1795
1880
|
scope,
|
|
@@ -1811,25 +1896,25 @@ function useArtistPanel({
|
|
|
1811
1896
|
...panel,
|
|
1812
1897
|
title: "Artist",
|
|
1813
1898
|
content: /* @__PURE__ */ jsxs22("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1814
|
-
/* @__PURE__ */
|
|
1815
|
-
/* @__PURE__ */
|
|
1816
|
-
/* @__PURE__ */
|
|
1817
|
-
/* @__PURE__ */
|
|
1818
|
-
/* @__PURE__ */
|
|
1819
|
-
/* @__PURE__ */
|
|
1899
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-3 w-16" }),
|
|
1900
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-4 w-32" }),
|
|
1901
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-3 w-16" }),
|
|
1902
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-4 w-24" }),
|
|
1903
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-16 mt-2.5" }),
|
|
1904
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-16" })
|
|
1820
1905
|
] })
|
|
1821
1906
|
};
|
|
1822
1907
|
case "not-found":
|
|
1823
1908
|
return {
|
|
1824
1909
|
...panel,
|
|
1825
1910
|
title: "Artist not found",
|
|
1826
|
-
content: /* @__PURE__ */
|
|
1911
|
+
content: /* @__PURE__ */ jsx29("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1827
1912
|
};
|
|
1828
1913
|
case "error":
|
|
1829
1914
|
return {
|
|
1830
1915
|
...panel,
|
|
1831
1916
|
title: "Artist",
|
|
1832
|
-
content: /* @__PURE__ */
|
|
1917
|
+
content: /* @__PURE__ */ jsx29("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
1833
1918
|
};
|
|
1834
1919
|
case "success": {
|
|
1835
1920
|
const { artist } = result;
|
|
@@ -1839,12 +1924,12 @@ function useArtistPanel({
|
|
|
1839
1924
|
...panel,
|
|
1840
1925
|
title: artist.name,
|
|
1841
1926
|
content: /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
1842
|
-
shareUrl && /* @__PURE__ */
|
|
1843
|
-
artist.image && /* @__PURE__ */
|
|
1844
|
-
/* @__PURE__ */
|
|
1845
|
-
/* @__PURE__ */
|
|
1846
|
-
/* @__PURE__ */
|
|
1847
|
-
artist.tracksCount > 0 && /* @__PURE__ */
|
|
1927
|
+
shareUrl && /* @__PURE__ */ jsx29("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsx29("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ jsx29(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
1928
|
+
artist.image && /* @__PURE__ */ jsx29(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
1929
|
+
/* @__PURE__ */ jsx29(ArtistHeader, { artist }),
|
|
1930
|
+
/* @__PURE__ */ jsx29(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
1931
|
+
/* @__PURE__ */ jsx29(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
1932
|
+
artist.tracksCount > 0 && /* @__PURE__ */ jsx29(
|
|
1848
1933
|
ArtistSongs,
|
|
1849
1934
|
{
|
|
1850
1935
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -1854,7 +1939,7 @@ function useArtistPanel({
|
|
|
1854
1939
|
}
|
|
1855
1940
|
)
|
|
1856
1941
|
] }),
|
|
1857
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */
|
|
1942
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx29(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
1858
1943
|
};
|
|
1859
1944
|
}
|
|
1860
1945
|
}
|
|
@@ -1867,7 +1952,7 @@ import {
|
|
|
1867
1952
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1868
1953
|
|
|
1869
1954
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
1870
|
-
import { useState as
|
|
1955
|
+
import { useState as useState8 } from "react";
|
|
1871
1956
|
import { Pause as Pause2, Play as Play3 } from "lucide-react";
|
|
1872
1957
|
import { FieldList as FieldList2, PanelSection as PanelSection6, Skeleton as Skeleton5 } from "@behindthemusictree/ui";
|
|
1873
1958
|
import { PlayStates as PlayStates3, usePlayer as usePlayer9 } from "@behindthemusictree/app-kit/player";
|
|
@@ -1878,19 +1963,19 @@ import {
|
|
|
1878
1963
|
useSongHistory,
|
|
1879
1964
|
useTrackList as useTrackList5
|
|
1880
1965
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1881
|
-
import { Fragment as Fragment12, jsx as
|
|
1966
|
+
import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1882
1967
|
var numberFormat = new Intl.NumberFormat("en");
|
|
1883
1968
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
1884
1969
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
1885
1970
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
1886
|
-
const [open, setOpen] =
|
|
1971
|
+
const [open, setOpen] = useState8(false);
|
|
1887
1972
|
const history = useSongHistory(uuid, getBackendBaseUrl, open);
|
|
1888
1973
|
let body = null;
|
|
1889
|
-
if (history.status === "loading") body = /* @__PURE__ */
|
|
1890
|
-
else if (history.status === "error") body = /* @__PURE__ */
|
|
1974
|
+
if (history.status === "loading") body = /* @__PURE__ */ jsx30(Skeleton5, { className: "h-10", "aria-busy": "true" });
|
|
1975
|
+
else if (history.status === "error") body = /* @__PURE__ */ jsx30("p", { className: "text-red-600", children: "Could not load the history." });
|
|
1891
1976
|
else if (history.status === "success")
|
|
1892
|
-
body = history.entries.length === 0 ? /* @__PURE__ */
|
|
1893
|
-
/* @__PURE__ */
|
|
1977
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ jsx30("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx30("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs23("li", { className: "py-1.5 text-sm", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx30("span", { className: "font-medium", children: entry.action }),
|
|
1894
1979
|
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs23("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1895
1980
|
" ",
|
|
1896
1981
|
"\u2014 ",
|
|
@@ -1910,8 +1995,8 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
1910
1995
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
1911
1996
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
1912
1997
|
children: [
|
|
1913
|
-
/* @__PURE__ */
|
|
1914
|
-
/* @__PURE__ */
|
|
1998
|
+
/* @__PURE__ */ jsx30("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
1999
|
+
/* @__PURE__ */ jsx30("div", { className: "mt-1.5", children: body })
|
|
1915
2000
|
]
|
|
1916
2001
|
}
|
|
1917
2002
|
);
|
|
@@ -1937,13 +2022,13 @@ function SongPanelContent({
|
|
|
1937
2022
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
1938
2023
|
fields.push({
|
|
1939
2024
|
label: "Genre",
|
|
1940
|
-
value: path ? /* @__PURE__ */
|
|
1941
|
-
index > 0 && /* @__PURE__ */
|
|
1942
|
-
/* @__PURE__ */
|
|
2025
|
+
value: path ? /* @__PURE__ */ jsx30("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs23("span", { children: [
|
|
2026
|
+
index > 0 && /* @__PURE__ */ jsx30("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2027
|
+
/* @__PURE__ */ jsx30("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
1943
2028
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
1944
2029
|
});
|
|
1945
2030
|
} else {
|
|
1946
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */
|
|
2031
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ jsx30("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
1947
2032
|
}
|
|
1948
2033
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
1949
2034
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -1953,11 +2038,11 @@ function SongPanelContent({
|
|
|
1953
2038
|
const links = [];
|
|
1954
2039
|
if (musicBrainzUrl)
|
|
1955
2040
|
links.push(
|
|
1956
|
-
/* @__PURE__ */
|
|
2041
|
+
/* @__PURE__ */ jsx30("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
1957
2042
|
);
|
|
1958
2043
|
if (song.youtubeVideo)
|
|
1959
2044
|
links.push(
|
|
1960
|
-
/* @__PURE__ */
|
|
2045
|
+
/* @__PURE__ */ jsx30(
|
|
1961
2046
|
"a",
|
|
1962
2047
|
{
|
|
1963
2048
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -1972,33 +2057,33 @@ function SongPanelContent({
|
|
|
1972
2057
|
if (links.length > 0)
|
|
1973
2058
|
fields.push({
|
|
1974
2059
|
label: "Links",
|
|
1975
|
-
value: /* @__PURE__ */
|
|
2060
|
+
value: /* @__PURE__ */ jsx30("span", { className: "flex gap-3", children: links })
|
|
1976
2061
|
});
|
|
1977
2062
|
return /* @__PURE__ */ jsxs23(Fragment12, { children: [
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
unplayableReason ? /* @__PURE__ */
|
|
2063
|
+
/* @__PURE__ */ jsx30("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2064
|
+
unplayableReason ? /* @__PURE__ */ jsx30("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs23(
|
|
1980
2065
|
"button",
|
|
1981
2066
|
{
|
|
1982
2067
|
type: "button",
|
|
1983
2068
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
1984
2069
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
1985
2070
|
children: [
|
|
1986
|
-
/* @__PURE__ */
|
|
2071
|
+
/* @__PURE__ */ jsx30("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__ */ jsx30(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx30(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1987
2072
|
isPlaying ? "Pause" : "Play"
|
|
1988
2073
|
]
|
|
1989
2074
|
}
|
|
1990
2075
|
),
|
|
1991
|
-
shareUrl && /* @__PURE__ */
|
|
2076
|
+
shareUrl && /* @__PURE__ */ jsx30(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1992
2077
|
] }) }),
|
|
1993
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */
|
|
1994
|
-
/* @__PURE__ */
|
|
1995
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */
|
|
1996
|
-
/* @__PURE__ */
|
|
2078
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx30("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx30(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
|
|
2079
|
+
/* @__PURE__ */ jsx30(FieldList2, { items: fields }),
|
|
2080
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx30(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx30("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx30("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2081
|
+
/* @__PURE__ */ jsx30(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
1997
2082
|
] });
|
|
1998
2083
|
}
|
|
1999
2084
|
|
|
2000
2085
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2001
|
-
import { jsx as
|
|
2086
|
+
import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2002
2087
|
function useSongPanel({
|
|
2003
2088
|
uuid,
|
|
2004
2089
|
scope,
|
|
@@ -2021,23 +2106,23 @@ function useSongPanel({
|
|
|
2021
2106
|
...panel,
|
|
2022
2107
|
title: "Song",
|
|
2023
2108
|
content: /* @__PURE__ */ jsxs24("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2024
|
-
/* @__PURE__ */
|
|
2025
|
-
/* @__PURE__ */
|
|
2026
|
-
/* @__PURE__ */
|
|
2027
|
-
/* @__PURE__ */
|
|
2109
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-3 w-16" }),
|
|
2110
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-4 w-32" }),
|
|
2111
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-3 w-16" }),
|
|
2112
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-4 w-24" })
|
|
2028
2113
|
] })
|
|
2029
2114
|
};
|
|
2030
2115
|
case "not-found":
|
|
2031
2116
|
return {
|
|
2032
2117
|
...panel,
|
|
2033
2118
|
title: "Song not found",
|
|
2034
|
-
content: /* @__PURE__ */
|
|
2119
|
+
content: /* @__PURE__ */ jsx31("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2035
2120
|
};
|
|
2036
2121
|
case "error":
|
|
2037
2122
|
return {
|
|
2038
2123
|
...panel,
|
|
2039
2124
|
title: "Song",
|
|
2040
|
-
content: /* @__PURE__ */
|
|
2125
|
+
content: /* @__PURE__ */ jsx31("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2041
2126
|
};
|
|
2042
2127
|
case "success": {
|
|
2043
2128
|
const { song } = result;
|
|
@@ -2045,7 +2130,7 @@ function useSongPanel({
|
|
|
2045
2130
|
return {
|
|
2046
2131
|
...panel,
|
|
2047
2132
|
title: song.title,
|
|
2048
|
-
content: /* @__PURE__ */
|
|
2133
|
+
content: /* @__PURE__ */ jsx31(
|
|
2049
2134
|
SongPanelContent,
|
|
2050
2135
|
{
|
|
2051
2136
|
song,
|
|
@@ -2065,12 +2150,12 @@ function useSongPanel({
|
|
|
2065
2150
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2066
2151
|
import { MdMusicNote, MdVisibility } from "react-icons/md";
|
|
2067
2152
|
import { SegmentedControl } from "@behindthemusictree/ui";
|
|
2068
|
-
import { jsx as
|
|
2153
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2069
2154
|
function SizeMetricToggle({
|
|
2070
2155
|
value,
|
|
2071
2156
|
onChange
|
|
2072
2157
|
}) {
|
|
2073
|
-
return /* @__PURE__ */
|
|
2158
|
+
return /* @__PURE__ */ jsx32(
|
|
2074
2159
|
SegmentedControl,
|
|
2075
2160
|
{
|
|
2076
2161
|
variant: "floating",
|
|
@@ -2078,15 +2163,15 @@ function SizeMetricToggle({
|
|
|
2078
2163
|
value,
|
|
2079
2164
|
onChange,
|
|
2080
2165
|
options: [
|
|
2081
|
-
{ value: "songs", label: /* @__PURE__ */
|
|
2082
|
-
{ value: "views", label: /* @__PURE__ */
|
|
2166
|
+
{ value: "songs", label: /* @__PURE__ */ jsx32(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2167
|
+
{ value: "views", label: /* @__PURE__ */ jsx32(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2083
2168
|
]
|
|
2084
2169
|
}
|
|
2085
2170
|
);
|
|
2086
2171
|
}
|
|
2087
2172
|
|
|
2088
2173
|
// src/genre-tree/GenreTreeView.tsx
|
|
2089
|
-
import { Fragment as Fragment13, jsx as
|
|
2174
|
+
import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2090
2175
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2091
2176
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2092
2177
|
var noop = () => {
|
|
@@ -2121,6 +2206,7 @@ function GenreTreeView({
|
|
|
2121
2206
|
onArtistExpandedChange,
|
|
2122
2207
|
getShareUrl,
|
|
2123
2208
|
extraSearchGroups = [],
|
|
2209
|
+
SearchGroups,
|
|
2124
2210
|
onSearchQueryChange,
|
|
2125
2211
|
onSearchFocus,
|
|
2126
2212
|
artistSearch = true,
|
|
@@ -2149,19 +2235,19 @@ function GenreTreeView({
|
|
|
2149
2235
|
}) {
|
|
2150
2236
|
const getShareUrlRef = useRef3(getShareUrl);
|
|
2151
2237
|
getShareUrlRef.current = getShareUrl;
|
|
2152
|
-
const [internalArtistUuid, setInternalArtistUuid] =
|
|
2238
|
+
const [internalArtistUuid, setInternalArtistUuid] = useState9(null);
|
|
2153
2239
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2154
2240
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2155
|
-
const [internalSongUuid, setInternalSongUuid] =
|
|
2241
|
+
const [internalSongUuid, setInternalSongUuid] = useState9(null);
|
|
2156
2242
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2157
2243
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2158
2244
|
const changeSong = (uuid) => {
|
|
2159
2245
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2160
2246
|
onSongUuidChange?.(uuid);
|
|
2161
2247
|
};
|
|
2162
|
-
const [internalSizeMetric, setInternalSizeMetric] =
|
|
2248
|
+
const [internalSizeMetric, setInternalSizeMetric] = useState9("songs");
|
|
2163
2249
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2164
|
-
const deferredSizeMetric =
|
|
2250
|
+
const deferredSizeMetric = useDeferredValue2(sizeMetric);
|
|
2165
2251
|
const changeSizeMetric = (metric) => {
|
|
2166
2252
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2167
2253
|
onSizeMetricChange?.(metric);
|
|
@@ -2198,7 +2284,7 @@ function GenreTreeView({
|
|
|
2198
2284
|
artistUuid
|
|
2199
2285
|
});
|
|
2200
2286
|
const restoreRevision = viewportRestore?.revision;
|
|
2201
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] =
|
|
2287
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = useState9({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2202
2288
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2203
2289
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2204
2290
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2232,11 +2318,11 @@ function GenreTreeView({
|
|
|
2232
2318
|
const openSongRef = useRef3(openSong);
|
|
2233
2319
|
openSongRef.current = openSong;
|
|
2234
2320
|
const resolvedRenderSongLink = useCallback6(
|
|
2235
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */
|
|
2321
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx33("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2236
2322
|
[renderSongLink]
|
|
2237
2323
|
);
|
|
2238
|
-
const [expandedSongUuid, setExpandedSongUuid] =
|
|
2239
|
-
const [expandedArtistUuid, setExpandedArtistUuid] =
|
|
2324
|
+
const [expandedSongUuid, setExpandedSongUuid] = useState9(null);
|
|
2325
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = useState9(null);
|
|
2240
2326
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2241
2327
|
const songBaseUrl = getBackendBaseUrl;
|
|
2242
2328
|
const artistPanel = useArtistPanel({
|
|
@@ -2244,7 +2330,7 @@ function GenreTreeView({
|
|
|
2244
2330
|
scope,
|
|
2245
2331
|
getBackendBaseUrl: artistBaseUrl,
|
|
2246
2332
|
getShareUrl: getShareUrlRef.current,
|
|
2247
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2333
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx33("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2248
2334
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2249
2335
|
onExpandedChange: (on) => {
|
|
2250
2336
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2253,7 +2339,7 @@ function GenreTreeView({
|
|
|
2253
2339
|
renderSongLink: resolvedRenderSongLink,
|
|
2254
2340
|
onClose: () => changeArtist(null)
|
|
2255
2341
|
});
|
|
2256
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2342
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx33("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2257
2343
|
const getGenrePath = (genre) => {
|
|
2258
2344
|
const path = [];
|
|
2259
2345
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2285,8 +2371,8 @@ function GenreTreeView({
|
|
|
2285
2371
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2286
2372
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2287
2373
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2288
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] =
|
|
2289
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] =
|
|
2374
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState9(false);
|
|
2375
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = useState9(false);
|
|
2290
2376
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2291
2377
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2292
2378
|
const changeCollapsed = (next) => {
|
|
@@ -2300,10 +2386,10 @@ function GenreTreeView({
|
|
|
2300
2386
|
useEffect5(() => {
|
|
2301
2387
|
setUncontrolledCollapsed(false);
|
|
2302
2388
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2303
|
-
const [isWheelRevealed, setIsWheelRevealed] =
|
|
2389
|
+
const [isWheelRevealed, setIsWheelRevealed] = useState9(false);
|
|
2304
2390
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2305
2391
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2306
|
-
const [resolveStyle, setResolveStyle] =
|
|
2392
|
+
const [resolveStyle, setResolveStyle] = useState9(null);
|
|
2307
2393
|
const handleNodeStyleResolverChange = useCallback6(
|
|
2308
2394
|
(resolver) => setResolveStyle(() => resolver),
|
|
2309
2395
|
[]
|
|
@@ -2318,7 +2404,7 @@ function GenreTreeView({
|
|
|
2318
2404
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2319
2405
|
resolveStyle
|
|
2320
2406
|
);
|
|
2321
|
-
const [selectionHistory, setSelectionHistory] =
|
|
2407
|
+
const [selectionHistory, setSelectionHistory] = useState9({
|
|
2322
2408
|
current: selectedNodeId,
|
|
2323
2409
|
previous: null
|
|
2324
2410
|
});
|
|
@@ -2331,28 +2417,6 @@ function GenreTreeView({
|
|
|
2331
2417
|
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2332
2418
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2333
2419
|
const { showPopup, hidePopup } = usePopup5();
|
|
2334
|
-
const [searchText, setSearchText] = useState8("");
|
|
2335
|
-
const artistGroup = useArtistSearchGroup(
|
|
2336
|
-
artistBaseUrl,
|
|
2337
|
-
searchText,
|
|
2338
|
-
(artist) => {
|
|
2339
|
-
openArtist(artist.uuid);
|
|
2340
|
-
onArtistSelect?.(artist);
|
|
2341
|
-
},
|
|
2342
|
-
Boolean(artistSearch)
|
|
2343
|
-
);
|
|
2344
|
-
const songGroup = useSongSearchGroup(
|
|
2345
|
-
songBaseUrl,
|
|
2346
|
-
searchText,
|
|
2347
|
-
(song) => {
|
|
2348
|
-
openSong(song.uuid);
|
|
2349
|
-
onSongSelect?.(song);
|
|
2350
|
-
},
|
|
2351
|
-
Boolean(songSearch)
|
|
2352
|
-
);
|
|
2353
|
-
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2354
|
-
(group) => group !== void 0
|
|
2355
|
-
);
|
|
2356
2420
|
const handleSelectedNodeChange = (node) => {
|
|
2357
2421
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2358
2422
|
selectNode(node);
|
|
@@ -2376,7 +2440,7 @@ function GenreTreeView({
|
|
|
2376
2440
|
};
|
|
2377
2441
|
const renderGenreBody = (relation) => {
|
|
2378
2442
|
const { node } = relation;
|
|
2379
|
-
const family = /* @__PURE__ */
|
|
2443
|
+
const family = /* @__PURE__ */ jsx33(
|
|
2380
2444
|
GenreNodeFamily,
|
|
2381
2445
|
{
|
|
2382
2446
|
node,
|
|
@@ -2387,7 +2451,7 @@ function GenreTreeView({
|
|
|
2387
2451
|
onSelectNode: selectGenreFromPanel
|
|
2388
2452
|
}
|
|
2389
2453
|
);
|
|
2390
|
-
const ancestors = /* @__PURE__ */
|
|
2454
|
+
const ancestors = /* @__PURE__ */ jsx33(GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2391
2455
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2392
2456
|
const criteria = genrePlaylist?.criteria;
|
|
2393
2457
|
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
@@ -2411,7 +2475,7 @@ function GenreTreeView({
|
|
|
2411
2475
|
) : void 0;
|
|
2412
2476
|
return {
|
|
2413
2477
|
primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2414
|
-
/* @__PURE__ */
|
|
2478
|
+
/* @__PURE__ */ jsx33(
|
|
2415
2479
|
GenreInfoPanelBody,
|
|
2416
2480
|
{
|
|
2417
2481
|
family: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
@@ -2420,6 +2484,7 @@ function GenreTreeView({
|
|
|
2420
2484
|
] }),
|
|
2421
2485
|
genreUuid,
|
|
2422
2486
|
genrePlaylist,
|
|
2487
|
+
catalogue: genrePlaylist.catalogue,
|
|
2423
2488
|
wikidataId: criteria.wikidataId,
|
|
2424
2489
|
scope,
|
|
2425
2490
|
getBackendBaseUrl,
|
|
@@ -2432,7 +2497,7 @@ function GenreTreeView({
|
|
|
2432
2497
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2433
2498
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2434
2499
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2435
|
-
/* @__PURE__ */
|
|
2500
|
+
/* @__PURE__ */ jsx33(
|
|
2436
2501
|
ChangeParentPopup,
|
|
2437
2502
|
{
|
|
2438
2503
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2507,19 +2572,19 @@ function GenreTreeView({
|
|
|
2507
2572
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2508
2573
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2509
2574
|
} : void 0;
|
|
2510
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */
|
|
2575
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx33(
|
|
2511
2576
|
IconButton,
|
|
2512
2577
|
{
|
|
2513
2578
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2514
2579
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2515
2580
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2516
|
-
children: activePanel.expanded ? /* @__PURE__ */
|
|
2581
|
+
children: activePanel.expanded ? /* @__PURE__ */ jsx33(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx33(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2517
2582
|
}
|
|
2518
2583
|
) : null;
|
|
2519
2584
|
const renderArtistFilter = () => {
|
|
2520
2585
|
if (isLoading || !filterArtist) return null;
|
|
2521
2586
|
return /* @__PURE__ */ jsxs25("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2522
|
-
/* @__PURE__ */
|
|
2587
|
+
/* @__PURE__ */ jsx33(
|
|
2523
2588
|
"button",
|
|
2524
2589
|
{
|
|
2525
2590
|
type: "button",
|
|
@@ -2531,7 +2596,7 @@ function GenreTreeView({
|
|
|
2531
2596
|
children: filterArtist.name
|
|
2532
2597
|
}
|
|
2533
2598
|
),
|
|
2534
|
-
/* @__PURE__ */
|
|
2599
|
+
/* @__PURE__ */ jsx33(
|
|
2535
2600
|
"button",
|
|
2536
2601
|
{
|
|
2537
2602
|
type: "button",
|
|
@@ -2544,25 +2609,35 @@ function GenreTreeView({
|
|
|
2544
2609
|
] });
|
|
2545
2610
|
};
|
|
2546
2611
|
const actions = /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2547
|
-
!isLoading && /* @__PURE__ */
|
|
2548
|
-
|
|
2612
|
+
!isLoading && /* @__PURE__ */ jsx33(
|
|
2613
|
+
GenreTreeSearch,
|
|
2549
2614
|
{
|
|
2615
|
+
artistBaseUrl,
|
|
2616
|
+
songBaseUrl,
|
|
2617
|
+
artistSearch: Boolean(artistSearch),
|
|
2618
|
+
songSearch: Boolean(songSearch),
|
|
2619
|
+
onArtistSelect: (artist) => {
|
|
2620
|
+
openArtist(artist.uuid);
|
|
2621
|
+
onArtistSelect?.(artist);
|
|
2622
|
+
},
|
|
2623
|
+
onSongSelect: (song) => {
|
|
2624
|
+
openSong(song.uuid);
|
|
2625
|
+
onSongSelect?.(song);
|
|
2626
|
+
},
|
|
2627
|
+
SearchGroups,
|
|
2550
2628
|
genrePlaylists: visibleGenrePlaylists,
|
|
2551
2629
|
onSelect: handleGenreSearchSelect,
|
|
2552
2630
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2553
2631
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2554
2632
|
onPrevious: handlePrevious,
|
|
2555
|
-
onQueryChange:
|
|
2556
|
-
setSearchText(text);
|
|
2557
|
-
onSearchQueryChange?.(text);
|
|
2558
|
-
},
|
|
2633
|
+
onQueryChange: onSearchQueryChange,
|
|
2559
2634
|
onFocus: onSearchFocus,
|
|
2560
|
-
extraGroups
|
|
2635
|
+
extraGroups: extraSearchGroups
|
|
2561
2636
|
}
|
|
2562
2637
|
),
|
|
2563
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2638
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx33("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2564
2639
|
/* @__PURE__ */ jsxs25("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: [
|
|
2565
|
-
!isLoading && !isControlled && /* @__PURE__ */
|
|
2640
|
+
!isLoading && !isControlled && /* @__PURE__ */ jsx33(
|
|
2566
2641
|
SegmentedControl2,
|
|
2567
2642
|
{
|
|
2568
2643
|
"aria-label": "Tree view mode",
|
|
@@ -2576,13 +2651,13 @@ function GenreTreeView({
|
|
|
2576
2651
|
]
|
|
2577
2652
|
}
|
|
2578
2653
|
),
|
|
2579
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */
|
|
2654
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ jsx33(
|
|
2580
2655
|
"div",
|
|
2581
2656
|
{
|
|
2582
2657
|
className: "flex items-center gap-1",
|
|
2583
2658
|
role: "group",
|
|
2584
2659
|
"aria-label": "Tree display options",
|
|
2585
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */
|
|
2660
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx33(
|
|
2586
2661
|
Button,
|
|
2587
2662
|
{
|
|
2588
2663
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2593,7 +2668,7 @@ function GenreTreeView({
|
|
|
2593
2668
|
)
|
|
2594
2669
|
}
|
|
2595
2670
|
),
|
|
2596
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */
|
|
2671
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ jsx33(
|
|
2597
2672
|
IconTextButton,
|
|
2598
2673
|
{
|
|
2599
2674
|
icon: Plus2,
|
|
@@ -2609,28 +2684,28 @@ function GenreTreeView({
|
|
|
2609
2684
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2610
2685
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2611
2686
|
children: [
|
|
2612
|
-
/* @__PURE__ */
|
|
2687
|
+
/* @__PURE__ */ jsx33(
|
|
2613
2688
|
"div",
|
|
2614
2689
|
{
|
|
2615
2690
|
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"}`,
|
|
2616
2691
|
children: actions
|
|
2617
2692
|
}
|
|
2618
2693
|
),
|
|
2619
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2694
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx33(
|
|
2620
2695
|
"div",
|
|
2621
2696
|
{
|
|
2622
2697
|
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"}`,
|
|
2623
2698
|
children: renderArtistFilter()
|
|
2624
2699
|
}
|
|
2625
2700
|
),
|
|
2626
|
-
/* @__PURE__ */
|
|
2627
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */
|
|
2628
|
-
isLoading ? /* @__PURE__ */
|
|
2701
|
+
/* @__PURE__ */ jsx33("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs25("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2702
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx33("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2703
|
+
isLoading ? /* @__PURE__ */ jsx33(GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2629
2704
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2630
2705
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2631
2706
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2632
2707
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2633
|
-
/* @__PURE__ */
|
|
2708
|
+
/* @__PURE__ */ jsx33("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx33(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx33(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx33(
|
|
2634
2709
|
GenrePlaylistTreeWheel,
|
|
2635
2710
|
{
|
|
2636
2711
|
scope,
|
|
@@ -2649,7 +2724,7 @@ function GenreTreeView({
|
|
|
2649
2724
|
busy,
|
|
2650
2725
|
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2651
2726
|
floatingControlsLeading,
|
|
2652
|
-
canSizeByViews && /* @__PURE__ */
|
|
2727
|
+
canSizeByViews && /* @__PURE__ */ jsx33(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2653
2728
|
] }) : void 0,
|
|
2654
2729
|
defaultViewport,
|
|
2655
2730
|
viewportRestore,
|
|
@@ -2658,7 +2733,7 @@ function GenreTreeView({
|
|
|
2658
2733
|
) }) })
|
|
2659
2734
|
) : viewMode === "pop-core" ? (
|
|
2660
2735
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2661
|
-
/* @__PURE__ */
|
|
2736
|
+
/* @__PURE__ */ jsx33("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx33(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx33(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx33(
|
|
2662
2737
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2663
2738
|
{
|
|
2664
2739
|
scope,
|
|
@@ -2677,7 +2752,7 @@ function GenreTreeView({
|
|
|
2677
2752
|
busy,
|
|
2678
2753
|
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2679
2754
|
floatingControlsLeading,
|
|
2680
|
-
canSizeByViews && /* @__PURE__ */
|
|
2755
|
+
canSizeByViews && /* @__PURE__ */ jsx33(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2681
2756
|
] }) : void 0,
|
|
2682
2757
|
defaultViewport,
|
|
2683
2758
|
viewportRestore,
|
|
@@ -2685,7 +2760,7 @@ function GenreTreeView({
|
|
|
2685
2760
|
...focus
|
|
2686
2761
|
}
|
|
2687
2762
|
) }) })
|
|
2688
|
-
) : viewMode === "outline" ? /* @__PURE__ */
|
|
2763
|
+
) : viewMode === "outline" ? /* @__PURE__ */ jsx33("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx33(
|
|
2689
2764
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2690
2765
|
{
|
|
2691
2766
|
outline: true,
|
|
@@ -2701,13 +2776,13 @@ function GenreTreeView({
|
|
|
2701
2776
|
selectedNodeId,
|
|
2702
2777
|
readOnly
|
|
2703
2778
|
}
|
|
2704
|
-
) }) : /* @__PURE__ */
|
|
2779
|
+
) }) : /* @__PURE__ */ jsx33("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(
|
|
2705
2780
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2706
|
-
return /* @__PURE__ */
|
|
2781
|
+
return /* @__PURE__ */ jsx33(
|
|
2707
2782
|
"div",
|
|
2708
2783
|
{
|
|
2709
2784
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2710
|
-
children: /* @__PURE__ */
|
|
2785
|
+
children: /* @__PURE__ */ jsx33("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx33(
|
|
2711
2786
|
GenrePlaylistTreePerRoot,
|
|
2712
2787
|
{
|
|
2713
2788
|
scope,
|
|
@@ -2733,7 +2808,7 @@ function GenreTreeView({
|
|
|
2733
2808
|
);
|
|
2734
2809
|
}
|
|
2735
2810
|
) }),
|
|
2736
|
-
/* @__PURE__ */
|
|
2811
|
+
/* @__PURE__ */ jsx33(
|
|
2737
2812
|
DetailPanel,
|
|
2738
2813
|
{
|
|
2739
2814
|
ref: panelRef,
|
|
@@ -2775,9 +2850,11 @@ import {
|
|
|
2775
2850
|
import { DetailPanel as DetailPanel2, IconButton as IconButton2, Skeleton as Skeleton7 } from "@behindthemusictree/ui";
|
|
2776
2851
|
import { Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
|
|
2777
2852
|
import {
|
|
2778
|
-
useFetchGenreDetail
|
|
2853
|
+
useFetchGenreDetail,
|
|
2854
|
+
genreMusicCount as genreMusicCount2,
|
|
2855
|
+
GenrePlaylistSummarySchema
|
|
2779
2856
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
2780
|
-
import { Fragment as Fragment14, jsx as
|
|
2857
|
+
import { Fragment as Fragment14, jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2781
2858
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2782
2859
|
function toNode(genre, parentId) {
|
|
2783
2860
|
return {
|
|
@@ -2820,7 +2897,7 @@ function GenreDetailPanel({
|
|
|
2820
2897
|
id: detail.uuid,
|
|
2821
2898
|
parentId: detail.parent?.uuid ?? null,
|
|
2822
2899
|
name: detail.name,
|
|
2823
|
-
itemCount: detail.tracksCount,
|
|
2900
|
+
itemCount: scope === "reference" ? genreMusicCount2(GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
|
|
2824
2901
|
wikidataId: detail.wikidataId
|
|
2825
2902
|
},
|
|
2826
2903
|
fill: rootColor,
|
|
@@ -2830,19 +2907,19 @@ function GenreDetailPanel({
|
|
|
2830
2907
|
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
2831
2908
|
)
|
|
2832
2909
|
};
|
|
2833
|
-
}, [detail]);
|
|
2910
|
+
}, [detail, scope]);
|
|
2834
2911
|
if (isError) {
|
|
2835
|
-
return /* @__PURE__ */
|
|
2912
|
+
return /* @__PURE__ */ jsx34("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
2836
2913
|
}
|
|
2837
2914
|
if (isPending || !detail || !view) {
|
|
2838
2915
|
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2839
|
-
/* @__PURE__ */
|
|
2840
|
-
/* @__PURE__ */
|
|
2841
|
-
/* @__PURE__ */
|
|
2916
|
+
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-6 w-2/3" }),
|
|
2917
|
+
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-4 w-full" }),
|
|
2918
|
+
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-4 w-3/4" })
|
|
2842
2919
|
] });
|
|
2843
2920
|
}
|
|
2844
2921
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
2845
|
-
const family = /* @__PURE__ */
|
|
2922
|
+
const family = /* @__PURE__ */ jsx34(
|
|
2846
2923
|
GenreNodeFamily2,
|
|
2847
2924
|
{
|
|
2848
2925
|
node: view.node,
|
|
@@ -2853,20 +2930,20 @@ function GenreDetailPanel({
|
|
|
2853
2930
|
onSelectNode: selectGenre
|
|
2854
2931
|
}
|
|
2855
2932
|
);
|
|
2856
|
-
const ancestors = /* @__PURE__ */
|
|
2933
|
+
const ancestors = /* @__PURE__ */ jsx34(GenreNodeAncestors2, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2857
2934
|
return /* @__PURE__ */ jsxs26(
|
|
2858
2935
|
DetailPanel2,
|
|
2859
2936
|
{
|
|
2860
2937
|
side: "left",
|
|
2861
2938
|
title: view.node.name,
|
|
2862
2939
|
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
2863
|
-
headerActions: onExpandedChange ? /* @__PURE__ */
|
|
2940
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ jsx34(
|
|
2864
2941
|
IconButton2,
|
|
2865
2942
|
{
|
|
2866
2943
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
2867
2944
|
style: { color: "inherit" },
|
|
2868
2945
|
onClick: () => onExpandedChange(!expanded),
|
|
2869
|
-
children: expanded ? /* @__PURE__ */
|
|
2946
|
+
children: expanded ? /* @__PURE__ */ jsx34(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx34(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2870
2947
|
}
|
|
2871
2948
|
) : void 0,
|
|
2872
2949
|
onClose,
|
|
@@ -2875,7 +2952,7 @@ function GenreDetailPanel({
|
|
|
2875
2952
|
onExpandedChange,
|
|
2876
2953
|
edgeTab,
|
|
2877
2954
|
children: [
|
|
2878
|
-
/* @__PURE__ */
|
|
2955
|
+
/* @__PURE__ */ jsx34(
|
|
2879
2956
|
GenreInfoPanelBody,
|
|
2880
2957
|
{
|
|
2881
2958
|
family: /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
@@ -2883,7 +2960,8 @@ function GenreDetailPanel({
|
|
|
2883
2960
|
ancestors
|
|
2884
2961
|
] }),
|
|
2885
2962
|
genreUuid: detail.uuid,
|
|
2886
|
-
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
2963
|
+
genrePlaylist: scope === "reference" ? GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
2964
|
+
catalogue: detail.catalogue,
|
|
2887
2965
|
wikidataId: detail.wikidataId,
|
|
2888
2966
|
scope,
|
|
2889
2967
|
getBackendBaseUrl,
|
|
@@ -2911,8 +2989,8 @@ import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useRef as
|
|
|
2911
2989
|
import { ListMusic } from "lucide-react";
|
|
2912
2990
|
import { useTrackList as useTrackList6 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2913
2991
|
import { TrackListOriginType as TrackListOriginType2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2914
|
-
import { useTrackNoun as
|
|
2915
|
-
import { jsx as
|
|
2992
|
+
import { useTrackNoun as useTrackNoun4 } from "@behindthemusictree/app-kit/track-noun";
|
|
2993
|
+
import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2916
2994
|
var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect2;
|
|
2917
2995
|
function TrackListSidebar({
|
|
2918
2996
|
className: className2 = "",
|
|
@@ -2926,7 +3004,7 @@ function TrackListSidebar({
|
|
|
2926
3004
|
renderSongLink
|
|
2927
3005
|
}) {
|
|
2928
3006
|
const { trackList, loadMore, autoAdvanceScrollRequest } = useTrackList6();
|
|
2929
|
-
const trackNoun =
|
|
3007
|
+
const trackNoun = useTrackNoun4();
|
|
2930
3008
|
const trackListRef = useRef4(null);
|
|
2931
3009
|
const previousOriginRef = useRef4(trackList?.origin ?? null);
|
|
2932
3010
|
const handledAutoAdvanceRequestIdRef = useRef4(null);
|
|
@@ -3024,15 +3102,15 @@ function TrackListSidebar({
|
|
|
3024
3102
|
/* @__PURE__ */ jsxs27("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3025
3103
|
/* @__PURE__ */ jsxs27("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3026
3104
|
/* @__PURE__ */ jsxs27("div", { className: "origin flex text-xl", children: [
|
|
3027
|
-
/* @__PURE__ */
|
|
3028
|
-
/* @__PURE__ */
|
|
3105
|
+
/* @__PURE__ */ jsx35("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3106
|
+
/* @__PURE__ */ jsx35("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__ */ jsx35("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3029
3107
|
] }),
|
|
3030
3108
|
/* @__PURE__ */ jsxs27("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3031
3109
|
trackList.origin.type === TrackListOriginType2.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType2.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3032
3110
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3033
3111
|
] })
|
|
3034
3112
|
] }),
|
|
3035
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */
|
|
3113
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx35(
|
|
3036
3114
|
"button",
|
|
3037
3115
|
{
|
|
3038
3116
|
type: "button",
|
|
@@ -3041,7 +3119,7 @@ function TrackListSidebar({
|
|
|
3041
3119
|
"aria-expanded": !isCollapsed,
|
|
3042
3120
|
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"}`,
|
|
3043
3121
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3044
|
-
children: /* @__PURE__ */
|
|
3122
|
+
children: /* @__PURE__ */ jsx35(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3045
3123
|
}
|
|
3046
3124
|
)
|
|
3047
3125
|
] }),
|
|
@@ -3055,7 +3133,7 @@ function TrackListSidebar({
|
|
|
3055
3133
|
onPointerDown: markUserScrollIntent,
|
|
3056
3134
|
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",
|
|
3057
3135
|
children: [
|
|
3058
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */
|
|
3136
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ jsx35("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx35(
|
|
3059
3137
|
TrackItem,
|
|
3060
3138
|
{
|
|
3061
3139
|
track,
|
|
@@ -3066,7 +3144,7 @@ function TrackListSidebar({
|
|
|
3066
3144
|
renderSongLink
|
|
3067
3145
|
}
|
|
3068
3146
|
) }, track.uuid)),
|
|
3069
|
-
hasMore && /* @__PURE__ */
|
|
3147
|
+
hasMore && /* @__PURE__ */ jsx35("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3070
3148
|
]
|
|
3071
3149
|
}
|
|
3072
3150
|
)
|
|
@@ -3076,7 +3154,7 @@ function TrackListSidebar({
|
|
|
3076
3154
|
}
|
|
3077
3155
|
|
|
3078
3156
|
// src/genre-tree/PlayerPanel.tsx
|
|
3079
|
-
import { useEffect as useEffect7, useState as
|
|
3157
|
+
import { useEffect as useEffect7, useState as useState10 } from "react";
|
|
3080
3158
|
import { twMerge } from "tailwind-merge";
|
|
3081
3159
|
import {
|
|
3082
3160
|
usePlayer as usePlayer11,
|
|
@@ -3091,7 +3169,7 @@ import { PanelEdgeTab } from "@behindthemusictree/ui";
|
|
|
3091
3169
|
import { Pause as Pause3, Play as Play4, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
|
|
3092
3170
|
import { formatTime, useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
|
|
3093
3171
|
import { PlayStates as PlayStates4, useCurrentTime, usePlayer as usePlayer10 } from "@behindthemusictree/app-kit/player";
|
|
3094
|
-
import { jsx as
|
|
3172
|
+
import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3095
3173
|
function PlayerControls() {
|
|
3096
3174
|
const {
|
|
3097
3175
|
duration,
|
|
@@ -3117,8 +3195,8 @@ function PlayerControls() {
|
|
|
3117
3195
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3118
3196
|
children: [
|
|
3119
3197
|
/* @__PURE__ */ jsxs28("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3120
|
-
/* @__PURE__ */
|
|
3121
|
-
/* @__PURE__ */
|
|
3198
|
+
/* @__PURE__ */ jsx36("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
|
|
3199
|
+
/* @__PURE__ */ jsx36(
|
|
3122
3200
|
"input",
|
|
3123
3201
|
{
|
|
3124
3202
|
"aria-label": "Seek",
|
|
@@ -3132,11 +3210,11 @@ function PlayerControls() {
|
|
|
3132
3210
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3133
3211
|
}
|
|
3134
3212
|
),
|
|
3135
|
-
/* @__PURE__ */
|
|
3213
|
+
/* @__PURE__ */ jsx36("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
|
|
3136
3214
|
] }),
|
|
3137
3215
|
/* @__PURE__ */ jsxs28("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3138
3216
|
/* @__PURE__ */ jsxs28("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3139
|
-
/* @__PURE__ */
|
|
3217
|
+
/* @__PURE__ */ jsx36(
|
|
3140
3218
|
"button",
|
|
3141
3219
|
{
|
|
3142
3220
|
type: "button",
|
|
@@ -3145,10 +3223,10 @@ function PlayerControls() {
|
|
|
3145
3223
|
disabled: !isReady || !hasPreviousTrack,
|
|
3146
3224
|
onClick: playPreviousTrack,
|
|
3147
3225
|
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",
|
|
3148
|
-
children: /* @__PURE__ */
|
|
3226
|
+
children: /* @__PURE__ */ jsx36(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3149
3227
|
}
|
|
3150
3228
|
),
|
|
3151
|
-
/* @__PURE__ */
|
|
3229
|
+
/* @__PURE__ */ jsx36(
|
|
3152
3230
|
"button",
|
|
3153
3231
|
{
|
|
3154
3232
|
type: "button",
|
|
@@ -3157,10 +3235,10 @@ function PlayerControls() {
|
|
|
3157
3235
|
disabled: !isReady,
|
|
3158
3236
|
onClick: handlePlayPauseAction,
|
|
3159
3237
|
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",
|
|
3160
|
-
children: isPlaying ? /* @__PURE__ */
|
|
3238
|
+
children: isPlaying ? /* @__PURE__ */ jsx36(Pause3, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx36(Play4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3161
3239
|
}
|
|
3162
3240
|
),
|
|
3163
|
-
/* @__PURE__ */
|
|
3241
|
+
/* @__PURE__ */ jsx36(
|
|
3164
3242
|
"button",
|
|
3165
3243
|
{
|
|
3166
3244
|
type: "button",
|
|
@@ -3169,14 +3247,14 @@ function PlayerControls() {
|
|
|
3169
3247
|
disabled: !isReady || !hasNextTrack,
|
|
3170
3248
|
onClick: playNextTrack,
|
|
3171
3249
|
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",
|
|
3172
|
-
children: /* @__PURE__ */
|
|
3250
|
+
children: /* @__PURE__ */ jsx36(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3173
3251
|
}
|
|
3174
3252
|
)
|
|
3175
3253
|
] }),
|
|
3176
3254
|
/* @__PURE__ */ jsxs28("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: [
|
|
3177
|
-
volume === 0 ? /* @__PURE__ */
|
|
3178
|
-
/* @__PURE__ */
|
|
3179
|
-
/* @__PURE__ */
|
|
3255
|
+
volume === 0 ? /* @__PURE__ */ jsx36(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx36(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3256
|
+
/* @__PURE__ */ jsx36("span", { className: "sr-only", children: "Volume" }),
|
|
3257
|
+
/* @__PURE__ */ jsx36(
|
|
3180
3258
|
"input",
|
|
3181
3259
|
{
|
|
3182
3260
|
"aria-label": "Volume",
|
|
@@ -3198,7 +3276,7 @@ function PlayerControls() {
|
|
|
3198
3276
|
}
|
|
3199
3277
|
|
|
3200
3278
|
// src/genre-tree/PlayerPanel.tsx
|
|
3201
|
-
import { jsx as
|
|
3279
|
+
import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3202
3280
|
var PANEL_ID = "player-panel";
|
|
3203
3281
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3204
3282
|
function PlayerPanel({
|
|
@@ -3212,12 +3290,12 @@ function PlayerPanel({
|
|
|
3212
3290
|
}) {
|
|
3213
3291
|
const { playerTrackObject, retryPlayback } = usePlayer11();
|
|
3214
3292
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = useTrackListSidebarVisibility();
|
|
3215
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] =
|
|
3293
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = useState10("");
|
|
3216
3294
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3217
3295
|
useEffect7(() => {
|
|
3218
3296
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3219
3297
|
}, [loadedTrackTitle]);
|
|
3220
|
-
const [uncontrolledExtended, setUncontrolledExtended] =
|
|
3298
|
+
const [uncontrolledExtended, setUncontrolledExtended] = useState10(false);
|
|
3221
3299
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3222
3300
|
const setIsExtendRequested = (next) => {
|
|
3223
3301
|
setUncontrolledExtended(next);
|
|
@@ -3251,7 +3329,7 @@ function PlayerPanel({
|
|
|
3251
3329
|
),
|
|
3252
3330
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3253
3331
|
children: [
|
|
3254
|
-
/* @__PURE__ */
|
|
3332
|
+
/* @__PURE__ */ jsx37(
|
|
3255
3333
|
PanelEdgeTab,
|
|
3256
3334
|
{
|
|
3257
3335
|
side: "right",
|
|
@@ -3270,19 +3348,19 @@ function PlayerPanel({
|
|
|
3270
3348
|
),
|
|
3271
3349
|
children: [
|
|
3272
3350
|
/* @__PURE__ */ jsxs29("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3273
|
-
/* @__PURE__ */
|
|
3351
|
+
/* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx37("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3274
3352
|
/* @__PURE__ */ jsxs29("div", { className: "relative w-full shrink-0", children: [
|
|
3275
|
-
/* @__PURE__ */
|
|
3353
|
+
/* @__PURE__ */ jsx37(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3276
3354
|
playerTrackObject?.loadError && /* @__PURE__ */ jsxs29("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3277
3355
|
playerTrackObject.loadError,
|
|
3278
|
-
/* @__PURE__ */
|
|
3356
|
+
/* @__PURE__ */ jsx37("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3279
3357
|
void retryPlayback();
|
|
3280
3358
|
}, children: "Retry" })
|
|
3281
3359
|
] })
|
|
3282
3360
|
] }),
|
|
3283
|
-
/* @__PURE__ */
|
|
3361
|
+
/* @__PURE__ */ jsx37(PlayerControls, {})
|
|
3284
3362
|
] }),
|
|
3285
|
-
/* @__PURE__ */
|
|
3363
|
+
/* @__PURE__ */ jsx37(
|
|
3286
3364
|
TrackListSidebar,
|
|
3287
3365
|
{
|
|
3288
3366
|
layout: "inline",
|
|
@@ -3309,11 +3387,11 @@ function PlayerPanel({
|
|
|
3309
3387
|
import { v4 as uuidv4 } from "uuid";
|
|
3310
3388
|
import { AiFillStar, AiOutlineStar } from "react-icons/ai";
|
|
3311
3389
|
import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
|
|
3312
|
-
import { jsx as
|
|
3390
|
+
import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3313
3391
|
function Rating({ rating, handleChange }) {
|
|
3314
3392
|
const instanceId = uuidv4();
|
|
3315
|
-
return /* @__PURE__ */
|
|
3316
|
-
/* @__PURE__ */
|
|
3393
|
+
return /* @__PURE__ */ jsx38("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs30("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3394
|
+
/* @__PURE__ */ jsx38(
|
|
3317
3395
|
"input",
|
|
3318
3396
|
{
|
|
3319
3397
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3325,16 +3403,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3325
3403
|
onChange: handleChange
|
|
3326
3404
|
}
|
|
3327
3405
|
),
|
|
3328
|
-
/* @__PURE__ */
|
|
3329
|
-
/* @__PURE__ */
|
|
3406
|
+
/* @__PURE__ */ jsx38("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs30("div", { className: "flex", children: [
|
|
3407
|
+
/* @__PURE__ */ jsx38(
|
|
3330
3408
|
AiOutlineStar,
|
|
3331
3409
|
{
|
|
3332
3410
|
className: `w-7 h-7 absolute top-0 left-0 ${rating === FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3333
3411
|
}
|
|
3334
3412
|
),
|
|
3335
|
-
/* @__PURE__ */
|
|
3413
|
+
/* @__PURE__ */ jsx38("span", { className: "flex-grow w-8" }),
|
|
3336
3414
|
" "
|
|
3337
|
-
] }) : /* @__PURE__ */
|
|
3415
|
+
] }) : /* @__PURE__ */ jsx38(
|
|
3338
3416
|
AiFillStar,
|
|
3339
3417
|
{
|
|
3340
3418
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3367,4 +3445,4 @@ export {
|
|
|
3367
3445
|
GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
|
|
3368
3446
|
GenreTreeOutlineSkeleton
|
|
3369
3447
|
};
|
|
3370
|
-
//# sourceMappingURL=chunk-
|
|
3448
|
+
//# sourceMappingURL=chunk-JP7PID7L.js.map
|