Skip to content

Commit 4264be6

Browse files
committed
Add dropdown menu to icon cells
1 parent 5c08613 commit 4264be6

File tree

8 files changed

+192
-9
lines changed

8 files changed

+192
-9
lines changed

package-lock.json

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"test": "vitest"
1111
},
1212
"dependencies": {
13+
"@vicons/ionicons5": "^0.12.0",
1314
"@vueuse/core": "^10.9.0",
1415
"ace-code": "^1.32.8",
1516
"md5": "^2.3.0",

src/components/BSMap/BSCell.vue

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
<template>
22
<BSSelectable v-slot="{ selectable }" :focused="props.focused">
3-
<div :class="[selectable, $style.cell]" :title="props.cell.src" :style="style" @click="() => emit('select')">
4-
<BSIcon v-for="(icon, index) in props.cell.icons" :key="index" :class="$style.icon" :icon="icon"
5-
@ratio="(ratio: number) => updateRatio(index, ratio)" />
6-
</div>
3+
<BSPopover v-model:show="popoverShown" :cell="props.cell" :ratio="ratio"
4+
@select="(offset, length) => emit('select', offset, length)">
5+
<div :class="[selectable, $style.cell]" :style="style" @click="() => emit('select', 0, props.cell.length)"
6+
@click.right.prevent="handleRightClick">
7+
<BSIcon v-for="(icon, index) in props.cell.icons" :key="index" :class="$style.icon" :icon="icon"
8+
@ratio="(ratio: number) => updateRatio(index, ratio)" />
9+
</div>
10+
</BSPopover>
711
</BSSelectable>
812
</template>
913

@@ -14,6 +18,7 @@ import type { RDTCell } from '@/ast';
1418
import styleFromParams from '@/utils/styleFromParams';
1519
1620
import BSSelectable from './BSSelectable.vue';
21+
import BSPopover from './BSPopover.vue';
1722
import BSIcon from './BSIcon.vue';
1823
1924
const props = defineProps<{
@@ -22,7 +27,7 @@ const props = defineProps<{
2227
}>();
2328
2429
const emit = defineEmits<{
25-
(e: 'select'): void;
30+
(e: 'select', offset: number, length: number): void;
2631
}>();
2732
2833
const ratio = ref(1);
@@ -32,6 +37,13 @@ const style = computed(() => ({
3237
'--bs-map-cell-ratio': (ratio.value == 1 ? undefined : ratio.value),
3338
}) as CSSProperties);
3439
40+
const popoverShown = ref(false);
41+
42+
function handleRightClick(): void {
43+
popoverShown.value = true;
44+
emit('select', 0, props.cell.length);
45+
}
46+
3547
function updateRatio(layer: number, newRatio: number): void {
3648
if (layer == 0) {
3749
// Only ratio of the first icon affects the cell

src/components/BSMap/BSInfo.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<BSSelectable v-slot="{ selectable }" :focused="props.focused">
33
<div :class="[selectable, $style.info]" :data-column="info.column" :title="props.info.text"
4-
@click="() => emit('select')">
4+
@click="() => emit('select', 0, props.info.length)">
55
{{ props.info.text }}
66
</div>
77
</BSSelectable>
@@ -17,7 +17,7 @@ const props = defineProps<{
1717
}>();
1818
1919
const emit = defineEmits<{
20-
(e: 'select'): void;
20+
(e: 'select', offset: number, length: number): void;
2121
}>();
2222
</script>
2323

src/components/BSMap/BSPopover.vue

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<template>
2+
<n-dropdown show-arrow trigger="manual" placement="right" :disabled="!enabled" :show="show" :options="options"
3+
:render-icon="renderIcon" :render-label="renderLabel" @select="onSelect" @clickoutside="onClickOutside">
4+
<slot />
5+
</n-dropdown>
6+
</template>
7+
8+
<script lang="ts" setup>
9+
import {
10+
type VNode,
11+
computed,
12+
defineEmits,
13+
defineProps,
14+
h,
15+
} from 'vue';
16+
17+
import {
18+
type DropdownOption,
19+
NDropdown,
20+
} from 'naive-ui';
21+
22+
import type { RDTCell, RDTIcon, RDTText } from '@/ast';
23+
24+
import BSPopoverIcon from './BSPopoverIcon.vue';
25+
import BSPopoverLabel from './BSPopoverLabel.vue';
26+
27+
const props = defineProps<{
28+
cell: RDTCell;
29+
ratio: number;
30+
}>();
31+
32+
const emit = defineEmits<{
33+
(e: 'select', offset: number, length: number): void;
34+
}>();
35+
36+
const show = defineModel<boolean>('show');
37+
38+
type BSCellPopoverOption = DropdownOption & {
39+
icon: RDTIcon | RDTText;
40+
};
41+
42+
const enabled = computed(() => props.cell.icons.length > 0);
43+
44+
const options = computed(() => props.cell.icons.map((icon) => ({
45+
key: `${icon.offset}`,
46+
icon,
47+
} as BSCellPopoverOption)));
48+
49+
function renderLabel(option: DropdownOption): VNode {
50+
return h(BSPopoverLabel, {
51+
icon: (option as BSCellPopoverOption).icon,
52+
});
53+
}
54+
55+
function renderIcon(option: DropdownOption): VNode {
56+
const { icon } = option as BSCellPopoverOption;
57+
return h(BSPopoverIcon, {
58+
icon,
59+
ratio: props.ratio,
60+
});
61+
}
62+
63+
function onSelect(_key: string, option: DropdownOption): void {
64+
const { icon } = option as BSCellPopoverOption;
65+
emit('select', icon.offset - props.cell.offset, icon.length);
66+
show.value = false;
67+
}
68+
69+
function onClickOutside(): void {
70+
show.value = false;
71+
}
72+
</script>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<div :class="$style.cell" :style="style">
3+
<BSIcon :icon="props.icon" />
4+
</div>
5+
</template>
6+
7+
<script lang="ts" setup>
8+
import { type CSSProperties, computed, defineProps } from 'vue';
9+
10+
import type { RDTIcon, RDTText } from '@/ast';
11+
import styleFromParams from '@/utils/styleFromParams';
12+
13+
import BSIcon from './BSIcon.vue';
14+
15+
const props = defineProps<{
16+
icon: RDTIcon | RDTText;
17+
ratio: number | undefined;
18+
}>();
19+
20+
const style = computed(() => ({
21+
...styleFromParams(props.icon.params, true),
22+
'--bs-map-cell-ratio': (props.ratio == 1 ? undefined : props.ratio),
23+
}) as CSSProperties);
24+
</script>
25+
26+
<style lang="scss" module>
27+
.cell {
28+
--bs-map-size: 20;
29+
width: calc(var(--bs-map-size) * var(--bs-map-cell-ratio, 1) * 1px);
30+
height: calc(var(--bs-map-size) * 1px);
31+
line-height: calc(var(--bs-map-size) * 1px);
32+
border: 1px solid #ddd;
33+
}
34+
</style>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<template>
2+
<div :class="$style.item">
3+
<div :class="$style.name">{{ src }}</div>
4+
<n-button v-if="props.icon.kind == 'icon'" :class="$style.open" quaternary block @click.stop="onClick">
5+
<n-icon>
6+
<OpenOutline />
7+
</n-icon>
8+
</n-button>
9+
</div>
10+
</template>
11+
12+
<script lang="ts" setup>
13+
import { computed, defineProps } from 'vue';
14+
15+
import { NButton, NIcon } from 'naive-ui';
16+
import { OpenOutline } from '@vicons/ionicons5';
17+
18+
import type { RDTIcon, RDTText } from '@/ast';
19+
20+
const props = defineProps<{
21+
icon: RDTIcon | RDTText
22+
}>();
23+
24+
const src = computed(() => props.icon.kind == 'icon'
25+
? props.icon.name
26+
: props.icon.text);
27+
28+
function onClick(): void {
29+
window.open(`https://commons.wikimedia.org/wiki/File:BSicon_${src.value}.svg`);
30+
}
31+
</script>
32+
33+
<style lang="scss" module>
34+
.item {
35+
display: flex;
36+
width: auto;
37+
margin-right: calc(var(--n-option-suffix-width) * -1 + 2px);
38+
39+
.name {
40+
flex: 1 1 auto;
41+
user-select: none;
42+
font-family: monospace;
43+
margin-right: 16px;
44+
}
45+
46+
.open {
47+
flex: 0 0 20px;
48+
opacity: 0;
49+
transition: opacity 200ms;
50+
}
51+
52+
&:hover {
53+
.open {
54+
opacity: 1;
55+
}
56+
}
57+
}
58+
</style>

src/components/BSMap/BSRow.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
<div :class="$style.cells" :style="rowStyle">
44
<BSCell v-for="(cell, index) in props.row.cells" :key="index" :cell="cell"
55
:focused="isFocused(cell.offset - props.row.offset, cell.length)"
6-
@select="() => emit('select', cell.offset - props.row.offset, cell.length)" />
6+
@select="(offset, length) => emit('select', cell.offset - props.row.offset + offset, length)" />
77
</div>
88
<div :class="$style.infos">
99
<BSInfo v-for="(info, index) in props.row.rInfo.filter(({ text }) => !!text)" :key="index" :info="info"
1010
:focused="isFocused(info.offset - props.row.offset, info.length)"
11-
@select="() => emit('select', info.offset - props.row.offset, info.length)" />
11+
@select="(offset, length) => emit('select', info.offset - props.row.offset + offset, length)" />
1212
</div>
1313
</div>
1414
</template>

0 commit comments

Comments
 (0)