Browse Source

resolved conflict from rebase

telamonian 5 years ago
parent
commit
6d520730ce

+ 54 - 0
packages/statusbar/src/components/group.tsx

@@ -0,0 +1,54 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import * as React from 'react';
+
+import { style, classes } from 'typestyle/lib';
+
+import { centeredFlex, leftToRight } from '../style/layout';
+
+const groupItemLayout = style(centeredFlex, leftToRight);
+
+/**
+ * A tsx component for a set of items logically grouped together.
+ */
+export function GroupItem(
+  props: GroupItem.IProps & React.HTMLAttributes<HTMLDivElement>
+): React.ReactElement<GroupItem.IProps> {
+  const { spacing, children, className, ...rest } = props;
+  const numChildren = React.Children.count(children);
+
+  return (
+    <div className={classes(groupItemLayout, className)} {...rest}>
+      {React.Children.map(children, (child, i) => {
+        if (i === 0) {
+          return <div style={{ marginRight: `${spacing}px` }}>{child}</div>;
+        } else if (i === numChildren - 1) {
+          return <div style={{ marginLeft: `${spacing}px` }}>{child}</div>;
+        } else {
+          return <div style={{ margin: `0px ${spacing}px` }}>{child}</div>;
+        }
+      })}
+    </div>
+  );
+}
+
+/**
+ * A namespace for GroupItem statics.
+ */
+export namespace GroupItem {
+  /**
+   * Props for the GroupItem.
+   */
+  export interface IProps {
+    /**
+     * The spacing, in px, between the items in the goup.
+     */
+    spacing: number;
+
+    /**
+     * The items to arrange in a group.
+     */
+    children: JSX.Element[];
+  }
+}

+ 198 - 0
packages/statusbar/src/components/hover.tsx

@@ -0,0 +1,198 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { HoverBox } from '@jupyterlab/apputils';
+
+import { Message } from '@phosphor/messaging';
+
+import { Widget, PanelLayout } from '@phosphor/widgets';
+
+import { style } from 'typestyle/lib';
+
+import { clickedItem, interactiveItem } from '../style/statusbar';
+
+const hoverItem = style({
+  boxShadow: '0px 4px 4px rgba(0, 0, 0, 0.25)'
+});
+
+/**
+ * Create and show a popup component.
+ *
+ * @param options - options for the popup
+ *
+ * @returns the popup that was created.
+ */
+export function showPopup(options: Popup.IOptions): Popup {
+  let dialog = new Popup(options);
+  dialog.launch();
+  return dialog;
+}
+
+/**
+ * A class for a Popup widget.
+ */
+export class Popup extends Widget {
+  /**
+   * Construct a new Popup.
+   */
+  constructor(options: Popup.IOptions) {
+    super();
+    this._body = options.body;
+    this._body.addClass(hoverItem);
+    this._anchor = options.anchor;
+    this._align = options.align;
+    let layout = (this.layout = new PanelLayout());
+    layout.addWidget(options.body);
+    this._body.node.addEventListener('resize', () => {
+      this.update();
+    });
+  }
+
+  /**
+   * Attach the popup widget to the page.
+   */
+  launch() {
+    this._setGeometry();
+    Widget.attach(this, document.body);
+    this.update();
+    this._anchor.addClass(clickedItem);
+    this._anchor.removeClass(interactiveItem);
+  }
+
+  /**
+   * Handle `'update'` messages for the widget.
+   */
+  protected onUpdateRequest(msg: Message): void {
+    this._setGeometry();
+    super.onUpdateRequest(msg);
+  }
+
+  /**
+   * Handle `'after-attach'` messages for the widget.
+   */
+  protected onAfterAttach(msg: Message): void {
+    document.addEventListener('click', this, false);
+    this.node.addEventListener('keypress', this, false);
+    window.addEventListener('resize', this, false);
+  }
+
+  /**
+   * Handle `'after-detach'` messages for the widget.
+   */
+  protected onAfterDetach(msg: Message): void {
+    document.removeEventListener('click', this, false);
+    this.node.removeEventListener('keypress', this, false);
+    window.removeEventListener('resize', this, false);
+  }
+
+  /**
+   * Handle `'resize'` messages for the widget.
+   */
+  protected onResize(): void {
+    this.update();
+  }
+
+  /**
+   * Dispose of the widget.
+   */
+  dispose() {
+    super.dispose();
+    this._anchor.removeClass(clickedItem);
+    this._anchor.addClass(interactiveItem);
+  }
+
+  /**
+   * Handle DOM events for the widget.
+   */
+  handleEvent(event: Event): void {
+    switch (event.type) {
+      case 'keydown':
+        this._evtKeydown(event as KeyboardEvent);
+        break;
+      case 'click':
+        this._evtClick(event as MouseEvent);
+        break;
+      case 'resize':
+        this.onResize();
+        break;
+      default:
+        break;
+    }
+  }
+
+  private _evtClick(event: MouseEvent): void {
+    if (
+      !!event.target &&
+      !(
+        this._body.node.contains(event.target as HTMLElement) ||
+        this._anchor.node.contains(event.target as HTMLElement)
+      )
+    ) {
+      this.dispose();
+    }
+  }
+
+  private _evtKeydown(event: KeyboardEvent): void {
+    // Check for escape key
+    switch (event.keyCode) {
+      case 27: // Escape.
+        event.stopPropagation();
+        event.preventDefault();
+        this.dispose();
+        break;
+      default:
+        break;
+    }
+  }
+
+  private _setGeometry(): void {
+    let aligned = 0;
+    const anchorRect = this._anchor.node.getBoundingClientRect();
+    const bodyRect = this._body.node.getBoundingClientRect();
+    if (this._align === 'right') {
+      aligned = -(bodyRect.width - anchorRect.width);
+    }
+    const style = window.getComputedStyle(this._body.node);
+    HoverBox.setGeometry({
+      anchor: anchorRect,
+      host: document.body,
+      maxHeight: 500,
+      minHeight: 20,
+      node: this._body.node,
+      offset: {
+        horizontal: aligned
+      },
+      privilege: 'forceAbove',
+      style
+    });
+  }
+
+  private _body: Widget;
+  private _anchor: Widget;
+  private _align: 'left' | 'right' | undefined;
+}
+
+/**
+ * A namespace for Popup statics.
+ */
+export namespace Popup {
+  /**
+   * Options for creating a Popup widget.
+   */
+  export interface IOptions {
+    /**
+     * The content of the popup.
+     */
+    body: Widget;
+
+    /**
+     * The widget to which we are attaching the popup.
+     */
+    anchor: Widget;
+
+    /**
+     * Whether to align the popup to the left or the right of the anchor.
+     */
+    align?: 'left' | 'right';
+  }
+}

+ 7 - 0
packages/statusbar/src/components/index.ts

@@ -0,0 +1,7 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+export * from './progressBar';
+export * from './text';
+export * from './group';
+export * from './hover';

+ 61 - 0
packages/statusbar/src/components/progressBar.tsx

@@ -0,0 +1,61 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import * as React from 'react';
+
+import { progressBarItem, fillerItem } from '../style/progressBar';
+
+/**
+ * A namespace for ProgressBar statics.
+ */
+export namespace ProgressBar {
+  /**
+   * Props for the ProgressBar.
+   */
+  export interface IProps {
+    /**
+     * The current progress percentage, from 0 to 100
+     */
+    percentage: number;
+  }
+}
+
+/**
+ * A functional tsx component for a progress bar.
+ */
+export function ProgressBar(props: ProgressBar.IProps) {
+  return (
+    <div className={progressBarItem}>
+      <Filler percentage={props.percentage} />
+    </div>
+  );
+}
+
+/**
+ * A namespace for Filler statics.
+ */
+namespace Filler {
+  /**
+   * Props for the Filler component.
+   */
+  export interface IProps {
+    /**
+     * The current percentage filled, from 0 to 100
+     */
+    percentage: number;
+  }
+}
+
+/**
+ * A functional tsx component for a partially filled div.
+ */
+function Filler(props: Filler.IProps) {
+  return (
+    <div
+      className={fillerItem}
+      style={{
+        width: `${props.percentage}px`
+      }}
+    />
+  );
+}

+ 42 - 0
packages/statusbar/src/components/text.tsx

@@ -0,0 +1,42 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import * as React from 'react';
+
+import { classes } from 'typestyle/lib';
+
+import { textItem } from '../style/text';
+
+/**
+ * A namespace for TextItem statics.
+ */
+export namespace TextItem {
+  /**
+   * Props for a TextItem.
+   */
+  export interface IProps {
+    /**
+     * The content of the text item.
+     */
+    source: string | number;
+
+    /**
+     * Hover text to give to the node.
+     */
+    title?: string;
+  }
+}
+
+/**
+ * A functional tsx component for a text item.
+ */
+export function TextItem(
+  props: TextItem.IProps & React.HTMLAttributes<HTMLSpanElement>
+): React.ReactElement<TextItem.IProps> {
+  const { title, source, className, ...rest } = props;
+  return (
+    <span className={classes(textItem, className)} title={title} {...rest}>
+      {source}
+    </span>
+  );
+}

+ 1 - 2
packages/ui-components/tsconfig.json

@@ -5,6 +5,5 @@
     "types": ["webpack-env"],
     "rootDir": "src"
   },
-  "include": ["src/**/*"],
-  "references": []
+  "include": ["src/**/*"]
 }