simple-merge-class-names 7.0.3 → 8.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -193
- package/dist/index.d.mts +39 -0
- package/dist/index.mjs +152 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +12 -10
- package/mergeClassNames.d.ts +0 -7
- package/mergeClassNames.js +0 -175
package/README.md
CHANGED
|
@@ -1,75 +1,84 @@
|
|
|
1
1
|
# simple-merge-class-names
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A class names merger for TypeScript, JavaScript, TSX / JSX (React).
|
|
4
4
|
|
|
5
|
-
For
|
|
5
|
+
> For production purposes there is also [https://www.npmjs.com/package/clsx](https://www.npmjs.com/package/clsx)
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
-
|
|
34
|
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
##
|
|
9
|
+
- [simple-merge-class-names](#simple-merge-class-names)
|
|
10
|
+
- [Table of Contents](#table-of-contents)
|
|
11
|
+
- [Stop Starving Gaza](#stop-starving-gaza)
|
|
12
|
+
- [Install](#install)
|
|
13
|
+
- [Install `Prettier` too](#install-prettier-too)
|
|
14
|
+
- [Exported Functions](#exported-functions)
|
|
15
|
+
- [Non-scale Usage](#non-scale-usage)
|
|
16
|
+
- [Example](#example)
|
|
17
|
+
- [Scale Usage](#scale-usage)
|
|
18
|
+
- [Create intermediary `@/mergeClassNames.js`](#create-intermediary-mergeclassnamesjs)
|
|
19
|
+
- [Project Structure](#project-structure)
|
|
20
|
+
- [Enable `@`-style Imports in Vite](#enable--style-imports-in-vite)
|
|
21
|
+
- [Example](#example-1)
|
|
22
|
+
- [During Dev, Debug Entire Project](#during-dev-debug-entire-project)
|
|
23
|
+
- [Valid Arguments](#valid-arguments)
|
|
24
|
+
- [Example](#example-2)
|
|
25
|
+
- [Invalid Arguments](#invalid-arguments)
|
|
26
|
+
- [Example](#example-3)
|
|
27
|
+
- [Developer Console Warnings](#developer-console-warnings)
|
|
28
|
+
- [Conditional Class Inclusion](#conditional-class-inclusion)
|
|
29
|
+
- [Avoid Short-circuit Syntax](#avoid-short-circuit-syntax)
|
|
30
|
+
- [Return Result](#return-result)
|
|
31
|
+
- [Example](#example-4)
|
|
32
|
+
- [Side Effect](#side-effect)
|
|
33
|
+
- [Example](#example-5)
|
|
34
|
+
- [Chaining](#chaining)
|
|
35
|
+
- [Usage of Browser Debugger](#usage-of-browser-debugger)
|
|
36
|
+
- [Testing Source Code](#testing-source-code)
|
|
37
|
+
- [Run Once](#run-once)
|
|
38
|
+
- [Run Watch Mode](#run-watch-mode)
|
|
39
|
+
- [License](#license)
|
|
40
|
+
|
|
41
|
+
## Stop Starving Gaza
|
|
42
|
+
|
|
43
|
+
- [Donate Direct Aid to Gazan Families](https://gazafunds.com/)
|
|
44
|
+
|
|
45
|
+
- [Call US Congress and Demand Immediate Opening of ALL Gaza Border Crossings](https://act.uscpr.org/a/letaidin)
|
|
46
|
+
|
|
47
|
+
- [Boycott Brands Supporting Gaza Holocaust](https://www.uplift.ie/bds/)
|
|
48
|
+
|
|
49
|
+
- [Legal Action](https://www.hindrajabfoundation.org/perpetrators)
|
|
50
|
+
|
|
51
|
+
## Install
|
|
42
52
|
|
|
43
53
|
```bash
|
|
44
54
|
pnpm add simple-merge-class-names
|
|
45
|
-
```
|
|
46
55
|
|
|
47
|
-
|
|
56
|
+
# or
|
|
48
57
|
yarn add simple-merge-class-names
|
|
49
|
-
```
|
|
50
58
|
|
|
51
|
-
|
|
59
|
+
# or
|
|
52
60
|
npm install simple-merge-class-names
|
|
53
61
|
```
|
|
54
62
|
|
|
55
|
-
###
|
|
63
|
+
### Install `Prettier` too
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
[https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode)
|
|
58
66
|
|
|
59
|
-
|
|
67
|
+
It will nicely format and split your classes across new lines, and alleviate wrist strain because you will use single quotes (single key) instead of double (2 keys).
|
|
60
68
|
|
|
61
|
-
|
|
69
|
+
## Exported Functions
|
|
62
70
|
|
|
63
|
-
|
|
71
|
+
| | `console.warn`s | Invokes JS `debugger;` statement on invalid arguments, which pauses execution when Debugger is attached |
|
|
72
|
+
| ------------------------- | --------------- | ------------------------------------------------------------------------------------------------------- |
|
|
73
|
+
| `mergeClassNames` | ✅ | ❌ |
|
|
74
|
+
| `mergeClassNamesDebugger` | ✅ | ✅ |
|
|
64
75
|
|
|
65
|
-
|
|
66
|
-
| ------------------------- | ----------------------- | ------------------ |
|
|
67
|
-
| `mergeClassNames` | ✅ | ❌ |
|
|
68
|
-
| `mergeClassNamesDebugger` | ✅ | ✅ |
|
|
76
|
+
## Non-scale Usage
|
|
69
77
|
|
|
70
|
-
|
|
78
|
+
### Example
|
|
71
79
|
|
|
72
80
|
```jsx
|
|
81
|
+
// src/App.jsx
|
|
73
82
|
import { mergeClassNames } from "simple-merge-class-names";
|
|
74
83
|
|
|
75
84
|
const Component = ({ condition }) => {
|
|
@@ -80,7 +89,7 @@ const Component = ({ condition }) => {
|
|
|
80
89
|
condition ? "min-h-dvh" : false,
|
|
81
90
|
"grid",
|
|
82
91
|
"grid-rows-[auto_1fr_auto]",
|
|
83
|
-
"outline"
|
|
92
|
+
"outline",
|
|
84
93
|
)}
|
|
85
94
|
>
|
|
86
95
|
Hello, world!
|
|
@@ -89,32 +98,106 @@ const Component = ({ condition }) => {
|
|
|
89
98
|
};
|
|
90
99
|
```
|
|
91
100
|
|
|
92
|
-
|
|
101
|
+
## Scale Usage
|
|
102
|
+
|
|
103
|
+

|
|
93
104
|
|
|
94
|
-
|
|
95
|
-
export declare const mergeClassNames: (
|
|
96
|
-
...args: (string | false)[]
|
|
97
|
-
) => string | false;
|
|
105
|
+
### Create intermediary `@/mergeClassNames.js`
|
|
98
106
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
#### Project Structure
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
my-react-app/
|
|
111
|
+
├─ src/
|
|
112
|
+
│ ├─ components/
|
|
113
|
+
│ ├─ mergeClassNames.js // <- create it here
|
|
114
|
+
│ ├─ App.jsx
|
|
115
|
+
│ └─ index.js
|
|
116
|
+
├─ package.json
|
|
117
|
+
├─ vite.config.js
|
|
118
|
+
└─ README.md
|
|
102
119
|
```
|
|
103
120
|
|
|
104
|
-
|
|
121
|
+
#### Enable `@`-style Imports in Vite
|
|
105
122
|
|
|
106
|
-
|
|
123
|
+
`@` points to `my-react-app/src`
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
// vite.config.js
|
|
127
|
+
|
|
128
|
+
import { defineConfig } from "vite";
|
|
129
|
+
import react from "@vitejs/plugin-react";
|
|
130
|
+
import tailwindcss from "@tailwindcss/vite";
|
|
131
|
+
import path from "path";
|
|
132
|
+
|
|
133
|
+
// https://vitejs.dev/config/
|
|
134
|
+
export default defineConfig({
|
|
135
|
+
plugins: [react(), tailwindcss()],
|
|
136
|
+
|
|
137
|
+
resolve: {
|
|
138
|
+
alias: {
|
|
139
|
+
"@": path.resolve(__dirname, "src"), // @ now points to "my-react-app/src"
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
// src/mergeClassNames.js
|
|
147
|
+
|
|
148
|
+
export { mergeClassNames } from "simple-merge-class-names";
|
|
149
|
+
|
|
150
|
+
// export { mergeClassNames as mergeClassNamesDebugger } from "simple-merge-class-names";
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
And toggle-comment between the first and second lines as needed.
|
|
154
|
+
|
|
155
|
+
### Example
|
|
107
156
|
|
|
108
|
-
|
|
157
|
+
#### During Dev, Debug Entire Project
|
|
109
158
|
|
|
110
|
-
|
|
159
|
+
```js
|
|
160
|
+
// @/mergeClassNames.js
|
|
161
|
+
|
|
162
|
+
// export { mergeClassNames } from "simple-merge-class-names";
|
|
163
|
+
|
|
164
|
+
export { mergeClassNames as mergeClassNamesDebugger } from "simple-merge-class-names";
|
|
165
|
+
```
|
|
111
166
|
|
|
112
|
-
|
|
167
|
+
```jsx
|
|
168
|
+
// @/App.jsx
|
|
113
169
|
|
|
114
|
-
|
|
170
|
+
import { mergeClassNames } from "@/mergeClassNames.js";
|
|
171
|
+
|
|
172
|
+
const Component = ({ condition }) => {
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
className={mergeClassNames(
|
|
176
|
+
"app",
|
|
177
|
+
condition ? "min-h-dvh" : false,
|
|
178
|
+
"grid",
|
|
179
|
+
"grid-rows-[auto_1fr_auto]",
|
|
180
|
+
"outline",
|
|
181
|
+
)}
|
|
182
|
+
>
|
|
183
|
+
Hello, world!
|
|
184
|
+
</div>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Valid Arguments
|
|
190
|
+
|
|
191
|
+
Only 2:
|
|
192
|
+
|
|
193
|
+
1. A valid string (not empty, not fully whitespace)
|
|
194
|
+
2. Value `false`
|
|
195
|
+
|
|
196
|
+
### Example
|
|
115
197
|
|
|
116
198
|
```js
|
|
117
199
|
mergeClassNames(
|
|
200
|
+
condition ? "daisy-btn-active" : false
|
|
118
201
|
"mx-auto",
|
|
119
202
|
"min-dvh ",
|
|
120
203
|
" flex",
|
|
@@ -123,101 +206,119 @@ mergeClassNames(
|
|
|
123
206
|
`
|
|
124
207
|
gap-y-4
|
|
125
208
|
`,
|
|
126
|
-
false,
|
|
127
|
-
condition ? "daisy-btn-active" : false
|
|
128
209
|
);
|
|
129
210
|
```
|
|
130
211
|
|
|
131
|
-
|
|
212
|
+
## Invalid Arguments
|
|
132
213
|
|
|
133
|
-
|
|
214
|
+
Anything that is not a valid argument, this includes:
|
|
134
215
|
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
-
|
|
142
|
-
-
|
|
143
|
-
-
|
|
144
|
-
-
|
|
145
|
-
-
|
|
146
|
-
-
|
|
147
|
-
-
|
|
216
|
+
- Invalid strings:
|
|
217
|
+
- Empty strings: _(`""`)_
|
|
218
|
+
- Fully whitespace strings: any consecutive combination of the following:
|
|
219
|
+
- new lines,
|
|
220
|
+
- spaces,
|
|
221
|
+
- tabs
|
|
222
|
+
- _(e.g. `" "`, `"\n "`, `" \t \n "`, etc.)_
|
|
223
|
+
- `true`
|
|
224
|
+
- `undefined`
|
|
225
|
+
- `null`
|
|
226
|
+
- Objects
|
|
227
|
+
- Numbers
|
|
228
|
+
- Big Int
|
|
229
|
+
- Symbols
|
|
148
230
|
|
|
149
|
-
|
|
150
|
-
// Example: These arguments will be **ignored**, and a console.warn will be printed
|
|
231
|
+
_All of above will be **ignored**, and cause a `console.warn` to be printed_
|
|
151
232
|
|
|
233
|
+
### Example
|
|
234
|
+
|
|
235
|
+
```js
|
|
152
236
|
const someVariable = "";
|
|
153
237
|
|
|
154
238
|
mergeClassNames(
|
|
155
|
-
someVariable,
|
|
156
|
-
" ",
|
|
157
|
-
"\n ",
|
|
158
|
-
" \t \n ",
|
|
159
|
-
`
|
|
239
|
+
someVariable,
|
|
240
|
+
" ",
|
|
241
|
+
"\n ",
|
|
242
|
+
" \t \n ",
|
|
243
|
+
`
|
|
160
244
|
\n
|
|
161
245
|
`,
|
|
162
|
-
true,
|
|
163
|
-
undefined,
|
|
164
|
-
null,
|
|
246
|
+
true,
|
|
247
|
+
undefined,
|
|
248
|
+
null,
|
|
165
249
|
{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
email: "email@example.com",
|
|
250
|
+
name: "name",
|
|
251
|
+
email: "name@example.com",
|
|
169
252
|
},
|
|
170
|
-
123,
|
|
171
|
-
123.45
|
|
253
|
+
123,
|
|
254
|
+
123.45,
|
|
172
255
|
);
|
|
173
256
|
```
|
|
174
257
|
|
|
258
|
+
#### Developer Console Warnings
|
|
259
|
+
|
|
175
260
|

|
|
176
261
|
|
|
177
|
-
|
|
262
|
+
## Conditional Class Inclusion
|
|
263
|
+
|
|
264
|
+
```jsx
|
|
265
|
+
mergeClassNames(condition ? "min-h-dvh" : false);
|
|
178
266
|
|
|
179
|
-
|
|
267
|
+
// or if you want preciseness.
|
|
268
|
+
mergeClassNames(condition === true ? "min-h-dvh" : false);
|
|
269
|
+
```
|
|
180
270
|
|
|
181
|
-
###
|
|
271
|
+
### Avoid Short-circuit Syntax
|
|
182
272
|
|
|
183
|
-
|
|
273
|
+
Avoid the syntax (`condition && "class-name"`) because it can produce falsy values (e.g. `0`, `""`, `undefined`, `null`) which will be ignored and warned about.
|
|
184
274
|
|
|
185
|
-
|
|
186
|
-
- _or_ `condition === true ? "class-name" : false` if you want to be specific.
|
|
275
|
+
## Return Result
|
|
187
276
|
|
|
188
|
-
|
|
277
|
+
String of all merged valid classes. Invalid arguments are ignored and warned about.
|
|
189
278
|
|
|
190
|
-
|
|
279
|
+
### Example
|
|
191
280
|
|
|
192
281
|
```jsx
|
|
193
282
|
import { mergeClassNames } from "simple-merge-class-names";
|
|
194
283
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
284
|
+
// "app min-h-dvh grid grid-rows-[auto_1fr_auto] outline"
|
|
285
|
+
mergeClassNames(
|
|
286
|
+
" app ",
|
|
287
|
+
undefined,
|
|
288
|
+
[" test "],
|
|
289
|
+
{ key: "value" },
|
|
290
|
+
"",
|
|
291
|
+
"min-h-dvh",
|
|
292
|
+
"grid ",
|
|
293
|
+
true,
|
|
294
|
+
null,
|
|
295
|
+
"grid-rows-[auto_1fr_auto]",
|
|
296
|
+
"outline",
|
|
297
|
+
" ",
|
|
298
|
+
);
|
|
210
299
|
```
|
|
211
300
|
|
|
212
|
-
|
|
301
|
+
## Side Effect
|
|
213
302
|
|
|
214
|
-
|
|
303
|
+
`console.warn`s if arguments contain invalid arguments.
|
|
215
304
|
|
|
216
|
-
|
|
305
|
+
### Example
|
|
217
306
|
|
|
218
|
-
|
|
307
|
+
```
|
|
308
|
+
Ignored invalid argument: >undefined< (undefined)
|
|
309
|
+
Ignored invalid argument: > test < (object)
|
|
310
|
+
Ignored invalid argument: >[object Object]< (object)
|
|
311
|
+
Ignored empty string: ""
|
|
312
|
+
Ignored invalid argument: >true< (boolean)
|
|
313
|
+
Ignored invalid argument: >null< (object)
|
|
314
|
+
Ignored whitespace string:
|
|
315
|
+
```
|
|
219
316
|
|
|
220
|
-
|
|
317
|
+
> _It can be empy_
|
|
318
|
+
|
|
319
|
+
1. **Valid `string`, never whitespace, always length >= 1**
|
|
320
|
+
|
|
321
|
+
## Chaining
|
|
221
322
|
|
|
222
323
|
Because of safe return types you can chain calls safely without worrying about warnings or arguments being ignored.
|
|
223
324
|
|
|
@@ -229,85 +330,45 @@ mergeClassNames(condition ? "disabled" : mergeClassNames(...) )
|
|
|
229
330
|
|
|
230
331
|
## Usage of Browser Debugger
|
|
231
332
|
|
|
232
|
-
|
|
333
|
+
1. Use **`import {mergeClassNamesDebugger as mergeClassNames}`** to debug the entire file.
|
|
334
|
+
|
|
335
|
+
```jsx
|
|
336
|
+
import { mergeClassNamesDebugger as mergeClassNames } from "simple-merge-class-names";
|
|
233
337
|
|
|
234
|
-
|
|
338
|
+
const Component = ({ condition }) => {
|
|
339
|
+
return (
|
|
340
|
+
<div
|
|
341
|
+
className={mergeClassNames(
|
|
342
|
+
"app",
|
|
343
|
+
condition ? "min-h-dvh" : false,
|
|
344
|
+
"grid",
|
|
345
|
+
"grid-rows-[auto_1fr_auto]",
|
|
346
|
+
"outline",
|
|
347
|
+
)}
|
|
348
|
+
>
|
|
349
|
+
Hello, world!
|
|
350
|
+
</div>
|
|
351
|
+
);
|
|
352
|
+
};
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
2. Enable Debugger
|
|
235
356
|
- _For chromium-based browsers it's On by default and you don't need to do anything AFAIK._
|
|
236
357
|
- _For Firefox:_ Open **_Developer Tools_:**
|
|
237
358
|
- _Make Sure_ **_Debugger_** _(tab)_ -> **`Pause on debugger statement`** is ticked.
|
|
238
359
|
- Keep Dev Tools open.
|
|
239
|
-

|
|
240
|
-
|
|
241
|
-
- Use **`import {mergeClassNamesDebugger as mergeClassNames}`** to debug the entire file, and keep the rest intact.
|
|
242
|
-
|
|
243
|
-
```jsx
|
|
244
|
-
import { mergeClassNamesDebugger as mergeClassNames } from "simple-merge-class-names";
|
|
245
|
-
|
|
246
|
-
const Component = ({ condition }) => {
|
|
247
|
-
return (
|
|
248
|
-
<div
|
|
249
|
-
className={mergeClassNames(
|
|
250
|
-
"app",
|
|
251
|
-
condition ? "min-h-dvh" : false,
|
|
252
|
-
"grid",
|
|
253
|
-
"grid-rows-[auto_1fr_auto]",
|
|
254
|
-
"outline"
|
|
255
|
-
)}
|
|
256
|
-
>
|
|
257
|
-
Hello, world!
|
|
258
|
-
</div>
|
|
259
|
-
);
|
|
260
|
-
};
|
|
261
|
-
```
|
|
262
|
-
|
|
263
|
-
- or call `mergeClassNamesDebugger` directly.
|
|
264
|
-
|
|
265
|
-
```jsx
|
|
266
|
-
import { mergeClassNamesDebugger } from "simple-merge-class-names";
|
|
267
|
-
|
|
268
|
-
const Component = ({ condition }) => {
|
|
269
|
-
return (
|
|
270
|
-
<div
|
|
271
|
-
className={mergeClassNamesDebugger(
|
|
272
|
-
"app",
|
|
273
|
-
condition ? "min-h-dvh" : false,
|
|
274
|
-
"grid",
|
|
275
|
-
"grid-rows-[auto_1fr_auto]",
|
|
276
|
-
"outline"
|
|
277
|
-
)}
|
|
278
|
-
>
|
|
279
|
-
Hello, world!
|
|
280
|
-
</div>
|
|
281
|
-
);
|
|
282
|
-
};
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
- Refresh the page, the debugger should connect:
|
|
286
|
-
|
|
287
|
-
- Navigate to the **_Call stack_**
|
|
288
|
-
- Click the function/component right before _`mergeClassNamesDebugger`_
|
|
289
|
-
|
|
290
|
-

|
|
291
|
-
|
|
292
|
-
- Hover over the arguments, one or several should be invalid:
|
|
293
|
-

|
|
294
|
-
|
|
295
|
-
## VSCode Workflow To Minimize Typing Strain
|
|
296
|
-
|
|
297
|
-
Use single quotes around class names, and activate `Prettier` which will neatly format and arrange the classes.
|
|
298
360
|
|
|
299
|
-
-
|
|
300
|
-
- Enable `Editor: Word Wrap`:
|
|
361
|
+

|
|
301
362
|
|
|
302
|
-
|
|
303
|
-
-
|
|
363
|
+
3. Refresh the page, the debugger should connect:
|
|
364
|
+
- Navigate to the **_Call stack_**
|
|
365
|
+
- Click the function/component right before _`mergeClassNamesDebugger`_
|
|
304
366
|
|
|
305
|
-
|
|
306
|
-
- Save the file
|
|
367
|
+

|
|
307
368
|
|
|
308
|
-
|
|
369
|
+
4. Hover over the arguments, one or several should be invalid:
|
|
309
370
|
|
|
310
|
-

|
|
311
372
|
|
|
312
373
|
## Testing Source Code
|
|
313
374
|
|
|
@@ -318,6 +379,7 @@ This project uses `Vitest` as the test runner for fast and modern testing.
|
|
|
318
379
|
```bash
|
|
319
380
|
git clone https://github.com/new-AF/simple-merge-class-names
|
|
320
381
|
cd simple-merge-class-names
|
|
382
|
+
pnpm install
|
|
321
383
|
pnpm test
|
|
322
384
|
```
|
|
323
385
|
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mergeClassNames - A class names merger for TypeScript, JavaScript, TSX / JSX (React).
|
|
3
|
+
*
|
|
4
|
+
* @license AGPL-3.0
|
|
5
|
+
* Copyright (C) 2026 Abdullah Fatota
|
|
6
|
+
*
|
|
7
|
+
*
|
|
8
|
+
* This program is free software: you can redistribute it and/or modify
|
|
9
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
10
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
11
|
+
* (at your option) any later version.
|
|
12
|
+
*
|
|
13
|
+
* This program is distributed in the hope that it will be useful,
|
|
14
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16
|
+
* GNU Affero General Public License for more details.
|
|
17
|
+
*
|
|
18
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
19
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
Only Valid Arguments:
|
|
23
|
+
1) valid strings: non-empty, non-whitespace
|
|
24
|
+
2) value `false`
|
|
25
|
+
|
|
26
|
+
Invalid arguments: anything else e.g.
|
|
27
|
+
- empty strings
|
|
28
|
+
- whitespace strings
|
|
29
|
+
- arrays
|
|
30
|
+
- numbers
|
|
31
|
+
- objects
|
|
32
|
+
- value true
|
|
33
|
+
- etc.
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
declare const mergeClassNames: (...input: (string | false)[]) => string;
|
|
37
|
+
declare const mergeClassNamesDebugger: (...input: (string | false)[]) => string;
|
|
38
|
+
|
|
39
|
+
export { mergeClassNames, mergeClassNamesDebugger };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// src/utils.ts
|
|
2
|
+
var classify = (value) => {
|
|
3
|
+
if (value === false) {
|
|
4
|
+
return {
|
|
5
|
+
status: "ignore",
|
|
6
|
+
value
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
if (typeof value !== "string") {
|
|
10
|
+
return {
|
|
11
|
+
status: "invalid",
|
|
12
|
+
value,
|
|
13
|
+
reason: 0 /* NotAString */
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
if (value === "") {
|
|
17
|
+
return {
|
|
18
|
+
status: "invalid",
|
|
19
|
+
value,
|
|
20
|
+
reason: 1 /* EmptyString */
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
const trimmed = value.trim();
|
|
24
|
+
if (trimmed === "") {
|
|
25
|
+
return {
|
|
26
|
+
status: "invalid",
|
|
27
|
+
value,
|
|
28
|
+
reason: 2 /* Whitespace */
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
status: "class-name",
|
|
33
|
+
value: trimmed
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
var getClassNames = (values) => {
|
|
37
|
+
return values.filter((obj) => obj.status === "class-name");
|
|
38
|
+
};
|
|
39
|
+
var getInvalid = (values) => {
|
|
40
|
+
return values.filter((obj) => obj.status === "invalid");
|
|
41
|
+
};
|
|
42
|
+
var warningMessage = ({
|
|
43
|
+
value,
|
|
44
|
+
reason
|
|
45
|
+
}) => {
|
|
46
|
+
if (reason === 0 /* NotAString */) {
|
|
47
|
+
if (value === null || value === void 0) {
|
|
48
|
+
return `Ignored non-string argument: ${value}`;
|
|
49
|
+
}
|
|
50
|
+
if (value === true) {
|
|
51
|
+
return `Ignored non-string argument: ${value} (boolean)`;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(value)) {
|
|
54
|
+
const sub = value.slice(0, 3);
|
|
55
|
+
const string = JSON.stringify(sub).slice(1, -1);
|
|
56
|
+
const ellipsisPart = value.length > sub.length ? ", ..." : "";
|
|
57
|
+
return `Ignored non-string argument: array ([${string}${ellipsisPart}])`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (reason === 1 /* EmptyString */) {
|
|
61
|
+
return `Ignored empty string argument.`;
|
|
62
|
+
}
|
|
63
|
+
if (reason === 2 /* Whitespace */) {
|
|
64
|
+
return `Ignored whitespace string argument: "${value}"`;
|
|
65
|
+
}
|
|
66
|
+
return `Ignored non-string argument: ${value} (${typeof value})`;
|
|
67
|
+
};
|
|
68
|
+
var warn = (invalid, functionName) => {
|
|
69
|
+
const warning = warningMessage(invalid);
|
|
70
|
+
console.warn(`[${functionName}]`, warning);
|
|
71
|
+
};
|
|
72
|
+
var activateDebugger = (_invalid) => {
|
|
73
|
+
debugger;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/mergeClassNames.ts
|
|
77
|
+
var mergeClassNamesCore = (values, onClassifiedInvalid) => {
|
|
78
|
+
const classified = values.map(classify);
|
|
79
|
+
if (onClassifiedInvalid) {
|
|
80
|
+
getInvalid(classified).forEach(onClassifiedInvalid);
|
|
81
|
+
}
|
|
82
|
+
const classNames = getClassNames(classified).map(
|
|
83
|
+
({ value }) => value
|
|
84
|
+
);
|
|
85
|
+
const finalClassName = classNames.join(" ");
|
|
86
|
+
return finalClassName;
|
|
87
|
+
};
|
|
88
|
+
var createCustomMergeClassNames = (options) => {
|
|
89
|
+
const invalidHandlers = [];
|
|
90
|
+
const functionName = options.name ?? "Custom mergeClassNames";
|
|
91
|
+
if (options["console-warn-invalid-and-whitespace-arguments"]) {
|
|
92
|
+
invalidHandlers.push((arg) => warn(arg, functionName));
|
|
93
|
+
}
|
|
94
|
+
if (options["activate-debugger-on-invalid-arguments"]) {
|
|
95
|
+
invalidHandlers.push(activateDebugger);
|
|
96
|
+
}
|
|
97
|
+
const combinedInvalidHandler = (value) => {
|
|
98
|
+
invalidHandlers.forEach((func) => func(value));
|
|
99
|
+
};
|
|
100
|
+
return (...input) => mergeClassNamesCore(
|
|
101
|
+
input,
|
|
102
|
+
invalidHandlers.length > 0 ? combinedInvalidHandler : void 0
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
var mergeClassNames = createCustomMergeClassNames({
|
|
106
|
+
"console-warn-invalid-and-whitespace-arguments": true,
|
|
107
|
+
"activate-debugger-on-invalid-arguments": false,
|
|
108
|
+
name: "mergeClassNames"
|
|
109
|
+
});
|
|
110
|
+
var mergeClassNamesDebugger = createCustomMergeClassNames({
|
|
111
|
+
"console-warn-invalid-and-whitespace-arguments": true,
|
|
112
|
+
"activate-debugger-on-invalid-arguments": true,
|
|
113
|
+
name: "mergeClassNamesDebugger"
|
|
114
|
+
});
|
|
115
|
+
/**
|
|
116
|
+
* mergeClassNames - A class names merger for TypeScript, JavaScript, TSX / JSX (React).
|
|
117
|
+
*
|
|
118
|
+
* @license AGPL-3.0
|
|
119
|
+
* Copyright (C) 2026 Abdullah Fatota
|
|
120
|
+
*
|
|
121
|
+
*
|
|
122
|
+
* This program is free software: you can redistribute it and/or modify
|
|
123
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
124
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
125
|
+
* (at your option) any later version.
|
|
126
|
+
*
|
|
127
|
+
* This program is distributed in the hope that it will be useful,
|
|
128
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
129
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
130
|
+
* GNU Affero General Public License for more details.
|
|
131
|
+
*
|
|
132
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
133
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
Only Valid Arguments:
|
|
137
|
+
1) valid strings: non-empty, non-whitespace
|
|
138
|
+
2) value `false`
|
|
139
|
+
|
|
140
|
+
Invalid arguments: anything else e.g.
|
|
141
|
+
- empty strings
|
|
142
|
+
- whitespace strings
|
|
143
|
+
- arrays
|
|
144
|
+
- numbers
|
|
145
|
+
- objects
|
|
146
|
+
- value true
|
|
147
|
+
- etc.
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
export { mergeClassNames, mergeClassNamesDebugger };
|
|
151
|
+
//# sourceMappingURL=index.mjs.map
|
|
152
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils.ts","../src/mergeClassNames.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["import {\r\n Classified,\r\n ClassifiedInvalid,\r\n ClassifiedInvalidReason,\r\n ClassifiedClassName,\r\n} from \"./types\";\r\n\r\n// classifies input arguments\r\nexport const classify = (value: string | false): Classified => {\r\n // FP pattern of mapping values with extra information.\r\n // The core computes data.\r\n // because TS types disappear in JS runtime\r\n\r\n // valid but ignored\r\n if (value === false) {\r\n return {\r\n status: \"ignore\",\r\n value,\r\n };\r\n }\r\n\r\n // invalid\r\n if (typeof value !== \"string\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.NotAString,\r\n };\r\n }\r\n\r\n // it's a string\r\n\r\n // invalid.\r\n if (value === \"\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.EmptyString,\r\n };\r\n }\r\n\r\n const trimmed = value.trim();\r\n\r\n // invalid\r\n if (trimmed === \"\") {\r\n return {\r\n status: \"invalid\",\r\n value,\r\n reason: ClassifiedInvalidReason.Whitespace,\r\n };\r\n }\r\n\r\n // valid\r\n return {\r\n status: \"class-name\",\r\n value: trimmed,\r\n };\r\n};\r\n\r\n// get only valid objects\r\nexport const getClassNames = (values: Classified[]): ClassifiedClassName[] => {\r\n return values.filter((obj) => obj.status === \"class-name\");\r\n};\r\n\r\n// get only valid objects\r\nexport const getInvalid = (values: Classified[]): ClassifiedInvalid[] => {\r\n return values.filter((obj) => obj.status === \"invalid\");\r\n};\r\n\r\nexport const warningMessage = ({\r\n value,\r\n reason,\r\n}: ClassifiedInvalid): string => {\r\n if (reason === ClassifiedInvalidReason.NotAString) {\r\n // null, undefined\r\n if (value === null || value === undefined) {\r\n return `Ignored non-string argument: ${value}`;\r\n }\r\n\r\n // true (because false was filtered out)\r\n if (value === true) {\r\n return `Ignored non-string argument: ${value} (boolean)`;\r\n }\r\n\r\n // array\r\n if (Array.isArray(value)) {\r\n const sub = value.slice(0, 3);\r\n const string = JSON.stringify(sub).slice(1, -1);\r\n const ellipsisPart = value.length > sub.length ? \", ...\" : \"\";\r\n\r\n return `Ignored non-string argument: array ([${string}${ellipsisPart}])`;\r\n }\r\n }\r\n\r\n // empty string\r\n if (reason === ClassifiedInvalidReason.EmptyString) {\r\n return `Ignored empty string argument.`;\r\n }\r\n\r\n // whitespace\r\n if (reason === ClassifiedInvalidReason.Whitespace) {\r\n return `Ignored whitespace string argument: \"${value}\"`;\r\n }\r\n\r\n // object, symbol, etc.\r\n return `Ignored non-string argument: ${value} (${typeof value})`;\r\n};\r\n\r\n// console.warn\r\nexport const warn = (invalid: ClassifiedInvalid, functionName: string) => {\r\n const warning = warningMessage(invalid);\r\n\r\n console.warn(`[${functionName}]`, warning);\r\n};\r\n\r\n// activates debugger\r\nexport const activateDebugger = (_invalid: ClassifiedInvalid) => {\r\n debugger;\r\n};\r\n","/**\r\n * mergeClassNames - A class names merger for TypeScript, JavaScript, TSX / JSX (React).\r\n *\r\n * @license AGPL-3.0\r\n * Copyright (C) 2026 Abdullah Fatota\r\n *\r\n *\r\n * This program is free software: you can redistribute it and/or modify\r\n * it under the terms of the GNU Affero General Public License as published by\r\n * the Free Software Foundation, either version 3 of the License, or\r\n * (at your option) any later version.\r\n *\r\n * This program is distributed in the hope that it will be useful,\r\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n * GNU Affero General Public License for more details.\r\n *\r\n * You should have received a copy of the GNU Affero General Public License\r\n * along with this program. If not, see <https://www.gnu.org/licenses/>.\r\n \r\n\r\nOnly Valid Arguments:\r\n 1) valid strings: non-empty, non-whitespace\r\n 2) value `false`\r\n\r\nInvalid arguments: anything else e.g.\r\n - empty strings\r\n - whitespace strings\r\n - arrays\r\n - numbers\r\n - objects\r\n - value true\r\n - etc.\r\n*/\r\n\r\nimport {\r\n ClassifiedInvalidFunction,\r\n ClassifiedInvalid,\r\n CustomOptions,\r\n} from \"./types\";\r\n\r\nimport {\r\n classify,\r\n getInvalid,\r\n warn,\r\n activateDebugger,\r\n getClassNames,\r\n} from \"./utils\";\r\n\r\n// joins valid strings into final className\r\nconst mergeClassNamesCore = (\r\n values: (string | false)[],\r\n onClassifiedInvalid?: ClassifiedInvalidFunction,\r\n) => {\r\n // classify arguments\r\n const classified = values.map(classify);\r\n\r\n // optional call invalid arguments handlers: warn and/or activate debugger\r\n if (onClassifiedInvalid) {\r\n getInvalid(classified).forEach(onClassifiedInvalid);\r\n }\r\n\r\n // valid strings only\r\n const classNames: string[] = getClassNames(classified).map(\r\n ({ value }) => value,\r\n );\r\n\r\n const finalClassName = classNames.join(\" \");\r\n return finalClassName;\r\n};\r\n\r\n// creates custom mergeClassNames e.g. warn = false, activate debugger = true\r\nexport const createCustomMergeClassNames = (options: CustomOptions) => {\r\n const invalidHandlers: ClassifiedInvalidFunction[] = [];\r\n\r\n // will be logged in console\r\n const functionName = options.name ?? \"Custom mergeClassNames\";\r\n\r\n if (options[\"console-warn-invalid-and-whitespace-arguments\"]) {\r\n invalidHandlers.push((arg) => warn(arg, functionName));\r\n }\r\n\r\n if (options[\"activate-debugger-on-invalid-arguments\"]) {\r\n invalidHandlers.push(activateDebugger);\r\n }\r\n\r\n const combinedInvalidHandler = (value: ClassifiedInvalid) => {\r\n invalidHandlers.forEach((func) => func(value));\r\n };\r\n\r\n // construct the mergeClassNames function\r\n return (...input: (string | false)[]) =>\r\n mergeClassNamesCore(\r\n input,\r\n invalidHandlers.length > 0 ? combinedInvalidHandler : undefined,\r\n );\r\n};\r\n\r\nexport const mergeClassNames = createCustomMergeClassNames({\r\n \"console-warn-invalid-and-whitespace-arguments\": true,\r\n \"activate-debugger-on-invalid-arguments\": false,\r\n name: \"mergeClassNames\",\r\n});\r\n\r\nexport const mergeClassNamesDebugger = createCustomMergeClassNames({\r\n \"console-warn-invalid-and-whitespace-arguments\": true,\r\n \"activate-debugger-on-invalid-arguments\": true,\r\n name: \"mergeClassNamesDebugger\",\r\n});\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "simple-merge-class-names",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "A
|
|
3
|
+
"version": "8.1.0",
|
|
4
|
+
"description": " A class names merger for TypeScript, JavaScript, TSX, and JSX (React)",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
|
-
"import": "./
|
|
8
|
-
"types": "./
|
|
7
|
+
"import": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.mts"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
+
"main": "./dist/index.mjs",
|
|
12
|
+
"types": "./dist/index.d.mts",
|
|
11
13
|
"files": [
|
|
12
|
-
"
|
|
13
|
-
"mergeClassNames.d.ts",
|
|
14
|
+
"dist",
|
|
14
15
|
"LICENSE.txt",
|
|
15
16
|
"README.md"
|
|
16
17
|
],
|
|
17
18
|
"sideEffects": false,
|
|
18
19
|
"keywords": [
|
|
19
|
-
"class-merger",
|
|
20
20
|
"class-names-merger",
|
|
21
|
+
"classes-merger",
|
|
21
22
|
"jsx",
|
|
22
23
|
"merge",
|
|
23
24
|
"mergeClassNames",
|
|
@@ -25,6 +26,7 @@
|
|
|
25
26
|
"merger",
|
|
26
27
|
"react",
|
|
27
28
|
"tailwindcss",
|
|
29
|
+
"tsx",
|
|
28
30
|
"utility"
|
|
29
31
|
],
|
|
30
32
|
"author": "Abdullah Fatota",
|
|
@@ -33,14 +35,14 @@
|
|
|
33
35
|
"type": "git",
|
|
34
36
|
"url": "git+https://github.com/new-AF/simple-merge-class-names.git"
|
|
35
37
|
},
|
|
36
|
-
"bugs": {
|
|
37
|
-
"url": "https://github.com/new-AF/simple-merge-class-names/issues"
|
|
38
|
-
},
|
|
39
38
|
"homepage": "https://github.com/new-AF/simple-merge-class-names",
|
|
40
39
|
"devDependencies": {
|
|
40
|
+
"tsup": "^8.5.1",
|
|
41
|
+
"typescript": "^6.0.3",
|
|
41
42
|
"vitest": "^3.2.3"
|
|
42
43
|
},
|
|
43
44
|
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
44
46
|
"test": "vitest",
|
|
45
47
|
"test:watch": "vitest --watch",
|
|
46
48
|
"test:ui": "vitest --ui"
|
package/mergeClassNames.d.ts
DELETED
package/mergeClassNames.js
DELETED
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* mergeClassNames - A straightforward utility for merging CSS class names in React + Tailwind, and other JavaScript projects.
|
|
3
|
-
*
|
|
4
|
-
* @license AGPL-3.0
|
|
5
|
-
* Copyright (C) 2025 Abdullah Fatota
|
|
6
|
-
*
|
|
7
|
-
* Example usage:
|
|
8
|
-
* import { mergeClassNames } from "simple-merge-class-names";
|
|
9
|
-
*
|
|
10
|
-
* function MyComponent() {
|
|
11
|
-
* return (
|
|
12
|
-
* <div
|
|
13
|
-
* className={mergeClassNames(
|
|
14
|
-
* "app",
|
|
15
|
-
* "min-h-dvh",
|
|
16
|
-
* "grid",
|
|
17
|
-
* "grid-rows-[auto_1fr_auto]",
|
|
18
|
-
* "outline"
|
|
19
|
-
* )}
|
|
20
|
-
* >
|
|
21
|
-
* Hello, world!
|
|
22
|
-
* </div>
|
|
23
|
-
* );
|
|
24
|
-
* }
|
|
25
|
-
*
|
|
26
|
-
* This program is free software: you can redistribute it and/or modify
|
|
27
|
-
* it under the terms of the GNU Affero General Public License as published by
|
|
28
|
-
* the Free Software Foundation, either version 3 of the License, or
|
|
29
|
-
* (at your option) any later version.
|
|
30
|
-
*
|
|
31
|
-
* This program is distributed in the hope that it will be useful,
|
|
32
|
-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
33
|
-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
34
|
-
* GNU Affero General Public License for more details.
|
|
35
|
-
*
|
|
36
|
-
* You should have received a copy of the GNU Affero General Public License
|
|
37
|
-
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
const partition = (array, keepPredicate) => {
|
|
41
|
-
const keep = [];
|
|
42
|
-
const ignore = [];
|
|
43
|
-
for (const element of array) {
|
|
44
|
-
const arrayRef = keepPredicate(element) ? keep : ignore;
|
|
45
|
-
arrayRef.push(element);
|
|
46
|
-
}
|
|
47
|
-
return [keep, ignore];
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const filterArguments = (argumentsArray) => {
|
|
51
|
-
/*
|
|
52
|
-
returns {
|
|
53
|
-
valid: bool,
|
|
54
|
-
value: any
|
|
55
|
-
(optional) isString: bool,
|
|
56
|
-
(optional) valueType: any
|
|
57
|
-
(optional) isValueFalse
|
|
58
|
-
(optional) isValidString: bool
|
|
59
|
-
}
|
|
60
|
-
*/
|
|
61
|
-
const isValidArgument = (value) => {
|
|
62
|
-
const valueType = typeof value;
|
|
63
|
-
|
|
64
|
-
if (value === false) {
|
|
65
|
-
return { valid: true, value };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (valueType === "string") {
|
|
69
|
-
const trimmed = value.trim();
|
|
70
|
-
|
|
71
|
-
// invalid string
|
|
72
|
-
if (trimmed === "") {
|
|
73
|
-
return {
|
|
74
|
-
valid: false,
|
|
75
|
-
value,
|
|
76
|
-
isString: true,
|
|
77
|
-
isValidString: false,
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
// valid string
|
|
81
|
-
return {
|
|
82
|
-
valid: true,
|
|
83
|
-
value: trimmed,
|
|
84
|
-
isString: true,
|
|
85
|
-
isValidString: true,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// everything else
|
|
90
|
-
return { valid: false, value, isString: false, valueType };
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
// array of objects
|
|
94
|
-
const newArguments = argumentsArray.map((element) =>
|
|
95
|
-
isValidArgument(element)
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
// includes `false`
|
|
99
|
-
const [validArgumentsWithFalse, invalidArguments] = partition(
|
|
100
|
-
newArguments,
|
|
101
|
-
({ valid }) => valid === true
|
|
102
|
-
);
|
|
103
|
-
|
|
104
|
-
// ignore `false`
|
|
105
|
-
const [validArguments, _] = partition(
|
|
106
|
-
validArgumentsWithFalse,
|
|
107
|
-
({ value }) => value !== false
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
// console.log({ validArgumentsWithFalse, validArguments, invalidArguments });
|
|
111
|
-
|
|
112
|
-
return { validArguments, invalidArguments };
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
const warnMessage = (
|
|
116
|
-
{ value, isString, valueType, isValidString },
|
|
117
|
-
callerFunctionName
|
|
118
|
-
) => {
|
|
119
|
-
if (isString === false) {
|
|
120
|
-
return `[${callerFunctionName}] Ignored non-string >${value}< (${valueType})`;
|
|
121
|
-
} else if (isValidString === false) {
|
|
122
|
-
return `[${callerFunctionName}] Ignored empty string "${value}"`;
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const warnOnly = (
|
|
127
|
-
invalidArgumentsArray,
|
|
128
|
-
callerFunctionName,
|
|
129
|
-
debuggerVariantName
|
|
130
|
-
) =>
|
|
131
|
-
invalidArgumentsArray.forEach((obj) => {
|
|
132
|
-
const message = warnMessage(obj, callerFunctionName);
|
|
133
|
-
console.warn(`${message}`);
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const warnDebug = (invalidArgumentsArray, callerFunctionName) =>
|
|
137
|
-
invalidArgumentsArray.forEach((obj) => {
|
|
138
|
-
console.warn(warnMessage(obj, callerFunctionName));
|
|
139
|
-
debugger;
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const join = (argumentsArray) => {
|
|
143
|
-
const space = "\x20"; // ASCII for single space (" "), decimal 32
|
|
144
|
-
const classNames = argumentsArray.map(({ value }) => value).join(space);
|
|
145
|
-
return classNames;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
const finalResult = (joinedClassNames) =>
|
|
149
|
-
joinedClassNames === "" ? false : joinedClassNames;
|
|
150
|
-
|
|
151
|
-
// exported
|
|
152
|
-
// export const mergeClassNamesNoWarning = (...argumentsArray) => {
|
|
153
|
-
// const { validArguments, _ } = filterArguments(argumentsArray);
|
|
154
|
-
// const classNames = join(validArguments);
|
|
155
|
-
// const result = finalResult(classNames);
|
|
156
|
-
// return result;
|
|
157
|
-
// };
|
|
158
|
-
|
|
159
|
-
export const mergeClassNames = (...argumentsArray) => {
|
|
160
|
-
const { validArguments, invalidArguments } =
|
|
161
|
-
filterArguments(argumentsArray);
|
|
162
|
-
warnOnly(invalidArguments, "mergeClassNames", "mergeClassNamesDebugger");
|
|
163
|
-
const classNames = join(validArguments);
|
|
164
|
-
const result = finalResult(classNames);
|
|
165
|
-
return result;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
export const mergeClassNamesDebugger = (...argumentsArray) => {
|
|
169
|
-
const { validArguments, invalidArguments } =
|
|
170
|
-
filterArguments(argumentsArray);
|
|
171
|
-
warnDebug(invalidArguments, "mergeClassNamesDebugger");
|
|
172
|
-
const classNames = join(validArguments);
|
|
173
|
-
const result = finalResult(classNames);
|
|
174
|
-
return result;
|
|
175
|
-
};
|