@behindthemusictree/app-kit-ui 34.0.0 → 35.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.
@@ -1,9 +1,9 @@
1
1
  import * as react from 'react';
2
- import { ReactNode, ComponentProps } from 'react';
2
+ import { ReactNode, ComponentType, ComponentProps } from 'react';
3
3
  import { z } from 'zod';
4
4
  import { GenreTreeProps, FocusChangeReason, GenreTreeNode, GenreTreeAction, GenreTreeViewMode, GenreTreeViewport, WheelZoomMode } from '@behindthemusictree/genre-tree-view';
5
5
  export { FocusChangeReason, GenreTreeAction, GenreTreeOutlineSkeleton, GenreTreeSkeleton, GenreTreeViewMode, GenreTreeViewSkeleton, GenreTreeViewSkeletonProps, GenreTreeViewport, GenreTreeWheelSkeleton } from '@behindthemusictree/genre-tree-view';
6
- import { TrackBase, CriteriaOverview, GenreUuid, CriteriaPlaylistMinimum, GenreMusicSelection, RenderArtistLink, RenderSongLink, CriteriaPlaylistSimple, ArtistSearchResult, SongSearchResult, UseGenreRemoteSearchOptions, GenreSearchResult, GenrePlaylistUuid, CriteriaMinimum, GenreCatalogueCounts, GenrePlaylistSummary } from '@behindthemusictree/app-kit/genre-tree';
6
+ import { TrackBase, CriteriaOverview, GenreUuid, CriteriaPlaylistMinimum, GenreMusicSelection, RenderArtistLink, RenderSongLink, CriteriaPlaylistSimple, ArtistSearchResult, SongSearchResult, UseGenreRemoteSearchOptions, GenreSearchResult, CriteriaMinimum, GenreCatalogueCounts, GenrePlaylistSummary } from '@behindthemusictree/app-kit/genre-tree';
7
7
  import { Scope } from '@behindthemusictree/app-kit/transport';
8
8
  import { DetailPanel } from '@behindthemusictree/ui';
9
9
 
@@ -24,7 +24,8 @@ type GenreDetailSlots<O extends CriteriaOverview> = {
24
24
  };
25
25
  type GenreDetailExtrasProps<O extends CriteriaOverview> = GenreDetailSlots<O> & GenrePlaylistTracksSlots & {
26
26
  genreUuid: GenreUuid;
27
- genrePlaylist: CriteriaPlaylistMinimum;
27
+ genrePlaylist: CriteriaPlaylistMinimum | null;
28
+ genreName?: string;
28
29
  selection?: GenreMusicSelection;
29
30
  wikidataId?: string | null;
30
31
  scope: Scope;
@@ -153,6 +154,34 @@ type UseGenreRemoteSearchGroupOptions = UseGenreRemoteSearchOptions & {
153
154
  /** Genres from the API's ranked `genres/search/`, rendered with the umbrella tag and the tree they live in. */
154
155
  declare function useGenreRemoteSearchGroup(getBaseUrl: () => string, text: string, onSelect: (genre: GenreSearchResult) => void, { key, label, filter, describe, ...options }?: UseGenreRemoteSearchGroupOptions): SearchGroup;
155
156
 
157
+ /** @deprecated Use `SearchGroup`. */
158
+ type GenreSearchExtraGroup = SearchGroup;
159
+ type GenreSearchProps<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple> = {
160
+ genrePlaylists: T[];
161
+ onSelect: (genrePlaylist: T) => void;
162
+ /** Name of the currently selected genre; fills the input whenever it changes. */
163
+ selectedName?: string | null;
164
+ /** When set, renders a "Previous" button left of the text and makes room for it. */
165
+ onPrevious?: () => void;
166
+ /** Called by the clear button, after it empties the input. */
167
+ onClear?: () => void;
168
+ placeholder?: string;
169
+ /** Called with what the user types, and with "" on clear or pick; never when `selectedName` refills the input. */
170
+ onQueryChange?: (query: string) => void;
171
+ /** Further result groups (places, artists, songs) the host resolves from `onQueryChange`. */
172
+ extraGroups?: SearchGroup[];
173
+ /** Called when the input gains focus. */
174
+ onFocus?: () => void;
175
+ };
176
+ declare function GenreSearch<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>({ genrePlaylists, onSelect, selectedName, onClear, onPrevious, placeholder, onQueryChange, extraGroups, onFocus, }: GenreSearchProps<T>): react.JSX.Element;
177
+
178
+ interface GenreSearchGroupsProps {
179
+ query: string;
180
+ focused: boolean;
181
+ onSelectGenre: (uuid: GenreUuid) => void;
182
+ children: (groups: SearchGroup[]) => ReactNode;
183
+ }
184
+
156
185
  type ParentChange = {
157
186
  genre: GenreUuid;
158
187
  from: GenreUuid | null;
@@ -162,8 +191,8 @@ type ParentChange = {
162
191
  /** Identifies the detail-panel entity whose current host route should be shared. */
163
192
  type GenreTreeShareTarget = {
164
193
  kind: "genre";
165
- /** Tree selection/route identity; distinct from the associated criteria UUID. */
166
- genrePlaylistUuid: GenrePlaylistUuid;
194
+ /** Genre entity identity, independent of its playlist or renderer. */
195
+ genreUuid: GenreUuid;
167
196
  name: string;
168
197
  scope: Scope;
169
198
  /** Preserve an active artist filter when sharing one of its genre details. */
@@ -207,11 +236,10 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
207
236
  additionalActions?: (node: GenreTreeNode) => GenreTreeAction[];
208
237
  /** Controlled view mode. When provided, the internal Stacked/Wheel toggle is not rendered — the consumer owns that UI. */
209
238
  viewMode?: GenreTreeViewMode;
210
- /** Controlled selection (a genre playlist uuid, i.e. the tree node id). When provided, the
211
- * consumer owns it — e.g. to move the selection after deleting the selected genre. */
212
- selectedNodeId?: string | null;
239
+ /** Selected genre entity, including genres outside the rendered tree. */
240
+ selectedGenreUuid?: GenreUuid | null;
213
241
  /** Called on every user-driven selection change (tree click, search pick, search clear). */
214
- onSelectedNodeIdChange?: (nodeId: string | null) => void;
242
+ onSelectedGenreUuidChange?: (genreUuid: GenreUuid | null) => void;
215
243
  /** Mount-only seed for the pan/zoom (wheel, pop-core and stacked views), replacing the initial fit. */
216
244
  defaultViewport?: GenreTreeViewport;
217
245
  /** Restore silently after layout; use a new revision for each navigation. Supply focus, selection,
@@ -225,6 +253,8 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
225
253
  readOnly?: boolean;
226
254
  /** Further result groups (e.g. places) shown in the search box next to genres, artists and songs. */
227
255
  extraSearchGroups?: SearchGroup[];
256
+ /** Host search hooks run inside this subtree, so typing does not rerender the tree page. */
257
+ SearchGroups?: ComponentType<GenreSearchGroupsProps>;
228
258
  /** Fires with the typed search text (empty when cleared), so a host can drive its own `extraSearchGroups`. */
229
259
  onSearchQueryChange?: (text: string) => void;
230
260
  /** Fires when the search box gains focus, e.g. to load data for `extraSearchGroups` lazily. */
@@ -288,7 +318,7 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
288
318
  collapsed?: boolean;
289
319
  onCollapsedChange?: (collapsed: boolean) => void;
290
320
  };
291
- declare function GenreTreeView<O extends CriteriaOverview = CriteriaOverview>({ scope, handleGenreCreationAction, handleGenreRenameAction, onParentChanged, getBackendBaseUrl, additionalActions, viewMode: controlledViewMode, selectedNodeId: controlledSelectedNodeId, onSelectedNodeIdChange, defaultViewport, viewportRestore, onViewportChange, readOnly, className, showDisplayOptions, showAddRoot, allowWheelRotation: controlledAllowWheelRotation, criteriaOverviewSchema, renderGenreDetailExtras, floatingControlsLeading, sizeMetric: controlledSizeMetric, onSizeMetricChange, renderArtistLink, artistUuid: controlledArtistUuid, onArtistUuidChange, artistExpanded: controlledArtistExpanded, onArtistExpandedChange, getShareUrl, extraSearchGroups, onSearchQueryChange, onSearchFocus, artistSearch, onArtistSelect, artistSearchBaseUrl, songUuid: controlledSongUuid, onSongUuidChange, songExpanded: controlledSongExpanded, onSongExpandedChange, songSearch, onSongSelect, renderSongLink, renderSummary, renderEssentialTracks, renderSongLeading, renderSongActions, renderTracksToolbar, renderDangerZone, expanded, onExpandedChange, collapsed, onCollapsedChange, overlayPanel, placement, ...focus }: GenreTreeViewProps<O>): react.JSX.Element;
321
+ declare function GenreTreeView<O extends CriteriaOverview = CriteriaOverview>({ scope, handleGenreCreationAction, handleGenreRenameAction, onParentChanged, getBackendBaseUrl, additionalActions, viewMode: controlledViewMode, selectedGenreUuid: controlledSelectedGenreUuid, onSelectedGenreUuidChange, defaultViewport, viewportRestore, onViewportChange, readOnly, className, showDisplayOptions, showAddRoot, allowWheelRotation: controlledAllowWheelRotation, criteriaOverviewSchema, renderGenreDetailExtras, floatingControlsLeading, sizeMetric: controlledSizeMetric, onSizeMetricChange, renderArtistLink, artistUuid: controlledArtistUuid, onArtistUuidChange, artistExpanded: controlledArtistExpanded, onArtistExpandedChange, getShareUrl, extraSearchGroups, SearchGroups, onSearchQueryChange, onSearchFocus, artistSearch, onArtistSelect, artistSearchBaseUrl, songUuid: controlledSongUuid, onSongUuidChange, songExpanded: controlledSongExpanded, onSongExpandedChange, songSearch, onSongSelect, renderSongLink, renderSummary, renderEssentialTracks, renderSongLeading, renderSongActions, renderTracksToolbar, renderDangerZone, expanded, onExpandedChange, collapsed, onCollapsedChange, overlayPanel, placement, ...focus }: GenreTreeViewProps<O>): react.JSX.Element;
292
322
 
293
323
  type GenreActionBandProps = {
294
324
  node: GenreTreeNode;
@@ -315,9 +345,11 @@ type GenreActionBandProps = {
315
345
  declare function GenreActionBand({ node, onPlayPause, isPlaying, onZoomIn, onAddChild, onRename, onChangeParent, side, onToggleSide, toggleSideDisabledReason, additionalActions, shareTarget, shareUrl, }: GenreActionBandProps): react.JSX.Element | null;
316
346
 
317
347
  /** What the action band reads of a genre's playlist: enough to play it and to know whether it can be. */
318
- type PlayableGenrePlaylist = (CriteriaPlaylistMinimum & {
348
+ type PlayableGenrePlaylist = ((CriteriaPlaylistMinimum & {
319
349
  tracksCount: number;
320
- }) | GenrePlaylistSummary;
350
+ }) | GenrePlaylistSummary) & {
351
+ criteria?: CriteriaMinimum | null;
352
+ };
321
353
  type GenreNodeActionBandProps = Omit<ComponentProps<typeof GenreActionBand>, "onPlayPause" | "isPlaying"> & {
322
354
  genrePlaylist: PlayableGenrePlaylist;
323
355
  playback: GenrePlaylistPlayback;
@@ -329,18 +361,19 @@ type GenreInfoPanelBodyProps<O extends CriteriaOverview> = {
329
361
  family: ReactNode;
330
362
  genreUuid: GenreUuid;
331
363
  catalogue?: GenreCatalogueCounts;
332
- genrePlaylist: PlayableGenrePlaylist;
364
+ countsContext?: string;
365
+ genrePlaylist: PlayableGenrePlaylist | null;
333
366
  wikidataId?: string | null;
334
367
  scope: Scope;
335
368
  getBackendBaseUrl: () => string;
336
369
  playback: GenrePlaylistPlayback;
337
370
  /** Action band props other than play, which the body derives from `playback`. */
338
371
  band: Omit<GenreNodeActionBandProps, "genrePlaylist" | "playback">;
339
- extras?: Omit<GenreDetailExtrasProps<O>, "family" | "genreUuid" | "genrePlaylist" | "wikidataId" | "scope" | "getBackendBaseUrl">;
372
+ extras?: Omit<GenreDetailExtrasProps<O>, "family" | "genreUuid" | "genreName" | "genrePlaylist" | "wikidataId" | "scope" | "getBackendBaseUrl">;
340
373
  };
341
374
  /** The body of a genre's info panel: action band, then the genre's overview, family and tracks.
342
375
  * Shared by the tree's panel and the standalone GenreDetailPanel. */
343
- declare function GenreInfoPanelBody<O extends CriteriaOverview>({ toolbar, family, genreUuid, genrePlaylist, catalogue, wikidataId, scope, getBackendBaseUrl, playback, band, extras, }: GenreInfoPanelBodyProps<O>): react.JSX.Element;
376
+ declare function GenreInfoPanelBody<O extends CriteriaOverview>({ toolbar, family, genreUuid, genrePlaylist, catalogue, countsContext, wikidataId, scope, getBackendBaseUrl, playback, band, extras, }: GenreInfoPanelBodyProps<O>): react.JSX.Element;
344
377
 
345
378
  type GenreDetailPanelProps<O extends CriteriaOverview> = {
346
379
  genreUuid: GenreUuid;
@@ -360,9 +393,9 @@ type GenreDetailPanelProps<O extends CriteriaOverview> = {
360
393
  /** Tab on the panel's inner edge, for hosts that hide and show the panel. */
361
394
  edgeTab?: ComponentProps<typeof DetailPanel>["edgeTab"];
362
395
  extras?: GenreInfoPanelBodyProps<O>["extras"];
396
+ headerClassName?: ComponentProps<typeof DetailPanel>["headerClassName"];
363
397
  };
364
- /** A genre's detail panel driven by one uuid, with no tree behind it. The tree's own panel shares its body. */
365
- declare function GenreDetailPanel<O extends CriteriaOverview>({ genreUuid, scope, getBackendBaseUrl, onClose, onSelectGenre, onZoomIn, shareUrl, shareTarget, collapsed, expanded, onExpandedChange, edgeTab, extras, }: GenreDetailPanelProps<O>): react.JSX.Element;
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;
366
399
 
367
400
  type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
368
401
  scope: Scope;
@@ -392,27 +425,6 @@ type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
392
425
  } & HostPanelProps;
393
426
  declare function GenrePlaylistTreePerRoot<T extends TrackBase>({ scope, className, rootUuid, genrePlaylistTreePerRoot, handleGenreCreationAction, handleGenreRenameAction, additionalActions, onNodeClick, onNodeHover, selectedNodeId, onSelectedNodeChange, readOnly, wheelZoom, defaultViewport, viewportRestore, onViewportChange, playback, ...hostPanel }: GenrePlaylistTreePerRootProps<T>): react.JSX.Element;
394
427
 
395
- /** @deprecated Use `SearchGroup`. */
396
- type GenreSearchExtraGroup = SearchGroup;
397
- type GenreSearchProps<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple> = {
398
- genrePlaylists: T[];
399
- onSelect: (genrePlaylist: T) => void;
400
- /** Name of the currently selected genre; fills the input whenever it changes. */
401
- selectedName?: string | null;
402
- /** When set, renders a "Previous" button left of the text and makes room for it. */
403
- onPrevious?: () => void;
404
- /** Called by the clear button, after it empties the input. */
405
- onClear?: () => void;
406
- placeholder?: string;
407
- /** Called with what the user types, and with "" on clear or pick; never when `selectedName` refills the input. */
408
- onQueryChange?: (query: string) => void;
409
- /** Further result groups (places, artists, songs) the host resolves from `onQueryChange`. */
410
- extraGroups?: SearchGroup[];
411
- /** Called when the input gains focus. */
412
- onFocus?: () => void;
413
- };
414
- declare function GenreSearch<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>({ genrePlaylists, onSelect, selectedName, onClear, onPrevious, placeholder, onQueryChange, extraGroups, onFocus, }: GenreSearchProps<T>): react.JSX.Element;
415
-
416
428
  interface TrackListSidebarProps<T extends TrackBase> {
417
429
  className?: string;
418
430
  renderDuration?: (track: T) => ReactNode;
@@ -471,4 +483,4 @@ interface RatingProps {
471
483
  }
472
484
  declare function Rating({ rating, handleChange }: RatingProps): react.JSX.Element;
473
485
 
474
- export { type ArtistSearchProps, GenreDetailPanel, type GenreDetailPanelProps, type GenreDetailSlots, type GenreFocusProps, GenreInfoPanelBody, type GenreInfoPanelBodyProps, GenreNodeActionBand, type GenrePlaylistPlayback, type GenrePlaylistTracksSlots, GenrePlaylistTreePerRoot, type GenrePlaylistTreePerRootProps, GenreSearch, type GenreSearchExtraGroup, type GenreSearchProps, type GenreTreeShareTarget, GenreTreeView, type GenreTreeViewProps, type GetGenreTreeShareUrl, type ParentChange, type PlayableGenrePlaylist, PlayerPanel, type PlayerPanelProps, Rating, type RatingProps, SEARCH_FAILED_STATUS, SearchBox, type SearchBoxProps, type SearchGroup, type SearchGroupItem, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps, type UseGenrePlaylistPlaybackOptions, type UseGenreRemoteSearchGroupOptions, searchStatus, useArtistSearchGroup, useGenrePlaylistPlayback, useGenreRemoteSearchGroup, useSongSearchGroup };
486
+ export { type ArtistSearchProps, GenreDetailPanel, type GenreDetailPanelProps, type GenreDetailSlots, type GenreFocusProps, GenreInfoPanelBody, type GenreInfoPanelBodyProps, GenreNodeActionBand, type GenrePlaylistPlayback, type GenrePlaylistTracksSlots, GenrePlaylistTreePerRoot, type GenrePlaylistTreePerRootProps, GenreSearch, type GenreSearchExtraGroup, type GenreSearchGroupsProps, type GenreSearchProps, type GenreTreeShareTarget, GenreTreeView, type GenreTreeViewProps, type GetGenreTreeShareUrl, type ParentChange, type PlayableGenrePlaylist, PlayerPanel, type PlayerPanelProps, Rating, type RatingProps, SEARCH_FAILED_STATUS, SearchBox, type SearchBoxProps, type SearchGroup, type SearchGroupItem, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps, type UseGenrePlaylistPlaybackOptions, type UseGenreRemoteSearchGroupOptions, searchStatus, useArtistSearchGroup, useGenrePlaylistPlayback, useGenreRemoteSearchGroup, useSongSearchGroup };
@@ -21,7 +21,7 @@ import {
21
21
  useGenrePlaylistPlayback,
22
22
  useGenreRemoteSearchGroup,
23
23
  useSongSearchGroup
24
- } from "../chunk-A7OWCEIK.js";
24
+ } from "../chunk-YC3WBUVO.js";
25
25
  import "../chunk-DKQQLLMY.js";
26
26
  export {
27
27
  GenreDetailPanel,