Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
45 changes: 45 additions & 0 deletions packages/code-studio/src/main/AppMainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
ContextActions,
GLOBAL_SHORTCUTS,
NAVIGATION_SHORTCUTS,
Popper,
type ContextAction,
Button,
Expand Down Expand Up @@ -235,6 +236,34 @@ export class AppMainContainer extends Component<
shortcut: IRIS_GRID_SHORTCUTS.TABLE.CLEAR_ALL_FILTERS,
isGlobal: true,
},
{
action: () => {
this.sendCycleStackForward();
},
shortcut: NAVIGATION_SHORTCUTS.CYCLE_TO_NEXT_STACK,
isGlobal: true,
},
{
action: () => {
this.sendCycleStackBackward();
},
shortcut: NAVIGATION_SHORTCUTS.CYCLE_TO_PREVIOUS_STACK,
isGlobal: true,
},
{
action: () => {
this.sendCycleTabForward();
},
shortcut: NAVIGATION_SHORTCUTS.CYCLE_TO_NEXT_TAB,
isGlobal: true,
},
{
action: () => {
this.sendCycleTabBackward();
},
shortcut: NAVIGATION_SHORTCUTS.CYCLE_TO_PREVIOUS_TAB,
isGlobal: true,
},
{
action: () => {
this.sendReopenLast();
Expand Down Expand Up @@ -411,6 +440,22 @@ export class AppMainContainer extends Component<
this.emitLayoutEvent(InputFilterEvent.CLEAR_ALL_FILTERS);
}

sendCycleStackForward(): void {
Comment thread
gzh2003 marked this conversation as resolved.
this.emitLayoutEvent(PanelEvent.CYCLE_TO_NEXT_STACK);
}

sendCycleStackBackward(): void {
this.emitLayoutEvent(PanelEvent.CYCLE_TO_PREVIOUS_STACK);
}

sendCycleTabForward(): void {
this.emitLayoutEvent(PanelEvent.CYCLE_TO_NEXT_TAB);
}

sendCycleTabBackward(): void {
this.emitLayoutEvent(PanelEvent.CYCLE_TO_PREVIOUS_TAB);
}

sendReopenLast(): void {
this.emitLayoutEvent(PanelEvent.REOPEN_LAST);
}
Expand Down
35 changes: 35 additions & 0 deletions packages/components/src/shortcuts/NavigationShortcuts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import ShortcutRegistry from './ShortcutRegistry';
import { MODIFIER, KEY } from './Shortcut';

const NAVIGATION_SHORTCUTS = {
CYCLE_TO_NEXT_STACK: ShortcutRegistry.createAndAdd({
id: 'NAVIGATION.CYCLE_TO_NEXT_STACK',
name: 'Cycle To Next Stack',
shortcut: [MODIFIER.CTRL, KEY.SINGLE_QUOTE],
macShortcut: [MODIFIER.CMD, KEY.SINGLE_QUOTE],
isEditable: true,
}),
CYCLE_TO_PREVIOUS_STACK: ShortcutRegistry.createAndAdd({
id: 'NAVIGATION.CYCLE_TO_PREVIOUS_STACK',
name: 'Cycle To Previous Stack',
shortcut: [MODIFIER.CTRL, KEY.SEMICOLON],
macShortcut: [MODIFIER.CMD, KEY.SEMICOLON],
isEditable: true,
}),
Comment thread
gzh2003 marked this conversation as resolved.
CYCLE_TO_NEXT_TAB: ShortcutRegistry.createAndAdd({
id: 'NAVIGATION.CYCLE_TO_NEXT_TAB',
name: 'Cycle To Next Tab',
shortcut: [MODIFIER.CTRL, MODIFIER.SHIFT, KEY.SINGLE_QUOTE],
macShortcut: [MODIFIER.CMD, MODIFIER.SHIFT, KEY.SINGLE_QUOTE],
isEditable: true,
}),
CYCLE_TO_PREVIOUS_TAB: ShortcutRegistry.createAndAdd({
id: 'NAVIGATION.CYCLE_TO_PREVIOUS_TAB',
name: 'Cycle To Previous TAB',
shortcut: [MODIFIER.CTRL, MODIFIER.SHIFT, KEY.SEMICOLON],
Comment thread
gzh2003 marked this conversation as resolved.
Outdated
macShortcut: [MODIFIER.CMD, MODIFIER.SHIFT, KEY.SEMICOLON],
isEditable: true,
}),
};

export default NAVIGATION_SHORTCUTS;
1 change: 1 addition & 0 deletions packages/components/src/shortcuts/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { default as GLOBAL_SHORTCUTS } from './GlobalShortcuts';
export { default as NAVIGATION_SHORTCUTS } from './NavigationShortcuts';
export { default as Shortcut } from './Shortcut';
export * from './Shortcut';
export { default as ShortcutRegistry } from './ShortcutRegistry';
12 changes: 12 additions & 0 deletions packages/dashboard/src/PanelEvent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ export const PanelEvent = Object.freeze({

// Panel is dropped
DROPPED: 'PanelEvent.DROPPED',

// Event to change focus to next stack
CYCLE_TO_NEXT_STACK: 'PanelEvent.CYCLE_TO_NEXT_STACK',

// Event to change focus to previous stack
CYCLE_TO_PREVIOUS_STACK: 'PanelEvent.CYCLE_TO_PREVIOUS_STACK',

// Event to change focus to next tab
CYCLE_TO_NEXT_TAB: 'PanelEvent.CYCLE_TO_NEXT_TAB',

// Event to change focus to previous tab
CYCLE_TO_PREVIOUS_TAB: 'PanelEvent.CYCLE_TO_PREVIOUS_TAB',
Comment thread
gzh2003 marked this conversation as resolved.
Outdated
});

export const {
Expand Down
103 changes: 103 additions & 0 deletions packages/dashboard/src/PanelManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import {

const log = Log.module('PanelManager');

enum CycleDirection {
Next,
Previous,
}

export type PanelHydraterFunction = (
name: string,
props: PanelProps
Expand Down Expand Up @@ -88,6 +93,11 @@ class PanelManager {
this.handleUnmount = this.handleUnmount.bind(this);
this.handleReopen = this.handleReopen.bind(this);
this.handleReopenLast = this.handleReopenLast.bind(this);
this.handleCycleToNextStack = this.handleCycleToNextStack.bind(this);
this.handleCycleToPreviousStack =
this.handleCycleToPreviousStack.bind(this);
this.handleCycleToNextTab = this.handleCycleToNextTab.bind(this);
this.handleCycleToPreviousTab = this.handleCycleToPreviousTab.bind(this);
this.handleDeleted = this.handleDeleted.bind(this);
this.handleClosed = this.handleClosed.bind(this);
this.handleControlClose = this.handleControlClose.bind(this);
Expand All @@ -112,6 +122,16 @@ class PanelManager {
eventHub.on(PanelEvent.MOUNT, this.handleMount);
eventHub.on(PanelEvent.UNMOUNT, this.handleUnmount);
eventHub.on(PanelEvent.REOPEN, this.handleReopen);
eventHub.on(PanelEvent.CYCLE_TO_NEXT_STACK, this.handleCycleToNextStack);
eventHub.on(
PanelEvent.CYCLE_TO_PREVIOUS_STACK,
this.handleCycleToPreviousStack
);
eventHub.on(PanelEvent.CYCLE_TO_NEXT_TAB, this.handleCycleToNextTab);
eventHub.on(
PanelEvent.CYCLE_TO_PREVIOUS_TAB,
this.handleCycleToPreviousTab
);
eventHub.on(PanelEvent.REOPEN_LAST, this.handleReopenLast);
eventHub.on(PanelEvent.DELETE, this.handleDeleted);
eventHub.on(PanelEvent.CLOSED, this.handleClosed);
Expand All @@ -125,6 +145,16 @@ class PanelManager {
eventHub.off(PanelEvent.MOUNT, this.handleMount);
eventHub.off(PanelEvent.UNMOUNT, this.handleUnmount);
eventHub.off(PanelEvent.REOPEN, this.handleReopen);
eventHub.off(PanelEvent.CYCLE_TO_NEXT_STACK, this.handleCycleToNextStack);
eventHub.off(
PanelEvent.CYCLE_TO_PREVIOUS_STACK,
this.handleCycleToPreviousStack
);
eventHub.off(PanelEvent.CYCLE_TO_NEXT_TAB, this.handleCycleToNextTab);
eventHub.off(
PanelEvent.CYCLE_TO_PREVIOUS_TAB,
this.handleCycleToPreviousTab
);
eventHub.off(PanelEvent.REOPEN_LAST, this.handleReopenLast);
eventHub.off(PanelEvent.DELETE, this.handleDeleted);
eventHub.off(PanelEvent.CLOSED, this.handleClosed);
Expand Down Expand Up @@ -271,6 +301,79 @@ class PanelManager {
this.sendUpdate();
}

handleCycleStack(direction: CycleDirection): void {
Comment thread
gzh2003 marked this conversation as resolved.
Outdated
const allStacks = LayoutUtils.getAllStackContainers(this.layout);
if (allStacks.length <= 1) {
return;
}

const focusedIndex = LayoutUtils.getFocusedStackIndex(this.layout);

// If no stack is focused, activate the first stack's content item
if (focusedIndex === -1) {
const targetStack = allStacks[0];
const activeContentIndex = targetStack.config.activeItemIndex;
const activeContentItem =
activeContentIndex != null
? targetStack.contentItems[activeContentIndex]
: targetStack.contentItems[0];

targetStack.setActiveContentItem(activeContentItem, true);
return;
}

const targetIndex =
direction === CycleDirection.Next
? (focusedIndex + 1) % allStacks.length
: (focusedIndex - 1 + allStacks.length) % allStacks.length;
const targetStack = allStacks[targetIndex];

const activeContentIndex = targetStack.config.activeItemIndex;
const activeContentItem =
activeContentIndex != null
? targetStack.contentItems[activeContentIndex]
: targetStack.contentItems[0];

targetStack.setActiveContentItem(activeContentItem, true);
}

handleCycleToNextStack(): void {
this.handleCycleStack(CycleDirection.Next);
}

handleCycleToPreviousStack(): void {
this.handleCycleStack(CycleDirection.Previous);
}

handleCycleTab(direction: CycleDirection): void {
const focusedStack = LayoutUtils.getFocusedStack(this.layout);
if (focusedStack === undefined) {
return;
}
const { contentItems } = focusedStack;

if (contentItems.length <= 1) {
return;
}

const activeItemIndex = focusedStack.config.activeItemIndex ?? 0;
const targetIndex =
direction === CycleDirection.Next
? (activeItemIndex + 1) % contentItems.length
: (activeItemIndex - 1 + contentItems.length) % contentItems.length;

const targetContentItem = contentItems[targetIndex];
focusedStack.setActiveContentItem(targetContentItem, true);
}

handleCycleToNextTab(): void {
this.handleCycleTab(CycleDirection.Next);
}

handleCycleToPreviousTab(): void {
this.handleCycleTab(CycleDirection.Previous);
}

/**
*
* @param panelConfig The config to hydrate and load
Expand Down
50 changes: 50 additions & 0 deletions packages/dashboard/src/layout/LayoutUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,56 @@ class LayoutUtils {
return this.addStack(newParent, !columnPreferred);
}

/**
* Gets all stack containers in the layout
* @param layout GoldenLayout instance
* @returns The found stack containers
*/
static getAllStackContainers(layout: GoldenLayout): Stack[] {
// eslint-disable-next-line no-underscore-dangle
return layout._findAllStackContainers();
}

/**
* Get the index of the stack that is currently focused
* @param layout GoldenLayout instance
* @returns The focused stack's index or -1 if not found
*/
static getFocusedStackIndex(layout: GoldenLayout): number {
const allStacks = LayoutUtils.getAllStackContainers(layout);

// NOTE: We target the 'lm_focusin' class because GoldenLayout automatically applies this class
// to tab elements when they receive focus. Until we enhance focus tracking in GoldenLayout, we
// will have to rely on this internal CSS class.
const foundIndex = allStacks.findIndex(stack =>
stack.header.tabs.some(tab =>
tab.element[0].classList.contains('lm_focusin')
)
);

return foundIndex;
}

/**
* Get the stack that is currently focused
* @param layout GoldenLayout instance
* @returns The focused stack or undefined if none found
*/
static getFocusedStack(layout: GoldenLayout): Stack | undefined {
const allStacks = LayoutUtils.getAllStackContainers(layout);

// NOTE: We target the 'lm_focusin' class because GoldenLayout automatically applies this class
// to tab elements when they receive focus. Until we enhance focus tracking in GoldenLayout, we
// will have to rely on this internal CSS class.
const focusedStack = allStacks.find(stack =>
stack.header.tabs.some(tab =>
tab.element[0].classList.contains('lm_focusin')
)
);

return focusedStack;
}

Comment thread
gzh2003 marked this conversation as resolved.
/**
* Gets a stack by its ID
* @param item Golden layout content item to search for the stack
Expand Down
4 changes: 2 additions & 2 deletions packages/golden-layout/src/items/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ export default class Component extends AbstractContentItem {
AbstractContentItem.prototype._$hide.call(this);
}

_$show() {
_$show(forceFocus?: boolean) {
this.container.show();
if (this.container._config.isFocusOnShow) {
if (this.container._config.isFocusOnShow || forceFocus) {
// focus the shown container element on show
// preventScroll isn't supported in safari, but also doesn't matter for illumon when 100% window
this.container._contentElement[0].focus({ preventScroll: true });
Expand Down
8 changes: 6 additions & 2 deletions packages/golden-layout/src/items/Stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export default class Stack extends AbstractContentItem {
}
}

setActiveContentItem(contentItem: AbstractContentItem) {
setActiveContentItem(contentItem: AbstractContentItem, forceFocus?: boolean) {
if (this.contentItems.indexOf(contentItem) === -1) {
throw new Error('contentItem is not a child of this stack');
}
Expand All @@ -156,7 +156,11 @@ export default class Stack extends AbstractContentItem {

this._activeContentItem = contentItem;
this.header.setActiveContentItem(contentItem);
contentItem._$show();
if (isComponent(contentItem)) {
contentItem._$show(forceFocus);
} else {
contentItem._$show();
}
this.emit('activeContentItemChanged', contentItem);
this.layoutManager.emit('activeContentItemChanged', contentItem);
this.emitBubblingEvent('stateChanged');
Expand Down
Loading