mirror of
https://github.com/rveciana/obsidian-cooklang.git
synced 2026-07-22 05:04:15 +00:00
Allow chosing fractions in quantities. Prettier config.
This commit is contained in:
parent
74b37280e3
commit
e990c31399
7 changed files with 205 additions and 152 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -1,6 +1,3 @@
|
|||
# vscode
|
||||
.vscode
|
||||
|
||||
# Intellij
|
||||
*.iml
|
||||
.idea
|
||||
|
|
|
|||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"[svelte]": {
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
||||
},
|
||||
}
|
||||
|
|
@ -1,29 +1,31 @@
|
|||
<script lang="ts">
|
||||
export let data:string;
|
||||
export let onChange: (newData: string) => void;
|
||||
$: onChange(data)
|
||||
|
||||
export let data: string;
|
||||
export let onChange: (newData: string) => void;
|
||||
$: onChange(data);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
||||
<div class="content" contenteditable="true" bind:textContent={data} on:keydown={(e)=>{
|
||||
if (e.key === 'Enter') {
|
||||
document.execCommand('insertLineBreak')
|
||||
e.preventDefault()
|
||||
}}}>{data}</div>
|
||||
|
||||
<div
|
||||
class="content"
|
||||
contenteditable="true"
|
||||
bind:textContent={data}
|
||||
on:keydown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
document.execCommand('insertLineBreak');
|
||||
e.preventDefault();
|
||||
}
|
||||
}}
|
||||
>
|
||||
{data}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
.content:empty:not(:focus):before {
|
||||
content: "Type the recipe here";
|
||||
opacity: 0.6;
|
||||
}
|
||||
.content{
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
|
||||
.content:empty:not(:focus):before {
|
||||
content: 'Type the recipe here';
|
||||
opacity: 0.6;
|
||||
}
|
||||
.content {
|
||||
white-space: pre-line;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -69,6 +69,17 @@ export class Settings extends PluginSettingTab {
|
|||
this.plugin.saveData(this.plugin.settings);
|
||||
this.plugin.reloadPluginViews();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Show quantities as fractions')
|
||||
.setDesc('Show the ingredient quantities as fractions instead of decimals, if possible')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showFractionsInQuantities)
|
||||
.onChange((value: boolean) => {
|
||||
this.plugin.settings.showFractionsInQuantities = value;
|
||||
this.plugin.saveData(this.plugin.settings);
|
||||
this.plugin.reloadPluginViews();
|
||||
}));
|
||||
|
||||
|
||||
}}
|
||||
|
|
@ -80,6 +91,7 @@ export interface CookLangSettings {
|
|||
showIngredientList: boolean;
|
||||
showCookwareList: boolean;
|
||||
showQuantitiesInline: boolean;
|
||||
showFractionsInQuantities: boolean;
|
||||
}
|
||||
|
||||
export const DEFAULT_SETTINGS: CookLangSettings = {
|
||||
|
|
@ -88,4 +100,5 @@ export const DEFAULT_SETTINGS: CookLangSettings = {
|
|||
showIngredientList: true,
|
||||
showCookwareList: true,
|
||||
showQuantitiesInline: false,
|
||||
showFractionsInQuantities: true,
|
||||
};
|
||||
|
|
@ -1,148 +1,157 @@
|
|||
<script lang="ts">
|
||||
import { Parser, type ParseResult } from '@cooklang/cooklang-ts';
|
||||
import { tooltip } from '@svelte-plugins/tooltips';
|
||||
import i18n from '../lang/i18n';
|
||||
import { Parser, type ParseResult } from '@cooklang/cooklang-ts';
|
||||
import { tooltip } from '@svelte-plugins/tooltips';
|
||||
import i18n from '../lang/i18n';
|
||||
import { DEFAULT_SETTINGS, type CookLangSettings } from './Settings';
|
||||
import { formatNumber } from './utils';
|
||||
|
||||
export let data: string;
|
||||
export let images: Record<string, string> = {};
|
||||
export let settings: CookLangSettings = DEFAULT_SETTINGS;
|
||||
|
||||
export let data:string;
|
||||
export let images: Record<string, string> = {}
|
||||
export let settings: CookLangSettings = DEFAULT_SETTINGS
|
||||
const translateIngredientsQuantity = (quantity: string | number) =>
|
||||
quantity === 'some'
|
||||
? $i18n.t('some')
|
||||
: formatNumber(quantity, settings.showFractionsInQuantities);
|
||||
|
||||
const translateIngredientsQuantity = (quantity:string|number)=>quantity==='some'?$i18n.t('some'):quantity;
|
||||
let recipe: ParseResult;
|
||||
|
||||
let recipe: ParseResult;
|
||||
|
||||
$: recipe = new Parser().parse(data);
|
||||
|
||||
$: recipe = new Parser().parse(data);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if data.length === 0}
|
||||
<p>{$i18n.t('empty')}</p>
|
||||
|
||||
{:else}
|
||||
|
||||
{#if settings.showImages && images.recipe}
|
||||
<img class="image-main" src={images.recipe} alt="Final result" />
|
||||
{/if}
|
||||
{#if settings.showIngredientList && recipe.ingredients.length > 0}
|
||||
<section class="section">
|
||||
<h3 class="section-title">{$i18n.t('ingredients')}</h3>
|
||||
{#if recipe.metadata.servings}
|
||||
<p class="servings">Servings: {recipe.metadata.servings}</p>
|
||||
{/if}
|
||||
|
||||
<ul class="ingredients">
|
||||
{#each recipe.ingredients as ingredient}
|
||||
<li>{translateIngredientsQuantity(ingredient.quantity)} {ingredient.units} {ingredient.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
{#if settings.showCookwareList && recipe.cookwares.length > 0}
|
||||
<section class="section">
|
||||
<h3 class="section-title">{$i18n.t('cookware')}</h3>
|
||||
<ul class="cookware">
|
||||
{#each recipe.cookwares as cookware}
|
||||
<li>{cookware.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
<h2 class="H2">{$i18n.t('method')}:</h2>
|
||||
|
||||
<div>
|
||||
{#each recipe.steps as step, i}
|
||||
<h3>{$i18n.t('step')} {i+1}</h3>
|
||||
{#if settings.showImages && images[i]}
|
||||
<img src={images[i]} alt="Final result" />
|
||||
{/if}
|
||||
<p>
|
||||
{#each step as stepPart}
|
||||
{#if stepPart.type === 'text'}
|
||||
<span>{stepPart.value}</span>
|
||||
{:else if stepPart.type === 'ingredient'}
|
||||
<span class="ingredient" use:tooltip={{ content: `${translateIngredientsQuantity(stepPart.quantity)} ${stepPart.units} ${stepPart.name}`, action:'hover', autoPosition:true, arrow: false }}>{settings.showQuantitiesInline?`${translateIngredientsQuantity(stepPart.quantity)} ${stepPart.units} ${stepPart.name}`: `${stepPart.name}`}</span>
|
||||
{:else if stepPart.type === 'cookware'}
|
||||
<span class="cookware" >{stepPart.name}</span>
|
||||
{:else if stepPart.type === 'timer'}
|
||||
<span class="timer">{stepPart.quantity} {stepPart.units}</span>
|
||||
{/if}
|
||||
|
||||
{/each}
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{#if data.length === 0}
|
||||
<p>{$i18n.t('empty')}</p>
|
||||
{:else}
|
||||
{#if settings.showImages && images.recipe}
|
||||
<img class="image-main" src={images.recipe} alt="Final result" />
|
||||
{/if}
|
||||
{#if settings.showIngredientList && recipe.ingredients.length > 0}
|
||||
<section class="section">
|
||||
<h3 class="section-title">{$i18n.t('ingredients')}</h3>
|
||||
{#if recipe.metadata.servings}
|
||||
<p class="servings">Servings: {recipe.metadata.servings}</p>
|
||||
{/if}
|
||||
|
||||
{#if recipe.metadata.source}
|
||||
<hr />
|
||||
<p>
|
||||
{$i18n.t('source')}: <a href={recipe.metadata.source}>{recipe.metadata.source}</a>
|
||||
</p>
|
||||
{/if}
|
||||
<ul class="ingredients">
|
||||
{#each recipe.ingredients as ingredient}
|
||||
<li>
|
||||
{translateIngredientsQuantity(ingredient.quantity)}
|
||||
{ingredient.units}
|
||||
{ingredient.name}
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
{#if settings.showCookwareList && recipe.cookwares.length > 0}
|
||||
<section class="section">
|
||||
<h3 class="section-title">{$i18n.t('cookware')}</h3>
|
||||
<ul class="cookware">
|
||||
{#each recipe.cookwares as cookware}
|
||||
<li>{cookware.name}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
{/if}
|
||||
<h2 class="H2">{$i18n.t('method')}:</h2>
|
||||
|
||||
<div>
|
||||
{#each recipe.steps as step, i}
|
||||
<h3>{$i18n.t('step')} {i + 1}</h3>
|
||||
{#if settings.showImages && images[i]}
|
||||
<img src={images[i]} alt="Final result" />
|
||||
{/if}
|
||||
<p>
|
||||
{#each step as stepPart}
|
||||
{#if stepPart.type === 'text'}
|
||||
<span>{stepPart.value}</span>
|
||||
{:else if stepPart.type === 'ingredient'}
|
||||
<span
|
||||
class="ingredient"
|
||||
use:tooltip={{
|
||||
content: `${translateIngredientsQuantity(stepPart.quantity)} ${stepPart.units} ${stepPart.name}`,
|
||||
action: 'hover',
|
||||
autoPosition: true,
|
||||
arrow: false
|
||||
}}
|
||||
>{settings.showQuantitiesInline
|
||||
? `${translateIngredientsQuantity(stepPart.quantity)} ${stepPart.units} ${stepPart.name}`
|
||||
: `${stepPart.name}`}</span
|
||||
>
|
||||
{:else if stepPart.type === 'cookware'}
|
||||
<span class="cookware">{stepPart.name}</span>
|
||||
{:else if stepPart.type === 'timer'}
|
||||
<span class="timer">{stepPart.quantity} {stepPart.units}</span>
|
||||
{/if}
|
||||
{/each}
|
||||
</p>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if recipe.metadata.source}
|
||||
<hr />
|
||||
<p>
|
||||
{$i18n.t('source')}: <a href={recipe.metadata.source}>{recipe.metadata.source}</a>
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.image-main{
|
||||
|
||||
width: 100%;}
|
||||
section {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0.25rem;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
|
||||
}
|
||||
.section-title {
|
||||
display: inline-block;
|
||||
left: 5px;
|
||||
top: -1.5rem;
|
||||
position: absolute;
|
||||
.image-main {
|
||||
width: 100%;
|
||||
}
|
||||
section {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 0.25rem;
|
||||
padding: 10px;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
.section-title {
|
||||
display: inline-block;
|
||||
left: 5px;
|
||||
top: -1.5rem;
|
||||
position: absolute;
|
||||
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
padding-left: 0.75rem;
|
||||
padding-right: 0.75rem;
|
||||
|
||||
|
||||
--tw-bg-opacity: 1;
|
||||
background-color:var(--background-primary);
|
||||
}
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: var(--background-primary);
|
||||
}
|
||||
|
||||
.servings {
|
||||
|
||||
width: fit-content;
|
||||
}
|
||||
.servings {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
|
||||
ul.ingredients{
|
||||
column-count: 2;
|
||||
}
|
||||
ul.ingredients {
|
||||
column-count: 2;
|
||||
}
|
||||
|
||||
ul.cookware{
|
||||
column-count: 2;
|
||||
}
|
||||
ul.cookware {
|
||||
column-count: 2;
|
||||
}
|
||||
|
||||
span.cookware {
|
||||
font-weight: 600;
|
||||
}
|
||||
span.cookware {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
span.ingredient {
|
||||
font-weight: 600;
|
||||
}
|
||||
span.ingredient {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
span.timer {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:global(.theme-light .tooltip) {
|
||||
--tooltip-background-color: rgb(235, 232, 233);
|
||||
--tooltip-color: black;
|
||||
--tooltip-box-shadow: 0 1px 8px rgb(125, 123, 123);
|
||||
}
|
||||
|
||||
span.timer {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
:global(.theme-light .tooltip) {
|
||||
--tooltip-background-color: rgb(235, 232, 233);
|
||||
--tooltip-color: black;
|
||||
--tooltip-box-shadow: 0 1px 8px rgb(125, 123, 123);
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -38,3 +38,30 @@ export const getI18n = (data:string) => {
|
|||
|
||||
|
||||
}
|
||||
|
||||
export const formatNumber = (num: number|string, useFraction:boolean=false): string => {
|
||||
if(typeof num === 'string') return num;
|
||||
const epsilon = 0.0001;
|
||||
if(Math.abs(num - 1/2) < epsilon) return useFraction?"1/2":"0.5";
|
||||
else if(Math.abs(num - 1/3) < epsilon) return useFraction?"1/3":"0.33";
|
||||
else if(Math.abs(num - 2/3) < epsilon) return useFraction?"2/3":"0.66";
|
||||
else if(Math.abs(num - 1/4) < epsilon) return useFraction?"1/4":"0.25";
|
||||
else if(Math.abs(num - 3/4) < epsilon) return useFraction?"3/4":"0.75";
|
||||
else if(Math.abs(num - 1/5) < epsilon) return useFraction?"1/5":"0.2";
|
||||
else if(Math.abs(num - 2/5) < epsilon) return useFraction?"2/5":"0.4";
|
||||
else if(Math.abs(num - 3/5) < epsilon) return useFraction?"3/5":"0.6";
|
||||
else if(Math.abs(num - 4/5) < epsilon) return useFraction?"4/5":"0.8";
|
||||
else if(Math.abs(num - 1/6) < epsilon) return useFraction?"1/6":"0.16";
|
||||
else if(Math.abs(num - 5/6) < epsilon) return useFraction?"5/6":"0.83";
|
||||
else if(Math.abs(num - 1/7) < epsilon) return useFraction?"1/7":"0.14";
|
||||
else if(Math.abs(num - 1/8) < epsilon) return useFraction?"1/8":"0.125";
|
||||
else if(Math.abs(num - 3/8) < epsilon) return useFraction?"3/8":"0.375";
|
||||
else if(Math.abs(num - 5/8) < epsilon) return useFraction?"5/8":"0.625";
|
||||
else if(Math.abs(num - 7/8) < epsilon) return useFraction?"7/8":"0.875";
|
||||
else if(Math.abs(num - 1/9) < epsilon) return useFraction?"1/9":"0.11";
|
||||
else if(Math.abs(num - 1/10) < epsilon) return useFraction?"1/10":"0.1";
|
||||
else if(Math.abs(num - 1/12) < epsilon) return useFraction?"1/12":"0.08";
|
||||
else if(Math.abs(num - 1/16) < epsilon) return useFraction?"1/16":"0.06";
|
||||
else if(Math.abs(num - 1/32) < epsilon) return useFraction?"1/32":"0.03";
|
||||
else return num.toString();
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.content.svelte-1x7gjtx:empty:not(:focus):before{content:"Type the recipe here";opacity:0.6}.content.svelte-1x7gjtx{white-space:pre-line}:root{--tooltip-arrow-size:10px;--tooltip-background-color:rgba(0, 0, 0, 0.9);--tooltip-border-radius:4px;--tooltip-box-shadow:0 1px 20px rgba(0, 0, 0, 0.25);--tooltip-font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
.content.svelte-1bdsa1h:empty:not(:focus):before{content:'Type the recipe here';opacity:0.6}.content.svelte-1bdsa1h{white-space:pre-line}:root{--tooltip-arrow-size:10px;--tooltip-background-color:rgba(0, 0, 0, 0.9);--tooltip-border-radius:4px;--tooltip-box-shadow:0 1px 20px rgba(0, 0, 0, 0.25);--tooltip-font-family:-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
|
||||
Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;--tooltip-font-size:14px;--tooltip-font-weight:500;--tooltip-line-height:1.25rem;--tooltip-color:#fff;--tooltip-offset-x:0px;--tooltip-offset-y:0px;--tooltip-padding:12px;--tooltip-pointer-events:none;--tooltip-white-space-hidden:nowrap;--tooltip-white-space-shown:normal;--tooltip-z-index:100}.tooltip.svelte-1xktviv{background-color:var(--tooltip-background-color);box-shadow:var(--tooltip-box-shadow);border-radius:var(--tooltip-border-radius);color:var(--tooltip-color);opacity:0;font-family:var(--tooltip-font-family);font-size:var(--tooltip-font-size);font-style:normal;font-weight:var(--tooltip-font-weight);line-height:var(--tooltip-line-height);padding:var(--tooltip-padding);pointer-events:var(--tooltip-pointer-events);position:absolute;text-align:left;visibility:hidden;white-space:var(--tooltip-white-space-hidden);z-index:var(--tooltip-z-index)}.tooltip.show.svelte-1xktviv{opacity:1;visibility:visible;white-space:var(--tooltip-white-space-shown)}.tooltip.bottom.svelte-1xktviv:after,.tooltip.left.svelte-1xktviv:after,.tooltip.right.svelte-1xktviv:after,.tooltip.top.svelte-1xktviv:after{border:var(--tooltip-arrow-size) solid var(--tooltip-background-color);content:' ';position:absolute}.tooltip.arrowless.svelte-1xktviv:after{border:0 !important}.tooltip.bottom.svelte-1xktviv,.tooltip.top.svelte-1xktviv{--tooltip-offset-x:0px;--tooltip-offset-y:12px}.tooltip.left.svelte-1xktviv,.tooltip.right.svelte-1xktviv{--tooltip-offset-x:12px;--tooltip-offset-y:0px}.tooltip.bottom.svelte-1xktviv{bottom:0;left:50%;transform:translate(
|
||||
calc(-50% + var(--tooltip-offset-x)),
|
||||
calc(100% + var(--tooltip-offset-y))
|
||||
|
|
@ -190,4 +190,4 @@
|
|||
calc(-50% + var(--tooltip-offset-x)),
|
||||
calc(100% + var(--tooltip-offset-y))
|
||||
)
|
||||
scale(1, 1)}.image-main.svelte-1ewzew3{width:100%}section.svelte-1ewzew3{border:1px solid #ccc;border-radius:0.25rem;padding:10px;margin-top:10px;position:relative}.section-title.svelte-1ewzew3{display:inline-block;left:5px;top:-1.5rem;position:absolute;font-size:0.75rem;font-weight:700;text-transform:uppercase;padding-left:0.75rem;padding-right:0.75rem;--tw-bg-opacity:1;background-color:var(--background-primary)}.servings.svelte-1ewzew3{width:fit-content}ul.ingredients.svelte-1ewzew3{column-count:2}ul.cookware.svelte-1ewzew3{column-count:2}span.cookware.svelte-1ewzew3{font-weight:600}span.ingredient.svelte-1ewzew3{font-weight:600}span.timer.svelte-1ewzew3{font-weight:600}.theme-light .tooltip{--tooltip-background-color:rgb(235, 232, 233);--tooltip-color:black;--tooltip-box-shadow:0 1px 8px rgb(125, 123, 123)}
|
||||
scale(1, 1)}.image-main.svelte-13gknjv{width:100%}section.svelte-13gknjv{border:1px solid #ccc;border-radius:0.25rem;padding:10px;margin-top:10px;position:relative}.section-title.svelte-13gknjv{display:inline-block;left:5px;top:-1.5rem;position:absolute;font-size:0.75rem;font-weight:700;text-transform:uppercase;padding-left:0.75rem;padding-right:0.75rem;--tw-bg-opacity:1;background-color:var(--background-primary)}.servings.svelte-13gknjv{width:fit-content}ul.ingredients.svelte-13gknjv{column-count:2}ul.cookware.svelte-13gknjv{column-count:2}span.cookware.svelte-13gknjv{font-weight:600}span.ingredient.svelte-13gknjv{font-weight:600}span.timer.svelte-13gknjv{font-weight:600}.theme-light .tooltip{--tooltip-background-color:rgb(235, 232, 233);--tooltip-color:black;--tooltip-box-shadow:0 1px 8px rgb(125, 123, 123)}
|
||||
Loading…
Reference in a new issue