Home

tailwind-ctp-intellisense @7ba8b4d8f883b5f049666edcc31d7f71d0237141 - refs - log -
-
https://git.jolheiser.com/tailwind-ctp-intellisense.git
Tailwind intellisense + Catppuccin
tailwind-ctp-intellisense / src / lsp / util / color.ts
- raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
const dlv = require('dlv')
import { State } from './state'
import removeMeta from './removeMeta'
import { TinyColor } from '@ctrl/tinycolor'
import { ensureArray, dedupe, flatten } from '../../util/array'

const COLOR_PROPS = [
  'caret-color',
  'color',
  'column-rule-color',
  'background-color',
  'border-color',
  'border-top-color',
  'border-right-color',
  'border-bottom-color',
  'border-left-color',
  'fill',
  'outline-color',
  'stop-color',
  'stroke',
  'text-decoration-color',
]

function isKeyword(value: string): boolean {
  return ['transparent', 'currentcolor'].includes(value.toLowerCase())
}

export function getColor(
  state: State,
  keys: string[]
): TinyColor | string | null {
  const item = dlv(state.classNames.classNames, keys)
  if (!item.__rule) return null
  const props = Object.keys(removeMeta(item))
  if (props.length === 0) return null
  const nonCustomProps = props.filter((prop) => !prop.startsWith('--'))

  const areAllCustom = nonCustomProps.length === 0

  if (
    !areAllCustom &&
    nonCustomProps.some((prop) => !COLOR_PROPS.includes(prop))
  ) {
    // they should all be color-based props
    return null
  }

  const propsToCheck = areAllCustom ? props : nonCustomProps

  const colors = flatten(
    propsToCheck.map((prop) => ensureArray(item[prop]).map(createColor))
  )

  // check that all of the values are valid colors
  if (colors.some((color) => typeof color !== 'string' && !color.isValid)) {
    return null
  }

  // check that all of the values are the same color, ignoring alpha
  const colorStrings = dedupe(
    colors.map((color) =>
      typeof color === 'string' ? color : `${color.r}-${color.g}-${color.b}`
    )
  )
  if (colorStrings.length !== 1) {
    return null
  }

  if (isKeyword(colorStrings[0])) {
    return colorStrings[0]
  }

  const nonKeywordColors = colors.filter(
    (color): color is TinyColor => typeof color !== 'string'
  )

  const alphas = dedupe(nonKeywordColors.map((color) => color.a))

  if (alphas.length === 1) {
    return nonKeywordColors[0]
  }

  if (alphas.length === 2 && alphas.includes(0)) {
    return nonKeywordColors.find((color) => color.a !== 0)
  }

  return null
}

export function getColorFromValue(value: unknown): string {
  if (typeof value !== 'string') return null
  if (value === 'transparent') {
    return 'rgba(0, 0, 0, 0.01)'
  }
  const color = new TinyColor(value)
  if (color.isValid) {
    return color.toRgbString()
  }
  return null
}

function createColor(str: string): TinyColor | string {
  if (isKeyword(str)) {
    return str
  }

  // matches: rgba(<r>, <g>, <b>, var(--bg-opacity))
  // TODO: support other formats? e.g. hsla, css level 4
  const match = str.match(
    /^\s*rgba\(\s*(?<r>[0-9]{1,3})\s*,\s*(?<g>[0-9]{1,3})\s*,\s*(?<b>[0-9]{1,3})\s*,\s*var/
  )

  if (match) {
    return new TinyColor({
      r: match.groups.r,
      g: match.groups.g,
      b: match.groups.b,
    })
  }

  return new TinyColor(str)
}