@jsenv/navi 0.4.2 → 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 +210 -13
- package/index.js +1 -0
- package/package.json +1 -1
- package/src/components/field/button.jsx +31 -2
- 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/src/components/svg/font_sized_svg.jsx +2 -0
- package/src/components/text/demos/demo_text_and_icon.html +465 -0
- package/src/components/text/text.jsx +48 -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,
|
|
@@ -21196,11 +21313,15 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
21196
21313
|
@layer navi {
|
|
21197
21314
|
.navi_button {
|
|
21198
21315
|
position: relative;
|
|
21199
|
-
display: inline-
|
|
21316
|
+
display: inline-flex;
|
|
21317
|
+
width: fit-content;
|
|
21318
|
+
height: fit-content;
|
|
21200
21319
|
padding: 0;
|
|
21201
21320
|
background: none;
|
|
21202
21321
|
border: none;
|
|
21322
|
+
border-radius: inherit;
|
|
21203
21323
|
outline: none;
|
|
21324
|
+
cursor: pointer;
|
|
21204
21325
|
|
|
21205
21326
|
--border-width: 1px;
|
|
21206
21327
|
--outline-width: 1px;
|
|
@@ -21294,6 +21415,9 @@ installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
|
21294
21415
|
--color: var(--color-readonly);
|
|
21295
21416
|
}
|
|
21296
21417
|
/* Disabled */
|
|
21418
|
+
.navi_button[data-disabled] {
|
|
21419
|
+
cursor: default;
|
|
21420
|
+
}
|
|
21297
21421
|
.navi_button[data-disabled] .navi_button_content {
|
|
21298
21422
|
--border-color: var(--border-color-disabled);
|
|
21299
21423
|
--background-color: var(--background-color-disabled);
|
|
@@ -21343,6 +21467,11 @@ const Button = forwardRef((props, ref) => {
|
|
|
21343
21467
|
WithActionInsideForm: ButtonWithActionInsideForm
|
|
21344
21468
|
});
|
|
21345
21469
|
});
|
|
21470
|
+
const alignXMapping = {
|
|
21471
|
+
start: undefined,
|
|
21472
|
+
center: "center",
|
|
21473
|
+
end: "flex-end"
|
|
21474
|
+
};
|
|
21346
21475
|
const ButtonBasic = forwardRef((props, ref) => {
|
|
21347
21476
|
const contextLoading = useContext(LoadingContext);
|
|
21348
21477
|
const contextLoadingElement = useContext(LoadingElementContext);
|
|
@@ -21354,8 +21483,12 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
21354
21483
|
loading,
|
|
21355
21484
|
constraints = [],
|
|
21356
21485
|
autoFocus,
|
|
21486
|
+
// visual
|
|
21357
21487
|
appearance = "navi",
|
|
21488
|
+
alignX = "start",
|
|
21358
21489
|
discrete,
|
|
21490
|
+
className,
|
|
21491
|
+
style,
|
|
21359
21492
|
children,
|
|
21360
21493
|
...rest
|
|
21361
21494
|
} = props;
|
|
@@ -21375,10 +21508,15 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
21375
21508
|
} else {
|
|
21376
21509
|
buttonChildren = children;
|
|
21377
21510
|
}
|
|
21511
|
+
const innerStyle = {
|
|
21512
|
+
"align-self": alignXMapping[alignX]
|
|
21513
|
+
};
|
|
21378
21514
|
return jsx("button", {
|
|
21379
21515
|
...rest,
|
|
21380
21516
|
ref: innerRef,
|
|
21381
|
-
className: appearance === "navi" ? "navi_button" : undefined,
|
|
21517
|
+
className: withPropsClassName(appearance === "navi" ? "navi_button" : undefined, className),
|
|
21518
|
+
style: withPropsStyle(innerStyle, style),
|
|
21519
|
+
disabled: innerDisabled,
|
|
21382
21520
|
"data-discrete": discrete ? "" : undefined,
|
|
21383
21521
|
"data-readonly": innerReadOnly ? "" : undefined,
|
|
21384
21522
|
"data-readonly-silent": innerLoading ? "" : undefined,
|
|
@@ -27636,12 +27774,14 @@ const useSignalSync = (value, initialValue = value) => {
|
|
|
27636
27774
|
const FontSizedSvg = ({
|
|
27637
27775
|
width = "1em",
|
|
27638
27776
|
height = "1em",
|
|
27777
|
+
style,
|
|
27639
27778
|
children,
|
|
27640
27779
|
...props
|
|
27641
27780
|
}) => {
|
|
27642
27781
|
return jsx("span", {
|
|
27643
27782
|
...props,
|
|
27644
27783
|
style: {
|
|
27784
|
+
...style,
|
|
27645
27785
|
display: "flex",
|
|
27646
27786
|
alignItems: "center",
|
|
27647
27787
|
width,
|
|
@@ -27803,6 +27943,63 @@ const Overflow = ({
|
|
|
27803
27943
|
});
|
|
27804
27944
|
};
|
|
27805
27945
|
|
|
27946
|
+
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
27947
|
+
:root {
|
|
27948
|
+
--navi-icon-align-y: center;
|
|
27949
|
+
}
|
|
27950
|
+
|
|
27951
|
+
.navi_text {
|
|
27952
|
+
display: inline-flex;
|
|
27953
|
+
align-items: baseline;
|
|
27954
|
+
gap: 0.1em;
|
|
27955
|
+
}
|
|
27956
|
+
|
|
27957
|
+
.navi_icon {
|
|
27958
|
+
--align-y: var(--navi-icon-align-y, center);
|
|
27959
|
+
|
|
27960
|
+
display: inline-flex;
|
|
27961
|
+
width: 1em;
|
|
27962
|
+
height: 1em;
|
|
27963
|
+
flex-shrink: 0;
|
|
27964
|
+
align-self: var(--align-y);
|
|
27965
|
+
line-height: 1em;
|
|
27966
|
+
}
|
|
27967
|
+
`;
|
|
27968
|
+
const Text = ({
|
|
27969
|
+
children,
|
|
27970
|
+
...rest
|
|
27971
|
+
}) => {
|
|
27972
|
+
return jsx("span", {
|
|
27973
|
+
...rest,
|
|
27974
|
+
className: "navi_text",
|
|
27975
|
+
children: children
|
|
27976
|
+
});
|
|
27977
|
+
};
|
|
27978
|
+
const alignYMapping = {
|
|
27979
|
+
start: "flex-start",
|
|
27980
|
+
center: "center",
|
|
27981
|
+
end: "flex-end"
|
|
27982
|
+
};
|
|
27983
|
+
const Icon = ({
|
|
27984
|
+
alignY,
|
|
27985
|
+
style,
|
|
27986
|
+
children,
|
|
27987
|
+
...rest
|
|
27988
|
+
}) => {
|
|
27989
|
+
const innerStyle = {
|
|
27990
|
+
...style
|
|
27991
|
+
};
|
|
27992
|
+
if (alignY !== "center") {
|
|
27993
|
+
innerStyle["--align-y"] = alignYMapping[alignY];
|
|
27994
|
+
}
|
|
27995
|
+
return jsx("span", {
|
|
27996
|
+
...rest,
|
|
27997
|
+
className: "navi_icon",
|
|
27998
|
+
style: innerStyle,
|
|
27999
|
+
children: children
|
|
28000
|
+
});
|
|
28001
|
+
};
|
|
28002
|
+
|
|
27806
28003
|
installImportMetaCss(import.meta);import.meta.css = /* css */`
|
|
27807
28004
|
.text_and_count {
|
|
27808
28005
|
display: flex;
|
|
@@ -27912,4 +28109,4 @@ const useDependenciesDiff = (inputs) => {
|
|
|
27912
28109
|
return diffRef.current;
|
|
27913
28110
|
};
|
|
27914
28111
|
|
|
27915
|
-
export { ActionRenderer, ActiveKeyboardShortcuts, Button, Checkbox, CheckboxList, Col, Colgroup, Details, Editable, ErrorBoundaryContext, FontSizedSvg, Form, IconAndText, Input, Label, Link, LinkWithIcon, Overflow, Radio, RadioList, Route, RowNumberCol, RowNumberTableCell, SINGLE_SPACE_CONSTRAINT, SVGMaskOverlay, Select, SelectionContext, SummaryMarker, Tab, TabList, Table, TableCell, Tbody, 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 };
|
|
28112
|
+
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 };
|
package/index.js
CHANGED
|
@@ -88,6 +88,7 @@ export { FontSizedSvg } from "./src/components/svg/font_sized_svg.jsx";
|
|
|
88
88
|
export { IconAndText } from "./src/components/svg/icon_and_text.jsx";
|
|
89
89
|
export { SVGMaskOverlay } from "./src/components/svg/svg_mask_overlay.jsx";
|
|
90
90
|
export { Overflow } from "./src/components/text/overflow.jsx";
|
|
91
|
+
export { Icon, Text } from "./src/components/text/text.jsx";
|
|
91
92
|
export { TextAndCount } from "./src/components/text/text_and_count.jsx";
|
|
92
93
|
// Callout, dialogs, ...
|
|
93
94
|
export { openCallout } from "./src/components/callout/callout.js";
|
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";
|
|
@@ -40,11 +42,15 @@ import.meta.css = /* css */ `
|
|
|
40
42
|
@layer navi {
|
|
41
43
|
.navi_button {
|
|
42
44
|
position: relative;
|
|
43
|
-
display: inline-
|
|
45
|
+
display: inline-flex;
|
|
46
|
+
width: fit-content;
|
|
47
|
+
height: fit-content;
|
|
44
48
|
padding: 0;
|
|
45
49
|
background: none;
|
|
46
50
|
border: none;
|
|
51
|
+
border-radius: inherit;
|
|
47
52
|
outline: none;
|
|
53
|
+
cursor: pointer;
|
|
48
54
|
|
|
49
55
|
--border-width: 1px;
|
|
50
56
|
--outline-width: 1px;
|
|
@@ -138,6 +144,9 @@ import.meta.css = /* css */ `
|
|
|
138
144
|
--color: var(--color-readonly);
|
|
139
145
|
}
|
|
140
146
|
/* Disabled */
|
|
147
|
+
.navi_button[data-disabled] {
|
|
148
|
+
cursor: default;
|
|
149
|
+
}
|
|
141
150
|
.navi_button[data-disabled] .navi_button_content {
|
|
142
151
|
--border-color: var(--border-color-disabled);
|
|
143
152
|
--background-color: var(--background-color-disabled);
|
|
@@ -188,6 +197,11 @@ export const Button = forwardRef((props, ref) => {
|
|
|
188
197
|
});
|
|
189
198
|
});
|
|
190
199
|
|
|
200
|
+
const alignXMapping = {
|
|
201
|
+
start: undefined,
|
|
202
|
+
center: "center",
|
|
203
|
+
end: "flex-end",
|
|
204
|
+
};
|
|
191
205
|
const ButtonBasic = forwardRef((props, ref) => {
|
|
192
206
|
const contextLoading = useContext(LoadingContext);
|
|
193
207
|
const contextLoadingElement = useContext(LoadingElementContext);
|
|
@@ -199,8 +213,14 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
199
213
|
loading,
|
|
200
214
|
constraints = [],
|
|
201
215
|
autoFocus,
|
|
216
|
+
|
|
217
|
+
// visual
|
|
202
218
|
appearance = "navi",
|
|
219
|
+
alignX = "start",
|
|
203
220
|
discrete,
|
|
221
|
+
className,
|
|
222
|
+
style,
|
|
223
|
+
|
|
204
224
|
children,
|
|
205
225
|
...rest
|
|
206
226
|
} = props;
|
|
@@ -221,11 +241,20 @@ const ButtonBasic = forwardRef((props, ref) => {
|
|
|
221
241
|
buttonChildren = children;
|
|
222
242
|
}
|
|
223
243
|
|
|
244
|
+
const innerStyle = {
|
|
245
|
+
"align-self": alignXMapping[alignX],
|
|
246
|
+
};
|
|
247
|
+
|
|
224
248
|
return (
|
|
225
249
|
<button
|
|
226
250
|
{...rest}
|
|
227
251
|
ref={innerRef}
|
|
228
|
-
className={
|
|
252
|
+
className={withPropsClassName(
|
|
253
|
+
appearance === "navi" ? "navi_button" : undefined,
|
|
254
|
+
className,
|
|
255
|
+
)}
|
|
256
|
+
style={withPropsStyle(innerStyle, style)}
|
|
257
|
+
disabled={innerDisabled}
|
|
229
258
|
data-discrete={discrete ? "" : undefined}
|
|
230
259
|
data-readonly={innerReadOnly ? "" : undefined}
|
|
231
260
|
data-readonly-silent={innerLoading ? "" : 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
|
+
};
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
export const FontSizedSvg = ({
|
|
24
24
|
width = "1em",
|
|
25
25
|
height = "1em",
|
|
26
|
+
style,
|
|
26
27
|
children,
|
|
27
28
|
...props
|
|
28
29
|
}) => {
|
|
@@ -30,6 +31,7 @@ export const FontSizedSvg = ({
|
|
|
30
31
|
<span
|
|
31
32
|
{...props}
|
|
32
33
|
style={{
|
|
34
|
+
...style,
|
|
33
35
|
display: "flex",
|
|
34
36
|
alignItems: "center",
|
|
35
37
|
width,
|
|
@@ -0,0 +1,465 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<title>Text and Icon Demo</title>
|
|
6
|
+
<script type="importmap">
|
|
7
|
+
{
|
|
8
|
+
"imports": {
|
|
9
|
+
"preact": "https://esm.sh/preact@10.22.0",
|
|
10
|
+
"preact/": "https://esm.sh/preact@10.22.0/"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
</head>
|
|
15
|
+
<body>
|
|
16
|
+
<div id="app"></div>
|
|
17
|
+
<script type="module" jsenv-type="module/jsx">
|
|
18
|
+
import { render } from "preact";
|
|
19
|
+
/* eslint-disable no-unused-vars */
|
|
20
|
+
import { Text, Icon } from "../text.jsx";
|
|
21
|
+
|
|
22
|
+
// Sample SVG icons
|
|
23
|
+
const HomeIcon = () => (
|
|
24
|
+
<svg
|
|
25
|
+
viewBox="0 0 24 24"
|
|
26
|
+
width="100%"
|
|
27
|
+
height="100%"
|
|
28
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
29
|
+
>
|
|
30
|
+
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" fill="currentColor" />
|
|
31
|
+
</svg>
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const UserIcon = () => (
|
|
35
|
+
<svg
|
|
36
|
+
viewBox="0 0 24 24"
|
|
37
|
+
width="100%"
|
|
38
|
+
height="100%"
|
|
39
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
40
|
+
>
|
|
41
|
+
<path
|
|
42
|
+
d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"
|
|
43
|
+
fill="currentColor"
|
|
44
|
+
/>
|
|
45
|
+
</svg>
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
const SettingsIcon = () => (
|
|
49
|
+
<svg
|
|
50
|
+
viewBox="0 0 24 24"
|
|
51
|
+
width="100%"
|
|
52
|
+
height="100%"
|
|
53
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
54
|
+
>
|
|
55
|
+
<path
|
|
56
|
+
d="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61 l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41 h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87 C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.82,11.69,4.82,12s0.02,0.64,0.07,0.94l-2.03,1.58 c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54 c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96 c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94z M12,15.6c-1.98,0-3.6-1.62-3.6-3.6 s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z"
|
|
57
|
+
fill="currentColor"
|
|
58
|
+
/>
|
|
59
|
+
</svg>
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const SearchIcon = () => (
|
|
63
|
+
<svg
|
|
64
|
+
viewBox="0 0 24 24"
|
|
65
|
+
width="100%"
|
|
66
|
+
height="100%"
|
|
67
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
68
|
+
>
|
|
69
|
+
<path
|
|
70
|
+
d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"
|
|
71
|
+
fill="currentColor"
|
|
72
|
+
/>
|
|
73
|
+
</svg>
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const StarIcon = () => (
|
|
77
|
+
<svg
|
|
78
|
+
viewBox="0 0 24 24"
|
|
79
|
+
width="100%"
|
|
80
|
+
height="100%"
|
|
81
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
82
|
+
>
|
|
83
|
+
<path
|
|
84
|
+
d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"
|
|
85
|
+
fill="currentColor"
|
|
86
|
+
/>
|
|
87
|
+
</svg>
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const HeartIcon = () => (
|
|
91
|
+
<svg
|
|
92
|
+
viewBox="0 0 24 24"
|
|
93
|
+
width="100%"
|
|
94
|
+
height="100%"
|
|
95
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
96
|
+
>
|
|
97
|
+
<path
|
|
98
|
+
d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"
|
|
99
|
+
fill="currentColor"
|
|
100
|
+
/>
|
|
101
|
+
</svg>
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const Demo = () => {
|
|
105
|
+
return (
|
|
106
|
+
<div
|
|
107
|
+
style={{
|
|
108
|
+
padding: "20px",
|
|
109
|
+
fontFamily: "system-ui, sans-serif",
|
|
110
|
+
lineHeight: "1.6",
|
|
111
|
+
maxWidth: "800px",
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<h1>Text and Icon Combinations</h1>
|
|
115
|
+
|
|
116
|
+
<section style={{ marginBottom: "40px" }}>
|
|
117
|
+
<h2>Basic Icon + Text</h2>
|
|
118
|
+
<div
|
|
119
|
+
style={{
|
|
120
|
+
display: "flex",
|
|
121
|
+
flexDirection: "column",
|
|
122
|
+
gap: "10px",
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
<Text>
|
|
126
|
+
<Icon>
|
|
127
|
+
<HomeIcon />
|
|
128
|
+
</Icon>
|
|
129
|
+
Home
|
|
130
|
+
</Text>
|
|
131
|
+
|
|
132
|
+
<Text>
|
|
133
|
+
<Icon>
|
|
134
|
+
<UserIcon />
|
|
135
|
+
</Icon>
|
|
136
|
+
Profile
|
|
137
|
+
</Text>
|
|
138
|
+
|
|
139
|
+
<Text>
|
|
140
|
+
<Icon>
|
|
141
|
+
<SettingsIcon />
|
|
142
|
+
</Icon>
|
|
143
|
+
Settings
|
|
144
|
+
</Text>
|
|
145
|
+
</div>
|
|
146
|
+
</section>
|
|
147
|
+
|
|
148
|
+
<section style={{ marginBottom: "40px" }}>
|
|
149
|
+
<h2>Text + Icon (Reversed Order)</h2>
|
|
150
|
+
<div
|
|
151
|
+
style={{
|
|
152
|
+
display: "flex",
|
|
153
|
+
flexDirection: "column",
|
|
154
|
+
gap: "10px",
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
<Text>
|
|
158
|
+
Search
|
|
159
|
+
<Icon>
|
|
160
|
+
<SearchIcon />
|
|
161
|
+
</Icon>
|
|
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
|
+
</div>
|
|
178
|
+
</section>
|
|
179
|
+
|
|
180
|
+
<section style={{ marginBottom: "40px" }}>
|
|
181
|
+
<h2>Multiple Icons</h2>
|
|
182
|
+
<div
|
|
183
|
+
style={{
|
|
184
|
+
display: "flex",
|
|
185
|
+
flexDirection: "column",
|
|
186
|
+
gap: "10px",
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
<Text>
|
|
190
|
+
<Icon>
|
|
191
|
+
<HomeIcon />
|
|
192
|
+
</Icon>
|
|
193
|
+
Left and right
|
|
194
|
+
<Icon>
|
|
195
|
+
<StarIcon />
|
|
196
|
+
</Icon>
|
|
197
|
+
</Text>
|
|
198
|
+
|
|
199
|
+
<Text>
|
|
200
|
+
<Icon>
|
|
201
|
+
<UserIcon />
|
|
202
|
+
</Icon>
|
|
203
|
+
<Icon>
|
|
204
|
+
<SettingsIcon />
|
|
205
|
+
</Icon>
|
|
206
|
+
Two on the left
|
|
207
|
+
</Text>
|
|
208
|
+
</div>
|
|
209
|
+
</section>
|
|
210
|
+
|
|
211
|
+
<section style={{ marginBottom: "40px" }}>
|
|
212
|
+
<h2>Different Font Sizes</h2>
|
|
213
|
+
<div
|
|
214
|
+
style={{
|
|
215
|
+
display: "flex",
|
|
216
|
+
flexDirection: "column",
|
|
217
|
+
gap: "15px",
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
<Text style={{ fontSize: "12px" }}>
|
|
221
|
+
<Icon>
|
|
222
|
+
<HomeIcon />
|
|
223
|
+
</Icon>
|
|
224
|
+
Small text with icon
|
|
225
|
+
</Text>
|
|
226
|
+
|
|
227
|
+
<Text style={{ fontSize: "16px" }}>
|
|
228
|
+
<Icon>
|
|
229
|
+
<UserIcon />
|
|
230
|
+
</Icon>
|
|
231
|
+
Normal text with icon
|
|
232
|
+
</Text>
|
|
233
|
+
|
|
234
|
+
<Text style={{ fontSize: "24px" }}>
|
|
235
|
+
<Icon>
|
|
236
|
+
<SettingsIcon />
|
|
237
|
+
</Icon>
|
|
238
|
+
Large text with icon
|
|
239
|
+
</Text>
|
|
240
|
+
|
|
241
|
+
<Text style={{ fontSize: "32px" }}>
|
|
242
|
+
<Icon>
|
|
243
|
+
<StarIcon />
|
|
244
|
+
</Icon>
|
|
245
|
+
Extra large text
|
|
246
|
+
</Text>
|
|
247
|
+
</div>
|
|
248
|
+
</section>
|
|
249
|
+
|
|
250
|
+
<section style={{ marginBottom: "40px" }}>
|
|
251
|
+
<h2>Colored Examples</h2>
|
|
252
|
+
<div
|
|
253
|
+
style={{
|
|
254
|
+
display: "flex",
|
|
255
|
+
flexDirection: "column",
|
|
256
|
+
gap: "10px",
|
|
257
|
+
}}
|
|
258
|
+
>
|
|
259
|
+
<Text style={{ color: "blue" }}>
|
|
260
|
+
<Icon>
|
|
261
|
+
<HomeIcon />
|
|
262
|
+
</Icon>
|
|
263
|
+
Blue home link
|
|
264
|
+
</Text>
|
|
265
|
+
|
|
266
|
+
<Text style={{ color: "green" }}>
|
|
267
|
+
<Icon>
|
|
268
|
+
<UserIcon />
|
|
269
|
+
</Icon>
|
|
270
|
+
Green user profile
|
|
271
|
+
</Text>
|
|
272
|
+
|
|
273
|
+
<Text style={{ color: "red" }}>
|
|
274
|
+
<Icon>
|
|
275
|
+
<HeartIcon />
|
|
276
|
+
</Icon>
|
|
277
|
+
Red heart favorite
|
|
278
|
+
</Text>
|
|
279
|
+
|
|
280
|
+
<Text style={{ color: "purple" }}>
|
|
281
|
+
<Icon>
|
|
282
|
+
<StarIcon />
|
|
283
|
+
</Icon>
|
|
284
|
+
Purple star rating
|
|
285
|
+
</Text>
|
|
286
|
+
</div>
|
|
287
|
+
</section>
|
|
288
|
+
|
|
289
|
+
<section style={{ marginBottom: "40px" }}>
|
|
290
|
+
<h2>Interactive Buttons</h2>
|
|
291
|
+
<div
|
|
292
|
+
style={{
|
|
293
|
+
display: "flex",
|
|
294
|
+
flexDirection: "column",
|
|
295
|
+
gap: "10px",
|
|
296
|
+
}}
|
|
297
|
+
>
|
|
298
|
+
<button
|
|
299
|
+
style={{
|
|
300
|
+
padding: "10px 15px",
|
|
301
|
+
border: "1px solid #ccc",
|
|
302
|
+
borderRadius: "4px",
|
|
303
|
+
background: "white",
|
|
304
|
+
cursor: "pointer",
|
|
305
|
+
fontSize: "14px",
|
|
306
|
+
}}
|
|
307
|
+
>
|
|
308
|
+
<Text>
|
|
309
|
+
<Icon>
|
|
310
|
+
<SearchIcon />
|
|
311
|
+
</Icon>
|
|
312
|
+
Search Files
|
|
313
|
+
</Text>
|
|
314
|
+
</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
|
+
</div>
|
|
354
|
+
</section>
|
|
355
|
+
|
|
356
|
+
<section style={{ marginBottom: "40px" }}>
|
|
357
|
+
<h2>Navigation Menu Style</h2>
|
|
358
|
+
<nav
|
|
359
|
+
style={{
|
|
360
|
+
background: "#f8f9fa",
|
|
361
|
+
padding: "20px",
|
|
362
|
+
borderRadius: "8px",
|
|
363
|
+
}}
|
|
364
|
+
>
|
|
365
|
+
<div
|
|
366
|
+
style={{
|
|
367
|
+
display: "flex",
|
|
368
|
+
flexDirection: "column",
|
|
369
|
+
gap: "15px",
|
|
370
|
+
}}
|
|
371
|
+
>
|
|
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
|
+
<Text
|
|
387
|
+
style={{
|
|
388
|
+
cursor: "pointer",
|
|
389
|
+
padding: "8px 12px",
|
|
390
|
+
borderRadius: "4px",
|
|
391
|
+
background: "#007bff",
|
|
392
|
+
color: "white",
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
<Icon>
|
|
396
|
+
<UserIcon />
|
|
397
|
+
</Icon>
|
|
398
|
+
Users
|
|
399
|
+
</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
|
+
</div>
|
|
414
|
+
</nav>
|
|
415
|
+
</section>
|
|
416
|
+
|
|
417
|
+
<section>
|
|
418
|
+
<h2>Inline Text with Icons</h2>
|
|
419
|
+
<p style={{ fontSize: "16px", lineHeight: "1.6" }}>
|
|
420
|
+
Welcome to our platform! Click on the{" "}
|
|
421
|
+
<Text>
|
|
422
|
+
<Icon>
|
|
423
|
+
<HomeIcon />
|
|
424
|
+
</Icon>
|
|
425
|
+
home
|
|
426
|
+
</Text>{" "}
|
|
427
|
+
button to return to the dashboard, or visit your{" "}
|
|
428
|
+
<Text>
|
|
429
|
+
<Icon>
|
|
430
|
+
<UserIcon />
|
|
431
|
+
</Icon>
|
|
432
|
+
profile
|
|
433
|
+
</Text>{" "}
|
|
434
|
+
to update your settings. Don't forget to{" "}
|
|
435
|
+
<Text>
|
|
436
|
+
<Icon>
|
|
437
|
+
<StarIcon />
|
|
438
|
+
</Icon>
|
|
439
|
+
star
|
|
440
|
+
</Text>{" "}
|
|
441
|
+
your favorite items and{" "}
|
|
442
|
+
<Text>
|
|
443
|
+
<Icon>
|
|
444
|
+
<HeartIcon />
|
|
445
|
+
</Icon>
|
|
446
|
+
like
|
|
447
|
+
</Text>{" "}
|
|
448
|
+
the content you enjoy. Use the{" "}
|
|
449
|
+
<Text>
|
|
450
|
+
<Icon>
|
|
451
|
+
<SearchIcon />
|
|
452
|
+
</Icon>
|
|
453
|
+
search
|
|
454
|
+
</Text>{" "}
|
|
455
|
+
feature to find what you're looking for quickly.
|
|
456
|
+
</p>
|
|
457
|
+
</section>
|
|
458
|
+
</div>
|
|
459
|
+
);
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
render(<Demo />, document.getElementById("app"));
|
|
463
|
+
</script>
|
|
464
|
+
</body>
|
|
465
|
+
</html>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import.meta.css = /* css */ `
|
|
2
|
+
:root {
|
|
3
|
+
--navi-icon-align-y: center;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.navi_text {
|
|
7
|
+
display: inline-flex;
|
|
8
|
+
align-items: baseline;
|
|
9
|
+
gap: 0.1em;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.navi_icon {
|
|
13
|
+
--align-y: var(--navi-icon-align-y, center);
|
|
14
|
+
|
|
15
|
+
display: inline-flex;
|
|
16
|
+
width: 1em;
|
|
17
|
+
height: 1em;
|
|
18
|
+
flex-shrink: 0;
|
|
19
|
+
align-self: var(--align-y);
|
|
20
|
+
line-height: 1em;
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
export const Text = ({ children, ...rest }) => {
|
|
25
|
+
return (
|
|
26
|
+
<span {...rest} className="navi_text">
|
|
27
|
+
{children}
|
|
28
|
+
</span>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const alignYMapping = {
|
|
33
|
+
start: "flex-start",
|
|
34
|
+
center: "center",
|
|
35
|
+
end: "flex-end",
|
|
36
|
+
};
|
|
37
|
+
export const Icon = ({ alignY, style, children, ...rest }) => {
|
|
38
|
+
const innerStyle = { ...style };
|
|
39
|
+
if (alignY !== "center") {
|
|
40
|
+
innerStyle["--align-y"] = alignYMapping[alignY];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<span {...rest} className="navi_icon" style={innerStyle}>
|
|
45
|
+
{children}
|
|
46
|
+
</span>
|
|
47
|
+
);
|
|
48
|
+
};
|