mirror of
https://github.com/mealie-recipes/mealie.git
synced 2025-08-03 04:25:24 +02:00
feat: Migrate to Nuxt 3 framework (#5184)
Co-authored-by: Michael Genson <71845777+michael-genson@users.noreply.github.com> Co-authored-by: Kuchenpirat <24235032+Kuchenpirat@users.noreply.github.com>
This commit is contained in:
parent
89ab7fac25
commit
c24d532608
403 changed files with 23959 additions and 19557 deletions
|
@ -14,13 +14,16 @@ function frac(x: number, D: number, mixed: boolean) {
|
|||
d1 += d2;
|
||||
n1 += n2;
|
||||
d2 = D + 1;
|
||||
} else if (d1 > d2) d2 = D + 1;
|
||||
}
|
||||
else if (d1 > d2) d2 = D + 1;
|
||||
else d1 = D + 1;
|
||||
break;
|
||||
} else if (x < m) {
|
||||
}
|
||||
else if (x < m) {
|
||||
n2 = n1 + n2;
|
||||
d2 = d1 + d2;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
n1 = n1 + n2;
|
||||
d1 = d1 + d2;
|
||||
}
|
||||
|
@ -58,7 +61,8 @@ function cont(x: number, D: number, mixed: boolean) {
|
|||
if (Q_1 > D) {
|
||||
Q = Q_2;
|
||||
P = P_2;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Q = Q_1;
|
||||
P = P_1;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, test, expect } from "vitest";
|
||||
import { parseIngredientText } from "./use-recipe-ingredients";
|
||||
import { RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
import type { RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
|
||||
describe(parseIngredientText.name, () => {
|
||||
const createRecipeIngredient = (overrides: Partial<RecipeIngredient>): RecipeIngredient => ({
|
||||
|
@ -59,7 +59,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 2,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: true },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("2 tbsps diced onions");
|
||||
|
@ -69,7 +69,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 2,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("2 tablespoons diced onions");
|
||||
|
@ -79,7 +79,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 1,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: true },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("1 tbsp diced onion");
|
||||
|
@ -89,7 +89,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 1,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("1 tablespoon diced onion");
|
||||
|
@ -99,7 +99,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 0.5,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: true },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("0.5 tbsp diced onion");
|
||||
|
@ -109,7 +109,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 0.5,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("0.5 tablespoon diced onion");
|
||||
|
@ -119,7 +119,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 0,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false)).toEqual("diced onions");
|
||||
|
@ -129,7 +129,7 @@ describe(parseIngredientText.name, () => {
|
|||
const ingredient = createRecipeIngredient({
|
||||
quantity: 1,
|
||||
unit: { id: "1", name: "tablespoon", pluralName: "tablespoons", abbreviation: "tbsp", pluralAbbreviation: "tbsps", useAbbreviation: false },
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" }
|
||||
food: { id: "1", name: "diced onion", pluralName: "diced onions" },
|
||||
});
|
||||
|
||||
expect(parseIngredientText(ingredient, false, 2)).toEqual("2 tablespoons diced onions");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import DOMPurify from "isomorphic-dompurify";
|
||||
import { useFraction } from "./use-fraction";
|
||||
import { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit, RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
import type { CreateIngredientFood, CreateIngredientUnit, IngredientFood, IngredientUnit, RecipeIngredient } from "~/lib/api/types/recipe";
|
||||
|
||||
const { frac } = useFraction();
|
||||
|
||||
export function sanitizeIngredientHTML(rawHtml: string) {
|
||||
|
@ -47,7 +48,7 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
|||
|
||||
const { quantity, food, unit, note } = ingredient;
|
||||
const usePluralUnit = quantity !== undefined && ((quantity || 0) * scale > 1 || (quantity || 0) * scale === 0);
|
||||
const usePluralFood = (!quantity) || quantity * scale > 1
|
||||
const usePluralFood = (!quantity) || quantity * scale > 1;
|
||||
|
||||
let returnQty = "";
|
||||
|
||||
|
@ -55,16 +56,17 @@ export function useParsedIngredientText(ingredient: RecipeIngredient, disableAmo
|
|||
if (quantity && Number(quantity) !== 0) {
|
||||
if (unit && !unit.fraction) {
|
||||
returnQty = Number((quantity * scale).toPrecision(3)).toString();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
const fraction = frac(quantity * scale, 10, true);
|
||||
if (fraction[0] !== undefined && fraction[0] > 0) {
|
||||
returnQty += fraction[0];
|
||||
}
|
||||
|
||||
if (fraction[1] > 0) {
|
||||
returnQty += includeFormating ?
|
||||
`<sup>${fraction[1]}</sup><span>⁄</span><sub>${fraction[2]}</sub>` :
|
||||
` ${fraction[1]}/${fraction[2]}`;
|
||||
returnQty += includeFormating
|
||||
? `<sup>${fraction[1]}</sup><span>⁄</span><sub>${fraction[2]}</sub>`
|
||||
: ` ${fraction[1]}/${fraction[2]}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
import { useContext } from "@nuxtjs/composition-api";
|
||||
|
||||
|
||||
export interface NutritionLabelType {
|
||||
[key: string]: {
|
||||
label: string;
|
||||
|
@ -9,55 +6,54 @@ export interface NutritionLabelType {
|
|||
};
|
||||
};
|
||||
|
||||
|
||||
export function useNutritionLabels() {
|
||||
const { i18n } = useContext();
|
||||
const i18n = useI18n();
|
||||
const labels = <NutritionLabelType>{
|
||||
calories: {
|
||||
label: i18n.tc("recipe.calories"),
|
||||
suffix: i18n.tc("recipe.calories-suffix"),
|
||||
label: i18n.t("recipe.calories"),
|
||||
suffix: i18n.t("recipe.calories-suffix"),
|
||||
},
|
||||
carbohydrateContent: {
|
||||
label: i18n.tc("recipe.carbohydrate-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.carbohydrate-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
cholesterolContent: {
|
||||
label: i18n.tc("recipe.cholesterol-content"),
|
||||
suffix: i18n.tc("recipe.milligrams"),
|
||||
label: i18n.t("recipe.cholesterol-content"),
|
||||
suffix: i18n.t("recipe.milligrams"),
|
||||
},
|
||||
fatContent: {
|
||||
label: i18n.tc("recipe.fat-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.fat-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
fiberContent: {
|
||||
label: i18n.tc("recipe.fiber-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.fiber-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
proteinContent: {
|
||||
label: i18n.tc("recipe.protein-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.protein-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
saturatedFatContent: {
|
||||
label: i18n.tc("recipe.saturated-fat-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.saturated-fat-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
sodiumContent: {
|
||||
label: i18n.tc("recipe.sodium-content"),
|
||||
suffix: i18n.tc("recipe.milligrams"),
|
||||
label: i18n.t("recipe.sodium-content"),
|
||||
suffix: i18n.t("recipe.milligrams"),
|
||||
},
|
||||
sugarContent: {
|
||||
label: i18n.tc("recipe.sugar-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.sugar-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
transFatContent: {
|
||||
label: i18n.tc("recipe.trans-fat-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.trans-fat-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
unsaturatedFatContent: {
|
||||
label: i18n.tc("recipe.unsaturated-fat-content"),
|
||||
suffix: i18n.tc("recipe.grams"),
|
||||
label: i18n.t("recipe.unsaturated-fat-content"),
|
||||
suffix: i18n.t("recipe.grams"),
|
||||
},
|
||||
};
|
||||
|
||||
return { labels }
|
||||
return { labels };
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { describe, test, expect } from "vitest";
|
||||
import { ref, Ref } from "@nuxtjs/composition-api";
|
||||
import { ref } from "vue";
|
||||
import { useRecipePermissions } from "./use-recipe-permissions";
|
||||
import { HouseholdSummary } from "~/lib/api/types/household";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { UserOut } from "~/lib/api/types/user";
|
||||
import type { HouseholdSummary } from "~/lib/api/types/household";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
import type { UserOut } from "~/lib/api/types/user";
|
||||
|
||||
describe("test use recipe permissions", () => {
|
||||
const commonUserId = "my-user-id";
|
||||
|
@ -67,7 +67,7 @@ describe("test use recipe permissions", () => {
|
|||
createUser({ id: "other-user-id" }),
|
||||
);
|
||||
expect(result.canEditRecipe.value).toBe(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
|
@ -79,14 +79,14 @@ describe("test use recipe permissions", () => {
|
|||
createUser({ id: "other-user-id" }),
|
||||
);
|
||||
expect(result.canEditRecipe.value).toBe(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
test("when user is not recipe owner, and user is other group, cannot edit", () => {
|
||||
const result = useRecipePermissions(
|
||||
createRecipe({}),
|
||||
createRecipeHousehold({}),
|
||||
createUser({ id: "other-user-id", groupId: "other-group-id"}),
|
||||
createUser({ id: "other-user-id", groupId: "other-group-id" }),
|
||||
);
|
||||
expect(result.canEditRecipe.value).toBe(false);
|
||||
});
|
||||
|
@ -113,7 +113,7 @@ describe("test use recipe permissions", () => {
|
|||
const result = useRecipePermissions(
|
||||
createRecipe({}, true),
|
||||
createRecipeHousehold({}),
|
||||
createUser({ id: "other-user-id"}),
|
||||
createUser({ id: "other-user-id" }),
|
||||
);
|
||||
expect(result.canEditRecipe.value).toBe(false);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { computed, Ref } from "@nuxtjs/composition-api";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import { HouseholdSummary } from "~/lib/api/types/household";
|
||||
import { UserOut } from "~/lib/api/types/user";
|
||||
import { computed } from "vue";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
import type { HouseholdSummary } from "~/lib/api/types/household";
|
||||
import type { UserOut } from "~/lib/api/types/user";
|
||||
|
||||
export function useRecipePermissions(
|
||||
recipe: Recipe,
|
||||
|
@ -40,5 +40,5 @@ export function useRecipePermissions(
|
|||
|
||||
return {
|
||||
canEditRecipe,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Ref, ref } from "@nuxtjs/composition-api";
|
||||
import { watchDebounced } from "@vueuse/core";
|
||||
import { UserApi } from "~/lib/api";
|
||||
import { ExploreApi } from "~/lib/api/public/explore";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import type { UserApi } from "~/lib/api";
|
||||
import type { ExploreApi } from "~/lib/api/public/explore";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
export interface UseRecipeSearchReturn {
|
||||
query: Ref<string>;
|
||||
|
@ -54,7 +53,7 @@ export function useRecipeSearch(api: UserApi | ExploreApi): UseRecipeSearchRetur
|
|||
async (term: string) => {
|
||||
await searchRecipes(term);
|
||||
},
|
||||
{ debounce: 500 }
|
||||
{ debounce: 500 },
|
||||
);
|
||||
|
||||
async function trigger() {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { computed, useContext } from "@nuxtjs/composition-api";
|
||||
import { TimelineEventType } from "~/lib/api/types/recipe";
|
||||
import type { TimelineEventType } from "~/lib/api/types/recipe";
|
||||
|
||||
export interface TimelineEventTypeData {
|
||||
value: TimelineEventType;
|
||||
|
@ -8,22 +7,23 @@ export interface TimelineEventTypeData {
|
|||
}
|
||||
|
||||
export const useTimelineEventTypes = () => {
|
||||
const { $globals, i18n } = useContext();
|
||||
const i18n = useI18n();
|
||||
const { $globals } = useNuxtApp();
|
||||
const eventTypeOptions = computed<TimelineEventTypeData[]>(() => {
|
||||
return [
|
||||
{
|
||||
value: "comment",
|
||||
label: i18n.tc("recipe.comment"),
|
||||
label: i18n.t("recipe.comment"),
|
||||
icon: $globals.icons.commentTextMultiple,
|
||||
},
|
||||
{
|
||||
value: "info",
|
||||
label: i18n.tc("settings.theme.info"),
|
||||
label: i18n.t("settings.theme.info"),
|
||||
icon: $globals.icons.informationVariant,
|
||||
},
|
||||
{
|
||||
value: "system",
|
||||
label: i18n.tc("general.system"),
|
||||
label: i18n.t("general.system"),
|
||||
icon: $globals.icons.cog,
|
||||
},
|
||||
];
|
||||
|
@ -31,5 +31,5 @@ export const useTimelineEventTypes = () => {
|
|||
|
||||
return {
|
||||
eventTypeOptions,
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { reactive, ref, useAsync } from "@nuxtjs/composition-api";
|
||||
import { useAsyncKey } from "../use-utils";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { VForm } from "~/types/vuetify";
|
||||
import { RecipeTool } from "~/lib/api/types/recipe";
|
||||
import type { VForm } from "~/types/vuetify";
|
||||
import type { RecipeTool } from "~/lib/api/types/recipe";
|
||||
|
||||
export const useTools = function (eager = true) {
|
||||
const workingToolData = reactive<RecipeTool>({
|
||||
|
@ -18,15 +17,16 @@ export const useTools = function (eager = true) {
|
|||
const actions = {
|
||||
getAll() {
|
||||
loading.value = true;
|
||||
const units = useAsync(async () => {
|
||||
const units = useAsyncData(useAsyncKey(), async () => {
|
||||
const { data } = await api.tools.getAll();
|
||||
|
||||
if (data) {
|
||||
return data.items;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}, useAsyncKey());
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
return units;
|
||||
|
@ -86,7 +86,8 @@ export const useTools = function (eager = true) {
|
|||
const tools = (() => {
|
||||
if (eager) {
|
||||
return actions.getAll();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return ref([]);
|
||||
}
|
||||
})();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { ref, onMounted } from "@nuxtjs/composition-api";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { Recipe } from "~/lib/api/types/recipe";
|
||||
import type { Recipe } from "~/lib/api/types/recipe";
|
||||
|
||||
export const useRecipe = function (slug: string, eager = true) {
|
||||
const api = useUserApi();
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useAsync, useRouter, ref } from "@nuxtjs/composition-api";
|
||||
import { ref } from "vue";
|
||||
import { useAsyncKey } from "../use-utils";
|
||||
import { usePublicExploreApi } from "~/composables/api/api-client";
|
||||
import { useUserApi } from "~/composables/api";
|
||||
import { OrderByNullPosition, Recipe } from "~/lib/api/types/recipe";
|
||||
import { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
||||
import type { OrderByNullPosition, Recipe } from "~/lib/api/types/recipe";
|
||||
import type { RecipeSearchQuery } from "~/lib/api/user/recipes/recipe";
|
||||
|
||||
export const allRecipes = ref<Recipe[]>([]);
|
||||
export const recentRecipes = ref<Recipe[]>([]);
|
||||
|
@ -13,7 +13,7 @@ function getParams(
|
|||
orderDirection = "desc",
|
||||
orderByNullPosition: OrderByNullPosition | null = null,
|
||||
query: RecipeSearchQuery | null = null,
|
||||
queryFilter: string | null = null
|
||||
queryFilter: string | null = null,
|
||||
) {
|
||||
return {
|
||||
orderBy,
|
||||
|
@ -53,7 +53,6 @@ export const useLazyRecipes = function (publicGroupSlug: string | null = null) {
|
|||
query: RecipeSearchQuery | null = null,
|
||||
queryFilter: string | null = null,
|
||||
) {
|
||||
|
||||
const { data, error } = await api.recipes.getAll(
|
||||
page,
|
||||
perPage,
|
||||
|
@ -113,7 +112,7 @@ export const useRecipes = (
|
|||
fetchRecipes = true,
|
||||
loadFood = false,
|
||||
queryFilter: string | null = null,
|
||||
publicGroupSlug: string | null = null
|
||||
publicGroupSlug: string | null = null,
|
||||
) => {
|
||||
const api = publicGroupSlug ? usePublicExploreApi(publicGroupSlug).explore : useUserApi();
|
||||
|
||||
|
@ -125,7 +124,8 @@ export const useRecipes = (
|
|||
page: 1,
|
||||
perPage: -1,
|
||||
};
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return {
|
||||
recipes: recentRecipes,
|
||||
page: 1,
|
||||
|
@ -142,9 +142,9 @@ export const useRecipes = (
|
|||
}
|
||||
|
||||
function getAllRecipes() {
|
||||
useAsync(async () => {
|
||||
useAsyncData(useAsyncKey(), async () => {
|
||||
await refreshRecipes();
|
||||
}, useAsyncKey());
|
||||
});
|
||||
}
|
||||
|
||||
function assignSorted(val: Array<Recipe>) {
|
||||
|
|
|
@ -11,11 +11,11 @@ function formatQuantity(val: number): string {
|
|||
const fraction = frac(val, 10, true);
|
||||
|
||||
if (fraction[0] !== undefined && fraction[0] > 0) {
|
||||
valString += fraction[0];
|
||||
valString += fraction[0];
|
||||
}
|
||||
|
||||
if (fraction[1] > 0) {
|
||||
valString += `<sup>${fraction[1]}</sup><span>⁄</span><sub>${fraction[2]}</sub>`;
|
||||
valString += `<sup>${fraction[1]}</sup><span>⁄</span><sub>${fraction[2]}</sub>`;
|
||||
}
|
||||
|
||||
return valString.trim();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue