Skip to content

Commit

Permalink
Add menu bar
Browse files Browse the repository at this point in the history
  • Loading branch information
xingrz committed Mar 27, 2024
1 parent 6652385 commit 8d819d2
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 17 deletions.
103 changes: 88 additions & 15 deletions src/App.vue
Original file line number Diff line number Diff line change
@@ -1,38 +1,97 @@
<template>
<n-config-provider>
<n-split direction="horizontal" :resize-trigger-size="8" v-model:size="editorStore.split">
<template #1>
<editor v-model:content="editorStore.content" v-model:selection="editorStore.selection"
v-model:scroll="editorStore.scroll" :icons="iconStore.icons" :size="editorStore.size" />
</template>
<template #2>
<scroller v-model:scroll="editorStore.scroll">
<BSMap :content="editorStore.content" :size="editorStore.size" />
</scroller>
</template>
<template #resize-trigger>
<div :class="$style.resizer">
</div>
</template>
</n-split>
<div :class="$style.container" :style="{ '--menu-bar-height': '34px' }">
<app-menu :class="$style.menu" :items="menuItems" />
<n-split :class="$style.main" direction="horizontal" :resize-trigger-size="8" v-model:size="editorStore.split">
<template #1>
<editor v-model:content="editorStore.content" v-model:selection="editorStore.selection"
v-model:scroll="editorStore.scroll" :icons="iconStore.icons" :size="editorStore.size" />
</template>
<template #2>
<scroller v-model:scroll="editorStore.scroll">
<BSMap :content="editorStore.content" :size="editorStore.size" />
</scroller>
</template>
<template #resize-trigger>
<div :class="$style.resizer" />
</template>
</n-split>
</div>
</n-config-provider>
</template>

<script lang="ts" setup>
import { h } from 'vue';
import {
type DropdownOption,
NConfigProvider,
NSplit,
} from 'naive-ui';
import { useEditorStore } from '@/stores/editor';
import { useIconStore } from '@/stores/icon';
import useDomEvented from '@/composables/useDomEvented';
import AppMenu from './components/AppMenu.vue';
import Scroller from './components/Scroller.vue';
import BSMap from './components/BSMap.vue';
import Editor from './components/Editor.vue';
import SizeSetter from './components/AppMenu/SizeSetter.vue';
const editorStore = useEditorStore();
const iconStore = useIconStore();
const isFullscreen = useDomEvented(document, 'fullscreenchange', () => !!document.fullscreenElement);
const menuItems: DropdownOption[] = [
{
label: 'View',
key: 'view',
children: [
{
key: 'icon-size',
type: 'render',
render: () => h(SizeSetter),
},
{ type: 'divider' },
{
label: () => isFullscreen.value ? 'Leave fullscreen' : 'Enter fullscreen',
key: 'fullscreen',
props: {
onClick: () => {
if (isFullscreen.value) {
document.exitFullscreen();
} else {
document.documentElement.requestFullscreen();
}
},
},
},
],
},
{
label: 'Help',
key: 'help',
children: [
{
label: () => h('a', {
href: 'https://github.com/xingrz/rdt-editor/issues/new',
target: '_blank',
}, 'Feedback'),
key: 'feedback',
},
{
label: () => h('a', {
href: 'https://github.com/xingrz/rdt-editor',
target: '_blank',
}, 'About RDT Editor'),
key: 'about',
},
],
},
];
</script>

<style lang="scss" module>
Expand All @@ -43,6 +102,20 @@ const iconStore = useIconStore();
overscroll-behavior: none;
}
.container {
display: flex;
flex-direction: column;
height: 100vh;
}
.menu {
height: var(--menu-bar-height);
}
.main {
height: calc(100vh - var(--menu-bar-height));
}
.resizer {
height: 100%;
background: #cccccc;
Expand Down
75 changes: 75 additions & 0 deletions src/components/AppMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<template>
<div :class="$style.menuBar">
<n-dropdown v-for="item in props.items" :key="item.key" :class="$style.dropdown" :options="item.children"
placement="bottom-start" :show="showingItem == item.key" @select="handleSelect"
@clickoutside="handleClickOutside">
<n-button quaternary :ref="(el) => bindMenuButton(item.key as string, el as ComponentPublicInstance | null)"
@mouseenter="(e) => handleMouseEnter(e, item.key as string)" @click="(e) => handleClick(e, item.key as string)">
{{ item.label }}
</n-button>
</n-dropdown>
</div>
</template>

<script lang="ts" setup>
import { defineProps, ref, type ComponentPublicInstance } from 'vue';
import {
type DropdownOption,
NButton,
NDropdown,
} from 'naive-ui';
const props = defineProps<{
items: DropdownOption[];
}>();
const showingItem = ref<string | null>(null);
const menuButtons: Record<string, HTMLElement> = {};
function bindMenuButton(key: string, el: ComponentPublicInstance | null): void {
if (el) {
menuButtons[key] = el.$el;
} else {
delete menuButtons[key];
}
}
function handleClick(_e: MouseEvent, key: string): void {
if (showingItem.value == null) {
showingItem.value = key;
} else {
showingItem.value = null;
}
}
function handleMouseEnter(_e: MouseEvent, key: string): void {
if (showingItem.value != null) {
menuButtons[showingItem.value]?.blur();
showingItem.value = key;
menuButtons[showingItem.value]?.focus();
}
}
function handleSelect(): void {
showingItem.value = null;
}
function handleClickOutside(e: MouseEvent): void {
if (showingItem.value && menuButtons[showingItem.value]?.contains(e.target as Node)) {
return;
}
showingItem.value = null;
}
</script>

<style lang="scss" module>
.menuBar {
display: flex;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
}
.dropdown {
min-width: 200px;
}
</style>
27 changes: 27 additions & 0 deletions src/components/AppMenu/SizeSetter.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<template>
<div :class="$style.setter">
<span>Icon size</span>
<n-slider v-model:value="editorStore.size" :min="20" :max="60" />
</div>
</template>

<script lang="ts" setup>
import {
NSlider,
} from 'naive-ui';
import { useEditorStore } from '@/stores/editor';
const editorStore = useEditorStore();
</script>

<style lang="scss" module>
.setter {
display: flex;
flex-direction: column;
gap: 4px;
width: 200px;
padding: 8px var(--n-option-prefix-width);
font-size: var(--n-font-size);
}
</style>
2 changes: 1 addition & 1 deletion src/components/Editor.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>

Check warning on line 1 in src/components/Editor.vue

View workflow job for this annotation

GitHub Actions / build

Component name "Editor" should always be multi-word
<div ref="holder" :style="{
width: `100%`,
height: `100vh`,
height: `100%`,
lineHeight: `${size}px`,
}" />
</template>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Scroller.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function onScroll(): void {

<style lang="scss" module>
.scroller {
height: 100vh;
height: 100%;
overflow-y: scroll;
overflow-x: hidden;
background: #f9f9f9;
Expand Down
19 changes: 19 additions & 0 deletions src/composables/useDomEvented.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { type UnwrapRef, onBeforeUnmount, onMounted, ref } from 'vue';

export default function useDomEvented<T>(target: EventTarget, type: string, getter: () => T) {
const value = ref<T>(getter());

const listener = () => {
value.value = getter() as UnwrapRef<T>;
};

onMounted(() => {
target.addEventListener(type, listener);
});

onBeforeUnmount(() => {
target.removeEventListener(type, listener);
});

return value;
}

0 comments on commit 8d819d2

Please sign in to comment.