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