@jsenv/navi 0.6.2 → 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.
- package/dist/jsenv_navi.js +320 -154
- package/index.js +5 -0
- package/package.json +2 -2
- package/src/components/action_execution/use_execute_action.js +3 -1
- package/src/components/callout/callout.js +2 -2
- package/src/components/field/button.jsx +4 -13
- package/src/components/field/form.jsx +2 -0
- package/src/components/layout/demos/demo_flex.html +506 -0
- package/src/components/layout/flex.jsx +154 -0
- package/src/components/layout/spacing.jsx +78 -67
- package/src/components/props_composition/with_props_style.js +7 -67
package/dist/jsenv_navi.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
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[
|
|
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,
|
|
1267
|
+
const mergedStyles = mergeStyles(styles, stylesToSet);
|
|
1198
1268
|
elementData.styles = mergedStyles;
|
|
1199
1269
|
updateAnimationStyles(animation, mergedStyles);
|
|
1200
1270
|
};
|
|
@@ -11735,7 +11805,7 @@ const openCallout = (
|
|
|
11735
11805
|
level = "warning",
|
|
11736
11806
|
onClose,
|
|
11737
11807
|
closeOnClickOutside = level === "info",
|
|
11738
|
-
|
|
11808
|
+
showErrorStack,
|
|
11739
11809
|
debug = false,
|
|
11740
11810
|
} = {},
|
|
11741
11811
|
) => {
|
|
@@ -11803,7 +11873,7 @@ const openCallout = (
|
|
|
11803
11873
|
if (Error.isError(newMessage)) {
|
|
11804
11874
|
const error = newMessage;
|
|
11805
11875
|
newMessage = error.message;
|
|
11806
|
-
if (
|
|
11876
|
+
if (showErrorStack && error.stack) {
|
|
11807
11877
|
newMessage += `<pre class="navi_callout_error_stack">${escapeHtml(String(error.stack))}</pre>`;
|
|
11808
11878
|
}
|
|
11809
11879
|
}
|
|
@@ -13766,6 +13836,7 @@ const useExecuteAction = (
|
|
|
13766
13836
|
elementRef,
|
|
13767
13837
|
{
|
|
13768
13838
|
errorEffect = "show_validation_message", // "show_validation_message" or "throw"
|
|
13839
|
+
errorMapping,
|
|
13769
13840
|
} = {},
|
|
13770
13841
|
) => {
|
|
13771
13842
|
// see https://medium.com/trabe/catching-asynchronous-errors-in-react-using-error-boundaries-5e8a5fd7b971
|
|
@@ -13783,7 +13854,8 @@ const useExecuteAction = (
|
|
|
13783
13854
|
const validationMessageTargetRef = useRef(null);
|
|
13784
13855
|
const addErrorMessage = (error) => {
|
|
13785
13856
|
const validationMessageTarget = validationMessageTargetRef.current;
|
|
13786
|
-
|
|
13857
|
+
const errorMapped = errorMapping ? errorMapping(error) : error;
|
|
13858
|
+
addCustomMessage(validationMessageTarget, "action_error", errorMapped, {
|
|
13787
13859
|
// This error should not prevent <form> submission
|
|
13788
13860
|
// so whenever user tries to submit the form the error is cleared
|
|
13789
13861
|
// (Hitting enter key, clicking on submit button, etc. would allow to re-submit the form in error state)
|
|
@@ -18508,160 +18580,108 @@ const useConstraints = (elementRef, constraints, targetSelector) => {
|
|
|
18508
18580
|
|
|
18509
18581
|
/**
|
|
18510
18582
|
* Merges a component's base style with style received from props.
|
|
18583
|
+
* Automatically normalizes style values (e.g., adds "px" units where needed).
|
|
18511
18584
|
*
|
|
18512
18585
|
* ```jsx
|
|
18513
18586
|
* const MyButton = ({ style, children }) => (
|
|
18514
|
-
* <button style={withPropsStyle({ padding:
|
|
18587
|
+
* <button style={withPropsStyle({ padding: 10 }, style)}>
|
|
18515
18588
|
* {children}
|
|
18516
18589
|
* </button>
|
|
18517
18590
|
* );
|
|
18518
18591
|
*
|
|
18519
18592
|
* // Usage:
|
|
18520
|
-
* <MyButton style={{ color: 'red', fontSize:
|
|
18593
|
+
* <MyButton style={{ color: 'red', fontSize: 14 }} />
|
|
18521
18594
|
* <MyButton style="color: blue; margin: 5px;" />
|
|
18522
18595
|
* <MyButton /> // Just base styles
|
|
18523
18596
|
* ```
|
|
18524
18597
|
*
|
|
18525
18598
|
* @param {string|object} baseStyle - The component's base style (string or object)
|
|
18526
18599
|
* @param {string|object} [styleFromProps] - Additional style from props (optional)
|
|
18527
|
-
* @returns {object} The merged style object
|
|
18600
|
+
* @returns {object} The merged and normalized style object
|
|
18528
18601
|
*/
|
|
18529
18602
|
const withPropsStyle = (baseStyle, styleFromProps) => {
|
|
18530
|
-
|
|
18531
|
-
return baseStyle;
|
|
18532
|
-
}
|
|
18533
|
-
if (!baseStyle) {
|
|
18534
|
-
return styleFromProps;
|
|
18535
|
-
}
|
|
18536
|
-
|
|
18537
|
-
// Parse base style to object if it's a string
|
|
18538
|
-
const parsedBaseStyle =
|
|
18539
|
-
typeof baseStyle === "string"
|
|
18540
|
-
? parseStyleString(baseStyle)
|
|
18541
|
-
: baseStyle || {};
|
|
18542
|
-
// Parse props style to object if it's a string
|
|
18543
|
-
const parsedPropsStyle =
|
|
18544
|
-
typeof styleFromProps === "string"
|
|
18545
|
-
? parseStyleString(styleFromProps)
|
|
18546
|
-
: styleFromProps;
|
|
18547
|
-
// Merge styles with props taking priority
|
|
18548
|
-
return { ...parsedBaseStyle, ...parsedPropsStyle };
|
|
18603
|
+
return mergeStyles(baseStyle, styleFromProps, "css");
|
|
18549
18604
|
};
|
|
18550
18605
|
|
|
18551
|
-
|
|
18552
|
-
* Parses a CSS style string into a style object.
|
|
18553
|
-
* Handles CSS properties with proper camelCase conversion.
|
|
18554
|
-
*
|
|
18555
|
-
* @param {string} styleString - CSS style string like "color: red; font-size: 14px;"
|
|
18556
|
-
* @returns {object} Style object with camelCase properties
|
|
18557
|
-
*/
|
|
18558
|
-
const parseStyleString = (styleString) => {
|
|
18606
|
+
const consumeSpacingProps = props => {
|
|
18559
18607
|
const style = {};
|
|
18560
|
-
|
|
18561
|
-
|
|
18562
|
-
|
|
18563
|
-
|
|
18564
|
-
|
|
18565
|
-
|
|
18566
|
-
|
|
18567
|
-
|
|
18568
|
-
|
|
18569
|
-
|
|
18570
|
-
|
|
18571
|
-
|
|
18572
|
-
|
|
18573
|
-
|
|
18574
|
-
|
|
18575
|
-
|
|
18576
|
-
|
|
18577
|
-
|
|
18578
|
-
if (
|
|
18579
|
-
|
|
18580
|
-
|
|
18581
|
-
|
|
18582
|
-
|
|
18583
|
-
|
|
18584
|
-
|
|
18585
|
-
|
|
18586
|
-
|
|
18587
|
-
|
|
18588
|
-
|
|
18589
|
-
|
|
18608
|
+
{
|
|
18609
|
+
const margin = props.margin;
|
|
18610
|
+
const marginX = props.marginX;
|
|
18611
|
+
const marginY = props.marginY;
|
|
18612
|
+
const marginLeft = props.marginLeft;
|
|
18613
|
+
const marginRight = props.marginRight;
|
|
18614
|
+
const marginTop = props.marginTop;
|
|
18615
|
+
const marginBottom = props.marginBottom;
|
|
18616
|
+
delete props.margin;
|
|
18617
|
+
delete props.marginX;
|
|
18618
|
+
delete props.marginY;
|
|
18619
|
+
delete props.marginLeft;
|
|
18620
|
+
delete props.marginRight;
|
|
18621
|
+
delete props.marginTop;
|
|
18622
|
+
delete props.marginBottom;
|
|
18623
|
+
if (margin !== undefined) {
|
|
18624
|
+
style.margin = margin;
|
|
18625
|
+
}
|
|
18626
|
+
if (marginLeft !== undefined) {
|
|
18627
|
+
style.marginLeft = marginLeft;
|
|
18628
|
+
} else if (marginX !== undefined) {
|
|
18629
|
+
style.marginLeft = marginX;
|
|
18630
|
+
}
|
|
18631
|
+
if (marginRight !== undefined) {
|
|
18632
|
+
style.marginRight = marginRight;
|
|
18633
|
+
} else if (marginX !== undefined) {
|
|
18634
|
+
style.marginRight = marginX;
|
|
18635
|
+
}
|
|
18636
|
+
if (marginTop !== undefined) {
|
|
18637
|
+
style.marginTop = marginTop;
|
|
18638
|
+
} else if (marginY !== undefined) {
|
|
18639
|
+
style.marginTop = marginY;
|
|
18640
|
+
}
|
|
18641
|
+
if (marginBottom !== undefined) {
|
|
18642
|
+
style.marginBottom = marginBottom;
|
|
18643
|
+
} else if (marginY !== undefined) {
|
|
18644
|
+
style.marginBottom = marginY;
|
|
18590
18645
|
}
|
|
18591
18646
|
}
|
|
18592
|
-
|
|
18593
|
-
|
|
18594
|
-
|
|
18595
|
-
|
|
18596
|
-
const
|
|
18597
|
-
|
|
18598
|
-
|
|
18599
|
-
|
|
18600
|
-
|
|
18601
|
-
|
|
18647
|
+
{
|
|
18648
|
+
const padding = props.padding;
|
|
18649
|
+
const paddingX = props.paddingX;
|
|
18650
|
+
const paddingY = props.paddingY;
|
|
18651
|
+
const paddingLeft = props.paddingLeft;
|
|
18652
|
+
const paddingRight = props.paddingRight;
|
|
18653
|
+
const paddingTop = props.paddingTop;
|
|
18654
|
+
const paddingBottom = props.paddingBottom;
|
|
18655
|
+
delete props.padding;
|
|
18656
|
+
delete props.paddingX;
|
|
18657
|
+
delete props.paddingY;
|
|
18658
|
+
delete props.paddingLeft;
|
|
18659
|
+
delete props.paddingRight;
|
|
18660
|
+
delete props.paddingTop;
|
|
18661
|
+
delete props.paddingBottom;
|
|
18662
|
+
if (padding !== undefined) {
|
|
18663
|
+
style.padding = padding;
|
|
18664
|
+
}
|
|
18665
|
+
if (paddingLeft !== undefined) {
|
|
18666
|
+
style.paddingLeft = paddingLeft;
|
|
18667
|
+
} else if (paddingX !== undefined) {
|
|
18668
|
+
style.paddingLeft = paddingX;
|
|
18669
|
+
}
|
|
18670
|
+
if (paddingRight !== undefined) {
|
|
18671
|
+
style.paddingRight = paddingRight;
|
|
18672
|
+
} else if (paddingX !== undefined) {
|
|
18673
|
+
style.paddingRight = paddingX;
|
|
18674
|
+
}
|
|
18675
|
+
if (paddingTop !== undefined) {
|
|
18676
|
+
style.paddingTop = paddingTop;
|
|
18677
|
+
} else if (paddingY !== undefined) {
|
|
18678
|
+
style.paddingTop = paddingY;
|
|
18679
|
+
}
|
|
18680
|
+
if (paddingBottom !== undefined) {
|
|
18681
|
+
style.paddingBottom = paddingBottom;
|
|
18682
|
+
} else if (paddingY !== undefined) {
|
|
18683
|
+
style.paddingBottom = paddingY;
|
|
18602
18684
|
}
|
|
18603
|
-
return undefined;
|
|
18604
|
-
};
|
|
18605
|
-
const margin = consume("margin");
|
|
18606
|
-
const marginX = consume("marginX");
|
|
18607
|
-
const marginY = consume("marginY");
|
|
18608
|
-
const marginLeft = consume("marginLeft");
|
|
18609
|
-
const marginRight = consume("marginRight");
|
|
18610
|
-
const marginTop = consume("marginTop");
|
|
18611
|
-
const marginBottom = consume("marginBottom");
|
|
18612
|
-
const style = {};
|
|
18613
|
-
if (margin !== undefined) {
|
|
18614
|
-
style.margin = margin;
|
|
18615
|
-
}
|
|
18616
|
-
if (marginLeft !== undefined) {
|
|
18617
|
-
style.marginLeft = marginLeft;
|
|
18618
|
-
} else if (marginX !== undefined) {
|
|
18619
|
-
style.marginLeft = marginX;
|
|
18620
|
-
}
|
|
18621
|
-
if (marginRight !== undefined) {
|
|
18622
|
-
style.marginRight = marginRight;
|
|
18623
|
-
} else if (marginX !== undefined) {
|
|
18624
|
-
style.marginRight = marginX;
|
|
18625
|
-
}
|
|
18626
|
-
if (marginTop !== undefined) {
|
|
18627
|
-
style.marginTop = marginTop;
|
|
18628
|
-
} else if (marginY !== undefined) {
|
|
18629
|
-
style.marginTop = marginY;
|
|
18630
|
-
}
|
|
18631
|
-
if (marginBottom !== undefined) {
|
|
18632
|
-
style.marginBottom = marginBottom;
|
|
18633
|
-
} else if (marginY !== undefined) {
|
|
18634
|
-
style.marginBottom = marginY;
|
|
18635
|
-
}
|
|
18636
|
-
const padding = consume("padding");
|
|
18637
|
-
const paddingX = consume("paddingX");
|
|
18638
|
-
const paddingY = consume("paddingY");
|
|
18639
|
-
const paddingLeft = consume("paddingLeft");
|
|
18640
|
-
const paddingRight = consume("paddingRight");
|
|
18641
|
-
const paddingTop = consume("paddingTop");
|
|
18642
|
-
const paddingBottom = consume("paddingBottom");
|
|
18643
|
-
if (padding !== undefined) {
|
|
18644
|
-
style.padding = padding;
|
|
18645
|
-
}
|
|
18646
|
-
if (paddingLeft !== undefined) {
|
|
18647
|
-
style.paddingLeft = paddingLeft;
|
|
18648
|
-
} else if (paddingX !== undefined) {
|
|
18649
|
-
style.paddingLeft = paddingX;
|
|
18650
|
-
}
|
|
18651
|
-
if (paddingRight !== undefined) {
|
|
18652
|
-
style.paddingRight = paddingRight;
|
|
18653
|
-
} else if (paddingX !== undefined) {
|
|
18654
|
-
style.paddingRight = paddingX;
|
|
18655
|
-
}
|
|
18656
|
-
if (paddingTop !== undefined) {
|
|
18657
|
-
style.paddingTop = paddingTop;
|
|
18658
|
-
} else if (paddingY !== undefined) {
|
|
18659
|
-
style.paddingTop = paddingY;
|
|
18660
|
-
}
|
|
18661
|
-
if (paddingBottom !== undefined) {
|
|
18662
|
-
style.paddingBottom = paddingBottom;
|
|
18663
|
-
} else if (paddingY !== undefined) {
|
|
18664
|
-
style.paddingBottom = paddingY;
|
|
18665
18685
|
}
|
|
18666
18686
|
return style;
|
|
18667
18687
|
};
|
|
@@ -21352,6 +21372,153 @@ const Editable = forwardRef((props, ref) => {
|
|
|
21352
21372
|
});
|
|
21353
21373
|
});
|
|
21354
21374
|
|
|
21375
|
+
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
21376
|
+
.navi_flex_row {
|
|
21377
|
+
display: flex;
|
|
21378
|
+
flex-direction: row;
|
|
21379
|
+
align-items: center;
|
|
21380
|
+
gap: 0;
|
|
21381
|
+
}
|
|
21382
|
+
|
|
21383
|
+
.navi_flex_column {
|
|
21384
|
+
display: flex;
|
|
21385
|
+
flex-direction: column;
|
|
21386
|
+
align-items: center;
|
|
21387
|
+
gap: 0;
|
|
21388
|
+
}
|
|
21389
|
+
|
|
21390
|
+
.navi_flex_item {
|
|
21391
|
+
flex-shrink: 0;
|
|
21392
|
+
}
|
|
21393
|
+
`;
|
|
21394
|
+
const FlexDirectionContext = createContext();
|
|
21395
|
+
const FlexRow = ({
|
|
21396
|
+
alignY,
|
|
21397
|
+
gap,
|
|
21398
|
+
style,
|
|
21399
|
+
children,
|
|
21400
|
+
...rest
|
|
21401
|
+
}) => {
|
|
21402
|
+
const innerStyle = withPropsStyle({
|
|
21403
|
+
alignItems: alignY,
|
|
21404
|
+
gap,
|
|
21405
|
+
...consumeSpacingProps(rest)
|
|
21406
|
+
}, style);
|
|
21407
|
+
return jsx("div", {
|
|
21408
|
+
...rest,
|
|
21409
|
+
className: "navi_flex_row",
|
|
21410
|
+
style: innerStyle,
|
|
21411
|
+
children: jsx(FlexDirectionContext.Provider, {
|
|
21412
|
+
value: "row",
|
|
21413
|
+
children: children
|
|
21414
|
+
})
|
|
21415
|
+
});
|
|
21416
|
+
};
|
|
21417
|
+
const FlexColumn = ({
|
|
21418
|
+
alignX,
|
|
21419
|
+
gap,
|
|
21420
|
+
style,
|
|
21421
|
+
children,
|
|
21422
|
+
...rest
|
|
21423
|
+
}) => {
|
|
21424
|
+
const innerStyle = withPropsStyle({
|
|
21425
|
+
alignItems: alignX,
|
|
21426
|
+
gap,
|
|
21427
|
+
...consumeSpacingProps(rest)
|
|
21428
|
+
}, style);
|
|
21429
|
+
return jsx("div", {
|
|
21430
|
+
...rest,
|
|
21431
|
+
className: "navi_flex_column",
|
|
21432
|
+
style: innerStyle,
|
|
21433
|
+
children: jsx(FlexDirectionContext.Provider, {
|
|
21434
|
+
value: "column",
|
|
21435
|
+
children: children
|
|
21436
|
+
})
|
|
21437
|
+
});
|
|
21438
|
+
};
|
|
21439
|
+
const useConsumAlignProps = props => {
|
|
21440
|
+
const flexDirection = useContext(FlexDirectionContext);
|
|
21441
|
+
const alignX = props.alignX;
|
|
21442
|
+
const alignY = props.alignY;
|
|
21443
|
+
delete props.alignX;
|
|
21444
|
+
delete props.alignY;
|
|
21445
|
+
const style = {};
|
|
21446
|
+
if (flexDirection === "row") {
|
|
21447
|
+
// In row direction: alignX controls justify-content, alignY controls align-self
|
|
21448
|
+
// Default alignY is "center" from CSS, so only set alignSelf when different
|
|
21449
|
+
if (alignY !== undefined && alignY !== "center") {
|
|
21450
|
+
style.alignSelf = alignY;
|
|
21451
|
+
}
|
|
21452
|
+
// For row, alignX uses auto margins for positioning
|
|
21453
|
+
// NOTE: Auto margins only work effectively for positioning individual items.
|
|
21454
|
+
// When multiple adjacent items have the same auto margin alignment (e.g., alignX="end"),
|
|
21455
|
+
// only the first item will be positioned as expected because subsequent items
|
|
21456
|
+
// will be positioned relative to the previous item's margins, not the container edge.
|
|
21457
|
+
if (alignX !== undefined) {
|
|
21458
|
+
if (alignX === "start") {
|
|
21459
|
+
style.marginRight = "auto";
|
|
21460
|
+
} else if (alignX === "end") {
|
|
21461
|
+
style.marginLeft = "auto";
|
|
21462
|
+
} else if (alignX === "center") {
|
|
21463
|
+
style.marginLeft = "auto";
|
|
21464
|
+
style.marginRight = "auto";
|
|
21465
|
+
}
|
|
21466
|
+
}
|
|
21467
|
+
} else if (flexDirection === "column") {
|
|
21468
|
+
// In column direction: alignX controls align-self, alignY uses auto margins
|
|
21469
|
+
// Default alignX is "center" from CSS, so only set alignSelf when different
|
|
21470
|
+
if (alignX !== undefined && alignX !== "center") {
|
|
21471
|
+
style.alignSelf = alignX;
|
|
21472
|
+
}
|
|
21473
|
+
// For column, alignY uses auto margins for positioning
|
|
21474
|
+
// NOTE: Same auto margin limitation applies - multiple adjacent items with
|
|
21475
|
+
// the same alignY won't all position relative to container edges.
|
|
21476
|
+
if (alignY !== undefined) {
|
|
21477
|
+
if (alignY === "start") {
|
|
21478
|
+
style.marginBottom = "auto";
|
|
21479
|
+
} else if (alignY === "end") {
|
|
21480
|
+
style.marginTop = "auto";
|
|
21481
|
+
} else if (alignY === "center") {
|
|
21482
|
+
style.marginTop = "auto";
|
|
21483
|
+
style.marginBottom = "auto";
|
|
21484
|
+
}
|
|
21485
|
+
}
|
|
21486
|
+
}
|
|
21487
|
+
return style;
|
|
21488
|
+
};
|
|
21489
|
+
const FlexItem = ({
|
|
21490
|
+
alignX,
|
|
21491
|
+
alignY,
|
|
21492
|
+
grow,
|
|
21493
|
+
shrink,
|
|
21494
|
+
className,
|
|
21495
|
+
style,
|
|
21496
|
+
children,
|
|
21497
|
+
...rest
|
|
21498
|
+
}) => {
|
|
21499
|
+
const flexDirection = useContext(FlexDirectionContext);
|
|
21500
|
+
if (!flexDirection) {
|
|
21501
|
+
console.warn("FlexItem must be used within a FlexRow or FlexColumn component.");
|
|
21502
|
+
}
|
|
21503
|
+
const innerClassName = withPropsClassName("navi_flex_item", className);
|
|
21504
|
+
const alignStyle = useConsumAlignProps({
|
|
21505
|
+
alignX,
|
|
21506
|
+
alignY
|
|
21507
|
+
});
|
|
21508
|
+
const innerStyle = withPropsStyle({
|
|
21509
|
+
flexGrow: grow ? 1 : undefined,
|
|
21510
|
+
flexShrink: shrink ? 1 : undefined,
|
|
21511
|
+
...consumeSpacingProps(rest),
|
|
21512
|
+
...alignStyle
|
|
21513
|
+
}, style);
|
|
21514
|
+
return jsx("div", {
|
|
21515
|
+
...rest,
|
|
21516
|
+
className: innerClassName,
|
|
21517
|
+
style: innerStyle,
|
|
21518
|
+
children: children
|
|
21519
|
+
});
|
|
21520
|
+
};
|
|
21521
|
+
|
|
21355
21522
|
const useFormEvents = (
|
|
21356
21523
|
elementRef,
|
|
21357
21524
|
{
|
|
@@ -21573,7 +21740,8 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
21573
21740
|
autoFocus,
|
|
21574
21741
|
// visual
|
|
21575
21742
|
appearance = "navi",
|
|
21576
|
-
alignX
|
|
21743
|
+
alignX,
|
|
21744
|
+
alignY,
|
|
21577
21745
|
discrete,
|
|
21578
21746
|
className,
|
|
21579
21747
|
style,
|
|
@@ -21599,13 +21767,9 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
21599
21767
|
const innerClassName = withPropsClassName(appearance === "navi" ? "navi_button" : undefined, className);
|
|
21600
21768
|
const innerStyle = withPropsStyle({
|
|
21601
21769
|
...consumeSpacingProps(rest),
|
|
21602
|
-
...(
|
|
21603
|
-
|
|
21604
|
-
|
|
21605
|
-
marginRight: "auto"
|
|
21606
|
-
} : {
|
|
21607
|
-
alignSelf: "end",
|
|
21608
|
-
marginLeft: "auto"
|
|
21770
|
+
...useConsumAlignProps({
|
|
21771
|
+
alignX,
|
|
21772
|
+
alignY
|
|
21609
21773
|
})
|
|
21610
21774
|
}, style);
|
|
21611
21775
|
return jsx("button", {
|
|
@@ -22171,6 +22335,7 @@ const FormWithAction = forwardRef((props, ref) => {
|
|
|
22171
22335
|
method,
|
|
22172
22336
|
actionErrorEffect = "show_validation_message",
|
|
22173
22337
|
// "show_validation_message" or "throw"
|
|
22338
|
+
errorMapping,
|
|
22174
22339
|
onActionPrevented,
|
|
22175
22340
|
onActionStart,
|
|
22176
22341
|
onActionAbort,
|
|
@@ -22184,7 +22349,8 @@ const FormWithAction = forwardRef((props, ref) => {
|
|
|
22184
22349
|
useImperativeHandle(ref, () => innerRef.current);
|
|
22185
22350
|
const [actionBoundToUIState] = useActionBoundToOneParam(action, uiState);
|
|
22186
22351
|
const executeAction = useExecuteAction(innerRef, {
|
|
22187
|
-
errorEffect: actionErrorEffect
|
|
22352
|
+
errorEffect: actionErrorEffect,
|
|
22353
|
+
errorMapping
|
|
22188
22354
|
});
|
|
22189
22355
|
const {
|
|
22190
22356
|
actionPending,
|
|
@@ -28212,4 +28378,4 @@ const useDependenciesDiff = (inputs) => {
|
|
|
28212
28378
|
return diffRef.current;
|
|
28213
28379
|
};
|
|
28214
28380
|
|
|
28215
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, Icon, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, Spacing, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, Text, TextAndCount, Thead, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, defineRoutes, enableDebugActions, enableDebugOnDocumentLoading, goBack, goForward, goTo, isCellSelected, isColumnSelected, isRowSelected, openCallout, rawUrlPart, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, valueInLocalStorage };
|
|
28381
|
+
export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FlexColumn, FlexItem, FlexRow, FontSizedSvg, Form, Icon, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, Spacing, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, Text, TextAndCount, Thead, Tr, UITransition, actionIntegratedVia, addCustomMessage, createAction, createSelectionKeyboardShortcuts, createUniqueValueConstraint, defineRoutes, enableDebugActions, enableDebugOnDocumentLoading, goBack, goForward, goTo, isCellSelected, isColumnSelected, isRowSelected, openCallout, rawUrlPart, reload, removeCustomMessage, rerunActions, resource, setBaseUrl, stopLoad, stringifyTableSelectionValue, updateActions, useActionData, useActionStatus, useCellsAndColumns, useDependenciesDiff, useDocumentState, useDocumentUrl, useEditionController, useFocusGroup, useKeyboardShortcuts, useNavState, useRouteStatus, useRunOnMount, useSelectableElement, useSelectionController, useSignalSync, useStateArray, valueInLocalStorage };
|
package/index.js
CHANGED
|
@@ -93,6 +93,11 @@ export { TextAndCount } from "./src/components/text/text_and_count.jsx";
|
|
|
93
93
|
// Callout, dialogs, ...
|
|
94
94
|
export { openCallout } from "./src/components/callout/callout.js";
|
|
95
95
|
// Layout
|
|
96
|
+
export {
|
|
97
|
+
FlexColumn,
|
|
98
|
+
FlexItem,
|
|
99
|
+
FlexRow,
|
|
100
|
+
} from "./src/components/layout/flex.jsx";
|
|
96
101
|
export { Spacing } from "./src/components/layout/spacing.jsx";
|
|
97
102
|
|
|
98
103
|
// Validation
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/navi",
|
|
3
|
-
"version": "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.
|
|
37
|
+
"@jsenv/dom": "0.5.1",
|
|
38
38
|
"@jsenv/humanize": "1.6.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
@@ -11,6 +11,7 @@ export const useExecuteAction = (
|
|
|
11
11
|
elementRef,
|
|
12
12
|
{
|
|
13
13
|
errorEffect = "show_validation_message", // "show_validation_message" or "throw"
|
|
14
|
+
errorMapping,
|
|
14
15
|
} = {},
|
|
15
16
|
) => {
|
|
16
17
|
// see https://medium.com/trabe/catching-asynchronous-errors-in-react-using-error-boundaries-5e8a5fd7b971
|
|
@@ -28,7 +29,8 @@ export const useExecuteAction = (
|
|
|
28
29
|
const validationMessageTargetRef = useRef(null);
|
|
29
30
|
const addErrorMessage = (error) => {
|
|
30
31
|
const validationMessageTarget = validationMessageTargetRef.current;
|
|
31
|
-
|
|
32
|
+
const errorMapped = errorMapping ? errorMapping(error) : error;
|
|
33
|
+
addCustomMessage(validationMessageTarget, "action_error", errorMapped, {
|
|
32
34
|
// This error should not prevent <form> submission
|
|
33
35
|
// so whenever user tries to submit the form the error is cleared
|
|
34
36
|
// (Hitting enter key, clicking on submit button, etc. would allow to re-submit the form in error state)
|
|
@@ -48,7 +48,7 @@ export const openCallout = (
|
|
|
48
48
|
level = "warning",
|
|
49
49
|
onClose,
|
|
50
50
|
closeOnClickOutside = level === "info",
|
|
51
|
-
|
|
51
|
+
showErrorStack,
|
|
52
52
|
debug = false,
|
|
53
53
|
} = {},
|
|
54
54
|
) => {
|
|
@@ -116,7 +116,7 @@ export const openCallout = (
|
|
|
116
116
|
if (Error.isError(newMessage)) {
|
|
117
117
|
const error = newMessage;
|
|
118
118
|
newMessage = error.message;
|
|
119
|
-
if (
|
|
119
|
+
if (showErrorStack && error.stack) {
|
|
120
120
|
newMessage += `<pre class="navi_callout_error_stack">${escapeHtml(String(error.stack))}</pre>`;
|
|
121
121
|
}
|
|
122
122
|
}
|