Skip to content

Commit

Permalink
Updates the list-tree toggle in commit details
Browse files Browse the repository at this point in the history
  • Loading branch information
d13 committed Oct 11, 2022
1 parent cc0f423 commit 322f915
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 17 deletions.
13 changes: 3 additions & 10 deletions src/webviews/apps/commitDetails/commitDetails.html
Expand Up @@ -133,17 +133,10 @@ <h2>Best practice</h2>
<webview-pane class="commit-details__files" collapsable expanded>
<span slot="title">Files changed </span>
<span slot="subtitle" data-region="stats"></span>
<action-nav slot="actions">
<action-item data-switch-value="list-tree" label="Show tree" icon="list-tree"></action-item>
</action-nav>

<div style="text-align: center">
<div class="switch">
<button type="button" class="switch__option" data-switch-value="list">
<code-icon icon="list-flat"></code-icon> Path
</button>
<button type="button" class="switch__option" data-switch-value="list-tree">
<code-icon icon="list-tree"></code-icon> Tree
</button>
</div>
</div>
<ul class="change-list" data-region="files">
<li class="change-list__item commit-details__item-skeleton">
<skeleton-loader></skeleton-loader>
Expand Down
19 changes: 12 additions & 7 deletions src/webviews/apps/commitDetails/commitDetails.ts
Expand Up @@ -36,6 +36,8 @@ import '../shared/components/progress';
import '../shared/components/list/list-container';
import '../shared/components/list/list-item';
import '../shared/components/list/file-change-list-item';
import '../shared/components/actions/action-item';
import '../shared/components/actions/action-nav';

const uncommittedSha = '0000000000000000000000000000000000000000';

Expand Down Expand Up @@ -126,13 +128,13 @@ export class CommitDetailsApp extends App<Serialized<State>> {

private onTreeSetting(e: MouseEvent) {
const isTree = (e.target as HTMLElement)?.getAttribute('data-switch-value') === 'list-tree';
if (isTree === this.state.preferences?.filesAsTree) return;
if (!isTree === this.state.preferences?.filesAsTree) return;

this.state.preferences = { ...this.state.preferences, filesAsTree: isTree };
this.state.preferences = { ...this.state.preferences, filesAsTree: !isTree };

this.renderFiles(this.state as CommitState);

this.sendCommand(PreferencesCommandType, { filesAsTree: isTree });
this.sendCommand(PreferencesCommandType, { filesAsTree: !isTree });
}

private onExpandedChange(e: WebviewPaneExpandedChangeEventDetail) {
Expand Down Expand Up @@ -330,15 +332,18 @@ export class CommitDetailsApp extends App<Serialized<State>> {
const $el = document.querySelector<HTMLElement>('[data-region="files"]');
if ($el == null) return;

const isTree = state.preferences?.filesAsTree === true;
const $toggle = document.querySelector('[data-switch-value]');
if ($toggle) {
$toggle.setAttribute('data-switch-value', isTree ? 'list-tree' : 'list');
$toggle.setAttribute('icon', isTree ? 'list-tree' : 'list-flat');
}

if (!state.selected.files?.length) {
$el.innerHTML = '';
return;
}

const isTree = state.preferences?.filesAsTree === true;
document.querySelector('[data-switch-value="list"]')?.classList.toggle('is-selected', !isTree);
document.querySelector('[data-switch-value="list-tree"]')?.classList.toggle('is-selected', isTree);

const stashAttr = state.selected.isStash
? 'stash '
: state.selected.sha === uncommittedSha
Expand Down
48 changes: 48 additions & 0 deletions src/webviews/apps/shared/components/actions/action-item.ts
@@ -0,0 +1,48 @@
import { attr, css, customElement, FASTElement, html } from '@microsoft/fast-element';

const template = html<ActionItem>`<a
role="${x => (!x.href ? 'button' : null)}"
type="${x => (!x.href ? 'button' : null)}"
aria-label="${x => x.label}"
title="${x => x.label}"
><code-icon icon="${x => x.icon}"></code-icon
></a>`;

const styles = css`
:host {
box-sizing: border-box;
display: inline-flex;
justify-content: center;
align-items: center;
width: 2rem;
height: 2rem;
border-radius: 0.5rem;
color: inherit;
padding: 0.2rem;
vertical-align: text-bottom;
text-decoration: none;
cursor: pointer;
}
:host(:focus) {
outline: 1px solid var(--vscode-focusBorder);
outline-offset: -1px;
}
:host(:hover) {
background-color: var(--vscode-toolbar-hoverBackground);
}
:host(:active) {
background-color: var(--vscode-toolbar-activeBackground);
}
`;

@customElement({ name: 'action-item', template: template, styles: styles })
export class ActionItem extends FASTElement {
@attr
href?: string;

@attr
label: string = '';

@attr
icon: string = '';
}
73 changes: 73 additions & 0 deletions src/webviews/apps/shared/components/actions/action-nav.ts
@@ -0,0 +1,73 @@
import { css, customElement, FASTElement, html, observable, slotted } from '@microsoft/fast-element';
import '../codicon';

const template = html<ActionNav>`<template role="navigation"><slot ${slotted('actionNodes')}></slot></template>`;

const styles = css`
:host {
display: flex;
align-items: center;
user-select: none;
}
`;

@customElement({ name: 'action-nav', template: template, styles: styles })
export class ActionNav extends FASTElement {
@observable
actionNodes?: HTMLElement[];

actionNodesDisposer?: () => void;
actionNodesChanged(_oldValue?: HTMLElement[], newValue?: HTMLElement[]) {
this.actionNodesDisposer?.();

if (!newValue?.length) {
return;
}

const handleKeydown = this.handleKeydown.bind(this);
const nodeEvents = newValue
?.filter(node => node.nodeType === 1)
.map((node, i) => {
node.setAttribute('tabindex', i === 0 ? '0' : '-1');
node.addEventListener('keydown', handleKeydown, false);
return {
dispose: () => {
node?.removeEventListener('keydown', handleKeydown, false);
},
};
});

this.actionNodesDisposer = () => {
nodeEvents?.forEach(({ dispose }) => dispose());
};
}

override disconnectedCallback() {
this.actionNodesDisposer?.();
}

handleKeydown(e: KeyboardEvent) {
if (!e.target || this.actionNodes == null || this.actionNodes.length < 2) return;
const target = e.target as HTMLElement;

let $next: HTMLElement | null = null;
if (e.key === 'ArrowLeft') {
$next = target.previousElementSibling as HTMLElement;
if ($next == null) {
const filteredNodes = this.actionNodes.filter(node => node.nodeType === 1);
$next = filteredNodes[filteredNodes.length - 1] ?? null;
}
} else if (e.key === 'ArrowRight') {
$next = target.nextElementSibling as HTMLElement;
if ($next == null) {
$next = this.actionNodes.find(node => node.nodeType === 1) ?? null;
}
}
if ($next == null || $next === target) {
return;
}
target.setAttribute('tabindex', '-1');
$next.setAttribute('tabindex', '0');
$next.focus();
}
}
4 changes: 4 additions & 0 deletions src/webviews/apps/shared/components/list/list-container.ts
Expand Up @@ -55,6 +55,10 @@ export class ListContainer extends FASTElement {
};
}

override disconnectedCallback() {
this.itemNodesDisposer?.();
}

handleBeforeSelected(e: Event) {
if (!e.target) return;

Expand Down
7 changes: 7 additions & 0 deletions src/webviews/apps/shared/components/webview-pane.ts
@@ -1,6 +1,7 @@
import { css, html, LitElement } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import './codicon';
import './actions/action-nav';

export interface WebviewPaneExpandedChangeEventDetail {
expanded: boolean;
Expand Down Expand Up @@ -81,6 +82,11 @@ export class WebviewPane extends LitElement {
:host([collapsable][expanded='false']) .content {
display: none;
}
slot[name='actions']::slotted(*) {
flex: none;
margin-left: auto;
}
`;

@property({ type: Boolean, reflect: true })
Expand Down Expand Up @@ -116,6 +122,7 @@ export class WebviewPane extends LitElement {
return html`
<header class="header">
${this.renderTitle()}
<slot name="actions"></slot>
<progress-indicator active="${this.loading}"></progress-indicator>
</header>
<div id="content" role="region" class="content">
Expand Down

0 comments on commit 322f915

Please sign in to comment.