@contember/utilities 1.2.0-beta.3 → 1.2.0-rc.1

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.
Files changed (215) hide show
  1. package/dist/development/assert-types/Predicates.js +20 -4
  2. package/dist/development/assert-types/Predicates.js.map +1 -1
  3. package/dist/development/assert-types/assert.js.map +1 -1
  4. package/dist/development/class-name/colorSchemeClassName.js +16 -0
  5. package/dist/development/class-name/colorSchemeClassName.js.map +1 -0
  6. package/dist/development/class-name/constants.js +7 -0
  7. package/dist/development/class-name/constants.js.map +1 -0
  8. package/dist/development/class-name/deduplicateClassName.js.map +1 -0
  9. package/dist/development/class-name/filterThemedClassName.js +68 -0
  10. package/dist/development/class-name/filterThemedClassName.js.map +1 -0
  11. package/dist/development/class-name/{Internal/flatClassNameList.js → flatClassNameList.js} +1 -1
  12. package/dist/development/class-name/flatClassNameList.js.map +1 -0
  13. package/dist/development/class-name/listClassName.js +2 -2
  14. package/dist/development/class-name/listClassName.js.map +1 -1
  15. package/dist/development/class-name/stateClassName.js +3 -6
  16. package/dist/development/class-name/stateClassName.js.map +1 -1
  17. package/dist/development/class-name/themeClassName.js +18 -0
  18. package/dist/development/class-name/themeClassName.js.map +1 -0
  19. package/dist/development/deprecate/currentOrDeprecated.js +20 -0
  20. package/dist/development/deprecate/currentOrDeprecated.js.map +1 -0
  21. package/dist/development/deprecate/deprecate.js +6 -0
  22. package/dist/development/deprecate/deprecate.js.map +1 -0
  23. package/dist/development/dom/stateDataAttributes.js +11 -0
  24. package/dist/development/dom/stateDataAttributes.js.map +1 -0
  25. package/dist/development/dom/svgSizeProps.js.map +1 -1
  26. package/dist/development/functional/omit.js +15 -0
  27. package/dist/development/functional/omit.js.map +1 -0
  28. package/dist/development/functional/pick.js.map +1 -1
  29. package/dist/development/index.js +31 -8
  30. package/dist/development/index.js.map +1 -1
  31. package/dist/development/string-utilities/kebabCase.js +8 -0
  32. package/dist/development/string-utilities/kebabCase.js.map +1 -0
  33. package/dist/production/assert-types/Predicates.js +20 -4
  34. package/dist/production/assert-types/Predicates.js.map +1 -1
  35. package/dist/production/assert-types/assert.js.map +1 -1
  36. package/dist/production/class-name/colorSchemeClassName.js +16 -0
  37. package/dist/production/class-name/colorSchemeClassName.js.map +1 -0
  38. package/dist/production/class-name/constants.js +7 -0
  39. package/dist/production/class-name/constants.js.map +1 -0
  40. package/dist/production/class-name/deduplicateClassName.js.map +1 -0
  41. package/dist/production/class-name/filterThemedClassName.js +68 -0
  42. package/dist/production/class-name/filterThemedClassName.js.map +1 -0
  43. package/dist/production/class-name/{Internal/flatClassNameList.js → flatClassNameList.js} +1 -1
  44. package/dist/production/class-name/flatClassNameList.js.map +1 -0
  45. package/dist/production/class-name/listClassName.js +2 -2
  46. package/dist/production/class-name/listClassName.js.map +1 -1
  47. package/dist/production/class-name/stateClassName.js +3 -6
  48. package/dist/production/class-name/stateClassName.js.map +1 -1
  49. package/dist/production/class-name/themeClassName.js +18 -0
  50. package/dist/production/class-name/themeClassName.js.map +1 -0
  51. package/dist/production/deprecate/currentOrDeprecated.js +20 -0
  52. package/dist/production/deprecate/currentOrDeprecated.js.map +1 -0
  53. package/dist/production/deprecate/deprecate.js +6 -0
  54. package/dist/production/deprecate/deprecate.js.map +1 -0
  55. package/dist/production/dom/stateDataAttributes.js +11 -0
  56. package/dist/production/dom/stateDataAttributes.js.map +1 -0
  57. package/dist/production/dom/svgSizeProps.js.map +1 -1
  58. package/dist/production/functional/omit.js +15 -0
  59. package/dist/production/functional/omit.js.map +1 -0
  60. package/dist/production/functional/pick.js.map +1 -1
  61. package/dist/production/index.js +31 -8
  62. package/dist/production/index.js.map +1 -1
  63. package/dist/production/string-utilities/kebabCase.js +8 -0
  64. package/dist/production/string-utilities/kebabCase.js.map +1 -0
  65. package/dist/types/assert-types/Predicates.d.ts +6 -2
  66. package/dist/types/assert-types/Predicates.d.ts.map +1 -1
  67. package/dist/types/assert-types/assert.d.ts +2 -1
  68. package/dist/types/assert-types/assert.d.ts.map +1 -1
  69. package/dist/types/assert-types/index.d.ts +1 -0
  70. package/dist/types/assert-types/index.d.ts.map +1 -1
  71. package/dist/types/assert-types/types.d.ts +5 -0
  72. package/dist/types/assert-types/types.d.ts.map +1 -0
  73. package/dist/types/class-name/colorSchemeClassName.d.ts +35 -0
  74. package/dist/types/class-name/colorSchemeClassName.d.ts.map +1 -0
  75. package/dist/types/class-name/constants.d.ts +42 -0
  76. package/dist/types/class-name/constants.d.ts.map +1 -0
  77. package/dist/types/class-name/deduplicateClassName.d.ts.map +1 -0
  78. package/dist/types/class-name/filterThemedClassName.d.ts +7 -0
  79. package/dist/types/class-name/filterThemedClassName.d.ts.map +1 -0
  80. package/dist/types/class-name/flatClassNameList.d.ts +3 -0
  81. package/dist/types/class-name/flatClassNameList.d.ts.map +1 -0
  82. package/dist/types/class-name/index.d.ts +7 -4
  83. package/dist/types/class-name/index.d.ts.map +1 -1
  84. package/dist/types/class-name/stateClassName.d.ts +9 -3
  85. package/dist/types/class-name/stateClassName.d.ts.map +1 -1
  86. package/dist/types/class-name/themeClassName.d.ts +87 -0
  87. package/dist/types/class-name/themeClassName.d.ts.map +1 -0
  88. package/dist/types/class-name/types.d.ts +14 -0
  89. package/dist/types/class-name/types.d.ts.map +1 -0
  90. package/dist/types/deprecate/currentOrDeprecated.d.ts +35 -0
  91. package/dist/types/deprecate/currentOrDeprecated.d.ts.map +1 -0
  92. package/dist/types/deprecate/deprecate.d.ts +19 -0
  93. package/dist/types/deprecate/deprecate.d.ts.map +1 -0
  94. package/dist/types/deprecate/index.d.ts +4 -0
  95. package/dist/types/deprecate/index.d.ts.map +1 -0
  96. package/dist/types/deprecate/types.d.ts +2 -0
  97. package/dist/types/deprecate/types.d.ts.map +1 -0
  98. package/dist/types/dom/index.d.ts +1 -0
  99. package/dist/types/dom/index.d.ts.map +1 -1
  100. package/dist/types/dom/stateDataAttributes.d.ts +4 -0
  101. package/dist/types/dom/stateDataAttributes.d.ts.map +1 -0
  102. package/dist/types/dom/svgSizeProps.d.ts.map +1 -1
  103. package/dist/types/functional/index.d.ts +1 -0
  104. package/dist/types/functional/index.d.ts.map +1 -1
  105. package/dist/types/functional/omit.d.ts +2 -0
  106. package/dist/types/functional/omit.d.ts.map +1 -0
  107. package/dist/types/functional/pick.d.ts +1 -1
  108. package/dist/types/functional/pick.d.ts.map +1 -1
  109. package/dist/types/index.d.ts +2 -1
  110. package/dist/types/index.d.ts.map +1 -1
  111. package/dist/types/string-utilities/index.d.ts +1 -0
  112. package/dist/types/string-utilities/index.d.ts.map +1 -1
  113. package/dist/types/string-utilities/kebabCase.d.ts +2 -0
  114. package/dist/types/string-utilities/kebabCase.d.ts.map +1 -0
  115. package/dist/types/tsconfig.tsbuildinfo +1 -1
  116. package/dist/types/types/DeepPartial.d.ts.map +1 -0
  117. package/dist/types/types/NonNullableRequired.d.ts.map +1 -0
  118. package/dist/types/types/NonOptional.d.ts +8 -0
  119. package/dist/types/types/NonOptional.d.ts.map +1 -0
  120. package/dist/types/types/ObjectKeyValue.d.ts.map +1 -0
  121. package/dist/types/types/PolymorphicRefs.d.ts.map +1 -0
  122. package/dist/types/types/Primitive.d.ts +2 -0
  123. package/dist/types/types/Primitive.d.ts.map +1 -0
  124. package/dist/types/types/RequiredDeepPlainObject.d.ts +4 -0
  125. package/dist/types/types/RequiredDeepPlainObject.d.ts.map +1 -0
  126. package/dist/types/types/Try.d.ts.map +1 -0
  127. package/dist/types/types/TypeofStringLiteral.d.ts +2 -0
  128. package/dist/types/types/TypeofStringLiteral.d.ts.map +1 -0
  129. package/dist/types/types/index.d.ts +11 -0
  130. package/dist/types/types/index.d.ts.map +1 -0
  131. package/package.json +4 -1
  132. package/src/assert-types/Predicates.ts +26 -5
  133. package/src/assert-types/assert.ts +2 -1
  134. package/src/assert-types/index.ts +1 -0
  135. package/src/assert-types/types.ts +5 -0
  136. package/src/class-name/colorSchemeClassName.ts +46 -0
  137. package/src/class-name/constants.ts +43 -0
  138. package/src/class-name/filterThemedClassName.ts +86 -0
  139. package/src/class-name/{Internal/flatClassNameList.ts → flatClassNameList.ts} +2 -2
  140. package/src/class-name/index.ts +7 -4
  141. package/src/class-name/listClassName.ts +2 -2
  142. package/src/class-name/stateClassName.ts +13 -8
  143. package/src/class-name/themeClassName.ts +117 -0
  144. package/src/class-name/types.ts +18 -0
  145. package/src/deprecate/currentOrDeprecated.ts +51 -0
  146. package/src/deprecate/deprecate.ts +31 -0
  147. package/src/deprecate/index.ts +3 -0
  148. package/src/deprecate/types.ts +2 -0
  149. package/src/dom/index.ts +1 -0
  150. package/src/dom/stateDataAttributes.ts +8 -0
  151. package/src/dom/svgSizeProps.ts +0 -2
  152. package/src/functional/index.ts +1 -0
  153. package/src/functional/omit.ts +13 -0
  154. package/src/functional/pick.ts +1 -1
  155. package/src/index.ts +2 -1
  156. package/src/string-utilities/index.ts +1 -0
  157. package/src/string-utilities/kebabCase.ts +5 -0
  158. package/src/types/NonOptional.ts +7 -0
  159. package/src/types/Primitive.ts +1 -0
  160. package/src/types/RequiredDeepPlainObject.ts +3 -0
  161. package/src/types/TypeofStringLiteral.ts +1 -0
  162. package/src/types/index.ts +20 -0
  163. package/dist/development/class-name/GlobalClassNamePrefixContext.js +0 -6
  164. package/dist/development/class-name/GlobalClassNamePrefixContext.js.map +0 -1
  165. package/dist/development/class-name/Internal/deduplicateClassName.js.map +0 -1
  166. package/dist/development/class-name/Internal/flatClassNameList.js.map +0 -1
  167. package/dist/development/class-name/useClassName.js +0 -18
  168. package/dist/development/class-name/useClassName.js.map +0 -1
  169. package/dist/development/class-name/useClassNameFactory.js +0 -21
  170. package/dist/development/class-name/useClassNameFactory.js.map +0 -1
  171. package/dist/production/class-name/GlobalClassNamePrefixContext.js +0 -6
  172. package/dist/production/class-name/GlobalClassNamePrefixContext.js.map +0 -1
  173. package/dist/production/class-name/Internal/deduplicateClassName.js.map +0 -1
  174. package/dist/production/class-name/Internal/flatClassNameList.js.map +0 -1
  175. package/dist/production/class-name/useClassName.js +0 -18
  176. package/dist/production/class-name/useClassName.js.map +0 -1
  177. package/dist/production/class-name/useClassNameFactory.js +0 -21
  178. package/dist/production/class-name/useClassNameFactory.js.map +0 -1
  179. package/dist/types/class-name/GlobalClassNamePrefixContext.d.ts +0 -2
  180. package/dist/types/class-name/GlobalClassNamePrefixContext.d.ts.map +0 -1
  181. package/dist/types/class-name/Internal/deduplicateClassName.d.ts.map +0 -1
  182. package/dist/types/class-name/Internal/flatClassNameList.d.ts +0 -3
  183. package/dist/types/class-name/Internal/flatClassNameList.d.ts.map +0 -1
  184. package/dist/types/class-name/Types.d.ts +0 -5
  185. package/dist/types/class-name/Types.d.ts.map +0 -1
  186. package/dist/types/class-name/useClassName.d.ts +0 -10
  187. package/dist/types/class-name/useClassName.d.ts.map +0 -1
  188. package/dist/types/class-name/useClassNameFactory.d.ts +0 -3
  189. package/dist/types/class-name/useClassNameFactory.d.ts.map +0 -1
  190. package/dist/types/typescript-utilities/DeepPartial.d.ts.map +0 -1
  191. package/dist/types/typescript-utilities/NonNullableRequired.d.ts.map +0 -1
  192. package/dist/types/typescript-utilities/ObjectKeyValue.d.ts.map +0 -1
  193. package/dist/types/typescript-utilities/PolymorphicRefs.d.ts.map +0 -1
  194. package/dist/types/typescript-utilities/Try.d.ts.map +0 -1
  195. package/dist/types/typescript-utilities/index.d.ts +0 -6
  196. package/dist/types/typescript-utilities/index.d.ts.map +0 -1
  197. package/src/class-name/GlobalClassNamePrefixContext.ts +0 -3
  198. package/src/class-name/Types.ts +0 -2
  199. package/src/class-name/useClassName.ts +0 -29
  200. package/src/class-name/useClassNameFactory.ts +0 -31
  201. package/src/typescript-utilities/index.ts +0 -5
  202. /package/dist/development/class-name/{Internal/deduplicateClassName.js → deduplicateClassName.js} +0 -0
  203. /package/dist/production/class-name/{Internal/deduplicateClassName.js → deduplicateClassName.js} +0 -0
  204. /package/dist/types/class-name/{Internal/deduplicateClassName.d.ts → deduplicateClassName.d.ts} +0 -0
  205. /package/dist/types/{typescript-utilities → types}/DeepPartial.d.ts +0 -0
  206. /package/dist/types/{typescript-utilities → types}/NonNullableRequired.d.ts +0 -0
  207. /package/dist/types/{typescript-utilities → types}/ObjectKeyValue.d.ts +0 -0
  208. /package/dist/types/{typescript-utilities → types}/PolymorphicRefs.d.ts +0 -0
  209. /package/dist/types/{typescript-utilities → types}/Try.d.ts +0 -0
  210. /package/src/class-name/{Internal/deduplicateClassName.ts → deduplicateClassName.ts} +0 -0
  211. /package/src/{typescript-utilities → types}/DeepPartial.ts +0 -0
  212. /package/src/{typescript-utilities → types}/NonNullableRequired.ts +0 -0
  213. /package/src/{typescript-utilities → types}/ObjectKeyValue.ts +0 -0
  214. /package/src/{typescript-utilities → types}/PolymorphicRefs.ts +0 -0
  215. /package/src/{typescript-utilities → types}/Try.ts +0 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@contember/utilities",
3
3
  "license": "Apache-2.0",
4
- "version": "1.2.0-beta.3",
4
+ "version": "1.2.0-rc.1",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "main": "./dist/production/index.js",
@@ -35,5 +35,8 @@
35
35
  "peerDependencies": {
36
36
  "react": "^17 || ^18"
37
37
  },
38
+ "dependencies": {
39
+ "type-fest": "^3.12.0"
40
+ },
38
41
  "stableVersion": "0.0.0"
39
42
  }
@@ -1,3 +1,5 @@
1
+ import { Predicate, SlugString, UnionOfPredicateTypes } from './types'
2
+
1
3
  export function isNull(value: unknown): value is null {
2
4
  return value === null
3
5
  }
@@ -7,7 +9,7 @@ export function isNotNull<T>(value: unknown): value is T {
7
9
  export function isUndefined(value: unknown): value is undefined {
8
10
  return value === undefined
9
11
  }
10
- export function isDefined<T>(value: unknown): value is T {
12
+ export function isDefined<T>(value: unknown): value is Exclude<T, undefined> {
11
13
  return value !== undefined
12
14
  }
13
15
  export function isNotNullish<T>(value: T): value is Exclude<T, null | undefined> {
@@ -25,6 +27,9 @@ export function isFalse(value: unknown): value is false {
25
27
  export function isString(value: unknown): value is string {
26
28
  return typeof value === 'string'
27
29
  }
30
+ export function isSlugString(value: unknown): value is SlugString {
31
+ return isNonEmptyString(value) && /^[a-z0-9_-]+$/.test(value)
32
+ }
28
33
  export function isNonEmptyString(value: unknown): value is string {
29
34
  return isString(value) && value.length > 0
30
35
  }
@@ -45,10 +50,26 @@ export function isNonNegativeNumber(value: unknown): value is number {
45
50
  export function isNumericString(value: string): value is string {
46
51
  return isString(value) && !isNaN(parseFloat(value))
47
52
  }
48
- export function isOneOfFactory<U, T = any>(options: T extends Array<U> | ReadonlyArray<U> ? T : never): (value: unknown) => value is U {
49
- return function isOneOfProduct(value: unknown): value is U {
50
- return options.includes(value as U)
51
- }
53
+ export function isOneOfFactory<U, T = any>(
54
+ members: T extends Array<U> | ReadonlyArray<U> ? T : never,
55
+ ): (value: unknown) => value is U {
56
+ return function isOneOfFactoryProduct(value: unknown): value is U {
57
+ return members.includes(value as U)
58
+ }
59
+ }
60
+ export function satisfiesOneOfFactory<T extends Array<Predicate<any, any>>>(
61
+ ...predicates: T
62
+ ): Predicate<unknown, UnionOfPredicateTypes<T>> {
63
+ return (value: any): value is UnionOfPredicateTypes<T> => {
64
+ return predicates.some(predicate => predicate(value))
65
+ }
66
+ }
67
+ export function isArrayOfMembersSatisfyingFactory<T>(
68
+ predicate: (value: unknown) => value is T,
69
+ ): (value: unknown) => value is Array<T> {
70
+ return function isArrayOfMembersSatisfyingProduct(value: unknown): value is Array<T> {
71
+ return Array.isArray(value) && value.every(predicate)
72
+ }
52
73
  }
53
74
  export function isHTMLElement(value: unknown): value is HTMLElement {
54
75
  return value instanceof HTMLElement
@@ -1,4 +1,5 @@
1
1
  import { AssertionError } from './AssertionError'
2
+ import { Predicate } from './types'
2
3
 
3
4
  export function assert<
4
5
  In,
@@ -6,7 +7,7 @@ export function assert<
6
7
  >(
7
8
  that: string | undefined = undefined,
8
9
  value: In,
9
- predicate: (value: In) => value is Out,
10
+ predicate: Predicate<In, Out>, // (value: In) => value is Out,
10
11
  ): asserts value is Out {
11
12
  if (!predicate(value)) {
12
13
  throw new AssertionError(value, that)
@@ -2,3 +2,4 @@ export * from './AssertionError'
2
2
  export * from './Predicates'
3
3
  export * from './assert'
4
4
  export * from './assertNever'
5
+ export * from './types'
@@ -0,0 +1,5 @@
1
+ import { Opaque } from 'type-fest'
2
+
3
+ export type Predicate<T, U extends T> = (value: T) => value is U
4
+ export type UnionOfPredicateTypes<T> = T extends Array<(value: any) => value is infer U> ? U : never
5
+ export type SlugString = Opaque<string, 'SlugString'>
@@ -0,0 +1,46 @@
1
+ import { KebabCase } from 'type-fest'
2
+ import { COLOR_SCHEME_CLASS_NAME_REG_EXP } from './constants'
3
+ import { ColorSchemeClassName } from './types'
4
+
5
+ /**
6
+ * Returns a string with a color scheme class name, prefixed with `scheme-`
7
+ *
8
+ * @param scheme - Color scheme name
9
+ * @returns A color scheme class name
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * const colorScheme = colorSchemeClassName('system')
14
+ * // ^ 'scheme-system'
15
+ *
16
+ * return (
17
+ * <div className={colorScheme}>
18
+ * <button className={controlsThemeClassName}>Click me</button>
19
+ * </div>
20
+ *
21
+ * // renders:
22
+ * // <div class="scheme-system">
23
+ * // <button class="scheme-system theme-default-controls theme-danger-controls:hover">Click me</button>
24
+ * // </div>
25
+ * ```
26
+ */
27
+ export function colorSchemeClassName<S extends KebabCase<string> = KebabCase<string>>(scheme: S): ColorSchemeClassName<S>;
28
+ export function colorSchemeClassName<S extends KebabCase<string> = KebabCase<string>>(scheme: null | undefined): undefined;
29
+ export function colorSchemeClassName<S extends KebabCase<string> = KebabCase<string>>(scheme: S | null | undefined): ColorSchemeClassName<S> | undefined;
30
+ export function colorSchemeClassName<S extends KebabCase<string> = KebabCase<string>>(scheme: S | null | undefined): ColorSchemeClassName<S> | undefined {
31
+ if (typeof scheme === 'string') {
32
+ return `scheme-${scheme}`
33
+ } else {
34
+ return undefined
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Tests if a string is a color scheme CSS class
40
+ * @param value - String to test
41
+ * @returns `true` if the string is a color scheme CSS class
42
+ * @internal
43
+ */
44
+ export function isColorSchemeClassName<T extends string>(value: string): value is ColorSchemeClassName<T> {
45
+ return COLOR_SCHEME_CLASS_NAME_REG_EXP.test(value)
46
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * RegExp that matches theme class names
3
+ *
4
+ * @internal
5
+ *
6
+ * If theme is `default` then it will match:
7
+ *
8
+ * - theme-default
9
+ * - theme-default:hover
10
+ * - theme-default:active
11
+ * - theme-default:focus
12
+ * - theme-default-content
13
+ * - theme-default-content:hover
14
+ * - theme-default-content:active
15
+ * - theme-default-content:focus
16
+ * - theme-default-controls
17
+ * - theme-default-controls:hover
18
+ * - theme-default-controls:active
19
+ * - theme-default-controls:focus
20
+ *
21
+ * If theme is `brand-color` then it will match:
22
+ *
23
+ * - theme-brand-color
24
+ * - theme-brand-color:hover
25
+ * - theme-brand-color:active
26
+ * - theme-brand-color:focus
27
+ * - theme-brand-color-content
28
+ * - theme-brand-color-content:hover
29
+ * - theme-brand-color-content:active
30
+ * - theme-brand-color-content:focus
31
+ * - theme-brand-color-controls
32
+ * - theme-brand-color-controls:hover
33
+ * - theme-brand-color-controls:active
34
+ * - theme-brand-color-controls:focus
35
+ */
36
+
37
+ export const THEME_CLASS_NAME_REG_EXP = /^theme-(?<name>[\w-]+?)(?:-(?<scope>content|controls))?(?<state>:[\w]+(?:[\w-]+)?)?$/
38
+
39
+ /**
40
+ * Regular expression for testing if a string is a color scheme CSS class
41
+ * @internal
42
+ */
43
+ export const COLOR_SCHEME_CLASS_NAME_REG_EXP = /^scheme-(?<colorScheme>[a-z0-9]|[a-z0-9]+[a-z-]+[a-z])$/
@@ -0,0 +1,86 @@
1
+ import { KebabCase } from 'type-fest'
2
+ import { isColorSchemeClassName } from './colorSchemeClassName'
3
+ import { THEME_CLASS_NAME_REG_EXP } from './constants'
4
+ import { flatClassNameList } from './flatClassNameList'
5
+ import { contentThemeClassName, controlsThemeClassName } from './themeClassName'
6
+ import { ColorSchemeClassName, NestedClassName } from './types'
7
+
8
+ export type ThemeConfig = { content: string | undefined, controls: string | undefined }
9
+
10
+ export function filterThemedClassName(
11
+ nestedClassName: NestedClassName,
12
+ defaultColorSchemeContext: ColorSchemeClassName,
13
+ ) {
14
+ const theme: ThemeConfig = { content: undefined, controls: undefined }
15
+ const stateThemes: Record<string, ThemeConfig> = {}
16
+ let colorScheme: ColorSchemeClassName | undefined = undefined
17
+
18
+ const flatClassName = flatClassNameList(nestedClassName).map(className => {
19
+ if (isColorSchemeClassName(className)) {
20
+ colorScheme = className
21
+ return undefined
22
+ }
23
+
24
+ const match = className.match(THEME_CLASS_NAME_REG_EXP)
25
+
26
+ if (match) {
27
+ const name = match.groups!.name as KebabCase<string>
28
+ const scope = match.groups!.scope as KebabCase<string>
29
+ const state = match.groups!.state as `:${KebabCase<string>}`
30
+
31
+ if (state) {
32
+ stateThemes[state] = stateThemes[state] ?? { content: undefined, controls: undefined }
33
+
34
+ if (!scope) {
35
+ stateThemes[state].content = name
36
+ stateThemes[state].controls = name
37
+ } else if (scope === 'content') {
38
+ stateThemes[state].content = name
39
+ } else if (scope === 'controls') {
40
+ stateThemes[state].controls = name
41
+ } else {
42
+ throw new Error(`Unknown theme scope: ${scope}`)
43
+ }
44
+ } else {
45
+ if (!scope) {
46
+ theme.content = name
47
+ theme.controls = name
48
+ } else if (scope === 'content') {
49
+ theme.content = name
50
+ } else if (scope === 'controls') {
51
+ theme.controls = name
52
+ } else {
53
+ throw new Error(`Unknown theme scope: ${scope}`)
54
+ }
55
+ }
56
+
57
+ return undefined
58
+ } else {
59
+ return className
60
+ }
61
+ }).filter(Boolean) as string[]
62
+
63
+ const themeClassNames: (string | undefined)[] = []
64
+
65
+ const content = contentThemeClassName(theme.content)
66
+ const controls = controlsThemeClassName(theme.controls)
67
+
68
+ themeClassNames.push(content, controls)
69
+
70
+ for (const state in stateThemes) {
71
+ const { content, controls } = stateThemes[state]
72
+
73
+ const contentClassName = contentThemeClassName(content, state as `:${KebabCase<string>}`)
74
+ const controlsClassName = controlsThemeClassName(controls, state as `:${KebabCase<string>}`)
75
+
76
+ themeClassNames.push(contentClassName, controlsClassName)
77
+ }
78
+
79
+ const flatThemedClassName = flatClassNameList(themeClassNames)
80
+
81
+ if (flatThemedClassName.length > 0 || colorScheme) {
82
+ return [colorScheme ?? defaultColorSchemeContext, ...flatThemedClassName, ...flatClassName]
83
+ } else {
84
+ return flatClassName
85
+ }
86
+ }
@@ -1,5 +1,5 @@
1
- import { assertNever } from '../../assert-types'
2
- import { NestedClassName } from '../Types'
1
+ import { assertNever } from '../assert-types'
2
+ import { NestedClassName } from './types'
3
3
 
4
4
  export function flatClassNameList(className: NestedClassName): string[] {
5
5
  if (!className) {
@@ -1,6 +1,9 @@
1
- export * from './GlobalClassNamePrefixContext'
2
- export * from './Types'
1
+ export * from './colorSchemeClassName'
2
+ export * from './constants'
3
+ export * from './deduplicateClassName'
4
+ export * from './filterThemedClassName'
5
+ export * from './flatClassNameList'
3
6
  export * from './listClassName'
4
7
  export * from './stateClassName'
5
- export * from './useClassName'
6
- export * from './useClassNameFactory'
8
+ export * from './themeClassName'
9
+ export * from './types'
@@ -1,5 +1,5 @@
1
- import { deduplicateClassName } from './Internal/deduplicateClassName'
2
- import { flatClassNameList } from './Internal/flatClassNameList'
1
+ import { deduplicateClassName } from './deduplicateClassName'
2
+ import { flatClassNameList } from './flatClassNameList'
3
3
 
4
4
  /**
5
5
  * Combines a list of class names into a single string, even nested
@@ -1,9 +1,9 @@
1
- import { ClassNameStateMap } from './Types'
1
+ import { toKebabCase } from '../string-utilities'
2
+ import { ClassNameStateMap } from './types'
2
3
 
3
- const UPPER_CASE_REG_EXP = /([A-Z])/g
4
-
5
- function toKebabCase(value: string) {
6
- return value.replace(UPPER_CASE_REG_EXP, '-$1').toLowerCase()
4
+ export interface StateClassNameOptions {
5
+ glue?: string;
6
+ removeFalsy?: boolean;
7
7
  }
8
8
 
9
9
  /**
@@ -13,12 +13,17 @@ function toKebabCase(value: string) {
13
13
  * stateClassName({ foo: true, bar: false, baz: 1 }) // => 'foo baz-1'
14
14
  *
15
15
  * @param state - State map object with class names as keys and literals as values.
16
- * @param glue - Glue to use between the class name and the value, default is '-'. Set to '' to disable.
16
+ * @param options - Options object
17
+ * @param options.glue - Glue to use between the class name and the value, default is '-'. Set to '' to disable.
18
+ * @param options.removeFalsy - Whether to remove falsy class names, default is true.
17
19
  * @returns Combined class names as string
18
20
  */
19
- export function stateClassName(state: ClassNameStateMap | null = null, glue = '-'): string[] {
21
+ export function stateClassName(
22
+ state: ClassNameStateMap | null = null,
23
+ { glue = '-', removeFalsy = true }: StateClassNameOptions = {},
24
+ ): string[] {
20
25
  const entries = Object.entries(state ?? []).map(
21
- ([className, value]) => value || value === 0
26
+ ([className, value]) => value || value === 0 || !removeFalsy
22
27
  ? (value === true ? toKebabCase(className) : `${toKebabCase(className)}${glue}${value}`)
23
28
  : '',
24
29
  ).filter(Boolean)
@@ -0,0 +1,117 @@
1
+ import { KebabCase } from 'type-fest'
2
+ import { colorSchemeClassName } from './colorSchemeClassName'
3
+ import { ThemeContentClassName, ThemeControlsClassName } from './types'
4
+
5
+ /**
6
+ * Returns a string with a content theme class name, prefixed with `theme-` and suffixed with `-content`
7
+ *
8
+ * IMPORTANT: Make sure to use color scheme class in combination with theme class names.
9
+ *
10
+ * @param contentTheme - Theme name basis for content
11
+ * @param state - State of the theme starting with a ":", e.g. `":hover"`, `":focus"`, `":active"`, `":focus-visible"`
12
+ * @returns A content theme class name
13
+ *
14
+ * @see {@link controlsThemeClassName}
15
+ * @see {@link (colorSchemeClassName:3)}
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * const colorScheme = colorSchemeClassName('system')
20
+ * // ^ 'scheme-system'
21
+ * const contentThemeClassName = contentThemeClassName('default')
22
+ * // ^ 'theme-default-content:hover'
23
+ * const dangerOnMouseOverTheme = controlsThemeClassName('danger', ':hover')
24
+ * // ^ 'theme-danger-controls:hover'
25
+ *
26
+ * return (
27
+ * <div className={`${colorScheme} ${contentThemeClassName}`}>
28
+ * <button className={`${colorScheme} ${dangerOnMouseOverTheme}`}>Delete</button>
29
+ * </div>
30
+ * )
31
+ * ```
32
+ */
33
+ export function contentThemeClassName<
34
+ ContentTheme extends KebabCase<string> = KebabCase<string>,
35
+ State extends `:${KebabCase<string>}` | null | undefined = undefined,
36
+ >(
37
+ theme: ContentTheme | null | undefined,
38
+ state?: State | null | undefined,
39
+ ) {
40
+ return theme ? `theme-${theme}-content${state ?? ''}` as ThemeContentClassName<ContentTheme, State> : undefined
41
+ }
42
+
43
+ /**
44
+ * Returns a string with a controls theme class name, prefixed with `theme-` and suffixed with `-controls`
45
+ *
46
+ * IMPORTANT: Make sure to use color scheme class in combination with theme class names.
47
+ *
48
+ * @param theme - Theme name basis for controls
49
+ * @param state - State of the theme starting with a ":", e.g. `":hover"`, `":focus"`, `":active"`, `":focus-visible"`
50
+ * @returns A controls theme class name
51
+ *
52
+ * @see {@link contentThemeClassName}
53
+ * @see {@link (colorSchemeClassName:3)}
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * const colorScheme = colorSchemeClassName('system')
58
+ * // ^ 'scheme-system'
59
+ * const contentThemeClassName = contentThemeClassName('default')
60
+ * // ^ 'theme-default-content:hover'
61
+ * const dangerOnMouseOverTheme = controlsThemeClassName('danger', ':hover')
62
+ * // ^ 'theme-danger-controls:hover'
63
+ *
64
+ * return (
65
+ * <div className={`${colorScheme} ${contentThemeClassName}`}>
66
+ * <button className={`${colorScheme} ${dangerOnMouseOverTheme}`}>Delete</button>
67
+ * </div>
68
+ * )
69
+ * ```
70
+ */
71
+ export function controlsThemeClassName<
72
+ ControlsTheme extends KebabCase<string> = KebabCase<string>,
73
+ State extends `:${KebabCase<string>}` | null | undefined = undefined,
74
+ >(
75
+ theme: ControlsTheme | null | undefined,
76
+ state?: State | null | undefined,
77
+ ) {
78
+ return theme ? `theme-${theme}-controls${state ?? ''}` as ThemeControlsClassName<ControlsTheme, State> : undefined
79
+ }
80
+
81
+ /**
82
+ * Returns a tuple with a content theme class name and a controls theme class name, prefixed with `theme-` and suffixed with `-content` and `-controls` respectively
83
+ *
84
+ * IMPORTANT: Make sure to use color scheme class in combination with theme class names.
85
+ *
86
+ * @param theme - Theme name basis for content and controls
87
+ * @param state - State of the theme starting with a ":", e.g. `":hover"`, `":focus"`, `":active"`, `":focus-visible"`
88
+ * @returns A tuple with a content theme class name and a controls theme class name
89
+ *
90
+ * @see {@link contentThemeClassName}
91
+ * @see {@link controlsThemeClassName}
92
+ * @see {@link (colorSchemeClassName:3)}
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * const colorScheme = colorSchemeClassName('system')
97
+ * // ^ 'scheme-system'
98
+ * const errorTheme = themeClassName('danger').join(' ')
99
+ * // ^ 'theme-danger-content theme-danger-controls'
100
+ *
101
+ * return (
102
+ * <div className={`${colorScheme} ${errorTheme}`}>File not found</div>
103
+ * )
104
+ * ```
105
+ */
106
+ export function themeClassName<
107
+ Theme extends KebabCase<string> = KebabCase<string>,
108
+ State extends `:${KebabCase<string>}` | null | undefined = undefined,
109
+ >(
110
+ theme: Theme | null | undefined,
111
+ state?: State | null | undefined,
112
+ ) {
113
+ return [
114
+ contentThemeClassName(theme, state),
115
+ controlsThemeClassName(theme, state),
116
+ ] as const
117
+ }
@@ -0,0 +1,18 @@
1
+ import { PropsWithChildren } from 'react'
2
+ import { KebabCase } from 'type-fest'
3
+
4
+ export type NestedClassName = string | false | null | undefined | (string | false | null | undefined)[] | NestedClassName[]
5
+ export type ClassNameStateMap = { [key: string]: string | number | boolean | null | undefined }
6
+
7
+ export type ComponentClassNameProps = PropsWithChildren<{
8
+ className?: NestedClassName;
9
+ componentClassName?: string | string[];
10
+ }>
11
+
12
+ export type ColorSchemeClassName<T extends KebabCase<string> = KebabCase<string>> = `scheme-${T}`
13
+ export type ThemeContentClassName<T extends KebabCase<string> = KebabCase<string>, S extends `:${KebabCase<string>}` | null | undefined = undefined> = S extends string
14
+ ? `theme-${T}-content${S}`
15
+ : `theme-${T}-content`
16
+ export type ThemeControlsClassName<T extends KebabCase<string> = KebabCase<string>, S extends `:${KebabCase<string>}` | null | undefined = undefined> = S extends string
17
+ ? `theme-${T}-controls${S}`
18
+ : `theme-${T}-controls`
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Resolves current or deprecated value. If both are defined, it will throw an error in development mode.
3
+ *
4
+ * @internal
5
+ *
6
+ * Errors are thrown only in development mode. In production mode, the deprecated value is returned.
7
+ *
8
+ * @param current - Current value that replaces deprecated value
9
+ * @param deprecated - Deprecated value that will be removed in the next release
10
+ * @param shouldThrow - Whether to throw an error if both current and deprecated are defined
11
+ * @returns Deprecated value if it is defined, otherwise current value
12
+ * @throws Error if both current and deprecated are defined
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * const current = 'current'
17
+ * const deprecated = 'deprecated'
18
+ *
19
+ * const value = currentOrDeprecated(current, deprecated)
20
+ *
21
+ * console.log(value) // 'deprecated'
22
+ * ```
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * const current = 'current'
27
+ * const deprecated = undefined
28
+ *
29
+ * const value = currentOrDeprecated(current, deprecated)
30
+ *
31
+ * console.log(value) // 'current'
32
+ * ```
33
+ */
34
+ export function currentOrDeprecated<R, D>(current: R, deprecated: D, shouldThrow: boolean = false): R | D {
35
+ if (deprecated !== undefined) {
36
+ if (current !== undefined && import.meta.env.DEV) {
37
+ console.error('Current value:', current)
38
+ console.error('Deprecated value:', deprecated)
39
+
40
+ if (shouldThrow) {
41
+ throw new Error(`You are using deprecated together with current value which is mutually exclusive. Use only one of them.`)
42
+ } else {
43
+ console.error(`You are using deprecated together with current value which is mutually exclusive. Use only one of them.`)
44
+ }
45
+ }
46
+
47
+ return deprecated
48
+ } else {
49
+ return current
50
+ }
51
+ }
@@ -0,0 +1,31 @@
1
+ import { SemverString } from './types'
2
+
3
+ /**
4
+ * Logs a deprecation warning if the assertion is `false`.
5
+ * @internal
6
+ *
7
+ * @param removal - Version when the deprecated feature will be removed
8
+ * @param assertion - Condition that must be true to avoid the deprecation warning
9
+ * @param deprecated - Name of the deprecated feature
10
+ * @param replacement - Name of the feature that replaces the deprecated feature
11
+ *
12
+ * @example
13
+ * ```ts
14
+ * const { visible } = props
15
+ * deprecate(visible !== 'default', '1.3.0', '"default"', '"hidden"')
16
+ * // Logs: Use of "default" is deprecated and might be removed in the next release. Use "hidden" instead.
17
+ * ```
18
+ */
19
+ export function deprecate(removal: SemverString, assertion: boolean, deprecated: string, replacement: string): void {
20
+ if (assertion === false) {
21
+ if (import.meta.env.DEV) {
22
+ const shouldThrow = Boolean(import.meta.env.VITE_CONTEMBER_ADMIN_STRICT_DEPRECATIONS)
23
+
24
+ if (shouldThrow) {
25
+ throw new Error(`Support for ${deprecated} was planned to be removed in the ${removal} release. Replace it with ${replacement} instead.`)
26
+ } else {
27
+ console.warn(`Use of ${deprecated} is deprecated and might be removed in the next release. Use ${replacement} instead.`)
28
+ }
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,3 @@
1
+ export * from './currentOrDeprecated'
2
+ export * from './deprecate'
3
+ export * from './types'
@@ -0,0 +1,2 @@
1
+
2
+ export type SemverString = `${number}.${number}.${number}` | `v${number}.${number}.${number}`
package/src/dom/index.ts CHANGED
@@ -3,4 +3,5 @@ export * from './getMatchingParentElement'
3
3
  export * from './getSizeFromResizeObserverEntry'
4
4
  export * from './isScrollable'
5
5
  export * from './parseTransformMatrix'
6
+ export * from './stateDataAttributes'
6
7
  export * from './svgSizeProps'
@@ -0,0 +1,8 @@
1
+ import { toKebabCase } from '../string-utilities'
2
+ import { dataAttribute } from './dataAttribute'
3
+
4
+ export function stateDataAttributes(state: Record<string, unknown>) {
5
+ return Object.fromEntries(Object.entries(state).map(
6
+ ([key, value]) => [`data-${toKebabCase(key).toLowerCase()}`, dataAttribute(value)],
7
+ ))
8
+ }
@@ -1,5 +1,3 @@
1
- import { SVGProps } from 'react'
2
-
3
1
  export interface SvgSizeProps {
4
2
  width: number;
5
3
  height: number;
@@ -1,2 +1,3 @@
1
+ export * from './omit'
1
2
  export * from './pick'
2
3
  export * from './setHasOneOf'
@@ -0,0 +1,13 @@
1
+ export function omit<T extends Object, K extends keyof T>(object: T, properties: ReadonlyArray<K>, strict: boolean = false): Omit<T, K> {
2
+ const next: Partial<T> = { ...object }
3
+
4
+ for (let key of properties) {
5
+ if (key in object) {
6
+ delete next[key]
7
+ } else if (strict) {
8
+ throw new Error(`Key "${String(key)}" does not exist in object ${JSON.stringify(object)}`)
9
+ }
10
+ }
11
+
12
+ return next as Omit<T, K>
13
+ }
@@ -1,4 +1,4 @@
1
- export function pick<T extends Object, K extends keyof T>(object: T, properties: K[]): Pick<T, K> {
1
+ export function pick<T extends Object, K extends keyof T>(object: T, properties: ReadonlyArray<K>): Pick<T, K> {
2
2
  const next: Partial<Pick<T, K>> = {}
3
3
 
4
4
  for (let key of properties) {
package/src/index.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  export * from './assert-types'
2
2
  export * from './class-name'
3
3
  export * from './css-utilities'
4
+ export * from './deprecate'
4
5
  export * from './dom'
5
6
  export * from './functional'
6
7
  export * from './string-utilities'
7
- export * from './typescript-utilities'
8
+ export * from './types'
@@ -1,2 +1,3 @@
1
1
  export * from './capitalize'
2
+ export * from './kebabCase'
2
3
  export * from './trimString'
@@ -0,0 +1,5 @@
1
+ const UPPER_CASE_REG_EXP = /([A-Z])/g
2
+
3
+ export function toKebabCase(value: string) {
4
+ return value.replace(UPPER_CASE_REG_EXP, '-$1').toLowerCase()
5
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns new type where all the properties are required
3
+ * and previously optional properties will accept undefined.
4
+ */
5
+ export type NonOptional<T> = {
6
+ [P in keyof Required<T>]: Pick<T, P> extends Required<Pick<T, P>> ? T[P] : (T[P] | undefined);
7
+ }
@@ -0,0 +1 @@
1
+ export type Primitive = string | number | boolean | null | undefined