aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShin'ya Ueoka <ueokande@i-beam.org>2020-09-21 14:20:38 +0900
committerGitHub <noreply@github.com>2020-09-21 14:20:38 +0900
commit7b8a96bbf7cb6d28c76c061a6b24a9fa821820b1 (patch)
tree4d00b60aa59e946f64ea6402ee7c78fc491f6203 /src
parent748ab17dc61a2bb3f1c1e3ea4b43d13ef23d8edf (diff)
parent9bba3ed79123affc8e557cf4211df51bd375742c (diff)
Merge pull request #837 from ueokande/use-styled-components
Use styled-components instead of vanilla CSS/SCSS in console
Diffstat (limited to 'src')
-rw-r--r--src/console/components/Console.tsx47
-rw-r--r--src/console/components/Theme.ts53
-rw-r--r--src/console/components/console.scss135
-rw-r--r--src/console/components/console/Completion.tsx49
-rw-r--r--src/console/components/console/CompletionItem.tsx75
-rw-r--r--src/console/components/console/CompletionTitle.tsx17
-rw-r--r--src/console/components/console/Input.tsx27
-rw-r--r--src/console/components/console/Message.tsx31
8 files changed, 237 insertions, 197 deletions
diff --git a/src/console/components/Console.tsx b/src/console/components/Console.tsx
index a0e22e4..a23c459 100644
--- a/src/console/components/Console.tsx
+++ b/src/console/components/Console.tsx
@@ -11,6 +11,13 @@ import CommandLineParser, {
} from "../commandline/CommandLineParser";
import { Command } from "../../shared/Command";
import ColorScheme from "../../shared/ColorScheme";
+import { LightTheme, DarkTheme } from "./Theme";
+import styled from "./Theme";
+import { ThemeProvider } from "styled-components";
+
+const ConsoleWrapper = styled.div`
+ border-top: 1px solid gray;
+`;
const COMPLETION_MAX_ITEMS = 33;
@@ -143,28 +150,34 @@ class Console extends React.Component<Props> {
case "command":
case "find":
return (
- <div data-theme={theme} className="vimvixen-console-command-wrapper">
- <Completion
- size={COMPLETION_MAX_ITEMS}
- completions={this.props.completions}
- select={this.props.select}
- />
- <Input
- ref={this.input}
- mode={this.props.mode}
- onBlur={this.onBlur.bind(this)}
- onKeyDown={this.onKeyDown.bind(this)}
- onChange={this.onChange.bind(this)}
- value={this.props.consoleText}
- />
- </div>
+ <ThemeProvider
+ theme={theme === ColorScheme.Dark ? DarkTheme : LightTheme}
+ >
+ <ConsoleWrapper>
+ <Completion
+ size={COMPLETION_MAX_ITEMS}
+ completions={this.props.completions}
+ select={this.props.select}
+ />
+ <Input
+ ref={this.input}
+ mode={this.props.mode}
+ onBlur={this.onBlur.bind(this)}
+ onKeyDown={this.onKeyDown.bind(this)}
+ onChange={this.onChange.bind(this)}
+ value={this.props.consoleText}
+ />
+ </ConsoleWrapper>
+ </ThemeProvider>
);
case "info":
case "error":
return (
- <div data-theme={theme}>
+ <ThemeProvider
+ theme={theme === ColorScheme.Dark ? DarkTheme : LightTheme}
+ >
<Message mode={this.props.mode}>{this.props.messageText}</Message>
- </div>
+ </ThemeProvider>
);
default:
return null;
diff --git a/src/console/components/Theme.ts b/src/console/components/Theme.ts
new file mode 100644
index 0000000..dd7baa5
--- /dev/null
+++ b/src/console/components/Theme.ts
@@ -0,0 +1,53 @@
+import baseStyled, { ThemedStyledInterface } from "styled-components";
+
+type Theme = {
+ completionTitleBackground: string;
+ completionTitleForeground: string;
+ completionItemBackground: string;
+ completionItemForeground: string;
+ completionItemDescriptionForeground: string;
+ completionSelectedBackground: string;
+ completionSelectedForeground: string;
+ commandBackground: string;
+ commandForeground: string;
+ consoleErrorBackground: string;
+ consoleErrorForeground: string;
+ consoleInfoBackground: string;
+ consoleInfoForeground: string;
+};
+
+export const LightTheme: Theme = {
+ completionTitleBackground: "lightgray",
+ completionTitleForeground: "#000000",
+ completionItemBackground: "#ffffff",
+ completionItemForeground: "#000000",
+ completionItemDescriptionForeground: "#008000",
+ completionSelectedBackground: "#ffff00",
+ completionSelectedForeground: "#000000",
+ commandBackground: "#ffffff",
+ commandForeground: "#000000",
+ consoleErrorBackground: "#ff0000",
+ consoleErrorForeground: "#ffffff",
+ consoleInfoBackground: "#ffffff",
+ consoleInfoForeground: "#018786",
+};
+
+export const DarkTheme: Theme = {
+ completionTitleBackground: "#052027",
+ completionTitleForeground: "white",
+ completionItemBackground: "#2f474f",
+ completionItemForeground: "white",
+ completionItemDescriptionForeground: "#86fab0",
+ completionSelectedBackground: "#eeff41",
+ completionSelectedForeground: "#000000",
+ commandBackground: "#052027",
+ commandForeground: "white",
+ consoleErrorBackground: "red",
+ consoleErrorForeground: "white",
+ consoleInfoBackground: "#052027",
+ consoleInfoForeground: "#ffffff",
+};
+
+const styled = baseStyled as ThemedStyledInterface<Theme>;
+
+export default styled;
diff --git a/src/console/components/console.scss b/src/console/components/console.scss
index ccb769b..2d548df 100644
--- a/src/console/components/console.scss
+++ b/src/console/components/console.scss
@@ -1,38 +1,18 @@
-[data-theme="light"] {
- --completion-title-background: lightgray;
- --completion-title-foreground: #000000;
- --completion-item-background: #ffffff;
- --completion-item-foreground: #000000;
- --completion-item-description-foreground: #008000;
- --completion-selected-background: #ffff00;
- --completion-selected-foreground: #000000;
- --command-background: #ffffff;
- --command-foreground: #000000;
- --console-error-background: #ff0000;
- --console-error-foreground: #ffffff;
- --console-info-background: #ffffff;
- --console-info-foreground: #018786;
-}
-
-[data-theme="dark"] {
- --completion-title-background: #052027;
- --completion-title-foreground: white;
- --completion-item-background: #2f474f;
- --completion-item-foreground: white;
- --completion-item-description-foreground: #86fab0;
- --completion-selected-background: #eeff41;
- --completion-selected-foreground: #000000;
- --command-background: #052027;
- --command-foreground: white;
- --console-error-background: red;
- --console-error-foreground: white;
- --console-info-background: #052027;
- --console-info-foreground: #ffffff;
-}
-
html, body, * {
margin: 0;
padding: 0;
+
+ font-style: normal;
+ font-family: monospace;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input {
+ font-style: normal;
+ font-family: monospace;
+ font-size: 12px;
+ line-height: 16px;
}
body {
@@ -47,95 +27,4 @@ body {
bottom: 0;
margin: 0;
padding: 0;
-
- @mixin console-font {
- font-style: normal;
- font-family: monospace;
- font-size: 12px;
- line-height: 16px;
- }
-
- &-command-wrapper {
- border-top: 1px solid gray;
- }
-
- &-completion {
- @include console-font;
-
- &-title {
- background-color: var(--completion-title-background);
- color: var(--completion-title-foreground);
- font-weight: bold;
- margin: 0;
- padding: 0;
- }
-
- &-item {
- background-color: var(--completion-item-background);
- color: var(--completion-item-foreground);
-
- padding-left: 1.5rem;
- background-position: 0 center;
- background-size: contain;
- background-repeat: no-repeat;
- white-space: pre;
-
- &.vimvixen-completion-selected {
- background-color: var(--completion-selected-background);
- color: var(--completion-selected-foreground);
- }
-
- &-caption {
- display: inline-block;
- width: 40%;
- text-overflow: ellipsis;
- overflow: hidden;
- }
-
- &-url {
- display: inline-block;
- color: var(--completion-item-description-foreground);
- width: 60%;
- text-overflow: ellipsis;
- overflow: hidden;
- }
- }
- }
-
- &-message {
- @include console-font;
-
- border-top: 1px solid gray;
- }
-
- &-error {
- background-color: var(--console-error-background);
- color: var(--console-error-foreground);
- font-weight: bold;
- }
-
- &-info {
- background-color: var(--console-info-background);
- color: var(--console-info-foreground);
- font-weight: normal;
- }
-
- &-command {
- background-color: var(--command-background);
- color: var(--command-foreground);
- display: flex;
-
- &-prompt {
- @include console-font;
- }
-
- &-input {
- border: none;
- flex-grow: 1;
- background-color: var(--command-background);
- color: var(--command-foreground);
-
- @include console-font;
- }
- }
}
diff --git a/src/console/components/console/Completion.tsx b/src/console/components/console/Completion.tsx
index 9b4cf15..09ae278 100644
--- a/src/console/components/console/Completion.tsx
+++ b/src/console/components/console/Completion.tsx
@@ -63,29 +63,52 @@ class Completion extends React.Component<Props, State> {
}
render() {
- let eles = [];
- let index = 0;
+ let itemIndex = 0;
+ let viewIndex = 0;
+ const groups: Array<JSX.Element> = [];
+ const viewOffset = this.state.viewOffset;
+ const viewSize = this.props.size;
- for (const group of this.props.completions) {
- eles.push(<CompletionTitle key={`group-${index}`} title={group.name} />);
+ this.props.completions.forEach((group, groupIndex) => {
+ const items = [];
+ const title = (
+ <CompletionTitle
+ id={`title-${groupIndex}`}
+ key={`group-${groupIndex}`}
+ shown={viewOffset <= viewIndex && viewIndex < viewOffset + viewSize}
+ title={group.name}
+ />
+ );
+ ++viewIndex;
for (const item of group.items) {
- eles.push(
+ items.push(
<CompletionItem
- key={`item-${index}`}
+ shown={viewOffset <= viewIndex && viewIndex < viewOffset + viewSize}
+ key={`item-${itemIndex}`}
icon={item.icon}
caption={item.caption}
url={item.url}
- highlight={index === this.props.select}
+ highlight={itemIndex === this.props.select}
+ aria-selected={itemIndex === this.props.select}
+ role="menuitem"
/>
);
- ++index;
+ ++viewIndex;
+ ++itemIndex;
}
- }
-
- const viewOffset = this.state.viewOffset;
- eles = eles.slice(viewOffset, viewOffset + this.props.size);
+ groups.push(
+ <div
+ key={`group-${groupIndex}`}
+ role="group"
+ aria-describedby={`title-${groupIndex}`}
+ >
+ {title}
+ <ul>{items}</ul>
+ </div>
+ );
+ });
- return <ul className="vimvixen-console-completion">{eles}</ul>;
+ return <div role="menu">{groups}</div>;
}
}
diff --git a/src/console/components/console/CompletionItem.tsx b/src/console/components/console/CompletionItem.tsx
index 657f360..5f2f9f6 100644
--- a/src/console/components/console/CompletionItem.tsx
+++ b/src/console/components/console/CompletionItem.tsx
@@ -1,35 +1,62 @@
import React from "react";
-import PropTypes from "prop-types";
+import styled from "../Theme";
+
+const Container = styled.li<{
+ shown: boolean;
+ icon: string;
+ highlight: boolean;
+}>`
+ background-image: ${({ icon }) => "url(" + icon + ")"};
+ background-color: ${({ highlight, theme }) =>
+ highlight
+ ? theme.completionSelectedBackground
+ : theme.completionItemBackground};
+ color: ${({ highlight, theme }) =>
+ highlight
+ ? theme.completionSelectedForeground
+ : theme.completionItemForeground};
+ display: ${({ shown }) => (shown ? "display" : "none")};
+ padding-left: 1.8rem;
+ background-position: 0 center;
+ background-size: contain;
+ background-repeat: no-repeat;
+ white-space: pre;
+`;
+
+const Caption = styled.span`
+ display: inline-block;
+ width: 40%;
+ text-overflow: ellipsis;
+ overflow: hidden;
+`;
+
+const Description = styled.span`
+ display: inline-block;
+ color: ${({ theme }) => theme.completionItemDescriptionForeground};
+ width: 60%;
+ text-overflow: ellipsis;
+ overflow: hidden;
+`;
interface Props {
+ shown: boolean;
highlight: boolean;
caption?: string;
url?: string;
icon?: string;
}
-const CompletionItem = (props: Props) => {
- let className = "vimvixen-console-completion-item";
- if (props.highlight) {
- className += " vimvixen-completion-selected";
- }
- return (
- <li
- className={className}
- style={{ backgroundImage: "url(" + props.icon + ")" }}
- >
- <span className="vimvixen-console-completion-item-caption">
- {props.caption}
- </span>
- <span className="vimvixen-console-completion-item-url">{props.url}</span>
- </li>
- );
-};
-
-CompletionItem.propTypes = {
- highlight: PropTypes.bool,
- caption: PropTypes.string,
- url: PropTypes.string,
-};
+const CompletionItem: React.FC<React.HTMLAttributes<HTMLElement> & Props> = (
+ props
+) => (
+ <Container
+ icon={props.icon || ""}
+ aria-labelledby={`completion-item-${props.caption}`}
+ {...props}
+ >
+ <Caption id={`completion-item-${props.caption}`}>{props.caption}</Caption>
+ <Description>{props.url}</Description>
+ </Container>
+);
export default CompletionItem;
diff --git a/src/console/components/console/CompletionTitle.tsx b/src/console/components/console/CompletionTitle.tsx
index 7257006..ec2fc8b 100644
--- a/src/console/components/console/CompletionTitle.tsx
+++ b/src/console/components/console/CompletionTitle.tsx
@@ -1,11 +1,22 @@
import React from "react";
+import styled from "../Theme";
+
+const Li = styled.li<{ shown: boolean }>`
+ display: ${({ shown }) => (shown ? "display" : "none")};
+ background-color: ${({ theme }) => theme.completionTitleBackground};
+ color: ${({ theme }) => theme.completionTitleForeground};
+ font-weight: bold;
+ margin: 0;
+ padding: 0;
+`;
interface Props {
+ shown: boolean;
title: string;
}
-const CompletionTitle = (props: Props) => {
- return <li className="vimvixen-console-completion-title">{props.title}</li>;
-};
+const CompletionTitle: React.FC<React.HTMLAttributes<HTMLElement> & Props> = (
+ props
+) => <Li {...props}>{props.title}</Li>;
export default CompletionTitle;
diff --git a/src/console/components/console/Input.tsx b/src/console/components/console/Input.tsx
index e412a0c..448b096 100644
--- a/src/console/components/console/Input.tsx
+++ b/src/console/components/console/Input.tsx
@@ -1,4 +1,22 @@
import React from "react";
+import styled from "../Theme";
+
+const Container = styled.div`
+ background-color: ${({ theme }) => theme.commandBackground};
+ color: ${({ theme }) => theme.commandForeground};
+ display: flex;
+`;
+
+const Prompt = styled.i`
+ font-style: normal;
+`;
+
+const InputInner = styled.input`
+ border: none;
+ flex-grow: 1;
+ background-color: ${({ theme }) => theme.commandBackground};
+ color: ${({ theme }) => theme.commandForeground};
+`;
interface Props {
mode: string;
@@ -32,17 +50,16 @@ class Input extends React.Component<Props> {
}
return (
- <div className="vimvixen-console-command">
- <i className="vimvixen-console-command-prompt">{prompt}</i>
- <input
- className="vimvixen-console-command-input"
+ <Container>
+ <Prompt>{prompt}</Prompt>
+ <InputInner
ref={this.input}
onBlur={this.props.onBlur}
onKeyDown={this.props.onKeyDown}
onChange={this.props.onChange}
value={this.props.value}
/>
- </div>
+ </Container>
);
}
}
diff --git a/src/console/components/console/Message.tsx b/src/console/components/console/Message.tsx
index fd1c9d7..73498fd 100644
--- a/src/console/components/console/Message.tsx
+++ b/src/console/components/console/Message.tsx
@@ -1,24 +1,31 @@
import React from "react";
+import styled from "../Theme";
+
+const Error = styled.p`
+ border-top: 1px solid gray;
+ background-color: ${({ theme }) => theme.consoleErrorBackground};
+ color: ${({ theme }) => theme.consoleErrorForeground};
+ font-weight: bold;
+`;
+
+const Info = styled.p`
+ border-top: 1px solid gray;
+ background-color: ${({ theme }) => theme.consoleInfoBackground};
+ color: ${({ theme }) => theme.consoleInfoForeground};
+ font-weight: normal;
+`;
interface Props {
mode: string;
children: string;
}
-const Message = (props: Props) => {
- switch (props.mode) {
+const Message: React.FC<Props> = ({ mode, children }) => {
+ switch (mode) {
case "error":
- return (
- <p className="vimvixen-console-message vimvixen-console-error">
- {props.children}
- </p>
- );
+ return <Error role="alert">{children}</Error>;
case "info":
- return (
- <p className="vimvixen-console-message vimvixen-console-info">
- {props.children}
- </p>
- );
+ return <Info role="status">{children}</Info>;
}
return null;
};