Skip to content

Needles should be recursively resolved #55

@jtmthf

Description

@jtmthf

I ended up finding a solution to my problem while writing this issue, but I believe it's something that styled-tools should handle by default. I'm working with the sample below which a reakit theme, and the issue is in contrastText. What ends up happening is that bg resolves to a prop-getter function instead of a string. The reason being that bgNeedle is a prop-getter that resolves to a prop-getter which is not supported by withProp. What I ended up doing is writing a custom implementation of withProp (below the example) that recursively resolves prop-getter functions. It would be useful if this was either the default behavior of withProp or was exposed as a separate function.

// utils.ts

import { range } from 'lodash-es';
import { getLuminance, lighten } from 'polished';
import { Needle, palette as p, withProp } from 'styled-tools';

export const contrastText = (bgNeedle: Needle<any>) =>
  withProp(
    [bgNeedle, p('black'), p('white')] as any,
    (bg: string, black: string, white: string) => {
      // bg ends up being a prop getter function here instead of a string.
      return getLuminance(bg) > 0.179 ? black : white;
    },
  );

export const contrastPalette = (palette: string, tone?: number) =>
  contrastText(p(palette, tone));

export const lightenPalette = (
  amount: number,
  palette: string,
  tone?: number,
) =>
  withProp([p(palette, tone)] as any, (color: string) =>
    lighten(amount, color),
  );

export const tonePalette = (palette: string) => [
  p(palette),
  ...range(0.1, 0.5, 0.1).map(i => lightenPalette(i, palette)),
];

export const tonePaletteText = (palette: string) =>
  range(5).map(i => contrastPalette(palette, i));

export const tonePaletteWithText = (name: string, palette: string) => ({
  [name]: tonePalette(palette),
  [`${name}Text`]: tonePaletteText(name)
});
// index.ts

import { tonePaletteWithText } from './utils';

export const palette = {
  white: '#fff',
  black: '#000',

  astronautBlue: '#003a5d',

  ...tonePaletteWithText('primary', 'astronautBlue'),
};

Solution

// simple deepWithProp

const deepWithProp = <Props extends object, T = undefined>(
  needle: Needle<Props> | Needle<Props>[],
  fn: (...args: any[]) => T
) => (props = {}): T => {
  if (Array.isArray(needle)) {
    const needles = needle.map(arg => deepWithProp(arg, x => x)(props));
    return fn(...needles);
  }
  if (typeof needle === "function") {
    return fn(deepWithProp(needle(props as Props), x => x)(props));
  }
  return fn(prop(needle, needle)(props));
};

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions