diff --git a/.changeset/brown-mangos-fetch.md b/.changeset/brown-mangos-fetch.md new file mode 100644 index 0000000..0a520fc --- /dev/null +++ b/.changeset/brown-mangos-fetch.md @@ -0,0 +1,5 @@ +--- +"@imgproxy/imgproxy-js-core": minor +--- + +Add [flip](https://docs.imgproxy.net/latest/usage/processing#flip) option support diff --git a/src/options/flip.ts b/src/options/flip.ts new file mode 100644 index 0000000..9953247 --- /dev/null +++ b/src/options/flip.ts @@ -0,0 +1,20 @@ +import type { FlipOptions, FlipOptionsPartial } from "../types/flip"; +import { guardIsUndef, normalizeBoolean } from "../utils"; + +const getOpt = (options: FlipOptionsPartial): FlipOptions | undefined => + options.flip || options.fl; + +const test = (options: FlipOptionsPartial): boolean => Boolean(getOpt(options)); + +const build = (options: FlipOptionsPartial): string => { + const flipOptions = getOpt(options); + + guardIsUndef(flipOptions, "flip"); + + const horizontal = flipOptions.horizontal ?? false; + const vertical = flipOptions.vertical ?? false; + + return `fl:${normalizeBoolean(horizontal)}:${normalizeBoolean(vertical)}`; +}; + +export { test, build }; diff --git a/src/options/index.ts b/src/options/index.ts index e33f036..b441c27 100644 --- a/src/options/index.ts +++ b/src/options/index.ts @@ -25,6 +25,7 @@ export * as extend from "./extend"; export * as extendAspectRatio from "./extendAspectRatio"; export * as fallbackImageUrl from "./fallbackImageUrl"; export * as filename from "./filename"; +export * as flip from "./flip"; export * as format from "./format"; export * as formatQuality from "./formatQuality"; export * as gradient from "./gradient"; diff --git a/src/types/flip.ts b/src/types/flip.ts new file mode 100644 index 0000000..fcbbe24 --- /dev/null +++ b/src/types/flip.ts @@ -0,0 +1,43 @@ +/** + * Boolean-like values for flip options + * + * @note Only `1`, `"t"`, or `true` are recognized as truthy values. + * Any other value (including `"true"` or `"1"` as strings) will be treated as false. + */ +type FlipBooleanValue = boolean | 1 | 0 | "t" | "f"; + +/** + * *Flip option* + * + * When set, imgproxy will flip the image along the specified axes. + * + * @default false:false + * + * @see {@link https://docs.imgproxy.net/latest/usage/processing#flip | Flip imgproxy docs} + */ +type FlipOptions = { + /** + * When set to `1`, `"t"`, or boolean `true`, imgproxy will flip the image horizontally. + * @default false + */ + horizontal?: FlipBooleanValue; + /** + * When set to `1`, `t`, or boolean `true`, imgproxy will flip the image vertically. + * @default false + */ + vertical?: FlipBooleanValue; +}; + +/** + * *Flip option* + * + * To describe the flip option, you can use the keyword `flip` or `fl`. + * + * @see https://docs.imgproxy.net/latest/usage/processing#flip + */ +interface FlipOptionsPartial { + flip?: FlipOptions; + fl?: FlipOptions; +} + +export { FlipOptions, FlipOptionsPartial }; diff --git a/src/types/index.ts b/src/types/index.ts index 0ea5ceb..304967f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -25,6 +25,7 @@ import type { ExtendAspectRatioOptionsPartial } from "./extendAspectRatio"; import type { ExtendOptionsPartial } from "./extend"; import type { FallbackImageUrlOptionsPartial } from "./fallbackImageUrl"; import type { FilenameOptionsPartial } from "./filename"; +import type { FlipOptionsPartial } from "./flip"; import type { FormatOptionsPartial } from "./format"; import type { FormatQualityOptionsPartial } from "./formatQuality"; import type { GradientOptionsPartial } from "./gradient"; @@ -105,6 +106,7 @@ export type Options = AdjustOptionsPartial & ExtendOptionsPartial & FallbackImageUrlOptionsPartial & FilenameOptionsPartial & + FlipOptionsPartial & FormatOptionsPartial & FormatQualityOptionsPartial & GradientOptionsPartial & diff --git a/tests/optionsBasic/flip.test.ts b/tests/optionsBasic/flip.test.ts new file mode 100644 index 0000000..5139dac --- /dev/null +++ b/tests/optionsBasic/flip.test.ts @@ -0,0 +1,88 @@ +import { describe, expect, it } from "vitest"; +import { test, build } from "../../src/options/flip"; + +describe("flip", () => { + describe("test", () => { + it("should return true if flip option is defined", () => { + expect(test({ flip: { horizontal: true } })).toEqual(true); + }); + + it("should return true if fl option is defined", () => { + expect(test({ fl: { vertical: true } })).toEqual(true); + }); + + it("should return false if flip option is undefined", () => { + expect(test({})).toEqual(false); + }); + }); + + describe("build", () => { + it("should throw an error if flip is undefined", () => { + expect(() => build({})).toThrow("flip option is undefined"); + }); + + it("should return fl:f:f if both horizontal and vertical are false", () => { + expect(build({ flip: { horizontal: false, vertical: false } })).toEqual( + "fl:f:f" + ); + }); + + it("should return fl:f:f if flip object is empty (defaults)", () => { + expect(build({ flip: {} })).toEqual("fl:f:f"); + }); + + it("should return fl:t:f if horizontal is true", () => { + expect(build({ flip: { horizontal: true } })).toEqual("fl:t:f"); + }); + + it("should return fl:f:t if vertical is true", () => { + expect(build({ flip: { vertical: true } })).toEqual("fl:f:t"); + }); + + it("should return fl:t:t if both are true", () => { + expect(build({ flip: { horizontal: true, vertical: true } })).toEqual( + "fl:t:t" + ); + }); + + it("should support horizontal as 1", () => { + expect(build({ flip: { horizontal: 1 } })).toEqual("fl:t:f"); + }); + + it("should support horizontal as 't'", () => { + expect(build({ flip: { horizontal: "t" } })).toEqual("fl:t:f"); + }); + + it("should support vertical as 1", () => { + expect(build({ flip: { vertical: 1 } })).toEqual("fl:f:t"); + }); + + it("should support vertical as 't'", () => { + expect(build({ flip: { vertical: "t" } })).toEqual("fl:f:t"); + }); + + it("should support both with various truthy values", () => { + expect(build({ flip: { horizontal: 1, vertical: "t" } })).toEqual( + "fl:t:t" + ); + }); + + it("should support 0 as false for horizontal", () => { + expect(build({ flip: { horizontal: 0, vertical: true } })).toEqual( + "fl:f:t" + ); + }); + + it("should work with fl shorthand", () => { + expect(build({ fl: { horizontal: true, vertical: false } })).toEqual( + "fl:t:f" + ); + }); + + it("should work with both horizontal and vertical using different value types", () => { + expect(build({ flip: { horizontal: true, vertical: 1 } })).toEqual( + "fl:t:t" + ); + }); + }); +});