@babylonjs/shared-ui-components 8.17.1 → 8.18.0
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/fluent/primitives/comboBox.d.ts +12 -0
- package/fluent/primitives/comboBox.js +32 -0
- package/fluent/primitives/comboBox.js.map +1 -0
- package/fluent/primitives/draggable.js +29 -6
- package/fluent/primitives/draggable.js.map +1 -1
- package/fluent/primitives/positionedPopover.d.ts +14 -0
- package/fluent/primitives/positionedPopover.js +34 -0
- package/fluent/primitives/positionedPopover.js.map +1 -0
- package/fluent/primitives/searchBar.d.ts +6 -0
- package/fluent/primitives/searchBar.js +16 -0
- package/fluent/primitives/searchBar.js.map +1 -0
- package/fluent/primitives/searchBox.d.ts +11 -4
- package/fluent/primitives/searchBox.js +91 -9
- package/fluent/primitives/searchBox.js.map +1 -1
- package/nodeGraphSystem/searchBox.d.ts +3 -1
- package/nodeGraphSystem/searchBox.js +25 -1
- package/nodeGraphSystem/searchBox.js.map +1 -1
- package/package.json +1 -1
@@ -0,0 +1,12 @@
|
|
1
|
+
import type { FunctionComponent } from "react";
|
2
|
+
export type ComboBoxProps = {
|
3
|
+
label: string;
|
4
|
+
value: string[];
|
5
|
+
onChange: (value: string) => void;
|
6
|
+
};
|
7
|
+
/**
|
8
|
+
* Wrapper around a Fluent ComboBox that allows for filtering options
|
9
|
+
* @param props
|
10
|
+
* @returns
|
11
|
+
*/
|
12
|
+
export declare const ComboBox: FunctionComponent<ComboBoxProps>;
|
@@ -0,0 +1,32 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { useState } from "react";
|
3
|
+
import { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId } from "@fluentui/react-components";
|
4
|
+
const useStyles = makeStyles({
|
5
|
+
root: {
|
6
|
+
// Stack the label above the field with a gap
|
7
|
+
display: "grid",
|
8
|
+
gridTemplateRows: "repeat(1fr)",
|
9
|
+
justifyItems: "start",
|
10
|
+
gap: "2px",
|
11
|
+
maxWidth: "400px",
|
12
|
+
},
|
13
|
+
});
|
14
|
+
/**
|
15
|
+
* Wrapper around a Fluent ComboBox that allows for filtering options
|
16
|
+
* @param props
|
17
|
+
* @returns
|
18
|
+
*/
|
19
|
+
export const ComboBox = (props) => {
|
20
|
+
const comboId = useId();
|
21
|
+
const styles = useStyles();
|
22
|
+
const [query, setQuery] = useState("");
|
23
|
+
const children = useComboboxFilter(query, props.value, {
|
24
|
+
noOptionsMessage: "No items match your search.",
|
25
|
+
});
|
26
|
+
const onOptionSelect = (_e, data) => {
|
27
|
+
setQuery(data.optionText ?? "");
|
28
|
+
data.optionText && props.onChange(data.optionText);
|
29
|
+
};
|
30
|
+
return (_jsxs("div", { className: styles.root, children: [_jsx("label", { id: comboId, children: props.label }), _jsx(FluentComboBox, { onOptionSelect: onOptionSelect, "aria-labelledby": comboId, placeholder: "Search..", onChange: (ev) => setQuery(ev.target.value), value: query, children: children })] }));
|
31
|
+
};
|
32
|
+
//# sourceMappingURL=comboBox.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"comboBox.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/comboBox.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,QAAQ,IAAI,cAAc,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAC;AAE9G,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,IAAI,EAAE;QACF,6CAA6C;QAC7C,OAAO,EAAE,MAAM;QACf,gBAAgB,EAAE,aAAa;QAC/B,YAAY,EAAE,OAAO;QACrB,GAAG,EAAE,KAAK;QACV,QAAQ,EAAE,OAAO;KACpB;CACJ,CAAC,CAAC;AAOH;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAqC,CAAC,KAAK,EAAE,EAAE;IAChE,MAAM,OAAO,GAAG,KAAK,EAAE,CAAC;IACxB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC;IACvC,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE;QACnD,gBAAgB,EAAE,6BAA6B;KAClD,CAAC,CAAC;IACH,MAAM,cAAc,GAAG,CAAC,EAAmB,EAAE,IAAwB,EAAE,EAAE;QACrE,QAAQ,CAAC,IAAI,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC;QAChC,IAAI,CAAC,UAAU,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC;IAEF,OAAO,CACH,eAAK,SAAS,EAAE,MAAM,CAAC,IAAI,aACvB,gBAAO,EAAE,EAAE,OAAO,YAAG,KAAK,CAAC,KAAK,GAAS,EACzC,KAAC,cAAc,IAAC,cAAc,EAAE,cAAc,qBAAmB,OAAO,EAAE,WAAW,EAAC,UAAU,EAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,YACrJ,QAAQ,GACI,IACf,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import type { FunctionComponent } from \"react\";\r\nimport { useState } from \"react\";\r\nimport { Combobox as FluentComboBox, makeStyles, useComboboxFilter, useId } from \"@fluentui/react-components\";\r\nimport type { OptionOnSelectData, SelectionEvents } from \"@fluentui/react-components\";\r\nconst useStyles = makeStyles({\r\n root: {\r\n // Stack the label above the field with a gap\r\n display: \"grid\",\r\n gridTemplateRows: \"repeat(1fr)\",\r\n justifyItems: \"start\",\r\n gap: \"2px\",\r\n maxWidth: \"400px\",\r\n },\r\n});\r\n\r\nexport type ComboBoxProps = {\r\n label: string;\r\n value: string[];\r\n onChange: (value: string) => void;\r\n};\r\n/**\r\n * Wrapper around a Fluent ComboBox that allows for filtering options\r\n * @param props\r\n * @returns\r\n */\r\nexport const ComboBox: FunctionComponent<ComboBoxProps> = (props) => {\r\n const comboId = useId();\r\n const styles = useStyles();\r\n\r\n const [query, setQuery] = useState(\"\");\r\n const children = useComboboxFilter(query, props.value, {\r\n noOptionsMessage: \"No items match your search.\",\r\n });\r\n const onOptionSelect = (_e: SelectionEvents, data: OptionOnSelectData) => {\r\n setQuery(data.optionText ?? \"\");\r\n data.optionText && props.onChange(data.optionText);\r\n };\r\n\r\n return (\r\n <div className={styles.root}>\r\n <label id={comboId}>{props.label}</label>\r\n <FluentComboBox onOptionSelect={onOptionSelect} aria-labelledby={comboId} placeholder=\"Search..\" onChange={(ev) => setQuery(ev.target.value)} value={query}>\r\n {children}\r\n </FluentComboBox>\r\n </div>\r\n );\r\n};\r\n"]}
|
@@ -1,33 +1,56 @@
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
2
|
import { makeStyles, tokens } from "@fluentui/react-components";
|
3
3
|
import { DeleteFilled } from "@fluentui/react-icons";
|
4
|
-
import { LineContainer } from "../hoc/propertyLines/propertyLine.js";
|
5
4
|
const useDraggableStyles = makeStyles({
|
6
5
|
draggable: {
|
7
|
-
display: "
|
6
|
+
display: "flex",
|
8
7
|
alignItems: "center",
|
8
|
+
justifyContent: "center",
|
9
|
+
position: "relative",
|
9
10
|
columnGap: tokens.spacingHorizontalS,
|
10
11
|
cursor: "grab",
|
11
12
|
textAlign: "center",
|
12
13
|
boxSizing: "border-box",
|
13
|
-
borderBottom: "black",
|
14
14
|
margin: `${tokens.spacingVerticalXS} 0px`,
|
15
|
+
padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalMNudge}`,
|
16
|
+
// Button-like styling
|
17
|
+
backgroundColor: tokens.colorNeutralBackground1,
|
15
18
|
border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,
|
19
|
+
borderRadius: tokens.borderRadiusMedium,
|
20
|
+
color: tokens.colorNeutralForeground1,
|
21
|
+
fontSize: tokens.fontSizeBase300,
|
22
|
+
fontFamily: tokens.fontFamilyBase,
|
23
|
+
fontWeight: tokens.fontWeightRegular,
|
24
|
+
lineHeight: tokens.lineHeightBase300,
|
25
|
+
minHeight: "32px",
|
16
26
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
17
27
|
":hover": {
|
18
|
-
backgroundColor: tokens.
|
28
|
+
backgroundColor: tokens.colorNeutralBackground1Hover,
|
29
|
+
},
|
30
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
31
|
+
":active": {
|
32
|
+
backgroundColor: tokens.colorNeutralBackground1Pressed,
|
19
33
|
},
|
20
34
|
},
|
21
35
|
icon: {
|
22
36
|
pointerEvents: "auto", // re‑enable interaction
|
23
37
|
display: "flex",
|
24
38
|
alignItems: "center",
|
39
|
+
position: "absolute",
|
40
|
+
right: tokens.spacingHorizontalSNudge,
|
41
|
+
color: tokens.colorNeutralForeground2,
|
42
|
+
cursor: "pointer",
|
43
|
+
fontSize: tokens.fontSizeBase400,
|
44
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
45
|
+
":hover": {
|
46
|
+
color: tokens.colorNeutralForeground2Hover,
|
47
|
+
},
|
25
48
|
},
|
26
49
|
});
|
27
50
|
export const DraggableLine = (props) => {
|
28
51
|
const classes = useDraggableStyles();
|
29
|
-
return (
|
52
|
+
return (_jsxs("div", { className: classes.draggable, title: props.tooltip, draggable: true, onDragStart: (event) => {
|
30
53
|
event.dataTransfer.setData(props.format, props.data);
|
31
|
-
}, children:
|
54
|
+
}, children: [props.label, props.onDelete && _jsx(DeleteFilled, { className: classes.icon, onClick: props.onDelete })] }));
|
32
55
|
};
|
33
56
|
//# sourceMappingURL=draggable.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"draggable.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/draggable.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;
|
1
|
+
{"version":3,"file":"draggable.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/draggable.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAUrD,MAAM,kBAAkB,GAAG,UAAU,CAAC;IAClC,SAAS,EAAE;QACP,OAAO,EAAE,MAAM;QACf,UAAU,EAAE,QAAQ;QACpB,cAAc,EAAE,QAAQ;QACxB,QAAQ,EAAE,UAAU;QACpB,SAAS,EAAE,MAAM,CAAC,kBAAkB;QACpC,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,YAAY;QACvB,MAAM,EAAE,GAAG,MAAM,CAAC,iBAAiB,MAAM;QACzC,OAAO,EAAE,GAAG,MAAM,CAAC,qBAAqB,IAAI,MAAM,CAAC,uBAAuB,EAAE;QAE5E,sBAAsB;QACtB,eAAe,EAAE,MAAM,CAAC,uBAAuB;QAC/C,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,UAAU,MAAM,CAAC,mBAAmB,EAAE;QACvE,YAAY,EAAE,MAAM,CAAC,kBAAkB;QACvC,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,QAAQ,EAAE,MAAM,CAAC,eAAe;QAChC,UAAU,EAAE,MAAM,CAAC,cAAc;QACjC,UAAU,EAAE,MAAM,CAAC,iBAAiB;QACpC,UAAU,EAAE,MAAM,CAAC,iBAAiB;QACpC,SAAS,EAAE,MAAM;QAEjB,gEAAgE;QAChE,QAAQ,EAAE;YACN,eAAe,EAAE,MAAM,CAAC,4BAA4B;SACvD;QAED,gEAAgE;QAChE,SAAS,EAAE;YACP,eAAe,EAAE,MAAM,CAAC,8BAA8B;SACzD;KACJ;IACD,IAAI,EAAE;QACF,aAAa,EAAE,MAAM,EAAE,wBAAwB;QAC/C,OAAO,EAAE,MAAM;QACf,UAAU,EAAE,QAAQ;QACpB,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,MAAM,EAAE,SAAS;QACjB,QAAQ,EAAE,MAAM,CAAC,eAAe;QAEhC,gEAAgE;QAChE,QAAQ,EAAE;YACN,KAAK,EAAE,MAAM,CAAC,4BAA4B;SAC7C;KACJ;CACJ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAgD,CAAC,KAAK,EAAE,EAAE;IAChF,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACrC,OAAO,CACH,eACI,SAAS,EAAE,OAAO,CAAC,SAAS,EAC5B,KAAK,EAAE,KAAK,CAAC,OAAO,EACpB,SAAS,EAAE,IAAI,EACf,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE;YACnB,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACzD,CAAC,aAEA,KAAK,CAAC,KAAK,EACX,KAAK,CAAC,QAAQ,IAAI,KAAC,YAAY,IAAC,SAAS,EAAE,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,QAAQ,GAAI,IACnF,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { makeStyles, tokens } from \"@fluentui/react-components\";\r\nimport { DeleteFilled } from \"@fluentui/react-icons\";\r\n\r\nexport type DraggableLineProps = {\r\n format: string;\r\n data: string;\r\n tooltip: string;\r\n label: string;\r\n onDelete?: () => void;\r\n};\r\n\r\nconst useDraggableStyles = makeStyles({\r\n draggable: {\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n justifyContent: \"center\",\r\n position: \"relative\",\r\n columnGap: tokens.spacingHorizontalS,\r\n cursor: \"grab\",\r\n textAlign: \"center\",\r\n boxSizing: \"border-box\",\r\n margin: `${tokens.spacingVerticalXS} 0px`,\r\n padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalMNudge}`,\r\n\r\n // Button-like styling\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n border: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n color: tokens.colorNeutralForeground1,\r\n fontSize: tokens.fontSizeBase300,\r\n fontFamily: tokens.fontFamilyBase,\r\n fontWeight: tokens.fontWeightRegular,\r\n lineHeight: tokens.lineHeightBase300,\r\n minHeight: \"32px\",\r\n\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n \":hover\": {\r\n backgroundColor: tokens.colorNeutralBackground1Hover,\r\n },\r\n\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n \":active\": {\r\n backgroundColor: tokens.colorNeutralBackground1Pressed,\r\n },\r\n },\r\n icon: {\r\n pointerEvents: \"auto\", // re‑enable interaction\r\n display: \"flex\",\r\n alignItems: \"center\",\r\n position: \"absolute\",\r\n right: tokens.spacingHorizontalSNudge,\r\n color: tokens.colorNeutralForeground2,\r\n cursor: \"pointer\",\r\n fontSize: tokens.fontSizeBase400,\r\n\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n \":hover\": {\r\n color: tokens.colorNeutralForeground2Hover,\r\n },\r\n },\r\n});\r\n\r\nexport const DraggableLine: React.FunctionComponent<DraggableLineProps> = (props) => {\r\n const classes = useDraggableStyles();\r\n return (\r\n <div\r\n className={classes.draggable}\r\n title={props.tooltip}\r\n draggable={true}\r\n onDragStart={(event) => {\r\n event.dataTransfer.setData(props.format, props.data);\r\n }}\r\n >\r\n {props.label}\r\n {props.onDelete && <DeleteFilled className={classes.icon} onClick={props.onDelete} />}\r\n </div>\r\n );\r\n};\r\n"]}
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import type { FunctionComponent, PropsWithChildren } from "react";
|
2
|
+
type PositionedPopoverProps = {
|
3
|
+
x: number;
|
4
|
+
y: number;
|
5
|
+
visible: boolean;
|
6
|
+
hide: () => void;
|
7
|
+
};
|
8
|
+
/**
|
9
|
+
* PositionedPopover component that shows a popover at specific coordinates
|
10
|
+
* @param props - The component props
|
11
|
+
* @returns The positioned popover component
|
12
|
+
*/
|
13
|
+
export declare const PositionedPopover: FunctionComponent<PropsWithChildren<PositionedPopoverProps>>;
|
14
|
+
export {};
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { Popover, PopoverSurface, PopoverTrigger } from "@fluentui/react-components";
|
3
|
+
import { useState, useEffect } from "react";
|
4
|
+
/**
|
5
|
+
* PositionedPopover component that shows a popover at specific coordinates
|
6
|
+
* @param props - The component props
|
7
|
+
* @returns The positioned popover component
|
8
|
+
*/
|
9
|
+
export const PositionedPopover = (props) => {
|
10
|
+
const [open, setOpen] = useState(false);
|
11
|
+
useEffect(() => {
|
12
|
+
setOpen(props.visible);
|
13
|
+
}, [props.visible, props.x, props.y]);
|
14
|
+
const handleOpenChange = (_, data) => {
|
15
|
+
setOpen(data.open);
|
16
|
+
if (!data.open) {
|
17
|
+
props.hide();
|
18
|
+
}
|
19
|
+
};
|
20
|
+
return (_jsxs(Popover, { open: open, onOpenChange: handleOpenChange, positioning: {
|
21
|
+
position: "below", // Places the popover directly below the trigger element
|
22
|
+
align: "center", // Centers the popover horizontally relative to the trigger element
|
23
|
+
autoSize: "height-always", //Automatically adjusts the popover height to fit within the viewport
|
24
|
+
fallbackPositions: ["above", "after", "before"], //If the primary position doesn't fit, automatically tries these positions in order
|
25
|
+
}, withArrow: false, children: [_jsx(PopoverTrigger, { children: _jsx("div", { style: {
|
26
|
+
position: "absolute",
|
27
|
+
left: `${props.x}px`,
|
28
|
+
top: `${props.y}px`,
|
29
|
+
width: 1,
|
30
|
+
height: 1,
|
31
|
+
pointerEvents: "none", // so it's invisible to interaction
|
32
|
+
} }) }), _jsx(PopoverSurface, { children: props.children })] }));
|
33
|
+
};
|
34
|
+
//# sourceMappingURL=positionedPopover.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"positionedPopover.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/positionedPopover.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAGrF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAS5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAiE,CAAC,KAAK,EAAE,EAAE;IACrG,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAExC,SAAS,CAAC,GAAG,EAAE;QACX,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC3B,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtC,MAAM,gBAAgB,GAAG,CAAC,CAAoB,EAAE,IAAsB,EAAE,EAAE;QACtE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEnB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACb,KAAK,CAAC,IAAI,EAAE,CAAC;QACjB,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CACH,MAAC,OAAO,IACJ,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE;YACT,QAAQ,EAAE,OAAO,EAAE,wDAAwD;YAC3E,KAAK,EAAE,QAAQ,EAAE,mEAAmE;YACpF,QAAQ,EAAE,eAAe,EAAE,qEAAqE;YAChG,iBAAiB,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,mFAAmF;SACvI,EACD,SAAS,EAAE,KAAK,aAEhB,KAAC,cAAc,cAEX,cACI,KAAK,EAAE;wBACH,QAAQ,EAAE,UAAU;wBACpB,IAAI,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI;wBACpB,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI;wBACnB,KAAK,EAAE,CAAC;wBACR,MAAM,EAAE,CAAC;wBACT,aAAa,EAAE,MAAM,EAAE,mCAAmC;qBAC7D,GACH,GACW,EACjB,KAAC,cAAc,cAAE,KAAK,CAAC,QAAQ,GAAkB,IAC3C,CACb,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { Popover, PopoverSurface, PopoverTrigger } from \"@fluentui/react-components\";\r\nimport type { OnOpenChangeData, OpenPopoverEvents } from \"@fluentui/react-components\";\r\nimport type { FunctionComponent, PropsWithChildren } from \"react\";\r\nimport { useState, useEffect } from \"react\";\r\n\r\ntype PositionedPopoverProps = {\r\n x: number;\r\n y: number;\r\n visible: boolean;\r\n hide: () => void;\r\n};\r\n\r\n/**\r\n * PositionedPopover component that shows a popover at specific coordinates\r\n * @param props - The component props\r\n * @returns The positioned popover component\r\n */\r\nexport const PositionedPopover: FunctionComponent<PropsWithChildren<PositionedPopoverProps>> = (props) => {\r\n const [open, setOpen] = useState(false);\r\n\r\n useEffect(() => {\r\n setOpen(props.visible);\r\n }, [props.visible, props.x, props.y]);\r\n\r\n const handleOpenChange = (_: OpenPopoverEvents, data: OnOpenChangeData) => {\r\n setOpen(data.open);\r\n\r\n if (!data.open) {\r\n props.hide();\r\n }\r\n };\r\n\r\n return (\r\n <Popover\r\n open={open}\r\n onOpenChange={handleOpenChange}\r\n positioning={{\r\n position: \"below\", // Places the popover directly below the trigger element\r\n align: \"center\", // Centers the popover horizontally relative to the trigger element\r\n autoSize: \"height-always\", //Automatically adjusts the popover height to fit within the viewport\r\n fallbackPositions: [\"above\", \"after\", \"before\"], //If the primary position doesn't fit, automatically tries these positions in order\r\n }}\r\n withArrow={false} // Removes arrow that points to trigger element\r\n >\r\n <PopoverTrigger>\r\n {/* Use the invisible div as the trigger location*/}\r\n <div\r\n style={{\r\n position: \"absolute\",\r\n left: `${props.x}px`,\r\n top: `${props.y}px`,\r\n width: 1,\r\n height: 1,\r\n pointerEvents: \"none\", // so it's invisible to interaction\r\n }}\r\n />\r\n </PopoverTrigger>\r\n <PopoverSurface>{props.children}</PopoverSurface>\r\n </Popover>\r\n );\r\n};\r\n"]}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
+
import { Field, SearchBox as FluentSearchBox, makeStyles } from "@fluentui/react-components";
|
3
|
+
import { forwardRef } from "react";
|
4
|
+
const useStyles = makeStyles({
|
5
|
+
search: {
|
6
|
+
minWidth: "50px",
|
7
|
+
},
|
8
|
+
});
|
9
|
+
export const SearchBar = forwardRef((props, ref) => {
|
10
|
+
const classes = useStyles();
|
11
|
+
const onChange = (_, data) => {
|
12
|
+
props.onChange(data.value);
|
13
|
+
};
|
14
|
+
return (_jsx(Field, { children: _jsx(FluentSearchBox, { ref: ref, className: classes.search, placeholder: props.placeholder, onChange: onChange }) }));
|
15
|
+
});
|
16
|
+
//# sourceMappingURL=searchBar.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"searchBar.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/searchBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,IAAI,eAAe,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAE7F,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAMnC,MAAM,SAAS,GAAG,UAAU,CAAC;IACzB,MAAM,EAAE;QACJ,QAAQ,EAAE,MAAM;KACnB;CACJ,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,SAAS,GAAG,UAAU,CAAgC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC9E,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAgE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE;QACtF,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC,CAAC;IAEF,OAAO,CACH,KAAC,KAAK,cACF,KAAC,eAAe,IAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,EAAE,QAAQ,GAAI,GACxG,CACX,CAAC;AACN,CAAC,CAAC,CAAC","sourcesContent":["import { Field, SearchBox as FluentSearchBox, makeStyles } from \"@fluentui/react-components\";\r\nimport type { InputOnChangeData, SearchBoxChangeEvent } from \"@fluentui/react-components\";\r\nimport { forwardRef } from \"react\";\r\n\r\ntype SearchProps = {\r\n onChange: (val: string) => void;\r\n placeholder?: string;\r\n};\r\nconst useStyles = makeStyles({\r\n search: {\r\n minWidth: \"50px\",\r\n },\r\n});\r\n\r\nexport const SearchBar = forwardRef<HTMLInputElement, SearchProps>((props, ref) => {\r\n const classes = useStyles();\r\n const onChange: (ev: SearchBoxChangeEvent, data: InputOnChangeData) => void = (_, data) => {\r\n props.onChange(data.value);\r\n };\r\n\r\n return (\r\n <Field>\r\n <FluentSearchBox ref={ref} className={classes.search} placeholder={props.placeholder} onChange={onChange} />\r\n </Field>\r\n );\r\n});\r\n"]}
|
@@ -1,6 +1,13 @@
|
|
1
|
-
type
|
2
|
-
|
3
|
-
|
1
|
+
import type { FunctionComponent } from "react";
|
2
|
+
type SearchBoxProps = {
|
3
|
+
items: string[];
|
4
|
+
onItemSelected: (item: string) => void;
|
5
|
+
title?: string;
|
4
6
|
};
|
5
|
-
|
7
|
+
/**
|
8
|
+
* SearchBox component that displays a popup with search functionality
|
9
|
+
* @param props - The component props
|
10
|
+
* @returns The search box component
|
11
|
+
*/
|
12
|
+
export declare const SearchBox: FunctionComponent<SearchBoxProps>;
|
6
13
|
export {};
|
@@ -1,15 +1,97 @@
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
2
|
-
import {
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
2
|
+
import { makeStyles, tokens } from "@fluentui/react-components";
|
3
|
+
import { SearchBar } from "./searchBar.js";
|
4
|
+
import { useState, useEffect } from "react";
|
5
|
+
const useSearchBoxStyles = makeStyles({
|
6
|
+
searchBox: {
|
7
|
+
width: "300px",
|
8
|
+
height: "400px",
|
9
|
+
backgroundColor: tokens.colorNeutralBackground1,
|
10
|
+
border: `${tokens.strokeWidthThick} solid ${tokens.colorNeutralStroke1}`,
|
11
|
+
borderRadius: tokens.borderRadiusMedium,
|
12
|
+
boxShadow: tokens.shadow16,
|
13
|
+
display: "grid",
|
14
|
+
gridTemplateRows: "auto auto 1fr",
|
15
|
+
overflow: "hidden", // Prevent content overflow
|
16
|
+
},
|
17
|
+
title: {
|
18
|
+
borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,
|
19
|
+
margin: tokens.spacingVerticalXS,
|
20
|
+
paddingBottom: tokens.spacingVerticalXS,
|
21
|
+
color: tokens.colorNeutralForeground1,
|
22
|
+
gridRow: "1",
|
23
|
+
fontSize: tokens.fontSizeBase300,
|
24
|
+
fontWeight: tokens.fontWeightSemibold,
|
25
|
+
},
|
26
|
+
filterContainer: {
|
27
|
+
margin: tokens.spacingVerticalXS,
|
28
|
+
paddingBottom: tokens.spacingVerticalXS,
|
29
|
+
gridRow: "2",
|
30
|
+
},
|
31
|
+
list: {
|
32
|
+
gridRow: "3",
|
33
|
+
overflowY: "auto",
|
34
|
+
display: "flex",
|
35
|
+
flexDirection: "column",
|
36
|
+
maxHeight: "100%",
|
37
|
+
},
|
38
|
+
listItem: {
|
39
|
+
marginLeft: tokens.spacingHorizontalXS,
|
40
|
+
marginRight: tokens.spacingHorizontalXS,
|
41
|
+
cursor: "pointer",
|
42
|
+
color: tokens.colorNeutralForeground1,
|
43
|
+
marginTop: tokens.spacingVerticalXXS,
|
44
|
+
marginBottom: tokens.spacingVerticalXXS,
|
45
|
+
padding: tokens.spacingVerticalXS,
|
46
|
+
borderRadius: tokens.borderRadiusSmall,
|
47
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
48
|
+
":hover": {
|
49
|
+
backgroundColor: tokens.colorNeutralBackground2Hover,
|
50
|
+
},
|
51
|
+
},
|
52
|
+
listItemSelected: {
|
53
|
+
backgroundColor: tokens.colorBrandBackground,
|
54
|
+
color: tokens.colorNeutralForegroundOnBrand,
|
55
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
56
|
+
":hover": {
|
57
|
+
backgroundColor: tokens.colorBrandBackgroundHover,
|
58
|
+
},
|
6
59
|
},
|
7
60
|
});
|
61
|
+
/**
|
62
|
+
* SearchBox component that displays a popup with search functionality
|
63
|
+
* @param props - The component props
|
64
|
+
* @returns The search box component
|
65
|
+
*/
|
8
66
|
export const SearchBox = (props) => {
|
9
|
-
const classes =
|
10
|
-
const
|
11
|
-
|
67
|
+
const classes = useSearchBoxStyles();
|
68
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
69
|
+
const [items, setItems] = useState(props.items);
|
70
|
+
// In future could replace this with a fluent component like menuList or comboBox depending on desired UX
|
71
|
+
const onKeyDown = (evt) => {
|
72
|
+
if (items.length === 0) {
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
if (evt.code === "Enter") {
|
76
|
+
props.onItemSelected(items[selectedIndex]);
|
77
|
+
return;
|
78
|
+
}
|
79
|
+
if (evt.code === "ArrowDown") {
|
80
|
+
setSelectedIndex((prev) => Math.min(prev + 1, items.length - 1));
|
81
|
+
return;
|
82
|
+
}
|
83
|
+
if (evt.code === "ArrowUp") {
|
84
|
+
setSelectedIndex((prev) => Math.max(prev - 1, 0));
|
85
|
+
return;
|
86
|
+
}
|
87
|
+
};
|
88
|
+
const onFilterChange = (filter) => {
|
89
|
+
const filteredItems = props.items.filter((item) => item.toLowerCase().includes(filter.toLowerCase()));
|
90
|
+
setItems(filteredItems);
|
12
91
|
};
|
13
|
-
|
92
|
+
useEffect(() => {
|
93
|
+
setItems(props.items);
|
94
|
+
}, [props.items]);
|
95
|
+
return (_jsxs("div", { className: classes.searchBox, onKeyDown: onKeyDown, children: [props.title ? _jsx("div", { className: classes.title, children: props.title }) : null, _jsx("div", { className: classes.filterContainer, children: _jsx(SearchBar, { onChange: onFilterChange, placeholder: "Search..." }) }), _jsx("div", { role: "listbox", className: classes.list, children: items.map((item, index) => (_jsx("div", { role: "option", className: `${classes.listItem} ${index === selectedIndex ? classes.listItemSelected : ""}`, onClick: () => props.onItemSelected(item), children: item }, item))) })] }));
|
14
96
|
};
|
15
97
|
//# sourceMappingURL=searchBox.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"searchBox.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/searchBox.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,KAAK,EAAE,SAAS,
|
1
|
+
{"version":3,"file":"searchBox.js","sourceRoot":"","sources":["../../../../../dev/sharedUiComponents/src/fluent/primitives/searchBox.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAQ5C,MAAM,kBAAkB,GAAG,UAAU,CAAC;IAClC,SAAS,EAAE;QACP,KAAK,EAAE,OAAO;QACd,MAAM,EAAE,OAAO;QACf,eAAe,EAAE,MAAM,CAAC,uBAAuB;QAC/C,MAAM,EAAE,GAAG,MAAM,CAAC,gBAAgB,UAAU,MAAM,CAAC,mBAAmB,EAAE;QACxE,YAAY,EAAE,MAAM,CAAC,kBAAkB;QACvC,SAAS,EAAE,MAAM,CAAC,QAAQ;QAC1B,OAAO,EAAE,MAAM;QACf,gBAAgB,EAAE,eAAe;QACjC,QAAQ,EAAE,QAAQ,EAAE,2BAA2B;KAClD;IACD,KAAK,EAAE;QACH,YAAY,EAAE,GAAG,MAAM,CAAC,eAAe,UAAU,MAAM,CAAC,mBAAmB,EAAE;QAC7E,MAAM,EAAE,MAAM,CAAC,iBAAiB;QAChC,aAAa,EAAE,MAAM,CAAC,iBAAiB;QACvC,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,OAAO,EAAE,GAAG;QACZ,QAAQ,EAAE,MAAM,CAAC,eAAe;QAChC,UAAU,EAAE,MAAM,CAAC,kBAAkB;KACxC;IACD,eAAe,EAAE;QACb,MAAM,EAAE,MAAM,CAAC,iBAAiB;QAChC,aAAa,EAAE,MAAM,CAAC,iBAAiB;QACvC,OAAO,EAAE,GAAG;KACf;IACD,IAAI,EAAE;QACF,OAAO,EAAE,GAAG;QACZ,SAAS,EAAE,MAAM;QACjB,OAAO,EAAE,MAAM;QACf,aAAa,EAAE,QAAQ;QACvB,SAAS,EAAE,MAAM;KACpB;IACD,QAAQ,EAAE;QACN,UAAU,EAAE,MAAM,CAAC,mBAAmB;QACtC,WAAW,EAAE,MAAM,CAAC,mBAAmB;QACvC,MAAM,EAAE,SAAS;QACjB,KAAK,EAAE,MAAM,CAAC,uBAAuB;QACrC,SAAS,EAAE,MAAM,CAAC,kBAAkB;QACpC,YAAY,EAAE,MAAM,CAAC,kBAAkB;QACvC,OAAO,EAAE,MAAM,CAAC,iBAAiB;QACjC,YAAY,EAAE,MAAM,CAAC,iBAAiB;QAEtC,gEAAgE;QAChE,QAAQ,EAAE;YACN,eAAe,EAAE,MAAM,CAAC,4BAA4B;SACvD;KACJ;IACD,gBAAgB,EAAE;QACd,eAAe,EAAE,MAAM,CAAC,oBAAoB;QAC5C,KAAK,EAAE,MAAM,CAAC,6BAA6B;QAE3C,gEAAgE;QAChE,QAAQ,EAAE;YACN,eAAe,EAAE,MAAM,CAAC,yBAAyB;SACpD;KACJ;CACJ,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAsC,CAAC,KAAK,EAAE,EAAE;IAClE,MAAM,OAAO,GAAG,kBAAkB,EAAE,CAAC;IACrC,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChD,yGAAyG;IACzG,MAAM,SAAS,GAAG,CAAC,GAAwB,EAAE,EAAE;QAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrB,OAAO;QACX,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACvB,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAC3B,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YACjE,OAAO;QACX,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACzB,gBAAgB,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAClD,OAAO;QACX,CAAC;IACL,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,EAAE;QACtC,MAAM,aAAa,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACtG,QAAQ,CAAC,aAAa,CAAC,CAAC;IAC5B,CAAC,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACX,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAElB,OAAO,CACH,eAAK,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,aAClD,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,cAAK,SAAS,EAAE,OAAO,CAAC,KAAK,YAAG,KAAK,CAAC,KAAK,GAAO,CAAC,CAAC,CAAC,IAAI,EACxE,cAAK,SAAS,EAAE,OAAO,CAAC,eAAe,YACnC,KAAC,SAAS,IAAC,QAAQ,EAAE,cAAc,EAAE,WAAW,EAAC,WAAW,GAAG,GAC7D,EACN,cAAK,IAAI,EAAC,SAAS,EAAC,SAAS,EAAE,OAAO,CAAC,IAAI,YACtC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CACxB,cACI,IAAI,EAAC,QAAQ,EAEb,SAAS,EAAE,GAAG,OAAO,CAAC,QAAQ,IAAI,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,EAAE,EAC3F,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,YAExC,IAAI,IAJA,IAAI,CAKP,CACT,CAAC,GACA,IACJ,CACT,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { makeStyles, tokens } from \"@fluentui/react-components\";\r\nimport { SearchBar } from \"./searchBar\";\r\nimport type { FunctionComponent } from \"react\";\r\nimport { useState, useEffect } from \"react\";\r\n\r\ntype SearchBoxProps = {\r\n items: string[];\r\n onItemSelected: (item: string) => void;\r\n title?: string;\r\n};\r\n\r\nconst useSearchBoxStyles = makeStyles({\r\n searchBox: {\r\n width: \"300px\",\r\n height: \"400px\",\r\n backgroundColor: tokens.colorNeutralBackground1,\r\n border: `${tokens.strokeWidthThick} solid ${tokens.colorNeutralStroke1}`,\r\n borderRadius: tokens.borderRadiusMedium,\r\n boxShadow: tokens.shadow16,\r\n display: \"grid\",\r\n gridTemplateRows: \"auto auto 1fr\",\r\n overflow: \"hidden\", // Prevent content overflow\r\n },\r\n title: {\r\n borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke2}`,\r\n margin: tokens.spacingVerticalXS,\r\n paddingBottom: tokens.spacingVerticalXS,\r\n color: tokens.colorNeutralForeground1,\r\n gridRow: \"1\",\r\n fontSize: tokens.fontSizeBase300,\r\n fontWeight: tokens.fontWeightSemibold,\r\n },\r\n filterContainer: {\r\n margin: tokens.spacingVerticalXS,\r\n paddingBottom: tokens.spacingVerticalXS,\r\n gridRow: \"2\",\r\n },\r\n list: {\r\n gridRow: \"3\",\r\n overflowY: \"auto\",\r\n display: \"flex\",\r\n flexDirection: \"column\",\r\n maxHeight: \"100%\",\r\n },\r\n listItem: {\r\n marginLeft: tokens.spacingHorizontalXS,\r\n marginRight: tokens.spacingHorizontalXS,\r\n cursor: \"pointer\",\r\n color: tokens.colorNeutralForeground1,\r\n marginTop: tokens.spacingVerticalXXS,\r\n marginBottom: tokens.spacingVerticalXXS,\r\n padding: tokens.spacingVerticalXS,\r\n borderRadius: tokens.borderRadiusSmall,\r\n\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n \":hover\": {\r\n backgroundColor: tokens.colorNeutralBackground2Hover,\r\n },\r\n },\r\n listItemSelected: {\r\n backgroundColor: tokens.colorBrandBackground,\r\n color: tokens.colorNeutralForegroundOnBrand,\r\n\r\n // eslint-disable-next-line @typescript-eslint/naming-convention\r\n \":hover\": {\r\n backgroundColor: tokens.colorBrandBackgroundHover,\r\n },\r\n },\r\n});\r\n\r\n/**\r\n * SearchBox component that displays a popup with search functionality\r\n * @param props - The component props\r\n * @returns The search box component\r\n */\r\nexport const SearchBox: FunctionComponent<SearchBoxProps> = (props) => {\r\n const classes = useSearchBoxStyles();\r\n const [selectedIndex, setSelectedIndex] = useState(0);\r\n const [items, setItems] = useState(props.items);\r\n // In future could replace this with a fluent component like menuList or comboBox depending on desired UX\r\n const onKeyDown = (evt: React.KeyboardEvent) => {\r\n if (items.length === 0) {\r\n return;\r\n }\r\n if (evt.code === \"Enter\") {\r\n props.onItemSelected(items[selectedIndex]);\r\n return;\r\n }\r\n\r\n if (evt.code === \"ArrowDown\") {\r\n setSelectedIndex((prev) => Math.min(prev + 1, items.length - 1));\r\n return;\r\n }\r\n\r\n if (evt.code === \"ArrowUp\") {\r\n setSelectedIndex((prev) => Math.max(prev - 1, 0));\r\n return;\r\n }\r\n };\r\n\r\n const onFilterChange = (filter: string) => {\r\n const filteredItems = props.items.filter((item) => item.toLowerCase().includes(filter.toLowerCase()));\r\n setItems(filteredItems);\r\n };\r\n\r\n useEffect(() => {\r\n setItems(props.items);\r\n }, [props.items]);\r\n\r\n return (\r\n <div className={classes.searchBox} onKeyDown={onKeyDown}>\r\n {props.title ? <div className={classes.title}>{props.title}</div> : null}\r\n <div className={classes.filterContainer}>\r\n <SearchBar onChange={onFilterChange} placeholder=\"Search...\" />\r\n </div>\r\n <div role=\"listbox\" className={classes.list}>\r\n {items.map((item, index) => (\r\n <div\r\n role=\"option\"\r\n key={item}\r\n className={`${classes.listItem} ${index === selectedIndex ? classes.listItemSelected : \"\"}`}\r\n onClick={() => props.onItemSelected(item)}\r\n >\r\n {item}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n );\r\n};\r\n"]}
|
@@ -21,5 +21,7 @@ export declare class SearchBoxComponent extends React.Component<ISearchBoxCompon
|
|
21
21
|
onFilterChange(evt: React.ChangeEvent<HTMLInputElement>): void;
|
22
22
|
onNewNodeRequested(name: string): void;
|
23
23
|
onKeyDown(evt: React.KeyboardEvent): void;
|
24
|
-
|
24
|
+
renderFluent(): import("react/jsx-runtime").JSX.Element;
|
25
|
+
renderOriginal(): import("react/jsx-runtime").JSX.Element | null;
|
26
|
+
render(): import("react/jsx-runtime").JSX.Element;
|
25
27
|
}
|
@@ -2,6 +2,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
3
3
|
import "./searchBox.scss";
|
4
4
|
import { NodeLedger } from "./nodeLedger.js";
|
5
|
+
import { ToolContext } from "../fluent/hoc/fluentToolWrapper.js";
|
6
|
+
import { PositionedPopover } from "../fluent/primitives/positionedPopover.js";
|
7
|
+
import { SearchBox } from "../fluent/primitives/searchBox.js";
|
5
8
|
/**
|
6
9
|
* The search box component.
|
7
10
|
*/
|
@@ -53,7 +56,25 @@ export class SearchBoxComponent extends React.Component {
|
|
53
56
|
return;
|
54
57
|
}
|
55
58
|
}
|
56
|
-
|
59
|
+
renderFluent() {
|
60
|
+
// Note this function no longer uses other helpers from this file for easy non-fluent removal
|
61
|
+
// Sort and deduplicate the node names.
|
62
|
+
this._nodes = Array.from(new Set(NodeLedger.RegisteredNodeNames.sort()));
|
63
|
+
const formattedNodes = this._nodes.map((name) => NodeLedger.NameFormatter(name));
|
64
|
+
return (_jsx(PositionedPopover, { x: this._targetX, y: this._targetY, visible: this.state.isVisible, hide: () => {
|
65
|
+
this.props.stateManager.modalIsDisplayed = false;
|
66
|
+
}, children: _jsx(SearchBox, { items: formattedNodes, onItemSelected: (item) => {
|
67
|
+
const originalName = this._nodes[formattedNodes.indexOf(item)];
|
68
|
+
this.props.stateManager.onNewBlockRequiredObservable.notifyObservers({
|
69
|
+
type: originalName,
|
70
|
+
targetX: this._targetX,
|
71
|
+
targetY: this._targetY,
|
72
|
+
needRepositioning: true,
|
73
|
+
smartAdd: true,
|
74
|
+
});
|
75
|
+
}, title: "Add a node" }) }));
|
76
|
+
}
|
77
|
+
renderOriginal() {
|
57
78
|
if (!this.state.isVisible) {
|
58
79
|
return null;
|
59
80
|
}
|
@@ -88,5 +109,8 @@ export class SearchBoxComponent extends React.Component {
|
|
88
109
|
return (_jsx("div", { className: "graph-search-box-list-item " + (this.state.selectedIndex === i ? "selected " : ""), onClick: () => this.onNewNodeRequested(name), children: NodeLedger.NameFormatter(name) }, name));
|
89
110
|
}) })] })] }));
|
90
111
|
}
|
112
|
+
render() {
|
113
|
+
return _jsx(ToolContext.Consumer, { children: ({ useFluent }) => (useFluent ? this.renderFluent() : this.renderOriginal()) });
|
114
|
+
}
|
91
115
|
}
|
92
116
|
//# sourceMappingURL=searchBox.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"searchBox.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/nodeGraphSystem/searchBox.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAM1C;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK,CAAC,SAAkG;IAM5I,YAAY,KAA+B;QACvC,KAAK,CAAC,KAAK,CAAC,CAAC;QAEb,IAAI,CAAC,KAAK,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QAEhE,IAAI,CAAC,aAAa,GAAG,CAAC,GAAkB,EAAE,EAAE;YACxC,IAAI,GAAG,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACvB,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,CAAC;QACL,CAAC,CAAC;QAEF,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,6BAA6B,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YAC9D,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,QAAQ,GAAG,GAAG,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC,CAAC;YACjE,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,gBAAgB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACzF,CAAC,CAAC,CAAC;IACP,CAAC;IAED,IAAI;QACA,IAAI,CAAC,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,GAAG,KAAK,CAAC;QACjD,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,mBAAmB,CAAC,SAAS,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;IAC5F,CAAC;IAED,cAAc,CAAC,GAAwC;QACnD,IAAI,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,kBAAkB,CAAC,IAAY;QAC3B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,4BAA4B,CAAC,eAAe,CAAC;YACjE,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,iBAAiB,EAAE,IAAI;YACvB,QAAQ,EAAE,IAAI;SACjB,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,EAAE,CAAC;IAChB,CAAC;IAED,SAAS,CAAC,GAAwB;QAC9B,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjD,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC;YAC/D,OAAO;QACX,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;YACjG,OAAO;QACX,CAAC;QAED,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;YAC5E,OAAO;QACX,CAAC;IACL,CAAC;IAEQ,MAAM;QACX,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,MAAM,aAAa,GAAG,GAAG,CAAC;QAC1B,MAAM,cAAc,GAAG,GAAG,CAAC;QAE3B,uCAAuC;QACvC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,UAAU,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAEzE,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;YACpB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,CAAC;YACtD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9G,CAAC;QAED,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,YAAY,CAAC,cAAc,CAAC,cAAc,CAAE,CAAC,qBAAqB,EAAE,CAAC;QACnH,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,aAAa,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QACtE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,cAAc,GAAG,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,QAAQ,GAAG;YACb,IAAI,EAAE,OAAO,GAAG,IAAI;YACpB,GAAG,EAAE,OAAO,GAAG,IAAI;SACtB,CAAC;QAEF,IAAI,OAAO,GAAG,aAAa,GAAG,aAAa,CAAC,KAAK,EAAE,CAAC;YAChD,QAAQ,CAAC,IAAI,GAAG,aAAa,CAAC,KAAK,GAAG,aAAa,GAAG,EAAE,GAAG,IAAI,CAAC;QACpE,CAAC;aAAM,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;YACtB,QAAQ,CAAC,IAAI,GAAG,MAAM,CAAC;QAC3B,CAAC;QAED,IAAI,OAAO,GAAG,cAAc,GAAG,aAAa,CAAC,MAAM,EAAE,CAAC;YAClD,QAAQ,CAAC,GAAG,GAAG,aAAa,CAAC,MAAM,GAAG,cAAc,GAAG,EAAE,GAAG,IAAI,CAAC;QACrE,CAAC;aAAM,IAAI,OAAO,GAAG,EAAE,EAAE,CAAC;YACtB,QAAQ,CAAC,GAAG,GAAG,MAAM,CAAC;QAC1B,CAAC;QAED,OAAO,CACH,eAAK,EAAE,EAAC,wBAAwB,aAC5B,cAAK,EAAE,EAAC,8BAA8B,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAQ,EACzE,eAAK,EAAE,EAAC,kBAAkB,EAAC,KAAK,EAAE,QAAQ,aACtC,cAAK,SAAS,EAAC,wBAAwB,2BAAiB,EACxD,gBACI,IAAI,EAAC,MAAM,EACX,WAAW,EAAC,WAAW,EACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAC3C,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EACvC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EACxB,SAAS,EAAC,yBAAyB,EACnC,SAAS,EAAE,IAAI,EACf,QAAQ,EAAE,CAAC,GACb,EACF,cAAK,SAAS,EAAC,uBAAuB,YACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gCACzB,OAAO,CACH,cACI,SAAS,EAAE,6BAA6B,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,EAC9F,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,YAG3C,UAAU,CAAC,aAAa,CAAC,IAAI,CAAC,IAF1B,IAAI,CAGP,CACT,CAAC;4BACN,CAAC,CAAC,GACA,IACJ,IACJ,CACT,CAAC;IACN,CAAC;CACJ","sourcesContent":["import * as React from \"react\";\r\nimport type { StateManager } from \"./stateManager\";\r\nimport \"./searchBox.scss\";\r\nimport { NodeLedger } from \"./nodeLedger\";\r\n\r\nexport interface ISearchBoxComponentProps {\r\n stateManager: StateManager;\r\n}\r\n\r\n/**\r\n * The search box component.\r\n */\r\nexport class SearchBoxComponent extends React.Component<ISearchBoxComponentProps, { isVisible: boolean; filter: string; selectedIndex: number }> {\r\n private _handleEscKey: (evt: KeyboardEvent) => void;\r\n private _targetX: number;\r\n private _targetY: number;\r\n private _nodes: string[];\r\n\r\n constructor(props: ISearchBoxComponentProps) {\r\n super(props);\r\n\r\n this.state = { isVisible: false, filter: \"\", selectedIndex: 0 };\r\n\r\n this._handleEscKey = (evt: KeyboardEvent) => {\r\n if (evt.key === \"Escape\") {\r\n this.hide();\r\n }\r\n };\r\n\r\n this.props.stateManager.onSearchBoxRequiredObservable.add((loc) => {\r\n this._targetX = loc.x;\r\n this._targetY = loc.y;\r\n this.setState({ isVisible: true, filter: \"\", selectedIndex: 0 });\r\n this.props.stateManager.hostDocument.addEventListener(\"keydown\", this._handleEscKey);\r\n });\r\n }\r\n\r\n hide() {\r\n this.setState({ isVisible: false });\r\n this.props.stateManager.modalIsDisplayed = false;\r\n this.props.stateManager.hostDocument.removeEventListener(\"keydown\", this._handleEscKey);\r\n }\r\n\r\n onFilterChange(evt: React.ChangeEvent<HTMLInputElement>) {\r\n this.setState({ filter: evt.target.value });\r\n }\r\n\r\n onNewNodeRequested(name: string) {\r\n this.props.stateManager.onNewBlockRequiredObservable.notifyObservers({\r\n type: name,\r\n targetX: this._targetX,\r\n targetY: this._targetY,\r\n needRepositioning: true,\r\n smartAdd: true,\r\n });\r\n\r\n this.hide();\r\n }\r\n\r\n onKeyDown(evt: React.KeyboardEvent) {\r\n if (evt.code === \"Enter\" && this._nodes.length > 0) {\r\n this.onNewNodeRequested(this._nodes[this.state.selectedIndex]);\r\n return;\r\n }\r\n\r\n if (evt.code === \"ArrowDown\" && this._nodes.length > 0) {\r\n this.setState({ selectedIndex: Math.min(this.state.selectedIndex + 1, this._nodes.length - 1) });\r\n return;\r\n }\r\n\r\n if (evt.code === \"ArrowUp\" && this._nodes.length > 0) {\r\n this.setState({ selectedIndex: Math.max(this.state.selectedIndex - 1, 0) });\r\n return;\r\n }\r\n }\r\n\r\n override render() {\r\n if (!this.state.isVisible) {\r\n return null;\r\n }\r\n\r\n const expectedWidth = 300;\r\n const expectedHeight = 400;\r\n\r\n // Sort and deduplicate the node names.\r\n this._nodes = Array.from(new Set(NodeLedger.RegisteredNodeNames.sort()));\r\n\r\n if (this.state.filter) {\r\n const filter = this.state.filter.toLowerCase().trim();\r\n this._nodes = this._nodes.filter((name) => NodeLedger.NameFormatter(name).toLowerCase().includes(filter));\r\n }\r\n\r\n const containerRect = this.props.stateManager.hostDocument.getElementById(\"graph-canvas\")!.getBoundingClientRect();\r\n const targetX = this._targetX - (expectedWidth / 2 + containerRect.x);\r\n const targetY = this._targetY - (expectedHeight / 2 + containerRect.y);\r\n const locStyle = {\r\n left: targetX + \"px\",\r\n top: targetY + \"px\",\r\n };\r\n\r\n if (targetX + expectedWidth > containerRect.width) {\r\n locStyle.left = containerRect.width - expectedWidth - 10 + \"px\";\r\n } else if (targetX < 10) {\r\n locStyle.left = \"10px\";\r\n }\r\n\r\n if (targetY + expectedHeight > containerRect.height) {\r\n locStyle.top = containerRect.height - expectedHeight - 10 + \"px\";\r\n } else if (targetY < 10) {\r\n locStyle.top = \"10px\";\r\n }\r\n\r\n return (\r\n <div id=\"graph-search-container\">\r\n <div id=\"graph-search-picking-blocker\" onClick={() => this.hide()}></div>\r\n <div id=\"graph-search-box\" style={locStyle}>\r\n <div className=\"graph-search-box-title\">Add a node</div>\r\n <input\r\n type=\"text\"\r\n placeholder=\"Search...\"\r\n onChange={(evt) => this.onFilterChange(evt)}\r\n onKeyDown={(evt) => this.onKeyDown(evt)}\r\n value={this.state.filter}\r\n className=\"graph-search-box-filter\"\r\n autoFocus={true}\r\n tabIndex={0}\r\n />\r\n <div className=\"graph-search-box-list\">\r\n {this._nodes.map((name, i) => {\r\n return (\r\n <div\r\n className={\"graph-search-box-list-item \" + (this.state.selectedIndex === i ? \"selected \" : \"\")}\r\n onClick={() => this.onNewNodeRequested(name)}\r\n key={name}\r\n >\r\n {NodeLedger.NameFormatter(name)}\r\n </div>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n}\r\n"]}
|
1
|
+
{"version":3,"file":"searchBox.js","sourceRoot":"","sources":["../../../../dev/sharedUiComponents/src/nodeGraphSystem/searchBox.tsx"],"names":[],"mappings":"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","sourcesContent":["import * as React from \"react\";\r\nimport type { StateManager } from \"./stateManager\";\r\nimport \"./searchBox.scss\";\r\nimport { NodeLedger } from \"./nodeLedger\";\r\nimport { ToolContext } from \"../fluent/hoc/fluentToolWrapper\";\r\nimport { PositionedPopover } from \"../fluent/primitives/positionedPopover\";\r\nimport { SearchBox } from \"../fluent/primitives/searchBox\";\r\n\r\nexport interface ISearchBoxComponentProps {\r\n stateManager: StateManager;\r\n}\r\n\r\n/**\r\n * The search box component.\r\n */\r\nexport class SearchBoxComponent extends React.Component<ISearchBoxComponentProps, { isVisible: boolean; filter: string; selectedIndex: number }> {\r\n private _handleEscKey: (evt: KeyboardEvent) => void;\r\n private _targetX: number;\r\n private _targetY: number;\r\n private _nodes: string[];\r\n\r\n constructor(props: ISearchBoxComponentProps) {\r\n super(props);\r\n\r\n this.state = { isVisible: false, filter: \"\", selectedIndex: 0 };\r\n\r\n this._handleEscKey = (evt: KeyboardEvent) => {\r\n if (evt.key === \"Escape\") {\r\n this.hide();\r\n }\r\n };\r\n\r\n this.props.stateManager.onSearchBoxRequiredObservable.add((loc) => {\r\n this._targetX = loc.x;\r\n this._targetY = loc.y;\r\n this.setState({ isVisible: true, filter: \"\", selectedIndex: 0 });\r\n this.props.stateManager.hostDocument.addEventListener(\"keydown\", this._handleEscKey);\r\n });\r\n }\r\n\r\n hide() {\r\n this.setState({ isVisible: false });\r\n this.props.stateManager.modalIsDisplayed = false;\r\n this.props.stateManager.hostDocument.removeEventListener(\"keydown\", this._handleEscKey);\r\n }\r\n\r\n onFilterChange(evt: React.ChangeEvent<HTMLInputElement>) {\r\n this.setState({ filter: evt.target.value });\r\n }\r\n\r\n onNewNodeRequested(name: string) {\r\n this.props.stateManager.onNewBlockRequiredObservable.notifyObservers({\r\n type: name,\r\n targetX: this._targetX,\r\n targetY: this._targetY,\r\n needRepositioning: true,\r\n smartAdd: true,\r\n });\r\n\r\n this.hide();\r\n }\r\n\r\n onKeyDown(evt: React.KeyboardEvent) {\r\n if (evt.code === \"Enter\" && this._nodes.length > 0) {\r\n this.onNewNodeRequested(this._nodes[this.state.selectedIndex]);\r\n return;\r\n }\r\n\r\n if (evt.code === \"ArrowDown\" && this._nodes.length > 0) {\r\n this.setState({ selectedIndex: Math.min(this.state.selectedIndex + 1, this._nodes.length - 1) });\r\n return;\r\n }\r\n\r\n if (evt.code === \"ArrowUp\" && this._nodes.length > 0) {\r\n this.setState({ selectedIndex: Math.max(this.state.selectedIndex - 1, 0) });\r\n return;\r\n }\r\n }\r\n\r\n renderFluent() {\r\n // Note this function no longer uses other helpers from this file for easy non-fluent removal\r\n\r\n // Sort and deduplicate the node names.\r\n this._nodes = Array.from(new Set(NodeLedger.RegisteredNodeNames.sort()));\r\n const formattedNodes = this._nodes.map((name) => NodeLedger.NameFormatter(name));\r\n\r\n return (\r\n <PositionedPopover\r\n x={this._targetX}\r\n y={this._targetY}\r\n visible={this.state.isVisible}\r\n hide={() => {\r\n this.props.stateManager.modalIsDisplayed = false;\r\n }}\r\n >\r\n <SearchBox\r\n items={formattedNodes}\r\n onItemSelected={(item: string) => {\r\n const originalName = this._nodes[formattedNodes.indexOf(item)];\r\n this.props.stateManager.onNewBlockRequiredObservable.notifyObservers({\r\n type: originalName,\r\n targetX: this._targetX,\r\n targetY: this._targetY,\r\n needRepositioning: true,\r\n smartAdd: true,\r\n });\r\n }}\r\n title=\"Add a node\"\r\n />\r\n </PositionedPopover>\r\n );\r\n }\r\n\r\n renderOriginal() {\r\n if (!this.state.isVisible) {\r\n return null;\r\n }\r\n\r\n const expectedWidth = 300;\r\n const expectedHeight = 400;\r\n\r\n // Sort and deduplicate the node names.\r\n this._nodes = Array.from(new Set(NodeLedger.RegisteredNodeNames.sort()));\r\n\r\n if (this.state.filter) {\r\n const filter = this.state.filter.toLowerCase().trim();\r\n this._nodes = this._nodes.filter((name) => NodeLedger.NameFormatter(name).toLowerCase().includes(filter));\r\n }\r\n\r\n const containerRect = this.props.stateManager.hostDocument.getElementById(\"graph-canvas\")!.getBoundingClientRect();\r\n const targetX = this._targetX - (expectedWidth / 2 + containerRect.x);\r\n const targetY = this._targetY - (expectedHeight / 2 + containerRect.y);\r\n const locStyle = {\r\n left: targetX + \"px\",\r\n top: targetY + \"px\",\r\n };\r\n\r\n if (targetX + expectedWidth > containerRect.width) {\r\n locStyle.left = containerRect.width - expectedWidth - 10 + \"px\";\r\n } else if (targetX < 10) {\r\n locStyle.left = \"10px\";\r\n }\r\n\r\n if (targetY + expectedHeight > containerRect.height) {\r\n locStyle.top = containerRect.height - expectedHeight - 10 + \"px\";\r\n } else if (targetY < 10) {\r\n locStyle.top = \"10px\";\r\n }\r\n\r\n return (\r\n <div id=\"graph-search-container\">\r\n <div id=\"graph-search-picking-blocker\" onClick={() => this.hide()}></div>\r\n <div id=\"graph-search-box\" style={locStyle}>\r\n <div className=\"graph-search-box-title\">Add a node</div>\r\n <input\r\n type=\"text\"\r\n placeholder=\"Search...\"\r\n onChange={(evt) => this.onFilterChange(evt)}\r\n onKeyDown={(evt) => this.onKeyDown(evt)}\r\n value={this.state.filter}\r\n className=\"graph-search-box-filter\"\r\n autoFocus={true}\r\n tabIndex={0}\r\n />\r\n <div className=\"graph-search-box-list\">\r\n {this._nodes.map((name, i) => {\r\n return (\r\n <div\r\n className={\"graph-search-box-list-item \" + (this.state.selectedIndex === i ? \"selected \" : \"\")}\r\n onClick={() => this.onNewNodeRequested(name)}\r\n key={name}\r\n >\r\n {NodeLedger.NameFormatter(name)}\r\n </div>\r\n );\r\n })}\r\n </div>\r\n </div>\r\n </div>\r\n );\r\n }\r\n\r\n override render() {\r\n return <ToolContext.Consumer>{({ useFluent }) => (useFluent ? this.renderFluent() : this.renderOriginal())}</ToolContext.Consumer>;\r\n }\r\n}\r\n"]}
|