@behindthemusictree/app-kit-ui 36.0.0 → 37.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -130,6 +130,7 @@ type SearchBoxProps = {
130
130
  onPicked?: (groupKey: string, itemKey: string) => void;
131
131
  /** Shows the clear button when set. */
132
132
  onClear?: () => void;
133
+ clearLabel?: string;
133
134
  /** An action left of the text, e.g. a Previous button. */
134
135
  leading?: ReactNode;
135
136
  className?: string;
@@ -137,7 +138,7 @@ type SearchBoxProps = {
137
138
  onFocus?: () => void;
138
139
  };
139
140
  /** A search pill over any number of result groups; picking an item calls that item's `onSelect`. */
140
- declare function SearchBox({ label, placeholder, value, onValueChange, groups, onPicked, onClear, leading, className, onFocus, }: SearchBoxProps): react.JSX.Element;
141
+ declare function SearchBox({ label, placeholder, value, onValueChange, groups, onPicked, onClear, clearLabel, leading, className, onFocus, }: SearchBoxProps): react.JSX.Element;
141
142
 
142
143
  declare function useArtistSearchGroup(getBaseUrl: () => string, text: string, onSelect: (artist: ArtistSearchResult) => void, enabled?: boolean): SearchGroup | undefined;
143
144
 
@@ -165,6 +166,9 @@ type GenreSearchProps<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>
165
166
  onPrevious?: () => void;
166
167
  /** Called by the clear button, after it empties the input. */
167
168
  onClear?: () => void;
169
+ /** Forces the clear button on or off; by default it shows while the input has something to clear. */
170
+ showClear?: boolean;
171
+ clearLabel?: string;
168
172
  placeholder?: string;
169
173
  /** Called with what the user types, and with "" on clear or pick; never when `selectedName` refills the input. */
170
174
  onQueryChange?: (query: string) => void;
@@ -173,7 +177,7 @@ type GenreSearchProps<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>
173
177
  /** Called when the input gains focus. */
174
178
  onFocus?: () => void;
175
179
  };
176
- declare function GenreSearch<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>({ genrePlaylists, onSelect, selectedName, onClear, onPrevious, placeholder, onQueryChange, extraGroups, onFocus, }: GenreSearchProps<T>): react.JSX.Element;
180
+ declare function GenreSearch<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>({ genrePlaylists, onSelect, selectedName, onClear, showClear, clearLabel, onPrevious, placeholder, onQueryChange, extraGroups, onFocus, }: GenreSearchProps<T>): react.JSX.Element;
177
181
 
178
182
  interface GenreSearchGroupsProps {
179
183
  query: string;
@@ -393,9 +397,10 @@ type GenreDetailPanelProps<O extends CriteriaOverview> = {
393
397
  /** Tab on the panel's inner edge, for hosts that hide and show the panel. */
394
398
  edgeTab?: ComponentProps<typeof DetailPanel>["edgeTab"];
395
399
  extras?: GenreInfoPanelBodyProps<O>["extras"];
396
- headerClassName?: ComponentProps<typeof DetailPanel>["headerClassName"];
400
+ /** The host's search field shows this genre's name and closes the panel. */
401
+ titleInSearch?: boolean;
397
402
  };
398
- declare function GenreDetailPanel<O extends CriteriaOverview>({ genreUuid, scope, getBackendBaseUrl, onClose, onSelectGenre, onZoomIn, shareUrl, shareTarget, collapsed, expanded, onExpandedChange, edgeTab, extras, headerClassName, }: GenreDetailPanelProps<O>): react.JSX.Element;
403
+ declare function GenreDetailPanel<O extends CriteriaOverview>({ genreUuid, scope, getBackendBaseUrl, onClose, onSelectGenre, onZoomIn, shareUrl, shareTarget, collapsed, expanded, onExpandedChange, edgeTab, extras, titleInSearch, }: GenreDetailPanelProps<O>): react.JSX.Element;
399
404
 
400
405
  type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
401
406
  scope: Scope;
@@ -21,7 +21,7 @@ import {
21
21
  useGenrePlaylistPlayback,
22
22
  useGenreRemoteSearchGroup,
23
23
  useSongSearchGroup
24
- } from "../chunk-RTXLUHJY.js";
24
+ } from "../chunk-FTYMRCJS.js";
25
25
  import "../chunk-DKQQLLMY.js";
26
26
  export {
27
27
  GenreDetailPanel,
package/dist/index.cjs CHANGED
@@ -1047,6 +1047,7 @@ function SearchBox({
1047
1047
  groups,
1048
1048
  onPicked,
1049
1049
  onClear,
1050
+ clearLabel,
1050
1051
  leading,
1051
1052
  className: className2,
1052
1053
  onFocus
@@ -1078,6 +1079,7 @@ function SearchBox({
1078
1079
  status,
1079
1080
  loading,
1080
1081
  onClear,
1082
+ clearLabel,
1081
1083
  leading,
1082
1084
  inputClassName: "focus-visible:ring-0"
1083
1085
  }
@@ -1173,6 +1175,8 @@ function GenreSearch({
1173
1175
  onSelect,
1174
1176
  selectedName = null,
1175
1177
  onClear,
1178
+ showClear,
1179
+ clearLabel,
1176
1180
  onPrevious,
1177
1181
  placeholder,
1178
1182
  onQueryChange,
@@ -1220,7 +1224,8 @@ function GenreSearch({
1220
1224
  if (groupKey !== "genres") setQuery("");
1221
1225
  onQueryChange?.("");
1222
1226
  },
1223
- onClear: canClear ? () => {
1227
+ clearLabel,
1228
+ onClear: showClear ?? canClear ? () => {
1224
1229
  clear();
1225
1230
  onQueryChange?.("");
1226
1231
  } : void 0,
@@ -2057,10 +2062,12 @@ function useGenreDetailContent({
2057
2062
  };
2058
2063
  }, [detail, scope]);
2059
2064
  if (isError) return {
2065
+ name: null,
2060
2066
  title: "Genre unavailable",
2061
2067
  content: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)("p", { role: "alert", className: "p-4 text-sm text-[var(--color-text)]", children: "This genre could not be loaded." })
2062
2068
  };
2063
2069
  if (isPending || !detail || !view) return {
2070
+ name: null,
2064
2071
  title: "Loading genre\u2026",
2065
2072
  content: /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)("div", { className: "flex flex-col gap-3 p-4", "aria-busy": "true", "data-testid": "genre-detail-panel-skeleton", children: [
2066
2073
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.Skeleton, { className: "h-6 w-2/3" }),
@@ -2082,6 +2089,7 @@ function useGenreDetailContent({
2082
2089
  );
2083
2090
  const ancestors = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_genre_tree_view4.GenreNodeAncestors, { ancestorNodes: view.ancestorNodes, onSelectNode: selectGenre });
2084
2091
  return {
2092
+ name: detail.name,
2085
2093
  title: detail.name,
2086
2094
  headerAccent: { background: view.fill, color: import_genre_tree_view4.ACCENT_TEXT_COLOR },
2087
2095
  aside: expanded ? ancestors : void 0,
@@ -2123,7 +2131,7 @@ function GenreDetailPanel({
2123
2131
  onExpandedChange,
2124
2132
  edgeTab,
2125
2133
  extras,
2126
- headerClassName
2134
+ titleInSearch
2127
2135
  }) {
2128
2136
  const panel = useGenreDetailContent({
2129
2137
  genreUuid,
@@ -2146,7 +2154,7 @@ function GenreDetailPanel({
2146
2154
  onExpandedChange,
2147
2155
  edgeTab,
2148
2156
  aside: panel.aside,
2149
- headerClassName,
2157
+ titleInSearch,
2150
2158
  headerActions: onExpandedChange ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_ui10.IconButton, { "aria-label": expanded ? "Shrink panel" : "Extend panel", onClick: () => onExpandedChange(!expanded), children: expanded ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react12.Minimize2, { "aria-hidden": "true", className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_lucide_react12.Maximize2, { "aria-hidden": "true", className: "h-4 w-4" }) }) : void 0,
2151
2159
  children: panel.content
2152
2160
  }
@@ -3122,7 +3130,6 @@ function GenreTreeView({
3122
3130
  if (node !== null && songUuid !== null) changeSong(null);
3123
3131
  selectNode(node);
3124
3132
  };
3125
- const handleGenreSearchClear = clearSelection;
3126
3133
  const handleGenreSearchSelect = (genrePlaylist2) => {
3127
3134
  reopenPanel();
3128
3135
  if (songUuid !== null) changeSong(null);
@@ -3205,7 +3212,7 @@ function GenreTreeView({
3205
3212
  expanded: overlayPanel.expanded ?? false,
3206
3213
  onExpandedChange: overlayPanel.onExpandedChange,
3207
3214
  onClose: overlayPanel.onClose,
3208
- compact: false
3215
+ titleInSearch: false
3209
3216
  };
3210
3217
  } else if (selectedGenreUuid) {
3211
3218
  activePanel = {
@@ -3215,7 +3222,7 @@ function GenreTreeView({
3215
3222
  expanded: isExpanded,
3216
3223
  onExpandedChange: changeExpanded,
3217
3224
  onClose: clearSelection,
3218
- compact: false,
3225
+ titleInSearch: true,
3219
3226
  headerAccent: relations ? { background: relations.fill, color: relations.textColor } : genreContent.headerAccent
3220
3227
  };
3221
3228
  } else if (builtInPanel) {
@@ -3226,7 +3233,7 @@ function GenreTreeView({
3226
3233
  expanded: builtInPanel.expanded,
3227
3234
  onExpandedChange: builtInPanel.onExpandedChange,
3228
3235
  onClose: builtInPanel.onClose,
3229
- compact: true
3236
+ titleInSearch: true
3230
3237
  };
3231
3238
  }
3232
3239
  const isPanelShown = activePanel !== null && (selectedGenreUuid !== null || isTreeReady);
@@ -3294,8 +3301,10 @@ function GenreTreeView({
3294
3301
  if (songUuid) changeSong(null);
3295
3302
  selectGenre(uuid);
3296
3303
  },
3297
- selectedName: (selectedGenreUuid ? genreContent.title : null) ?? (typeof builtInPanel?.title === "string" ? builtInPanel.title : null),
3298
- onClear: builtInSongPanel ? () => changeSong(null) : builtInArtistPanel ? () => changeArtist(null) : handleGenreSearchClear,
3304
+ selectedName: selectedGenreUuid ? genreContent.name : typeof builtInPanel?.title === "string" ? builtInPanel.title : null,
3305
+ onClear: activePanel?.onClose,
3306
+ showClear: activePanel?.titleInSearch ?? false,
3307
+ clearLabel: "Close panel",
3299
3308
  onPrevious: handlePrevious,
3300
3309
  onQueryChange: onSearchQueryChange,
3301
3310
  onFocus: onSearchFocus,
@@ -3485,8 +3494,7 @@ function GenreTreeView({
3485
3494
  ref: panelRef,
3486
3495
  side: "left",
3487
3496
  title: activePanel?.title ?? "",
3488
- hideTitle: activePanel?.compact,
3489
- hideClose: activePanel?.compact,
3497
+ titleInSearch: activePanel?.titleInSearch,
3490
3498
  headerAccent: activePanel?.headerAccent,
3491
3499
  headerActions: expandButton,
3492
3500
  onClose: activePanel?.onClose ?? noop,
@@ -3495,7 +3503,7 @@ function GenreTreeView({
3495
3503
  onExpandedChange: activePanel?.onExpandedChange,
3496
3504
  aside: activePanel?.aside,
3497
3505
  edgeTab: detailTab,
3498
- headerClassName: "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]",
3506
+ headerClassName: activePanel?.titleInSearch ? void 0 : "pt-[calc(var(--layout-search-h)+var(--layout-panel-inset)+0.5rem)]",
3499
3507
  children: activePanel?.aside && !activePanel.expanded ? /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { children: [
3500
3508
  activePanel.content,
3501
3509
  activePanel.aside