@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
|
@@ -46,19 +46,20 @@ __export(genre_tree_exports, {
|
|
|
46
46
|
module.exports = __toCommonJS(genre_tree_exports);
|
|
47
47
|
|
|
48
48
|
// src/genre-tree/GenreTreeView.tsx
|
|
49
|
-
var
|
|
49
|
+
var import_react17 = require("react");
|
|
50
50
|
var import_react_query3 = require("@tanstack/react-query");
|
|
51
51
|
var import_lucide_react11 = require("lucide-react");
|
|
52
52
|
var import_lucide_react12 = require("lucide-react");
|
|
53
53
|
var import_ui16 = require("@behindthemusictree/ui");
|
|
54
54
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
55
|
-
var
|
|
55
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
56
56
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
57
57
|
|
|
58
58
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
59
|
+
var import_genre_tree = require("@behindthemusictree/app-kit/genre-tree");
|
|
59
60
|
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
60
61
|
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
61
|
-
if (metric === "songs") return genrePlaylist
|
|
62
|
+
if (metric === "songs") return (0, import_genre_tree.genrePlaylistTrackCount)(genrePlaylist);
|
|
62
63
|
if (genrePlaylist.viewsCount === void 0) {
|
|
63
64
|
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
64
65
|
}
|
|
@@ -82,6 +83,7 @@ function toTreeNodes(genrePlaylists, sizeMetric) {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
86
|
+
var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
|
|
85
87
|
var import_react3 = require("react");
|
|
86
88
|
var import_genre_tree_view = require("@behindthemusictree/genre-tree-view");
|
|
87
89
|
var import_player = require("@behindthemusictree/app-kit/player");
|
|
@@ -306,29 +308,28 @@ var import_transport = require("@behindthemusictree/app-kit/transport");
|
|
|
306
308
|
|
|
307
309
|
// src/genre-tree/playlist-tree/useGenrePlaylistPlayback.ts
|
|
308
310
|
var import_react2 = require("react");
|
|
309
|
-
var
|
|
311
|
+
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
310
312
|
function useGenrePlaylistPlayback({
|
|
311
313
|
scope,
|
|
312
314
|
genrePlaylists,
|
|
313
315
|
artist = null
|
|
314
316
|
}) {
|
|
315
|
-
const { trackList, playNewTrackListFromGenrePlaylist
|
|
317
|
+
const { trackList, playNewTrackListFromGenrePlaylist } = (0, import_genre_tree2.useTrackList)();
|
|
316
318
|
const origin = trackList?.origin;
|
|
317
319
|
const playingNodeId = (0, import_react2.useMemo)(() => {
|
|
318
|
-
if (!origin) return null;
|
|
319
|
-
|
|
320
|
-
if (
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
},
|
|
330
|
-
|
|
331
|
-
);
|
|
320
|
+
if (!origin || origin.type !== import_genre_tree2.TrackListOriginType.GENRE_PLAYLIST || origin.scope !== scope) return null;
|
|
321
|
+
const playlistOrigin = origin;
|
|
322
|
+
if (playlistOrigin.artistUuid !== artist?.uuid) return null;
|
|
323
|
+
const playlist = genrePlaylists.find((item) => item.uuid === origin.uuid) ?? playlistOrigin.criteriaPlaylist;
|
|
324
|
+
const expected = scope === "reference" && playlist && !("criteria" in playlist && playlist.criteria === null) ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
|
|
325
|
+
if (playlistOrigin.selection && expected && (0, import_genre_tree2.genreMusicSelectionKey)(playlistOrigin.selection).join() !== (0, import_genre_tree2.genreMusicSelectionKey)(expected).join()) return null;
|
|
326
|
+
return origin.uuid;
|
|
327
|
+
}, [origin, artist, genrePlaylists, scope]);
|
|
328
|
+
const play = (0, import_react2.useCallback)(async (playlist) => {
|
|
329
|
+
if (artist && !playlist.criteria) return;
|
|
330
|
+
const selection = scope === "reference" && playlist.criteria !== null ? (0, import_genre_tree2.defaultGenreMusicSelection)(import_genre_tree2.GenreCatalogueCountsSchema.parse(playlist)) : void 0;
|
|
331
|
+
return selection || artist ? playNewTrackListFromGenrePlaylist(playlist, scope, { selection, ...artist && { artistUuid: artist.uuid } }) : playNewTrackListFromGenrePlaylist(playlist, scope);
|
|
332
|
+
}, [artist, scope, playNewTrackListFromGenrePlaylist]);
|
|
332
333
|
return (0, import_react2.useMemo)(() => ({ playingNodeId, play }), [playingNodeId, play]);
|
|
333
334
|
}
|
|
334
335
|
|
|
@@ -363,7 +364,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
363
364
|
id: genrePlaylist.uuid,
|
|
364
365
|
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
365
366
|
name: genrePlaylist.name,
|
|
366
|
-
itemCount: genrePlaylist
|
|
367
|
+
itemCount: (0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist),
|
|
367
368
|
actionable: Boolean(genrePlaylist.criteria),
|
|
368
369
|
side: genrePlaylist.criteria?.side ?? void 0,
|
|
369
370
|
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
@@ -379,7 +380,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
379
380
|
setIsPlaying(!isPlaying);
|
|
380
381
|
return;
|
|
381
382
|
}
|
|
382
|
-
if (genrePlaylist
|
|
383
|
+
if ((0, import_genre_tree3.genrePlaylistTrackCount)(genrePlaylist) === 0) {
|
|
383
384
|
return;
|
|
384
385
|
}
|
|
385
386
|
play(genrePlaylist).catch((error) => {
|
|
@@ -443,6 +444,7 @@ function GenrePlaylistTreePerRoot({
|
|
|
443
444
|
}
|
|
444
445
|
|
|
445
446
|
// src/genre-tree/playlist-tree/TreeWheel.tsx
|
|
447
|
+
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
446
448
|
var import_react4 = require("react");
|
|
447
449
|
var import_genre_tree_view2 = require("@behindthemusictree/genre-tree-view");
|
|
448
450
|
var import_player2 = require("@behindthemusictree/app-kit/player");
|
|
@@ -495,7 +497,7 @@ function GenrePlaylistTreeWheel({
|
|
|
495
497
|
setIsPlaying(!isPlaying);
|
|
496
498
|
return;
|
|
497
499
|
}
|
|
498
|
-
if (genrePlaylist
|
|
500
|
+
if ((0, import_genre_tree4.genrePlaylistTrackCount)(genrePlaylist) === 0) {
|
|
499
501
|
return;
|
|
500
502
|
}
|
|
501
503
|
play(genrePlaylist).catch((error) => {
|
|
@@ -560,6 +562,7 @@ function GenrePlaylistTreeWheel({
|
|
|
560
562
|
}
|
|
561
563
|
|
|
562
564
|
// src/genre-tree/playlist-tree/TreeWheelRadialPopCore.tsx
|
|
565
|
+
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
563
566
|
var import_react5 = require("react");
|
|
564
567
|
var import_genre_tree_view3 = require("@behindthemusictree/genre-tree-view");
|
|
565
568
|
var import_player3 = require("@behindthemusictree/app-kit/player");
|
|
@@ -604,7 +607,7 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
604
607
|
setIsPlaying(!isPlaying);
|
|
605
608
|
return;
|
|
606
609
|
}
|
|
607
|
-
if (genrePlaylist
|
|
610
|
+
if ((0, import_genre_tree5.genrePlaylistTrackCount)(genrePlaylist) === 0) {
|
|
608
611
|
return;
|
|
609
612
|
}
|
|
610
613
|
play(genrePlaylist).catch((error) => {
|
|
@@ -699,10 +702,13 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
699
702
|
] });
|
|
700
703
|
}
|
|
701
704
|
|
|
705
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
706
|
+
var import_react8 = require("react");
|
|
707
|
+
|
|
702
708
|
// src/genre-tree/GenreSearch.tsx
|
|
703
709
|
var import_lucide_react3 = require("lucide-react");
|
|
704
710
|
var import_ui4 = require("@behindthemusictree/ui");
|
|
705
|
-
var
|
|
711
|
+
var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
|
|
706
712
|
|
|
707
713
|
// src/genre-tree/search/SearchBox.tsx
|
|
708
714
|
var import_react7 = require("react");
|
|
@@ -758,10 +764,10 @@ var SEARCH_FAILED_STATUS = "Search failed";
|
|
|
758
764
|
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
759
765
|
|
|
760
766
|
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
761
|
-
var
|
|
767
|
+
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
762
768
|
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
763
769
|
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
764
|
-
const search = (0,
|
|
770
|
+
const search = (0, import_genre_tree6.useArtistSearch)(getBaseUrl, enabled ? text : "");
|
|
765
771
|
if (!enabled) return void 0;
|
|
766
772
|
return {
|
|
767
773
|
key: "artists",
|
|
@@ -789,17 +795,17 @@ function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
|
789
795
|
}
|
|
790
796
|
|
|
791
797
|
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
792
|
-
var
|
|
798
|
+
var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
|
|
793
799
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
794
800
|
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
795
|
-
const search = (0,
|
|
801
|
+
const search = (0, import_genre_tree7.useSongSearch)(getBaseUrl, enabled ? text : "");
|
|
796
802
|
if (!enabled) return void 0;
|
|
797
803
|
return {
|
|
798
804
|
key: "songs",
|
|
799
805
|
label: "Songs",
|
|
800
806
|
items: search.songs.map((song) => ({
|
|
801
807
|
key: song.uuid,
|
|
802
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: (0,
|
|
808
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: (0, import_genre_tree7.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
803
809
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
804
810
|
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
805
811
|
] }),
|
|
@@ -811,10 +817,10 @@ function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
|
811
817
|
}
|
|
812
818
|
|
|
813
819
|
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
814
|
-
var
|
|
820
|
+
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
815
821
|
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
816
822
|
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
817
|
-
const search = (0,
|
|
823
|
+
const search = (0, import_genre_tree8.useGenreRemoteSearch)(getBaseUrl, text, options);
|
|
818
824
|
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
819
825
|
return {
|
|
820
826
|
key,
|
|
@@ -848,7 +854,7 @@ function GenreSearch({
|
|
|
848
854
|
extraGroups = [],
|
|
849
855
|
onFocus
|
|
850
856
|
}) {
|
|
851
|
-
const { query, setQuery, results, canClear, clear, select } = (0,
|
|
857
|
+
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree9.useGenreSearch)({
|
|
852
858
|
genrePlaylists,
|
|
853
859
|
onSelect,
|
|
854
860
|
selectedName,
|
|
@@ -899,7 +905,47 @@ function GenreSearch({
|
|
|
899
905
|
) });
|
|
900
906
|
}
|
|
901
907
|
|
|
908
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
909
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
910
|
+
function GenreTreeSearch({
|
|
911
|
+
artistBaseUrl,
|
|
912
|
+
songBaseUrl,
|
|
913
|
+
onArtistSelect,
|
|
914
|
+
onSongSelect,
|
|
915
|
+
artistSearch,
|
|
916
|
+
songSearch,
|
|
917
|
+
SearchGroups,
|
|
918
|
+
extraGroups = [],
|
|
919
|
+
onQueryChange,
|
|
920
|
+
onFocus,
|
|
921
|
+
...props
|
|
922
|
+
}) {
|
|
923
|
+
const [query, setQuery] = (0, import_react8.useState)("");
|
|
924
|
+
const [focused, setFocused] = (0, import_react8.useState)(false);
|
|
925
|
+
const deferredQuery = (0, import_react8.useDeferredValue)(query);
|
|
926
|
+
const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
|
|
927
|
+
const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
|
|
928
|
+
const render = (hostGroups) => /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
929
|
+
GenreSearch,
|
|
930
|
+
{
|
|
931
|
+
...props,
|
|
932
|
+
onQueryChange: (text) => {
|
|
933
|
+
setQuery(text);
|
|
934
|
+
onQueryChange?.(text);
|
|
935
|
+
},
|
|
936
|
+
onFocus: () => {
|
|
937
|
+
setFocused(true);
|
|
938
|
+
onFocus?.();
|
|
939
|
+
},
|
|
940
|
+
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
941
|
+
}
|
|
942
|
+
);
|
|
943
|
+
return SearchGroups ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
944
|
+
}
|
|
945
|
+
|
|
902
946
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
947
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
948
|
+
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
903
949
|
var import_player6 = require("@behindthemusictree/app-kit/player");
|
|
904
950
|
|
|
905
951
|
// src/genre-tree/GenreActionBand.tsx
|
|
@@ -910,25 +956,25 @@ var import_lucide_react6 = require("lucide-react");
|
|
|
910
956
|
var import_popup3 = require("@behindthemusictree/app-kit/popup");
|
|
911
957
|
|
|
912
958
|
// src/genre-tree/SharePopup.tsx
|
|
913
|
-
var
|
|
959
|
+
var import_react10 = require("react");
|
|
914
960
|
var import_lucide_react5 = require("lucide-react");
|
|
915
961
|
var import_fa = require("react-icons/fa");
|
|
916
962
|
var import_si = require("react-icons/si");
|
|
917
963
|
|
|
918
964
|
// src/popup/Toast.tsx
|
|
919
|
-
var
|
|
965
|
+
var import_react9 = require("react");
|
|
920
966
|
var import_react_dom2 = require("react-dom");
|
|
921
|
-
var
|
|
967
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
922
968
|
function Toast({ message, onClose, durationMs = 2200 }) {
|
|
923
|
-
const [mounted, setMounted] = (0,
|
|
924
|
-
(0,
|
|
969
|
+
const [mounted, setMounted] = (0, import_react9.useState)(false);
|
|
970
|
+
(0, import_react9.useEffect)(() => {
|
|
925
971
|
setMounted(true);
|
|
926
972
|
const timeout = window.setTimeout(onClose, durationMs);
|
|
927
973
|
return () => window.clearTimeout(timeout);
|
|
928
974
|
}, [durationMs, onClose]);
|
|
929
975
|
if (!mounted) return null;
|
|
930
976
|
return (0, import_react_dom2.createPortal)(
|
|
931
|
-
/* @__PURE__ */ (0,
|
|
977
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "pointer-events-none fixed inset-x-0 bottom-8 z-[60] flex justify-center px-4", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
932
978
|
"div",
|
|
933
979
|
{
|
|
934
980
|
role: "status",
|
|
@@ -943,12 +989,12 @@ function Toast({ message, onClose, durationMs = 2200 }) {
|
|
|
943
989
|
}
|
|
944
990
|
|
|
945
991
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
946
|
-
var
|
|
992
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
947
993
|
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";
|
|
948
994
|
function Contents({ icon, label }) {
|
|
949
|
-
return /* @__PURE__ */ (0,
|
|
950
|
-
/* @__PURE__ */ (0,
|
|
951
|
-
/* @__PURE__ */ (0,
|
|
995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("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 }),
|
|
997
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
952
998
|
] });
|
|
953
999
|
}
|
|
954
1000
|
function ShareDestinationButton({
|
|
@@ -960,29 +1006,29 @@ function ShareDestinationButton({
|
|
|
960
1006
|
ariaLabel
|
|
961
1007
|
}) {
|
|
962
1008
|
if (href) {
|
|
963
|
-
return /* @__PURE__ */ (0,
|
|
1009
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Contents, { icon, label }) });
|
|
964
1010
|
}
|
|
965
|
-
return /* @__PURE__ */ (0,
|
|
1011
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Contents, { icon, label }) });
|
|
966
1012
|
}
|
|
967
1013
|
|
|
968
1014
|
// src/genre-tree/SharePreviewCard.tsx
|
|
969
1015
|
var import_lucide_react4 = require("lucide-react");
|
|
970
|
-
var
|
|
1016
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
971
1017
|
function SharePreviewCard({ target, url }) {
|
|
972
1018
|
const Icon = { genre: import_lucide_react4.Music2, artist: import_lucide_react4.UserRound, song: import_lucide_react4.Music }[target.kind];
|
|
973
1019
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
974
|
-
return /* @__PURE__ */ (0,
|
|
975
|
-
/* @__PURE__ */ (0,
|
|
976
|
-
/* @__PURE__ */ (0,
|
|
977
|
-
/* @__PURE__ */ (0,
|
|
978
|
-
/* @__PURE__ */ (0,
|
|
979
|
-
/* @__PURE__ */ (0,
|
|
1020
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
1021
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
1022
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
1023
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
1024
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
980
1026
|
] })
|
|
981
1027
|
] }) });
|
|
982
1028
|
}
|
|
983
1029
|
|
|
984
1030
|
// src/genre-tree/SharePopup.tsx
|
|
985
|
-
var
|
|
1031
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
986
1032
|
function resolveAbsoluteUrl(url) {
|
|
987
1033
|
try {
|
|
988
1034
|
const parsed = new URL(url, window.location.href);
|
|
@@ -1015,20 +1061,20 @@ async function copyText(text) {
|
|
|
1015
1061
|
}
|
|
1016
1062
|
}
|
|
1017
1063
|
function SharePopup({ target, url, onClose }) {
|
|
1018
|
-
const [absoluteUrl, setAbsoluteUrl] = (0,
|
|
1019
|
-
const [copyStatus, setCopyStatus] = (0,
|
|
1020
|
-
const [canUseNativeShare, setCanUseNativeShare] = (0,
|
|
1021
|
-
const [shareError, setShareError] = (0,
|
|
1022
|
-
const [toastId, setToastId] = (0,
|
|
1023
|
-
(0,
|
|
1064
|
+
const [absoluteUrl, setAbsoluteUrl] = (0, import_react10.useState)(url);
|
|
1065
|
+
const [copyStatus, setCopyStatus] = (0, import_react10.useState)("idle");
|
|
1066
|
+
const [canUseNativeShare, setCanUseNativeShare] = (0, import_react10.useState)(false);
|
|
1067
|
+
const [shareError, setShareError] = (0, import_react10.useState)(null);
|
|
1068
|
+
const [toastId, setToastId] = (0, import_react10.useState)(0);
|
|
1069
|
+
(0, import_react10.useEffect)(() => {
|
|
1024
1070
|
setAbsoluteUrl(resolveAbsoluteUrl(url) ?? "");
|
|
1025
1071
|
setCopyStatus("idle");
|
|
1026
1072
|
setShareError(null);
|
|
1027
1073
|
}, [url]);
|
|
1028
|
-
(0,
|
|
1074
|
+
(0, import_react10.useEffect)(() => {
|
|
1029
1075
|
setCanUseNativeShare(typeof navigator !== "undefined" && typeof navigator.share === "function");
|
|
1030
1076
|
}, []);
|
|
1031
|
-
const destinationUrls = (0,
|
|
1077
|
+
const destinationUrls = (0, import_react10.useMemo)(() => {
|
|
1032
1078
|
if (!absoluteUrl) return null;
|
|
1033
1079
|
const message = `${target.name} \u2014 ${absoluteUrl}`;
|
|
1034
1080
|
return {
|
|
@@ -1046,7 +1092,7 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1046
1092
|
setCopyStatus(copied ? "idle" : "failed");
|
|
1047
1093
|
if (copied) setToastId((id) => id + 1);
|
|
1048
1094
|
};
|
|
1049
|
-
const dismissToast = (0,
|
|
1095
|
+
const dismissToast = (0, import_react10.useCallback)(() => setToastId(0), []);
|
|
1050
1096
|
const handleNativeShare = async () => {
|
|
1051
1097
|
if (!absoluteUrl || !navigator.share) return;
|
|
1052
1098
|
setShareError(null);
|
|
@@ -1057,8 +1103,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1057
1103
|
setShareError("Could not open the share options.");
|
|
1058
1104
|
}
|
|
1059
1105
|
};
|
|
1060
|
-
return /* @__PURE__ */ (0,
|
|
1061
|
-
/* @__PURE__ */ (0,
|
|
1106
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
|
|
1107
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1062
1108
|
BasePopup_default,
|
|
1063
1109
|
{
|
|
1064
1110
|
title: "Share",
|
|
@@ -1073,67 +1119,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1073
1119
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
1074
1120
|
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)]",
|
|
1075
1121
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1076
|
-
children: /* @__PURE__ */ (0,
|
|
1077
|
-
/* @__PURE__ */ (0,
|
|
1078
|
-
/* @__PURE__ */ (0,
|
|
1079
|
-
/* @__PURE__ */ (0,
|
|
1122
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1123
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1124
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1125
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1080
1126
|
ShareDestinationButton,
|
|
1081
1127
|
{
|
|
1082
1128
|
label: "Copy Link",
|
|
1083
|
-
icon: /* @__PURE__ */ (0,
|
|
1129
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react5.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1084
1130
|
onClick: () => void handleCopy(),
|
|
1085
1131
|
ariaLabel: "Copy link",
|
|
1086
1132
|
disabled: !absoluteUrl
|
|
1087
1133
|
}
|
|
1088
1134
|
),
|
|
1089
|
-
/* @__PURE__ */ (0,
|
|
1135
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1090
1136
|
ShareDestinationButton,
|
|
1091
1137
|
{
|
|
1092
1138
|
label: "WhatsApp",
|
|
1093
|
-
icon: /* @__PURE__ */ (0,
|
|
1139
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_fa.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1094
1140
|
href: destinationUrls?.whatsapp,
|
|
1095
1141
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1096
1142
|
disabled: !absoluteUrl
|
|
1097
1143
|
}
|
|
1098
1144
|
),
|
|
1099
|
-
/* @__PURE__ */ (0,
|
|
1145
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1100
1146
|
ShareDestinationButton,
|
|
1101
1147
|
{
|
|
1102
1148
|
label: "Gmail",
|
|
1103
|
-
icon: /* @__PURE__ */ (0,
|
|
1149
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1104
1150
|
href: destinationUrls?.gmail,
|
|
1105
1151
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1106
1152
|
disabled: !absoluteUrl
|
|
1107
1153
|
}
|
|
1108
1154
|
),
|
|
1109
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1155
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
1110
1156
|
ShareDestinationButton,
|
|
1111
1157
|
{
|
|
1112
1158
|
label: "More",
|
|
1113
|
-
icon: /* @__PURE__ */ (0,
|
|
1159
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react5.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1114
1160
|
onClick: () => void handleNativeShare(),
|
|
1115
1161
|
ariaLabel: "More sharing options",
|
|
1116
1162
|
disabled: !absoluteUrl
|
|
1117
1163
|
}
|
|
1118
1164
|
)
|
|
1119
1165
|
] }),
|
|
1120
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1121
|
-
shareError && /* @__PURE__ */ (0,
|
|
1166
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1167
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
1122
1168
|
] })
|
|
1123
1169
|
}
|
|
1124
1170
|
),
|
|
1125
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1171
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1126
1172
|
] });
|
|
1127
1173
|
}
|
|
1128
1174
|
|
|
1129
1175
|
// src/genre-tree/ShareActionButton.tsx
|
|
1130
|
-
var
|
|
1176
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1131
1177
|
function ShareActionButton({ target, url }) {
|
|
1132
1178
|
const { showPopup, hidePopup } = (0, import_popup3.usePopup)();
|
|
1133
1179
|
const openSharePopup = () => {
|
|
1134
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1180
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1135
1181
|
};
|
|
1136
|
-
return /* @__PURE__ */ (0,
|
|
1182
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
|
|
1137
1183
|
"button",
|
|
1138
1184
|
{
|
|
1139
1185
|
type: "button",
|
|
@@ -1142,7 +1188,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1142
1188
|
"aria-label": `Share ${target.name}`,
|
|
1143
1189
|
title: `Share ${target.name}`,
|
|
1144
1190
|
children: [
|
|
1145
|
-
/* @__PURE__ */ (0,
|
|
1191
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_lucide_react6.Share2, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1146
1192
|
"Share"
|
|
1147
1193
|
]
|
|
1148
1194
|
}
|
|
@@ -1150,7 +1196,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1150
1196
|
}
|
|
1151
1197
|
|
|
1152
1198
|
// src/genre-tree/GenreActionBand.tsx
|
|
1153
|
-
var
|
|
1199
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1154
1200
|
function GenreActionBand({
|
|
1155
1201
|
node,
|
|
1156
1202
|
onPlayPause,
|
|
@@ -1172,7 +1218,7 @@ function GenreActionBand({
|
|
|
1172
1218
|
items.push({
|
|
1173
1219
|
key: "play-pause",
|
|
1174
1220
|
label: isPlaying ? "Pause" : "Play",
|
|
1175
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
1221
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1176
1222
|
onClick: onPlayPause
|
|
1177
1223
|
});
|
|
1178
1224
|
}
|
|
@@ -1180,24 +1226,24 @@ function GenreActionBand({
|
|
|
1180
1226
|
items.push({
|
|
1181
1227
|
key: "zoom-in",
|
|
1182
1228
|
label: "Focus",
|
|
1183
|
-
icon: /* @__PURE__ */ (0,
|
|
1229
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1184
1230
|
onClick: onZoomIn
|
|
1185
1231
|
});
|
|
1186
1232
|
}
|
|
1187
1233
|
if (onAddChild) {
|
|
1188
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1234
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1189
1235
|
}
|
|
1190
1236
|
if (onRename) {
|
|
1191
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1237
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1192
1238
|
}
|
|
1193
1239
|
if (onChangeParent) {
|
|
1194
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1240
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1195
1241
|
}
|
|
1196
1242
|
if (onToggleSide) {
|
|
1197
1243
|
adminItems.push({
|
|
1198
1244
|
key: "toggle-side",
|
|
1199
1245
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1200
|
-
icon: /* @__PURE__ */ (0,
|
|
1246
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1201
1247
|
onClick: onToggleSide,
|
|
1202
1248
|
disabled: !!toggleSideDisabledReason,
|
|
1203
1249
|
title: toggleSideDisabledReason
|
|
@@ -1213,7 +1259,7 @@ function GenreActionBand({
|
|
|
1213
1259
|
});
|
|
1214
1260
|
}
|
|
1215
1261
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
1216
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1262
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1217
1263
|
"button",
|
|
1218
1264
|
{
|
|
1219
1265
|
type: "button",
|
|
@@ -1222,25 +1268,25 @@ function GenreActionBand({
|
|
|
1222
1268
|
onClick: item.onClick,
|
|
1223
1269
|
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",
|
|
1224
1270
|
children: [
|
|
1225
|
-
/* @__PURE__ */ (0,
|
|
1271
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
|
|
1226
1272
|
item.label
|
|
1227
1273
|
]
|
|
1228
1274
|
},
|
|
1229
1275
|
item.key
|
|
1230
1276
|
));
|
|
1231
|
-
return /* @__PURE__ */ (0,
|
|
1232
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0,
|
|
1277
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
1278
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1233
1279
|
renderItems(items),
|
|
1234
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1280
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1235
1281
|
] }),
|
|
1236
|
-
adminItems.length > 0 && /* @__PURE__ */ (0,
|
|
1282
|
+
adminItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
1237
1283
|
"section",
|
|
1238
1284
|
{
|
|
1239
1285
|
"aria-label": "Admin actions",
|
|
1240
1286
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
1241
1287
|
children: [
|
|
1242
|
-
/* @__PURE__ */ (0,
|
|
1243
|
-
/* @__PURE__ */ (0,
|
|
1288
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1289
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
1244
1290
|
]
|
|
1245
1291
|
}
|
|
1246
1292
|
)
|
|
@@ -1249,23 +1295,23 @@ function GenreActionBand({
|
|
|
1249
1295
|
|
|
1250
1296
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1251
1297
|
var import_ui6 = require("@behindthemusictree/ui");
|
|
1252
|
-
var
|
|
1298
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1253
1299
|
|
|
1254
1300
|
// src/genre-tree/ClampedText.tsx
|
|
1255
|
-
var
|
|
1256
|
-
var
|
|
1301
|
+
var import_react11 = require("react");
|
|
1302
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1257
1303
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1258
|
-
const ref = (0,
|
|
1259
|
-
const [expanded, setExpanded] = (0,
|
|
1260
|
-
const [overflows, setOverflows] = (0,
|
|
1261
|
-
(0,
|
|
1304
|
+
const ref = (0, import_react11.useRef)(null);
|
|
1305
|
+
const [expanded, setExpanded] = (0, import_react11.useState)(false);
|
|
1306
|
+
const [overflows, setOverflows] = (0, import_react11.useState)(false);
|
|
1307
|
+
(0, import_react11.useLayoutEffect)(() => {
|
|
1262
1308
|
const element = ref.current;
|
|
1263
1309
|
if (!element || expanded) return;
|
|
1264
1310
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1265
1311
|
}, [children, expanded]);
|
|
1266
|
-
return /* @__PURE__ */ (0,
|
|
1267
|
-
/* @__PURE__ */ (0,
|
|
1268
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1312
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
1313
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1314
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
1269
1315
|
"button",
|
|
1270
1316
|
{
|
|
1271
1317
|
type: "button",
|
|
@@ -1279,7 +1325,7 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1279
1325
|
}
|
|
1280
1326
|
|
|
1281
1327
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1282
|
-
var
|
|
1328
|
+
var import_react12 = require("react");
|
|
1283
1329
|
var import_react_query = require("@tanstack/react-query");
|
|
1284
1330
|
var import_zod = require("zod");
|
|
1285
1331
|
var import_fa2 = require("react-icons/fa");
|
|
@@ -1292,22 +1338,23 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
|
1292
1338
|
var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
1293
1339
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
1294
1340
|
var import_transport6 = require("@behindthemusictree/app-kit/transport");
|
|
1295
|
-
var
|
|
1296
|
-
var
|
|
1297
|
-
var
|
|
1298
|
-
var
|
|
1341
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1342
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1343
|
+
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1344
|
+
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1345
|
+
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1299
1346
|
|
|
1300
1347
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1301
|
-
var
|
|
1302
|
-
var
|
|
1348
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1349
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1303
1350
|
function MusicBrainzRecordingLink({
|
|
1304
1351
|
song,
|
|
1305
1352
|
children,
|
|
1306
1353
|
className: className2 = ""
|
|
1307
1354
|
}) {
|
|
1308
|
-
const url = (0,
|
|
1309
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1310
|
-
return /* @__PURE__ */ (0,
|
|
1355
|
+
const url = (0, import_genre_tree10.musicBrainzRecordingUrl)(song);
|
|
1356
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_jsx_runtime22.Fragment, { children });
|
|
1357
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
1311
1358
|
"a",
|
|
1312
1359
|
{
|
|
1313
1360
|
href: url,
|
|
@@ -1322,11 +1369,11 @@ function MusicBrainzRecordingLink({
|
|
|
1322
1369
|
}
|
|
1323
1370
|
|
|
1324
1371
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1325
|
-
var
|
|
1372
|
+
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1326
1373
|
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
1327
|
-
var
|
|
1374
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1328
1375
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1329
|
-
var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(
|
|
1376
|
+
var SongPageSchema = (0, import_transport4.PaginatedResponseSchema)(import_genre_tree14.SongDetailedSchema).extend({
|
|
1330
1377
|
overallTotal: import_zod.z.number().nullable(),
|
|
1331
1378
|
page: import_zod.z.number().nullable(),
|
|
1332
1379
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1344,6 +1391,7 @@ var nextCursor = (next) => {
|
|
|
1344
1391
|
function GenrePlaylistTracks({
|
|
1345
1392
|
genreUuid,
|
|
1346
1393
|
genrePlaylist,
|
|
1394
|
+
selection,
|
|
1347
1395
|
scope,
|
|
1348
1396
|
getBackendBaseUrl,
|
|
1349
1397
|
renderArtistLink,
|
|
@@ -1353,7 +1401,7 @@ function GenrePlaylistTracks({
|
|
|
1353
1401
|
renderTracksToolbar,
|
|
1354
1402
|
artist = null
|
|
1355
1403
|
}) {
|
|
1356
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0,
|
|
1404
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist, playNewTrackListFromGenreCatalogue } = (0, import_genre_tree15.useTrackList)();
|
|
1357
1405
|
const { session, sessionRestored } = (0, import_auth.useSession)();
|
|
1358
1406
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1359
1407
|
const { showPopup } = (0, import_popup4.usePopup)();
|
|
@@ -1362,15 +1410,15 @@ function GenrePlaylistTracks({
|
|
|
1362
1410
|
const { fetch } = (0, import_transport3.useFetchWrapper)(getBackendBaseUrl);
|
|
1363
1411
|
const playback = (0, import_transport3.usePlaybackContext)();
|
|
1364
1412
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1365
|
-
queryKey: [...scope === "me" ?
|
|
1413
|
+
queryKey: [...scope === "me" ? import_genre_tree12.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), "artist", artist.uuid] : import_genre_tree13.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid, selection), ...playback.key ? [playback.key] : []],
|
|
1366
1414
|
queryFn: async ({ pageParam }) => {
|
|
1367
1415
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1368
1416
|
const response = await fetch(
|
|
1369
|
-
|
|
1417
|
+
import_genre_tree13.libraryEndpoints.reference.songs.list(),
|
|
1370
1418
|
true,
|
|
1371
1419
|
false,
|
|
1372
1420
|
{},
|
|
1373
|
-
{ genre: genreUuid, ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1421
|
+
{ genre: genreUuid, ...(0, import_genre_tree11.genreMusicQueryParams)(selection), ...artist && { artist: artist.uuid }, pageSize: CATALOGUE_PAGE_SIZE, withTotal: false, ...pageParam && { cursor: pageParam } }
|
|
1374
1422
|
);
|
|
1375
1423
|
const parsed = (0, import_transport5.parseWithLog)(SongPageSchema, response, "GenrePlaylistTracks");
|
|
1376
1424
|
return { tracks: parsed.results, total: parsed.overallTotal, nextPage: parsed.next ? nextCursor(parsed.next) : null };
|
|
@@ -1379,8 +1427,8 @@ function GenrePlaylistTracks({
|
|
|
1379
1427
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1380
1428
|
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1381
1429
|
});
|
|
1382
|
-
const [sentinel, setSentinel] = (0,
|
|
1383
|
-
(0,
|
|
1430
|
+
const [sentinel, setSentinel] = (0, import_react12.useState)(null);
|
|
1431
|
+
(0, import_react12.useEffect)(() => {
|
|
1384
1432
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1385
1433
|
const observer = new IntersectionObserver((entries) => {
|
|
1386
1434
|
if (entries.some((entry) => entry.isIntersecting)) {
|
|
@@ -1394,38 +1442,43 @@ function GenrePlaylistTracks({
|
|
|
1394
1442
|
const tracks = pages.flatMap((page) => page.tracks);
|
|
1395
1443
|
const total = pages[0]?.total ?? void 0;
|
|
1396
1444
|
const play = (startSongUuid) => {
|
|
1397
|
-
const started = artist ? playNewTrackListFromArtist(artist, scope, {
|
|
1445
|
+
const started = artist ? playNewTrackListFromArtist(artist, scope, {
|
|
1446
|
+
startSongUuid,
|
|
1447
|
+
ordering: "title",
|
|
1448
|
+
genreUuid,
|
|
1449
|
+
...selection && { selection }
|
|
1450
|
+
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genrePlaylist.name }, scope, { startSongUuid, ...selection && { selection } }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1398
1451
|
started.catch((error) => {
|
|
1399
1452
|
console.error("Failed to play genre playlist:", error);
|
|
1400
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1453
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(InternalErrorPopup, { errorCode: import_transport6.ErrorCode.CLIENT_UNKNOWN }));
|
|
1401
1454
|
});
|
|
1402
1455
|
};
|
|
1403
|
-
return /* @__PURE__ */ (0,
|
|
1456
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui5.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { children: [
|
|
1404
1457
|
renderTracksToolbar?.(),
|
|
1405
|
-
isPending ? /* @__PURE__ */ (0,
|
|
1458
|
+
isPending ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui5.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("p", { role: "alert", children: [
|
|
1406
1459
|
"Couldn't load ",
|
|
1407
1460
|
trackNoun.plural,
|
|
1408
1461
|
".",
|
|
1409
1462
|
" ",
|
|
1410
|
-
/* @__PURE__ */ (0,
|
|
1411
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1463
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1464
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("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: [
|
|
1412
1465
|
tracks.map((track, index) => {
|
|
1413
1466
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1414
1467
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1415
1468
|
const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
|
|
1416
|
-
const text = /* @__PURE__ */ (0,
|
|
1417
|
-
/* @__PURE__ */ (0,
|
|
1418
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1419
|
-
/* @__PURE__ */ (0,
|
|
1420
|
-
track.album && /* @__PURE__ */ (0,
|
|
1469
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1470
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree14.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1471
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1472
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1473
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1421
1474
|
] });
|
|
1422
1475
|
const leading = renderSongLeading?.(track, index);
|
|
1423
1476
|
const actions = renderSongActions?.(track);
|
|
1424
|
-
const isUnplayable = (0,
|
|
1477
|
+
const isUnplayable = (0, import_genre_tree14.isSong)(track) && (0, import_genre_tree14.songUnplayableReason)(track) !== null;
|
|
1425
1478
|
if (isUnplayable) {
|
|
1426
|
-
return /* @__PURE__ */ (0,
|
|
1479
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1427
1480
|
leading,
|
|
1428
|
-
/* @__PURE__ */ (0,
|
|
1481
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1429
1482
|
"button",
|
|
1430
1483
|
{
|
|
1431
1484
|
type: "button",
|
|
@@ -1433,27 +1486,27 @@ function GenrePlaylistTracks({
|
|
|
1433
1486
|
"aria-disabled": "true",
|
|
1434
1487
|
"aria-label": label,
|
|
1435
1488
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1436
|
-
children: /* @__PURE__ */ (0,
|
|
1489
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1437
1490
|
}
|
|
1438
1491
|
),
|
|
1439
1492
|
text,
|
|
1440
1493
|
actions
|
|
1441
1494
|
] }, track.uuid);
|
|
1442
1495
|
}
|
|
1443
|
-
return /* @__PURE__ */ (0,
|
|
1496
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
|
|
1444
1497
|
"li",
|
|
1445
1498
|
{
|
|
1446
1499
|
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)]" : ""}`,
|
|
1447
1500
|
children: [
|
|
1448
1501
|
leading,
|
|
1449
|
-
/* @__PURE__ */ (0,
|
|
1502
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1450
1503
|
"button",
|
|
1451
1504
|
{
|
|
1452
1505
|
type: "button",
|
|
1453
1506
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1454
1507
|
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 !== import_player5.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1455
1508
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1456
|
-
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1509
|
+
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_ui5.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1457
1510
|
}
|
|
1458
1511
|
),
|
|
1459
1512
|
text,
|
|
@@ -1463,7 +1516,7 @@ function GenrePlaylistTracks({
|
|
|
1463
1516
|
track.uuid
|
|
1464
1517
|
);
|
|
1465
1518
|
}),
|
|
1466
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1519
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1467
1520
|
] })
|
|
1468
1521
|
] }) });
|
|
1469
1522
|
}
|
|
@@ -1471,10 +1524,11 @@ function GenrePlaylistTracks({
|
|
|
1471
1524
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1472
1525
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1473
1526
|
var import_si2 = require("react-icons/si");
|
|
1474
|
-
var
|
|
1527
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1475
1528
|
function GenreDetailExtras({
|
|
1476
1529
|
genreUuid,
|
|
1477
1530
|
genrePlaylist,
|
|
1531
|
+
selection,
|
|
1478
1532
|
wikidataId,
|
|
1479
1533
|
scope,
|
|
1480
1534
|
getBackendBaseUrl,
|
|
@@ -1488,24 +1542,24 @@ function GenreDetailExtras({
|
|
|
1488
1542
|
...trackSlots
|
|
1489
1543
|
}) {
|
|
1490
1544
|
const trackNoun = (0, import_track_noun2.useTrackNoun)();
|
|
1491
|
-
const { data: overview, isPending } = (0,
|
|
1545
|
+
const { data: overview, isPending } = (0, import_genre_tree17.useFetchGenreOverview)(
|
|
1492
1546
|
genreUuid,
|
|
1493
1547
|
scope,
|
|
1494
1548
|
getBackendBaseUrl,
|
|
1495
1549
|
criteriaOverviewSchema
|
|
1496
1550
|
);
|
|
1497
1551
|
if (isPending) {
|
|
1498
|
-
return /* @__PURE__ */ (0,
|
|
1499
|
-
/* @__PURE__ */ (0,
|
|
1552
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
1553
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui6.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui6.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1500
1554
|
family
|
|
1501
1555
|
] });
|
|
1502
1556
|
}
|
|
1503
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1557
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_jsx_runtime24.Fragment, { children: family });
|
|
1504
1558
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1505
1559
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1506
|
-
return /* @__PURE__ */ (0,
|
|
1507
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1508
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
1560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_jsx_runtime24.Fragment, { children: [
|
|
1561
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui6.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1562
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
1509
1563
|
"a",
|
|
1510
1564
|
{
|
|
1511
1565
|
href: summarySourceUrl,
|
|
@@ -1513,12 +1567,12 @@ function GenreDetailExtras({
|
|
|
1513
1567
|
rel: "noopener noreferrer",
|
|
1514
1568
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1515
1569
|
children: [
|
|
1516
|
-
/* @__PURE__ */ (0,
|
|
1570
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1517
1571
|
"Wikipedia"
|
|
1518
1572
|
]
|
|
1519
1573
|
}
|
|
1520
1574
|
),
|
|
1521
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1575
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
1522
1576
|
"a",
|
|
1523
1577
|
{
|
|
1524
1578
|
href: wikidataUrl,
|
|
@@ -1526,21 +1580,22 @@ function GenreDetailExtras({
|
|
|
1526
1580
|
rel: "noopener noreferrer",
|
|
1527
1581
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1528
1582
|
children: [
|
|
1529
|
-
/* @__PURE__ */ (0,
|
|
1583
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1530
1584
|
"Wikidata"
|
|
1531
1585
|
]
|
|
1532
1586
|
}
|
|
1533
1587
|
)
|
|
1534
1588
|
] }) }),
|
|
1535
|
-
/* @__PURE__ */ (0,
|
|
1589
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui6.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1536
1590
|
family,
|
|
1537
1591
|
renderGenreDetailExtras?.(overview),
|
|
1538
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1539
|
-
/* @__PURE__ */ (0,
|
|
1592
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_ui6.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime24.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1593
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
|
|
1540
1594
|
GenrePlaylistTracks,
|
|
1541
1595
|
{
|
|
1542
1596
|
genreUuid,
|
|
1543
1597
|
genrePlaylist,
|
|
1598
|
+
selection,
|
|
1544
1599
|
scope,
|
|
1545
1600
|
getBackendBaseUrl,
|
|
1546
1601
|
renderArtistLink,
|
|
@@ -1552,8 +1607,8 @@ function GenreDetailExtras({
|
|
|
1552
1607
|
}
|
|
1553
1608
|
|
|
1554
1609
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1555
|
-
var
|
|
1556
|
-
var
|
|
1610
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1611
|
+
var import_react13 = require("react");
|
|
1557
1612
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1558
1613
|
const { isPlaying, setIsPlaying } = (0, import_player6.usePlayer)();
|
|
1559
1614
|
const isCurrentTrackList = playback.playingNodeId === genrePlaylist.uuid;
|
|
@@ -1566,11 +1621,11 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1566
1621
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1567
1622
|
});
|
|
1568
1623
|
};
|
|
1569
|
-
return /* @__PURE__ */ (0,
|
|
1624
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
|
|
1570
1625
|
GenreActionBand,
|
|
1571
1626
|
{
|
|
1572
1627
|
...bandProps,
|
|
1573
|
-
onPlayPause: genrePlaylist.tracksCount > 0 ? handlePlayPause : void 0,
|
|
1628
|
+
onPlayPause: ("genreOnlyTracksCount" in genrePlaylist ? (0, import_genre_tree18.genreMusicCount)(genrePlaylist) : genrePlaylist.tracksCount) > 0 ? handlePlayPause : void 0,
|
|
1574
1629
|
isPlaying: isCurrentTrackList && isPlaying
|
|
1575
1630
|
}
|
|
1576
1631
|
);
|
|
@@ -1580,6 +1635,7 @@ function GenreInfoPanelBody({
|
|
|
1580
1635
|
family,
|
|
1581
1636
|
genreUuid,
|
|
1582
1637
|
genrePlaylist,
|
|
1638
|
+
catalogue,
|
|
1583
1639
|
wikidataId,
|
|
1584
1640
|
scope,
|
|
1585
1641
|
getBackendBaseUrl,
|
|
@@ -1587,10 +1643,31 @@ function GenreInfoPanelBody({
|
|
|
1587
1643
|
band,
|
|
1588
1644
|
extras
|
|
1589
1645
|
}) {
|
|
1590
|
-
|
|
1646
|
+
const noun = (0, import_track_noun3.useTrackNoun)();
|
|
1647
|
+
const summary = scope === "reference" ? import_genre_tree18.GenreCatalogueCountsSchema.parse(genrePlaylist) : void 0;
|
|
1648
|
+
const selection = summary ? (0, import_genre_tree18.defaultGenreMusicSelection)(summary) : void 0;
|
|
1649
|
+
const describeCounts = (counts) => {
|
|
1650
|
+
const scopes = counts.genreAndSubgenresTracksCount;
|
|
1651
|
+
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`;
|
|
1652
|
+
};
|
|
1653
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_jsx_runtime25.Fragment, { children: [
|
|
1591
1654
|
toolbar,
|
|
1592
|
-
/* @__PURE__ */ (0,
|
|
1593
|
-
/* @__PURE__ */ (0,
|
|
1655
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1656
|
+
summary && catalogue && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1657
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { children: [
|
|
1658
|
+
"Catalogue ",
|
|
1659
|
+
noun.plural,
|
|
1660
|
+
": ",
|
|
1661
|
+
describeCounts(catalogue)
|
|
1662
|
+
] }),
|
|
1663
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("p", { children: [
|
|
1664
|
+
"Playlist ",
|
|
1665
|
+
noun.plural,
|
|
1666
|
+
": ",
|
|
1667
|
+
describeCounts(summary)
|
|
1668
|
+
] })
|
|
1669
|
+
] }),
|
|
1670
|
+
/* @__PURE__ */ (0, import_react13.createElement)(
|
|
1594
1671
|
GenreDetailExtras,
|
|
1595
1672
|
{
|
|
1596
1673
|
...extras,
|
|
@@ -1598,6 +1675,7 @@ function GenreInfoPanelBody({
|
|
|
1598
1675
|
family,
|
|
1599
1676
|
genreUuid,
|
|
1600
1677
|
genrePlaylist,
|
|
1678
|
+
selection,
|
|
1601
1679
|
wikidataId,
|
|
1602
1680
|
scope,
|
|
1603
1681
|
getBackendBaseUrl
|
|
@@ -1607,9 +1685,9 @@ function GenreInfoPanelBody({
|
|
|
1607
1685
|
}
|
|
1608
1686
|
|
|
1609
1687
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1610
|
-
var
|
|
1611
|
-
var
|
|
1612
|
-
var
|
|
1688
|
+
var import_react14 = require("react");
|
|
1689
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1690
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1613
1691
|
function ChangeParentPopup({
|
|
1614
1692
|
genrePlaylist,
|
|
1615
1693
|
genrePlaylists,
|
|
@@ -1618,9 +1696,9 @@ function ChangeParentPopup({
|
|
|
1618
1696
|
onClose,
|
|
1619
1697
|
onParentChanged
|
|
1620
1698
|
}) {
|
|
1621
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1622
|
-
const candidates = (0,
|
|
1623
|
-
() => (0,
|
|
1699
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree19.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1700
|
+
const candidates = (0, import_react14.useMemo)(
|
|
1701
|
+
() => (0, import_genre_tree19.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1624
1702
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1625
1703
|
);
|
|
1626
1704
|
const handleSelect = (newParent) => {
|
|
@@ -1637,19 +1715,19 @@ function ChangeParentPopup({
|
|
|
1637
1715
|
}
|
|
1638
1716
|
);
|
|
1639
1717
|
};
|
|
1640
|
-
(0,
|
|
1718
|
+
(0, import_react14.useEffect)(() => {
|
|
1641
1719
|
const handleKeyDown = (event) => {
|
|
1642
1720
|
if (event.key === "Escape") onClose();
|
|
1643
1721
|
};
|
|
1644
1722
|
window.addEventListener("keydown", handleKeyDown);
|
|
1645
1723
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1646
1724
|
}, [onClose]);
|
|
1647
|
-
return /* @__PURE__ */ (0,
|
|
1648
|
-
/* @__PURE__ */ (0,
|
|
1725
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1649
1727
|
"Current parent: ",
|
|
1650
1728
|
genrePlaylist.parent?.name ?? "none"
|
|
1651
1729
|
] }),
|
|
1652
|
-
/* @__PURE__ */ (0,
|
|
1730
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1653
1731
|
GenreSearch,
|
|
1654
1732
|
{
|
|
1655
1733
|
genrePlaylists: candidates,
|
|
@@ -1657,13 +1735,13 @@ function ChangeParentPopup({
|
|
|
1657
1735
|
placeholder: "Search a new parent\u2026"
|
|
1658
1736
|
}
|
|
1659
1737
|
) }),
|
|
1660
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1738
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1661
1739
|
] });
|
|
1662
1740
|
}
|
|
1663
1741
|
|
|
1664
1742
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1665
1743
|
var import_ui12 = require("@behindthemusictree/ui");
|
|
1666
|
-
var
|
|
1744
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1667
1745
|
|
|
1668
1746
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1669
1747
|
var import_ui8 = require("@behindthemusictree/ui");
|
|
@@ -1672,7 +1750,7 @@ var import_ui8 = require("@behindthemusictree/ui");
|
|
|
1672
1750
|
var import_lucide_react8 = require("lucide-react");
|
|
1673
1751
|
var import_si3 = require("react-icons/si");
|
|
1674
1752
|
var import_ui7 = require("@behindthemusictree/ui");
|
|
1675
|
-
var
|
|
1753
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1676
1754
|
var LINK_KINDS = {
|
|
1677
1755
|
official: { label: "Official website", Icon: import_lucide_react8.Globe },
|
|
1678
1756
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -1709,7 +1787,7 @@ function artistLinks(artist) {
|
|
|
1709
1787
|
return links;
|
|
1710
1788
|
}
|
|
1711
1789
|
function ArtistLinks({ links }) {
|
|
1712
|
-
return /* @__PURE__ */ (0,
|
|
1790
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime27.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_ui7.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1713
1791
|
}
|
|
1714
1792
|
|
|
1715
1793
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1720,7 +1798,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1720
1798
|
}
|
|
1721
1799
|
|
|
1722
1800
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1723
|
-
var
|
|
1801
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1724
1802
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1725
1803
|
function ArtistHeader({ artist }) {
|
|
1726
1804
|
const fields = [];
|
|
@@ -1730,35 +1808,35 @@ function ArtistHeader({ artist }) {
|
|
|
1730
1808
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1731
1809
|
if (years) fields.push({ label: "Active", value: years });
|
|
1732
1810
|
const links = artistLinks(artist);
|
|
1733
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
1811
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ArtistLinks, { links }) });
|
|
1734
1812
|
if (fields.length === 0) return null;
|
|
1735
|
-
return /* @__PURE__ */ (0,
|
|
1813
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.FieldList, { items: fields });
|
|
1736
1814
|
}
|
|
1737
1815
|
|
|
1738
1816
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1739
|
-
var
|
|
1817
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1740
1818
|
function ExternalLink({ href, children }) {
|
|
1741
|
-
return /* @__PURE__ */ (0,
|
|
1819
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1742
1820
|
}
|
|
1743
1821
|
function ArtistPhoto({ image, name }) {
|
|
1744
|
-
return /* @__PURE__ */ (0,
|
|
1745
|
-
/* @__PURE__ */ (0,
|
|
1746
|
-
/* @__PURE__ */ (0,
|
|
1822
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1824
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1747
1825
|
"Photo: ",
|
|
1748
|
-
/* @__PURE__ */ (0,
|
|
1826
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1749
1827
|
",",
|
|
1750
1828
|
" ",
|
|
1751
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
1829
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1752
1830
|
] })
|
|
1753
1831
|
] });
|
|
1754
1832
|
}
|
|
1755
1833
|
|
|
1756
1834
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1757
1835
|
var import_ui9 = require("@behindthemusictree/ui");
|
|
1758
|
-
var
|
|
1836
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1759
1837
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1760
1838
|
if (memberships.length === 0) return null;
|
|
1761
|
-
return /* @__PURE__ */ (0,
|
|
1839
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_ui9.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
1762
1840
|
const details = [
|
|
1763
1841
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1764
1842
|
...membership.roles
|
|
@@ -1766,9 +1844,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1766
1844
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1767
1845
|
return (
|
|
1768
1846
|
// A member can have several periods in the same group.
|
|
1769
|
-
/* @__PURE__ */ (0,
|
|
1770
|
-
/* @__PURE__ */ (0,
|
|
1771
|
-
detailsText && /* @__PURE__ */ (0,
|
|
1847
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1848
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1849
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1772
1850
|
" \u2014 ",
|
|
1773
1851
|
detailsText
|
|
1774
1852
|
] })
|
|
@@ -1779,9 +1857,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1779
1857
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1780
1858
|
if (memberships.length === 0) return null;
|
|
1781
1859
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1782
|
-
return /* @__PURE__ */ (0,
|
|
1783
|
-
/* @__PURE__ */ (0,
|
|
1784
|
-
/* @__PURE__ */ (0,
|
|
1860
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_ui9.PanelSection, { title, children: [
|
|
1861
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1862
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1785
1863
|
MembershipList,
|
|
1786
1864
|
{
|
|
1787
1865
|
title: "Past",
|
|
@@ -1794,7 +1872,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1794
1872
|
|
|
1795
1873
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1796
1874
|
var import_ui10 = require("@behindthemusictree/ui");
|
|
1797
|
-
var
|
|
1875
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1798
1876
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1799
1877
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1800
1878
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1814,34 +1892,34 @@ function byYear(a, b) {
|
|
|
1814
1892
|
function ArtistDiscography({ albums }) {
|
|
1815
1893
|
if (albums.length === 0) return null;
|
|
1816
1894
|
const sorted = [...albums].sort(byYear);
|
|
1817
|
-
return /* @__PURE__ */ (0,
|
|
1895
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
1818
1896
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1819
1897
|
if (groupAlbums.length === 0) return null;
|
|
1820
|
-
return /* @__PURE__ */ (0,
|
|
1821
|
-
/* @__PURE__ */ (0,
|
|
1822
|
-
/* @__PURE__ */ (0,
|
|
1898
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1899
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1900
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1823
1901
|
] }, album.uuid)) }) }, group);
|
|
1824
1902
|
}) });
|
|
1825
1903
|
}
|
|
1826
1904
|
|
|
1827
1905
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1828
|
-
var
|
|
1906
|
+
var import_react15 = require("react");
|
|
1829
1907
|
var import_react_query2 = require("@tanstack/react-query");
|
|
1830
1908
|
var import_lucide_react9 = require("lucide-react");
|
|
1831
1909
|
var import_ui11 = require("@behindthemusictree/ui");
|
|
1832
1910
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
1833
|
-
var
|
|
1911
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1834
1912
|
var import_transport7 = require("@behindthemusictree/app-kit/transport");
|
|
1835
1913
|
|
|
1836
1914
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1837
1915
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
1838
|
-
var
|
|
1916
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1839
1917
|
|
|
1840
1918
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
1841
1919
|
var import_fa3 = require("react-icons/fa");
|
|
1842
1920
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1843
1921
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
1844
|
-
var
|
|
1922
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1845
1923
|
function TrackPositionPlayPause({
|
|
1846
1924
|
position,
|
|
1847
1925
|
uuid,
|
|
@@ -1855,14 +1933,14 @@ function TrackPositionPlayPause({
|
|
|
1855
1933
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
1856
1934
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
1857
1935
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1858
|
-
return /* @__PURE__ */ (0,
|
|
1936
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
1859
1937
|
"div",
|
|
1860
1938
|
{
|
|
1861
1939
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1862
1940
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1863
1941
|
onClick: handlePlayPauseClick,
|
|
1864
1942
|
children: [
|
|
1865
|
-
/* @__PURE__ */ (0,
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
|
|
1866
1944
|
"span",
|
|
1867
1945
|
{
|
|
1868
1946
|
className: "track-position-play-pause__bars",
|
|
@@ -1870,18 +1948,18 @@ function TrackPositionPlayPause({
|
|
|
1870
1948
|
role: "img",
|
|
1871
1949
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1872
1950
|
children: [
|
|
1873
|
-
/* @__PURE__ */ (0,
|
|
1874
|
-
/* @__PURE__ */ (0,
|
|
1875
|
-
/* @__PURE__ */ (0,
|
|
1951
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
1876
1954
|
]
|
|
1877
1955
|
}
|
|
1878
1956
|
) : position }),
|
|
1879
|
-
/* @__PURE__ */ (0,
|
|
1957
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1880
1958
|
"div",
|
|
1881
1959
|
{
|
|
1882
1960
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1883
1961
|
"aria-hidden": "true",
|
|
1884
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
1962
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_fa3.FaPlay, {})
|
|
1885
1963
|
}
|
|
1886
1964
|
)
|
|
1887
1965
|
]
|
|
@@ -1890,9 +1968,9 @@ function TrackPositionPlayPause({
|
|
|
1890
1968
|
}
|
|
1891
1969
|
|
|
1892
1970
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1893
|
-
var
|
|
1894
|
-
var
|
|
1895
|
-
var
|
|
1971
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1972
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1973
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1896
1974
|
function TrackItem({
|
|
1897
1975
|
track,
|
|
1898
1976
|
position,
|
|
@@ -1902,11 +1980,11 @@ function TrackItem({
|
|
|
1902
1980
|
renderSongLink
|
|
1903
1981
|
}) {
|
|
1904
1982
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
1905
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
1983
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree20.useTrackList)();
|
|
1906
1984
|
const scope = trackList?.origin?.scope ?? null;
|
|
1907
1985
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
1908
1986
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
1909
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
1987
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree21.isSong)(track) && (0, import_genre_tree21.songUnplayableReason)(track) !== null;
|
|
1910
1988
|
const handlePlayPauseClick = (event) => {
|
|
1911
1989
|
event.stopPropagation();
|
|
1912
1990
|
if (isCurrentTrack) {
|
|
@@ -1916,12 +1994,12 @@ function TrackItem({
|
|
|
1916
1994
|
loadTrackForPlayer(track.uuid);
|
|
1917
1995
|
}
|
|
1918
1996
|
};
|
|
1919
|
-
return /* @__PURE__ */ (0,
|
|
1997
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
|
|
1920
1998
|
"div",
|
|
1921
1999
|
{
|
|
1922
2000
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1923
2001
|
children: [
|
|
1924
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2002
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1925
2003
|
"div",
|
|
1926
2004
|
{
|
|
1927
2005
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1929,7 +2007,7 @@ function TrackItem({
|
|
|
1929
2007
|
"aria-disabled": "true",
|
|
1930
2008
|
children: position
|
|
1931
2009
|
}
|
|
1932
|
-
) : /* @__PURE__ */ (0,
|
|
2010
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1933
2011
|
TrackPositionPlayPause,
|
|
1934
2012
|
{
|
|
1935
2013
|
position,
|
|
@@ -1938,12 +2016,12 @@ function TrackItem({
|
|
|
1938
2016
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1939
2017
|
}
|
|
1940
2018
|
),
|
|
1941
|
-
/* @__PURE__ */ (0,
|
|
1942
|
-
/* @__PURE__ */ (0,
|
|
1943
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2019
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2020
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree21.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2021
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_genre_tree22.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1944
2022
|
] }),
|
|
1945
|
-
/* @__PURE__ */ (0,
|
|
1946
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2023
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2024
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1947
2025
|
"div",
|
|
1948
2026
|
{
|
|
1949
2027
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1951,28 +2029,28 @@ function TrackItem({
|
|
|
1951
2029
|
children: renderDuration(track)
|
|
1952
2030
|
}
|
|
1953
2031
|
),
|
|
1954
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2032
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1955
2033
|
]
|
|
1956
2034
|
}
|
|
1957
2035
|
);
|
|
1958
2036
|
}
|
|
1959
2037
|
|
|
1960
2038
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1961
|
-
var
|
|
1962
|
-
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(
|
|
2039
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
2040
|
+
var SongPageSchema2 = (0, import_transport7.PaginatedResponseSchema)(import_genre_tree23.SongDetailedSchema);
|
|
1963
2041
|
var SORTS = [
|
|
1964
2042
|
{ ordering: "title", label: "Title" },
|
|
1965
2043
|
{ ordering: "-albumYear", label: "Year" }
|
|
1966
2044
|
];
|
|
1967
2045
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
1968
|
-
const [page, setPage] = (0,
|
|
1969
|
-
const [ordering, setOrdering] = (0,
|
|
2046
|
+
const [page, setPage] = (0, import_react15.useState)(1);
|
|
2047
|
+
const [ordering, setOrdering] = (0, import_react15.useState)("title");
|
|
1970
2048
|
const { fetch } = (0, import_transport7.useFetchWrapper)(getBackendBaseUrl);
|
|
1971
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2049
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree23.useTrackList)();
|
|
1972
2050
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
1973
2051
|
const { data, isPending, isError } = (0, import_transport7.useQueryWithParse)({
|
|
1974
2052
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
1975
|
-
queryFn: () => fetch(
|
|
2053
|
+
queryFn: () => fetch(import_genre_tree23.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
1976
2054
|
artist: artist.uuid,
|
|
1977
2055
|
ordering,
|
|
1978
2056
|
page
|
|
@@ -1987,25 +2065,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1987
2065
|
});
|
|
1988
2066
|
const handleRowClickCapture = (event, song) => {
|
|
1989
2067
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
1990
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2068
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree23.songUnplayableReason)(song) !== null) return;
|
|
1991
2069
|
event.stopPropagation();
|
|
1992
2070
|
void play(song.uuid);
|
|
1993
2071
|
};
|
|
1994
|
-
return /* @__PURE__ */ (0,
|
|
1995
|
-
/* @__PURE__ */ (0,
|
|
1996
|
-
/* @__PURE__ */ (0,
|
|
2072
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2073
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2074
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
|
|
1997
2075
|
"button",
|
|
1998
2076
|
{
|
|
1999
2077
|
type: "button",
|
|
2000
2078
|
onClick: () => void play(),
|
|
2001
2079
|
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",
|
|
2002
2080
|
children: [
|
|
2003
|
-
/* @__PURE__ */ (0,
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react9.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2004
2082
|
"Play all"
|
|
2005
2083
|
]
|
|
2006
2084
|
}
|
|
2007
2085
|
),
|
|
2008
|
-
/* @__PURE__ */ (0,
|
|
2086
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2009
2087
|
"button",
|
|
2010
2088
|
{
|
|
2011
2089
|
type: "button",
|
|
@@ -2020,7 +2098,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2020
2098
|
sort.ordering
|
|
2021
2099
|
)) })
|
|
2022
2100
|
] }),
|
|
2023
|
-
/* @__PURE__ */ (0,
|
|
2101
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2024
2102
|
TrackItem,
|
|
2025
2103
|
{
|
|
2026
2104
|
track: song,
|
|
@@ -2029,12 +2107,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2029
2107
|
renderSongLink
|
|
2030
2108
|
}
|
|
2031
2109
|
) }, song.uuid)) }),
|
|
2032
|
-
/* @__PURE__ */ (0,
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui11.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2033
2111
|
] }) });
|
|
2034
2112
|
}
|
|
2035
2113
|
|
|
2036
2114
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2037
|
-
var
|
|
2115
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2038
2116
|
function useArtistPanel({
|
|
2039
2117
|
uuid,
|
|
2040
2118
|
scope,
|
|
@@ -2046,7 +2124,7 @@ function useArtistPanel({
|
|
|
2046
2124
|
onExpandedChange,
|
|
2047
2125
|
onClose
|
|
2048
2126
|
}) {
|
|
2049
|
-
const result = (0,
|
|
2127
|
+
const result = (0, import_genre_tree24.useArtist)(uuid, getBackendBaseUrl);
|
|
2050
2128
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2051
2129
|
switch (result.status) {
|
|
2052
2130
|
case "idle":
|
|
@@ -2055,26 +2133,26 @@ function useArtistPanel({
|
|
|
2055
2133
|
return {
|
|
2056
2134
|
...panel,
|
|
2057
2135
|
title: "Artist",
|
|
2058
|
-
content: /* @__PURE__ */ (0,
|
|
2059
|
-
/* @__PURE__ */ (0,
|
|
2060
|
-
/* @__PURE__ */ (0,
|
|
2061
|
-
/* @__PURE__ */ (0,
|
|
2062
|
-
/* @__PURE__ */ (0,
|
|
2063
|
-
/* @__PURE__ */ (0,
|
|
2064
|
-
/* @__PURE__ */ (0,
|
|
2136
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2137
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-3 w-16" }),
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-4 w-32" }),
|
|
2139
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-3 w-16" }),
|
|
2140
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-4 w-24" }),
|
|
2141
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2142
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.Skeleton, { className: "h-16" })
|
|
2065
2143
|
] })
|
|
2066
2144
|
};
|
|
2067
2145
|
case "not-found":
|
|
2068
2146
|
return {
|
|
2069
2147
|
...panel,
|
|
2070
2148
|
title: "Artist not found",
|
|
2071
|
-
content: /* @__PURE__ */ (0,
|
|
2149
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2072
2150
|
};
|
|
2073
2151
|
case "error":
|
|
2074
2152
|
return {
|
|
2075
2153
|
...panel,
|
|
2076
2154
|
title: "Artist",
|
|
2077
|
-
content: /* @__PURE__ */ (0,
|
|
2155
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2078
2156
|
};
|
|
2079
2157
|
case "success": {
|
|
2080
2158
|
const { artist } = result;
|
|
@@ -2083,13 +2161,13 @@ function useArtistPanel({
|
|
|
2083
2161
|
return {
|
|
2084
2162
|
...panel,
|
|
2085
2163
|
title: artist.name,
|
|
2086
|
-
content: /* @__PURE__ */ (0,
|
|
2087
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2088
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2089
|
-
/* @__PURE__ */ (0,
|
|
2090
|
-
/* @__PURE__ */ (0,
|
|
2091
|
-
/* @__PURE__ */ (0,
|
|
2092
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2164
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
2165
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2166
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2167
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistHeader, { artist }),
|
|
2168
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2169
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2170
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2093
2171
|
ArtistSongs,
|
|
2094
2172
|
{
|
|
2095
2173
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2099,7 +2177,7 @@ function useArtistPanel({
|
|
|
2099
2177
|
}
|
|
2100
2178
|
)
|
|
2101
2179
|
] }),
|
|
2102
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2180
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2103
2181
|
};
|
|
2104
2182
|
}
|
|
2105
2183
|
}
|
|
@@ -2107,48 +2185,48 @@ function useArtistPanel({
|
|
|
2107
2185
|
|
|
2108
2186
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2109
2187
|
var import_ui14 = require("@behindthemusictree/ui");
|
|
2110
|
-
var
|
|
2188
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2111
2189
|
|
|
2112
2190
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2113
|
-
var
|
|
2191
|
+
var import_react16 = require("react");
|
|
2114
2192
|
var import_lucide_react10 = require("lucide-react");
|
|
2115
2193
|
var import_ui13 = require("@behindthemusictree/ui");
|
|
2116
2194
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2117
|
-
var
|
|
2118
|
-
var
|
|
2195
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2196
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2119
2197
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2120
2198
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2121
2199
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2122
2200
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2123
|
-
const [open, setOpen] = (0,
|
|
2124
|
-
const history = (0,
|
|
2201
|
+
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
2202
|
+
const history = (0, import_genre_tree25.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2125
2203
|
let body = null;
|
|
2126
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2127
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2204
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2205
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2128
2206
|
else if (history.status === "success")
|
|
2129
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2130
|
-
/* @__PURE__ */ (0,
|
|
2131
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2207
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2208
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2209
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2132
2210
|
" ",
|
|
2133
2211
|
"\u2014 ",
|
|
2134
2212
|
entry.oldValue ?? "none",
|
|
2135
2213
|
" \u2192 ",
|
|
2136
2214
|
entry.newValue ?? "none"
|
|
2137
2215
|
] }) : null,
|
|
2138
|
-
/* @__PURE__ */ (0,
|
|
2216
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2139
2217
|
entry.actorPseudo ?? "Anonymous",
|
|
2140
2218
|
" \xB7 ",
|
|
2141
2219
|
dateFormat.format(new Date(entry.createdOn))
|
|
2142
2220
|
] })
|
|
2143
2221
|
] }, entry.uuid)) });
|
|
2144
|
-
return /* @__PURE__ */ (0,
|
|
2222
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
2145
2223
|
"details",
|
|
2146
2224
|
{
|
|
2147
2225
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2148
2226
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2149
2227
|
children: [
|
|
2150
|
-
/* @__PURE__ */ (0,
|
|
2151
|
-
/* @__PURE__ */ (0,
|
|
2228
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2229
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "mt-1.5", children: body })
|
|
2152
2230
|
]
|
|
2153
2231
|
}
|
|
2154
2232
|
);
|
|
@@ -2162,25 +2240,25 @@ function SongPanelContent({
|
|
|
2162
2240
|
genrePath,
|
|
2163
2241
|
onSelectGenre
|
|
2164
2242
|
}) {
|
|
2165
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2243
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree25.useTrackList)();
|
|
2166
2244
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2167
|
-
const unplayableReason = (0,
|
|
2245
|
+
const unplayableReason = (0, import_genre_tree25.songUnplayableReason)(song);
|
|
2168
2246
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2169
2247
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2170
2248
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2171
|
-
const musicBrainzUrl = (0,
|
|
2249
|
+
const musicBrainzUrl = (0, import_genre_tree25.musicBrainzRecordingUrl)(song);
|
|
2172
2250
|
const fields = [];
|
|
2173
2251
|
if (song.genre) {
|
|
2174
2252
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2175
2253
|
fields.push({
|
|
2176
2254
|
label: "Genre",
|
|
2177
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2178
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2179
|
-
/* @__PURE__ */ (0,
|
|
2255
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { children: [
|
|
2256
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2257
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2180
2258
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2181
2259
|
});
|
|
2182
2260
|
} else {
|
|
2183
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2261
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2184
2262
|
}
|
|
2185
2263
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2186
2264
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2190,11 +2268,11 @@ function SongPanelContent({
|
|
|
2190
2268
|
const links = [];
|
|
2191
2269
|
if (musicBrainzUrl)
|
|
2192
2270
|
links.push(
|
|
2193
|
-
/* @__PURE__ */ (0,
|
|
2271
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2194
2272
|
);
|
|
2195
2273
|
if (song.youtubeVideo)
|
|
2196
2274
|
links.push(
|
|
2197
|
-
/* @__PURE__ */ (0,
|
|
2275
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2198
2276
|
"a",
|
|
2199
2277
|
{
|
|
2200
2278
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2209,33 +2287,33 @@ function SongPanelContent({
|
|
|
2209
2287
|
if (links.length > 0)
|
|
2210
2288
|
fields.push({
|
|
2211
2289
|
label: "Links",
|
|
2212
|
-
value: /* @__PURE__ */ (0,
|
|
2290
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "flex gap-3", children: links })
|
|
2213
2291
|
});
|
|
2214
|
-
return /* @__PURE__ */ (0,
|
|
2215
|
-
/* @__PURE__ */ (0,
|
|
2216
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
|
|
2293
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2294
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
2217
2295
|
"button",
|
|
2218
2296
|
{
|
|
2219
2297
|
type: "button",
|
|
2220
2298
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2221
2299
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2222
2300
|
children: [
|
|
2223
|
-
/* @__PURE__ */ (0,
|
|
2301
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2224
2302
|
isPlaying ? "Pause" : "Play"
|
|
2225
2303
|
]
|
|
2226
2304
|
}
|
|
2227
2305
|
),
|
|
2228
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2306
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2229
2307
|
] }) }),
|
|
2230
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2231
|
-
/* @__PURE__ */ (0,
|
|
2232
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2233
|
-
/* @__PURE__ */ (0,
|
|
2308
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_genre_tree25.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.FieldList, { items: fields }),
|
|
2310
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2311
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2234
2312
|
] });
|
|
2235
2313
|
}
|
|
2236
2314
|
|
|
2237
2315
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2238
|
-
var
|
|
2316
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2239
2317
|
function useSongPanel({
|
|
2240
2318
|
uuid,
|
|
2241
2319
|
scope,
|
|
@@ -2248,7 +2326,7 @@ function useSongPanel({
|
|
|
2248
2326
|
onExpandedChange,
|
|
2249
2327
|
onClose
|
|
2250
2328
|
}) {
|
|
2251
|
-
const result = (0,
|
|
2329
|
+
const result = (0, import_genre_tree26.useSong)(uuid, getBackendBaseUrl);
|
|
2252
2330
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2253
2331
|
switch (result.status) {
|
|
2254
2332
|
case "idle":
|
|
@@ -2257,24 +2335,24 @@ function useSongPanel({
|
|
|
2257
2335
|
return {
|
|
2258
2336
|
...panel,
|
|
2259
2337
|
title: "Song",
|
|
2260
|
-
content: /* @__PURE__ */ (0,
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2262
|
-
/* @__PURE__ */ (0,
|
|
2263
|
-
/* @__PURE__ */ (0,
|
|
2264
|
-
/* @__PURE__ */ (0,
|
|
2338
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2339
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.Skeleton, { className: "h-3 w-16" }),
|
|
2340
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.Skeleton, { className: "h-4 w-32" }),
|
|
2341
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.Skeleton, { className: "h-3 w-16" }),
|
|
2342
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.Skeleton, { className: "h-4 w-24" })
|
|
2265
2343
|
] })
|
|
2266
2344
|
};
|
|
2267
2345
|
case "not-found":
|
|
2268
2346
|
return {
|
|
2269
2347
|
...panel,
|
|
2270
2348
|
title: "Song not found",
|
|
2271
|
-
content: /* @__PURE__ */ (0,
|
|
2349
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2272
2350
|
};
|
|
2273
2351
|
case "error":
|
|
2274
2352
|
return {
|
|
2275
2353
|
...panel,
|
|
2276
2354
|
title: "Song",
|
|
2277
|
-
content: /* @__PURE__ */ (0,
|
|
2355
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2278
2356
|
};
|
|
2279
2357
|
case "success": {
|
|
2280
2358
|
const { song } = result;
|
|
@@ -2282,7 +2360,7 @@ function useSongPanel({
|
|
|
2282
2360
|
return {
|
|
2283
2361
|
...panel,
|
|
2284
2362
|
title: song.title,
|
|
2285
|
-
content: /* @__PURE__ */ (0,
|
|
2363
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2286
2364
|
SongPanelContent,
|
|
2287
2365
|
{
|
|
2288
2366
|
song,
|
|
@@ -2302,12 +2380,12 @@ function useSongPanel({
|
|
|
2302
2380
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2303
2381
|
var import_md = require("react-icons/md");
|
|
2304
2382
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
2305
|
-
var
|
|
2383
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2306
2384
|
function SizeMetricToggle({
|
|
2307
2385
|
value,
|
|
2308
2386
|
onChange
|
|
2309
2387
|
}) {
|
|
2310
|
-
return /* @__PURE__ */ (0,
|
|
2388
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2311
2389
|
import_ui15.SegmentedControl,
|
|
2312
2390
|
{
|
|
2313
2391
|
variant: "floating",
|
|
@@ -2315,15 +2393,15 @@ function SizeMetricToggle({
|
|
|
2315
2393
|
value,
|
|
2316
2394
|
onChange,
|
|
2317
2395
|
options: [
|
|
2318
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2319
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2396
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2397
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2320
2398
|
]
|
|
2321
2399
|
}
|
|
2322
2400
|
);
|
|
2323
2401
|
}
|
|
2324
2402
|
|
|
2325
2403
|
// src/genre-tree/GenreTreeView.tsx
|
|
2326
|
-
var
|
|
2404
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2327
2405
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2328
2406
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2329
2407
|
var noop = () => {
|
|
@@ -2358,6 +2436,7 @@ function GenreTreeView({
|
|
|
2358
2436
|
onArtistExpandedChange,
|
|
2359
2437
|
getShareUrl,
|
|
2360
2438
|
extraSearchGroups = [],
|
|
2439
|
+
SearchGroups,
|
|
2361
2440
|
onSearchQueryChange,
|
|
2362
2441
|
onSearchFocus,
|
|
2363
2442
|
artistSearch = true,
|
|
@@ -2384,21 +2463,21 @@ function GenreTreeView({
|
|
|
2384
2463
|
placement = "default",
|
|
2385
2464
|
...focus
|
|
2386
2465
|
}) {
|
|
2387
|
-
const getShareUrlRef = (0,
|
|
2466
|
+
const getShareUrlRef = (0, import_react17.useRef)(getShareUrl);
|
|
2388
2467
|
getShareUrlRef.current = getShareUrl;
|
|
2389
|
-
const [internalArtistUuid, setInternalArtistUuid] = (0,
|
|
2468
|
+
const [internalArtistUuid, setInternalArtistUuid] = (0, import_react17.useState)(null);
|
|
2390
2469
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2391
2470
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2392
|
-
const [internalSongUuid, setInternalSongUuid] = (0,
|
|
2471
|
+
const [internalSongUuid, setInternalSongUuid] = (0, import_react17.useState)(null);
|
|
2393
2472
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2394
2473
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2395
2474
|
const changeSong = (uuid) => {
|
|
2396
2475
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2397
2476
|
onSongUuidChange?.(uuid);
|
|
2398
2477
|
};
|
|
2399
|
-
const [internalSizeMetric, setInternalSizeMetric] = (0,
|
|
2478
|
+
const [internalSizeMetric, setInternalSizeMetric] = (0, import_react17.useState)("songs");
|
|
2400
2479
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2401
|
-
const deferredSizeMetric = (0,
|
|
2480
|
+
const deferredSizeMetric = (0, import_react17.useDeferredValue)(sizeMetric);
|
|
2402
2481
|
const changeSizeMetric = (metric) => {
|
|
2403
2482
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2404
2483
|
onSizeMetricChange?.(metric);
|
|
@@ -2425,7 +2504,7 @@ function GenreTreeView({
|
|
|
2425
2504
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2426
2505
|
toggleWheelRotation,
|
|
2427
2506
|
onNodeHover: handleNodeHover
|
|
2428
|
-
} = (0,
|
|
2507
|
+
} = (0, import_genre_tree27.useGenreTreeViewState)({
|
|
2429
2508
|
scope,
|
|
2430
2509
|
getBackendBaseUrl,
|
|
2431
2510
|
viewMode: controlledViewMode,
|
|
@@ -2435,7 +2514,7 @@ function GenreTreeView({
|
|
|
2435
2514
|
artistUuid
|
|
2436
2515
|
});
|
|
2437
2516
|
const restoreRevision = viewportRestore?.revision;
|
|
2438
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] = (0,
|
|
2517
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = (0, import_react17.useState)({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2439
2518
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2440
2519
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2441
2520
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2444,14 +2523,14 @@ function GenreTreeView({
|
|
|
2444
2523
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2445
2524
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2446
2525
|
}
|
|
2447
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2448
|
-
const { mutate: updateGenre } = (0,
|
|
2526
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree27.genreUpdateMutationKey }) > 0;
|
|
2527
|
+
const { mutate: updateGenre } = (0, import_genre_tree27.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2449
2528
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2450
2529
|
const isArtistFiltered = artistUuid !== null;
|
|
2451
2530
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2452
|
-
const artistResult = (0,
|
|
2531
|
+
const artistResult = (0, import_genre_tree27.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2453
2532
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2454
|
-
const filterArtist = (0,
|
|
2533
|
+
const filterArtist = (0, import_react17.useMemo)(
|
|
2455
2534
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
2456
2535
|
[artistUuid, filterArtistName]
|
|
2457
2536
|
);
|
|
@@ -2466,14 +2545,14 @@ function GenreTreeView({
|
|
|
2466
2545
|
if (selectedNodeId !== null) clearSelection();
|
|
2467
2546
|
changeSong(uuid);
|
|
2468
2547
|
};
|
|
2469
|
-
const openSongRef = (0,
|
|
2548
|
+
const openSongRef = (0, import_react17.useRef)(openSong);
|
|
2470
2549
|
openSongRef.current = openSong;
|
|
2471
|
-
const resolvedRenderSongLink = (0,
|
|
2472
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2550
|
+
const resolvedRenderSongLink = (0, import_react17.useCallback)(
|
|
2551
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2473
2552
|
[renderSongLink]
|
|
2474
2553
|
);
|
|
2475
|
-
const [expandedSongUuid, setExpandedSongUuid] = (0,
|
|
2476
|
-
const [expandedArtistUuid, setExpandedArtistUuid] = (0,
|
|
2554
|
+
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react17.useState)(null);
|
|
2555
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react17.useState)(null);
|
|
2477
2556
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2478
2557
|
const songBaseUrl = getBackendBaseUrl;
|
|
2479
2558
|
const artistPanel = useArtistPanel({
|
|
@@ -2481,7 +2560,7 @@ function GenreTreeView({
|
|
|
2481
2560
|
scope,
|
|
2482
2561
|
getBackendBaseUrl: artistBaseUrl,
|
|
2483
2562
|
getShareUrl: getShareUrlRef.current,
|
|
2484
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2563
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2485
2564
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2486
2565
|
onExpandedChange: (on) => {
|
|
2487
2566
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2490,7 +2569,7 @@ function GenreTreeView({
|
|
|
2490
2569
|
renderSongLink: resolvedRenderSongLink,
|
|
2491
2570
|
onClose: () => changeArtist(null)
|
|
2492
2571
|
});
|
|
2493
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2572
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2494
2573
|
const getGenrePath = (genre) => {
|
|
2495
2574
|
const path = [];
|
|
2496
2575
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2522,8 +2601,8 @@ function GenreTreeView({
|
|
|
2522
2601
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2523
2602
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2524
2603
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2525
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0,
|
|
2526
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] = (0,
|
|
2604
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react17.useState)(false);
|
|
2605
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = (0, import_react17.useState)(false);
|
|
2527
2606
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2528
2607
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2529
2608
|
const changeCollapsed = (next) => {
|
|
@@ -2534,28 +2613,28 @@ function GenreTreeView({
|
|
|
2534
2613
|
setUncontrolledExpanded(next);
|
|
2535
2614
|
onExpandedChange?.(next);
|
|
2536
2615
|
};
|
|
2537
|
-
(0,
|
|
2616
|
+
(0, import_react17.useEffect)(() => {
|
|
2538
2617
|
setUncontrolledCollapsed(false);
|
|
2539
2618
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2540
|
-
const [isWheelRevealed, setIsWheelRevealed] = (0,
|
|
2619
|
+
const [isWheelRevealed, setIsWheelRevealed] = (0, import_react17.useState)(false);
|
|
2541
2620
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2542
2621
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2543
|
-
const [resolveStyle, setResolveStyle] = (0,
|
|
2544
|
-
const handleNodeStyleResolverChange = (0,
|
|
2622
|
+
const [resolveStyle, setResolveStyle] = (0, import_react17.useState)(null);
|
|
2623
|
+
const handleNodeStyleResolverChange = (0, import_react17.useCallback)(
|
|
2545
2624
|
(resolver) => setResolveStyle(() => resolver),
|
|
2546
2625
|
[]
|
|
2547
2626
|
);
|
|
2548
|
-
const panelRef = (0,
|
|
2627
|
+
const panelRef = (0, import_react17.useRef)(null);
|
|
2549
2628
|
const obscuredInset = (0, import_ui16.useObscuredInset)(panelRef);
|
|
2550
2629
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2551
2630
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2552
|
-
const treeNodes = (0,
|
|
2631
|
+
const treeNodes = (0, import_react17.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2553
2632
|
const relations = (0, import_genre_tree_view4.useGenreNodeRelations)(
|
|
2554
2633
|
treeNodes,
|
|
2555
2634
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2556
2635
|
resolveStyle
|
|
2557
2636
|
);
|
|
2558
|
-
const [selectionHistory, setSelectionHistory] = (0,
|
|
2637
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react17.useState)({
|
|
2559
2638
|
current: selectedNodeId,
|
|
2560
2639
|
previous: null
|
|
2561
2640
|
});
|
|
@@ -2568,28 +2647,6 @@ function GenreTreeView({
|
|
|
2568
2647
|
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2569
2648
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2570
2649
|
const { showPopup, hidePopup } = (0, import_popup5.usePopup)();
|
|
2571
|
-
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2572
|
-
const artistGroup = useArtistSearchGroup(
|
|
2573
|
-
artistBaseUrl,
|
|
2574
|
-
searchText,
|
|
2575
|
-
(artist) => {
|
|
2576
|
-
openArtist(artist.uuid);
|
|
2577
|
-
onArtistSelect?.(artist);
|
|
2578
|
-
},
|
|
2579
|
-
Boolean(artistSearch)
|
|
2580
|
-
);
|
|
2581
|
-
const songGroup = useSongSearchGroup(
|
|
2582
|
-
songBaseUrl,
|
|
2583
|
-
searchText,
|
|
2584
|
-
(song) => {
|
|
2585
|
-
openSong(song.uuid);
|
|
2586
|
-
onSongSelect?.(song);
|
|
2587
|
-
},
|
|
2588
|
-
Boolean(songSearch)
|
|
2589
|
-
);
|
|
2590
|
-
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2591
|
-
(group) => group !== void 0
|
|
2592
|
-
);
|
|
2593
2650
|
const handleSelectedNodeChange = (node) => {
|
|
2594
2651
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2595
2652
|
selectNode(node);
|
|
@@ -2605,7 +2662,7 @@ function GenreTreeView({
|
|
|
2605
2662
|
const handleGenreSearchZoomIn = () => {
|
|
2606
2663
|
if (selectedUuid) focus.onFocusGenreChange?.(selectedUuid, "zoom-in");
|
|
2607
2664
|
};
|
|
2608
|
-
const zoomInRef = (0,
|
|
2665
|
+
const zoomInRef = (0, import_react17.useRef)(void 0);
|
|
2609
2666
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2610
2667
|
const selectGenreFromPanel = (nodeId) => {
|
|
2611
2668
|
const genrePlaylist = genrePlaylistByNodeId.get(nodeId);
|
|
@@ -2613,7 +2670,7 @@ function GenreTreeView({
|
|
|
2613
2670
|
};
|
|
2614
2671
|
const renderGenreBody = (relation) => {
|
|
2615
2672
|
const { node } = relation;
|
|
2616
|
-
const family = /* @__PURE__ */ (0,
|
|
2673
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2617
2674
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2618
2675
|
{
|
|
2619
2676
|
node,
|
|
@@ -2624,10 +2681,10 @@ function GenreTreeView({
|
|
|
2624
2681
|
onSelectNode: selectGenreFromPanel
|
|
2625
2682
|
}
|
|
2626
2683
|
);
|
|
2627
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2684
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2628
2685
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2629
2686
|
const criteria = genrePlaylist?.criteria;
|
|
2630
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2687
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2631
2688
|
family,
|
|
2632
2689
|
ancestors
|
|
2633
2690
|
] }) };
|
|
@@ -2647,16 +2704,17 @@ function GenreTreeView({
|
|
|
2647
2704
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2648
2705
|
) : void 0;
|
|
2649
2706
|
return {
|
|
2650
|
-
primary: /* @__PURE__ */ (0,
|
|
2651
|
-
/* @__PURE__ */ (0,
|
|
2707
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2708
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2652
2709
|
GenreInfoPanelBody,
|
|
2653
2710
|
{
|
|
2654
|
-
family: /* @__PURE__ */ (0,
|
|
2711
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2655
2712
|
family,
|
|
2656
2713
|
ancestors
|
|
2657
2714
|
] }),
|
|
2658
2715
|
genreUuid,
|
|
2659
2716
|
genrePlaylist,
|
|
2717
|
+
catalogue: genrePlaylist.catalogue,
|
|
2660
2718
|
wikidataId: criteria.wikidataId,
|
|
2661
2719
|
scope,
|
|
2662
2720
|
getBackendBaseUrl,
|
|
@@ -2669,7 +2727,7 @@ function GenreTreeView({
|
|
|
2669
2727
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2670
2728
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2671
2729
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2672
|
-
/* @__PURE__ */ (0,
|
|
2730
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2673
2731
|
ChangeParentPopup,
|
|
2674
2732
|
{
|
|
2675
2733
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2744,19 +2802,19 @@ function GenreTreeView({
|
|
|
2744
2802
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2745
2803
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2746
2804
|
} : void 0;
|
|
2747
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2805
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2748
2806
|
import_ui16.IconButton,
|
|
2749
2807
|
{
|
|
2750
2808
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2751
2809
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2752
2810
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2753
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
2811
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react12.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react12.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2754
2812
|
}
|
|
2755
2813
|
) : null;
|
|
2756
2814
|
const renderArtistFilter = () => {
|
|
2757
2815
|
if (isLoading || !filterArtist) return null;
|
|
2758
|
-
return /* @__PURE__ */ (0,
|
|
2759
|
-
/* @__PURE__ */ (0,
|
|
2816
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2817
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2760
2818
|
"button",
|
|
2761
2819
|
{
|
|
2762
2820
|
type: "button",
|
|
@@ -2768,7 +2826,7 @@ function GenreTreeView({
|
|
|
2768
2826
|
children: filterArtist.name
|
|
2769
2827
|
}
|
|
2770
2828
|
),
|
|
2771
|
-
/* @__PURE__ */ (0,
|
|
2829
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2772
2830
|
"button",
|
|
2773
2831
|
{
|
|
2774
2832
|
type: "button",
|
|
@@ -2780,26 +2838,36 @@ function GenreTreeView({
|
|
|
2780
2838
|
)
|
|
2781
2839
|
] });
|
|
2782
2840
|
};
|
|
2783
|
-
const actions = /* @__PURE__ */ (0,
|
|
2784
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
2785
|
-
|
|
2841
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2842
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2843
|
+
GenreTreeSearch,
|
|
2786
2844
|
{
|
|
2845
|
+
artistBaseUrl,
|
|
2846
|
+
songBaseUrl,
|
|
2847
|
+
artistSearch: Boolean(artistSearch),
|
|
2848
|
+
songSearch: Boolean(songSearch),
|
|
2849
|
+
onArtistSelect: (artist) => {
|
|
2850
|
+
openArtist(artist.uuid);
|
|
2851
|
+
onArtistSelect?.(artist);
|
|
2852
|
+
},
|
|
2853
|
+
onSongSelect: (song) => {
|
|
2854
|
+
openSong(song.uuid);
|
|
2855
|
+
onSongSelect?.(song);
|
|
2856
|
+
},
|
|
2857
|
+
SearchGroups,
|
|
2787
2858
|
genrePlaylists: visibleGenrePlaylists,
|
|
2788
2859
|
onSelect: handleGenreSearchSelect,
|
|
2789
2860
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2790
2861
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2791
2862
|
onPrevious: handlePrevious,
|
|
2792
|
-
onQueryChange:
|
|
2793
|
-
setSearchText(text);
|
|
2794
|
-
onSearchQueryChange?.(text);
|
|
2795
|
-
},
|
|
2863
|
+
onQueryChange: onSearchQueryChange,
|
|
2796
2864
|
onFocus: onSearchFocus,
|
|
2797
|
-
extraGroups
|
|
2865
|
+
extraGroups: extraSearchGroups
|
|
2798
2866
|
}
|
|
2799
2867
|
),
|
|
2800
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2801
|
-
/* @__PURE__ */ (0,
|
|
2802
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
2868
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2869
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
2870
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2803
2871
|
import_ui16.SegmentedControl,
|
|
2804
2872
|
{
|
|
2805
2873
|
"aria-label": "Tree view mode",
|
|
@@ -2813,13 +2881,13 @@ function GenreTreeView({
|
|
|
2813
2881
|
]
|
|
2814
2882
|
}
|
|
2815
2883
|
),
|
|
2816
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
2884
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2817
2885
|
"div",
|
|
2818
2886
|
{
|
|
2819
2887
|
className: "flex items-center gap-1",
|
|
2820
2888
|
role: "group",
|
|
2821
2889
|
"aria-label": "Tree display options",
|
|
2822
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
2890
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2823
2891
|
import_ui16.Button,
|
|
2824
2892
|
{
|
|
2825
2893
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2830,7 +2898,7 @@ function GenreTreeView({
|
|
|
2830
2898
|
)
|
|
2831
2899
|
}
|
|
2832
2900
|
),
|
|
2833
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
2901
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2834
2902
|
import_ui16.IconTextButton,
|
|
2835
2903
|
{
|
|
2836
2904
|
icon: import_lucide_react11.Plus,
|
|
@@ -2840,34 +2908,34 @@ function GenreTreeView({
|
|
|
2840
2908
|
)
|
|
2841
2909
|
] })
|
|
2842
2910
|
] });
|
|
2843
|
-
return /* @__PURE__ */ (0,
|
|
2911
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2844
2912
|
"div",
|
|
2845
2913
|
{
|
|
2846
2914
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2847
2915
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2848
2916
|
children: [
|
|
2849
|
-
/* @__PURE__ */ (0,
|
|
2917
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2850
2918
|
"div",
|
|
2851
2919
|
{
|
|
2852
2920
|
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"}`,
|
|
2853
2921
|
children: actions
|
|
2854
2922
|
}
|
|
2855
2923
|
),
|
|
2856
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
2924
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2857
2925
|
"div",
|
|
2858
2926
|
{
|
|
2859
2927
|
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"}`,
|
|
2860
2928
|
children: renderArtistFilter()
|
|
2861
2929
|
}
|
|
2862
2930
|
),
|
|
2863
|
-
/* @__PURE__ */ (0,
|
|
2864
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
2865
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
2931
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2932
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2933
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2866
2934
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2867
2935
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2868
2936
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2869
2937
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2870
|
-
/* @__PURE__ */ (0,
|
|
2938
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2871
2939
|
GenrePlaylistTreeWheel,
|
|
2872
2940
|
{
|
|
2873
2941
|
scope,
|
|
@@ -2884,9 +2952,9 @@ function GenreTreeView({
|
|
|
2884
2952
|
allowWheelRotation,
|
|
2885
2953
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2886
2954
|
busy,
|
|
2887
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2955
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2888
2956
|
floatingControlsLeading,
|
|
2889
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2957
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2890
2958
|
] }) : void 0,
|
|
2891
2959
|
defaultViewport,
|
|
2892
2960
|
viewportRestore,
|
|
@@ -2895,7 +2963,7 @@ function GenreTreeView({
|
|
|
2895
2963
|
) }) })
|
|
2896
2964
|
) : viewMode === "pop-core" ? (
|
|
2897
2965
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2898
|
-
/* @__PURE__ */ (0,
|
|
2966
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2899
2967
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2900
2968
|
{
|
|
2901
2969
|
scope,
|
|
@@ -2912,9 +2980,9 @@ function GenreTreeView({
|
|
|
2912
2980
|
allowWheelRotation,
|
|
2913
2981
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2914
2982
|
busy,
|
|
2915
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
2983
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2916
2984
|
floatingControlsLeading,
|
|
2917
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
2985
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2918
2986
|
] }) : void 0,
|
|
2919
2987
|
defaultViewport,
|
|
2920
2988
|
viewportRestore,
|
|
@@ -2922,7 +2990,7 @@ function GenreTreeView({
|
|
|
2922
2990
|
...focus
|
|
2923
2991
|
}
|
|
2924
2992
|
) }) })
|
|
2925
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
2993
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2926
2994
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2927
2995
|
{
|
|
2928
2996
|
outline: true,
|
|
@@ -2938,13 +3006,13 @@ function GenreTreeView({
|
|
|
2938
3006
|
selectedNodeId,
|
|
2939
3007
|
readOnly
|
|
2940
3008
|
}
|
|
2941
|
-
) }) : /* @__PURE__ */ (0,
|
|
3009
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
2942
3010
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2943
|
-
return /* @__PURE__ */ (0,
|
|
3011
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2944
3012
|
"div",
|
|
2945
3013
|
{
|
|
2946
3014
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2947
|
-
children: /* @__PURE__ */ (0,
|
|
3015
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2948
3016
|
GenrePlaylistTreePerRoot,
|
|
2949
3017
|
{
|
|
2950
3018
|
scope,
|
|
@@ -2970,7 +3038,7 @@ function GenreTreeView({
|
|
|
2970
3038
|
);
|
|
2971
3039
|
}
|
|
2972
3040
|
) }),
|
|
2973
|
-
/* @__PURE__ */ (0,
|
|
3041
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2974
3042
|
import_ui16.DetailPanel,
|
|
2975
3043
|
{
|
|
2976
3044
|
ref: panelRef,
|
|
@@ -2987,7 +3055,7 @@ function GenreTreeView({
|
|
|
2987
3055
|
aside: activePanel?.aside,
|
|
2988
3056
|
edgeTab: detailTab,
|
|
2989
3057
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2990
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3058
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2991
3059
|
activePanel.content,
|
|
2992
3060
|
activePanel.aside
|
|
2993
3061
|
] }) : activePanel?.content
|
|
@@ -3000,12 +3068,12 @@ function GenreTreeView({
|
|
|
3000
3068
|
}
|
|
3001
3069
|
|
|
3002
3070
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
3003
|
-
var
|
|
3071
|
+
var import_react18 = require("react");
|
|
3004
3072
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3005
3073
|
var import_ui17 = require("@behindthemusictree/ui");
|
|
3006
3074
|
var import_lucide_react13 = require("lucide-react");
|
|
3007
|
-
var
|
|
3008
|
-
var
|
|
3075
|
+
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3076
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
3009
3077
|
var RELATIVE_ITEM_COUNT = 1;
|
|
3010
3078
|
function toNode(genre, parentId) {
|
|
3011
3079
|
return {
|
|
@@ -3032,9 +3100,9 @@ function GenreDetailPanel({
|
|
|
3032
3100
|
edgeTab,
|
|
3033
3101
|
extras
|
|
3034
3102
|
}) {
|
|
3035
|
-
const { data: detail, isPending, isError } = (0,
|
|
3103
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree28.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3036
3104
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3037
|
-
const view = (0,
|
|
3105
|
+
const view = (0, import_react18.useMemo)(() => {
|
|
3038
3106
|
if (!detail) return null;
|
|
3039
3107
|
const rootColor = (0, import_genre_tree_view5.getGenreTreeColor)(detail.root.wikidataId);
|
|
3040
3108
|
const relative = (genre, parentId) => ({
|
|
@@ -3048,7 +3116,7 @@ function GenreDetailPanel({
|
|
|
3048
3116
|
id: detail.uuid,
|
|
3049
3117
|
parentId: detail.parent?.uuid ?? null,
|
|
3050
3118
|
name: detail.name,
|
|
3051
|
-
itemCount: detail.tracksCount,
|
|
3119
|
+
itemCount: scope === "reference" ? (0, import_genre_tree28.genreMusicCount)(import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist)) : detail.tracksCount,
|
|
3052
3120
|
wikidataId: detail.wikidataId
|
|
3053
3121
|
},
|
|
3054
3122
|
fill: rootColor,
|
|
@@ -3058,19 +3126,19 @@ function GenreDetailPanel({
|
|
|
3058
3126
|
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
3059
3127
|
)
|
|
3060
3128
|
};
|
|
3061
|
-
}, [detail]);
|
|
3129
|
+
}, [detail, scope]);
|
|
3062
3130
|
if (isError) {
|
|
3063
|
-
return /* @__PURE__ */ (0,
|
|
3131
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3064
3132
|
}
|
|
3065
3133
|
if (isPending || !detail || !view) {
|
|
3066
|
-
return /* @__PURE__ */ (0,
|
|
3067
|
-
/* @__PURE__ */ (0,
|
|
3068
|
-
/* @__PURE__ */ (0,
|
|
3069
|
-
/* @__PURE__ */ (0,
|
|
3134
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3135
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-6 w-2/3" }),
|
|
3136
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-4 w-full" }),
|
|
3137
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui17.Skeleton, { className: "h-4 w-3/4" })
|
|
3070
3138
|
] });
|
|
3071
3139
|
}
|
|
3072
3140
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3073
|
-
const family = /* @__PURE__ */ (0,
|
|
3141
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3074
3142
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3075
3143
|
{
|
|
3076
3144
|
node: view.node,
|
|
@@ -3081,20 +3149,20 @@ function GenreDetailPanel({
|
|
|
3081
3149
|
onSelectNode: selectGenre
|
|
3082
3150
|
}
|
|
3083
3151
|
);
|
|
3084
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3085
|
-
return /* @__PURE__ */ (0,
|
|
3152
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3153
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
3086
3154
|
import_ui17.DetailPanel,
|
|
3087
3155
|
{
|
|
3088
3156
|
side: "left",
|
|
3089
3157
|
title: view.node.name,
|
|
3090
3158
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3091
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3159
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3092
3160
|
import_ui17.IconButton,
|
|
3093
3161
|
{
|
|
3094
3162
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3095
3163
|
style: { color: "inherit" },
|
|
3096
3164
|
onClick: () => onExpandedChange(!expanded),
|
|
3097
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
3165
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react13.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3098
3166
|
}
|
|
3099
3167
|
) : void 0,
|
|
3100
3168
|
onClose,
|
|
@@ -3103,15 +3171,16 @@ function GenreDetailPanel({
|
|
|
3103
3171
|
onExpandedChange,
|
|
3104
3172
|
edgeTab,
|
|
3105
3173
|
children: [
|
|
3106
|
-
/* @__PURE__ */ (0,
|
|
3174
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
3107
3175
|
GenreInfoPanelBody,
|
|
3108
3176
|
{
|
|
3109
|
-
family: /* @__PURE__ */ (0,
|
|
3177
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
3110
3178
|
family,
|
|
3111
3179
|
ancestors
|
|
3112
3180
|
] }),
|
|
3113
3181
|
genreUuid: detail.uuid,
|
|
3114
|
-
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3182
|
+
genrePlaylist: scope === "reference" ? import_genre_tree28.GenrePlaylistSummarySchema.parse(detail.criteriaPlaylist) : { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
3183
|
+
catalogue: detail.catalogue,
|
|
3115
3184
|
wikidataId: detail.wikidataId,
|
|
3116
3185
|
scope,
|
|
3117
3186
|
getBackendBaseUrl,
|
|
@@ -3130,13 +3199,13 @@ function GenreDetailPanel({
|
|
|
3130
3199
|
var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
3131
3200
|
|
|
3132
3201
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3133
|
-
var
|
|
3202
|
+
var import_react19 = require("react");
|
|
3134
3203
|
var import_lucide_react14 = require("lucide-react");
|
|
3135
|
-
var
|
|
3136
|
-
var
|
|
3137
|
-
var
|
|
3138
|
-
var
|
|
3139
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
3204
|
+
var import_genre_tree29 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3205
|
+
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3206
|
+
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
3207
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
3208
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react19.useEffect : import_react19.useLayoutEffect;
|
|
3140
3209
|
function TrackListSidebar({
|
|
3141
3210
|
className: className2 = "",
|
|
3142
3211
|
renderDuration,
|
|
@@ -3148,18 +3217,18 @@ function TrackListSidebar({
|
|
|
3148
3217
|
renderArtistLink,
|
|
3149
3218
|
renderSongLink
|
|
3150
3219
|
}) {
|
|
3151
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3152
|
-
const trackNoun = (0,
|
|
3153
|
-
const trackListRef = (0,
|
|
3154
|
-
const previousOriginRef = (0,
|
|
3155
|
-
const handledAutoAdvanceRequestIdRef = (0,
|
|
3156
|
-
const shouldFollowPlaybackRef = (0,
|
|
3157
|
-
const programmaticScrollTopRef = (0,
|
|
3158
|
-
const userScrollIntentRef = (0,
|
|
3159
|
-
const userScrollIntentTimerRef = (0,
|
|
3160
|
-
const loadMoreSentinelRef = (0,
|
|
3220
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree29.useTrackList)();
|
|
3221
|
+
const trackNoun = (0, import_track_noun4.useTrackNoun)();
|
|
3222
|
+
const trackListRef = (0, import_react19.useRef)(null);
|
|
3223
|
+
const previousOriginRef = (0, import_react19.useRef)(trackList?.origin ?? null);
|
|
3224
|
+
const handledAutoAdvanceRequestIdRef = (0, import_react19.useRef)(null);
|
|
3225
|
+
const shouldFollowPlaybackRef = (0, import_react19.useRef)(true);
|
|
3226
|
+
const programmaticScrollTopRef = (0, import_react19.useRef)(null);
|
|
3227
|
+
const userScrollIntentRef = (0, import_react19.useRef)(false);
|
|
3228
|
+
const userScrollIntentTimerRef = (0, import_react19.useRef)(null);
|
|
3229
|
+
const loadMoreSentinelRef = (0, import_react19.useRef)(null);
|
|
3161
3230
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
3162
|
-
(0,
|
|
3231
|
+
(0, import_react19.useEffect)(() => {
|
|
3163
3232
|
const sentinel = loadMoreSentinelRef.current;
|
|
3164
3233
|
if (!hasMore || !sentinel) return;
|
|
3165
3234
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -3179,11 +3248,11 @@ function TrackListSidebar({
|
|
|
3179
3248
|
programmaticScrollTopRef.current = null;
|
|
3180
3249
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
3181
3250
|
}, [trackList?.origin]);
|
|
3182
|
-
(0,
|
|
3251
|
+
(0, import_react19.useEffect)(() => {
|
|
3183
3252
|
const list = trackListRef.current;
|
|
3184
3253
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
3185
3254
|
}, [isCollapsed]);
|
|
3186
|
-
(0,
|
|
3255
|
+
(0, import_react19.useEffect)(
|
|
3187
3256
|
() => () => {
|
|
3188
3257
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
3189
3258
|
},
|
|
@@ -3237,25 +3306,25 @@ function TrackListSidebar({
|
|
|
3237
3306
|
}
|
|
3238
3307
|
};
|
|
3239
3308
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3240
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3309
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
3241
3310
|
"div",
|
|
3242
3311
|
{
|
|
3243
3312
|
"data-theme": "dark",
|
|
3244
3313
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3245
3314
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3246
3315
|
children: [
|
|
3247
|
-
/* @__PURE__ */ (0,
|
|
3248
|
-
/* @__PURE__ */ (0,
|
|
3249
|
-
/* @__PURE__ */ (0,
|
|
3250
|
-
/* @__PURE__ */ (0,
|
|
3251
|
-
/* @__PURE__ */ (0,
|
|
3316
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3317
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3318
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3319
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3320
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3252
3321
|
] }),
|
|
3253
|
-
/* @__PURE__ */ (0,
|
|
3254
|
-
trackList.origin.type ===
|
|
3322
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3323
|
+
trackList.origin.type === import_genre_tree30.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree30.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3255
3324
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3256
3325
|
] })
|
|
3257
3326
|
] }),
|
|
3258
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3327
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3259
3328
|
"button",
|
|
3260
3329
|
{
|
|
3261
3330
|
type: "button",
|
|
@@ -3264,11 +3333,11 @@ function TrackListSidebar({
|
|
|
3264
3333
|
"aria-expanded": !isCollapsed,
|
|
3265
3334
|
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"}`,
|
|
3266
3335
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3267
|
-
children: /* @__PURE__ */ (0,
|
|
3336
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3268
3337
|
}
|
|
3269
3338
|
)
|
|
3270
3339
|
] }),
|
|
3271
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3340
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
3272
3341
|
"ul",
|
|
3273
3342
|
{
|
|
3274
3343
|
ref: trackListRef,
|
|
@@ -3278,7 +3347,7 @@ function TrackListSidebar({
|
|
|
3278
3347
|
onPointerDown: markUserScrollIntent,
|
|
3279
3348
|
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",
|
|
3280
3349
|
children: [
|
|
3281
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3350
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
3282
3351
|
TrackItem,
|
|
3283
3352
|
{
|
|
3284
3353
|
track,
|
|
@@ -3289,7 +3358,7 @@ function TrackListSidebar({
|
|
|
3289
3358
|
renderSongLink
|
|
3290
3359
|
}
|
|
3291
3360
|
) }, track.uuid)),
|
|
3292
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3361
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3293
3362
|
]
|
|
3294
3363
|
}
|
|
3295
3364
|
)
|
|
@@ -3299,17 +3368,17 @@ function TrackListSidebar({
|
|
|
3299
3368
|
}
|
|
3300
3369
|
|
|
3301
3370
|
// src/genre-tree/PlayerPanel.tsx
|
|
3302
|
-
var
|
|
3371
|
+
var import_react20 = require("react");
|
|
3303
3372
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3304
3373
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3305
|
-
var
|
|
3374
|
+
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3306
3375
|
var import_ui18 = require("@behindthemusictree/ui");
|
|
3307
3376
|
|
|
3308
3377
|
// src/genre-tree/PlayerControls.tsx
|
|
3309
3378
|
var import_lucide_react15 = require("lucide-react");
|
|
3310
|
-
var
|
|
3379
|
+
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3311
3380
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3312
|
-
var
|
|
3381
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
3313
3382
|
function PlayerControls() {
|
|
3314
3383
|
const {
|
|
3315
3384
|
duration,
|
|
@@ -3321,22 +3390,22 @@ function PlayerControls() {
|
|
|
3321
3390
|
setVolume,
|
|
3322
3391
|
volume
|
|
3323
3392
|
} = (0, import_player12.usePlayer)();
|
|
3324
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3393
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree31.useTrackList)();
|
|
3325
3394
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3326
3395
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3327
3396
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3328
3397
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3329
3398
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3330
3399
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3331
|
-
return /* @__PURE__ */ (0,
|
|
3400
|
+
return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
|
|
3332
3401
|
"section",
|
|
3333
3402
|
{
|
|
3334
3403
|
"aria-label": "Player controls",
|
|
3335
3404
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3336
3405
|
children: [
|
|
3337
|
-
/* @__PURE__ */ (0,
|
|
3338
|
-
/* @__PURE__ */ (0,
|
|
3339
|
-
/* @__PURE__ */ (0,
|
|
3406
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3407
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree31.formatTime)(safeCurrentTime) }),
|
|
3408
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3340
3409
|
"input",
|
|
3341
3410
|
{
|
|
3342
3411
|
"aria-label": "Seek",
|
|
@@ -3350,11 +3419,11 @@ function PlayerControls() {
|
|
|
3350
3419
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3351
3420
|
}
|
|
3352
3421
|
),
|
|
3353
|
-
/* @__PURE__ */ (0,
|
|
3422
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree31.formatTime)(safeDuration) })
|
|
3354
3423
|
] }),
|
|
3355
|
-
/* @__PURE__ */ (0,
|
|
3356
|
-
/* @__PURE__ */ (0,
|
|
3357
|
-
/* @__PURE__ */ (0,
|
|
3424
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3425
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3426
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3358
3427
|
"button",
|
|
3359
3428
|
{
|
|
3360
3429
|
type: "button",
|
|
@@ -3363,10 +3432,10 @@ function PlayerControls() {
|
|
|
3363
3432
|
disabled: !isReady || !hasPreviousTrack,
|
|
3364
3433
|
onClick: playPreviousTrack,
|
|
3365
3434
|
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",
|
|
3366
|
-
children: /* @__PURE__ */ (0,
|
|
3435
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react15.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3367
3436
|
}
|
|
3368
3437
|
),
|
|
3369
|
-
/* @__PURE__ */ (0,
|
|
3438
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3370
3439
|
"button",
|
|
3371
3440
|
{
|
|
3372
3441
|
type: "button",
|
|
@@ -3375,10 +3444,10 @@ function PlayerControls() {
|
|
|
3375
3444
|
disabled: !isReady,
|
|
3376
3445
|
onClick: handlePlayPauseAction,
|
|
3377
3446
|
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",
|
|
3378
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3447
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react15.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react15.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3379
3448
|
}
|
|
3380
3449
|
),
|
|
3381
|
-
/* @__PURE__ */ (0,
|
|
3450
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3382
3451
|
"button",
|
|
3383
3452
|
{
|
|
3384
3453
|
type: "button",
|
|
@@ -3387,14 +3456,14 @@ function PlayerControls() {
|
|
|
3387
3456
|
disabled: !isReady || !hasNextTrack,
|
|
3388
3457
|
onClick: playNextTrack,
|
|
3389
3458
|
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",
|
|
3390
|
-
children: /* @__PURE__ */ (0,
|
|
3459
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react15.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3391
3460
|
}
|
|
3392
3461
|
)
|
|
3393
3462
|
] }),
|
|
3394
|
-
/* @__PURE__ */ (0,
|
|
3395
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3396
|
-
/* @__PURE__ */ (0,
|
|
3397
|
-
/* @__PURE__ */ (0,
|
|
3463
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3464
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react15.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react15.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3465
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3466
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
3398
3467
|
"input",
|
|
3399
3468
|
{
|
|
3400
3469
|
"aria-label": "Volume",
|
|
@@ -3416,7 +3485,7 @@ function PlayerControls() {
|
|
|
3416
3485
|
}
|
|
3417
3486
|
|
|
3418
3487
|
// src/genre-tree/PlayerPanel.tsx
|
|
3419
|
-
var
|
|
3488
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
3420
3489
|
var PANEL_ID = "player-panel";
|
|
3421
3490
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3422
3491
|
function PlayerPanel({
|
|
@@ -3429,13 +3498,13 @@ function PlayerPanel({
|
|
|
3429
3498
|
renderSongLink
|
|
3430
3499
|
}) {
|
|
3431
3500
|
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3432
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3433
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0,
|
|
3501
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree32.useTrackListSidebarVisibility)();
|
|
3502
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react20.useState)("");
|
|
3434
3503
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3435
|
-
(0,
|
|
3504
|
+
(0, import_react20.useEffect)(() => {
|
|
3436
3505
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3437
3506
|
}, [loadedTrackTitle]);
|
|
3438
|
-
const [uncontrolledExtended, setUncontrolledExtended] = (0,
|
|
3507
|
+
const [uncontrolledExtended, setUncontrolledExtended] = (0, import_react20.useState)(false);
|
|
3439
3508
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3440
3509
|
const setIsExtendRequested = (next) => {
|
|
3441
3510
|
setUncontrolledExtended(next);
|
|
@@ -3456,7 +3525,7 @@ function PlayerPanel({
|
|
|
3456
3525
|
onClick: () => setIsExtendRequested(true),
|
|
3457
3526
|
expanded: false
|
|
3458
3527
|
} : null;
|
|
3459
|
-
return /* @__PURE__ */ (0,
|
|
3528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
3460
3529
|
"div",
|
|
3461
3530
|
{
|
|
3462
3531
|
id: PANEL_ID,
|
|
@@ -3469,7 +3538,7 @@ function PlayerPanel({
|
|
|
3469
3538
|
),
|
|
3470
3539
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3471
3540
|
children: [
|
|
3472
|
-
/* @__PURE__ */ (0,
|
|
3541
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3473
3542
|
import_ui18.PanelEdgeTab,
|
|
3474
3543
|
{
|
|
3475
3544
|
side: "right",
|
|
@@ -3479,7 +3548,7 @@ function PlayerPanel({
|
|
|
3479
3548
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3480
3549
|
}
|
|
3481
3550
|
),
|
|
3482
|
-
/* @__PURE__ */ (0,
|
|
3551
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
|
|
3483
3552
|
"div",
|
|
3484
3553
|
{
|
|
3485
3554
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3487,20 +3556,20 @@ function PlayerPanel({
|
|
|
3487
3556
|
isExtended ? "flex-row" : "flex-col"
|
|
3488
3557
|
),
|
|
3489
3558
|
children: [
|
|
3490
|
-
/* @__PURE__ */ (0,
|
|
3491
|
-
/* @__PURE__ */ (0,
|
|
3492
|
-
/* @__PURE__ */ (0,
|
|
3493
|
-
/* @__PURE__ */ (0,
|
|
3494
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3559
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3560
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3561
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3562
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3563
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3495
3564
|
playerTrackObject.loadError,
|
|
3496
|
-
/* @__PURE__ */ (0,
|
|
3565
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3497
3566
|
void retryPlayback();
|
|
3498
3567
|
}, children: "Retry" })
|
|
3499
3568
|
] })
|
|
3500
3569
|
] }),
|
|
3501
|
-
/* @__PURE__ */ (0,
|
|
3570
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PlayerControls, {})
|
|
3502
3571
|
] }),
|
|
3503
|
-
/* @__PURE__ */ (0,
|
|
3572
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
3504
3573
|
TrackListSidebar,
|
|
3505
3574
|
{
|
|
3506
3575
|
layout: "inline",
|
|
@@ -3526,33 +3595,33 @@ function PlayerPanel({
|
|
|
3526
3595
|
// src/genre-tree/Rating.tsx
|
|
3527
3596
|
var import_uuid = require("uuid");
|
|
3528
3597
|
var import_ai = require("react-icons/ai");
|
|
3529
|
-
var
|
|
3530
|
-
var
|
|
3598
|
+
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3599
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
3531
3600
|
function Rating({ rating, handleChange }) {
|
|
3532
3601
|
const instanceId = (0, import_uuid.v4)();
|
|
3533
|
-
return /* @__PURE__ */ (0,
|
|
3534
|
-
/* @__PURE__ */ (0,
|
|
3602
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3603
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3535
3604
|
"input",
|
|
3536
3605
|
{
|
|
3537
3606
|
id: `star-${instanceId}-${i}`,
|
|
3538
3607
|
className: "w-6 h-6 opacity-0",
|
|
3539
3608
|
type: "radio",
|
|
3540
3609
|
name: "rating",
|
|
3541
|
-
value: i === 0 ?
|
|
3610
|
+
value: i === 0 ? import_genre_tree33.FORM_RATING_NULL_VALUE : i,
|
|
3542
3611
|
checked: rating === (i === 0 ? null : i),
|
|
3543
3612
|
onChange: handleChange
|
|
3544
3613
|
}
|
|
3545
3614
|
),
|
|
3546
|
-
/* @__PURE__ */ (0,
|
|
3547
|
-
/* @__PURE__ */ (0,
|
|
3615
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex", children: [
|
|
3616
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3548
3617
|
import_ai.AiOutlineStar,
|
|
3549
3618
|
{
|
|
3550
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3619
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree33.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3551
3620
|
}
|
|
3552
3621
|
),
|
|
3553
|
-
/* @__PURE__ */ (0,
|
|
3622
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("span", { className: "flex-grow w-8" }),
|
|
3554
3623
|
" "
|
|
3555
|
-
] }) : /* @__PURE__ */ (0,
|
|
3624
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3556
3625
|
import_ai.AiFillStar,
|
|
3557
3626
|
{
|
|
3558
3627
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|