@behindthemusictree/app-kit-ui 41.0.0 → 42.0.1

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, 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, CriteriaMinimum, GenreCatalogueCounts, GenrePlaylistSummary } from '@behindthemusictree/app-kit/genre-tree';
6
+ import { TrackBase, CriteriaOverview, GenreUuid, CriteriaPlaylistMinimum, GenreMusicSelection, RenderArtistLink, RenderSongLink, CriteriaPlaylistSimple, ArtistSearchResult, SongSearchResult, UseGenreRemoteSearchOptions, GenreSearchResult, CriteriaMinimum, RenderPlaceLink, 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
 
@@ -321,6 +321,8 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
321
321
  /** When set, song titles in the info panel's track list render through it (e.g. a router `Link` to the song).
322
322
  * By default they are buttons that open the song panel. */
323
323
  renderSongLink?: RenderSongLink;
324
+ /** When set, the artist panel renders the artist's origin through it (e.g. a router `Link` to the map region). */
325
+ renderPlaceLink?: RenderPlaceLink;
324
326
  /** Resolves a selected genre, artist or song to a share URL using the host app's routes. */
325
327
  getShareUrl?: GetGenreTreeShareUrl;
326
328
  } & ArtistSearchProps & GenreDetailSlots<O> & GenrePlaylistTracksSlots & GenreFocusProps & {
@@ -336,7 +338,7 @@ type GenreTreeViewProps<O extends CriteriaOverview = CriteriaOverview> = {
336
338
  collapsed?: boolean;
337
339
  onCollapsedChange?: (collapsed: boolean) => void;
338
340
  };
339
- 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;
341
+ 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, renderPlaceLink, renderSummary, renderEssentialTracks, renderSongLeading, renderSongActions, renderTracksToolbar, renderDangerZone, expanded, onExpandedChange, collapsed, onCollapsedChange, overlayPanel, placement, ...focus }: GenreTreeViewProps<O>): react.JSX.Element;
340
342
 
341
343
  type GenreActionBandProps = {
342
344
  node: GenreTreeNode;
@@ -529,11 +531,12 @@ type EntityPanelOptions = {
529
531
  getShareUrl?: GetGenreTreeShareUrl;
530
532
  renderArtistLink: RenderArtistLink;
531
533
  renderSongLink: RenderSongLink;
534
+ renderPlaceLink?: RenderPlaceLink;
532
535
  song: EntityPanelState;
533
536
  artist: EntityPanelState;
534
537
  } & SongGenreNavigation;
535
538
  /** The song or artist detail overlay panel; a song takes precedence over an artist, undefined when neither is set. */
536
- declare function useEntityPanel({ scope, getBackendBaseUrl, getArtistBackendBaseUrl, getShareUrl, renderArtistLink, renderSongLink, song, artist, ...navigation }: EntityPanelOptions): OverlayPanel | undefined;
539
+ declare function useEntityPanel({ scope, getBackendBaseUrl, getArtistBackendBaseUrl, getShareUrl, renderArtistLink, renderSongLink, renderPlaceLink, song, artist, ...navigation }: EntityPanelOptions): OverlayPanel | undefined;
537
540
 
538
541
  type EntityDetailPanelProps = {
539
542
  scope: Scope;
@@ -548,11 +551,12 @@ type EntityDetailPanelProps = {
548
551
  renderArtistLink?: RenderArtistLink;
549
552
  /** Titles render as buttons that open the song by default. */
550
553
  renderSongLink?: RenderSongLink;
554
+ renderPlaceLink?: RenderPlaceLink;
551
555
  collapsed?: boolean;
552
556
  edgeTab?: ComponentProps<typeof DetailPanel>["edgeTab"];
553
557
  } & SongGenreNavigation;
554
558
  /** The song or artist detail in the shared left DetailPanel, for a host that has no genre tree. Renders nothing
555
559
  * while neither is set; a song takes precedence over an artist. */
556
- declare function EntityDetailPanel({ scope, getBackendBaseUrl, getArtistBackendBaseUrl, getShareUrl, songUuid, onSongUuidChange, artistUuid, onArtistUuidChange, renderArtistLink, renderSongLink, collapsed, edgeTab, ...navigation }: EntityDetailPanelProps): react.JSX.Element | null;
560
+ declare function EntityDetailPanel({ scope, getBackendBaseUrl, getArtistBackendBaseUrl, getShareUrl, songUuid, onSongUuidChange, artistUuid, onArtistUuidChange, renderArtistLink, renderSongLink, renderPlaceLink, collapsed, edgeTab, ...navigation }: EntityDetailPanelProps): react.JSX.Element | null;
557
561
 
558
562
  export { type ArtistSearchProps, EntityDetailPanel, type EntityDetailPanelProps, type EntityPanelOptions, type EntityPanelState, 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, type SongGenreNavigation, TrackItem, type TrackItemProps, TrackListSidebar, type TrackListSidebarProps, TrackPositionPlayPause, type TrackPositionPlayPauseProps, type UseGenrePlaylistPlaybackOptions, type UseGenreRemoteSearchGroupOptions, searchStatus, useArtistSearchGroup, useEntityPanel, useGenrePlaylistPlayback, useGenreRemoteSearchGroup, useSongSearchGroup };
@@ -23,7 +23,7 @@ import {
23
23
  useGenrePlaylistPlayback,
24
24
  useGenreRemoteSearchGroup,
25
25
  useSongSearchGroup
26
- } from "../chunk-GR4PCPCU.js";
26
+ } from "../chunk-PVYUC2PN.js";
27
27
  import "../chunk-2GH7CSCE.js";
28
28
  export {
29
29
  EntityDetailPanel,
package/dist/index.cjs CHANGED
@@ -2372,11 +2372,37 @@ function formatYears(beginYear, endYear, ended) {
2372
2372
  // src/genre-tree/artist/ArtistHeader.tsx
2373
2373
  var import_jsx_runtime41 = require("react/jsx-runtime");
2374
2374
  var countryNames = new Intl.DisplayNames(["en"], { type: "region" });
2375
- function ArtistHeader({ artist }) {
2375
+ function originLabel(artist, origin) {
2376
+ if (origin.source === "area") return "From";
2377
+ if (artist.type === "person") return "Born in";
2378
+ if (artist.type === "group") return "Formed in";
2379
+ return "From";
2380
+ }
2381
+ function OriginValue({
2382
+ origin,
2383
+ country,
2384
+ renderPlaceLink
2385
+ }) {
2386
+ const place = renderPlaceLink && origin.regionId !== null ? renderPlaceLink({ ...origin, regionId: origin.regionId }) : origin.name;
2387
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
2388
+ place,
2389
+ country && origin.kind !== "country" && `, ${country}`
2390
+ ] });
2391
+ }
2392
+ function ArtistHeader({
2393
+ artist,
2394
+ renderPlaceLink
2395
+ }) {
2376
2396
  const fields = [];
2377
2397
  if (artist.type) fields.push({ label: "Type", value: artist.type.charAt(0).toUpperCase() + artist.type.slice(1) });
2378
- const country = artist.country && countryNames.of(artist.country);
2379
- if (country) fields.push({ label: "Country", value: country });
2398
+ const country = artist.country ? countryNames.of(artist.country) : void 0;
2399
+ if (artist.origin) {
2400
+ fields.push({
2401
+ label: originLabel(artist, artist.origin),
2402
+ value: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(OriginValue, { origin: artist.origin, country, renderPlaceLink })
2403
+ });
2404
+ }
2405
+ if (country && !artist.origin) fields.push({ label: "Country", value: country });
2380
2406
  const years = formatYears(artist.beginYear, artist.endYear, artist.ended);
2381
2407
  if (years) fields.push({ label: "Active", value: years });
2382
2408
  const links = artistLinks(artist);
@@ -2665,6 +2691,7 @@ function useArtistPanel({
2665
2691
  getShareUrl,
2666
2692
  renderArtistLink,
2667
2693
  renderSongLink,
2694
+ renderPlaceLink,
2668
2695
  expanded,
2669
2696
  onExpandedChange,
2670
2697
  onClose
@@ -2715,7 +2742,7 @@ function useArtistPanel({
2715
2742
  }
2716
2743
  ),
2717
2744
  artist.image && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistPhoto, { image: artist.image, name: artist.name }),
2718
- /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistHeader, { artist }),
2745
+ /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistHeader, { artist, renderPlaceLink }),
2719
2746
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistMembers, { title: "Members", memberships: artist.members, renderArtistLink }),
2720
2747
  /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ArtistMembers, { title: "Member of", memberships: artist.memberOf, renderArtistLink }),
2721
2748
  artist.tracksCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
@@ -2923,6 +2950,7 @@ function useEntityPanel({
2923
2950
  getShareUrl,
2924
2951
  renderArtistLink,
2925
2952
  renderSongLink,
2953
+ renderPlaceLink,
2926
2954
  song,
2927
2955
  artist,
2928
2956
  ...navigation
@@ -2941,7 +2969,8 @@ function useEntityPanel({
2941
2969
  getBackendBaseUrl: getArtistBackendBaseUrl ?? getBackendBaseUrl,
2942
2970
  getShareUrl,
2943
2971
  renderArtistLink,
2944
- renderSongLink
2972
+ renderSongLink,
2973
+ renderPlaceLink
2945
2974
  });
2946
2975
  return songPanel ?? artistPanel;
2947
2976
  }
@@ -3018,6 +3047,7 @@ function GenreTreeView({
3018
3047
  songSearch = true,
3019
3048
  onSongSelect,
3020
3049
  renderSongLink,
3050
+ renderPlaceLink,
3021
3051
  renderSummary,
3022
3052
  renderEssentialTracks,
3023
3053
  renderSongLeading,
@@ -3147,6 +3177,7 @@ function GenreTreeView({
3147
3177
  getShareUrl: getShareUrlRef.current,
3148
3178
  renderArtistLink: resolvedRenderArtistLink,
3149
3179
  renderSongLink: resolvedRenderSongLink,
3180
+ renderPlaceLink,
3150
3181
  song: {
3151
3182
  uuid: songUuid,
3152
3183
  expanded: controlledSongExpanded ?? (songUuid !== null && expandedSongUuid === songUuid),
@@ -4052,6 +4083,7 @@ function EntityDetailPanel({
4052
4083
  onArtistUuidChange,
4053
4084
  renderArtistLink,
4054
4085
  renderSongLink,
4086
+ renderPlaceLink,
4055
4087
  collapsed,
4056
4088
  edgeTab,
4057
4089
  ...navigation
@@ -4063,6 +4095,7 @@ function EntityDetailPanel({
4063
4095
  getBackendBaseUrl,
4064
4096
  getArtistBackendBaseUrl,
4065
4097
  getShareUrl,
4098
+ renderPlaceLink,
4066
4099
  renderArtistLink: renderArtistLink ?? ((artist) => artist.isSpecialPurpose ? artist.name : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("button", { type: "button", onClick: () => onArtistUuidChange(artist.uuid), className: "hover:underline", children: artist.name })),
4067
4100
  renderSongLink: renderSongLink ?? ((song) => /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("button", { type: "button", onClick: () => onSongUuidChange(song.uuid), className: "hover:underline", children: song.title })),
4068
4101
  song: {