@behindthemusictree/app-kit-ui 26.1.0 → 29.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 } from 'react';
2
+ import { ReactNode, ComponentProps } from 'react';
3
3
  import { z } from 'zod';
4
- import { GenreTreeProps, FocusChangeReason, GenreTreeNode, GenreTreeAction, GenreTreeViewMode, GenreTreeViewport, ControlsOrientation, InfoPanelSections, InfoPanelBody, WheelZoomMode } from '@behindthemusictree/genre-tree-view';
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';
4
+ import { GenreTreeProps, FocusChangeReason, GenreTreeNode, GenreTreeAction, GenreTreeViewMode, GenreTreeViewport, InfoPanelSections, InfoPanelBody, WheelZoomMode } from '@behindthemusictree/genre-tree-view';
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';
7
7
  import { Scope } from '@behindthemusictree/app-kit/transport';
8
8
 
9
9
  type GenrePlaylistTracksSlots = {
@@ -21,15 +21,46 @@ type GenreDetailSlots<O extends CriteriaOverview> = {
21
21
  /** Replaces the default Essential section (e.g. "Essential songs"). */
22
22
  renderEssentialTracks?: (overview: O) => ReactNode;
23
23
  };
24
+ type GenreDetailExtrasProps<O extends CriteriaOverview> = GenreDetailSlots<O> & GenrePlaylistTracksSlots & {
25
+ genreUuid: GenreUuid;
26
+ genrePlaylist: CriteriaPlaylistMinimum;
27
+ wikidataId?: string | null;
28
+ scope: Scope;
29
+ getBackendBaseUrl: () => string;
30
+ criteriaOverviewSchema?: z.ZodType<O, unknown>;
31
+ renderGenreDetailExtras?: (overview: O) => ReactNode;
32
+ renderArtistLink?: RenderArtistLink;
33
+ /** When set, the tracks list shows only this artist's songs in the genre. */
34
+ artist?: {
35
+ uuid: string;
36
+ name: string;
37
+ } | null;
38
+ /** genre-tree-view's Family section, placed right after the Summary. */
39
+ family: ReactNode;
40
+ };
24
41
 
25
42
  /** Controlled expand state and danger-zone slot of the info panel, plus the generic overlay panel, as typed by genre-tree-view. */
26
43
  type InfoPanelCurationProps = Partial<Pick<GenreTreeProps, "renderDangerZone" | "expanded" | "onExpandedChange" | "collapsed" | "hideExpandButton" | "overlayPanel">>;
27
44
 
45
+ /** What a play button needs of a playlist; `criteria` is only read when playing one artist's songs in the genre. */
46
+ type PlayableGenrePlaylistInput = CriteriaPlaylistMinimum & {
47
+ criteria?: CriteriaPlaylistSimple["criteria"];
48
+ };
28
49
  /** What a tree node's play button plays, and which node is the one currently playing. */
29
50
  type GenrePlaylistPlayback = {
30
51
  playingNodeId: string | null;
31
- play: (genrePlaylist: CriteriaPlaylistSimple) => Promise<void>;
52
+ play: (genrePlaylist: PlayableGenrePlaylistInput) => Promise<void>;
53
+ };
54
+ type UseGenrePlaylistPlaybackOptions = {
55
+ scope: Scope;
56
+ genrePlaylists: CriteriaPlaylistSimple[];
57
+ /** When set, nodes play only this artist's songs in the genre's subtree instead of the whole genre playlist. */
58
+ artist?: {
59
+ uuid: string;
60
+ name: string;
61
+ } | null;
32
62
  };
63
+ declare function useGenrePlaylistPlayback<T extends TrackBase = TrackBase>({ scope, genrePlaylists, artist, }: UseGenrePlaylistPlaybackOptions): GenrePlaylistPlayback;
33
64
 
34
65
  type GenreTreeSizeMetric = "songs" | "views";
35
66
 
@@ -54,6 +85,24 @@ type ParentChange = {
54
85
  to: GenreUuid;
55
86
  };
56
87
 
88
+ /** Identifies the detail-panel entity whose current host route should be shared. */
89
+ type GenreTreeShareTarget = {
90
+ kind: "genre";
91
+ /** Tree selection/route identity; distinct from the associated criteria UUID. */
92
+ genrePlaylistUuid: GenrePlaylistUuid;
93
+ name: string;
94
+ scope: Scope;
95
+ /** Preserve an active artist filter when sharing one of its genre details. */
96
+ artistUuid: string | null;
97
+ } | {
98
+ kind: "artist";
99
+ uuid: string;
100
+ name: string;
101
+ scope: Scope;
102
+ };
103
+ /** Host applications own routing and return an absolute or relative URL for this target. */
104
+ type GetGenreTreeShareUrl = (target: GenreTreeShareTarget) => string | null | undefined;
105
+
57
106
  /** Artists are searched by default; picking one opens its panel (see `artistUuid`). */
58
107
  type ArtistSearchProps = {
59
108
  artistSearch: false;
@@ -85,6 +134,10 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
85
134
  onSelectedNodeIdChange?: (nodeId: string | null) => void;
86
135
  /** Mount-only seed for the pan/zoom (wheel, pop-core and stacked views), replacing the initial fit. */
87
136
  defaultViewport?: GenreTreeViewport;
137
+ /** Restore silently after layout; use a new revision for each navigation. Supply focus, selection,
138
+ * and restoration together. User interaction supersedes a pending request. In stacked view,
139
+ * the selected genre must resolve to a root; outline ignores restoration. */
140
+ viewportRestore?: GenreTreeProps["viewportRestore"];
88
141
  /** Fires after a user-driven pan/zoom settles. In stacked view, only the tree holding the selection reports. */
89
142
  onViewportChange?: (viewport: GenreTreeViewport) => void;
90
143
  /** When true, hides the "Add root" button and suppresses per-node
@@ -102,8 +155,8 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
102
155
  criteriaOverviewSchema?: z.ZodType<O, unknown>;
103
156
  /** Consumer-specific rows rendered in the info panel after Summary. */
104
157
  renderGenreDetailExtras?: (overview: O) => ReactNode;
105
- /** Layout of the wheel/tree floating zoom/pop/fit controls. Defaults to "horizontal". */
106
- controlsOrientation?: ControlsOrientation;
158
+ /** Host control displayed before the size metric in the floating controls. */
159
+ floatingControlsLeading?: ReactNode;
107
160
  /** Controlled size metric of the wheel and pop-core nodes: song count or summed song views. Uncontrolled when omitted
108
161
  * (starts on songs). The toggle is hidden while an artist filters the tree, which only has song counts. */
109
162
  sizeMetric?: GenreTreeSizeMetric;
@@ -121,12 +174,88 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
121
174
  /** Controlled expanded state of the artist panel. Uncontrolled, it resets to compact on each artist. */
122
175
  artistExpanded?: boolean;
123
176
  onArtistExpandedChange?: (expanded: boolean) => void;
177
+ /** Resolves a selected genre or artist to a share URL using the host app's routes. */
178
+ getShareUrl?: GetGenreTreeShareUrl;
124
179
  } & ArtistSearchProps & GenreDetailSlots<O> & GenrePlaylistTracksSlots & InfoPanelCurationProps & GenreFocusProps & {
125
180
  /** Hides the info panel behind its edge tab; uncontrolled when omitted. Only applies with `placement="regions"`. */
126
181
  collapsed?: boolean;
127
182
  onCollapsedChange?: (collapsed: boolean) => void;
128
183
  };
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;
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;
185
+
186
+ type GenreActionBandProps = {
187
+ node: GenreTreeNode;
188
+ /** When set, shows the Play/Pause action. */
189
+ onPlayPause?: () => void;
190
+ isPlaying?: boolean;
191
+ /** When set, shows the "Zoom in" action. */
192
+ onZoomIn?: () => void;
193
+ /** When set, shows the "Add" (sub-genre) action. */
194
+ onAddChild?: () => void;
195
+ /** When set, shows the "Rename" action. */
196
+ onRename?: () => void;
197
+ /** When set, shows the "Parent" (change parent) action. */
198
+ onChangeParent?: () => void;
199
+ side?: "core" | "pop";
200
+ /** When set, shows the "Make pop" / "Make core" action. */
201
+ onToggleSide?: () => void;
202
+ /** When set, disables the side toggle and explains why in its tooltip. */
203
+ toggleSideDisabledReason?: string;
204
+ additionalActions?: (node: GenreTreeNode) => GenreTreeAction[];
205
+ shareTarget?: GenreTreeShareTarget;
206
+ shareUrl?: string;
207
+ };
208
+ declare function GenreActionBand({ node, onPlayPause, isPlaying, onZoomIn, onAddChild, onRename, onChangeParent, side, onToggleSide, toggleSideDisabledReason, additionalActions, shareTarget, shareUrl, }: GenreActionBandProps): react.JSX.Element | null;
209
+
210
+ /** What the action band reads of a genre's playlist: enough to play it and to know whether it can be. */
211
+ type PlayableGenrePlaylist = CriteriaPlaylistMinimum & {
212
+ tracksCount: number;
213
+ };
214
+ type GenreNodeActionBandProps = Omit<ComponentProps<typeof GenreActionBand>, "onPlayPause" | "isPlaying"> & {
215
+ genrePlaylist: PlayableGenrePlaylist;
216
+ playback: GenrePlaylistPlayback;
217
+ };
218
+ declare function GenreNodeActionBand({ genrePlaylist, playback, ...bandProps }: GenreNodeActionBandProps): react.JSX.Element;
219
+ type GenreInfoPanelBodyProps<O extends CriteriaOverview> = {
220
+ /** genre-tree-view's toolbar section, rendered first. */
221
+ toolbar?: ReactNode;
222
+ family: ReactNode;
223
+ genreUuid: GenreUuid;
224
+ genrePlaylist: PlayableGenrePlaylist;
225
+ wikidataId?: string | null;
226
+ scope: Scope;
227
+ getBackendBaseUrl: () => string;
228
+ playback: GenrePlaylistPlayback;
229
+ /** Action band props other than play, which the body derives from `playback`. */
230
+ band: Omit<GenreNodeActionBandProps, "genrePlaylist" | "playback">;
231
+ extras?: Omit<GenreDetailExtrasProps<O>, "family" | "genreUuid" | "genrePlaylist" | "wikidataId" | "scope" | "getBackendBaseUrl">;
232
+ };
233
+ /** The body of a genre's info panel: action band, then the genre's overview, family and tracks.
234
+ * Shared by the tree's panel and the standalone GenreDetailPanel. */
235
+ declare function GenreInfoPanelBody<O extends CriteriaOverview>({ toolbar, family, genreUuid, genrePlaylist, wikidataId, scope, getBackendBaseUrl, playback, band, extras, }: GenreInfoPanelBodyProps<O>): react.JSX.Element;
236
+
237
+ type GenreDetailPanelProps<O extends CriteriaOverview> = {
238
+ genreUuid: GenreUuid;
239
+ scope: Scope;
240
+ getBackendBaseUrl: () => string;
241
+ onClose: () => void;
242
+ /** Fired with the uuid of a family or ancestor chip the user picked. */
243
+ onSelectGenre: (genreUuid: GenreUuid) => void;
244
+ /** When set, the panel offers "Focus". */
245
+ onZoomIn?: () => void;
246
+ /** Absolute or relative URL that shares this genre; the share action shows only when set. */
247
+ shareUrl?: string;
248
+ shareTarget?: GenreTreeShareTarget;
249
+ collapsed?: boolean;
250
+ expanded?: boolean;
251
+ onExpandedChange?: (expanded: boolean) => void;
252
+ extras?: GenreInfoPanelBodyProps<O>["extras"];
253
+ };
254
+ /** A genre's detail panel driven by one uuid, with no tree behind it. The tree's own panel shares its body. */
255
+ declare function GenreDetailPanel<O extends CriteriaOverview>({ genreUuid, scope, getBackendBaseUrl, onClose, onSelectGenre, onZoomIn, shareUrl, shareTarget, collapsed, expanded, onExpandedChange, extras, }: GenreDetailPanelProps<O>): react.JSX.Element;
256
+
257
+ /** Gives the brand layout tokens a zero-width left column while no left detail panel is showing. */
258
+ declare function useLeftDetailPanelLayout(isPanelShown: boolean, enabled?: boolean): void;
130
259
 
131
260
  type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
132
261
  scope: Scope;
@@ -152,16 +281,16 @@ type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
152
281
  /** When false, suppresses the hover toolbar on every node. Defaults to true. */
153
282
  showToolbar?: boolean;
154
283
  wheelZoom?: WheelZoomMode;
155
- /** Layout of the floating zoom/pop/fit controls. Defaults to "vertical". */
156
- controlsOrientation?: ControlsOrientation;
157
284
  /** Mount-only seed for the pan/zoom, replacing the initial fit. */
158
285
  defaultViewport?: GenreTreeViewport;
286
+ /** Runtime viewport restoration request. */
287
+ viewportRestore?: GenreTreeProps["viewportRestore"];
159
288
  /** Fires after a user-driven pan/zoom settles. */
160
289
  onViewportChange?: (viewport: GenreTreeViewport) => void;
161
290
  /** What node play buttons play; defaults to each node's whole genre playlist. */
162
291
  playback?: GenrePlaylistPlayback;
163
292
  } & InfoPanelCurationProps;
164
- declare function GenrePlaylistTreePerRoot<T extends TrackBase>({ scope, className, rootUuid, genrePlaylistTreePerRoot, handleGenreCreationAction, handleGenreRenameAction, additionalActions, onNodeClick, onNodeHover, renderInfoPanelBody, selectedNodeId, onSelectedNodeChange, hideInfoPanelClose, hideInfoPanelTitle, readOnly, showToolbar, wheelZoom, controlsOrientation, defaultViewport, onViewportChange, playback, ...infoPanelCuration }: GenrePlaylistTreePerRootProps<T>): react.JSX.Element;
293
+ 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;
165
294
 
166
295
  type GenreSearchExtraResults = {
167
296
  /** Heading of this group; genre matches then get a "Genres" heading above it. */
@@ -198,16 +327,20 @@ interface TrackListSidebarProps<T extends TrackBase> {
198
327
  renderDuration?: (track: T) => ReactNode;
199
328
  renderActions?: (track: T) => ReactNode;
200
329
  layout?: "fixed" | "inline";
330
+ isCollapsed?: boolean;
331
+ onExpand?: () => void;
332
+ onMinimize?: () => void;
201
333
  /** When set, artist names render through it (e.g. a router `Link`). */
202
334
  renderArtistLink?: RenderArtistLink;
203
335
  }
204
- declare function TrackListSidebar<T extends TrackBase>({ className, renderDuration, renderActions, layout, renderArtistLink, }: TrackListSidebarProps<T>): react.JSX.Element | null;
336
+ declare function TrackListSidebar<T extends TrackBase>({ className, renderDuration, renderActions, layout, isCollapsed, onExpand, onMinimize, renderArtistLink, }: TrackListSidebarProps<T>): react.JSX.Element | null;
205
337
 
206
338
  interface PlayerPanelProps {
207
339
  className?: string;
208
- /** Hidden via CSS, never unmounted, so the video keeps playing while the panel is closed. */
340
+ /** Hidden via CSS, never unmounted, so the video keeps playing while the host hides the panel. */
209
341
  isVisible?: boolean;
210
- onClose: () => void;
342
+ /** @deprecated The player no longer closes; its header action minimizes to video-only mode. */
343
+ onClose?: () => void;
211
344
  /** Controlled extended state; uncontrolled when omitted. */
212
345
  expanded?: boolean;
213
346
  onExpandedChange?: (expanded: boolean) => void;
@@ -215,7 +348,7 @@ interface PlayerPanelProps {
215
348
  extendedWidth?: string;
216
349
  renderArtistLink?: RenderArtistLink;
217
350
  }
218
- declare function PlayerPanel({ className, isVisible, onClose, expanded, onExpandedChange, extendedWidth, renderArtistLink, }: PlayerPanelProps): react.JSX.Element;
351
+ declare function PlayerPanel({ className, isVisible, expanded, onExpandedChange, extendedWidth, renderArtistLink, }: PlayerPanelProps): react.JSX.Element;
219
352
 
220
353
  interface TrackItemProps<T extends TrackBase> {
221
354
  track: T;
@@ -231,8 +364,9 @@ interface TrackPositionPlayPauseProps {
231
364
  position: number;
232
365
  uuid: string;
233
366
  handlePlayPauseClick: (event: React.MouseEvent) => void;
367
+ isSelected?: boolean;
234
368
  }
235
- declare function TrackPositionPlayPause({ position, uuid, handlePlayPauseClick }: TrackPositionPlayPauseProps): react.JSX.Element;
369
+ declare function TrackPositionPlayPause({ position, uuid, handlePlayPauseClick, isSelected, }: TrackPositionPlayPauseProps): react.JSX.Element;
236
370
 
237
371
  interface RatingProps {
238
372
  rating: number | null | undefined;
@@ -259,4 +393,4 @@ interface PanelEdgeTabProps {
259
393
  }
260
394
  declare function PanelEdgeTab({ side, edge, center, controls, className }: PanelEdgeTabProps): react.JSX.Element | null;
261
395
 
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 };
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 };
@@ -1,4 +1,7 @@
1
1
  import {
2
+ GenreDetailPanel,
3
+ GenreInfoPanelBody,
4
+ GenreNodeActionBand,
2
5
  GenrePlaylistTreePerRoot,
3
6
  GenreSearch,
4
7
  GenreTreeOutlineSkeleton,
@@ -11,10 +14,15 @@ import {
11
14
  Rating,
12
15
  TrackItem,
13
16
  TrackListSidebar,
14
- TrackPositionPlayPause
15
- } from "../chunk-KPWV4LEJ.js";
16
- import "../chunk-VDXUW7PP.js";
17
+ TrackPositionPlayPause,
18
+ useGenrePlaylistPlayback,
19
+ useLeftDetailPanelLayout
20
+ } from "../chunk-TFLX6C5Q.js";
21
+ import "../chunk-DKQQLLMY.js";
17
22
  export {
23
+ GenreDetailPanel,
24
+ GenreInfoPanelBody,
25
+ GenreNodeActionBand,
18
26
  GenrePlaylistTreePerRoot,
19
27
  GenreSearch,
20
28
  GenreTreeOutlineSkeleton,
@@ -27,6 +35,8 @@ export {
27
35
  Rating,
28
36
  TrackItem,
29
37
  TrackListSidebar,
30
- TrackPositionPlayPause
38
+ TrackPositionPlayPause,
39
+ useGenrePlaylistPlayback,
40
+ useLeftDetailPanelLayout
31
41
  };
32
42
  //# sourceMappingURL=index.js.map
@@ -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
+