@jsenv/navi 0.7.0 → 0.7.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.
@@ -721,6 +721,9 @@ const pxProperties = [
721
721
  "borderTopRightRadius",
722
722
  "borderBottomLeftRadius",
723
723
  "borderBottomRightRadius",
724
+ "gap",
725
+ "rowGap",
726
+ "columnGap",
724
727
  ];
725
728
 
726
729
  // Properties that need deg units
@@ -840,13 +843,67 @@ const normalizeNumber = (value, context, unit, propertyName) => {
840
843
 
841
844
  // Normalize styles for DOM application
842
845
  const normalizeStyles = (styles, context = "js") => {
846
+ if (typeof styles === "string") {
847
+ styles = parseStyleString(styles);
848
+ return styles;
849
+ }
843
850
  const normalized = {};
844
- for (const [key, value] of Object.entries(styles)) {
851
+ for (const key of Object.keys(styles)) {
852
+ const value = styles[key];
845
853
  normalized[key] = normalizeStyle(value, key, context);
846
854
  }
847
855
  return normalized;
848
856
  };
849
857
 
858
+ /**
859
+ * Parses a CSS style string into a style object.
860
+ * Handles CSS properties with proper camelCase conversion.
861
+ *
862
+ * @param {string} styleString - CSS style string like "color: red; font-size: 14px;"
863
+ * @returns {object} Style object with camelCase properties
864
+ */
865
+ const parseStyleString = (styleString, context = "js") => {
866
+ const style = {};
867
+
868
+ if (!styleString || typeof styleString !== "string") {
869
+ return style;
870
+ }
871
+
872
+ // Split by semicolon and process each declaration
873
+ const declarations = styleString.split(";");
874
+
875
+ for (let declaration of declarations) {
876
+ declaration = declaration.trim();
877
+ if (!declaration) continue;
878
+
879
+ const colonIndex = declaration.indexOf(":");
880
+ if (colonIndex === -1) continue;
881
+
882
+ const property = declaration.slice(0, colonIndex).trim();
883
+ const value = declaration.slice(colonIndex + 1).trim();
884
+
885
+ if (property && value) {
886
+ // CSS custom properties (starting with --) should NOT be converted to camelCase
887
+ if (property.startsWith("--")) {
888
+ style[property] = normalizeStyle(value, property, context);
889
+ } else {
890
+ // Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
891
+ const camelCaseProperty = property.replace(
892
+ /-([a-z])/g,
893
+ (match, letter) => letter.toUpperCase(),
894
+ );
895
+ style[camelCaseProperty] = normalizeStyle(
896
+ value,
897
+ camelCaseProperty,
898
+ context,
899
+ );
900
+ }
901
+ }
902
+ }
903
+
904
+ return style;
905
+ };
906
+
850
907
  // Convert transform object to CSS string
851
908
  const stringifyCSSTransform = (transformObj) => {
852
909
  const transforms = [];
@@ -1007,15 +1064,28 @@ const parseSimple2DMatrix = (a, b, c, d, e, f) => {
1007
1064
  };
1008
1065
 
1009
1066
  // Merge two style objects, handling special cases like transform
1010
- const mergeStyles = (stylesA, stylesB) => {
1011
- const result = { ...stylesA };
1012
- for (const key of Object.keys(stylesB)) {
1013
- if (key === "transform") {
1014
- result[key] = mergeOneStyle(stylesA[key], stylesB[key], key);
1067
+ const mergeStyles = (stylesA, stylesB, context = "js") => {
1068
+ if (!stylesA) {
1069
+ return normalizeStyles(stylesB, context);
1070
+ }
1071
+ if (!stylesB) {
1072
+ return normalizeStyles(stylesA, context);
1073
+ }
1074
+ const result = {};
1075
+ const aKeys = Object.keys(stylesA);
1076
+ const bKeyToVisitSet = new Set(Object.keys(stylesB));
1077
+ for (const aKey of aKeys) {
1078
+ const bHasKey = bKeyToVisitSet.has(aKey);
1079
+ if (bHasKey) {
1080
+ bKeyToVisitSet.delete(aKey);
1081
+ result[aKey] = mergeOneStyle(stylesA[aKey], stylesB[aKey], aKey, context);
1015
1082
  } else {
1016
- result[key] = stylesB[key];
1083
+ result[aKey] = normalizeStyle(stylesA[aKey], aKey, context);
1017
1084
  }
1018
1085
  }
1086
+ for (const bKey of bKeyToVisitSet) {
1087
+ result[bKey] = normalizeStyle(stylesB[bKey], bKey, context);
1088
+ }
1019
1089
  return result;
1020
1090
  };
1021
1091
 
@@ -1177,9 +1247,9 @@ const createStyleController = (name = "anonymous") => {
1177
1247
  throw new Error("styles must be an object");
1178
1248
  }
1179
1249
 
1180
- const normalizedStylesToSet = normalizeStyles(stylesToSet, "js");
1181
1250
  const elementData = elementWeakMap.get(element);
1182
1251
  if (!elementData) {
1252
+ const normalizedStylesToSet = normalizeStyles(stylesToSet, "js");
1183
1253
  const animation = createAnimationForStyles(
1184
1254
  element,
1185
1255
  normalizedStylesToSet,
@@ -1194,7 +1264,7 @@ const createStyleController = (name = "anonymous") => {
1194
1264
  }
1195
1265
 
1196
1266
  const { styles, animation } = elementData;
1197
- const mergedStyles = mergeStyles(styles, normalizedStylesToSet);
1267
+ const mergedStyles = mergeStyles(styles, stylesToSet);
1198
1268
  elementData.styles = mergedStyles;
1199
1269
  updateAnimationStyles(animation, mergedStyles);
1200
1270
  };
@@ -18510,89 +18580,27 @@ const useConstraints = (elementRef, constraints, targetSelector) => {
18510
18580
 
18511
18581
  /**
18512
18582
  * Merges a component's base style with style received from props.
18583
+ * Automatically normalizes style values (e.g., adds "px" units where needed).
18513
18584
  *
18514
18585
  * ```jsx
18515
18586
  * const MyButton = ({ style, children }) => (
18516
- * <button style={withPropsStyle({ padding: '10px' }, style)}>
18587
+ * <button style={withPropsStyle({ padding: 10 }, style)}>
18517
18588
  * {children}
18518
18589
  * </button>
18519
18590
  * );
18520
18591
  *
18521
18592
  * // Usage:
18522
- * <MyButton style={{ color: 'red', fontSize: '14px' }} />
18593
+ * <MyButton style={{ color: 'red', fontSize: 14 }} />
18523
18594
  * <MyButton style="color: blue; margin: 5px;" />
18524
18595
  * <MyButton /> // Just base styles
18525
18596
  * ```
18526
18597
  *
18527
18598
  * @param {string|object} baseStyle - The component's base style (string or object)
18528
18599
  * @param {string|object} [styleFromProps] - Additional style from props (optional)
18529
- * @returns {object} The merged style object
18600
+ * @returns {object} The merged and normalized style object
18530
18601
  */
18531
18602
  const withPropsStyle = (baseStyle, styleFromProps) => {
18532
- if (!styleFromProps) {
18533
- return baseStyle;
18534
- }
18535
- if (!baseStyle) {
18536
- return styleFromProps;
18537
- }
18538
-
18539
- // Parse base style to object if it's a string
18540
- const parsedBaseStyle =
18541
- typeof baseStyle === "string"
18542
- ? parseStyleString(baseStyle)
18543
- : baseStyle || {};
18544
- // Parse props style to object if it's a string
18545
- const parsedPropsStyle =
18546
- typeof styleFromProps === "string"
18547
- ? parseStyleString(styleFromProps)
18548
- : styleFromProps;
18549
- // Merge styles with props taking priority
18550
- return { ...parsedBaseStyle, ...parsedPropsStyle };
18551
- };
18552
-
18553
- /**
18554
- * Parses a CSS style string into a style object.
18555
- * Handles CSS properties with proper camelCase conversion.
18556
- *
18557
- * @param {string} styleString - CSS style string like "color: red; font-size: 14px;"
18558
- * @returns {object} Style object with camelCase properties
18559
- */
18560
- const parseStyleString = (styleString) => {
18561
- const style = {};
18562
-
18563
- if (!styleString || typeof styleString !== "string") {
18564
- return style;
18565
- }
18566
-
18567
- // Split by semicolon and process each declaration
18568
- const declarations = styleString.split(";");
18569
-
18570
- for (let declaration of declarations) {
18571
- declaration = declaration.trim();
18572
- if (!declaration) continue;
18573
-
18574
- const colonIndex = declaration.indexOf(":");
18575
- if (colonIndex === -1) continue;
18576
-
18577
- const property = declaration.slice(0, colonIndex).trim();
18578
- const value = declaration.slice(colonIndex + 1).trim();
18579
-
18580
- if (property && value) {
18581
- // CSS custom properties (starting with --) should NOT be converted to camelCase
18582
- if (property.startsWith("--")) {
18583
- style[property] = value;
18584
- } else {
18585
- // Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
18586
- const camelCaseProperty = property.replace(
18587
- /-([a-z])/g,
18588
- (match, letter) => letter.toUpperCase(),
18589
- );
18590
- style[camelCaseProperty] = value;
18591
- }
18592
- }
18593
- }
18594
-
18595
- return style;
18603
+ return mergeStyles(baseStyle, styleFromProps, "css");
18596
18604
  };
18597
18605
 
18598
18606
  const consumeSpacingProps = props => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Library of components including navigation to create frontend applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -34,7 +34,7 @@
34
34
  "prepublishOnly": "npm run build"
35
35
  },
36
36
  "dependencies": {
37
- "@jsenv/dom": "0.5.0",
37
+ "@jsenv/dom": "0.5.1",
38
38
  "@jsenv/humanize": "1.6.0"
39
39
  },
40
40
  "devDependencies": {
@@ -1,86 +1,26 @@
1
+ import { mergeStyles } from "@jsenv/dom";
2
+
1
3
  /**
2
4
  * Merges a component's base style with style received from props.
5
+ * Automatically normalizes style values (e.g., adds "px" units where needed).
3
6
  *
4
7
  * ```jsx
5
8
  * const MyButton = ({ style, children }) => (
6
- * <button style={withPropsStyle({ padding: '10px' }, style)}>
9
+ * <button style={withPropsStyle({ padding: 10 }, style)}>
7
10
  * {children}
8
11
  * </button>
9
12
  * );
10
13
  *
11
14
  * // Usage:
12
- * <MyButton style={{ color: 'red', fontSize: '14px' }} />
15
+ * <MyButton style={{ color: 'red', fontSize: 14 }} />
13
16
  * <MyButton style="color: blue; margin: 5px;" />
14
17
  * <MyButton /> // Just base styles
15
18
  * ```
16
19
  *
17
20
  * @param {string|object} baseStyle - The component's base style (string or object)
18
21
  * @param {string|object} [styleFromProps] - Additional style from props (optional)
19
- * @returns {object} The merged style object
22
+ * @returns {object} The merged and normalized style object
20
23
  */
21
24
  export const withPropsStyle = (baseStyle, styleFromProps) => {
22
- if (!styleFromProps) {
23
- return baseStyle;
24
- }
25
- if (!baseStyle) {
26
- return styleFromProps;
27
- }
28
-
29
- // Parse base style to object if it's a string
30
- const parsedBaseStyle =
31
- typeof baseStyle === "string"
32
- ? parseStyleString(baseStyle)
33
- : baseStyle || {};
34
- // Parse props style to object if it's a string
35
- const parsedPropsStyle =
36
- typeof styleFromProps === "string"
37
- ? parseStyleString(styleFromProps)
38
- : styleFromProps;
39
- // Merge styles with props taking priority
40
- return { ...parsedBaseStyle, ...parsedPropsStyle };
41
- };
42
-
43
- /**
44
- * Parses a CSS style string into a style object.
45
- * Handles CSS properties with proper camelCase conversion.
46
- *
47
- * @param {string} styleString - CSS style string like "color: red; font-size: 14px;"
48
- * @returns {object} Style object with camelCase properties
49
- */
50
- const parseStyleString = (styleString) => {
51
- const style = {};
52
-
53
- if (!styleString || typeof styleString !== "string") {
54
- return style;
55
- }
56
-
57
- // Split by semicolon and process each declaration
58
- const declarations = styleString.split(";");
59
-
60
- for (let declaration of declarations) {
61
- declaration = declaration.trim();
62
- if (!declaration) continue;
63
-
64
- const colonIndex = declaration.indexOf(":");
65
- if (colonIndex === -1) continue;
66
-
67
- const property = declaration.slice(0, colonIndex).trim();
68
- const value = declaration.slice(colonIndex + 1).trim();
69
-
70
- if (property && value) {
71
- // CSS custom properties (starting with --) should NOT be converted to camelCase
72
- if (property.startsWith("--")) {
73
- style[property] = value;
74
- } else {
75
- // Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
76
- const camelCaseProperty = property.replace(
77
- /-([a-z])/g,
78
- (match, letter) => letter.toUpperCase(),
79
- );
80
- style[camelCaseProperty] = value;
81
- }
82
- }
83
- }
84
-
85
- return style;
25
+ return mergeStyles(baseStyle, styleFromProps, "css");
86
26
  };