Skip to content

Commit 31e2ff3

Browse files
author
Andrii Kirmas
committed
Implement chainable basic. Remove leading generics
1 parent 933a1ae commit 31e2ff3

File tree

5 files changed

+80
-98
lines changed

5 files changed

+80
-98
lines changed

README.md

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,25 @@ import classNamingBasic from "react-classnaming/basic"
3232

3333
const {className, classnames: {class2, class3, class4, class5}} = props
3434

35-
<div {...classNamingBasic("class1", {class2, class3})}>
36-
<div className={`${classNamingBasic({class4})}`}>
37-
<div className={classNamingBasic<string>({class5})}>
35+
<div {...classNamingBasic("class1", {class2, class3})} />
36+
<div className={`${classNamingBasic({class4})}`} />
37+
```
38+
39+
#### Chained
40+
41+
```tsx
42+
const {className, classnames: {
43+
Column_1, Column_2,
44+
Row_1, Row_2
45+
}} = props
46+
, cn1 = classNamingBasic(className, {Column_1})
47+
, cn2 = classNamingBasic(className, {Column_2})
48+
49+
<div {...cn1({ Row_1 })} />,
50+
<div {...cn1({ Row_2 })} />,
51+
<div {...cn2({ Row_1 })} />,
52+
<div {...cn2({ Row_2 })} />,
53+
<div {...classNamingBasic({ Column_1 })({ Column_2 })({ Row_1 })({ Row_2 })} />
3854
```
3955

4056
### From css-module or simulation
@@ -46,8 +62,8 @@ import css from "./some.css" // {"class1": "hash1", "class2": "hash2"}
4662
4763
const classNaming = classNamingCtx({className: "App", classnames: css})
4864

49-
<div {...classes(true, {class1: true, class2: false})}/> // className="App hash1"
50-
<div {...classes("class2")}/> // className="hash2"
65+
<div {...classes(true, {class1: true, class2: false})} /> // className="App hash1"
66+
<div {...classes("class2")} /> // className="hash2"
5167
```
5268

5369
### TS generic for props
@@ -103,7 +119,7 @@ const props: ClassNames<"class2"> = {"classnames": css}
103119
const {class2} = props.classnames
104120

105121
<div {...classNamingBasic({class1, class2})} />
106-
<div id={classNamingBasic<string>({class1, class2})} />
122+
<div id={`${classNamingBasic({class1, class2})}`} />
107123
```
108124

109125
### CSS module

src/basic.spec.tsx

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function Root({
1717
classnames
1818
}}/>
1919
<div
20-
className={classNamingBasic<string>({App__Footer})}
20+
className={`${classNamingBasic({App__Footer})}`}
2121
data-classname={`${classNamingBasic({App__Footer})}`}
2222
/>
2323
</>
@@ -114,3 +114,28 @@ it("additional type check after rename", () => {
114114
<div className="class2"/>
115115
)
116116
})
117+
118+
it("chaining", () => {
119+
const props = {className: "Cell", classnames: classNamesCheck()}
120+
121+
const {className, classnames: {
122+
Column_1, Column_2,
123+
Row_1, Row_2
124+
}} = props
125+
, cn1 = classNamingBasic(className, {Column_1})
126+
, cn2 = classNamingBasic(className, {Column_2})
127+
128+
expectRender(
129+
<div {...cn1({ Row_1 })} />,
130+
<div {...cn1({ Row_2 })} />,
131+
<div {...cn2({ Row_1 })} />,
132+
<div {...cn2({ Row_2 })} />,
133+
<div {...classNamingBasic({ Column_1 })({ Column_2 })({ Row_1 })({ Row_2 })} />
134+
).toSame(
135+
<div className="Cell Column_1 Row_1" />,
136+
<div className="Cell Column_1 Row_2" />,
137+
<div className="Cell Column_2 Row_1" />,
138+
<div className="Cell Column_2 Row_2" />,
139+
<div className="Column_1 Column_2 Row_1 Row_2" />
140+
)
141+
})

src/basic.ts

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
export type { ClassNames } from "./defs"
2-
import { dehash, wrapper } from "./core"
3-
import type { ClassNamesMap, ClassNamed, GetClassKeys } from "./defs"
2+
import { dehash, wrapper, joinWithLead } from "./core"
3+
import type { ClassNamesMap, ClassNamed, GetClassKeys, ClassValue, ReactRelated } from "./defs"
44

55
export default classNamingBasic
66

77
/**
88
* Makes `className` string from imported CSS
9-
* @example <div className={classNaming<string>({ClassName})} />
10-
* @example <div {...classNaming({ClassName})} />
9+
* @example <div className={`${classNamingBasic({ClassName})}`} />
10+
* @example <div {...classNamingBasic({ClassName})} />
11+
* @example const cn = classNamingBasic({C1})({C2}); <div {...cn({C3})({C4})} />
1112
*/
12-
function classNamingBasic<Return, ClassKeys extends string = string>(
13-
classnames: ClassNamesMap<string extends Return ? ClassKeys : GetClassKeys<Return>>
14-
): Return extends string ? string : ClassNamed
13+
function classNamingBasic<Source extends ReactRelated>(
14+
classnames: ClassNamesMap<GetClassKeys<Source>>
15+
): ClassNamingChain
1516

1617
/**
1718
* Makes `className` string from imported CSS
18-
* @example <div className={classNaming<string>({ClassName})} />
19-
* @example <div {...classNaming({ClassName})} />
19+
* @example <div className={`${classNamingBasic({ClassName})}`} />
20+
* @example <div {...classNamingBasic({ClassName})} />
21+
* @example const cn = classNamingBasic({C1})({C2}); <div {...cn({C3})({C4})} />
2022
*/
21-
function classNamingBasic<Return, ClassKeys extends string = string>(
23+
function classNamingBasic<Source extends ReactRelated>(
2224
propagatedClassName: undefined|string,
23-
classnames: ClassNamesMap<string extends Return ? ClassKeys : GetClassKeys<Return>>
24-
): Return extends string ? string : ClassNamed
25+
classnames: ClassNamesMap<GetClassKeys<Source>>
26+
): ClassNamingChain
2527

2628
function classNamingBasic(
2729
arg0: undefined|string|ClassNamesMap<string>,
@@ -30,24 +32,20 @@ function classNamingBasic(
3032
const classnames = typeof arg0 === "object" ? arg0 : arg1
3133
, className = typeof arg0 === "object" ? undefined : arg0
3234

33-
return _classNaming(classnames!, className, {})
35+
return _classNaming(classnames!, className)
3436
}
3537

36-
function _classNaming<T extends Partial<ClassNamed>>(
37-
classnames: ClassNamesMap<string>,
38-
className: undefined|string,
39-
destination: T
40-
): T & ClassNamed {
41-
42-
// TODO For propagation
43-
// $defineProperty(
44-
// classnames,
45-
// "toString",
46-
// {
47-
// value: undefined
48-
// }
49-
// )
50-
// $assign(destination, {classnames})
51-
52-
return wrapper(className, dehash(classnames), destination)
38+
function _classNaming(
39+
classes: ClassNamesMap<string>,
40+
propagate: undefined|string,
41+
) : ClassNamingChain {
42+
const className = joinWithLead(propagate, dehash(classes))
43+
, host: ClassNamingCall = classes => _classNaming(classes, className)
44+
45+
return wrapper(className, [], host)
46+
}
47+
48+
type ClassNamingChain = ClassNamingCall & {
49+
className: string
5350
}
51+
type ClassNamingCall = (classes: Record<string, ClassValue>) => ClassNamingChain

src/direct.test.ts

Lines changed: 0 additions & 58 deletions
This file was deleted.

src/versus-classnames.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ it("usage interface", () => {
2525

2626
const {classnames: {class2}} = props
2727

28-
expect(
29-
classNamingBasic({class1, class2})
30-
).toStrictEqual({
28+
expect({
29+
...classNamingBasic({class1, class2})
30+
}).toStrictEqual({
3131
//@ts-expect-error `classnames` has no possibility for type hints
3232
className: classnames_default<"whatever">("class1", "class2")
3333
})
@@ -48,3 +48,4 @@ it("css module", () => expect({
4848
)
4949
))
5050

51+
it.todo("Does `classnames` have chainable interface?")

0 commit comments

Comments
 (0)