@behindthemusictree/app-kit-ui 24.0.1 → 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.
- package/dist/{chunk-BPCFNBFO.js → chunk-5PQWJRDZ.js} +1160 -552
- package/dist/chunk-5PQWJRDZ.js.map +1 -0
- package/dist/chunk-DKQQLLMY.js +246 -0
- package/dist/chunk-DKQQLLMY.js.map +1 -0
- package/dist/{chunk-ZLCRD7OK.js → chunk-XUC4UJDQ.js} +2 -2
- package/dist/genre-tree/index.cjs +1352 -639
- package/dist/genre-tree/index.cjs.map +1 -1
- package/dist/genre-tree/index.d.ts +53 -10
- package/dist/genre-tree/index.js +2 -2
- package/dist/genre-tree/styles.css +80 -0
- package/dist/index.cjs +1381 -668
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -4
- package/dist/popup/index.cjs +179 -73
- package/dist/popup/index.cjs.map +1 -1
- package/dist/popup/index.d.ts +22 -2
- package/dist/popup/index.js +5 -3
- package/package.json +7 -7
- package/dist/chunk-BPCFNBFO.js.map +0 -1
- package/dist/chunk-VDXUW7PP.js +0 -141
- package/dist/chunk-VDXUW7PP.js.map +0 -1
- /package/dist/{chunk-ZLCRD7OK.js.map → chunk-XUC4UJDQ.js.map} +0 -0
|
@@ -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, 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 = {
|
|
@@ -22,8 +22,8 @@ type GenreDetailSlots<O extends CriteriaOverview> = {
|
|
|
22
22
|
renderEssentialTracks?: (overview: O) => ReactNode;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
/** Controlled
|
|
26
|
-
type InfoPanelCurationProps = Partial<Pick<GenreTreeProps, "
|
|
25
|
+
/** Controlled expand state and danger-zone slot of the info panel, plus the generic overlay panel, as typed by genre-tree-view. */
|
|
26
|
+
type InfoPanelCurationProps = Partial<Pick<GenreTreeProps, "renderDangerZone" | "expanded" | "onExpandedChange" | "collapsed" | "hideExpandButton" | "overlayPanel">>;
|
|
27
27
|
|
|
28
28
|
/** What a tree node's play button plays, and which node is the one currently playing. */
|
|
29
29
|
type GenrePlaylistPlayback = {
|
|
@@ -31,6 +31,8 @@ type GenrePlaylistPlayback = {
|
|
|
31
31
|
play: (genrePlaylist: CriteriaPlaylistSimple) => Promise<void>;
|
|
32
32
|
};
|
|
33
33
|
|
|
34
|
+
type GenreTreeSizeMetric = "songs" | "views";
|
|
35
|
+
|
|
34
36
|
/** Pop-core focus is controlled-only: pass both props or neither. Ignored in outline mode. */
|
|
35
37
|
type GenreFocusProps = {
|
|
36
38
|
focusGenreUuid?: undefined;
|
|
@@ -46,6 +48,30 @@ type GenreFocusProps = {
|
|
|
46
48
|
onFocusGenreChange: (uuid: string | null, reason: FocusChangeReason) => void;
|
|
47
49
|
};
|
|
48
50
|
|
|
51
|
+
type ParentChange = {
|
|
52
|
+
genre: GenreUuid;
|
|
53
|
+
from: GenreUuid | null;
|
|
54
|
+
to: GenreUuid;
|
|
55
|
+
};
|
|
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
|
+
|
|
49
75
|
/** Artists are searched by default; picking one opens its panel (see `artistUuid`). */
|
|
50
76
|
type ArtistSearchProps = {
|
|
51
77
|
artistSearch: false;
|
|
@@ -65,6 +91,8 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
|
|
|
65
91
|
handleGenreCreationAction: (parent: CriteriaMinimum | null) => void;
|
|
66
92
|
handleGenreRenameAction: (genre: CriteriaMinimum) => void;
|
|
67
93
|
getBackendBaseUrl: () => string;
|
|
94
|
+
/** Called after the Parent popup moved a genre; `from` and `to` are genre (criteria) uuids, `from` null for a root genre. */
|
|
95
|
+
onParentChanged?: (change: ParentChange) => void;
|
|
68
96
|
additionalActions?: (node: GenreTreeNode) => GenreTreeAction[];
|
|
69
97
|
/** Controlled view mode. When provided, the internal Stacked/Wheel toggle is not rendered — the consumer owns that UI. */
|
|
70
98
|
viewMode?: GenreTreeViewMode;
|
|
@@ -94,6 +122,12 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
|
|
|
94
122
|
renderGenreDetailExtras?: (overview: O) => ReactNode;
|
|
95
123
|
/** Layout of the wheel/tree floating zoom/pop/fit controls. Defaults to "horizontal". */
|
|
96
124
|
controlsOrientation?: ControlsOrientation;
|
|
125
|
+
/** Host control displayed before the size metric in the floating controls. */
|
|
126
|
+
floatingControlsLeading?: ReactNode;
|
|
127
|
+
/** Controlled size metric of the wheel and pop-core nodes: song count or summed song views. Uncontrolled when omitted
|
|
128
|
+
* (starts on songs). The toggle is hidden while an artist filters the tree, which only has song counts. */
|
|
129
|
+
sizeMetric?: GenreTreeSizeMetric;
|
|
130
|
+
onSizeMetricChange?: (metric: GenreTreeSizeMetric) => void;
|
|
97
131
|
/** When set, artist names in the info panel's track list render through it (e.g. a router `Link`). In the artist
|
|
98
132
|
* panel, names otherwise render as buttons that switch the panel to that artist. */
|
|
99
133
|
renderArtistLink?: RenderArtistLink;
|
|
@@ -107,12 +141,14 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
|
|
|
107
141
|
/** Controlled expanded state of the artist panel. Uncontrolled, it resets to compact on each artist. */
|
|
108
142
|
artistExpanded?: boolean;
|
|
109
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;
|
|
110
146
|
} & ArtistSearchProps & GenreDetailSlots<O> & GenrePlaylistTracksSlots & InfoPanelCurationProps & GenreFocusProps & {
|
|
111
147
|
/** Hides the info panel behind its edge tab; uncontrolled when omitted. Only applies with `placement="regions"`. */
|
|
112
148
|
collapsed?: boolean;
|
|
113
149
|
onCollapsedChange?: (collapsed: boolean) => void;
|
|
114
150
|
};
|
|
115
|
-
declare function GenreTreeView<O extends CriteriaOverview = CriteriaOverview>({ scope, handleGenreCreationAction, handleGenreRenameAction, getBackendBaseUrl, additionalActions, viewMode: controlledViewMode, selectedNodeId: controlledSelectedNodeId, onSelectedNodeIdChange, defaultViewport, onViewportChange, readOnly, className, showDisplayOptions, showAddRoot, allowWheelRotation: controlledAllowWheelRotation, criteriaOverviewSchema, renderGenreDetailExtras, controlsOrientation, renderArtistLink, artistUuid: controlledArtistUuid, onArtistUuidChange, artistExpanded: controlledArtistExpanded, onArtistExpandedChange, artistSearch, onArtistSelect, artistSearchBaseUrl, renderSummary, renderEssentialTracks, renderSongLeading, renderSongActions, renderTracksToolbar,
|
|
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;
|
|
116
152
|
|
|
117
153
|
type GenrePlaylistTreePerRootProps<T extends TrackBase> = {
|
|
118
154
|
scope: Scope;
|
|
@@ -159,6 +195,8 @@ type GenreSearchExtraResults = {
|
|
|
159
195
|
}[];
|
|
160
196
|
/** Announced politely, e.g. "No artists found". */
|
|
161
197
|
status?: string;
|
|
198
|
+
/** Shows a spinner in place of the search icon while the host resolves this group. */
|
|
199
|
+
loading?: boolean;
|
|
162
200
|
};
|
|
163
201
|
type GenreSearchProps<T extends CriteriaPlaylistSimple = CriteriaPlaylistSimple> = {
|
|
164
202
|
genrePlaylists: T[];
|
|
@@ -182,16 +220,20 @@ interface TrackListSidebarProps<T extends TrackBase> {
|
|
|
182
220
|
renderDuration?: (track: T) => ReactNode;
|
|
183
221
|
renderActions?: (track: T) => ReactNode;
|
|
184
222
|
layout?: "fixed" | "inline";
|
|
223
|
+
isCollapsed?: boolean;
|
|
224
|
+
onExpand?: () => void;
|
|
225
|
+
onMinimize?: () => void;
|
|
185
226
|
/** When set, artist names render through it (e.g. a router `Link`). */
|
|
186
227
|
renderArtistLink?: RenderArtistLink;
|
|
187
228
|
}
|
|
188
|
-
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;
|
|
189
230
|
|
|
190
231
|
interface PlayerPanelProps {
|
|
191
232
|
className?: string;
|
|
192
|
-
/** Hidden via CSS, never unmounted, so the video keeps playing while the
|
|
233
|
+
/** Hidden via CSS, never unmounted, so the video keeps playing while the host hides the panel. */
|
|
193
234
|
isVisible?: boolean;
|
|
194
|
-
|
|
235
|
+
/** @deprecated The player no longer closes; its header action minimizes to video-only mode. */
|
|
236
|
+
onClose?: () => void;
|
|
195
237
|
/** Controlled extended state; uncontrolled when omitted. */
|
|
196
238
|
expanded?: boolean;
|
|
197
239
|
onExpandedChange?: (expanded: boolean) => void;
|
|
@@ -199,7 +241,7 @@ interface PlayerPanelProps {
|
|
|
199
241
|
extendedWidth?: string;
|
|
200
242
|
renderArtistLink?: RenderArtistLink;
|
|
201
243
|
}
|
|
202
|
-
declare function PlayerPanel({ className, isVisible,
|
|
244
|
+
declare function PlayerPanel({ className, isVisible, expanded, onExpandedChange, extendedWidth, renderArtistLink, }: PlayerPanelProps): react.JSX.Element;
|
|
203
245
|
|
|
204
246
|
interface TrackItemProps<T extends TrackBase> {
|
|
205
247
|
track: T;
|
|
@@ -215,8 +257,9 @@ interface TrackPositionPlayPauseProps {
|
|
|
215
257
|
position: number;
|
|
216
258
|
uuid: string;
|
|
217
259
|
handlePlayPauseClick: (event: React.MouseEvent) => void;
|
|
260
|
+
isSelected?: boolean;
|
|
218
261
|
}
|
|
219
|
-
declare function TrackPositionPlayPause({ position, uuid, handlePlayPauseClick }: TrackPositionPlayPauseProps): react.JSX.Element;
|
|
262
|
+
declare function TrackPositionPlayPause({ position, uuid, handlePlayPauseClick, isSelected, }: TrackPositionPlayPauseProps): react.JSX.Element;
|
|
220
263
|
|
|
221
264
|
interface RatingProps {
|
|
222
265
|
rating: number | null | undefined;
|
|
@@ -243,4 +286,4 @@ interface PanelEdgeTabProps {
|
|
|
243
286
|
}
|
|
244
287
|
declare function PanelEdgeTab({ side, edge, center, controls, className }: PanelEdgeTabProps): react.JSX.Element | null;
|
|
245
288
|
|
|
246
|
-
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, 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 };
|
package/dist/genre-tree/index.js
CHANGED
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
TrackItem,
|
|
13
13
|
TrackListSidebar,
|
|
14
14
|
TrackPositionPlayPause
|
|
15
|
-
} from "../chunk-
|
|
16
|
-
import "../chunk-
|
|
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
|
+
|