Move UI components to ui/ directory

This commit is contained in:
hensm
2019-04-09 01:11:24 +01:00
parent a6ebb4fd22
commit 56ec766d86
22 changed files with 19 additions and 15 deletions

11
ext/src/ui/popup/index.html Executable file
View File

@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="styles/index.css">
<script src="bundle.js" defer></script>
</head>
<body>
<div id="root"></div>
</body>
</html>

240
ext/src/ui/popup/index.tsx Executable file
View File

@@ -0,0 +1,240 @@
/* tslint:disable:max-line-length */
"use strict";
import React, { Component } from "react";
import ReactDOM from "react-dom";
import { getNextEllipsis } from "../../lib/utils";
import * as types from "../../types";
const _ = browser.i18n.getMessage;
// macOS styles
browser.runtime.getPlatformInfo()
.then(platformInfo => {
if (platformInfo.os === "mac") {
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = "styles/mac.css";
document.head.appendChild(link);
}
});
const winWidth = 350;
let winHeight = 200;
let frameHeight: number;
let frameWidth: number;
interface PopupAppState {
receivers: types.Receiver[];
selectedMedia: string;
isLoading: boolean;
}
class PopupApp extends Component<{}, PopupAppState> {
private port: browser.runtime.Port;
private win: browser.windows.Window;
constructor (props: {}) {
super(props);
this.state = {
receivers: []
, selectedMedia: "app"
, isLoading: false
};
// Store window ref
browser.windows.getCurrent().then(win => {
this.win = win;
frameHeight = win.height - window.innerHeight;
frameWidth = win.width - window.innerWidth;
});
this.onSelectChange = this.onSelectChange.bind(this);
this.onCast = this.onCast.bind(this);
}
public componentDidMount () {
const backgroundPort = browser.runtime.connect({
name: "popup"
});
backgroundPort.onMessage.addListener((message: types.Message) => {
if (message.subject === "popup:/assignShim") {
this.setPort(message.data.tabId
, message.data.frameId);
}
});
}
public render () {
const shareMedia =
this.state.selectedMedia === "tab"
|| this.state.selectedMedia === "screen";
return (
<div>
<div className="media-select">
Cast
<select value={ this.state.selectedMedia }
onChange={ this.onSelectChange }
className="media-select-dropdown">
<option value="app" disabled={ shareMedia }>this site's app</option>
<option value="tab" disabled={ !shareMedia }>Tab</option>
<option value="screen" disabled={ !shareMedia }>Screen</option>
</select>
to:
</div>
<ul className="receivers">
{ this.state.receivers.map((receiver, i) => {
return (
<Receiver receiver={ receiver }
onCast={ this.onCast }
isLoading={ this.state.isLoading }
key={ i }/>
);
})}
</ul>
</div>
);
}
private async setPort (shimTabId: number, shimFrameId: number) {
if (this.port) {
this.port.disconnect();
}
this.port = browser.tabs.connect(shimTabId, {
name: "popup"
, frameId: shimFrameId
});
this.port.postMessage({
subject: "shim:/popupReady"
});
this.port.onMessage.addListener((message: types.Message) => {
switch (message.subject) {
case "popup:/populateReceiverList": {
this.setState({
receivers: message.data.receivers
, selectedMedia: message.data.selectedMedia
}, () => {
// Get height of content without window decoration
winHeight = document.body.clientHeight + frameHeight;
// Adjust height to fit content
browser.windows.update(this.win.id, {
height: winHeight
});
});
break;
}
case "popup:/close": {
window.close();
break;
}
}
});
}
private onCast (receiver: types.Receiver) {
this.setState({
isLoading: true
});
this.port.postMessage({
subject: "shim:/selectReceiver"
, data: {
receiver
, selectedMedia: this.state.selectedMedia
, a: 5
}
});
}
private onSelectChange (ev: React.ChangeEvent<HTMLSelectElement>) {
this.setState({
selectedMedia: ev.target.value
});
}
}
interface ReceiverProps {
receiver: types.Receiver;
isLoading: boolean;
onCast (receiver: types.Receiver): void;
}
interface ReceiverState {
ellipsis: string;
isLoading: boolean;
}
class Receiver extends Component<ReceiverProps, ReceiverState> {
constructor (props: ReceiverProps) {
super(props);
this.state = {
isLoading: false
, ellipsis: ""
};
this.handleCast = this.handleCast.bind(this);
}
public render () {
return (
<li className="receiver">
<div className="receiver-name">
{ this.props.receiver.friendlyName }
</div>
<div className="receiver-address">
{ `${this.props.receiver.address}:${this.props.receiver.port}` }
</div>
<div className="receiver-status">
{ this.props.receiver.currentApp &&
`- ${this.props.receiver.currentApp}` }
</div>
<button className="receiver-connect"
onClick={ this.handleCast }
disabled={this.props.isLoading}>
{ this.state.isLoading
? _("popupCastingButtonLabel") +
(this.state.isLoading
? this.state.ellipsis
: "")
: _("popupCastButtonLabel") }
</button>
</li>
);
}
private handleCast () {
this.props.onCast(this.props.receiver);
this.setState({
isLoading: true
});
setInterval(() => {
this.setState(state => ({
ellipsis: getNextEllipsis(state.ellipsis)
}));
}, 500);
}
}
ReactDOM.render(
<PopupApp />
, document.querySelector("#root"));

View File

@@ -0,0 +1,68 @@
body {
background: -moz-dialog;
color: -moz-dialogtext;
margin: initial;
font: message-box;
}
.media-select {
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
margin: 0 1em;
padding: 0.75em 0;
}
.media-select-dropdown {
display: inline-block;
margin: 0 0.5em;
}
.receivers {
list-style: none;
margin: initial;
padding: initial;
}
.receiver {
column-gap: 0.75em;
display: grid;
flex-direction: column;
grid-template-columns: min-content 1fr min-content;
grid-template-rows: min-content min-content 1fr;
grid-template-areas:
"name name connect"
"address status connect";
justify-content: center;
margin: 0 1em;
padding: 0.75em 0;
position: relative;
}
.receiver:not(:last-child) {
border-bottom: 1px solid rgba(0, 0, 0, 0.25);
}
.receiver-name,
.receiver-address {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.receiver-name {
font-size: 1.1em;
grid-area: name;
}
.receiver-address {
color: GrayText;
grid-area: address;
}
.receiver-status {
grid-area: status;
}
.receiver-connect {
align-self: center;
grid-area: connect;
justify-self: end;
min-width: 100px;
height: 32px;
}

22
ext/src/ui/popup/styles/mac.css Executable file
View File

@@ -0,0 +1,22 @@
body {
background: rgb(236, 236, 236);
font: menu;
}
button,
select {
font: inherit;
}
button:not([disabled]):hover:active {
color: -moz-mac-buttonactivetext;
}
.receiver-address,
.receiver-status {
font: message-box;
}
.receiver-connect {
height: 22px;
}