@behindthemusictree/app-kit-ui 30.0.0 → 31.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/dist/{chunk-2HE2HDFA.js → chunk-AQASG5KT.js} +476 -528
- package/dist/chunk-AQASG5KT.js.map +1 -0
- package/dist/genre-tree/index.cjs +512 -570
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +35 -38
- package/dist/genre-tree/index.js +3 -7
- package/dist/index.cjs +512 -570
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -7
- package/package.json +6 -6
- package/dist/chunk-2HE2HDFA.js.map +0 -1
|
@@ -5,13 +5,17 @@ import {
|
|
|
5
5
|
} from "./chunk-DKQQLLMY.js";
|
|
6
6
|
|
|
7
7
|
// src/genre-tree/GenreTreeView.tsx
|
|
8
|
-
import { useCallback as useCallback6, useDeferredValue, useEffect as
|
|
8
|
+
import { useCallback as useCallback6, useDeferredValue, useEffect as useEffect5, useMemo as useMemo7, useRef as useRef4, useState as useState7 } from "react";
|
|
9
9
|
import { useIsMutating } from "@tanstack/react-query";
|
|
10
10
|
import { Plus as Plus2 } from "lucide-react";
|
|
11
|
-
import {
|
|
11
|
+
import { Maximize2, Minimize2 } from "lucide-react";
|
|
12
|
+
import { IconTextButton, IconButton, Button, SegmentedControl as SegmentedControl2, DetailPanel, useObscuredInset } from "@behindthemusictree/ui";
|
|
12
13
|
import {
|
|
14
|
+
GenreNodeAncestors,
|
|
15
|
+
GenreNodeFamily,
|
|
13
16
|
GenreTreeWheelSkeleton,
|
|
14
|
-
GenreTreeViewSkeleton
|
|
17
|
+
GenreTreeViewSkeleton,
|
|
18
|
+
useGenreNodeRelations
|
|
15
19
|
} from "@behindthemusictree/genre-tree-view";
|
|
16
20
|
import {
|
|
17
21
|
useArtistSearch,
|
|
@@ -21,6 +25,32 @@ import {
|
|
|
21
25
|
import { useGenreTreeViewState, useArtist as useArtist2, useUpdateGenre as useUpdateGenre2, genreUpdateMutationKey } from "@behindthemusictree/app-kit/genre-tree";
|
|
22
26
|
import { usePopup as usePopup5 } from "@behindthemusictree/app-kit/popup";
|
|
23
27
|
|
|
28
|
+
// src/genre-tree/playlist-tree/size-metric.ts
|
|
29
|
+
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
30
|
+
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
31
|
+
if (metric === "songs") return genrePlaylist.tracksCount;
|
|
32
|
+
if (genrePlaylist.viewsCount === void 0) {
|
|
33
|
+
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
34
|
+
}
|
|
35
|
+
return genrePlaylist.viewsCount;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// src/genre-tree/playlist-tree/toTreeNodes.ts
|
|
39
|
+
function toTreeNodes(genrePlaylists, sizeMetric) {
|
|
40
|
+
return genrePlaylists.map((genrePlaylist) => ({
|
|
41
|
+
id: genrePlaylist.uuid,
|
|
42
|
+
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
43
|
+
name: genrePlaylist.name,
|
|
44
|
+
itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
|
|
45
|
+
actionable: Boolean(genrePlaylist.criteria),
|
|
46
|
+
detached: !genrePlaylist.criteria,
|
|
47
|
+
side: genrePlaylist.criteria?.side ?? void 0,
|
|
48
|
+
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
49
|
+
umbrella: genrePlaylist.isUmbrella,
|
|
50
|
+
scene: genrePlaylist.scene ?? void 0
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
|
|
24
54
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
25
55
|
import { useCallback as useCallback2, useMemo as useMemo2 } from "react";
|
|
26
56
|
import {
|
|
@@ -63,7 +93,6 @@ function useGenrePlaylistPlayback({
|
|
|
63
93
|
}
|
|
64
94
|
|
|
65
95
|
// src/genre-tree/playlist-tree/TreePerRoot.tsx
|
|
66
|
-
import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
|
|
67
96
|
import { jsx } from "react/jsx-runtime";
|
|
68
97
|
function GenrePlaylistTreePerRoot({
|
|
69
98
|
scope,
|
|
@@ -75,24 +104,19 @@ function GenrePlaylistTreePerRoot({
|
|
|
75
104
|
additionalActions,
|
|
76
105
|
onNodeClick,
|
|
77
106
|
onNodeHover,
|
|
78
|
-
renderInfoPanelBody,
|
|
79
107
|
selectedNodeId,
|
|
80
108
|
onSelectedNodeChange,
|
|
81
|
-
hideInfoPanelClose,
|
|
82
|
-
hideInfoPanelTitle,
|
|
83
109
|
readOnly = false,
|
|
84
|
-
showToolbar,
|
|
85
110
|
wheelZoom,
|
|
86
111
|
defaultViewport,
|
|
87
112
|
viewportRestore,
|
|
88
113
|
onViewportChange,
|
|
89
114
|
playback,
|
|
90
|
-
...
|
|
115
|
+
...hostPanel
|
|
91
116
|
}) {
|
|
92
117
|
const { isPlaying, setIsPlaying } = usePlayer();
|
|
93
118
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists: genrePlaylistTreePerRoot });
|
|
94
119
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
95
|
-
const trackNoun = useTrackNoun();
|
|
96
120
|
const { showPopup } = usePopup();
|
|
97
121
|
const nodes = useMemo2(
|
|
98
122
|
() => genrePlaylistTreePerRoot.map((genrePlaylist) => ({
|
|
@@ -157,7 +181,6 @@ function GenrePlaylistTreePerRoot({
|
|
|
157
181
|
{
|
|
158
182
|
className: className2,
|
|
159
183
|
nodes,
|
|
160
|
-
itemCountLabel: `${capitalize(trackNoun.singular)} count`,
|
|
161
184
|
rootColor: getGenreTreeColor(rootUuid),
|
|
162
185
|
playingNodeId,
|
|
163
186
|
playState: isPlaying ? "playing" : "paused",
|
|
@@ -168,18 +191,13 @@ function GenrePlaylistTreePerRoot({
|
|
|
168
191
|
additionalActions,
|
|
169
192
|
onNodeClick,
|
|
170
193
|
onNodeHover,
|
|
171
|
-
renderInfoPanelBody,
|
|
172
194
|
selectedNodeId,
|
|
173
195
|
onSelectedNodeChange,
|
|
174
|
-
hideInfoPanelClose,
|
|
175
|
-
hideInfoPanelTitle,
|
|
176
|
-
showToolbar,
|
|
177
196
|
wheelZoom,
|
|
178
197
|
defaultViewport,
|
|
179
198
|
viewportRestore,
|
|
180
199
|
onViewportChange,
|
|
181
|
-
...
|
|
182
|
-
readOnly
|
|
200
|
+
...hostPanel
|
|
183
201
|
}
|
|
184
202
|
);
|
|
185
203
|
}
|
|
@@ -192,19 +210,6 @@ import {
|
|
|
192
210
|
import { usePlayer as usePlayer2 } from "@behindthemusictree/app-kit/player";
|
|
193
211
|
import { usePopup as usePopup2 } from "@behindthemusictree/app-kit/popup";
|
|
194
212
|
import { ErrorCode as ErrorCode2 } from "@behindthemusictree/app-kit/transport";
|
|
195
|
-
|
|
196
|
-
// src/genre-tree/playlist-tree/size-metric.ts
|
|
197
|
-
var hasViewsCount = (genrePlaylists) => genrePlaylists.every((genrePlaylist) => genrePlaylist.viewsCount !== void 0);
|
|
198
|
-
var genrePlaylistSize = (genrePlaylist, metric) => {
|
|
199
|
-
if (metric === "songs") return genrePlaylist.tracksCount;
|
|
200
|
-
if (genrePlaylist.viewsCount === void 0) {
|
|
201
|
-
throw new Error(`Genre playlist ${genrePlaylist.uuid} has no viewsCount to size by views`);
|
|
202
|
-
}
|
|
203
|
-
return genrePlaylist.viewsCount;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
// src/genre-tree/playlist-tree/TreeWheel.tsx
|
|
207
|
-
import { capitalize as capitalize2, useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
|
|
208
213
|
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
209
214
|
function GenrePlaylistTreeWheel({
|
|
210
215
|
scope,
|
|
@@ -215,14 +220,10 @@ function GenrePlaylistTreeWheel({
|
|
|
215
220
|
additionalActions,
|
|
216
221
|
onNodeClick,
|
|
217
222
|
onNodeHover,
|
|
218
|
-
renderInfoPanelBody,
|
|
219
223
|
selectedNodeId,
|
|
220
224
|
onSelectedNodeChange,
|
|
221
|
-
hideInfoPanelClose,
|
|
222
|
-
hideInfoPanelTitle,
|
|
223
225
|
readOnly = false,
|
|
224
226
|
allowWheelRotation,
|
|
225
|
-
showToolbar,
|
|
226
227
|
sizeMetric = "songs",
|
|
227
228
|
floatingControlsLeading,
|
|
228
229
|
busy,
|
|
@@ -230,12 +231,11 @@ function GenrePlaylistTreeWheel({
|
|
|
230
231
|
viewportRestore,
|
|
231
232
|
onViewportChange,
|
|
232
233
|
playback,
|
|
233
|
-
...
|
|
234
|
+
...hostPanel
|
|
234
235
|
}) {
|
|
235
236
|
const { isPlaying, setIsPlaying } = usePlayer2();
|
|
236
237
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
|
|
237
238
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
238
|
-
const trackNoun = useTrackNoun2();
|
|
239
239
|
const { showPopup } = usePopup2();
|
|
240
240
|
const nodes = useMemo3(
|
|
241
241
|
() => genrePlaylists.map((genrePlaylist) => ({
|
|
@@ -299,7 +299,6 @@ function GenrePlaylistTreeWheel({
|
|
|
299
299
|
{
|
|
300
300
|
className: className2,
|
|
301
301
|
nodes,
|
|
302
|
-
itemCountLabel: sizeMetric === "views" ? "Views" : `${capitalize2(trackNoun.singular)} count`,
|
|
303
302
|
playingNodeId,
|
|
304
303
|
playState: isPlaying ? "playing" : "paused",
|
|
305
304
|
onPlayPause: handlePlayPause,
|
|
@@ -309,20 +308,15 @@ function GenrePlaylistTreeWheel({
|
|
|
309
308
|
additionalActions,
|
|
310
309
|
onNodeClick,
|
|
311
310
|
onNodeHover,
|
|
312
|
-
renderInfoPanelBody,
|
|
313
311
|
selectedNodeId,
|
|
314
312
|
onSelectedNodeChange,
|
|
315
|
-
hideInfoPanelClose,
|
|
316
|
-
hideInfoPanelTitle,
|
|
317
313
|
allowWheelRotation,
|
|
318
|
-
showToolbar,
|
|
319
314
|
floatingControlsLeading,
|
|
320
315
|
busy,
|
|
321
316
|
defaultViewport,
|
|
322
317
|
viewportRestore,
|
|
323
318
|
onViewportChange,
|
|
324
|
-
...
|
|
325
|
-
readOnly
|
|
319
|
+
...hostPanel
|
|
326
320
|
}
|
|
327
321
|
);
|
|
328
322
|
}
|
|
@@ -334,7 +328,6 @@ import {
|
|
|
334
328
|
GenreTreeWheelRadialPopCore
|
|
335
329
|
} from "@behindthemusictree/genre-tree-view";
|
|
336
330
|
import { usePlayer as usePlayer3 } from "@behindthemusictree/app-kit/player";
|
|
337
|
-
import { capitalize as capitalize3, useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
|
|
338
331
|
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
339
332
|
function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
340
333
|
const {
|
|
@@ -346,14 +339,10 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
346
339
|
additionalActions,
|
|
347
340
|
onNodeClick,
|
|
348
341
|
onNodeHover,
|
|
349
|
-
renderInfoPanelBody,
|
|
350
342
|
selectedNodeId,
|
|
351
343
|
onSelectedNodeChange,
|
|
352
|
-
hideInfoPanelClose,
|
|
353
|
-
hideInfoPanelTitle,
|
|
354
344
|
readOnly = false,
|
|
355
345
|
allowWheelRotation,
|
|
356
|
-
showToolbar,
|
|
357
346
|
sizeMetric = "songs",
|
|
358
347
|
floatingControlsLeading,
|
|
359
348
|
busy,
|
|
@@ -364,33 +353,14 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
364
353
|
playback
|
|
365
354
|
} = props;
|
|
366
355
|
const focus = props;
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
onExpandedChange: props.onExpandedChange,
|
|
371
|
-
collapsed: props.collapsed,
|
|
372
|
-
hideExpandButton: props.hideExpandButton,
|
|
373
|
-
overlayPanel: props.overlayPanel
|
|
356
|
+
const hostPanel = {
|
|
357
|
+
obscuredInset: props.obscuredInset,
|
|
358
|
+
onNodeStyleResolverChange: props.onNodeStyleResolverChange
|
|
374
359
|
};
|
|
375
360
|
const { isPlaying, setIsPlaying } = usePlayer3();
|
|
376
361
|
const defaultPlayback = useGenrePlaylistPlayback({ scope, genrePlaylists });
|
|
377
362
|
const { playingNodeId, play } = playback ?? defaultPlayback;
|
|
378
|
-
const
|
|
379
|
-
const nodes = useMemo4(
|
|
380
|
-
() => genrePlaylists.map((genrePlaylist) => ({
|
|
381
|
-
id: genrePlaylist.uuid,
|
|
382
|
-
parentId: genrePlaylist.parent?.uuid ?? null,
|
|
383
|
-
name: genrePlaylist.name,
|
|
384
|
-
itemCount: genrePlaylistSize(genrePlaylist, sizeMetric),
|
|
385
|
-
actionable: Boolean(genrePlaylist.criteria),
|
|
386
|
-
detached: !genrePlaylist.criteria,
|
|
387
|
-
side: genrePlaylist.criteria?.side ?? void 0,
|
|
388
|
-
wikidataId: genrePlaylist.criteria?.wikidataId ?? null,
|
|
389
|
-
umbrella: genrePlaylist.isUmbrella,
|
|
390
|
-
scene: genrePlaylist.scene ?? void 0
|
|
391
|
-
})),
|
|
392
|
-
[genrePlaylists, sizeMetric]
|
|
393
|
-
);
|
|
363
|
+
const nodes = useMemo4(() => toTreeNodes(genrePlaylists, sizeMetric), [genrePlaylists, sizeMetric]);
|
|
394
364
|
const handlePlayPause = useCallback4(
|
|
395
365
|
(nodeId) => {
|
|
396
366
|
const genrePlaylist = genrePlaylists.find((g) => g.uuid === nodeId);
|
|
@@ -431,7 +401,6 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
431
401
|
const treeProps = {
|
|
432
402
|
className: className2,
|
|
433
403
|
nodes,
|
|
434
|
-
itemCountLabel: sizeMetric === "views" ? "Views" : `${capitalize3(trackNoun.singular)} count`,
|
|
435
404
|
playingNodeId,
|
|
436
405
|
playState: isPlaying ? "playing" : "paused",
|
|
437
406
|
onPlayPause: handlePlayPause,
|
|
@@ -441,14 +410,9 @@ function GenrePlaylistTreeWheelRadialPopCore(props) {
|
|
|
441
410
|
additionalActions,
|
|
442
411
|
onNodeClick,
|
|
443
412
|
onNodeHover,
|
|
444
|
-
renderInfoPanelBody,
|
|
445
413
|
selectedNodeId,
|
|
446
414
|
onSelectedNodeChange,
|
|
447
|
-
|
|
448
|
-
hideInfoPanelTitle,
|
|
449
|
-
showToolbar,
|
|
450
|
-
...infoPanelCuration,
|
|
451
|
-
readOnly
|
|
415
|
+
...hostPanel
|
|
452
416
|
};
|
|
453
417
|
return outline ? /* @__PURE__ */ jsx3(GenreTreeOutline, { ...treeProps }) : /* @__PURE__ */ jsx3(
|
|
454
418
|
GenreTreeWheelRadialPopCore,
|
|
@@ -568,7 +532,7 @@ function GenreSearch({
|
|
|
568
532
|
type: "button",
|
|
569
533
|
"aria-label": "Previous",
|
|
570
534
|
title: "Previous",
|
|
571
|
-
className: "genre-search-previous absolute left-2 top-1/2 z-10
|
|
535
|
+
className: "genre-search-previous absolute left-2 top-1/2 z-10 flex items-center justify-center p-1.5 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full -translate-y-1/2 hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
572
536
|
onClick: onPrevious,
|
|
573
537
|
children: /* @__PURE__ */ jsx5(ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
574
538
|
}
|
|
@@ -590,7 +554,7 @@ function GenreSearch({
|
|
|
590
554
|
onPick: handlePick,
|
|
591
555
|
status,
|
|
592
556
|
loading: loading ? "Searching\u2026" : void 0,
|
|
593
|
-
inputClassName:
|
|
557
|
+
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md`,
|
|
594
558
|
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
595
559
|
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
596
560
|
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
@@ -1027,7 +991,7 @@ function MusicBrainzRecordingLink({
|
|
|
1027
991
|
|
|
1028
992
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1029
993
|
import { ArtistNames } from "@behindthemusictree/app-kit/genre-tree";
|
|
1030
|
-
import { capitalize
|
|
994
|
+
import { capitalize, useTrackNoun } from "@behindthemusictree/app-kit/track-noun";
|
|
1031
995
|
import { jsx as jsx13, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1032
996
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1033
997
|
var SongPageSchema = PaginatedResponseSchema(SongDetailedSchema).extend({
|
|
@@ -1061,7 +1025,7 @@ function GenrePlaylistTracks({
|
|
|
1061
1025
|
const { session, sessionRestored } = useSession();
|
|
1062
1026
|
const { playerTrackObject, playState, handlePlayPauseAction } = usePlayer4();
|
|
1063
1027
|
const { showPopup } = usePopup4();
|
|
1064
|
-
const trackNoun =
|
|
1028
|
+
const trackNoun = useTrackNoun();
|
|
1065
1029
|
const { uuid } = genrePlaylist;
|
|
1066
1030
|
const { fetch } = useFetchWrapper(getBackendBaseUrl);
|
|
1067
1031
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({
|
|
@@ -1106,7 +1070,7 @@ function GenrePlaylistTracks({
|
|
|
1106
1070
|
};
|
|
1107
1071
|
return /* @__PURE__ */ jsxs9("div", { className: "gtv-info-panel-children", children: [
|
|
1108
1072
|
/* @__PURE__ */ jsxs9("span", { className: "gtv-info-panel-children-title", children: [
|
|
1109
|
-
|
|
1073
|
+
capitalize(trackNoun.plural),
|
|
1110
1074
|
total !== void 0 && ` (${total})`
|
|
1111
1075
|
] }),
|
|
1112
1076
|
renderTracksToolbar?.(),
|
|
@@ -1177,7 +1141,7 @@ function GenrePlaylistTracks({
|
|
|
1177
1141
|
}
|
|
1178
1142
|
|
|
1179
1143
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1180
|
-
import { useTrackNoun as
|
|
1144
|
+
import { useTrackNoun as useTrackNoun2 } from "@behindthemusictree/app-kit/track-noun";
|
|
1181
1145
|
import { SiWikidata, SiWikipedia } from "react-icons/si";
|
|
1182
1146
|
import { Fragment as Fragment6, jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1183
1147
|
function GenreDetailExtras({
|
|
@@ -1195,7 +1159,7 @@ function GenreDetailExtras({
|
|
|
1195
1159
|
family,
|
|
1196
1160
|
...trackSlots
|
|
1197
1161
|
}) {
|
|
1198
|
-
const trackNoun =
|
|
1162
|
+
const trackNoun = useTrackNoun2();
|
|
1199
1163
|
const { data: overview, isPending } = useFetchGenreOverview(
|
|
1200
1164
|
genreUuid,
|
|
1201
1165
|
scope,
|
|
@@ -1329,69 +1293,13 @@ function GenreInfoPanelBody({
|
|
|
1329
1293
|
] });
|
|
1330
1294
|
}
|
|
1331
1295
|
|
|
1332
|
-
// src/genre-tree/PanelEdgeTab.tsx
|
|
1333
|
-
import { ChevronLeft, ChevronRight } from "lucide-react";
|
|
1334
|
-
import { twMerge } from "tailwind-merge";
|
|
1335
|
-
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1336
|
-
var buttonClassName = "flex h-12 w-full items-center justify-center p-0 text-[var(--color-text-muted)] hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)] focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-[var(--color-primary)]";
|
|
1337
|
-
function PanelEdgeTab({ side, edge, center, controls, className: className2 }) {
|
|
1338
|
-
if (!edge && !center) return null;
|
|
1339
|
-
const isLeft = side === "left";
|
|
1340
|
-
const EdgeIcon = isLeft ? ChevronLeft : ChevronRight;
|
|
1341
|
-
const CenterIcon = isLeft ? ChevronRight : ChevronLeft;
|
|
1342
|
-
const renderButton = (action, icon) => /* @__PURE__ */ jsx16(
|
|
1343
|
-
"button",
|
|
1344
|
-
{
|
|
1345
|
-
type: "button",
|
|
1346
|
-
"aria-label": action.label,
|
|
1347
|
-
title: action.label,
|
|
1348
|
-
"aria-controls": controls,
|
|
1349
|
-
"aria-expanded": action.expanded,
|
|
1350
|
-
className: buttonClassName,
|
|
1351
|
-
onClick: action.onClick,
|
|
1352
|
-
children: icon
|
|
1353
|
-
}
|
|
1354
|
-
);
|
|
1355
|
-
const edgeButton = edge && renderButton(edge, /* @__PURE__ */ jsx16(EdgeIcon, { "aria-hidden": "true", className: "h-5 w-5" }));
|
|
1356
|
-
const centerButton = center && renderButton(center, /* @__PURE__ */ jsx16(CenterIcon, { "aria-hidden": "true", className: "h-5 w-5" }));
|
|
1357
|
-
const buttons = isLeft ? [edgeButton, centerButton] : [centerButton, edgeButton];
|
|
1358
|
-
return /* @__PURE__ */ jsxs12(
|
|
1359
|
-
"div",
|
|
1360
|
-
{
|
|
1361
|
-
className: twMerge(
|
|
1362
|
-
"panel-edge-tab flex w-[var(--layout-edge-tab-w)] flex-col overflow-hidden bg-[var(--color-surface)] shadow-[0_1px_2px_rgba(60,64,67,0.3),0_2px_6px_2px_rgba(60,64,67,0.15)] [clip-path:inset(-0.5rem_-0.5rem_-0.5rem_0)]",
|
|
1363
|
-
isLeft ? "rounded-r-lg" : "rounded-l-lg [clip-path:inset(-0.5rem_0_-0.5rem_-0.5rem)]",
|
|
1364
|
-
className2
|
|
1365
|
-
),
|
|
1366
|
-
children: [
|
|
1367
|
-
buttons[0],
|
|
1368
|
-
buttons[0] && buttons[1] && /* @__PURE__ */ jsx16("div", { role: "presentation", className: "h-px shrink-0 bg-[var(--color-border)]" }),
|
|
1369
|
-
buttons[1]
|
|
1370
|
-
]
|
|
1371
|
-
}
|
|
1372
|
-
);
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
// src/genre-tree/useLeftDetailPanelLayout.ts
|
|
1376
|
-
import { useEffect as useEffect4 } from "react";
|
|
1377
|
-
function useLeftDetailPanelLayout(isPanelShown, enabled = true) {
|
|
1378
|
-
useEffect4(() => {
|
|
1379
|
-
if (!enabled || isPanelShown) return void 0;
|
|
1380
|
-
const root = document.documentElement;
|
|
1381
|
-
root.style.setProperty("--layout-left-w", "0px");
|
|
1382
|
-
return () => {
|
|
1383
|
-
root.style.removeProperty("--layout-left-w");
|
|
1384
|
-
};
|
|
1385
|
-
}, [enabled, isPanelShown]);
|
|
1386
|
-
}
|
|
1387
|
-
|
|
1388
1296
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1389
|
-
import { useEffect as
|
|
1297
|
+
import { useEffect as useEffect4, useMemo as useMemo6 } from "react";
|
|
1390
1298
|
import {
|
|
1391
1299
|
getReparentCandidates,
|
|
1392
1300
|
useUpdateGenre
|
|
1393
1301
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1394
|
-
import { jsx as
|
|
1302
|
+
import { jsx as jsx16, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1395
1303
|
function ChangeParentPopup({
|
|
1396
1304
|
genrePlaylist,
|
|
1397
1305
|
genrePlaylists,
|
|
@@ -1419,19 +1327,19 @@ function ChangeParentPopup({
|
|
|
1419
1327
|
}
|
|
1420
1328
|
);
|
|
1421
1329
|
};
|
|
1422
|
-
|
|
1330
|
+
useEffect4(() => {
|
|
1423
1331
|
const handleKeyDown = (event) => {
|
|
1424
1332
|
if (event.key === "Escape") onClose();
|
|
1425
1333
|
};
|
|
1426
1334
|
window.addEventListener("keydown", handleKeyDown);
|
|
1427
1335
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1428
1336
|
}, [onClose]);
|
|
1429
|
-
return /* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1337
|
+
return /* @__PURE__ */ jsxs12(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1338
|
+
/* @__PURE__ */ jsxs12("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1431
1339
|
"Current parent: ",
|
|
1432
1340
|
genrePlaylist.parent?.name ?? "none"
|
|
1433
1341
|
] }),
|
|
1434
|
-
/* @__PURE__ */
|
|
1342
|
+
/* @__PURE__ */ jsx16("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ jsx16(
|
|
1435
1343
|
GenreSearch,
|
|
1436
1344
|
{
|
|
1437
1345
|
genrePlaylists: candidates,
|
|
@@ -1439,7 +1347,7 @@ function ChangeParentPopup({
|
|
|
1439
1347
|
placeholder: "Search a new parent\u2026"
|
|
1440
1348
|
}
|
|
1441
1349
|
) }),
|
|
1442
|
-
formErrors.length > 0 && /* @__PURE__ */
|
|
1350
|
+
formErrors.length > 0 && /* @__PURE__ */ jsx16("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1443
1351
|
] });
|
|
1444
1352
|
}
|
|
1445
1353
|
|
|
@@ -1463,7 +1371,7 @@ import {
|
|
|
1463
1371
|
SiYoutube
|
|
1464
1372
|
} from "react-icons/si";
|
|
1465
1373
|
import { Chip } from "@behindthemusictree/ui";
|
|
1466
|
-
import { jsx as
|
|
1374
|
+
import { jsx as jsx17 } from "react/jsx-runtime";
|
|
1467
1375
|
var LINK_KINDS = {
|
|
1468
1376
|
official: { label: "Official website", Icon: Globe },
|
|
1469
1377
|
wikipedia: { label: "Wikipedia", Icon: SiWikipedia2 },
|
|
@@ -1500,7 +1408,7 @@ function artistLinks(artist) {
|
|
|
1500
1408
|
return links;
|
|
1501
1409
|
}
|
|
1502
1410
|
function ArtistLinks({ links }) {
|
|
1503
|
-
return /* @__PURE__ */
|
|
1411
|
+
return /* @__PURE__ */ jsx17("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ jsx17("li", { children: /* @__PURE__ */ jsx17(Chip, { href: url, icon: /* @__PURE__ */ jsx17(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1504
1412
|
}
|
|
1505
1413
|
|
|
1506
1414
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1511,7 +1419,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1511
1419
|
}
|
|
1512
1420
|
|
|
1513
1421
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1514
|
-
import { jsx as
|
|
1422
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1515
1423
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1516
1424
|
function ArtistHeader({ artist }) {
|
|
1517
1425
|
const fields = [];
|
|
@@ -1521,35 +1429,35 @@ function ArtistHeader({ artist }) {
|
|
|
1521
1429
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1522
1430
|
if (years) fields.push({ label: "Active", value: years });
|
|
1523
1431
|
const links = artistLinks(artist);
|
|
1524
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */
|
|
1432
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ jsx18(ArtistLinks, { links }) });
|
|
1525
1433
|
if (fields.length === 0) return null;
|
|
1526
|
-
return /* @__PURE__ */
|
|
1434
|
+
return /* @__PURE__ */ jsx18(FieldList, { items: fields });
|
|
1527
1435
|
}
|
|
1528
1436
|
|
|
1529
1437
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1530
|
-
import { jsx as
|
|
1438
|
+
import { jsx as jsx19, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1531
1439
|
function ExternalLink({ href, children }) {
|
|
1532
|
-
return /* @__PURE__ */
|
|
1440
|
+
return /* @__PURE__ */ jsx19("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1533
1441
|
}
|
|
1534
1442
|
function ArtistPhoto({ image, name }) {
|
|
1535
|
-
return /* @__PURE__ */
|
|
1536
|
-
/* @__PURE__ */
|
|
1537
|
-
/* @__PURE__ */
|
|
1443
|
+
return /* @__PURE__ */ jsxs13("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
1444
|
+
/* @__PURE__ */ jsx19("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
1445
|
+
/* @__PURE__ */ jsxs13("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1538
1446
|
"Photo: ",
|
|
1539
|
-
/* @__PURE__ */
|
|
1447
|
+
/* @__PURE__ */ jsx19(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1540
1448
|
",",
|
|
1541
1449
|
" ",
|
|
1542
|
-
image.licenseUrl ? /* @__PURE__ */
|
|
1450
|
+
image.licenseUrl ? /* @__PURE__ */ jsx19(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1543
1451
|
] })
|
|
1544
1452
|
] });
|
|
1545
1453
|
}
|
|
1546
1454
|
|
|
1547
1455
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1548
1456
|
import { PanelGroup, PanelSection } from "@behindthemusictree/ui";
|
|
1549
|
-
import { jsx as
|
|
1457
|
+
import { jsx as jsx20, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1550
1458
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1551
1459
|
if (memberships.length === 0) return null;
|
|
1552
|
-
return /* @__PURE__ */
|
|
1460
|
+
return /* @__PURE__ */ jsx20(PanelGroup, { title, children: /* @__PURE__ */ jsx20("ul", { children: memberships.map((membership, index) => {
|
|
1553
1461
|
const details = [
|
|
1554
1462
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1555
1463
|
...membership.roles
|
|
@@ -1557,9 +1465,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1557
1465
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1558
1466
|
return (
|
|
1559
1467
|
// A member can have several periods in the same group.
|
|
1560
|
-
/* @__PURE__ */
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
detailsText && /* @__PURE__ */
|
|
1468
|
+
/* @__PURE__ */ jsxs14("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
1469
|
+
/* @__PURE__ */ jsx20("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
1470
|
+
detailsText && /* @__PURE__ */ jsxs14("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1563
1471
|
" \u2014 ",
|
|
1564
1472
|
detailsText
|
|
1565
1473
|
] })
|
|
@@ -1570,9 +1478,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1570
1478
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
1571
1479
|
if (memberships.length === 0) return null;
|
|
1572
1480
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
1573
|
-
return /* @__PURE__ */
|
|
1574
|
-
/* @__PURE__ */
|
|
1575
|
-
/* @__PURE__ */
|
|
1481
|
+
return /* @__PURE__ */ jsxs14(PanelSection, { title, children: [
|
|
1482
|
+
/* @__PURE__ */ jsx20(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
1483
|
+
/* @__PURE__ */ jsx20(
|
|
1576
1484
|
MembershipList,
|
|
1577
1485
|
{
|
|
1578
1486
|
title: "Past",
|
|
@@ -1585,7 +1493,7 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
1585
1493
|
|
|
1586
1494
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
1587
1495
|
import { PanelGroup as PanelGroup2, PanelSection as PanelSection2 } from "@behindthemusictree/ui";
|
|
1588
|
-
import { jsx as
|
|
1496
|
+
import { jsx as jsx21, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1589
1497
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
1590
1498
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
1591
1499
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -1605,12 +1513,12 @@ function byYear(a, b) {
|
|
|
1605
1513
|
function ArtistDiscography({ albums }) {
|
|
1606
1514
|
if (albums.length === 0) return null;
|
|
1607
1515
|
const sorted = [...albums].sort(byYear);
|
|
1608
|
-
return /* @__PURE__ */
|
|
1516
|
+
return /* @__PURE__ */ jsx21(PanelSection2, { title: "Discography", children: GROUPS.map((group) => {
|
|
1609
1517
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
1610
1518
|
if (groupAlbums.length === 0) return null;
|
|
1611
|
-
return /* @__PURE__ */
|
|
1612
|
-
/* @__PURE__ */
|
|
1613
|
-
/* @__PURE__ */
|
|
1519
|
+
return /* @__PURE__ */ jsx21(PanelGroup2, { title: group, children: /* @__PURE__ */ jsx21("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ jsxs15("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
1520
|
+
/* @__PURE__ */ jsx21("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
1521
|
+
/* @__PURE__ */ jsx21("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
1614
1522
|
] }, album.uuid)) }) }, group);
|
|
1615
1523
|
}) });
|
|
1616
1524
|
}
|
|
@@ -1637,7 +1545,7 @@ import { useTrackList as useTrackList3 } from "@behindthemusictree/app-kit/genre
|
|
|
1637
1545
|
import { FaPlay as FaPlay2, FaPause as FaPause2 } from "react-icons/fa";
|
|
1638
1546
|
import { usePlayer as usePlayer6 } from "@behindthemusictree/app-kit/player";
|
|
1639
1547
|
import { PlayStates as PlayStates2 } from "@behindthemusictree/app-kit/player";
|
|
1640
|
-
import { jsx as
|
|
1548
|
+
import { jsx as jsx22, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1641
1549
|
function TrackPositionPlayPause({
|
|
1642
1550
|
position,
|
|
1643
1551
|
uuid,
|
|
@@ -1651,14 +1559,14 @@ function TrackPositionPlayPause({
|
|
|
1651
1559
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PLAYING;
|
|
1652
1560
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === PlayStates2.PAUSED;
|
|
1653
1561
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
1654
|
-
return /* @__PURE__ */
|
|
1562
|
+
return /* @__PURE__ */ jsxs16(
|
|
1655
1563
|
"div",
|
|
1656
1564
|
{
|
|
1657
1565
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
1658
1566
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
1659
1567
|
onClick: handlePlayPauseClick,
|
|
1660
1568
|
children: [
|
|
1661
|
-
/* @__PURE__ */
|
|
1569
|
+
/* @__PURE__ */ jsx22("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ jsx22("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ jsxs16(
|
|
1662
1570
|
"span",
|
|
1663
1571
|
{
|
|
1664
1572
|
className: "track-position-play-pause__bars",
|
|
@@ -1666,18 +1574,18 @@ function TrackPositionPlayPause({
|
|
|
1666
1574
|
role: "img",
|
|
1667
1575
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
1668
1576
|
children: [
|
|
1669
|
-
/* @__PURE__ */
|
|
1670
|
-
/* @__PURE__ */
|
|
1671
|
-
/* @__PURE__ */
|
|
1577
|
+
/* @__PURE__ */ jsx22("span", { className: "track-position-play-pause__bar" }),
|
|
1578
|
+
/* @__PURE__ */ jsx22("span", { className: "track-position-play-pause__bar" }),
|
|
1579
|
+
/* @__PURE__ */ jsx22("span", { className: "track-position-play-pause__bar" })
|
|
1672
1580
|
]
|
|
1673
1581
|
}
|
|
1674
1582
|
) : position }),
|
|
1675
|
-
/* @__PURE__ */
|
|
1583
|
+
/* @__PURE__ */ jsx22(
|
|
1676
1584
|
"div",
|
|
1677
1585
|
{
|
|
1678
1586
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
1679
1587
|
"aria-hidden": "true",
|
|
1680
|
-
children: isAwaitingPlayback ? /* @__PURE__ */
|
|
1588
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ jsx22("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ jsx22(FaPause2, {}) : /* @__PURE__ */ jsx22(FaPlay2, {})
|
|
1681
1589
|
}
|
|
1682
1590
|
)
|
|
1683
1591
|
]
|
|
@@ -1688,7 +1596,7 @@ function TrackPositionPlayPause({
|
|
|
1688
1596
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
1689
1597
|
import { isSong as isSong2, songUnplayableReason as songUnplayableReason2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1690
1598
|
import { ArtistNames as ArtistNames2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
1691
|
-
import { jsx as
|
|
1599
|
+
import { jsx as jsx23, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1692
1600
|
function TrackItem({
|
|
1693
1601
|
track,
|
|
1694
1602
|
position,
|
|
@@ -1712,12 +1620,12 @@ function TrackItem({
|
|
|
1712
1620
|
loadTrackForPlayer(track.uuid);
|
|
1713
1621
|
}
|
|
1714
1622
|
};
|
|
1715
|
-
return /* @__PURE__ */
|
|
1623
|
+
return /* @__PURE__ */ jsxs17(
|
|
1716
1624
|
"div",
|
|
1717
1625
|
{
|
|
1718
1626
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
1719
1627
|
children: [
|
|
1720
|
-
isUnplayable ? /* @__PURE__ */
|
|
1628
|
+
isUnplayable ? /* @__PURE__ */ jsx23(
|
|
1721
1629
|
"div",
|
|
1722
1630
|
{
|
|
1723
1631
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -1725,7 +1633,7 @@ function TrackItem({
|
|
|
1725
1633
|
"aria-disabled": "true",
|
|
1726
1634
|
children: position
|
|
1727
1635
|
}
|
|
1728
|
-
) : /* @__PURE__ */
|
|
1636
|
+
) : /* @__PURE__ */ jsx23(
|
|
1729
1637
|
TrackPositionPlayPause,
|
|
1730
1638
|
{
|
|
1731
1639
|
position,
|
|
@@ -1734,12 +1642,12 @@ function TrackItem({
|
|
|
1734
1642
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
1735
1643
|
}
|
|
1736
1644
|
),
|
|
1737
|
-
/* @__PURE__ */
|
|
1738
|
-
/* @__PURE__ */
|
|
1739
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */
|
|
1645
|
+
/* @__PURE__ */ jsxs17("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
1646
|
+
/* @__PURE__ */ jsx23("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ jsx23("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : isSong2(track) ? /* @__PURE__ */ jsx23(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
1647
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ jsx23("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx23(ArtistNames2, { artists: track.artists, renderArtistLink }) }) : ""
|
|
1740
1648
|
] }),
|
|
1741
|
-
/* @__PURE__ */
|
|
1742
|
-
renderDuration && /* @__PURE__ */
|
|
1649
|
+
/* @__PURE__ */ jsx23("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ jsx23("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
1650
|
+
renderDuration && /* @__PURE__ */ jsx23(
|
|
1743
1651
|
"div",
|
|
1744
1652
|
{
|
|
1745
1653
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -1747,14 +1655,14 @@ function TrackItem({
|
|
|
1747
1655
|
children: renderDuration(track)
|
|
1748
1656
|
}
|
|
1749
1657
|
),
|
|
1750
|
-
renderActions && /* @__PURE__ */
|
|
1658
|
+
renderActions && /* @__PURE__ */ jsx23("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
1751
1659
|
]
|
|
1752
1660
|
}
|
|
1753
1661
|
);
|
|
1754
1662
|
}
|
|
1755
1663
|
|
|
1756
1664
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
1757
|
-
import { Fragment as Fragment8, jsx as
|
|
1665
|
+
import { Fragment as Fragment8, jsx as jsx24, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1758
1666
|
var SongPageSchema2 = PaginatedResponseSchema2(SongDetailedSchema2);
|
|
1759
1667
|
var SORTS = [
|
|
1760
1668
|
{ ordering: "title", label: "Title" },
|
|
@@ -1787,21 +1695,21 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1787
1695
|
event.stopPropagation();
|
|
1788
1696
|
void play(song.uuid);
|
|
1789
1697
|
};
|
|
1790
|
-
return /* @__PURE__ */
|
|
1791
|
-
/* @__PURE__ */
|
|
1792
|
-
/* @__PURE__ */
|
|
1698
|
+
return /* @__PURE__ */ jsx24(PanelSection3, { title: "Songs", children: isPending ? /* @__PURE__ */ jsx24(Skeleton3, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ jsx24("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ jsx24("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ jsxs18(Fragment8, { children: [
|
|
1699
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
1700
|
+
/* @__PURE__ */ jsxs18(
|
|
1793
1701
|
"button",
|
|
1794
1702
|
{
|
|
1795
1703
|
type: "button",
|
|
1796
1704
|
onClick: () => void play(),
|
|
1797
1705
|
className: "flex w-fit items-center gap-1.5 px-3 py-1 text-xs font-medium bg-gray-900 text-white rounded-full hover:bg-gray-700",
|
|
1798
1706
|
children: [
|
|
1799
|
-
/* @__PURE__ */
|
|
1707
|
+
/* @__PURE__ */ jsx24(Play2, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
1800
1708
|
"Play all"
|
|
1801
1709
|
]
|
|
1802
1710
|
}
|
|
1803
1711
|
),
|
|
1804
|
-
/* @__PURE__ */
|
|
1712
|
+
/* @__PURE__ */ jsx24("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ jsx24(
|
|
1805
1713
|
"button",
|
|
1806
1714
|
{
|
|
1807
1715
|
type: "button",
|
|
@@ -1816,7 +1724,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1816
1724
|
sort.ordering
|
|
1817
1725
|
)) })
|
|
1818
1726
|
] }),
|
|
1819
|
-
/* @__PURE__ */
|
|
1727
|
+
/* @__PURE__ */ jsx24("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ jsx24("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ jsx24(
|
|
1820
1728
|
TrackItem,
|
|
1821
1729
|
{
|
|
1822
1730
|
track: song,
|
|
@@ -1825,12 +1733,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
1825
1733
|
renderSongLink
|
|
1826
1734
|
}
|
|
1827
1735
|
) }, song.uuid)) }),
|
|
1828
|
-
/* @__PURE__ */
|
|
1736
|
+
/* @__PURE__ */ jsx24(Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
1829
1737
|
] }) });
|
|
1830
1738
|
}
|
|
1831
1739
|
|
|
1832
1740
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1833
|
-
import { Fragment as Fragment9, jsx as
|
|
1741
|
+
import { Fragment as Fragment9, jsx as jsx25, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1834
1742
|
function useArtistPanel({
|
|
1835
1743
|
uuid,
|
|
1836
1744
|
scope,
|
|
@@ -1851,26 +1759,26 @@ function useArtistPanel({
|
|
|
1851
1759
|
return {
|
|
1852
1760
|
...panel,
|
|
1853
1761
|
title: "Artist",
|
|
1854
|
-
content: /* @__PURE__ */
|
|
1855
|
-
/* @__PURE__ */
|
|
1856
|
-
/* @__PURE__ */
|
|
1857
|
-
/* @__PURE__ */
|
|
1858
|
-
/* @__PURE__ */
|
|
1859
|
-
/* @__PURE__ */
|
|
1860
|
-
/* @__PURE__ */
|
|
1762
|
+
content: /* @__PURE__ */ jsxs19("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1763
|
+
/* @__PURE__ */ jsx25(Skeleton4, { className: "h-3 w-16" }),
|
|
1764
|
+
/* @__PURE__ */ jsx25(Skeleton4, { className: "h-4 w-32" }),
|
|
1765
|
+
/* @__PURE__ */ jsx25(Skeleton4, { className: "h-3 w-16" }),
|
|
1766
|
+
/* @__PURE__ */ jsx25(Skeleton4, { className: "h-4 w-24" }),
|
|
1767
|
+
/* @__PURE__ */ jsx25(Skeleton4, { className: "h-16 mt-2.5" }),
|
|
1768
|
+
/* @__PURE__ */ jsx25(Skeleton4, { className: "h-16" })
|
|
1861
1769
|
] })
|
|
1862
1770
|
};
|
|
1863
1771
|
case "not-found":
|
|
1864
1772
|
return {
|
|
1865
1773
|
...panel,
|
|
1866
1774
|
title: "Artist not found",
|
|
1867
|
-
content: /* @__PURE__ */
|
|
1775
|
+
content: /* @__PURE__ */ jsx25("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
1868
1776
|
};
|
|
1869
1777
|
case "error":
|
|
1870
1778
|
return {
|
|
1871
1779
|
...panel,
|
|
1872
1780
|
title: "Artist",
|
|
1873
|
-
content: /* @__PURE__ */
|
|
1781
|
+
content: /* @__PURE__ */ jsx25("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
1874
1782
|
};
|
|
1875
1783
|
case "success": {
|
|
1876
1784
|
const { artist } = result;
|
|
@@ -1879,13 +1787,13 @@ function useArtistPanel({
|
|
|
1879
1787
|
return {
|
|
1880
1788
|
...panel,
|
|
1881
1789
|
title: artist.name,
|
|
1882
|
-
content: /* @__PURE__ */
|
|
1883
|
-
shareUrl && /* @__PURE__ */
|
|
1884
|
-
artist.image && /* @__PURE__ */
|
|
1885
|
-
/* @__PURE__ */
|
|
1886
|
-
/* @__PURE__ */
|
|
1887
|
-
/* @__PURE__ */
|
|
1888
|
-
artist.tracksCount > 0 && /* @__PURE__ */
|
|
1790
|
+
content: /* @__PURE__ */ jsxs19(Fragment9, { children: [
|
|
1791
|
+
shareUrl && /* @__PURE__ */ jsx25("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsx25("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ jsx25(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
1792
|
+
artist.image && /* @__PURE__ */ jsx25(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
1793
|
+
/* @__PURE__ */ jsx25(ArtistHeader, { artist }),
|
|
1794
|
+
/* @__PURE__ */ jsx25(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
1795
|
+
/* @__PURE__ */ jsx25(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
1796
|
+
artist.tracksCount > 0 && /* @__PURE__ */ jsx25(
|
|
1889
1797
|
ArtistSongs,
|
|
1890
1798
|
{
|
|
1891
1799
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -1895,7 +1803,7 @@ function useArtistPanel({
|
|
|
1895
1803
|
}
|
|
1896
1804
|
)
|
|
1897
1805
|
] }),
|
|
1898
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */
|
|
1806
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ jsx25(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
1899
1807
|
};
|
|
1900
1808
|
}
|
|
1901
1809
|
}
|
|
@@ -1919,7 +1827,7 @@ import {
|
|
|
1919
1827
|
useSongHistory,
|
|
1920
1828
|
useTrackList as useTrackList5
|
|
1921
1829
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
1922
|
-
import { Fragment as Fragment10, jsx as
|
|
1830
|
+
import { Fragment as Fragment10, jsx as jsx26, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
1923
1831
|
var numberFormat = new Intl.NumberFormat("en");
|
|
1924
1832
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
1925
1833
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
@@ -1927,32 +1835,32 @@ function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
|
1927
1835
|
const [open, setOpen] = useState6(false);
|
|
1928
1836
|
const history = useSongHistory(uuid, getBackendBaseUrl, open);
|
|
1929
1837
|
let body = null;
|
|
1930
|
-
if (history.status === "loading") body = /* @__PURE__ */
|
|
1931
|
-
else if (history.status === "error") body = /* @__PURE__ */
|
|
1838
|
+
if (history.status === "loading") body = /* @__PURE__ */ jsx26(Skeleton5, { className: "h-10", "aria-busy": "true" });
|
|
1839
|
+
else if (history.status === "error") body = /* @__PURE__ */ jsx26("p", { className: "text-red-600", children: "Could not load the history." });
|
|
1932
1840
|
else if (history.status === "success")
|
|
1933
|
-
body = history.entries.length === 0 ? /* @__PURE__ */
|
|
1934
|
-
/* @__PURE__ */
|
|
1935
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */
|
|
1841
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ jsx26("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ jsx26("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ jsxs20("li", { className: "py-1.5 text-sm", children: [
|
|
1842
|
+
/* @__PURE__ */ jsx26("span", { className: "font-medium", children: entry.action }),
|
|
1843
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ jsxs20("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1936
1844
|
" ",
|
|
1937
1845
|
"\u2014 ",
|
|
1938
1846
|
entry.oldValue ?? "none",
|
|
1939
1847
|
" \u2192 ",
|
|
1940
1848
|
entry.newValue ?? "none"
|
|
1941
1849
|
] }) : null,
|
|
1942
|
-
/* @__PURE__ */
|
|
1850
|
+
/* @__PURE__ */ jsxs20("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
1943
1851
|
entry.actorPseudo ?? "Anonymous",
|
|
1944
1852
|
" \xB7 ",
|
|
1945
1853
|
dateFormat.format(new Date(entry.createdOn))
|
|
1946
1854
|
] })
|
|
1947
1855
|
] }, entry.uuid)) });
|
|
1948
|
-
return /* @__PURE__ */
|
|
1856
|
+
return /* @__PURE__ */ jsxs20(
|
|
1949
1857
|
"details",
|
|
1950
1858
|
{
|
|
1951
1859
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
1952
1860
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
1953
1861
|
children: [
|
|
1954
|
-
/* @__PURE__ */
|
|
1955
|
-
/* @__PURE__ */
|
|
1862
|
+
/* @__PURE__ */ jsx26("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
1863
|
+
/* @__PURE__ */ jsx26("div", { className: "mt-1.5", children: body })
|
|
1956
1864
|
]
|
|
1957
1865
|
}
|
|
1958
1866
|
);
|
|
@@ -1978,13 +1886,13 @@ function SongPanelContent({
|
|
|
1978
1886
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
1979
1887
|
fields.push({
|
|
1980
1888
|
label: "Genre",
|
|
1981
|
-
value: path ? /* @__PURE__ */
|
|
1982
|
-
index > 0 && /* @__PURE__ */
|
|
1983
|
-
/* @__PURE__ */
|
|
1889
|
+
value: path ? /* @__PURE__ */ jsx26("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ jsxs20("span", { children: [
|
|
1890
|
+
index > 0 && /* @__PURE__ */ jsx26("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
1891
|
+
/* @__PURE__ */ jsx26("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
1984
1892
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
1985
1893
|
});
|
|
1986
1894
|
} else {
|
|
1987
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */
|
|
1895
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ jsx26("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
1988
1896
|
}
|
|
1989
1897
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
1990
1898
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -1994,11 +1902,11 @@ function SongPanelContent({
|
|
|
1994
1902
|
const links = [];
|
|
1995
1903
|
if (musicBrainzUrl)
|
|
1996
1904
|
links.push(
|
|
1997
|
-
/* @__PURE__ */
|
|
1905
|
+
/* @__PURE__ */ jsx26("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
1998
1906
|
);
|
|
1999
1907
|
if (song.youtubeVideo)
|
|
2000
1908
|
links.push(
|
|
2001
|
-
/* @__PURE__ */
|
|
1909
|
+
/* @__PURE__ */ jsx26(
|
|
2002
1910
|
"a",
|
|
2003
1911
|
{
|
|
2004
1912
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2013,33 +1921,33 @@ function SongPanelContent({
|
|
|
2013
1921
|
if (links.length > 0)
|
|
2014
1922
|
fields.push({
|
|
2015
1923
|
label: "Links",
|
|
2016
|
-
value: /* @__PURE__ */
|
|
1924
|
+
value: /* @__PURE__ */ jsx26("span", { className: "flex gap-3", children: links })
|
|
2017
1925
|
});
|
|
2018
|
-
return /* @__PURE__ */
|
|
2019
|
-
/* @__PURE__ */
|
|
2020
|
-
unplayableReason ? /* @__PURE__ */
|
|
1926
|
+
return /* @__PURE__ */ jsxs20(Fragment10, { children: [
|
|
1927
|
+
/* @__PURE__ */ jsx26("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ jsxs20("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1928
|
+
unplayableReason ? /* @__PURE__ */ jsx26("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ jsxs20(
|
|
2021
1929
|
"button",
|
|
2022
1930
|
{
|
|
2023
1931
|
type: "button",
|
|
2024
1932
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2025
1933
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2026
1934
|
children: [
|
|
2027
|
-
/* @__PURE__ */
|
|
1935
|
+
/* @__PURE__ */ jsx26("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ jsx26(Pause2, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ jsx26(Play3, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2028
1936
|
isPlaying ? "Pause" : "Play"
|
|
2029
1937
|
]
|
|
2030
1938
|
}
|
|
2031
1939
|
),
|
|
2032
|
-
shareUrl && /* @__PURE__ */
|
|
1940
|
+
shareUrl && /* @__PURE__ */ jsx26(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2033
1941
|
] }) }),
|
|
2034
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */
|
|
2035
|
-
/* @__PURE__ */
|
|
2036
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */
|
|
2037
|
-
/* @__PURE__ */
|
|
1942
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ jsx26("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ jsx26(ArtistNames3, { artists: song.artists, renderArtistLink }) }),
|
|
1943
|
+
/* @__PURE__ */ jsx26(FieldList2, { items: fields }),
|
|
1944
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ jsx26(PanelSection4, { title: "Essential in", children: /* @__PURE__ */ jsx26("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ jsx26("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
1945
|
+
/* @__PURE__ */ jsx26(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2038
1946
|
] });
|
|
2039
1947
|
}
|
|
2040
1948
|
|
|
2041
1949
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2042
|
-
import { jsx as
|
|
1950
|
+
import { jsx as jsx27, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2043
1951
|
function useSongPanel({
|
|
2044
1952
|
uuid,
|
|
2045
1953
|
scope,
|
|
@@ -2061,24 +1969,24 @@ function useSongPanel({
|
|
|
2061
1969
|
return {
|
|
2062
1970
|
...panel,
|
|
2063
1971
|
title: "Song",
|
|
2064
|
-
content: /* @__PURE__ */
|
|
2065
|
-
/* @__PURE__ */
|
|
2066
|
-
/* @__PURE__ */
|
|
2067
|
-
/* @__PURE__ */
|
|
2068
|
-
/* @__PURE__ */
|
|
1972
|
+
content: /* @__PURE__ */ jsxs21("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
1973
|
+
/* @__PURE__ */ jsx27(Skeleton6, { className: "h-3 w-16" }),
|
|
1974
|
+
/* @__PURE__ */ jsx27(Skeleton6, { className: "h-4 w-32" }),
|
|
1975
|
+
/* @__PURE__ */ jsx27(Skeleton6, { className: "h-3 w-16" }),
|
|
1976
|
+
/* @__PURE__ */ jsx27(Skeleton6, { className: "h-4 w-24" })
|
|
2069
1977
|
] })
|
|
2070
1978
|
};
|
|
2071
1979
|
case "not-found":
|
|
2072
1980
|
return {
|
|
2073
1981
|
...panel,
|
|
2074
1982
|
title: "Song not found",
|
|
2075
|
-
content: /* @__PURE__ */
|
|
1983
|
+
content: /* @__PURE__ */ jsx27("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2076
1984
|
};
|
|
2077
1985
|
case "error":
|
|
2078
1986
|
return {
|
|
2079
1987
|
...panel,
|
|
2080
1988
|
title: "Song",
|
|
2081
|
-
content: /* @__PURE__ */
|
|
1989
|
+
content: /* @__PURE__ */ jsx27("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2082
1990
|
};
|
|
2083
1991
|
case "success": {
|
|
2084
1992
|
const { song } = result;
|
|
@@ -2086,7 +1994,7 @@ function useSongPanel({
|
|
|
2086
1994
|
return {
|
|
2087
1995
|
...panel,
|
|
2088
1996
|
title: song.title,
|
|
2089
|
-
content: /* @__PURE__ */
|
|
1997
|
+
content: /* @__PURE__ */ jsx27(
|
|
2090
1998
|
SongPanelContent,
|
|
2091
1999
|
{
|
|
2092
2000
|
song,
|
|
@@ -2106,12 +2014,12 @@ function useSongPanel({
|
|
|
2106
2014
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2107
2015
|
import { MdMusicNote, MdVisibility } from "react-icons/md";
|
|
2108
2016
|
import { SegmentedControl } from "@behindthemusictree/ui";
|
|
2109
|
-
import { jsx as
|
|
2017
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
2110
2018
|
function SizeMetricToggle({
|
|
2111
2019
|
value,
|
|
2112
2020
|
onChange
|
|
2113
2021
|
}) {
|
|
2114
|
-
return /* @__PURE__ */
|
|
2022
|
+
return /* @__PURE__ */ jsx28(
|
|
2115
2023
|
SegmentedControl,
|
|
2116
2024
|
{
|
|
2117
2025
|
variant: "floating",
|
|
@@ -2119,26 +2027,19 @@ function SizeMetricToggle({
|
|
|
2119
2027
|
value,
|
|
2120
2028
|
onChange,
|
|
2121
2029
|
options: [
|
|
2122
|
-
{ value: "songs", label: /* @__PURE__ */
|
|
2123
|
-
{ value: "views", label: /* @__PURE__ */
|
|
2030
|
+
{ value: "songs", label: /* @__PURE__ */ jsx28(MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2031
|
+
{ value: "views", label: /* @__PURE__ */ jsx28(MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2124
2032
|
]
|
|
2125
2033
|
}
|
|
2126
2034
|
);
|
|
2127
2035
|
}
|
|
2128
2036
|
|
|
2129
2037
|
// src/genre-tree/GenreTreeView.tsx
|
|
2130
|
-
import { Fragment as Fragment11, jsx as
|
|
2131
|
-
var PREVIOUS_BUTTON_CSS = `
|
|
2132
|
-
.genre-tree-view .search-overlay .gtv-info-panel-previous { display: none; }
|
|
2133
|
-
.genre-tree-view:has(.search-overlay .gtv-info-panel-previous) .genre-search input { padding-left: 2.75rem; }
|
|
2134
|
-
.genre-tree-view:has(.search-overlay .gtv-info-panel-previous) .genre-search-previous { display: flex; }
|
|
2135
|
-
@keyframes genre-tree-view-previous-in { from { opacity: 0; transform: translate(-0.5rem, -50%); } to { opacity: 1; transform: translate(0, -50%); } }
|
|
2136
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
2137
|
-
.genre-search-previous { animation: genre-tree-view-previous-in 200ms ease-out; }
|
|
2138
|
-
}
|
|
2139
|
-
`;
|
|
2038
|
+
import { Fragment as Fragment11, jsx as jsx29, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
2140
2039
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2141
|
-
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]
|
|
2040
|
+
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2041
|
+
var noop = () => {
|
|
2042
|
+
};
|
|
2142
2043
|
function GenreTreeView({
|
|
2143
2044
|
scope,
|
|
2144
2045
|
handleGenreCreationAction,
|
|
@@ -2277,7 +2178,7 @@ function GenreTreeView({
|
|
|
2277
2178
|
const openSongRef = useRef4(openSong);
|
|
2278
2179
|
openSongRef.current = openSong;
|
|
2279
2180
|
const resolvedRenderSongLink = useCallback6(
|
|
2280
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */
|
|
2181
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ jsx29("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2281
2182
|
[renderSongLink]
|
|
2282
2183
|
);
|
|
2283
2184
|
const [expandedSongUuid, setExpandedSongUuid] = useState7(null);
|
|
@@ -2289,7 +2190,7 @@ function GenreTreeView({
|
|
|
2289
2190
|
scope,
|
|
2290
2191
|
getBackendBaseUrl: artistBaseUrl,
|
|
2291
2192
|
getShareUrl: getShareUrlRef.current,
|
|
2292
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2193
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx29("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2293
2194
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2294
2195
|
onExpandedChange: (on) => {
|
|
2295
2196
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2298,7 +2199,7 @@ function GenreTreeView({
|
|
|
2298
2199
|
renderSongLink: resolvedRenderSongLink,
|
|
2299
2200
|
onClose: () => changeArtist(null)
|
|
2300
2201
|
});
|
|
2301
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */
|
|
2202
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ jsx29("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2302
2203
|
const getGenrePath = (genre) => {
|
|
2303
2204
|
const path = [];
|
|
2304
2205
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2330,7 +2231,6 @@ function GenreTreeView({
|
|
|
2330
2231
|
const builtInSongPanel = overlayPanel || hasSelectedGenreNode ? void 0 : songPanel;
|
|
2331
2232
|
const builtInArtistPanel = overlayPanel || hasSelectedGenreNode || builtInSongPanel ? void 0 : artistPanel;
|
|
2332
2233
|
const builtInPanel = builtInSongPanel ?? builtInArtistPanel;
|
|
2333
|
-
const activeOverlayPanel = overlayPanel ?? (builtInPanel && { ...builtInPanel, hideTitle: true, hideClose: true });
|
|
2334
2234
|
const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState7(false);
|
|
2335
2235
|
const [uncontrolledExpanded, setUncontrolledExpanded] = useState7(false);
|
|
2336
2236
|
const isCollapsed = collapsed ?? uncontrolledCollapsed;
|
|
@@ -2343,38 +2243,39 @@ function GenreTreeView({
|
|
|
2343
2243
|
setUncontrolledExpanded(next);
|
|
2344
2244
|
onExpandedChange?.(next);
|
|
2345
2245
|
};
|
|
2346
|
-
|
|
2246
|
+
useEffect5(() => {
|
|
2347
2247
|
setUncontrolledCollapsed(false);
|
|
2348
2248
|
}, [selectedNodeId, artistUuid, songUuid]);
|
|
2349
|
-
const isDetailHidden = isCollapsed || !hasSelectedGenreNode && !activeOverlayPanel;
|
|
2350
|
-
useLeftDetailPanelLayout(!isDetailHidden, placement === "regions");
|
|
2351
|
-
const detailOverlay = placement === "regions" && activeOverlayPanel?.onExpandedChange ? activeOverlayPanel : void 0;
|
|
2352
|
-
const detailExpanded = detailOverlay ? !!detailOverlay.expanded : isExpanded;
|
|
2353
|
-
const onDetailExpandedChange = detailOverlay ? detailOverlay.onExpandedChange : changeExpanded;
|
|
2354
|
-
const infoPanelCuration = {
|
|
2355
|
-
renderDangerZone,
|
|
2356
|
-
expanded: placement === "regions" ? isExpanded : expanded,
|
|
2357
|
-
onExpandedChange: placement === "regions" ? changeExpanded : onExpandedChange,
|
|
2358
|
-
collapsed: placement === "regions" ? isCollapsed : void 0,
|
|
2359
|
-
hideExpandButton: placement === "regions" ? true : void 0,
|
|
2360
|
-
overlayPanel: detailOverlay ? { ...detailOverlay, collapsed: isCollapsed, hideExpandButton: true } : activeOverlayPanel
|
|
2361
|
-
};
|
|
2362
2249
|
const [isWheelRevealed, setIsWheelRevealed] = useState7(false);
|
|
2363
2250
|
const usesHandoff = viewMode === "wheel" || viewMode === "pop-core";
|
|
2364
2251
|
const isTreeReady = !isLoading && (!usesHandoff || isWheelRevealed);
|
|
2365
|
-
const
|
|
2366
|
-
const
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2252
|
+
const [resolveStyle, setResolveStyle] = useState7(null);
|
|
2253
|
+
const handleNodeStyleResolverChange = useCallback6(
|
|
2254
|
+
(resolver) => setResolveStyle(() => resolver),
|
|
2255
|
+
[]
|
|
2256
|
+
);
|
|
2257
|
+
const panelRef = useRef4(null);
|
|
2258
|
+
const obscuredInset = useObscuredInset(panelRef);
|
|
2259
|
+
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2260
|
+
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2261
|
+
const treeNodes = useMemo7(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
2262
|
+
const relations = useGenreNodeRelations(
|
|
2263
|
+
treeNodes,
|
|
2264
|
+
!overlayPanel && hasSelectedGenreNode ? selectedNodeId : null,
|
|
2265
|
+
resolveStyle
|
|
2266
|
+
);
|
|
2267
|
+
const [selectionHistory, setSelectionHistory] = useState7({
|
|
2268
|
+
current: selectedNodeId,
|
|
2269
|
+
previous: null
|
|
2270
|
+
});
|
|
2271
|
+
if (selectionHistory.current !== selectedNodeId) {
|
|
2272
|
+
setSelectionHistory({
|
|
2273
|
+
current: selectedNodeId,
|
|
2274
|
+
previous: selectionHistory.current ?? selectionHistory.previous
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2277
|
+
const previousPlaylist = selectionHistory.previous !== null && selectionHistory.previous !== selectedNodeId ? genrePlaylistByNodeId.get(selectionHistory.previous) : void 0;
|
|
2278
|
+
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2378
2279
|
const { showPopup, hidePopup } = usePopup5();
|
|
2379
2280
|
const [searchText, setSearchText] = useState7("");
|
|
2380
2281
|
const artistSearchResult = useArtistSearch(artistBaseUrl, artistSearch ? searchText : "");
|
|
@@ -2383,15 +2284,15 @@ function GenreTreeView({
|
|
|
2383
2284
|
label: "Artists",
|
|
2384
2285
|
items: artistSearchResult.artists.map((artist) => ({
|
|
2385
2286
|
key: artist.uuid,
|
|
2386
|
-
content: /* @__PURE__ */
|
|
2387
|
-
/* @__PURE__ */
|
|
2287
|
+
content: /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
2288
|
+
/* @__PURE__ */ jsxs22("span", { className: "block truncate font-medium", children: [
|
|
2388
2289
|
artist.name,
|
|
2389
|
-
artist.disambiguation && /* @__PURE__ */
|
|
2290
|
+
artist.disambiguation && /* @__PURE__ */ jsxs22("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
2390
2291
|
" \xB7 ",
|
|
2391
2292
|
artist.disambiguation
|
|
2392
2293
|
] })
|
|
2393
2294
|
] }),
|
|
2394
|
-
artist.matchedAlias && /* @__PURE__ */
|
|
2295
|
+
artist.matchedAlias && /* @__PURE__ */ jsxs22("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
2395
2296
|
"also known as ",
|
|
2396
2297
|
artist.matchedAlias
|
|
2397
2298
|
] })
|
|
@@ -2410,9 +2311,9 @@ function GenreTreeView({
|
|
|
2410
2311
|
label: "Songs",
|
|
2411
2312
|
items: songSearchResult.songs.map((song) => ({
|
|
2412
2313
|
key: song.uuid,
|
|
2413
|
-
content: /* @__PURE__ */
|
|
2414
|
-
/* @__PURE__ */
|
|
2415
|
-
/* @__PURE__ */
|
|
2314
|
+
content: /* @__PURE__ */ jsxs22("span", { className: isSongSearchResultPlayable(song) ? void 0 : "opacity-60", children: [
|
|
2315
|
+
/* @__PURE__ */ jsx29("span", { className: "block truncate font-medium", children: song.title }),
|
|
2316
|
+
/* @__PURE__ */ jsx29("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
2416
2317
|
] }),
|
|
2417
2318
|
onSelect: () => {
|
|
2418
2319
|
openSong(song.uuid);
|
|
@@ -2440,34 +2341,47 @@ function GenreTreeView({
|
|
|
2440
2341
|
};
|
|
2441
2342
|
const zoomInRef = useRef4(void 0);
|
|
2442
2343
|
zoomInRef.current = canZoomIn ? handleGenreSearchZoomIn : void 0;
|
|
2443
|
-
const
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2344
|
+
const selectGenreFromPanel = (nodeId) => {
|
|
2345
|
+
const genrePlaylist = genrePlaylistByNodeId.get(nodeId);
|
|
2346
|
+
if (genrePlaylist) handleGenreSearchSelect(genrePlaylist);
|
|
2347
|
+
};
|
|
2348
|
+
const renderGenreBody = (relation) => {
|
|
2349
|
+
const { node } = relation;
|
|
2350
|
+
const family = /* @__PURE__ */ jsx29(
|
|
2351
|
+
GenreNodeFamily,
|
|
2352
|
+
{
|
|
2353
|
+
node,
|
|
2354
|
+
fill: relation.fill,
|
|
2355
|
+
textColor: relation.textColor,
|
|
2356
|
+
parentNode: relation.parentNode,
|
|
2357
|
+
childNodes: relation.childNodes,
|
|
2358
|
+
onSelectNode: selectGenreFromPanel
|
|
2359
|
+
}
|
|
2360
|
+
);
|
|
2361
|
+
const ancestors = /* @__PURE__ */ jsx29(GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2362
|
+
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2363
|
+
const criteria = genrePlaylist?.criteria;
|
|
2364
|
+
if (!genrePlaylist || !criteria) return { primary: family, aside: ancestors };
|
|
2365
|
+
const genreUuid = criteria.uuid;
|
|
2366
|
+
const parentPlaylist = visibleGenrePlaylists.find(({ uuid }) => uuid === genrePlaylist.parent?.uuid);
|
|
2367
|
+
const canToggleSide = !readOnly && parentPlaylist !== void 0 && parentPlaylist.parent === null;
|
|
2368
|
+
const side = criteria.side ?? "core";
|
|
2369
|
+
const shareTarget = {
|
|
2370
|
+
kind: "genre",
|
|
2371
|
+
genrePlaylistUuid: genrePlaylist.uuid,
|
|
2372
|
+
name: criteria.name,
|
|
2373
|
+
scope,
|
|
2374
|
+
artistUuid
|
|
2375
|
+
};
|
|
2376
|
+
const shareUrl = getShareUrlRef.current?.(shareTarget) ?? void 0;
|
|
2377
|
+
const popSibling = canToggleSide && side === "core" ? visibleGenrePlaylists.find(
|
|
2378
|
+
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2379
|
+
) : void 0;
|
|
2380
|
+
return {
|
|
2381
|
+
primary: /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
2382
|
+
/* @__PURE__ */ jsx29(
|
|
2468
2383
|
GenreInfoPanelBody,
|
|
2469
2384
|
{
|
|
2470
|
-
toolbar,
|
|
2471
2385
|
family,
|
|
2472
2386
|
genreUuid,
|
|
2473
2387
|
genrePlaylist,
|
|
@@ -2483,7 +2397,7 @@ function GenreTreeView({
|
|
|
2483
2397
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2484
2398
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2485
2399
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2486
|
-
/* @__PURE__ */
|
|
2400
|
+
/* @__PURE__ */ jsx29(
|
|
2487
2401
|
ChangeParentPopup,
|
|
2488
2402
|
{
|
|
2489
2403
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2514,49 +2428,65 @@ function GenreTreeView({
|
|
|
2514
2428
|
}
|
|
2515
2429
|
}
|
|
2516
2430
|
),
|
|
2517
|
-
|
|
2518
|
-
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
additionalActions,
|
|
2523
|
-
readOnly,
|
|
2524
|
-
handleGenreCreationAction,
|
|
2525
|
-
handleGenreRenameAction,
|
|
2526
|
-
showPopup,
|
|
2527
|
-
hidePopup,
|
|
2528
|
-
onParentChanged,
|
|
2529
|
-
visibleGenrePlaylists,
|
|
2530
|
-
updateGenre,
|
|
2531
|
-
playback,
|
|
2532
|
-
filterArtist,
|
|
2533
|
-
scope,
|
|
2534
|
-
getBackendBaseUrl,
|
|
2535
|
-
criteriaOverviewSchema,
|
|
2536
|
-
renderGenreDetailExtras,
|
|
2537
|
-
renderArtistLink,
|
|
2538
|
-
resolvedRenderSongLink,
|
|
2539
|
-
renderSummary,
|
|
2540
|
-
renderEssentialTracks,
|
|
2541
|
-
renderSongLeading,
|
|
2542
|
-
renderSongActions,
|
|
2543
|
-
renderTracksToolbar
|
|
2544
|
-
]
|
|
2545
|
-
);
|
|
2431
|
+
renderDangerZone?.(node)
|
|
2432
|
+
] }),
|
|
2433
|
+
aside: ancestors
|
|
2434
|
+
};
|
|
2435
|
+
};
|
|
2546
2436
|
const isRotationControlled = controlledAllowWheelRotation !== void 0;
|
|
2547
2437
|
const allowWheelRotation = controlledAllowWheelRotation ?? internalAllowWheelRotation;
|
|
2548
|
-
|
|
2549
|
-
|
|
2438
|
+
let activePanel = null;
|
|
2439
|
+
if (overlayPanel) {
|
|
2440
|
+
activePanel = {
|
|
2441
|
+
title: overlayPanel.title,
|
|
2442
|
+
content: overlayPanel.content,
|
|
2443
|
+
aside: overlayPanel.extraContent,
|
|
2444
|
+
expanded: overlayPanel.expanded ?? false,
|
|
2445
|
+
onExpandedChange: overlayPanel.onExpandedChange,
|
|
2446
|
+
onClose: overlayPanel.onClose,
|
|
2447
|
+
compact: false
|
|
2448
|
+
};
|
|
2449
|
+
} else if (relations) {
|
|
2450
|
+
const { primary, aside } = renderGenreBody(relations);
|
|
2451
|
+
activePanel = {
|
|
2452
|
+
title: relations.node.name,
|
|
2453
|
+
content: primary,
|
|
2454
|
+
aside,
|
|
2455
|
+
expanded: isExpanded,
|
|
2456
|
+
onExpandedChange: changeExpanded,
|
|
2457
|
+
onClose: clearSelection,
|
|
2458
|
+
compact: true,
|
|
2459
|
+
headerAccent: { background: relations.fill, color: relations.textColor }
|
|
2460
|
+
};
|
|
2461
|
+
} else if (builtInPanel) {
|
|
2462
|
+
activePanel = {
|
|
2463
|
+
title: builtInPanel.title,
|
|
2464
|
+
content: builtInPanel.content,
|
|
2465
|
+
aside: builtInPanel.extraContent,
|
|
2466
|
+
expanded: builtInPanel.expanded,
|
|
2467
|
+
onExpandedChange: builtInPanel.onExpandedChange,
|
|
2468
|
+
onClose: builtInPanel.onClose,
|
|
2469
|
+
compact: true
|
|
2470
|
+
};
|
|
2471
|
+
}
|
|
2472
|
+
const isPanelShown = isTreeReady && activePanel !== null;
|
|
2473
|
+
const detailTab = placement === "regions" && activePanel?.onExpandedChange ? {
|
|
2474
|
+
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2475
|
+
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2476
|
+
} : void 0;
|
|
2477
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ jsx29(
|
|
2478
|
+
IconButton,
|
|
2550
2479
|
{
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2480
|
+
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2481
|
+
style: activePanel.headerAccent && { color: "inherit" },
|
|
2482
|
+
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2483
|
+
children: activePanel.expanded ? /* @__PURE__ */ jsx29(Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx29(Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2554
2484
|
}
|
|
2555
2485
|
) : null;
|
|
2556
2486
|
const renderArtistFilter = () => {
|
|
2557
2487
|
if (isLoading || !filterArtist) return null;
|
|
2558
|
-
return /* @__PURE__ */
|
|
2559
|
-
/* @__PURE__ */
|
|
2488
|
+
return /* @__PURE__ */ jsxs22("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
2489
|
+
/* @__PURE__ */ jsx29(
|
|
2560
2490
|
"button",
|
|
2561
2491
|
{
|
|
2562
2492
|
type: "button",
|
|
@@ -2568,7 +2498,7 @@ function GenreTreeView({
|
|
|
2568
2498
|
children: filterArtist.name
|
|
2569
2499
|
}
|
|
2570
2500
|
),
|
|
2571
|
-
/* @__PURE__ */
|
|
2501
|
+
/* @__PURE__ */ jsx29(
|
|
2572
2502
|
"button",
|
|
2573
2503
|
{
|
|
2574
2504
|
type: "button",
|
|
@@ -2580,22 +2510,22 @@ function GenreTreeView({
|
|
|
2580
2510
|
)
|
|
2581
2511
|
] });
|
|
2582
2512
|
};
|
|
2583
|
-
const actions = /* @__PURE__ */
|
|
2584
|
-
!isLoading && /* @__PURE__ */
|
|
2513
|
+
const actions = /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
2514
|
+
!isLoading && /* @__PURE__ */ jsx29(
|
|
2585
2515
|
GenreSearch,
|
|
2586
2516
|
{
|
|
2587
2517
|
genrePlaylists: visibleGenrePlaylists,
|
|
2588
2518
|
onSelect: handleGenreSearchSelect,
|
|
2589
|
-
selectedName: selectedGenrePlaylist?.name ?? builtInPanel?.title
|
|
2519
|
+
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
2590
2520
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
2591
2521
|
onPrevious: handlePrevious,
|
|
2592
2522
|
onQueryChange: artistSearch || songSearch ? setSearchText : void 0,
|
|
2593
2523
|
extraGroups
|
|
2594
2524
|
}
|
|
2595
2525
|
),
|
|
2596
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2597
|
-
/* @__PURE__ */
|
|
2598
|
-
!isLoading && !isControlled && /* @__PURE__ */
|
|
2526
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx29("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
2527
|
+
/* @__PURE__ */ jsxs22("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: [
|
|
2528
|
+
!isLoading && !isControlled && /* @__PURE__ */ jsx29(
|
|
2599
2529
|
SegmentedControl2,
|
|
2600
2530
|
{
|
|
2601
2531
|
"aria-label": "Tree view mode",
|
|
@@ -2609,13 +2539,13 @@ function GenreTreeView({
|
|
|
2609
2539
|
]
|
|
2610
2540
|
}
|
|
2611
2541
|
),
|
|
2612
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */
|
|
2542
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ jsx29(
|
|
2613
2543
|
"div",
|
|
2614
2544
|
{
|
|
2615
2545
|
className: "flex items-center gap-1",
|
|
2616
2546
|
role: "group",
|
|
2617
2547
|
"aria-label": "Tree display options",
|
|
2618
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */
|
|
2548
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ jsx29(
|
|
2619
2549
|
Button,
|
|
2620
2550
|
{
|
|
2621
2551
|
variant: allowWheelRotation ? "default" : "outline",
|
|
@@ -2626,7 +2556,7 @@ function GenreTreeView({
|
|
|
2626
2556
|
)
|
|
2627
2557
|
}
|
|
2628
2558
|
),
|
|
2629
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */
|
|
2559
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ jsx29(
|
|
2630
2560
|
IconTextButton,
|
|
2631
2561
|
{
|
|
2632
2562
|
icon: Plus2,
|
|
@@ -2636,42 +2566,34 @@ function GenreTreeView({
|
|
|
2636
2566
|
)
|
|
2637
2567
|
] })
|
|
2638
2568
|
] });
|
|
2639
|
-
return /* @__PURE__ */
|
|
2569
|
+
return /* @__PURE__ */ jsxs22(
|
|
2640
2570
|
"div",
|
|
2641
2571
|
{
|
|
2642
|
-
|
|
2572
|
+
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
2643
2573
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
2644
2574
|
children: [
|
|
2645
|
-
/* @__PURE__ */
|
|
2646
|
-
/* @__PURE__ */ jsx30(
|
|
2575
|
+
/* @__PURE__ */ jsx29(
|
|
2647
2576
|
"div",
|
|
2648
2577
|
{
|
|
2649
2578
|
className: `actions-container absolute z-30 flex items-center gap-2 ${placement === "regions" ? "left-[calc(var(--layout-gutter)+var(--layout-panel-inset))] top-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:right-[calc(var(--layout-gutter)+var(--layout-panel-inset))] max-md:top-[var(--layout-band-top)] max-md:flex-col max-md:items-stretch" : "left-5 top-5"}`,
|
|
2650
2579
|
children: actions
|
|
2651
2580
|
}
|
|
2652
2581
|
),
|
|
2653
|
-
!isLoading && filterArtist && /* @__PURE__ */
|
|
2582
|
+
!isLoading && filterArtist && /* @__PURE__ */ jsx29(
|
|
2654
2583
|
"div",
|
|
2655
2584
|
{
|
|
2656
2585
|
className: `absolute left-1/2 z-30 hidden -translate-x-1/2 md:block ${placement === "regions" ? "top-[calc(var(--layout-gutter)+var(--layout-panel-inset))]" : "top-5"}`,
|
|
2657
2586
|
children: renderArtistFilter()
|
|
2658
2587
|
}
|
|
2659
2588
|
),
|
|
2660
|
-
|
|
2661
|
-
"div",
|
|
2662
|
-
{
|
|
2663
|
-
className: `absolute top-1/2 z-30 -translate-y-1/2 max-md:hidden transition-[opacity,visibility] duration-150 ease-out ${isTreeReady ? "opacity-100" : "pointer-events-none invisible opacity-0"} ${detailExpanded ? "right-[calc(var(--layout-center-right)-var(--layout-edge-tab-w))]" : isCollapsed ? "left-0" : "left-[calc(var(--layout-gutter)+var(--layout-left-w))]"}`,
|
|
2664
|
-
children: detailTab
|
|
2665
|
-
}
|
|
2666
|
-
),
|
|
2667
|
-
/* @__PURE__ */ jsx30("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs23("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2668
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx30("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2669
|
-
isLoading ? /* @__PURE__ */ jsx30(GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2589
|
+
/* @__PURE__ */ jsx29("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ jsxs22("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
2590
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ jsx29("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
2591
|
+
isLoading ? /* @__PURE__ */ jsx29(GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
2670
2592
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
2671
2593
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
2672
2594
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
2673
2595
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
2674
|
-
/* @__PURE__ */
|
|
2596
|
+
/* @__PURE__ */ jsx29("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx29(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx29(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx29(
|
|
2675
2597
|
GenrePlaylistTreeWheel,
|
|
2676
2598
|
{
|
|
2677
2599
|
scope,
|
|
@@ -2680,29 +2602,26 @@ function GenreTreeView({
|
|
|
2680
2602
|
handleGenreRenameAction,
|
|
2681
2603
|
additionalActions,
|
|
2682
2604
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2683
|
-
hideInfoPanelClose: true,
|
|
2684
|
-
hideInfoPanelTitle: true,
|
|
2685
2605
|
onNodeHover: handleNodeHover,
|
|
2686
|
-
|
|
2606
|
+
...hostPanel,
|
|
2687
2607
|
playback,
|
|
2688
2608
|
selectedNodeId,
|
|
2689
2609
|
readOnly,
|
|
2690
2610
|
allowWheelRotation,
|
|
2691
2611
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2692
2612
|
busy,
|
|
2693
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2613
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
|
|
2694
2614
|
floatingControlsLeading,
|
|
2695
|
-
canSizeByViews && /* @__PURE__ */
|
|
2615
|
+
canSizeByViews && /* @__PURE__ */ jsx29(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2696
2616
|
] }) : void 0,
|
|
2697
2617
|
defaultViewport,
|
|
2698
2618
|
viewportRestore,
|
|
2699
|
-
onViewportChange
|
|
2700
|
-
...infoPanelCuration
|
|
2619
|
+
onViewportChange
|
|
2701
2620
|
}
|
|
2702
2621
|
) }) })
|
|
2703
2622
|
) : viewMode === "pop-core" ? (
|
|
2704
2623
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
2705
|
-
/* @__PURE__ */
|
|
2624
|
+
/* @__PURE__ */ jsx29("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ jsx29(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ jsx29(GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ jsx29(
|
|
2706
2625
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2707
2626
|
{
|
|
2708
2627
|
scope,
|
|
@@ -2711,28 +2630,25 @@ function GenreTreeView({
|
|
|
2711
2630
|
handleGenreRenameAction,
|
|
2712
2631
|
additionalActions,
|
|
2713
2632
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2714
|
-
hideInfoPanelClose: true,
|
|
2715
|
-
hideInfoPanelTitle: true,
|
|
2716
2633
|
onNodeHover: handleNodeHover,
|
|
2717
|
-
|
|
2634
|
+
...hostPanel,
|
|
2718
2635
|
playback,
|
|
2719
2636
|
selectedNodeId,
|
|
2720
2637
|
readOnly,
|
|
2721
2638
|
allowWheelRotation,
|
|
2722
2639
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
2723
2640
|
busy,
|
|
2724
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */
|
|
2641
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ jsxs22("div", { className: "flex items-center gap-2", children: [
|
|
2725
2642
|
floatingControlsLeading,
|
|
2726
|
-
canSizeByViews && /* @__PURE__ */
|
|
2643
|
+
canSizeByViews && /* @__PURE__ */ jsx29(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
2727
2644
|
] }) : void 0,
|
|
2728
2645
|
defaultViewport,
|
|
2729
2646
|
viewportRestore,
|
|
2730
2647
|
onViewportChange,
|
|
2731
|
-
...infoPanelCuration,
|
|
2732
2648
|
...focus
|
|
2733
2649
|
}
|
|
2734
2650
|
) }) })
|
|
2735
|
-
) : viewMode === "outline" ? /* @__PURE__ */
|
|
2651
|
+
) : viewMode === "outline" ? /* @__PURE__ */ jsx29("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ jsx29(
|
|
2736
2652
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
2737
2653
|
{
|
|
2738
2654
|
outline: true,
|
|
@@ -2742,22 +2658,19 @@ function GenreTreeView({
|
|
|
2742
2658
|
handleGenreRenameAction,
|
|
2743
2659
|
additionalActions,
|
|
2744
2660
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2745
|
-
hideInfoPanelClose: true,
|
|
2746
|
-
hideInfoPanelTitle: true,
|
|
2747
2661
|
onNodeHover: handleNodeHover,
|
|
2748
|
-
|
|
2662
|
+
...hostPanel,
|
|
2749
2663
|
playback,
|
|
2750
2664
|
selectedNodeId,
|
|
2751
|
-
readOnly
|
|
2752
|
-
...infoPanelCuration
|
|
2665
|
+
readOnly
|
|
2753
2666
|
}
|
|
2754
|
-
) }) : /* @__PURE__ */
|
|
2755
|
-
([uuid, genrePlaylistTreePerRoot]
|
|
2756
|
-
return /* @__PURE__ */
|
|
2667
|
+
) }) : /* @__PURE__ */ jsx29("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(
|
|
2668
|
+
([uuid, genrePlaylistTreePerRoot]) => {
|
|
2669
|
+
return /* @__PURE__ */ jsx29(
|
|
2757
2670
|
"div",
|
|
2758
2671
|
{
|
|
2759
2672
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
2760
|
-
children: /* @__PURE__ */
|
|
2673
|
+
children: /* @__PURE__ */ jsx29("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ jsx29(
|
|
2761
2674
|
GenrePlaylistTreePerRoot,
|
|
2762
2675
|
{
|
|
2763
2676
|
scope,
|
|
@@ -2767,26 +2680,45 @@ function GenreTreeView({
|
|
|
2767
2680
|
handleGenreRenameAction,
|
|
2768
2681
|
additionalActions,
|
|
2769
2682
|
onSelectedNodeChange: handleSelectedNodeChange,
|
|
2770
|
-
hideInfoPanelClose: true,
|
|
2771
|
-
hideInfoPanelTitle: true,
|
|
2772
2683
|
onNodeHover: handleNodeHover,
|
|
2773
|
-
|
|
2684
|
+
...hostPanel,
|
|
2774
2685
|
playback,
|
|
2775
2686
|
selectedNodeId: uuid === selectedGenrePlaylist?.root.uuid ? selectedNodeId : null,
|
|
2776
2687
|
readOnly,
|
|
2777
2688
|
wheelZoom: "modifier",
|
|
2778
2689
|
defaultViewport: uuid === selectedGenrePlaylist?.root.uuid ? defaultViewport : void 0,
|
|
2779
2690
|
viewportRestore: uuid === selectedGenrePlaylist?.root.uuid && uuid === restoreRootUuid ? viewportRestore : void 0,
|
|
2780
|
-
onViewportChange: uuid === selectedGenrePlaylist?.root.uuid ? onViewportChange : void 0
|
|
2781
|
-
...infoPanelCuration,
|
|
2782
|
-
overlayPanel: rootIndex === 0 ? activeOverlayPanel : void 0
|
|
2691
|
+
onViewportChange: uuid === selectedGenrePlaylist?.root.uuid ? onViewportChange : void 0
|
|
2783
2692
|
}
|
|
2784
2693
|
) })
|
|
2785
2694
|
},
|
|
2786
2695
|
uuid
|
|
2787
2696
|
);
|
|
2788
2697
|
}
|
|
2789
|
-
) })
|
|
2698
|
+
) }),
|
|
2699
|
+
/* @__PURE__ */ jsx29(
|
|
2700
|
+
DetailPanel,
|
|
2701
|
+
{
|
|
2702
|
+
ref: panelRef,
|
|
2703
|
+
side: "left",
|
|
2704
|
+
title: activePanel?.title ?? "",
|
|
2705
|
+
hideTitle: activePanel?.compact,
|
|
2706
|
+
hideClose: activePanel?.compact,
|
|
2707
|
+
headerAccent: activePanel?.headerAccent,
|
|
2708
|
+
headerActions: expandButton,
|
|
2709
|
+
onClose: activePanel?.onClose ?? noop,
|
|
2710
|
+
collapsed: isCollapsed || !isTreeReady || !activePanel,
|
|
2711
|
+
expanded: activePanel?.expanded,
|
|
2712
|
+
onExpandedChange: activePanel?.onExpandedChange,
|
|
2713
|
+
aside: activePanel?.aside,
|
|
2714
|
+
edgeTab: detailTab,
|
|
2715
|
+
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
2716
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ jsxs22(Fragment11, { children: [
|
|
2717
|
+
activePanel.content,
|
|
2718
|
+
activePanel.aside
|
|
2719
|
+
] }) : activePanel?.content
|
|
2720
|
+
}
|
|
2721
|
+
)
|
|
2790
2722
|
] }) })
|
|
2791
2723
|
]
|
|
2792
2724
|
}
|
|
@@ -2797,17 +2729,18 @@ function GenreTreeView({
|
|
|
2797
2729
|
import { useMemo as useMemo8 } from "react";
|
|
2798
2730
|
import {
|
|
2799
2731
|
ACCENT_TEXT_COLOR,
|
|
2732
|
+
GenreNodeAncestors as GenreNodeAncestors2,
|
|
2733
|
+
GenreNodeFamily as GenreNodeFamily2,
|
|
2800
2734
|
getGenreTreeColor as getGenreTreeColor2,
|
|
2801
|
-
InfoPanel,
|
|
2802
2735
|
TEXT_COLOR,
|
|
2803
2736
|
tintSurface
|
|
2804
2737
|
} from "@behindthemusictree/genre-tree-view";
|
|
2805
|
-
import { Skeleton as Skeleton7 } from "@behindthemusictree/ui";
|
|
2738
|
+
import { DetailPanel as DetailPanel2, IconButton as IconButton2, Skeleton as Skeleton7 } from "@behindthemusictree/ui";
|
|
2739
|
+
import { Maximize2 as Maximize22, Minimize2 as Minimize22 } from "lucide-react";
|
|
2806
2740
|
import {
|
|
2807
2741
|
useFetchGenreDetail
|
|
2808
2742
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
2809
|
-
import { jsx as
|
|
2810
|
-
var ITEM_COUNT_LABEL = "Songs";
|
|
2743
|
+
import { jsx as jsx30, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
2811
2744
|
var RELATIVE_ITEM_COUNT = 1;
|
|
2812
2745
|
function toNode(genre, parentId) {
|
|
2813
2746
|
return {
|
|
@@ -2831,6 +2764,7 @@ function GenreDetailPanel({
|
|
|
2831
2764
|
collapsed,
|
|
2832
2765
|
expanded,
|
|
2833
2766
|
onExpandedChange,
|
|
2767
|
+
edgeTab,
|
|
2834
2768
|
extras
|
|
2835
2769
|
}) {
|
|
2836
2770
|
const { data: detail, isPending, isError } = useFetchGenreDetail(genreUuid, scope, getBackendBaseUrl);
|
|
@@ -2843,7 +2777,7 @@ function GenreDetailPanel({
|
|
|
2843
2777
|
fill: tintSurface(rootColor),
|
|
2844
2778
|
textColor: TEXT_COLOR
|
|
2845
2779
|
});
|
|
2846
|
-
const
|
|
2780
|
+
const ancestors2 = [...detail.ascendants].sort((a, b) => b.degree - a.degree);
|
|
2847
2781
|
return {
|
|
2848
2782
|
node: {
|
|
2849
2783
|
id: detail.uuid,
|
|
@@ -2855,57 +2789,72 @@ function GenreDetailPanel({
|
|
|
2855
2789
|
fill: rootColor,
|
|
2856
2790
|
parentNode: detail.parent ? relative(detail.parent, null) : null,
|
|
2857
2791
|
childNodes: detail.children.map((child) => relative(child, detail.uuid)),
|
|
2858
|
-
ancestorNodes:
|
|
2859
|
-
({ ascendant }, index) => relative(ascendant, index === 0 ? null :
|
|
2792
|
+
ancestorNodes: ancestors2.map(
|
|
2793
|
+
({ ascendant }, index) => relative(ascendant, index === 0 ? null : ancestors2[index - 1].ascendant.uuid)
|
|
2860
2794
|
)
|
|
2861
2795
|
};
|
|
2862
2796
|
}, [detail]);
|
|
2863
2797
|
if (isError) {
|
|
2864
|
-
return /* @__PURE__ */
|
|
2798
|
+
return /* @__PURE__ */ jsx30("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
2865
2799
|
}
|
|
2866
2800
|
if (isPending || !detail || !view) {
|
|
2867
|
-
return /* @__PURE__ */
|
|
2868
|
-
/* @__PURE__ */
|
|
2869
|
-
/* @__PURE__ */
|
|
2870
|
-
/* @__PURE__ */
|
|
2801
|
+
return /* @__PURE__ */ jsxs23("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
2802
|
+
/* @__PURE__ */ jsx30(Skeleton7, { className: "h-6 w-2/3" }),
|
|
2803
|
+
/* @__PURE__ */ jsx30(Skeleton7, { className: "h-4 w-full" }),
|
|
2804
|
+
/* @__PURE__ */ jsx30(Skeleton7, { className: "h-4 w-3/4" })
|
|
2871
2805
|
] });
|
|
2872
2806
|
}
|
|
2873
|
-
const
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
{
|
|
2877
|
-
toolbar: sections.toolbar,
|
|
2878
|
-
family: sections.family,
|
|
2879
|
-
genreUuid: detail.uuid,
|
|
2880
|
-
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
2881
|
-
wikidataId: detail.wikidataId,
|
|
2882
|
-
scope,
|
|
2883
|
-
getBackendBaseUrl,
|
|
2884
|
-
playback,
|
|
2885
|
-
band: { node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
2886
|
-
extras
|
|
2887
|
-
}
|
|
2888
|
-
),
|
|
2889
|
-
aside: null
|
|
2890
|
-
});
|
|
2891
|
-
return /* @__PURE__ */ jsx31(
|
|
2892
|
-
InfoPanel,
|
|
2807
|
+
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
2808
|
+
const family = /* @__PURE__ */ jsx30(
|
|
2809
|
+
GenreNodeFamily2,
|
|
2893
2810
|
{
|
|
2894
2811
|
node: view.node,
|
|
2895
2812
|
fill: view.fill,
|
|
2896
2813
|
textColor: ACCENT_TEXT_COLOR,
|
|
2897
2814
|
parentNode: view.parentNode,
|
|
2898
2815
|
childNodes: view.childNodes,
|
|
2899
|
-
|
|
2816
|
+
onSelectNode: selectGenre
|
|
2817
|
+
}
|
|
2818
|
+
);
|
|
2819
|
+
const ancestors = /* @__PURE__ */ jsx30(GenreNodeAncestors2, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
2820
|
+
return /* @__PURE__ */ jsxs23(
|
|
2821
|
+
DetailPanel2,
|
|
2822
|
+
{
|
|
2900
2823
|
side: "left",
|
|
2824
|
+
title: view.node.name,
|
|
2825
|
+
headerAccent: { background: view.fill, color: ACCENT_TEXT_COLOR },
|
|
2826
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ jsx30(
|
|
2827
|
+
IconButton2,
|
|
2828
|
+
{
|
|
2829
|
+
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
2830
|
+
style: { color: "inherit" },
|
|
2831
|
+
onClick: () => onExpandedChange(!expanded),
|
|
2832
|
+
children: expanded ? /* @__PURE__ */ jsx30(Minimize22, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ jsx30(Maximize22, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2833
|
+
}
|
|
2834
|
+
) : void 0,
|
|
2901
2835
|
onClose,
|
|
2902
|
-
|
|
2903
|
-
itemCountLabel: ITEM_COUNT_LABEL,
|
|
2904
|
-
renderInfoPanelBody: renderBody,
|
|
2836
|
+
collapsed,
|
|
2905
2837
|
expanded,
|
|
2906
2838
|
onExpandedChange,
|
|
2907
|
-
|
|
2908
|
-
|
|
2839
|
+
edgeTab,
|
|
2840
|
+
aside: ancestors,
|
|
2841
|
+
children: [
|
|
2842
|
+
/* @__PURE__ */ jsx30(
|
|
2843
|
+
GenreInfoPanelBody,
|
|
2844
|
+
{
|
|
2845
|
+
family,
|
|
2846
|
+
genreUuid: detail.uuid,
|
|
2847
|
+
genrePlaylist: { ...detail.criteriaPlaylist, tracksCount: detail.tracksCount },
|
|
2848
|
+
wikidataId: detail.wikidataId,
|
|
2849
|
+
scope,
|
|
2850
|
+
getBackendBaseUrl,
|
|
2851
|
+
playback,
|
|
2852
|
+
band: { node: view.node, onZoomIn, shareUrl, shareTarget: shareUrl ? shareTarget : void 0 },
|
|
2853
|
+
extras
|
|
2854
|
+
}
|
|
2855
|
+
),
|
|
2856
|
+
!expanded && ancestors
|
|
2857
|
+
]
|
|
2909
2858
|
}
|
|
2910
2859
|
);
|
|
2911
2860
|
}
|
|
@@ -2919,13 +2868,13 @@ import {
|
|
|
2919
2868
|
} from "@behindthemusictree/genre-tree-view";
|
|
2920
2869
|
|
|
2921
2870
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
2922
|
-
import { useEffect as
|
|
2871
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect2, useRef as useRef5 } from "react";
|
|
2923
2872
|
import { ListMusic } from "lucide-react";
|
|
2924
2873
|
import { useTrackList as useTrackList6 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2925
2874
|
import { TrackListOriginType as TrackListOriginType2 } from "@behindthemusictree/app-kit/genre-tree";
|
|
2926
|
-
import { useTrackNoun as
|
|
2927
|
-
import { jsx as
|
|
2928
|
-
var useBrowserLayoutEffect = typeof window === "undefined" ?
|
|
2875
|
+
import { useTrackNoun as useTrackNoun3 } from "@behindthemusictree/app-kit/track-noun";
|
|
2876
|
+
import { jsx as jsx31, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
2877
|
+
var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect6 : useLayoutEffect2;
|
|
2929
2878
|
function TrackListSidebar({
|
|
2930
2879
|
className: className2 = "",
|
|
2931
2880
|
renderDuration,
|
|
@@ -2938,7 +2887,7 @@ function TrackListSidebar({
|
|
|
2938
2887
|
renderSongLink
|
|
2939
2888
|
}) {
|
|
2940
2889
|
const { trackList, loadMore, autoAdvanceScrollRequest } = useTrackList6();
|
|
2941
|
-
const trackNoun =
|
|
2890
|
+
const trackNoun = useTrackNoun3();
|
|
2942
2891
|
const trackListRef = useRef5(null);
|
|
2943
2892
|
const previousOriginRef = useRef5(trackList?.origin ?? null);
|
|
2944
2893
|
const handledAutoAdvanceRequestIdRef = useRef5(null);
|
|
@@ -2948,7 +2897,7 @@ function TrackListSidebar({
|
|
|
2948
2897
|
const userScrollIntentTimerRef = useRef5(null);
|
|
2949
2898
|
const loadMoreSentinelRef = useRef5(null);
|
|
2950
2899
|
const hasMore = !!trackList && trackList.nextPage !== null;
|
|
2951
|
-
|
|
2900
|
+
useEffect6(() => {
|
|
2952
2901
|
const sentinel = loadMoreSentinelRef.current;
|
|
2953
2902
|
if (!hasMore || !sentinel) return;
|
|
2954
2903
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -2968,11 +2917,11 @@ function TrackListSidebar({
|
|
|
2968
2917
|
programmaticScrollTopRef.current = null;
|
|
2969
2918
|
if (trackListRef.current) trackListRef.current.scrollTop = 0;
|
|
2970
2919
|
}, [trackList?.origin]);
|
|
2971
|
-
|
|
2920
|
+
useEffect6(() => {
|
|
2972
2921
|
const list = trackListRef.current;
|
|
2973
2922
|
if (!isCollapsed && list?.scrollTop === 0) shouldFollowPlaybackRef.current = true;
|
|
2974
2923
|
}, [isCollapsed]);
|
|
2975
|
-
|
|
2924
|
+
useEffect6(
|
|
2976
2925
|
() => () => {
|
|
2977
2926
|
if (userScrollIntentTimerRef.current !== null) clearTimeout(userScrollIntentTimerRef.current);
|
|
2978
2927
|
},
|
|
@@ -3026,25 +2975,25 @@ function TrackListSidebar({
|
|
|
3026
2975
|
}
|
|
3027
2976
|
};
|
|
3028
2977
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3029
|
-
return trackList ? /* @__PURE__ */
|
|
2978
|
+
return trackList ? /* @__PURE__ */ jsxs24(
|
|
3030
2979
|
"div",
|
|
3031
2980
|
{
|
|
3032
2981
|
"data-theme": "dark",
|
|
3033
2982
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3034
2983
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3035
2984
|
children: [
|
|
3036
|
-
/* @__PURE__ */
|
|
3037
|
-
/* @__PURE__ */
|
|
3038
|
-
/* @__PURE__ */
|
|
3039
|
-
/* @__PURE__ */
|
|
3040
|
-
/* @__PURE__ */
|
|
2985
|
+
/* @__PURE__ */ jsxs24("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
2986
|
+
/* @__PURE__ */ jsxs24("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
2987
|
+
/* @__PURE__ */ jsxs24("div", { className: "origin flex text-xl", children: [
|
|
2988
|
+
/* @__PURE__ */ jsx31("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
2989
|
+
/* @__PURE__ */ jsx31("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__ */ jsx31("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3041
2990
|
] }),
|
|
3042
|
-
/* @__PURE__ */
|
|
2991
|
+
/* @__PURE__ */ jsxs24("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3043
2992
|
trackList.origin.type === TrackListOriginType2.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === TrackListOriginType2.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3044
2993
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3045
2994
|
] })
|
|
3046
2995
|
] }),
|
|
3047
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */
|
|
2996
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ jsx31(
|
|
3048
2997
|
"button",
|
|
3049
2998
|
{
|
|
3050
2999
|
type: "button",
|
|
@@ -3053,11 +3002,11 @@ function TrackListSidebar({
|
|
|
3053
3002
|
"aria-expanded": !isCollapsed,
|
|
3054
3003
|
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"}`,
|
|
3055
3004
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3056
|
-
children: /* @__PURE__ */
|
|
3005
|
+
children: /* @__PURE__ */ jsx31(ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3057
3006
|
}
|
|
3058
3007
|
)
|
|
3059
3008
|
] }),
|
|
3060
|
-
!isCollapsed && /* @__PURE__ */
|
|
3009
|
+
!isCollapsed && /* @__PURE__ */ jsxs24(
|
|
3061
3010
|
"ul",
|
|
3062
3011
|
{
|
|
3063
3012
|
ref: trackListRef,
|
|
@@ -3067,7 +3016,7 @@ function TrackListSidebar({
|
|
|
3067
3016
|
onPointerDown: markUserScrollIntent,
|
|
3068
3017
|
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",
|
|
3069
3018
|
children: [
|
|
3070
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */
|
|
3019
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ jsx31("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ jsx31(
|
|
3071
3020
|
TrackItem,
|
|
3072
3021
|
{
|
|
3073
3022
|
track,
|
|
@@ -3078,7 +3027,7 @@ function TrackListSidebar({
|
|
|
3078
3027
|
renderSongLink
|
|
3079
3028
|
}
|
|
3080
3029
|
) }, track.uuid)),
|
|
3081
|
-
hasMore && /* @__PURE__ */
|
|
3030
|
+
hasMore && /* @__PURE__ */ jsx31("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3082
3031
|
]
|
|
3083
3032
|
}
|
|
3084
3033
|
)
|
|
@@ -3088,8 +3037,8 @@ function TrackListSidebar({
|
|
|
3088
3037
|
}
|
|
3089
3038
|
|
|
3090
3039
|
// src/genre-tree/PlayerPanel.tsx
|
|
3091
|
-
import { useEffect as
|
|
3092
|
-
import { twMerge
|
|
3040
|
+
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
3041
|
+
import { twMerge } from "tailwind-merge";
|
|
3093
3042
|
import {
|
|
3094
3043
|
usePlayer as usePlayer11,
|
|
3095
3044
|
PlayerVideoSurface
|
|
@@ -3097,12 +3046,13 @@ import {
|
|
|
3097
3046
|
import {
|
|
3098
3047
|
useTrackListSidebarVisibility
|
|
3099
3048
|
} from "@behindthemusictree/app-kit/genre-tree";
|
|
3049
|
+
import { PanelEdgeTab } from "@behindthemusictree/ui";
|
|
3100
3050
|
|
|
3101
3051
|
// src/genre-tree/PlayerControls.tsx
|
|
3102
3052
|
import { Pause as Pause3, Play as Play4, SkipBack, SkipForward, Volume2, VolumeX } from "lucide-react";
|
|
3103
3053
|
import { formatTime, useTrackList as useTrackList7 } from "@behindthemusictree/app-kit/genre-tree";
|
|
3104
3054
|
import { PlayStates as PlayStates4, useCurrentTime, usePlayer as usePlayer10 } from "@behindthemusictree/app-kit/player";
|
|
3105
|
-
import { jsx as
|
|
3055
|
+
import { jsx as jsx32, jsxs as jsxs25 } from "react/jsx-runtime";
|
|
3106
3056
|
function PlayerControls() {
|
|
3107
3057
|
const {
|
|
3108
3058
|
duration,
|
|
@@ -3121,15 +3071,15 @@ function PlayerControls() {
|
|
|
3121
3071
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3122
3072
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3123
3073
|
const isPlaying = playState === PlayStates4.PLAYING;
|
|
3124
|
-
return /* @__PURE__ */
|
|
3074
|
+
return /* @__PURE__ */ jsxs25(
|
|
3125
3075
|
"section",
|
|
3126
3076
|
{
|
|
3127
3077
|
"aria-label": "Player controls",
|
|
3128
3078
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3129
3079
|
children: [
|
|
3130
|
-
/* @__PURE__ */
|
|
3131
|
-
/* @__PURE__ */
|
|
3132
|
-
/* @__PURE__ */
|
|
3080
|
+
/* @__PURE__ */ jsxs25("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3081
|
+
/* @__PURE__ */ jsx32("span", { "aria-label": "Elapsed time", children: formatTime(safeCurrentTime) }),
|
|
3082
|
+
/* @__PURE__ */ jsx32(
|
|
3133
3083
|
"input",
|
|
3134
3084
|
{
|
|
3135
3085
|
"aria-label": "Seek",
|
|
@@ -3143,11 +3093,11 @@ function PlayerControls() {
|
|
|
3143
3093
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3144
3094
|
}
|
|
3145
3095
|
),
|
|
3146
|
-
/* @__PURE__ */
|
|
3096
|
+
/* @__PURE__ */ jsx32("span", { "aria-label": "Duration", children: formatTime(safeDuration) })
|
|
3147
3097
|
] }),
|
|
3148
|
-
/* @__PURE__ */
|
|
3149
|
-
/* @__PURE__ */
|
|
3150
|
-
/* @__PURE__ */
|
|
3098
|
+
/* @__PURE__ */ jsxs25("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3099
|
+
/* @__PURE__ */ jsxs25("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3100
|
+
/* @__PURE__ */ jsx32(
|
|
3151
3101
|
"button",
|
|
3152
3102
|
{
|
|
3153
3103
|
type: "button",
|
|
@@ -3156,10 +3106,10 @@ function PlayerControls() {
|
|
|
3156
3106
|
disabled: !isReady || !hasPreviousTrack,
|
|
3157
3107
|
onClick: playPreviousTrack,
|
|
3158
3108
|
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",
|
|
3159
|
-
children: /* @__PURE__ */
|
|
3109
|
+
children: /* @__PURE__ */ jsx32(SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3160
3110
|
}
|
|
3161
3111
|
),
|
|
3162
|
-
/* @__PURE__ */
|
|
3112
|
+
/* @__PURE__ */ jsx32(
|
|
3163
3113
|
"button",
|
|
3164
3114
|
{
|
|
3165
3115
|
type: "button",
|
|
@@ -3168,10 +3118,10 @@ function PlayerControls() {
|
|
|
3168
3118
|
disabled: !isReady,
|
|
3169
3119
|
onClick: handlePlayPauseAction,
|
|
3170
3120
|
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",
|
|
3171
|
-
children: isPlaying ? /* @__PURE__ */
|
|
3121
|
+
children: isPlaying ? /* @__PURE__ */ jsx32(Pause3, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ jsx32(Play4, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3172
3122
|
}
|
|
3173
3123
|
),
|
|
3174
|
-
/* @__PURE__ */
|
|
3124
|
+
/* @__PURE__ */ jsx32(
|
|
3175
3125
|
"button",
|
|
3176
3126
|
{
|
|
3177
3127
|
type: "button",
|
|
@@ -3180,14 +3130,14 @@ function PlayerControls() {
|
|
|
3180
3130
|
disabled: !isReady || !hasNextTrack,
|
|
3181
3131
|
onClick: playNextTrack,
|
|
3182
3132
|
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",
|
|
3183
|
-
children: /* @__PURE__ */
|
|
3133
|
+
children: /* @__PURE__ */ jsx32(SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3184
3134
|
}
|
|
3185
3135
|
)
|
|
3186
3136
|
] }),
|
|
3187
|
-
/* @__PURE__ */
|
|
3188
|
-
volume === 0 ? /* @__PURE__ */
|
|
3189
|
-
/* @__PURE__ */
|
|
3190
|
-
/* @__PURE__ */
|
|
3137
|
+
/* @__PURE__ */ jsxs25("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: [
|
|
3138
|
+
volume === 0 ? /* @__PURE__ */ jsx32(VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ jsx32(Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3139
|
+
/* @__PURE__ */ jsx32("span", { className: "sr-only", children: "Volume" }),
|
|
3140
|
+
/* @__PURE__ */ jsx32(
|
|
3191
3141
|
"input",
|
|
3192
3142
|
{
|
|
3193
3143
|
"aria-label": "Volume",
|
|
@@ -3209,7 +3159,7 @@ function PlayerControls() {
|
|
|
3209
3159
|
}
|
|
3210
3160
|
|
|
3211
3161
|
// src/genre-tree/PlayerPanel.tsx
|
|
3212
|
-
import { jsx as
|
|
3162
|
+
import { jsx as jsx33, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
3213
3163
|
var PANEL_ID = "player-panel";
|
|
3214
3164
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3215
3165
|
function PlayerPanel({
|
|
@@ -3225,7 +3175,7 @@ function PlayerPanel({
|
|
|
3225
3175
|
const { toggleTrackListSidebar, isTrackListSidebarVisible } = useTrackListSidebarVisibility();
|
|
3226
3176
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = useState8("");
|
|
3227
3177
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3228
|
-
|
|
3178
|
+
useEffect7(() => {
|
|
3229
3179
|
if (loadedTrackTitle) setLastLoadedTrackTitle(loadedTrackTitle);
|
|
3230
3180
|
}, [loadedTrackTitle]);
|
|
3231
3181
|
const [uncontrolledExtended, setUncontrolledExtended] = useState8(false);
|
|
@@ -3249,11 +3199,11 @@ function PlayerPanel({
|
|
|
3249
3199
|
onClick: () => setIsExtendRequested(true),
|
|
3250
3200
|
expanded: false
|
|
3251
3201
|
} : null;
|
|
3252
|
-
return /* @__PURE__ */
|
|
3202
|
+
return /* @__PURE__ */ jsxs26(
|
|
3253
3203
|
"div",
|
|
3254
3204
|
{
|
|
3255
3205
|
id: PANEL_ID,
|
|
3256
|
-
className:
|
|
3206
|
+
className: twMerge(
|
|
3257
3207
|
"fixed right-[var(--layout-gutter)] top-[var(--layout-band-top)] z-40 max-h-[calc(100vh-var(--layout-band-top)-3.5rem)]",
|
|
3258
3208
|
!isExtended && "w-[var(--layout-right-w)]",
|
|
3259
3209
|
isTrackListSidebarVisible && "h-[calc(100vh-var(--layout-band-top)-3.5rem)]",
|
|
@@ -3262,7 +3212,7 @@ function PlayerPanel({
|
|
|
3262
3212
|
),
|
|
3263
3213
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3264
3214
|
children: [
|
|
3265
|
-
/* @__PURE__ */
|
|
3215
|
+
/* @__PURE__ */ jsx33(
|
|
3266
3216
|
PanelEdgeTab,
|
|
3267
3217
|
{
|
|
3268
3218
|
side: "right",
|
|
@@ -3272,30 +3222,30 @@ function PlayerPanel({
|
|
|
3272
3222
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3273
3223
|
}
|
|
3274
3224
|
),
|
|
3275
|
-
/* @__PURE__ */
|
|
3225
|
+
/* @__PURE__ */ jsxs26(
|
|
3276
3226
|
"div",
|
|
3277
3227
|
{
|
|
3278
|
-
className:
|
|
3228
|
+
className: twMerge(
|
|
3279
3229
|
"flex h-full max-h-[inherit] overflow-hidden rounded-[var(--radius-panel,1rem)] bg-zinc-950 text-zinc-100 shadow-[0_1px_2px_rgba(0,0,0,0.5),0_2px_6px_2px_rgba(0,0,0,0.3)]",
|
|
3280
3230
|
isExtended ? "flex-row" : "flex-col"
|
|
3281
3231
|
),
|
|
3282
3232
|
children: [
|
|
3283
|
-
/* @__PURE__ */
|
|
3284
|
-
/* @__PURE__ */
|
|
3285
|
-
/* @__PURE__ */
|
|
3286
|
-
/* @__PURE__ */
|
|
3287
|
-
playerTrackObject?.loadError && /* @__PURE__ */
|
|
3233
|
+
/* @__PURE__ */ jsxs26("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3234
|
+
/* @__PURE__ */ jsx33("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ jsx33("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3235
|
+
/* @__PURE__ */ jsxs26("div", { className: "relative w-full shrink-0", children: [
|
|
3236
|
+
/* @__PURE__ */ jsx33(PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3237
|
+
playerTrackObject?.loadError && /* @__PURE__ */ jsx33("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: playerTrackObject.loadError })
|
|
3288
3238
|
] }),
|
|
3289
|
-
/* @__PURE__ */
|
|
3239
|
+
/* @__PURE__ */ jsx33(PlayerControls, {})
|
|
3290
3240
|
] }),
|
|
3291
|
-
/* @__PURE__ */
|
|
3241
|
+
/* @__PURE__ */ jsx33(
|
|
3292
3242
|
TrackListSidebar,
|
|
3293
3243
|
{
|
|
3294
3244
|
layout: "inline",
|
|
3295
3245
|
isCollapsed: !isTrackListSidebarVisible,
|
|
3296
3246
|
onExpand: toggleTrackListSidebar,
|
|
3297
3247
|
onMinimize: hideSongList,
|
|
3298
|
-
className:
|
|
3248
|
+
className: twMerge(
|
|
3299
3249
|
"min-h-0 min-w-0 flex-1",
|
|
3300
3250
|
isExtended && "border-l border-zinc-800"
|
|
3301
3251
|
),
|
|
@@ -3315,11 +3265,11 @@ function PlayerPanel({
|
|
|
3315
3265
|
import { v4 as uuidv4 } from "uuid";
|
|
3316
3266
|
import { AiFillStar, AiOutlineStar } from "react-icons/ai";
|
|
3317
3267
|
import { FORM_RATING_NULL_VALUE } from "@behindthemusictree/app-kit/genre-tree";
|
|
3318
|
-
import { jsx as
|
|
3268
|
+
import { jsx as jsx34, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
3319
3269
|
function Rating({ rating, handleChange }) {
|
|
3320
3270
|
const instanceId = uuidv4();
|
|
3321
|
-
return /* @__PURE__ */
|
|
3322
|
-
/* @__PURE__ */
|
|
3271
|
+
return /* @__PURE__ */ jsx34("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ jsxs27("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3272
|
+
/* @__PURE__ */ jsx34(
|
|
3323
3273
|
"input",
|
|
3324
3274
|
{
|
|
3325
3275
|
id: `star-${instanceId}-${i}`,
|
|
@@ -3331,16 +3281,16 @@ function Rating({ rating, handleChange }) {
|
|
|
3331
3281
|
onChange: handleChange
|
|
3332
3282
|
}
|
|
3333
3283
|
),
|
|
3334
|
-
/* @__PURE__ */
|
|
3335
|
-
/* @__PURE__ */
|
|
3284
|
+
/* @__PURE__ */ jsx34("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ jsxs27("div", { className: "flex", children: [
|
|
3285
|
+
/* @__PURE__ */ jsx34(
|
|
3336
3286
|
AiOutlineStar,
|
|
3337
3287
|
{
|
|
3338
3288
|
className: `w-7 h-7 absolute top-0 left-0 ${rating === FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3339
3289
|
}
|
|
3340
3290
|
),
|
|
3341
|
-
/* @__PURE__ */
|
|
3291
|
+
/* @__PURE__ */ jsx34("span", { className: "flex-grow w-8" }),
|
|
3342
3292
|
" "
|
|
3343
|
-
] }) : /* @__PURE__ */
|
|
3293
|
+
] }) : /* @__PURE__ */ jsx34(
|
|
3344
3294
|
AiFillStar,
|
|
3345
3295
|
{
|
|
3346
3296
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3355,8 +3305,6 @@ export {
|
|
|
3355
3305
|
GenreSearch,
|
|
3356
3306
|
GenreNodeActionBand,
|
|
3357
3307
|
GenreInfoPanelBody,
|
|
3358
|
-
PanelEdgeTab,
|
|
3359
|
-
useLeftDetailPanelLayout,
|
|
3360
3308
|
TrackPositionPlayPause,
|
|
3361
3309
|
TrackItem,
|
|
3362
3310
|
GenreTreeView,
|
|
@@ -3369,4 +3317,4 @@ export {
|
|
|
3369
3317
|
GenreTreeViewSkeleton2 as GenreTreeViewSkeleton,
|
|
3370
3318
|
GenreTreeOutlineSkeleton
|
|
3371
3319
|
};
|
|
3372
|
-
//# sourceMappingURL=chunk-
|
|
3320
|
+
//# sourceMappingURL=chunk-AQASG5KT.js.map
|