Skip to content

Commit

Permalink
#24 Sandbox bem query
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrii Kirmas committed Mar 10, 2021
1 parent b6d9ba2 commit 659a8a8
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 49 deletions.
229 changes: 180 additions & 49 deletions __sandbox__/bem.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { Primitive } from "react-classnaming/ts-swiss.defs"
import { CssModule } from "../src/definitions.defs"

export {}


it("tree2classes", () => {

type BemTree = {
Expand Down Expand Up @@ -54,10 +52,16 @@ type ClassNames = {
"App--dark": string
"App__Container": string
"App__Container--loading": string
"App__Container--status--loading": string
"App__Container--status--error": string
"App__Header": string
"Btn": string
"Btn--disabled": string
"Btn--info--warning": string
"Btn--info--error": string
"Btn__Icon": string
"Btn__Icon--big": string
"Footer": string
}

it("take blocks", () => {
Expand All @@ -76,13 +80,14 @@ it("take blocks", () => {

const suite: Record<Blocks<ClassNames>, Blocks<ClassNames>> = {
App: "App",
Btn: "Btn"
Btn: "Btn",
Footer: "Footer"
}

expect(suite).toBeInstanceOf(Object)
})

it("upon delimiter", () => {
describe("upon delimiter", () => {
type Strip<Str extends string, Delimiter extends string> = Str extends `${infer Lead}${Delimiter}${string}` ? Lead : Str
type StripFromObj<T, Delimiter extends string> = {[K in string & keyof T]: Strip<K,Delimiter>}[keyof T]
type GetMods<T, B extends string, E extends string|undefined> = {
Expand All @@ -91,54 +96,180 @@ it("upon delimiter", () => {
: K extends `${B}--${infer M}` ? M : never
}[keyof T]

type Bemer<ClassNames extends CssModule> = (
<
BE extends StripFromObj<ClassNames, "--">,
Block extends Strip<BE, "__">,
Element extends undefined|(BE extends `${Block}__${infer Element}` ? Element : undefined) = undefined,
Modifier extends undefined|GetMods<ClassNames, Block, Element> = undefined
>(
block: Block,
element?: Element,
modifier?: Modifier
) => `${
Block
}${
Element extends string ? ` ${Block}__${Element}` : ""
}${
Modifier extends string
? ` ${Block}${
Element extends string ? `__${Element}` : ""
}--${Modifier}`
: ""
}`
)


function beming<ClassNames extends CssModule>() {
const host: Bemer<ClassNames> = ((block, element?, modifier?) => {
const elemened = element ? `${block}__${element}` : ""
const moded = modifier ? ` ${element ? elemened : block}--${modifier}` : ""

return `${
block
}${
element ! ? " " : ""
it("args", () => {
type Bemer<ClassNames extends CssModule> = (
<
BE extends StripFromObj<ClassNames, "--">,
Block extends Strip<BE, "__">,
Element extends undefined|(BE extends `${Block}__${infer Element}` ? Element : undefined) = undefined,
Modifier extends undefined|GetMods<ClassNames, Block, Element> = undefined
>(
block: Block,
element?: Element,
modifier?: Modifier
) => `${
Block
}${
elemened
Element extends string ? ` ${Block}__${Element}` : ""
}${
moded
Modifier extends string
? ` ${Block}${
Element extends string ? `__${Element}` : ""
}--${Modifier}`
: ""
}`
}) as Bemer<ClassNames>

return host
}
)


function beming<ClassNames extends CssModule>() {
const host: Bemer<ClassNames> = ((block, element?, modifier?) => {
const elemened = element ? `${block}__${element}` : ""
const moded = modifier ? ` ${element ? elemened : block}--${modifier}` : ""

return `${
block
}${
element ! ? " " : ""
}${
elemened
}${
moded
}`
}) as Bemer<ClassNames>

return host
}


const bemer = beming<ClassNames>()
, $return = bemer("Btn", "Icon", "big")
, typed: typeof $return = "Btn Btn__Icon Btn__Icon--big"

expect($return).toBe(typed)
})

it("query", () => {
type BemQuery<ClassNames extends CssModule, delE extends string = "__", delM extends string = "--"> =
<
classes extends keyof ClassNames,
BE extends StripFromObj<ClassNames, delM>,
Block extends Strip<BE, delE>,
Q extends {
[b in Block]?: boolean | (
{
// ""?: classes extends `${b}--${infer _}`
// ? {
// [m in classes extends `${b}--${infer M}--${infer _}`
// ? M
// : classes extends `${b}--${infer M}`
// ? M
// : never
// ]?: classes extends `${b}--${m}--${infer V}`
// ? false|V
// : boolean
// }
// : never
// {
// [m in MV extends `${infer M}--${infer _}` ? M : MV]:
// MV extends `${m}--${infer V}`
// ? false|V
// : boolean
} & {
[e in BE extends `${b}${delE}${infer E}` ? E : never]?: boolean | {
[
m in classes extends `${b}${delE}${e}${delM}${infer M}${delM}${infer _}`
? M
: classes extends `${b}${delE}${e}${delM}${infer M}`
? M
: never
]?: classes extends `${b}${delE}${e}${delM}${m}${delM}${infer V}`
? false|V
: boolean

}
})
}
>(arg: Q) => {[K in
keyof Q
| {[b in keyof Q]: Q[b] extends Primitive ? never : `${b}${delE}${keyof Q[b]}`}[keyof Q]
| {
[b in keyof Q]: {[e in keyof Q[b]]: Q[b][e] extends Primitive ? never :
{[m in keyof Q[b][e]]:
Q[b][e][m] extends string
? `${b}${delE}${e}${delM}${m}${delM}${Q[b][e][m]}`
: `${b}${delE}${e}${delM}${m}`
}[keyof Q[b][e]]
}[keyof Q[b]]
}[keyof Q]
]: boolean}

const bemer = beming<ClassNames>()
, $return = bemer("Btn", "Icon", "big")
, typed: typeof $return = "Btn Btn__Icon Btn__Icon--big"
//@ts-expect-error
const q: BemQuery<ClassNames> = x => {
const $result: Record<string, undefined|boolean> = {}

expect($return).toBe(typed)
})
for (const block in x) {
const bVal = x[block]
if (!bVal) {
//@ts-expect-error
$result[block] = bVal
continue
}

$result[block] = true

for (const el in bVal) {
//@ts-expect-error
const eVal = bVal[el]
, be = `${block}__${el}`
if (!eVal) {
$result[be] = eVal
continue
}

$result[be] = true

for (const mod in eVal) {
const value: string|boolean = eVal[mod]
switch (typeof value) {
case "boolean":
$result[`${be}--${mod}`] = value
break
case "string":
$result[`${be}--${mod}--${value}`] = true
break
}
}
}
}

return $result
}
, res = q({
"App": {
"Header": false,
"Container": {
"loading": true,
"status": "error"
}
},
"Btn": {
"Icon": {
"big": true
}
},
"Footer": false
})

expect(res).toStrictEqual({
"App": true,
"App__Container": true,
"App__Container--loading": true,
"App__Container--status--error": true,
"App__Header": false,
"Btn": true,
"Btn__Icon": true,
"Btn__Icon--big": true,
"Footer": false,
} as typeof res)
})
})
2 changes: 2 additions & 0 deletions src/ts-swiss.defs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@ export type KnownKeys<T> = {
} extends {[_ in keyof T]: infer U} ? U : never;

export type OmitIndexed<T> = Pick<T, KnownKeys<T> & keyof T>

export type Primitive = undefined | null | boolean | number | string | symbol | bigint

0 comments on commit 659a8a8

Please sign in to comment.