@behindthemusictree/app-kit-ui 34.0.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-A7OWCEIK.js → chunk-JP7PID7L.js} +333 -303
- package/dist/chunk-JP7PID7L.js.map +1 -0
- package/dist/genre-tree/index.cjs +444 -414
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +32 -24
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +439 -409
- 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-A7OWCEIK.js.map +0 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-DKQQLLMY.js";
|
|
6
6
|
|
|
7
7
|
// src/genre-tree/GenreTreeView.tsx
|
|
8
|
-
import { useCallback as useCallback6, useDeferredValue, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef3, useState as
|
|
8
|
+
import { useCallback as useCallback6, useDeferredValue as useDeferredValue2, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef3, useState as useState9 } from "react";
|
|
9
9
|
import { useIsMutating } from "@tanstack/react-query";
|
|
10
10
|
import { Plus as Plus2 } from "lucide-react";
|
|
11
11
|
import { Maximize2, Minimize2 } from "lucide-react";
|
|
@@ -465,6 +465,9 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
465
465
|
] });
|
|
466
466
|
}
|
|
467
467
|
|
|
468
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
469
|
+
import { useDeferredValue, useState as useState3 } from "react";
|
|
470
|
+
|
|
468
471
|
// src/genre-tree/GenreSearch.tsx
|
|
469
472
|
import { ArrowLeft } from "lucide-react";
|
|
470
473
|
import { SEARCH_LEADING_BUTTON_CLASS } from "@behindthemusictree/ui";
|
|
@@ -670,6 +673,44 @@ function GenreSearch({
|
|
|
670
673
|
) });
|
|
671
674
|
}
|
|
672
675
|
|
|
676
|
+
// src/genre-tree/GenreTreeSearch.tsx
|
|
677
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
678
|
+
function GenreTreeSearch({
|
|
679
|
+
artistBaseUrl,
|
|
680
|
+
songBaseUrl,
|
|
681
|
+
onArtistSelect,
|
|
682
|
+
onSongSelect,
|
|
683
|
+
artistSearch,
|
|
684
|
+
songSearch,
|
|
685
|
+
SearchGroups,
|
|
686
|
+
extraGroups = [],
|
|
687
|
+
onQueryChange,
|
|
688
|
+
onFocus,
|
|
689
|
+
...props
|
|
690
|
+
}) {
|
|
691
|
+
const [query, setQuery] = useState3("");
|
|
692
|
+
const [focused, setFocused] = useState3(false);
|
|
693
|
+
const deferredQuery = useDeferredValue(query);
|
|
694
|
+
const artistGroup = useArtistSearchGroup(artistBaseUrl, deferredQuery, onArtistSelect, artistSearch);
|
|
695
|
+
const songGroup = useSongSearchGroup(songBaseUrl, deferredQuery, onSongSelect, songSearch);
|
|
696
|
+
const render = (hostGroups) => /* @__PURE__ */ jsx9(
|
|
697
|
+
GenreSearch,
|
|
698
|
+
{
|
|
699
|
+
...props,
|
|
700
|
+
onQueryChange: (text) => {
|
|
701
|
+
setQuery(text);
|
|
702
|
+
onQueryChange?.(text);
|
|
703
|
+
},
|
|
704
|
+
onFocus: () => {
|
|
705
|
+
setFocused(true);
|
|
706
|
+
onFocus?.();
|
|
707
|
+
},
|
|
708
|
+
extraGroups: [...extraGroups, ...hostGroups, artistGroup, songGroup].filter((group) => group !== void 0)
|
|
709
|
+
}
|
|
710
|
+
);
|
|
711
|
+
return SearchGroups ? /* @__PURE__ */ jsx9(SearchGroups, { query: deferredQuery, focused, children: render }) : render([]);
|
|
712
|
+
}
|
|
713
|
+
|
|
673
714
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
674
715
|
import {
|
|
675
716
|
genreMusicCount,
|
|
@@ -687,18 +728,18 @@ import { Share2 as Share22 } from "lucide-react";
|
|
|
687
728
|
import { usePopup as usePopup3 } from "@behindthemusictree/app-kit/popup";
|
|
688
729
|
|
|
689
730
|
// src/genre-tree/SharePopup.tsx
|
|
690
|
-
import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo5, useState as
|
|
731
|
+
import { useCallback as useCallback5, useEffect as useEffect2, useMemo as useMemo5, useState as useState4 } from "react";
|
|
691
732
|
import { Link2, Share2 } from "lucide-react";
|
|
692
733
|
import { FaWhatsapp } from "react-icons/fa";
|
|
693
734
|
import { SiGmail } from "react-icons/si";
|
|
694
735
|
|
|
695
736
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
696
|
-
import { Fragment as Fragment4, jsx as
|
|
737
|
+
import { Fragment as Fragment4, jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
697
738
|
var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-xs text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
|
|
698
739
|
function Contents({ icon, label }) {
|
|
699
740
|
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
700
|
-
/* @__PURE__ */
|
|
701
|
-
/* @__PURE__ */
|
|
741
|
+
/* @__PURE__ */ jsx10("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
|
|
742
|
+
/* @__PURE__ */ jsx10("span", { className: "max-w-full truncate", children: label })
|
|
702
743
|
] });
|
|
703
744
|
}
|
|
704
745
|
function ShareDestinationButton({
|
|
@@ -710,29 +751,29 @@ function ShareDestinationButton({
|
|
|
710
751
|
ariaLabel
|
|
711
752
|
}) {
|
|
712
753
|
if (href) {
|
|
713
|
-
return /* @__PURE__ */
|
|
754
|
+
return /* @__PURE__ */ jsx10("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
|
|
714
755
|
}
|
|
715
|
-
return /* @__PURE__ */
|
|
756
|
+
return /* @__PURE__ */ jsx10("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx10(Contents, { icon, label }) });
|
|
716
757
|
}
|
|
717
758
|
|
|
718
759
|
// src/genre-tree/SharePreviewCard.tsx
|
|
719
760
|
import { Music, Music2, UserRound } from "lucide-react";
|
|
720
|
-
import { jsx as
|
|
761
|
+
import { jsx as jsx11, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
721
762
|
function SharePreviewCard({ target, url }) {
|
|
722
763
|
const Icon = { genre: Music2, artist: UserRound, song: Music }[target.kind];
|
|
723
764
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
724
|
-
return /* @__PURE__ */
|
|
725
|
-
/* @__PURE__ */
|
|
765
|
+
return /* @__PURE__ */ jsx11("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ jsxs7("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
766
|
+
/* @__PURE__ */ jsx11("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx11(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
726
767
|
/* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
727
|
-
/* @__PURE__ */
|
|
728
|
-
/* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
768
|
+
/* @__PURE__ */ jsx11("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
769
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
770
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
730
771
|
] })
|
|
731
772
|
] }) });
|
|
732
773
|
}
|
|
733
774
|
|
|
734
775
|
// src/genre-tree/SharePopup.tsx
|
|
735
|
-
import { Fragment as Fragment5, jsx as
|
|
776
|
+
import { Fragment as Fragment5, jsx as jsx12, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
736
777
|
function resolveAbsoluteUrl(url) {
|
|
737
778
|
try {
|
|
738
779
|
const parsed = new URL(url, window.location.href);
|
|
@@ -765,11 +806,11 @@ async function copyText(text) {
|
|
|
765
806
|
}
|
|
766
807
|
}
|
|
767
808
|
function SharePopup({ target, url, onClose }) {
|
|
768
|
-
const [absoluteUrl, setAbsoluteUrl] =
|
|
769
|
-
const [copyStatus, setCopyStatus] =
|
|
770
|
-
const [canUseNativeShare, setCanUseNativeShare] =
|
|
771
|
-
const [shareError, setShareError] =
|
|
772
|
-
const [toastId, setToastId] =
|
|
809
|
+
const [absoluteUrl, setAbsoluteUrl] = useState4(url);
|
|
810
|
+
const [copyStatus, setCopyStatus] = useState4("idle");
|
|
811
|
+
const [canUseNativeShare, setCanUseNativeShare] = useState4(false);
|
|
812
|
+
const [shareError, setShareError] = useState4(null);
|
|
813
|
+
const [toastId, setToastId] = useState4(0);
|
|
773
814
|
useEffect2(() => {
|
|
774
815
|
setAbsoluteUrl(resolveAbsoluteUrl(url) ?? "");
|
|
775
816
|
setCopyStatus("idle");
|
|
@@ -808,7 +849,7 @@ function SharePopup({ target, url, onClose }) {
|
|
|
808
849
|
}
|
|
809
850
|
};
|
|
810
851
|
return /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
811
|
-
/* @__PURE__ */
|
|
852
|
+
/* @__PURE__ */ jsx12(
|
|
812
853
|
BasePopup_default,
|
|
813
854
|
{
|
|
814
855
|
title: "Share",
|
|
@@ -824,64 +865,64 @@ function SharePopup({ target, url, onClose }) {
|
|
|
824
865
|
closeButtonClassName: "!h-10 !w-10 !rounded-none !border-0 !bg-transparent !p-0 !text-[var(--color-text)] hover:!bg-transparent hover:!text-[var(--color-text)]",
|
|
825
866
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
826
867
|
children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-4", children: [
|
|
827
|
-
/* @__PURE__ */
|
|
868
|
+
/* @__PURE__ */ jsx12(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
828
869
|
/* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
829
|
-
/* @__PURE__ */
|
|
870
|
+
/* @__PURE__ */ jsx12(
|
|
830
871
|
ShareDestinationButton,
|
|
831
872
|
{
|
|
832
873
|
label: "Copy Link",
|
|
833
|
-
icon: /* @__PURE__ */
|
|
874
|
+
icon: /* @__PURE__ */ jsx12(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
834
875
|
onClick: () => void handleCopy(),
|
|
835
876
|
ariaLabel: "Copy link",
|
|
836
877
|
disabled: !absoluteUrl
|
|
837
878
|
}
|
|
838
879
|
),
|
|
839
|
-
/* @__PURE__ */
|
|
880
|
+
/* @__PURE__ */ jsx12(
|
|
840
881
|
ShareDestinationButton,
|
|
841
882
|
{
|
|
842
883
|
label: "WhatsApp",
|
|
843
|
-
icon: /* @__PURE__ */
|
|
884
|
+
icon: /* @__PURE__ */ jsx12(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
844
885
|
href: destinationUrls?.whatsapp,
|
|
845
886
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
846
887
|
disabled: !absoluteUrl
|
|
847
888
|
}
|
|
848
889
|
),
|
|
849
|
-
/* @__PURE__ */
|
|
890
|
+
/* @__PURE__ */ jsx12(
|
|
850
891
|
ShareDestinationButton,
|
|
851
892
|
{
|
|
852
893
|
label: "Gmail",
|
|
853
|
-
icon: /* @__PURE__ */
|
|
894
|
+
icon: /* @__PURE__ */ jsx12(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
854
895
|
href: destinationUrls?.gmail,
|
|
855
896
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
856
897
|
disabled: !absoluteUrl
|
|
857
898
|
}
|
|
858
899
|
),
|
|
859
|
-
canUseNativeShare && /* @__PURE__ */
|
|
900
|
+
canUseNativeShare && /* @__PURE__ */ jsx12(
|
|
860
901
|
ShareDestinationButton,
|
|
861
902
|
{
|
|
862
903
|
label: "More",
|
|
863
|
-
icon: /* @__PURE__ */
|
|
904
|
+
icon: /* @__PURE__ */ jsx12(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
864
905
|
onClick: () => void handleNativeShare(),
|
|
865
906
|
ariaLabel: "More sharing options",
|
|
866
907
|
disabled: !absoluteUrl
|
|
867
908
|
}
|
|
868
909
|
)
|
|
869
910
|
] }),
|
|
870
|
-
copyStatus === "failed" && /* @__PURE__ */
|
|
871
|
-
shareError && /* @__PURE__ */
|
|
911
|
+
copyStatus === "failed" && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
912
|
+
shareError && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
872
913
|
] })
|
|
873
914
|
}
|
|
874
915
|
),
|
|
875
|
-
toastId > 0 && /* @__PURE__ */
|
|
916
|
+
toastId > 0 && /* @__PURE__ */ jsx12(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
876
917
|
] });
|
|
877
918
|
}
|
|
878
919
|
|
|
879
920
|
// src/genre-tree/ShareActionButton.tsx
|
|
880
|
-
import { jsx as
|
|
921
|
+
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
881
922
|
function ShareActionButton({ target, url }) {
|
|
882
923
|
const { showPopup, hidePopup } = usePopup3();
|
|
883
924
|
const openSharePopup = () => {
|
|
884
|
-
showPopup(/* @__PURE__ */
|
|
925
|
+
showPopup(/* @__PURE__ */ jsx13(SharePopup, { target, url, onClose: hidePopup }));
|
|
885
926
|
};
|
|
886
927
|
return /* @__PURE__ */ jsxs9(
|
|
887
928
|
"button",
|
|
@@ -892,7 +933,7 @@ function ShareActionButton({ target, url }) {
|
|
|
892
933
|
"aria-label": `Share ${target.name}`,
|
|
893
934
|
title: `Share ${target.name}`,
|
|
894
935
|
children: [
|
|
895
|
-
/* @__PURE__ */
|
|
936
|
+
/* @__PURE__ */ jsx13("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: /* @__PURE__ */ jsx13(Share22, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
896
937
|
"Share"
|
|
897
938
|
]
|
|
898
939
|
}
|
|
@@ -900,7 +941,7 @@ function ShareActionButton({ target, url }) {
|
|
|
900
941
|
}
|
|
901
942
|
|
|
902
943
|
// src/genre-tree/GenreActionBand.tsx
|
|
903
|
-
import { jsx as
|
|
944
|
+
import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
904
945
|
function GenreActionBand({
|
|
905
946
|
node,
|
|
906
947
|
onPlayPause,
|
|
@@ -922,7 +963,7 @@ function GenreActionBand({
|
|
|
922
963
|
items.push({
|
|
923
964
|
key: "play-pause",
|
|
924
965
|
label: isPlaying ? "Pause" : "Play",
|
|
925
|
-
icon: isPlaying ? /* @__PURE__ */
|
|
966
|
+
icon: isPlaying ? /* @__PURE__ */ jsx14(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx14(Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
926
967
|
onClick: onPlayPause
|
|
927
968
|
});
|
|
928
969
|
}
|
|
@@ -930,24 +971,24 @@ function GenreActionBand({
|
|
|
930
971
|
items.push({
|
|
931
972
|
key: "zoom-in",
|
|
932
973
|
label: "Focus",
|
|
933
|
-
icon: /* @__PURE__ */
|
|
974
|
+
icon: /* @__PURE__ */ jsx14(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
934
975
|
onClick: onZoomIn
|
|
935
976
|
});
|
|
936
977
|
}
|
|
937
978
|
if (onAddChild) {
|
|
938
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */
|
|
979
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx14(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
939
980
|
}
|
|
940
981
|
if (onRename) {
|
|
941
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */
|
|
982
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx14(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
942
983
|
}
|
|
943
984
|
if (onChangeParent) {
|
|
944
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */
|
|
985
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx14(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
945
986
|
}
|
|
946
987
|
if (onToggleSide) {
|
|
947
988
|
adminItems.push({
|
|
948
989
|
key: "toggle-side",
|
|
949
990
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
950
|
-
icon: /* @__PURE__ */
|
|
991
|
+
icon: /* @__PURE__ */ jsx14(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
951
992
|
onClick: onToggleSide,
|
|
952
993
|
disabled: !!toggleSideDisabledReason,
|
|
953
994
|
title: toggleSideDisabledReason
|
|
@@ -972,7 +1013,7 @@ function GenreActionBand({
|
|
|
972
1013
|
onClick: item.onClick,
|
|
973
1014
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none disabled:opacity-50",
|
|
974
1015
|
children: [
|
|
975
|
-
/* @__PURE__ */
|
|
1016
|
+
/* @__PURE__ */ jsx14("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
|
|
976
1017
|
item.label
|
|
977
1018
|
]
|
|
978
1019
|
},
|
|
@@ -981,7 +1022,7 @@ function GenreActionBand({
|
|
|
981
1022
|
return /* @__PURE__ */ jsxs10("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
982
1023
|
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
983
1024
|
renderItems(items),
|
|
984
|
-
shareTarget && shareUrl && /* @__PURE__ */
|
|
1025
|
+
shareTarget && shareUrl && /* @__PURE__ */ jsx14(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
985
1026
|
] }),
|
|
986
1027
|
adminItems.length > 0 && /* @__PURE__ */ jsxs10(
|
|
987
1028
|
"section",
|
|
@@ -989,8 +1030,8 @@ function GenreActionBand({
|
|
|
989
1030
|
"aria-label": "Admin actions",
|
|
990
1031
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
991
1032
|
children: [
|
|
992
|
-
/* @__PURE__ */
|
|
993
|
-
/* @__PURE__ */
|
|
1033
|
+
/* @__PURE__ */ jsx14("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1034
|
+
/* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
994
1035
|
]
|
|
995
1036
|
}
|
|
996
1037
|
)
|
|
@@ -1002,20 +1043,20 @@ import { PanelSection as PanelSection2, Skeleton as Skeleton2 } from "@behindthe
|
|
|
1002
1043
|
import { useFetchGenreOverview } from "@behindthemusictree/app-kit/genre-tree";
|
|
1003
1044
|
|
|
1004
1045
|
// src/genre-tree/ClampedText.tsx
|
|
1005
|
-
import { useLayoutEffect, useRef as useRef2, useState as
|
|
1006
|
-
import { Fragment as Fragment6, jsx as
|
|
1046
|
+
import { useLayoutEffect, useRef as useRef2, useState as useState5 } from "react";
|
|
1047
|
+
import { Fragment as Fragment6, jsx as jsx15, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1007
1048
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1008
1049
|
const ref = useRef2(null);
|
|
1009
|
-
const [expanded, setExpanded] =
|
|
1010
|
-
const [overflows, setOverflows] =
|
|
1050
|
+
const [expanded, setExpanded] = useState5(false);
|
|
1051
|
+
const [overflows, setOverflows] = useState5(false);
|
|
1011
1052
|
useLayoutEffect(() => {
|
|
1012
1053
|
const element = ref.current;
|
|
1013
1054
|
if (!element || expanded) return;
|
|
1014
1055
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1015
1056
|
}, [children, expanded]);
|
|
1016
1057
|
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1017
|
-
/* @__PURE__ */
|
|
1018
|
-
(overflows || expanded) && /* @__PURE__ */
|
|
1058
|
+
/* @__PURE__ */ jsx15("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1059
|
+
(overflows || expanded) && /* @__PURE__ */ jsx15(
|
|
1019
1060
|
"button",
|
|
1020
1061
|
{
|
|
1021
1062
|
type: "button",
|
|
@@ -1029,7 +1070,7 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1029
1070
|
}
|
|
1030
1071
|
|
|
1031
1072
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1032
|
-
import { useEffect as useEffect3, useState as
|
|
1073
|
+
import { useEffect as useEffect3, useState as useState6 } from "react";
|
|
1033
1074
|
import { useInfiniteQuery } from "@tanstack/react-query";
|
|
1034
1075
|
import { z } from "zod";
|
|
1035
1076
|
import { FaPlay, FaPause } from "react-icons/fa";
|
|
@@ -1050,15 +1091,15 @@ import { useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre
|
|
|
1050
1091
|
|
|
1051
1092
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1052
1093
|
import { musicBrainzRecordingUrl } from "@behindthemusictree/app-kit/genre-tree";
|
|
1053
|
-
import { Fragment as Fragment7, jsx as
|
|
1094
|
+
import { Fragment as Fragment7, jsx as jsx16 } from "react/jsx-runtime";
|
|
1054
1095
|
function MusicBrainzRecordingLink({
|
|
1055
1096
|
song,
|
|
1056
1097
|
children,
|
|
1057
1098
|
className: className2 = ""
|
|
1058
1099
|
}) {
|
|
1059
1100
|
const url = musicBrainzRecordingUrl(song);
|
|
1060
|
-
if (!url) return /* @__PURE__ */
|
|
1061
|
-
return /* @__PURE__ */
|
|
1101
|
+
if (!url) return /* @__PURE__ */ jsx16(Fragment7, { children });
|
|
1102
|
+
return /* @__PURE__ */ jsx16(
|
|
1062
1103
|
"a",
|
|
1063
1104
|
{
|
|
1064
1105
|
href: url,
|
|
@@ -1075,7 +1116,7 @@ function MusicBrainzRecordingLink({
|
|
|
1075
1116
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1076
1117
|
import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
|
|
1077
1118
|
import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
|
|
1078
|
-
import { jsx as
|
|
1119
|
+
import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1079
1120
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1080
1121
|
var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
|
|
1081
1122
|
overallTotal: z.number().nullable(),
|
|
@@ -1131,7 +1172,7 @@ function GenrePlaylistTracks({
|
|
|
1131
1172
|
getNextPageParam: (lastPage) => lastPage.nextPage ?? void 0,
|
|
1132
1173
|
enabled: playback.ready && (scope !== "me" || sessionRestored && !!session?.accessToken)
|
|
1133
1174
|
});
|
|
1134
|
-
const [sentinel, setSentinel] =
|
|
1175
|
+
const [sentinel, setSentinel] = useState6(null);
|
|
1135
1176
|
useEffect3(() => {
|
|
1136
1177
|
if (!hasNextPage || isFetchingNextPage || !sentinel) return;
|
|
1137
1178
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -1154,27 +1195,27 @@ function GenrePlaylistTracks({
|
|
|
1154
1195
|
}) : scope === "reference" ? playNewTrackListFromGenreCatalogue({ uuid: genreUuid, name: genrePlaylist.name }, scope, { startSongUuid, ...selection && { selection } }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1155
1196
|
started.catch((error) => {
|
|
1156
1197
|
console.error("Failed to play genre playlist:", error);
|
|
1157
|
-
showPopup(/* @__PURE__ */
|
|
1198
|
+
showPopup(/* @__PURE__ */ jsx17(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
|
|
1158
1199
|
});
|
|
1159
1200
|
};
|
|
1160
|
-
return /* @__PURE__ */
|
|
1201
|
+
return /* @__PURE__ */ jsx17(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
|
|
1161
1202
|
renderTracksToolbar?.(),
|
|
1162
|
-
isPending ? /* @__PURE__ */
|
|
1203
|
+
isPending ? /* @__PURE__ */ jsx17(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
|
|
1163
1204
|
"Couldn't load ",
|
|
1164
1205
|
trackNoun.plural,
|
|
1165
1206
|
".",
|
|
1166
1207
|
" ",
|
|
1167
|
-
/* @__PURE__ */
|
|
1168
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */
|
|
1208
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1209
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ jsx17("p", { children: "\u2014" }) : /* @__PURE__ */ jsxs12("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genrePlaylist.name} ${trackNoun.plural}`, children: [
|
|
1169
1210
|
tracks.map((track, index) => {
|
|
1170
1211
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1171
1212
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1172
1213
|
const isPlaying = isCurrent && playState === PlayStates.PLAYING;
|
|
1173
1214
|
const text = /* @__PURE__ */ jsxs12("span", { className: TEXT_CELL, children: [
|
|
1174
|
-
/* @__PURE__ */
|
|
1175
|
-
track.artists?.length ? /* @__PURE__ */
|
|
1176
|
-
/* @__PURE__ */
|
|
1177
|
-
track.album && /* @__PURE__ */
|
|
1215
|
+
/* @__PURE__ */ jsx17("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : isSong(track) ? /* @__PURE__ */ jsx17(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1216
|
+
track.artists?.length ? /* @__PURE__ */ jsx17("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1217
|
+
/* @__PURE__ */ jsx17("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx17(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1218
|
+
track.album && /* @__PURE__ */ jsx17("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1178
1219
|
] });
|
|
1179
1220
|
const leading = renderSongLeading?.(track, index);
|
|
1180
1221
|
const actions = renderSongActions?.(track);
|
|
@@ -1182,7 +1223,7 @@ function GenrePlaylistTracks({
|
|
|
1182
1223
|
if (isUnplayable) {
|
|
1183
1224
|
return /* @__PURE__ */ jsxs12("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1184
1225
|
leading,
|
|
1185
|
-
/* @__PURE__ */
|
|
1226
|
+
/* @__PURE__ */ jsx17(
|
|
1186
1227
|
"button",
|
|
1187
1228
|
{
|
|
1188
1229
|
type: "button",
|
|
@@ -1190,7 +1231,7 @@ function GenrePlaylistTracks({
|
|
|
1190
1231
|
"aria-disabled": "true",
|
|
1191
1232
|
"aria-label": label,
|
|
1192
1233
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1193
|
-
children: /* @__PURE__ */
|
|
1234
|
+
children: /* @__PURE__ */ jsx17(FaPlay, { size: 10 })
|
|
1194
1235
|
}
|
|
1195
1236
|
),
|
|
1196
1237
|
text,
|
|
@@ -1203,14 +1244,14 @@ function GenrePlaylistTracks({
|
|
|
1203
1244
|
className: `group flex min-h-8 items-center gap-1 rounded px-1 hover:bg-[var(--color-neutral-100)] ${isCurrent ? "font-medium text-[var(--color-primary)]" : ""}`,
|
|
1204
1245
|
children: [
|
|
1205
1246
|
leading,
|
|
1206
|
-
/* @__PURE__ */
|
|
1247
|
+
/* @__PURE__ */ jsx17(
|
|
1207
1248
|
"button",
|
|
1208
1249
|
{
|
|
1209
1250
|
type: "button",
|
|
1210
1251
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1211
1252
|
className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1212
1253
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1213
|
-
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */
|
|
1254
|
+
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx17(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx17(FaPause, { size: 10 }) : /* @__PURE__ */ jsx17(FaPlay, { size: 10 })
|
|
1214
1255
|
}
|
|
1215
1256
|
),
|
|
1216
1257
|
text,
|
|
@@ -1220,7 +1261,7 @@ function GenrePlaylistTracks({
|
|
|
1220
1261
|
track.uuid
|
|
1221
1262
|
);
|
|
1222
1263
|
}),
|
|
1223
|
-
hasNextPage && /* @__PURE__ */
|
|
1264
|
+
hasNextPage && /* @__PURE__ */ jsx17("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1224
1265
|
] })
|
|
1225
1266
|
] }) });
|
|
1226
1267
|
}
|
|
@@ -1228,7 +1269,7 @@ function GenrePlaylistTracks({
|
|
|
1228
1269
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1229
1270
|
import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
|
|
1230
1271
|
import { SiWikidata, SiWikipedia } from "react-icons/si";
|
|
1231
|
-
import { Fragment as Fragment8, jsx as
|
|
1272
|
+
import { Fragment as Fragment8, jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1232
1273
|
function GenreDetailExtras({
|
|
1233
1274
|
genreUuid,
|
|
1234
1275
|
genrePlaylist,
|
|
@@ -1254,15 +1295,15 @@ function GenreDetailExtras({
|
|
|
1254
1295
|
);
|
|
1255
1296
|
if (isPending) {
|
|
1256
1297
|
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1257
|
-
/* @__PURE__ */
|
|
1298
|
+
/* @__PURE__ */ jsx18(PanelSection2, { title: "Summary", children: /* @__PURE__ */ jsx18("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ jsx18(Skeleton2, { className: "h-4 w-3/4" }) }) }),
|
|
1258
1299
|
family
|
|
1259
1300
|
] });
|
|
1260
1301
|
}
|
|
1261
|
-
if (!overview) return /* @__PURE__ */
|
|
1302
|
+
if (!overview) return /* @__PURE__ */ jsx18(Fragment8, { children: family });
|
|
1262
1303
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1263
1304
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1264
1305
|
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1265
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */
|
|
1306
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx18(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1266
1307
|
summarySourceUrl && /* @__PURE__ */ jsxs13(
|
|
1267
1308
|
"a",
|
|
1268
1309
|
{
|
|
@@ -1271,7 +1312,7 @@ function GenreDetailExtras({
|
|
|
1271
1312
|
rel: "noopener noreferrer",
|
|
1272
1313
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1273
1314
|
children: [
|
|
1274
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsx18(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1275
1316
|
"Wikipedia"
|
|
1276
1317
|
]
|
|
1277
1318
|
}
|
|
@@ -1284,17 +1325,17 @@ function GenreDetailExtras({
|
|
|
1284
1325
|
rel: "noopener noreferrer",
|
|
1285
1326
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1286
1327
|
children: [
|
|
1287
|
-
/* @__PURE__ */
|
|
1328
|
+
/* @__PURE__ */ jsx18(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1288
1329
|
"Wikidata"
|
|
1289
1330
|
]
|
|
1290
1331
|
}
|
|
1291
1332
|
)
|
|
1292
1333
|
] }) }),
|
|
1293
|
-
/* @__PURE__ */
|
|
1334
|
+
/* @__PURE__ */ jsx18(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx18(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1294
1335
|
family,
|
|
1295
1336
|
renderGenreDetailExtras?.(overview),
|
|
1296
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */
|
|
1297
|
-
/* @__PURE__ */
|
|
1337
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx18(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx18("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx18("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx18("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1338
|
+
/* @__PURE__ */ jsx18(
|
|
1298
1339
|
GenrePlaylistTracks,
|
|
1299
1340
|
{
|
|
1300
1341
|
genreUuid,
|
|
@@ -1311,7 +1352,7 @@ function GenreDetailExtras({
|
|
|
1311
1352
|
}
|
|
1312
1353
|
|
|
1313
1354
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1314
|
-
import { Fragment as Fragment9, jsx as
|
|
1355
|
+
import { Fragment as Fragment9, jsx as jsx19, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1315
1356
|
import { createElement } from "react";
|
|
1316
1357
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1317
1358
|
const { isPlaying, setIsPlaying } = usePlayer5();
|
|
@@ -1325,7 +1366,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1325
1366
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1326
1367
|
});
|
|
1327
1368
|
};
|
|
1328
|
-
return /* @__PURE__ */
|
|
1369
|
+
return /* @__PURE__ */ jsx19(
|
|
1329
1370
|
GenreActionBand,
|
|
1330
1371
|
{
|
|
1331
1372
|
...bandProps,
|
|
@@ -1356,7 +1397,7 @@ function GenreInfoPanelBody({
|
|
|
1356
1397
|
};
|
|
1357
1398
|
return /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1358
1399
|
toolbar,
|
|
1359
|
-
/* @__PURE__ */
|
|
1400
|
+
/* @__PURE__ */ jsx19(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1360
1401
|
summary && catalogue && /* @__PURE__ */ jsxs14("div", { className: "px-3.5 py-3 text-sm text-[var(--color-text)]", children: [
|
|
1361
1402
|
/* @__PURE__ */ jsxs14("p", { children: [
|
|
1362
1403
|
"Catalogue ",
|
|
@@ -1394,7 +1435,7 @@ import {
|
|
|
1394
1435
|
getReparentCandidates,
|
|
1395
1436
|
useUpdateGenre
|
|
1396
1437
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1397
|
-
import { jsx as
|
|
1438
|
+
import { jsx as jsx20, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1398
1439
|
function ChangeParentPopup({
|
|
1399
1440
|
genrePlaylist,
|
|
1400
1441
|
genrePlaylists,
|
|
@@ -1434,7 +1475,7 @@ function ChangeParentPopup({
|
|
|
1434
1475
|
"Current parent: ",
|
|
1435
1476
|
genrePlaylist.parent?.name ?? "none"
|
|
1436
1477
|
] }),
|
|
1437
|
-
/* @__PURE__ */
|
|
1478
|
+
/* @__PURE__ */ jsx20("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx20(
|
|
1438
1479
|
GenreSearch,
|
|
1439
1480
|
{
|
|
1440
1481
|
genrePlaylists: candidates,
|
|
@@ -1442,7 +1483,7 @@ function ChangeParentPopup({
|
|
|
1442
1483
|
placeholder: "Search a new parent\u2026"
|
|
1443
1484
|
}
|
|
1444
1485
|
) }),
|
|
1445
|
-
formErrors.length > 0 && /* @__PURE__ */
|
|
1486
|
+
formErrors.length > 0 && /* @__PURE__ */ jsx20("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1446
1487
|
] });
|
|
1447
1488
|
}
|
|
1448
1489
|
|
|
@@ -1466,7 +1507,7 @@ import {
|
|
|
1466
1507
|
SiYoutube
|
|
1467
1508
|
} from "react-icons/si";
|
|
1468
1509
|
import { Chip } from "@behindthemusictree/ui";
|
|
1469
|
-
import { jsx as
|
|
1510
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1470
1511
|
var LINK_KINDS = {
|
|
1471
1512
|
official: { label: "Official website", Icon: Globe },
|
|
1472
1513
|
wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
|
|
@@ -1503,7 +1544,7 @@ function artistLinks(artist) {
|
|
|
1503
1544
|
return links;
|
|
1504
1545
|
}
|
|
1505
1546
|
function ArtistLinks({ links }) {
|
|
1506
|
-
return /* @__PURE__ */
|
|
1547
|
+
return /* @__PURE__ */ jsx21("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx21("li", { children: /* @__PURE__ */ jsx21(Chip, { href: url, icon: /* @__PURE__ */ jsx21(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1507
1548
|
}
|
|
1508
1549
|
|
|
1509
1550
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1514,7 +1555,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1514
1555
|
}
|
|
1515
1556
|
|
|
1516
1557
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1517
|
-
import { jsx as
|
|
1558
|
+
import { jsx as jsx22 } from "react/jsx-runtime";
|
|
1518
1559
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1519
1560
|
function ArtistHeader({ artist }) {
|
|
1520
1561
|
const fields = [];
|
|
@@ -1524,35 +1565,35 @@ function ArtistHeader({ artist }) {
|
|
|
1524
1565
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1525
1566
|
if (years) fields.push({ label: "Active", value: years });
|
|
1526
1567
|
const links = artistLinks(artist);
|
|
1527
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */
|
|
1568
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx22(ArtistLinks, { links }) });
|
|
1528
1569
|
if (fields.length === 0) return null;
|
|
1529
|
-
return /* @__PURE__ */
|
|
1570
|
+
return /* @__PURE__ */ jsx22(FieldList, { items: fields });
|
|
1530
1571
|
}
|
|
1531
1572
|
|
|
1532
1573
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1533
|
-
import { jsx as
|
|
1574
|
+
import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1534
1575
|
function ExternalLink({ href, children }) {
|
|
1535
|
-
return /* @__PURE__ */
|
|
1576
|
+
return /* @__PURE__ */ jsx23("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1536
1577
|
}
|
|
1537
1578
|
function ArtistPhoto({ image, name }) {
|
|
1538
1579
|
return /* @__PURE__ */ jsxs16("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1539
|
-
/* @__PURE__ */
|
|
1580
|
+
/* @__PURE__ */ jsx23("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1540
1581
|
/* @__PURE__ */ jsxs16("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1541
1582
|
"Photo: ",
|
|
1542
|
-
/* @__PURE__ */
|
|
1583
|
+
/* @__PURE__ */ jsx23(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1543
1584
|
",",
|
|
1544
1585
|
" ",
|
|
1545
|
-
image.licenseUrl ? /* @__PURE__ */
|
|
1586
|
+
image.licenseUrl ? /* @__PURE__ */ jsx23(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1546
1587
|
] })
|
|
1547
1588
|
] });
|
|
1548
1589
|
}
|
|
1549
1590
|
|
|
1550
1591
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1551
1592
|
import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
|
|
1552
|
-
import { jsx as
|
|
1593
|
+
import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1553
1594
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1554
1595
|
if (memberships.length === 0) return null;
|
|
1555
|
-
return /* @__PURE__ */
|
|
1596
|
+
return /* @__PURE__ */ jsx24(PanelGroup, { title, children: /* @__PURE__ */ jsx24("ul", { children: memberships.map((membership, index) => {
|
|
1556
1597
|
const details = [
|
|
1557
1598
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1558
1599
|
...membership.roles
|
|
@@ -1561,7 +1602,7 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1561
1602
|
return (
|
|
1562
1603
|
// A member can have several periods in the same group.
|
|
1563
1604
|
/* @__PURE__ */ jsxs17("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1564
|
-
/* @__PURE__ */
|
|
1605
|
+
/* @__PURE__ */ jsx24("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1565
1606
|
detailsText && /* @__PURE__ */ jsxs17("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1566
1607
|
" \u2014 ",
|
|
1567
1608
|
detailsText
|
|
@@ -1574,8 +1615,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1574
1615
|
if (memberships.length === 0) return null;
|
|
1575
1616
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1576
1617
|
return /* @__PURE__ */ jsxs17(PanelSection3, { title, children: [
|
|
1577
|
-
/* @__PURE__ */
|
|
1578
|
-
/* @__PURE__ */
|
|
1618
|
+
/* @__PURE__ */ jsx24(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1619
|
+
/* @__PURE__ */ jsx24(
|
|
1579
1620
|
MembershipList,
|
|
1580
1621
|
{
|
|
1581
1622
|
title: "Past",
|
|
@@ -1588,7 +1629,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1588
1629
|
|
|
1589
1630
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1590
1631
|
import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
|
|
1591
|
-
import { jsx as
|
|
1632
|
+
import { jsx as jsx25, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1592
1633
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1593
1634
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1594
1635
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1608,18 +1649,18 @@ function byYear(a, b) {
|
|
|
1608
1649
|
function ArtistDiscography({ albums }) {
|
|
1609
1650
|
if (albums.length === 0) return null;
|
|
1610
1651
|
const sorted = [...albums].sort(byYear);
|
|
1611
|
-
return /* @__PURE__ */
|
|
1652
|
+
return /* @__PURE__ */ jsx25(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
|
|
1612
1653
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1613
1654
|
if (groupAlbums.length === 0) return null;
|
|
1614
|
-
return /* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1655
|
+
return /* @__PURE__ */ jsx25(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx25("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs18("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1656
|
+
/* @__PURE__ */ jsx25("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1657
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1617
1658
|
] }, album.uuid)) }) }, group);
|
|
1618
1659
|
}) });
|
|
1619
1660
|
}
|
|
1620
1661
|
|
|
1621
1662
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1622
|
-
import { useState as
|
|
1663
|
+
import { useState as useState7 } from "react";
|
|
1623
1664
|
import { keepPreviousData } from "@tanstack/react-query";
|
|
1624
1665
|
import { Play as Play2 } from "lucide-react";
|
|
1625
1666
|
import { Pagination, PanelSection as PanelSection5, Skeleton as Skeleton3 } from "@behindthemusictree/ui";
|
|
@@ -1640,7 +1681,7 @@ import { useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre
|
|
|
1640
1681
|
import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
|
|
1641
1682
|
import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
|
|
1642
1683
|
import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
|
|
1643
|
-
import { jsx as
|
|
1684
|
+
import { jsx as jsx26, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1644
1685
|
function TrackPositionPlayPause({
|
|
1645
1686
|
position,
|
|
1646
1687
|
uuid,
|
|
@@ -1661,7 +1702,7 @@ function TrackPositionPlayPause({
|
|
|
1661
1702
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1662
1703
|
onClick: handlePlayPauseClick,
|
|
1663
1704
|
children: [
|
|
1664
|
-
/* @__PURE__ */
|
|
1705
|
+
/* @__PURE__ */ jsx26("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs19(
|
|
1665
1706
|
"span",
|
|
1666
1707
|
{
|
|
1667
1708
|
className: "track-position-play-pause__bars",
|
|
@@ -1669,18 +1710,18 @@ function TrackPositionPlayPause({
|
|
|
1669
1710
|
role: "img",
|
|
1670
1711
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1671
1712
|
children: [
|
|
1672
|
-
/* @__PURE__ */
|
|
1673
|
-
/* @__PURE__ */
|
|
1674
|
-
/* @__PURE__ */
|
|
1713
|
+
/* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__bar" }),
|
|
1714
|
+
/* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__bar" }),
|
|
1715
|
+
/* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__bar" })
|
|
1675
1716
|
]
|
|
1676
1717
|
}
|
|
1677
1718
|
) : position }),
|
|
1678
|
-
/* @__PURE__ */
|
|
1719
|
+
/* @__PURE__ */ jsx26(
|
|
1679
1720
|
"div",
|
|
1680
1721
|
{
|
|
1681
1722
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1682
1723
|
"aria-hidden": "true",
|
|
1683
|
-
children: isAwaitingPlayback ? /* @__PURE__ */
|
|
1724
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ jsx26("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx26(FaPause2, {}) : /* @__PURE__ */ jsx26(FaPlay2, {})
|
|
1684
1725
|
}
|
|
1685
1726
|
)
|
|
1686
1727
|
]
|
|
@@ -1691,7 +1732,7 @@ function TrackPositionPlayPause({
|
|
|
1691
1732
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1692
1733
|
import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1693
1734
|
import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1694
|
-
import { jsx as
|
|
1735
|
+
import { jsx as jsx27, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1695
1736
|
function TrackItem({
|
|
1696
1737
|
track,
|
|
1697
1738
|
position,
|
|
@@ -1720,7 +1761,7 @@ function TrackItem({
|
|
|
1720
1761
|
{
|
|
1721
1762
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1722
1763
|
children: [
|
|
1723
|
-
isUnplayable ? /* @__PURE__ */
|
|
1764
|
+
isUnplayable ? /* @__PURE__ */ jsx27(
|
|
1724
1765
|
"div",
|
|
1725
1766
|
{
|
|
1726
1767
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1728,7 +1769,7 @@ function TrackItem({
|
|
|
1728
1769
|
"aria-disabled": "true",
|
|
1729
1770
|
children: position
|
|
1730
1771
|
}
|
|
1731
|
-
) : /* @__PURE__ */
|
|
1772
|
+
) : /* @__PURE__ */ jsx27(
|
|
1732
1773
|
TrackPositionPlayPause,
|
|
1733
1774
|
{
|
|
1734
1775
|
position,
|
|
@@ -1738,11 +1779,11 @@ function TrackItem({
|
|
|
1738
1779
|
}
|
|
1739
1780
|
),
|
|
1740
1781
|
/* @__PURE__ */ jsxs20("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1741
|
-
/* @__PURE__ */
|
|
1742
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */
|
|
1782
|
+
/* @__PURE__ */ jsx27("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx27("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx27(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1783
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx27("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx27(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1743
1784
|
] }),
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
renderDuration && /* @__PURE__ */
|
|
1785
|
+
/* @__PURE__ */ jsx27("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx27("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1786
|
+
renderDuration && /* @__PURE__ */ jsx27(
|
|
1746
1787
|
"div",
|
|
1747
1788
|
{
|
|
1748
1789
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1750,22 +1791,22 @@ function TrackItem({
|
|
|
1750
1791
|
children: renderDuration(track)
|
|
1751
1792
|
}
|
|
1752
1793
|
),
|
|
1753
|
-
renderActions && /* @__PURE__ */
|
|
1794
|
+
renderActions && /* @__PURE__ */ jsx27("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1754
1795
|
]
|
|
1755
1796
|
}
|
|
1756
1797
|
);
|
|
1757
1798
|
}
|
|
1758
1799
|
|
|
1759
1800
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1760
|
-
import { Fragment as Fragment10, jsx as
|
|
1801
|
+
import { Fragment as Fragment10, jsx as jsx28, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1761
1802
|
var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
|
|
1762
1803
|
var SORTS = [
|
|
1763
1804
|
{ ordering: "title", label: "Title" },
|
|
1764
1805
|
{ ordering: "-albumYear", label: "Year" }
|
|
1765
1806
|
];
|
|
1766
1807
|
function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLink }) {
|
|
1767
|
-
const [page, setPage] =
|
|
1768
|
-
const [ordering, setOrdering] =
|
|
1808
|
+
const [page, setPage] = useState7(1);
|
|
1809
|
+
const [ordering, setOrdering] = useState7("title");
|
|
1769
1810
|
const { fetch } = useFetchWrapper2(getBackendBaseUrl);
|
|
1770
1811
|
const { playNewTrackListFromArtist } = useTrackList4();
|
|
1771
1812
|
const { playerTrackObject } = usePlayer8();
|
|
@@ -1790,7 +1831,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1790
1831
|
event.stopPropagation();
|
|
1791
1832
|
void play(song.uuid);
|
|
1792
1833
|
};
|
|
1793
|
-
return /* @__PURE__ */
|
|
1834
|
+
return /* @__PURE__ */ jsx28(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx28(Skeleton3, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx28("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx28("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs21(Fragment10, { children: [
|
|
1794
1835
|
/* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1795
1836
|
/* @__PURE__ */ jsxs21(
|
|
1796
1837
|
"button",
|
|
@@ -1799,12 +1840,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1799
1840
|
onClick: () => void play(),
|
|
1800
1841
|
className: "flex w-fit items-center gap-1.5 px-3 py-1 text-xs font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
|
|
1801
1842
|
children: [
|
|
1802
|
-
/* @__PURE__ */
|
|
1843
|
+
/* @__PURE__ */ jsx28(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1803
1844
|
"Play all"
|
|
1804
1845
|
]
|
|
1805
1846
|
}
|
|
1806
1847
|
),
|
|
1807
|
-
/* @__PURE__ */
|
|
1848
|
+
/* @__PURE__ */ jsx28("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx28(
|
|
1808
1849
|
"button",
|
|
1809
1850
|
{
|
|
1810
1851
|
type: "button",
|
|
@@ -1819,7 +1860,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1819
1860
|
sort.ordering
|
|
1820
1861
|
)) })
|
|
1821
1862
|
] }),
|
|
1822
|
-
/* @__PURE__ */
|
|
1863
|
+
/* @__PURE__ */ jsx28("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx28("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx28(
|
|
1823
1864
|
TrackItem,
|
|
1824
1865
|
{
|
|
1825
1866
|
track: song,
|
|
@@ -1828,12 +1869,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1828
1869
|
renderSongLink
|
|
1829
1870
|
}
|
|
1830
1871
|
) }, song.uuid)) }),
|
|
1831
|
-
/* @__PURE__ */
|
|
1872
|
+
/* @__PURE__ */ jsx28(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1832
1873
|
] }) });
|
|
1833
1874
|
}
|
|
1834
1875
|
|
|
1835
1876
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1836
|
-
import { Fragment as Fragment11, jsx as
|
|
1877
|
+
import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1837
1878
|
function useArtistPanel({
|
|
1838
1879
|
uuid,
|
|
1839
1880
|
scope,
|
|
@@ -1855,25 +1896,25 @@ function useArtistPanel({
|
|
|
1855
1896
|
...panel,
|
|
1856
1897
|
title: "Artist",
|
|
1857
1898
|
content: /* @__PURE__ */ jsxs22("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1858
|
-
/* @__PURE__ */
|
|
1859
|
-
/* @__PURE__ */
|
|
1860
|
-
/* @__PURE__ */
|
|
1861
|
-
/* @__PURE__ */
|
|
1862
|
-
/* @__PURE__ */
|
|
1863
|
-
/* @__PURE__ */
|
|
1899
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-3 w-16" }),
|
|
1900
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-4 w-32" }),
|
|
1901
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-3 w-16" }),
|
|
1902
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-4 w-24" }),
|
|
1903
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-16 mt-2.5" }),
|
|
1904
|
+
/* @__PURE__ */ jsx29(Skeleton4, { className: "h-16" })
|
|
1864
1905
|
] })
|
|
1865
1906
|
};
|
|
1866
1907
|
case "not-found":
|
|
1867
1908
|
return {
|
|
1868
1909
|
...panel,
|
|
1869
1910
|
title: "Artist not found",
|
|
1870
|
-
content: /* @__PURE__ */
|
|
1911
|
+
content: /* @__PURE__ */ jsx29("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1871
1912
|
};
|
|
1872
1913
|
case "error":
|
|
1873
1914
|
return {
|
|
1874
1915
|
...panel,
|
|
1875
1916
|
title: "Artist",
|
|
1876
|
-
content: /* @__PURE__ */
|
|
1917
|
+
content: /* @__PURE__ */ jsx29("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
1877
1918
|
};
|
|
1878
1919
|
case "success": {
|
|
1879
1920
|
const { artist } = result;
|
|
@@ -1883,12 +1924,12 @@ function useArtistPanel({
|
|
|
1883
1924
|
...panel,
|
|
1884
1925
|
title: artist.name,
|
|
1885
1926
|
content: /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
1886
|
-
shareUrl && /* @__PURE__ */
|
|
1887
|
-
artist.image && /* @__PURE__ */
|
|
1888
|
-
/* @__PURE__ */
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
/* @__PURE__ */
|
|
1891
|
-
artist.tracksCount > 0 && /* @__PURE__ */
|
|
1927
|
+
shareUrl && /* @__PURE__ */ jsx29("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsx29("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ jsx29(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
1928
|
+
artist.image && /* @__PURE__ */ jsx29(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
1929
|
+
/* @__PURE__ */ jsx29(ArtistHeader, { artist }),
|
|
1930
|
+
/* @__PURE__ */ jsx29(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
1931
|
+
/* @__PURE__ */ jsx29(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
1932
|
+
artist.tracksCount > 0 && /* @__PURE__ */ jsx29(
|
|
1892
1933
|
ArtistSongs,
|
|
1893
1934
|
{
|
|
1894
1935
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -1898,7 +1939,7 @@ function useArtistPanel({
|
|
|
1898
1939
|
}
|
|
1899
1940
|
)
|
|
1900
1941
|
] }),
|
|
1901
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */
|
|
1942
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx29(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
1902
1943
|
};
|
|
1903
1944
|
}
|
|
1904
1945
|
}
|
|
@@ -1911,7 +1952,7 @@ import {
|
|
|
1911
1952
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1912
1953
|
|
|
1913
1954
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
1914
|
-
import { useState as
|
|
1955
|
+
import { useState as useState8 } from "react";
|
|
1915
1956
|
import { Pause as Pause2, Play as Play3 } from "lucide-react";
|
|
1916
1957
|
import { FieldList as FieldList2, PanelSection as PanelSection6, Skeleton as Skeleton5 } from "@behindthemusictree/ui";
|
|
1917
1958
|
import { PlayStates as PlayStates3, usePlayer as usePlayer9 } from "@behindthemusictree/app-kit/player";
|
|
@@ -1922,19 +1963,19 @@ import {
|
|
|
1922
1963
|
useSongHistory,
|
|
1923
1964
|
useTrackList as useTrackList5
|
|
1924
1965
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1925
|
-
import { Fragment as Fragment12, jsx as
|
|
1966
|
+
import { Fragment as Fragment12, jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1926
1967
|
var numberFormat = new Intl.NumberFormat("en");
|
|
1927
1968
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
1928
1969
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
1929
1970
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
1930
|
-
const [open, setOpen] =
|
|
1971
|
+
const [open, setOpen] = useState8(false);
|
|
1931
1972
|
const history = useSongHistory(uuid, getBackendBaseUrl, open);
|
|
1932
1973
|
let body = null;
|
|
1933
|
-
if (history.status === "loading") body = /* @__PURE__ */
|
|
1934
|
-
else if (history.status === "error") body = /* @__PURE__ */
|
|
1974
|
+
if (history.status === "loading") body = /* @__PURE__ */ jsx30(Skeleton5, { className: "h-10", "aria-busy": "true" });
|
|
1975
|
+
else if (history.status === "error") body = /* @__PURE__ */ jsx30("p", { className: "text-red-600", children: "Could not load the history." });
|
|
1935
1976
|
else if (history.status === "success")
|
|
1936
|
-
body = history.entries.length === 0 ? /* @__PURE__ */
|
|
1937
|
-
/* @__PURE__ */
|
|
1977
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ jsx30("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx30("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs23("li", { className: "py-1.5 text-sm", children: [
|
|
1978
|
+
/* @__PURE__ */ jsx30("span", { className: "font-medium", children: entry.action }),
|
|
1938
1979
|
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs23("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1939
1980
|
" ",
|
|
1940
1981
|
"\u2014 ",
|
|
@@ -1954,8 +1995,8 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
1954
1995
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
1955
1996
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
1956
1997
|
children: [
|
|
1957
|
-
/* @__PURE__ */
|
|
1958
|
-
/* @__PURE__ */
|
|
1998
|
+
/* @__PURE__ */ jsx30("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
1999
|
+
/* @__PURE__ */ jsx30("div", { className: "mt-1.5", children: body })
|
|
1959
2000
|
]
|
|
1960
2001
|
}
|
|
1961
2002
|
);
|
|
@@ -1981,13 +2022,13 @@ function SongPanelContent({
|
|
|
1981
2022
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
1982
2023
|
fields.push({
|
|
1983
2024
|
label: "Genre",
|
|
1984
|
-
value: path ? /* @__PURE__ */
|
|
1985
|
-
index > 0 && /* @__PURE__ */
|
|
1986
|
-
/* @__PURE__ */
|
|
2025
|
+
value: path ? /* @__PURE__ */ jsx30("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs23("span", { children: [
|
|
2026
|
+
index > 0 && /* @__PURE__ */ jsx30("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2027
|
+
/* @__PURE__ */ jsx30("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
1987
2028
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
1988
2029
|
});
|
|
1989
2030
|
} else {
|
|
1990
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */
|
|
2031
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ jsx30("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
1991
2032
|
}
|
|
1992
2033
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
1993
2034
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -1997,11 +2038,11 @@ function SongPanelContent({
|
|
|
1997
2038
|
const links = [];
|
|
1998
2039
|
if (musicBrainzUrl)
|
|
1999
2040
|
links.push(
|
|
2000
|
-
/* @__PURE__ */
|
|
2041
|
+
/* @__PURE__ */ jsx30("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2001
2042
|
);
|
|
2002
2043
|
if (song.youtubeVideo)
|
|
2003
2044
|
links.push(
|
|
2004
|
-
/* @__PURE__ */
|
|
2045
|
+
/* @__PURE__ */ jsx30(
|
|
2005
2046
|
"a",
|
|
2006
2047
|
{
|
|
2007
2048
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2016,33 +2057,33 @@ function SongPanelContent({
|
|
|
2016
2057
|
if (links.length > 0)
|
|
2017
2058
|
fields.push({
|
|
2018
2059
|
label: "Links",
|
|
2019
|
-
value: /* @__PURE__ */
|
|
2060
|
+
value: /* @__PURE__ */ jsx30("span", { className: "flex gap-3", children: links })
|
|
2020
2061
|
});
|
|
2021
2062
|
return /* @__PURE__ */ jsxs23(Fragment12, { children: [
|
|
2022
|
-
/* @__PURE__ */
|
|
2023
|
-
unplayableReason ? /* @__PURE__ */
|
|
2063
|
+
/* @__PURE__ */ jsx30("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxs23("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2064
|
+
unplayableReason ? /* @__PURE__ */ jsx30("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs23(
|
|
2024
2065
|
"button",
|
|
2025
2066
|
{
|
|
2026
2067
|
type: "button",
|
|
2027
2068
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2028
2069
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2029
2070
|
children: [
|
|
2030
|
-
/* @__PURE__ */
|
|
2071
|
+
/* @__PURE__ */ jsx30("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ jsx30(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx30(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2031
2072
|
isPlaying ? "Pause" : "Play"
|
|
2032
2073
|
]
|
|
2033
2074
|
}
|
|
2034
2075
|
),
|
|
2035
|
-
shareUrl && /* @__PURE__ */
|
|
2076
|
+
shareUrl && /* @__PURE__ */ jsx30(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2036
2077
|
] }) }),
|
|
2037
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */
|
|
2038
|
-
/* @__PURE__ */
|
|
2039
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */
|
|
2040
|
-
/* @__PURE__ */
|
|
2078
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx30("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx30(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
|
|
2079
|
+
/* @__PURE__ */ jsx30(FieldList2, { items: fields }),
|
|
2080
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx30(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx30("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx30("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2081
|
+
/* @__PURE__ */ jsx30(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2041
2082
|
] });
|
|
2042
2083
|
}
|
|
2043
2084
|
|
|
2044
2085
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2045
|
-
import { jsx as
|
|
2086
|
+
import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2046
2087
|
function useSongPanel({
|
|
2047
2088
|
uuid,
|
|
2048
2089
|
scope,
|
|
@@ -2065,23 +2106,23 @@ function useSongPanel({
|
|
|
2065
2106
|
...panel,
|
|
2066
2107
|
title: "Song",
|
|
2067
2108
|
content: /* @__PURE__ */ jsxs24("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2068
|
-
/* @__PURE__ */
|
|
2069
|
-
/* @__PURE__ */
|
|
2070
|
-
/* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2109
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-3 w-16" }),
|
|
2110
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-4 w-32" }),
|
|
2111
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-3 w-16" }),
|
|
2112
|
+
/* @__PURE__ */ jsx31(Skeleton6, { className: "h-4 w-24" })
|
|
2072
2113
|
] })
|
|
2073
2114
|
};
|
|
2074
2115
|
case "not-found":
|
|
2075
2116
|
return {
|
|
2076
2117
|
...panel,
|
|
2077
2118
|
title: "Song not found",
|
|
2078
|
-
content: /* @__PURE__ */
|
|
2119
|
+
content: /* @__PURE__ */ jsx31("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2079
2120
|
};
|
|
2080
2121
|
case "error":
|
|
2081
2122
|
return {
|
|
2082
2123
|
...panel,
|
|
2083
2124
|
title: "Song",
|
|
2084
|
-
content: /* @__PURE__ */
|
|
2125
|
+
content: /* @__PURE__ */ jsx31("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2085
2126
|
};
|
|
2086
2127
|
case "success": {
|
|
2087
2128
|
const { song } = result;
|
|
@@ -2089,7 +2130,7 @@ function useSongPanel({
|
|
|
2089
2130
|
return {
|
|
2090
2131
|
...panel,
|
|
2091
2132
|
title: song.title,
|
|
2092
|
-
content: /* @__PURE__ */
|
|
2133
|
+
content: /* @__PURE__ */ jsx31(
|
|
2093
2134
|
SongPanelContent,
|
|
2094
2135
|
{
|
|
2095
2136
|
song,
|
|
@@ -2109,12 +2150,12 @@ function useSongPanel({
|
|
|
2109
2150
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2110
2151
|
import { MdMusicNote, MdVisibility } from "react-icons/md";
|
|
2111
2152
|
import { SegmentedControl } from "@behindthemusictree/ui";
|
|
2112
|
-
import { jsx as
|
|
2153
|
+
import { jsx as jsx32 } from "react/jsx-runtime";
|
|
2113
2154
|
function SizeMetricToggle({
|
|
2114
2155
|
value,
|
|
2115
2156
|
onChange
|
|
2116
2157
|
}) {
|
|
2117
|
-
return /* @__PURE__ */
|
|
2158
|
+
return /* @__PURE__ */ jsx32(
|
|
2118
2159
|
SegmentedControl,
|
|
2119
2160
|
{
|
|
2120
2161
|
variant: "floating",
|
|
@@ -2122,15 +2163,15 @@ function SizeMetricToggle({
|
|
|
2122
2163
|
value,
|
|
2123
2164
|
onChange,
|
|
2124
2165
|
options: [
|
|
2125
|
-
{ value: "songs", label: /* @__PURE__ */
|
|
2126
|
-
{ value: "views", label: /* @__PURE__ */
|
|
2166
|
+
{ value: "songs", label: /* @__PURE__ */ jsx32(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2167
|
+
{ value: "views", label: /* @__PURE__ */ jsx32(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2127
2168
|
]
|
|
2128
2169
|
}
|
|
2129
2170
|
);
|
|
2130
2171
|
}
|
|
2131
2172
|
|
|
2132
2173
|
// src/genre-tree/GenreTreeView.tsx
|
|
2133
|
-
import { Fragment as Fragment13, jsx as
|
|
2174
|
+
import { Fragment as Fragment13, jsx as jsx33, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2134
2175
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2135
2176
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2136
2177
|
var noop = () => {
|
|
@@ -2165,6 +2206,7 @@ function GenreTreeView({
|
|
|
2165
2206
|
onArtistExpandedChange,
|
|
2166
2207
|
getShareUrl,
|
|
2167
2208
|
extraSearchGroups = [],
|
|
2209
|
+
SearchGroups,
|
|
2168
2210
|
onSearchQueryChange,
|
|
2169
2211
|
onSearchFocus,
|
|
2170
2212
|
artistSearch = true,
|
|
@@ -2193,19 +2235,19 @@ function GenreTreeView({
|
|
|
2193
2235
|
}) {
|
|
2194
2236
|
const getShareUrlRef = useRef3(getShareUrl);
|
|
2195
2237
|
getShareUrlRef.current = getShareUrl;
|
|
2196
|
-
const [internalArtistUuid, setInternalArtistUuid] =
|
|
2238
|
+
const [internalArtistUuid, setInternalArtistUuid] = useState9(null);
|
|
2197
2239
|
const isArtistControlled = controlledArtistUuid !== void 0;
|
|
2198
2240
|
const artistUuid = isArtistControlled ? controlledArtistUuid : internalArtistUuid;
|
|
2199
|
-
const [internalSongUuid, setInternalSongUuid] =
|
|
2241
|
+
const [internalSongUuid, setInternalSongUuid] = useState9(null);
|
|
2200
2242
|
const isSongControlled = controlledSongUuid !== void 0;
|
|
2201
2243
|
const songUuid = isSongControlled ? controlledSongUuid : internalSongUuid;
|
|
2202
2244
|
const changeSong = (uuid) => {
|
|
2203
2245
|
if (!isSongControlled) setInternalSongUuid(uuid);
|
|
2204
2246
|
onSongUuidChange?.(uuid);
|
|
2205
2247
|
};
|
|
2206
|
-
const [internalSizeMetric, setInternalSizeMetric] =
|
|
2248
|
+
const [internalSizeMetric, setInternalSizeMetric] = useState9("songs");
|
|
2207
2249
|
const sizeMetric = controlledSizeMetric ?? internalSizeMetric;
|
|
2208
|
-
const deferredSizeMetric =
|
|
2250
|
+
const deferredSizeMetric = useDeferredValue2(sizeMetric);
|
|
2209
2251
|
const changeSizeMetric = (metric) => {
|
|
2210
2252
|
if (controlledSizeMetric === void 0) setInternalSizeMetric(metric);
|
|
2211
2253
|
onSizeMetricChange?.(metric);
|
|
@@ -2242,7 +2284,7 @@ function GenreTreeView({
|
|
|
2242
2284
|
artistUuid
|
|
2243
2285
|
});
|
|
2244
2286
|
const restoreRevision = viewportRestore?.revision;
|
|
2245
|
-
const [stackedRestoreOwner, setStackedRestoreOwner] =
|
|
2287
|
+
const [stackedRestoreOwner, setStackedRestoreOwner] = useState9({ revision: restoreRevision, rootUuid: selectedGenrePlaylist?.root.uuid });
|
|
2246
2288
|
let restoreRootUuid = stackedRestoreOwner.rootUuid;
|
|
2247
2289
|
if (stackedRestoreOwner.revision !== restoreRevision) {
|
|
2248
2290
|
restoreRootUuid = selectedGenrePlaylist?.root.uuid;
|
|
@@ -2276,11 +2318,11 @@ function GenreTreeView({
|
|
|
2276
2318
|
const openSongRef = useRef3(openSong);
|
|
2277
2319
|
openSongRef.current = openSong;
|
|
2278
2320
|
const resolvedRenderSongLink = useCallback6(
|
|
2279
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */
|
|
2321
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx33("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2280
2322
|
[renderSongLink]
|
|
2281
2323
|
);
|
|
2282
|
-
const [expandedSongUuid, setExpandedSongUuid] =
|
|
2283
|
-
const [expandedArtistUuid, setExpandedArtistUuid] =
|
|
2324
|
+
const [expandedSongUuid, setExpandedSongUuid] = useState9(null);
|
|
2325
|
+
const [expandedArtistUuid, setExpandedArtistUuid] = useState9(null);
|
|
2284
2326
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2285
2327
|
const songBaseUrl = getBackendBaseUrl;
|
|
2286
2328
|
const artistPanel = useArtistPanel({
|
|
@@ -2288,7 +2330,7 @@ function GenreTreeView({
|
|
|
2288
2330
|
scope,
|
|
2289
2331
|
getBackendBaseUrl: artistBaseUrl,
|
|
2290
2332
|
getShareUrl: getShareUrlRef.current,
|
|
2291
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2333
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx33("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2292
2334
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2293
2335
|
onExpandedChange: (on) => {
|
|
2294
2336
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2297,7 +2339,7 @@ function GenreTreeView({
|
|
|
2297
2339
|
renderSongLink: resolvedRenderSongLink,
|
|
2298
2340
|
onClose: () => changeArtist(null)
|
|
2299
2341
|
});
|
|
2300
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2342
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx33("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2301
2343
|
const getGenrePath = (genre) => {
|
|
2302
2344
|
const path = [];
|
|
2303
2345
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2329,8 +2371,8 @@ function GenreTreeView({
|
|
|
2329
2371
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2330
2372
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2331
2373
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2332
|
-
const [uncontrolledCollapsed, setUncontrolledCollapsed] =
|
|
2333
|
-
const [uncontrolledExpanded, setUncontrolledExpanded] =
|
|
2374
|
+
const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState9(false);
|
|
2375
|
+
const [uncontrolledExpanded, setUncontrolledExpanded] = useState9(false);
|
|
2334
2376
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
2335
2377
|
const isExpanded = expanded ?? uncontrolledExpanded;
|
|
2336
2378
|
const changeCollapsed = (next) => {
|
|
@@ -2344,10 +2386,10 @@ function GenreTreeView({
|
|
|
2344
2386
|
useEffect5(() => {
|
|
2345
2387
|
setUncontrolledCollapsed(false);
|
|
2346
2388
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2347
|
-
const [isWheelRevealed, setIsWheelRevealed] =
|
|
2389
|
+
const [isWheelRevealed, setIsWheelRevealed] = useState9(false);
|
|
2348
2390
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2349
2391
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2350
|
-
const [resolveStyle, setResolveStyle] =
|
|
2392
|
+
const [resolveStyle, setResolveStyle] = useState9(null);
|
|
2351
2393
|
const handleNodeStyleResolverChange = useCallback6(
|
|
2352
2394
|
(resolver) => setResolveStyle(() => resolver),
|
|
2353
2395
|
[]
|
|
@@ -2362,7 +2404,7 @@ function GenreTreeView({
|
|
|
2362
2404
|
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2363
2405
|
resolveStyle
|
|
2364
2406
|
);
|
|
2365
|
-
const [selectionHistory, setSelectionHistory] =
|
|
2407
|
+
const [selectionHistory, setSelectionHistory] = useState9({
|
|
2366
2408
|
current: selectedNodeId,
|
|
2367
2409
|
previous: null
|
|
2368
2410
|
});
|
|
@@ -2375,28 +2417,6 @@ function GenreTreeView({
|
|
|
2375
2417
|
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2376
2418
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2377
2419
|
const { showPopup, hidePopup } = usePopup5();
|
|
2378
|
-
const [searchText, setSearchText] = useState8("");
|
|
2379
|
-
const artistGroup = useArtistSearchGroup(
|
|
2380
|
-
artistBaseUrl,
|
|
2381
|
-
searchText,
|
|
2382
|
-
(artist) => {
|
|
2383
|
-
openArtist(artist.uuid);
|
|
2384
|
-
onArtistSelect?.(artist);
|
|
2385
|
-
},
|
|
2386
|
-
Boolean(artistSearch)
|
|
2387
|
-
);
|
|
2388
|
-
const songGroup = useSongSearchGroup(
|
|
2389
|
-
songBaseUrl,
|
|
2390
|
-
searchText,
|
|
2391
|
-
(song) => {
|
|
2392
|
-
openSong(song.uuid);
|
|
2393
|
-
onSongSelect?.(song);
|
|
2394
|
-
},
|
|
2395
|
-
Boolean(songSearch)
|
|
2396
|
-
);
|
|
2397
|
-
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2398
|
-
(group) => group !== void 0
|
|
2399
|
-
);
|
|
2400
2420
|
const handleSelectedNodeChange = (node) => {
|
|
2401
2421
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2402
2422
|
selectNode(node);
|
|
@@ -2420,7 +2440,7 @@ function GenreTreeView({
|
|
|
2420
2440
|
};
|
|
2421
2441
|
const renderGenreBody = (relation) => {
|
|
2422
2442
|
const { node } = relation;
|
|
2423
|
-
const family = /* @__PURE__ */
|
|
2443
|
+
const family = /* @__PURE__ */ jsx33(
|
|
2424
2444
|
GenreNodeFamily,
|
|
2425
2445
|
{
|
|
2426
2446
|
node,
|
|
@@ -2431,7 +2451,7 @@ function GenreTreeView({
|
|
|
2431
2451
|
onSelectNode: selectGenreFromPanel
|
|
2432
2452
|
}
|
|
2433
2453
|
);
|
|
2434
|
-
const ancestors = /* @__PURE__ */
|
|
2454
|
+
const ancestors = /* @__PURE__ */ jsx33(GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2435
2455
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2436
2456
|
const criteria = genrePlaylist?.criteria;
|
|
2437
2457
|
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
@@ -2455,7 +2475,7 @@ function GenreTreeView({
|
|
|
2455
2475
|
) : void 0;
|
|
2456
2476
|
return {
|
|
2457
2477
|
primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2458
|
-
/* @__PURE__ */
|
|
2478
|
+
/* @__PURE__ */ jsx33(
|
|
2459
2479
|
GenreInfoPanelBody,
|
|
2460
2480
|
{
|
|
2461
2481
|
family: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
@@ -2477,7 +2497,7 @@ function GenreTreeView({
|
|
|
2477
2497
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2478
2498
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2479
2499
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2480
|
-
/* @__PURE__ */
|
|
2500
|
+
/* @__PURE__ */ jsx33(
|
|
2481
2501
|
ChangeParentPopup,
|
|
2482
2502
|
{
|
|
2483
2503
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2552,19 +2572,19 @@ function GenreTreeView({
|
|
|
2552
2572
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2553
2573
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2554
2574
|
} : void 0;
|
|
2555
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */
|
|
2575
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx33(
|
|
2556
2576
|
IconButton,
|
|
2557
2577
|
{
|
|
2558
2578
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2559
2579
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2560
2580
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2561
|
-
children: activePanel.expanded ? /* @__PURE__ */
|
|
2581
|
+
children: activePanel.expanded ? /* @__PURE__ */ jsx33(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx33(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2562
2582
|
}
|
|
2563
2583
|
) : null;
|
|
2564
2584
|
const renderArtistFilter = () => {
|
|
2565
2585
|
if (isLoading || !filterArtist) return null;
|
|
2566
2586
|
return /* @__PURE__ */ jsxs25("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2567
|
-
/* @__PURE__ */
|
|
2587
|
+
/* @__PURE__ */ jsx33(
|
|
2568
2588
|
"button",
|
|
2569
2589
|
{
|
|
2570
2590
|
type: "button",
|
|
@@ -2576,7 +2596,7 @@ function GenreTreeView({
|
|
|
2576
2596
|
children: filterArtist.name
|
|
2577
2597
|
}
|
|
2578
2598
|
),
|
|
2579
|
-
/* @__PURE__ */
|
|
2599
|
+
/* @__PURE__ */ jsx33(
|
|
2580
2600
|
"button",
|
|
2581
2601
|
{
|
|
2582
2602
|
type: "button",
|
|
@@ -2589,25 +2609,35 @@ function GenreTreeView({
|
|
|
2589
2609
|
] });
|
|
2590
2610
|
};
|
|
2591
2611
|
const actions = /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2592
|
-
!isLoading && /* @__PURE__ */
|
|
2593
|
-
|
|
2612
|
+
!isLoading && /* @__PURE__ */ jsx33(
|
|
2613
|
+
GenreTreeSearch,
|
|
2594
2614
|
{
|
|
2615
|
+
artistBaseUrl,
|
|
2616
|
+
songBaseUrl,
|
|
2617
|
+
artistSearch: Boolean(artistSearch),
|
|
2618
|
+
songSearch: Boolean(songSearch),
|
|
2619
|
+
onArtistSelect: (artist) => {
|
|
2620
|
+
openArtist(artist.uuid);
|
|
2621
|
+
onArtistSelect?.(artist);
|
|
2622
|
+
},
|
|
2623
|
+
onSongSelect: (song) => {
|
|
2624
|
+
openSong(song.uuid);
|
|
2625
|
+
onSongSelect?.(song);
|
|
2626
|
+
},
|
|
2627
|
+
SearchGroups,
|
|
2595
2628
|
genrePlaylists: visibleGenrePlaylists,
|
|
2596
2629
|
onSelect: handleGenreSearchSelect,
|
|
2597
2630
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2598
2631
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2599
2632
|
onPrevious: handlePrevious,
|
|
2600
|
-
onQueryChange:
|
|
2601
|
-
setSearchText(text);
|
|
2602
|
-
onSearchQueryChange?.(text);
|
|
2603
|
-
},
|
|
2633
|
+
onQueryChange: onSearchQueryChange,
|
|
2604
2634
|
onFocus: onSearchFocus,
|
|
2605
|
-
extraGroups
|
|
2635
|
+
extraGroups: extraSearchGroups
|
|
2606
2636
|
}
|
|
2607
2637
|
),
|
|
2608
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2638
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx33("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2609
2639
|
/* @__PURE__ */ jsxs25("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
2610
|
-
!isLoading && !isControlled && /* @__PURE__ */
|
|
2640
|
+
!isLoading && !isControlled && /* @__PURE__ */ jsx33(
|
|
2611
2641
|
SegmentedControl2,
|
|
2612
2642
|
{
|
|
2613
2643
|
"aria-label": "Tree view mode",
|
|
@@ -2621,13 +2651,13 @@ function GenreTreeView({
|
|
|
2621
2651
|
]
|
|
2622
2652
|
}
|
|
2623
2653
|
),
|
|
2624
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */
|
|
2654
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ jsx33(
|
|
2625
2655
|
"div",
|
|
2626
2656
|
{
|
|
2627
2657
|
className: "flex items-center gap-1",
|
|
2628
2658
|
role: "group",
|
|
2629
2659
|
"aria-label": "Tree display options",
|
|
2630
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */
|
|
2660
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx33(
|
|
2631
2661
|
Button,
|
|
2632
2662
|
{
|
|
2633
2663
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2638,7 +2668,7 @@ function GenreTreeView({
|
|
|
2638
2668
|
)
|
|
2639
2669
|
}
|
|
2640
2670
|
),
|
|
2641
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */
|
|
2671
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ jsx33(
|
|
2642
2672
|
IconTextButton,
|
|
2643
2673
|
{
|
|
2644
2674
|
icon: Plus2,
|
|
@@ -2654,28 +2684,28 @@ function GenreTreeView({
|
|
|
2654
2684
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2655
2685
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2656
2686
|
children: [
|
|
2657
|
-
/* @__PURE__ */
|
|
2687
|
+
/* @__PURE__ */ jsx33(
|
|
2658
2688
|
"div",
|
|
2659
2689
|
{
|
|
2660
2690
|
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
|
|
2661
2691
|
children: actions
|
|
2662
2692
|
}
|
|
2663
2693
|
),
|
|
2664
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2694
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx33(
|
|
2665
2695
|
"div",
|
|
2666
2696
|
{
|
|
2667
2697
|
className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "top-5"}`,
|
|
2668
2698
|
children: renderArtistFilter()
|
|
2669
2699
|
}
|
|
2670
2700
|
),
|
|
2671
|
-
/* @__PURE__ */
|
|
2672
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */
|
|
2673
|
-
isLoading ? /* @__PURE__ */
|
|
2701
|
+
/* @__PURE__ */ jsx33("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs25("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2702
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx33("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2703
|
+
isLoading ? /* @__PURE__ */ jsx33(GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2674
2704
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2675
2705
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2676
2706
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2677
2707
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2678
|
-
/* @__PURE__ */
|
|
2708
|
+
/* @__PURE__ */ jsx33("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx33(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx33(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx33(
|
|
2679
2709
|
GenrePlaylistTreeWheel,
|
|
2680
2710
|
{
|
|
2681
2711
|
scope,
|
|
@@ -2694,7 +2724,7 @@ function GenreTreeView({
|
|
|
2694
2724
|
busy,
|
|
2695
2725
|
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2696
2726
|
floatingControlsLeading,
|
|
2697
|
-
canSizeByViews && /* @__PURE__ */
|
|
2727
|
+
canSizeByViews && /* @__PURE__ */ jsx33(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2698
2728
|
] }) : void 0,
|
|
2699
2729
|
defaultViewport,
|
|
2700
2730
|
viewportRestore,
|
|
@@ -2703,7 +2733,7 @@ function GenreTreeView({
|
|
|
2703
2733
|
) }) })
|
|
2704
2734
|
) : viewMode === "pop-core" ? (
|
|
2705
2735
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2706
|
-
/* @__PURE__ */
|
|
2736
|
+
/* @__PURE__ */ jsx33("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx33(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx33(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx33(
|
|
2707
2737
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2708
2738
|
{
|
|
2709
2739
|
scope,
|
|
@@ -2722,7 +2752,7 @@ function GenreTreeView({
|
|
|
2722
2752
|
busy,
|
|
2723
2753
|
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2724
2754
|
floatingControlsLeading,
|
|
2725
|
-
canSizeByViews && /* @__PURE__ */
|
|
2755
|
+
canSizeByViews && /* @__PURE__ */ jsx33(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2726
2756
|
] }) : void 0,
|
|
2727
2757
|
defaultViewport,
|
|
2728
2758
|
viewportRestore,
|
|
@@ -2730,7 +2760,7 @@ function GenreTreeView({
|
|
|
2730
2760
|
...focus
|
|
2731
2761
|
}
|
|
2732
2762
|
) }) })
|
|
2733
|
-
) : viewMode === "outline" ? /* @__PURE__ */
|
|
2763
|
+
) : viewMode === "outline" ? /* @__PURE__ */ jsx33("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx33(
|
|
2734
2764
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2735
2765
|
{
|
|
2736
2766
|
outline: true,
|
|
@@ -2746,13 +2776,13 @@ function GenreTreeView({
|
|
|
2746
2776
|
selectedNodeId,
|
|
2747
2777
|
readOnly
|
|
2748
2778
|
}
|
|
2749
|
-
) }) : /* @__PURE__ */
|
|
2779
|
+
) }) : /* @__PURE__ */ jsx33("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
2750
2780
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2751
|
-
return /* @__PURE__ */
|
|
2781
|
+
return /* @__PURE__ */ jsx33(
|
|
2752
2782
|
"div",
|
|
2753
2783
|
{
|
|
2754
2784
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2755
|
-
children: /* @__PURE__ */
|
|
2785
|
+
children: /* @__PURE__ */ jsx33("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx33(
|
|
2756
2786
|
GenrePlaylistTreePerRoot,
|
|
2757
2787
|
{
|
|
2758
2788
|
scope,
|
|
@@ -2778,7 +2808,7 @@ function GenreTreeView({
|
|
|
2778
2808
|
);
|
|
2779
2809
|
}
|
|
2780
2810
|
) }),
|
|
2781
|
-
/* @__PURE__ */
|
|
2811
|
+
/* @__PURE__ */ jsx33(
|
|
2782
2812
|
DetailPanel,
|
|
2783
2813
|
{
|
|
2784
2814
|
ref: panelRef,
|
|
@@ -2824,7 +2854,7 @@ import {
|
|
|
2824
2854
|
genreMusicCount as genreMusicCount2,
|
|
2825
2855
|
GenrePlaylistSummarySchema
|
|
2826
2856
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
2827
|
-
import { Fragment as Fragment14, jsx as
|
|
2857
|
+
import { Fragment as Fragment14, jsx as jsx34, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2828
2858
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2829
2859
|
function toNode(genre, parentId) {
|
|
2830
2860
|
return {
|
|
@@ -2879,17 +2909,17 @@ function GenreDetailPanel({
|
|
|
2879
2909
|
};
|
|
2880
2910
|
}, [detail, scope]);
|
|
2881
2911
|
if (isError) {
|
|
2882
|
-
return /* @__PURE__ */
|
|
2912
|
+
return /* @__PURE__ */ jsx34("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
2883
2913
|
}
|
|
2884
2914
|
if (isPending || !detail || !view) {
|
|
2885
2915
|
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2886
|
-
/* @__PURE__ */
|
|
2887
|
-
/* @__PURE__ */
|
|
2888
|
-
/* @__PURE__ */
|
|
2916
|
+
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-6 w-2/3" }),
|
|
2917
|
+
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-4 w-full" }),
|
|
2918
|
+
/* @__PURE__ */ jsx34(Skeleton7, { className: "h-4 w-3/4" })
|
|
2889
2919
|
] });
|
|
2890
2920
|
}
|
|
2891
2921
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
2892
|
-
const family = /* @__PURE__ */
|
|
2922
|
+
const family = /* @__PURE__ */ jsx34(
|
|
2893
2923
|
GenreNodeFamily2,
|
|
2894
2924
|
{
|
|
2895
2925
|
node: view.node,
|
|
@@ -2900,20 +2930,20 @@ function GenreDetailPanel({
|
|
|
2900
2930
|
onSelectNode: selectGenre
|
|
2901
2931
|
}
|
|
2902
2932
|
);
|
|
2903
|
-
const ancestors = /* @__PURE__ */
|
|
2933
|
+
const ancestors = /* @__PURE__ */ jsx34(GenreNodeAncestors2, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2904
2934
|
return /* @__PURE__ */ jsxs26(
|
|
2905
2935
|
DetailPanel2,
|
|
2906
2936
|
{
|
|
2907
2937
|
side: "left",
|
|
2908
2938
|
title: view.node.name,
|
|
2909
2939
|
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
2910
|
-
headerActions: onExpandedChange ? /* @__PURE__ */
|
|
2940
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ jsx34(
|
|
2911
2941
|
IconButton2,
|
|
2912
2942
|
{
|
|
2913
2943
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
2914
2944
|
style: { color: "inherit" },
|
|
2915
2945
|
onClick: () => onExpandedChange(!expanded),
|
|
2916
|
-
children: expanded ? /* @__PURE__ */
|
|
2946
|
+
children: expanded ? /* @__PURE__ */ jsx34(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx34(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2917
2947
|
}
|
|
2918
2948
|
) : void 0,
|
|
2919
2949
|
onClose,
|
|
@@ -2922,7 +2952,7 @@ function GenreDetailPanel({
|
|
|
2922
2952
|
onExpandedChange,
|
|
2923
2953
|
edgeTab,
|
|
2924
2954
|
children: [
|
|
2925
|
-
/* @__PURE__ */
|
|
2955
|
+
/* @__PURE__ */ jsx34(
|
|
2926
2956
|
GenreInfoPanelBody,
|
|
2927
2957
|
{
|
|
2928
2958
|
family: /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
@@ -2960,7 +2990,7 @@ import { ListMusic } from "lucide-react";
|
|
|
2960
2990
|
import { useTrackList as useTrackList6 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2961
2991
|
import { TrackListOriginType as TrackListOriginType2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2962
2992
|
import { useTrackNoun as useTrackNoun4 } from "@behindthemusictree/app-kit/track-noun";
|
|
2963
|
-
import { jsx as
|
|
2993
|
+
import { jsx as jsx35, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2964
2994
|
var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect2;
|
|
2965
2995
|
function TrackListSidebar({
|
|
2966
2996
|
className: className2 = "",
|
|
@@ -3072,15 +3102,15 @@ function TrackListSidebar({
|
|
|
3072
3102
|
/* @__PURE__ */ jsxs27("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3073
3103
|
/* @__PURE__ */ jsxs27("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3074
3104
|
/* @__PURE__ */ jsxs27("div", { className: "origin flex text-xl", children: [
|
|
3075
|
-
/* @__PURE__ */
|
|
3076
|
-
/* @__PURE__ */
|
|
3105
|
+
/* @__PURE__ */ jsx35("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3106
|
+
/* @__PURE__ */ jsx35("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ jsx35("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3077
3107
|
] }),
|
|
3078
3108
|
/* @__PURE__ */ jsxs27("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3079
3109
|
trackList.origin.type === TrackListOriginType2.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType2.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3080
3110
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3081
3111
|
] })
|
|
3082
3112
|
] }),
|
|
3083
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */
|
|
3113
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx35(
|
|
3084
3114
|
"button",
|
|
3085
3115
|
{
|
|
3086
3116
|
type: "button",
|
|
@@ -3089,7 +3119,7 @@ function TrackListSidebar({
|
|
|
3089
3119
|
"aria-expanded": !isCollapsed,
|
|
3090
3120
|
className: `ml-2 flex h-8 w-8 shrink-0 items-center justify-center rounded-md hover:bg-[var(--color-surface-muted)] ${isCollapsed ? "text-[var(--color-text-muted)]" : "text-white"}`,
|
|
3091
3121
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3092
|
-
children: /* @__PURE__ */
|
|
3122
|
+
children: /* @__PURE__ */ jsx35(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3093
3123
|
}
|
|
3094
3124
|
)
|
|
3095
3125
|
] }),
|
|
@@ -3103,7 +3133,7 @@ function TrackListSidebar({
|
|
|
3103
3133
|
onPointerDown: markUserScrollIntent,
|
|
3104
3134
|
className: layout === "inline" ? "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0" : "track-list flex-1 min-h-0 overflow-auto list-none p-0 m-0",
|
|
3105
3135
|
children: [
|
|
3106
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */
|
|
3136
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ jsx35("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx35(
|
|
3107
3137
|
TrackItem,
|
|
3108
3138
|
{
|
|
3109
3139
|
track,
|
|
@@ -3114,7 +3144,7 @@ function TrackListSidebar({
|
|
|
3114
3144
|
renderSongLink
|
|
3115
3145
|
}
|
|
3116
3146
|
) }, track.uuid)),
|
|
3117
|
-
hasMore && /* @__PURE__ */
|
|
3147
|
+
hasMore && /* @__PURE__ */ jsx35("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3118
3148
|
]
|
|
3119
3149
|
}
|
|
3120
3150
|
)
|
|
@@ -3124,7 +3154,7 @@ function TrackListSidebar({
|
|
|
3124
3154
|
}
|
|
3125
3155
|
|
|
3126
3156
|
// src/genre-tree/PlayerPanel.tsx
|
|
3127
|
-
import { useEffect as useEffect7, useState as
|
|
3157
|
+
import { useEffect as useEffect7, useState as useState10 } from "react";
|
|
3128
3158
|
import { twMerge } from "tailwind-merge";
|
|
3129
3159
|
import {
|
|
3130
3160
|
usePlayer as usePlayer11,
|
|
@@ -3139,7 +3169,7 @@ import { PanelEdgeTab } from "@behindthemusictree/ui";
|
|
|
3139
3169
|
import { Pause as Pause3, Play as Play4, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
|
|
3140
3170
|
import { formatTime, useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
|
|
3141
3171
|
import { PlayStates as PlayStates4, useCurrentTime, usePlayer as usePlayer10 } from "@behindthemusictree/app-kit/player";
|
|
3142
|
-
import { jsx as
|
|
3172
|
+
import { jsx as jsx36, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3143
3173
|
function PlayerControls() {
|
|
3144
3174
|
const {
|
|
3145
3175
|
duration,
|
|
@@ -3165,8 +3195,8 @@ function PlayerControls() {
|
|
|
3165
3195
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3166
3196
|
children: [
|
|
3167
3197
|
/* @__PURE__ */ jsxs28("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3168
|
-
/* @__PURE__ */
|
|
3169
|
-
/* @__PURE__ */
|
|
3198
|
+
/* @__PURE__ */ jsx36("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
|
|
3199
|
+
/* @__PURE__ */ jsx36(
|
|
3170
3200
|
"input",
|
|
3171
3201
|
{
|
|
3172
3202
|
"aria-label": "Seek",
|
|
@@ -3180,11 +3210,11 @@ function PlayerControls() {
|
|
|
3180
3210
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3181
3211
|
}
|
|
3182
3212
|
),
|
|
3183
|
-
/* @__PURE__ */
|
|
3213
|
+
/* @__PURE__ */ jsx36("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
|
|
3184
3214
|
] }),
|
|
3185
3215
|
/* @__PURE__ */ jsxs28("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3186
3216
|
/* @__PURE__ */ jsxs28("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3187
|
-
/* @__PURE__ */
|
|
3217
|
+
/* @__PURE__ */ jsx36(
|
|
3188
3218
|
"button",
|
|
3189
3219
|
{
|
|
3190
3220
|
type: "button",
|
|
@@ -3193,10 +3223,10 @@ function PlayerControls() {
|
|
|
3193
3223
|
disabled: !isReady || !hasPreviousTrack,
|
|
3194
3224
|
onClick: playPreviousTrack,
|
|
3195
3225
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
3196
|
-
children: /* @__PURE__ */
|
|
3226
|
+
children: /* @__PURE__ */ jsx36(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3197
3227
|
}
|
|
3198
3228
|
),
|
|
3199
|
-
/* @__PURE__ */
|
|
3229
|
+
/* @__PURE__ */ jsx36(
|
|
3200
3230
|
"button",
|
|
3201
3231
|
{
|
|
3202
3232
|
type: "button",
|
|
@@ -3205,10 +3235,10 @@ function PlayerControls() {
|
|
|
3205
3235
|
disabled: !isReady,
|
|
3206
3236
|
onClick: handlePlayPauseAction,
|
|
3207
3237
|
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-white text-zinc-950 hover:bg-zinc-200 disabled:cursor-not-allowed disabled:opacity-40",
|
|
3208
|
-
children: isPlaying ? /* @__PURE__ */
|
|
3238
|
+
children: isPlaying ? /* @__PURE__ */ jsx36(Pause3, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx36(Play4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3209
3239
|
}
|
|
3210
3240
|
),
|
|
3211
|
-
/* @__PURE__ */
|
|
3241
|
+
/* @__PURE__ */ jsx36(
|
|
3212
3242
|
"button",
|
|
3213
3243
|
{
|
|
3214
3244
|
type: "button",
|
|
@@ -3217,14 +3247,14 @@ function PlayerControls() {
|
|
|
3217
3247
|
disabled: !isReady || !hasNextTrack,
|
|
3218
3248
|
onClick: playNextTrack,
|
|
3219
3249
|
className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-full text-zinc-300 hover:bg-zinc-800 hover:text-white disabled:cursor-not-allowed disabled:opacity-40",
|
|
3220
|
-
children: /* @__PURE__ */
|
|
3250
|
+
children: /* @__PURE__ */ jsx36(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3221
3251
|
}
|
|
3222
3252
|
)
|
|
3223
3253
|
] }),
|
|
3224
3254
|
/* @__PURE__ */ jsxs28("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3225
|
-
volume === 0 ? /* @__PURE__ */
|
|
3226
|
-
/* @__PURE__ */
|
|
3227
|
-
/* @__PURE__ */
|
|
3255
|
+
volume === 0 ? /* @__PURE__ */ jsx36(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx36(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3256
|
+
/* @__PURE__ */ jsx36("span", { className: "sr-only", children: "Volume" }),
|
|
3257
|
+
/* @__PURE__ */ jsx36(
|
|
3228
3258
|
"input",
|
|
3229
3259
|
{
|
|
3230
3260
|
"aria-label": "Volume",
|
|
@@ -3246,7 +3276,7 @@ function PlayerControls() {
|
|
|
3246
3276
|
}
|
|
3247
3277
|
|
|
3248
3278
|
// src/genre-tree/PlayerPanel.tsx
|
|
3249
|
-
import { jsx as
|
|
3279
|
+
import { jsx as jsx37, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3250
3280
|
var PANEL_ID = "player-panel";
|
|
3251
3281
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3252
3282
|
function PlayerPanel({
|
|
@@ -3260,12 +3290,12 @@ function PlayerPanel({
|
|
|
3260
3290
|
}) {
|
|
3261
3291
|
const { playerTrackObject, retryPlayback } = usePlayer11();
|
|
3262
3292
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = useTrackListSidebarVisibility();
|
|
3263
|
-
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] =
|
|
3293
|
+
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = useState10("");
|
|
3264
3294
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3265
3295
|
useEffect7(() => {
|
|
3266
3296
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3267
3297
|
}, [loadedTrackTitle]);
|
|
3268
|
-
const [uncontrolledExtended, setUncontrolledExtended] =
|
|
3298
|
+
const [uncontrolledExtended, setUncontrolledExtended] = useState10(false);
|
|
3269
3299
|
const isExtendRequested = expanded ?? uncontrolledExtended;
|
|
3270
3300
|
const setIsExtendRequested = (next) => {
|
|
3271
3301
|
setUncontrolledExtended(next);
|
|
@@ -3299,7 +3329,7 @@ function PlayerPanel({
|
|
|
3299
3329
|
),
|
|
3300
3330
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3301
3331
|
children: [
|
|
3302
|
-
/* @__PURE__ */
|
|
3332
|
+
/* @__PURE__ */ jsx37(
|
|
3303
3333
|
PanelEdgeTab,
|
|
3304
3334
|
{
|
|
3305
3335
|
side: "right",
|
|
@@ -3318,19 +3348,19 @@ function PlayerPanel({
|
|
|
3318
3348
|
),
|
|
3319
3349
|
children: [
|
|
3320
3350
|
/* @__PURE__ */ jsxs29("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3321
|
-
/* @__PURE__ */
|
|
3351
|
+
/* @__PURE__ */ jsx37("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx37("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3322
3352
|
/* @__PURE__ */ jsxs29("div", { className: "relative w-full shrink-0", children: [
|
|
3323
|
-
/* @__PURE__ */
|
|
3353
|
+
/* @__PURE__ */ jsx37(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3324
3354
|
playerTrackObject?.loadError && /* @__PURE__ */ jsxs29("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3325
3355
|
playerTrackObject.loadError,
|
|
3326
|
-
/* @__PURE__ */
|
|
3356
|
+
/* @__PURE__ */ jsx37("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3327
3357
|
void retryPlayback();
|
|
3328
3358
|
}, children: "Retry" })
|
|
3329
3359
|
] })
|
|
3330
3360
|
] }),
|
|
3331
|
-
/* @__PURE__ */
|
|
3361
|
+
/* @__PURE__ */ jsx37(PlayerControls, {})
|
|
3332
3362
|
] }),
|
|
3333
|
-
/* @__PURE__ */
|
|
3363
|
+
/* @__PURE__ */ jsx37(
|
|
3334
3364
|
TrackListSidebar,
|
|
3335
3365
|
{
|
|
3336
3366
|
layout: "inline",
|
|
@@ -3357,11 +3387,11 @@ function PlayerPanel({
|
|
|
3357
3387
|
import { v4 as uuidv4 } from "uuid";
|
|
3358
3388
|
import { AiFillStar, AiOutlineStar } from "react-icons/ai";
|
|
3359
3389
|
import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
|
|
3360
|
-
import { jsx as
|
|
3390
|
+
import { jsx as jsx38, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3361
3391
|
function Rating({ rating, handleChange }) {
|
|
3362
3392
|
const instanceId = uuidv4();
|
|
3363
|
-
return /* @__PURE__ */
|
|
3364
|
-
/* @__PURE__ */
|
|
3393
|
+
return /* @__PURE__ */ jsx38("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs30("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3394
|
+
/* @__PURE__ */ jsx38(
|
|
3365
3395
|
"input",
|
|
3366
3396
|
{
|
|
3367
3397
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3373,16 +3403,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3373
3403
|
onChange: handleChange
|
|
3374
3404
|
}
|
|
3375
3405
|
),
|
|
3376
|
-
/* @__PURE__ */
|
|
3377
|
-
/* @__PURE__ */
|
|
3406
|
+
/* @__PURE__ */ jsx38("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs30("div", { className: "flex", children: [
|
|
3407
|
+
/* @__PURE__ */ jsx38(
|
|
3378
3408
|
AiOutlineStar,
|
|
3379
3409
|
{
|
|
3380
3410
|
className: `w-7 h-7 absolute top-0 left-0 ${rating === FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3381
3411
|
}
|
|
3382
3412
|
),
|
|
3383
|
-
/* @__PURE__ */
|
|
3413
|
+
/* @__PURE__ */ jsx38("span", { className: "flex-grow w-8" }),
|
|
3384
3414
|
" "
|
|
3385
|
-
] }) : /* @__PURE__ */
|
|
3415
|
+
] }) : /* @__PURE__ */ jsx38(
|
|
3386
3416
|
AiFillStar,
|
|
3387
3417
|
{
|
|
3388
3418
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3415,4 +3445,4 @@ export {
|
|
|
3415
3445
|
GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
|
|
3416
3446
|
GenreTreeOutlineSkeleton
|
|
3417
3447
|
};
|
|
3418
|
-
//# sourceMappingURL=chunk-
|
|
3448
|
+
//# sourceMappingURL=chunk-JP7PID7L.js.map
|