Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/deepPalette.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// @flow
import {
resolvePaletteKey,
resolvePaletteTone,
resolvePaletteDefaultValue,
toArray,
clamp,
resolveValue
} from "./utils";
import type { PaletteProps } from ".";

/**
* Returns `props.theme.palette[key || props.palette][tone || props.tone || 0]` or `defaultValue`.
* Supports deep resolving.
* @see {@link #palette palette}
*/
const deepPalette = (
keyOrTone?: string | number,
toneOrDefaultValue?: any,
defaultValue?: any
) => (props: PaletteProps) => {
const key = resolvePaletteKey(keyOrTone, props);
const tone = resolvePaletteTone(keyOrTone, toneOrDefaultValue, props);
const finalDefaultValue = resolvePaletteDefaultValue(
toneOrDefaultValue,
tone,
defaultValue
);

if (!props.theme.palette || !props.theme.palette[key]) {
return finalDefaultValue;
}

const tones = toArray(resolveValue(props.theme.palette[key], props));

if (tone < 0) {
return resolveValue(
tones[clamp(tones.length + tone, 0, tones.length - 1)],
props
);
}

return resolveValue(tones[clamp(tone, 0, tones.length - 1)], props);
};

export default deepPalette;
26 changes: 26 additions & 0 deletions src/deepProp.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// @flow
import { get, resolveValue } from "./utils";
import type { PropsFn } from ".";

/**
* Returns the value of `props[path]` or `defaultValue`. Supports deep
* resolving.
* @see {@link #prop prop}
*/
const deepProp = (path: string, defaultValue?: any): PropsFn => (
props = {}
) => {
if (typeof props[path] !== "undefined") {
return resolveValue(props[path], props);
}

const object = get(props, path);

if (typeof object !== "undefined") {
return resolveValue(object, props);
}

return defaultValue;
};

export default deepProp;
14 changes: 14 additions & 0 deletions src/deepTheme.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @flow
import deepProp from "./deepProp";
import type { PropsWithTheme } from ".";

/**
* Same as `deepProp`, except that it returns `props.theme[path]` instead of
* `props[path]`.
* @see {@link #theme theme}
*/
const deepTheme = (path: string, defaultValue?: any) => (
props: PropsWithTheme
) => deepProp(`theme.${path}`, defaultValue)(props);

export default deepTheme;
3 changes: 3 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export deepPalette from "./deepPalette";
export deepProp from "./deepProp";
export deepTheme from "./deepTheme";
export ifNotProp from "./ifNotProp";
export ifProp from "./ifProp";
export prop from "./prop";
Expand Down
5 changes: 5 additions & 0 deletions src/index.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@ export type Needle = string | Function;
export type PropsFn = (props?: Object) => any;

export type PropsWithTheme = { theme: { [x: string]: any } };

export type PaletteProps = PropsWithTheme & {
palette?: string,
tone?: number
};
40 changes: 16 additions & 24 deletions src/palette.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
// @flow
import type { PropsWithTheme } from ".";

type Props = PropsWithTheme & {
palette?: string,
tone?: number
};

const toArray = (arg: any) => (Array.isArray(arg) ? arg : [arg]);

const clamp = (number: number, min: number, max: number) => {
if (number < min) return min;
if (number > max) return max;
return number;
};
import {
resolvePaletteKey,
resolvePaletteTone,
resolvePaletteDefaultValue,
toArray,
clamp
} from "./utils";
import type { PaletteProps } from ".";

/**
* Returns `props.theme.palette[key || props.palette][tone || props.tone || 0]` or `defaultValue`.
Expand Down Expand Up @@ -45,16 +39,14 @@ const palette = (
keyOrTone?: string | number,
toneOrDefaultValue?: any,
defaultValue?: any
) => (props: Props) => {
const key = typeof keyOrTone === "string" ? keyOrTone : props.palette;
const tone =
typeof keyOrTone === "number"
? keyOrTone
: typeof toneOrDefaultValue === "number"
? toneOrDefaultValue
: props.tone || 0;
const finalDefaultValue =
toneOrDefaultValue !== tone ? toneOrDefaultValue : defaultValue;
) => (props: PaletteProps) => {
const key = resolvePaletteKey(keyOrTone, props);
const tone = resolvePaletteTone(keyOrTone, toneOrDefaultValue, props);
const finalDefaultValue = resolvePaletteDefaultValue(
toneOrDefaultValue,
tone,
defaultValue
);

if (!props.theme.palette || !props.theme.palette[key]) {
return finalDefaultValue;
Expand Down
17 changes: 4 additions & 13 deletions src/prop.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// @flow
import { get } from "./utils";
import type { PropsFn } from ".";

/**
Expand All @@ -16,20 +17,10 @@ const prop = (path: string, defaultValue?: any): PropsFn => (props = {}) => {
return props[path];
}

if (path && path.indexOf(".") > 0) {
const paths = path.split(".");
const { length } = paths;
let object = props[paths[0]];
let index = 1;
const object = get(props, path);

while (object != null && index < length) {
object = object[paths[index]];
index += 1;
}

if (typeof object !== "undefined") {
return object;
}
if (typeof object !== "undefined") {
return object;
}

return defaultValue;
Expand Down
56 changes: 56 additions & 0 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// @flow
import type { PaletteProps } from ".";

export function get(props: {}, path: string) {
if (path && path.indexOf(".") > 0) {
const paths = path.split(".");
const { length } = paths;
let object = props[paths[0]];
let index = 1;

while (object != null && index < length) {
object = object[paths[index]];
index += 1;
}

return object;
}
return undefined;
}

export function resolveValue(value: any, props: Object) {
if (typeof value === "function") {
return resolveValue(value(props), props);
}
return value;
}

export const toArray = (arg: any) => (Array.isArray(arg) ? arg : [arg]);

export const clamp = (number: number, min: number, max: number) => {
if (number < min) return min;
if (number > max) return max;
return number;
};

export const resolvePaletteKey = (
keyOrTone?: string | number,
props: PaletteProps
) => (typeof keyOrTone === "string" ? keyOrTone : props.palette);

export const resolvePaletteTone = (
keyOrTone?: string | number,
toneOrDefaultValue?: any,
props: PaletteProps
) =>
typeof keyOrTone === "number"
? keyOrTone
: typeof toneOrDefaultValue === "number"
? toneOrDefaultValue
: props.tone || 0;

export const resolvePaletteDefaultValue = (
toneOrDefaultValue?: any,
tone: number,
defaultValue?: any
) => (toneOrDefaultValue !== tone ? toneOrDefaultValue : defaultValue);
48 changes: 48 additions & 0 deletions test/deepPalette.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import deepPalette from "../src/deepPalette";

const theme = {
palette: {
primary: [() => "primary0", () => "primary1", () => "primary2"],
secondary: () => "secondary0"
}
};

test("deep only tone", () => {
expect(deepPalette()({ theme })).toBeUndefined();
expect(deepPalette(0)({ theme })).toBeUndefined();
expect(deepPalette()({ theme, palette: "primary" })).toBe("primary0");
expect(deepPalette()({ theme, palette: "primary", tone: 2 })).toBe(
"primary2"
);
expect(deepPalette(0)({ theme, palette: "primary" })).toBe("primary0");
expect(deepPalette(1)({ theme, palette: "primary" })).toBe("primary1");
expect(deepPalette(-1)({ theme, palette: "primary" })).toBe("primary2");
expect(deepPalette(-5)({ theme, palette: "primary" })).toBe("primary0");
expect(deepPalette(5)({ theme, palette: "primary" })).toBe("primary2");
expect(deepPalette(0)({ theme, palette: "secondary" })).toBe("secondary0");
expect(deepPalette(1)({ theme, palette: "secondary" })).toBe("secondary0");
expect(deepPalette(0)({ theme, palette: "other" })).toBeUndefined();
expect(deepPalette(1, "foo")({ theme })).toBe("foo");
expect(deepPalette(1, "foo")({ theme, palette: "other" })).toBe("foo");
});

test("deep palette and tone", () => {
expect(deepPalette("primary")({ theme })).toBe("primary0");
expect(deepPalette("primary")({ theme, tone: 2 })).toBe("primary2");
expect(deepPalette("primary", 0)({ theme })).toBe("primary0");
expect(deepPalette("primary", 5)({ theme })).toBe("primary2");
expect(deepPalette("primary", -1)({ theme })).toBe("primary2");
expect(deepPalette("primary", -5)({ theme })).toBe("primary0");
expect(deepPalette("secondary", 0)({ theme })).toBe("secondary0");
expect(deepPalette("secondary", 1)({ theme })).toBe("secondary0");
expect(deepPalette("secondary", 1, "foo")({ theme })).toBe("secondary0");
expect(deepPalette("other", 1)({ theme })).toBeUndefined();
expect(deepPalette("other", "foo")({ theme })).toBe("foo");
expect(deepPalette("other", 1, "foo")({ theme })).toBe("foo");
expect(
deepPalette("other", 1)({ theme, palette: "primary" })
).toBeUndefined();
expect(deepPalette("other", 1, "foo")({ theme, palette: "primary" })).toBe(
"foo"
);
});
30 changes: 30 additions & 0 deletions test/deepProp.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import deepProp from "../src/deepProp";

test("string argument", () => {
expect(deepProp("color")()).toBeUndefined();
expect(deepProp("color")({})).toBeUndefined();
expect(deepProp("color")({ color: () => "red" })).toBe("red");
expect(deepProp("color")({ color: props => props.bg, bg: "red" })).toBe(
"red"
);
});

test("deep string argument", () => {
expect(deepProp("color.primary")()).toBeUndefined();
expect(deepProp("color.primary")({})).toBeUndefined();
expect(deepProp("color.primary")({ color: { primary: () => "red" } })).toBe(
"red"
);
expect(
deepProp("color.primary")({
color: { primary: deepProp("color.secondary"), secondary: "blue" }
})
).toBe("blue");
});

test("defaultValue", () => {
expect(deepProp("color", "red")()).toBe("red");
expect(deepProp("color", "red")({})).toBe("red");
expect(deepProp("color", "red")({ color: () => "blue" })).toBe("blue");
expect(deepProp("color.primary", "red")({})).toBe("red");
});
32 changes: 32 additions & 0 deletions test/deepTheme.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import deepTheme from "../src/deepTheme";

test("string argument", () => {
expect(deepTheme("color")({ theme: { color: () => "red" } })).toBe("red");
expect(
deepTheme("color")({
theme: { color: props => props.theme.bg, bg: "blue" }
})
).toBe("blue");
expect(
deepTheme("color")({ theme: { color: deepTheme("bg"), bg: "blue" } })
).toBe("blue");
expect(
deepTheme("linkColor")({
theme: {
blue: "#007bff",
primary: deepTheme("blue"),
linkColor: deepTheme("primary")
}
})
).toBe("#007bff");
});

test("deep string argument", () => {
expect(
deepTheme("color.primary")({
theme: {
color: { primary: deepTheme("color.secondary"), secondary: "blue" }
}
})
).toBe("blue");
});
Loading