@jsenv/navi 0.5.0 → 0.5.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 +138 -17
- package/package.json +1 -1
- package/src/components/field/button.jsx +20 -7
- package/src/components/field/input_textual.jsx +16 -10
- package/src/components/props_composition/with_props_class_name.js +37 -0
- package/src/components/props_composition/with_props_style.js +81 -0
package/dist/jsenv_navi.js
CHANGED
|
@@ -20528,6 +20528,126 @@ const InputRadioWithAction = () => {
|
|
|
20528
20528
|
};
|
|
20529
20529
|
const InputRadioInsideForm = InputRadio;
|
|
20530
20530
|
|
|
20531
|
+
/**
|
|
20532
|
+
* Merges a component's base className with className received from props.
|
|
20533
|
+
*
|
|
20534
|
+
* ```jsx
|
|
20535
|
+
* const MyButton = ({ className, children }) => (
|
|
20536
|
+
* <button className={withPropsClassName("btn", className)}>
|
|
20537
|
+
* {children}
|
|
20538
|
+
* </button>
|
|
20539
|
+
* );
|
|
20540
|
+
*
|
|
20541
|
+
* // Usage:
|
|
20542
|
+
* <MyButton className="primary large" /> // Results in "btn primary large"
|
|
20543
|
+
* <MyButton /> // Results in "btn"
|
|
20544
|
+
* ```
|
|
20545
|
+
*
|
|
20546
|
+
* @param {string} baseClassName - The component's base CSS class name
|
|
20547
|
+
* @param {string} [classNameFromProps] - Additional className from props (optional)
|
|
20548
|
+
* @returns {string} The merged className string
|
|
20549
|
+
*/
|
|
20550
|
+
const withPropsClassName = (baseClassName, classNameFromProps) => {
|
|
20551
|
+
if (!classNameFromProps) {
|
|
20552
|
+
return baseClassName;
|
|
20553
|
+
}
|
|
20554
|
+
|
|
20555
|
+
// Trim and normalize whitespace from the props className
|
|
20556
|
+
const trimmedPropsClassName = classNameFromProps.trim();
|
|
20557
|
+
if (!trimmedPropsClassName) {
|
|
20558
|
+
return baseClassName;
|
|
20559
|
+
}
|
|
20560
|
+
|
|
20561
|
+
// Normalize multiple spaces to single spaces and combine
|
|
20562
|
+
const normalizedPropsClassName = trimmedPropsClassName.replace(/\s+/g, " ");
|
|
20563
|
+
if (!baseClassName) {
|
|
20564
|
+
return normalizedPropsClassName;
|
|
20565
|
+
}
|
|
20566
|
+
return `${baseClassName} ${normalizedPropsClassName}`;
|
|
20567
|
+
};
|
|
20568
|
+
|
|
20569
|
+
/**
|
|
20570
|
+
* Merges a component's base style with style received from props.
|
|
20571
|
+
*
|
|
20572
|
+
* ```jsx
|
|
20573
|
+
* const MyButton = ({ style, children }) => (
|
|
20574
|
+
* <button style={withPropsStyle({ padding: '10px' }, style)}>
|
|
20575
|
+
* {children}
|
|
20576
|
+
* </button>
|
|
20577
|
+
* );
|
|
20578
|
+
*
|
|
20579
|
+
* // Usage:
|
|
20580
|
+
* <MyButton style={{ color: 'red', fontSize: '14px' }} />
|
|
20581
|
+
* <MyButton style="color: blue; margin: 5px;" />
|
|
20582
|
+
* <MyButton /> // Just base styles
|
|
20583
|
+
* ```
|
|
20584
|
+
*
|
|
20585
|
+
* @param {string|object} baseStyle - The component's base style (string or object)
|
|
20586
|
+
* @param {string|object} [styleFromProps] - Additional style from props (optional)
|
|
20587
|
+
* @returns {object} The merged style object
|
|
20588
|
+
*/
|
|
20589
|
+
const withPropsStyle = (baseStyle, styleFromProps) => {
|
|
20590
|
+
if (!styleFromProps) {
|
|
20591
|
+
return baseStyle;
|
|
20592
|
+
}
|
|
20593
|
+
if (!baseStyle) {
|
|
20594
|
+
return styleFromProps;
|
|
20595
|
+
}
|
|
20596
|
+
|
|
20597
|
+
// Parse base style to object if it's a string
|
|
20598
|
+
const parsedBaseStyle =
|
|
20599
|
+
typeof baseStyle === "string"
|
|
20600
|
+
? parseStyleString(baseStyle)
|
|
20601
|
+
: baseStyle || {};
|
|
20602
|
+
// Parse props style to object if it's a string
|
|
20603
|
+
const parsedPropsStyle =
|
|
20604
|
+
typeof styleFromProps === "string"
|
|
20605
|
+
? parseStyleString(styleFromProps)
|
|
20606
|
+
: styleFromProps;
|
|
20607
|
+
// Merge styles with props taking priority
|
|
20608
|
+
return { ...parsedBaseStyle, ...parsedPropsStyle };
|
|
20609
|
+
};
|
|
20610
|
+
|
|
20611
|
+
/**
|
|
20612
|
+
* Parses a CSS style string into a style object.
|
|
20613
|
+
* Handles CSS properties with proper camelCase conversion.
|
|
20614
|
+
*
|
|
20615
|
+
* @param {string} styleString - CSS style string like "color: red; font-size: 14px;"
|
|
20616
|
+
* @returns {object} Style object with camelCase properties
|
|
20617
|
+
*/
|
|
20618
|
+
const parseStyleString = (styleString) => {
|
|
20619
|
+
const style = {};
|
|
20620
|
+
|
|
20621
|
+
if (!styleString || typeof styleString !== "string") {
|
|
20622
|
+
return style;
|
|
20623
|
+
}
|
|
20624
|
+
|
|
20625
|
+
// Split by semicolon and process each declaration
|
|
20626
|
+
const declarations = styleString.split(";");
|
|
20627
|
+
|
|
20628
|
+
for (let declaration of declarations) {
|
|
20629
|
+
declaration = declaration.trim();
|
|
20630
|
+
if (!declaration) continue;
|
|
20631
|
+
|
|
20632
|
+
const colonIndex = declaration.indexOf(":");
|
|
20633
|
+
if (colonIndex === -1) continue;
|
|
20634
|
+
|
|
20635
|
+
const property = declaration.slice(0, colonIndex).trim();
|
|
20636
|
+
const value = declaration.slice(colonIndex + 1).trim();
|
|
20637
|
+
|
|
20638
|
+
if (property && value) {
|
|
20639
|
+
// Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
|
|
20640
|
+
const camelCaseProperty = property.replace(/-([a-z])/g, (match, letter) =>
|
|
20641
|
+
letter.toUpperCase(),
|
|
20642
|
+
);
|
|
20643
|
+
|
|
20644
|
+
style[camelCaseProperty] = value;
|
|
20645
|
+
}
|
|
20646
|
+
}
|
|
20647
|
+
|
|
20648
|
+
return style;
|
|
20649
|
+
};
|
|
20650
|
+
|
|
20531
20651
|
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
20532
20652
|
@layer navi {
|
|
20533
20653
|
.navi_input {
|
|
@@ -20641,11 +20761,13 @@ const InputTextualBasic = forwardRef((props, ref) => {
|
|
|
20641
20761
|
autoFocus,
|
|
20642
20762
|
autoFocusVisible,
|
|
20643
20763
|
autoSelect,
|
|
20764
|
+
// visual
|
|
20644
20765
|
appearance = "navi",
|
|
20645
20766
|
accentColor,
|
|
20646
|
-
style,
|
|
20647
20767
|
width,
|
|
20648
20768
|
height,
|
|
20769
|
+
className,
|
|
20770
|
+
style,
|
|
20649
20771
|
...rest
|
|
20650
20772
|
} = props;
|
|
20651
20773
|
const innerRef = useRef();
|
|
@@ -20662,19 +20784,14 @@ const InputTextualBasic = forwardRef((props, ref) => {
|
|
|
20662
20784
|
});
|
|
20663
20785
|
useConstraints(innerRef, constraints);
|
|
20664
20786
|
const innerStyle = {
|
|
20665
|
-
|
|
20787
|
+
width,
|
|
20788
|
+
height
|
|
20666
20789
|
};
|
|
20667
|
-
if (width !== undefined) {
|
|
20668
|
-
innerStyle.width = width;
|
|
20669
|
-
}
|
|
20670
|
-
if (height !== undefined) {
|
|
20671
|
-
innerStyle.height = height;
|
|
20672
|
-
}
|
|
20673
20790
|
const inputTextual = jsx("input", {
|
|
20674
20791
|
...rest,
|
|
20675
20792
|
ref: innerRef,
|
|
20676
|
-
className: appearance === "navi" ? "navi_input" : undefined,
|
|
20677
|
-
style: innerStyle,
|
|
20793
|
+
className: withPropsClassName(appearance === "navi" ? "navi_input" : undefined, className),
|
|
20794
|
+
style: withPropsStyle(innerStyle, style),
|
|
20678
20795
|
type: type,
|
|
20679
20796
|
"data-value": uiState,
|
|
20680
20797
|
value: innerValue,
|
|
@@ -21350,6 +21467,11 @@ const Button = forwardRef((props, ref) => {
|
|
|
21350
21467
|
WithActionInsideForm: ButtonWithActionInsideForm
|
|
21351
21468
|
});
|
|
21352
21469
|
});
|
|
21470
|
+
const alignXMapping = {
|
|
21471
|
+
start: undefined,
|
|
21472
|
+
center: "center",
|
|
21473
|
+
end: "flex-end"
|
|
21474
|
+
};
|
|
21353
21475
|
const ButtonBasic = forwardRef((props, ref) => {
|
|
21354
21476
|
const contextLoading = useContext(LoadingContext);
|
|
21355
21477
|
const contextLoadingElement = useContext(LoadingElementContext);
|
|
@@ -21361,10 +21483,12 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
21361
21483
|
loading,
|
|
21362
21484
|
constraints = [],
|
|
21363
21485
|
autoFocus,
|
|
21486
|
+
// visual
|
|
21364
21487
|
appearance = "navi",
|
|
21365
21488
|
alignX = "start",
|
|
21366
|
-
style,
|
|
21367
21489
|
discrete,
|
|
21490
|
+
className,
|
|
21491
|
+
style,
|
|
21368
21492
|
children,
|
|
21369
21493
|
...rest
|
|
21370
21494
|
} = props;
|
|
@@ -21385,16 +21509,13 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
21385
21509
|
buttonChildren = children;
|
|
21386
21510
|
}
|
|
21387
21511
|
const innerStyle = {
|
|
21388
|
-
|
|
21512
|
+
"align-self": alignXMapping[alignX]
|
|
21389
21513
|
};
|
|
21390
|
-
if (alignX !== "start") {
|
|
21391
|
-
innerStyle["align-self"] = alignX === "center" ? "center" : "flex-end";
|
|
21392
|
-
}
|
|
21393
21514
|
return jsx("button", {
|
|
21394
21515
|
...rest,
|
|
21395
21516
|
ref: innerRef,
|
|
21396
|
-
className: appearance === "navi" ? "navi_button" : undefined,
|
|
21397
|
-
style: innerStyle,
|
|
21517
|
+
className: withPropsClassName(appearance === "navi" ? "navi_button" : undefined, className),
|
|
21518
|
+
style: withPropsStyle(innerStyle, style),
|
|
21398
21519
|
disabled: innerDisabled,
|
|
21399
21520
|
"data-discrete": discrete ? "" : undefined,
|
|
21400
21521
|
"data-readonly": innerReadOnly ? "" : undefined,
|
package/package.json
CHANGED
|
@@ -15,6 +15,8 @@ import { renderActionableComponent } from "../action_execution/render_actionable
|
|
|
15
15
|
import { useAction } from "../action_execution/use_action.js";
|
|
16
16
|
import { useExecuteAction } from "../action_execution/use_execute_action.js";
|
|
17
17
|
import { LoaderBackground } from "../loader/loader_background.jsx";
|
|
18
|
+
import { withPropsClassName } from "../props_composition/with_props_class_name.js";
|
|
19
|
+
import { withPropsStyle } from "../props_composition/with_props_style.js";
|
|
18
20
|
import { useAutoFocus } from "../use_auto_focus.js";
|
|
19
21
|
import { initCustomField } from "./custom_field.js";
|
|
20
22
|
import { useActionEvents } from "./use_action_events.js";
|
|
@@ -195,6 +197,11 @@ export const Button = forwardRef((props, ref) => {
|
|
|
195
197
|
});
|
|
196
198
|
});
|
|
197
199
|
|
|
200
|
+
const alignXMapping = {
|
|
201
|
+
start: undefined,
|
|
202
|
+
center: "center",
|
|
203
|
+
end: "flex-end",
|
|
204
|
+
};
|
|
198
205
|
const ButtonBasic = forwardRef((props, ref) => {
|
|
199
206
|
const contextLoading = useContext(LoadingContext);
|
|
200
207
|
const contextLoadingElement = useContext(LoadingElementContext);
|
|
@@ -206,10 +213,14 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
206
213
|
loading,
|
|
207
214
|
constraints = [],
|
|
208
215
|
autoFocus,
|
|
216
|
+
|
|
217
|
+
// visual
|
|
209
218
|
appearance = "navi",
|
|
210
219
|
alignX = "start",
|
|
211
|
-
style,
|
|
212
220
|
discrete,
|
|
221
|
+
className,
|
|
222
|
+
style,
|
|
223
|
+
|
|
213
224
|
children,
|
|
214
225
|
...rest
|
|
215
226
|
} = props;
|
|
@@ -230,17 +241,19 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
230
241
|
buttonChildren = children;
|
|
231
242
|
}
|
|
232
243
|
|
|
233
|
-
const innerStyle = {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
}
|
|
244
|
+
const innerStyle = {
|
|
245
|
+
"align-self": alignXMapping[alignX],
|
|
246
|
+
};
|
|
237
247
|
|
|
238
248
|
return (
|
|
239
249
|
<button
|
|
240
250
|
{...rest}
|
|
241
251
|
ref={innerRef}
|
|
242
|
-
className={
|
|
243
|
-
|
|
252
|
+
className={withPropsClassName(
|
|
253
|
+
appearance === "navi" ? "navi_button" : undefined,
|
|
254
|
+
className,
|
|
255
|
+
)}
|
|
256
|
+
style={withPropsStyle(innerStyle, style)}
|
|
244
257
|
disabled={innerDisabled}
|
|
245
258
|
data-discrete={discrete ? "" : undefined}
|
|
246
259
|
data-readonly={innerReadOnly ? "" : undefined}
|
|
@@ -32,6 +32,8 @@ import { renderActionableComponent } from "../action_execution/render_actionable
|
|
|
32
32
|
import { useActionBoundToOneParam } from "../action_execution/use_action.js";
|
|
33
33
|
import { useExecuteAction } from "../action_execution/use_execute_action.js";
|
|
34
34
|
import { LoadableInlineElement } from "../loader/loader_background.jsx";
|
|
35
|
+
import { withPropsClassName } from "../props_composition/with_props_class_name.js";
|
|
36
|
+
import { withPropsStyle } from "../props_composition/with_props_style.js";
|
|
35
37
|
import { useAutoFocus } from "../use_auto_focus.js";
|
|
36
38
|
import { initCustomField } from "./custom_field.js";
|
|
37
39
|
import { ReportReadOnlyOnLabelContext } from "./label.jsx";
|
|
@@ -163,11 +165,15 @@ const InputTextualBasic = forwardRef((props, ref) => {
|
|
|
163
165
|
autoFocus,
|
|
164
166
|
autoFocusVisible,
|
|
165
167
|
autoSelect,
|
|
168
|
+
|
|
169
|
+
// visual
|
|
166
170
|
appearance = "navi",
|
|
167
171
|
accentColor,
|
|
168
|
-
style,
|
|
169
172
|
width,
|
|
170
173
|
height,
|
|
174
|
+
className,
|
|
175
|
+
style,
|
|
176
|
+
|
|
171
177
|
...rest
|
|
172
178
|
} = props;
|
|
173
179
|
const innerRef = useRef();
|
|
@@ -188,19 +194,19 @@ const InputTextualBasic = forwardRef((props, ref) => {
|
|
|
188
194
|
});
|
|
189
195
|
useConstraints(innerRef, constraints);
|
|
190
196
|
|
|
191
|
-
const innerStyle = {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
if (height !== undefined) {
|
|
196
|
-
innerStyle.height = height;
|
|
197
|
-
}
|
|
197
|
+
const innerStyle = {
|
|
198
|
+
width,
|
|
199
|
+
height,
|
|
200
|
+
};
|
|
198
201
|
const inputTextual = (
|
|
199
202
|
<input
|
|
200
203
|
{...rest}
|
|
201
204
|
ref={innerRef}
|
|
202
|
-
className={
|
|
203
|
-
|
|
205
|
+
className={withPropsClassName(
|
|
206
|
+
appearance === "navi" ? "navi_input" : undefined,
|
|
207
|
+
className,
|
|
208
|
+
)}
|
|
209
|
+
style={withPropsStyle(innerStyle, style)}
|
|
204
210
|
type={type}
|
|
205
211
|
data-value={uiState}
|
|
206
212
|
value={innerValue}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges a component's base className with className received from props.
|
|
3
|
+
*
|
|
4
|
+
* ```jsx
|
|
5
|
+
* const MyButton = ({ className, children }) => (
|
|
6
|
+
* <button className={withPropsClassName("btn", className)}>
|
|
7
|
+
* {children}
|
|
8
|
+
* </button>
|
|
9
|
+
* );
|
|
10
|
+
*
|
|
11
|
+
* // Usage:
|
|
12
|
+
* <MyButton className="primary large" /> // Results in "btn primary large"
|
|
13
|
+
* <MyButton /> // Results in "btn"
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* @param {string} baseClassName - The component's base CSS class name
|
|
17
|
+
* @param {string} [classNameFromProps] - Additional className from props (optional)
|
|
18
|
+
* @returns {string} The merged className string
|
|
19
|
+
*/
|
|
20
|
+
export const withPropsClassName = (baseClassName, classNameFromProps) => {
|
|
21
|
+
if (!classNameFromProps) {
|
|
22
|
+
return baseClassName;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Trim and normalize whitespace from the props className
|
|
26
|
+
const trimmedPropsClassName = classNameFromProps.trim();
|
|
27
|
+
if (!trimmedPropsClassName) {
|
|
28
|
+
return baseClassName;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Normalize multiple spaces to single spaces and combine
|
|
32
|
+
const normalizedPropsClassName = trimmedPropsClassName.replace(/\s+/g, " ");
|
|
33
|
+
if (!baseClassName) {
|
|
34
|
+
return normalizedPropsClassName;
|
|
35
|
+
}
|
|
36
|
+
return `${baseClassName} ${normalizedPropsClassName}`;
|
|
37
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merges a component's base style with style received from props.
|
|
3
|
+
*
|
|
4
|
+
* ```jsx
|
|
5
|
+
* const MyButton = ({ style, children }) => (
|
|
6
|
+
* <button style={withPropsStyle({ padding: '10px' }, style)}>
|
|
7
|
+
* {children}
|
|
8
|
+
* </button>
|
|
9
|
+
* );
|
|
10
|
+
*
|
|
11
|
+
* // Usage:
|
|
12
|
+
* <MyButton style={{ color: 'red', fontSize: '14px' }} />
|
|
13
|
+
* <MyButton style="color: blue; margin: 5px;" />
|
|
14
|
+
* <MyButton /> // Just base styles
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* @param {string|object} baseStyle - The component's base style (string or object)
|
|
18
|
+
* @param {string|object} [styleFromProps] - Additional style from props (optional)
|
|
19
|
+
* @returns {object} The merged style object
|
|
20
|
+
*/
|
|
21
|
+
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
|
+
// Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
|
|
72
|
+
const camelCaseProperty = property.replace(/-([a-z])/g, (match, letter) =>
|
|
73
|
+
letter.toUpperCase(),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
style[camelCaseProperty] = value;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return style;
|
|
81
|
+
};
|