@behindthemusictree/app-kit-ui 29.0.1 → 30.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.
@@ -3,7 +3,7 @@ import { ReactNode, ComponentProps } from 'react';
3
3
  import { z } from 'zod';
4
4
  import { GenreTreeProps, FocusChangeReason, GenreTreeNode, GenreTreeAction, GenreTreeViewMode, GenreTreeViewport, InfoPanelSections, InfoPanelBody, 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, RenderArtistLink, CriteriaPlaylistSimple, GenrePlaylistUuid, ArtistSearchResult, CriteriaMinimum } from '@behindthemusictree/app-kit/genre-tree';
6
+ import { TrackBase, CriteriaOverview, GenreUuid, CriteriaPlaylistMinimum, RenderArtistLink, RenderSongLink, CriteriaPlaylistSimple, GenrePlaylistUuid, ArtistSearchResult, CriteriaMinimum, SongSearchResult } from '@behindthemusictree/app-kit/genre-tree';
7
7
  import { Scope } from '@behindthemusictree/app-kit/transport';
8
8
 
9
9
  type GenrePlaylistTracksSlots = {
@@ -30,6 +30,7 @@ type GenreDetailExtrasProps<O extends CriteriaOverview> = GenreDetailSlots<O> &
30
30
  criteriaOverviewSchema?: z.ZodType<O, unknown>;
31
31
  renderGenreDetailExtras?: (overview: O) => ReactNode;
32
32
  renderArtistLink?: RenderArtistLink;
33
+ renderSongLink?: RenderSongLink;
33
34
  /** When set, the tracks list shows only this artist's songs in the genre. */
34
35
  artist?: {
35
36
  uuid: string;
@@ -99,6 +100,12 @@ type GenreTreeShareTarget = {
99
100
  uuid: string;
100
101
  name: string;
101
102
  scope: Scope;
103
+ } | {
104
+ kind: "song";
105
+ uuid: string;
106
+ /** The song title. */
107
+ name: string;
108
+ scope: Scope;
102
109
  };
103
110
  /** Host applications own routing and return an absolute or relative URL for this target. */
104
111
  type GetGenreTreeShareUrl = (target: GenreTreeShareTarget) => string | null | undefined;
@@ -174,14 +181,28 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
174
181
  /** Controlled expanded state of the artist panel. Uncontrolled, it resets to compact on each artist. */
175
182
  artistExpanded?: boolean;
176
183
  onArtistExpandedChange?: (expanded: boolean) => void;
177
- /** Resolves a selected genre or artist to a share URL using the host app's routes. */
184
+ /** Controlled song panel (a song uuid). Like the artist panel it shares the info panel's slot: opening a song clears
185
+ * the selected genre and the artist; selecting a genre closes it. A host `overlayPanel` takes precedence. */
186
+ songUuid?: string | null;
187
+ /** Called when the user opens a song (search pick, title link) or closes its panel. */
188
+ onSongUuidChange?: (uuid: string | null) => void;
189
+ /** Controlled expanded state of the song panel. Uncontrolled, it resets to compact on each song. */
190
+ songExpanded?: boolean;
191
+ onSongExpandedChange?: (expanded: boolean) => void;
192
+ /** Songs are searched by default; picking one opens its panel. */
193
+ songSearch?: boolean;
194
+ onSongSelect?: (song: SongSearchResult) => void;
195
+ /** When set, song titles in the info panel's track list render through it (e.g. a router `Link` to the song).
196
+ * By default they are buttons that open the song panel. */
197
+ renderSongLink?: RenderSongLink;
198
+ /** Resolves a selected genre, artist or song to a share URL using the host app's routes. */
178
199
  getShareUrl?: GetGenreTreeShareUrl;
179
200
  } & ArtistSearchProps & GenreDetailSlots<O> & GenrePlaylistTracksSlots & InfoPanelCurationProps & GenreFocusProps & {
180
201
  /** Hides the info panel behind its edge tab; uncontrolled when omitted. Only applies with `placement="regions"`. */
181
202
  collapsed?: boolean;
182
203
  onCollapsedChange?: (collapsed: boolean) => void;
183
204
  };
184
- 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, artistSearch, onArtistSelect, artistSearchBaseUrl, renderSummary, renderEssentialTracks, renderSongLeading, renderSongActions, renderTracksToolbar, renderDangerZone, expanded, onExpandedChange, collapsed, onCollapsedChange, overlayPanel, placement, ...focus }: GenreTreeViewProps<O>): react.JSX.Element;
205
+ 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, 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;
185
206
 
186
207
  type GenreActionBandProps = {
187
208
  node: GenreTreeNode;
@@ -292,8 +313,9 @@ type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
292
313
  } & InfoPanelCurationProps;
293
314
  declare function GenrePlaylistTreePerRoot<T extends TrackBase>({ scope, className, rootUuid, genrePlaylistTreePerRoot, handleGenreCreationAction, handleGenreRenameAction, additionalActions, onNodeClick, onNodeHover, renderInfoPanelBody, selectedNodeId, onSelectedNodeChange, hideInfoPanelClose, hideInfoPanelTitle, readOnly, showToolbar, wheelZoom, defaultViewport, viewportRestore, onViewportChange, playback, ...infoPanelCuration }: GenrePlaylistTreePerRootProps<T>): react.JSX.Element;
294
315
 
295
- type GenreSearchExtraResults = {
296
- /** Heading of this group; genre matches then get a "Genres" heading above it. */
316
+ type GenreSearchExtraGroup = {
317
+ key: string;
318
+ /** Heading of this group; genre matches then get a "Genres" heading too. */
297
319
  label: string;
298
320
  items: {
299
321
  key: string;
@@ -317,10 +339,10 @@ type GenreSearchProps<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>
317
339
  placeholder?: string;
318
340
  /** Called with what the user types, and with "" on clear or pick; never when `selectedName` refills the input. */
319
341
  onQueryChange?: (query: string) => void;
320
- /** A second result group (e.g. artists) the host resolves from `onQueryChange`. */
321
- extraResults?: GenreSearchExtraResults;
342
+ /** Further result groups (artists, songs) the host resolves from `onQueryChange`. */
343
+ extraGroups?: GenreSearchExtraGroup[];
322
344
  };
323
- declare function GenreSearch<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>({ genrePlaylists, onSelect, selectedName, onClear, onPrevious, placeholder, onQueryChange, extraResults, }: GenreSearchProps<T>): react.JSX.Element;
345
+ declare function GenreSearch<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple>({ genrePlaylists, onSelect, selectedName, onClear, onPrevious, placeholder, onQueryChange, extraGroups, }: GenreSearchProps<T>): react.JSX.Element;
324
346
 
325
347
  interface TrackListSidebarProps<T extends TrackBase> {
326
348
  className?: string;
@@ -332,8 +354,10 @@ interface TrackListSidebarProps<T extends TrackBase> {
332
354
  onMinimize?: () => void;
333
355
  /** When set, artist names render through it (e.g. a router `Link`). */
334
356
  renderArtistLink?: RenderArtistLink;
357
+ /** When set, song titles render through it (e.g. open the song panel). */
358
+ renderSongLink?: RenderSongLink;
335
359
  }
336
- declare function TrackListSidebar<T extends TrackBase>({ className, renderDuration, renderActions, layout, isCollapsed, onExpand, onMinimize, renderArtistLink, }: TrackListSidebarProps<T>): react.JSX.Element | null;
360
+ declare function TrackListSidebar<T extends TrackBase>({ className, renderDuration, renderActions, layout, isCollapsed, onExpand, onMinimize, renderArtistLink, renderSongLink, }: TrackListSidebarProps<T>): react.JSX.Element | null;
337
361
 
338
362
  interface PlayerPanelProps {
339
363
  className?: string;
@@ -347,8 +371,10 @@ interface PlayerPanelProps {
347
371
  /** Width once extended, e.g. to stop short of a left-hand detail panel. */
348
372
  extendedWidth?: string;
349
373
  renderArtistLink?: RenderArtistLink;
374
+ /** When set, song titles in the tracklist render through it (e.g. open the song panel). */
375
+ renderSongLink?: RenderSongLink;
350
376
  }
351
- declare function PlayerPanel({ className, isVisible, expanded, onExpandedChange, extendedWidth, renderArtistLink, }: PlayerPanelProps): react.JSX.Element;
377
+ declare function PlayerPanel({ className, isVisible, expanded, onExpandedChange, extendedWidth, renderArtistLink, renderSongLink, }: PlayerPanelProps): react.JSX.Element;
352
378
 
353
379
  interface TrackItemProps<T extends TrackBase> {
354
380
  track: T;
@@ -357,8 +383,10 @@ interface TrackItemProps<T extends TrackBase> {
357
383
  renderActions?: (track: T) => ReactNode;
358
384
  /** When set, artist names render through it (e.g. a router `Link`). */
359
385
  renderArtistLink?: RenderArtistLink;
386
+ /** When set, a song title renders through it (e.g. opens the song panel) instead of linking to MusicBrainz. */
387
+ renderSongLink?: RenderSongLink;
360
388
  }
361
- declare function TrackItem<T extends TrackBase>({ track, position, renderDuration, renderActions, renderArtistLink, }: TrackItemProps<T>): react.JSX.Element;
389
+ declare function TrackItem<T extends TrackBase>({ track, position, renderDuration, renderActions, renderArtistLink, renderSongLink, }: TrackItemProps<T>): react.JSX.Element;
362
390
 
363
391
  interface TrackPositionPlayPauseProps {
364
392
  position: number;
@@ -393,4 +421,4 @@ interface PanelEdgeTabProps {
393
421
  }
394
422
  declare function PanelEdgeTab({ side, edge, center, controls, className }: PanelEdgeTabProps): react.JSX.Element | null;
395
423
 
396
- export { type ArtistSearchProps, GenreDetailPanel, type GenreDetailPanelProps, type GenreDetailSlots, type GenreFocusProps, GenreInfoPanelBody, type GenreInfoPanelBodyProps, GenreNodeActionBand, type GenrePlaylistPlayback, type GenrePlaylistTracksSlots, GenrePlaylistTreePerRoot, type GenrePlaylistTreePerRootProps, GenreSearch, type GenreSearchExtraResults, type GenreSearchProps, type GenreTreeShareTarget, GenreTreeView, type GenreTreeViewProps, type GetGenreTreeShareUrl, type InfoPanelCurationProps, PanelEdgeTab, type PanelEdgeTabAction, type PanelEdgeTabProps, type ParentChange, type PlayableGenrePlaylist, PlayerPanel, type PlayerPanelProps, Rating, type RatingProps, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps, type UseGenrePlaylistPlaybackOptions, useGenrePlaylistPlayback, useLeftDetailPanelLayout };
424
+ 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 InfoPanelCurationProps, PanelEdgeTab, type PanelEdgeTabAction, type PanelEdgeTabProps, type ParentChange, type PlayableGenrePlaylist, PlayerPanel, type PlayerPanelProps, Rating, type RatingProps, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps, type UseGenrePlaylistPlaybackOptions, useGenrePlaylistPlayback, useLeftDetailPanelLayout };
@@ -17,7 +17,7 @@ import {
17
17
  TrackPositionPlayPause,
18
18
  useGenrePlaylistPlayback,
19
19
  useLeftDetailPanelLayout
20
- } from "../chunk-TFLX6C5Q.js";
20
+ } from "../chunk-2HE2HDFA.js";
21
21
  import "../chunk-DKQQLLMY.js";
22
22
  export {
23
23
  GenreDetailPanel,