@jsenv/navi 0.5.4 → 0.6.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.
@@ -18506,6 +18506,178 @@ const useConstraints = (elementRef, constraints, targetSelector) => {
18506
18506
  }, constraints);
18507
18507
  };
18508
18508
 
18509
+ /**
18510
+ * Merges a component's base style with style received from props.
18511
+ *
18512
+ * ```jsx
18513
+ * const MyButton = ({ style, children }) => (
18514
+ * <button style={withPropsStyle({ padding: '10px' }, style)}>
18515
+ * {children}
18516
+ * </button>
18517
+ * );
18518
+ *
18519
+ * // Usage:
18520
+ * <MyButton style={{ color: 'red', fontSize: '14px' }} />
18521
+ * <MyButton style="color: blue; margin: 5px;" />
18522
+ * <MyButton /> // Just base styles
18523
+ * ```
18524
+ *
18525
+ * @param {string|object} baseStyle - The component's base style (string or object)
18526
+ * @param {string|object} [styleFromProps] - Additional style from props (optional)
18527
+ * @returns {object} The merged style object
18528
+ */
18529
+ const withPropsStyle = (baseStyle, styleFromProps) => {
18530
+ if (!styleFromProps) {
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 };
18549
+ };
18550
+
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) => {
18559
+ const style = {};
18560
+
18561
+ if (!styleString || typeof styleString !== "string") {
18562
+ return style;
18563
+ }
18564
+
18565
+ // Split by semicolon and process each declaration
18566
+ const declarations = styleString.split(";");
18567
+
18568
+ for (let declaration of declarations) {
18569
+ declaration = declaration.trim();
18570
+ if (!declaration) continue;
18571
+
18572
+ const colonIndex = declaration.indexOf(":");
18573
+ if (colonIndex === -1) continue;
18574
+
18575
+ const property = declaration.slice(0, colonIndex).trim();
18576
+ const value = declaration.slice(colonIndex + 1).trim();
18577
+
18578
+ if (property && value) {
18579
+ // CSS custom properties (starting with --) should NOT be converted to camelCase
18580
+ if (property.startsWith("--")) {
18581
+ style[property] = value;
18582
+ } else {
18583
+ // Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
18584
+ const camelCaseProperty = property.replace(
18585
+ /-([a-z])/g,
18586
+ (match, letter) => letter.toUpperCase(),
18587
+ );
18588
+ style[camelCaseProperty] = value;
18589
+ }
18590
+ }
18591
+ }
18592
+
18593
+ return style;
18594
+ };
18595
+
18596
+ const consumeSpacingProps = props => {
18597
+ const consume = name => {
18598
+ if (Object.hasOwn(props, name)) {
18599
+ const value = props[name];
18600
+ delete props[name];
18601
+ return value;
18602
+ }
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
+ }
18666
+ return style;
18667
+ };
18668
+ const Spacing = ({
18669
+ style,
18670
+ children,
18671
+ ...rest
18672
+ }) => {
18673
+ const styleForSpacing = consumeSpacingProps(rest);
18674
+ return jsx("div", {
18675
+ ...rest,
18676
+ style: withPropsStyle(styleForSpacing, style),
18677
+ children: children
18678
+ });
18679
+ };
18680
+
18509
18681
  const useNetworkSpeed = () => {
18510
18682
  return networkSpeedSignal.value;
18511
18683
  };
@@ -20057,16 +20229,17 @@ const NaviCheckbox = ({
20057
20229
  useLayoutEffect(() => {
20058
20230
  return initCustomField(ref.current, inputRef.current);
20059
20231
  }, []);
20232
+ const innerStyle = withPropsStyle({
20233
+ ...(accentColor ? {
20234
+ "--accent-color": accentColor
20235
+ } : {}),
20236
+ ...consumeSpacingProps(rest)
20237
+ }, style);
20060
20238
  return jsxs("div", {
20061
20239
  ...rest,
20062
20240
  ref: ref,
20063
20241
  className: "navi_checkbox",
20064
- style: {
20065
- ...(accentColor ? {
20066
- "--accent-color": accentColor
20067
- } : {}),
20068
- ...style
20069
- },
20242
+ style: innerStyle,
20070
20243
  "data-readonly": readOnly ? "" : undefined,
20071
20244
  "data-disabled": disabled ? "" : undefined,
20072
20245
  children: [children, jsx("div", {
@@ -20481,16 +20654,17 @@ const NaviRadio = ({
20481
20654
  useLayoutEffect(() => {
20482
20655
  return initCustomField(ref.current, inputRef.current);
20483
20656
  }, []);
20657
+ const innerStyle = withPropsStyle({
20658
+ ...(accentColor ? {
20659
+ "--accent-color": accentColor
20660
+ } : {}),
20661
+ ...consumeSpacingProps(rest)
20662
+ }, style);
20484
20663
  return jsxs("span", {
20485
20664
  ...rest,
20486
20665
  ref: ref,
20487
20666
  className: "navi_radio",
20488
- style: {
20489
- ...(accentColor ? {
20490
- "--accent-color": accentColor
20491
- } : {}),
20492
- ...style
20493
- },
20667
+ style: innerStyle,
20494
20668
  "data-readonly": readOnly ? "" : undefined,
20495
20669
  "data-disabled": disabled ? "" : undefined,
20496
20670
  children: [children, jsx("span", {
@@ -20566,93 +20740,6 @@ const withPropsClassName = (baseClassName, classNameFromProps) => {
20566
20740
  return `${baseClassName} ${normalizedPropsClassName}`;
20567
20741
  };
20568
20742
 
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
- // CSS custom properties (starting with --) should NOT be converted to camelCase
20640
- if (property.startsWith("--")) {
20641
- style[property] = value;
20642
- } else {
20643
- // Convert kebab-case to camelCase (e.g., "font-size" -> "fontSize")
20644
- const camelCaseProperty = property.replace(
20645
- /-([a-z])/g,
20646
- (match, letter) => letter.toUpperCase(),
20647
- );
20648
- style[camelCaseProperty] = value;
20649
- }
20650
- }
20651
- }
20652
-
20653
- return style;
20654
- };
20655
-
20656
20743
  installImportMetaCss(import.meta);import.meta.css = /* css */`
20657
20744
  @layer navi {
20658
20745
  .navi_input {
@@ -20788,15 +20875,17 @@ const InputTextualBasic = forwardRef((props, ref) => {
20788
20875
  autoSelect
20789
20876
  });
20790
20877
  useConstraints(innerRef, constraints);
20791
- const innerStyle = {
20878
+ const innerClassName = withPropsClassName(appearance === "navi" ? "navi_input" : undefined, className);
20879
+ const innerStyle = withPropsStyle({
20792
20880
  width,
20793
- height
20794
- };
20881
+ height,
20882
+ ...consumeSpacingProps(rest)
20883
+ }, style);
20795
20884
  const inputTextual = jsx("input", {
20796
20885
  ...rest,
20797
20886
  ref: innerRef,
20798
- className: withPropsClassName(appearance === "navi" ? "navi_input" : undefined, className),
20799
- style: withPropsStyle(innerStyle, style),
20887
+ className: innerClassName,
20888
+ style: innerStyle,
20800
20889
  type: type,
20801
20890
  "data-value": uiState,
20802
20891
  value: innerValue,
@@ -21471,11 +21560,6 @@ const Button = forwardRef((props, ref) => {
21471
21560
  WithActionInsideForm: ButtonWithActionInsideForm
21472
21561
  });
21473
21562
  });
21474
- const alignXMapping = {
21475
- start: undefined,
21476
- center: "center",
21477
- end: "flex-end"
21478
- };
21479
21563
  const ButtonBasic = forwardRef((props, ref) => {
21480
21564
  const contextLoading = useContext(LoadingContext);
21481
21565
  const contextLoadingElement = useContext(LoadingElementContext);
@@ -21512,14 +21596,23 @@ const ButtonBasic = forwardRef((props, ref) => {
21512
21596
  } else {
21513
21597
  buttonChildren = children;
21514
21598
  }
21515
- const innerStyle = {
21516
- "align-self": alignXMapping[alignX]
21517
- };
21599
+ const innerClassName = withPropsClassName(appearance === "navi" ? "navi_button" : undefined, className);
21600
+ const innerStyle = withPropsStyle({
21601
+ ...consumeSpacingProps(rest),
21602
+ ...(alignX === "start" ? {} : alignX === "center" ? {
21603
+ alignSelf: "center",
21604
+ marginLeft: "auto",
21605
+ marginRight: "auto"
21606
+ } : {
21607
+ alignSelf: "end",
21608
+ marginRight: "auto"
21609
+ })
21610
+ }, style);
21518
21611
  return jsx("button", {
21519
21612
  ...rest,
21520
21613
  ref: innerRef,
21521
- className: withPropsClassName(appearance === "navi" ? "navi_button" : undefined, className),
21522
- style: withPropsStyle(innerStyle, style),
21614
+ className: innerClassName,
21615
+ style: innerStyle,
21523
21616
  disabled: innerDisabled,
21524
21617
  "data-discrete": discrete ? "" : undefined,
21525
21618
  "data-readonly": innerReadOnly ? "" : undefined,
@@ -27955,11 +28048,24 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
27955
28048
  `;
27956
28049
  const Text = ({
27957
28050
  children,
28051
+ color,
28052
+ bold,
28053
+ italic,
28054
+ underline,
28055
+ style,
27958
28056
  ...rest
27959
28057
  }) => {
28058
+ const innerStyle = withPropsStyle({
28059
+ color,
28060
+ fontWeight: bold ? "bold" : undefined,
28061
+ fontStyle: italic ? "italic" : undefined,
28062
+ textDecoration: underline ? "underline" : undefined,
28063
+ ...consumeSpacingProps(rest)
28064
+ }, style);
27960
28065
  return jsx("span", {
27961
28066
  ...rest,
27962
28067
  className: "navi_text",
28068
+ style: innerStyle,
27963
28069
  children: children
27964
28070
  });
27965
28071
  };
@@ -28097,4 +28203,4 @@ const useDependenciesDiff = (inputs) => {
28097
28203
  return diffRef.current;
28098
28204
  };
28099
28205
 
28100
- 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, 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 };
28206
+ 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 };
package/index.js CHANGED
@@ -92,6 +92,8 @@ export { Icon, Text } from "./src/components/text/text.jsx";
92
92
  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
+ // Layout
96
+ export { Spacing } from "./src/components/layout/spacing.jsx";
95
97
 
96
98
  // Validation
97
99
  export { createUniqueValueConstraint } from "./src/validation/constraints/create_unique_value_constraint.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/navi",
3
- "version": "0.5.4",
3
+ "version": "0.6.1",
4
4
  "description": "Library of components including navigation to create frontend applications",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,6 +14,7 @@ import { FormActionContext } from "../action_execution/form_context.js";
14
14
  import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
15
15
  import { useAction } from "../action_execution/use_action.js";
16
16
  import { useExecuteAction } from "../action_execution/use_execute_action.js";
17
+ import { consumeSpacingProps } from "../layout/spacing.jsx";
17
18
  import { LoaderBackground } from "../loader/loader_background.jsx";
18
19
  import { withPropsClassName } from "../props_composition/with_props_class_name.js";
19
20
  import { withPropsStyle } from "../props_composition/with_props_style.js";
@@ -196,11 +197,6 @@ export const Button = forwardRef((props, ref) => {
196
197
  });
197
198
  });
198
199
 
199
- const alignXMapping = {
200
- start: undefined,
201
- center: "center",
202
- end: "flex-end",
203
- };
204
200
  const ButtonBasic = forwardRef((props, ref) => {
205
201
  const contextLoading = useContext(LoadingContext);
206
202
  const contextLoadingElement = useContext(LoadingElementContext);
@@ -240,19 +236,35 @@ const ButtonBasic = forwardRef((props, ref) => {
240
236
  buttonChildren = children;
241
237
  }
242
238
 
243
- const innerStyle = {
244
- "align-self": alignXMapping[alignX],
245
- };
239
+ const innerClassName = withPropsClassName(
240
+ appearance === "navi" ? "navi_button" : undefined,
241
+ className,
242
+ );
243
+ const innerStyle = withPropsStyle(
244
+ {
245
+ ...consumeSpacingProps(rest),
246
+ ...(alignX === "start"
247
+ ? {}
248
+ : alignX === "center"
249
+ ? {
250
+ alignSelf: "center",
251
+ marginLeft: "auto",
252
+ marginRight: "auto",
253
+ }
254
+ : {
255
+ alignSelf: "end",
256
+ marginRight: "auto",
257
+ }),
258
+ },
259
+ style,
260
+ );
246
261
 
247
262
  return (
248
263
  <button
249
264
  {...rest}
250
265
  ref={innerRef}
251
- className={withPropsClassName(
252
- appearance === "navi" ? "navi_button" : undefined,
253
- className,
254
- )}
255
- style={withPropsStyle(innerStyle, style)}
266
+ className={innerClassName}
267
+ style={innerStyle}
256
268
  disabled={innerDisabled}
257
269
  data-discrete={discrete ? "" : undefined}
258
270
  data-readonly={innerReadOnly ? "" : undefined}
@@ -13,10 +13,12 @@ import { useConstraints } from "../../validation/hooks/use_constraints.js";
13
13
  import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
14
14
  import { useActionBoundToOneParam } from "../action_execution/use_action.js";
15
15
  import { useExecuteAction } from "../action_execution/use_execute_action.js";
16
+ import { consumeSpacingProps } from "../layout/spacing.jsx";
16
17
  import {
17
18
  LoadableInlineElement,
18
19
  LoaderBackground,
19
20
  } from "../loader/loader_background.jsx";
21
+ import { withPropsStyle } from "../props_composition/with_props_style.js";
20
22
  import { useAutoFocus } from "../use_auto_focus.js";
21
23
  import { initCustomField } from "./custom_field.js";
22
24
  import {
@@ -331,15 +333,20 @@ const NaviCheckbox = ({
331
333
  return initCustomField(ref.current, inputRef.current);
332
334
  }, []);
333
335
 
336
+ const innerStyle = withPropsStyle(
337
+ {
338
+ ...(accentColor ? { "--accent-color": accentColor } : {}),
339
+ ...consumeSpacingProps(rest),
340
+ },
341
+ style,
342
+ );
343
+
334
344
  return (
335
345
  <div
336
346
  {...rest}
337
347
  ref={ref}
338
348
  className="navi_checkbox"
339
- style={{
340
- ...(accentColor ? { "--accent-color": accentColor } : {}),
341
- ...style,
342
- }}
349
+ style={innerStyle}
343
350
  data-readonly={readOnly ? "" : undefined}
344
351
  data-disabled={disabled ? "" : undefined}
345
352
  >
@@ -8,10 +8,12 @@ import {
8
8
 
9
9
  import { useConstraints } from "../../validation/hooks/use_constraints.js";
10
10
  import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
11
+ import { consumeSpacingProps } from "../layout/spacing.jsx";
11
12
  import {
12
13
  LoadableInlineElement,
13
14
  LoaderBackground,
14
15
  } from "../loader/loader_background.jsx";
16
+ import { withPropsStyle } from "../props_composition/with_props_style.js";
15
17
  import { useAutoFocus } from "../use_auto_focus.js";
16
18
  import { initCustomField } from "./custom_field.js";
17
19
  import {
@@ -370,15 +372,20 @@ const NaviRadio = ({
370
372
  return initCustomField(ref.current, inputRef.current);
371
373
  }, []);
372
374
 
375
+ const innerStyle = withPropsStyle(
376
+ {
377
+ ...(accentColor ? { "--accent-color": accentColor } : {}),
378
+ ...consumeSpacingProps(rest),
379
+ },
380
+ style,
381
+ );
382
+
373
383
  return (
374
384
  <span
375
385
  {...rest}
376
386
  ref={ref}
377
387
  className="navi_radio"
378
- style={{
379
- ...(accentColor ? { "--accent-color": accentColor } : {}),
380
- ...style,
381
- }}
388
+ style={innerStyle}
382
389
  data-readonly={readOnly ? "" : undefined}
383
390
  data-disabled={disabled ? "" : undefined}
384
391
  >
@@ -31,6 +31,7 @@ import { useConstraints } from "../../validation/hooks/use_constraints.js";
31
31
  import { renderActionableComponent } from "../action_execution/render_actionable_component.jsx";
32
32
  import { useActionBoundToOneParam } from "../action_execution/use_action.js";
33
33
  import { useExecuteAction } from "../action_execution/use_execute_action.js";
34
+ import { consumeSpacingProps } from "../layout/spacing.jsx";
34
35
  import { LoadableInlineElement } from "../loader/loader_background.jsx";
35
36
  import { withPropsClassName } from "../props_composition/with_props_class_name.js";
36
37
  import { withPropsStyle } from "../props_composition/with_props_style.js";
@@ -194,19 +195,24 @@ const InputTextualBasic = forwardRef((props, ref) => {
194
195
  });
195
196
  useConstraints(innerRef, constraints);
196
197
 
197
- const innerStyle = {
198
- width,
199
- height,
200
- };
198
+ const innerClassName = withPropsClassName(
199
+ appearance === "navi" ? "navi_input" : undefined,
200
+ className,
201
+ );
202
+ const innerStyle = withPropsStyle(
203
+ {
204
+ width,
205
+ height,
206
+ ...consumeSpacingProps(rest),
207
+ },
208
+ style,
209
+ );
201
210
  const inputTextual = (
202
211
  <input
203
212
  {...rest}
204
213
  ref={innerRef}
205
- className={withPropsClassName(
206
- appearance === "navi" ? "navi_input" : undefined,
207
- className,
208
- )}
209
- style={withPropsStyle(innerStyle, style)}
214
+ className={innerClassName}
215
+ style={innerStyle}
210
216
  type={type}
211
217
  data-value={uiState}
212
218
  value={innerValue}
@@ -0,0 +1,87 @@
1
+ import { withPropsStyle } from "../props_composition/with_props_style.js";
2
+
3
+ export const consumeSpacingProps = (props) => {
4
+ const consume = (name) => {
5
+ if (Object.hasOwn(props, name)) {
6
+ const value = props[name];
7
+ delete props[name];
8
+ return value;
9
+ }
10
+ return undefined;
11
+ };
12
+ const margin = consume("margin");
13
+ const marginX = consume("marginX");
14
+ const marginY = consume("marginY");
15
+ const marginLeft = consume("marginLeft");
16
+ const marginRight = consume("marginRight");
17
+ const marginTop = consume("marginTop");
18
+ const marginBottom = consume("marginBottom");
19
+
20
+ const style = {};
21
+ if (margin !== undefined) {
22
+ style.margin = margin;
23
+ }
24
+ if (marginLeft !== undefined) {
25
+ style.marginLeft = marginLeft;
26
+ } else if (marginX !== undefined) {
27
+ style.marginLeft = marginX;
28
+ }
29
+ if (marginRight !== undefined) {
30
+ style.marginRight = marginRight;
31
+ } else if (marginX !== undefined) {
32
+ style.marginRight = marginX;
33
+ }
34
+ if (marginTop !== undefined) {
35
+ style.marginTop = marginTop;
36
+ } else if (marginY !== undefined) {
37
+ style.marginTop = marginY;
38
+ }
39
+ if (marginBottom !== undefined) {
40
+ style.marginBottom = marginBottom;
41
+ } else if (marginY !== undefined) {
42
+ style.marginBottom = marginY;
43
+ }
44
+
45
+ const padding = consume("padding");
46
+ const paddingX = consume("paddingX");
47
+ const paddingY = consume("paddingY");
48
+ const paddingLeft = consume("paddingLeft");
49
+ const paddingRight = consume("paddingRight");
50
+ const paddingTop = consume("paddingTop");
51
+ const paddingBottom = consume("paddingBottom");
52
+
53
+ if (padding !== undefined) {
54
+ style.padding = padding;
55
+ }
56
+ if (paddingLeft !== undefined) {
57
+ style.paddingLeft = paddingLeft;
58
+ } else if (paddingX !== undefined) {
59
+ style.paddingLeft = paddingX;
60
+ }
61
+ if (paddingRight !== undefined) {
62
+ style.paddingRight = paddingRight;
63
+ } else if (paddingX !== undefined) {
64
+ style.paddingRight = paddingX;
65
+ }
66
+ if (paddingTop !== undefined) {
67
+ style.paddingTop = paddingTop;
68
+ } else if (paddingY !== undefined) {
69
+ style.paddingTop = paddingY;
70
+ }
71
+ if (paddingBottom !== undefined) {
72
+ style.paddingBottom = paddingBottom;
73
+ } else if (paddingY !== undefined) {
74
+ style.paddingBottom = paddingY;
75
+ }
76
+
77
+ return style;
78
+ };
79
+
80
+ export const Spacing = ({ style, children, ...rest }) => {
81
+ const styleForSpacing = consumeSpacingProps(rest);
82
+ return (
83
+ <div {...rest} style={withPropsStyle(styleForSpacing, style)}>
84
+ {children}
85
+ </div>
86
+ );
87
+ };
@@ -160,20 +160,6 @@
160
160
  <SearchIcon />
161
161
  </Icon>
162
162
  </Text>
163
-
164
- <Text>
165
- Favorites
166
- <Icon>
167
- <StarIcon />
168
- </Icon>
169
- </Text>
170
-
171
- <Text>
172
- Liked
173
- <Icon>
174
- <HeartIcon />
175
- </Icon>
176
- </Text>
177
163
  </div>
178
164
  </section>
179
165
 
@@ -205,6 +191,32 @@
205
191
  </Icon>
206
192
  Two on the left
207
193
  </Text>
194
+
195
+ <Text>
196
+ Two on the right
197
+ <Icon>
198
+ <StarIcon />
199
+ </Icon>
200
+ <Icon>
201
+ <HeartIcon />
202
+ </Icon>
203
+ </Text>
204
+
205
+ <Text>
206
+ <Icon>
207
+ <HomeIcon />
208
+ </Icon>
209
+ <Icon>
210
+ <UserIcon />
211
+ </Icon>
212
+ Two on the left and right
213
+ <Icon>
214
+ <StarIcon />
215
+ </Icon>
216
+ <Icon>
217
+ <HeartIcon />
218
+ </Icon>
219
+ </Text>
208
220
  </div>
209
221
  </section>
210
222
 
@@ -312,44 +324,6 @@
312
324
  Search Files
313
325
  </Text>
314
326
  </button>
315
-
316
- <button
317
- style={{
318
- padding: "10px 15px",
319
- border: "none",
320
- borderRadius: "4px",
321
- background: "#007bff",
322
- color: "white",
323
- cursor: "pointer",
324
- fontSize: "14px",
325
- }}
326
- >
327
- <Text>
328
- <Icon>
329
- <UserIcon />
330
- </Icon>
331
- Create Account
332
- </Text>
333
- </button>
334
-
335
- <button
336
- style={{
337
- padding: "10px 15px",
338
- border: "1px solid #dc3545",
339
- borderRadius: "4px",
340
- background: "transparent",
341
- color: "#dc3545",
342
- cursor: "pointer",
343
- fontSize: "14px",
344
- }}
345
- >
346
- <Text>
347
- <Icon>
348
- <HeartIcon />
349
- </Icon>
350
- Add to Favorites
351
- </Text>
352
- </button>
353
327
  </div>
354
328
  </section>
355
329
 
@@ -369,20 +343,6 @@
369
343
  gap: "15px",
370
344
  }}
371
345
  >
372
- <Text
373
- style={{
374
- cursor: "pointer",
375
- padding: "8px 12px",
376
- borderRadius: "4px",
377
- transition: "background-color 0.2s",
378
- }}
379
- >
380
- <Icon>
381
- <HomeIcon />
382
- </Icon>
383
- Dashboard
384
- </Text>
385
-
386
346
  <Text
387
347
  style={{
388
348
  cursor: "pointer",
@@ -397,19 +357,6 @@
397
357
  </Icon>
398
358
  Users
399
359
  </Text>
400
-
401
- <Text
402
- style={{
403
- cursor: "pointer",
404
- padding: "8px 12px",
405
- borderRadius: "4px",
406
- }}
407
- >
408
- <Icon>
409
- <SettingsIcon />
410
- </Icon>
411
- Settings
412
- </Text>
413
360
  </div>
414
361
  </nav>
415
362
  </section>
@@ -418,41 +365,50 @@
418
365
  <h2>Inline Text with Icons</h2>
419
366
  <p style={{ fontSize: "16px", lineHeight: "1.6" }}>
420
367
  Welcome to our platform! Click on the{" "}
421
- <Text>
368
+ <Text bold>
422
369
  <Icon>
423
370
  <HomeIcon />
424
371
  </Icon>
425
372
  home
426
373
  </Text>{" "}
427
374
  button to return to the dashboard, or visit your{" "}
428
- <Text>
375
+ <Text italic>
429
376
  <Icon>
430
377
  <UserIcon />
431
378
  </Icon>
432
379
  profile
433
380
  </Text>{" "}
434
381
  to update your settings. Don't forget to{" "}
435
- <Text>
382
+ <Text underline>
436
383
  <Icon>
437
384
  <StarIcon />
438
385
  </Icon>
439
386
  star
440
387
  </Text>{" "}
441
388
  your favorite items and{" "}
442
- <Text>
389
+ <Text bold italic>
443
390
  <Icon>
444
391
  <HeartIcon />
445
392
  </Icon>
446
393
  like
447
394
  </Text>{" "}
448
395
  the content you enjoy. Use the{" "}
449
- <Text>
396
+ <Text bold underline>
450
397
  <Icon>
451
398
  <SearchIcon />
452
399
  </Icon>
453
400
  search
454
401
  </Text>{" "}
455
- feature to find what you're looking for quickly.
402
+ feature to find what you're looking for quickly. You can also
403
+ combine{" "}
404
+ <Text italic underline>
405
+ multiple styles
406
+ </Text>{" "}
407
+ together for{" "}
408
+ <Text bold italic underline>
409
+ maximum emphasis
410
+ </Text>
411
+ !
456
412
  </p>
457
413
  </section>
458
414
  </div>
@@ -1,3 +1,6 @@
1
+ import { consumeSpacingProps } from "../layout/spacing.jsx";
2
+ import { withPropsStyle } from "../props_composition/with_props_style.js";
3
+
1
4
  import.meta.css = /* css */ `
2
5
  :root {
3
6
  --navi-icon-align-y: center;
@@ -21,9 +24,28 @@ import.meta.css = /* css */ `
21
24
  }
22
25
  `;
23
26
 
24
- export const Text = ({ children, ...rest }) => {
27
+ export const Text = ({
28
+ children,
29
+ color,
30
+ bold,
31
+ italic,
32
+ underline,
33
+ style,
34
+ ...rest
35
+ }) => {
36
+ const innerStyle = withPropsStyle(
37
+ {
38
+ color,
39
+ fontWeight: bold ? "bold" : undefined,
40
+ fontStyle: italic ? "italic" : undefined,
41
+ textDecoration: underline ? "underline" : undefined,
42
+ ...consumeSpacingProps(rest),
43
+ },
44
+ style,
45
+ );
46
+
25
47
  return (
26
- <span {...rest} className="navi_text">
48
+ <span {...rest} className="navi_text" style={innerStyle}>
27
49
  {children}
28
50
  </span>
29
51
  );