@behindthemusictree/app-kit-ui 33.1.0 → 33.3.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-2M4Q2IJU.js → chunk-QMELESFB.js} +518 -460
- package/dist/chunk-QMELESFB.js.map +1 -0
- package/dist/genre-tree/index.cjs +598 -535
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +74 -20
- package/dist/genre-tree/index.js +15 -3
- package/dist/index.cjs +596 -533
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -3
- package/package.json +5 -5
- package/dist/chunk-2M4Q2IJU.js.map +0 -1
|
@@ -17,11 +17,6 @@ import {
|
|
|
17
17
|
GenreTreeViewSkeleton,
|
|
18
18
|
useGenreNodeRelations
|
|
19
19
|
} from "@behindthemusictree/genre-tree-view";
|
|
20
|
-
import {
|
|
21
|
-
useArtistSearch,
|
|
22
|
-
useSongSearch,
|
|
23
|
-
isSongSearchResultPlayable
|
|
24
|
-
} from "@behindthemusictree/app-kit/genre-tree";
|
|
25
20
|
import { useGenreTreeViewState, useArtist as useArtist2, useUpdateGenre as useUpdateGenre2, genreUpdateMutationKey } from "@behindthemusictree/app-kit/genre-tree";
|
|
26
21
|
import { usePopup as usePopup5 } from "@behindthemusictree/app-kit/popup";
|
|
27
22
|
|
|
@@ -465,11 +460,148 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
465
460
|
}
|
|
466
461
|
|
|
467
462
|
// src/genre-tree/GenreSearch.tsx
|
|
468
|
-
import {
|
|
469
|
-
import {
|
|
470
|
-
import { Combobox } from "@behindthemusictree/ui";
|
|
463
|
+
import { ArrowLeft } from "lucide-react";
|
|
464
|
+
import { SEARCH_LEADING_BUTTON_CLASS } from "@behindthemusictree/ui";
|
|
471
465
|
import { useGenreSearch } from "@behindthemusictree/app-kit/genre-tree";
|
|
472
|
-
|
|
466
|
+
|
|
467
|
+
// src/genre-tree/search/SearchBox.tsx
|
|
468
|
+
import { useState as useState2 } from "react";
|
|
469
|
+
import { SearchCombobox } from "@behindthemusictree/ui";
|
|
470
|
+
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
471
|
+
function SearchBox({
|
|
472
|
+
label,
|
|
473
|
+
placeholder,
|
|
474
|
+
value,
|
|
475
|
+
onValueChange,
|
|
476
|
+
groups,
|
|
477
|
+
onPicked,
|
|
478
|
+
onClear,
|
|
479
|
+
leading,
|
|
480
|
+
className: className2,
|
|
481
|
+
onFocus
|
|
482
|
+
}) {
|
|
483
|
+
const [open, setOpen] = useState2(false);
|
|
484
|
+
const loading = groups.some((group) => group.loading);
|
|
485
|
+
const status = groups.map((group) => group.status).filter(Boolean).join(". ");
|
|
486
|
+
const handlePick = (groupKey, itemKey) => {
|
|
487
|
+
setOpen(false);
|
|
488
|
+
groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
|
|
489
|
+
onPicked?.(groupKey, itemKey);
|
|
490
|
+
};
|
|
491
|
+
return /* @__PURE__ */ jsx5("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ jsx5(
|
|
492
|
+
SearchCombobox,
|
|
493
|
+
{
|
|
494
|
+
ariaLabel: label,
|
|
495
|
+
listLabel: `${label} results`,
|
|
496
|
+
value,
|
|
497
|
+
placeholder: placeholder ?? `${label}\u2026`,
|
|
498
|
+
groups: groups.map((group) => ({
|
|
499
|
+
key: group.key,
|
|
500
|
+
label: group.label,
|
|
501
|
+
options: group.items.map((item) => ({ key: item.key, content: item.content }))
|
|
502
|
+
})),
|
|
503
|
+
open,
|
|
504
|
+
onOpenChange: setOpen,
|
|
505
|
+
onValueChange,
|
|
506
|
+
onPick: handlePick,
|
|
507
|
+
status,
|
|
508
|
+
loading,
|
|
509
|
+
onClear,
|
|
510
|
+
leading,
|
|
511
|
+
inputClassName: "focus-visible:ring-0"
|
|
512
|
+
}
|
|
513
|
+
) });
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// src/genre-tree/search/SearchGroup.ts
|
|
517
|
+
var SEARCH_FAILED_STATUS = "Search failed";
|
|
518
|
+
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
519
|
+
|
|
520
|
+
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
521
|
+
import { useArtistSearch } from "@behindthemusictree/app-kit/genre-tree";
|
|
522
|
+
import { Fragment, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
523
|
+
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
524
|
+
const search = useArtistSearch(getBaseUrl, enabled ? text : "");
|
|
525
|
+
if (!enabled) return void 0;
|
|
526
|
+
return {
|
|
527
|
+
key: "artists",
|
|
528
|
+
label: "Artists",
|
|
529
|
+
items: search.artists.map((artist) => ({
|
|
530
|
+
key: artist.uuid,
|
|
531
|
+
content: /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
532
|
+
/* @__PURE__ */ jsxs2("span", { className: "block truncate font-medium", children: [
|
|
533
|
+
artist.name,
|
|
534
|
+
artist.disambiguation && /* @__PURE__ */ jsxs2("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
535
|
+
" \xB7 ",
|
|
536
|
+
artist.disambiguation
|
|
537
|
+
] })
|
|
538
|
+
] }),
|
|
539
|
+
artist.matchedAlias && /* @__PURE__ */ jsxs2("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
540
|
+
"also known as ",
|
|
541
|
+
artist.matchedAlias
|
|
542
|
+
] })
|
|
543
|
+
] }),
|
|
544
|
+
onSelect: () => onSelect(artist)
|
|
545
|
+
})),
|
|
546
|
+
status: searchStatus(search, search.artists.length, "artists"),
|
|
547
|
+
loading: search.pending
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
552
|
+
import {
|
|
553
|
+
isSongSearchResultPlayable,
|
|
554
|
+
useSongSearch
|
|
555
|
+
} from "@behindthemusictree/app-kit/genre-tree";
|
|
556
|
+
import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
557
|
+
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
558
|
+
const search = useSongSearch(getBaseUrl, enabled ? text : "");
|
|
559
|
+
if (!enabled) return void 0;
|
|
560
|
+
return {
|
|
561
|
+
key: "songs",
|
|
562
|
+
label: "Songs",
|
|
563
|
+
items: search.songs.map((song) => ({
|
|
564
|
+
key: song.uuid,
|
|
565
|
+
content: /* @__PURE__ */ jsxs3("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
|
|
566
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate font-medium", children: song.title }),
|
|
567
|
+
/* @__PURE__ */ jsx6("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
568
|
+
] }),
|
|
569
|
+
onSelect: () => onSelect(song)
|
|
570
|
+
})),
|
|
571
|
+
status: searchStatus(search, search.songs.length, "songs"),
|
|
572
|
+
loading: search.pending
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
577
|
+
import {
|
|
578
|
+
useGenreRemoteSearch
|
|
579
|
+
} from "@behindthemusictree/app-kit/genre-tree";
|
|
580
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
581
|
+
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
582
|
+
const search = useGenreRemoteSearch(getBaseUrl, text, options);
|
|
583
|
+
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
584
|
+
return {
|
|
585
|
+
key,
|
|
586
|
+
label,
|
|
587
|
+
items: genres.map((genre) => ({
|
|
588
|
+
key: genre.uuid,
|
|
589
|
+
content: /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
590
|
+
/* @__PURE__ */ jsxs4("span", { className: "block truncate font-medium", children: [
|
|
591
|
+
genre.name,
|
|
592
|
+
genre.isUmbrella && /* @__PURE__ */ jsx7("span", { className: "ml-2 text-xs font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
593
|
+
] }),
|
|
594
|
+
/* @__PURE__ */ jsx7("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
|
|
595
|
+
] }),
|
|
596
|
+
onSelect: () => onSelect(genre)
|
|
597
|
+
})),
|
|
598
|
+
status: searchStatus(search, genres.length, "genres"),
|
|
599
|
+
loading: search.pending
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/genre-tree/GenreSearch.tsx
|
|
604
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
473
605
|
function GenreSearch({
|
|
474
606
|
genrePlaylists,
|
|
475
607
|
onSelect,
|
|
@@ -478,7 +610,8 @@ function GenreSearch({
|
|
|
478
610
|
onPrevious,
|
|
479
611
|
placeholder,
|
|
480
612
|
onQueryChange,
|
|
481
|
-
extraGroups = []
|
|
613
|
+
extraGroups = [],
|
|
614
|
+
onFocus
|
|
482
615
|
}) {
|
|
483
616
|
const { query, setQuery, results, canClear, clear, select } = useGenreSearch({
|
|
484
617
|
genrePlaylists,
|
|
@@ -486,112 +619,49 @@ function GenreSearch({
|
|
|
486
619
|
selectedName,
|
|
487
620
|
onClear
|
|
488
621
|
});
|
|
489
|
-
const [open, setOpen] = useState2(false);
|
|
490
622
|
const active = query !== "" && query !== selectedName;
|
|
491
623
|
const hasExtras = extraGroups.length > 0;
|
|
492
624
|
const label = hasExtras ? "Search" : "Search a genre";
|
|
493
|
-
const loading = active && extraGroups.some((group) => group.loading);
|
|
494
|
-
const status = active ? extraGroups.map((group) => group.status).filter(Boolean).join(". ") : "";
|
|
495
625
|
const groups = [
|
|
496
626
|
{
|
|
497
627
|
key: "genres",
|
|
498
628
|
label: hasExtras ? "Genres" : void 0,
|
|
499
|
-
|
|
629
|
+
items: results.map((genrePlaylist) => ({
|
|
500
630
|
key: genrePlaylist.uuid,
|
|
501
|
-
content: /* @__PURE__ */
|
|
631
|
+
content: /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
502
632
|
genrePlaylist.name,
|
|
503
|
-
genrePlaylist.isUmbrella && /* @__PURE__ */
|
|
504
|
-
] })
|
|
633
|
+
genrePlaylist.isUmbrella && /* @__PURE__ */ jsx8("span", { className: "ml-2 text-xs italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
634
|
+
] }),
|
|
635
|
+
onSelect: () => select(genrePlaylist)
|
|
505
636
|
}))
|
|
506
637
|
},
|
|
507
|
-
...extraGroups.map(
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
options: active ? group.items.map((item) => ({ key: item.key, content: item.content })) : []
|
|
511
|
-
}))
|
|
638
|
+
...extraGroups.map(
|
|
639
|
+
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
640
|
+
)
|
|
512
641
|
];
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
642
|
+
return /* @__PURE__ */ jsx8("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ jsx8(
|
|
643
|
+
SearchBox,
|
|
644
|
+
{
|
|
645
|
+
label,
|
|
646
|
+
placeholder,
|
|
647
|
+
value: query,
|
|
648
|
+
onValueChange: (value) => {
|
|
649
|
+
setQuery(value);
|
|
650
|
+
onQueryChange?.(value);
|
|
651
|
+
},
|
|
652
|
+
groups,
|
|
653
|
+
onPicked: (groupKey) => {
|
|
654
|
+
if (groupKey !== "genres") setQuery("");
|
|
655
|
+
onQueryChange?.("");
|
|
656
|
+
},
|
|
657
|
+
onClear: canClear ? () => {
|
|
658
|
+
clear();
|
|
659
|
+
onQueryChange?.("");
|
|
660
|
+
} : void 0,
|
|
661
|
+
leading: onPrevious && /* @__PURE__ */ jsx8("button", { type: "button", "aria-label": "Previous", title: "Previous", className: SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ jsx8(ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
662
|
+
onFocus
|
|
521
663
|
}
|
|
522
|
-
|
|
523
|
-
if (!item) return;
|
|
524
|
-
setQuery("");
|
|
525
|
-
onQueryChange?.("");
|
|
526
|
-
item.onSelect();
|
|
527
|
-
};
|
|
528
|
-
return /* @__PURE__ */ jsxs2("div", { className: "genre-search relative w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: [
|
|
529
|
-
onPrevious && /* @__PURE__ */ jsx5(
|
|
530
|
-
"button",
|
|
531
|
-
{
|
|
532
|
-
type: "button",
|
|
533
|
-
"aria-label": "Previous",
|
|
534
|
-
title: "Previous",
|
|
535
|
-
className: "genre-search-previous absolute left-2 top-1/2 z-10 flex items-center justify-center p-1.5 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full -translate-y-1/2 hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
536
|
-
onClick: onPrevious,
|
|
537
|
-
children: /* @__PURE__ */ jsx5(ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
538
|
-
}
|
|
539
|
-
),
|
|
540
|
-
/* @__PURE__ */ jsx5(
|
|
541
|
-
Combobox,
|
|
542
|
-
{
|
|
543
|
-
ariaLabel: label,
|
|
544
|
-
listLabel: `${label} results`,
|
|
545
|
-
value: query,
|
|
546
|
-
placeholder: placeholder ?? `${label}\u2026`,
|
|
547
|
-
groups,
|
|
548
|
-
open,
|
|
549
|
-
onOpenChange: setOpen,
|
|
550
|
-
onValueChange: (value) => {
|
|
551
|
-
setQuery(value);
|
|
552
|
-
onQueryChange?.(value);
|
|
553
|
-
},
|
|
554
|
-
onPick: handlePick,
|
|
555
|
-
status,
|
|
556
|
-
loading: loading ? "Searching\u2026" : void 0,
|
|
557
|
-
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md focus-visible:ring-0`,
|
|
558
|
-
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
559
|
-
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
560
|
-
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
561
|
-
}
|
|
562
|
-
),
|
|
563
|
-
/* @__PURE__ */ jsxs2("div", { className: "absolute right-3 top-1/2 flex items-center -translate-y-1/2", children: [
|
|
564
|
-
/* @__PURE__ */ jsxs2("span", { "data-loading": loading, className: "relative p-2 text-[var(--color-text-muted)] pointer-events-none", children: [
|
|
565
|
-
/* @__PURE__ */ jsx5(
|
|
566
|
-
Search,
|
|
567
|
-
{
|
|
568
|
-
"aria-hidden": "true",
|
|
569
|
-
className: `w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-0" : ""}`
|
|
570
|
-
}
|
|
571
|
-
),
|
|
572
|
-
/* @__PURE__ */ jsx5(
|
|
573
|
-
LoaderCircle,
|
|
574
|
-
{
|
|
575
|
-
"aria-hidden": "true",
|
|
576
|
-
className: `absolute inset-2 w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-100 delay-300 animate-spin motion-reduce:animate-none" : "opacity-0"}`
|
|
577
|
-
}
|
|
578
|
-
)
|
|
579
|
-
] }),
|
|
580
|
-
canClear && /* @__PURE__ */ jsx5(
|
|
581
|
-
"button",
|
|
582
|
-
{
|
|
583
|
-
type: "button",
|
|
584
|
-
"aria-label": "Clear search",
|
|
585
|
-
className: "flex items-center justify-center p-2 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
586
|
-
onClick: () => {
|
|
587
|
-
clear();
|
|
588
|
-
onQueryChange?.("");
|
|
589
|
-
},
|
|
590
|
-
children: /* @__PURE__ */ jsx5(X, { "aria-hidden": "true", className: "w-6 h-6" })
|
|
591
|
-
}
|
|
592
|
-
)
|
|
593
|
-
] })
|
|
594
|
-
] });
|
|
664
|
+
) });
|
|
595
665
|
}
|
|
596
666
|
|
|
597
667
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
@@ -611,12 +681,12 @@ import { FaWhatsapp } from "react-icons/fa";
|
|
|
611
681
|
import { SiGmail } from "react-icons/si";
|
|
612
682
|
|
|
613
683
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
614
|
-
import { Fragment as
|
|
684
|
+
import { Fragment as Fragment4, jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
615
685
|
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";
|
|
616
686
|
function Contents({ icon, label }) {
|
|
617
|
-
return /* @__PURE__ */
|
|
618
|
-
/* @__PURE__ */
|
|
619
|
-
/* @__PURE__ */
|
|
687
|
+
return /* @__PURE__ */ jsxs6(Fragment4, { children: [
|
|
688
|
+
/* @__PURE__ */ jsx9("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 }),
|
|
689
|
+
/* @__PURE__ */ jsx9("span", { className: "max-w-full truncate", children: label })
|
|
620
690
|
] });
|
|
621
691
|
}
|
|
622
692
|
function ShareDestinationButton({
|
|
@@ -628,29 +698,29 @@ function ShareDestinationButton({
|
|
|
628
698
|
ariaLabel
|
|
629
699
|
}) {
|
|
630
700
|
if (href) {
|
|
631
|
-
return /* @__PURE__ */
|
|
701
|
+
return /* @__PURE__ */ jsx9("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx9(Contents, { icon, label }) });
|
|
632
702
|
}
|
|
633
|
-
return /* @__PURE__ */
|
|
703
|
+
return /* @__PURE__ */ jsx9("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ jsx9(Contents, { icon, label }) });
|
|
634
704
|
}
|
|
635
705
|
|
|
636
706
|
// src/genre-tree/SharePreviewCard.tsx
|
|
637
707
|
import { Music, Music2, UserRound } from "lucide-react";
|
|
638
|
-
import { jsx as
|
|
708
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
639
709
|
function SharePreviewCard({ target, url }) {
|
|
640
710
|
const Icon = { genre: Music2, artist: UserRound, song: Music }[target.kind];
|
|
641
711
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
642
|
-
return /* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
644
|
-
/* @__PURE__ */
|
|
645
|
-
/* @__PURE__ */
|
|
646
|
-
/* @__PURE__ */
|
|
647
|
-
/* @__PURE__ */
|
|
712
|
+
return /* @__PURE__ */ jsx10("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: [
|
|
713
|
+
/* @__PURE__ */ jsx10("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ jsx10(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
714
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
715
|
+
/* @__PURE__ */ jsx10("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
716
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
717
|
+
/* @__PURE__ */ jsx10("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
648
718
|
] })
|
|
649
719
|
] }) });
|
|
650
720
|
}
|
|
651
721
|
|
|
652
722
|
// src/genre-tree/SharePopup.tsx
|
|
653
|
-
import { Fragment as
|
|
723
|
+
import { Fragment as Fragment5, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
654
724
|
function resolveAbsoluteUrl(url) {
|
|
655
725
|
try {
|
|
656
726
|
const parsed = new URL(url, window.location.href);
|
|
@@ -725,8 +795,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
725
795
|
setShareError("Could not open the share options.");
|
|
726
796
|
}
|
|
727
797
|
};
|
|
728
|
-
return /* @__PURE__ */
|
|
729
|
-
/* @__PURE__ */
|
|
798
|
+
return /* @__PURE__ */ jsxs8(Fragment5, { children: [
|
|
799
|
+
/* @__PURE__ */ jsx11(
|
|
730
800
|
BasePopup_default,
|
|
731
801
|
{
|
|
732
802
|
title: "Share",
|
|
@@ -741,67 +811,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
741
811
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
742
812
|
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)]",
|
|
743
813
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
744
|
-
children: /* @__PURE__ */
|
|
745
|
-
/* @__PURE__ */
|
|
746
|
-
/* @__PURE__ */
|
|
747
|
-
/* @__PURE__ */
|
|
814
|
+
children: /* @__PURE__ */ jsxs8("div", { className: "flex flex-col gap-4", children: [
|
|
815
|
+
/* @__PURE__ */ jsx11(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
816
|
+
/* @__PURE__ */ jsxs8("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
817
|
+
/* @__PURE__ */ jsx11(
|
|
748
818
|
ShareDestinationButton,
|
|
749
819
|
{
|
|
750
820
|
label: "Copy Link",
|
|
751
|
-
icon: /* @__PURE__ */
|
|
821
|
+
icon: /* @__PURE__ */ jsx11(Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
752
822
|
onClick: () => void handleCopy(),
|
|
753
823
|
ariaLabel: "Copy link",
|
|
754
824
|
disabled: !absoluteUrl
|
|
755
825
|
}
|
|
756
826
|
),
|
|
757
|
-
/* @__PURE__ */
|
|
827
|
+
/* @__PURE__ */ jsx11(
|
|
758
828
|
ShareDestinationButton,
|
|
759
829
|
{
|
|
760
830
|
label: "WhatsApp",
|
|
761
|
-
icon: /* @__PURE__ */
|
|
831
|
+
icon: /* @__PURE__ */ jsx11(FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
762
832
|
href: destinationUrls?.whatsapp,
|
|
763
833
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
764
834
|
disabled: !absoluteUrl
|
|
765
835
|
}
|
|
766
836
|
),
|
|
767
|
-
/* @__PURE__ */
|
|
837
|
+
/* @__PURE__ */ jsx11(
|
|
768
838
|
ShareDestinationButton,
|
|
769
839
|
{
|
|
770
840
|
label: "Gmail",
|
|
771
|
-
icon: /* @__PURE__ */
|
|
841
|
+
icon: /* @__PURE__ */ jsx11(SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
772
842
|
href: destinationUrls?.gmail,
|
|
773
843
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
774
844
|
disabled: !absoluteUrl
|
|
775
845
|
}
|
|
776
846
|
),
|
|
777
|
-
canUseNativeShare && /* @__PURE__ */
|
|
847
|
+
canUseNativeShare && /* @__PURE__ */ jsx11(
|
|
778
848
|
ShareDestinationButton,
|
|
779
849
|
{
|
|
780
850
|
label: "More",
|
|
781
|
-
icon: /* @__PURE__ */
|
|
851
|
+
icon: /* @__PURE__ */ jsx11(Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
782
852
|
onClick: () => void handleNativeShare(),
|
|
783
853
|
ariaLabel: "More sharing options",
|
|
784
854
|
disabled: !absoluteUrl
|
|
785
855
|
}
|
|
786
856
|
)
|
|
787
857
|
] }),
|
|
788
|
-
copyStatus === "failed" && /* @__PURE__ */
|
|
789
|
-
shareError && /* @__PURE__ */
|
|
858
|
+
copyStatus === "failed" && /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
859
|
+
shareError && /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
790
860
|
] })
|
|
791
861
|
}
|
|
792
862
|
),
|
|
793
|
-
toastId > 0 && /* @__PURE__ */
|
|
863
|
+
toastId > 0 && /* @__PURE__ */ jsx11(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
794
864
|
] });
|
|
795
865
|
}
|
|
796
866
|
|
|
797
867
|
// src/genre-tree/ShareActionButton.tsx
|
|
798
|
-
import { jsx as
|
|
868
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
799
869
|
function ShareActionButton({ target, url }) {
|
|
800
870
|
const { showPopup, hidePopup } = usePopup3();
|
|
801
871
|
const openSharePopup = () => {
|
|
802
|
-
showPopup(/* @__PURE__ */
|
|
872
|
+
showPopup(/* @__PURE__ */ jsx12(SharePopup, { target, url, onClose: hidePopup }));
|
|
803
873
|
};
|
|
804
|
-
return /* @__PURE__ */
|
|
874
|
+
return /* @__PURE__ */ jsxs9(
|
|
805
875
|
"button",
|
|
806
876
|
{
|
|
807
877
|
type: "button",
|
|
@@ -810,7 +880,7 @@ function ShareActionButton({ target, url }) {
|
|
|
810
880
|
"aria-label": `Share ${target.name}`,
|
|
811
881
|
title: `Share ${target.name}`,
|
|
812
882
|
children: [
|
|
813
|
-
/* @__PURE__ */
|
|
883
|
+
/* @__PURE__ */ jsx12("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__ */ jsx12(Share22, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
814
884
|
"Share"
|
|
815
885
|
]
|
|
816
886
|
}
|
|
@@ -818,7 +888,7 @@ function ShareActionButton({ target, url }) {
|
|
|
818
888
|
}
|
|
819
889
|
|
|
820
890
|
// src/genre-tree/GenreActionBand.tsx
|
|
821
|
-
import { jsx as
|
|
891
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
822
892
|
function GenreActionBand({
|
|
823
893
|
node,
|
|
824
894
|
onPlayPause,
|
|
@@ -840,7 +910,7 @@ function GenreActionBand({
|
|
|
840
910
|
items.push({
|
|
841
911
|
key: "play-pause",
|
|
842
912
|
label: isPlaying ? "Pause" : "Play",
|
|
843
|
-
icon: isPlaying ? /* @__PURE__ */
|
|
913
|
+
icon: isPlaying ? /* @__PURE__ */ jsx13(Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx13(Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
844
914
|
onClick: onPlayPause
|
|
845
915
|
});
|
|
846
916
|
}
|
|
@@ -848,24 +918,24 @@ function GenreActionBand({
|
|
|
848
918
|
items.push({
|
|
849
919
|
key: "zoom-in",
|
|
850
920
|
label: "Focus",
|
|
851
|
-
icon: /* @__PURE__ */
|
|
921
|
+
icon: /* @__PURE__ */ jsx13(ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
852
922
|
onClick: onZoomIn
|
|
853
923
|
});
|
|
854
924
|
}
|
|
855
925
|
if (onAddChild) {
|
|
856
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */
|
|
926
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ jsx13(Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
857
927
|
}
|
|
858
928
|
if (onRename) {
|
|
859
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */
|
|
929
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ jsx13(Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
860
930
|
}
|
|
861
931
|
if (onChangeParent) {
|
|
862
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */
|
|
932
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ jsx13(MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
863
933
|
}
|
|
864
934
|
if (onToggleSide) {
|
|
865
935
|
adminItems.push({
|
|
866
936
|
key: "toggle-side",
|
|
867
937
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
868
|
-
icon: /* @__PURE__ */
|
|
938
|
+
icon: /* @__PURE__ */ jsx13(ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
869
939
|
onClick: onToggleSide,
|
|
870
940
|
disabled: !!toggleSideDisabledReason,
|
|
871
941
|
title: toggleSideDisabledReason
|
|
@@ -881,7 +951,7 @@ function GenreActionBand({
|
|
|
881
951
|
});
|
|
882
952
|
}
|
|
883
953
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
884
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */
|
|
954
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ jsxs10(
|
|
885
955
|
"button",
|
|
886
956
|
{
|
|
887
957
|
type: "button",
|
|
@@ -890,25 +960,25 @@ function GenreActionBand({
|
|
|
890
960
|
onClick: item.onClick,
|
|
891
961
|
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",
|
|
892
962
|
children: [
|
|
893
|
-
/* @__PURE__ */
|
|
963
|
+
/* @__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)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
|
|
894
964
|
item.label
|
|
895
965
|
]
|
|
896
966
|
},
|
|
897
967
|
item.key
|
|
898
968
|
));
|
|
899
|
-
return /* @__PURE__ */
|
|
900
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */
|
|
969
|
+
return /* @__PURE__ */ jsxs10("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
970
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
901
971
|
renderItems(items),
|
|
902
|
-
shareTarget && shareUrl && /* @__PURE__ */
|
|
972
|
+
shareTarget && shareUrl && /* @__PURE__ */ jsx13(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
903
973
|
] }),
|
|
904
|
-
adminItems.length > 0 && /* @__PURE__ */
|
|
974
|
+
adminItems.length > 0 && /* @__PURE__ */ jsxs10(
|
|
905
975
|
"section",
|
|
906
976
|
{
|
|
907
977
|
"aria-label": "Admin actions",
|
|
908
978
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
909
979
|
children: [
|
|
910
|
-
/* @__PURE__ */
|
|
911
|
-
/* @__PURE__ */
|
|
980
|
+
/* @__PURE__ */ jsx13("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
981
|
+
/* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
912
982
|
]
|
|
913
983
|
}
|
|
914
984
|
)
|
|
@@ -921,7 +991,7 @@ import { useFetchGenreOverview } from "@behindthemusictree/app-kit/genre-tree";
|
|
|
921
991
|
|
|
922
992
|
// src/genre-tree/ClampedText.tsx
|
|
923
993
|
import { useLayoutEffect, useRef as useRef2, useState as useState4 } from "react";
|
|
924
|
-
import { Fragment as
|
|
994
|
+
import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
925
995
|
function ClampedText({ children, className: className2 = "" }) {
|
|
926
996
|
const ref = useRef2(null);
|
|
927
997
|
const [expanded, setExpanded] = useState4(false);
|
|
@@ -931,9 +1001,9 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
931
1001
|
if (!element || expanded) return;
|
|
932
1002
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
933
1003
|
}, [children, expanded]);
|
|
934
|
-
return /* @__PURE__ */
|
|
935
|
-
/* @__PURE__ */
|
|
936
|
-
(overflows || expanded) && /* @__PURE__ */
|
|
1004
|
+
return /* @__PURE__ */ jsxs11(Fragment6, { children: [
|
|
1005
|
+
/* @__PURE__ */ jsx14("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1006
|
+
(overflows || expanded) && /* @__PURE__ */ jsx14(
|
|
937
1007
|
"button",
|
|
938
1008
|
{
|
|
939
1009
|
type: "button",
|
|
@@ -967,15 +1037,15 @@ import { useTrackList as useTrackList2 } from "@behindthemusictree/app-kit/genre
|
|
|
967
1037
|
|
|
968
1038
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
969
1039
|
import { musicBrainzRecordingUrl } from "@behindthemusictree/app-kit/genre-tree";
|
|
970
|
-
import { Fragment as
|
|
1040
|
+
import { Fragment as Fragment7, jsx as jsx15 } from "react/jsx-runtime";
|
|
971
1041
|
function MusicBrainzRecordingLink({
|
|
972
1042
|
song,
|
|
973
1043
|
children,
|
|
974
1044
|
className: className2 = ""
|
|
975
1045
|
}) {
|
|
976
1046
|
const url = musicBrainzRecordingUrl(song);
|
|
977
|
-
if (!url) return /* @__PURE__ */
|
|
978
|
-
return /* @__PURE__ */
|
|
1047
|
+
if (!url) return /* @__PURE__ */ jsx15(Fragment7, { children });
|
|
1048
|
+
return /* @__PURE__ */ jsx15(
|
|
979
1049
|
"a",
|
|
980
1050
|
{
|
|
981
1051
|
href: url,
|
|
@@ -992,7 +1062,7 @@ function MusicBrainzRecordingLink({
|
|
|
992
1062
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
993
1063
|
import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
|
|
994
1064
|
import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
|
|
995
|
-
import { jsx as
|
|
1065
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
996
1066
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
997
1067
|
var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
|
|
998
1068
|
overallTotal: z.number().nullable(),
|
|
@@ -1065,35 +1135,35 @@ function GenrePlaylistTracks({
|
|
|
1065
1135
|
const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1066
1136
|
started.catch((error) => {
|
|
1067
1137
|
console.error("Failed to play genre playlist:", error);
|
|
1068
|
-
showPopup(/* @__PURE__ */
|
|
1138
|
+
showPopup(/* @__PURE__ */ jsx16(InternalErrorPopup, { errorCode: ErrorCode3.CLIENT_UNKNOWN }));
|
|
1069
1139
|
});
|
|
1070
1140
|
};
|
|
1071
|
-
return /* @__PURE__ */
|
|
1141
|
+
return /* @__PURE__ */ jsx16(PanelSection, { title: `${capitalize(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ jsxs12("div", { children: [
|
|
1072
1142
|
renderTracksToolbar?.(),
|
|
1073
|
-
isPending ? /* @__PURE__ */
|
|
1143
|
+
isPending ? /* @__PURE__ */ jsx16(Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ jsxs12("p", { role: "alert", children: [
|
|
1074
1144
|
"Couldn't load ",
|
|
1075
1145
|
trackNoun.plural,
|
|
1076
1146
|
".",
|
|
1077
1147
|
" ",
|
|
1078
|
-
/* @__PURE__ */
|
|
1079
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */
|
|
1148
|
+
/* @__PURE__ */ jsx16("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1149
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ jsx16("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: [
|
|
1080
1150
|
tracks.map((track, index) => {
|
|
1081
1151
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1082
1152
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1083
1153
|
const isPlaying = isCurrent && playState === PlayStates.PLAYING;
|
|
1084
|
-
const text = /* @__PURE__ */
|
|
1085
|
-
/* @__PURE__ */
|
|
1086
|
-
track.artists?.length ? /* @__PURE__ */
|
|
1087
|
-
/* @__PURE__ */
|
|
1088
|
-
track.album && /* @__PURE__ */
|
|
1154
|
+
const text = /* @__PURE__ */ jsxs12("span", { className: TEXT_CELL, children: [
|
|
1155
|
+
/* @__PURE__ */ jsx16("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : isSong(track) ? /* @__PURE__ */ jsx16(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1156
|
+
track.artists?.length ? /* @__PURE__ */ jsx16("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1157
|
+
/* @__PURE__ */ jsx16("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ jsx16(ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1158
|
+
track.album && /* @__PURE__ */ jsx16("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1089
1159
|
] });
|
|
1090
1160
|
const leading = renderSongLeading?.(track, index);
|
|
1091
1161
|
const actions = renderSongActions?.(track);
|
|
1092
1162
|
const isUnplayable = isSong(track) && songUnplayableReason(track) !== null;
|
|
1093
1163
|
if (isUnplayable) {
|
|
1094
|
-
return /* @__PURE__ */
|
|
1164
|
+
return /* @__PURE__ */ jsxs12("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1095
1165
|
leading,
|
|
1096
|
-
/* @__PURE__ */
|
|
1166
|
+
/* @__PURE__ */ jsx16(
|
|
1097
1167
|
"button",
|
|
1098
1168
|
{
|
|
1099
1169
|
type: "button",
|
|
@@ -1101,27 +1171,27 @@ function GenrePlaylistTracks({
|
|
|
1101
1171
|
"aria-disabled": "true",
|
|
1102
1172
|
"aria-label": label,
|
|
1103
1173
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1104
|
-
children: /* @__PURE__ */
|
|
1174
|
+
children: /* @__PURE__ */ jsx16(FaPlay, { size: 10 })
|
|
1105
1175
|
}
|
|
1106
1176
|
),
|
|
1107
1177
|
text,
|
|
1108
1178
|
actions
|
|
1109
1179
|
] }, track.uuid);
|
|
1110
1180
|
}
|
|
1111
|
-
return /* @__PURE__ */
|
|
1181
|
+
return /* @__PURE__ */ jsxs12(
|
|
1112
1182
|
"li",
|
|
1113
1183
|
{
|
|
1114
1184
|
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)]" : ""}`,
|
|
1115
1185
|
children: [
|
|
1116
1186
|
leading,
|
|
1117
|
-
/* @__PURE__ */
|
|
1187
|
+
/* @__PURE__ */ jsx16(
|
|
1118
1188
|
"button",
|
|
1119
1189
|
{
|
|
1120
1190
|
type: "button",
|
|
1121
1191
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1122
1192
|
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"}`,
|
|
1123
1193
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1124
|
-
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */
|
|
1194
|
+
children: isCurrent && playState === PlayStates.LOADING ? /* @__PURE__ */ jsx16(RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ jsx16(FaPause, { size: 10 }) : /* @__PURE__ */ jsx16(FaPlay, { size: 10 })
|
|
1125
1195
|
}
|
|
1126
1196
|
),
|
|
1127
1197
|
text,
|
|
@@ -1131,7 +1201,7 @@ function GenrePlaylistTracks({
|
|
|
1131
1201
|
track.uuid
|
|
1132
1202
|
);
|
|
1133
1203
|
}),
|
|
1134
|
-
hasNextPage && /* @__PURE__ */
|
|
1204
|
+
hasNextPage && /* @__PURE__ */ jsx16("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1135
1205
|
] })
|
|
1136
1206
|
] }) });
|
|
1137
1207
|
}
|
|
@@ -1139,7 +1209,7 @@ function GenrePlaylistTracks({
|
|
|
1139
1209
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1140
1210
|
import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
|
|
1141
1211
|
import { SiWikidata, SiWikipedia } from "react-icons/si";
|
|
1142
|
-
import { Fragment as
|
|
1212
|
+
import { Fragment as Fragment8, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1143
1213
|
function GenreDetailExtras({
|
|
1144
1214
|
genreUuid,
|
|
1145
1215
|
genrePlaylist,
|
|
@@ -1163,17 +1233,17 @@ function GenreDetailExtras({
|
|
|
1163
1233
|
criteriaOverviewSchema
|
|
1164
1234
|
);
|
|
1165
1235
|
if (isPending) {
|
|
1166
|
-
return /* @__PURE__ */
|
|
1167
|
-
/* @__PURE__ */
|
|
1236
|
+
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1237
|
+
/* @__PURE__ */ jsx17(PanelSection2, { title: "Summary", children: /* @__PURE__ */ jsx17("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ jsx17(Skeleton2, { className: "h-4 w-3/4" }) }) }),
|
|
1168
1238
|
family
|
|
1169
1239
|
] });
|
|
1170
1240
|
}
|
|
1171
|
-
if (!overview) return /* @__PURE__ */
|
|
1241
|
+
if (!overview) return /* @__PURE__ */ jsx17(Fragment8, { children: family });
|
|
1172
1242
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1173
1243
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1174
|
-
return /* @__PURE__ */
|
|
1175
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */
|
|
1176
|
-
summarySourceUrl && /* @__PURE__ */
|
|
1244
|
+
return /* @__PURE__ */ jsxs13(Fragment8, { children: [
|
|
1245
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ jsx17(PanelSection2, { title: "Links", children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1246
|
+
summarySourceUrl && /* @__PURE__ */ jsxs13(
|
|
1177
1247
|
"a",
|
|
1178
1248
|
{
|
|
1179
1249
|
href: summarySourceUrl,
|
|
@@ -1181,12 +1251,12 @@ function GenreDetailExtras({
|
|
|
1181
1251
|
rel: "noopener noreferrer",
|
|
1182
1252
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1183
1253
|
children: [
|
|
1184
|
-
/* @__PURE__ */
|
|
1254
|
+
/* @__PURE__ */ jsx17(SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1185
1255
|
"Wikipedia"
|
|
1186
1256
|
]
|
|
1187
1257
|
}
|
|
1188
1258
|
),
|
|
1189
|
-
wikidataUrl && /* @__PURE__ */
|
|
1259
|
+
wikidataUrl && /* @__PURE__ */ jsxs13(
|
|
1190
1260
|
"a",
|
|
1191
1261
|
{
|
|
1192
1262
|
href: wikidataUrl,
|
|
@@ -1194,17 +1264,17 @@ function GenreDetailExtras({
|
|
|
1194
1264
|
rel: "noopener noreferrer",
|
|
1195
1265
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1196
1266
|
children: [
|
|
1197
|
-
/* @__PURE__ */
|
|
1267
|
+
/* @__PURE__ */ jsx17(SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1198
1268
|
"Wikidata"
|
|
1199
1269
|
]
|
|
1200
1270
|
}
|
|
1201
1271
|
)
|
|
1202
1272
|
] }) }),
|
|
1203
|
-
/* @__PURE__ */
|
|
1273
|
+
/* @__PURE__ */ jsx17(PanelSection2, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ jsx17(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1204
1274
|
family,
|
|
1205
1275
|
renderGenreDetailExtras?.(overview),
|
|
1206
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1276
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ jsx17(PanelSection2, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ jsx17("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ jsx17("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ jsx17("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1277
|
+
/* @__PURE__ */ jsx17(
|
|
1208
1278
|
GenrePlaylistTracks,
|
|
1209
1279
|
{
|
|
1210
1280
|
genreUuid,
|
|
@@ -1220,7 +1290,7 @@ function GenreDetailExtras({
|
|
|
1220
1290
|
}
|
|
1221
1291
|
|
|
1222
1292
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1223
|
-
import { Fragment as
|
|
1293
|
+
import { Fragment as Fragment9, jsx as jsx18, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1224
1294
|
import { createElement } from "react";
|
|
1225
1295
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1226
1296
|
const { isPlaying, setIsPlaying } = usePlayer5();
|
|
@@ -1234,7 +1304,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1234
1304
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1235
1305
|
});
|
|
1236
1306
|
};
|
|
1237
|
-
return /* @__PURE__ */
|
|
1307
|
+
return /* @__PURE__ */ jsx18(
|
|
1238
1308
|
GenreActionBand,
|
|
1239
1309
|
{
|
|
1240
1310
|
...bandProps,
|
|
@@ -1255,9 +1325,9 @@ function GenreInfoPanelBody({
|
|
|
1255
1325
|
band,
|
|
1256
1326
|
extras
|
|
1257
1327
|
}) {
|
|
1258
|
-
return /* @__PURE__ */
|
|
1328
|
+
return /* @__PURE__ */ jsxs14(Fragment9, { children: [
|
|
1259
1329
|
toolbar,
|
|
1260
|
-
/* @__PURE__ */
|
|
1330
|
+
/* @__PURE__ */ jsx18(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1261
1331
|
/* @__PURE__ */ createElement(
|
|
1262
1332
|
GenreDetailExtras,
|
|
1263
1333
|
{
|
|
@@ -1280,7 +1350,7 @@ import {
|
|
|
1280
1350
|
getReparentCandidates,
|
|
1281
1351
|
useUpdateGenre
|
|
1282
1352
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1283
|
-
import { jsx as
|
|
1353
|
+
import { jsx as jsx19, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1284
1354
|
function ChangeParentPopup({
|
|
1285
1355
|
genrePlaylist,
|
|
1286
1356
|
genrePlaylists,
|
|
@@ -1315,12 +1385,12 @@ function ChangeParentPopup({
|
|
|
1315
1385
|
window.addEventListener("keydown", handleKeyDown);
|
|
1316
1386
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1317
1387
|
}, [onClose]);
|
|
1318
|
-
return /* @__PURE__ */
|
|
1319
|
-
/* @__PURE__ */
|
|
1388
|
+
return /* @__PURE__ */ jsxs15(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1389
|
+
/* @__PURE__ */ jsxs15("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1320
1390
|
"Current parent: ",
|
|
1321
1391
|
genrePlaylist.parent?.name ?? "none"
|
|
1322
1392
|
] }),
|
|
1323
|
-
/* @__PURE__ */
|
|
1393
|
+
/* @__PURE__ */ jsx19("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx19(
|
|
1324
1394
|
GenreSearch,
|
|
1325
1395
|
{
|
|
1326
1396
|
genrePlaylists: candidates,
|
|
@@ -1328,7 +1398,7 @@ function ChangeParentPopup({
|
|
|
1328
1398
|
placeholder: "Search a new parent\u2026"
|
|
1329
1399
|
}
|
|
1330
1400
|
) }),
|
|
1331
|
-
formErrors.length > 0 && /* @__PURE__ */
|
|
1401
|
+
formErrors.length > 0 && /* @__PURE__ */ jsx19("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1332
1402
|
] });
|
|
1333
1403
|
}
|
|
1334
1404
|
|
|
@@ -1352,7 +1422,7 @@ import {
|
|
|
1352
1422
|
SiYoutube
|
|
1353
1423
|
} from "react-icons/si";
|
|
1354
1424
|
import { Chip } from "@behindthemusictree/ui";
|
|
1355
|
-
import { jsx as
|
|
1425
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
1356
1426
|
var LINK_KINDS = {
|
|
1357
1427
|
official: { label: "Official website", Icon: Globe },
|
|
1358
1428
|
wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
|
|
@@ -1389,7 +1459,7 @@ function artistLinks(artist) {
|
|
|
1389
1459
|
return links;
|
|
1390
1460
|
}
|
|
1391
1461
|
function ArtistLinks({ links }) {
|
|
1392
|
-
return /* @__PURE__ */
|
|
1462
|
+
return /* @__PURE__ */ jsx20("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx20("li", { children: /* @__PURE__ */ jsx20(Chip, { href: url, icon: /* @__PURE__ */ jsx20(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1393
1463
|
}
|
|
1394
1464
|
|
|
1395
1465
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1400,7 +1470,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1400
1470
|
}
|
|
1401
1471
|
|
|
1402
1472
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1403
|
-
import { jsx as
|
|
1473
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
1404
1474
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1405
1475
|
function ArtistHeader({ artist }) {
|
|
1406
1476
|
const fields = [];
|
|
@@ -1410,35 +1480,35 @@ function ArtistHeader({ artist }) {
|
|
|
1410
1480
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1411
1481
|
if (years) fields.push({ label: "Active", value: years });
|
|
1412
1482
|
const links = artistLinks(artist);
|
|
1413
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */
|
|
1483
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx21(ArtistLinks, { links }) });
|
|
1414
1484
|
if (fields.length === 0) return null;
|
|
1415
|
-
return /* @__PURE__ */
|
|
1485
|
+
return /* @__PURE__ */ jsx21(FieldList, { items: fields });
|
|
1416
1486
|
}
|
|
1417
1487
|
|
|
1418
1488
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1419
|
-
import { jsx as
|
|
1489
|
+
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1420
1490
|
function ExternalLink({ href, children }) {
|
|
1421
|
-
return /* @__PURE__ */
|
|
1491
|
+
return /* @__PURE__ */ jsx22("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1422
1492
|
}
|
|
1423
1493
|
function ArtistPhoto({ image, name }) {
|
|
1424
|
-
return /* @__PURE__ */
|
|
1425
|
-
/* @__PURE__ */
|
|
1426
|
-
/* @__PURE__ */
|
|
1494
|
+
return /* @__PURE__ */ jsxs16("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1495
|
+
/* @__PURE__ */ jsx22("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1496
|
+
/* @__PURE__ */ jsxs16("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1427
1497
|
"Photo: ",
|
|
1428
|
-
/* @__PURE__ */
|
|
1498
|
+
/* @__PURE__ */ jsx22(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1429
1499
|
",",
|
|
1430
1500
|
" ",
|
|
1431
|
-
image.licenseUrl ? /* @__PURE__ */
|
|
1501
|
+
image.licenseUrl ? /* @__PURE__ */ jsx22(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1432
1502
|
] })
|
|
1433
1503
|
] });
|
|
1434
1504
|
}
|
|
1435
1505
|
|
|
1436
1506
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1437
1507
|
import { PanelGroup, PanelSection as PanelSection3 } from "@behindthemusictree/ui";
|
|
1438
|
-
import { jsx as
|
|
1508
|
+
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1439
1509
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1440
1510
|
if (memberships.length === 0) return null;
|
|
1441
|
-
return /* @__PURE__ */
|
|
1511
|
+
return /* @__PURE__ */ jsx23(PanelGroup, { title, children: /* @__PURE__ */ jsx23("ul", { children: memberships.map((membership, index) => {
|
|
1442
1512
|
const details = [
|
|
1443
1513
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1444
1514
|
...membership.roles
|
|
@@ -1446,9 +1516,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1446
1516
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1447
1517
|
return (
|
|
1448
1518
|
// A member can have several periods in the same group.
|
|
1449
|
-
/* @__PURE__ */
|
|
1450
|
-
/* @__PURE__ */
|
|
1451
|
-
detailsText && /* @__PURE__ */
|
|
1519
|
+
/* @__PURE__ */ jsxs17("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1520
|
+
/* @__PURE__ */ jsx23("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1521
|
+
detailsText && /* @__PURE__ */ jsxs17("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1452
1522
|
" \u2014 ",
|
|
1453
1523
|
detailsText
|
|
1454
1524
|
] })
|
|
@@ -1459,9 +1529,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1459
1529
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1460
1530
|
if (memberships.length === 0) return null;
|
|
1461
1531
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1462
|
-
return /* @__PURE__ */
|
|
1463
|
-
/* @__PURE__ */
|
|
1464
|
-
/* @__PURE__ */
|
|
1532
|
+
return /* @__PURE__ */ jsxs17(PanelSection3, { title, children: [
|
|
1533
|
+
/* @__PURE__ */ jsx23(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1534
|
+
/* @__PURE__ */ jsx23(
|
|
1465
1535
|
MembershipList,
|
|
1466
1536
|
{
|
|
1467
1537
|
title: "Past",
|
|
@@ -1474,7 +1544,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1474
1544
|
|
|
1475
1545
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1476
1546
|
import { PanelGroup as PanelGroup2, PanelSection as PanelSection4 } from "@behindthemusictree/ui";
|
|
1477
|
-
import { jsx as
|
|
1547
|
+
import { jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1478
1548
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1479
1549
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1480
1550
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1494,12 +1564,12 @@ function byYear(a, b) {
|
|
|
1494
1564
|
function ArtistDiscography({ albums }) {
|
|
1495
1565
|
if (albums.length === 0) return null;
|
|
1496
1566
|
const sorted = [...albums].sort(byYear);
|
|
1497
|
-
return /* @__PURE__ */
|
|
1567
|
+
return /* @__PURE__ */ jsx24(PanelSection4, { title: "Discography", children: GROUPS.map((group) => {
|
|
1498
1568
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1499
1569
|
if (groupAlbums.length === 0) return null;
|
|
1500
|
-
return /* @__PURE__ */
|
|
1501
|
-
/* @__PURE__ */
|
|
1502
|
-
/* @__PURE__ */
|
|
1570
|
+
return /* @__PURE__ */ jsx24(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx24("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs18("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1571
|
+
/* @__PURE__ */ jsx24("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1572
|
+
/* @__PURE__ */ jsx24("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1503
1573
|
] }, album.uuid)) }) }, group);
|
|
1504
1574
|
}) });
|
|
1505
1575
|
}
|
|
@@ -1526,7 +1596,7 @@ import { useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre
|
|
|
1526
1596
|
import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
|
|
1527
1597
|
import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
|
|
1528
1598
|
import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
|
|
1529
|
-
import { jsx as
|
|
1599
|
+
import { jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1530
1600
|
function TrackPositionPlayPause({
|
|
1531
1601
|
position,
|
|
1532
1602
|
uuid,
|
|
@@ -1540,14 +1610,14 @@ function TrackPositionPlayPause({
|
|
|
1540
1610
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PLAYING;
|
|
1541
1611
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PAUSED;
|
|
1542
1612
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1543
|
-
return /* @__PURE__ */
|
|
1613
|
+
return /* @__PURE__ */ jsxs19(
|
|
1544
1614
|
"div",
|
|
1545
1615
|
{
|
|
1546
1616
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1547
1617
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1548
1618
|
onClick: handlePlayPauseClick,
|
|
1549
1619
|
children: [
|
|
1550
|
-
/* @__PURE__ */
|
|
1620
|
+
/* @__PURE__ */ jsx25("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs19(
|
|
1551
1621
|
"span",
|
|
1552
1622
|
{
|
|
1553
1623
|
className: "track-position-play-pause__bars",
|
|
@@ -1555,18 +1625,18 @@ function TrackPositionPlayPause({
|
|
|
1555
1625
|
role: "img",
|
|
1556
1626
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1557
1627
|
children: [
|
|
1558
|
-
/* @__PURE__ */
|
|
1559
|
-
/* @__PURE__ */
|
|
1560
|
-
/* @__PURE__ */
|
|
1628
|
+
/* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__bar" }),
|
|
1629
|
+
/* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__bar" }),
|
|
1630
|
+
/* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__bar" })
|
|
1561
1631
|
]
|
|
1562
1632
|
}
|
|
1563
1633
|
) : position }),
|
|
1564
|
-
/* @__PURE__ */
|
|
1634
|
+
/* @__PURE__ */ jsx25(
|
|
1565
1635
|
"div",
|
|
1566
1636
|
{
|
|
1567
1637
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1568
1638
|
"aria-hidden": "true",
|
|
1569
|
-
children: isAwaitingPlayback ? /* @__PURE__ */
|
|
1639
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ jsx25("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx25(FaPause2, {}) : /* @__PURE__ */ jsx25(FaPlay2, {})
|
|
1570
1640
|
}
|
|
1571
1641
|
)
|
|
1572
1642
|
]
|
|
@@ -1577,7 +1647,7 @@ function TrackPositionPlayPause({
|
|
|
1577
1647
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1578
1648
|
import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1579
1649
|
import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1580
|
-
import { jsx as
|
|
1650
|
+
import { jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1581
1651
|
function TrackItem({
|
|
1582
1652
|
track,
|
|
1583
1653
|
position,
|
|
@@ -1601,12 +1671,12 @@ function TrackItem({
|
|
|
1601
1671
|
loadTrackForPlayer(track.uuid);
|
|
1602
1672
|
}
|
|
1603
1673
|
};
|
|
1604
|
-
return /* @__PURE__ */
|
|
1674
|
+
return /* @__PURE__ */ jsxs20(
|
|
1605
1675
|
"div",
|
|
1606
1676
|
{
|
|
1607
1677
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1608
1678
|
children: [
|
|
1609
|
-
isUnplayable ? /* @__PURE__ */
|
|
1679
|
+
isUnplayable ? /* @__PURE__ */ jsx26(
|
|
1610
1680
|
"div",
|
|
1611
1681
|
{
|
|
1612
1682
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1614,7 +1684,7 @@ function TrackItem({
|
|
|
1614
1684
|
"aria-disabled": "true",
|
|
1615
1685
|
children: position
|
|
1616
1686
|
}
|
|
1617
|
-
) : /* @__PURE__ */
|
|
1687
|
+
) : /* @__PURE__ */ jsx26(
|
|
1618
1688
|
TrackPositionPlayPause,
|
|
1619
1689
|
{
|
|
1620
1690
|
position,
|
|
@@ -1623,12 +1693,12 @@ function TrackItem({
|
|
|
1623
1693
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1624
1694
|
}
|
|
1625
1695
|
),
|
|
1626
|
-
/* @__PURE__ */
|
|
1627
|
-
/* @__PURE__ */
|
|
1628
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */
|
|
1696
|
+
/* @__PURE__ */ jsxs20("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1697
|
+
/* @__PURE__ */ jsx26("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx26("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx26(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1698
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx26("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx26(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1629
1699
|
] }),
|
|
1630
|
-
/* @__PURE__ */
|
|
1631
|
-
renderDuration && /* @__PURE__ */
|
|
1700
|
+
/* @__PURE__ */ jsx26("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx26("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1701
|
+
renderDuration && /* @__PURE__ */ jsx26(
|
|
1632
1702
|
"div",
|
|
1633
1703
|
{
|
|
1634
1704
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1636,14 +1706,14 @@ function TrackItem({
|
|
|
1636
1706
|
children: renderDuration(track)
|
|
1637
1707
|
}
|
|
1638
1708
|
),
|
|
1639
|
-
renderActions && /* @__PURE__ */
|
|
1709
|
+
renderActions && /* @__PURE__ */ jsx26("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1640
1710
|
]
|
|
1641
1711
|
}
|
|
1642
1712
|
);
|
|
1643
1713
|
}
|
|
1644
1714
|
|
|
1645
1715
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1646
|
-
import { Fragment as
|
|
1716
|
+
import { Fragment as Fragment10, jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
1647
1717
|
var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
|
|
1648
1718
|
var SORTS = [
|
|
1649
1719
|
{ ordering: "title", label: "Title" },
|
|
@@ -1676,21 +1746,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1676
1746
|
event.stopPropagation();
|
|
1677
1747
|
void play(song.uuid);
|
|
1678
1748
|
};
|
|
1679
|
-
return /* @__PURE__ */
|
|
1680
|
-
/* @__PURE__ */
|
|
1681
|
-
/* @__PURE__ */
|
|
1749
|
+
return /* @__PURE__ */ jsx27(PanelSection5, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx27(Skeleton3, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx27("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx27("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs21(Fragment10, { children: [
|
|
1750
|
+
/* @__PURE__ */ jsxs21("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1751
|
+
/* @__PURE__ */ jsxs21(
|
|
1682
1752
|
"button",
|
|
1683
1753
|
{
|
|
1684
1754
|
type: "button",
|
|
1685
1755
|
onClick: () => void play(),
|
|
1686
1756
|
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",
|
|
1687
1757
|
children: [
|
|
1688
|
-
/* @__PURE__ */
|
|
1758
|
+
/* @__PURE__ */ jsx27(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1689
1759
|
"Play all"
|
|
1690
1760
|
]
|
|
1691
1761
|
}
|
|
1692
1762
|
),
|
|
1693
|
-
/* @__PURE__ */
|
|
1763
|
+
/* @__PURE__ */ jsx27("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx27(
|
|
1694
1764
|
"button",
|
|
1695
1765
|
{
|
|
1696
1766
|
type: "button",
|
|
@@ -1705,7 +1775,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1705
1775
|
sort.ordering
|
|
1706
1776
|
)) })
|
|
1707
1777
|
] }),
|
|
1708
|
-
/* @__PURE__ */
|
|
1778
|
+
/* @__PURE__ */ jsx27("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx27("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx27(
|
|
1709
1779
|
TrackItem,
|
|
1710
1780
|
{
|
|
1711
1781
|
track: song,
|
|
@@ -1714,12 +1784,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1714
1784
|
renderSongLink
|
|
1715
1785
|
}
|
|
1716
1786
|
) }, song.uuid)) }),
|
|
1717
|
-
/* @__PURE__ */
|
|
1787
|
+
/* @__PURE__ */ jsx27(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1718
1788
|
] }) });
|
|
1719
1789
|
}
|
|
1720
1790
|
|
|
1721
1791
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1722
|
-
import { Fragment as
|
|
1792
|
+
import { Fragment as Fragment11, jsx as jsx28, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
1723
1793
|
function useArtistPanel({
|
|
1724
1794
|
uuid,
|
|
1725
1795
|
scope,
|
|
@@ -1740,26 +1810,26 @@ function useArtistPanel({
|
|
|
1740
1810
|
return {
|
|
1741
1811
|
...panel,
|
|
1742
1812
|
title: "Artist",
|
|
1743
|
-
content: /* @__PURE__ */
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
1746
|
-
/* @__PURE__ */
|
|
1747
|
-
/* @__PURE__ */
|
|
1748
|
-
/* @__PURE__ */
|
|
1749
|
-
/* @__PURE__ */
|
|
1813
|
+
content: /* @__PURE__ */ jsxs22("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1814
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-3 w-16" }),
|
|
1815
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-4 w-32" }),
|
|
1816
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-3 w-16" }),
|
|
1817
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-4 w-24" }),
|
|
1818
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-16 mt-2.5" }),
|
|
1819
|
+
/* @__PURE__ */ jsx28(Skeleton4, { className: "h-16" })
|
|
1750
1820
|
] })
|
|
1751
1821
|
};
|
|
1752
1822
|
case "not-found":
|
|
1753
1823
|
return {
|
|
1754
1824
|
...panel,
|
|
1755
1825
|
title: "Artist not found",
|
|
1756
|
-
content: /* @__PURE__ */
|
|
1826
|
+
content: /* @__PURE__ */ jsx28("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1757
1827
|
};
|
|
1758
1828
|
case "error":
|
|
1759
1829
|
return {
|
|
1760
1830
|
...panel,
|
|
1761
1831
|
title: "Artist",
|
|
1762
|
-
content: /* @__PURE__ */
|
|
1832
|
+
content: /* @__PURE__ */ jsx28("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
1763
1833
|
};
|
|
1764
1834
|
case "success": {
|
|
1765
1835
|
const { artist } = result;
|
|
@@ -1768,13 +1838,13 @@ function useArtistPanel({
|
|
|
1768
1838
|
return {
|
|
1769
1839
|
...panel,
|
|
1770
1840
|
title: artist.name,
|
|
1771
|
-
content: /* @__PURE__ */
|
|
1772
|
-
shareUrl && /* @__PURE__ */
|
|
1773
|
-
artist.image && /* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1776
|
-
/* @__PURE__ */
|
|
1777
|
-
artist.tracksCount > 0 && /* @__PURE__ */
|
|
1841
|
+
content: /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
1842
|
+
shareUrl && /* @__PURE__ */ jsx28("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsx28("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ jsx28(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
1843
|
+
artist.image && /* @__PURE__ */ jsx28(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
1844
|
+
/* @__PURE__ */ jsx28(ArtistHeader, { artist }),
|
|
1845
|
+
/* @__PURE__ */ jsx28(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
1846
|
+
/* @__PURE__ */ jsx28(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
1847
|
+
artist.tracksCount > 0 && /* @__PURE__ */ jsx28(
|
|
1778
1848
|
ArtistSongs,
|
|
1779
1849
|
{
|
|
1780
1850
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -1784,7 +1854,7 @@ function useArtistPanel({
|
|
|
1784
1854
|
}
|
|
1785
1855
|
)
|
|
1786
1856
|
] }),
|
|
1787
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */
|
|
1857
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx28(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
1788
1858
|
};
|
|
1789
1859
|
}
|
|
1790
1860
|
}
|
|
@@ -1808,7 +1878,7 @@ import {
|
|
|
1808
1878
|
useSongHistory,
|
|
1809
1879
|
useTrackList as useTrackList5
|
|
1810
1880
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1811
|
-
import { Fragment as
|
|
1881
|
+
import { Fragment as Fragment12, jsx as jsx29, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
1812
1882
|
var numberFormat = new Intl.NumberFormat("en");
|
|
1813
1883
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
1814
1884
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -1816,32 +1886,32 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
1816
1886
|
const [open, setOpen] = useState7(false);
|
|
1817
1887
|
const history = useSongHistory(uuid, getBackendBaseUrl, open);
|
|
1818
1888
|
let body = null;
|
|
1819
|
-
if (history.status === "loading") body = /* @__PURE__ */
|
|
1820
|
-
else if (history.status === "error") body = /* @__PURE__ */
|
|
1889
|
+
if (history.status === "loading") body = /* @__PURE__ */ jsx29(Skeleton5, { className: "h-10", "aria-busy": "true" });
|
|
1890
|
+
else if (history.status === "error") body = /* @__PURE__ */ jsx29("p", { className: "text-red-600", children: "Could not load the history." });
|
|
1821
1891
|
else if (history.status === "success")
|
|
1822
|
-
body = history.entries.length === 0 ? /* @__PURE__ */
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */
|
|
1892
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ jsx29("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx29("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs23("li", { className: "py-1.5 text-sm", children: [
|
|
1893
|
+
/* @__PURE__ */ jsx29("span", { className: "font-medium", children: entry.action }),
|
|
1894
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs23("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1825
1895
|
" ",
|
|
1826
1896
|
"\u2014 ",
|
|
1827
1897
|
entry.oldValue ?? "none",
|
|
1828
1898
|
" \u2192 ",
|
|
1829
1899
|
entry.newValue ?? "none"
|
|
1830
1900
|
] }) : null,
|
|
1831
|
-
/* @__PURE__ */
|
|
1901
|
+
/* @__PURE__ */ jsxs23("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
1832
1902
|
entry.actorPseudo ?? "Anonymous",
|
|
1833
1903
|
" \xB7 ",
|
|
1834
1904
|
dateFormat.format(new Date(entry.createdOn))
|
|
1835
1905
|
] })
|
|
1836
1906
|
] }, entry.uuid)) });
|
|
1837
|
-
return /* @__PURE__ */
|
|
1907
|
+
return /* @__PURE__ */ jsxs23(
|
|
1838
1908
|
"details",
|
|
1839
1909
|
{
|
|
1840
1910
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
1841
1911
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
1842
1912
|
children: [
|
|
1843
|
-
/* @__PURE__ */
|
|
1844
|
-
/* @__PURE__ */
|
|
1913
|
+
/* @__PURE__ */ jsx29("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
1914
|
+
/* @__PURE__ */ jsx29("div", { className: "mt-1.5", children: body })
|
|
1845
1915
|
]
|
|
1846
1916
|
}
|
|
1847
1917
|
);
|
|
@@ -1867,13 +1937,13 @@ function SongPanelContent({
|
|
|
1867
1937
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
1868
1938
|
fields.push({
|
|
1869
1939
|
label: "Genre",
|
|
1870
|
-
value: path ? /* @__PURE__ */
|
|
1871
|
-
index > 0 && /* @__PURE__ */
|
|
1872
|
-
/* @__PURE__ */
|
|
1940
|
+
value: path ? /* @__PURE__ */ jsx29("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs23("span", { children: [
|
|
1941
|
+
index > 0 && /* @__PURE__ */ jsx29("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
1942
|
+
/* @__PURE__ */ jsx29("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
1873
1943
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
1874
1944
|
});
|
|
1875
1945
|
} else {
|
|
1876
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */
|
|
1946
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ jsx29("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
1877
1947
|
}
|
|
1878
1948
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
1879
1949
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -1883,11 +1953,11 @@ function SongPanelContent({
|
|
|
1883
1953
|
const links = [];
|
|
1884
1954
|
if (musicBrainzUrl)
|
|
1885
1955
|
links.push(
|
|
1886
|
-
/* @__PURE__ */
|
|
1956
|
+
/* @__PURE__ */ jsx29("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
1887
1957
|
);
|
|
1888
1958
|
if (song.youtubeVideo)
|
|
1889
1959
|
links.push(
|
|
1890
|
-
/* @__PURE__ */
|
|
1960
|
+
/* @__PURE__ */ jsx29(
|
|
1891
1961
|
"a",
|
|
1892
1962
|
{
|
|
1893
1963
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -1902,33 +1972,33 @@ function SongPanelContent({
|
|
|
1902
1972
|
if (links.length > 0)
|
|
1903
1973
|
fields.push({
|
|
1904
1974
|
label: "Links",
|
|
1905
|
-
value: /* @__PURE__ */
|
|
1975
|
+
value: /* @__PURE__ */ jsx29("span", { className: "flex gap-3", children: links })
|
|
1906
1976
|
});
|
|
1907
|
-
return /* @__PURE__ */
|
|
1908
|
-
/* @__PURE__ */
|
|
1909
|
-
unplayableReason ? /* @__PURE__ */
|
|
1977
|
+
return /* @__PURE__ */ jsxs23(Fragment12, { children: [
|
|
1978
|
+
/* @__PURE__ */ jsx29("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: [
|
|
1979
|
+
unplayableReason ? /* @__PURE__ */ jsx29("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs23(
|
|
1910
1980
|
"button",
|
|
1911
1981
|
{
|
|
1912
1982
|
type: "button",
|
|
1913
1983
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
1914
1984
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
1915
1985
|
children: [
|
|
1916
|
-
/* @__PURE__ */
|
|
1986
|
+
/* @__PURE__ */ jsx29("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__ */ jsx29(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx29(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1917
1987
|
isPlaying ? "Pause" : "Play"
|
|
1918
1988
|
]
|
|
1919
1989
|
}
|
|
1920
1990
|
),
|
|
1921
|
-
shareUrl && /* @__PURE__ */
|
|
1991
|
+
shareUrl && /* @__PURE__ */ jsx29(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1922
1992
|
] }) }),
|
|
1923
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */
|
|
1926
|
-
/* @__PURE__ */
|
|
1993
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx29("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx29(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
|
|
1994
|
+
/* @__PURE__ */ jsx29(FieldList2, { items: fields }),
|
|
1995
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx29(PanelSection6, { title: "Essential in", children: /* @__PURE__ */ jsx29("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx29("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
1996
|
+
/* @__PURE__ */ jsx29(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
1927
1997
|
] });
|
|
1928
1998
|
}
|
|
1929
1999
|
|
|
1930
2000
|
// src/genre-tree/song/useSongPanel.tsx
|
|
1931
|
-
import { jsx as
|
|
2001
|
+
import { jsx as jsx30, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
1932
2002
|
function useSongPanel({
|
|
1933
2003
|
uuid,
|
|
1934
2004
|
scope,
|
|
@@ -1950,24 +2020,24 @@ function useSongPanel({
|
|
|
1950
2020
|
return {
|
|
1951
2021
|
...panel,
|
|
1952
2022
|
title: "Song",
|
|
1953
|
-
content: /* @__PURE__ */
|
|
1954
|
-
/* @__PURE__ */
|
|
1955
|
-
/* @__PURE__ */
|
|
1956
|
-
/* @__PURE__ */
|
|
1957
|
-
/* @__PURE__ */
|
|
2023
|
+
content: /* @__PURE__ */ jsxs24("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2024
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-3 w-16" }),
|
|
2025
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-4 w-32" }),
|
|
2026
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-3 w-16" }),
|
|
2027
|
+
/* @__PURE__ */ jsx30(Skeleton6, { className: "h-4 w-24" })
|
|
1958
2028
|
] })
|
|
1959
2029
|
};
|
|
1960
2030
|
case "not-found":
|
|
1961
2031
|
return {
|
|
1962
2032
|
...panel,
|
|
1963
2033
|
title: "Song not found",
|
|
1964
|
-
content: /* @__PURE__ */
|
|
2034
|
+
content: /* @__PURE__ */ jsx30("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
1965
2035
|
};
|
|
1966
2036
|
case "error":
|
|
1967
2037
|
return {
|
|
1968
2038
|
...panel,
|
|
1969
2039
|
title: "Song",
|
|
1970
|
-
content: /* @__PURE__ */
|
|
2040
|
+
content: /* @__PURE__ */ jsx30("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
1971
2041
|
};
|
|
1972
2042
|
case "success": {
|
|
1973
2043
|
const { song } = result;
|
|
@@ -1975,7 +2045,7 @@ function useSongPanel({
|
|
|
1975
2045
|
return {
|
|
1976
2046
|
...panel,
|
|
1977
2047
|
title: song.title,
|
|
1978
|
-
content: /* @__PURE__ */
|
|
2048
|
+
content: /* @__PURE__ */ jsx30(
|
|
1979
2049
|
SongPanelContent,
|
|
1980
2050
|
{
|
|
1981
2051
|
song,
|
|
@@ -1995,12 +2065,12 @@ function useSongPanel({
|
|
|
1995
2065
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
1996
2066
|
import { MdMusicNote, MdVisibility } from "react-icons/md";
|
|
1997
2067
|
import { SegmentedControl } from "@behindthemusictree/ui";
|
|
1998
|
-
import { jsx as
|
|
2068
|
+
import { jsx as jsx31 } from "react/jsx-runtime";
|
|
1999
2069
|
function SizeMetricToggle({
|
|
2000
2070
|
value,
|
|
2001
2071
|
onChange
|
|
2002
2072
|
}) {
|
|
2003
|
-
return /* @__PURE__ */
|
|
2073
|
+
return /* @__PURE__ */ jsx31(
|
|
2004
2074
|
SegmentedControl,
|
|
2005
2075
|
{
|
|
2006
2076
|
variant: "floating",
|
|
@@ -2008,15 +2078,15 @@ function SizeMetricToggle({
|
|
|
2008
2078
|
value,
|
|
2009
2079
|
onChange,
|
|
2010
2080
|
options: [
|
|
2011
|
-
{ value: "songs", label: /* @__PURE__ */
|
|
2012
|
-
{ value: "views", label: /* @__PURE__ */
|
|
2081
|
+
{ value: "songs", label: /* @__PURE__ */ jsx31(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2082
|
+
{ value: "views", label: /* @__PURE__ */ jsx31(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2013
2083
|
]
|
|
2014
2084
|
}
|
|
2015
2085
|
);
|
|
2016
2086
|
}
|
|
2017
2087
|
|
|
2018
2088
|
// src/genre-tree/GenreTreeView.tsx
|
|
2019
|
-
import { Fragment as
|
|
2089
|
+
import { Fragment as Fragment13, jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
2020
2090
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2021
2091
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2022
2092
|
var noop = () => {
|
|
@@ -2050,6 +2120,9 @@ function GenreTreeView({
|
|
|
2050
2120
|
artistExpanded: controlledArtistExpanded,
|
|
2051
2121
|
onArtistExpandedChange,
|
|
2052
2122
|
getShareUrl,
|
|
2123
|
+
extraSearchGroups = [],
|
|
2124
|
+
onSearchQueryChange,
|
|
2125
|
+
onSearchFocus,
|
|
2053
2126
|
artistSearch = true,
|
|
2054
2127
|
onArtistSelect,
|
|
2055
2128
|
artistSearchBaseUrl,
|
|
@@ -2159,7 +2232,7 @@ function GenreTreeView({
|
|
|
2159
2232
|
const openSongRef = useRef3(openSong);
|
|
2160
2233
|
openSongRef.current = openSong;
|
|
2161
2234
|
const resolvedRenderSongLink = useCallback6(
|
|
2162
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */
|
|
2235
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx32("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2163
2236
|
[renderSongLink]
|
|
2164
2237
|
);
|
|
2165
2238
|
const [expandedSongUuid, setExpandedSongUuid] = useState8(null);
|
|
@@ -2171,7 +2244,7 @@ function GenreTreeView({
|
|
|
2171
2244
|
scope,
|
|
2172
2245
|
getBackendBaseUrl: artistBaseUrl,
|
|
2173
2246
|
getShareUrl: getShareUrlRef.current,
|
|
2174
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2247
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx32("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2175
2248
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2176
2249
|
onExpandedChange: (on) => {
|
|
2177
2250
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2180,7 +2253,7 @@ function GenreTreeView({
|
|
|
2180
2253
|
renderSongLink: resolvedRenderSongLink,
|
|
2181
2254
|
onClose: () => changeArtist(null)
|
|
2182
2255
|
});
|
|
2183
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2256
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx32("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2184
2257
|
const getGenrePath = (genre) => {
|
|
2185
2258
|
const path = [];
|
|
2186
2259
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2259,52 +2332,27 @@ function GenreTreeView({
|
|
|
2259
2332
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2260
2333
|
const { showPopup, hidePopup } = usePopup5();
|
|
2261
2334
|
const [searchText, setSearchText] = useState8("");
|
|
2262
|
-
const
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
onArtistSelect?.(artist);
|
|
2284
|
-
}
|
|
2285
|
-
})),
|
|
2286
|
-
status: artistSearchResult.isError ? "Search failed" : artistSearchResult.settled && artistSearchResult.artists.length === 0 ? "No artists found" : "",
|
|
2287
|
-
loading: artistSearchResult.pending
|
|
2288
|
-
} : void 0;
|
|
2289
|
-
const songSearchResult = useSongSearch(songBaseUrl, songSearch ? searchText : "");
|
|
2290
|
-
const songGroup = songSearch ? {
|
|
2291
|
-
key: "songs",
|
|
2292
|
-
label: "Songs",
|
|
2293
|
-
items: songSearchResult.songs.map((song) => ({
|
|
2294
|
-
key: song.uuid,
|
|
2295
|
-
content: /* @__PURE__ */ jsxs22("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
|
|
2296
|
-
/* @__PURE__ */ jsx29("span", { className: "block truncate font-medium", children: song.title }),
|
|
2297
|
-
/* @__PURE__ */ jsx29("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
2298
|
-
] }),
|
|
2299
|
-
onSelect: () => {
|
|
2300
|
-
openSong(song.uuid);
|
|
2301
|
-
onSongSelect?.(song);
|
|
2302
|
-
}
|
|
2303
|
-
})),
|
|
2304
|
-
status: songSearchResult.isError ? "Search failed" : songSearchResult.settled && songSearchResult.songs.length === 0 ? "No songs found" : "",
|
|
2305
|
-
loading: songSearchResult.pending
|
|
2306
|
-
} : void 0;
|
|
2307
|
-
const extraGroups = [artistGroup, songGroup].filter((group) => group !== void 0);
|
|
2335
|
+
const artistGroup = useArtistSearchGroup(
|
|
2336
|
+
artistBaseUrl,
|
|
2337
|
+
searchText,
|
|
2338
|
+
(artist) => {
|
|
2339
|
+
openArtist(artist.uuid);
|
|
2340
|
+
onArtistSelect?.(artist);
|
|
2341
|
+
},
|
|
2342
|
+
Boolean(artistSearch)
|
|
2343
|
+
);
|
|
2344
|
+
const songGroup = useSongSearchGroup(
|
|
2345
|
+
songBaseUrl,
|
|
2346
|
+
searchText,
|
|
2347
|
+
(song) => {
|
|
2348
|
+
openSong(song.uuid);
|
|
2349
|
+
onSongSelect?.(song);
|
|
2350
|
+
},
|
|
2351
|
+
Boolean(songSearch)
|
|
2352
|
+
);
|
|
2353
|
+
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2354
|
+
(group) => group !== void 0
|
|
2355
|
+
);
|
|
2308
2356
|
const handleSelectedNodeChange = (node) => {
|
|
2309
2357
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2310
2358
|
selectNode(node);
|
|
@@ -2328,7 +2376,7 @@ function GenreTreeView({
|
|
|
2328
2376
|
};
|
|
2329
2377
|
const renderGenreBody = (relation) => {
|
|
2330
2378
|
const { node } = relation;
|
|
2331
|
-
const family = /* @__PURE__ */
|
|
2379
|
+
const family = /* @__PURE__ */ jsx32(
|
|
2332
2380
|
GenreNodeFamily,
|
|
2333
2381
|
{
|
|
2334
2382
|
node,
|
|
@@ -2339,10 +2387,10 @@ function GenreTreeView({
|
|
|
2339
2387
|
onSelectNode: selectGenreFromPanel
|
|
2340
2388
|
}
|
|
2341
2389
|
);
|
|
2342
|
-
const ancestors = /* @__PURE__ */
|
|
2390
|
+
const ancestors = /* @__PURE__ */ jsx32(GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2343
2391
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2344
2392
|
const criteria = genrePlaylist?.criteria;
|
|
2345
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */
|
|
2393
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2346
2394
|
family,
|
|
2347
2395
|
ancestors
|
|
2348
2396
|
] }) };
|
|
@@ -2362,11 +2410,11 @@ function GenreTreeView({
|
|
|
2362
2410
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2363
2411
|
) : void 0;
|
|
2364
2412
|
return {
|
|
2365
|
-
primary: /* @__PURE__ */
|
|
2366
|
-
/* @__PURE__ */
|
|
2413
|
+
primary: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2414
|
+
/* @__PURE__ */ jsx32(
|
|
2367
2415
|
GenreInfoPanelBody,
|
|
2368
2416
|
{
|
|
2369
|
-
family: /* @__PURE__ */
|
|
2417
|
+
family: /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2370
2418
|
family,
|
|
2371
2419
|
ancestors
|
|
2372
2420
|
] }),
|
|
@@ -2384,7 +2432,7 @@ function GenreTreeView({
|
|
|
2384
2432
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2385
2433
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2386
2434
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2387
|
-
/* @__PURE__ */
|
|
2435
|
+
/* @__PURE__ */ jsx32(
|
|
2388
2436
|
ChangeParentPopup,
|
|
2389
2437
|
{
|
|
2390
2438
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2459,19 +2507,19 @@ function GenreTreeView({
|
|
|
2459
2507
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2460
2508
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2461
2509
|
} : void 0;
|
|
2462
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */
|
|
2510
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx32(
|
|
2463
2511
|
IconButton,
|
|
2464
2512
|
{
|
|
2465
2513
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2466
2514
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2467
2515
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2468
|
-
children: activePanel.expanded ? /* @__PURE__ */
|
|
2516
|
+
children: activePanel.expanded ? /* @__PURE__ */ jsx32(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx32(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2469
2517
|
}
|
|
2470
2518
|
) : null;
|
|
2471
2519
|
const renderArtistFilter = () => {
|
|
2472
2520
|
if (isLoading || !filterArtist) return null;
|
|
2473
|
-
return /* @__PURE__ */
|
|
2474
|
-
/* @__PURE__ */
|
|
2521
|
+
return /* @__PURE__ */ jsxs25("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2522
|
+
/* @__PURE__ */ jsx32(
|
|
2475
2523
|
"button",
|
|
2476
2524
|
{
|
|
2477
2525
|
type: "button",
|
|
@@ -2483,7 +2531,7 @@ function GenreTreeView({
|
|
|
2483
2531
|
children: filterArtist.name
|
|
2484
2532
|
}
|
|
2485
2533
|
),
|
|
2486
|
-
/* @__PURE__ */
|
|
2534
|
+
/* @__PURE__ */ jsx32(
|
|
2487
2535
|
"button",
|
|
2488
2536
|
{
|
|
2489
2537
|
type: "button",
|
|
@@ -2495,8 +2543,8 @@ function GenreTreeView({
|
|
|
2495
2543
|
)
|
|
2496
2544
|
] });
|
|
2497
2545
|
};
|
|
2498
|
-
const actions = /* @__PURE__ */
|
|
2499
|
-
!isLoading && /* @__PURE__ */
|
|
2546
|
+
const actions = /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2547
|
+
!isLoading && /* @__PURE__ */ jsx32(
|
|
2500
2548
|
GenreSearch,
|
|
2501
2549
|
{
|
|
2502
2550
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -2504,13 +2552,17 @@ function GenreTreeView({
|
|
|
2504
2552
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2505
2553
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2506
2554
|
onPrevious: handlePrevious,
|
|
2507
|
-
onQueryChange:
|
|
2555
|
+
onQueryChange: (text) => {
|
|
2556
|
+
setSearchText(text);
|
|
2557
|
+
onSearchQueryChange?.(text);
|
|
2558
|
+
},
|
|
2559
|
+
onFocus: onSearchFocus,
|
|
2508
2560
|
extraGroups
|
|
2509
2561
|
}
|
|
2510
2562
|
),
|
|
2511
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2512
|
-
/* @__PURE__ */
|
|
2513
|
-
!isLoading && !isControlled && /* @__PURE__ */
|
|
2563
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx32("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2564
|
+
/* @__PURE__ */ jsxs25("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
2565
|
+
!isLoading && !isControlled && /* @__PURE__ */ jsx32(
|
|
2514
2566
|
SegmentedControl2,
|
|
2515
2567
|
{
|
|
2516
2568
|
"aria-label": "Tree view mode",
|
|
@@ -2524,13 +2576,13 @@ function GenreTreeView({
|
|
|
2524
2576
|
]
|
|
2525
2577
|
}
|
|
2526
2578
|
),
|
|
2527
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */
|
|
2579
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ jsx32(
|
|
2528
2580
|
"div",
|
|
2529
2581
|
{
|
|
2530
2582
|
className: "flex items-center gap-1",
|
|
2531
2583
|
role: "group",
|
|
2532
2584
|
"aria-label": "Tree display options",
|
|
2533
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */
|
|
2585
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx32(
|
|
2534
2586
|
Button,
|
|
2535
2587
|
{
|
|
2536
2588
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2541,7 +2593,7 @@ function GenreTreeView({
|
|
|
2541
2593
|
)
|
|
2542
2594
|
}
|
|
2543
2595
|
),
|
|
2544
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */
|
|
2596
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ jsx32(
|
|
2545
2597
|
IconTextButton,
|
|
2546
2598
|
{
|
|
2547
2599
|
icon: Plus2,
|
|
@@ -2551,34 +2603,34 @@ function GenreTreeView({
|
|
|
2551
2603
|
)
|
|
2552
2604
|
] })
|
|
2553
2605
|
] });
|
|
2554
|
-
return /* @__PURE__ */
|
|
2606
|
+
return /* @__PURE__ */ jsxs25(
|
|
2555
2607
|
"div",
|
|
2556
2608
|
{
|
|
2557
2609
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2558
2610
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2559
2611
|
children: [
|
|
2560
|
-
/* @__PURE__ */
|
|
2612
|
+
/* @__PURE__ */ jsx32(
|
|
2561
2613
|
"div",
|
|
2562
2614
|
{
|
|
2563
2615
|
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"}`,
|
|
2564
2616
|
children: actions
|
|
2565
2617
|
}
|
|
2566
2618
|
),
|
|
2567
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2619
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx32(
|
|
2568
2620
|
"div",
|
|
2569
2621
|
{
|
|
2570
2622
|
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"}`,
|
|
2571
2623
|
children: renderArtistFilter()
|
|
2572
2624
|
}
|
|
2573
2625
|
),
|
|
2574
|
-
/* @__PURE__ */
|
|
2575
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */
|
|
2576
|
-
isLoading ? /* @__PURE__ */
|
|
2626
|
+
/* @__PURE__ */ jsx32("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: [
|
|
2627
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx32("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2628
|
+
isLoading ? /* @__PURE__ */ jsx32(GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2577
2629
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2578
2630
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2579
2631
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2580
2632
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2581
|
-
/* @__PURE__ */
|
|
2633
|
+
/* @__PURE__ */ jsx32("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx32(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx32(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx32(
|
|
2582
2634
|
GenrePlaylistTreeWheel,
|
|
2583
2635
|
{
|
|
2584
2636
|
scope,
|
|
@@ -2595,9 +2647,9 @@ function GenreTreeView({
|
|
|
2595
2647
|
allowWheelRotation,
|
|
2596
2648
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2597
2649
|
busy,
|
|
2598
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2650
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2599
2651
|
floatingControlsLeading,
|
|
2600
|
-
canSizeByViews && /* @__PURE__ */
|
|
2652
|
+
canSizeByViews && /* @__PURE__ */ jsx32(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2601
2653
|
] }) : void 0,
|
|
2602
2654
|
defaultViewport,
|
|
2603
2655
|
viewportRestore,
|
|
@@ -2606,7 +2658,7 @@ function GenreTreeView({
|
|
|
2606
2658
|
) }) })
|
|
2607
2659
|
) : viewMode === "pop-core" ? (
|
|
2608
2660
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2609
|
-
/* @__PURE__ */
|
|
2661
|
+
/* @__PURE__ */ jsx32("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx32(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx32(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx32(
|
|
2610
2662
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2611
2663
|
{
|
|
2612
2664
|
scope,
|
|
@@ -2623,9 +2675,9 @@ function GenreTreeView({
|
|
|
2623
2675
|
allowWheelRotation,
|
|
2624
2676
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2625
2677
|
busy,
|
|
2626
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2678
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2", children: [
|
|
2627
2679
|
floatingControlsLeading,
|
|
2628
|
-
canSizeByViews && /* @__PURE__ */
|
|
2680
|
+
canSizeByViews && /* @__PURE__ */ jsx32(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2629
2681
|
] }) : void 0,
|
|
2630
2682
|
defaultViewport,
|
|
2631
2683
|
viewportRestore,
|
|
@@ -2633,7 +2685,7 @@ function GenreTreeView({
|
|
|
2633
2685
|
...focus
|
|
2634
2686
|
}
|
|
2635
2687
|
) }) })
|
|
2636
|
-
) : viewMode === "outline" ? /* @__PURE__ */
|
|
2688
|
+
) : viewMode === "outline" ? /* @__PURE__ */ jsx32("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx32(
|
|
2637
2689
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2638
2690
|
{
|
|
2639
2691
|
outline: true,
|
|
@@ -2649,13 +2701,13 @@ function GenreTreeView({
|
|
|
2649
2701
|
selectedNodeId,
|
|
2650
2702
|
readOnly
|
|
2651
2703
|
}
|
|
2652
|
-
) }) : /* @__PURE__ */
|
|
2704
|
+
) }) : /* @__PURE__ */ jsx32("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(
|
|
2653
2705
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2654
|
-
return /* @__PURE__ */
|
|
2706
|
+
return /* @__PURE__ */ jsx32(
|
|
2655
2707
|
"div",
|
|
2656
2708
|
{
|
|
2657
2709
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2658
|
-
children: /* @__PURE__ */
|
|
2710
|
+
children: /* @__PURE__ */ jsx32("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx32(
|
|
2659
2711
|
GenrePlaylistTreePerRoot,
|
|
2660
2712
|
{
|
|
2661
2713
|
scope,
|
|
@@ -2681,7 +2733,7 @@ function GenreTreeView({
|
|
|
2681
2733
|
);
|
|
2682
2734
|
}
|
|
2683
2735
|
) }),
|
|
2684
|
-
/* @__PURE__ */
|
|
2736
|
+
/* @__PURE__ */ jsx32(
|
|
2685
2737
|
DetailPanel,
|
|
2686
2738
|
{
|
|
2687
2739
|
ref: panelRef,
|
|
@@ -2698,7 +2750,7 @@ function GenreTreeView({
|
|
|
2698
2750
|
aside: activePanel?.aside,
|
|
2699
2751
|
edgeTab: detailTab,
|
|
2700
2752
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2701
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */
|
|
2753
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ jsxs25(Fragment13, { children: [
|
|
2702
2754
|
activePanel.content,
|
|
2703
2755
|
activePanel.aside
|
|
2704
2756
|
] }) : activePanel?.content
|
|
@@ -2725,7 +2777,7 @@ import { Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
|
|
|
2725
2777
|
import {
|
|
2726
2778
|
useFetchGenreDetail
|
|
2727
2779
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
2728
|
-
import { Fragment as
|
|
2780
|
+
import { Fragment as Fragment14, jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
2729
2781
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2730
2782
|
function toNode(genre, parentId) {
|
|
2731
2783
|
return {
|
|
@@ -2780,17 +2832,17 @@ function GenreDetailPanel({
|
|
|
2780
2832
|
};
|
|
2781
2833
|
}, [detail]);
|
|
2782
2834
|
if (isError) {
|
|
2783
|
-
return /* @__PURE__ */
|
|
2835
|
+
return /* @__PURE__ */ jsx33("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
2784
2836
|
}
|
|
2785
2837
|
if (isPending || !detail || !view) {
|
|
2786
|
-
return /* @__PURE__ */
|
|
2787
|
-
/* @__PURE__ */
|
|
2788
|
-
/* @__PURE__ */
|
|
2789
|
-
/* @__PURE__ */
|
|
2838
|
+
return /* @__PURE__ */ jsxs26("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2839
|
+
/* @__PURE__ */ jsx33(Skeleton7, { className: "h-6 w-2/3" }),
|
|
2840
|
+
/* @__PURE__ */ jsx33(Skeleton7, { className: "h-4 w-full" }),
|
|
2841
|
+
/* @__PURE__ */ jsx33(Skeleton7, { className: "h-4 w-3/4" })
|
|
2790
2842
|
] });
|
|
2791
2843
|
}
|
|
2792
2844
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
2793
|
-
const family = /* @__PURE__ */
|
|
2845
|
+
const family = /* @__PURE__ */ jsx33(
|
|
2794
2846
|
GenreNodeFamily2,
|
|
2795
2847
|
{
|
|
2796
2848
|
node: view.node,
|
|
@@ -2801,20 +2853,20 @@ function GenreDetailPanel({
|
|
|
2801
2853
|
onSelectNode: selectGenre
|
|
2802
2854
|
}
|
|
2803
2855
|
);
|
|
2804
|
-
const ancestors = /* @__PURE__ */
|
|
2805
|
-
return /* @__PURE__ */
|
|
2856
|
+
const ancestors = /* @__PURE__ */ jsx33(GenreNodeAncestors2, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2857
|
+
return /* @__PURE__ */ jsxs26(
|
|
2806
2858
|
DetailPanel2,
|
|
2807
2859
|
{
|
|
2808
2860
|
side: "left",
|
|
2809
2861
|
title: view.node.name,
|
|
2810
2862
|
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
2811
|
-
headerActions: onExpandedChange ? /* @__PURE__ */
|
|
2863
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ jsx33(
|
|
2812
2864
|
IconButton2,
|
|
2813
2865
|
{
|
|
2814
2866
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
2815
2867
|
style: { color: "inherit" },
|
|
2816
2868
|
onClick: () => onExpandedChange(!expanded),
|
|
2817
|
-
children: expanded ? /* @__PURE__ */
|
|
2869
|
+
children: expanded ? /* @__PURE__ */ jsx33(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx33(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2818
2870
|
}
|
|
2819
2871
|
) : void 0,
|
|
2820
2872
|
onClose,
|
|
@@ -2823,10 +2875,10 @@ function GenreDetailPanel({
|
|
|
2823
2875
|
onExpandedChange,
|
|
2824
2876
|
edgeTab,
|
|
2825
2877
|
children: [
|
|
2826
|
-
/* @__PURE__ */
|
|
2878
|
+
/* @__PURE__ */ jsx33(
|
|
2827
2879
|
GenreInfoPanelBody,
|
|
2828
2880
|
{
|
|
2829
|
-
family: /* @__PURE__ */
|
|
2881
|
+
family: /* @__PURE__ */ jsxs26(Fragment14, { children: [
|
|
2830
2882
|
family,
|
|
2831
2883
|
ancestors
|
|
2832
2884
|
] }),
|
|
@@ -2860,7 +2912,7 @@ import { ListMusic } from "lucide-react";
|
|
|
2860
2912
|
import { useTrackList as useTrackList6 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2861
2913
|
import { TrackListOriginType as TrackListOriginType2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2862
2914
|
import { useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
|
|
2863
|
-
import { jsx as
|
|
2915
|
+
import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
2864
2916
|
var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect2;
|
|
2865
2917
|
function TrackListSidebar({
|
|
2866
2918
|
className: className2 = "",
|
|
@@ -2962,25 +3014,25 @@ function TrackListSidebar({
|
|
|
2962
3014
|
}
|
|
2963
3015
|
};
|
|
2964
3016
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
2965
|
-
return trackList ? /* @__PURE__ */
|
|
3017
|
+
return trackList ? /* @__PURE__ */ jsxs27(
|
|
2966
3018
|
"div",
|
|
2967
3019
|
{
|
|
2968
3020
|
"data-theme": "dark",
|
|
2969
3021
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
2970
3022
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
2971
3023
|
children: [
|
|
2972
|
-
/* @__PURE__ */
|
|
2973
|
-
/* @__PURE__ */
|
|
2974
|
-
/* @__PURE__ */
|
|
2975
|
-
/* @__PURE__ */
|
|
2976
|
-
/* @__PURE__ */
|
|
3024
|
+
/* @__PURE__ */ jsxs27("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3025
|
+
/* @__PURE__ */ jsxs27("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3026
|
+
/* @__PURE__ */ jsxs27("div", { className: "origin flex text-xl", children: [
|
|
3027
|
+
/* @__PURE__ */ jsx34("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3028
|
+
/* @__PURE__ */ jsx34("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__ */ jsx34("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
2977
3029
|
] }),
|
|
2978
|
-
/* @__PURE__ */
|
|
3030
|
+
/* @__PURE__ */ jsxs27("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
2979
3031
|
trackList.origin.type === TrackListOriginType2.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType2.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
2980
3032
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
2981
3033
|
] })
|
|
2982
3034
|
] }),
|
|
2983
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */
|
|
3035
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx34(
|
|
2984
3036
|
"button",
|
|
2985
3037
|
{
|
|
2986
3038
|
type: "button",
|
|
@@ -2989,11 +3041,11 @@ function TrackListSidebar({
|
|
|
2989
3041
|
"aria-expanded": !isCollapsed,
|
|
2990
3042
|
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"}`,
|
|
2991
3043
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
2992
|
-
children: /* @__PURE__ */
|
|
3044
|
+
children: /* @__PURE__ */ jsx34(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
2993
3045
|
}
|
|
2994
3046
|
)
|
|
2995
3047
|
] }),
|
|
2996
|
-
!isCollapsed && /* @__PURE__ */
|
|
3048
|
+
!isCollapsed && /* @__PURE__ */ jsxs27(
|
|
2997
3049
|
"ul",
|
|
2998
3050
|
{
|
|
2999
3051
|
ref: trackListRef,
|
|
@@ -3003,7 +3055,7 @@ function TrackListSidebar({
|
|
|
3003
3055
|
onPointerDown: markUserScrollIntent,
|
|
3004
3056
|
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",
|
|
3005
3057
|
children: [
|
|
3006
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */
|
|
3058
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ jsx34("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx34(
|
|
3007
3059
|
TrackItem,
|
|
3008
3060
|
{
|
|
3009
3061
|
track,
|
|
@@ -3014,7 +3066,7 @@ function TrackListSidebar({
|
|
|
3014
3066
|
renderSongLink
|
|
3015
3067
|
}
|
|
3016
3068
|
) }, track.uuid)),
|
|
3017
|
-
hasMore && /* @__PURE__ */
|
|
3069
|
+
hasMore && /* @__PURE__ */ jsx34("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3018
3070
|
]
|
|
3019
3071
|
}
|
|
3020
3072
|
)
|
|
@@ -3039,7 +3091,7 @@ import { PanelEdgeTab } from "@behindthemusictree/ui";
|
|
|
3039
3091
|
import { Pause as Pause3, Play as Play4, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
|
|
3040
3092
|
import { formatTime, useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
|
|
3041
3093
|
import { PlayStates as PlayStates4, useCurrentTime, usePlayer as usePlayer10 } from "@behindthemusictree/app-kit/player";
|
|
3042
|
-
import { jsx as
|
|
3094
|
+
import { jsx as jsx35, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
3043
3095
|
function PlayerControls() {
|
|
3044
3096
|
const {
|
|
3045
3097
|
duration,
|
|
@@ -3058,15 +3110,15 @@ function PlayerControls() {
|
|
|
3058
3110
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3059
3111
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3060
3112
|
const isPlaying = playState === PlayStates4.PLAYING;
|
|
3061
|
-
return /* @__PURE__ */
|
|
3113
|
+
return /* @__PURE__ */ jsxs28(
|
|
3062
3114
|
"section",
|
|
3063
3115
|
{
|
|
3064
3116
|
"aria-label": "Player controls",
|
|
3065
3117
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3066
3118
|
children: [
|
|
3067
|
-
/* @__PURE__ */
|
|
3068
|
-
/* @__PURE__ */
|
|
3069
|
-
/* @__PURE__ */
|
|
3119
|
+
/* @__PURE__ */ jsxs28("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3120
|
+
/* @__PURE__ */ jsx35("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
|
|
3121
|
+
/* @__PURE__ */ jsx35(
|
|
3070
3122
|
"input",
|
|
3071
3123
|
{
|
|
3072
3124
|
"aria-label": "Seek",
|
|
@@ -3080,11 +3132,11 @@ function PlayerControls() {
|
|
|
3080
3132
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3081
3133
|
}
|
|
3082
3134
|
),
|
|
3083
|
-
/* @__PURE__ */
|
|
3135
|
+
/* @__PURE__ */ jsx35("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
|
|
3084
3136
|
] }),
|
|
3085
|
-
/* @__PURE__ */
|
|
3086
|
-
/* @__PURE__ */
|
|
3087
|
-
/* @__PURE__ */
|
|
3137
|
+
/* @__PURE__ */ jsxs28("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3138
|
+
/* @__PURE__ */ jsxs28("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3139
|
+
/* @__PURE__ */ jsx35(
|
|
3088
3140
|
"button",
|
|
3089
3141
|
{
|
|
3090
3142
|
type: "button",
|
|
@@ -3093,10 +3145,10 @@ function PlayerControls() {
|
|
|
3093
3145
|
disabled: !isReady || !hasPreviousTrack,
|
|
3094
3146
|
onClick: playPreviousTrack,
|
|
3095
3147
|
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",
|
|
3096
|
-
children: /* @__PURE__ */
|
|
3148
|
+
children: /* @__PURE__ */ jsx35(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3097
3149
|
}
|
|
3098
3150
|
),
|
|
3099
|
-
/* @__PURE__ */
|
|
3151
|
+
/* @__PURE__ */ jsx35(
|
|
3100
3152
|
"button",
|
|
3101
3153
|
{
|
|
3102
3154
|
type: "button",
|
|
@@ -3105,10 +3157,10 @@ function PlayerControls() {
|
|
|
3105
3157
|
disabled: !isReady,
|
|
3106
3158
|
onClick: handlePlayPauseAction,
|
|
3107
3159
|
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",
|
|
3108
|
-
children: isPlaying ? /* @__PURE__ */
|
|
3160
|
+
children: isPlaying ? /* @__PURE__ */ jsx35(Pause3, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx35(Play4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3109
3161
|
}
|
|
3110
3162
|
),
|
|
3111
|
-
/* @__PURE__ */
|
|
3163
|
+
/* @__PURE__ */ jsx35(
|
|
3112
3164
|
"button",
|
|
3113
3165
|
{
|
|
3114
3166
|
type: "button",
|
|
@@ -3117,14 +3169,14 @@ function PlayerControls() {
|
|
|
3117
3169
|
disabled: !isReady || !hasNextTrack,
|
|
3118
3170
|
onClick: playNextTrack,
|
|
3119
3171
|
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",
|
|
3120
|
-
children: /* @__PURE__ */
|
|
3172
|
+
children: /* @__PURE__ */ jsx35(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3121
3173
|
}
|
|
3122
3174
|
)
|
|
3123
3175
|
] }),
|
|
3124
|
-
/* @__PURE__ */
|
|
3125
|
-
volume === 0 ? /* @__PURE__ */
|
|
3126
|
-
/* @__PURE__ */
|
|
3127
|
-
/* @__PURE__ */
|
|
3176
|
+
/* @__PURE__ */ jsxs28("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3177
|
+
volume === 0 ? /* @__PURE__ */ jsx35(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx35(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3178
|
+
/* @__PURE__ */ jsx35("span", { className: "sr-only", children: "Volume" }),
|
|
3179
|
+
/* @__PURE__ */ jsx35(
|
|
3128
3180
|
"input",
|
|
3129
3181
|
{
|
|
3130
3182
|
"aria-label": "Volume",
|
|
@@ -3146,7 +3198,7 @@ function PlayerControls() {
|
|
|
3146
3198
|
}
|
|
3147
3199
|
|
|
3148
3200
|
// src/genre-tree/PlayerPanel.tsx
|
|
3149
|
-
import { jsx as
|
|
3201
|
+
import { jsx as jsx36, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
3150
3202
|
var PANEL_ID = "player-panel";
|
|
3151
3203
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3152
3204
|
function PlayerPanel({
|
|
@@ -3186,7 +3238,7 @@ function PlayerPanel({
|
|
|
3186
3238
|
onClick: () => setIsExtendRequested(true),
|
|
3187
3239
|
expanded: false
|
|
3188
3240
|
} : null;
|
|
3189
|
-
return /* @__PURE__ */
|
|
3241
|
+
return /* @__PURE__ */ jsxs29(
|
|
3190
3242
|
"div",
|
|
3191
3243
|
{
|
|
3192
3244
|
id: PANEL_ID,
|
|
@@ -3199,7 +3251,7 @@ function PlayerPanel({
|
|
|
3199
3251
|
),
|
|
3200
3252
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3201
3253
|
children: [
|
|
3202
|
-
/* @__PURE__ */
|
|
3254
|
+
/* @__PURE__ */ jsx36(
|
|
3203
3255
|
PanelEdgeTab,
|
|
3204
3256
|
{
|
|
3205
3257
|
side: "right",
|
|
@@ -3209,7 +3261,7 @@ function PlayerPanel({
|
|
|
3209
3261
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3210
3262
|
}
|
|
3211
3263
|
),
|
|
3212
|
-
/* @__PURE__ */
|
|
3264
|
+
/* @__PURE__ */ jsxs29(
|
|
3213
3265
|
"div",
|
|
3214
3266
|
{
|
|
3215
3267
|
className: twMerge(
|
|
@@ -3217,20 +3269,20 @@ function PlayerPanel({
|
|
|
3217
3269
|
isExtended ? "flex-row" : "flex-col"
|
|
3218
3270
|
),
|
|
3219
3271
|
children: [
|
|
3220
|
-
/* @__PURE__ */
|
|
3221
|
-
/* @__PURE__ */
|
|
3222
|
-
/* @__PURE__ */
|
|
3223
|
-
/* @__PURE__ */
|
|
3224
|
-
playerTrackObject?.loadError && /* @__PURE__ */
|
|
3272
|
+
/* @__PURE__ */ jsxs29("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3273
|
+
/* @__PURE__ */ jsx36("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx36("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3274
|
+
/* @__PURE__ */ jsxs29("div", { className: "relative w-full shrink-0", children: [
|
|
3275
|
+
/* @__PURE__ */ jsx36(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3276
|
+
playerTrackObject?.loadError && /* @__PURE__ */ jsxs29("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3225
3277
|
playerTrackObject.loadError,
|
|
3226
|
-
/* @__PURE__ */
|
|
3278
|
+
/* @__PURE__ */ jsx36("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3227
3279
|
void retryPlayback();
|
|
3228
3280
|
}, children: "Retry" })
|
|
3229
3281
|
] })
|
|
3230
3282
|
] }),
|
|
3231
|
-
/* @__PURE__ */
|
|
3283
|
+
/* @__PURE__ */ jsx36(PlayerControls, {})
|
|
3232
3284
|
] }),
|
|
3233
|
-
/* @__PURE__ */
|
|
3285
|
+
/* @__PURE__ */ jsx36(
|
|
3234
3286
|
TrackListSidebar,
|
|
3235
3287
|
{
|
|
3236
3288
|
layout: "inline",
|
|
@@ -3257,11 +3309,11 @@ function PlayerPanel({
|
|
|
3257
3309
|
import { v4 as uuidv4 } from "uuid";
|
|
3258
3310
|
import { AiFillStar, AiOutlineStar } from "react-icons/ai";
|
|
3259
3311
|
import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
|
|
3260
|
-
import { jsx as
|
|
3312
|
+
import { jsx as jsx37, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
3261
3313
|
function Rating({ rating, handleChange }) {
|
|
3262
3314
|
const instanceId = uuidv4();
|
|
3263
|
-
return /* @__PURE__ */
|
|
3264
|
-
/* @__PURE__ */
|
|
3315
|
+
return /* @__PURE__ */ jsx37("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs30("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3316
|
+
/* @__PURE__ */ jsx37(
|
|
3265
3317
|
"input",
|
|
3266
3318
|
{
|
|
3267
3319
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3273,16 +3325,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3273
3325
|
onChange: handleChange
|
|
3274
3326
|
}
|
|
3275
3327
|
),
|
|
3276
|
-
/* @__PURE__ */
|
|
3277
|
-
/* @__PURE__ */
|
|
3328
|
+
/* @__PURE__ */ jsx37("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs30("div", { className: "flex", children: [
|
|
3329
|
+
/* @__PURE__ */ jsx37(
|
|
3278
3330
|
AiOutlineStar,
|
|
3279
3331
|
{
|
|
3280
3332
|
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)]"}`
|
|
3281
3333
|
}
|
|
3282
3334
|
),
|
|
3283
|
-
/* @__PURE__ */
|
|
3335
|
+
/* @__PURE__ */ jsx37("span", { className: "flex-grow w-8" }),
|
|
3284
3336
|
" "
|
|
3285
|
-
] }) : /* @__PURE__ */
|
|
3337
|
+
] }) : /* @__PURE__ */ jsx37(
|
|
3286
3338
|
AiFillStar,
|
|
3287
3339
|
{
|
|
3288
3340
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3294,6 +3346,12 @@ function Rating({ rating, handleChange }) {
|
|
|
3294
3346
|
export {
|
|
3295
3347
|
useGenrePlaylistPlayback,
|
|
3296
3348
|
GenrePlaylistTreePerRoot,
|
|
3349
|
+
SearchBox,
|
|
3350
|
+
SEARCH_FAILED_STATUS,
|
|
3351
|
+
searchStatus,
|
|
3352
|
+
useArtistSearchGroup,
|
|
3353
|
+
useSongSearchGroup,
|
|
3354
|
+
useGenreRemoteSearchGroup,
|
|
3297
3355
|
GenreSearch,
|
|
3298
3356
|
GenreNodeActionBand,
|
|
3299
3357
|
GenreInfoPanelBody,
|
|
@@ -3309,4 +3367,4 @@ export {
|
|
|
3309
3367
|
GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
|
|
3310
3368
|
GenreTreeOutlineSkeleton
|
|
3311
3369
|
};
|
|
3312
|
-
//# sourceMappingURL=chunk-
|
|
3370
|
+
//# sourceMappingURL=chunk-QMELESFB.js.map
|