mirror of
https://github.com/estruyf/vscode-front-matter.git
synced 2026-08-08 18:03:17 +02:00
New dropdown implementation
This commit is contained in:
Generated
+20
-11
@@ -34,7 +34,6 @@
|
||||
"version": "7.12.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz",
|
||||
"integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.4"
|
||||
}
|
||||
@@ -1135,6 +1134,11 @@
|
||||
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
|
||||
"dev": true
|
||||
},
|
||||
"compute-scroll-into-view": {
|
||||
"version": "1.0.16",
|
||||
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.16.tgz",
|
||||
"integrity": "sha512-a85LHKY81oQnikatZYA90pufpZ6sQx++BoCxOEMsjpZx+ZnaKGQnCyCehTRr/1p9GBIAHTjcU9k71kSYWloLiQ=="
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
@@ -1489,6 +1493,17 @@
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"downshift": {
|
||||
"version": "6.0.6",
|
||||
"resolved": "https://registry.npmjs.org/downshift/-/downshift-6.0.6.tgz",
|
||||
"integrity": "sha512-tmLab3cXCn6PtZYl9V8r/nB2m+7/nCNrwo0B3kTHo/2lRBHr+1en1VNOQt2wIt0ajanAnxquZ00WPCyxe6cNFQ==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.11.2",
|
||||
"compute-scroll-into-view": "^1.0.14",
|
||||
"prop-types": "^15.7.2",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"duplexify": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
||||
@@ -2736,8 +2751,7 @@
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||
},
|
||||
"js-yaml": {
|
||||
"version": "3.13.1",
|
||||
@@ -2908,7 +2922,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
}
|
||||
@@ -3223,8 +3236,7 @@
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||
"dev": true
|
||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
||||
},
|
||||
"object-copy": {
|
||||
"version": "0.1.0",
|
||||
@@ -3521,7 +3533,6 @@
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -3650,8 +3661,7 @@
|
||||
"react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
||||
"dev": true
|
||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.1",
|
||||
@@ -3693,8 +3703,7 @@
|
||||
"regenerator-runtime": {
|
||||
"version": "0.13.7",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
|
||||
"dev": true
|
||||
"integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
|
||||
},
|
||||
"regex-not": {
|
||||
"version": "1.0.2",
|
||||
|
||||
+3
-1
@@ -246,5 +246,7 @@
|
||||
"webpack": "4.44.2",
|
||||
"webpack-cli": "3.3.12"
|
||||
},
|
||||
"dependencies": {}
|
||||
"dependencies": {
|
||||
"downshift": "6.0.6"
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
|
||||
import { Article, Settings, StatusListener } from './commands';
|
||||
import { Template } from './commands/Template';
|
||||
import { TaxonomyType } from './models';
|
||||
import { TagType } from './viewpanel/TagType';
|
||||
import { ExplorerView } from './webview/ExplorerView';
|
||||
|
||||
let frontMatterStatusBar: vscode.StatusBarItem;
|
||||
@@ -18,12 +19,18 @@ export function activate({ subscriptions, extensionUri }: vscode.ExtensionContex
|
||||
}
|
||||
});
|
||||
|
||||
let insertTags = vscode.commands.registerCommand('frontMatter.insertTags', () => {
|
||||
Article.insert(TaxonomyType.Tag);
|
||||
let insertTags = vscode.commands.registerCommand('frontMatter.insertTags', async () => {
|
||||
// Article.insert(TaxonomyType.Tag);
|
||||
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
|
||||
await vscode.commands.executeCommand('workbench.action.focusSideBar');
|
||||
explorerSidebar.triggerInputFocus(TagType.tags);
|
||||
});
|
||||
|
||||
let insertCategories = vscode.commands.registerCommand('frontMatter.insertCategories', () => {
|
||||
Article.insert(TaxonomyType.Category);
|
||||
let insertCategories = vscode.commands.registerCommand('frontMatter.insertCategories', async () => {
|
||||
// Article.insert(TaxonomyType.Category);
|
||||
await vscode.commands.executeCommand('workbench.view.extension.frontmatter-explorer');
|
||||
await vscode.commands.executeCommand('workbench.action.focusSideBar');
|
||||
explorerSidebar.triggerInputFocus(TagType.categories);
|
||||
});
|
||||
|
||||
let createTag = vscode.commands.registerCommand('frontMatter.createTag', () => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export enum Command {
|
||||
loading = "loading",
|
||||
metadata = "metadata",
|
||||
settings = "settings"
|
||||
settings = "settings",
|
||||
focusOnTags = "focusOnTags",
|
||||
focusOnCategories = "focusOnCategories"
|
||||
}
|
||||
@@ -10,7 +10,7 @@ export interface IViewPanelProps {
|
||||
}
|
||||
|
||||
export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React.PropsWithChildren<IViewPanelProps>) => {
|
||||
const { loading, metadata, settings } = useMessages();
|
||||
const { loading, metadata, settings, focusElm, unsetFocus } = useMessages();
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -35,10 +35,24 @@ export const ViewPanel: React.FunctionComponent<IViewPanelProps> = (props: React
|
||||
settings && metadata && <Actions metadata={metadata} settings={settings} />
|
||||
}
|
||||
{
|
||||
(settings && settings.tags && settings.tags.length > 0) && <TagPicker type={TagType.tags} crntSelected={metadata.tags || []} options={settings.tags} freeform={settings.freeform} />
|
||||
(settings && settings.tags && settings.tags.length > 0) && (
|
||||
<TagPicker type={TagType.tags}
|
||||
crntSelected={metadata.tags || []}
|
||||
options={settings.tags}
|
||||
freeform={settings.freeform}
|
||||
focussed={focusElm === TagType.tags}
|
||||
unsetFocus={unsetFocus} />
|
||||
)
|
||||
}
|
||||
{
|
||||
(settings && settings.categories && settings.categories.length > 0) && <TagPicker type={TagType.categories} crntSelected={metadata.categories || []} options={settings.categories} freeform={settings.freeform} />
|
||||
(settings && settings.categories && settings.categories.length > 0) && (
|
||||
<TagPicker type={TagType.categories}
|
||||
crntSelected={metadata.categories || []}
|
||||
options={settings.categories}
|
||||
freeform={settings.freeform}
|
||||
focussed={focusElm === TagType.categories}
|
||||
unsetFocus={unsetFocus} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -5,18 +5,26 @@ import { usePrevious } from '../hooks/usePrevious';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { TagType } from '../TagType';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { TextField } from '@material-ui/core';
|
||||
import Downshift from 'downshift'
|
||||
|
||||
export interface ITagPickerProps {
|
||||
type: string;
|
||||
crntSelected: string[];
|
||||
options: string[];
|
||||
freeform: boolean;
|
||||
focussed: boolean;
|
||||
unsetFocus: () => void;
|
||||
}
|
||||
|
||||
// https://v4-2-1.material-ui.com/components/autocomplete/
|
||||
// https://github.com/downshift-js/downshift/issues/751
|
||||
|
||||
export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React.PropsWithChildren<ITagPickerProps>) => {
|
||||
const { type, crntSelected, options, freeform } = props;
|
||||
const { type, crntSelected, options, freeform, focussed, unsetFocus } = props;
|
||||
const [ selected, setSelected ] = React.useState<string[]>([]);
|
||||
const prevSelected = usePrevious(crntSelected);
|
||||
const inputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const { getRootProps, getInputProps, getListboxProps, getOptionProps, groupedOptions } = useAutocomplete({
|
||||
id: 'use-autocomplete',
|
||||
@@ -47,6 +55,18 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
|
||||
const cmdType = type === TagType.tags ? CommandToCode.updateTags : CommandToCode.updateCategories;
|
||||
MessageHelper.sendMessage(cmdType, values);
|
||||
};
|
||||
|
||||
const triggerFocus = () => {
|
||||
if (focussed && inputRef && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
setTimeout(() => {
|
||||
triggerFocus();
|
||||
}, 100);
|
||||
}, [focussed]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (prevSelected !== crntSelected) {
|
||||
@@ -56,10 +76,55 @@ export const TagPicker: React.FunctionComponent<ITagPickerProps> = (props: React
|
||||
|
||||
return (
|
||||
<div className={`article__tags`}>
|
||||
<h3>{type}</h3>
|
||||
<h3>{type} - {focussed ? 'true' : 'false'}</h3>
|
||||
|
||||
|
||||
<Downshift onChange={selection =>
|
||||
alert(selection ? `You selected ${selection.value}` : 'Selection Cleared')
|
||||
}
|
||||
itemToString={item => (item ? item.value : '')}>
|
||||
{({
|
||||
getInputProps,
|
||||
getItemProps,
|
||||
getMenuProps,
|
||||
isOpen,
|
||||
inputValue,
|
||||
highlightedIndex,
|
||||
selectedItem,
|
||||
getRootProps,
|
||||
openMenu
|
||||
}) => (
|
||||
<>
|
||||
<div
|
||||
style={{display: 'inline-block'}}
|
||||
{...getRootProps(undefined, {suppressRefError: true})}
|
||||
>
|
||||
<input {...getInputProps({ ref: inputRef, onFocus: openMenu })} onBlur={unsetFocus} />
|
||||
</div>
|
||||
<ul {...getMenuProps()}>
|
||||
{ isOpen ? options.filter(item => !inputValue || item.includes(inputValue)).map((item, index) => (
|
||||
<li
|
||||
{...getItemProps({
|
||||
key: item,
|
||||
index,
|
||||
item,
|
||||
style: {
|
||||
backgroundColor: highlightedIndex === index ? 'lightgray' : 'white',
|
||||
fontWeight: selectedItem === item ? 'bold' : 'normal',
|
||||
}
|
||||
})}
|
||||
>
|
||||
{item}
|
||||
</li>
|
||||
))
|
||||
: null}
|
||||
</ul>
|
||||
</>
|
||||
)}
|
||||
</Downshift>
|
||||
|
||||
<div {...getRootProps()}>
|
||||
<input {...getInputProps()} placeholder={`Pick your ${type.toLowerCase()}`} />
|
||||
<TextField {...getInputProps()} placeholder={`Pick your ${type.toLowerCase()}`} />
|
||||
</div>
|
||||
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@ import { PanelSettings } from '../../models/PanelSettings';
|
||||
import { Command } from '../Command';
|
||||
import { CommandToCode } from '../CommandToCode';
|
||||
import { MessageHelper } from '../helper/MessageHelper';
|
||||
import { TagType } from '../TagType';
|
||||
|
||||
const vscode = MessageHelper.getVsCodeAPI();
|
||||
|
||||
@@ -10,6 +11,7 @@ export default function useMessages() {
|
||||
const [metadata, setMetadata] = useState<any>({});
|
||||
const [settings, setSettings] = useState<PanelSettings>();
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [focusElm, setFocus] = useState<TagType | null>(null);
|
||||
|
||||
window.addEventListener('message', event => {
|
||||
const message = event.data;
|
||||
@@ -26,6 +28,12 @@ export default function useMessages() {
|
||||
case Command.loading:
|
||||
setLoading(message.data);
|
||||
break;
|
||||
case Command.focusOnTags:
|
||||
setFocus(TagType.tags);
|
||||
break;
|
||||
case Command.focusOnCategories:
|
||||
setFocus(TagType.categories);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -37,6 +45,8 @@ export default function useMessages() {
|
||||
return {
|
||||
metadata,
|
||||
settings,
|
||||
loading
|
||||
focusElm,
|
||||
loading,
|
||||
unsetFocus: () => { setFocus(null) }
|
||||
};
|
||||
}
|
||||
@@ -54,6 +54,8 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
*/
|
||||
public async resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Promise<void> {
|
||||
|
||||
console.log(context);
|
||||
|
||||
this.panel = webviewView;
|
||||
|
||||
webviewView.webview.options = {
|
||||
@@ -124,6 +126,18 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
this.postWebviewMessage({ command: Command.metadata, data: metadata });
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the webview panel to focus on tags or categories input
|
||||
* @param tagType
|
||||
*/
|
||||
public triggerInputFocus(tagType: TagType) {
|
||||
if (tagType === TagType.tags) {
|
||||
this.postWebviewMessage({ command: Command.focusOnTags });
|
||||
} else {
|
||||
this.postWebviewMessage({ command: Command.focusOnCategories });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the extension settings
|
||||
*/
|
||||
@@ -204,7 +218,7 @@ export class ExplorerView implements WebviewViewProvider, Disposable {
|
||||
* Post data to the panel
|
||||
* @param msg
|
||||
*/
|
||||
private postWebviewMessage(msg: { command: Command, data: any }) {
|
||||
private postWebviewMessage(msg: { command: Command, data?: any }) {
|
||||
this.panel!.webview.postMessage(msg);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user