Skip to content

Commit 075461f

Browse files
author
Andrii Kirmas
committed
Split function in submodules
1 parent 128d926 commit 075461f

File tree

6 files changed

+208
-196
lines changed

6 files changed

+208
-196
lines changed

src/basic.spec.tsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React from "react"
2+
import expectToRender from "../expect-to-render"
3+
import type { ClassNames } from "./defs"
4+
import classNamingBasic from "./basic"
5+
import classNamesCheck from "./check"
6+
7+
function Button({className, "classNames": { Btn }}: ClassNames<true, "Btn">) {
8+
return <button {...classNamingBasic(className, { Btn })}/>
9+
}
10+
11+
function Root({
12+
classNames, "classNames": { App__Item, App__Footer }
13+
}: ClassNames<"App__Item"|"App__Footer", typeof Button>) {
14+
return <>
15+
<Button {...{
16+
...classNamingBasic({ App__Item }),
17+
classNames
18+
}}/>
19+
<div
20+
className={classNamingBasic<string>({App__Footer})}
21+
data-class={`${classNamingBasic({App__Footer})}`}
22+
/>
23+
</>
24+
}
25+
26+
it("not css module", () => expectToRender(
27+
<Root classNames={classNamesCheck()}/>,
28+
[
29+
'<button class="App__Item Btn"></button>',
30+
'<div class="App__Footer" data-class="App__Footer"></div>'
31+
]
32+
))
33+
34+
it("css module", () => expectToRender(
35+
<Root classNames={{
36+
App__Footer: "footer-hash",
37+
App__Item: "item-hash",
38+
Btn: "btn-hash"
39+
}}/>,
40+
[
41+
'<button class="item-hash btn-hash"></button>',
42+
'<div class="footer-hash" data-class="footer-hash"></div>'
43+
]
44+
))
45+
46+
it("vscode renamed", () => {
47+
function Root({
48+
"classNames": {App: App__Container}
49+
}: ClassNames<"App">) {
50+
return <div {...classNamingBasic({App: App__Container})}/>
51+
}
52+
53+
expectToRender(
54+
<Root classNames={classNamesCheck()} />,
55+
'<div class="App"></div>'
56+
)
57+
})

src/basic.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/** TBD
2+
* 1. <div {...{false, undefined, null}}/> falls attributes
3+
*/
4+
export type { ClassNames } from "./defs"
5+
import type { ClassNamesMap, ClassNamed } from "./defs"
6+
7+
const {
8+
keys: $keys,
9+
defineProperty: $defineProperty,
10+
assign: $assign
11+
} = Object
12+
, classNameKey = "className" as const
13+
14+
export default classNamingBasic
15+
16+
/**
17+
* Makes `className` string from imported CSS
18+
* @example <div className={classNaming<string>({ClassName})} />
19+
* @example <div {...classNaming({ClassName})} />
20+
*/
21+
function classNamingBasic<Return, ClassKeys extends string = string>(
22+
classNames: ClassNamesMap<ClassKeys>
23+
): Return extends string ? string : ClassNamed
24+
25+
/**
26+
* Makes `className` string from imported CSS
27+
* @example <div className={classNaming<string>({ClassName})} />
28+
* @example <div {...classNaming({ClassName})} />
29+
*/
30+
function classNamingBasic<Return, ClassKeys extends string = string>(
31+
propagatedClassName: undefined|string,
32+
classNames: ClassNamesMap<ClassKeys>
33+
): Return extends string ? string : ClassNamed
34+
35+
function classNamingBasic(
36+
arg0: undefined|string|ClassNamesMap<string>,
37+
arg1: undefined|ClassNamesMap<string> = undefined
38+
): ClassNamed {
39+
const classNames = typeof arg0 === "object" ? arg0 : arg1
40+
, className = typeof arg0 === "object" ? undefined : arg0
41+
42+
return _classNaming(classNames!, className, {})
43+
}
44+
45+
function _classNaming<T extends Partial<ClassNamed>>(
46+
classNames: ClassNamesMap<string>,
47+
className: undefined|string,
48+
destination: T
49+
): T & ClassNamed {
50+
const keys = $keys(classNames)
51+
, {length} = keys
52+
53+
for (let i = length; i--;) {
54+
const key = keys[i]
55+
, value = classNames[key]
56+
57+
if (typeof value === "string")
58+
keys[i] = value
59+
}
60+
61+
const classString = `${
62+
!className
63+
? ""
64+
: `${className} `
65+
}${
66+
keys
67+
.join(" ")
68+
}`
69+
70+
// TODO For propagation
71+
// $defineProperty(
72+
// classNames,
73+
// "toString",
74+
// {
75+
// value: undefined
76+
// }
77+
// )
78+
// $assign(destination, {classNames})
79+
80+
$assign(destination, {[classNameKey]: classString})
81+
82+
$defineProperty(
83+
destination,
84+
"toString",
85+
{
86+
value: () => classString
87+
}
88+
)
89+
90+
return destination as T & ClassNamed
91+
}

src/check.spec.tsx

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React from "react"
2+
import classNamesCheck from "./check"
3+
import type { ClassNames, ClassNamesMap } from "./defs"
4+
5+
function Root(_: ClassNames<"fc1"|"fc2">) {
6+
return null
7+
}
8+
9+
it("dummy", () => {
10+
<Root classNames={classNamesCheck()}/>;
11+
12+
//@ts-expect-error Property 'fc2' is missing
13+
<Root classNames={classNamesCheck<"fc1">()}/>;
14+
<Root classNames={classNamesCheck<"fc1"|"fc2"|"etc">()}/>;
15+
16+
expect(true).toBe(true)
17+
})
18+
19+
it("check that all are used", () => {
20+
const classNames = {} as ClassNamesMap<"fc1"|"fc2"|"fc3">;
21+
//@ts-expect-error is missing the following properties
22+
<Root classNames={
23+
classNamesCheck<"">(classNames)
24+
} />;
25+
26+
// TypeScript doesn't check redundant props
27+
<Root classNames={{} as ClassNamesMap<"fc1"|"fc2">} />;
28+
<Root classNames={classNames} />;
29+
<Root classNames={classNames as ClassNames<typeof Root>["classNames"]} />;
30+
31+
//@ts-expect-error //TODO TBD redundant props
32+
<Root classNames={classNamesCheck<typeof Root>(classNames)} />;
33+
//@ts-expect-error //TODO TBD not error
34+
<Root3 classNames={classNamesCheck<typeof Root3>(classNames)} />;
35+
36+
function Root3(_: ClassNames<"fc1"|"fc2"|"fc3">) {
37+
return null
38+
}
39+
40+
expect(true).toBe(true)
41+
})

src/check.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
export type { ClassNames } from "./defs"
2+
import { EMPTY_OBJECT } from "./consts"
3+
import type { ClassNamesMap, ReactRelated } from "./defs"
4+
5+
export default classNamesCheck
6+
7+
function classNamesCheck<
8+
K extends string | ReactRelated = string,
9+
T extends ClassNamesMap<string> = never
10+
>(
11+
classNames = EMPTY_OBJECT as [T] extends [never] ? ClassNamesMap<string> : T
12+
) {
13+
14+
return classNames as ClassNamesMap<
15+
Extract<K, string>
16+
>
17+
}

src/index.spec.tsx

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

0 commit comments

Comments
 (0)