Skip to content

Commit

Permalink
Add dropdown menu to icon cells
Browse files Browse the repository at this point in the history
  • Loading branch information
xingrz committed Mar 29, 2024
1 parent 3296ab1 commit ad90604
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 7 deletions.
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"lint": "eslint --ext .ts,.vue src"
},
"dependencies": {
"@vicons/ionicons5": "^0.12.0",
"ace-code": "^1.32.8",
"md5": "^2.3.0",
"naive-ui": "^2.38.1",
Expand Down
73 changes: 67 additions & 6 deletions src/components/BSMap/BSCell.vue
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
<template>
<BSSelectable v-slot="{ selectable }" :row="props.row" :offset="props.offset" :length="props.content.length">
<div :class="[selectable, $style.cell]" :title="content" @click="handleClick" :style="style">
<BSIcon v-for="(icon, index) in (parts?.icons || [])" :key="index" :content="icon" :stacked="stacked"
@ratio="(ratio: number) => updateRatio(index, ratio)" />
</div>
<BSSelectable v-slot="{ selectable }" :row="props.row" :offset="props.offset" :length="props.content.length"
:hovered="popoverShow">
<n-dropdown show-arrow trigger="hover" placement="right" v-model:show="popoverShow" :disabled="!popoverEnabled"
:options="popoverOptions" :render-label="renderPopoverLabel" :render-icon="renderPopoverIcon"
@select="onPopoverSelect">
<div :class="[selectable, $style.cell]" :style="style" @click="handleClick">
<BSIcon v-for="({ icon }, index) in (parts?.icons || [])" :key="index" :content="icon" :stacked="stacked"
@ratio="(ratio: number) => updateRatio(index, ratio)" />
</div>
</n-dropdown>
</BSSelectable>
</template>

<script lang="ts" setup>
import {
type CSSProperties,
type VNode,
computed,
defineProps,
h,
ref,
} from 'vue';
import {
type DropdownOption,
NDropdown,
} from 'naive-ui';
import { useEditorStore } from '@/stores/editor';
import styleFromParams from '@/utils/styleFromParams';
import BSSelectable from './BSSelectable.vue';
import BSIcon from './BSIcon.vue';
import BSCellPopoverLabel from './BSCellPopoverLabel.vue';
const props = defineProps<{
content: string;
Expand All @@ -36,7 +49,13 @@ const parts = computed(() => {
const [nonParam, ...params] = props.content.split('!_');
const [nonLink, ...links] = nonParam.split('!@');
const icons = nonLink.split('!~').filter((icon) => !!icon);
let offset = 0;
const icons = nonLink.split('!~').map((icon) => {
const o = offset;
offset += icon.length + 2;
return { icon, offset: o };
}).filter(({ icon }) => !!icon);
return { icons, links, params };
});
Expand All @@ -63,6 +82,48 @@ function updateRatio(layer: number, newRatio: number): void {
ratio.value = newRatio;
}
}
type BSCellPopoverOption = DropdownOption & {
offset: number;
};
const popoverShow = ref(false);
const popoverEnabled = computed(() => (parts.value?.icons || []).length > 0);
const popoverOptions = computed(() => (parts.value?.icons || []).map(({ icon, offset }) => ({
label: icon,
key: icon,
offset: offset,
} as BSCellPopoverOption)));
function renderPopoverLabel(option: DropdownOption): VNode {
return h(BSCellPopoverLabel, {
content: option.label as string,
});
}
function renderPopoverIcon(option: DropdownOption): VNode {
return h(BSIcon, {
content: option.label as string,
stacked: stacked.value,
style: {
width: '20px',
height: '20px',
border: '1px solid #ddd',
} as CSSProperties,
});
}
function onPopoverSelect(_key: string, option: DropdownOption): void {
const { label, offset } = option as BSCellPopoverOption;
editorStore.selection = {
row: props.row,
offset: props.offset + offset,
length: (label as string).length,
from: 'preview',
};
}
</script>

<style lang="scss" module>
Expand Down
43 changes: 43 additions & 0 deletions src/components/BSMap/BSCellPopoverLabel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<template>
<div :class="$style.item">
<code :class="$style.name">{{ props.content }}</code>
<n-button :class="$style.open" quaternary block @click="onClick">
<n-icon>
<OpenOutline />
</n-icon>
</n-button>
</div>
</template>

<script lang="ts" setup>
import { defineProps } from 'vue';
import { NButton, NIcon } from 'naive-ui';
import { OpenOutline } from '@vicons/ionicons5';
const props = defineProps<{
content: string;
}>();
function onClick(e: MouseEvent): void {
e.stopPropagation();
window.open(`https://commons.wikimedia.org/wiki/File:BSicon_${props.content}.svg`);
}
</script>

<style lang="scss" module>
.item {
display: flex;
width: auto;
gap: 16px;
margin-right: calc(var(--n-option-suffix-width) * -1 + 2px);
}
.name {
flex: 1 1 auto;
}
.open {
flex: 0 0 20px;
}
</style>
5 changes: 4 additions & 1 deletion src/components/BSMap/BSSelectable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<slot :selectable="{
[$style.selectable]: true,
[$style.focused]: focused,
[$style.hovered]: props.hovered,
}" />
</template>

Expand All @@ -14,6 +15,7 @@ const props = defineProps<{
row: number;
offset: number;
length: number;
hovered?: boolean;
}>();
const editorStore = useEditorStore();
Expand Down Expand Up @@ -50,7 +52,8 @@ const focused = computed(() => {
transition: opacity 200ms;
}
&:hover::after {
&:hover::after,
&.hovered::after {
opacity: 0.2;
}
Expand Down

0 comments on commit ad90604

Please sign in to comment.