Skip to content

Commit

Permalink
Bring full support for row- and cell-params (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
xingrz committed Mar 27, 2024
1 parent 57e067b commit 11d69b4
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 66 deletions.
20 changes: 13 additions & 7 deletions src/components/BSMap/BSCell.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div :class="$style.cell" :title="content" @click="handleClick" :style="style">
<BSIcon v-for="(icon, index) in icons" :key="index" :content="icon"
<BSIcon v-for="(icon, index) in (parts?.icons || [])" :key="index" :content="icon" :stacked="stacked"
@ratio="(ratio: number) => updateRatio(index, ratio)" />
</div>
</template>
Expand All @@ -14,6 +14,7 @@ import {
} from 'vue';
import { useEditorStore } from '@/stores/editor';
import styleFromParams from '@/utils/styleFromParams';
import BSIcon from './BSIcon.vue';
Expand All @@ -27,15 +28,20 @@ const editorStore = useEditorStore();
const ratio = ref(1);
const icons = computed(() => {
if (!props.content) return [];
return props.content
.split('!~')
.map((icon) => icon.trim())
.filter((icon) => !!icon);
const parts = computed(() => {
if (!props.content) return;
const [nonParam, ...params] = props.content.split('!_');
const [nonLink, ...links] = nonParam.split('!@');
const icons = nonLink.split('!~').filter((icon) => !!icon);
return { icons, links, params };
});
const stacked = computed(() => !!parts.value && parts.value.icons.length > 1);
const style = computed(() => ({
...styleFromParams(parts.value?.params?.[0]),
'--bs-map-cell-ratio': (ratio.value == 1 ? undefined : ratio.value),
}) as CSSProperties);
Expand Down
93 changes: 36 additions & 57 deletions src/components/BSMap/BSIcon.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<template>
<div v-if="label" :class="$style.label" :style="style" :data-bold="label.params.b || label.params.bold"
:data-align="(label.params.align || ``).toUpperCase()">
<span>{{ label.text }}</span>
<div v-if="text" :class="$style.text" :style="style">
<span>{{ text.data }}</span>
</div>
<img v-else :class="$style.icon" :style="style" :src="icon?.data" />
</template>
Expand All @@ -12,16 +11,15 @@ import {
computed,
defineEmits,
defineProps,
onMounted,
toRef,
watch,
} from 'vue';
import { parse } from 'qs';
import { useIconStore } from '@/stores/icon';
import styleFromParams from '@/utils/styleFromParams';
const props = defineProps<{
content: string;
stacked: boolean;
}>();
const emit = defineEmits<{
Expand All @@ -30,40 +28,39 @@ const emit = defineEmits<{
const iconStore = useIconStore();
const content = toRef(props, 'content');
const content = computed<string>(() => {
return props.stacked ? props.content.split('__')[0] : props.content;
});
const params = computed<string | undefined>(() => {
// available only for stacked icons
return props.stacked ? props.content.split('__')[1] : undefined;
});
const label = computed(() => {
if (content.value && content.value.match(/^(.*)\*([^_]+)(__(.+)$)?/)) {
const ratio = selectTextWidth(RegExp.$1) as number;
const text = RegExp.$2;
const params = parseTextParams(RegExp.$4 || '');
return { text, ratio, params };
const text = computed(() => {
let match: RegExpMatchArray | null = null;
if (content.value && (match = content.value.match(/^(.*)\*([^_]+)$/))) {
const ratio = selectTextWidth(match[1]) as number;
const data = match[2];
return { data, ratio };
} else {
return undefined;
}
});
const icon = computed(() => label.value ? undefined : iconStore.icons[content.value]);
const icon = computed(() => text.value ? undefined : iconStore.icons[content.value]);
watch(content, (content) => {
if (content && !label.value) {
if (content && !text.value) {
iconStore.resolve(content);
}
}, { immediate: true });
const ratio = computed(() => {
if (!content.value) return 1;
if (label.value) {
return label.value.ratio || 1;
} else {
return icon.value?.ratio || 1;
}
});
watch(ratio, (ratio) => emit('ratio', ratio));
onMounted(() => emit('ratio', ratio.value));
const ratio = computed(() => (text.value ?? icon.value)?.ratio ?? 1);
watch(ratio, (ratio) => emit('ratio', ratio), { immediate: true });
const style = computed(() => ({
...styleFromParams(params.value),
'--bs-map-icon-ratio': (ratio.value == 1 ? undefined : ratio.value),
}) as CSSProperties);
Expand All @@ -75,59 +72,41 @@ function selectTextWidth(flag: string): number | undefined {
return 0.25;
case 'd':
return 0.5;
case 'cd':
return 0.75;
case 'b':
return 2;
case 's':
return 4;
case 'bs':
return 6;
case 'w':
return 8;
default:
return undefined;
}
}
function parseTextParams(str: string): Record<string, string> {
return parse(str, {
delimiter: ','
}) as Record<string, string>;
}
</script>

<style lang="scss" module>
.icon,
.label {
.text {
position: absolute;
user-select: none;
width: calc(var(--bs-map-size) * var(--bs-map-icon-ratio, 1) * 1px);
height: calc(var(--bs-map-size) * 1px);
}
.label {
.text {
font-family: monospace;
font-size: calc(var(--bs-map-size) * 1px - 8px);
text-align: center;
font-size: calc(var(--bs-map-size) / 2 * 1px);
text-align: var(--bs-map-cell-halign, center);
z-index: 1; // texts are always stacked over icons
span {
line-height: 0.75;
}
&[data-align="L"] {
text-align: left;
}
&[data-align="R"] {
text-align: right;
}
&[data-align="A"] span {
vertical-align: top;
}
&[data-align="E"] span {
vertical-align: bottom;
}
&[data-bold="1"] {
font-weight: bold;
line-height: 1;
vertical-align: var(--bs-map-cell-valign, center);
}
}
</style>
8 changes: 6 additions & 2 deletions src/components/BSMap/BSRow.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div :class="$style.row">
<div :class="$style.cells">
<div :class="$style.cells" :style="rowStyle">
<BSCell v-for="({ cell, offset }, index) in cells" :key="index"
:class="{ [$style.seletable]: true, [$style.focused]: isFocused(row, offset, cell.length) }" :content="cell"
:row="row" :offset="offset" />
Expand All @@ -17,6 +17,7 @@
import { computed, defineProps } from 'vue';
import { useEditorStore } from '@/stores/editor';
import styleFromParams from '@/utils/styleFromParams';
import BSCell from './BSCell.vue';
import BSText from './BSText.vue';
Expand Down Expand Up @@ -47,7 +48,7 @@ const cells = computed(() => {
});
const texts = computed(() => {
const texts = parts.value.slice(1)
const texts = parts.value.slice(1, 5)
.map(({ part, offset }, i) => ({ text: part, offset, align: i + 1 }));
// Shorthand that the only text is treated as the main (the 2nd) text
Expand All @@ -58,6 +59,8 @@ const texts = computed(() => {
return texts.filter(({ text }) => text != '' && text != ' ');
});
const rowStyle = computed(() => styleFromParams(parts.value[5]?.part));
function isFocused(row: number, offset: number, length: number): boolean {
const { selection } = editorStore;
return (
Expand Down Expand Up @@ -115,3 +118,4 @@ function isFocused(row: number, offset: number, length: number): boolean {
}
}
</style>
@/utils/styleFromParams
51 changes: 51 additions & 0 deletions src/utils/styleFromParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { parse } from 'qs';
import type { CSSProperties } from 'vue';

const YES = ['1', 'yes', 'y', 'true'];
const LEFT = ['l', 'left'];
const RIGHT = ['r', 'right'];
const TOP_LEFT = ['la', 'tl', 'c4', 'nw', 'top-left', 'topleft'];
const TOP_RIGHT = ['ra', 'tr', 'c1', 'ne', 'top-right', 'topright'];
const BOTTOM_LEFT = ['le', 'bl', 'c3', 'sw', 'bottom-left', 'bottomleft'];
const BOTTOM_RIGHT = ['re', 'br', 'c2', 'se', 'bottom-right', 'bottomright'];

export default function styleFromParams(text: string | undefined): CSSProperties {
const style = {} as CSSProperties;
const params = parse(text ?? '', { delimiter: ',' }) as Record<string, string>;

if (params.bg) {
style.backgroundColor = params.bg;
} else if (params.background) {
style.backgroundColor = params.background;
} else if (params.bgcolor) {
style.backgroundColor = params.bgcolor;
}

if (params.color) {
style.color = params.color;
} else if (params.colour) {
style.color = params.colour;
}

if (YES.includes(params.b ?? params.bold)) {
style.fontWeight = 'bold';
}

if (YES.includes(params.i ?? params.it ?? params.italic)) {
style.fontStyle = 'italic';
}

if ([...LEFT, ...TOP_LEFT].includes(params.align)) {
style['--bs-map-cell-halign'] = 'left';
} else if ([...RIGHT, ...TOP_RIGHT].includes(params.align)) {
style['--bs-map-cell-halign'] = 'right';
}

if ([...TOP_LEFT, ...TOP_RIGHT].includes(params.align)) {
style['--bs-map-cell-valign'] = 'top';
} else if ([...BOTTOM_LEFT, ...BOTTOM_RIGHT].includes(params.align)) {
style['--bs-map-cell-valign'] = 'bottom';
}

return style;
}

0 comments on commit 11d69b4

Please sign in to comment.