Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TextInput won't let Arrow Left and Right keys propagate outside when focused #22

Merged
merged 10 commits into from
Jun 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions KaiUIngInferno/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion KaiUIngInferno/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
20 changes: 20 additions & 0 deletions KaiUIngInferno/src/ui/OptionItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
interface OptionItemProps {
text: string;
isFocused?: boolean;
}

export default function OptionItem({ text, isFocused }: OptionItemProps) {
return (
<div
tabIndex={0}
ref={(ref: HTMLElement | null) => {
if (ref) {
isFocused ? ref.focus() : ref.blur();
}
}}
className="kai-om-item"
$HasTextChildren
>
{text}
</div>);
}
105 changes: 105 additions & 0 deletions KaiUIngInferno/src/ui/OptionMenu.tsx
Original file line number Diff line number Diff line change
@@ -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<OptionMenuProps, OptionMenuState> {
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(
<TextInput
id="optMenuSearch"
defaultValue={searchTerm}
placeholder="Search"
onChange={(text: string) => 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 (
<div className="kai-om">
<header $HasTextChildren>{this.props.header}</header>
<nav $HasKeyedChildren>
{childrenToRender}
</nav>
</div>
);
}
};
2 changes: 1 addition & 1 deletion KaiUIngInferno/src/ui/SoftKey.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = <img src={props.icon} width={20} height={20} alt="" />;
} else {
Expand Down
48 changes: 34 additions & 14 deletions KaiUIngInferno/src/ui/TextInput.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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;
Expand All @@ -18,26 +18,49 @@ interface TextInputProps {
focusClass?: string;
}

class TextInput extends Component<TextInputProps> {
interface State {
value: string;
}

class TextInput extends Component<Props, State> {
private onChange: (_evt?: Event) => void;
private textInput: any;
private textInputRef: RefObject<HTMLInputElement>;
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 = {
value: defaultValue || "",
};
}



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() {
Expand All @@ -51,9 +74,8 @@ class TextInput extends Component<TextInputProps> {
id={this.props.id}
tabIndex={0}
className={itemCls}
style={`background-color: ${
this.props.isFocused ? morecolor.focusColor : ""
}`}
style={`background-color: ${this.props.isFocused ? morecolor.focusColor : ""
}`}
>
<label className={labelCls} $HasTextChildren>
{this.props.label}
Expand All @@ -70,9 +92,7 @@ class TextInput extends Component<TextInputProps> {
defaultValue={this.state.value || ""}
placeholder={this.props.placeholder || ""}
style={`color: ${this.props.isFocused ? "var(--text-color)" : ""}`}
ref={(input) => {
this.textInput = input;
}}
ref={this.textInputRef}
/>
</div>
);
Expand Down
4 changes: 1 addition & 3 deletions KaiUIngInferno/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { VNode } from "inferno";

function asArray(children: VNode | VNode[]) : VNode[] {
function asArray(children: any | any[]) : any[] {
farooqkz marked this conversation as resolved.
Show resolved Hide resolved
if (children instanceof Array) {
return children;
} else {
Expand Down
Loading