diff --git a/package-lock.json b/package-lock.json index 7d905124..f280a09c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 116671d4..01529e93 100644 --- a/package.json +++ b/package.json @@ -246,5 +246,7 @@ "webpack": "4.44.2", "webpack-cli": "3.3.12" }, - "dependencies": {} + "dependencies": { + "downshift": "6.0.6" + } } diff --git a/src/extension.ts b/src/extension.ts index be19004b..2f0240e7 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -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', () => { diff --git a/src/viewpanel/Command.ts b/src/viewpanel/Command.ts index 829ae865..33ce40d3 100644 --- a/src/viewpanel/Command.ts +++ b/src/viewpanel/Command.ts @@ -1,5 +1,7 @@ export enum Command { loading = "loading", metadata = "metadata", - settings = "settings" + settings = "settings", + focusOnTags = "focusOnTags", + focusOnCategories = "focusOnCategories" } \ No newline at end of file diff --git a/src/viewpanel/ViewPanel.tsx b/src/viewpanel/ViewPanel.tsx index e51bd77e..716b780c 100644 --- a/src/viewpanel/ViewPanel.tsx +++ b/src/viewpanel/ViewPanel.tsx @@ -10,7 +10,7 @@ export interface IViewPanelProps { } export const ViewPanel: React.FunctionComponent = (props: React.PropsWithChildren) => { - const { loading, metadata, settings } = useMessages(); + const { loading, metadata, settings, focusElm, unsetFocus } = useMessages(); if (loading) { return ( @@ -35,10 +35,24 @@ export const ViewPanel: React.FunctionComponent = (props: React settings && metadata && } { - (settings && settings.tags && settings.tags.length > 0) && + (settings && settings.tags && settings.tags.length > 0) && ( + + ) } { - (settings && settings.categories && settings.categories.length > 0) && + (settings && settings.categories && settings.categories.length > 0) && ( + + ) } ); diff --git a/src/viewpanel/components/TagPicker.tsx b/src/viewpanel/components/TagPicker.tsx index 6f78e577..a9874590 100644 --- a/src/viewpanel/components/TagPicker.tsx +++ b/src/viewpanel/components/TagPicker.tsx @@ -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 = (props: React.PropsWithChildren) => { - const { type, crntSelected, options, freeform } = props; + const { type, crntSelected, options, freeform, focussed, unsetFocus } = props; const [ selected, setSelected ] = React.useState([]); const prevSelected = usePrevious(crntSelected); + const inputRef = React.useRef(null); const { getRootProps, getInputProps, getListboxProps, getOptionProps, groupedOptions } = useAutocomplete({ id: 'use-autocomplete', @@ -47,6 +55,18 @@ export const TagPicker: React.FunctionComponent = (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 = (props: React return (
-

{type}

+

{type} - {focussed ? 'true' : 'false'}

+ + + + alert(selection ? `You selected ${selection.value}` : 'Selection Cleared') + } + itemToString={item => (item ? item.value : '')}> + {({ + getInputProps, + getItemProps, + getMenuProps, + isOpen, + inputValue, + highlightedIndex, + selectedItem, + getRootProps, + openMenu + }) => ( + <> +
+ +
+
    + { isOpen ? options.filter(item => !inputValue || item.includes(inputValue)).map((item, index) => ( +
  • + {item} +
  • + )) + : null} +
+ + )} +
- +
{ diff --git a/src/viewpanel/hooks/useMessages.tsx b/src/viewpanel/hooks/useMessages.tsx index 59804c85..083c8a23 100644 --- a/src/viewpanel/hooks/useMessages.tsx +++ b/src/viewpanel/hooks/useMessages.tsx @@ -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({}); const [settings, setSettings] = useState(); const [loading, setLoading] = useState(false); + const [focusElm, setFocus] = useState(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) } }; } \ No newline at end of file diff --git a/src/webview/ExplorerView.ts b/src/webview/ExplorerView.ts index 18a4b6be..c08cea0c 100644 --- a/src/webview/ExplorerView.ts +++ b/src/webview/ExplorerView.ts @@ -54,6 +54,8 @@ export class ExplorerView implements WebviewViewProvider, Disposable { */ public async resolveWebviewView(webviewView: WebviewView, context: WebviewViewResolveContext, token: CancellationToken): Promise { + 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); }