diff --git a/inlang/source-code/bundle-component/package.json b/inlang/source-code/bundle-component/package.json index e39439b889..ad4919c545 100644 --- a/inlang/source-code/bundle-component/package.json +++ b/inlang/source-code/bundle-component/package.json @@ -42,6 +42,7 @@ "@lit/task": "^1.0.0", "@shoelace-style/shoelace": "2.14.0", "@sinclair/typebox": "0.31.28", + "@storybook/preview-api": "^8.2.9", "chroma-js": "^2.4.2", "lexical": "^0.16.1", "typescript": "^5.5.4" diff --git a/inlang/source-code/bundle-component/src/helper/crud/input/create.test.ts b/inlang/source-code/bundle-component/src/helper/crud/input/create.test.ts deleted file mode 100644 index 23337afc21..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/input/create.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, expect, it } from "vitest" -import createInput from "./create.js" -import { createBundle } from "@inlang/sdk2" - -describe("createInput", () => { - it("Should create an input declarations", () => { - const messageBundle = createBundle({ - id: "bundleId", - messages: [ - { - bundleId: "bundleId", - id: "testId1", - locale: "en", - selectors: [], - declarations: [ - { - type: "input", - name: "count", - value: { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - }, - ], - variants: [], - }, - { - bundleId: "bundleId", - id: "testId2", - locale: "de", - selectors: [], - declarations: [ - { - type: "input", - name: "count", - value: { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - }, - ], - variants: [], - }, - ], - }) - - createInput({ messageBundle, inputName: "user_name" }) - - expect(messageBundle.messages[0]!.declarations.length).toBe(2) - expect(messageBundle.messages[0]!.declarations[1]!.name).toBe("user_name") - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/input/create.ts b/inlang/source-code/bundle-component/src/helper/crud/input/create.ts deleted file mode 100644 index efa1114922..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/input/create.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { Declaration, BundleNested } from "@inlang/sdk2" - -/** - * Creates an input in all messages of a message bundle. - * The function mutates the message bundle. - * @param props.messageBundle The message bundle to create the input in. - * @param props.inputName The name of the input to create. - * - * @example - * createInput({ messageBundle, inputName: "myInput" }) - */ - -const createInput = (props: { messageBundle: BundleNested; inputName: string }) => { - for (const message of props.messageBundle.messages) { - if ( - message.declarations.some((declaration: Declaration) => declaration.name === props.inputName) - ) { - console.error("Input with name already exists") - return - } else { - message.declarations.push({ - type: "input", - name: props.inputName, - value: { - type: "expression", - arg: { - type: "variable", - name: props.inputName, - }, - }, - }) - } - } -} - -export default createInput diff --git a/inlang/source-code/bundle-component/src/helper/crud/input/delete.test.ts b/inlang/source-code/bundle-component/src/helper/crud/input/delete.test.ts deleted file mode 100644 index 7bad4ca22d..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/input/delete.test.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { describe, expect, it } from "vitest" -import deleteInput from "./delete.js" -import { createBundle } from "@inlang/sdk2" - -describe("deleteInput", () => { - it("Should delete a specific input declarations", () => { - const messageBundle = createBundle({ - id: "bundleId", - messages: [ - { - bundleId: "bundleId", - id: "testId", - locale: "en", - selectors: [], - declarations: [ - { - type: "input", - name: "count", - value: { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - }, - { - type: "input", - name: "user_name", - value: { - type: "expression", - arg: { - type: "variable", - name: "user_name", - }, - }, - }, - ], - variants: [], - }, - { - bundleId: "bundleId", - id: "testId", - locale: "en", - selectors: [], - declarations: [ - { - type: "input", - name: "count", - value: { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - }, - { - type: "input", - name: "user_name", - value: { - type: "expression", - arg: { - type: "variable", - name: "user_name", - }, - }, - }, - ], - variants: [], - }, - ], - }) - const inputs = deleteInput({ - messageBundle, - input: messageBundle.messages[0]!.declarations[0]!, - }) - expect(messageBundle.messages[0]!.declarations.length).toBe(1) - expect(messageBundle.messages[1]!.declarations.length).toBe(1) - expect(messageBundle.messages[0]!.declarations[0]!.name).toBe("user_name") - expect(messageBundle.messages[1]!.declarations[0]!.name).toBe("user_name") - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/input/delete.ts b/inlang/source-code/bundle-component/src/helper/crud/input/delete.ts deleted file mode 100644 index d58adf5bc1..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/input/delete.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Declaration, BundleNested } from "@inlang/sdk2" - -/** - * Deletes an input from all messages of a message bundle. - * The function mutates the message bundle. - * @param props.messageBundle The message bundle to delete the input from. - * @param props.input The input to delete. - */ - -const deleteInput = (props: { messageBundle: BundleNested; input: Declaration }) => { - for (const message of props.messageBundle.messages) { - const index = message.declarations.findIndex((d: any) => d.name === props.input.name) - if (index === -1) { - continue - } else { - message.declarations.splice(index, 1) - } - } -} - -export default deleteInput diff --git a/inlang/source-code/bundle-component/src/helper/crud/input/get.test.ts b/inlang/source-code/bundle-component/src/helper/crud/input/get.test.ts deleted file mode 100644 index 0789df73a0..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/input/get.test.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { describe, expect, it } from "vitest" -import getInput from "./get.js" -import { createBundle } from "@inlang/sdk2" - -describe("getInput", () => { - it("Should return all found input declarations", () => { - const messageBundle = createBundle({ - id: "bundleId", - messages: [ - { - bundleId: "bundleId", - id: "testId", - locale: "en", - selectors: [], - declarations: [ - { - type: "input", - name: "count", - value: { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - }, - ], - variants: [], - }, - { - bundleId: "bundleId", - id: "testId", - locale: "en", - selectors: [], - declarations: [ - { - type: "input", - name: "count", - value: { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - }, - ], - variants: [], - }, - { - bundleId: "bundleId", - id: "testId", - locale: "en", - selectors: [], - declarations: [], - variants: [], - }, - ], - }) - const inputs = getInput({ messageBundle }) - expect(inputs.length).toBe(1) - expect(inputs[0]!.name).toBe("count") - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/input/get.ts b/inlang/source-code/bundle-component/src/helper/crud/input/get.ts deleted file mode 100644 index 792d7c8521..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/input/get.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Declaration, BundleNested } from "@inlang/sdk2" - -/** - * Gets all inputs from a message bundle. - * @param props.messageBundle The message bundle to get the inputs from. - * @returns All inputs from the message bundle. - */ - -const getInputs = (props: { messageBundle: BundleNested }): Declaration[] => { - const inputs: Declaration[] = [] - for (const message of props.messageBundle.messages) { - for (const declaration of message.declarations) { - if (declaration.type === "input" && !inputs.some((d) => d.name === declaration.name)) { - inputs.push(declaration) - } - } - } - return inputs -} - -export default getInputs diff --git a/inlang/source-code/bundle-component/src/helper/crud/selector/add.test.ts b/inlang/source-code/bundle-component/src/helper/crud/selector/add.test.ts deleted file mode 100644 index f0534e0114..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/selector/add.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { type Expression, type MessageNested } from "@inlang/sdk2" -import { describe, expect, it } from "vitest" -import addSelector from "./add.js" - -describe("addSelector", () => { - it("Should add selector", () => { - const message: MessageNested = { - bundleId: "testId", - id: "testId", - locale: "en", - selectors: [ - { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - ], - declarations: [], - variants: [], - } - - const newSelector = { - type: "expression", - arg: { - type: "variable", - name: "name", - }, - } - - addSelector({ message, selector: newSelector as Expression }) - - expect(message.selectors.length).toBe(2) - // @ts-ignore - expect(message.selectors[1]!.arg.name).toBe("name") - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/selector/add.ts b/inlang/source-code/bundle-component/src/helper/crud/selector/add.ts deleted file mode 100644 index 4cf1efe66d..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/selector/add.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { createVariant, type Expression, type MessageNested } from "@inlang/sdk2" - -/** - * Adds a selector to a message. - * The function mutates the message. - * @param props.message The message to add the selector to. - * @param props.selector The selector to add. - * - * @example - * addSelector({ message, selector: { type: "expression", arg: { type: "variable", name: "mySelector" } } }) - */ - -const addSelector = (props: { message: MessageNested; selector: Expression }) => { - props.message.selectors.push(props.selector) - if (props.message.variants.length !== 0) { - for (const variant of props.message.variants) { - variant.match[props.selector.arg.name] = "*" - } - } else { - props.message.variants.push( - createVariant({ - messageId: props.message.id, - match: { [props.selector.arg.name]: "*" }, - text: "", - }) - ) - } -} - -export default addSelector diff --git a/inlang/source-code/bundle-component/src/helper/crud/selector/delete.test.ts b/inlang/source-code/bundle-component/src/helper/crud/selector/delete.test.ts deleted file mode 100644 index c74dae3073..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/selector/delete.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { type MessageNested } from "@inlang/sdk2" -import { describe, expect, it } from "vitest" -import deleteSelector from "./delete.js" - -describe("deleteSelector", () => { - it("Should delete selector", () => { - const message: MessageNested = { - bundleId: "bundleTestId", - id: "testId", - locale: "en", - selectors: [ - { - type: "expression", - arg: { - type: "variable", - name: "count", - }, - }, - { - type: "expression", - arg: { - type: "variable", - name: "name", - }, - }, - ], - declarations: [], - variants: [ - { - id: "variantId", - messageId: "testId", - match: { - count: "1", - name: "John", - }, - pattern: [ - { - type: "text", - value: "Hello ", - }, - ], - }, - ], - } - - deleteSelector({ message, index: 0 }) - - expect(message.selectors.length).toBe(1) - // @ts-ignore - expect(message.selectors[0]!.arg.name).toBe("name") - expect(Object.keys(message.variants[0]!.match).length).toBe(1) - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/selector/delete.ts b/inlang/source-code/bundle-component/src/helper/crud/selector/delete.ts deleted file mode 100644 index 1789e94b48..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/selector/delete.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { MessageNested } from "@inlang/sdk2" - -/** - * Deletes a selector from a message. - * The function mutates the message. - * @param props.message The message to delete the selector from. - * @param props.index The index of the selector to delete. - * - * @example - * deleteSelector({ message, index: 0 }) - */ - -const deleteSelector = (props: { message: MessageNested; index: number }) => { - const selectorName = props.message.selectors[props.index]!.arg.name - if (selectorName) { - props.message.selectors.splice(props.index, 1) - for (const variant of props.message.variants) { - for (const name in variant.match) { - if (name === selectorName) { - delete variant.match[name] - } - } - } - } else { - console.error("Index is not pointing on a selector") - } -} - -export default deleteSelector diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/delete.test.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/delete.test.ts deleted file mode 100644 index 0e6e9a2bcf..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/delete.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { type MessageNested, createVariant } from "@inlang/sdk2" -import { describe, expect, it } from "vitest" -import deleteVariant from "./delete.js" - -describe("deleteVariant", () => { - it("Should delete variant", () => { - const messageId = "testId" - const message: MessageNested = { - bundleId: "testBundleId", - id: messageId, - locale: "en", - selectors: [], - declarations: [], - variants: [ - createVariant({ messageId, id: "a", match: { count: "*" } }), - createVariant({ messageId, id: "b", match: { count: "one" } }), - ], - } - - deleteVariant({ message, variant: message.variants[1]! }) - - expect(message.variants.length).toBe(1) - expect(message.variants[0]!.id).toBe("a") - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/delete.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/delete.ts deleted file mode 100644 index 5a5019824e..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/delete.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { MessageNested, Variant } from "@inlang/sdk2" - -/** - * Deletes a variant from a message. - * The function mutates the message. - * @param props.message The message to delete the variant from. - * @param props.variant The variant to delete. - */ - -const deleteVariant = (props: { message: MessageNested; variant: Variant }) => { - // index from array where the variant is located - const index = props.message.variants.findIndex((variant) => variant.id === props.variant.id) - - if (index > -1) { - // Delete existing variant - props.message.variants.splice(index, 1) - } -} - -export default deleteVariant diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/isCatchAll.test.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/isCatchAll.test.ts deleted file mode 100644 index 3baf927386..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/isCatchAll.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { createVariant } from "@inlang/sdk2" -import { describe, expect, it } from "vitest" -import variantIsCatchAll from "./isCatchAll.js" - -describe("isCatchAll", () => { - it("Should return true if variant is catchAll", () => { - expect( - variantIsCatchAll({ variant: createVariant({ messageId: "testId", match: { count: "*" } }) }) - ).toBe(true) - expect( - variantIsCatchAll({ - variant: createVariant({ messageId: "testId", match: { count: "*", count2: "*" } }), - }) - ).toBe(true) - }) - it("Should return false if variant is not catchAll", () => { - expect( - variantIsCatchAll({ - variant: createVariant({ messageId: "testId", match: { count: "one" } }), - }) - ).toBe(false) - expect( - variantIsCatchAll({ - variant: createVariant({ messageId: "testId", match: { count: "*", count2: "one" } }), - }) - ).toBe(false) - expect( - variantIsCatchAll({ - variant: createVariant({ messageId: "testId", match: { count: "one", count2: "*" } }), - }) - ).toBe(false) - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/isCatchAll.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/isCatchAll.ts deleted file mode 100644 index 7e322dc282..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/isCatchAll.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Variant } from "@inlang/sdk2" - -/** - * Returns true if the variant has a catch-all match. - * @param props.variant The variant to check. - * @returns True if the variant has a catch-all match. - */ -const variantIsCatchAll = (props: { variant: Variant }): boolean => { - if (Object.values(props.variant.match).filter((match) => match !== "*").length === 0) { - return true - } else { - return false - } -} - -export default variantIsCatchAll diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/sortAll.test.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/sortAll.test.ts deleted file mode 100644 index e8b8dce8c0..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/sortAll.test.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { describe, expect, it } from "vitest" -import sortAllVariants from "./sortAll.js" -import { createVariant, type Expression, type Variant } from "@inlang/sdk2" - -describe("sortAllVariants", () => { - it("should sort variants based on matches (desc-alphabetically)", () => { - const variants: Variant[] = [ - createVariant({ messageId: "testId", id: "1", match: { letter: "a" } }), - createVariant({ messageId: "testId", id: "2", match: { letter: "b" } }), - createVariant({ messageId: "testId", id: "3", match: { letter: "c" } }), - ] - const ignoreVariantIds: string[] = [] - const selectors: Expression[] = [ - { - type: "expression", - arg: { - type: "variable", - name: "letter", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - ] - - const sortedVariants = sortAllVariants({ variants, ignoreVariantIds, selectors }) - - expect(sortedVariants).toEqual([ - createVariant({ messageId: "testId", id: "3", match: { letter: "c" } }), - createVariant({ messageId: "testId", id: "2", match: { letter: "b" } }), - createVariant({ messageId: "testId", id: "1", match: { letter: "a" } }), - ]) - }) - - it("should sort variants based on matches (desc-alphabetically) complex", () => { - const variants: Variant[] = [ - createVariant({ - messageId: "testId", - id: "1", - match: { letter1: "a", letter2: "b", letter3: "c" }, - }), - createVariant({ - messageId: "testId", - id: "2", - match: { letter1: "b", letter2: "b", letter3: "b" }, - }), - createVariant({ - messageId: "testId", - id: "3", - match: { letter1: "b", letter2: "a", letter3: "a" }, - }), - ] - const ignoreVariantIds: string[] = [] - const selectors: Expression[] = [ - { - type: "expression", - arg: { - type: "variable", - name: "letter1", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - { - type: "expression", - arg: { - type: "variable", - name: "letter2", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - { - type: "expression", - arg: { - type: "variable", - name: "letter3", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - ] - - const sortedVariants = sortAllVariants({ variants, ignoreVariantIds, selectors }) - - expect(sortedVariants).toEqual([ - createVariant({ - messageId: "testId", - id: "2", - match: { letter1: "b", letter2: "b", letter3: "b" }, - }), - createVariant({ - messageId: "testId", - id: "3", - match: { letter1: "b", letter2: "a", letter3: "a" }, - }), - createVariant({ - messageId: "testId", - id: "1", - match: { letter1: "a", letter2: "b", letter3: "c" }, - }), - ]) - }) - - it("should sort variants based on matches (desc-alphabetically) with ignoreVariantIds", () => { - const variants: Variant[] = [ - createVariant({ - messageId: "testId", - id: "1", - match: { letter1: "a", letter2: "b", letter3: "c" }, - }), - createVariant({ - messageId: "testId", - id: "2", - match: { letter1: "b", letter2: "b", letter3: "b" }, - }), - createVariant({ - messageId: "testId", - id: "3", - match: { letter1: "b", letter2: "a", letter3: "a" }, - }), - createVariant({ - messageId: "testId", - id: "4", - match: { letter1: "z", letter2: "z", letter3: "z" }, - }), - ] - const ignoreVariantIds: string[] = ["4"] - const selectors: Expression[] = [ - { - type: "expression", - arg: { - type: "variable", - name: "letter1", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - { - type: "expression", - arg: { - type: "variable", - name: "letter2", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - { - type: "expression", - arg: { - type: "variable", - name: "letter3", - }, - annotation: { - type: "function", - name: "string", - options: [], - }, - }, - ] - - const sortedVariants = sortAllVariants({ variants, ignoreVariantIds, selectors }) - - expect(sortedVariants).toEqual([ - createVariant({ - messageId: "testId", - id: "2", - match: { letter1: "b", letter2: "b", letter3: "b" }, - }), - createVariant({ - messageId: "testId", - id: "3", - match: { letter1: "b", letter2: "a", letter3: "a" }, - }), - createVariant({ - messageId: "testId", - id: "1", - match: { letter1: "a", letter2: "b", letter3: "c" }, - }), - createVariant({ - messageId: "testId", - id: "4", - match: { letter1: "z", letter2: "z", letter3: "z" }, - }), - ]) - }) - - it("should handle empty variants array", () => { - const variants: Variant[] = [] - const ignoreVariantIds: string[] = [] - const selectors: Expression[] = [] - - const sortedVariants = sortAllVariants({ variants, ignoreVariantIds, selectors }) - - expect(sortedVariants).toEqual([]) - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/sortAll.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/sortAll.ts deleted file mode 100644 index 8e54f9385c..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/sortAll.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { type Expression, type Variant } from "@inlang/sdk2" - -/** - * Sort all variants by their match values. - * The function does not mutate the variants array. - * @param props.variants The variants to sort. The variants will be sorted in place. - * @param props.ignoreVariantIds The ids of the variants to ignore in the sorting. These variants will be placed at the end of the list. - * @returns The sorted variants. - */ -const sortAllVariants = (props: { - variants: Variant[] - ignoreVariantIds: string[] - selectors: Expression[] -}): Variant[] => { - const sortedVariants: Variant[] = structuredClone(props.variants) - sortedVariants.sort((a, b) => { - return compareValues(a, b, 0, props.ignoreVariantIds, props.selectors) - }) - return sortedVariants -} - -const compareValues = ( - a: Variant, - b: Variant, - index: number, - ignoreVariantIds: string[], - selectors: Expression[] -): number => { - const selectorName = selectors[index]?.arg.name - if (!selectorName) return 0 - - if (a.match[selectorName] && b.match[selectorName]) { - if (ignoreVariantIds.includes(a.id)) return 1 - if (a.match[selectorName]! < b.match[selectorName]!) return 1 - if (a.match[selectorName]! > b.match[selectorName]!) return -1 - } - - if (Object.values(a.match).length === index + 1) return 0 - if (index > 10) return 0 - return compareValues(a, b, index + 1, ignoreVariantIds, selectors) -} - -export default sortAllVariants diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/updateMatch.test.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/updateMatch.test.ts deleted file mode 100644 index 8edecbd7ac..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/updateMatch.test.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { describe, expect, it } from "vitest" -import updateMatch from "./updateMatch.js" -import { createVariant, type Variant } from "@inlang/sdk2" - -describe("updateMatch", () => { - it("should update the value at the specified match index", () => { - const variant: Variant = createVariant({ - messageId: "testId", - id: "test-id", - match: { selector1: "apple", selector2: "banana", selector3: "cherry" }, - }) - const selectorName = "selector2" - const value = "orange" - - updateMatch({ variant, selectorName, value }) - - expect(variant.match[selectorName]).toBe(value) - expect(Object.keys(variant.match).length).toBe(3) - }) - - it("should not update the value if match name doesn't exist", () => { - const variant: Variant = createVariant({ - messageId: "testId", - id: "test-id", - match: { selector1: "apple", selector2: "banana", selector3: "cherry" }, - }) - const selectorName = "selector4" - const value = "orange" - - updateMatch({ variant, selectorName, value }) - - expect(variant.match[selectorName]).toBeUndefined() - expect(Object.keys(variant.match).length).toBe(3) - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/updateMatch.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/updateMatch.ts deleted file mode 100644 index ad03a04ffb..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/updateMatch.ts +++ /dev/null @@ -1,19 +0,0 @@ -import type { Variant } from "@inlang/sdk2" - -/** - * Update the match at the specified index with the new value. - * The function mutates the variant. - * @param props.variant The variant to update. - * @param props.matchIndex The index of the match to update. - * @param props.value The new value to set at the match index. - */ - -const updateMatch = (props: { variant: Variant; selectorName: string; value: string }) => { - // if matchName is not in variant, return - if (!props.variant.match[props.selectorName]) return - - // update the match with value (mutates variant) - props.variant.match[props.selectorName] = props.value -} - -export default updateMatch diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/upsert.test.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/upsert.test.ts deleted file mode 100644 index 8c9b1309c8..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/upsert.test.ts +++ /dev/null @@ -1,84 +0,0 @@ -/* eslint-disable @typescript-eslint/no-non-null-assertion */ -import { createMessage, createBundle, createVariant } from "@inlang/sdk2" -import { describe, expect, it } from "vitest" -import upsertVariant from "./upsert.js" - -describe("upsertVariant", () => { - it("Should update existing variant", () => { - const bundle = createBundle({ - id: "bundle-id", - messages: [ - { - bundleId: "bundle-id", - id: "test_message_id", - locale: "en", - declarations: [], - selectors: [], - variants: [ - createVariant({ - messageId: "testId", - id: "test_upsertVariant_id", - match: { count: "*" }, - text: "Hello World", - }), - ], - }, - ], - }) - - expect(bundle.messages).toHaveLength(1) - expect(bundle.messages[0]?.variants[0]?.pattern).toStrictEqual([ - { type: "text", value: "Hello World" }, - ]) - - upsertVariant({ - message: bundle.messages[0]!, - variant: { - messageId: bundle.messages[0]!.id, - id: "test_upsertVariant_id", - match: { count: "*" }, - pattern: [{ type: "text", value: "Hello Universe" }], - }, - }) - - expect(bundle.messages[0]?.variants).toHaveLength(1) - expect(bundle.messages[0]?.variants[0]?.pattern).toStrictEqual([ - { type: "text", value: "Hello Universe" }, - ]) - }) - - it("Should create a new variant", () => { - const bundle = createBundle({ - id: "bundle-id", - messages: [ - createMessage({ - bundleId: "bundle-id", - locale: "en", - text: "Hello World", - match: { count: "*" }, - }), - ], - }) - - expect(bundle.messages).toHaveLength(1) - expect(bundle.messages[0]?.variants[0]?.pattern).toStrictEqual([ - { type: "text", value: "Hello World" }, - ]) - - upsertVariant({ - message: bundle.messages[0]!, - variant: { - messageId: bundle.messages[0]!.id, - id: "test_upsertVariant_id", - match: { count: "one" }, - pattern: [{ type: "text", value: "Hello Universe" }], - }, - }) - - expect(bundle.messages[0]?.variants).toHaveLength(2) - // it's 0 because it's sorted alphabetically - expect(bundle.messages[0]?.variants[1]?.pattern).toStrictEqual([ - { type: "text", value: "Hello Universe" }, - ]) - }) -}) diff --git a/inlang/source-code/bundle-component/src/helper/crud/variant/upsert.ts b/inlang/source-code/bundle-component/src/helper/crud/variant/upsert.ts deleted file mode 100644 index f4ced42391..0000000000 --- a/inlang/source-code/bundle-component/src/helper/crud/variant/upsert.ts +++ /dev/null @@ -1,33 +0,0 @@ -import type { MessageNested, Variant } from "@inlang/sdk2" - -/** - * Upsert a variant into a message. If a variant with the same match already exists, it will be updated, otherwise a new variant will be added. - * The function mutates message. - * @param props.message The message to upsert the variant into. - * @param props.variant The variant to upsert. - */ - -const upsertVariant = (props: { - message: MessageNested - variant: Variant -}): Variant | undefined => { - const existingVariant = props.message.variants.find((variant) => variant.id === props.variant.id) - - if (existingVariant) { - // Update existing variant - // check if pattern is different - if (JSON.stringify(existingVariant.pattern) !== JSON.stringify(props.variant.pattern)) { - existingVariant.pattern = props.variant.pattern - return existingVariant - } else { - // pattern is the same - return - } - } else { - // Insert new variant - props.message.variants.push(props.variant) - return props.variant - } -} - -export default upsertVariant diff --git a/inlang/source-code/bundle-component/src/helper/event.ts b/inlang/source-code/bundle-component/src/helper/event.ts new file mode 100644 index 0000000000..78cbdf521e --- /dev/null +++ b/inlang/source-code/bundle-component/src/helper/event.ts @@ -0,0 +1,26 @@ +//generalized event dispatcher + +import type { Bundle, Message, Variant } from "@inlang/sdk2" + +export type DispatchChangeInterface = { + type: "Bundle" | "Message" | "Variant" + operation: "create" | "update" | "delete" + newData: Bundle | Message | Variant | undefined + meta?: Record +} + +export const createChangeEvent = (props: DispatchChangeInterface) => { + const onChangeEvent = new CustomEvent("change", { + bubbles: true, + composed: true, + detail: { + argument: { + type: props.type, + operation: props.operation, + newData: props.newData, + meta: props.meta, + }, + }, + }) + return onChangeEvent +} diff --git a/inlang/source-code/bundle-component/src/helper/crud/pattern/patternToString.test.ts b/inlang/source-code/bundle-component/src/helper/patternToString.test.ts similarity index 100% rename from inlang/source-code/bundle-component/src/helper/crud/pattern/patternToString.test.ts rename to inlang/source-code/bundle-component/src/helper/patternToString.test.ts diff --git a/inlang/source-code/bundle-component/src/helper/crud/pattern/patternToString.ts b/inlang/source-code/bundle-component/src/helper/patternToString.ts similarity index 100% rename from inlang/source-code/bundle-component/src/helper/crud/pattern/patternToString.ts rename to inlang/source-code/bundle-component/src/helper/patternToString.ts diff --git a/inlang/source-code/bundle-component/src/helper/simplifyBundle.ts b/inlang/source-code/bundle-component/src/helper/simplifyBundle.ts deleted file mode 100644 index 05d3dc0852..0000000000 --- a/inlang/source-code/bundle-component/src/helper/simplifyBundle.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { BundleNested } from "@inlang/sdk2" - -export const simplifyBundle = (bundle: BundleNested) => { - // all patterns should become a single text pattern that is randomized - for (const message of bundle.messages) { - for (const variant of message.variants) { - variant.pattern = [{ type: "text", value: createRandomString(8) }] - } - } - - return bundle -} - -const createRandomString = (length: number) => { - const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - let result = "" - for (let i = 0; i < length; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)) - } - return result -} diff --git a/inlang/source-code/bundle-component/src/helper/crud/pattern/stringToPattern.test.ts b/inlang/source-code/bundle-component/src/helper/stringToPattern.test.ts similarity index 100% rename from inlang/source-code/bundle-component/src/helper/crud/pattern/stringToPattern.test.ts rename to inlang/source-code/bundle-component/src/helper/stringToPattern.test.ts diff --git a/inlang/source-code/bundle-component/src/helper/crud/pattern/stringToPattern.ts b/inlang/source-code/bundle-component/src/helper/stringToPattern.ts similarity index 100% rename from inlang/source-code/bundle-component/src/helper/crud/pattern/stringToPattern.ts rename to inlang/source-code/bundle-component/src/helper/stringToPattern.ts diff --git a/inlang/source-code/bundle-component/src/index.ts b/inlang/source-code/bundle-component/src/index.ts index d4ae0646f3..a418a335b0 100644 --- a/inlang/source-code/bundle-component/src/index.ts +++ b/inlang/source-code/bundle-component/src/index.ts @@ -1,5 +1,9 @@ +//main components export { default as InlangBundle } from "./stories/inlang-bundle.js" -export { default as InlangBundleHeader } from "./stories/inlang-bundle-header.js" -export { default as InlangMessage } from "./stories/inlang-message.js" -export { default as InlangVariant } from "./stories/inlang-variant.js" +export { default as InlangMessage } from "./stories/message/inlang-message.js" +export { default as InlangVariant } from "./stories/variant/inlang-variant.js" export { default as InlangPatternEditor } from "./stories/pattern-editor/inlang-pattern-editor.js" + +//modals & actions +export { default as InlangBundleAction } from "./stories/actions/bundle-action/inlang-bundle-action.js" +export { default as InlangAddSelector } from "./stories/actions/add-selector/inlang-add-selector.js" diff --git a/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.mdx b/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.mdx new file mode 100644 index 0000000000..1a9ee7a0fb --- /dev/null +++ b/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.mdx @@ -0,0 +1,19 @@ +import { Canvas, Meta } from '@storybook/blocks'; + +import * as InlangAddInput from './inlang-add-input.stories.ts'; + + + +# InlangAddInput + +The component is currently only used inside of the bundle. That means that it can't be slotted. + + + +#### Props: +- `messages`: The messages as Message[] to get inputs + +#### Event: +- `change`: The component dispatches a change event that contains all necessary information. + +> The event bubbles up, so you can have a change event listener on a higher level. \ No newline at end of file diff --git a/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.stories.ts b/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.stories.ts new file mode 100644 index 0000000000..55ab7e7502 --- /dev/null +++ b/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.stories.ts @@ -0,0 +1,28 @@ +import { pluralBundle } from "@inlang/sdk2" +import "./inlang-add-input.ts" +import "./../../inlang-bundle.ts" +import type { Meta, StoryObj } from "@storybook/web-components" +import { html } from "lit" + +const meta: Meta = { + component: "inlang-add-input", + title: "Public/Actions/inlang-add-input", + argTypes: { + messages: { control: "object" }, + }, +} + +export default meta + +export const Example: StoryObj = { + render: () => { + return html` +
+ Press me +
` + }, +} diff --git a/inlang/source-code/bundle-component/src/stories/inlang-add-input.ts b/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.ts similarity index 75% rename from inlang/source-code/bundle-component/src/stories/inlang-add-input.ts rename to inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.ts index 4d75f9c960..661833461e 100644 --- a/inlang/source-code/bundle-component/src/stories/inlang-add-input.ts +++ b/inlang/source-code/bundle-component/src/stories/actions/add-input/inlang-add-input.ts @@ -1,12 +1,19 @@ import { LitElement, css, html } from "lit" import { customElement, property, state } from "lit/decorators.js" +import { createChangeEvent } from "../../../helper/event.js" +import type { Message } from "@inlang/sdk2" +import { baseStyling } from "../../../styling/base.js" import SlDropdown from "@shoelace-style/shoelace/dist/components/dropdown/dropdown.component.js" import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js" +if (!customElements.get("sl-dropdown")) customElements.define("sl-dropdown", SlDropdown) +if (!customElements.get("sl-input")) customElements.define("sl-input", SlInput) + @customElement("inlang-add-input") export default class InlangAddInput extends LitElement { static override styles = [ + baseStyling, css` .button-wrapper { height: 44px; @@ -62,9 +69,8 @@ export default class InlangAddInput extends LitElement { `, ] - //props - @property() - addInput: (inputName: string) => void = () => {} + @property({ type: Array }) + messages: Message[] | undefined //state @state() @@ -82,13 +88,11 @@ export default class InlangAddInput extends LitElement { class="dropdown" @sl-show=${(e: CustomEvent) => { const dropdown = this.shadowRoot?.querySelector("sl-dropdown") - if (dropdown) { - if (e.target === dropdown) { - const input: SlInput | undefined | null = this.shadowRoot?.querySelector("sl-input") - setTimeout(() => { - if (input) input.focus() - }) - } + if (dropdown && e.target === dropdown) { + const input: SlInput | undefined | null = this.shadowRoot?.querySelector("sl-input") + setTimeout(() => { + if (input) input.focus() + }) } }} > @@ -110,9 +114,33 @@ export default class InlangAddInput extends LitElement { @keydown=${(e: KeyboardEvent) => { if (e.key === "Enter") { if (this._newInput && this._newInput.trim() !== "") { - this.addInput(this._newInput) + for (const message of this.messages ?? []) { + const newMessage = structuredClone(message) + + newMessage.declarations.push({ + type: "input", + name: this._newInput!, + value: { + type: "expression", + arg: { + type: "variable", + name: this._newInput!, + }, + }, + }) + + this.dispatchEvent( + createChangeEvent({ + type: "Message", + operation: "update", + newData: newMessage, + }) + ) + } } + this._newInput = "" + const dropdown = this.shadowRoot?.querySelector(".dropdown") as SlDropdown dropdown.hide() } diff --git a/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.mdx b/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.mdx new file mode 100644 index 0000000000..28fd8d93c5 --- /dev/null +++ b/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.mdx @@ -0,0 +1,20 @@ +import { Canvas, Meta } from '@storybook/blocks'; + +import * as InlangAddSelector from './inlang-add-selector.stories.ts'; + + + +# InlangAddSelector + +The component is currently only used inside of the bundle. That means that it can't be slotted. + + + +#### Props: +- `message`: The message as Message +- `messages`: The messages as Message[] to get inputs + +#### Event: +- `change`: The component dispatches a change event that contains all necessary information. + +> The event bubbles up, so you can have a change event listener on a higher level. \ No newline at end of file diff --git a/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.stories.ts b/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.stories.ts new file mode 100644 index 0000000000..729ae1cbf1 --- /dev/null +++ b/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.stories.ts @@ -0,0 +1,31 @@ +import { pluralBundle } from "@inlang/sdk2" +import "./inlang-add-selector.ts" +import type { Meta, StoryObj } from "@storybook/web-components" +import { html } from "lit" + +const meta: Meta = { + component: "inlang-add-selector", + title: "Public/Actions/inlang-add-selector", + argTypes: { + message: { control: "object" }, + messages: { control: "array" }, + }, +} + +export default meta + +export const Example: StoryObj = { + render: () => { + return html` +
+ +
` + }, +} diff --git a/inlang/source-code/bundle-component/src/stories/inlang-selector-configurator.ts b/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.ts similarity index 68% rename from inlang/source-code/bundle-component/src/stories/inlang-selector-configurator.ts rename to inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.ts index 42bc792bb8..e40f905741 100644 --- a/inlang/source-code/bundle-component/src/stories/inlang-selector-configurator.ts +++ b/inlang/source-code/bundle-component/src/stories/actions/add-selector/inlang-add-selector.ts @@ -1,25 +1,33 @@ import { LitElement, css, html } from "lit" import { customElement, property, state } from "lit/decorators.js" - -import SlDropdown from "@shoelace-style/shoelace/dist/components/dropdown/dropdown.component.js" - +import { createChangeEvent } from "../../../helper/event.js" +import { baseStyling } from "../../../styling/base.js" import { - type Declaration, - createMessage, createVariant, type Message, - type Variant, type MessageNested, type Expression, - type ProjectSettings, + Declaration, } from "@inlang/sdk2" -import addSelector from "../helper/crud/selector/add.js" -import upsertVariant from "../helper/crud/variant/upsert.js" -import "./inlang-add-input.js" -@customElement("inlang-selector-configurator") -export default class InlangSelectorConfigurator extends LitElement { +import SlDropdown from "@shoelace-style/shoelace/dist/components/dropdown/dropdown.component.js" +import SlSelect from "@shoelace-style/shoelace/dist/components/select/select.component.js" +import SlInput from "@shoelace-style/shoelace/dist/components/input/input.component.js" +import SlOption from "@shoelace-style/shoelace/dist/components/option/option.component.js" +import SlButton from "@shoelace-style/shoelace/dist/components/button/button.component.js" +import SlTooltip from "@shoelace-style/shoelace/dist/components/tooltip/tooltip.component.js" + +if (!customElements.get("sl-dropdown")) customElements.define("sl-dropdown", SlDropdown) +if (!customElements.get("sl-select")) customElements.define("sl-select", SlSelect) +if (!customElements.get("sl-input")) customElements.define("sl-input", SlInput) +if (!customElements.get("sl-option")) customElements.define("sl-option", SlOption) +if (!customElements.get("sl-button")) customElements.define("sl-button", SlButton) +if (!customElements.get("sl-tooltip")) customElements.define("sl-tooltip", SlTooltip) + +@customElement("inlang-add-selector") +export default class InlangAddSelector extends LitElement { static override styles = [ + baseStyling, css` .button-wrapper { height: 44px; @@ -142,32 +150,24 @@ export default class InlangSelectorConfigurator extends LitElement { sl-input::part(base) { font-size: 13px; } + .add-selector::part(base) { + border-radius: 4px; + cursor: pointer; + font-size: 14px; + } + .add-selector::part(base):hover { + background-color: var(--sl-input-background-color-hover); + color: var(--sl-input-color-hover); + border: 1px solid var(--sl-input-border-color-hover); + } `, ] - @property() - inputs: Declaration[] | undefined - - @property() - bundleId: string | undefined - @property() message?: MessageNested | undefined @property() - locale: ProjectSettings["locales"][number] | undefined - - @property() - triggerMessageBundleRefresh: () => void = () => {} - - @property() - triggerSave: () => void = () => {} - - @property() - addMessage: (newMessage: MessageNested) => void = () => {} - - @property() - addInput: (inputName: string) => void = () => {} + messages?: MessageNested | undefined @state() private _input: string | undefined @@ -184,209 +184,162 @@ export default class InlangSelectorConfigurator extends LitElement { @state() private _newInputSting: string | undefined - // events - dispatchOnInsertMessage(message: Message, variants: Variant[]) { - const onInsertMessage = new CustomEvent("insert-message", { - bubbles: true, - composed: true, - detail: { - argument: { - message, - variants, - }, - }, - }) - this.dispatchEvent(onInsertMessage) - } - - dispatchOnUpdateMessage(message: Message, variants: Variant[]) { - const onUpdateMessage = new CustomEvent("update-message", { - bubbles: true, - composed: true, - detail: { - argument: { - message, - variants, - }, - }, - }) - this.dispatchEvent(onUpdateMessage) - } - - dispatchOnInsertVariant(variant: Variant) { - const onInsertVariant = new CustomEvent("insert-variant", { - bubbles: true, - composed: true, - detail: { - argument: { - variant, - }, - }, - }) - this.dispatchEvent(onInsertVariant) - } - - dispatchOnUpdateVariant(variant: Variant) { - const onUpdateVariant = new CustomEvent("update-variant", { - bubbles: true, - composed: true, - detail: { - argument: { - variant, - }, - }, - }) - this.dispatchEvent(onUpdateVariant) - } - private _getPluralCategories = (): string[] | undefined => { - return this.locale - ? [...new Intl.PluralRules(this.locale).resolvedOptions().pluralCategories, "*"] + return this.message?.locale + ? [...new Intl.PluralRules(this.message.locale).resolvedOptions().pluralCategories, "*"] : undefined } + private _getInputs = () => { + const inputs: Declaration[] = [] + if (this.messages) { + for (const message of this.messages as unknown as Message[]) { + for (const declaration of message.declarations) { + if (declaration.type === "input" && !inputs.some((d) => d.name === declaration.name)) { + inputs.push(declaration) + } + } + } + } + return inputs + } + private _handleAddSelector = (newMatchers: string[]) => { - // get dropdown by "dropdown" class + // close dropdown const dropdown = this.shadowRoot?.querySelector(".dropdown") as SlDropdown if (dropdown) dropdown.hide() - if (this._isNewInput && this._newInputSting && this._newInputSting.length > 0) { - this.addInput(this._newInputSting) - this._input = this._newInputSting - } + // Step 0 | (optinal) adding input + this._addInput() - if (this._input) { - if (!this.message && this.locale) { - // create selector in not present message - const newMessage = createMessage({ - bundleId: this.bundleId!, - locale: this.locale, - text: "", - }) + if (this._input && this.message) { + // get variant matcher + const message = structuredClone(this.message) + const _variantsMatcher = (message ? message.variants : []).map((variant) => variant.match) - // add selector - addSelector({ - message: newMessage, - selector: { - type: "expression", - arg: { - type: "variable", - name: this._input, - }, - annotation: { - type: "function", - name: this._function || "plural", - options: [], - }, - }, - }) + // Step 1 | add selector to message + this._updateSelector() - this._addVariants({ - message: newMessage, - variantsMatcher: [], - newMatchers: newMatchers, - newSelectorName: this._input, - }) - this.addMessage(newMessage) - this.dispatchOnInsertMessage(newMessage, newMessage.variants) - for (const variant of newMessage.variants) { - this.dispatchOnInsertVariant(variant) - } - } else if (this.message) { - // get variant matchers arrays - const _variants = structuredClone(this.message ? this.message.variants : []) - const _variantsMatcher = _variants.map((variant) => variant.match) + // Step 2 | add "*" to existing variants + this._addMatchToExistingVariants() + + // Step 3 | get newCombinations and add new variants + const newCombinations = this._generateNewMatcherCombinations({ + variantsMatcher: _variantsMatcher, + newMatchers: newMatchers, + newSelectorName: this._input, + }) + this._addVariantsFromNewCombinations(newCombinations) + } + } + + private _addInput = () => { + if (this._isNewInput && this._newInputSting && this._newInputSting.length > 0) { + for (const message of (this.messages as any as Message[]) || []) { + const newMessage = structuredClone(message) - // add selector - addSelector({ - message: this.message, - selector: { + newMessage.declarations.push({ + type: "input", + name: this._newInputSting, + value: { type: "expression", arg: { type: "variable", - name: this._input, - }, - annotation: { - type: "function", - name: this._function || "plural", - options: [], + name: this._newInputSting, }, }, }) - const updatedVariants = structuredClone(this.message.variants) - this.dispatchOnUpdateMessage(this.message, updatedVariants) + this.dispatchEvent( + createChangeEvent({ + type: "Message", + operation: "update", + newData: newMessage, + }) + ) + } + this._input = this._newInputSting + } + } - this._addVariants({ - message: this.message, - variantsMatcher: _variantsMatcher, - newMatchers: newMatchers, - newSelectorName: this._input, + private _updateSelector = () => { + if (this.message && this._input) { + this.message.selectors.push({ + type: "expression", + arg: { + type: "variable", + name: this._input, + }, + annotation: { + type: "function", + name: this._function || "plural", + options: [], + }, + }) + this.dispatchEvent( + createChangeEvent({ + type: "Message", + operation: "update", + newData: this.message, }) + ) + } + } - // only inserted variants should be dispatched -> show filter - const insertedVariants = this.message.variants.filter( - (variant) => !updatedVariants.find((v) => v.id === variant.id) + private _addMatchToExistingVariants = () => { + if (this.message && this._input) { + for (const variant of this.message.variants) { + variant.match[this._input] = "*" + this.dispatchEvent( + createChangeEvent({ + type: "Variant", + operation: "update", + newData: variant, + }) ) - for (const variant of insertedVariants) { - this.dispatchOnInsertVariant(variant) - } - - for (const updatedVariant of updatedVariants) { - this.dispatchOnUpdateVariant(updatedVariant) - } } + } + } - this.triggerSave() - this.triggerMessageBundleRefresh() + private _addVariantsFromNewCombinations = (newCombinations: Array>) => { + if (this.message) { + for (const combination of newCombinations) { + const newVariant = createVariant({ + messageId: this.message.id, + match: combination, + text: "", + }) + this.dispatchEvent( + createChangeEvent({ + type: "Variant", + operation: "create", + newData: newVariant, + }) + ) + } } } - private _addVariants = (props: { - message: MessageNested + private _generateNewMatcherCombinations = (props: { variantsMatcher: Record[] newMatchers: string[] newSelectorName: string - }) => { + }): Array> => { const newMatchers = props.newMatchers.filter((category) => category !== "*") + const newCombinations: Array> = [] if (newMatchers) { - if (props.variantsMatcher && props.variantsMatcher.length > 0) { - for (const variantMatcher of props.variantsMatcher) { - for (const category of newMatchers) { - upsertVariant({ - message: props.message, - variant: createVariant({ - messageId: props.message.id, - // combine the matches that are already present with the new category -> like a matrix - match: { ...variantMatcher, ...{ [props.newSelectorName]: category } }, - }), - }) - } - } - } else { + for (const variantMatcher of props.variantsMatcher) { for (const category of newMatchers) { - upsertVariant({ - message: props.message, - variant: createVariant({ - messageId: props.message.id, - // combine the matches that are already present with the new category -> like a matrix - match: { [props.newSelectorName]: category }, - }), - }) + newCombinations.push({ ...variantMatcher, ...{ [props.newSelectorName]: category } }) } } } - } - - private _resetConfiguration = () => { - this._input = this.inputs && this.inputs[0] && this.inputs[0].name - this._function = "plural" - this._matchers = this._getPluralCategories() || ["*"] + return newCombinations } override async firstUpdated() { await this.updateComplete - this._input = this.inputs && this.inputs[0] && this.inputs[0].name + this._input = this._getInputs()?.[0]?.name this._function = "plural" this._matchers = this._getPluralCategories() || ["*"] } @@ -400,23 +353,37 @@ export default class InlangSelectorConfigurator extends LitElement { const dropdown = this.shadowRoot?.querySelector("sl-dropdown") if (dropdown && e.target === dropdown) { this._input = - this.inputs && this.inputs.length > 0 && this.inputs[0] - ? this.inputs[0].name + this._getInputs() && this._getInputs().length > 0 && this._getInputs()[0] + ? this._getInputs()[0]!.name : undefined - if (this.inputs && this.inputs.length === 0) { + if (this._getInputs() && this._getInputs().length === 0) { this._isNewInput = true } } }} >
- + + + + + Selector + +