#307 - List field

This commit is contained in:
Elio Struyf
2022-06-02 09:19:06 +02:00
parent daeaf0a59d
commit bd2860e225
7 changed files with 161 additions and 10 deletions
+1
View File
@@ -4,6 +4,7 @@
### 🎨 Enhancements
- [#307](https://github.com/estruyf/vscode-front-matter/issues/307): New `list` field which allows to create a list of items
- [#345](https://github.com/estruyf/vscode-front-matter/issues/345): Media dashboard UI improvements to visualize the content and public folders
### 🐞 Fixes
+1
View File
@@ -806,6 +806,7 @@
"fields",
"json",
"block",
"list",
"dataFile"
],
"description": "Define the type of field"
+1 -1
View File
@@ -48,7 +48,7 @@ export interface ContentType {
pageBundle?: boolean;
}
export type FieldType = "string" | "number" | "datetime" | "boolean" | "image" | "choice" | "tags" | "categories" | "draft" | "taxonomy" | "fields" | "json" | "block" | "file" | "dataFile";
export type FieldType = "string" | "number" | "datetime" | "boolean" | "image" | "choice" | "tags" | "categories" | "draft" | "taxonomy" | "fields" | "json" | "block" | "file" | "dataFile" | "list";
export interface Field {
title?: string;
@@ -198,7 +198,7 @@ export const DataBlockField: React.FunctionComponent<IDataBlockFieldProps> = ({
}
return (
<div className='json_data__field'>
<div className='block_field'>
<VsLabel>
<div className={`metadata_field__label`}>
@@ -0,0 +1,126 @@
import { PencilIcon, TrashIcon, ViewListIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useCallback, useRef } from 'react';
import { VsLabel } from '../VscodeComponents';
export interface IListFieldProps {
label: string;
value: string[] | null;
onChange: (value: string | string[]) => void;
}
export const ListField: React.FunctionComponent<IListFieldProps> = ({ label, value, onChange }: React.PropsWithChildren<IListFieldProps>) => {
const [ text, setText ] = React.useState<string | null>("");
const [ list, setList ] = React.useState<string[] | null>(value);
const [ itemToEdit, setItemToEdit ] = React.useState<number | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
const onTextChange = (txtValue: string) => {
setText(txtValue);
// onChange(txtValue);
};
const onSaveForm = useCallback(() => {
if (itemToEdit !== null) {
if (list && text) {
const newList = [...(list || [])];
newList[itemToEdit] = text;
setList(newList);
onChange(newList);
}
} else {
if (text) {
const newList = list ? [ ...list, text ] : [ text ];
setList(newList);
onChange(newList);
}
}
onCancelForm();
}, [list, text]);
const onEdit = useCallback((index: number) => {
setItemToEdit(index);
setText(list ? list[index] : "");
inputRef.current?.focus();
}, [list]);
const onCancelForm = useCallback(() => {
setText("");
setItemToEdit(null);
}, []);
const onDelete = useCallback((index: number) => {
if (list) {
const newList = [...list];
newList.splice(index, 1);
setList(newList);
onChange(newList);
}
}, [list]);
let isValid = true;
return (
<div className={`list_field`}>
<VsLabel>
<div className={`metadata_field__label`}>
<ViewListIcon style={{ width: "16px", height: "16px" }} /> <span style={{ lineHeight: "16px"}}>{label}</span>
</div>
</VsLabel>
<input
ref={inputRef}
className={`metadata_field__input`}
value={text || ""}
onChange={(e) => onTextChange(e.currentTarget.value)}
onKeyDown={(e) => {
if (e.key === "Enter") {
onSaveForm();
}
}}
style={{
border: isValid ? "1px solid var(--vscode-inputValidation-infoBorder)" : "1px solid var(--vscode-inputValidation-warningBorder)"
}} />
<div className={`list_field__form__buttons`}>
<button
className={`list_field__form__button__save`}
title={`Save`}
onClick={onSaveForm}
disabled={!text}>
{itemToEdit !== null ? `Update` : `Add`}
</button>
<button
className={`list_field__form__button__cancel`}
title={`Cancel`}
onClick={onCancelForm}>
Cancel
</button>
</div>
<ul className='list_field__list'>
{
list && list.map((item, index) => (
<li className='list_field__list__item' key={index}>
<div>
{item}
</div>
<div>
<button title='Edit record' className='list_field__list__button list_field__list__button_edit' onClick={() => onEdit(index)}>
<PencilIcon className='list_field__list__button_icon' />
<span className='sr-only'>Edit</span>
</button>
<button title='Delete record' className='list_field__list__button list_field__list__button_delete' onClick={() => onDelete(index)}>
<TrashIcon className='list_field__list__button_icon' />
<span className='sr-only'>Delete</span>
</button>
</div>
</li>
))
}
</ul>
</div>
);
};
@@ -21,6 +21,7 @@ import { DataFileField } from './DataFileField';
import { DateTimeField } from './DateTimeField';
import { DraftField } from './DraftField';
import { FileField } from './FileField';
import { ListField } from './ListField';
import { NumberField } from './NumberField';
import { PreviewImageField, PreviewImageValue } from './PreviewImageField';
import { TextField } from './TextField';
@@ -360,6 +361,15 @@ export const WrapperField: React.FunctionComponent<IWrapperFieldProps> = ({
onChange={(value => onSendUpdate(field.name, value, parentFields))} />
</FieldBoundary>
);
} else if (field.type === 'list') {
return (
<FieldBoundary key={field.name} fieldName={field.title || field.name}>
<ListField
label={field.title || field.name}
value={fieldValue}
onChange={(value => onSendUpdate(field.name, value, parentFields))} />
</FieldBoundary>
);
} else {
console.warn(`Unknown field type: ${field.type}`);
return null;
+21 -8
View File
@@ -1,11 +1,13 @@
.block_field__form {
.block_field__form,
.list_field__form {
background-color: transparent;
border: 1px dashed var(--vscode-button-background);
padding: 1rem;
}
.block_field__form__buttons {
.block_field__form__buttons,
.list_field__form__buttons {
display: flex;
justify-content: space-between;
margin-top: 1rem;
@@ -23,7 +25,8 @@
}
}
.block_field__form__button__save {
.block_field__form__button__save,
.list_field__form__button__save {
background-color: var(--vscode-button-background);
color: var(--vscode-button-foreground);
@@ -32,7 +35,8 @@
}
}
.block_field__form__button__cancel {
.block_field__form__button__cancel,
.list_field__form__button__cancel {
background-color: var(--vscode-button-secondaryBackground);
color: var(--vscode-button-secondaryForeground);
@@ -41,7 +45,9 @@
}
}
.json_data__field {
.json_data__field,
.block_field,
.list_field {
border: 1px dashed var(--vscode-button-secondaryBackground);
color: var(--vscode--settings-headerForeground);
padding: .5rem 1rem;
@@ -120,7 +126,12 @@
}
}
.json_data__list {
.list_field__list {
padding-left: 0;
}
.json_data__list,
.list_field__list {
margin-top: 1rem;
label {
@@ -229,7 +240,8 @@
}
.sortable_item button,
.json_data__list__button {
.json_data__list__button,
.list_field__list__button {
width: auto;
background: none;
color: inherit;
@@ -244,7 +256,8 @@
}
}
.json_data__list__button_icon {
.json_data__list__button_icon,
.list_field__list__button_icon {
height: 1rem;
width: 1rem;
}