Move Menu into a separate component
This commit is contained in:
parent
e23e4a82dd
commit
e629b404a6
|
@ -9,58 +9,6 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
.menu {
|
|
||||||
font-family: $font-family-menu;
|
|
||||||
font-size: $font-size;
|
|
||||||
background: $theme-color;
|
|
||||||
color: $white;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.button {
|
|
||||||
width: $menu-button-size;
|
|
||||||
height: $menu-button-size;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
color: white;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background: rgba(255, 255, 255, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:disabled {
|
|
||||||
color: rgba(255, 255, 255, 0.5);
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.space {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.separator {
|
|
||||||
$margin: 3px;
|
|
||||||
|
|
||||||
background: rgba(255, 255, 255, 0.15);
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 1px;
|
|
||||||
height: $menu-button-size - 2 * $margin;
|
|
||||||
margin: $margin;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box-container {
|
|
||||||
position: absolute;
|
|
||||||
top: 100%;
|
|
||||||
right: $search-box-offset + 20px; // keep space for scrollbar
|
|
||||||
margin-top: $search-box-offset;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.hidden-input-label {
|
.hidden-input-label {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
|
|
@ -13,9 +13,6 @@
|
||||||
SCROLL_DURATION,
|
SCROLL_DURATION,
|
||||||
STATE_PROPS
|
STATE_PROPS
|
||||||
} from './constants.js'
|
} from './constants.js'
|
||||||
import SearchBox from './SearchBox.svelte'
|
|
||||||
import Icon from 'svelte-awesome'
|
|
||||||
import { faCut, faClone, faCopy, faPaste, faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
|
|
||||||
import { createHistory } from './history.js'
|
import { createHistory } from './history.js'
|
||||||
import JSONNode from './JSONNode.svelte'
|
import JSONNode from './JSONNode.svelte'
|
||||||
import {
|
import {
|
||||||
|
@ -37,6 +34,7 @@
|
||||||
import jump from './assets/jump.js/src/jump.js'
|
import jump from './assets/jump.js/src/jump.js'
|
||||||
import { expandPath, stateUtils } from './utils/stateUtils.js'
|
import { expandPath, stateUtils } from './utils/stateUtils.js'
|
||||||
import { getNextKeys, patchProps } from './utils/updateProps.js'
|
import { getNextKeys, patchProps } from './utils/updateProps.js'
|
||||||
|
import Menu from './Menu.svelte'
|
||||||
|
|
||||||
let divContents
|
let divContents
|
||||||
let domHiddenInput
|
let domHiddenInput
|
||||||
|
@ -247,27 +245,22 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeSearchText (text) {
|
async function handleSearchText (text) {
|
||||||
searchText = text
|
searchText = text
|
||||||
await tick() // await for the search results to be updated
|
await tick() // await for the search results to be updated
|
||||||
focusActiveSearchResult(searchResult && searchResult.activeItem)
|
focusActiveSearchResult(searchResult && searchResult.activeItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function nextSearchResult () {
|
async function handleNextSearchResult () {
|
||||||
searchResult = searchNext(searchResult)
|
searchResult = searchNext(searchResult)
|
||||||
focusActiveSearchResult(searchResult && searchResult.activeItem)
|
focusActiveSearchResult(searchResult && searchResult.activeItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
function previousSearchResult () {
|
function handlePreviousSearchResult () {
|
||||||
searchResult = searchPrevious(searchResult)
|
searchResult = searchPrevious(searchResult)
|
||||||
focusActiveSearchResult(searchResult && searchResult.activeItem)
|
focusActiveSearchResult(searchResult && searchResult.activeItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearSearchResult () {
|
|
||||||
showSearch = false
|
|
||||||
searchText = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
async function focusActiveSearchResult (activeItem) {
|
async function focusActiveSearchResult (activeItem) {
|
||||||
if (activeItem) {
|
if (activeItem) {
|
||||||
state = expandPath(state, activeItem.path)
|
state = expandPath(state, activeItem.path)
|
||||||
|
@ -316,10 +309,6 @@
|
||||||
// should never be called on the root
|
// should never be called on the root
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToggleSearch() {
|
|
||||||
showSearch = !showSearch
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle expanded state of a node
|
* Toggle expanded state of a node
|
||||||
* @param {Path} path
|
* @param {Path} path
|
||||||
|
@ -440,88 +429,25 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="jsoneditor" on:keydown={handleKeyDown}>
|
<div class="jsoneditor" on:keydown={handleKeyDown}>
|
||||||
<div class="menu">
|
<Menu
|
||||||
<button
|
historyState={historyState}
|
||||||
class="button cut"
|
searchText={searchText}
|
||||||
on:click={handleCut}
|
searchResult={searchResult}
|
||||||
disabled={!hasSelectionContents}
|
bind:showSearch
|
||||||
title="Cut (Ctrl+X)"
|
hasSelectionContents={hasSelectionContents}
|
||||||
>
|
hasClipboardContents={hasClipboardContents}
|
||||||
<Icon data={faCut} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="button copy"
|
|
||||||
on:click={handleCopy}
|
|
||||||
disabled={!hasSelectionContents}
|
|
||||||
title="Copy (Ctrl+C)"
|
|
||||||
>
|
|
||||||
<Icon data={faCopy} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="button paste"
|
|
||||||
on:click={handlePaste}
|
|
||||||
disabled={!hasClipboardContents}
|
|
||||||
title="Paste (Ctrl+V)"
|
|
||||||
>
|
|
||||||
<Icon data={faPaste} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="separator"></div>
|
onCut={handleCut}
|
||||||
|
onCopy={handleCopy}
|
||||||
|
onPaste={handlePaste}
|
||||||
|
onDuplicate={handleDuplicate}
|
||||||
|
onUndo={handleUndo}
|
||||||
|
onRedo={handleRedo}
|
||||||
|
|
||||||
<button
|
onSearchText={handleSearchText}
|
||||||
class="button duplicate"
|
onNextSearchResult={handleNextSearchResult}
|
||||||
on:click={handleDuplicate}
|
onPreviousSearchResult={handlePreviousSearchResult}
|
||||||
disabled={!hasSelectionContents}
|
/>
|
||||||
title="Duplicate (Ctrl+D)"
|
|
||||||
>
|
|
||||||
<Icon data={faClone} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="separator"></div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="button search"
|
|
||||||
on:click={handleToggleSearch}
|
|
||||||
title="Search (Ctrl+F)"
|
|
||||||
>
|
|
||||||
<Icon data={faSearch} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="separator"></div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="button undo"
|
|
||||||
disabled={!historyState.canUndo}
|
|
||||||
on:click={handleUndo}
|
|
||||||
title="Undo (Ctrl+Z)"
|
|
||||||
>
|
|
||||||
<Icon data={faUndo} />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="button redo"
|
|
||||||
disabled={!historyState.canRedo}
|
|
||||||
on:click={handleRedo}
|
|
||||||
title="Redo (Ctrl+Shift+Z)"
|
|
||||||
>
|
|
||||||
<Icon data={faRedo} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div class="space"></div>
|
|
||||||
|
|
||||||
{#if showSearch}
|
|
||||||
<div class="search-box-container">
|
|
||||||
<SearchBox
|
|
||||||
text={searchText}
|
|
||||||
resultCount={searchResult ? searchResult.count : 0}
|
|
||||||
activeIndex={searchResult ? searchResult.activeIndex : 0}
|
|
||||||
onChange={changeSearchText}
|
|
||||||
onNext={nextSearchResult}
|
|
||||||
onPrevious={previousSearchResult}
|
|
||||||
onClose={clearSearchResult}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
<label class="hidden-input-label">
|
<label class="hidden-input-label">
|
||||||
<input
|
<input
|
||||||
class="hidden-input"
|
class="hidden-input"
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
@import './styles.scss';
|
||||||
|
|
||||||
|
.menu {
|
||||||
|
font-family: $font-family-menu;
|
||||||
|
font-size: $font-size;
|
||||||
|
background: $theme-color;
|
||||||
|
color: $white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.button {
|
||||||
|
width: $menu-button-size;
|
||||||
|
height: $menu-button-size;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.space {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.separator {
|
||||||
|
$margin: 3px;
|
||||||
|
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 1px;
|
||||||
|
height: $menu-button-size - 2 * $margin;
|
||||||
|
margin: $margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: $search-box-offset + 20px; // keep space for scrollbar
|
||||||
|
margin-top: $search-box-offset;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
<script>
|
||||||
|
import Icon from 'svelte-awesome'
|
||||||
|
import { faCut, faClone, faCopy, faPaste, faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import SearchBox from './SearchBox.svelte'
|
||||||
|
|
||||||
|
export let searchText
|
||||||
|
export let searchResult
|
||||||
|
export let showSearch = false
|
||||||
|
export let hasSelectionContents
|
||||||
|
export let hasClipboardContents
|
||||||
|
export let historyState
|
||||||
|
|
||||||
|
export let onCut
|
||||||
|
export let onCopy
|
||||||
|
export let onPaste
|
||||||
|
export let onDuplicate
|
||||||
|
export let onUndo
|
||||||
|
export let onRedo
|
||||||
|
|
||||||
|
export let onSearchText
|
||||||
|
export let onNextSearchResult
|
||||||
|
export let onPreviousSearchResult
|
||||||
|
|
||||||
|
function handleToggleSearch() {
|
||||||
|
showSearch = !showSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSearchResult () {
|
||||||
|
showSearch = false
|
||||||
|
onSearchText('')
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="menu">
|
||||||
|
<button
|
||||||
|
class="button cut"
|
||||||
|
on:click={onCut}
|
||||||
|
disabled={!hasSelectionContents}
|
||||||
|
title="Cut (Ctrl+X)"
|
||||||
|
>
|
||||||
|
<Icon data={faCut} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button copy"
|
||||||
|
on:click={onCopy}
|
||||||
|
disabled={!hasSelectionContents}
|
||||||
|
title="Copy (Ctrl+C)"
|
||||||
|
>
|
||||||
|
<Icon data={faCopy} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button paste"
|
||||||
|
on:click={onPaste}
|
||||||
|
disabled={!hasClipboardContents}
|
||||||
|
title="Paste (Ctrl+V)"
|
||||||
|
>
|
||||||
|
<Icon data={faPaste} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button duplicate"
|
||||||
|
on:click={onDuplicate}
|
||||||
|
disabled={!hasSelectionContents}
|
||||||
|
title="Duplicate (Ctrl+D)"
|
||||||
|
>
|
||||||
|
<Icon data={faClone} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button search"
|
||||||
|
on:click={handleToggleSearch}
|
||||||
|
title="Search (Ctrl+F)"
|
||||||
|
>
|
||||||
|
<Icon data={faSearch} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="separator"></div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="button undo"
|
||||||
|
disabled={!historyState.canUndo}
|
||||||
|
on:click={onUndo}
|
||||||
|
title="Undo (Ctrl+Z)"
|
||||||
|
>
|
||||||
|
<Icon data={faUndo} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="button redo"
|
||||||
|
disabled={!historyState.canRedo}
|
||||||
|
on:click={onRedo}
|
||||||
|
title="Redo (Ctrl+Shift+Z)"
|
||||||
|
>
|
||||||
|
<Icon data={faRedo} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="space"></div>
|
||||||
|
|
||||||
|
{#if showSearch}
|
||||||
|
<div class="search-box-container">
|
||||||
|
<SearchBox
|
||||||
|
text={searchText}
|
||||||
|
resultCount={searchResult ? searchResult.count : 0}
|
||||||
|
activeIndex={searchResult ? searchResult.activeIndex : 0}
|
||||||
|
onChange={onSearchText}
|
||||||
|
onNext={onNextSearchResult}
|
||||||
|
onPrevious={onPreviousSearchResult}
|
||||||
|
onClose={clearSearchResult}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style src="Menu.scss"></style>
|
Loading…
Reference in New Issue