Skip to content

Commit 6d0b680

Browse files
committed
fix #246
1 parent e81419e commit 6d0b680

File tree

12 files changed

+206
-51
lines changed

12 files changed

+206
-51
lines changed

exampleVault/View Fields/View Field.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ file: Example Note with Embeds
1414
image: Other/Images/img_butterfly.webp
1515
someInputValue: 1
1616
someComputedValue: 2
17+
images:
18+
- Other/Images/img_flower.webp
19+
- Other/Images/img_butterfly.webp
20+
- Other/Images/subfolder/img_frozen_branch.jpg
21+
- Other/Images/img_drops.jpg
1722
---
1823

1924
`INPUT[number:number1]`
@@ -57,8 +62,23 @@ Self Loop Error: `VIEW[**{computed}**][text():computed]`
5762
INPUT[imageSuggester(optionQuery("Other/Images")):image]
5863
```
5964

60-
6165
`VIEW[!\[\[{image}\]\]][text(renderMarkdown)]`
66+
`VIEW[{image}][image]`
67+
68+
```meta-bind
69+
INPUT[imageSuggester(optionQuery("Other/Images")):images[0]]
70+
```
71+
```meta-bind
72+
INPUT[imageSuggester(optionQuery("Other/Images")):images[1]]
73+
```
74+
```meta-bind
75+
INPUT[imageSuggester(optionQuery("Other/Images")):images[2]]
76+
```
77+
```meta-bind
78+
INPUT[imageSuggester(optionQuery("Other/Images")):images[3]]
79+
```
80+
81+
`VIEW[{images}][image]`
6282

6383
## Arrays and Objects
6484

packages/core/src/config/FieldConfigs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ export enum ViewFieldType {
484484
MATH = 'math',
485485
TEXT = 'text',
486486
LINK = 'link',
487+
IMAGE = 'image',
487488

488489
INVALID = 'invalid',
489490
}

packages/core/src/fields/inputFields/fields/ImageSuggester/ImageSuggesterCard.svelte

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,27 +13,12 @@
1313
</script>
1414

1515
<div
16-
class="image-card"
16+
class="mb-image-card"
1717
on:click={() => onSelect(image)}
1818
on:keydown={event => keySelect(event, image)}
1919
role="button"
2020
tabindex="0"
2121
>
22-
<img class="image-card-image" src={plugin.internal.imagePathToUri(image)} alt={image} />
23-
<span class="image-card-text">{image}</span>
22+
<img class="mb-image-card-image" src={plugin.internal.imagePathToUri(image)} alt={image} />
23+
<span class="mb-image-card-text">{image}</span>
2424
</div>
25-
26-
<style>
27-
.image-card-image {
28-
width: 100%;
29-
height: fit-content;
30-
max-height: 500px;
31-
object-fit: contain;
32-
}
33-
34-
.image-card-text {
35-
display: block;
36-
margin: var(--size-4-2);
37-
margin-bottom: var(--size-4-4);
38-
}
39-
</style>

packages/core/src/fields/inputFields/fields/ImageSuggester/ImageSuggesterComponent.svelte

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -31,32 +31,3 @@
3131
<button class="btn btn-active" on:click={openSuggester} on:keydown={openSuggesterOnKey}> Change Image</button>
3232
</div>
3333
</div>
34-
35-
<style>
36-
.mb-image-suggest-input {
37-
background: var(--background-secondary);
38-
border-radius: var(--mb-border-radius);
39-
border: var(--mb-border-width) solid var(--background-modifier-border);
40-
padding: var(--size-4-2);
41-
width: 100%;
42-
}
43-
44-
.mb-image-suggest-image {
45-
width: 100%;
46-
height: fit-content;
47-
max-height: 500px;
48-
object-fit: contain;
49-
}
50-
51-
.mb-image-suggest-footer {
52-
display: flex;
53-
flex-direction: row;
54-
color: var(--text-normal);
55-
align-items: baseline;
56-
}
57-
58-
.mb-image-suggest-footer-text {
59-
flex: 1;
60-
margin-left: var(--size-4-2);
61-
}
62-
</style>

packages/core/src/fields/viewFields/ViewFieldFactory.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { type ViewFieldBase } from 'packages/core/src/fields/viewFields/ViewFiel
55
import { LinkVF } from 'packages/core/src/fields/viewFields/fields/LinkVF';
66
import { MathVF } from 'packages/core/src/fields/viewFields/fields/MathVF';
77
import { TextVF } from 'packages/core/src/fields/viewFields/fields/TextVF';
8+
import { ImageVF } from 'packages/core/src/fields/viewFields/fields/ImageVF';
9+
import { expectType } from 'packages/core/src/utils/Utils';
810

911
export class ViewFieldFactory {
1012
plugin: IPlugin;
@@ -24,8 +26,12 @@ export class ViewFieldFactory {
2426
return new TextVF(base);
2527
} else if (type === ViewFieldType.LINK) {
2628
return new LinkVF(base);
29+
} else if (type === ViewFieldType.IMAGE) {
30+
return new ImageVF(base);
2731
}
2832

33+
expectType<ViewFieldType.INVALID>(type);
34+
2935
return undefined;
3036
}
3137
}
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import { AbstractViewField } from 'packages/core/src/fields/viewFields/AbstractViewField';
2+
import { type ViewFieldBase } from 'packages/core/src/fields/viewFields/ViewFieldBase';
3+
import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser';
4+
import { type BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration';
5+
import { Signal } from 'packages/core/src/utils/Signal';
6+
import { getUUID } from 'packages/core/src/utils/Utils';
7+
import {
8+
ErrorLevel,
9+
MetaBindExpressionError,
10+
MetaBindValidationError,
11+
} from 'packages/core/src/utils/errors/MetaBindErrors';
12+
import ImageGrid from 'packages/core/src/utils/components/ImageGrid.svelte';
13+
14+
export class ImageVF extends AbstractViewField {
15+
component?: ImageGrid;
16+
17+
constructor(base: ViewFieldBase) {
18+
super(base);
19+
}
20+
21+
protected buildVariables(): void {
22+
// filter out empty strings
23+
const entries: (string | BindTargetDeclaration)[] = this.base
24+
.getDeclaration()
25+
.declarationArray.filter(x => (typeof x === 'string' ? x : true));
26+
27+
if (entries.length !== 1) {
28+
throw new MetaBindValidationError({
29+
errorLevel: ErrorLevel.ERROR,
30+
effect: 'can not create view field',
31+
cause: 'image view filed only supports exactly a single bind target and not text content',
32+
});
33+
}
34+
35+
const firstEntry = entries[0];
36+
if (typeof firstEntry === 'string') {
37+
throw new MetaBindValidationError({
38+
errorLevel: ErrorLevel.ERROR,
39+
effect: 'can not create view field',
40+
cause: 'image view filed only supports exactly a single bind target and not text content',
41+
});
42+
}
43+
44+
firstEntry.listenToChildren = true;
45+
46+
this.variables = [
47+
{
48+
bindTargetDeclaration: firstEntry,
49+
inputSignal: new Signal<unknown>(undefined),
50+
uuid: getUUID(),
51+
contextName: `MB_VAR_0`,
52+
},
53+
];
54+
}
55+
56+
protected computeValue(): string {
57+
if (this.variables.length !== 1) {
58+
throw new MetaBindExpressionError({
59+
errorLevel: ErrorLevel.CRITICAL,
60+
effect: 'failed to evaluate image view field',
61+
cause: 'there should be exactly one variable',
62+
});
63+
}
64+
65+
const variable = this.variables[0];
66+
const content = variable.inputSignal.get();
67+
68+
// we want the return value to be a human-readable string, since someone could save this to the frontmatter
69+
if (typeof content === 'string') {
70+
return MDLinkParser.convertToLinkString(content);
71+
} else if (Array.isArray(content)) {
72+
const strings = content.filter(x => typeof x === 'string') as string[];
73+
return strings
74+
.map(x => MDLinkParser.convertToLinkString(x))
75+
.filter(x => x !== '')
76+
.join(', ');
77+
} else {
78+
return '';
79+
}
80+
}
81+
82+
protected onInitialRender(container: HTMLElement): void {
83+
this.component = new ImageGrid({
84+
target: container,
85+
props: {
86+
images: [],
87+
plugin: this.base.plugin,
88+
},
89+
});
90+
}
91+
92+
protected async onRerender(container: HTMLElement, text: string): Promise<void> {
93+
const linkList = MDLinkParser.parseLinkList(text);
94+
this.component?.$destroy();
95+
this.component = new ImageGrid({
96+
target: container,
97+
props: {
98+
images: linkList.map(x => x.target),
99+
plugin: this.base.plugin,
100+
},
101+
});
102+
}
103+
104+
protected onUnmount(): void {
105+
super.onUnmount();
106+
107+
this.component?.$destroy();
108+
}
109+
}

packages/core/src/fields/viewFields/fields/LinkVF.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export class LinkVF extends AbstractViewField {
4141
});
4242
}
4343

44+
firstEntry.listenToChildren = true;
45+
4446
this.variables = [
4547
{
4648
bindTargetDeclaration: firstEntry,

packages/core/src/modals/modalContents/ImageSuggesterModalComponent.svelte

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,3 @@
3333
<ImageSuggesterCard plugin={plugin} image={option.value} onSelect={onSelect}></ImageSuggesterCard>
3434
{/each}
3535
</div>
36-
37-
<style>
38-
</style>

packages/core/src/utils/InputFieldExamples.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export const VIEW_FIELD_EXAMPLE_DECLARATIONS: Record<ViewFieldType, string> = {
3535
math: 'VIEW[{exampleProperty} + 2][math]',
3636
text: 'VIEW[some text {exampleProperty}][text]',
3737
link: 'VIEW[{exampleProperty}][link]',
38+
image: 'VIEW[{exampleProperty}][image]',
3839

3940
invalid: '',
4041
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<script lang="ts">
2+
import { IPlugin } from 'packages/core/src/IPlugin';
3+
4+
export let plugin: IPlugin;
5+
export let image: string;
6+
</script>
7+
8+
<div class="mb-image-card">
9+
<img class="mb-image-card-image" src={plugin.internal.imagePathToUri(image)} alt={image} />
10+
</div>

0 commit comments

Comments
 (0)