mirror of
https://github.com/FoxxMD/multi-scrobbler.git
synced 2026-10-19 15:26:33 +00:00
feat: placeholder input mode switch
This commit is contained in:
parent
1308719e18
commit
c06b13a8b9
5 changed files with 42 additions and 12 deletions
|
|
@ -35,7 +35,10 @@ import {
|
|||
LuBraces,
|
||||
LuText,
|
||||
LuCode,
|
||||
LuPlus
|
||||
LuPlus,
|
||||
LuSearch,
|
||||
LuTextCursor,
|
||||
LuCaseUpper
|
||||
} from "react-icons/lu"
|
||||
import { VscDebugRestart } from 'react-icons/vsc';
|
||||
import { HiMiniStop } from "react-icons/hi2";
|
||||
|
|
@ -332,4 +335,16 @@ export const CodeButton = makeIconButton(LuCode);
|
|||
|
||||
export const PlusIconRaw = LuPlus;
|
||||
export const PlusIcon = makeChakraIcon(LuPlus);
|
||||
export const PlusButton = makeIconButton(LuPlus);
|
||||
export const PlusButton = makeIconButton(LuPlus);
|
||||
|
||||
export const SearchIconRaw = LuSearch;
|
||||
export const SearchIcon = makeChakraIcon(LuSearch);
|
||||
export const SearchButton = makeIconButton(LuSearch);
|
||||
|
||||
export const CursorIconRaw = LuTextCursor;
|
||||
export const CursorIcon = makeChakraIcon(LuTextCursor);
|
||||
export const CursorButton = makeIconButton(LuTextCursor);
|
||||
|
||||
export const LettersIconRaw = LuCaseUpper;
|
||||
export const LettersIcon = makeChakraIcon(LuCaseUpper);
|
||||
export const LettersButton = makeIconButton(LuCaseUpper);
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import { Field, HStack, Input, Stack } from "@chakra-ui/react";
|
||||
import { Field, HStack, Input, InputGroup, Stack } from "@chakra-ui/react";
|
||||
import type { FieldWithValue } from "@tanstack/react-form";
|
||||
import { useState, type ReactNode } from "react";
|
||||
import type { Credit, MusicServices } from "../../../core/Atomic"
|
||||
import { PlusButton } from "../icons/ChakraIcons";
|
||||
import { PlusButton, SearchButton, SearchIcon } from "../icons/ChakraIcons";
|
||||
import { MusicServiceInput } from "../musicServices/MusicServiceEditable";
|
||||
import { MusicServiceIndicator } from "../musicServices/MusicServiceIndicators";
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ export interface CreditInputProps {
|
|||
value?: Credit
|
||||
onChange: (val: Credit) => void
|
||||
onBlur?: () => void
|
||||
onInputSwitch?: () => void
|
||||
placeholder?: string
|
||||
/** Limits the music service types that can be chosen EX ['track'] */
|
||||
useWith?: string[]
|
||||
|
|
@ -21,6 +22,7 @@ export const CreditInput = (props: CreditInputProps) => {
|
|||
value = { name: '' },
|
||||
onChange,
|
||||
onBlur,
|
||||
onInputSwitch,
|
||||
placeholder,
|
||||
useWith
|
||||
} = props;
|
||||
|
|
@ -38,11 +40,13 @@ export const CreditInput = (props: CreditInputProps) => {
|
|||
|
||||
return (
|
||||
<Stack width="100%" gap="2">
|
||||
<Input
|
||||
value={value.name}
|
||||
placeholder={placeholder}
|
||||
onBlur={onBlur}
|
||||
onChange={(e) => onChange({ ...value, name: e.currentTarget.value })} />
|
||||
<InputGroup endElement={onInputSwitch !== undefined ? <SearchButton variant="ghost" size="xs" onClick={() => onInputSwitch()}/> : undefined}>
|
||||
<Input
|
||||
value={value.name}
|
||||
placeholder={placeholder}
|
||||
onBlur={onBlur}
|
||||
onChange={(e) => onChange({ ...value, name: e.currentTarget.value })} />
|
||||
</InputGroup>
|
||||
<HStack wrap="wrap" alignItems="normal">
|
||||
{metadata.map((x, i) => (
|
||||
<MusicServiceIndicator
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { AbsoluteCenter, Avatar, Box, Combobox, HStack, Icon, Menu, Portal, Span, Spinner, Switch, StackSeparator, Text, InputGroup, type ListCollection, Flex } from "@chakra-ui/react"
|
||||
import type { MetadataResultBase, MetadataResultImage, MetadataResultServiceScore } from "../../../core/Api"
|
||||
import { BracesIcon, getMusicServiceIconElement, TextIcon } from "../icons/ChakraIcons"
|
||||
import { BracesIcon, getMusicServiceIconElement, LettersButton, TextIcon } from "../icons/ChakraIcons"
|
||||
import React, { useRef, useState } from "react"
|
||||
import { useDebouncer } from "@tanstack/react-pacer"
|
||||
import { MSErrorBoundary } from "../ErrorBoundary"
|
||||
import { EllipsisButtonMenu } from "../buttonMenus/ButtonMenu"
|
||||
import { formatNumber } from "../../../core/DataUtils"
|
||||
import { TextMuted } from "../TextMuted"
|
||||
import { Tooltip } from "../ToggleTip"
|
||||
|
||||
export const LeftSideMetadataResultContent = (props: MetadataResultServiceScore & MetadataResultImage) => {
|
||||
if (props.image === undefined) {
|
||||
|
|
@ -83,11 +84,12 @@ export interface MetadataSearchComboboxProps<T extends MetadataSearchResult> {
|
|||
onFreetext: (name: string) => void
|
||||
/** Called when a search should run. Typing is debounced here, a context mode switch is not */
|
||||
onQueryChange: (query: string, contextMode: boolean) => void
|
||||
onInputSwitch?: () => void,
|
||||
renderItem: (item: T, onPick: (val: T) => void) => React.JSX.Element
|
||||
}
|
||||
|
||||
export const MetadataSearchCombobox = <T extends MetadataSearchResult>(props: MetadataSearchComboboxProps<T>) => {
|
||||
const { placeholder, collection, isLoading, isError, onChange, onFreetext, onQueryChange, renderItem, initialInput = '' } = props;
|
||||
const { placeholder, collection, isLoading, isError, onChange, onFreetext, onQueryChange, renderItem, initialInput = '', onInputSwitch } = props;
|
||||
|
||||
// a ref, not state: combobox captures onInteractOutside when the popup opens so state read there is stale
|
||||
const rawInput = useRef<string | undefined>(initialInput === '' ? undefined : initialInput);
|
||||
|
|
@ -144,6 +146,11 @@ export const MetadataSearchCombobox = <T extends MetadataSearchResult>(props: Me
|
|||
<HStack>Switch to {contextMode ? 'text-only' : 'context'} {modeIconElm} search mode {isLoading ? <Spinner size="xs" borderWidth="1px" /> : undefined}</HStack>
|
||||
</Combobox.Item>);
|
||||
|
||||
let plainInputSwitch: React.JSX.Element | undefined = undefined;
|
||||
if(onInputSwitch !== undefined) {
|
||||
plainInputSwitch = <Tooltip content="Switch to plain text input mode."><LettersButton variant="ghost" onClick={() => onInputSwitch()}/></Tooltip>
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex flexGrow="1">
|
||||
<MSErrorBoundary>
|
||||
|
|
@ -190,6 +197,7 @@ export const MetadataSearchCombobox = <T extends MetadataSearchResult>(props: Me
|
|||
<Combobox.IndicatorGroup>
|
||||
{hasSearched && !isLoading ? <TextMuted textStyle="xs" hideBelow="sm">{collection.size} Matches</TextMuted> : undefined}
|
||||
{!open && isLoading ? <Spinner size="xs" borderWidth="1px" /> : undefined}
|
||||
{plainInputSwitch}
|
||||
<ModeIconHint/>
|
||||
<Combobox.ClearTrigger />
|
||||
<Combobox.Trigger />
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ export type TrackOnChange = Pick<TrackData, 'track' | 'artists' | 'album' | 'alb
|
|||
export interface TrackSearchProps {
|
||||
initial?: Credit
|
||||
onChange: (val: TrackOnChange) => void
|
||||
onInputSwitch?: () => void
|
||||
contextData?: TrackDataCreditBase
|
||||
}
|
||||
|
||||
|
|
@ -169,6 +170,7 @@ export const TrackSearch = (props: TrackSearchProps) => {
|
|||
isError={query.isError}
|
||||
initialInput={selectedItem?.name}
|
||||
onChange={doChange}
|
||||
onInputSwitch={props.onInputSwitch}
|
||||
onFreetext={(name) => doChange({ track: { name } })}
|
||||
onQueryChange={search}
|
||||
renderItem={(item, onPick) => <TrackSearchResultItem data={item} onPick={onPick} />}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,8 @@ const meta = preview.type<{ args: PropsAndCustomArgs }>().meta({
|
|||
],
|
||||
args: {
|
||||
initial: undefined,
|
||||
onChange: (val) => console.log(val)
|
||||
onChange: (val) => console.log(val),
|
||||
onInputSwitch: () => console.log('changing input')
|
||||
},
|
||||
// Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#story-args
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue