generated from tpl/obsidian-sample-plugin
Add spice field handling
This commit is contained in:
parent
20ffc78e8e
commit
113ddefac7
|
@ -302,7 +302,10 @@ export default class BookTrackerPlugin extends Plugin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rating = await RatingModal.createAndOpen(this.app);
|
const rating = await RatingModal.createAndOpen(
|
||||||
|
this.app,
|
||||||
|
this.settings.spiceProperty !== ""
|
||||||
|
);
|
||||||
|
|
||||||
await this.readingLog.addEntry(
|
await this.readingLog.addEntry(
|
||||||
activeFile.basename,
|
activeFile.basename,
|
||||||
|
@ -317,6 +320,9 @@ export default class BookTrackerPlugin extends Plugin {
|
||||||
frontMatter[this.settings.statusProperty] = READ_STATE;
|
frontMatter[this.settings.statusProperty] = READ_STATE;
|
||||||
frontMatter[this.settings.endDateProperty] = endDate;
|
frontMatter[this.settings.endDateProperty] = endDate;
|
||||||
frontMatter[this.settings.ratingProperty] = rating;
|
frontMatter[this.settings.ratingProperty] = rating;
|
||||||
|
if (this.settings.spiceProperty !== "") {
|
||||||
|
frontMatter[this.settings.spiceProperty] = rating;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
new Notice("Reading finished for " + activeFile.name);
|
new Notice("Reading finished for " + activeFile.name);
|
||||||
|
|
|
@ -1,14 +1,26 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Star, StarHalf } from "lucide-svelte";
|
import { Star, StarHalf } from "lucide-svelte";
|
||||||
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
value?: number;
|
value?: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
|
max?: number;
|
||||||
|
half?: boolean;
|
||||||
|
inactive?: Snippet;
|
||||||
|
active?: Snippet;
|
||||||
|
partial?: Snippet;
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxV = 5;
|
let {
|
||||||
|
value = $bindable(),
|
||||||
let { value = $bindable(), name }: Props = $props();
|
name = "",
|
||||||
|
max = 5,
|
||||||
|
half = false,
|
||||||
|
inactive,
|
||||||
|
active,
|
||||||
|
partial,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
let ctrl: HTMLElement | null = $state(null);
|
let ctrl: HTMLElement | null = $state(null);
|
||||||
let w = $state(0);
|
let w = $state(0);
|
||||||
|
@ -27,7 +39,7 @@
|
||||||
let displayVal = $derived(hovering ? valueHover : (value ?? 0));
|
let displayVal = $derived(hovering ? valueHover : (value ?? 0));
|
||||||
let items = $derived.by(() => {
|
let items = $derived.by(() => {
|
||||||
const full = Number.isInteger(displayVal);
|
const full = Number.isInteger(displayVal);
|
||||||
return Array.from({ length: maxV }, (_, i) => i + 1).map((index) => ({
|
return Array.from({ length: max }, (_, i) => i + 1).map((index) => ({
|
||||||
index,
|
index,
|
||||||
state:
|
state:
|
||||||
index <= Math.ceil(displayVal)
|
index <= Math.ceil(displayVal)
|
||||||
|
@ -39,7 +51,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
function calcSliderPos(e: MouseEvent) {
|
function calcSliderPos(e: MouseEvent) {
|
||||||
return (e.offsetX / (e.target as HTMLElement).clientWidth) * maxV;
|
return (e.offsetX / (e.target as HTMLElement).clientWidth) * max;
|
||||||
}
|
}
|
||||||
|
|
||||||
function onclick() {
|
function onclick() {
|
||||||
|
@ -53,7 +65,14 @@
|
||||||
|
|
||||||
function onmousemove(e: MouseEvent) {
|
function onmousemove(e: MouseEvent) {
|
||||||
hovering = true;
|
hovering = true;
|
||||||
valueHover = Math.ceil(calcSliderPos(e) * 2) / 2;
|
|
||||||
|
const newValue = calcSliderPos(e);
|
||||||
|
|
||||||
|
if (half) {
|
||||||
|
valueHover = Math.ceil(newValue * 2) / 2;
|
||||||
|
} else {
|
||||||
|
valueHover = Math.ceil(newValue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -62,18 +81,37 @@
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
<!-- svelte-ignore a11y_mouse_events_have_key_events -->
|
||||||
<div class="rating-input" {onclick} {onmouseout}>
|
<div class="rating-input" {onclick} {onmouseout}>
|
||||||
|
<input type="number" {name} bind:value />
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<div class="ctrl" {onmousemove} bind:this={ctrl}></div>
|
<div class="ctrl" {onmousemove} bind:this={ctrl}></div>
|
||||||
<div class="cont m-1" bind:clientWidth={w} bind:clientHeight={h}>
|
<div class="cont m-1" bind:clientWidth={w} bind:clientHeight={h}>
|
||||||
{#each items as item (item.index)}
|
{#each items as item (item.index)}
|
||||||
<div class="rating-item">
|
<div class="rating-item">
|
||||||
{#if item.state === "inactive"}
|
{#if item.state === "inactive"}
|
||||||
<Star fill="var(--interactive-normal)" />
|
{#if inactive}
|
||||||
|
{@render inactive()}
|
||||||
|
{:else}
|
||||||
|
<Star color="var(--background-modifier-border)" />
|
||||||
|
{/if}
|
||||||
{:else if item.state === "active"}
|
{:else if item.state === "active"}
|
||||||
<Star fill="var(--interactive-accent)" />
|
{#if active}
|
||||||
|
{@render active()}
|
||||||
|
{:else}
|
||||||
|
<Star
|
||||||
|
color="var(--color-yellow)"
|
||||||
|
fill="rgba(var(--color-yellow-rgb), 0.2)"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{:else if item.state === "partial"}
|
{:else if item.state === "partial"}
|
||||||
<Star fill="var(--interactive-normal)" />
|
{#if partial}
|
||||||
<StarHalf fill="var(--interactive-accent)" />
|
{@render partial()}
|
||||||
|
{:else}
|
||||||
|
<Star color="var(--background-modifier-border)" />
|
||||||
|
<StarHalf
|
||||||
|
color="var(--color-yellow)"
|
||||||
|
fill="rgba(var(--color-yellow-rgb), 0.2)"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -84,6 +122,10 @@
|
||||||
.rating-input {
|
.rating-input {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
input {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.ctrl {
|
.ctrl {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
|
@ -3,16 +3,27 @@ import { App } from "obsidian";
|
||||||
import { SvelteModal } from "./SvelteModal";
|
import { SvelteModal } from "./SvelteModal";
|
||||||
|
|
||||||
export class RatingModal extends SvelteModal<typeof RatingModalView> {
|
export class RatingModal extends SvelteModal<typeof RatingModalView> {
|
||||||
constructor(app: App, onSubmit: (rating: number) => void = () => {}) {
|
constructor(
|
||||||
super(app, RatingModalView, { props: { onSubmit } });
|
app: App,
|
||||||
|
spiceConfigured: boolean,
|
||||||
|
onSubmit: (rating: number, spice: number) => void = () => {}
|
||||||
|
) {
|
||||||
|
super(app, RatingModalView, { props: { spiceConfigured, onSubmit } });
|
||||||
}
|
}
|
||||||
|
|
||||||
static createAndOpen(app: App): Promise<number> {
|
static createAndOpen(
|
||||||
|
app: App,
|
||||||
|
spiceConfigured: boolean
|
||||||
|
): Promise<{ rating: number; spice: number }> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const modal = new RatingModal(app, (rating) => {
|
const modal = new RatingModal(
|
||||||
|
app,
|
||||||
|
spiceConfigured,
|
||||||
|
(rating, spice) => {
|
||||||
modal.close();
|
modal.close();
|
||||||
resolve(rating);
|
resolve({ rating, spice });
|
||||||
});
|
}
|
||||||
|
);
|
||||||
modal.open();
|
modal.open();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,53 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import RatingInput from "@ui/components/RatingInput.svelte";
|
import RatingInput from "@ui/components/RatingInput.svelte";
|
||||||
|
import { Flame } from "lucide-svelte";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onSubmit: (value: number) => void;
|
spiceConfigured?: boolean;
|
||||||
|
onSubmit: (rating: number, spice: number) => void;
|
||||||
}
|
}
|
||||||
let { onSubmit }: Props = $props();
|
let { spiceConfigured = false, onSubmit }: Props = $props();
|
||||||
|
|
||||||
let value = $state(0);
|
let rating = $state(0);
|
||||||
|
let spice = $state(0);
|
||||||
|
|
||||||
function onsubmit(ev: SubmitEvent) {
|
function onsubmit(ev: SubmitEvent) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
onSubmit(value);
|
onSubmit(rating, spice);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="obt-rating">
|
<div class="obt-rating">
|
||||||
<h2>Rating</h2>
|
<h2>Rating</h2>
|
||||||
<form {onsubmit}>
|
<form {onsubmit}>
|
||||||
<div class="value-field">
|
<div class="field">
|
||||||
<label for="value">Rating</label>
|
<label for="rating">Rating</label>
|
||||||
<RatingInput bind:value />
|
<RatingInput name="rating" half bind:value={rating} />
|
||||||
</div>
|
</div>
|
||||||
|
{#if spiceConfigured}
|
||||||
|
<div class="field">
|
||||||
|
<label for="spice">Spice</label>
|
||||||
|
<RatingInput name="spice" bind:value={spice}>
|
||||||
|
{#snippet inactive()}
|
||||||
|
<Flame color="var(--background-modifier-border)" />
|
||||||
|
{/snippet}
|
||||||
|
{#snippet active()}
|
||||||
|
<Flame
|
||||||
|
color="var(--color-red)"
|
||||||
|
fill="rgba(var(--color-red-rgb), 0.2)"
|
||||||
|
/>
|
||||||
|
{/snippet}
|
||||||
|
</RatingInput>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<button type="submit">Submit</button>
|
<button type="submit">Submit</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@use "../styles/utils";
|
||||||
|
|
||||||
.obt-rating {
|
.obt-rating {
|
||||||
padding-bottom: var(--size-4-4);
|
padding-bottom: var(--size-4-4);
|
||||||
|
|
||||||
|
@ -39,13 +60,13 @@
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--size-4-4);
|
gap: var(--size-4-4);
|
||||||
|
|
||||||
.value-field {
|
.field {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
label {
|
label {
|
||||||
display: none;
|
@include utils.visually-hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
import type BookTrackerPlugin from "@src/main";
|
import type BookTrackerPlugin from "@src/main";
|
||||||
import { createSettingsStore } from "./store";
|
import { createSettingsStore } from "./store";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import FieldSuggest from "@ui/components/suggesters/FieldSuggest.svelte";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
plugin: BookTrackerPlugin;
|
plugin: BookTrackerPlugin;
|
||||||
|
@ -121,6 +122,15 @@
|
||||||
bind:value={$settings.ratingProperty}
|
bind:value={$settings.ratingProperty}
|
||||||
accepts={["number"]}
|
accepts={["number"]}
|
||||||
/>
|
/>
|
||||||
|
<FieldSuggestItem
|
||||||
|
{app}
|
||||||
|
id="spice-field"
|
||||||
|
name="Spice Field"
|
||||||
|
description={`Select the field to use for spice rating.
|
||||||
|
Set to empty to disable.`}
|
||||||
|
bind:value={$settings.spiceProperty}
|
||||||
|
accepts={["number"]}
|
||||||
|
/>
|
||||||
<FieldSuggestItem
|
<FieldSuggestItem
|
||||||
{app}
|
{app}
|
||||||
id="page-count-field"
|
id="page-count-field"
|
||||||
|
|
|
@ -12,6 +12,7 @@ export interface BookTrackerSettings {
|
||||||
startDateProperty: string;
|
startDateProperty: string;
|
||||||
endDateProperty: string;
|
endDateProperty: string;
|
||||||
ratingProperty: string;
|
ratingProperty: string;
|
||||||
|
spiceProperty: string;
|
||||||
pageCountProperty: string;
|
pageCountProperty: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,5 +30,6 @@ export const DEFAULT_SETTINGS: BookTrackerSettings = {
|
||||||
startDateProperty: "startDate",
|
startDateProperty: "startDate",
|
||||||
endDateProperty: "endDate",
|
endDateProperty: "endDate",
|
||||||
ratingProperty: "rating",
|
ratingProperty: "rating",
|
||||||
|
spiceProperty: "",
|
||||||
pageCountProperty: "pageCount",
|
pageCountProperty: "pageCount",
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue