1
|
|
|
import type { ClassNamingContext, ClassNamed, ClassNamesMap, ClassHash } from "./defs" |
2
|
|
|
import {joinWithLead, resolver, wrapper} from "./core" |
3
|
|
|
import { emptize } from "./utils" |
4
|
|
|
|
5
|
|
|
const stackedKey = Symbol("stacked") |
6
|
|
|
|
7
|
|
|
emptize(classNamingCtx) |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Makes `className` string from imported CSS |
11
|
|
|
* @example <div className={`${classNamingBasic({ClassName})}`} /> |
12
|
|
|
* @example <div {...classNamingBasic({ClassName})} /> |
13
|
|
|
* @example const cn = classNamingBasic({C1})({C2}); <div {...cn({C3})({C4})} /> |
14
|
|
|
*/ |
15
|
|
|
interface ClassNamingCall< |
16
|
|
|
//TODO `extends ReactRelated` |
17
|
|
|
Source extends ClassNamesMap |
18
|
|
|
> { |
19
|
|
|
/** |
20
|
|
|
* @example classes(true) === props.className |
21
|
|
|
* @example classes({class1: true, class2: false}) === "class1" |
22
|
|
|
* @example classes(true, {class1: true, class2: false}) |
23
|
|
|
*/ |
24
|
|
|
( |
25
|
|
|
arg0?: ClassNamingContext<Source> | true | string | ActionsMap<Source>, |
26
|
|
|
arg1?: [Extract<typeof arg0, true|string>] extends [never] |
27
|
|
|
? never |
28
|
|
|
: ActionsMap<Source> |
29
|
|
|
): ClassNaming<Source> |
30
|
|
|
// Using overloads will make error not in certain argument but on all call - 'No overload found' |
31
|
|
|
// (propagateClassName: true): ClassNaming<Source> |
32
|
|
|
// (expression: ToggleMap<Source>): ClassNaming<Source> |
33
|
|
|
// (propagateClassName: true, expression: ToggleMap<Source>): ClassNaming<Source> |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
//TODO no `className` - no first `true` |
37
|
|
|
interface ClassNaming<Source extends ClassNamesMap> extends ClassNamed, ClassNamingCall<Source> {} |
38
|
|
|
|
39
|
|
|
type ClassNamingThis<Source extends ClassNamesMap> = ClassNamingContext<Source> & { |
40
|
|
|
//TODO change to Symbol |
41
|
|
|
[stackedKey]: string|undefined |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
type ActionsMap<K extends ClassNamesMap> = {[k in keyof K]?: ClassHash|boolean} |
45
|
|
|
// type SubMap<K extends ClassNamesMap> = {[k in keyof K]?: ClassHash} |
46
|
|
|
// type ToggleMap<K extends ClassNamesMap> = {[k in keyof K]?: boolean} |
47
|
|
|
|
48
|
|
|
export default classNamingCtx |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @example const classes = classNamingCtx(this.props) |
52
|
|
|
* @example const classes = classNamingCtx({className, classnames}) |
53
|
|
|
* @example const classes = classNamingCtx({classnames}) |
54
|
|
|
*/ |
55
|
|
|
|
56
|
|
|
function classNamingCtx< |
57
|
|
|
//TODO `extends ReactRelated` |
58
|
|
|
Source extends ClassNamesMap |
59
|
|
|
>( |
60
|
|
|
this: void | ClassNamingThis<Source>, |
61
|
|
|
// arg0?: typeof this extends void ? ClassNamingContext<Source> : (true | ToggleMap<Source>), |
62
|
|
|
// arg1?: typeof this extends void ? never : typeof arg0 extends true ? ToggleMap<Source> : never, |
63
|
|
|
arg0?: ClassNamingContext<Source> | (string | true | ActionsMap<Source>), |
64
|
|
|
arg1?: [Extract<typeof arg0, true|string>] extends [never] ? never : ActionsMap<Source> |
65
|
|
|
): ClassNaming<Source> { |
66
|
|
|
// istanbul ignore next //TODO Solve TS tricks with context |
67
|
|
|
const thisArg = this || {} |
68
|
|
|
|
69
|
|
|
context_assign: |
70
|
|
|
if ( |
71
|
|
|
!(stackedKey in thisArg) |
72
|
|
|
&& typeof arg0 === "object" |
73
|
|
|
) { |
74
|
|
|
const {classnames, className} = arg0 // as ClassNamingContext<Source> |
75
|
|
|
if ( |
76
|
|
|
classnames !== null && typeof classnames === "object" |
77
|
|
|
&& ( |
78
|
|
|
className === undefined || typeof className === "string" |
79
|
|
|
) |
80
|
|
|
) { |
81
|
|
|
emptize(classnames) |
82
|
|
|
const host: ClassNamingCall<Source> = classNamingCtx.bind({classnames, className, [stackedKey]: undefined}) |
83
|
|
|
|
84
|
|
|
return wrapper(host, className) |
85
|
|
|
} |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
const { |
89
|
|
|
className, |
90
|
|
|
classnames, |
91
|
|
|
[stackedKey]: preStacked, |
92
|
|
|
} = thisArg as ClassNamingThis<Source> |
93
|
|
|
, withPropagation = arg0 === true |
94
|
|
|
, source = typeof arg0 === "object" ? arg0 as ActionsMap<Source>: arg1 |
95
|
|
|
, allowed = source && resolver(classnames, source) |
96
|
|
|
, withInjection = typeof arg0 !== "string" ? preStacked : joinWithLead(preStacked, arg0) |
97
|
|
|
, stacked = joinWithLead(withInjection, allowed) |
98
|
|
|
, result = joinWithLead(withPropagation && className, stacked) |
99
|
|
|
, host: ClassNamingCall<Source> = classNamingCtx.bind({classnames, className, [stackedKey]: stacked}) |
100
|
|
|
|
101
|
|
|
classnames && emptize(classnames) |
102
|
|
|
|
103
|
|
|
return wrapper( |
104
|
|
|
host, |
105
|
|
|
result, |
106
|
|
|
) |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
|