@behindthemusictree/app-kit-ui 33.1.0 → 33.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2M4Q2IJU.js → chunk-QMELESFB.js} +518 -460
- package/dist/chunk-QMELESFB.js.map +1 -0
- package/dist/genre-tree/index.cjs +598 -535
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +74 -20
- package/dist/genre-tree/index.js +15 -3
- package/dist/index.cjs +596 -533
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -3
- package/package.json +5 -5
- package/dist/chunk-2M4Q2IJU.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -40,13 +40,19 @@ __export(src_exports, {
|
|
|
40
40
|
PopupButtons: () => PopupButtons,
|
|
41
41
|
PopupTitle: () => PopupTitle,
|
|
42
42
|
Rating: () => Rating,
|
|
43
|
+
SEARCH_FAILED_STATUS: () => SEARCH_FAILED_STATUS,
|
|
44
|
+
SearchBox: () => SearchBox,
|
|
43
45
|
SpotifyAuthErrorPopup: () => SpotifyAuthErrorPopup,
|
|
44
46
|
Toast: () => Toast,
|
|
45
47
|
TrackItem: () => TrackItem,
|
|
46
48
|
TrackListSidebar: () => TrackListSidebar,
|
|
47
49
|
TrackPositionPlayPause: () => TrackPositionPlayPause,
|
|
48
50
|
TrackUploadPopup: () => TrackUploadPopup,
|
|
49
|
-
|
|
51
|
+
searchStatus: () => searchStatus,
|
|
52
|
+
useArtistSearchGroup: () => useArtistSearchGroup,
|
|
53
|
+
useGenrePlaylistPlayback: () => useGenrePlaylistPlayback,
|
|
54
|
+
useGenreRemoteSearchGroup: () => useGenreRemoteSearchGroup,
|
|
55
|
+
useSongSearchGroup: () => useSongSearchGroup
|
|
50
56
|
});
|
|
51
57
|
module.exports = __toCommonJS(src_exports);
|
|
52
58
|
|
|
@@ -585,10 +591,9 @@ var import_react16 = require("react");
|
|
|
585
591
|
var import_react_query3 = require("@tanstack/react-query");
|
|
586
592
|
var import_lucide_react15 = require("lucide-react");
|
|
587
593
|
var import_lucide_react16 = require("lucide-react");
|
|
588
|
-
var
|
|
594
|
+
var import_ui19 = require("@behindthemusictree/ui");
|
|
589
595
|
var import_genre_tree_view4 = require("@behindthemusictree/genre-tree-view");
|
|
590
|
-
var
|
|
591
|
-
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
596
|
+
var import_genre_tree21 = require("@behindthemusictree/app-kit/genre-tree");
|
|
592
597
|
var import_popup6 = require("@behindthemusictree/app-kit/popup");
|
|
593
598
|
|
|
594
599
|
// src/genre-tree/playlist-tree/size-metric.ts
|
|
@@ -1020,11 +1025,143 @@ function GenreTreeWheelHandoff({ skeleton, children, onRevealedChange }) {
|
|
|
1020
1025
|
}
|
|
1021
1026
|
|
|
1022
1027
|
// src/genre-tree/GenreSearch.tsx
|
|
1023
|
-
var import_react8 = require("react");
|
|
1024
1028
|
var import_lucide_react7 = require("lucide-react");
|
|
1029
|
+
var import_ui7 = require("@behindthemusictree/ui");
|
|
1030
|
+
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1031
|
+
|
|
1032
|
+
// src/genre-tree/search/SearchBox.tsx
|
|
1033
|
+
var import_react8 = require("react");
|
|
1025
1034
|
var import_ui6 = require("@behindthemusictree/ui");
|
|
1026
|
-
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1027
1035
|
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
1036
|
+
function SearchBox({
|
|
1037
|
+
label,
|
|
1038
|
+
placeholder,
|
|
1039
|
+
value,
|
|
1040
|
+
onValueChange,
|
|
1041
|
+
groups,
|
|
1042
|
+
onPicked,
|
|
1043
|
+
onClear,
|
|
1044
|
+
leading,
|
|
1045
|
+
className: className2,
|
|
1046
|
+
onFocus
|
|
1047
|
+
}) {
|
|
1048
|
+
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
1049
|
+
const loading = groups.some((group) => group.loading);
|
|
1050
|
+
const status = groups.map((group) => group.status).filter(Boolean).join(". ");
|
|
1051
|
+
const handlePick = (groupKey, itemKey) => {
|
|
1052
|
+
setOpen(false);
|
|
1053
|
+
groups.find((group) => group.key === groupKey)?.items.find((item) => item.key === itemKey)?.onSelect();
|
|
1054
|
+
onPicked?.(groupKey, itemKey);
|
|
1055
|
+
};
|
|
1056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: className2, onFocusCapture: onFocus, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1057
|
+
import_ui6.SearchCombobox,
|
|
1058
|
+
{
|
|
1059
|
+
ariaLabel: label,
|
|
1060
|
+
listLabel: `${label} results`,
|
|
1061
|
+
value,
|
|
1062
|
+
placeholder: placeholder ?? `${label}\u2026`,
|
|
1063
|
+
groups: groups.map((group) => ({
|
|
1064
|
+
key: group.key,
|
|
1065
|
+
label: group.label,
|
|
1066
|
+
options: group.items.map((item) => ({ key: item.key, content: item.content }))
|
|
1067
|
+
})),
|
|
1068
|
+
open,
|
|
1069
|
+
onOpenChange: setOpen,
|
|
1070
|
+
onValueChange,
|
|
1071
|
+
onPick: handlePick,
|
|
1072
|
+
status,
|
|
1073
|
+
loading,
|
|
1074
|
+
onClear,
|
|
1075
|
+
leading,
|
|
1076
|
+
inputClassName: "focus-visible:ring-0"
|
|
1077
|
+
}
|
|
1078
|
+
) });
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// src/genre-tree/search/SearchGroup.ts
|
|
1082
|
+
var SEARCH_FAILED_STATUS = "Search failed";
|
|
1083
|
+
var searchStatus = (state, count, noun) => state.isError ? SEARCH_FAILED_STATUS : state.settled && count === 0 ? `No ${noun} found` : "";
|
|
1084
|
+
|
|
1085
|
+
// src/genre-tree/search/useArtistSearchGroup.tsx
|
|
1086
|
+
var import_genre_tree2 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1087
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1088
|
+
function useArtistSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
1089
|
+
const search = (0, import_genre_tree2.useArtistSearch)(getBaseUrl, enabled ? text : "");
|
|
1090
|
+
if (!enabled) return void 0;
|
|
1091
|
+
return {
|
|
1092
|
+
key: "artists",
|
|
1093
|
+
label: "Artists",
|
|
1094
|
+
items: search.artists.map((artist) => ({
|
|
1095
|
+
key: artist.uuid,
|
|
1096
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_jsx_runtime17.Fragment, { children: [
|
|
1097
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
1098
|
+
artist.name,
|
|
1099
|
+
artist.disambiguation && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "font-normal text-[var(--color-neutral-500)]", children: [
|
|
1100
|
+
" \xB7 ",
|
|
1101
|
+
artist.disambiguation
|
|
1102
|
+
] })
|
|
1103
|
+
] }),
|
|
1104
|
+
artist.matchedAlias && /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [
|
|
1105
|
+
"also known as ",
|
|
1106
|
+
artist.matchedAlias
|
|
1107
|
+
] })
|
|
1108
|
+
] }),
|
|
1109
|
+
onSelect: () => onSelect(artist)
|
|
1110
|
+
})),
|
|
1111
|
+
status: searchStatus(search, search.artists.length, "artists"),
|
|
1112
|
+
loading: search.pending
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// src/genre-tree/search/useSongSearchGroup.tsx
|
|
1117
|
+
var import_genre_tree3 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1118
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1119
|
+
function useSongSearchGroup(getBaseUrl, text, onSelect, enabled = true) {
|
|
1120
|
+
const search = (0, import_genre_tree3.useSongSearch)(getBaseUrl, enabled ? text : "");
|
|
1121
|
+
if (!enabled) return void 0;
|
|
1122
|
+
return {
|
|
1123
|
+
key: "songs",
|
|
1124
|
+
label: "Songs",
|
|
1125
|
+
items: search.songs.map((song) => ({
|
|
1126
|
+
key: song.uuid,
|
|
1127
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("span", { className: (0, import_genre_tree3.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
1128
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
1129
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
1130
|
+
] }),
|
|
1131
|
+
onSelect: () => onSelect(song)
|
|
1132
|
+
})),
|
|
1133
|
+
status: searchStatus(search, search.songs.length, "songs"),
|
|
1134
|
+
loading: search.pending
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
// src/genre-tree/search/useGenreRemoteSearchGroup.tsx
|
|
1139
|
+
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1140
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
1141
|
+
function useGenreRemoteSearchGroup(getBaseUrl, text, onSelect, { key = "genres", label = "Genres", filter, describe, ...options } = {}) {
|
|
1142
|
+
const search = (0, import_genre_tree4.useGenreRemoteSearch)(getBaseUrl, text, options);
|
|
1143
|
+
const genres = filter ? search.genres.filter(filter) : search.genres;
|
|
1144
|
+
return {
|
|
1145
|
+
key,
|
|
1146
|
+
label,
|
|
1147
|
+
items: genres.map((genre) => ({
|
|
1148
|
+
key: genre.uuid,
|
|
1149
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_jsx_runtime19.Fragment, { children: [
|
|
1150
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)("span", { className: "block truncate font-medium", children: [
|
|
1151
|
+
genre.name,
|
|
1152
|
+
genre.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "ml-2 text-xs font-normal italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
1153
|
+
] }),
|
|
1154
|
+
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: describe?.(genre) ?? `${genre.treeName} \xB7 ${genre.root.name}` })
|
|
1155
|
+
] }),
|
|
1156
|
+
onSelect: () => onSelect(genre)
|
|
1157
|
+
})),
|
|
1158
|
+
status: searchStatus(search, genres.length, "genres"),
|
|
1159
|
+
loading: search.pending
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// src/genre-tree/GenreSearch.tsx
|
|
1164
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
1028
1165
|
function GenreSearch({
|
|
1029
1166
|
genrePlaylists,
|
|
1030
1167
|
onSelect,
|
|
@@ -1033,120 +1170,58 @@ function GenreSearch({
|
|
|
1033
1170
|
onPrevious,
|
|
1034
1171
|
placeholder,
|
|
1035
1172
|
onQueryChange,
|
|
1036
|
-
extraGroups = []
|
|
1173
|
+
extraGroups = [],
|
|
1174
|
+
onFocus
|
|
1037
1175
|
}) {
|
|
1038
|
-
const { query, setQuery, results, canClear, clear, select } = (0,
|
|
1176
|
+
const { query, setQuery, results, canClear, clear, select } = (0, import_genre_tree5.useGenreSearch)({
|
|
1039
1177
|
genrePlaylists,
|
|
1040
1178
|
onSelect,
|
|
1041
1179
|
selectedName,
|
|
1042
1180
|
onClear
|
|
1043
1181
|
});
|
|
1044
|
-
const [open, setOpen] = (0, import_react8.useState)(false);
|
|
1045
1182
|
const active = query !== "" && query !== selectedName;
|
|
1046
1183
|
const hasExtras = extraGroups.length > 0;
|
|
1047
1184
|
const label = hasExtras ? "Search" : "Search a genre";
|
|
1048
|
-
const loading = active && extraGroups.some((group) => group.loading);
|
|
1049
|
-
const status = active ? extraGroups.map((group) => group.status).filter(Boolean).join(". ") : "";
|
|
1050
1185
|
const groups = [
|
|
1051
1186
|
{
|
|
1052
1187
|
key: "genres",
|
|
1053
1188
|
label: hasExtras ? "Genres" : void 0,
|
|
1054
|
-
|
|
1189
|
+
items: results.map((genrePlaylist) => ({
|
|
1055
1190
|
key: genrePlaylist.uuid,
|
|
1056
|
-
content: /* @__PURE__ */ (0,
|
|
1191
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
1057
1192
|
genrePlaylist.name,
|
|
1058
|
-
genrePlaylist.isUmbrella && /* @__PURE__ */ (0,
|
|
1059
|
-
] })
|
|
1193
|
+
genrePlaylist.isUmbrella && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "ml-2 text-xs italic text-[var(--color-text-muted)]", children: "umbrella" })
|
|
1194
|
+
] }),
|
|
1195
|
+
onSelect: () => select(genrePlaylist)
|
|
1060
1196
|
}))
|
|
1061
1197
|
},
|
|
1062
|
-
...extraGroups.map(
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
options: active ? group.items.map((item) => ({ key: item.key, content: item.content })) : []
|
|
1066
|
-
}))
|
|
1198
|
+
...extraGroups.map(
|
|
1199
|
+
(group) => active ? group : { ...group, items: [], status: "", loading: false }
|
|
1200
|
+
)
|
|
1067
1201
|
];
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1202
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "genre-search w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
1203
|
+
SearchBox,
|
|
1204
|
+
{
|
|
1205
|
+
label,
|
|
1206
|
+
placeholder,
|
|
1207
|
+
value: query,
|
|
1208
|
+
onValueChange: (value) => {
|
|
1209
|
+
setQuery(value);
|
|
1210
|
+
onQueryChange?.(value);
|
|
1211
|
+
},
|
|
1212
|
+
groups,
|
|
1213
|
+
onPicked: (groupKey) => {
|
|
1214
|
+
if (groupKey !== "genres") setQuery("");
|
|
1215
|
+
onQueryChange?.("");
|
|
1216
|
+
},
|
|
1217
|
+
onClear: canClear ? () => {
|
|
1218
|
+
clear();
|
|
1219
|
+
onQueryChange?.("");
|
|
1220
|
+
} : void 0,
|
|
1221
|
+
leading: onPrevious && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("button", { type: "button", "aria-label": "Previous", title: "Previous", className: import_ui7.SEARCH_LEADING_BUTTON_CLASS, onClick: onPrevious, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "h-4 w-4" }) }),
|
|
1222
|
+
onFocus
|
|
1076
1223
|
}
|
|
1077
|
-
|
|
1078
|
-
if (!item) return;
|
|
1079
|
-
setQuery("");
|
|
1080
|
-
onQueryChange?.("");
|
|
1081
|
-
item.onSelect();
|
|
1082
|
-
};
|
|
1083
|
-
return /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "genre-search relative w-[calc(var(--layout-detail-panel-w,25.5rem)-2*var(--layout-panel-inset,1rem))] shrink-0 max-md:w-full", children: [
|
|
1084
|
-
onPrevious && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1085
|
-
"button",
|
|
1086
|
-
{
|
|
1087
|
-
type: "button",
|
|
1088
|
-
"aria-label": "Previous",
|
|
1089
|
-
title: "Previous",
|
|
1090
|
-
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)]",
|
|
1091
|
-
onClick: onPrevious,
|
|
1092
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1093
|
-
}
|
|
1094
|
-
),
|
|
1095
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1096
|
-
import_ui6.Combobox,
|
|
1097
|
-
{
|
|
1098
|
-
ariaLabel: label,
|
|
1099
|
-
listLabel: `${label} results`,
|
|
1100
|
-
value: query,
|
|
1101
|
-
placeholder: placeholder ?? `${label}\u2026`,
|
|
1102
|
-
groups,
|
|
1103
|
-
open,
|
|
1104
|
-
onOpenChange: setOpen,
|
|
1105
|
-
onValueChange: (value) => {
|
|
1106
|
-
setQuery(value);
|
|
1107
|
-
onQueryChange?.(value);
|
|
1108
|
-
},
|
|
1109
|
-
onPick: handlePick,
|
|
1110
|
-
status,
|
|
1111
|
-
loading: loading ? "Searching\u2026" : void 0,
|
|
1112
|
-
inputClassName: `h-[var(--layout-search-h,3rem)] ${onPrevious ? "pl-11" : "pl-6"} pr-20 text-base transition-[padding] duration-200 ease-out motion-reduce:transition-none bg-[var(--color-surface)] border-[var(--color-border)] rounded-full shadow-md focus-visible:ring-0`,
|
|
1113
|
-
className: "genre-search-results z-40 rounded-xl shadow-md",
|
|
1114
|
-
listClassName: "max-h-[min(20rem,50dvh)]",
|
|
1115
|
-
optionClassName: "[@media(pointer:coarse)]:min-h-[var(--layout-touch-target)]"
|
|
1116
|
-
}
|
|
1117
|
-
),
|
|
1118
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "absolute right-3 top-1/2 flex items-center -translate-y-1/2", children: [
|
|
1119
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("span", { "data-loading": loading, className: "relative p-2 text-[var(--color-text-muted)] pointer-events-none", children: [
|
|
1120
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1121
|
-
import_lucide_react7.Search,
|
|
1122
|
-
{
|
|
1123
|
-
"aria-hidden": "true",
|
|
1124
|
-
className: `w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-0" : ""}`
|
|
1125
|
-
}
|
|
1126
|
-
),
|
|
1127
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1128
|
-
import_lucide_react7.LoaderCircle,
|
|
1129
|
-
{
|
|
1130
|
-
"aria-hidden": "true",
|
|
1131
|
-
className: `absolute inset-2 w-6 h-6 transition-opacity duration-200 motion-reduce:transition-none ${loading ? "opacity-100 delay-300 animate-spin motion-reduce:animate-none" : "opacity-0"}`
|
|
1132
|
-
}
|
|
1133
|
-
)
|
|
1134
|
-
] }),
|
|
1135
|
-
canClear && /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1136
|
-
"button",
|
|
1137
|
-
{
|
|
1138
|
-
type: "button",
|
|
1139
|
-
"aria-label": "Clear search",
|
|
1140
|
-
className: "flex items-center justify-center p-2 [@media(pointer:coarse)]:min-h-[var(--layout-touch-target)] [@media(pointer:coarse)]:min-w-[var(--layout-touch-target)] text-[var(--color-text-muted)] rounded-full hover:bg-[var(--color-surface-muted)] hover:text-[var(--color-text)]",
|
|
1141
|
-
onClick: () => {
|
|
1142
|
-
clear();
|
|
1143
|
-
onQueryChange?.("");
|
|
1144
|
-
},
|
|
1145
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react7.X, { "aria-hidden": "true", className: "w-6 h-6" })
|
|
1146
|
-
}
|
|
1147
|
-
)
|
|
1148
|
-
] })
|
|
1149
|
-
] });
|
|
1224
|
+
) });
|
|
1150
1225
|
}
|
|
1151
1226
|
|
|
1152
1227
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
@@ -1166,12 +1241,12 @@ var import_fa3 = require("react-icons/fa");
|
|
|
1166
1241
|
var import_si = require("react-icons/si");
|
|
1167
1242
|
|
|
1168
1243
|
// src/genre-tree/ShareDestinationButton.tsx
|
|
1169
|
-
var
|
|
1244
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
1170
1245
|
var className = "group flex min-w-0 flex-1 flex-col items-center justify-center gap-1.5 rounded-xl px-2 py-2 text-xs text-[var(--color-text)] transition-colors hover:bg-[var(--color-surface-muted)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] disabled:cursor-not-allowed disabled:opacity-50";
|
|
1171
1246
|
function Contents({ icon, label }) {
|
|
1172
|
-
return /* @__PURE__ */ (0,
|
|
1173
|
-
/* @__PURE__ */ (0,
|
|
1174
|
-
/* @__PURE__ */ (0,
|
|
1247
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
|
|
1248
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: icon }),
|
|
1249
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "max-w-full truncate", children: label })
|
|
1175
1250
|
] });
|
|
1176
1251
|
}
|
|
1177
1252
|
function ShareDestinationButton({
|
|
@@ -1183,29 +1258,29 @@ function ShareDestinationButton({
|
|
|
1183
1258
|
ariaLabel
|
|
1184
1259
|
}) {
|
|
1185
1260
|
if (href) {
|
|
1186
|
-
return /* @__PURE__ */ (0,
|
|
1261
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Contents, { icon, label }) });
|
|
1187
1262
|
}
|
|
1188
|
-
return /* @__PURE__ */ (0,
|
|
1263
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("button", { type: "button", onClick, disabled, "aria-label": ariaLabel, className, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Contents, { icon, label }) });
|
|
1189
1264
|
}
|
|
1190
1265
|
|
|
1191
1266
|
// src/genre-tree/SharePreviewCard.tsx
|
|
1192
1267
|
var import_lucide_react8 = require("lucide-react");
|
|
1193
|
-
var
|
|
1268
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
1194
1269
|
function SharePreviewCard({ target, url }) {
|
|
1195
1270
|
const Icon = { genre: import_lucide_react8.Music2, artist: import_lucide_react8.UserRound, song: import_lucide_react8.Music }[target.kind];
|
|
1196
1271
|
const typeLabel = { genre: "Genre", artist: "Artist", song: "Song" }[target.kind];
|
|
1197
|
-
return /* @__PURE__ */ (0,
|
|
1198
|
-
/* @__PURE__ */ (0,
|
|
1199
|
-
/* @__PURE__ */ (0,
|
|
1200
|
-
/* @__PURE__ */ (0,
|
|
1201
|
-
/* @__PURE__ */ (0,
|
|
1202
|
-
/* @__PURE__ */ (0,
|
|
1272
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "rounded-3xl bg-[var(--color-surface-muted)] p-4", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-h-32 overflow-hidden rounded-2xl bg-[var(--color-surface)] shadow-lg", children: [
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "flex w-28 shrink-0 items-center justify-center bg-[var(--color-primary)]/15 text-[var(--color-primary)]", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Icon, { "aria-hidden": "true", className: "h-10 w-10" }) }),
|
|
1274
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1 p-4", children: [
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-xs font-medium uppercase tracking-wide text-[var(--color-text-muted)]", children: typeLabel }),
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-base font-semibold text-[var(--color-text)]", title: target.name, children: target.name }),
|
|
1277
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "truncate text-sm text-[var(--color-primary)]", title: url, children: url })
|
|
1203
1278
|
] })
|
|
1204
1279
|
] }) });
|
|
1205
1280
|
}
|
|
1206
1281
|
|
|
1207
1282
|
// src/genre-tree/SharePopup.tsx
|
|
1208
|
-
var
|
|
1283
|
+
var import_jsx_runtime23 = require("react/jsx-runtime");
|
|
1209
1284
|
function resolveAbsoluteUrl(url) {
|
|
1210
1285
|
try {
|
|
1211
1286
|
const parsed = new URL(url, window.location.href);
|
|
@@ -1280,8 +1355,8 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1280
1355
|
setShareError("Could not open the share options.");
|
|
1281
1356
|
}
|
|
1282
1357
|
};
|
|
1283
|
-
return /* @__PURE__ */ (0,
|
|
1284
|
-
/* @__PURE__ */ (0,
|
|
1358
|
+
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
|
|
1359
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1285
1360
|
BasePopup_default,
|
|
1286
1361
|
{
|
|
1287
1362
|
title: "Share",
|
|
@@ -1296,67 +1371,67 @@ function SharePopup({ target, url, onClose }) {
|
|
|
1296
1371
|
titleTextClassName: "!text-xl !font-semibold !text-[var(--color-text)]",
|
|
1297
1372
|
closeButtonClassName: "!h-10 !w-10 !rounded-none !border-0 !bg-transparent !p-0 !text-[var(--color-text)] hover:!bg-transparent hover:!text-[var(--color-text)]",
|
|
1298
1373
|
contentClassName: "!rounded-none !bg-transparent !px-6 !pb-6 !pt-2",
|
|
1299
|
-
children: /* @__PURE__ */ (0,
|
|
1300
|
-
/* @__PURE__ */ (0,
|
|
1301
|
-
/* @__PURE__ */ (0,
|
|
1302
|
-
/* @__PURE__ */ (0,
|
|
1374
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(SharePreviewCard, { target, url: absoluteUrl || url }),
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)("div", { className: "grid grid-cols-3 gap-1 rounded-3xl bg-[var(--color-surface)] p-3 sm:grid-cols-4", children: [
|
|
1377
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1303
1378
|
ShareDestinationButton,
|
|
1304
1379
|
{
|
|
1305
1380
|
label: "Copy Link",
|
|
1306
|
-
icon: /* @__PURE__ */ (0,
|
|
1381
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react9.Link2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1307
1382
|
onClick: () => void handleCopy(),
|
|
1308
1383
|
ariaLabel: "Copy link",
|
|
1309
1384
|
disabled: !absoluteUrl
|
|
1310
1385
|
}
|
|
1311
1386
|
),
|
|
1312
|
-
/* @__PURE__ */ (0,
|
|
1387
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1313
1388
|
ShareDestinationButton,
|
|
1314
1389
|
{
|
|
1315
1390
|
label: "WhatsApp",
|
|
1316
|
-
icon: /* @__PURE__ */ (0,
|
|
1391
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_fa3.FaWhatsapp, { "aria-hidden": "true", className: "h-9 w-9 text-[#25D366]" }),
|
|
1317
1392
|
href: destinationUrls?.whatsapp,
|
|
1318
1393
|
ariaLabel: `Share ${target.name} via WhatsApp`,
|
|
1319
1394
|
disabled: !absoluteUrl
|
|
1320
1395
|
}
|
|
1321
1396
|
),
|
|
1322
|
-
/* @__PURE__ */ (0,
|
|
1397
|
+
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1323
1398
|
ShareDestinationButton,
|
|
1324
1399
|
{
|
|
1325
1400
|
label: "Gmail",
|
|
1326
|
-
icon: /* @__PURE__ */ (0,
|
|
1401
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_si.SiGmail, { "aria-hidden": "true", className: "h-8 w-8 text-[#EA4335]" }),
|
|
1327
1402
|
href: destinationUrls?.gmail,
|
|
1328
1403
|
ariaLabel: `Email ${target.name} with Gmail`,
|
|
1329
1404
|
disabled: !absoluteUrl
|
|
1330
1405
|
}
|
|
1331
1406
|
),
|
|
1332
|
-
canUseNativeShare && /* @__PURE__ */ (0,
|
|
1407
|
+
canUseNativeShare && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
|
|
1333
1408
|
ShareDestinationButton,
|
|
1334
1409
|
{
|
|
1335
1410
|
label: "More",
|
|
1336
|
-
icon: /* @__PURE__ */ (0,
|
|
1411
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_lucide_react9.Share2, { "aria-hidden": "true", className: "h-6 w-6" }),
|
|
1337
1412
|
onClick: () => void handleNativeShare(),
|
|
1338
1413
|
ariaLabel: "More sharing options",
|
|
1339
1414
|
disabled: !absoluteUrl
|
|
1340
1415
|
}
|
|
1341
1416
|
)
|
|
1342
1417
|
] }),
|
|
1343
|
-
copyStatus === "failed" && /* @__PURE__ */ (0,
|
|
1344
|
-
shareError && /* @__PURE__ */ (0,
|
|
1418
|
+
copyStatus === "failed" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: "Could not copy the link." }),
|
|
1419
|
+
shareError && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)("p", { role: "alert", className: "text-center text-sm text-[var(--color-red-500)]", children: shareError })
|
|
1345
1420
|
] })
|
|
1346
1421
|
}
|
|
1347
1422
|
),
|
|
1348
|
-
toastId > 0 && /* @__PURE__ */ (0,
|
|
1423
|
+
toastId > 0 && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(Toast, { message: "Link copied", onClose: dismissToast }, toastId)
|
|
1349
1424
|
] });
|
|
1350
1425
|
}
|
|
1351
1426
|
|
|
1352
1427
|
// src/genre-tree/ShareActionButton.tsx
|
|
1353
|
-
var
|
|
1428
|
+
var import_jsx_runtime24 = require("react/jsx-runtime");
|
|
1354
1429
|
function ShareActionButton({ target, url }) {
|
|
1355
1430
|
const { showPopup, hidePopup } = (0, import_popup4.usePopup)();
|
|
1356
1431
|
const openSharePopup = () => {
|
|
1357
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1432
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime24.jsx)(SharePopup, { target, url, onClose: hidePopup }));
|
|
1358
1433
|
};
|
|
1359
|
-
return /* @__PURE__ */ (0,
|
|
1434
|
+
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(
|
|
1360
1435
|
"button",
|
|
1361
1436
|
{
|
|
1362
1437
|
type: "button",
|
|
@@ -1365,7 +1440,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1365
1440
|
"aria-label": `Share ${target.name}`,
|
|
1366
1441
|
title: `Share ${target.name}`,
|
|
1367
1442
|
children: [
|
|
1368
|
-
/* @__PURE__ */ (0,
|
|
1443
|
+
/* @__PURE__ */ (0, import_jsx_runtime24.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(import_lucide_react10.Share2, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
1369
1444
|
"Share"
|
|
1370
1445
|
]
|
|
1371
1446
|
}
|
|
@@ -1373,7 +1448,7 @@ function ShareActionButton({ target, url }) {
|
|
|
1373
1448
|
}
|
|
1374
1449
|
|
|
1375
1450
|
// src/genre-tree/GenreActionBand.tsx
|
|
1376
|
-
var
|
|
1451
|
+
var import_jsx_runtime25 = require("react/jsx-runtime");
|
|
1377
1452
|
function GenreActionBand({
|
|
1378
1453
|
node,
|
|
1379
1454
|
onPlayPause,
|
|
@@ -1395,7 +1470,7 @@ function GenreActionBand({
|
|
|
1395
1470
|
items.push({
|
|
1396
1471
|
key: "play-pause",
|
|
1397
1472
|
label: isPlaying ? "Pause" : "Play",
|
|
1398
|
-
icon: isPlaying ? /* @__PURE__ */ (0,
|
|
1473
|
+
icon: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Play, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1399
1474
|
onClick: onPlayPause
|
|
1400
1475
|
});
|
|
1401
1476
|
}
|
|
@@ -1403,24 +1478,24 @@ function GenreActionBand({
|
|
|
1403
1478
|
items.push({
|
|
1404
1479
|
key: "zoom-in",
|
|
1405
1480
|
label: "Focus",
|
|
1406
|
-
icon: /* @__PURE__ */ (0,
|
|
1481
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.ZoomIn, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1407
1482
|
onClick: onZoomIn
|
|
1408
1483
|
});
|
|
1409
1484
|
}
|
|
1410
1485
|
if (onAddChild) {
|
|
1411
|
-
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0,
|
|
1486
|
+
adminItems.push({ key: "add-child", label: "Add", icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Plus, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onAddChild });
|
|
1412
1487
|
}
|
|
1413
1488
|
if (onRename) {
|
|
1414
|
-
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0,
|
|
1489
|
+
adminItems.push({ key: "rename", label: "Rename", icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.Pencil, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onRename });
|
|
1415
1490
|
}
|
|
1416
1491
|
if (onChangeParent) {
|
|
1417
|
-
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0,
|
|
1492
|
+
adminItems.push({ key: "change-parent", label: "Parent", icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.MoveUpRight, { "aria-hidden": "true", className: "w-5 h-5" }), onClick: onChangeParent });
|
|
1418
1493
|
}
|
|
1419
1494
|
if (onToggleSide) {
|
|
1420
1495
|
adminItems.push({
|
|
1421
1496
|
key: "toggle-side",
|
|
1422
1497
|
label: side === "pop" ? "Make core" : "Make pop",
|
|
1423
|
-
icon: /* @__PURE__ */ (0,
|
|
1498
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_lucide_react11.ArrowLeftRight, { "aria-hidden": "true", className: "w-5 h-5" }),
|
|
1424
1499
|
onClick: onToggleSide,
|
|
1425
1500
|
disabled: !!toggleSideDisabledReason,
|
|
1426
1501
|
title: toggleSideDisabledReason
|
|
@@ -1436,7 +1511,7 @@ function GenreActionBand({
|
|
|
1436
1511
|
});
|
|
1437
1512
|
}
|
|
1438
1513
|
if (items.length === 0 && (!shareTarget || !shareUrl) && adminItems.length === 0) return null;
|
|
1439
|
-
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0,
|
|
1514
|
+
const renderItems = (list) => list.map((item) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1440
1515
|
"button",
|
|
1441
1516
|
{
|
|
1442
1517
|
type: "button",
|
|
@@ -1445,25 +1520,25 @@ function GenreActionBand({
|
|
|
1445
1520
|
onClick: item.onClick,
|
|
1446
1521
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none disabled:opacity-50",
|
|
1447
1522
|
children: [
|
|
1448
|
-
/* @__PURE__ */ (0,
|
|
1523
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)] group-disabled:group-hover:bg-[var(--color-surface-muted)]", children: item.icon }),
|
|
1449
1524
|
item.label
|
|
1450
1525
|
]
|
|
1451
1526
|
},
|
|
1452
1527
|
item.key
|
|
1453
1528
|
));
|
|
1454
|
-
return /* @__PURE__ */ (0,
|
|
1455
|
-
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0,
|
|
1529
|
+
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: [
|
|
1530
|
+
(items.length > 0 || shareTarget && shareUrl) && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
1456
1531
|
renderItems(items),
|
|
1457
|
-
shareTarget && shareUrl && /* @__PURE__ */ (0,
|
|
1532
|
+
shareTarget && shareUrl && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
1458
1533
|
] }),
|
|
1459
|
-
adminItems.length > 0 && /* @__PURE__ */ (0,
|
|
1534
|
+
adminItems.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
|
|
1460
1535
|
"section",
|
|
1461
1536
|
{
|
|
1462
1537
|
"aria-label": "Admin actions",
|
|
1463
1538
|
className: `px-3.5 py-3 ${items.length > 0 ? "border-t border-[var(--color-border)]" : ""}`,
|
|
1464
1539
|
children: [
|
|
1465
|
-
/* @__PURE__ */ (0,
|
|
1466
|
-
/* @__PURE__ */ (0,
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("h3", { className: "mb-2 text-xs font-semibold uppercase tracking-wide text-[var(--color-text-muted)]", children: "Admin" }),
|
|
1541
|
+
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)("div", { className: "flex flex-wrap gap-1", children: renderItems(adminItems) })
|
|
1467
1542
|
]
|
|
1468
1543
|
}
|
|
1469
1544
|
)
|
|
@@ -1471,12 +1546,12 @@ function GenreActionBand({
|
|
|
1471
1546
|
}
|
|
1472
1547
|
|
|
1473
1548
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1474
|
-
var
|
|
1475
|
-
var
|
|
1549
|
+
var import_ui9 = require("@behindthemusictree/ui");
|
|
1550
|
+
var import_genre_tree12 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1476
1551
|
|
|
1477
1552
|
// src/genre-tree/ClampedText.tsx
|
|
1478
1553
|
var import_react10 = require("react");
|
|
1479
|
-
var
|
|
1554
|
+
var import_jsx_runtime26 = require("react/jsx-runtime");
|
|
1480
1555
|
function ClampedText({ children, className: className2 = "" }) {
|
|
1481
1556
|
const ref = (0, import_react10.useRef)(null);
|
|
1482
1557
|
const [expanded, setExpanded] = (0, import_react10.useState)(false);
|
|
@@ -1486,9 +1561,9 @@ function ClampedText({ children, className: className2 = "" }) {
|
|
|
1486
1561
|
if (!element || expanded) return;
|
|
1487
1562
|
setOverflows(element.scrollHeight > element.clientHeight);
|
|
1488
1563
|
}, [children, expanded]);
|
|
1489
|
-
return /* @__PURE__ */ (0,
|
|
1490
|
-
/* @__PURE__ */ (0,
|
|
1491
|
-
(overflows || expanded) && /* @__PURE__ */ (0,
|
|
1564
|
+
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
|
|
1565
|
+
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)("p", { ref, className: `${className2} ${expanded ? "" : "line-clamp-3"}`, children }),
|
|
1566
|
+
(overflows || expanded) && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
|
|
1492
1567
|
"button",
|
|
1493
1568
|
{
|
|
1494
1569
|
type: "button",
|
|
@@ -1506,7 +1581,7 @@ var import_react11 = require("react");
|
|
|
1506
1581
|
var import_react_query = require("@tanstack/react-query");
|
|
1507
1582
|
var import_zod = require("zod");
|
|
1508
1583
|
var import_fa4 = require("react-icons/fa");
|
|
1509
|
-
var
|
|
1584
|
+
var import_ui8 = require("@behindthemusictree/ui");
|
|
1510
1585
|
var import_auth2 = require("@behindthemusictree/app-kit/auth");
|
|
1511
1586
|
var import_transport5 = require("@behindthemusictree/app-kit/transport");
|
|
1512
1587
|
var import_transport6 = require("@behindthemusictree/app-kit/transport");
|
|
@@ -1515,22 +1590,22 @@ var import_player4 = require("@behindthemusictree/app-kit/player");
|
|
|
1515
1590
|
var import_player5 = require("@behindthemusictree/app-kit/player");
|
|
1516
1591
|
var import_popup5 = require("@behindthemusictree/app-kit/popup");
|
|
1517
1592
|
var import_transport8 = require("@behindthemusictree/app-kit/transport");
|
|
1518
|
-
var import_genre_tree4 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1519
|
-
var import_genre_tree5 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1520
|
-
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1521
1593
|
var import_genre_tree7 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1594
|
+
var import_genre_tree8 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1595
|
+
var import_genre_tree9 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1596
|
+
var import_genre_tree10 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1522
1597
|
|
|
1523
1598
|
// src/genre-tree/MusicBrainzRecordingLink.tsx
|
|
1524
|
-
var
|
|
1525
|
-
var
|
|
1599
|
+
var import_genre_tree6 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1600
|
+
var import_jsx_runtime27 = require("react/jsx-runtime");
|
|
1526
1601
|
function MusicBrainzRecordingLink({
|
|
1527
1602
|
song,
|
|
1528
1603
|
children,
|
|
1529
1604
|
className: className2 = ""
|
|
1530
1605
|
}) {
|
|
1531
|
-
const url = (0,
|
|
1532
|
-
if (!url) return /* @__PURE__ */ (0,
|
|
1533
|
-
return /* @__PURE__ */ (0,
|
|
1606
|
+
const url = (0, import_genre_tree6.musicBrainzRecordingUrl)(song);
|
|
1607
|
+
if (!url) return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_jsx_runtime27.Fragment, { children });
|
|
1608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
|
|
1534
1609
|
"a",
|
|
1535
1610
|
{
|
|
1536
1611
|
href: url,
|
|
@@ -1545,11 +1620,11 @@ function MusicBrainzRecordingLink({
|
|
|
1545
1620
|
}
|
|
1546
1621
|
|
|
1547
1622
|
// src/genre-tree/GenrePlaylistTracks.tsx
|
|
1548
|
-
var
|
|
1623
|
+
var import_genre_tree11 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1549
1624
|
var import_track_noun2 = require("@behindthemusictree/app-kit/track-noun");
|
|
1550
|
-
var
|
|
1625
|
+
var import_jsx_runtime28 = require("react/jsx-runtime");
|
|
1551
1626
|
var CATALOGUE_PAGE_SIZE = 100;
|
|
1552
|
-
var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(
|
|
1627
|
+
var SongPageSchema = (0, import_transport6.PaginatedResponseSchema)(import_genre_tree9.SongDetailedSchema).extend({
|
|
1553
1628
|
overallTotal: import_zod.z.number().nullable(),
|
|
1554
1629
|
page: import_zod.z.number().nullable(),
|
|
1555
1630
|
totalPages: import_zod.z.number().nullable()
|
|
@@ -1576,7 +1651,7 @@ function GenrePlaylistTracks({
|
|
|
1576
1651
|
renderTracksToolbar,
|
|
1577
1652
|
artist = null
|
|
1578
1653
|
}) {
|
|
1579
|
-
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0,
|
|
1654
|
+
const { fetchGenrePlaylistTracksPage, playNewTrackListFromGenrePlaylist, playNewTrackListFromArtist } = (0, import_genre_tree10.useTrackList)();
|
|
1580
1655
|
const { session, sessionRestored } = (0, import_auth2.useSession)();
|
|
1581
1656
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player4.usePlayer)();
|
|
1582
1657
|
const { showPopup } = (0, import_popup5.usePopup)();
|
|
@@ -1585,11 +1660,11 @@ function GenrePlaylistTracks({
|
|
|
1585
1660
|
const { fetch } = (0, import_transport5.useFetchWrapper)(getBackendBaseUrl);
|
|
1586
1661
|
const playback = (0, import_transport5.usePlaybackContext)();
|
|
1587
1662
|
const { data, isPending, isError, refetch, hasNextPage, isFetchingNextPage, fetchNextPage } = (0, import_react_query.useInfiniteQuery)({
|
|
1588
|
-
queryKey: [...scope === "me" ?
|
|
1663
|
+
queryKey: [...scope === "me" ? import_genre_tree7.genrePlaylistQueryKeys.me.tracks(uuid) : artist ? [...import_genre_tree8.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), "artist", artist.uuid] : import_genre_tree8.libraryQueryKeys.reference.songs.byGenre(getBackendBaseUrl(), genreUuid), ...playback.key ? [playback.key] : []],
|
|
1589
1664
|
queryFn: async ({ pageParam }) => {
|
|
1590
1665
|
if (scope === "me") return fetchGenrePlaylistTracksPage(uuid, scope, Number(pageParam));
|
|
1591
1666
|
const response = await fetch(
|
|
1592
|
-
|
|
1667
|
+
import_genre_tree8.libraryEndpoints.reference.songs.list(),
|
|
1593
1668
|
true,
|
|
1594
1669
|
false,
|
|
1595
1670
|
{},
|
|
@@ -1620,35 +1695,35 @@ function GenrePlaylistTracks({
|
|
|
1620
1695
|
const started = artist ? playNewTrackListFromArtist(artist, scope, { startSongUuid, ordering: "title", genreUuid }) : playNewTrackListFromGenrePlaylist(genrePlaylist, scope, { startSongUuid });
|
|
1621
1696
|
started.catch((error) => {
|
|
1622
1697
|
console.error("Failed to play genre playlist:", error);
|
|
1623
|
-
showPopup(/* @__PURE__ */ (0,
|
|
1698
|
+
showPopup(/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(InternalErrorPopup, { errorCode: import_transport8.ErrorCode.CLIENT_UNKNOWN }));
|
|
1624
1699
|
});
|
|
1625
1700
|
};
|
|
1626
|
-
return /* @__PURE__ */ (0,
|
|
1701
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.PanelSection, { title: `${(0, import_track_noun2.capitalize)(trackNoun.plural)}${total !== void 0 ? ` (${total})` : ""}`, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("div", { children: [
|
|
1627
1702
|
renderTracksToolbar?.(),
|
|
1628
|
-
isPending ? /* @__PURE__ */ (0,
|
|
1703
|
+
isPending ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.Skeleton, { className: "h-4 w-3/4", "aria-busy": "true" }) : isError && tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("p", { role: "alert", children: [
|
|
1629
1704
|
"Couldn't load ",
|
|
1630
1705
|
trackNoun.plural,
|
|
1631
1706
|
".",
|
|
1632
1707
|
" ",
|
|
1633
|
-
/* @__PURE__ */ (0,
|
|
1634
|
-
] }) : tracks.length === 0 ? /* @__PURE__ */ (0,
|
|
1708
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { type: "button", className: "underline", onClick: () => void refetch(), children: "Retry" })
|
|
1709
|
+
] }) : tracks.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("p", { children: "\u2014" }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("ul", { className: "mt-1 max-h-64 overflow-y-auto text-[0.8125rem] leading-[1.45] [container-type:inline-size]", "aria-label": `${genrePlaylist.name} ${trackNoun.plural}`, children: [
|
|
1635
1710
|
tracks.map((track, index) => {
|
|
1636
1711
|
const label = track.artists?.length ? `${track.title} \u2014 ${track.artists.map((artist2) => artist2.name).join(", ")}` : track.title;
|
|
1637
1712
|
const isCurrent = playerTrackObject?.track.id === track.uuid;
|
|
1638
1713
|
const isPlaying = isCurrent && playState === import_player5.PlayStates.PLAYING;
|
|
1639
|
-
const text = /* @__PURE__ */ (0,
|
|
1640
|
-
/* @__PURE__ */ (0,
|
|
1641
|
-
track.artists?.length ? /* @__PURE__ */ (0,
|
|
1642
|
-
/* @__PURE__ */ (0,
|
|
1643
|
-
track.album && /* @__PURE__ */ (0,
|
|
1714
|
+
const text = /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("span", { className: TEXT_CELL, children: [
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: TITLE_CELL, children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree9.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }),
|
|
1716
|
+
track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: SEPARATOR, children: " \u2014 " }) : null,
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: COLUMN_CELL, children: track.artists?.length ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_genre_tree11.ArtistNames, { artists: track.artists, renderArtistLink }) : null }),
|
|
1718
|
+
track.album && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("span", { className: ALBUM_CELL, children: track.album.name })
|
|
1644
1719
|
] });
|
|
1645
1720
|
const leading = renderSongLeading?.(track, index);
|
|
1646
1721
|
const actions = renderSongActions?.(track);
|
|
1647
|
-
const isUnplayable = (0,
|
|
1722
|
+
const isUnplayable = (0, import_genre_tree9.isSong)(track) && (0, import_genre_tree9.songUnplayableReason)(track) !== null;
|
|
1648
1723
|
if (isUnplayable) {
|
|
1649
|
-
return /* @__PURE__ */ (0,
|
|
1724
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)("li", { className: "flex min-h-8 items-center gap-1 px-1 opacity-50", children: [
|
|
1650
1725
|
leading,
|
|
1651
|
-
/* @__PURE__ */ (0,
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1652
1727
|
"button",
|
|
1653
1728
|
{
|
|
1654
1729
|
type: "button",
|
|
@@ -1656,27 +1731,27 @@ function GenrePlaylistTracks({
|
|
|
1656
1731
|
"aria-disabled": "true",
|
|
1657
1732
|
"aria-label": label,
|
|
1658
1733
|
className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-full opacity-0 cursor-not-allowed",
|
|
1659
|
-
children: /* @__PURE__ */ (0,
|
|
1734
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1660
1735
|
}
|
|
1661
1736
|
),
|
|
1662
1737
|
text,
|
|
1663
1738
|
actions
|
|
1664
1739
|
] }, track.uuid);
|
|
1665
1740
|
}
|
|
1666
|
-
return /* @__PURE__ */ (0,
|
|
1741
|
+
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(
|
|
1667
1742
|
"li",
|
|
1668
1743
|
{
|
|
1669
1744
|
className: `group flex min-h-8 items-center gap-1 rounded px-1 hover:bg-[var(--color-neutral-100)] ${isCurrent ? "font-medium text-[var(--color-primary)]" : ""}`,
|
|
1670
1745
|
children: [
|
|
1671
1746
|
leading,
|
|
1672
|
-
/* @__PURE__ */ (0,
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
|
|
1673
1748
|
"button",
|
|
1674
1749
|
{
|
|
1675
1750
|
type: "button",
|
|
1676
1751
|
"aria-label": `${isPlaying ? "Pause" : "Play"} ${label}`,
|
|
1677
1752
|
className: `flex h-6 w-6 shrink-0 items-center justify-center rounded-full cursor-pointer group-hover:opacity-100 focus-visible:opacity-100 ${isCurrent && playState !== import_player5.PlayStates.STOPPED ? "opacity-100" : "opacity-0 [@media(pointer:coarse)]:opacity-100"}`,
|
|
1678
1753
|
onClick: () => isCurrent ? handlePlayPauseAction() : play(track.uuid),
|
|
1679
|
-
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0,
|
|
1754
|
+
children: isCurrent && playState === import_player5.PlayStates.LOADING ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_ui8.RingLoader, { size: 10 }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa4.FaPause, { size: 10 }) : /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_fa4.FaPlay, { size: 10 })
|
|
1680
1755
|
}
|
|
1681
1756
|
),
|
|
1682
1757
|
text,
|
|
@@ -1686,7 +1761,7 @@ function GenrePlaylistTracks({
|
|
|
1686
1761
|
track.uuid
|
|
1687
1762
|
);
|
|
1688
1763
|
}),
|
|
1689
|
-
hasNextPage && /* @__PURE__ */ (0,
|
|
1764
|
+
hasNextPage && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("li", { ref: setSentinel, "aria-hidden": "true", className: "h-4" })
|
|
1690
1765
|
] })
|
|
1691
1766
|
] }) });
|
|
1692
1767
|
}
|
|
@@ -1694,7 +1769,7 @@ function GenrePlaylistTracks({
|
|
|
1694
1769
|
// src/genre-tree/GenreDetailExtras.tsx
|
|
1695
1770
|
var import_track_noun3 = require("@behindthemusictree/app-kit/track-noun");
|
|
1696
1771
|
var import_si2 = require("react-icons/si");
|
|
1697
|
-
var
|
|
1772
|
+
var import_jsx_runtime29 = require("react/jsx-runtime");
|
|
1698
1773
|
function GenreDetailExtras({
|
|
1699
1774
|
genreUuid,
|
|
1700
1775
|
genrePlaylist,
|
|
@@ -1711,24 +1786,24 @@ function GenreDetailExtras({
|
|
|
1711
1786
|
...trackSlots
|
|
1712
1787
|
}) {
|
|
1713
1788
|
const trackNoun = (0, import_track_noun3.useTrackNoun)();
|
|
1714
|
-
const { data: overview, isPending } = (0,
|
|
1789
|
+
const { data: overview, isPending } = (0, import_genre_tree12.useFetchGenreOverview)(
|
|
1715
1790
|
genreUuid,
|
|
1716
1791
|
scope,
|
|
1717
1792
|
getBackendBaseUrl,
|
|
1718
1793
|
criteriaOverviewSchema
|
|
1719
1794
|
);
|
|
1720
1795
|
if (isPending) {
|
|
1721
|
-
return /* @__PURE__ */ (0,
|
|
1722
|
-
/* @__PURE__ */ (0,
|
|
1796
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { "aria-busy": "true", "data-testid": "genre-detail-extras-skeleton", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.Skeleton, { className: "h-4 w-3/4" }) }) }),
|
|
1723
1798
|
family
|
|
1724
1799
|
] });
|
|
1725
1800
|
}
|
|
1726
|
-
if (!overview) return /* @__PURE__ */ (0,
|
|
1801
|
+
if (!overview) return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_jsx_runtime29.Fragment, { children: family });
|
|
1727
1802
|
const { summary, summarySourceUrl, essentialSongs } = overview;
|
|
1728
1803
|
const wikidataUrl = wikidataId ? `https://www.wikidata.org/wiki/${encodeURIComponent(wikidataId)}` : null;
|
|
1729
|
-
return /* @__PURE__ */ (0,
|
|
1730
|
-
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0,
|
|
1731
|
-
summarySourceUrl && /* @__PURE__ */ (0,
|
|
1804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_jsx_runtime29.Fragment, { children: [
|
|
1805
|
+
(summarySourceUrl || wikidataUrl) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Links", children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: "flex flex-wrap gap-x-3 gap-y-1 text-xs text-[var(--color-text)]", children: [
|
|
1806
|
+
summarySourceUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1732
1807
|
"a",
|
|
1733
1808
|
{
|
|
1734
1809
|
href: summarySourceUrl,
|
|
@@ -1736,12 +1811,12 @@ function GenreDetailExtras({
|
|
|
1736
1811
|
rel: "noopener noreferrer",
|
|
1737
1812
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1738
1813
|
children: [
|
|
1739
|
-
/* @__PURE__ */ (0,
|
|
1814
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikipedia, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1740
1815
|
"Wikipedia"
|
|
1741
1816
|
]
|
|
1742
1817
|
}
|
|
1743
1818
|
),
|
|
1744
|
-
wikidataUrl && /* @__PURE__ */ (0,
|
|
1819
|
+
wikidataUrl && /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(
|
|
1745
1820
|
"a",
|
|
1746
1821
|
{
|
|
1747
1822
|
href: wikidataUrl,
|
|
@@ -1749,17 +1824,17 @@ function GenreDetailExtras({
|
|
|
1749
1824
|
rel: "noopener noreferrer",
|
|
1750
1825
|
className: "inline-flex items-center gap-1.5 text-[var(--color-text-muted)] hover:text-[var(--color-text)]",
|
|
1751
1826
|
children: [
|
|
1752
|
-
/* @__PURE__ */ (0,
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_si2.SiWikidata, { "aria-hidden": "true", className: "h-3.5 w-3.5" }),
|
|
1753
1828
|
"Wikidata"
|
|
1754
1829
|
]
|
|
1755
1830
|
}
|
|
1756
1831
|
)
|
|
1757
1832
|
] }) }),
|
|
1758
|
-
/* @__PURE__ */ (0,
|
|
1833
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: "Summary", children: renderSummary ? renderSummary(overview) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(ClampedText, { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: summary ?? "\u2014" }, genreUuid) }),
|
|
1759
1834
|
family,
|
|
1760
1835
|
renderGenreDetailExtras?.(overview),
|
|
1761
|
-
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0,
|
|
1762
|
-
/* @__PURE__ */ (0,
|
|
1836
|
+
(renderEssentialTracks || essentialSongs) && /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_ui9.PanelSection, { title: `Essential ${trackNoun.plural}`, children: renderEssentialTracks ? renderEssentialTracks(overview) : essentialSongs && essentialSongs.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("ul", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: essentialSongs.map((track) => /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("li", { className: "py-1", children: track.title }, track.uuid)) }) : /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("p", { className: "text-[0.8125rem] leading-[1.45] text-[var(--color-text)]", children: "\u2014" }) }),
|
|
1837
|
+
/* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
|
|
1763
1838
|
GenrePlaylistTracks,
|
|
1764
1839
|
{
|
|
1765
1840
|
genreUuid,
|
|
@@ -1775,7 +1850,7 @@ function GenreDetailExtras({
|
|
|
1775
1850
|
}
|
|
1776
1851
|
|
|
1777
1852
|
// src/genre-tree/GenreInfoPanelBody.tsx
|
|
1778
|
-
var
|
|
1853
|
+
var import_jsx_runtime30 = require("react/jsx-runtime");
|
|
1779
1854
|
var import_react12 = require("react");
|
|
1780
1855
|
function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
1781
1856
|
const { isPlaying, setIsPlaying } = (0, import_player6.usePlayer)();
|
|
@@ -1789,7 +1864,7 @@ function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }) {
|
|
|
1789
1864
|
console.error("Failed to load genre playlist tracks:", error);
|
|
1790
1865
|
});
|
|
1791
1866
|
};
|
|
1792
|
-
return /* @__PURE__ */ (0,
|
|
1867
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
|
|
1793
1868
|
GenreActionBand,
|
|
1794
1869
|
{
|
|
1795
1870
|
...bandProps,
|
|
@@ -1810,9 +1885,9 @@ function GenreInfoPanelBody({
|
|
|
1810
1885
|
band,
|
|
1811
1886
|
extras
|
|
1812
1887
|
}) {
|
|
1813
|
-
return /* @__PURE__ */ (0,
|
|
1888
|
+
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(import_jsx_runtime30.Fragment, { children: [
|
|
1814
1889
|
toolbar,
|
|
1815
|
-
/* @__PURE__ */ (0,
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(GenreNodeActionBand, { ...band, genrePlaylist, playback }),
|
|
1816
1891
|
/* @__PURE__ */ (0, import_react12.createElement)(
|
|
1817
1892
|
GenreDetailExtras,
|
|
1818
1893
|
{
|
|
@@ -1831,8 +1906,8 @@ function GenreInfoPanelBody({
|
|
|
1831
1906
|
|
|
1832
1907
|
// src/genre-tree/ChangeParentPopup.tsx
|
|
1833
1908
|
var import_react13 = require("react");
|
|
1834
|
-
var
|
|
1835
|
-
var
|
|
1909
|
+
var import_genre_tree13 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1910
|
+
var import_jsx_runtime31 = require("react/jsx-runtime");
|
|
1836
1911
|
function ChangeParentPopup({
|
|
1837
1912
|
genrePlaylist,
|
|
1838
1913
|
genrePlaylists,
|
|
@@ -1841,9 +1916,9 @@ function ChangeParentPopup({
|
|
|
1841
1916
|
onClose,
|
|
1842
1917
|
onParentChanged
|
|
1843
1918
|
}) {
|
|
1844
|
-
const { mutate, isPending, formErrors } = (0,
|
|
1919
|
+
const { mutate, isPending, formErrors } = (0, import_genre_tree13.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
1845
1920
|
const candidates = (0, import_react13.useMemo)(
|
|
1846
|
-
() => (0,
|
|
1921
|
+
() => (0, import_genre_tree13.getReparentCandidates)(genrePlaylists, genrePlaylist.uuid),
|
|
1847
1922
|
[genrePlaylists, genrePlaylist.uuid]
|
|
1848
1923
|
);
|
|
1849
1924
|
const handleSelect = (newParent) => {
|
|
@@ -1867,12 +1942,12 @@ function ChangeParentPopup({
|
|
|
1867
1942
|
window.addEventListener("keydown", handleKeyDown);
|
|
1868
1943
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
1869
1944
|
}, [onClose]);
|
|
1870
|
-
return /* @__PURE__ */ (0,
|
|
1871
|
-
/* @__PURE__ */ (0,
|
|
1945
|
+
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(BasePopup_default, { title: `Change parent of ${genrePlaylist.name}`, onClose, showCancelButton: true, fitContent: true, children: [
|
|
1946
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsxs)("p", { className: "mb-3 text-sm text-[var(--color-text-muted)]", children: [
|
|
1872
1947
|
"Current parent: ",
|
|
1873
1948
|
genrePlaylist.parent?.name ?? "none"
|
|
1874
1949
|
] }),
|
|
1875
|
-
/* @__PURE__ */ (0,
|
|
1950
|
+
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)("div", { "aria-busy": isPending, className: isPending ? "pointer-events-none opacity-60" : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
|
|
1876
1951
|
GenreSearch,
|
|
1877
1952
|
{
|
|
1878
1953
|
genrePlaylists: candidates,
|
|
@@ -1880,22 +1955,22 @@ function ChangeParentPopup({
|
|
|
1880
1955
|
placeholder: "Search a new parent\u2026"
|
|
1881
1956
|
}
|
|
1882
1957
|
) }),
|
|
1883
|
-
formErrors.length > 0 && /* @__PURE__ */ (0,
|
|
1958
|
+
formErrors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime31.jsx)("p", { role: "alert", className: "mt-3 text-sm text-[var(--color-red-500)]", children: formErrors.map(({ message }) => message).join(" ") })
|
|
1884
1959
|
] });
|
|
1885
1960
|
}
|
|
1886
1961
|
|
|
1887
1962
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
1888
|
-
var
|
|
1889
|
-
var
|
|
1963
|
+
var import_ui15 = require("@behindthemusictree/ui");
|
|
1964
|
+
var import_genre_tree18 = require("@behindthemusictree/app-kit/genre-tree");
|
|
1890
1965
|
|
|
1891
1966
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1892
|
-
var
|
|
1967
|
+
var import_ui11 = require("@behindthemusictree/ui");
|
|
1893
1968
|
|
|
1894
1969
|
// src/genre-tree/artist/ArtistLinks.tsx
|
|
1895
1970
|
var import_lucide_react12 = require("lucide-react");
|
|
1896
1971
|
var import_si3 = require("react-icons/si");
|
|
1897
|
-
var
|
|
1898
|
-
var
|
|
1972
|
+
var import_ui10 = require("@behindthemusictree/ui");
|
|
1973
|
+
var import_jsx_runtime32 = require("react/jsx-runtime");
|
|
1899
1974
|
var LINK_KINDS = {
|
|
1900
1975
|
official: { label: "Official website", Icon: import_lucide_react12.Globe },
|
|
1901
1976
|
wikipedia: { label: "Wikipedia", Icon: import_si3.SiWikipedia },
|
|
@@ -1932,7 +2007,7 @@ function artistLinks(artist) {
|
|
|
1932
2007
|
return links;
|
|
1933
2008
|
}
|
|
1934
2009
|
function ArtistLinks({ links }) {
|
|
1935
|
-
return /* @__PURE__ */ (0,
|
|
2010
|
+
return /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("ul", { className: "flex flex-wrap gap-1.5", children: links.map(({ key, url, Icon, label }) => /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("li", { children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.Chip, { href: url, icon: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }), children: label }) }, key)) });
|
|
1936
2011
|
}
|
|
1937
2012
|
|
|
1938
2013
|
// src/genre-tree/artist/formatYears.ts
|
|
@@ -1943,7 +2018,7 @@ function formatYears(beginYear, endYear, ended) {
|
|
|
1943
2018
|
}
|
|
1944
2019
|
|
|
1945
2020
|
// src/genre-tree/artist/ArtistHeader.tsx
|
|
1946
|
-
var
|
|
2021
|
+
var import_jsx_runtime33 = require("react/jsx-runtime");
|
|
1947
2022
|
var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
|
|
1948
2023
|
function ArtistHeader({ artist }) {
|
|
1949
2024
|
const fields = [];
|
|
@@ -1953,35 +2028,35 @@ function ArtistHeader({ artist }) {
|
|
|
1953
2028
|
const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
|
|
1954
2029
|
if (years) fields.push({ label: "Active", value: years });
|
|
1955
2030
|
const links = artistLinks(artist);
|
|
1956
|
-
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0,
|
|
2031
|
+
if (links.length > 0) fields.push({ label: "Links", value: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(ArtistLinks, { links }) });
|
|
1957
2032
|
if (fields.length === 0) return null;
|
|
1958
|
-
return /* @__PURE__ */ (0,
|
|
2033
|
+
return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_ui11.FieldList, { items: fields });
|
|
1959
2034
|
}
|
|
1960
2035
|
|
|
1961
2036
|
// src/genre-tree/artist/ArtistPhoto.tsx
|
|
1962
|
-
var
|
|
2037
|
+
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
1963
2038
|
function ExternalLink({ href, children }) {
|
|
1964
|
-
return /* @__PURE__ */ (0,
|
|
2039
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)("a", { href, target: "_blank", rel: "noopener noreferrer", className: "underline-offset-2 hover:underline", children });
|
|
1965
2040
|
}
|
|
1966
2041
|
function ArtistPhoto({ image, name }) {
|
|
1967
|
-
return /* @__PURE__ */ (0,
|
|
1968
|
-
/* @__PURE__ */ (0,
|
|
1969
|
-
/* @__PURE__ */ (0,
|
|
2042
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("figure", { className: "flex flex-col gap-1.5 px-6 py-2.5", children: [
|
|
2043
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("img", { src: image.url, alt: name, className: "h-64 w-full object-contain bg-[var(--color-surface-muted)] rounded" }),
|
|
2044
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("figcaption", { className: "text-xs text-[var(--color-text-muted)]", children: [
|
|
1970
2045
|
"Photo: ",
|
|
1971
|
-
/* @__PURE__ */ (0,
|
|
2046
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.pageUrl, children: image.author ?? "Wikimedia Commons" }),
|
|
1972
2047
|
",",
|
|
1973
2048
|
" ",
|
|
1974
|
-
image.licenseUrl ? /* @__PURE__ */ (0,
|
|
2049
|
+
image.licenseUrl ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(ExternalLink, { href: image.licenseUrl, children: image.license }) : image.license
|
|
1975
2050
|
] })
|
|
1976
2051
|
] });
|
|
1977
2052
|
}
|
|
1978
2053
|
|
|
1979
2054
|
// src/genre-tree/artist/ArtistMembers.tsx
|
|
1980
|
-
var
|
|
1981
|
-
var
|
|
2055
|
+
var import_ui12 = require("@behindthemusictree/ui");
|
|
2056
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
1982
2057
|
function MembershipList({ title, memberships, renderArtistLink }) {
|
|
1983
2058
|
if (memberships.length === 0) return null;
|
|
1984
|
-
return /* @__PURE__ */ (0,
|
|
2059
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_ui12.PanelGroup, { title, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("ul", { children: memberships.map((membership, index) => {
|
|
1985
2060
|
const details = [
|
|
1986
2061
|
formatYears(membership.beginYear, membership.endYear, membership.ended),
|
|
1987
2062
|
...membership.roles
|
|
@@ -1989,9 +2064,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
1989
2064
|
const detailsText = details.filter(Boolean).join(" \xB7 ");
|
|
1990
2065
|
return (
|
|
1991
2066
|
// A member can have several periods in the same group.
|
|
1992
|
-
/* @__PURE__ */ (0,
|
|
1993
|
-
/* @__PURE__ */ (0,
|
|
1994
|
-
detailsText && /* @__PURE__ */ (0,
|
|
2067
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("li", { className: "truncate py-0.5", title: detailsText || void 0, children: [
|
|
2068
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "font-medium text-[var(--color-text)]", children: renderArtistLink(membership.artist) }),
|
|
2069
|
+
detailsText && /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
1995
2070
|
" \u2014 ",
|
|
1996
2071
|
detailsText
|
|
1997
2072
|
] })
|
|
@@ -2002,9 +2077,9 @@ function MembershipList({ title, memberships, renderArtistLink }) {
|
|
|
2002
2077
|
function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
2003
2078
|
if (memberships.length === 0) return null;
|
|
2004
2079
|
const isCurrent = (membership) => !membership.ended && membership.endYear === null;
|
|
2005
|
-
return /* @__PURE__ */ (0,
|
|
2006
|
-
/* @__PURE__ */ (0,
|
|
2007
|
-
/* @__PURE__ */ (0,
|
|
2080
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(import_ui12.PanelSection, { title, children: [
|
|
2081
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(MembershipList, { title: "Current", memberships: memberships.filter(isCurrent), renderArtistLink }),
|
|
2082
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2008
2083
|
MembershipList,
|
|
2009
2084
|
{
|
|
2010
2085
|
title: "Past",
|
|
@@ -2016,8 +2091,8 @@ function ArtistMembers({ title, memberships, renderArtistLink }) {
|
|
|
2016
2091
|
}
|
|
2017
2092
|
|
|
2018
2093
|
// src/genre-tree/artist/ArtistDiscography.tsx
|
|
2019
|
-
var
|
|
2020
|
-
var
|
|
2094
|
+
var import_ui13 = require("@behindthemusictree/ui");
|
|
2095
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2021
2096
|
var GROUPS = ["Albums", "EPs", "Singles", "Live", "Compilations", "Other"];
|
|
2022
2097
|
var PRIMARY_GROUPS = { album: "Albums", ep: "EPs", single: "Singles" };
|
|
2023
2098
|
function discographyGroup({ primaryType, secondaryTypes }) {
|
|
@@ -2037,12 +2112,12 @@ function byYear(a, b) {
|
|
|
2037
2112
|
function ArtistDiscography({ albums }) {
|
|
2038
2113
|
if (albums.length === 0) return null;
|
|
2039
2114
|
const sorted = [...albums].sort(byYear);
|
|
2040
|
-
return /* @__PURE__ */ (0,
|
|
2115
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelSection, { title: "Discography", children: GROUPS.map((group) => {
|
|
2041
2116
|
const groupAlbums = sorted.filter((album) => discographyGroup(album) === group);
|
|
2042
2117
|
if (groupAlbums.length === 0) return null;
|
|
2043
|
-
return /* @__PURE__ */ (0,
|
|
2044
|
-
/* @__PURE__ */ (0,
|
|
2045
|
-
/* @__PURE__ */ (0,
|
|
2118
|
+
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_ui13.PanelGroup, { title: group, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("ul", { children: groupAlbums.map((album) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("li", { className: "grid grid-cols-[2.5rem_minmax(0,1fr)] gap-2 py-0.5", children: [
|
|
2119
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "tabular-nums text-[var(--color-text-muted)]", children: album.year ?? "\u2014" }),
|
|
2120
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)("span", { className: "truncate text-[var(--color-text)]", title: album.name, children: album.name })
|
|
2046
2121
|
] }, album.uuid)) }) }, group);
|
|
2047
2122
|
}) });
|
|
2048
2123
|
}
|
|
@@ -2051,20 +2126,20 @@ function ArtistDiscography({ albums }) {
|
|
|
2051
2126
|
var import_react14 = require("react");
|
|
2052
2127
|
var import_react_query2 = require("@tanstack/react-query");
|
|
2053
2128
|
var import_lucide_react13 = require("lucide-react");
|
|
2054
|
-
var
|
|
2129
|
+
var import_ui14 = require("@behindthemusictree/ui");
|
|
2055
2130
|
var import_player10 = require("@behindthemusictree/app-kit/player");
|
|
2056
|
-
var
|
|
2131
|
+
var import_genre_tree17 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2057
2132
|
var import_transport9 = require("@behindthemusictree/app-kit/transport");
|
|
2058
2133
|
|
|
2059
2134
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2060
2135
|
var import_player9 = require("@behindthemusictree/app-kit/player");
|
|
2061
|
-
var
|
|
2136
|
+
var import_genre_tree14 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2062
2137
|
|
|
2063
2138
|
// src/genre-tree/TrackPositionPlayPause.tsx
|
|
2064
2139
|
var import_fa5 = require("react-icons/fa");
|
|
2065
2140
|
var import_player7 = require("@behindthemusictree/app-kit/player");
|
|
2066
2141
|
var import_player8 = require("@behindthemusictree/app-kit/player");
|
|
2067
|
-
var
|
|
2142
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2068
2143
|
function TrackPositionPlayPause({
|
|
2069
2144
|
position,
|
|
2070
2145
|
uuid,
|
|
@@ -2078,14 +2153,14 @@ function TrackPositionPlayPause({
|
|
|
2078
2153
|
const isPlaying = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PLAYING;
|
|
2079
2154
|
const isPaused = isThisTrack && isConfirmedPlayable && playState === import_player8.PlayStates.PAUSED;
|
|
2080
2155
|
const isCurrentTrackActive = isPlaying || isPaused;
|
|
2081
|
-
return /* @__PURE__ */ (0,
|
|
2156
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2082
2157
|
"div",
|
|
2083
2158
|
{
|
|
2084
2159
|
className: "track-position-play-pause relative flex items-center justify-center gap-2 text-lg w-16 cursor-pointer",
|
|
2085
2160
|
style: { minWidth: "4rem", maxWidth: "4rem" },
|
|
2086
2161
|
onClick: handlePlayPauseClick,
|
|
2087
2162
|
children: [
|
|
2088
|
-
/* @__PURE__ */ (0,
|
|
2163
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("div", { className: "track-position-play-pause__status group-hover:hidden flex items-center justify-center", children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__spinner", role: "status", "aria-label": "Loading track" }) : isCurrentTrackActive ? /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2089
2164
|
"span",
|
|
2090
2165
|
{
|
|
2091
2166
|
className: "track-position-play-pause__bars",
|
|
@@ -2093,18 +2168,18 @@ function TrackPositionPlayPause({
|
|
|
2093
2168
|
role: "img",
|
|
2094
2169
|
"aria-label": isPlaying ? "Playing" : "Paused",
|
|
2095
2170
|
children: [
|
|
2096
|
-
/* @__PURE__ */ (0,
|
|
2097
|
-
/* @__PURE__ */ (0,
|
|
2098
|
-
/* @__PURE__ */ (0,
|
|
2171
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2172
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" }),
|
|
2173
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__bar" })
|
|
2099
2174
|
]
|
|
2100
2175
|
}
|
|
2101
2176
|
) : position }),
|
|
2102
|
-
/* @__PURE__ */ (0,
|
|
2177
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2103
2178
|
"div",
|
|
2104
2179
|
{
|
|
2105
2180
|
className: `hidden group-hover:flex items-center justify-center ${isCurrentTrackActive ? "" : "absolute inset-0"}`,
|
|
2106
2181
|
"aria-hidden": "true",
|
|
2107
|
-
children: isAwaitingPlayback ? /* @__PURE__ */ (0,
|
|
2182
|
+
children: isAwaitingPlayback ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "track-position-play-pause__spinner" }) : isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fa5.FaPause, {}) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_fa5.FaPlay, {})
|
|
2108
2183
|
}
|
|
2109
2184
|
)
|
|
2110
2185
|
]
|
|
@@ -2113,9 +2188,9 @@ function TrackPositionPlayPause({
|
|
|
2113
2188
|
}
|
|
2114
2189
|
|
|
2115
2190
|
// src/genre-tree/track-list-sidebar/TrackItem.tsx
|
|
2116
|
-
var
|
|
2117
|
-
var
|
|
2118
|
-
var
|
|
2191
|
+
var import_genre_tree15 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2192
|
+
var import_genre_tree16 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2193
|
+
var import_jsx_runtime38 = require("react/jsx-runtime");
|
|
2119
2194
|
function TrackItem({
|
|
2120
2195
|
track,
|
|
2121
2196
|
position,
|
|
@@ -2125,11 +2200,11 @@ function TrackItem({
|
|
|
2125
2200
|
renderSongLink
|
|
2126
2201
|
}) {
|
|
2127
2202
|
const { handlePlayPauseAction, playerTrackObject, loadTrackForPlayer } = (0, import_player9.usePlayer)();
|
|
2128
|
-
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0,
|
|
2203
|
+
const { trackList, selectedTrack, toTrackAtPosition, runtimeUnplayableTrackIds } = (0, import_genre_tree14.useTrackList)();
|
|
2129
2204
|
const scope = trackList?.origin?.scope ?? null;
|
|
2130
2205
|
const isCurrentTrack = playerTrackObject?.track.id === track.uuid;
|
|
2131
2206
|
const hasPlaybackError = isCurrentTrack && !!playerTrackObject?.loadError;
|
|
2132
|
-
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0,
|
|
2207
|
+
const isUnplayable = hasPlaybackError || runtimeUnplayableTrackIds?.has(track.uuid) === true || (0, import_genre_tree15.isSong)(track) && (0, import_genre_tree15.songUnplayableReason)(track) !== null;
|
|
2133
2208
|
const handlePlayPauseClick = (event) => {
|
|
2134
2209
|
event.stopPropagation();
|
|
2135
2210
|
if (isCurrentTrack) {
|
|
@@ -2139,12 +2214,12 @@ function TrackItem({
|
|
|
2139
2214
|
loadTrackForPlayer(track.uuid);
|
|
2140
2215
|
}
|
|
2141
2216
|
};
|
|
2142
|
-
return /* @__PURE__ */ (0,
|
|
2217
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
2143
2218
|
"div",
|
|
2144
2219
|
{
|
|
2145
2220
|
className: `track-item flex h-14 text-[var(--color-text)] ${isUnplayable ? "opacity-50" : "hover:bg-[var(--color-surface-muted)] group"}`,
|
|
2146
2221
|
children: [
|
|
2147
|
-
isUnplayable ? /* @__PURE__ */ (0,
|
|
2222
|
+
isUnplayable ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2148
2223
|
"div",
|
|
2149
2224
|
{
|
|
2150
2225
|
className: "flex items-center justify-center text-lg w-16",
|
|
@@ -2152,7 +2227,7 @@ function TrackItem({
|
|
|
2152
2227
|
"aria-disabled": "true",
|
|
2153
2228
|
children: position
|
|
2154
2229
|
}
|
|
2155
|
-
) : /* @__PURE__ */ (0,
|
|
2230
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2156
2231
|
TrackPositionPlayPause,
|
|
2157
2232
|
{
|
|
2158
2233
|
position,
|
|
@@ -2161,12 +2236,12 @@ function TrackItem({
|
|
|
2161
2236
|
isSelected: selectedTrack?.uuid === track.uuid
|
|
2162
2237
|
}
|
|
2163
2238
|
),
|
|
2164
|
-
/* @__PURE__ */ (0,
|
|
2165
|
-
/* @__PURE__ */ (0,
|
|
2166
|
-
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0,
|
|
2239
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "title-artist-container flex flex-col items-start justify-center min-w-0 flex-1", children: [
|
|
2240
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "flex max-w-full items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "title text-lg font-bold text-[var(--color-text)] max-w-full truncate", children: renderSongLink ? renderSongLink(track) : (0, import_genre_tree15.isSong)(track) ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MusicBrainzRecordingLink, { song: track, className: "hover:underline focus-visible:underline", children: track.title }) : track.title }) }),
|
|
2241
|
+
track.artists && track.artists.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "artist text-base max-w-full truncate text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_genre_tree16.ArtistNames, { artists: track.artists, renderArtistLink }) }) : ""
|
|
2167
2242
|
] }),
|
|
2168
|
-
/* @__PURE__ */ (0,
|
|
2169
|
-
renderDuration && /* @__PURE__ */ (0,
|
|
2243
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "genre-name-container flex items-center justify-start w-[21.6%] min-w-0 shrink-0", children: track.genre ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "genre-name max-w-full whitespace-normal break-words p-1 text-xs font-bold", children: track.genre.name }) : "" }),
|
|
2244
|
+
renderDuration && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
2170
2245
|
"div",
|
|
2171
2246
|
{
|
|
2172
2247
|
className: "duration flex text-base w-16 items-center justify-center",
|
|
@@ -2174,15 +2249,15 @@ function TrackItem({
|
|
|
2174
2249
|
children: renderDuration(track)
|
|
2175
2250
|
}
|
|
2176
2251
|
),
|
|
2177
|
-
renderActions && /* @__PURE__ */ (0,
|
|
2252
|
+
renderActions && /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "edit flex text-base w-6 items-center justify-center mr-2 cursor-pointer", children: renderActions(track) })
|
|
2178
2253
|
]
|
|
2179
2254
|
}
|
|
2180
2255
|
);
|
|
2181
2256
|
}
|
|
2182
2257
|
|
|
2183
2258
|
// src/genre-tree/artist/ArtistSongs.tsx
|
|
2184
|
-
var
|
|
2185
|
-
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(
|
|
2259
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
2260
|
+
var SongPageSchema2 = (0, import_transport9.PaginatedResponseSchema)(import_genre_tree17.SongDetailedSchema);
|
|
2186
2261
|
var SORTS = [
|
|
2187
2262
|
{ ordering: "title", label: "Title" },
|
|
2188
2263
|
{ ordering: "-albumYear", label: "Year" }
|
|
@@ -2191,11 +2266,11 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2191
2266
|
const [page, setPage] = (0, import_react14.useState)(1);
|
|
2192
2267
|
const [ordering, setOrdering] = (0, import_react14.useState)("title");
|
|
2193
2268
|
const { fetch } = (0, import_transport9.useFetchWrapper)(getBackendBaseUrl);
|
|
2194
|
-
const { playNewTrackListFromArtist } = (0,
|
|
2269
|
+
const { playNewTrackListFromArtist } = (0, import_genre_tree17.useTrackList)();
|
|
2195
2270
|
const { playerTrackObject } = (0, import_player10.usePlayer)();
|
|
2196
2271
|
const { data, isPending, isError } = (0, import_transport9.useQueryWithParse)({
|
|
2197
2272
|
queryKey: ["referenceSongs", "artist", artist.uuid, ordering, page],
|
|
2198
|
-
queryFn: () => fetch(
|
|
2273
|
+
queryFn: () => fetch(import_genre_tree17.libraryEndpoints.reference.songs.list(), true, false, void 0, {
|
|
2199
2274
|
artist: artist.uuid,
|
|
2200
2275
|
ordering,
|
|
2201
2276
|
page
|
|
@@ -2210,25 +2285,25 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2210
2285
|
});
|
|
2211
2286
|
const handleRowClickCapture = (event, song) => {
|
|
2212
2287
|
if (!(event.target instanceof Element) || !event.target.closest(".track-position-play-pause")) return;
|
|
2213
|
-
if (playerTrackObject?.track.id === song.uuid || (0,
|
|
2288
|
+
if (playerTrackObject?.track.id === song.uuid || (0, import_genre_tree17.songUnplayableReason)(song) !== null) return;
|
|
2214
2289
|
event.stopPropagation();
|
|
2215
2290
|
void play(song.uuid);
|
|
2216
2291
|
};
|
|
2217
|
-
return /* @__PURE__ */ (0,
|
|
2218
|
-
/* @__PURE__ */ (0,
|
|
2219
|
-
/* @__PURE__ */ (0,
|
|
2292
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.PanelSection, { title: "Songs", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.Skeleton, { className: "h-14", "aria-busy": "true" }) : isError ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-red-600", children: "Could not load songs." }) : data.results.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No songs yet." }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
|
|
2293
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-wrap items-center justify-between gap-2 mb-2", children: [
|
|
2294
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
|
|
2220
2295
|
"button",
|
|
2221
2296
|
{
|
|
2222
2297
|
type: "button",
|
|
2223
2298
|
onClick: () => void play(),
|
|
2224
2299
|
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",
|
|
2225
2300
|
children: [
|
|
2226
|
-
/* @__PURE__ */ (0,
|
|
2301
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_lucide_react13.Play, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
2227
2302
|
"Play all"
|
|
2228
2303
|
]
|
|
2229
2304
|
}
|
|
2230
2305
|
),
|
|
2231
|
-
/* @__PURE__ */ (0,
|
|
2306
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("div", { role: "group", "aria-label": "Sort songs by", className: "flex gap-1 text-xs", children: SORTS.map((sort) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2232
2307
|
"button",
|
|
2233
2308
|
{
|
|
2234
2309
|
type: "button",
|
|
@@ -2243,7 +2318,7 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2243
2318
|
sort.ordering
|
|
2244
2319
|
)) })
|
|
2245
2320
|
] }),
|
|
2246
|
-
/* @__PURE__ */ (0,
|
|
2321
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)("ul", { className: "overflow-hidden mb-2 bg-zinc-950 rounded-lg", children: data.results.map((song, index) => /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("li", { onClickCapture: (event) => handleRowClickCapture(event, song), children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
|
|
2247
2322
|
TrackItem,
|
|
2248
2323
|
{
|
|
2249
2324
|
track: song,
|
|
@@ -2252,12 +2327,12 @@ function ArtistSongs({ artist, getBackendBaseUrl, renderArtistLink, renderSongLi
|
|
|
2252
2327
|
renderSongLink
|
|
2253
2328
|
}
|
|
2254
2329
|
) }, song.uuid)) }),
|
|
2255
|
-
/* @__PURE__ */ (0,
|
|
2330
|
+
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_ui14.Pagination, { currentPage: page, totalPages: data.totalPages, onPageChange: setPage })
|
|
2256
2331
|
] }) });
|
|
2257
2332
|
}
|
|
2258
2333
|
|
|
2259
2334
|
// src/genre-tree/artist/useArtistPanel.tsx
|
|
2260
|
-
var
|
|
2335
|
+
var import_jsx_runtime40 = require("react/jsx-runtime");
|
|
2261
2336
|
function useArtistPanel({
|
|
2262
2337
|
uuid,
|
|
2263
2338
|
scope,
|
|
@@ -2269,7 +2344,7 @@ function useArtistPanel({
|
|
|
2269
2344
|
onExpandedChange,
|
|
2270
2345
|
onClose
|
|
2271
2346
|
}) {
|
|
2272
|
-
const result = (0,
|
|
2347
|
+
const result = (0, import_genre_tree18.useArtist)(uuid, getBackendBaseUrl);
|
|
2273
2348
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2274
2349
|
switch (result.status) {
|
|
2275
2350
|
case "idle":
|
|
@@ -2278,26 +2353,26 @@ function useArtistPanel({
|
|
|
2278
2353
|
return {
|
|
2279
2354
|
...panel,
|
|
2280
2355
|
title: "Artist",
|
|
2281
|
-
content: /* @__PURE__ */ (0,
|
|
2282
|
-
/* @__PURE__ */ (0,
|
|
2283
|
-
/* @__PURE__ */ (0,
|
|
2284
|
-
/* @__PURE__ */ (0,
|
|
2285
|
-
/* @__PURE__ */ (0,
|
|
2286
|
-
/* @__PURE__ */ (0,
|
|
2287
|
-
/* @__PURE__ */ (0,
|
|
2356
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2357
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2358
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-4 w-32" }),
|
|
2359
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-3 w-16" }),
|
|
2360
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-4 w-24" }),
|
|
2361
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-16 mt-2.5" }),
|
|
2362
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_ui15.Skeleton, { className: "h-16" })
|
|
2288
2363
|
] })
|
|
2289
2364
|
};
|
|
2290
2365
|
case "not-found":
|
|
2291
2366
|
return {
|
|
2292
2367
|
...panel,
|
|
2293
2368
|
title: "Artist not found",
|
|
2294
|
-
content: /* @__PURE__ */ (0,
|
|
2369
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This artist does not exist." })
|
|
2295
2370
|
};
|
|
2296
2371
|
case "error":
|
|
2297
2372
|
return {
|
|
2298
2373
|
...panel,
|
|
2299
2374
|
title: "Artist",
|
|
2300
|
-
content: /* @__PURE__ */ (0,
|
|
2375
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this artist." })
|
|
2301
2376
|
};
|
|
2302
2377
|
case "success": {
|
|
2303
2378
|
const { artist } = result;
|
|
@@ -2306,13 +2381,13 @@ function useArtistPanel({
|
|
|
2306
2381
|
return {
|
|
2307
2382
|
...panel,
|
|
2308
2383
|
title: artist.name,
|
|
2309
|
-
content: /* @__PURE__ */ (0,
|
|
2310
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2311
|
-
artist.image && /* @__PURE__ */ (0,
|
|
2312
|
-
/* @__PURE__ */ (0,
|
|
2313
|
-
/* @__PURE__ */ (0,
|
|
2314
|
-
/* @__PURE__ */ (0,
|
|
2315
|
-
artist.tracksCount > 0 && /* @__PURE__ */ (0,
|
|
2384
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_jsx_runtime40.Fragment, { children: [
|
|
2385
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: shareTarget && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl }) }) }),
|
|
2386
|
+
artist.image && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
|
|
2387
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistHeader, { artist }),
|
|
2388
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
|
|
2389
|
+
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
|
|
2390
|
+
artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
|
|
2316
2391
|
ArtistSongs,
|
|
2317
2392
|
{
|
|
2318
2393
|
artist: { uuid: artist.uuid, name: artist.name },
|
|
@@ -2322,56 +2397,56 @@ function useArtistPanel({
|
|
|
2322
2397
|
}
|
|
2323
2398
|
)
|
|
2324
2399
|
] }),
|
|
2325
|
-
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0,
|
|
2400
|
+
extraContent: artist.albums.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(ArtistDiscography, { albums: artist.albums }) : void 0
|
|
2326
2401
|
};
|
|
2327
2402
|
}
|
|
2328
2403
|
}
|
|
2329
2404
|
}
|
|
2330
2405
|
|
|
2331
2406
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2332
|
-
var
|
|
2333
|
-
var
|
|
2407
|
+
var import_ui17 = require("@behindthemusictree/ui");
|
|
2408
|
+
var import_genre_tree20 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2334
2409
|
|
|
2335
2410
|
// src/genre-tree/song/SongPanelContent.tsx
|
|
2336
2411
|
var import_react15 = require("react");
|
|
2337
2412
|
var import_lucide_react14 = require("lucide-react");
|
|
2338
|
-
var
|
|
2413
|
+
var import_ui16 = require("@behindthemusictree/ui");
|
|
2339
2414
|
var import_player11 = require("@behindthemusictree/app-kit/player");
|
|
2340
|
-
var
|
|
2341
|
-
var
|
|
2415
|
+
var import_genre_tree19 = require("@behindthemusictree/app-kit/genre-tree");
|
|
2416
|
+
var import_jsx_runtime41 = require("react/jsx-runtime");
|
|
2342
2417
|
var numberFormat = new Intl.NumberFormat("en");
|
|
2343
2418
|
var dateFormat = new Intl.DateTimeFormat("en", { dateStyle: "medium" });
|
|
2344
2419
|
var externalLink = "text-[var(--color-primary)] hover:underline focus-visible:underline";
|
|
2345
2420
|
function SongHistory({ uuid, getBackendBaseUrl }) {
|
|
2346
2421
|
const [open, setOpen] = (0, import_react15.useState)(false);
|
|
2347
|
-
const history = (0,
|
|
2422
|
+
const history = (0, import_genre_tree19.useSongHistory)(uuid, getBackendBaseUrl, open);
|
|
2348
2423
|
let body = null;
|
|
2349
|
-
if (history.status === "loading") body = /* @__PURE__ */ (0,
|
|
2350
|
-
else if (history.status === "error") body = /* @__PURE__ */ (0,
|
|
2424
|
+
if (history.status === "loading") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.Skeleton, { className: "h-10", "aria-busy": "true" });
|
|
2425
|
+
else if (history.status === "error") body = /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-red-600", children: "Could not load the history." });
|
|
2351
2426
|
else if (history.status === "success")
|
|
2352
|
-
body = history.entries.length === 0 ? /* @__PURE__ */ (0,
|
|
2353
|
-
/* @__PURE__ */ (0,
|
|
2354
|
-
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0,
|
|
2427
|
+
body = history.entries.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "text-[var(--color-text-muted)]", children: "No changes yet." }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { className: "divide-y divide-[var(--color-border)]", children: history.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("li", { className: "py-1.5 text-sm", children: [
|
|
2428
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "font-medium", children: entry.action }),
|
|
2429
|
+
entry.oldValue !== null || entry.newValue !== null ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "text-[var(--color-text-muted)]", children: [
|
|
2355
2430
|
" ",
|
|
2356
2431
|
"\u2014 ",
|
|
2357
2432
|
entry.oldValue ?? "none",
|
|
2358
2433
|
" \u2192 ",
|
|
2359
2434
|
entry.newValue ?? "none"
|
|
2360
2435
|
] }) : null,
|
|
2361
|
-
/* @__PURE__ */ (0,
|
|
2436
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "block text-xs text-[var(--color-text-muted)]", children: [
|
|
2362
2437
|
entry.actorPseudo ?? "Anonymous",
|
|
2363
2438
|
" \xB7 ",
|
|
2364
2439
|
dateFormat.format(new Date(entry.createdOn))
|
|
2365
2440
|
] })
|
|
2366
2441
|
] }, entry.uuid)) });
|
|
2367
|
-
return /* @__PURE__ */ (0,
|
|
2442
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2368
2443
|
"details",
|
|
2369
2444
|
{
|
|
2370
2445
|
onToggle: (event) => setOpen(event.currentTarget.open),
|
|
2371
2446
|
className: "px-6 pb-2.5 mt-2.5 border-t border-[var(--color-border)]",
|
|
2372
2447
|
children: [
|
|
2373
|
-
/* @__PURE__ */ (0,
|
|
2374
|
-
/* @__PURE__ */ (0,
|
|
2448
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("summary", { className: "mt-2.5 cursor-pointer text-[0.6875rem] font-semibold uppercase text-[var(--color-text-muted)]", children: "History" }),
|
|
2449
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "mt-1.5", children: body })
|
|
2375
2450
|
]
|
|
2376
2451
|
}
|
|
2377
2452
|
);
|
|
@@ -2385,25 +2460,25 @@ function SongPanelContent({
|
|
|
2385
2460
|
genrePath,
|
|
2386
2461
|
onSelectGenre
|
|
2387
2462
|
}) {
|
|
2388
|
-
const { playNewTrackListFromTrackUuid } = (0,
|
|
2463
|
+
const { playNewTrackListFromTrackUuid } = (0, import_genre_tree19.useTrackList)();
|
|
2389
2464
|
const { playerTrackObject, playState, handlePlayPauseAction } = (0, import_player11.usePlayer)();
|
|
2390
|
-
const unplayableReason = (0,
|
|
2465
|
+
const unplayableReason = (0, import_genre_tree19.songUnplayableReason)(song);
|
|
2391
2466
|
const isCurrent = playerTrackObject?.track.id === song.uuid;
|
|
2392
2467
|
const isPlaying = isCurrent && playState === import_player11.PlayStates.PLAYING;
|
|
2393
2468
|
const shareTarget = { kind: "song", uuid: song.uuid, name: song.title, scope };
|
|
2394
|
-
const musicBrainzUrl = (0,
|
|
2469
|
+
const musicBrainzUrl = (0, import_genre_tree19.musicBrainzRecordingUrl)(song);
|
|
2395
2470
|
const fields = [];
|
|
2396
2471
|
if (song.genre) {
|
|
2397
2472
|
const path = genrePath.length > 0 ? genrePath : null;
|
|
2398
2473
|
fields.push({
|
|
2399
2474
|
label: "Genre",
|
|
2400
|
-
value: path ? /* @__PURE__ */ (0,
|
|
2401
|
-
index > 0 && /* @__PURE__ */ (0,
|
|
2402
|
-
/* @__PURE__ */ (0,
|
|
2475
|
+
value: path ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex flex-wrap items-center gap-x-1", children: path.map((genrePlaylist, index) => /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { children: [
|
|
2476
|
+
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { "aria-hidden": "true", children: "\u203A " }),
|
|
2477
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("button", { type: "button", onClick: () => onSelectGenre(genrePlaylist), className: "hover:underline", children: genrePlaylist.name })
|
|
2403
2478
|
] }, genrePlaylist.uuid)) }) : song.genre.name
|
|
2404
2479
|
});
|
|
2405
2480
|
} else {
|
|
2406
|
-
fields.push({ label: "Genre", value: /* @__PURE__ */ (0,
|
|
2481
|
+
fields.push({ label: "Genre", value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "text-[var(--color-text-muted)]", children: "No genre" }) });
|
|
2407
2482
|
}
|
|
2408
2483
|
if (song.firstReleaseYear) fields.push({ label: "First released", value: song.firstReleaseYear });
|
|
2409
2484
|
const viewCount = song.youtubeVideo?.viewCount;
|
|
@@ -2413,11 +2488,11 @@ function SongPanelContent({
|
|
|
2413
2488
|
const links = [];
|
|
2414
2489
|
if (musicBrainzUrl)
|
|
2415
2490
|
links.push(
|
|
2416
|
-
/* @__PURE__ */ (0,
|
|
2491
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("a", { href: musicBrainzUrl, target: "_blank", rel: "noopener noreferrer", className: externalLink, children: "MusicBrainz" }, "mb")
|
|
2417
2492
|
);
|
|
2418
2493
|
if (song.youtubeVideo)
|
|
2419
2494
|
links.push(
|
|
2420
|
-
/* @__PURE__ */ (0,
|
|
2495
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
|
|
2421
2496
|
"a",
|
|
2422
2497
|
{
|
|
2423
2498
|
href: `https://www.youtube.com/watch?v=${encodeURIComponent(song.youtubeVideo.id)}`,
|
|
@@ -2432,33 +2507,33 @@ function SongPanelContent({
|
|
|
2432
2507
|
if (links.length > 0)
|
|
2433
2508
|
fields.push({
|
|
2434
2509
|
label: "Links",
|
|
2435
|
-
value: /* @__PURE__ */ (0,
|
|
2510
|
+
value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex gap-3", children: links })
|
|
2436
2511
|
});
|
|
2437
|
-
return /* @__PURE__ */ (0,
|
|
2438
|
-
/* @__PURE__ */ (0,
|
|
2439
|
-
unplayableReason ? /* @__PURE__ */ (0,
|
|
2512
|
+
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
|
|
2513
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("div", { className: "genre-action-band border-b border-[var(--color-border)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("div", { className: "flex flex-wrap gap-1 px-3.5 py-3", children: [
|
|
2514
|
+
unplayableReason ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "flex items-center px-2.5 text-sm text-[var(--color-text-muted)]", children: unplayableReason }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
|
|
2440
2515
|
"button",
|
|
2441
2516
|
{
|
|
2442
2517
|
type: "button",
|
|
2443
2518
|
onClick: () => isCurrent ? handlePlayPauseAction() : playNewTrackListFromTrackUuid(song, scope),
|
|
2444
2519
|
className: "group flex flex-col items-center gap-1 w-16 text-xs text-center text-[var(--color-text)] focus-visible:outline-none",
|
|
2445
2520
|
children: [
|
|
2446
|
-
/* @__PURE__ */ (0,
|
|
2521
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "flex items-center justify-center w-10 h-10 rounded-full group-focus-visible:ring-2 group-focus-visible:ring-[var(--color-focus-ring)] bg-[var(--color-surface-muted)] transition-colors group-hover:bg-[var(--color-border)]", children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.Pause, { "aria-hidden": "true", className: "w-5 h-5" }) : /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_lucide_react14.Play, { "aria-hidden": "true", className: "w-5 h-5" }) }),
|
|
2447
2522
|
isPlaying ? "Pause" : "Play"
|
|
2448
2523
|
]
|
|
2449
2524
|
}
|
|
2450
2525
|
),
|
|
2451
|
-
shareUrl && /* @__PURE__ */ (0,
|
|
2526
|
+
shareUrl && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(ShareActionButton, { target: shareTarget, url: shareUrl })
|
|
2452
2527
|
] }) }),
|
|
2453
|
-
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0,
|
|
2454
|
-
/* @__PURE__ */ (0,
|
|
2455
|
-
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0,
|
|
2456
|
-
/* @__PURE__ */ (0,
|
|
2528
|
+
song.artists && song.artists.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("p", { className: "px-6 pt-2.5 text-[var(--color-text-muted)]", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_genre_tree19.ArtistNames, { artists: song.artists, renderArtistLink }) }),
|
|
2529
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.FieldList, { items: fields }),
|
|
2530
|
+
song.essentialGenres && song.essentialGenres.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_ui16.PanelSection, { title: "Essential in", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("ul", { children: song.essentialGenres.map((genre) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("li", { className: "py-0.5", children: genre.name }, genre.uuid)) }) }),
|
|
2531
|
+
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(SongHistory, { uuid: song.uuid, getBackendBaseUrl })
|
|
2457
2532
|
] });
|
|
2458
2533
|
}
|
|
2459
2534
|
|
|
2460
2535
|
// src/genre-tree/song/useSongPanel.tsx
|
|
2461
|
-
var
|
|
2536
|
+
var import_jsx_runtime42 = require("react/jsx-runtime");
|
|
2462
2537
|
function useSongPanel({
|
|
2463
2538
|
uuid,
|
|
2464
2539
|
scope,
|
|
@@ -2471,7 +2546,7 @@ function useSongPanel({
|
|
|
2471
2546
|
onExpandedChange,
|
|
2472
2547
|
onClose
|
|
2473
2548
|
}) {
|
|
2474
|
-
const result = (0,
|
|
2549
|
+
const result = (0, import_genre_tree20.useSong)(uuid, getBackendBaseUrl);
|
|
2475
2550
|
const panel = { expanded, onExpandedChange, onClose };
|
|
2476
2551
|
switch (result.status) {
|
|
2477
2552
|
case "idle":
|
|
@@ -2480,24 +2555,24 @@ function useSongPanel({
|
|
|
2480
2555
|
return {
|
|
2481
2556
|
...panel,
|
|
2482
2557
|
title: "Song",
|
|
2483
|
-
content: /* @__PURE__ */ (0,
|
|
2484
|
-
/* @__PURE__ */ (0,
|
|
2485
|
-
/* @__PURE__ */ (0,
|
|
2486
|
-
/* @__PURE__ */ (0,
|
|
2487
|
-
/* @__PURE__ */ (0,
|
|
2558
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "aria-busy": "true", className: "flex flex-col gap-2.5 px-6 py-2.5", children: [
|
|
2559
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2560
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-32" }),
|
|
2561
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-3 w-16" }),
|
|
2562
|
+
/* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_ui17.Skeleton, { className: "h-4 w-24" })
|
|
2488
2563
|
] })
|
|
2489
2564
|
};
|
|
2490
2565
|
case "not-found":
|
|
2491
2566
|
return {
|
|
2492
2567
|
...panel,
|
|
2493
2568
|
title: "Song not found",
|
|
2494
|
-
content: /* @__PURE__ */ (0,
|
|
2569
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 py-2.5 text-[var(--color-text-muted)]", children: "This song does not exist." })
|
|
2495
2570
|
};
|
|
2496
2571
|
case "error":
|
|
2497
2572
|
return {
|
|
2498
2573
|
...panel,
|
|
2499
2574
|
title: "Song",
|
|
2500
|
-
content: /* @__PURE__ */ (0,
|
|
2575
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("p", { className: "px-6 py-2.5 text-red-600", children: "Could not load this song." })
|
|
2501
2576
|
};
|
|
2502
2577
|
case "success": {
|
|
2503
2578
|
const { song } = result;
|
|
@@ -2505,7 +2580,7 @@ function useSongPanel({
|
|
|
2505
2580
|
return {
|
|
2506
2581
|
...panel,
|
|
2507
2582
|
title: song.title,
|
|
2508
|
-
content: /* @__PURE__ */ (0,
|
|
2583
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
|
|
2509
2584
|
SongPanelContent,
|
|
2510
2585
|
{
|
|
2511
2586
|
song,
|
|
@@ -2524,29 +2599,29 @@ function useSongPanel({
|
|
|
2524
2599
|
|
|
2525
2600
|
// src/genre-tree/SizeMetricToggle.tsx
|
|
2526
2601
|
var import_md2 = require("react-icons/md");
|
|
2527
|
-
var
|
|
2528
|
-
var
|
|
2602
|
+
var import_ui18 = require("@behindthemusictree/ui");
|
|
2603
|
+
var import_jsx_runtime43 = require("react/jsx-runtime");
|
|
2529
2604
|
function SizeMetricToggle({
|
|
2530
2605
|
value,
|
|
2531
2606
|
onChange
|
|
2532
2607
|
}) {
|
|
2533
|
-
return /* @__PURE__ */ (0,
|
|
2534
|
-
|
|
2608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
|
|
2609
|
+
import_ui18.SegmentedControl,
|
|
2535
2610
|
{
|
|
2536
2611
|
variant: "floating",
|
|
2537
2612
|
"aria-label": "Size nodes by",
|
|
2538
2613
|
value,
|
|
2539
2614
|
onChange,
|
|
2540
2615
|
options: [
|
|
2541
|
-
{ value: "songs", label: /* @__PURE__ */ (0,
|
|
2542
|
-
{ value: "views", label: /* @__PURE__ */ (0,
|
|
2616
|
+
{ value: "songs", label: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_md2.MdMusicNote, { size: 18, "aria-hidden": true }), "aria-label": "Size by songs", title: "Node size by song count" },
|
|
2617
|
+
{ value: "views", label: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_md2.MdVisibility, { size: 18, "aria-hidden": true }), "aria-label": "Size by views", title: "Node size by song views" }
|
|
2543
2618
|
]
|
|
2544
2619
|
}
|
|
2545
2620
|
);
|
|
2546
2621
|
}
|
|
2547
2622
|
|
|
2548
2623
|
// src/genre-tree/GenreTreeView.tsx
|
|
2549
|
-
var
|
|
2624
|
+
var import_jsx_runtime44 = require("react/jsx-runtime");
|
|
2550
2625
|
var NO_POP_ROOT_TITLE = "This genre tree has no 'Mainstream Pop' root yet";
|
|
2551
2626
|
var REGIONS_CLASS = "[--gtv-center-left:var(--layout-center-left)] [--gtv-center-right:var(--layout-center-right)] [--gtv-center-top:var(--layout-band-top)]";
|
|
2552
2627
|
var noop = () => {
|
|
@@ -2580,6 +2655,9 @@ function GenreTreeView({
|
|
|
2580
2655
|
artistExpanded: controlledArtistExpanded,
|
|
2581
2656
|
onArtistExpandedChange,
|
|
2582
2657
|
getShareUrl,
|
|
2658
|
+
extraSearchGroups = [],
|
|
2659
|
+
onSearchQueryChange,
|
|
2660
|
+
onSearchFocus,
|
|
2583
2661
|
artistSearch = true,
|
|
2584
2662
|
onArtistSelect,
|
|
2585
2663
|
artistSearchBaseUrl,
|
|
@@ -2645,7 +2723,7 @@ function GenreTreeView({
|
|
|
2645
2723
|
allowWheelRotation: internalAllowWheelRotation,
|
|
2646
2724
|
toggleWheelRotation,
|
|
2647
2725
|
onNodeHover: handleNodeHover
|
|
2648
|
-
} = (0,
|
|
2726
|
+
} = (0, import_genre_tree21.useGenreTreeViewState)({
|
|
2649
2727
|
scope,
|
|
2650
2728
|
getBackendBaseUrl,
|
|
2651
2729
|
viewMode: controlledViewMode,
|
|
@@ -2664,12 +2742,12 @@ function GenreTreeView({
|
|
|
2664
2742
|
restoreRootUuid = selectedGenrePlaylist.root.uuid;
|
|
2665
2743
|
setStackedRestoreOwner({ revision: restoreRevision, rootUuid: restoreRootUuid });
|
|
2666
2744
|
}
|
|
2667
|
-
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey:
|
|
2668
|
-
const { mutate: updateGenre } = (0,
|
|
2745
|
+
const isGenreUpdating = (0, import_react_query3.useIsMutating)({ mutationKey: import_genre_tree21.genreUpdateMutationKey }) > 0;
|
|
2746
|
+
const { mutate: updateGenre } = (0, import_genre_tree21.useUpdateGenre)(scope, getBackendBaseUrl);
|
|
2669
2747
|
const busy = deferredSizeMetric !== sizeMetric || isTreeFetching || isGenreUpdating;
|
|
2670
2748
|
const isArtistFiltered = artistUuid !== null;
|
|
2671
2749
|
const canSizeByViews = !isArtistFiltered && hasViewsCount(visibleGenrePlaylists);
|
|
2672
|
-
const artistResult = (0,
|
|
2750
|
+
const artistResult = (0, import_genre_tree21.useArtist)(artistUuid, artistSearchBaseUrl ?? getBackendBaseUrl);
|
|
2673
2751
|
const filterArtistName = artistResult.status === "success" ? artistResult.artist.name : null;
|
|
2674
2752
|
const filterArtist = (0, import_react16.useMemo)(
|
|
2675
2753
|
() => artistUuid !== null && filterArtistName !== null ? { uuid: artistUuid, name: filterArtistName } : null,
|
|
@@ -2689,7 +2767,7 @@ function GenreTreeView({
|
|
|
2689
2767
|
const openSongRef = (0, import_react16.useRef)(openSong);
|
|
2690
2768
|
openSongRef.current = openSong;
|
|
2691
2769
|
const resolvedRenderSongLink = (0, import_react16.useCallback)(
|
|
2692
|
-
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0,
|
|
2770
|
+
(song) => renderSongLink ? renderSongLink(song) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openSongRef.current(song.uuid), className: "hover:underline", children: song.title }),
|
|
2693
2771
|
[renderSongLink]
|
|
2694
2772
|
);
|
|
2695
2773
|
const [expandedSongUuid, setExpandedSongUuid] = (0, import_react16.useState)(null);
|
|
@@ -2701,7 +2779,7 @@ function GenreTreeView({
|
|
|
2701
2779
|
scope,
|
|
2702
2780
|
getBackendBaseUrl: artistBaseUrl,
|
|
2703
2781
|
getShareUrl: getShareUrlRef.current,
|
|
2704
|
-
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2782
|
+
renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name })),
|
|
2705
2783
|
expanded: controlledArtistExpanded ?? (artistUuid !== null && expandedArtistUuid === artistUuid),
|
|
2706
2784
|
onExpandedChange: (on) => {
|
|
2707
2785
|
if (controlledArtistExpanded === void 0) setExpandedArtistUuid(on ? artistUuid : null);
|
|
@@ -2710,7 +2788,7 @@ function GenreTreeView({
|
|
|
2710
2788
|
renderSongLink: resolvedRenderSongLink,
|
|
2711
2789
|
onClose: () => changeArtist(null)
|
|
2712
2790
|
});
|
|
2713
|
-
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0,
|
|
2791
|
+
const resolvedRenderArtistLink = renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("button", { type: "button", onClick: () => openArtist(artist.uuid), className: "hover:underline", children: artist.name }));
|
|
2714
2792
|
const getGenrePath = (genre) => {
|
|
2715
2793
|
const path = [];
|
|
2716
2794
|
let current = visibleGenrePlaylists.find((playlist) => playlist.criteria?.uuid === genre.uuid);
|
|
@@ -2766,7 +2844,7 @@ function GenreTreeView({
|
|
|
2766
2844
|
[]
|
|
2767
2845
|
);
|
|
2768
2846
|
const panelRef = (0, import_react16.useRef)(null);
|
|
2769
|
-
const obscuredInset = (0,
|
|
2847
|
+
const obscuredInset = (0, import_ui19.useObscuredInset)(panelRef);
|
|
2770
2848
|
const hostPanel = { obscuredInset, onNodeStyleResolverChange: handleNodeStyleResolverChange };
|
|
2771
2849
|
const nodeMetric = isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric;
|
|
2772
2850
|
const treeNodes = (0, import_react16.useMemo)(() => toTreeNodes(visibleGenrePlaylists, nodeMetric), [visibleGenrePlaylists, nodeMetric]);
|
|
@@ -2789,52 +2867,27 @@ function GenreTreeView({
|
|
|
2789
2867
|
const handlePrevious = previousPlaylist && visibleGenrePlaylists.includes(previousPlaylist) ? () => handleGenreSearchSelect(previousPlaylist) : void 0;
|
|
2790
2868
|
const { showPopup, hidePopup } = (0, import_popup6.usePopup)();
|
|
2791
2869
|
const [searchText, setSearchText] = (0, import_react16.useState)("");
|
|
2792
|
-
const
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
onArtistSelect?.(artist);
|
|
2814
|
-
}
|
|
2815
|
-
})),
|
|
2816
|
-
status: artistSearchResult.isError ? "Search failed" : artistSearchResult.settled && artistSearchResult.artists.length === 0 ? "No artists found" : "",
|
|
2817
|
-
loading: artistSearchResult.pending
|
|
2818
|
-
} : void 0;
|
|
2819
|
-
const songSearchResult = (0, import_genre_tree18.useSongSearch)(songBaseUrl, songSearch ? searchText : "");
|
|
2820
|
-
const songGroup = songSearch ? {
|
|
2821
|
-
key: "songs",
|
|
2822
|
-
label: "Songs",
|
|
2823
|
-
items: songSearchResult.songs.map((song) => ({
|
|
2824
|
-
key: song.uuid,
|
|
2825
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)("span", { className: (0, import_genre_tree18.isSongSearchResultPlayable)(song) ? void 0 : "opacity-60", children: [
|
|
2826
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "block truncate font-medium", children: song.title }),
|
|
2827
|
-
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)("span", { className: "block truncate text-xs text-[var(--color-neutral-500)]", children: [song.artists.map((artist) => artist.name).join(", "), song.genre?.name ?? "No genre"].filter(Boolean).join(" \xB7 ") })
|
|
2828
|
-
] }),
|
|
2829
|
-
onSelect: () => {
|
|
2830
|
-
openSong(song.uuid);
|
|
2831
|
-
onSongSelect?.(song);
|
|
2832
|
-
}
|
|
2833
|
-
})),
|
|
2834
|
-
status: songSearchResult.isError ? "Search failed" : songSearchResult.settled && songSearchResult.songs.length === 0 ? "No songs found" : "",
|
|
2835
|
-
loading: songSearchResult.pending
|
|
2836
|
-
} : void 0;
|
|
2837
|
-
const extraGroups = [artistGroup, songGroup].filter((group) => group !== void 0);
|
|
2870
|
+
const artistGroup = useArtistSearchGroup(
|
|
2871
|
+
artistBaseUrl,
|
|
2872
|
+
searchText,
|
|
2873
|
+
(artist) => {
|
|
2874
|
+
openArtist(artist.uuid);
|
|
2875
|
+
onArtistSelect?.(artist);
|
|
2876
|
+
},
|
|
2877
|
+
Boolean(artistSearch)
|
|
2878
|
+
);
|
|
2879
|
+
const songGroup = useSongSearchGroup(
|
|
2880
|
+
songBaseUrl,
|
|
2881
|
+
searchText,
|
|
2882
|
+
(song) => {
|
|
2883
|
+
openSong(song.uuid);
|
|
2884
|
+
onSongSelect?.(song);
|
|
2885
|
+
},
|
|
2886
|
+
Boolean(songSearch)
|
|
2887
|
+
);
|
|
2888
|
+
const extraGroups = [...extraSearchGroups, artistGroup, songGroup].filter(
|
|
2889
|
+
(group) => group !== void 0
|
|
2890
|
+
);
|
|
2838
2891
|
const handleSelectedNodeChange = (node) => {
|
|
2839
2892
|
if (node !== null && songUuid !== null) changeSong(null);
|
|
2840
2893
|
selectNode(node);
|
|
@@ -2858,7 +2911,7 @@ function GenreTreeView({
|
|
|
2858
2911
|
};
|
|
2859
2912
|
const renderGenreBody = (relation) => {
|
|
2860
2913
|
const { node } = relation;
|
|
2861
|
-
const family = /* @__PURE__ */ (0,
|
|
2914
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2862
2915
|
import_genre_tree_view4.GenreNodeFamily,
|
|
2863
2916
|
{
|
|
2864
2917
|
node,
|
|
@@ -2869,10 +2922,10 @@ function GenreTreeView({
|
|
|
2869
2922
|
onSelectNode: selectGenreFromPanel
|
|
2870
2923
|
}
|
|
2871
2924
|
);
|
|
2872
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
2925
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: relation.ancestorNodes, onSelectNode: selectGenreFromPanel });
|
|
2873
2926
|
const genrePlaylist = genrePlaylistByNodeId.get(node.id);
|
|
2874
2927
|
const criteria = genrePlaylist?.criteria;
|
|
2875
|
-
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0,
|
|
2928
|
+
if (!genrePlaylist || !criteria) return { primary: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
2876
2929
|
family,
|
|
2877
2930
|
ancestors
|
|
2878
2931
|
] }) };
|
|
@@ -2892,11 +2945,11 @@ function GenreTreeView({
|
|
|
2892
2945
|
(sibling) => sibling.parent?.uuid === parentPlaylist.uuid && sibling.uuid !== genrePlaylist.uuid && sibling.criteria?.side === "pop"
|
|
2893
2946
|
) : void 0;
|
|
2894
2947
|
return {
|
|
2895
|
-
primary: /* @__PURE__ */ (0,
|
|
2896
|
-
/* @__PURE__ */ (0,
|
|
2948
|
+
primary: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
2949
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2897
2950
|
GenreInfoPanelBody,
|
|
2898
2951
|
{
|
|
2899
|
-
family: /* @__PURE__ */ (0,
|
|
2952
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
2900
2953
|
family,
|
|
2901
2954
|
ancestors
|
|
2902
2955
|
] }),
|
|
@@ -2914,7 +2967,7 @@ function GenreTreeView({
|
|
|
2914
2967
|
onAddChild: readOnly ? void 0 : () => handleGenreCreationAction(criteria),
|
|
2915
2968
|
onRename: readOnly ? void 0 : () => handleGenreRenameAction(criteria),
|
|
2916
2969
|
onChangeParent: readOnly ? void 0 : () => showPopup(
|
|
2917
|
-
/* @__PURE__ */ (0,
|
|
2970
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
2918
2971
|
ChangeParentPopup,
|
|
2919
2972
|
{
|
|
2920
2973
|
genrePlaylist: { ...genrePlaylist, criteria },
|
|
@@ -2989,19 +3042,19 @@ function GenreTreeView({
|
|
|
2989
3042
|
edge: activePanel.expanded ? { label: "Shrink panel", onClick: () => activePanel.onExpandedChange?.(false) } : isCollapsed ? null : { label: "Hide panel", onClick: () => changeCollapsed(true) },
|
|
2990
3043
|
center: activePanel.expanded ? null : isCollapsed ? { label: "Show panel", onClick: () => changeCollapsed(false) } : activePanel.onExpandedChange ? { label: "Extend panel", onClick: () => activePanel.onExpandedChange?.(true) } : null
|
|
2991
3044
|
} : void 0;
|
|
2992
|
-
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0,
|
|
2993
|
-
|
|
3045
|
+
const expandButton = placement === "default" && activePanel?.onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3046
|
+
import_ui19.IconButton,
|
|
2994
3047
|
{
|
|
2995
3048
|
"aria-label": activePanel.expanded ? "Shrink panel" : "Extend panel",
|
|
2996
3049
|
style: activePanel.headerAccent && { color: "inherit" },
|
|
2997
3050
|
onClick: () => activePanel.onExpandedChange?.(!activePanel.expanded),
|
|
2998
|
-
children: activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3051
|
+
children: activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react16.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react16.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
2999
3052
|
}
|
|
3000
3053
|
) : null;
|
|
3001
3054
|
const renderArtistFilter = () => {
|
|
3002
3055
|
if (isLoading || !filterArtist) return null;
|
|
3003
|
-
return /* @__PURE__ */ (0,
|
|
3004
|
-
/* @__PURE__ */ (0,
|
|
3056
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("span", { className: "inline-flex items-center overflow-hidden rounded-full bg-[var(--color-neutral-100)] text-sm", children: [
|
|
3057
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3005
3058
|
"button",
|
|
3006
3059
|
{
|
|
3007
3060
|
type: "button",
|
|
@@ -3013,7 +3066,7 @@ function GenreTreeView({
|
|
|
3013
3066
|
children: filterArtist.name
|
|
3014
3067
|
}
|
|
3015
3068
|
),
|
|
3016
|
-
/* @__PURE__ */ (0,
|
|
3069
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3017
3070
|
"button",
|
|
3018
3071
|
{
|
|
3019
3072
|
type: "button",
|
|
@@ -3025,8 +3078,8 @@ function GenreTreeView({
|
|
|
3025
3078
|
)
|
|
3026
3079
|
] });
|
|
3027
3080
|
};
|
|
3028
|
-
const actions = /* @__PURE__ */ (0,
|
|
3029
|
-
!isLoading && /* @__PURE__ */ (0,
|
|
3081
|
+
const actions = /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
3082
|
+
!isLoading && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3030
3083
|
GenreSearch,
|
|
3031
3084
|
{
|
|
3032
3085
|
genrePlaylists: visibleGenrePlaylists,
|
|
@@ -3034,14 +3087,18 @@ function GenreTreeView({
|
|
|
3034
3087
|
selectedName: selectedGenrePlaylist?.name ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
|
|
3035
3088
|
onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
|
|
3036
3089
|
onPrevious: handlePrevious,
|
|
3037
|
-
onQueryChange:
|
|
3090
|
+
onQueryChange: (text) => {
|
|
3091
|
+
setSearchText(text);
|
|
3092
|
+
onSearchQueryChange?.(text);
|
|
3093
|
+
},
|
|
3094
|
+
onFocus: onSearchFocus,
|
|
3038
3095
|
extraGroups
|
|
3039
3096
|
}
|
|
3040
3097
|
),
|
|
3041
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3042
|
-
/* @__PURE__ */ (0,
|
|
3043
|
-
!isLoading && !isControlled && /* @__PURE__ */ (0,
|
|
3044
|
-
|
|
3098
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "hidden w-full justify-center max-md:flex", children: renderArtistFilter() }),
|
|
3099
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "contents max-md:flex max-md:w-full max-md:items-center max-md:gap-2 max-md:overflow-x-auto max-md:[scrollbar-width:none] max-md:[&>*]:shrink-0", children: [
|
|
3100
|
+
!isLoading && !isControlled && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3101
|
+
import_ui19.SegmentedControl,
|
|
3045
3102
|
{
|
|
3046
3103
|
"aria-label": "Tree view mode",
|
|
3047
3104
|
value: viewMode,
|
|
@@ -3054,14 +3111,14 @@ function GenreTreeView({
|
|
|
3054
3111
|
]
|
|
3055
3112
|
}
|
|
3056
3113
|
),
|
|
3057
|
-
!isLoading && showDisplayOptions && /* @__PURE__ */ (0,
|
|
3114
|
+
!isLoading && showDisplayOptions && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3058
3115
|
"div",
|
|
3059
3116
|
{
|
|
3060
3117
|
className: "flex items-center gap-1",
|
|
3061
3118
|
role: "group",
|
|
3062
3119
|
"aria-label": "Tree display options",
|
|
3063
|
-
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0,
|
|
3064
|
-
|
|
3120
|
+
children: !isRotationControlled && viewMode !== "stacked" && viewMode !== "outline" && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3121
|
+
import_ui19.Button,
|
|
3065
3122
|
{
|
|
3066
3123
|
variant: allowWheelRotation ? "default" : "outline",
|
|
3067
3124
|
size: "sm",
|
|
@@ -3071,8 +3128,8 @@ function GenreTreeView({
|
|
|
3071
3128
|
)
|
|
3072
3129
|
}
|
|
3073
3130
|
),
|
|
3074
|
-
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0,
|
|
3075
|
-
|
|
3131
|
+
!isLoading && !readOnly && showAddRoot && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3132
|
+
import_ui19.IconTextButton,
|
|
3076
3133
|
{
|
|
3077
3134
|
icon: import_lucide_react15.Plus,
|
|
3078
3135
|
text: "Add root",
|
|
@@ -3081,34 +3138,34 @@ function GenreTreeView({
|
|
|
3081
3138
|
)
|
|
3082
3139
|
] })
|
|
3083
3140
|
] });
|
|
3084
|
-
return /* @__PURE__ */ (0,
|
|
3141
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
|
|
3085
3142
|
"div",
|
|
3086
3143
|
{
|
|
3087
3144
|
"data-left-panel": isPanelShown && !isCollapsed ? void 0 : "hidden",
|
|
3088
3145
|
className: ["genre-tree-view relative flex flex-col h-full", placement === "regions" && REGIONS_CLASS, className2].filter(Boolean).join(" "),
|
|
3089
3146
|
children: [
|
|
3090
|
-
/* @__PURE__ */ (0,
|
|
3147
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3091
3148
|
"div",
|
|
3092
3149
|
{
|
|
3093
3150
|
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"}`,
|
|
3094
3151
|
children: actions
|
|
3095
3152
|
}
|
|
3096
3153
|
),
|
|
3097
|
-
!isLoading && filterArtist && /* @__PURE__ */ (0,
|
|
3154
|
+
!isLoading && filterArtist && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3098
3155
|
"div",
|
|
3099
3156
|
{
|
|
3100
3157
|
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"}`,
|
|
3101
3158
|
children: renderArtistFilter()
|
|
3102
3159
|
}
|
|
3103
3160
|
),
|
|
3104
|
-
/* @__PURE__ */ (0,
|
|
3105
|
-
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0,
|
|
3106
|
-
isLoading ? /* @__PURE__ */ (0,
|
|
3161
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "content-container flex-1 min-h-0 flex flex-row gap-4", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "tree-view-container relative flex-1 min-w-0 flex flex-col h-full", children: [
|
|
3162
|
+
!isLoading && viewMode === "outline" && floatingControlsLeading && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "absolute bottom-3 right-3 z-10", children: floatingControlsLeading }),
|
|
3163
|
+
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreTreeViewSkeleton, { viewMode }) : viewMode === "wheel" ? (
|
|
3107
3164
|
// The info panel's top edge aligns with the tree's; the search bar floats `--layout-panel-inset` below it,
|
|
3108
3165
|
// and the header inset (the bar's height plus that inset) extends the header's fill behind the bar,
|
|
3109
3166
|
// Google Maps style; the 8px bottom padding mirrors the inset above the bar, and the hidden title's height is zeroed.
|
|
3110
3167
|
// The panel's own Zoom in button is hidden: the search bar carries it.
|
|
3111
|
-
/* @__PURE__ */ (0,
|
|
3168
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3112
3169
|
GenrePlaylistTreeWheel,
|
|
3113
3170
|
{
|
|
3114
3171
|
scope,
|
|
@@ -3125,9 +3182,9 @@ function GenreTreeView({
|
|
|
3125
3182
|
allowWheelRotation,
|
|
3126
3183
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3127
3184
|
busy,
|
|
3128
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3185
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3129
3186
|
floatingControlsLeading,
|
|
3130
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3187
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3131
3188
|
] }) : void 0,
|
|
3132
3189
|
defaultViewport,
|
|
3133
3190
|
viewportRestore,
|
|
@@ -3136,7 +3193,7 @@ function GenreTreeView({
|
|
|
3136
3193
|
) }) })
|
|
3137
3194
|
) : viewMode === "pop-core" ? (
|
|
3138
3195
|
// Same as the wheel: the header inset extends its fill behind the floating search bar, Google Maps style.
|
|
3139
|
-
/* @__PURE__ */ (0,
|
|
3196
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container search-overlay relative flex-1 w-full min-h-0 ", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(GenreTreeWheelHandoff, { skeleton: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_genre_tree_view4.GenreTreeWheelSkeleton, {}), onRevealedChange: setIsWheelRevealed, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3140
3197
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3141
3198
|
{
|
|
3142
3199
|
scope,
|
|
@@ -3153,9 +3210,9 @@ function GenreTreeView({
|
|
|
3153
3210
|
allowWheelRotation,
|
|
3154
3211
|
sizeMetric: isArtistFiltered || !canSizeByViews ? "songs" : deferredSizeMetric,
|
|
3155
3212
|
busy,
|
|
3156
|
-
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0,
|
|
3213
|
+
floatingControlsLeading: floatingControlsLeading || canSizeByViews ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
3157
3214
|
floatingControlsLeading,
|
|
3158
|
-
canSizeByViews && /* @__PURE__ */ (0,
|
|
3215
|
+
canSizeByViews && /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SizeMetricToggle, { value: sizeMetric, onChange: changeSizeMetric })
|
|
3159
3216
|
] }) : void 0,
|
|
3160
3217
|
defaultViewport,
|
|
3161
3218
|
viewportRestore,
|
|
@@ -3163,7 +3220,7 @@ function GenreTreeView({
|
|
|
3163
3220
|
...focus
|
|
3164
3221
|
}
|
|
3165
3222
|
) }) })
|
|
3166
|
-
) : viewMode === "outline" ? /* @__PURE__ */ (0,
|
|
3223
|
+
) : viewMode === "outline" ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container flex-1 min-h-0 w-full overflow-y-auto relative", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3167
3224
|
GenrePlaylistTreeWheelRadialPopCore,
|
|
3168
3225
|
{
|
|
3169
3226
|
outline: true,
|
|
@@ -3179,13 +3236,13 @@ function GenreTreeView({
|
|
|
3179
3236
|
selectedNodeId,
|
|
3180
3237
|
readOnly
|
|
3181
3238
|
}
|
|
3182
|
-
) }) : /* @__PURE__ */ (0,
|
|
3239
|
+
) }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "tree-container relative flex flex-col gap-4 w-full pt-14 overflow-x-auto overflow-y-auto text-[var(--color-text)]", children: Object.entries(groupedGenrePlaylistsByRoot).map(
|
|
3183
3240
|
([uuid, genrePlaylistTreePerRoot]) => {
|
|
3184
|
-
return /* @__PURE__ */ (0,
|
|
3241
|
+
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3185
3242
|
"div",
|
|
3186
3243
|
{
|
|
3187
3244
|
className: "tree-per-root-container relative shrink-0 h-[28rem] mt-2 mr-16 p-2 bg-[var(--color-neutral-100)] rounded-lg",
|
|
3188
|
-
children: /* @__PURE__ */ (0,
|
|
3245
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "graph-container relative z-10 h-full", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3189
3246
|
GenrePlaylistTreePerRoot,
|
|
3190
3247
|
{
|
|
3191
3248
|
scope,
|
|
@@ -3211,8 +3268,8 @@ function GenreTreeView({
|
|
|
3211
3268
|
);
|
|
3212
3269
|
}
|
|
3213
3270
|
) }),
|
|
3214
|
-
/* @__PURE__ */ (0,
|
|
3215
|
-
|
|
3271
|
+
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
|
|
3272
|
+
import_ui19.DetailPanel,
|
|
3216
3273
|
{
|
|
3217
3274
|
ref: panelRef,
|
|
3218
3275
|
side: "left",
|
|
@@ -3228,7 +3285,7 @@ function GenreTreeView({
|
|
|
3228
3285
|
aside: activePanel?.aside,
|
|
3229
3286
|
edgeTab: detailTab,
|
|
3230
3287
|
headerClassName: activePanel?.compact ? "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]" : void 0,
|
|
3231
|
-
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0,
|
|
3288
|
+
children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
|
|
3232
3289
|
activePanel.content,
|
|
3233
3290
|
activePanel.aside
|
|
3234
3291
|
] }) : activePanel?.content
|
|
@@ -3243,10 +3300,10 @@ function GenreTreeView({
|
|
|
3243
3300
|
// src/genre-tree/GenreDetailPanel.tsx
|
|
3244
3301
|
var import_react17 = require("react");
|
|
3245
3302
|
var import_genre_tree_view5 = require("@behindthemusictree/genre-tree-view");
|
|
3246
|
-
var
|
|
3303
|
+
var import_ui20 = require("@behindthemusictree/ui");
|
|
3247
3304
|
var import_lucide_react17 = require("lucide-react");
|
|
3248
|
-
var
|
|
3249
|
-
var
|
|
3305
|
+
var import_genre_tree22 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3306
|
+
var import_jsx_runtime45 = require("react/jsx-runtime");
|
|
3250
3307
|
var RELATIVE_ITEM_COUNT = 1;
|
|
3251
3308
|
function toNode(genre, parentId) {
|
|
3252
3309
|
return {
|
|
@@ -3273,7 +3330,7 @@ function GenreDetailPanel({
|
|
|
3273
3330
|
edgeTab,
|
|
3274
3331
|
extras
|
|
3275
3332
|
}) {
|
|
3276
|
-
const { data: detail, isPending, isError } = (0,
|
|
3333
|
+
const { data: detail, isPending, isError } = (0, import_genre_tree22.useFetchGenreDetail)(genreUuid, scope, getBackendBaseUrl);
|
|
3277
3334
|
const playback = useGenrePlaylistPlayback({ scope, genrePlaylists: [] });
|
|
3278
3335
|
const view = (0, import_react17.useMemo)(() => {
|
|
3279
3336
|
if (!detail) return null;
|
|
@@ -3301,17 +3358,17 @@ function GenreDetailPanel({
|
|
|
3301
3358
|
};
|
|
3302
3359
|
}, [detail]);
|
|
3303
3360
|
if (isError) {
|
|
3304
|
-
return /* @__PURE__ */ (0,
|
|
3361
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." });
|
|
3305
3362
|
}
|
|
3306
3363
|
if (isPending || !detail || !view) {
|
|
3307
|
-
return /* @__PURE__ */ (0,
|
|
3308
|
-
/* @__PURE__ */ (0,
|
|
3309
|
-
/* @__PURE__ */ (0,
|
|
3310
|
-
/* @__PURE__ */ (0,
|
|
3364
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
|
|
3365
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui20.Skeleton, { className: "h-6 w-2/3" }),
|
|
3366
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui20.Skeleton, { className: "h-4 w-full" }),
|
|
3367
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_ui20.Skeleton, { className: "h-4 w-3/4" })
|
|
3311
3368
|
] });
|
|
3312
3369
|
}
|
|
3313
3370
|
const selectGenre = (nodeId) => onSelectGenre(nodeId);
|
|
3314
|
-
const family = /* @__PURE__ */ (0,
|
|
3371
|
+
const family = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3315
3372
|
import_genre_tree_view5.GenreNodeFamily,
|
|
3316
3373
|
{
|
|
3317
3374
|
node: view.node,
|
|
@@ -3322,20 +3379,20 @@ function GenreDetailPanel({
|
|
|
3322
3379
|
onSelectNode: selectGenre
|
|
3323
3380
|
}
|
|
3324
3381
|
);
|
|
3325
|
-
const ancestors = /* @__PURE__ */ (0,
|
|
3326
|
-
return /* @__PURE__ */ (0,
|
|
3327
|
-
|
|
3382
|
+
const ancestors = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_genre_tree_view5.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
|
|
3383
|
+
return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(
|
|
3384
|
+
import_ui20.DetailPanel,
|
|
3328
3385
|
{
|
|
3329
3386
|
side: "left",
|
|
3330
3387
|
title: view.node.name,
|
|
3331
3388
|
headerAccent: { background: view.fill, color: import_genre_tree_view5.ACCENT_TEXT_COLOR },
|
|
3332
|
-
headerActions: onExpandedChange ? /* @__PURE__ */ (0,
|
|
3333
|
-
|
|
3389
|
+
headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3390
|
+
import_ui20.IconButton,
|
|
3334
3391
|
{
|
|
3335
3392
|
"aria-label": expanded ? "Shrink panel" : "Extend panel",
|
|
3336
3393
|
style: { color: "inherit" },
|
|
3337
3394
|
onClick: () => onExpandedChange(!expanded),
|
|
3338
|
-
children: expanded ? /* @__PURE__ */ (0,
|
|
3395
|
+
children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react17.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_lucide_react17.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3339
3396
|
}
|
|
3340
3397
|
) : void 0,
|
|
3341
3398
|
onClose,
|
|
@@ -3344,10 +3401,10 @@ function GenreDetailPanel({
|
|
|
3344
3401
|
onExpandedChange,
|
|
3345
3402
|
edgeTab,
|
|
3346
3403
|
children: [
|
|
3347
|
-
/* @__PURE__ */ (0,
|
|
3404
|
+
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
|
|
3348
3405
|
GenreInfoPanelBody,
|
|
3349
3406
|
{
|
|
3350
|
-
family: /* @__PURE__ */ (0,
|
|
3407
|
+
family: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { children: [
|
|
3351
3408
|
family,
|
|
3352
3409
|
ancestors
|
|
3353
3410
|
] }),
|
|
@@ -3373,10 +3430,10 @@ var import_genre_tree_view6 = require("@behindthemusictree/genre-tree-view");
|
|
|
3373
3430
|
// src/genre-tree/track-list-sidebar/TrackListSidebar.tsx
|
|
3374
3431
|
var import_react18 = require("react");
|
|
3375
3432
|
var import_lucide_react18 = require("lucide-react");
|
|
3376
|
-
var
|
|
3377
|
-
var
|
|
3433
|
+
var import_genre_tree23 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3434
|
+
var import_genre_tree24 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3378
3435
|
var import_track_noun4 = require("@behindthemusictree/app-kit/track-noun");
|
|
3379
|
-
var
|
|
3436
|
+
var import_jsx_runtime46 = require("react/jsx-runtime");
|
|
3380
3437
|
var useBrowserLayoutEffect = typeof window === "undefined" ? import_react18.useEffect : import_react18.useLayoutEffect;
|
|
3381
3438
|
function TrackListSidebar({
|
|
3382
3439
|
className: className2 = "",
|
|
@@ -3389,7 +3446,7 @@ function TrackListSidebar({
|
|
|
3389
3446
|
renderArtistLink,
|
|
3390
3447
|
renderSongLink
|
|
3391
3448
|
}) {
|
|
3392
|
-
const { trackList, loadMore, autoAdvanceScrollRequest } = (0,
|
|
3449
|
+
const { trackList, loadMore, autoAdvanceScrollRequest } = (0, import_genre_tree23.useTrackList)();
|
|
3393
3450
|
const trackNoun = (0, import_track_noun4.useTrackNoun)();
|
|
3394
3451
|
const trackListRef = (0, import_react18.useRef)(null);
|
|
3395
3452
|
const previousOriginRef = (0, import_react18.useRef)(trackList?.origin ?? null);
|
|
@@ -3478,25 +3535,25 @@ function TrackListSidebar({
|
|
|
3478
3535
|
}
|
|
3479
3536
|
};
|
|
3480
3537
|
const positionClasses = layout === "inline" ? "relative w-full flex h-full flex-col" : "fixed right-0 w-[50rem] flex flex-col";
|
|
3481
|
-
return trackList ? /* @__PURE__ */ (0,
|
|
3538
|
+
return trackList ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3482
3539
|
"div",
|
|
3483
3540
|
{
|
|
3484
3541
|
"data-theme": "dark",
|
|
3485
3542
|
className: `track-list-sidebar ${positionClasses} rounded-2xl bg-[var(--color-surface)] pb-1 ${className2}`,
|
|
3486
3543
|
style: layout === "fixed" ? { top: "63.5px", maxHeight: "calc(100vh - 63.5px - 79px)" } : void 0,
|
|
3487
3544
|
children: [
|
|
3488
|
-
/* @__PURE__ */ (0,
|
|
3489
|
-
/* @__PURE__ */ (0,
|
|
3490
|
-
/* @__PURE__ */ (0,
|
|
3491
|
-
/* @__PURE__ */ (0,
|
|
3492
|
-
/* @__PURE__ */ (0,
|
|
3545
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "header flex h-16 items-center px-4 py-2 text-[var(--color-text)]", children: [
|
|
3546
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "min-w-0 flex flex-1 flex-col justify-center", children: [
|
|
3547
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "origin flex text-xl", children: [
|
|
3548
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "from h-auto flex flex-col justify-center items-center mr-2", children: "From" }),
|
|
3549
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "name-container flex flex-col justify-center items-center text-[var(--color-text)] font-bold pr-2 max-w-[27.5rem]", children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("div", { className: "name max-w-full truncate", children: trackList.origin.label }) })
|
|
3493
3550
|
] }),
|
|
3494
|
-
/* @__PURE__ */ (0,
|
|
3495
|
-
trackList.origin.type ===
|
|
3551
|
+
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className: "info flex items-center text-sm text-[var(--color-text-muted)]", children: [
|
|
3552
|
+
trackList.origin.type === import_genre_tree24.TrackListOriginType.GENRE_PLAYLIST ? "Genre \u2022 " : trackList.origin.type === import_genre_tree24.TrackListOriginType.ARTIST ? "Artist \u2022 " : `${trackNoun.singular} playlist \u2022 `,
|
|
3496
3553
|
`${trackList.total} ${trackList.total > 1 ? trackNoun.plural : trackNoun.singular}`
|
|
3497
3554
|
] })
|
|
3498
3555
|
] }),
|
|
3499
|
-
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0,
|
|
3556
|
+
(isCollapsed && onExpand || !isCollapsed && onMinimize) && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3500
3557
|
"button",
|
|
3501
3558
|
{
|
|
3502
3559
|
type: "button",
|
|
@@ -3505,11 +3562,11 @@ function TrackListSidebar({
|
|
|
3505
3562
|
"aria-expanded": !isCollapsed,
|
|
3506
3563
|
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"}`,
|
|
3507
3564
|
onClick: isCollapsed ? onExpand : onMinimize,
|
|
3508
|
-
children: /* @__PURE__ */ (0,
|
|
3565
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react18.ListMusic, { "aria-hidden": "true", className: "h-5 w-5" })
|
|
3509
3566
|
}
|
|
3510
3567
|
)
|
|
3511
3568
|
] }),
|
|
3512
|
-
!isCollapsed && /* @__PURE__ */ (0,
|
|
3569
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(
|
|
3513
3570
|
"ul",
|
|
3514
3571
|
{
|
|
3515
3572
|
ref: trackListRef,
|
|
@@ -3519,7 +3576,7 @@ function TrackListSidebar({
|
|
|
3519
3576
|
onPointerDown: markUserScrollIntent,
|
|
3520
3577
|
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",
|
|
3521
3578
|
children: [
|
|
3522
|
-
trackList.tracks.map((track, index) => /* @__PURE__ */ (0,
|
|
3579
|
+
trackList.tracks.map((track, index) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { "data-track-uuid": track.uuid, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
|
|
3523
3580
|
TrackItem,
|
|
3524
3581
|
{
|
|
3525
3582
|
track,
|
|
@@ -3530,7 +3587,7 @@ function TrackListSidebar({
|
|
|
3530
3587
|
renderSongLink
|
|
3531
3588
|
}
|
|
3532
3589
|
) }, track.uuid)),
|
|
3533
|
-
hasMore && /* @__PURE__ */ (0,
|
|
3590
|
+
hasMore && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)("li", { ref: loadMoreSentinelRef, "aria-hidden": "true", className: "h-px" })
|
|
3534
3591
|
]
|
|
3535
3592
|
}
|
|
3536
3593
|
)
|
|
@@ -3543,14 +3600,14 @@ function TrackListSidebar({
|
|
|
3543
3600
|
var import_react19 = require("react");
|
|
3544
3601
|
var import_tailwind_merge = require("tailwind-merge");
|
|
3545
3602
|
var import_player13 = require("@behindthemusictree/app-kit/player");
|
|
3546
|
-
var
|
|
3547
|
-
var
|
|
3603
|
+
var import_genre_tree26 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3604
|
+
var import_ui21 = require("@behindthemusictree/ui");
|
|
3548
3605
|
|
|
3549
3606
|
// src/genre-tree/PlayerControls.tsx
|
|
3550
3607
|
var import_lucide_react19 = require("lucide-react");
|
|
3551
|
-
var
|
|
3608
|
+
var import_genre_tree25 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3552
3609
|
var import_player12 = require("@behindthemusictree/app-kit/player");
|
|
3553
|
-
var
|
|
3610
|
+
var import_jsx_runtime47 = require("react/jsx-runtime");
|
|
3554
3611
|
function PlayerControls() {
|
|
3555
3612
|
const {
|
|
3556
3613
|
duration,
|
|
@@ -3562,22 +3619,22 @@ function PlayerControls() {
|
|
|
3562
3619
|
setVolume,
|
|
3563
3620
|
volume
|
|
3564
3621
|
} = (0, import_player12.usePlayer)();
|
|
3565
|
-
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0,
|
|
3622
|
+
const { hasNextTrack, hasPreviousTrack, playNextTrack, playPreviousTrack } = (0, import_genre_tree25.useTrackList)();
|
|
3566
3623
|
const currentTime = (0, import_player12.useCurrentTime)();
|
|
3567
3624
|
const isBusy = isLoading || playState === import_player12.PlayStates.LOADING;
|
|
3568
3625
|
const isReady = !!playerTrackObject?.isReady && !isBusy && !playerTrackObject.loadError;
|
|
3569
3626
|
const safeDuration = Number.isFinite(duration) && duration > 0 ? duration : 0;
|
|
3570
3627
|
const safeCurrentTime = Number.isFinite(currentTime) ? Math.max(0, Math.min(currentTime, safeDuration)) : 0;
|
|
3571
3628
|
const isPlaying = playState === import_player12.PlayStates.PLAYING;
|
|
3572
|
-
return /* @__PURE__ */ (0,
|
|
3629
|
+
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
|
|
3573
3630
|
"section",
|
|
3574
3631
|
{
|
|
3575
3632
|
"aria-label": "Player controls",
|
|
3576
3633
|
className: "player-controls flex shrink-0 flex-col gap-2 border-b border-zinc-800 px-3 py-2 text-zinc-200",
|
|
3577
3634
|
children: [
|
|
3578
|
-
/* @__PURE__ */ (0,
|
|
3579
|
-
/* @__PURE__ */ (0,
|
|
3580
|
-
/* @__PURE__ */ (0,
|
|
3635
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "flex min-w-0 items-center gap-2 text-xs tabular-nums text-zinc-400", children: [
|
|
3636
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Elapsed time", children: (0, import_genre_tree25.formatTime)(safeCurrentTime) }),
|
|
3637
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3581
3638
|
"input",
|
|
3582
3639
|
{
|
|
3583
3640
|
"aria-label": "Seek",
|
|
@@ -3591,11 +3648,11 @@ function PlayerControls() {
|
|
|
3591
3648
|
onChange: (event) => seekTo(Number(event.currentTarget.value))
|
|
3592
3649
|
}
|
|
3593
3650
|
),
|
|
3594
|
-
/* @__PURE__ */ (0,
|
|
3651
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { "aria-label": "Duration", children: (0, import_genre_tree25.formatTime)(safeDuration) })
|
|
3595
3652
|
] }),
|
|
3596
|
-
/* @__PURE__ */ (0,
|
|
3597
|
-
/* @__PURE__ */ (0,
|
|
3598
|
-
/* @__PURE__ */ (0,
|
|
3653
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "grid min-h-9 grid-cols-[1fr_auto_1fr] items-center gap-3", children: [
|
|
3654
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { className: "col-start-2 flex items-center justify-center gap-2", children: [
|
|
3655
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3599
3656
|
"button",
|
|
3600
3657
|
{
|
|
3601
3658
|
type: "button",
|
|
@@ -3604,10 +3661,10 @@ function PlayerControls() {
|
|
|
3604
3661
|
disabled: !isReady || !hasPreviousTrack,
|
|
3605
3662
|
onClick: playPreviousTrack,
|
|
3606
3663
|
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",
|
|
3607
|
-
children: /* @__PURE__ */ (0,
|
|
3664
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.SkipBack, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3608
3665
|
}
|
|
3609
3666
|
),
|
|
3610
|
-
/* @__PURE__ */ (0,
|
|
3667
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3611
3668
|
"button",
|
|
3612
3669
|
{
|
|
3613
3670
|
type: "button",
|
|
@@ -3616,10 +3673,10 @@ function PlayerControls() {
|
|
|
3616
3673
|
disabled: !isReady,
|
|
3617
3674
|
onClick: handlePlayPauseAction,
|
|
3618
3675
|
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",
|
|
3619
|
-
children: isPlaying ? /* @__PURE__ */ (0,
|
|
3676
|
+
children: isPlaying ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.Pause, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.Play, { "aria-hidden": "true", className: "h-4 w-4", fill: "currentColor" })
|
|
3620
3677
|
}
|
|
3621
3678
|
),
|
|
3622
|
-
/* @__PURE__ */ (0,
|
|
3679
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3623
3680
|
"button",
|
|
3624
3681
|
{
|
|
3625
3682
|
type: "button",
|
|
@@ -3628,14 +3685,14 @@ function PlayerControls() {
|
|
|
3628
3685
|
disabled: !isReady || !hasNextTrack,
|
|
3629
3686
|
onClick: playNextTrack,
|
|
3630
3687
|
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",
|
|
3631
|
-
children: /* @__PURE__ */ (0,
|
|
3688
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.SkipForward, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
3632
3689
|
}
|
|
3633
3690
|
)
|
|
3634
3691
|
] }),
|
|
3635
|
-
/* @__PURE__ */ (0,
|
|
3636
|
-
volume === 0 ? /* @__PURE__ */ (0,
|
|
3637
|
-
/* @__PURE__ */ (0,
|
|
3638
|
-
/* @__PURE__ */ (0,
|
|
3692
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("label", { className: "col-start-3 flex w-[4.2rem] shrink-0 items-center gap-[0.35rem] justify-self-end text-zinc-400 sm:w-[4.9rem]", children: [
|
|
3693
|
+
volume === 0 ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.VolumeX, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_lucide_react19.Volume2, { "aria-hidden": "true", className: "h-[0.7rem] w-[0.7rem] shrink-0" }),
|
|
3694
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { className: "sr-only", children: "Volume" }),
|
|
3695
|
+
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
|
|
3639
3696
|
"input",
|
|
3640
3697
|
{
|
|
3641
3698
|
"aria-label": "Volume",
|
|
@@ -3657,7 +3714,7 @@ function PlayerControls() {
|
|
|
3657
3714
|
}
|
|
3658
3715
|
|
|
3659
3716
|
// src/genre-tree/PlayerPanel.tsx
|
|
3660
|
-
var
|
|
3717
|
+
var import_jsx_runtime48 = require("react/jsx-runtime");
|
|
3661
3718
|
var PANEL_ID = "player-panel";
|
|
3662
3719
|
var DEFAULT_EXTENDED_WIDTH = "calc(100vw - var(--layout-center-left) - var(--layout-gutter))";
|
|
3663
3720
|
function PlayerPanel({
|
|
@@ -3670,7 +3727,7 @@ function PlayerPanel({
|
|
|
3670
3727
|
renderSongLink
|
|
3671
3728
|
}) {
|
|
3672
3729
|
const { playerTrackObject, retryPlayback } = (0, import_player13.usePlayer)();
|
|
3673
|
-
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0,
|
|
3730
|
+
const { toggleTrackListSidebar, isTrackListSidebarVisible } = (0, import_genre_tree26.useTrackListSidebarVisibility)();
|
|
3674
3731
|
const [lastLoadedTrackTitle, setLastLoadedTrackTitle] = (0, import_react19.useState)("");
|
|
3675
3732
|
const loadedTrackTitle = playerTrackObject?.track.title;
|
|
3676
3733
|
(0, import_react19.useEffect)(() => {
|
|
@@ -3697,7 +3754,7 @@ function PlayerPanel({
|
|
|
3697
3754
|
onClick: () => setIsExtendRequested(true),
|
|
3698
3755
|
expanded: false
|
|
3699
3756
|
} : null;
|
|
3700
|
-
return /* @__PURE__ */ (0,
|
|
3757
|
+
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3701
3758
|
"div",
|
|
3702
3759
|
{
|
|
3703
3760
|
id: PANEL_ID,
|
|
@@ -3710,8 +3767,8 @@ function PlayerPanel({
|
|
|
3710
3767
|
),
|
|
3711
3768
|
style: isExtended ? { width: extendedWidth } : void 0,
|
|
3712
3769
|
children: [
|
|
3713
|
-
/* @__PURE__ */ (0,
|
|
3714
|
-
|
|
3770
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3771
|
+
import_ui21.PanelEdgeTab,
|
|
3715
3772
|
{
|
|
3716
3773
|
side: "right",
|
|
3717
3774
|
edge: tabEdge,
|
|
@@ -3720,7 +3777,7 @@ function PlayerPanel({
|
|
|
3720
3777
|
className: "absolute right-full top-1/2 -translate-y-1/2"
|
|
3721
3778
|
}
|
|
3722
3779
|
),
|
|
3723
|
-
/* @__PURE__ */ (0,
|
|
3780
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
|
|
3724
3781
|
"div",
|
|
3725
3782
|
{
|
|
3726
3783
|
className: (0, import_tailwind_merge.twMerge)(
|
|
@@ -3728,20 +3785,20 @@ function PlayerPanel({
|
|
|
3728
3785
|
isExtended ? "flex-row" : "flex-col"
|
|
3729
3786
|
),
|
|
3730
3787
|
children: [
|
|
3731
|
-
/* @__PURE__ */ (0,
|
|
3732
|
-
/* @__PURE__ */ (0,
|
|
3733
|
-
/* @__PURE__ */ (0,
|
|
3734
|
-
/* @__PURE__ */ (0,
|
|
3735
|
-
playerTrackObject?.loadError && /* @__PURE__ */ (0,
|
|
3788
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "flex w-[var(--layout-right-w)] shrink-0 flex-col", children: [
|
|
3789
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("div", { className: "flex shrink-0 items-center justify-between gap-2 border-b border-zinc-800 p-2", children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "min-w-0 truncate text-sm font-semibold", children: loadedTrackTitle || lastLoadedTrackTitle }) }),
|
|
3790
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { className: "relative w-full shrink-0", children: [
|
|
3791
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_player13.PlayerVideoSurface, { className: "aspect-video w-full bg-black" }),
|
|
3792
|
+
playerTrackObject?.loadError && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("span", { className: "absolute bottom-2 left-2 right-2 text-xs text-red-400", children: [
|
|
3736
3793
|
playerTrackObject.loadError,
|
|
3737
|
-
/* @__PURE__ */ (0,
|
|
3794
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)("button", { type: "button", className: "ml-2 underline", onClick: () => {
|
|
3738
3795
|
void retryPlayback();
|
|
3739
3796
|
}, children: "Retry" })
|
|
3740
3797
|
] })
|
|
3741
3798
|
] }),
|
|
3742
|
-
/* @__PURE__ */ (0,
|
|
3799
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(PlayerControls, {})
|
|
3743
3800
|
] }),
|
|
3744
|
-
/* @__PURE__ */ (0,
|
|
3801
|
+
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
|
|
3745
3802
|
TrackListSidebar,
|
|
3746
3803
|
{
|
|
3747
3804
|
layout: "inline",
|
|
@@ -3767,33 +3824,33 @@ function PlayerPanel({
|
|
|
3767
3824
|
// src/genre-tree/Rating.tsx
|
|
3768
3825
|
var import_uuid = require("uuid");
|
|
3769
3826
|
var import_ai = require("react-icons/ai");
|
|
3770
|
-
var
|
|
3771
|
-
var
|
|
3827
|
+
var import_genre_tree27 = require("@behindthemusictree/app-kit/genre-tree");
|
|
3828
|
+
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
3772
3829
|
function Rating({ rating, handleChange }) {
|
|
3773
3830
|
const instanceId = (0, import_uuid.v4)();
|
|
3774
|
-
return /* @__PURE__ */ (0,
|
|
3775
|
-
/* @__PURE__ */ (0,
|
|
3831
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "flex items-center", children: [...Array(6)].map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: `relative ${i === 0 ? "mr-4" : "mr-1"}`, children: [
|
|
3832
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3776
3833
|
"input",
|
|
3777
3834
|
{
|
|
3778
3835
|
id: `star-${instanceId}-${i}`,
|
|
3779
3836
|
className: "w-6 h-6 opacity-0",
|
|
3780
3837
|
type: "radio",
|
|
3781
3838
|
name: "rating",
|
|
3782
|
-
value: i === 0 ?
|
|
3839
|
+
value: i === 0 ? import_genre_tree27.FORM_RATING_NULL_VALUE : i,
|
|
3783
3840
|
checked: rating === (i === 0 ? null : i),
|
|
3784
3841
|
onChange: handleChange
|
|
3785
3842
|
}
|
|
3786
3843
|
),
|
|
3787
|
-
/* @__PURE__ */ (0,
|
|
3788
|
-
/* @__PURE__ */ (0,
|
|
3844
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("label", { htmlFor: `star-${instanceId}-${i}`, className: "cursor-pointer", children: i === 0 ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)("div", { className: "flex", children: [
|
|
3845
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3789
3846
|
import_ai.AiOutlineStar,
|
|
3790
3847
|
{
|
|
3791
|
-
className: `w-7 h-7 absolute top-0 left-0 ${rating ===
|
|
3848
|
+
className: `w-7 h-7 absolute top-0 left-0 ${rating === import_genre_tree27.FORM_RATING_NULL_VALUE ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
3792
3849
|
}
|
|
3793
3850
|
),
|
|
3794
|
-
/* @__PURE__ */ (0,
|
|
3851
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "flex-grow w-8" }),
|
|
3795
3852
|
" "
|
|
3796
|
-
] }) : /* @__PURE__ */ (0,
|
|
3853
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
3797
3854
|
import_ai.AiFillStar,
|
|
3798
3855
|
{
|
|
3799
3856
|
className: `w-7 h-7 absolute top-0 left-0 ${(rating ?? -1) >= i ? "text-[var(--color-accent-rating)]" : "text-[var(--color-text-muted)]"}`
|
|
@@ -3823,12 +3880,18 @@ function Rating({ rating, handleChange }) {
|
|
|
3823
3880
|
PopupButtons,
|
|
3824
3881
|
PopupTitle,
|
|
3825
3882
|
Rating,
|
|
3883
|
+
SEARCH_FAILED_STATUS,
|
|
3884
|
+
SearchBox,
|
|
3826
3885
|
SpotifyAuthErrorPopup,
|
|
3827
3886
|
Toast,
|
|
3828
3887
|
TrackItem,
|
|
3829
3888
|
TrackListSidebar,
|
|
3830
3889
|
TrackPositionPlayPause,
|
|
3831
3890
|
TrackUploadPopup,
|
|
3832
|
-
|
|
3891
|
+
searchStatus,
|
|
3892
|
+
useArtistSearchGroup,
|
|
3893
|
+
useGenrePlaylistPlayback,
|
|
3894
|
+
useGenreRemoteSearchGroup,
|
|
3895
|
+
useSongSearchGroup
|
|
3833
3896
|
});
|
|
3834
3897
|
//# sourceMappingURL=index.cjs.map
|