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 Apr 4, 2024
1 parent 277f742 commit a1d7b2d
Show file tree
Hide file tree
Showing 6 changed files with 203 additions and 6 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
29 changes: 23 additions & 6 deletions src/components/BSMap/BSCell.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
<template>
<BSSelectable v-slot="{ selectable }" :row="props.row" :offset="props.offset" :length="props.src.length">
<div :class="[selectable, $style.cell]" :title="props.src" @click="handleClick" :style="style">
<BSIcon v-for="(icon, index) in (parts?.icons || [])" :key="index" :src="icon"
@ratio="(ratio: number) => updateRatio(index, ratio)" />
</div>
<BSPopover v-model:show="popoverShown" :row="props.row" :offset="props.offset" :ratio="ratio"
:icons="parts?.icons || []" :params="parts?.params">
<div :class="[selectable, $style.cell]" :style="style" @click="handleClick"
@click.right.prevent="handleRightClick">
<BSIcon v-for="({ part }, index) in (parts?.icons || [])" :key="index" :src="part"
@ratio="(ratio: number) => updateRatio(index, ratio)" />
</div>
</BSPopover>
</BSSelectable>
</template>
Expand All @@ -16,9 +20,11 @@ import {
} from 'vue';
import { useEditorStore } from '@/stores/editor';
import splitWithOffset from '@/utils/splitWithOffset';
import styleFromParams from '@/utils/styleFromParams';
import BSSelectable from './BSSelectable.vue';
import BSPopover from './BSPopover.vue';
import BSIcon from './BSIcon.vue';
const props = defineProps<{
Expand All @@ -34,9 +40,13 @@ const ratio = ref(1);
const parts = computed(() => {
if (!props.src) return;
const [nonParam, ...params] = props.src.split('!_');
const match = props.src.match(/^( *)([^ ]+)( *)$/);
const leading = (match?.[1] || '').length;
const nonSpace = match?.[2] || props.src;
const [nonParam, ...params] = nonSpace.split('!_');
const [nonLink, ...links] = nonParam.split('!@');
const icons = nonLink.split('!~').filter((icon) => !!icon);
const icons = splitWithOffset(nonLink, '!~', leading).filter(({ part }) => !!part);
return { icons, links, params };
});
Expand All @@ -46,6 +56,8 @@ const style = computed(() => ({
'--bs-map-cell-ratio': (ratio.value == 1 ? undefined : ratio.value),
}) as CSSProperties);
const popoverShown = ref(false);
function handleClick(): void {
editorStore.selection = {
row: props.row,
Expand All @@ -55,6 +67,11 @@ function handleClick(): void {
};
}
function handleRightClick(): void {
popoverShown.value = true;
handleClick();
}
function updateRatio(layer: number, newRatio: number): void {
if (layer == 0) {
// Only ratio of the first icon affects the cell
Expand Down
83 changes: 83 additions & 0 deletions src/components/BSMap/BSPopover.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<template>
<n-dropdown show-arrow trigger="manual" placement="right" :disabled="!enabled" :show="show" :options="options"
:render-icon="renderIcon" :render-label="renderLabel" @select="onSelect" @clickoutside="onClickOutside">
<slot />
</n-dropdown>
</template>

<script lang="ts" setup>
import {
type VNode,
computed,
defineProps,
h,
} from 'vue';
import {
type DropdownOption,
NDropdown,
} from 'naive-ui';
import { useEditorStore } from '@/stores/editor';
import BSPopoverIcon from './BSPopoverIcon.vue';
import BSPopoverLabel from './BSPopoverLabel.vue';
const props = defineProps<{
row: number;
offset: number;
ratio: number;
icons: {
part: string;
offset: number;
}[];
params: string[] | undefined;
}>();
const show = defineModel<boolean>('show');
const editorStore = useEditorStore();
type BSCellPopoverOption = DropdownOption & {
offset: number;
};
const enabled = computed(() => props.icons.length > 0);
const options = computed(() => props.icons.map(({ part, offset }) => ({
label: part,
key: `${offset}_${part}`,
offset: offset,
} as BSCellPopoverOption)));
function renderLabel(option: DropdownOption): VNode {
return h(BSPopoverLabel, {
src: option.label as string,
});
}
function renderIcon(option: DropdownOption): VNode {
return h(BSPopoverIcon, {
src: option.label as string,
ratio: props.ratio,
params: props.params?.[0],
});
}
function onSelect(_key: string, option: DropdownOption): void {
const { label, offset } = option as BSCellPopoverOption;
const src = (label as string).split('__')[0];
editorStore.selection = {
row: props.row,
offset: props.offset + offset,
length: src.length,
from: 'preview',
};
show.value = false;
}
function onClickOutside(): void {
show.value = false;
}
</script>
35 changes: 35 additions & 0 deletions src/components/BSMap/BSPopoverIcon.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<div :class="$style.cell" :style="style">
<BSIcon :src="props.src" />
</div>
</template>

<script lang="ts" setup>
import { type CSSProperties, computed, defineProps } from 'vue';
import styleFromParams from '@/utils/styleFromParams';
import BSIcon from './BSIcon.vue';
const props = defineProps<{
src: string;
ratio: number | undefined;
params: string | undefined;
}>();
const style = computed(() => ({
...styleFromParams(props.params, true),
'--bs-map-cell-ratio': (props.ratio == 1 ? undefined : props.ratio),
}) as CSSProperties);
</script>


<style lang="scss" module>
.cell {
--bs-map-size: 20;
width: calc(var(--bs-map-size) * var(--bs-map-cell-ratio, 1) * 1px);
height: calc(var(--bs-map-size) * 1px);
line-height: calc(var(--bs-map-size) * 1px);
border: 1px solid #ddd;
}
</style>
55 changes: 55 additions & 0 deletions src/components/BSMap/BSPopoverLabel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<template>
<div :class="$style.item">
<div :class="$style.name">{{ src }}</div>
<n-button v-if="isIcon" :class="$style.open" quaternary block @click.stop="onClick">
<n-icon>
<OpenOutline />
</n-icon>
</n-button>
</div>
</template>

<script lang="ts" setup>
import { computed, defineProps } from 'vue';
import { NButton, NIcon } from 'naive-ui';
import { OpenOutline } from '@vicons/ionicons5';
const props = defineProps<{
src: string;
}>();
const src = computed(() => props.src.split('__')[0]);
const isIcon = computed(() => !props.src.includes('*'));
function onClick(): void {
window.open(`https://commons.wikimedia.org/wiki/File:BSicon_${src.value}.svg`);
}
</script>

<style lang="scss" module>
.item {
display: flex;
width: auto;
margin-right: calc(var(--n-option-suffix-width) * -1 + 2px);
.name {
flex: 1 1 auto;
user-select: none;
font-family: monospace;
margin-right: 16px;
}
.open {
flex: 0 0 20px;
opacity: 0;
transition: opacity 200ms;
}
&:hover {
.open {
opacity: 1;
}
}
}
</style>

0 comments on commit a1d7b2d

Please sign in to comment.