Skip to content

Commit

Permalink
Implement chainable basic. Remove leading generics
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrii Kirmas committed Feb 24, 2021
1 parent 933a1ae commit 31e2ff3
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 98 deletions.
28 changes: 22 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,25 @@ import classNamingBasic from "react-classnaming/basic"

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

<div {...classNamingBasic("class1", {class2, class3})}>
<div className={`${classNamingBasic({class4})}`}>
<div className={classNamingBasic<string>({class5})}>
<div {...classNamingBasic("class1", {class2, class3})} />
<div className={`${classNamingBasic({class4})}`} />
```

#### Chained

```tsx
const {className, classnames: {
Column_1, Column_2,
Row_1, Row_2
}} = props
, cn1 = classNamingBasic(className, {Column_1})
, cn2 = classNamingBasic(className, {Column_2})

<div {...cn1({ Row_1 })} />,
<div {...cn1({ Row_2 })} />,
<div {...cn2({ Row_1 })} />,
<div {...cn2({ Row_2 })} />,
<div {...classNamingBasic({ Column_1 })({ Column_2 })({ Row_1 })({ Row_2 })} />
```

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

<div {...classes(true, {class1: true, class2: false})}/> // className="App hash1"
<div {...classes("class2")}/> // className="hash2"
<div {...classes(true, {class1: true, class2: false})} /> // className="App hash1"
<div {...classes("class2")} /> // className="hash2"
```

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

<div {...classNamingBasic({class1, class2})} />
<div id={classNamingBasic<string>({class1, class2})} />
<div id={`${classNamingBasic({class1, class2})}`} />
```

### CSS module
Expand Down
27 changes: 26 additions & 1 deletion src/basic.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function Root({
classnames
}}/>
<div
className={classNamingBasic<string>({App__Footer})}
className={`${classNamingBasic({App__Footer})}`}
data-classname={`${classNamingBasic({App__Footer})}`}
/>
</>
Expand Down Expand Up @@ -114,3 +114,28 @@ it("additional type check after rename", () => {
<div className="class2"/>
)
})

it("chaining", () => {
const props = {className: "Cell", classnames: classNamesCheck()}

const {className, classnames: {
Column_1, Column_2,
Row_1, Row_2
}} = props
, cn1 = classNamingBasic(className, {Column_1})
, cn2 = classNamingBasic(className, {Column_2})

expectRender(
<div {...cn1({ Row_1 })} />,
<div {...cn1({ Row_2 })} />,
<div {...cn2({ Row_1 })} />,
<div {...cn2({ Row_2 })} />,
<div {...classNamingBasic({ Column_1 })({ Column_2 })({ Row_1 })({ Row_2 })} />
).toSame(
<div className="Cell Column_1 Row_1" />,
<div className="Cell Column_1 Row_2" />,
<div className="Cell Column_2 Row_1" />,
<div className="Cell Column_2 Row_2" />,
<div className="Column_1 Column_2 Row_1 Row_2" />
)
})
58 changes: 28 additions & 30 deletions src/basic.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
export type { ClassNames } from "./defs"
import { dehash, wrapper } from "./core"
import type { ClassNamesMap, ClassNamed, GetClassKeys } from "./defs"
import { dehash, wrapper, joinWithLead } from "./core"
import type { ClassNamesMap, ClassNamed, GetClassKeys, ClassValue, ReactRelated } from "./defs"

export default classNamingBasic

/**
* Makes `className` string from imported CSS
* @example <div className={classNaming<string>({ClassName})} />
* @example <div {...classNaming({ClassName})} />
* @example <div className={`${classNamingBasic({ClassName})}`} />
* @example <div {...classNamingBasic({ClassName})} />
* @example const cn = classNamingBasic({C1})({C2}); <div {...cn({C3})({C4})} />
*/
function classNamingBasic<Return, ClassKeys extends string = string>(
classnames: ClassNamesMap<string extends Return ? ClassKeys : GetClassKeys<Return>>
): Return extends string ? string : ClassNamed
function classNamingBasic<Source extends ReactRelated>(
classnames: ClassNamesMap<GetClassKeys<Source>>
): ClassNamingChain

/**
* Makes `className` string from imported CSS
* @example <div className={classNaming<string>({ClassName})} />
* @example <div {...classNaming({ClassName})} />
* @example <div className={`${classNamingBasic({ClassName})}`} />
* @example <div {...classNamingBasic({ClassName})} />
* @example const cn = classNamingBasic({C1})({C2}); <div {...cn({C3})({C4})} />
*/
function classNamingBasic<Return, ClassKeys extends string = string>(
function classNamingBasic<Source extends ReactRelated>(
propagatedClassName: undefined|string,
classnames: ClassNamesMap<string extends Return ? ClassKeys : GetClassKeys<Return>>
): Return extends string ? string : ClassNamed
classnames: ClassNamesMap<GetClassKeys<Source>>
): ClassNamingChain

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

return _classNaming(classnames!, className, {})
return _classNaming(classnames!, className)
}

function _classNaming<T extends Partial<ClassNamed>>(
classnames: ClassNamesMap<string>,
className: undefined|string,
destination: T
): T & ClassNamed {

// TODO For propagation
// $defineProperty(
// classnames,
// "toString",
// {
// value: undefined
// }
// )
// $assign(destination, {classnames})

return wrapper(className, dehash(classnames), destination)
function _classNaming(
classes: ClassNamesMap<string>,
propagate: undefined|string,
) : ClassNamingChain {
const className = joinWithLead(propagate, dehash(classes))
, host: ClassNamingCall = classes => _classNaming(classes, className)

return wrapper(className, [], host)
}

type ClassNamingChain = ClassNamingCall & {
className: string
}
type ClassNamingCall = (classes: Record<string, ClassValue>) => ClassNamingChain
58 changes: 0 additions & 58 deletions src/direct.test.ts

This file was deleted.

7 changes: 4 additions & 3 deletions src/versus-classnames.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ it("usage interface", () => {

const {classnames: {class2}} = props

expect(
classNamingBasic({class1, class2})
).toStrictEqual({
expect({
...classNamingBasic({class1, class2})
}).toStrictEqual({
//@ts-expect-error `classnames` has no possibility for type hints
className: classnames_default<"whatever">("class1", "class2")
})
Expand All @@ -48,3 +48,4 @@ it("css module", () => expect({
)
))

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

0 comments on commit 31e2ff3

Please sign in to comment.