@behindthemusictree/app-kit-ui 40.1.0 → 42.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/{chunk-DPYUE6WD.js → chunk-PVYUC2PN.js} +336 -279
- package/dist/chunk-PVYUC2PN.js.map +1 -0
- package/dist/genre-tree/index.cjs +344 -291
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +15 -7
- package/dist/genre-tree/index.js +1 -1
- package/dist/index.cjs +344 -291
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/dist/chunk-DPYUE6WD.js.map +0 -1
|
@@ -55,6 +55,7 @@ var import_lucide_react14 = require("lucide-react");
|
|
|
55
55
|
var import_ui21 = require("@behindthemusictree/ui");
|
|
56
56
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
57
57
|
var import_genre_tree30 = require("@behindthemusictree/app-kit/genre-tree");
|
|
58
|
+
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
58
59
|
var import_popup4 = require("@behindthemusictree/app-kit/popup");
|
|
59
60
|
|
|
60
61
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1402,6 +1403,29 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1402
1403
|
] });
|
|
1403
1404
|
}
|
|
1404
1405
|
|
|
1406
|
+
// src/genre-tree/CommonsPhoto.tsx
|
|
1407
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1408
|
+
function ExternalLink({ href, children }) {
|
|
1409
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1410
|
+
}
|
|
1411
|
+
function CommonsPhoto({
|
|
1412
|
+
image,
|
|
1413
|
+
alt,
|
|
1414
|
+
creditLabel = "Photo"
|
|
1415
|
+
}) {
|
|
1416
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("figure", { className: "flex flex-col gap-1.5", children: [
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("img", { src: image.url, alt, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
1419
|
+
creditLabel,
|
|
1420
|
+
": ",
|
|
1421
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1422
|
+
",",
|
|
1423
|
+
" ",
|
|
1424
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1425
|
+
] })
|
|
1426
|
+
] });
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1405
1429
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1406
1430
|
var import_react13 = require("react");
|
|
1407
1431
|
var import_react_query = require("@tanstack/react-query");
|
|
@@ -1424,15 +1448,15 @@ var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
1424
1448
|
|
|
1425
1449
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1426
1450
|
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1427
|
-
var
|
|
1451
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1428
1452
|
function MusicBrainzRecordingLink({
|
|
1429
1453
|
song,
|
|
1430
1454
|
children,
|
|
1431
1455
|
className: className2 = ""
|
|
1432
1456
|
}) {
|
|
1433
1457
|
const url = (0, import_genre_tree11.musicBrainzRecordingUrl)(song);
|
|
1434
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1435
|
-
return /* @__PURE__ */ (0,
|
|
1458
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children });
|
|
1459
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1436
1460
|
"a",
|
|
1437
1461
|
{
|
|
1438
1462
|
href: url,
|
|
@@ -1449,7 +1473,7 @@ function MusicBrainzRecordingLink({
|
|
|
1449
1473
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1450
1474
|
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1451
1475
|
var import_track_noun = require("@behindthemusictree/app-kit/track-noun");
|
|
1452
|
-
var
|
|
1476
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1453
1477
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1454
1478
|
var SongPageSchema = (0, import_transport3.PaginatedResponseSchema)(import_genre_tree15.SongDetailedSchema).extend({
|
|
1455
1479
|
overallTotal: import_zod.z.number().nullable(),
|
|
@@ -1536,35 +1560,35 @@ function GenrePlaylistTracks({
|
|
|
1536
1560
|
started.catch((error) => {
|
|
1537
1561
|
console.error("Failed to play genre playlist:", error);
|
|
1538
1562
|
if (error instanceof import_genre_tree16.TracksFailureAlreadyReportedError) return;
|
|
1539
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1563
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(InternalErrorPopup, { errorCode: error instanceof import_transport5.AppError ? error.code : import_transport5.ErrorCode.CLIENT_UNKNOWN }));
|
|
1540
1564
|
});
|
|
1541
1565
|
};
|
|
1542
|
-
return /* @__PURE__ */ (0,
|
|
1566
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { children: [
|
|
1543
1567
|
renderTracksToolbar?.(),
|
|
1544
|
-
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0,
|
|
1568
|
+
scope === "me" && !genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { children: "\u2014" }) : isPending ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { role: "alert", children: [
|
|
1545
1569
|
"Couldn't load ",
|
|
1546
1570
|
trackNoun.plural,
|
|
1547
1571
|
".",
|
|
1548
1572
|
" ",
|
|
1549
|
-
/* @__PURE__ */ (0,
|
|
1550
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1573
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1574
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-body [container-type:inline-size]", "aria-label": `${genreName} ${trackNoun.plural}`, children: [
|
|
1551
1575
|
tracks.map((track, index) => {
|
|
1552
1576
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1553
1577
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1554
1578
|
const isPlaying = isCurrent && playState === import_player6.PlayStates.PLAYING;
|
|
1555
|
-
const text = /* @__PURE__ */ (0,
|
|
1556
|
-
/* @__PURE__ */ (0,
|
|
1557
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1558
|
-
/* @__PURE__ */ (0,
|
|
1559
|
-
track.album && /* @__PURE__ */ (0,
|
|
1579
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1580
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1581
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1582
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree17.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1583
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1560
1584
|
] });
|
|
1561
1585
|
const leading = renderSongLeading?.(track, index);
|
|
1562
1586
|
const actions = renderSongActions?.(track);
|
|
1563
1587
|
const isUnplayable = (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
|
|
1564
1588
|
if (isUnplayable) {
|
|
1565
|
-
return /* @__PURE__ */ (0,
|
|
1589
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1566
1590
|
leading,
|
|
1567
|
-
/* @__PURE__ */ (0,
|
|
1591
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1568
1592
|
"button",
|
|
1569
1593
|
{
|
|
1570
1594
|
type: "button",
|
|
@@ -1572,27 +1596,27 @@ function GenrePlaylistTracks({
|
|
|
1572
1596
|
"aria-disabled": "true",
|
|
1573
1597
|
"aria-label": label,
|
|
1574
1598
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1575
|
-
children: /* @__PURE__ */ (0,
|
|
1599
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1576
1600
|
}
|
|
1577
1601
|
),
|
|
1578
1602
|
text,
|
|
1579
1603
|
actions
|
|
1580
1604
|
] }, track.uuid);
|
|
1581
1605
|
}
|
|
1582
|
-
return /* @__PURE__ */ (0,
|
|
1606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1583
1607
|
"li",
|
|
1584
1608
|
{
|
|
1585
1609
|
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)]" : ""}`,
|
|
1586
1610
|
children: [
|
|
1587
1611
|
leading,
|
|
1588
|
-
/* @__PURE__ */ (0,
|
|
1612
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1589
1613
|
"button",
|
|
1590
1614
|
{
|
|
1591
1615
|
type: "button",
|
|
1592
1616
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1593
1617
|
className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== import_player6.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1594
1618
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1595
|
-
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1619
|
+
children: isCurrent && playState === import_player6.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa2.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa2.FaPlay, { size: 10 })
|
|
1596
1620
|
}
|
|
1597
1621
|
),
|
|
1598
1622
|
text,
|
|
@@ -1602,7 +1626,7 @@ function GenrePlaylistTracks({
|
|
|
1602
1626
|
track.uuid
|
|
1603
1627
|
);
|
|
1604
1628
|
}),
|
|
1605
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1629
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1606
1630
|
] })
|
|
1607
1631
|
] }) });
|
|
1608
1632
|
}
|
|
@@ -1610,7 +1634,7 @@ function GenrePlaylistTracks({
|
|
|
1610
1634
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1611
1635
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1612
1636
|
var import_si2 = require("react-icons/si");
|
|
1613
|
-
var
|
|
1637
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1614
1638
|
function GenreDetailExtras({
|
|
1615
1639
|
genreUuid,
|
|
1616
1640
|
genrePlaylist,
|
|
@@ -1636,17 +1660,18 @@ function GenreDetailExtras({
|
|
|
1636
1660
|
criteriaOverviewSchema
|
|
1637
1661
|
);
|
|
1638
1662
|
if (isPending) {
|
|
1639
|
-
return /* @__PURE__ */ (0,
|
|
1640
|
-
/* @__PURE__ */ (0,
|
|
1663
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1664
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1641
1665
|
family
|
|
1642
1666
|
] });
|
|
1643
1667
|
}
|
|
1644
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1645
|
-
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1668
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_jsx_runtime29.Fragment, { children: family });
|
|
1669
|
+
const { summary, summarySourceUrl, essentialSongs, image } = overview;
|
|
1646
1670
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1647
|
-
return /* @__PURE__ */ (0,
|
|
1648
|
-
|
|
1649
|
-
|
|
1671
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1672
|
+
image && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(CommonsPhoto, { image, alt: overview.name, creditLabel: "Image" }),
|
|
1673
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-caption text-[var(--color-text)]", children: [
|
|
1674
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1650
1675
|
"a",
|
|
1651
1676
|
{
|
|
1652
1677
|
href: summarySourceUrl,
|
|
@@ -1654,12 +1679,12 @@ function GenreDetailExtras({
|
|
|
1654
1679
|
rel: "noopener noreferrer",
|
|
1655
1680
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1656
1681
|
children: [
|
|
1657
|
-
/* @__PURE__ */ (0,
|
|
1682
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1658
1683
|
"Wikipedia"
|
|
1659
1684
|
]
|
|
1660
1685
|
}
|
|
1661
1686
|
),
|
|
1662
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1687
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1663
1688
|
"a",
|
|
1664
1689
|
{
|
|
1665
1690
|
href: wikidataUrl,
|
|
@@ -1667,17 +1692,17 @@ function GenreDetailExtras({
|
|
|
1667
1692
|
rel: "noopener noreferrer",
|
|
1668
1693
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1669
1694
|
children: [
|
|
1670
|
-
/* @__PURE__ */ (0,
|
|
1695
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1671
1696
|
"Wikidata"
|
|
1672
1697
|
]
|
|
1673
1698
|
}
|
|
1674
1699
|
)
|
|
1675
1700
|
] }) }),
|
|
1676
|
-
/* @__PURE__ */ (0,
|
|
1701
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ClampedText, { className: "text-body text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1677
1702
|
family,
|
|
1678
1703
|
renderGenreDetailExtras?.(overview),
|
|
1679
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1680
|
-
/* @__PURE__ */ (0,
|
|
1704
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "text-body text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-body text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1705
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1681
1706
|
GenrePlaylistTracks,
|
|
1682
1707
|
{
|
|
1683
1708
|
genreUuid,
|
|
@@ -1695,10 +1720,10 @@ function GenreDetailExtras({
|
|
|
1695
1720
|
}
|
|
1696
1721
|
|
|
1697
1722
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1698
|
-
var
|
|
1723
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1699
1724
|
var import_react14 = require("react");
|
|
1700
1725
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1701
|
-
return /* @__PURE__ */ (0,
|
|
1726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreActionBand, { ...bandProps, playActions: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenrePlayActions, { genrePlaylist, playback }) });
|
|
1702
1727
|
}
|
|
1703
1728
|
function GenreInfoPanelBody({
|
|
1704
1729
|
toolbar,
|
|
@@ -1715,9 +1740,9 @@ function GenreInfoPanelBody({
|
|
|
1715
1740
|
}) {
|
|
1716
1741
|
const summary = scope === "reference" ? import_genre_tree19.GenreCatalogueCountsSchema.parse(genrePlaylist ?? catalogue) : void 0;
|
|
1717
1742
|
const selection = summary ? (0, import_genre_tree19.defaultGenreMusicSelection)(summary) : void 0;
|
|
1718
|
-
return /* @__PURE__ */ (0,
|
|
1743
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1719
1744
|
toolbar,
|
|
1720
|
-
genrePlaylist ? /* @__PURE__ */ (0,
|
|
1745
|
+
genrePlaylist ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreActionBand, { ...band }),
|
|
1721
1746
|
/* @__PURE__ */ (0, import_react14.createElement)(
|
|
1722
1747
|
GenreDetailExtras,
|
|
1723
1748
|
{
|
|
@@ -1737,7 +1762,7 @@ function GenreInfoPanelBody({
|
|
|
1737
1762
|
}
|
|
1738
1763
|
|
|
1739
1764
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
1740
|
-
var
|
|
1765
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1741
1766
|
var RELATIVE_ITEM_COUNT = 1;
|
|
1742
1767
|
function toNode(genre, parentId) {
|
|
1743
1768
|
return {
|
|
@@ -1762,7 +1787,8 @@ function useGenreDetailContent({
|
|
|
1762
1787
|
expanded,
|
|
1763
1788
|
treeStyles
|
|
1764
1789
|
}) {
|
|
1765
|
-
const
|
|
1790
|
+
const result = (0, import_genre_tree20.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
1791
|
+
const detail = result.status === "success" ? result.genre : void 0;
|
|
1766
1792
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: [], artist: extras?.artist });
|
|
1767
1793
|
const playback = hostPlayback ?? defaultPlayback;
|
|
1768
1794
|
const view = (0, import_react15.useMemo)(() => {
|
|
@@ -1791,22 +1817,27 @@ function useGenreDetailContent({
|
|
|
1791
1817
|
)
|
|
1792
1818
|
};
|
|
1793
1819
|
}, [detail, scope, treeStyles]);
|
|
1794
|
-
if (
|
|
1820
|
+
if (result.status === "not-found") return {
|
|
1821
|
+
name: null,
|
|
1822
|
+
title: "Genre not found",
|
|
1823
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This genre does not exist." })
|
|
1824
|
+
};
|
|
1825
|
+
if (result.status === "error") return {
|
|
1795
1826
|
name: null,
|
|
1796
1827
|
title: "Genre unavailable",
|
|
1797
|
-
content: /* @__PURE__ */ (0,
|
|
1828
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { role: "alert", className: "text-body text-[var(--color-text)]", children: "This genre could not be loaded." })
|
|
1798
1829
|
};
|
|
1799
|
-
if (
|
|
1830
|
+
if (!detail || !view) return {
|
|
1800
1831
|
name: null,
|
|
1801
1832
|
title: "Loading genre\u2026",
|
|
1802
|
-
content: /* @__PURE__ */ (0,
|
|
1803
|
-
/* @__PURE__ */ (0,
|
|
1804
|
-
/* @__PURE__ */ (0,
|
|
1805
|
-
/* @__PURE__ */ (0,
|
|
1833
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("div", { className: "flex flex-col gap-3", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
1834
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
|
|
1835
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.Skeleton, { className: "h-4 w-full" }),
|
|
1836
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.Skeleton, { className: "h-4 w-3/4" })
|
|
1806
1837
|
] })
|
|
1807
1838
|
};
|
|
1808
1839
|
const selectGenre = (nodeId) => onSelectGenre(import_genre_tree20.GenreUuidSchema.parse(nodeId));
|
|
1809
|
-
const family = /* @__PURE__ */ (0,
|
|
1840
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1810
1841
|
import_genre_tree_view4.GenreNodeFamily,
|
|
1811
1842
|
{
|
|
1812
1843
|
node: view.node,
|
|
@@ -1817,17 +1848,17 @@ function useGenreDetailContent({
|
|
|
1817
1848
|
onSelectNode: selectGenre
|
|
1818
1849
|
}
|
|
1819
1850
|
);
|
|
1820
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
1851
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
1821
1852
|
return {
|
|
1822
1853
|
name: detail.name,
|
|
1823
1854
|
title: detail.name,
|
|
1824
1855
|
headerAccent: { background: view.fill, color: view.textColor },
|
|
1825
1856
|
aside: expanded ? ancestors : void 0,
|
|
1826
|
-
content: /* @__PURE__ */ (0,
|
|
1827
|
-
/* @__PURE__ */ (0,
|
|
1857
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1858
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1828
1859
|
GenreInfoPanelBody,
|
|
1829
1860
|
{
|
|
1830
|
-
family: /* @__PURE__ */ (0,
|
|
1861
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_jsx_runtime31.Fragment, { children: [
|
|
1831
1862
|
family,
|
|
1832
1863
|
!expanded && ancestors
|
|
1833
1864
|
] }),
|
|
@@ -1871,7 +1902,7 @@ function GenreDetailPanel({
|
|
|
1871
1902
|
expanded,
|
|
1872
1903
|
band: { onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 }
|
|
1873
1904
|
});
|
|
1874
|
-
return /* @__PURE__ */ (0,
|
|
1905
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1875
1906
|
import_ui10.DetailPanel,
|
|
1876
1907
|
{
|
|
1877
1908
|
side: "left",
|
|
@@ -1884,7 +1915,7 @@ function GenreDetailPanel({
|
|
|
1884
1915
|
edgeTab,
|
|
1885
1916
|
aside: panel.aside,
|
|
1886
1917
|
titleInSearch,
|
|
1887
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
1918
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_ui10.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react9.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react9.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
|
|
1888
1919
|
children: panel.content
|
|
1889
1920
|
}
|
|
1890
1921
|
);
|
|
@@ -1893,7 +1924,7 @@ function GenreDetailPanel({
|
|
|
1893
1924
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1894
1925
|
var import_react16 = require("react");
|
|
1895
1926
|
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1896
|
-
var
|
|
1927
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1897
1928
|
function ChangeParentPopup({
|
|
1898
1929
|
genrePlaylist,
|
|
1899
1930
|
genrePlaylists,
|
|
@@ -1928,12 +1959,12 @@ function ChangeParentPopup({
|
|
|
1928
1959
|
window.addEventListener("keydown", handleKeyDown);
|
|
1929
1960
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1930
1961
|
}, [onClose]);
|
|
1931
|
-
return /* @__PURE__ */ (0,
|
|
1932
|
-
/* @__PURE__ */ (0,
|
|
1962
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("p", { className: "mb-3 text-body text-[var(--color-text-muted)]", children: [
|
|
1933
1964
|
"Current parent: ",
|
|
1934
1965
|
genrePlaylist.parent?.name ?? "none"
|
|
1935
1966
|
] }),
|
|
1936
|
-
/* @__PURE__ */ (0,
|
|
1967
|
+
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
|
|
1937
1968
|
GenreSearch,
|
|
1938
1969
|
{
|
|
1939
1970
|
genrePlaylists: candidates,
|
|
@@ -1941,7 +1972,7 @@ function ChangeParentPopup({
|
|
|
1941
1972
|
placeholder: "Search a new parent\u2026"
|
|
1942
1973
|
}
|
|
1943
1974
|
) }),
|
|
1944
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1975
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { role: "alert", className: "mt-3 text-body text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1945
1976
|
] });
|
|
1946
1977
|
}
|
|
1947
1978
|
|
|
@@ -1954,7 +1985,7 @@ var import_lucide_react10 = require("lucide-react");
|
|
|
1954
1985
|
var import_ui11 = require("@behindthemusictree/ui");
|
|
1955
1986
|
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1956
1987
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
1957
|
-
var
|
|
1988
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1958
1989
|
function ArtistActionBand({ artist, share }) {
|
|
1959
1990
|
const { trackList, playNewTrackListFromArtist } = (0, import_genre_tree22.useTrackList)();
|
|
1960
1991
|
const { isPlaying, setIsPlaying } = (0, import_player7.usePlayer)();
|
|
@@ -1970,11 +2001,11 @@ function ArtistActionBand({ artist, share }) {
|
|
|
1970
2001
|
console.error("Failed to load artist tracks:", error);
|
|
1971
2002
|
});
|
|
1972
2003
|
};
|
|
1973
|
-
return /* @__PURE__ */ (0,
|
|
1974
|
-
/* @__PURE__ */ (0,
|
|
2004
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(import_ui11.ActionBand, { children: [
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
|
|
1975
2006
|
import_ui11.ActionButton,
|
|
1976
2007
|
{
|
|
1977
|
-
icon: playing ? /* @__PURE__ */ (0,
|
|
2008
|
+
icon: playing ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react10.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_lucide_react10.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1978
2009
|
label: playing ? "Pause" : "Play",
|
|
1979
2010
|
caption: String(artist.playableTracksCount),
|
|
1980
2011
|
disabled: artist.playableTracksCount === 0,
|
|
@@ -1993,7 +2024,7 @@ var import_ui13 = require("@behindthemusictree/ui");
|
|
|
1993
2024
|
var import_lucide_react11 = require("lucide-react");
|
|
1994
2025
|
var import_si3 = require("react-icons/si");
|
|
1995
2026
|
var import_ui12 = require("@behindthemusictree/ui");
|
|
1996
|
-
var
|
|
2027
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1997
2028
|
var LINK_KINDS = {
|
|
1998
2029
|
official: { label: "Official website", Icon: import_lucide_react11.Globe },
|
|
1999
2030
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -2030,7 +2061,7 @@ function artistLinks(artist) {
|
|
|
2030
2061
|
return links;
|
|
2031
2062
|
}
|
|
2032
2063
|
function ArtistLinks({ links }) {
|
|
2033
|
-
return /* @__PURE__ */ (0,
|
|
2064
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_ui12.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
2034
2065
|
}
|
|
2035
2066
|
|
|
2036
2067
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -2041,45 +2072,59 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
2041
2072
|
}
|
|
2042
2073
|
|
|
2043
2074
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
2044
|
-
var
|
|
2075
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2045
2076
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
2046
|
-
function
|
|
2077
|
+
function originLabel(artist, origin) {
|
|
2078
|
+
if (origin.source === "area") return "From";
|
|
2079
|
+
if (artist.type === "person") return "Born in";
|
|
2080
|
+
if (artist.type === "group") return "Formed in";
|
|
2081
|
+
return "From";
|
|
2082
|
+
}
|
|
2083
|
+
function OriginValue({
|
|
2084
|
+
origin,
|
|
2085
|
+
country,
|
|
2086
|
+
renderPlaceLink
|
|
2087
|
+
}) {
|
|
2088
|
+
const place = renderPlaceLink && origin.regionId !== null ? renderPlaceLink({ ...origin, regionId: origin.regionId }) : origin.name;
|
|
2089
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_jsx_runtime35.Fragment, { children: [
|
|
2090
|
+
place,
|
|
2091
|
+
country && origin.kind !== "country" && `, ${country}`
|
|
2092
|
+
] });
|
|
2093
|
+
}
|
|
2094
|
+
function ArtistHeader({
|
|
2095
|
+
artist,
|
|
2096
|
+
renderPlaceLink
|
|
2097
|
+
}) {
|
|
2047
2098
|
const fields = [];
|
|
2048
2099
|
if (artist.type) fields.push({ label: "Type", value: artist.type.charAt(0).toUpperCase() + artist.type.slice(1) });
|
|
2049
|
-
const country = artist.country
|
|
2050
|
-
if (
|
|
2100
|
+
const country = artist.country ? countryNames.of(artist.country) : void 0;
|
|
2101
|
+
if (artist.origin) {
|
|
2102
|
+
fields.push({
|
|
2103
|
+
label: originLabel(artist, artist.origin),
|
|
2104
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(OriginValue, { origin: artist.origin, country, renderPlaceLink })
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
if (country && !artist.origin) fields.push({ label: "Country", value: country });
|
|
2051
2108
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
2052
2109
|
if (years) fields.push({ label: "Active", value: years });
|
|
2053
2110
|
const links = artistLinks(artist);
|
|
2054
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2111
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ArtistLinks, { links }) });
|
|
2055
2112
|
if (fields.length === 0) return null;
|
|
2056
|
-
return /* @__PURE__ */ (0,
|
|
2113
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui13.FieldList, { items: fields });
|
|
2057
2114
|
}
|
|
2058
2115
|
|
|
2059
2116
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
2060
|
-
var
|
|
2061
|
-
function ExternalLink({ href, children }) {
|
|
2062
|
-
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
2063
|
-
}
|
|
2117
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2064
2118
|
function ArtistPhoto({ image, name }) {
|
|
2065
|
-
return /* @__PURE__ */ (0,
|
|
2066
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2067
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("figcaption", { className: "text-caption text-[var(--color-text-muted)]", children: [
|
|
2068
|
-
"Photo: ",
|
|
2069
|
-
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
2070
|
-
",",
|
|
2071
|
-
" ",
|
|
2072
|
-
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
2073
|
-
] })
|
|
2074
|
-
] });
|
|
2119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(CommonsPhoto, { image, alt: name });
|
|
2075
2120
|
}
|
|
2076
2121
|
|
|
2077
2122
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
2078
2123
|
var import_ui14 = require("@behindthemusictree/ui");
|
|
2079
|
-
var
|
|
2124
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2080
2125
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
2081
2126
|
if (memberships.length === 0) return null;
|
|
2082
|
-
return /* @__PURE__ */ (0,
|
|
2127
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_ui14.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
2083
2128
|
const details = [
|
|
2084
2129
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
2085
2130
|
...membership.roles
|
|
@@ -2087,9 +2132,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2087
2132
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
2088
2133
|
return (
|
|
2089
2134
|
// A member can have several periods in the same group.
|
|
2090
|
-
/* @__PURE__ */ (0,
|
|
2091
|
-
/* @__PURE__ */ (0,
|
|
2092
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2135
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2136
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2137
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2093
2138
|
" \u2014 ",
|
|
2094
2139
|
detailsText
|
|
2095
2140
|
] })
|
|
@@ -2100,9 +2145,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2100
2145
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2101
2146
|
if (memberships.length === 0) return null;
|
|
2102
2147
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2103
|
-
return /* @__PURE__ */ (0,
|
|
2104
|
-
/* @__PURE__ */ (0,
|
|
2105
|
-
/* @__PURE__ */ (0,
|
|
2148
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_ui14.PanelSection, { title, children: [
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2106
2151
|
MembershipList,
|
|
2107
2152
|
{
|
|
2108
2153
|
title: "Past",
|
|
@@ -2115,7 +2160,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2115
2160
|
|
|
2116
2161
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2117
2162
|
var import_ui15 = require("@behindthemusictree/ui");
|
|
2118
|
-
var
|
|
2163
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2119
2164
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2120
2165
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2121
2166
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2135,12 +2180,12 @@ function byYear(a, b) {
|
|
|
2135
2180
|
function ArtistDiscography({ albums }) {
|
|
2136
2181
|
if (albums.length === 0) return null;
|
|
2137
2182
|
const sorted = [...albums].sort(byYear);
|
|
2138
|
-
return /* @__PURE__ */ (0,
|
|
2183
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2139
2184
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2140
2185
|
if (groupAlbums.length === 0) return null;
|
|
2141
|
-
return /* @__PURE__ */ (0,
|
|
2142
|
-
/* @__PURE__ */ (0,
|
|
2143
|
-
/* @__PURE__ */ (0,
|
|
2186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_ui15.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2187
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2188
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2144
2189
|
] }, album.uuid)) }) }, group);
|
|
2145
2190
|
}) });
|
|
2146
2191
|
}
|
|
@@ -2161,7 +2206,7 @@ var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
|
2161
2206
|
var import_fa3 = require("react-icons/fa");
|
|
2162
2207
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2163
2208
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2164
|
-
var
|
|
2209
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2165
2210
|
function TrackPositionPlayPause({
|
|
2166
2211
|
position,
|
|
2167
2212
|
uuid,
|
|
@@ -2175,14 +2220,14 @@ function TrackPositionPlayPause({
|
|
|
2175
2220
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PLAYING;
|
|
2176
2221
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player9.PlayStates.PAUSED;
|
|
2177
2222
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2178
|
-
return /* @__PURE__ */ (0,
|
|
2223
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2179
2224
|
"div",
|
|
2180
2225
|
{
|
|
2181
2226
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2182
2227
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2183
2228
|
onClick: handlePlayPauseClick,
|
|
2184
2229
|
children: [
|
|
2185
|
-
/* @__PURE__ */ (0,
|
|
2230
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2186
2231
|
"span",
|
|
2187
2232
|
{
|
|
2188
2233
|
className: "track-position-play-pause__bars",
|
|
@@ -2190,18 +2235,18 @@ function TrackPositionPlayPause({
|
|
|
2190
2235
|
role: "img",
|
|
2191
2236
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2192
2237
|
children: [
|
|
2193
|
-
/* @__PURE__ */ (0,
|
|
2194
|
-
/* @__PURE__ */ (0,
|
|
2195
|
-
/* @__PURE__ */ (0,
|
|
2238
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2239
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2240
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2196
2241
|
]
|
|
2197
2242
|
}
|
|
2198
2243
|
) : position }),
|
|
2199
|
-
/* @__PURE__ */ (0,
|
|
2244
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2200
2245
|
"div",
|
|
2201
2246
|
{
|
|
2202
2247
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2203
2248
|
"aria-hidden": "true",
|
|
2204
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2249
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_fa3.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_fa3.FaPlay, {})
|
|
2205
2250
|
}
|
|
2206
2251
|
)
|
|
2207
2252
|
]
|
|
@@ -2212,7 +2257,7 @@ function TrackPositionPlayPause({
|
|
|
2212
2257
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2213
2258
|
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2214
2259
|
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2215
|
-
var
|
|
2260
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2216
2261
|
function TrackItem({
|
|
2217
2262
|
track,
|
|
2218
2263
|
position,
|
|
@@ -2236,12 +2281,12 @@ function TrackItem({
|
|
|
2236
2281
|
loadTrackForPlayer(track.uuid);
|
|
2237
2282
|
}
|
|
2238
2283
|
};
|
|
2239
|
-
return /* @__PURE__ */ (0,
|
|
2284
|
+
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
|
|
2240
2285
|
"div",
|
|
2241
2286
|
{
|
|
2242
2287
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2243
2288
|
children: [
|
|
2244
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2289
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2245
2290
|
"div",
|
|
2246
2291
|
{
|
|
2247
2292
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2249,7 +2294,7 @@ function TrackItem({
|
|
|
2249
2294
|
"aria-disabled": "true",
|
|
2250
2295
|
children: position
|
|
2251
2296
|
}
|
|
2252
|
-
) : /* @__PURE__ */ (0,
|
|
2297
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2253
2298
|
TrackPositionPlayPause,
|
|
2254
2299
|
{
|
|
2255
2300
|
position,
|
|
@@ -2258,12 +2303,12 @@ function TrackItem({
|
|
|
2258
2303
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2259
2304
|
}
|
|
2260
2305
|
),
|
|
2261
|
-
/* @__PURE__ */ (0,
|
|
2262
|
-
/* @__PURE__ */ (0,
|
|
2263
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2306
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2307
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree24.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2308
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_genre_tree25.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2264
2309
|
] }),
|
|
2265
|
-
/* @__PURE__ */ (0,
|
|
2266
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-caption font-bold", children: track.genre.name }) : "" }),
|
|
2311
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2267
2312
|
"div",
|
|
2268
2313
|
{
|
|
2269
2314
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2271,14 +2316,14 @@ function TrackItem({
|
|
|
2271
2316
|
children: renderDuration(track)
|
|
2272
2317
|
}
|
|
2273
2318
|
),
|
|
2274
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2319
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2275
2320
|
]
|
|
2276
2321
|
}
|
|
2277
2322
|
);
|
|
2278
2323
|
}
|
|
2279
2324
|
|
|
2280
2325
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2281
|
-
var
|
|
2326
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2282
2327
|
var SongPageSchema2 = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree26.SongDetailedSchema);
|
|
2283
2328
|
var SORTS = [
|
|
2284
2329
|
{ ordering: "title", label: "Title" },
|
|
@@ -2311,8 +2356,8 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2311
2356
|
event.stopPropagation();
|
|
2312
2357
|
void play(song.uuid);
|
|
2313
2358
|
};
|
|
2314
|
-
return /* @__PURE__ */ (0,
|
|
2315
|
-
/* @__PURE__ */ (0,
|
|
2359
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "flex flex-wrap items-center justify-end gap-2 mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-caption", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2316
2361
|
"button",
|
|
2317
2362
|
{
|
|
2318
2363
|
type: "button",
|
|
@@ -2326,7 +2371,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2326
2371
|
},
|
|
2327
2372
|
sort.ordering
|
|
2328
2373
|
)) }) }),
|
|
2329
|
-
/* @__PURE__ */ (0,
|
|
2374
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2330
2375
|
TrackItem,
|
|
2331
2376
|
{
|
|
2332
2377
|
track: song,
|
|
@@ -2335,12 +2380,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2335
2380
|
renderSongLink
|
|
2336
2381
|
}
|
|
2337
2382
|
) }, song.uuid)) }),
|
|
2338
|
-
/* @__PURE__ */ (0,
|
|
2383
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2339
2384
|
] }) });
|
|
2340
2385
|
}
|
|
2341
2386
|
|
|
2342
2387
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2343
|
-
var
|
|
2388
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2344
2389
|
function useArtistPanel({
|
|
2345
2390
|
uuid,
|
|
2346
2391
|
scope,
|
|
@@ -2348,6 +2393,7 @@ function useArtistPanel({
|
|
|
2348
2393
|
getShareUrl,
|
|
2349
2394
|
renderArtistLink,
|
|
2350
2395
|
renderSongLink,
|
|
2396
|
+
renderPlaceLink,
|
|
2351
2397
|
expanded,
|
|
2352
2398
|
onExpandedChange,
|
|
2353
2399
|
onClose
|
|
@@ -2361,26 +2407,26 @@ function useArtistPanel({
|
|
|
2361
2407
|
return {
|
|
2362
2408
|
...panel,
|
|
2363
2409
|
title: "Artist",
|
|
2364
|
-
content: /* @__PURE__ */ (0,
|
|
2365
|
-
/* @__PURE__ */ (0,
|
|
2366
|
-
/* @__PURE__ */ (0,
|
|
2367
|
-
/* @__PURE__ */ (0,
|
|
2368
|
-
/* @__PURE__ */ (0,
|
|
2369
|
-
/* @__PURE__ */ (0,
|
|
2370
|
-
/* @__PURE__ */ (0,
|
|
2410
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2411
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2412
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
|
|
2413
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2414
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" }),
|
|
2415
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2416
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-16" })
|
|
2371
2417
|
] })
|
|
2372
2418
|
};
|
|
2373
2419
|
case "not-found":
|
|
2374
2420
|
return {
|
|
2375
2421
|
...panel,
|
|
2376
2422
|
title: "Artist not found",
|
|
2377
|
-
content: /* @__PURE__ */ (0,
|
|
2423
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2378
2424
|
};
|
|
2379
2425
|
case "error":
|
|
2380
2426
|
return {
|
|
2381
2427
|
...panel,
|
|
2382
2428
|
title: "Artist",
|
|
2383
|
-
content: /* @__PURE__ */ (0,
|
|
2429
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "text-red-600", children: "Could not load this artist." })
|
|
2384
2430
|
};
|
|
2385
2431
|
case "success": {
|
|
2386
2432
|
const { artist } = result;
|
|
@@ -2389,19 +2435,19 @@ function useArtistPanel({
|
|
|
2389
2435
|
return {
|
|
2390
2436
|
...panel,
|
|
2391
2437
|
title: artist.name,
|
|
2392
|
-
content: /* @__PURE__ */ (0,
|
|
2393
|
-
/* @__PURE__ */ (0,
|
|
2438
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
|
|
2439
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2394
2440
|
ArtistActionBand,
|
|
2395
2441
|
{
|
|
2396
2442
|
artist,
|
|
2397
|
-
share: shareTarget && shareUrl ? /* @__PURE__ */ (0,
|
|
2443
|
+
share: shareTarget && shareUrl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) : void 0
|
|
2398
2444
|
}
|
|
2399
2445
|
),
|
|
2400
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2401
|
-
/* @__PURE__ */ (0,
|
|
2402
|
-
/* @__PURE__ */ (0,
|
|
2403
|
-
/* @__PURE__ */ (0,
|
|
2404
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2446
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2447
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistHeader, { artist, renderPlaceLink }),
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2450
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2405
2451
|
ArtistSongs,
|
|
2406
2452
|
{
|
|
2407
2453
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2411,7 +2457,7 @@ function useArtistPanel({
|
|
|
2411
2457
|
}
|
|
2412
2458
|
)
|
|
2413
2459
|
] }),
|
|
2414
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2460
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2415
2461
|
};
|
|
2416
2462
|
}
|
|
2417
2463
|
}
|
|
@@ -2427,7 +2473,7 @@ var import_lucide_react12 = require("lucide-react");
|
|
|
2427
2473
|
var import_ui18 = require("@behindthemusictree/ui");
|
|
2428
2474
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
2429
2475
|
var import_genre_tree28 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2430
|
-
var
|
|
2476
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2431
2477
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2432
2478
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2433
2479
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -2435,27 +2481,27 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
2435
2481
|
const [open, setOpen] = (0, import_react18.useState)(false);
|
|
2436
2482
|
const history = (0, import_genre_tree28.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2437
2483
|
let body = null;
|
|
2438
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2439
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2484
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2485
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2440
2486
|
else if (history.status === "success")
|
|
2441
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2442
|
-
/* @__PURE__ */ (0,
|
|
2443
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2487
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("li", { className: "py-1.5 text-body", children: [
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2489
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2444
2490
|
" ",
|
|
2445
2491
|
"\u2014 ",
|
|
2446
2492
|
entry.oldValue ?? "none",
|
|
2447
2493
|
" \u2192 ",
|
|
2448
2494
|
entry.newValue ?? "none"
|
|
2449
2495
|
] }) : null,
|
|
2450
|
-
/* @__PURE__ */ (0,
|
|
2496
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { className: "block text-caption text-[var(--color-text-muted)]", children: [
|
|
2451
2497
|
entry.actorPseudo ?? "Anonymous",
|
|
2452
2498
|
" \xB7 ",
|
|
2453
2499
|
dateFormat.format(new Date(entry.createdOn))
|
|
2454
2500
|
] })
|
|
2455
2501
|
] }, entry.uuid)) });
|
|
2456
|
-
return /* @__PURE__ */ (0,
|
|
2457
|
-
/* @__PURE__ */ (0,
|
|
2458
|
-
/* @__PURE__ */ (0,
|
|
2502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelBleed, { padded: true, className: "border-t border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("details", { onToggle: (event) => setOpen(event.currentTarget.open), className: "pb-2.5", children: [
|
|
2503
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("summary", { className: `mt-2.5 cursor-pointer ${import_ui18.PANEL_LABEL_CLASS}`, children: "History" }),
|
|
2504
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "mt-1.5", children: body })
|
|
2459
2505
|
] }) });
|
|
2460
2506
|
}
|
|
2461
2507
|
function SongPanelContent(props) {
|
|
@@ -2474,13 +2520,13 @@ function SongPanelContent(props) {
|
|
|
2474
2520
|
const genre = song.genre;
|
|
2475
2521
|
fields.push({
|
|
2476
2522
|
label: "Genre",
|
|
2477
|
-
value: onSelectGenre ? /* @__PURE__ */ (0,
|
|
2478
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2479
|
-
/* @__PURE__ */ (0,
|
|
2523
|
+
value: onSelectGenre ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => onSelectGenre(genre), className: "hover:underline", children: genre.name }) : path ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("span", { children: [
|
|
2524
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2525
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("button", { type: "button", onClick: () => props.onSelectGenrePlaylist?.(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2480
2526
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2481
2527
|
});
|
|
2482
2528
|
} else {
|
|
2483
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2529
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2484
2530
|
}
|
|
2485
2531
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2486
2532
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2490,11 +2536,11 @@ function SongPanelContent(props) {
|
|
|
2490
2536
|
const links = [];
|
|
2491
2537
|
if (musicBrainzUrl)
|
|
2492
2538
|
links.push(
|
|
2493
|
-
/* @__PURE__ */ (0,
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2494
2540
|
);
|
|
2495
2541
|
if (song.youtubeVideo)
|
|
2496
2542
|
links.push(
|
|
2497
|
-
/* @__PURE__ */ (0,
|
|
2543
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2498
2544
|
"a",
|
|
2499
2545
|
{
|
|
2500
2546
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2509,31 +2555,31 @@ function SongPanelContent(props) {
|
|
|
2509
2555
|
if (links.length > 0)
|
|
2510
2556
|
fields.push({
|
|
2511
2557
|
label: "Links",
|
|
2512
|
-
value: /* @__PURE__ */ (0,
|
|
2558
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("span", { className: "flex gap-3", children: links })
|
|
2513
2559
|
});
|
|
2514
|
-
return /* @__PURE__ */ (0,
|
|
2515
|
-
/* @__PURE__ */ (0,
|
|
2516
|
-
/* @__PURE__ */ (0,
|
|
2560
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelBleed, { children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_ui18.ActionBand, { children: [
|
|
2562
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2517
2563
|
import_ui18.ActionButton,
|
|
2518
2564
|
{
|
|
2519
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
2565
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_lucide_react12.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
2520
2566
|
label: isPlaying ? "Pause" : "Play",
|
|
2521
2567
|
disabled: !!unplayableReason,
|
|
2522
2568
|
title: unplayableReason ?? void 0,
|
|
2523
2569
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope)
|
|
2524
2570
|
}
|
|
2525
2571
|
),
|
|
2526
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2572
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2527
2573
|
] }) }),
|
|
2528
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2529
|
-
/* @__PURE__ */ (0,
|
|
2530
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2531
|
-
/* @__PURE__ */ (0,
|
|
2574
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("p", { className: "text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_genre_tree28.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2575
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.FieldList, { items: fields }),
|
|
2576
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_ui18.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2532
2578
|
] });
|
|
2533
2579
|
}
|
|
2534
2580
|
|
|
2535
2581
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2536
|
-
var
|
|
2582
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2537
2583
|
function useSongPanel({
|
|
2538
2584
|
uuid,
|
|
2539
2585
|
scope,
|
|
@@ -2554,24 +2600,24 @@ function useSongPanel({
|
|
|
2554
2600
|
return {
|
|
2555
2601
|
...panel,
|
|
2556
2602
|
title: "Song",
|
|
2557
|
-
content: /* @__PURE__ */ (0,
|
|
2558
|
-
/* @__PURE__ */ (0,
|
|
2559
|
-
/* @__PURE__ */ (0,
|
|
2560
|
-
/* @__PURE__ */ (0,
|
|
2561
|
-
/* @__PURE__ */ (0,
|
|
2603
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5", children: [
|
|
2604
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
|
|
2605
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-4 w-32" }),
|
|
2606
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-3 w-16" }),
|
|
2607
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_ui19.Skeleton, { className: "h-4 w-24" })
|
|
2562
2608
|
] })
|
|
2563
2609
|
};
|
|
2564
2610
|
case "not-found":
|
|
2565
2611
|
return {
|
|
2566
2612
|
...panel,
|
|
2567
2613
|
title: "Song not found",
|
|
2568
|
-
content: /* @__PURE__ */ (0,
|
|
2614
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2569
2615
|
};
|
|
2570
2616
|
case "error":
|
|
2571
2617
|
return {
|
|
2572
2618
|
...panel,
|
|
2573
2619
|
title: "Song",
|
|
2574
|
-
content: /* @__PURE__ */ (0,
|
|
2620
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("p", { className: "text-red-600", children: "Could not load this song." })
|
|
2575
2621
|
};
|
|
2576
2622
|
case "success": {
|
|
2577
2623
|
const { song } = result;
|
|
@@ -2579,7 +2625,7 @@ function useSongPanel({
|
|
|
2579
2625
|
return {
|
|
2580
2626
|
...panel,
|
|
2581
2627
|
title: song.title,
|
|
2582
|
-
content: /* @__PURE__ */ (0,
|
|
2628
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2583
2629
|
SongPanelContent,
|
|
2584
2630
|
{
|
|
2585
2631
|
song,
|
|
@@ -2606,6 +2652,7 @@ function useEntityPanel({
|
|
|
2606
2652
|
getShareUrl,
|
|
2607
2653
|
renderArtistLink,
|
|
2608
2654
|
renderSongLink,
|
|
2655
|
+
renderPlaceLink,
|
|
2609
2656
|
song,
|
|
2610
2657
|
artist,
|
|
2611
2658
|
...navigation
|
|
@@ -2624,7 +2671,8 @@ function useEntityPanel({
|
|
|
2624
2671
|
getBackendBaseUrl: getArtistBackendBaseUrl ?? getBackendBaseUrl,
|
|
2625
2672
|
getShareUrl,
|
|
2626
2673
|
renderArtistLink,
|
|
2627
|
-
renderSongLink
|
|
2674
|
+
renderSongLink,
|
|
2675
|
+
renderPlaceLink
|
|
2628
2676
|
});
|
|
2629
2677
|
return songPanel ?? artistPanel;
|
|
2630
2678
|
}
|
|
@@ -2632,12 +2680,12 @@ function useEntityPanel({
|
|
|
2632
2680
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2633
2681
|
var import_md = require("react-icons/md");
|
|
2634
2682
|
var import_ui20 = require("@behindthemusictree/ui");
|
|
2635
|
-
var
|
|
2683
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
2636
2684
|
function SizeMetricToggle({
|
|
2637
2685
|
value,
|
|
2638
2686
|
onChange
|
|
2639
2687
|
}) {
|
|
2640
|
-
return /* @__PURE__ */ (0,
|
|
2688
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
2641
2689
|
import_ui20.SegmentedControl,
|
|
2642
2690
|
{
|
|
2643
2691
|
variant: "floating",
|
|
@@ -2645,15 +2693,15 @@ function SizeMetricToggle({
|
|
|
2645
2693
|
value,
|
|
2646
2694
|
onChange,
|
|
2647
2695
|
options: [
|
|
2648
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2649
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2696
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_md.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2697
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_md.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2650
2698
|
]
|
|
2651
2699
|
}
|
|
2652
2700
|
);
|
|
2653
2701
|
}
|
|
2654
2702
|
|
|
2655
2703
|
// src/genre-tree/GenreTreeView.tsx
|
|
2656
|
-
var
|
|
2704
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
2657
2705
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2658
2706
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2659
2707
|
var noop = () => {
|
|
@@ -2701,6 +2749,7 @@ function GenreTreeView({
|
|
|
2701
2749
|
songSearch = true,
|
|
2702
2750
|
onSongSelect,
|
|
2703
2751
|
renderSongLink,
|
|
2752
|
+
renderPlaceLink,
|
|
2704
2753
|
renderSummary,
|
|
2705
2754
|
renderEssentialTracks,
|
|
2706
2755
|
renderSongLeading,
|
|
@@ -2761,7 +2810,7 @@ function GenreTreeView({
|
|
|
2761
2810
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2762
2811
|
toggleWheelRotation,
|
|
2763
2812
|
onNodeHover: handleNodeHover
|
|
2764
|
-
} = (0,
|
|
2813
|
+
} = (0, import_genre_tree31.useGenreTreeViewState)({
|
|
2765
2814
|
scope,
|
|
2766
2815
|
getBackendBaseUrl,
|
|
2767
2816
|
viewMode: controlledViewMode,
|
|
@@ -2780,12 +2829,12 @@ function GenreTreeView({
|
|
|
2780
2829
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2781
2830
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2782
2831
|
}
|
|
2783
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2784
|
-
const { mutate: updateGenre } = (0,
|
|
2832
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree31.genreUpdateMutationKey }) > 0;
|
|
2833
|
+
const { mutate: updateGenre } = (0, import_genre_tree31.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2785
2834
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2786
2835
|
const isArtistFiltered = artistUuid !== null;
|
|
2787
2836
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2788
|
-
const artistResult = (0,
|
|
2837
|
+
const artistResult = (0, import_genre_tree31.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2789
2838
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2790
2839
|
const filterArtist = (0, import_react19.useMemo)(
|
|
2791
2840
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2805,14 +2854,14 @@ function GenreTreeView({
|
|
|
2805
2854
|
const openSongRef = (0, import_react19.useRef)(openSong);
|
|
2806
2855
|
openSongRef.current = openSong;
|
|
2807
2856
|
const resolvedRenderSongLink = (0, import_react19.useCallback)(
|
|
2808
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2857
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2809
2858
|
[renderSongLink]
|
|
2810
2859
|
);
|
|
2811
2860
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react19.useState)(null);
|
|
2812
2861
|
const [expandedArtistUuid, setExpandedArtistUuid] = (0, import_react19.useState)(null);
|
|
2813
2862
|
const artistBaseUrl = artistSearchBaseUrl ?? getBackendBaseUrl;
|
|
2814
2863
|
const songBaseUrl = getBackendBaseUrl;
|
|
2815
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2864
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2816
2865
|
const getGenrePath = (genre) => {
|
|
2817
2866
|
const path = [];
|
|
2818
2867
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2830,6 +2879,7 @@ function GenreTreeView({
|
|
|
2830
2879
|
getShareUrl: getShareUrlRef.current,
|
|
2831
2880
|
renderArtistLink: resolvedRenderArtistLink,
|
|
2832
2881
|
renderSongLink: resolvedRenderSongLink,
|
|
2882
|
+
renderPlaceLink,
|
|
2833
2883
|
song: {
|
|
2834
2884
|
uuid: songUuid,
|
|
2835
2885
|
expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
|
|
@@ -2889,9 +2939,10 @@ function GenreTreeView({
|
|
|
2889
2939
|
resolveStyle
|
|
2890
2940
|
);
|
|
2891
2941
|
const treeStyles = (0, import_react19.useMemo)(() => toGenreNodeStyles(relations, visibleGenrePlaylists), [relations, visibleGenrePlaylists]);
|
|
2892
|
-
const
|
|
2893
|
-
|
|
2894
|
-
|
|
2942
|
+
const validSelectedGenreUuid = (0, import_genre_tree30.toGenreUuid)(selectedGenreUuid);
|
|
2943
|
+
const [selectionHistory, setSelectionHistory] = (0, import_react19.useState)({ current: validSelectedGenreUuid, previous: null });
|
|
2944
|
+
if (selectionHistory.current !== validSelectedGenreUuid) {
|
|
2945
|
+
setSelectionHistory({ current: validSelectedGenreUuid, previous: selectionHistory.current ?? selectionHistory.previous });
|
|
2895
2946
|
}
|
|
2896
2947
|
const handlePrevious = selectionHistory.previous ? () => selectGenre(selectionHistory.previous) : void 0;
|
|
2897
2948
|
const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
|
|
@@ -2923,7 +2974,7 @@ function GenreTreeView({
|
|
|
2923
2974
|
const canToggleSide = !readOnly && criteria !== void 0 && criteria !== null && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2924
2975
|
const side = criteria?.side ?? "core";
|
|
2925
2976
|
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find((sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist?.uuid && sibling.criteria?.side === "pop") : void 0;
|
|
2926
|
-
const shareTarget =
|
|
2977
|
+
const shareTarget = validSelectedGenreUuid ? { kind: "genre", genreUuid: validSelectedGenreUuid, name: criteria?.name ?? "Genre", scope, artistUuid } : void 0;
|
|
2927
2978
|
const shareUrl = shareTarget ? getShareUrlRef.current?.(shareTarget) ?? void 0 : void 0;
|
|
2928
2979
|
const genreContent = useGenreDetailContent({
|
|
2929
2980
|
genreUuid: selectedGenreUuid,
|
|
@@ -2945,7 +2996,7 @@ function GenreTreeView({
|
|
|
2945
2996
|
onAddChild: readOnly || !criteria ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2946
2997
|
onRename: readOnly || !criteria ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2947
2998
|
onChangeParent: readOnly || !genrePlaylist || !criteria ? void 0 : () => showPopup(
|
|
2948
|
-
/* @__PURE__ */ (0,
|
|
2999
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
2949
3000
|
ChangeParentPopup,
|
|
2950
3001
|
{
|
|
2951
3002
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2989,7 +3040,7 @@ function GenreTreeView({
|
|
|
2989
3040
|
onClose: otherPanel.onClose,
|
|
2990
3041
|
searchName: otherPanel.title
|
|
2991
3042
|
};
|
|
2992
|
-
} else if (selectedGenreUuid) {
|
|
3043
|
+
} else if (selectedGenreUuid !== null) {
|
|
2993
3044
|
activePanel = {
|
|
2994
3045
|
title: genreContent.title,
|
|
2995
3046
|
content: genreContent.content,
|
|
@@ -3006,19 +3057,19 @@ function GenreTreeView({
|
|
|
3006
3057
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
3007
3058
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
3008
3059
|
} : void 0;
|
|
3009
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
3060
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3010
3061
|
import_ui21.IconButton,
|
|
3011
3062
|
{
|
|
3012
3063
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
3013
3064
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
3014
3065
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
3015
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3066
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react14.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react14.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3016
3067
|
}
|
|
3017
3068
|
) : null;
|
|
3018
3069
|
const renderArtistFilter = () => {
|
|
3019
3070
|
if (isLoading || !filterArtist) return null;
|
|
3020
|
-
return /* @__PURE__ */ (0,
|
|
3021
|
-
/* @__PURE__ */ (0,
|
|
3071
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-body", children: [
|
|
3072
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3022
3073
|
"button",
|
|
3023
3074
|
{
|
|
3024
3075
|
type: "button",
|
|
@@ -3030,7 +3081,7 @@ function GenreTreeView({
|
|
|
3030
3081
|
children: filterArtist.name
|
|
3031
3082
|
}
|
|
3032
3083
|
),
|
|
3033
|
-
/* @__PURE__ */ (0,
|
|
3084
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3034
3085
|
"button",
|
|
3035
3086
|
{
|
|
3036
3087
|
type: "button",
|
|
@@ -3042,8 +3093,8 @@ function GenreTreeView({
|
|
|
3042
3093
|
)
|
|
3043
3094
|
] });
|
|
3044
3095
|
};
|
|
3045
|
-
const actions = /* @__PURE__ */ (0,
|
|
3046
|
-
!isLoading && !isError && /* @__PURE__ */ (0,
|
|
3096
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3097
|
+
!isLoading && !isError && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3047
3098
|
GenreTreeSearch,
|
|
3048
3099
|
{
|
|
3049
3100
|
artistBaseUrl,
|
|
@@ -3076,9 +3127,9 @@ function GenreTreeView({
|
|
|
3076
3127
|
extraGroups: extraSearchGroups
|
|
3077
3128
|
}
|
|
3078
3129
|
),
|
|
3079
|
-
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0,
|
|
3080
|
-
/* @__PURE__ */ (0,
|
|
3081
|
-
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0,
|
|
3130
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3131
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
3132
|
+
!isLoading && !isError && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3082
3133
|
import_ui21.SegmentedControl,
|
|
3083
3134
|
{
|
|
3084
3135
|
"aria-label": "Tree view mode",
|
|
@@ -3092,13 +3143,13 @@ function GenreTreeView({
|
|
|
3092
3143
|
]
|
|
3093
3144
|
}
|
|
3094
3145
|
),
|
|
3095
|
-
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3146
|
+
!isLoading && !isError && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3096
3147
|
"div",
|
|
3097
3148
|
{
|
|
3098
3149
|
className: "flex items-center gap-1",
|
|
3099
3150
|
role: "group",
|
|
3100
3151
|
"aria-label": "Tree display options",
|
|
3101
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3152
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3102
3153
|
import_ui21.Button,
|
|
3103
3154
|
{
|
|
3104
3155
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -3109,7 +3160,7 @@ function GenreTreeView({
|
|
|
3109
3160
|
)
|
|
3110
3161
|
}
|
|
3111
3162
|
),
|
|
3112
|
-
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3163
|
+
!isLoading && !isError && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3113
3164
|
import_ui21.IconTextButton,
|
|
3114
3165
|
{
|
|
3115
3166
|
icon: import_lucide_react13.Plus,
|
|
@@ -3119,38 +3170,38 @@ function GenreTreeView({
|
|
|
3119
3170
|
)
|
|
3120
3171
|
] })
|
|
3121
3172
|
] });
|
|
3122
|
-
return /* @__PURE__ */ (0,
|
|
3173
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3123
3174
|
"div",
|
|
3124
3175
|
{
|
|
3125
3176
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3126
3177
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3127
3178
|
children: [
|
|
3128
|
-
/* @__PURE__ */ (0,
|
|
3179
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3129
3180
|
"div",
|
|
3130
3181
|
{
|
|
3131
3182
|
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))] md:left-[var(--layout-panel-inset)] md:top-[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"}`,
|
|
3132
3183
|
children: actions
|
|
3133
3184
|
}
|
|
3134
3185
|
),
|
|
3135
|
-
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0,
|
|
3186
|
+
!isLoading && !isError && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3136
3187
|
"div",
|
|
3137
3188
|
{
|
|
3138
3189
|
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))] md:top-[var(--layout-panel-inset)]" : "top-5"}`,
|
|
3139
3190
|
children: renderArtistFilter()
|
|
3140
3191
|
}
|
|
3141
3192
|
),
|
|
3142
|
-
/* @__PURE__ */ (0,
|
|
3143
|
-
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3144
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3193
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3194
|
+
!isLoading && !isError && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3195
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeViewSkeleton, { viewMode }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("p", { role: "alert", className: "m-auto text-center", children: [
|
|
3145
3196
|
"Couldn't load the genre tree.",
|
|
3146
3197
|
" ",
|
|
3147
|
-
/* @__PURE__ */ (0,
|
|
3198
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("button", { type: "button", className: "underline", onClick: refetch, children: "Retry" })
|
|
3148
3199
|
] }) : viewMode === "wheel" ? (
|
|
3149
3200
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3150
3201
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3151
3202
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3152
3203
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3153
|
-
/* @__PURE__ */ (0,
|
|
3204
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3154
3205
|
GenrePlaylistTreeWheel,
|
|
3155
3206
|
{
|
|
3156
3207
|
scope,
|
|
@@ -3168,9 +3219,9 @@ function GenreTreeView({
|
|
|
3168
3219
|
allowWheelRotation,
|
|
3169
3220
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3170
3221
|
busy,
|
|
3171
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3222
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3172
3223
|
floatingControlsLeading,
|
|
3173
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3224
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3174
3225
|
] }) : void 0,
|
|
3175
3226
|
defaultViewport,
|
|
3176
3227
|
viewportRestore,
|
|
@@ -3179,7 +3230,7 @@ function GenreTreeView({
|
|
|
3179
3230
|
) }) })
|
|
3180
3231
|
) : viewMode === "pop-core" ? (
|
|
3181
3232
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3182
|
-
/* @__PURE__ */ (0,
|
|
3233
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_genre_tree_view5.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3183
3234
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3184
3235
|
{
|
|
3185
3236
|
scope,
|
|
@@ -3197,9 +3248,9 @@ function GenreTreeView({
|
|
|
3197
3248
|
allowWheelRotation,
|
|
3198
3249
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3199
3250
|
busy,
|
|
3200
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3251
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3201
3252
|
floatingControlsLeading,
|
|
3202
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3253
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3203
3254
|
] }) : void 0,
|
|
3204
3255
|
defaultViewport,
|
|
3205
3256
|
viewportRestore,
|
|
@@ -3207,7 +3258,7 @@ function GenreTreeView({
|
|
|
3207
3258
|
...focus
|
|
3208
3259
|
}
|
|
3209
3260
|
) }) })
|
|
3210
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3261
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3211
3262
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3212
3263
|
{
|
|
3213
3264
|
outline: true,
|
|
@@ -3224,13 +3275,13 @@ function GenreTreeView({
|
|
|
3224
3275
|
selectedNodeId,
|
|
3225
3276
|
readOnly
|
|
3226
3277
|
}
|
|
3227
|
-
) }) : /* @__PURE__ */ (0,
|
|
3278
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
3228
3279
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3229
|
-
return /* @__PURE__ */ (0,
|
|
3280
|
+
return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3230
3281
|
"div",
|
|
3231
3282
|
{
|
|
3232
3283
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3233
|
-
children: /* @__PURE__ */ (0,
|
|
3284
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3234
3285
|
GenrePlaylistTreePerRoot,
|
|
3235
3286
|
{
|
|
3236
3287
|
scope,
|
|
@@ -3257,7 +3308,7 @@ function GenreTreeView({
|
|
|
3257
3308
|
);
|
|
3258
3309
|
}
|
|
3259
3310
|
) }),
|
|
3260
|
-
/* @__PURE__ */ (0,
|
|
3311
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3261
3312
|
import_ui21.DetailPanel,
|
|
3262
3313
|
{
|
|
3263
3314
|
ref: panelRef,
|
|
@@ -3272,7 +3323,7 @@ function GenreTreeView({
|
|
|
3272
3323
|
onExpandedChange: activePanel?.onExpandedChange,
|
|
3273
3324
|
aside: activePanel?.aside,
|
|
3274
3325
|
edgeTab: detailTab,
|
|
3275
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3326
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
|
|
3276
3327
|
activePanel.content,
|
|
3277
3328
|
activePanel.aside
|
|
3278
3329
|
] }) : activePanel?.content
|
|
@@ -3290,10 +3341,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3290
3341
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3291
3342
|
var import_react20 = require("react");
|
|
3292
3343
|
var import_lucide_react15 = require("lucide-react");
|
|
3293
|
-
var import_genre_tree31 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3294
3344
|
var import_genre_tree32 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3345
|
+
var import_genre_tree33 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3295
3346
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
3296
|
-
var
|
|
3347
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3297
3348
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react20.useEffect : import_react20.useLayoutEffect;
|
|
3298
3349
|
function TrackListSidebar({
|
|
3299
3350
|
className: className2 = "",
|
|
@@ -3306,7 +3357,7 @@ function TrackListSidebar({
|
|
|
3306
3357
|
renderArtistLink,
|
|
3307
3358
|
renderSongLink
|
|
3308
3359
|
}) {
|
|
3309
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3360
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree32.useTrackList)();
|
|
3310
3361
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
3311
3362
|
const trackListRef = (0, import_react20.useRef)(null);
|
|
3312
3363
|
const previousOriginRef = (0, import_react20.useRef)(trackList?.origin ?? null);
|
|
@@ -3395,25 +3446,25 @@ function TrackListSidebar({
|
|
|
3395
3446
|
}
|
|
3396
3447
|
};
|
|
3397
3448
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3398
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3449
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3399
3450
|
"div",
|
|
3400
3451
|
{
|
|
3401
3452
|
"data-theme": "dark",
|
|
3402
3453
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3403
3454
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3404
3455
|
children: [
|
|
3405
|
-
/* @__PURE__ */ (0,
|
|
3406
|
-
/* @__PURE__ */ (0,
|
|
3407
|
-
/* @__PURE__ */ (0,
|
|
3408
|
-
/* @__PURE__ */ (0,
|
|
3409
|
-
/* @__PURE__ */ (0,
|
|
3456
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3457
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3458
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3459
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3460
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3410
3461
|
] }),
|
|
3411
|
-
/* @__PURE__ */ (0,
|
|
3412
|
-
trackList.origin.type ===
|
|
3462
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "info flex items-center text-body text-[var(--color-text-muted)]", children: [
|
|
3463
|
+
trackList.origin.type === import_genre_tree33.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree33.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3413
3464
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3414
3465
|
] })
|
|
3415
3466
|
] }),
|
|
3416
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3467
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3417
3468
|
"button",
|
|
3418
3469
|
{
|
|
3419
3470
|
type: "button",
|
|
@@ -3422,11 +3473,11 @@ function TrackListSidebar({
|
|
|
3422
3473
|
"aria-expanded": !isCollapsed,
|
|
3423
3474
|
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"}`,
|
|
3424
3475
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3425
|
-
children: /* @__PURE__ */ (0,
|
|
3476
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react15.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3426
3477
|
}
|
|
3427
3478
|
)
|
|
3428
3479
|
] }),
|
|
3429
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3480
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3430
3481
|
"ul",
|
|
3431
3482
|
{
|
|
3432
3483
|
ref: trackListRef,
|
|
@@ -3436,7 +3487,7 @@ function TrackListSidebar({
|
|
|
3436
3487
|
onPointerDown: markUserScrollIntent,
|
|
3437
3488
|
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",
|
|
3438
3489
|
children: [
|
|
3439
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3490
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3440
3491
|
TrackItem,
|
|
3441
3492
|
{
|
|
3442
3493
|
track,
|
|
@@ -3447,7 +3498,7 @@ function TrackListSidebar({
|
|
|
3447
3498
|
renderSongLink
|
|
3448
3499
|
}
|
|
3449
3500
|
) }, track.uuid)),
|
|
3450
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3501
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3451
3502
|
]
|
|
3452
3503
|
}
|
|
3453
3504
|
)
|
|
@@ -3460,14 +3511,14 @@ function TrackListSidebar({
|
|
|
3460
3511
|
var import_react21 = require("react");
|
|
3461
3512
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3462
3513
|
var import_player14 = require("@behindthemusictree/app-kit/player");
|
|
3463
|
-
var
|
|
3514
|
+
var import_genre_tree35 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3464
3515
|
var import_ui22 = require("@behindthemusictree/ui");
|
|
3465
3516
|
|
|
3466
3517
|
// src/genre-tree/PlayerControls.tsx
|
|
3467
3518
|
var import_lucide_react16 = require("lucide-react");
|
|
3468
|
-
var
|
|
3519
|
+
var import_genre_tree34 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3469
3520
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3470
|
-
var
|
|
3521
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3471
3522
|
function PlayerControls() {
|
|
3472
3523
|
const {
|
|
3473
3524
|
duration,
|
|
@@ -3479,22 +3530,22 @@ function PlayerControls() {
|
|
|
3479
3530
|
setVolume,
|
|
3480
3531
|
volume
|
|
3481
3532
|
} = (0, import_player13.usePlayer)();
|
|
3482
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3533
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree34.useTrackList)();
|
|
3483
3534
|
const currentTime = (0, import_player13.useCurrentTime)();
|
|
3484
3535
|
const isBusy = isLoading || playState === import_player13.PlayStates.LOADING;
|
|
3485
3536
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3486
3537
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3487
3538
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3488
3539
|
const isPlaying = playState === import_player13.PlayStates.PLAYING;
|
|
3489
|
-
return /* @__PURE__ */ (0,
|
|
3540
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3490
3541
|
"section",
|
|
3491
3542
|
{
|
|
3492
3543
|
"aria-label": "Player controls",
|
|
3493
3544
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3494
3545
|
children: [
|
|
3495
|
-
/* @__PURE__ */ (0,
|
|
3496
|
-
/* @__PURE__ */ (0,
|
|
3497
|
-
/* @__PURE__ */ (0,
|
|
3546
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3547
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree34.formatTime)(safeCurrentTime) }),
|
|
3548
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3498
3549
|
"input",
|
|
3499
3550
|
{
|
|
3500
3551
|
"aria-label": "Seek",
|
|
@@ -3508,11 +3559,11 @@ function PlayerControls() {
|
|
|
3508
3559
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3509
3560
|
}
|
|
3510
3561
|
),
|
|
3511
|
-
/* @__PURE__ */ (0,
|
|
3562
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree34.formatTime)(safeDuration) })
|
|
3512
3563
|
] }),
|
|
3513
|
-
/* @__PURE__ */ (0,
|
|
3514
|
-
/* @__PURE__ */ (0,
|
|
3515
|
-
/* @__PURE__ */ (0,
|
|
3564
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3565
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3566
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3516
3567
|
"button",
|
|
3517
3568
|
{
|
|
3518
3569
|
type: "button",
|
|
@@ -3521,10 +3572,10 @@ function PlayerControls() {
|
|
|
3521
3572
|
disabled: !isReady || !hasPreviousTrack,
|
|
3522
3573
|
onClick: playPreviousTrack,
|
|
3523
3574
|
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",
|
|
3524
|
-
children: /* @__PURE__ */ (0,
|
|
3575
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3525
3576
|
}
|
|
3526
3577
|
),
|
|
3527
|
-
/* @__PURE__ */ (0,
|
|
3578
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3528
3579
|
"button",
|
|
3529
3580
|
{
|
|
3530
3581
|
type: "button",
|
|
@@ -3533,10 +3584,10 @@ function PlayerControls() {
|
|
|
3533
3584
|
disabled: !isReady,
|
|
3534
3585
|
onClick: handlePlayPauseAction,
|
|
3535
3586
|
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",
|
|
3536
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3587
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3537
3588
|
}
|
|
3538
3589
|
),
|
|
3539
|
-
/* @__PURE__ */ (0,
|
|
3590
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3540
3591
|
"button",
|
|
3541
3592
|
{
|
|
3542
3593
|
type: "button",
|
|
@@ -3545,14 +3596,14 @@ function PlayerControls() {
|
|
|
3545
3596
|
disabled: !isReady || !hasNextTrack,
|
|
3546
3597
|
onClick: playNextTrack,
|
|
3547
3598
|
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",
|
|
3548
|
-
children: /* @__PURE__ */ (0,
|
|
3599
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3549
3600
|
}
|
|
3550
3601
|
)
|
|
3551
3602
|
] }),
|
|
3552
|
-
/* @__PURE__ */ (0,
|
|
3553
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3554
|
-
/* @__PURE__ */ (0,
|
|
3555
|
-
/* @__PURE__ */ (0,
|
|
3603
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3604
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_lucide_react16.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3605
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3606
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3556
3607
|
"input",
|
|
3557
3608
|
{
|
|
3558
3609
|
"aria-label": "Volume",
|
|
@@ -3574,7 +3625,7 @@ function PlayerControls() {
|
|
|
3574
3625
|
}
|
|
3575
3626
|
|
|
3576
3627
|
// src/genre-tree/PlayerPanel.tsx
|
|
3577
|
-
var
|
|
3628
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3578
3629
|
var PANEL_ID = "player-panel";
|
|
3579
3630
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3580
3631
|
function PlayerPanel({
|
|
@@ -3587,7 +3638,7 @@ function PlayerPanel({
|
|
|
3587
3638
|
renderSongLink
|
|
3588
3639
|
}) {
|
|
3589
3640
|
const { playerTrackObject, retryPlayback } = (0, import_player14.usePlayer)();
|
|
3590
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3641
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree35.useTrackListSidebarVisibility)();
|
|
3591
3642
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react21.useState)("");
|
|
3592
3643
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3593
3644
|
(0, import_react21.useEffect)(() => {
|
|
@@ -3614,7 +3665,7 @@ function PlayerPanel({
|
|
|
3614
3665
|
onClick: () => setIsExtendRequested(true),
|
|
3615
3666
|
expanded: false
|
|
3616
3667
|
} : null;
|
|
3617
|
-
return /* @__PURE__ */ (0,
|
|
3668
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
3618
3669
|
"div",
|
|
3619
3670
|
{
|
|
3620
3671
|
id: PANEL_ID,
|
|
@@ -3627,7 +3678,7 @@ function PlayerPanel({
|
|
|
3627
3678
|
),
|
|
3628
3679
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3629
3680
|
children: [
|
|
3630
|
-
/* @__PURE__ */ (0,
|
|
3681
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3631
3682
|
import_ui22.PanelEdgeTab,
|
|
3632
3683
|
{
|
|
3633
3684
|
side: "right",
|
|
@@ -3637,7 +3688,7 @@ function PlayerPanel({
|
|
|
3637
3688
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3638
3689
|
}
|
|
3639
3690
|
),
|
|
3640
|
-
/* @__PURE__ */ (0,
|
|
3691
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
3641
3692
|
"div",
|
|
3642
3693
|
{
|
|
3643
3694
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3645,20 +3696,20 @@ function PlayerPanel({
|
|
|
3645
3696
|
isExtended ? "flex-row" : "flex-col"
|
|
3646
3697
|
),
|
|
3647
3698
|
children: [
|
|
3648
|
-
/* @__PURE__ */ (0,
|
|
3649
|
-
/* @__PURE__ */ (0,
|
|
3650
|
-
/* @__PURE__ */ (0,
|
|
3651
|
-
/* @__PURE__ */ (0,
|
|
3652
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3699
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3700
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3701
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3702
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_player14.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3703
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3653
3704
|
playerTrackObject.loadError,
|
|
3654
|
-
/* @__PURE__ */ (0,
|
|
3705
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3655
3706
|
void retryPlayback();
|
|
3656
3707
|
}, children: "Retry" })
|
|
3657
3708
|
] })
|
|
3658
3709
|
] }),
|
|
3659
|
-
/* @__PURE__ */ (0,
|
|
3710
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(PlayerControls, {})
|
|
3660
3711
|
] }),
|
|
3661
|
-
/* @__PURE__ */ (0,
|
|
3712
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3662
3713
|
TrackListSidebar,
|
|
3663
3714
|
{
|
|
3664
3715
|
layout: "inline",
|
|
@@ -3684,33 +3735,33 @@ function PlayerPanel({
|
|
|
3684
3735
|
// src/genre-tree/Rating.tsx
|
|
3685
3736
|
var import_uuid = require("uuid");
|
|
3686
3737
|
var import_ai = require("react-icons/ai");
|
|
3687
|
-
var
|
|
3688
|
-
var
|
|
3738
|
+
var import_genre_tree36 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3739
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
3689
3740
|
function Rating({ rating, handleChange }) {
|
|
3690
3741
|
const instanceId = (0, import_uuid.v4)();
|
|
3691
|
-
return /* @__PURE__ */ (0,
|
|
3692
|
-
/* @__PURE__ */ (0,
|
|
3742
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3743
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3693
3744
|
"input",
|
|
3694
3745
|
{
|
|
3695
3746
|
id: `star-${instanceId}-${i}`,
|
|
3696
3747
|
className: "w-6 h-6 opacity-0",
|
|
3697
3748
|
type: "radio",
|
|
3698
3749
|
name: "rating",
|
|
3699
|
-
value: i === 0 ?
|
|
3750
|
+
value: i === 0 ? import_genre_tree36.FORM_RATING_NULL_VALUE : i,
|
|
3700
3751
|
checked: rating === (i === 0 ? null : i),
|
|
3701
3752
|
onChange: handleChange
|
|
3702
3753
|
}
|
|
3703
3754
|
),
|
|
3704
|
-
/* @__PURE__ */ (0,
|
|
3705
|
-
/* @__PURE__ */ (0,
|
|
3755
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "flex", children: [
|
|
3756
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3706
3757
|
import_ai.AiOutlineStar,
|
|
3707
3758
|
{
|
|
3708
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3759
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree36.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3709
3760
|
}
|
|
3710
3761
|
),
|
|
3711
|
-
/* @__PURE__ */ (0,
|
|
3762
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "flex-grow w-8" }),
|
|
3712
3763
|
" "
|
|
3713
|
-
] }) : /* @__PURE__ */ (0,
|
|
3764
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
3714
3765
|
import_ai.AiFillStar,
|
|
3715
3766
|
{
|
|
3716
3767
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3722,7 +3773,7 @@ function Rating({ rating, handleChange }) {
|
|
|
3722
3773
|
// src/genre-tree/EntityDetailPanel.tsx
|
|
3723
3774
|
var import_react22 = require("react");
|
|
3724
3775
|
var import_ui23 = require("@behindthemusictree/ui");
|
|
3725
|
-
var
|
|
3776
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
3726
3777
|
function EntityDetailPanel({
|
|
3727
3778
|
scope,
|
|
3728
3779
|
getBackendBaseUrl,
|
|
@@ -3734,6 +3785,7 @@ function EntityDetailPanel({
|
|
|
3734
3785
|
onArtistUuidChange,
|
|
3735
3786
|
renderArtistLink,
|
|
3736
3787
|
renderSongLink,
|
|
3788
|
+
renderPlaceLink,
|
|
3737
3789
|
collapsed,
|
|
3738
3790
|
edgeTab,
|
|
3739
3791
|
...navigation
|
|
@@ -3745,8 +3797,9 @@ function EntityDetailPanel({
|
|
|
3745
3797
|
getBackendBaseUrl,
|
|
3746
3798
|
getArtistBackendBaseUrl,
|
|
3747
3799
|
getShareUrl,
|
|
3748
|
-
|
|
3749
|
-
|
|
3800
|
+
renderPlaceLink,
|
|
3801
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
3802
|
+
renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
|
|
3750
3803
|
song: {
|
|
3751
3804
|
uuid: songUuid,
|
|
3752
3805
|
expanded: songUuid !== null && expandedSongUuid === songUuid,
|
|
@@ -3762,7 +3815,7 @@ function EntityDetailPanel({
|
|
|
3762
3815
|
...navigation
|
|
3763
3816
|
});
|
|
3764
3817
|
if (!panel) return null;
|
|
3765
|
-
return /* @__PURE__ */ (0,
|
|
3818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
3766
3819
|
import_ui23.DetailPanel,
|
|
3767
3820
|
{
|
|
3768
3821
|
side: "left",
|
|
@@ -3774,7 +3827,7 @@ function EntityDetailPanel({
|
|
|
3774
3827
|
onExpandedChange: panel.onExpandedChange,
|
|
3775
3828
|
aside: panel.extraContent,
|
|
3776
3829
|
edgeTab,
|
|
3777
|
-
children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0,
|
|
3830
|
+
children: panel.extraContent && !panel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_jsx_runtime51.Fragment, { children: [
|
|
3778
3831
|
panel.content,
|
|
3779
3832
|
panel.extraContent
|
|
3780
3833
|
] }) : panel.content
|