diff --git a/KaiUIngInferno/package.json b/KaiUIngInferno/package.json index 4874fbe..32d1364 100644 --- a/KaiUIngInferno/package.json +++ b/KaiUIngInferno/package.json @@ -22,11 +22,11 @@ }, "devDependencies": { "@babel/core": "^7.21.0", - "@parcel/packager-ts": "2.8.3", + "@parcel/packager-ts": "2.9.3", "@parcel/transformer-babel": "^2.8.3", "@parcel/transformer-sass": "^2.8.3", "@parcel/transformer-typescript-tsc": "^2.8.3", - "@parcel/transformer-typescript-types": "2.8.3", + "@parcel/transformer-typescript-types": "2.9.3", "babel-plugin-inferno": "^6.6.0", "classnames": "^2.3.2", "inferno": "^8.2.2", diff --git a/KaiUIngInferno/src/index.ts b/KaiUIngInferno/src/index.ts index 52d3132..2ea44fb 100644 --- a/KaiUIngInferno/src/index.ts +++ b/KaiUIngInferno/src/index.ts @@ -8,13 +8,15 @@ export { default as TextInput } from "./ui/TextInput"; export { default as Separator } from "./ui/Separator"; export { default as Button } from "./ui/Button"; + export { default as Avatar } from "./ui/Avatar"; export { default as DropDownMenu } from "./DropDownMenu"; + export { default as ListView } from "./views/ListView"; export { default as ListViewKeyed } from "./views/ListViewKeyed"; export { default as ListViewNonKeyed } from "./views/ListViewNonKeyed"; export { default as TabView } from "./views/TabView"; export { asArray } from "./utils"; -export { default as toast } from "./toast"; +export { default as toast } from "./toast"; \ No newline at end of file diff --git a/KaiUIngInferno/src/ui/OptionItem.tsx b/KaiUIngInferno/src/ui/OptionItem.tsx new file mode 100644 index 0000000..e4ceec4 --- /dev/null +++ b/KaiUIngInferno/src/ui/OptionItem.tsx @@ -0,0 +1,20 @@ +interface OptionItemProps { + text: string; + isFocused?: boolean; +} + +export default function OptionItem({ text, isFocused }: OptionItemProps) { + return ( +
{ + if (ref) { + isFocused ? ref.focus() : ref.blur(); + } + }} + className="kai-om-item" + $HasTextChildren + > + {text} +
); +} diff --git a/KaiUIngInferno/src/ui/OptionMenu.tsx b/KaiUIngInferno/src/ui/OptionMenu.tsx new file mode 100644 index 0000000..3486285 --- /dev/null +++ b/KaiUIngInferno/src/ui/OptionMenu.tsx @@ -0,0 +1,105 @@ +import { Component } from "inferno"; +import TextInput from "./TextInput"; +import "KaiUI/src/components/OptionMenu/OptionMenu.scss"; +import { asArray } from "../utils"; + +interface OptionMenuProps { + header: string; + children: any; + onChangeIndex?: (index: number) => void; + isActive: boolean; + onExit: () => void; + enableSearch?: boolean; +} + +interface OptionMenuState { + selectedItem: number; + searchTerm: string; +} + +export default class OptionMenu extends Component { + public state: OptionMenuState; + + handleKeyDown = (evt: KeyboardEvent) => { + if (!this.props.isActive) { + return; + } + const childrenArray: any[] = asArray(this.props.children); + const childrenLength = childrenArray.length; + evt.stopPropagation(); + let index = this.state.selectedItem; + switch (evt.key) { + case "Backspace": + index !== 0 && this.props.onExit(); + break; + case "ArrowDown": + index--; + break; + case "ArrowUp": + index++; + break; + default: + break; + } + index = (index + childrenLength) % childrenLength; + this.setState({ selectedItem: index }); + } + + constructor(props: any) { + super(props); + this.state = { + selectedItem: 0, + searchTerm: "" + } + } + + componentDidMount() { + document.addEventListener("keydown", this.handleKeyDown); + } + + componentWillUnmount() { + document.removeEventListener("keydown", this.handleKeyDown); + } + + componentDidUpdate(lastProps: OptionMenuProps, lastState: OptionMenuState) { + lastProps.onChangeIndex && lastProps.onChangeIndex(lastState.selectedItem); + } + + render() { + const { searchTerm, selectedItem } = this.state; + let childrenToRender = this.props.children; + if (this.props.enableSearch) { + childrenToRender.unshift( + this.setState({ searchTerm: text })} + label="" + fieldType="text" + /> + ); + } + + childrenToRender = childrenToRender.filter((child: any) => { + if (child.props.fieldType === "text") { + return true; + } + if (child.props.text && child.props.text.indexOf(searchTerm) >= 0) { + return true; + } + return false; + }); + + childrenToRender[selectedItem].props.isFocused = true; + + return ( +
+
{this.props.header}
+ +
+ ); + } +}; diff --git a/KaiUIngInferno/src/ui/SoftKey.tsx b/KaiUIngInferno/src/ui/SoftKey.tsx index 4ca7fdf..cd224fa 100644 --- a/KaiUIngInferno/src/ui/SoftKey.tsx +++ b/KaiUIngInferno/src/ui/SoftKey.tsx @@ -10,7 +10,7 @@ interface ButtonProps { const prefixCls = "kai-softkey"; function Button(props: ButtonProps) { - let renderedIcon: JSX.Element; + let renderedIcon: Component; if (props.icon && props.icon.toString().indexOf("kai-") === -1) { renderedIcon = ; } else { diff --git a/KaiUIngInferno/src/ui/TextInput.tsx b/KaiUIngInferno/src/ui/TextInput.tsx index 3901434..ee83079 100644 --- a/KaiUIngInferno/src/ui/TextInput.tsx +++ b/KaiUIngInferno/src/ui/TextInput.tsx @@ -1,4 +1,4 @@ -import { Component } from "inferno"; +import { Component, RefObject, createRef } from "inferno"; import classnames from "classnames"; import "KaiUI/src/components/TextInput/TextInput.scss"; import morecolor from "../morecolor"; @@ -7,7 +7,7 @@ const prefixCls = "kai-text-input"; const labelCls = `${prefixCls}-label p-thi`; const inputCls = `${prefixCls}-label p-pri`; -interface TextInputProps { +interface Props { onChange?: (text: string) => void; isFocused?: boolean; fieldType: string; @@ -18,17 +18,30 @@ interface TextInputProps { focusClass?: string; } -class TextInput extends Component { +interface State { + value: string; +} + +class TextInput extends Component { private onChange: (_evt?: Event) => void; - private textInput: any; + private textInputRef: RefObject; public state: { value: string }; - constructor(props: TextInputProps) { + onKeyDown = (evt: KeyboardEvent) => { + if (!this.props.isFocused) return; // do we need this? --Farooq + if (evt.key === "ArrowLeft" || evt.key === "ArrowRight") { + evt.stopImmediatePropagation(); + } + }; + + constructor(props: any) { const { defaultValue } = props; super(props); + this.textInputRef = createRef(); this.onChange = (_evt?: Event) => { - this.setState({ value: this.textInput.value }); - if (this.props.onChange) this.props.onChange(this.textInput.value); + if (!this.textInputRef.current) return; + this.setState({ value: this.textInputRef.current.value }); + if (this.props.onChange) this.props.onChange(this.textInputRef.current.value); }; this.state = { @@ -36,8 +49,18 @@ class TextInput extends Component { }; } + + + componentDidMount() { + this.textInputRef.current?.addEventListener("keydown", this.onKeyDown, true) + } + + componentWillUnmount() { + this.textInputRef.current?.removeEventListener("keydown", this.onKeyDown, true) + } + componentDidUpdate() { - if (this.props.isFocused) this.textInput.focus(); + if (this.props.isFocused) this.textInputRef.current?.focus(); } render() { @@ -51,9 +74,8 @@ class TextInput extends Component { id={this.props.id} tabIndex={0} className={itemCls} - style={`background-color: ${ - this.props.isFocused ? morecolor.focusColor : "" - }`} + style={`background-color: ${this.props.isFocused ? morecolor.focusColor : "" + }`} >