mirror of
https://github.com/wavetermdev/waveterm.git
synced 2024-12-22 16:48:23 +01:00
Add main screens to Cmd-P search (#288)
* declare types as global * remove mobx types aliases * put back model_old.ts * remove types.ts * add main screens to Cmd-P search * make viewData optional * minor hint fix * address improvements suggested by evan * more on let to const
This commit is contained in:
parent
85f22a0733
commit
532e65c8ab
@ -5,7 +5,7 @@ import * as React from "react";
|
|||||||
import * as mobxReact from "mobx-react";
|
import * as mobxReact from "mobx-react";
|
||||||
import * as mobx from "mobx";
|
import * as mobx from "mobx";
|
||||||
import { boundMethod } from "autobind-decorator";
|
import { boundMethod } from "autobind-decorator";
|
||||||
import { For } from "tsx-control-statements/components";
|
import { If, For } from "tsx-control-statements/components";
|
||||||
import cn from "classnames";
|
import cn from "classnames";
|
||||||
import { GlobalModel, GlobalCommandRunner } from "../../../models";
|
import { GlobalModel, GlobalCommandRunner } from "../../../models";
|
||||||
import { Modal, TextField, InputDecoration, Tooltip } from "../elements";
|
import { Modal, TextField, InputDecoration, Tooltip } from "../elements";
|
||||||
@ -15,6 +15,11 @@ import { ReactComponent as SquareIcon } from "../../assets/icons/tab/square.svg"
|
|||||||
|
|
||||||
import "./tabswitcher.less";
|
import "./tabswitcher.less";
|
||||||
|
|
||||||
|
type ViewDataType = {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
type SwitcherDataType = {
|
type SwitcherDataType = {
|
||||||
sessionId: string;
|
sessionId: string;
|
||||||
sessionName: string;
|
sessionName: string;
|
||||||
@ -24,9 +29,25 @@ type SwitcherDataType = {
|
|||||||
screenName: string;
|
screenName: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
color: string;
|
color: string;
|
||||||
|
viewData?: ViewDataType;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MaxOptionsToDisplay = 100;
|
const MaxOptionsToDisplay = 100;
|
||||||
|
const additionalOptions = [
|
||||||
|
{ label: "Connections", value: "connections" },
|
||||||
|
{ label: "History", value: "history" },
|
||||||
|
{ label: "Settings", value: "clientsettings" },
|
||||||
|
].map((item, index) => ({
|
||||||
|
sessionId: `additional-${index}`,
|
||||||
|
sessionName: "",
|
||||||
|
sessionIdx: -1,
|
||||||
|
screenId: `additional-${index}`,
|
||||||
|
screenIdx: -1,
|
||||||
|
screenName: "",
|
||||||
|
icon: "",
|
||||||
|
color: "",
|
||||||
|
viewData: item,
|
||||||
|
}));
|
||||||
|
|
||||||
@mobxReact.observer
|
@mobxReact.observer
|
||||||
class TabSwitcherModal extends React.Component<{}, {}> {
|
class TabSwitcherModal extends React.Component<{}, {}> {
|
||||||
@ -44,8 +65,8 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.activeSessionIdx = GlobalModel.getActiveSession().sessionIdx.get();
|
this.activeSessionIdx = GlobalModel.getActiveSession().sessionIdx.get();
|
||||||
let oSessions = GlobalModel.sessionList;
|
const oSessions = GlobalModel.sessionList;
|
||||||
let oScreens = GlobalModel.screenMap;
|
const oScreens = GlobalModel.screenMap;
|
||||||
oScreens.forEach((oScreen) => {
|
oScreens.forEach((oScreen) => {
|
||||||
if (oScreen == null) {
|
if (oScreen == null) {
|
||||||
return;
|
return;
|
||||||
@ -54,13 +75,13 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Find the matching session in the observable array
|
// Find the matching session in the observable array
|
||||||
let foundSession = oSessions.find((s) => {
|
const foundSession = oSessions.find((s) => {
|
||||||
return s.sessionId == oScreen.sessionId && !s.archived.get();
|
return s.sessionId == oScreen.sessionId && !s.archived.get();
|
||||||
});
|
});
|
||||||
if (!foundSession) {
|
if (!foundSession) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let data: SwitcherDataType = {
|
const data: SwitcherDataType = {
|
||||||
sessionName: foundSession.name.get(),
|
sessionName: foundSession.name.get(),
|
||||||
sessionId: foundSession.sessionId,
|
sessionId: foundSession.sessionId,
|
||||||
sessionIdx: foundSession.sessionIdx.get(),
|
sessionIdx: foundSession.sessionIdx.get(),
|
||||||
@ -85,11 +106,11 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
let currFocusedIdx = this.focusedIdx.get();
|
const currFocusedIdx = this.focusedIdx.get();
|
||||||
|
|
||||||
// Check if selectedIdx has changed
|
// Check if selectedIdx has changed
|
||||||
if (currFocusedIdx !== this.prevFocusedIdx) {
|
if (currFocusedIdx !== this.prevFocusedIdx) {
|
||||||
let optionElement = this.optionRefs[currFocusedIdx]?.current;
|
const optionElement = this.optionRefs[currFocusedIdx]?.current;
|
||||||
|
|
||||||
if (optionElement) {
|
if (optionElement) {
|
||||||
optionElement.scrollIntoView({ block: "nearest" });
|
optionElement.scrollIntoView({ block: "nearest" });
|
||||||
@ -106,7 +127,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
getTabIcon(screen: Screen): string {
|
getTabIcon(screen: Screen): string {
|
||||||
let tabIcon = "default";
|
let tabIcon = "default";
|
||||||
let screenOpts = screen.opts.get();
|
const screenOpts = screen.opts.get();
|
||||||
if (screenOpts != null && !util.isBlank(screenOpts.tabicon)) {
|
if (screenOpts != null && !util.isBlank(screenOpts.tabicon)) {
|
||||||
tabIcon = screenOpts.tabicon;
|
tabIcon = screenOpts.tabicon;
|
||||||
}
|
}
|
||||||
@ -116,7 +137,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
getTabColor(screen: Screen): string {
|
getTabColor(screen: Screen): string {
|
||||||
let tabColor = "default";
|
let tabColor = "default";
|
||||||
let screenOpts = screen.opts.get();
|
const screenOpts = screen.opts.get();
|
||||||
if (screenOpts != null && !util.isBlank(screenOpts.tabcolor)) {
|
if (screenOpts != null && !util.isBlank(screenOpts.tabcolor)) {
|
||||||
tabColor = screenOpts.tabcolor;
|
tabColor = screenOpts.tabcolor;
|
||||||
}
|
}
|
||||||
@ -129,7 +150,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
this.closeModal();
|
this.closeModal();
|
||||||
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
} else if (e.key === "ArrowUp" || e.key === "ArrowDown") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
let newIndex = this.calculateNewIndex(e.key === "ArrowUp");
|
const newIndex = this.calculateNewIndex(e.key === "ArrowUp");
|
||||||
this.setFocusedIndex(newIndex);
|
this.setFocusedIndex(newIndex);
|
||||||
} else if (e.key === "Enter") {
|
} else if (e.key === "Enter") {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -139,7 +160,7 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
calculateNewIndex(isUpKey) {
|
calculateNewIndex(isUpKey) {
|
||||||
let currentIndex = this.focusedIdx.get();
|
const currentIndex = this.focusedIdx.get();
|
||||||
if (isUpKey) {
|
if (isUpKey) {
|
||||||
return Math.max(currentIndex - 1, 0);
|
return Math.max(currentIndex - 1, 0);
|
||||||
} else {
|
} else {
|
||||||
@ -162,6 +183,11 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
@boundMethod
|
@boundMethod
|
||||||
handleSelect(index: number): void {
|
handleSelect(index: number): void {
|
||||||
const selectedOption = this.sOptions[index];
|
const selectedOption = this.sOptions[index];
|
||||||
|
if (selectedOption.sessionIdx === -1) {
|
||||||
|
GlobalCommandRunner.switchView(selectedOption.viewData.value);
|
||||||
|
this.closeModal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (selectedOption) {
|
if (selectedOption) {
|
||||||
GlobalCommandRunner.switchScreen(selectedOption.screenId, selectedOption.sessionId);
|
GlobalCommandRunner.switchScreen(selectedOption.screenId, selectedOption.sessionId);
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
@ -189,27 +215,28 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
@mobx.computed
|
@mobx.computed
|
||||||
@boundMethod
|
@boundMethod
|
||||||
filterOptions(searchInput: string): SwitcherDataType[] {
|
filterOptions(searchInput: string): SwitcherDataType[] {
|
||||||
let filteredScreens = [];
|
const searchLower = searchInput.toLowerCase();
|
||||||
|
|
||||||
for (let i = 0; i < this.options.length; i++) {
|
|
||||||
let tab = this.options[i];
|
|
||||||
let match = false;
|
|
||||||
|
|
||||||
|
let filteredScreens = this.options.filter((tab) => {
|
||||||
if (searchInput.includes("/")) {
|
if (searchInput.includes("/")) {
|
||||||
let [sessionFilter, screenFilter] = searchInput.split("/").map((s) => s.trim().toLowerCase());
|
const [sessionFilter, screenFilter] = searchInput.split("/").map((s) => s.trim().toLowerCase());
|
||||||
match =
|
return (
|
||||||
tab.sessionName.toLowerCase().includes(sessionFilter) &&
|
tab.sessionName.toLowerCase().includes(sessionFilter) &&
|
||||||
tab.screenName.toLowerCase().includes(screenFilter);
|
tab.screenName.toLowerCase().includes(screenFilter)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
match =
|
return (
|
||||||
tab.sessionName.toLowerCase().includes(searchInput) ||
|
tab.sessionName.toLowerCase().includes(searchLower) ||
|
||||||
tab.screenName.toLowerCase().includes(searchInput);
|
tab.screenName.toLowerCase().includes(searchLower)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Add tab to filtered list if it matches the criteria
|
if (searchLower.length > 0) {
|
||||||
if (match) {
|
const additionalFiltered = additionalOptions.filter((item) =>
|
||||||
filteredScreens.push(tab);
|
item.viewData?.label.toLowerCase().includes(searchLower)
|
||||||
}
|
);
|
||||||
|
filteredScreens = filteredScreens.concat(additionalFiltered);
|
||||||
}
|
}
|
||||||
|
|
||||||
return filteredScreens;
|
return filteredScreens;
|
||||||
@ -218,9 +245,10 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
@mobx.computed
|
@mobx.computed
|
||||||
@boundMethod
|
@boundMethod
|
||||||
sortOptions(options: SwitcherDataType[]): SwitcherDataType[] {
|
sortOptions(options: SwitcherDataType[]): SwitcherDataType[] {
|
||||||
return options.sort((a, b) => {
|
const mainOptions = options.filter((o) => o.sessionIdx !== -1);
|
||||||
let aInCurrentSession = a.sessionIdx === this.activeSessionIdx;
|
mainOptions.sort((a, b) => {
|
||||||
let bInCurrentSession = b.sessionIdx === this.activeSessionIdx;
|
const aInCurrentSession = a.sessionIdx === this.activeSessionIdx;
|
||||||
|
const bInCurrentSession = b.sessionIdx === this.activeSessionIdx;
|
||||||
|
|
||||||
// Tabs in the current session are sorted by screenIdx
|
// Tabs in the current session are sorted by screenIdx
|
||||||
if (aInCurrentSession && bInCurrentSession) {
|
if (aInCurrentSession && bInCurrentSession) {
|
||||||
@ -243,11 +271,16 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const additionalOptions = options.filter((o) => o.sessionIdx === -1);
|
||||||
|
additionalOptions.sort((a, b) => a.viewData?.label.localeCompare(b.viewData?.label));
|
||||||
|
|
||||||
|
return mainOptions.concat(additionalOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
renderIcon(option: SwitcherDataType): React.ReactNode {
|
renderIcon(option: SwitcherDataType): React.ReactNode {
|
||||||
let tabIcon = option.icon;
|
const tabIcon = option.icon;
|
||||||
if (tabIcon === "default" || tabIcon === "square") {
|
if (tabIcon === "default" || tabIcon === "square") {
|
||||||
return <SquareIcon className="left-icon" />;
|
return <SquareIcon className="left-icon" />;
|
||||||
}
|
}
|
||||||
@ -268,10 +301,15 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
})}
|
})}
|
||||||
onClick={() => this.handleSelect(index)}
|
onClick={() => this.handleSelect(index)}
|
||||||
>
|
>
|
||||||
|
<If condition={option.sessionIdx != -1}>
|
||||||
<div className={cn("icon", "color-" + option.color)}>{this.renderIcon(option)}</div>
|
<div className={cn("icon", "color-" + option.color)}>{this.renderIcon(option)}</div>
|
||||||
<div className="tabname">
|
<div className="tabname">
|
||||||
#{option.sessionName} / {option.screenName}
|
#{option.sessionName} / {option.screenName}
|
||||||
</div>
|
</div>
|
||||||
|
</If>
|
||||||
|
<If condition={option.sessionIdx == -1}>
|
||||||
|
<div className="tabname">{option.viewData?.label}</div>
|
||||||
|
</If>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -290,13 +328,13 @@ class TabSwitcherModal extends React.Component<{}, {}> {
|
|||||||
decoration={{
|
decoration={{
|
||||||
startDecoration: (
|
startDecoration: (
|
||||||
<InputDecoration position="start">
|
<InputDecoration position="start">
|
||||||
<div className="tabswitcher-search-prefix">Switch to Tab:</div>
|
<div className="tabswitcher-search-prefix">Go to:</div>
|
||||||
</InputDecoration>
|
</InputDecoration>
|
||||||
),
|
),
|
||||||
endDecoration: (
|
endDecoration: (
|
||||||
<InputDecoration>
|
<InputDecoration>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
message={`Type to filter workspaces and tabs.`}
|
message={`Type to filter workspaces, tabs and views.`}
|
||||||
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
icon={<i className="fa-sharp fa-regular fa-circle-question" />}
|
||||||
>
|
>
|
||||||
<i className="fa-sharp fa-regular fa-circle-question" />
|
<i className="fa-sharp fa-regular fa-circle-question" />
|
||||||
|
@ -34,6 +34,12 @@ class CommandRunner {
|
|||||||
return prtn;
|
return prtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switchView(view: string) {
|
||||||
|
mobx.action(() => {
|
||||||
|
GlobalModel.activeMainView.set(view);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
switchSession(session: string) {
|
switchSession(session: string) {
|
||||||
mobx.action(() => {
|
mobx.action(() => {
|
||||||
GlobalModel.activeMainView.set("session");
|
GlobalModel.activeMainView.set("session");
|
||||||
|
Loading…
Reference in New Issue
Block a user