@behindthemusictree/app-kit-ui 26.1.0 → 28.0.2

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 } from 'react';
3
3
  import { z } from 'zod';
4
4
  import { GenreTreeProps, FocusChangeReason, GenreTreeNode, GenreTreeAction, GenreTreeViewMode, GenreTreeViewport, ControlsOrientation, InfoPanelSections, InfoPanelBody, WheelZoomMode } from '@behindthemusictree/genre-tree-view';
5
5
  export { ControlsOrientation, FocusChangeReason, GenreTreeAction, GenreTreeOutlineSkeleton, GenreTreeSkeleton, GenreTreeViewMode, GenreTreeViewSkeleton, GenreTreeViewSkeletonProps, GenreTreeViewport, GenreTreeWheelSkeleton } from '@behindthemusictree/genre-tree-view';
6
- import { TrackBase, CriteriaOverview, CriteriaPlaylistSimple, GenreUuid, ArtistSearchResult, CriteriaMinimum, RenderArtistLink } from '@behindthemusictree/app-kit/genre-tree';
6
+ import { TrackBase, CriteriaOverview, CriteriaPlaylistSimple, GenreUuid, GenrePlaylistUuid, ArtistSearchResult, CriteriaMinimum, RenderArtistLink } from '@behindthemusictree/app-kit/genre-tree';
7
7
  import { Scope } from '@behindthemusictree/app-kit/transport';
8
8
 
9
9
  type GenrePlaylistTracksSlots = {
@@ -54,6 +54,24 @@ type ParentChange = {
54
54
  to: GenreUuid;
55
55
  };
56
56
 
57
+ /** Identifies the detail-panel entity whose current host route should be shared. */
58
+ type GenreTreeShareTarget = {
59
+ kind: "genre";
60
+ /** Tree selection/route identity; distinct from the associated criteria UUID. */
61
+ genrePlaylistUuid: GenrePlaylistUuid;
62
+ name: string;
63
+ scope: Scope;
64
+ /** Preserve an active artist filter when sharing one of its genre details. */
65
+ artistUuid: string | null;
66
+ } | {
67
+ kind: "artist";
68
+ uuid: string;
69
+ name: string;
70
+ scope: Scope;
71
+ };
72
+ /** Host applications own routing and return an absolute or relative URL for this target. */
73
+ type GetGenreTreeShareUrl = (target: GenreTreeShareTarget) => string | null | undefined;
74
+
57
75
  /** Artists are searched by default; picking one opens its panel (see `artistUuid`). */
58
76
  type ArtistSearchProps = {
59
77
  artistSearch: false;
@@ -104,6 +122,8 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
104
122
  renderGenreDetailExtras?: (overview: O) => ReactNode;
105
123
  /** Layout of the wheel/tree floating zoom/pop/fit controls. Defaults to "horizontal". */
106
124
  controlsOrientation?: ControlsOrientation;
125
+ /** Host control displayed before the size metric in the floating controls. */
126
+ floatingControlsLeading?: ReactNode;
107
127
  /** Controlled size metric of the wheel and pop-core nodes: song count or summed song views. Uncontrolled when omitted
108
128
  * (starts on songs). The toggle is hidden while an artist filters the tree, which only has song counts. */
109
129
  sizeMetric?: GenreTreeSizeMetric;
@@ -121,12 +141,14 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
121
141
  /** Controlled expanded state of the artist panel. Uncontrolled, it resets to compact on each artist. */
122
142
  artistExpanded?: boolean;
123
143
  onArtistExpandedChange?: (expanded: boolean) => void;
144
+ /** Resolves a selected genre or artist to a share URL using the host app's routes. */
145
+ getShareUrl?: GetGenreTreeShareUrl;
124
146
  } & ArtistSearchProps & GenreDetailSlots<O> & GenrePlaylistTracksSlots & InfoPanelCurationProps & GenreFocusProps & {
125
147
  /** Hides the info panel behind its edge tab; uncontrolled when omitted. Only applies with `placement="regions"`. */
126
148
  collapsed?: boolean;
127
149
  onCollapsedChange?: (collapsed: boolean) => void;
128
150
  };
129
- declare function GenreTreeView<O extends CriteriaOverview = CriteriaOverview>({ scope, handleGenreCreationAction, handleGenreRenameAction, onParentChanged, getBackendBaseUrl, additionalActions, viewMode: controlledViewMode, selectedNodeId: controlledSelectedNodeId, onSelectedNodeIdChange, defaultViewport, onViewportChange, readOnly, className, showDisplayOptions, showAddRoot, allowWheelRotation: controlledAllowWheelRotation, criteriaOverviewSchema, renderGenreDetailExtras, controlsOrientation, sizeMetric: controlledSizeMetric, onSizeMetricChange, renderArtistLink, artistUuid: controlledArtistUuid, onArtistUuidChange, artistExpanded: controlledArtistExpanded, onArtistExpandedChange, artistSearch, onArtistSelect, artistSearchBaseUrl, renderSummary, renderEssentialTracks, renderSongLeading, renderSongActions, renderTracksToolbar, renderDangerZone, expanded, onExpandedChange, collapsed, onCollapsedChange, overlayPanel, placement, ...focus }: GenreTreeViewProps<O>): react.JSX.Element;
151
+ declare function GenreTreeView<O extends CriteriaOverview = CriteriaOverview>({ scope, handleGenreCreationAction, handleGenreRenameAction, onParentChanged, getBackendBaseUrl, additionalActions, viewMode: controlledViewMode, selectedNodeId: controlledSelectedNodeId, onSelectedNodeIdChange, defaultViewport, onViewportChange, readOnly, className, showDisplayOptions, showAddRoot, allowWheelRotation: controlledAllowWheelRotation, criteriaOverviewSchema, renderGenreDetailExtras, controlsOrientation, 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;
130
152
 
131
153
  type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
132
154
  scope: Scope;
@@ -198,16 +220,20 @@ interface TrackListSidebarProps<T extends TrackBase> {
198
220
  renderDuration?: (track: T) => ReactNode;
199
221
  renderActions?: (track: T) => ReactNode;
200
222
  layout?: "fixed" | "inline";
223
+ isCollapsed?: boolean;
224
+ onExpand?: () => void;
225
+ onMinimize?: () => void;
201
226
  /** When set, artist names render through it (e.g. a router `Link`). */
202
227
  renderArtistLink?: RenderArtistLink;
203
228
  }
204
- declare function TrackListSidebar<T extends TrackBase>({ className, renderDuration, renderActions, layout, renderArtistLink, }: TrackListSidebarProps<T>): react.JSX.Element | null;
229
+ declare function TrackListSidebar<T extends TrackBase>({ className, renderDuration, renderActions, layout, isCollapsed, onExpand, onMinimize, renderArtistLink, }: TrackListSidebarProps<T>): react.JSX.Element | null;
205
230
 
206
231
  interface PlayerPanelProps {
207
232
  className?: string;
208
- /** Hidden via CSS, never unmounted, so the video keeps playing while the panel is closed. */
233
+ /** Hidden via CSS, never unmounted, so the video keeps playing while the host hides the panel. */
209
234
  isVisible?: boolean;
210
- onClose: () => void;
235
+ /** @deprecated The player no longer closes; its header action minimizes to video-only mode. */
236
+ onClose?: () => void;
211
237
  /** Controlled extended state; uncontrolled when omitted. */
212
238
  expanded?: boolean;
213
239
  onExpandedChange?: (expanded: boolean) => void;
@@ -215,7 +241,7 @@ interface PlayerPanelProps {
215
241
  extendedWidth?: string;
216
242
  renderArtistLink?: RenderArtistLink;
217
243
  }
218
- declare function PlayerPanel({ className, isVisible, onClose, expanded, onExpandedChange, extendedWidth, renderArtistLink, }: PlayerPanelProps): react.JSX.Element;
244
+ declare function PlayerPanel({ className, isVisible, expanded, onExpandedChange, extendedWidth, renderArtistLink, }: PlayerPanelProps): react.JSX.Element;
219
245
 
220
246
  interface TrackItemProps<T extends TrackBase> {
221
247
  track: T;
@@ -231,8 +257,9 @@ interface TrackPositionPlayPauseProps {
231
257
  position: number;
232
258
  uuid: string;
233
259
  handlePlayPauseClick: (event: React.MouseEvent) => void;
260
+ isSelected?: boolean;
234
261
  }
235
- declare function TrackPositionPlayPause({ position, uuid, handlePlayPauseClick }: TrackPositionPlayPauseProps): react.JSX.Element;
262
+ declare function TrackPositionPlayPause({ position, uuid, handlePlayPauseClick, isSelected, }: TrackPositionPlayPauseProps): react.JSX.Element;
236
263
 
237
264
  interface RatingProps {
238
265
  rating: number | null | undefined;
@@ -259,4 +286,4 @@ interface PanelEdgeTabProps {
259
286
  }
260
287
  declare function PanelEdgeTab({ side, edge, center, controls, className }: PanelEdgeTabProps): react.JSX.Element | null;
261
288
 
262
- export { type ArtistSearchProps, type GenreDetailSlots, type GenreFocusProps, type GenrePlaylistTracksSlots, GenrePlaylistTreePerRoot, type GenrePlaylistTreePerRootProps, GenreSearch, type GenreSearchExtraResults, type GenreSearchProps, GenreTreeView, type GenreTreeViewProps, type InfoPanelCurationProps, PanelEdgeTab, type PanelEdgeTabAction, type PanelEdgeTabProps, type ParentChange, PlayerPanel, type PlayerPanelProps, Rating, type RatingProps, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps };
289
+ export { type ArtistSearchProps, type GenreDetailSlots, type GenreFocusProps, 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, PlayerPanel, type PlayerPanelProps, Rating, type RatingProps, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps };
@@ -12,8 +12,8 @@ import {
12
12
  TrackItem,
13
13
  TrackListSidebar,
14
14
  TrackPositionPlayPause
15
- } from "../chunk-KPWV4LEJ.js";
16
- import "../chunk-VDXUW7PP.js";
15
+ } from "../chunk-5PQWJRDZ.js";
16
+ import "../chunk-DKQQLLMY.js";
17
17
  export {
18
18
  GenrePlaylistTreePerRoot,
19
19
  GenreSearch,
@@ -1 +1,81 @@
1
1
  @import "@behindthemusictree/genre-tree-view/styles.css";
2
+
3
+ .track-position-play-pause__spinner {
4
+ display: inline-block;
5
+ width: 16px;
6
+ height: 16px;
7
+ flex: none;
8
+ border: 2px solid rgb(255 255 255 / 65%);
9
+ border-top-color: #22c55e;
10
+ border-radius: 50%;
11
+ animation: app-kit-track-spinner 700ms linear infinite;
12
+ }
13
+
14
+ .track-position-play-pause__bars {
15
+ display: flex;
16
+ width: 16px;
17
+ height: 16px;
18
+ align-items: flex-end;
19
+ justify-content: center;
20
+ gap: 2px;
21
+ }
22
+
23
+ .track-position-play-pause__bar {
24
+ display: block;
25
+ width: 3px;
26
+ flex: none;
27
+ border-radius: 9999px;
28
+ background-color: var(--color-green-500, #22c55e);
29
+ transform-origin: bottom center;
30
+ }
31
+
32
+ .track-position-play-pause__bar:nth-child(1) {
33
+ height: 8px;
34
+ }
35
+
36
+ .track-position-play-pause__bar:nth-child(2) {
37
+ height: 13px;
38
+ }
39
+
40
+ .track-position-play-pause__bar:nth-child(3) {
41
+ height: 10px;
42
+ }
43
+
44
+ .track-position-play-pause__bars[data-playing="true"] .track-position-play-pause__bar {
45
+ animation: app-kit-playing-bars 700ms ease-in-out infinite alternate;
46
+ }
47
+
48
+ .track-position-play-pause__bars[data-playing="true"] .track-position-play-pause__bar:nth-child(2) {
49
+ animation-delay: 180ms;
50
+ }
51
+
52
+ .track-position-play-pause__bars[data-playing="true"] .track-position-play-pause__bar:nth-child(3) {
53
+ animation-delay: 360ms;
54
+ }
55
+
56
+ @keyframes app-kit-playing-bars {
57
+ from {
58
+ transform: scaleY(0.35);
59
+ }
60
+
61
+ to {
62
+ transform: scaleY(1);
63
+ }
64
+ }
65
+
66
+ @keyframes app-kit-track-spinner {
67
+ to {
68
+ transform: rotate(360deg);
69
+ }
70
+ }
71
+
72
+ @media (prefers-reduced-motion: reduce) {
73
+ .track-position-play-pause__spinner {
74
+ animation-duration: 1400ms;
75
+ }
76
+
77
+ .track-position-play-pause__bars[data-playing="true"] .track-position-play-pause__bar {
78
+ animation: none;
79
+ }
80
+ }
81
+