Snippet variables

This commit is contained in:
Elio Struyf
2022-03-04 09:08:25 +01:00
parent eeb1fc9cb4
commit 4065019525
7 changed files with 112 additions and 44 deletions
+7
View File
@@ -0,0 +1,7 @@
export const SnippetVariables = {
FM_SELECTED_TEXT: 'FM_SELECTED_TEXT',
FM_TEXT: 'FM_TEXT_',
FM_MULTILINE: 'FM_MULTILINE_',
};
+2
View File
@@ -7,7 +7,9 @@ export * from './FrameworkDetectors';
export * from './Links';
export * from './LocalStore';
export * from './Navigation';
export * from './SnippetVariables';
export * from './TelemetryEvent';
export * from './charCode';
export * from './charMap';
export * from './context';
export * from './settings';
@@ -1,5 +1,6 @@
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { SnippetVariables } from '../../../constants';
export interface INewFormProps {
title: string;
@@ -84,7 +85,7 @@ export const NewForm: React.FunctionComponent<INewFormProps> = ({ title, descrip
<dl className="divide-y divide-gray-200 dark:divide-vulcan-200" style={{ zIndex: -1 }}>
<div className="py-2 flex justify-between text-xs font-medium">
<dt className="text-vulcan-100 dark:text-whisper-900">Insert selected text (can still be updated)</dt>
<dd className="text-vulcan-300 dark:text-whisper-500 text-right">{`\${selection}`}</dd>
<dd className="text-vulcan-300 dark:text-whisper-500 text-right">{`\${${SnippetVariables.FM_SELECTED_TEXT}}`}</dd>
</div>
<div className="py-2 flex justify-between text-xs font-medium">
@@ -1,11 +1,13 @@
import { Messenger } from '@estruyf/vscode/dist/client';
import { ChevronDownIcon } from '@heroicons/react/outline';
import * as React from 'react';
import { useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { SnippetVariables } from '../../../constants';
import { Choice, SnippetParser, Variable, VariableResolver } from '../../../helpers/SnippetParser';
import { SnippetField } from '../../../models';
import { DashboardMessage } from '../../DashboardMessage';
import { ViewDataSelector } from '../../state';
import { SnippetInputField } from './SnippetInputField';
export interface ISnippetFormProps {
@@ -17,14 +19,6 @@ export interface SnippetFormHandle {
onSave: () => void;
}
interface SnippetField {
name: string;
value: string;
type: 'text' | 'select';
tmString: string;
options?: string[];
}
const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFormProps> = ({ snippet, selection }, ref) => {
const viewData = useRecoilValue(ViewDataSelector);
const [ fields, setFields ] = useState<SnippetField[]>([]);
@@ -33,8 +27,8 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
setFields(prevFields => prevFields.map(f => f.name === field.name ? { ...f, value } : f));
}, [setFields]);
const insertSelectionValue = useCallback((fieldName: string) => {
if (selection && fieldName === 'selection') {
const insertSelectionValue = useCallback((value: string) => {
if (selection && value === SnippetVariables.FM_SELECTED_TEXT) {
return selection;
}
@@ -59,6 +53,22 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
return true;
}
const fieldNameRender = (fieldName: string) => {
if (fieldName === SnippetVariables.FM_SELECTED_TEXT) {
return 'Selected Text';
}
if (fieldName.startsWith(SnippetVariables.FM_TEXT)) {
return fieldName.replace(SnippetVariables.FM_TEXT, '');
}
if (fieldName.startsWith(SnippetVariables.FM_MULTILINE)) {
return fieldName.replace(SnippetVariables.FM_MULTILINE, '');
}
return fieldName;
}
useImperativeHandle(ref, () => ({
onSave() {
if (!snippetBody) {
@@ -73,6 +83,16 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
}));
useEffect(() => {
// Defines the type of field that needs to be rendered
const getFieldType = (fieldName: string) => {
if (fieldName.startsWith(SnippetVariables.FM_MULTILINE)) {
return 'textarea';
}
return 'text';
}
// Get all placeholder variables from the snippet
const snippetParser = new SnippetParser();
const body = typeof snippet.body === "string" ? snippet.body : snippet.body.join(`\n`);
@@ -84,16 +104,16 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
for (const placeholder of placeholders) {
const tmString = placeholder.toTextmateString();
console.log(tmString)
// If only a variable is defined, it will not contain children
if (placeholder.children.length === 0) {
allFields.push({
type: 'text',
type: getFieldType(placeholder.index as string),
name: placeholder.index,
value: insertSelectionValue(placeholder.index as string) || '',
tmString
});
} else {
// Children are defined, so it means it is a choice field or the variable has a default value
for (const child of placeholder.children as any[]) {
if (child instanceof Choice) {
const options = child.options.map(o => o.value);
@@ -107,9 +127,9 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
});
} else {
allFields.push({
type: 'text',
type: getFieldType(placeholder.index as string),
name: placeholder.index,
value: insertSelectionValue(placeholder.index as string) || (child as any).value || "",
value: insertSelectionValue((child as any).value as string) || insertSelectionValue(placeholder.index as string) || (child as any).value || "",
tmString
});
}
@@ -132,35 +152,12 @@ const SnippetForm: React.ForwardRefRenderFunction<SnippetFormHandle, ISnippetFor
shouldShowField(field.name, index, allFields) && (
<div key={index}>
<label htmlFor={field.name} className="block text-sm font-medium capitalize">
{field.name}
{fieldNameRender(field.name)}
</label>
<div className="mt-1">
{
field.type === 'select' ? (
<div className='relative'>
<select
name={field.name}
value={field.value || ""}
className="focus:outline-none block w-full sm:text-sm border-gray-300 text-vulcan-500"
onChange={e => onTextChange(field, e.target.value)}>
{
field.options?.map((option: string, index: number) => (
<option key={index} value={option}>{option}</option>
))
}
</select>
<ChevronDownIcon className="absolute top-3 right-2 w-4 h-4 text-gray-500" />
</div>
) : (
<textarea
name={field.name}
value={field.value || ""}
className="focus:outline-none block w-full sm:text-sm border-gray-300 text-vulcan-500"
onChange={(e) => onTextChange(field, e.currentTarget.value)}
/>
)
}
<SnippetInputField
field={field}
onValueChange={onTextChange} />
</div>
</div>
)
@@ -0,0 +1,53 @@
import * as React from 'react';
import { ChevronDownIcon } from '@heroicons/react/outline';
import { SnippetField } from '../../../models';
import { SnippetVariables } from '../../../constants';
export interface ISnippetInputFieldProps {
field: SnippetField;
onValueChange: (field: SnippetField, value: string) => void
}
export const SnippetInputField: React.FunctionComponent<ISnippetInputFieldProps> = ({ field, onValueChange }: React.PropsWithChildren<ISnippetInputFieldProps>) => {
if (field.type === 'select') {
return (
<div className='relative'>
<select
name={field.name}
value={field.value || ""}
className="focus:outline-none block w-full sm:text-sm border-gray-300 text-vulcan-500"
onChange={e => onValueChange(field, e.target.value)}>
{
field.options?.map((option: string, index: number) => (
<option key={index} value={option}>{option}</option>
))
}
</select>
<ChevronDownIcon className="absolute top-3 right-2 w-4 h-4 text-gray-500" />
</div>
)
}
if (field.type === 'textarea') {
return (
<textarea
name={field.name}
value={field.value || ""}
className="focus:outline-none block w-full sm:text-sm border-gray-300 text-vulcan-500"
onChange={(e) => onValueChange(field, e.currentTarget.value)}
/>
)
}
return (
<input
type="text"
name={field.name}
value={field.value || ""}
className="focus:outline-none block w-full sm:text-sm border-gray-300 text-vulcan-500"
onChange={(e) => onValueChange(field, e.currentTarget.value)}
/>
);
};
+7
View File
@@ -0,0 +1,7 @@
export interface SnippetField {
name: string;
value: string;
type: 'text' | 'textarea' | 'select';
tmString: string;
options?: string[];
}
+1
View File
@@ -10,6 +10,7 @@ export * from './DraftField';
export * from './Framework';
export * from './MediaPaths';
export * from './PanelSettings';
export * from './SnippetField';
export * from './SortOrder';
export * from './SortType';
export * from './SortingSetting';