Create dropdown menu (WIP)
This commit is contained in:
parent
c441663528
commit
a6bb790f5e
|
@ -0,0 +1,46 @@
|
||||||
|
@import '../styles.scss';
|
||||||
|
|
||||||
|
.menu-dropdown {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
list-style-type : none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.items {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
background: white;
|
||||||
|
z-index: 2;
|
||||||
|
color: $black;
|
||||||
|
box-shadow: $box-shadow;
|
||||||
|
|
||||||
|
&.visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $background-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
color: $gray;
|
||||||
|
background: unset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<script>
|
||||||
|
import Icon from 'svelte-awesome'
|
||||||
|
import { faCaretDown } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { onDestroy, onMount } from 'svelte'
|
||||||
|
import { keyComboFromEvent} from '../utils/keyBindings.js'
|
||||||
|
|
||||||
|
/** @type {MenuDropdownItem[]} */
|
||||||
|
export let items = []
|
||||||
|
|
||||||
|
export let title = null
|
||||||
|
export let width = '120px'
|
||||||
|
export let visible = false
|
||||||
|
export let disabled = false
|
||||||
|
|
||||||
|
function toggleShow (event) {
|
||||||
|
event.stopPropagation()
|
||||||
|
visible = !visible
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick () {
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyDown (event) {
|
||||||
|
const combo = keyComboFromEvent(event)
|
||||||
|
if (combo === 'Escape') {
|
||||||
|
event.preventDefault()
|
||||||
|
visible = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
document.addEventListener('click', handleClick)
|
||||||
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
document.removeEventListener('click', handleClick)
|
||||||
|
document.removeEventListener('keydown', handleKeyDown)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="menu-dropdown" title={title} on:click={handleClick}>
|
||||||
|
<slot name="defaultItem"></slot>
|
||||||
|
|
||||||
|
<button on:click={toggleShow}>
|
||||||
|
<Icon data={faCaretDown} />
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="items" class:visible style="width: {width};">
|
||||||
|
<ul>
|
||||||
|
{#each items as item}
|
||||||
|
<li>
|
||||||
|
<button on:click={item.onClick} disabled={disabled}>
|
||||||
|
{item.text}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style src="./DropdownMenu.scss"></style>
|
|
@ -45,8 +45,6 @@
|
||||||
|
|
||||||
let selection = null
|
let selection = null
|
||||||
let clipboard = null
|
let clipboard = null
|
||||||
$: hasSelectionContents = selection != null && selection.paths != null
|
|
||||||
$: hasClipboardContents = clipboard != null && selection != null
|
|
||||||
|
|
||||||
$: state = syncState(doc, state, [], (path) => path.length < 1)
|
$: state = syncState(doc, state, [], (path) => path.length < 1)
|
||||||
|
|
||||||
|
@ -209,6 +207,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleInsert() {
|
||||||
|
|
||||||
|
if (selection != null) {
|
||||||
|
console.log('insert', { selection })
|
||||||
|
|
||||||
|
// TODO: impelemnt insert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: cleanup
|
// TODO: cleanup
|
||||||
$: console.log('doc', doc)
|
$: console.log('doc', doc)
|
||||||
$: console.log('state', state)
|
$: console.log('state', state)
|
||||||
|
@ -382,6 +389,10 @@
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
handleDuplicate()
|
handleDuplicate()
|
||||||
}
|
}
|
||||||
|
if (combo === 'Ctrl+Insert' || combo === 'Command+Insert') {
|
||||||
|
event.preventDefault()
|
||||||
|
handleInsert()
|
||||||
|
}
|
||||||
if (combo === 'Escape') {
|
if (combo === 'Escape') {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
selection = null
|
selection = null
|
||||||
|
@ -433,9 +444,9 @@
|
||||||
searchText={searchText}
|
searchText={searchText}
|
||||||
searchResult={searchResult}
|
searchResult={searchResult}
|
||||||
bind:showSearch
|
bind:showSearch
|
||||||
hasSelectionContents={hasSelectionContents}
|
selection={selection}
|
||||||
hasClipboardContents={hasClipboardContents}
|
clipboard={clipboard}
|
||||||
|
|
||||||
onCut={handleCut}
|
onCut={handleCut}
|
||||||
onCopy={handleCopy}
|
onCopy={handleCopy}
|
||||||
onPaste={handlePaste}
|
onPaste={handlePaste}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script>
|
<script>
|
||||||
import { debounce, isEqual } from 'lodash-es'
|
import { debounce, isEqual } from 'lodash-es'
|
||||||
import { rename } from '../logic/operations.js'
|
import { rename } from '../logic/operations.js'
|
||||||
|
import { singleton } from './singleton.js'
|
||||||
import {
|
import {
|
||||||
DEBOUNCE_DELAY,
|
DEBOUNCE_DELAY,
|
||||||
DEFAULT_LIMIT,
|
DEFAULT_LIMIT,
|
||||||
|
@ -39,12 +40,6 @@
|
||||||
export let onSelect
|
export let onSelect
|
||||||
export let selection
|
export let selection
|
||||||
|
|
||||||
const singleton = {
|
|
||||||
mousedown: false,
|
|
||||||
selectionAnchor: null, // Path
|
|
||||||
selectionFocus: null // Path
|
|
||||||
}
|
|
||||||
|
|
||||||
$: expanded = state && state[STATE_EXPANDED]
|
$: expanded = state && state[STATE_EXPANDED]
|
||||||
$: limit = state && state[STATE_LIMIT]
|
$: limit = state && state[STATE_LIMIT]
|
||||||
$: props = state && state[STATE_PROPS]
|
$: props = state && state[STATE_PROPS]
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
<script>
|
<script>
|
||||||
import Icon from 'svelte-awesome'
|
import Icon from 'svelte-awesome'
|
||||||
import { faCut, faClone, faCopy, faPaste, faSearch, faUndo, faRedo } from '@fortawesome/free-solid-svg-icons'
|
import { faCut, faClone, faCopy, faPaste, faSearch, faUndo, faRedo, faPlus } from '@fortawesome/free-solid-svg-icons'
|
||||||
import SearchBox from './SearchBox.svelte'
|
import SearchBox from './SearchBox.svelte'
|
||||||
|
import DropdownMenu from './DropdownMenu.svelte'
|
||||||
|
|
||||||
export let searchText
|
export let searchText
|
||||||
export let searchResult
|
export let searchResult
|
||||||
export let showSearch = false
|
export let showSearch = false
|
||||||
export let hasSelectionContents
|
export let selection
|
||||||
export let hasClipboardContents
|
export let clipboard
|
||||||
export let historyState
|
export let historyState
|
||||||
|
|
||||||
export let onCut
|
export let onCut
|
||||||
|
@ -21,6 +22,10 @@
|
||||||
export let onNextSearchResult
|
export let onNextSearchResult
|
||||||
export let onPreviousSearchResult
|
export let onPreviousSearchResult
|
||||||
|
|
||||||
|
$: hasSelection = selection != null
|
||||||
|
$: hasSelectionContents = selection != null && selection.paths != null
|
||||||
|
$: hasClipboardContents = clipboard != null && selection != null
|
||||||
|
|
||||||
function handleToggleSearch() {
|
function handleToggleSearch() {
|
||||||
showSearch = !showSearch
|
showSearch = !showSearch
|
||||||
}
|
}
|
||||||
|
@ -30,6 +35,36 @@
|
||||||
onSearchText('')
|
onSearchText('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleInsertValue () {
|
||||||
|
console.log('TODO: insert value')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {MenuDropdownItem[]} */
|
||||||
|
const insertItems = [
|
||||||
|
{
|
||||||
|
text: "Insert value",
|
||||||
|
onClick: handleInsertValue,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Insert object",
|
||||||
|
onClick: () => {
|
||||||
|
console.log('TODO: insert object')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Insert array",
|
||||||
|
onClick: () => {
|
||||||
|
console.log('TODO: insert array')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Insert structure",
|
||||||
|
onClick: () => {
|
||||||
|
console.log('TODO: insert structure')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
@ -69,6 +104,21 @@
|
||||||
<Icon data={faClone} />
|
<Icon data={faClone} />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<DropdownMenu
|
||||||
|
items={insertItems}
|
||||||
|
title="Insert new value (Ctrl+Insert)"
|
||||||
|
disabled={!hasSelection}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
class="button insert"
|
||||||
|
slot="defaultItem"
|
||||||
|
on:click={handleInsertValue}
|
||||||
|
disabled={!hasSelection}
|
||||||
|
>
|
||||||
|
<Icon data={faPlus} />
|
||||||
|
</button>
|
||||||
|
</DropdownMenu>
|
||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -6,7 +6,7 @@ $search-size: 24px;
|
||||||
border: 2px solid $theme-color;
|
border: 2px solid $theme-color;
|
||||||
border-radius: $border-radius;
|
border-radius: $border-radius;
|
||||||
background: $white;
|
background: $white;
|
||||||
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.24);
|
box-shadow: $box-shadow;
|
||||||
|
|
||||||
.search-form {
|
.search-form {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
// used by JSONNode during dragging
|
||||||
|
export const singleton = {
|
||||||
|
mousedown: false,
|
||||||
|
selectionAnchor: null, // Path
|
||||||
|
selectionFocus: null // Path
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ $gray: #9d9d9d;
|
||||||
$gray-icon: $gray;
|
$gray-icon: $gray;
|
||||||
$light-gray: #c0c0c0;
|
$light-gray: #c0c0c0;
|
||||||
$selection-background: #e0e0e0;
|
$selection-background: #e0e0e0;
|
||||||
|
$background-gray: #f5f5f5;
|
||||||
|
|
||||||
$line-height: 18px;
|
$line-height: 18px;
|
||||||
$indentation-width: 18px; // IMPORTANT: keep in sync with js constant INDENTATION_WIDTH
|
$indentation-width: 18px; // IMPORTANT: keep in sync with js constant INDENTATION_WIDTH
|
||||||
|
@ -36,3 +37,15 @@ $border-radius: 3px;
|
||||||
|
|
||||||
$menu-padding: 5px;
|
$menu-padding: 5px;
|
||||||
$bottom-height: 5px;
|
$bottom-height: 5px;
|
||||||
|
|
||||||
|
$box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.24);
|
||||||
|
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
color: inherit;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: $font-family-menu;
|
||||||
|
font-size: $font-size;
|
||||||
|
padding: $menu-padding;
|
||||||
|
}
|
||||||
|
|
|
@ -75,3 +75,10 @@
|
||||||
/**
|
/**
|
||||||
* @typedef {MultiSelectionSchema | BeforeSelection | AppendSelection} SelectionSchema
|
* @typedef {MultiSelectionSchema | BeforeSelection | AppendSelection} SelectionSchema
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} MenuDropdownItem
|
||||||
|
* @property {string} text
|
||||||
|
* @property {function} onClick
|
||||||
|
* @property {boolean} [default=false]
|
||||||
|
**/
|
||||||
|
|
Loading…
Reference in New Issue