Passed
Branch main (393d41)
by Andrii
04:53 queued 02:15
created

src/ctx.ts   A

Complexity

Total Complexity 8
Complexity/F 4

Size

Lines of Code 117
Function Count 2

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 8
eloc 77
mnd 6
bc 6
fnc 2
dl 0
loc 117
rs 10
bpm 3
cpm 4
noi 0
c 0
b 0
f 0
1
import type { Falsy, ToggleMap, ClassValue, ClassNamer, ClassNamed, ClassNamesMap, EmptyObject } from "./defs"
2
import { emptize, stringifyClassNamed, truthyKeys } from "./utils"
3
4
emptize(classNamer)
5
6
//TODO no `className` - no first `true`
7
interface tClassNaming<
8
  ClassKeys extends string,
9
  withClassNames extends boolean | undefined
10
> {
11
  /**
12
   * @example classes(true) === props.className
13
   * @example classes({class1: true, class2: false}) === "class1"
14
   * @example classes("class3", false && "class4") === "class3"
15
   * @example classes(true, {class1: true, class2: false}, "class3", false && "class4") === `${props.className} class1 class3`
16
  */
17
 // Using overloads will make error not in certain argument but on all call - 'No overload found'
18
  (
19
    propagate_or_map_or_expression?: true | ToggleMap<ClassKeys> | ClassKeys | Falsy,
20
    map_or_expression?: (
21
      [Extract<typeof propagate_or_map_or_expression, true>] extends [never]
22
      ? never
23
      : ToggleMap<ClassKeys>
24
    ) | ClassKeys | Falsy,
25
    ...expressions: (ClassKeys | Falsy)[]
26
  ) : ClassNamed & (
27
    withClassNames extends true
28
    ? {classnames: ClassNamesMap<ClassKeys>}
29
    : EmptyObject
30
  ) 
31
}
32
33
export default classNamingCtx
34
35
/**
36
 * @example const classes = classNamingCtx(this.props)
37
 * @example const classes = classNamingCtx({className, classnames})
38
 * @example const classes = classNamingCtx({classnames})
39
 */
40
function classNamingCtx<
41
  ClassKeys extends string,
42
  withClassNames extends boolean|undefined
43
>(
44
  {classnames, className}: ClassNamer<ClassKeys>,
45
  options?: ClassNamerOptions<withClassNames>
46
) {
47
  return classNamer.bind({classnames, className, ...options}) as tClassNaming<ClassKeys, withClassNames>
48
}
49
50
type ClassNamerOptions<
51
  withClassNames extends undefined|boolean
52
> = Partial<{
53
  withClassNames: withClassNames
54
  // withSelf: boolean
55
}>
56
57
function classNamer<
58
  ClassKeys extends string,
59
  withClassNames extends boolean|undefined
60
>(
61
  this: Partial<ClassNamer<ClassKeys> & ClassNamerOptions<withClassNames>>,
62
  arg0?: true | ToggleMap<ClassKeys> | ClassKeys,
63
  arg1?: ToggleMap<ClassKeys> | ClassKeys,
64
  ...args: (ClassKeys | Falsy)[]
65
): ClassNamed & Partial<Pick<typeof this, "classnames">> {
66
  const {
67
    className: _propagated,
68
    classnames,
69
    withClassNames,
70
    // withSelf
71
  } = this
72
  , withPropagation = arg0 === true
73
  , allowed: ClassKeys[] = truthyKeys(arg0 === true ? false : arg0)
74
  .concat(truthyKeys(arg1))
75
  //@ts-expect-error
76
  .concat(args)
77
  .filter<ClassKeys>(
78
    //@ts-expect-error
79
    Boolean
80
  )
81
  
82
  emptize(classnames)
83
84
  for (let i = allowed.length; i--;) {
85
    const key = allowed[i]
86
    , hash: ClassValue = classnames?.[key]
87
    
88
    if (hash !== undefined)
89
      //@ts-expect-error
90
      allowed[i] = hash
91
  }
92
  
93
  const allowedString = allowed.join(" ")
94
  , propagated = withPropagation && _propagated || ""
95
  
96
  //TODO Consider undefined|empty|never for type error
97
  , className = `${
98
    propagated
99
  }${
100
    propagated && allowedString
101
    ? " "
102
    : ""
103
  }${
104
    allowedString
105
  }`
106
  
107
  if (!withClassNames) {
108
    return stringifyClassNamed({
109
      className
110
    })
111
  } else {
112
    return stringifyClassNamed({
113
      className,
114
      classnames
115
    })
116
  }
117
}