Code editor open-close improvements (#9365)

Closes #9209

https://github.com/enso-org/enso/assets/6566674/43984978-004b-4ed2-a55e-de9302dc1fdd

- Added new menu item `Code editor` which toggles visibility of, well, code editor
- Changed the menu implementation:
- Fixed platform-dependent “More” icon. It was displayed awfully on Mac.
- Added simple opacity animation.
- Reduced width slightly.
- Refactored the layout, removed unused classes and CSS…
- Added animated background when hovering “Show all” icon
- Added close button to the upper left corner of the code editor (upper right is occupied by resize controls)
This commit is contained in:
Ilya Bogdanov 2024-03-14 21:05:29 +04:00 committed by GitHub
parent 1b59744660
commit b33079e68b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 102 additions and 64 deletions

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import type { ChangeSet, Diagnostic, Highlighter } from '@/components/CodeEditor/codemirror'
import SvgIcon from '@/components/SvgIcon.vue'
import { usePointer } from '@/composables/events'
import { useGraphStore, type NodeId } from '@/stores/graph'
import { useProjectStore } from '@/stores/project'
@ -38,6 +39,8 @@ const {
textEditToChangeSpec,
} = await import('@/components/CodeEditor/codemirror')
const emit = defineEmits<{ close: [] }>()
const projectStore = useProjectStore()
const graphStore = useGraphStore()
const suggestionDbStore = useSuggestionDbStore()
@ -347,6 +350,7 @@ const editorStyle = computed(() => {
<circle cx="14" cy="14" r="1.5" />
</svg>
</div>
<SvgIcon name="enso_logo" class="closeButton button" @click="emit('close')" />
</div>
</template>
@ -403,6 +407,18 @@ const editorStyle = computed(() => {
}
}
.closeButton {
position: absolute;
top: 4px;
left: 6px;
color: red;
opacity: 0.3;
&:hover {
opacity: 0.6;
}
}
.CodeEditor :deep(.cm-editor) {
position: relative;
color: white;

View File

@ -1,5 +1,6 @@
<script setup lang="ts">
import SvgIcon from '@/components/SvgIcon.vue'
import { isMacLike } from '@/composables/events'
import { ref } from 'vue'
const isDropdownOpen = ref(false)
@ -7,44 +8,50 @@ const isDropdownOpen = ref(false)
const props = defineProps<{
zoomLevel: number
}>()
const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: [] }>()
const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: []; toggleCodeEditor: [] }>()
// TODO: replace with codeEditorBindigs.toggle: https://github.com/enso-org/enso/issues/9411.
const toggleCodeEditorShortcut = isMacLike ? 'Cmd + `' : 'Ctrl + `'
</script>
<template>
<div class="ExtendedMenu">
<div class="moreIcon" @pointerdown="isDropdownOpen = !isDropdownOpen"></div>
<Transition name="dropdown">
<div v-show="isDropdownOpen" class="ExtendedMenuPane">
<div class="row">
<div class="zoomBar row">
<div class="label">Zoom</div>
<div class="zoomControl last">
<div
class="zoomButton minus"
title="Decrease zoom"
@pointerdown.stop="emit('zoomOut')"
/>
<span
class="zoomScaleLabel"
v-text="props.zoomLevel ? props.zoomLevel.toFixed(0) + '%' : '?'"
></span>
<div
class="zoomButton plus"
title="increase zoom"
@pointerdown.stop="emit('zoomIn')"
/>
</div>
</div>
<div
class="ExtendedMenu"
@pointerdown.stop
@pointerup.stop
@click.stop="isDropdownOpen = !isDropdownOpen"
>
<SvgIcon name="folder_opened" class="moreIcon" />
</div>
<Transition name="dropdown">
<div
v-show="isDropdownOpen"
class="ExtendedMenuPane"
@pointerdown.stop
@pointerup.stop
@click.stop
>
<div class="row">
<div class="label">Zoom</div>
<div class="zoomControl">
<div class="zoomButton minus" title="Decrease zoom" @click="emit('zoomOut')" />
<span
class="zoomScaleLabel"
v-text="props.zoomLevel ? props.zoomLevel.toFixed(0) + '%' : '?'"
></span>
<div class="zoomButton plus" title="increase zoom" @click="emit('zoomIn')" />
<div class="divider"></div>
<SvgIcon
name="show_all"
class="last showAllIcon"
@pointerdown="emit('fitToAllClicked')"
/>
<div class="showAllIconHighlight">
<SvgIcon name="show_all" class="showAllIcon" @click="emit('fitToAllClicked')" />
</div>
</div>
</div>
</Transition>
</div>
<div class="row clickableRow" @click="emit('toggleCodeEditor')">
<div class="label">Code Editor</div>
<div>{{ toggleCodeEditorShortcut }}</div>
</div>
</div>
</Transition>
</template>
<style scoped>
@ -56,32 +63,34 @@ const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: [] }>()
height: 32px;
margin-left: auto;
margin-right: 125px;
}
.ExtendedMenu:before {
position: absolute;
content: '';
border-radius: var(--radius-full);
background: var(--color-frame-bg);
backdrop-filter: var(--blur-app-bg);
width: 32px;
height: 32px;
cursor: pointer;
}
.ExtendedMenuPane {
position: fixed;
display: flex;
width: 300px;
flex-direction: column;
width: 250px;
top: 40px;
margin-top: 6px;
padding: 4px;
right: 0px;
border-radius: var(--radius-full);
border-radius: 12px;
background: var(--color-frame-bg);
backdrop-filter: var(--blur-app-bg);
}
.clickableRow {
cursor: pointer;
transition: background-color 0.3s;
&:hover {
background-color: var(--color-menu-entry-hover-bg);
}
}
.label {
user-select: none;
pointer-events: none;
@ -90,14 +99,10 @@ const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: [] }>()
.row {
width: 100%;
display: flex;
gap: 4px;
padding-left: 4px;
padding: 0 8px 0 8px;
justify-content: space-between;
align-items: center;
justify-content: center;
}
.last {
margin-left: auto;
border-radius: 12px;
}
.divider {
@ -115,9 +120,20 @@ const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: [] }>()
align-items: center;
}
.showAllIcon {
margin-right: 10px;
.showAllIconHighlight {
display: flex;
justify-items: center;
align-items: center;
padding-left: 4px;
cursor: pointer;
width: 24px;
height: 24px;
margin: -4px -4px;
border-radius: var(--radius-full);
transition: background-color 0.3s;
&:hover {
background-color: var(--color-menu-entry-hover-bg);
}
}
.zoomScaleLabel {
@ -126,18 +142,8 @@ const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: [] }>()
}
.moreIcon {
width: 32px;
height: 32px;
text-align: center;
font-size: 24px;
font-family: var(--font-code);
position: relative;
right: -4px;
top: 8px;
}
.moreIcon:before {
content: '\2807';
left: 8px;
}
.zoomButton {
@ -187,4 +193,14 @@ const emit = defineEmits<{ zoomIn: []; zoomOut: []; fitToAllClicked: [] }>()
margin: auto 2px;
height: 2px;
}
.dropdown-enter-active,
.dropdown-leave-active {
transition: opacity 0.25s ease;
}
.dropdown-enter-from,
.dropdown-leave-to {
opacity: 0;
}
</style>

View File

@ -292,9 +292,12 @@ const { handleClick } = useDoubleClick(
)
const codeEditorArea = ref<HTMLElement>()
const showCodeEditor = ref(false)
const toggleCodeEditor = () => {
showCodeEditor.value = !showCodeEditor.value
}
const codeEditorHandler = codeEditorBindings.handler({
toggle() {
showCodeEditor.value = !showCodeEditor.value
toggleCodeEditor()
},
})
@ -595,11 +598,12 @@ function handleEdgeDrop(source: AstId, position: Vec2) {
@fitToAllClicked="zoomToSelected"
@zoomIn="graphNavigator.stepZoom(+1)"
@zoomOut="graphNavigator.stepZoom(-1)"
@toggleCodeEditor="toggleCodeEditor"
/>
<PlusButton @pointerdown.stop @click.stop="addNodeAuto()" @pointerup.stop />
<Transition>
<Suspense ref="codeEditorArea">
<CodeEditor v-if="showCodeEditor" />
<CodeEditor v-if="showCodeEditor" @close="showCodeEditor = false" />
</Suspense>
</Transition>
<SceneScroller

View File

@ -22,6 +22,7 @@ const emit = defineEmits<{
fitToAllClicked: []
zoomIn: []
zoomOut: []
toggleCodeEditor: []
}>()
const LEFT_PADDING_PX = 11
@ -56,6 +57,7 @@ const barStyle = computed(() => {
@fitToAllClicked="emit('fitToAllClicked')"
@zoomIn="emit('zoomIn')"
@zoomOut="emit('zoomOut')"
@toggleCodeEditor="emit('toggleCodeEditor')"
/>
</div>
</template>