mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-05-06 13:32:30 +02:00
20
src/panelWebView/components/Fields/ChoiceButton.tsx
Normal file
20
src/panelWebView/components/Fields/ChoiceButton.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import { XIcon } from '@heroicons/react/outline';
|
||||
import * as React from 'react';
|
||||
|
||||
export interface IChoiceButtonProps {
|
||||
title: string;
|
||||
value: string;
|
||||
onClick: (value: string) => void;
|
||||
}
|
||||
|
||||
export const ChoiceButton: React.FunctionComponent<IChoiceButtonProps> = ({title, value, onClick}: React.PropsWithChildren<IChoiceButtonProps>) => {
|
||||
return (
|
||||
<button
|
||||
title={`Remove ${title}`}
|
||||
className="metadata_field__choice__button"
|
||||
onClick={() => onClick(value)}>
|
||||
{title}
|
||||
<XIcon className={`metadata_field__choice__button_icon`} />
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -1,24 +1,78 @@
|
||||
import { CheckIcon } from '@heroicons/react/outline';
|
||||
import { CheckIcon, ChevronDownIcon } from '@heroicons/react/outline';
|
||||
import Downshift from 'downshift';
|
||||
import * as React from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { Choice } from '../../../models/Choice';
|
||||
import { VsLabel } from '../VscodeComponents';
|
||||
import { ChoiceButton } from './ChoiceButton';
|
||||
|
||||
export interface IChoiceFieldProps {
|
||||
label: string;
|
||||
selected: string;
|
||||
choices: string[];
|
||||
onChange: (value: string) => void;
|
||||
selected: string | string[];
|
||||
choices: string[] | Choice[];
|
||||
multiSelect?: boolean;
|
||||
onChange: (value: string | string[]) => void;
|
||||
}
|
||||
|
||||
export const ChoiceField: React.FunctionComponent<IChoiceFieldProps> = ({label, selected, choices, onChange}: React.PropsWithChildren<IChoiceFieldProps>) => {
|
||||
const [ crntSelected, setCrntSelected ] = React.useState<string | null>(selected);
|
||||
export const ChoiceField: React.FunctionComponent<IChoiceFieldProps> = ({label, selected, choices, multiSelect, onChange}: React.PropsWithChildren<IChoiceFieldProps>) => {
|
||||
const [ crntSelected, setCrntSelected ] = React.useState<string | string[] | null>(selected);
|
||||
const dsRef = React.useRef<Downshift<string> | null>(null);
|
||||
|
||||
const onValueChange = (txtValue: string) => {
|
||||
setCrntSelected(txtValue);
|
||||
onChange(txtValue);
|
||||
if (multiSelect) {
|
||||
const newValue = [...(crntSelected || []) as string[], txtValue];
|
||||
setCrntSelected(newValue);
|
||||
onChange(newValue);
|
||||
} else {
|
||||
setCrntSelected(txtValue);
|
||||
onChange(txtValue);
|
||||
}
|
||||
};
|
||||
|
||||
const containsSelected = crntSelected && choices.indexOf(crntSelected) !== -1;
|
||||
const removeSelected = (txtValue: string) => {
|
||||
if (multiSelect) {
|
||||
const newValue = [...(crntSelected || [])].filter(v => v !== txtValue);
|
||||
setCrntSelected(newValue);
|
||||
onChange(newValue);
|
||||
} else {
|
||||
setCrntSelected("");
|
||||
onChange("");
|
||||
}
|
||||
};
|
||||
|
||||
const getValue = (value: string | Choice, type: "id" | "title") => {
|
||||
if (typeof value === 'string' || typeof value === 'number') {
|
||||
return `${value}`;
|
||||
}
|
||||
return `${value[type]}`;
|
||||
};
|
||||
|
||||
const getChoiceValue = (value: string) => {
|
||||
const choice = (choices as Array<string | Choice>).find((c: string | Choice) => getValue(c, 'id') === value);
|
||||
if (choice) {
|
||||
return getValue(choice, 'title');
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (crntSelected !== selected) {
|
||||
setCrntSelected(selected);
|
||||
}
|
||||
}, [selected]);
|
||||
|
||||
const availableChoices = !multiSelect ? choices : (choices as Array<string | Choice>).filter((choice: string | Choice) => {
|
||||
const value = typeof choice === 'string' || typeof choice === 'number' ? choice : choice.id;
|
||||
|
||||
if (typeof crntSelected === 'string') {
|
||||
return crntSelected !== `${value}`;
|
||||
} else if (crntSelected instanceof Array) {
|
||||
return crntSelected.indexOf(`${value}`) === -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={`metadata_field`}>
|
||||
<VsLabel>
|
||||
@@ -26,17 +80,48 @@ export const ChoiceField: React.FunctionComponent<IChoiceFieldProps> = ({label,
|
||||
<CheckIcon style={{ width: "16px", height: "16px" }} /> <span style={{ lineHeight: "16px"}}>{label}</span>
|
||||
</div>
|
||||
</VsLabel>
|
||||
|
||||
<select
|
||||
value={crntSelected || ""}
|
||||
placeholder={`Select from your ${label}`}
|
||||
className={`metadata_field__choice`}
|
||||
onChange={(e) => onValueChange(e.currentTarget.value)}>
|
||||
{ !containsSelected && <option value='' disabled hidden></option> }
|
||||
{choices.map((choice, index) => (
|
||||
<option key={index} value={choice}>{choice}</option>
|
||||
))}
|
||||
</select>
|
||||
|
||||
<Downshift
|
||||
ref={dsRef}
|
||||
onChange={(selected) => onValueChange(selected || "")}
|
||||
itemToString={item => (item ? item : '')}>
|
||||
{({ getToggleButtonProps, getItemProps, getMenuProps, isOpen, getRootProps }) => (
|
||||
<div {...getRootProps(undefined, {suppressRefError: true})} className={`metadata_field__choice`}>
|
||||
<button
|
||||
{...getToggleButtonProps({
|
||||
className: `metadata_field__choice__toggle`,
|
||||
disabled: availableChoices.length === 0
|
||||
})}>
|
||||
<span>{`Select your ${label} value`}</span>
|
||||
<ChevronDownIcon className="icon" />
|
||||
</button>
|
||||
|
||||
<ul className={`metadata_field__choice_list ${isOpen ? "open" : "closed" }`} {...getMenuProps()}>
|
||||
{
|
||||
isOpen ? availableChoices.map((choice, index) => (
|
||||
<li {...getItemProps({
|
||||
key: getValue(choice, 'id'),
|
||||
index,
|
||||
item: getValue(choice, 'id'),
|
||||
})}>
|
||||
{ getValue(choice, 'title') || <span className={`metadata_field__choice_list__item`}>Clear value</span> }
|
||||
</li>
|
||||
)) : null
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</Downshift>
|
||||
|
||||
{
|
||||
crntSelected instanceof Array ? crntSelected.map((value: string) => (
|
||||
<ChoiceButton key={value} value={value} title={getChoiceValue(value)} onClick={removeSelected} />
|
||||
)) : (
|
||||
crntSelected && (
|
||||
<ChoiceButton key={crntSelected} value={crntSelected} title={getChoiceValue(crntSelected)} onClick={removeSelected} />
|
||||
)
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -139,6 +139,7 @@ export const Metadata: React.FunctionComponent<IMetadataProps> = ({settings, met
|
||||
label={field.title || field.name}
|
||||
selected={choiceValue as string}
|
||||
choices={choices}
|
||||
multiSelect={field.multiSelect}
|
||||
onChange={(value => sendUpdate(field.name, value))} />
|
||||
);
|
||||
} else if (field.type === 'tags') {
|
||||
|
||||
Reference in New Issue
Block a user