diff options
author | Shin'ya Ueoka <ueokande@i-beam.org> | 2020-09-21 14:20:38 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-21 14:20:38 +0900 |
commit | 7b8a96bbf7cb6d28c76c061a6b24a9fa821820b1 (patch) | |
tree | 4d00b60aa59e946f64ea6402ee7c78fc491f6203 /src | |
parent | 748ab17dc61a2bb3f1c1e3ea4b43d13ef23d8edf (diff) | |
parent | 9bba3ed79123affc8e557cf4211df51bd375742c (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.tsx | 47 | ||||
-rw-r--r-- | src/console/components/Theme.ts | 53 | ||||
-rw-r--r-- | src/console/components/console.scss | 135 | ||||
-rw-r--r-- | src/console/components/console/Completion.tsx | 49 | ||||
-rw-r--r-- | src/console/components/console/CompletionItem.tsx | 75 | ||||
-rw-r--r-- | src/console/components/console/CompletionTitle.tsx | 17 | ||||
-rw-r--r-- | src/console/components/console/Input.tsx | 27 | ||||
-rw-r--r-- | src/console/components/console/Message.tsx | 31 |
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; }; |