mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 17:49:58 +00:00
Re-work whitelist feature to allow per-site UA configuration
This commit is contained in:
@@ -1,316 +0,0 @@
|
||||
/* eslint-disable max-len */
|
||||
"use strict";
|
||||
|
||||
import React, { Component } from "react";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
|
||||
interface EditableListProps {
|
||||
data: string[];
|
||||
itemPattern: RegExp;
|
||||
onChange (data: string[]): void;
|
||||
itemPatternError (err?: string): string;
|
||||
}
|
||||
|
||||
interface EditableListState {
|
||||
addingNewItem: boolean;
|
||||
rawView: boolean;
|
||||
rawViewValue: string;
|
||||
}
|
||||
|
||||
export default class EditableList extends Component<
|
||||
EditableListProps, EditableListState> {
|
||||
|
||||
private rawViewTextArea: (HTMLTextAreaElement | null) = null;
|
||||
|
||||
constructor(props: EditableListProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
addingNewItem: false
|
||||
, rawView: false
|
||||
, rawViewValue: ""
|
||||
};
|
||||
|
||||
this.handleItemRemove = this.handleItemRemove.bind(this);
|
||||
this.handleItemEdit = this.handleItemEdit.bind(this);
|
||||
this.handleSwitchView = this.handleSwitchView.bind(this);
|
||||
this.handleSaveRaw = this.handleSaveRaw.bind(this);
|
||||
this.handleRawViewTextAreaChange = this.handleRawViewTextAreaChange.bind(this);
|
||||
this.handleAddItem = this.handleAddItem.bind(this);
|
||||
this.handleNewItemRemove = this.handleNewItemRemove.bind(this);
|
||||
this.handleNewItemEdit = this.handleNewItemEdit.bind(this);
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className="editable-list">
|
||||
{ this.state.rawView
|
||||
? (
|
||||
<textarea className="editable-list__raw-view"
|
||||
rows={ Math.min(this.props.data.length, 10) }
|
||||
value={ this.state.rawViewValue }
|
||||
onChange={ this.handleRawViewTextAreaChange }
|
||||
ref={ el => { this.rawViewTextArea = el; }}>
|
||||
</textarea>
|
||||
) : (
|
||||
<ul className="editable-list__items">
|
||||
{ this.props.data.map((item, i) =>
|
||||
<EditableListItem text={ item }
|
||||
itemPattern={ this.props.itemPattern }
|
||||
itemPatternError={ this.props.itemPatternError }
|
||||
onRemove={ this.handleItemRemove }
|
||||
onEdit={ this.handleItemEdit }
|
||||
key={ i } /> )}
|
||||
|
||||
{ this.state.addingNewItem &&
|
||||
<EditableListItem text=""
|
||||
itemPattern={ this.props.itemPattern }
|
||||
itemPatternError={ this.props.itemPatternError }
|
||||
onRemove={ this.handleNewItemRemove }
|
||||
onEdit={ this.handleNewItemEdit }
|
||||
editing={ true } /> }
|
||||
</ul>
|
||||
)}
|
||||
<hr />
|
||||
<div className="editable-list__view-actions">
|
||||
{ !this.state.rawView &&
|
||||
<button className="editable-list__add-button ghost"
|
||||
title={ _("optionsUserAgentWhitelistAddItem") }
|
||||
onClick={ this.handleAddItem }
|
||||
type="button">
|
||||
<img src="assets/photon_new.svg" alt="icon, add" />
|
||||
</button> }
|
||||
|
||||
{ this.state.rawView &&
|
||||
<button className="editable-list__save-raw-button"
|
||||
onClick={ this.handleSaveRaw }
|
||||
type="button">
|
||||
{ _("optionsUserAgentWhitelistSaveRaw") }
|
||||
</button> }
|
||||
|
||||
<button className="editable-list__view-button"
|
||||
onClick={ this.handleSwitchView }
|
||||
type="button">
|
||||
{ this.state.rawView
|
||||
? _("optionsUserAgentWhitelistBasicView")
|
||||
: _("optionsUserAgentWhitelistRawView") }
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private handleItemRemove(item: string) {
|
||||
const newItems = new Set(this.props.data);
|
||||
newItems.delete(item);
|
||||
|
||||
this.props.onChange([ ...newItems ]);
|
||||
}
|
||||
|
||||
private handleItemEdit(item: string, newValue: string) {
|
||||
this.props.onChange(this.props.data.map(
|
||||
currentItem => currentItem === item
|
||||
? newValue
|
||||
: currentItem));
|
||||
}
|
||||
|
||||
private handleSwitchView() {
|
||||
this.setState(currentState => {
|
||||
if (currentState.rawView) {
|
||||
return {
|
||||
rawView: false
|
||||
, rawViewValue: ""
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
rawView: true
|
||||
, rawViewValue: this.props.data.join("\n")
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private handleSaveRaw() {
|
||||
this.setState(currentState => {
|
||||
const newItems = currentState.rawViewValue.split("\n")
|
||||
.filter(item => item !== "");
|
||||
|
||||
if ("itemPattern" in this.props) {
|
||||
for (const item of newItems) {
|
||||
if (!this.props.itemPattern.test(item)) {
|
||||
this.rawViewTextArea?.setCustomValidity(
|
||||
this.props.itemPatternError(item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.rawViewTextArea?.setCustomValidity("");
|
||||
}
|
||||
|
||||
this.props.onChange(newItems);
|
||||
});
|
||||
}
|
||||
|
||||
private handleRawViewTextAreaChange(ev: React.ChangeEvent<HTMLTextAreaElement>) {
|
||||
if (!this.rawViewTextArea) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.rawViewTextArea.scrollHeight > this.rawViewTextArea.clientHeight) {
|
||||
this.rawViewTextArea.style.height = `${this.rawViewTextArea.scrollHeight}px`;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
rawViewValue: ev.target.value
|
||||
});
|
||||
}
|
||||
|
||||
private handleAddItem() {
|
||||
this.setState({
|
||||
addingNewItem: true
|
||||
});
|
||||
}
|
||||
|
||||
private handleNewItemRemove() {
|
||||
this.setState({
|
||||
addingNewItem: false
|
||||
});
|
||||
}
|
||||
|
||||
private handleNewItemEdit(_item: string, newItem: string) {
|
||||
this.setState({
|
||||
addingNewItem: false
|
||||
}, () => {
|
||||
this.props.onChange([ ...this.props.data, newItem ]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
interface EditableListItemProps {
|
||||
text: string;
|
||||
itemPattern: RegExp;
|
||||
editing?: boolean;
|
||||
itemPatternError (err?: string): string;
|
||||
onRemove (item: string): void;
|
||||
onEdit (item: string, newValue: string): void;
|
||||
}
|
||||
|
||||
interface EditableListItemState {
|
||||
editing: boolean;
|
||||
editValue: string;
|
||||
}
|
||||
|
||||
class EditableListItem extends Component<
|
||||
EditableListItemProps, EditableListItemState> {
|
||||
|
||||
private input: (HTMLInputElement | null) = null;
|
||||
|
||||
constructor(props: EditableListItemProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
editing: this.props.editing || false
|
||||
, editValue: ""
|
||||
};
|
||||
|
||||
this.handleRemove = this.handleRemove.bind(this);
|
||||
this.handleEditBegin = this.handleEditBegin.bind(this);
|
||||
this.handleEditEnd = this.handleEditEnd.bind(this);
|
||||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
this.handleInputKeyPress = this.handleInputKeyPress.bind(this);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const selected = this.state.editing
|
||||
? "editable-list__item--selected" : "";
|
||||
|
||||
return (
|
||||
<li className={`editable-list__item ${selected}`}>
|
||||
<div className="editable-list__title"
|
||||
onDoubleClick={ this.handleEditBegin }>
|
||||
|
||||
{ this.state.editing
|
||||
? <input className="editable-list__edit-field"
|
||||
type="text"
|
||||
ref={ input => this.input = input }
|
||||
value={ this.state.editValue }
|
||||
onBlur={ this.handleEditEnd }
|
||||
onChange={ this.handleInputChange }
|
||||
onKeyPress={ this.handleInputKeyPress }/>
|
||||
: this.props.text }
|
||||
</div>
|
||||
|
||||
<button className="editable-list__edit-button ghost"
|
||||
title={ _("optionsUserAgentWhitelistEditItem") }
|
||||
onClick={ this.handleEditBegin }
|
||||
type="button">
|
||||
<img src="assets/photon_edit.svg" alt="icon, edit" />
|
||||
</button>
|
||||
|
||||
<button className="editable-list__remove-button ghost"
|
||||
title={ _("optionsUserAgentWhitelistRemoveItem") }
|
||||
onClick={ this.handleRemove }
|
||||
type="button">
|
||||
<img src="assets/photon_delete.svg" alt="icon, remove" />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
private stopEditing(input: HTMLInputElement) {
|
||||
if (this.props.editing
|
||||
&& !this.props.itemPattern.test(this.state.editValue)) {
|
||||
input.setCustomValidity(this.props.itemPatternError());
|
||||
}
|
||||
|
||||
if (!input.validity.valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.onEdit(this.props.text, this.state.editValue);
|
||||
this.setState({
|
||||
editing: false
|
||||
, editValue: ""
|
||||
});
|
||||
}
|
||||
|
||||
private handleRemove() {
|
||||
this.props.onRemove(this.props.text);
|
||||
}
|
||||
|
||||
private handleEditBegin() {
|
||||
if (!this.state.editing) {
|
||||
this.setState({
|
||||
editing: true
|
||||
, editValue: this.props.text
|
||||
}, () => {
|
||||
this.input?.focus();
|
||||
this.input?.select();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private handleEditEnd(ev: React.FocusEvent<HTMLInputElement>) {
|
||||
this.stopEditing(ev.target);
|
||||
}
|
||||
|
||||
private handleInputChange(ev: React.ChangeEvent<HTMLInputElement>) {
|
||||
this.setState({
|
||||
editValue: ev.target.value
|
||||
});
|
||||
|
||||
// If invalid, set custom error from parent
|
||||
ev.target.setCustomValidity(!this.props.itemPattern.test(ev.target.value)
|
||||
? this.props.itemPatternError()
|
||||
: "");
|
||||
}
|
||||
|
||||
private handleInputKeyPress(ev: React.KeyboardEvent<HTMLInputElement>) {
|
||||
if (ev.key === "Enter") {
|
||||
this.stopEditing(ev.target as HTMLInputElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
225
ext/src/ui/options/Whitelist.tsx
Normal file
225
ext/src/ui/options/Whitelist.tsx
Normal file
@@ -0,0 +1,225 @@
|
||||
/* eslint-disable max-len */
|
||||
"use strict";
|
||||
|
||||
import React, { Component } from "react";
|
||||
|
||||
import type { WhitelistItemData } from "../../background/whitelist";
|
||||
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/matchPattern";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
interface WhitelistProps {
|
||||
items: WhitelistItemData[];
|
||||
onChange: (items: WhitelistItemData[]) => void;
|
||||
}
|
||||
interface WhitelistState {
|
||||
addingNewItem: boolean;
|
||||
}
|
||||
|
||||
/** Editable list component for site whitelist. */
|
||||
export default class Whitelist extends Component<
|
||||
WhitelistProps,
|
||||
WhitelistState
|
||||
> {
|
||||
state: WhitelistState = {
|
||||
addingNewItem: false
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="whitelist">
|
||||
<ul className="whitelist__items">
|
||||
{this.props.items.map((item, i) => (
|
||||
<WhitelistItem
|
||||
value={item}
|
||||
onEdit={(oldValue, newValue) => {
|
||||
// Replace item
|
||||
this.props.onChange(
|
||||
this.props.items.map(item =>
|
||||
item.pattern === oldValue?.pattern
|
||||
? newValue
|
||||
: item
|
||||
)
|
||||
);
|
||||
}}
|
||||
onRemove={value => {
|
||||
// Remove item
|
||||
this.props.onChange(
|
||||
this.props.items.filter(
|
||||
item => item.pattern !== value?.pattern
|
||||
)
|
||||
);
|
||||
}}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
|
||||
{this.state.addingNewItem && (
|
||||
<WhitelistItem
|
||||
isEditing={true}
|
||||
onEdit={(__, newValue) => {
|
||||
// Add new item
|
||||
this.setState({ addingNewItem: false }, () => {
|
||||
this.props.onChange([
|
||||
...this.props.items,
|
||||
newValue
|
||||
]);
|
||||
});
|
||||
}}
|
||||
onRemove={() => {
|
||||
// Cancel adding new item
|
||||
this.setState({ addingNewItem: false });
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
<hr />
|
||||
|
||||
<div className="whitelist__view-actions">
|
||||
<button
|
||||
className="whitelist__add-button ghost"
|
||||
title={_("optionsSiteWhitelistAddItem")}
|
||||
onClick={() => this.setState({ addingNewItem: true })}
|
||||
type="button"
|
||||
>
|
||||
<img src="assets/photon_new.svg" alt="icon, add" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface WhitelistItemProps {
|
||||
value?: WhitelistItemData;
|
||||
/** Initial editing state */
|
||||
isEditing?: boolean;
|
||||
onEdit: (
|
||||
oldValue: WhitelistItemData | undefined,
|
||||
newValue: WhitelistItemData
|
||||
) => void;
|
||||
onRemove: (value?: WhitelistItemData) => void;
|
||||
}
|
||||
interface WhitelistItemState {
|
||||
isEditing: boolean;
|
||||
editValue?: WhitelistItemData;
|
||||
}
|
||||
|
||||
/** Editable item component for whitelist. */
|
||||
class WhitelistItem extends Component<WhitelistItemProps, WhitelistItemState> {
|
||||
private inputElement: HTMLInputElement | null = null;
|
||||
|
||||
constructor(props: WhitelistItemProps) {
|
||||
super(props);
|
||||
this.state = { isEditing: props.isEditing ?? false };
|
||||
|
||||
this.beginEditing = this.beginEditing.bind(this);
|
||||
this.finishEditing = this.finishEditing.bind(this);
|
||||
}
|
||||
|
||||
/** Sets editing state and focuses input field. */
|
||||
private beginEditing() {
|
||||
if (this.state.isEditing) return;
|
||||
|
||||
this.setState(
|
||||
{
|
||||
isEditing: true,
|
||||
editValue: this.props.value
|
||||
},
|
||||
() => {
|
||||
this.inputElement?.focus();
|
||||
this.inputElement?.select();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/** Checks input validity and sends edit update. */
|
||||
private finishEditing() {
|
||||
if (!this.state.isEditing || !this.state.editValue) return;
|
||||
|
||||
if (this.inputElement?.validity.valid) {
|
||||
this.props.onEdit(this.props.value, this.state.editValue);
|
||||
this.setState({
|
||||
isEditing: false,
|
||||
editValue: undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const selectedClassName = this.state.isEditing
|
||||
? "whitelist__item--selected"
|
||||
: "";
|
||||
|
||||
return (
|
||||
<li className={`whitelist__item ${selectedClassName}`}>
|
||||
<div
|
||||
className="whitelist__title"
|
||||
onDoubleClick={this.beginEditing}
|
||||
>
|
||||
{this.state.isEditing ? (
|
||||
<input
|
||||
ref={el => (this.inputElement = el)}
|
||||
type="text"
|
||||
className="whitelist__input-pattern"
|
||||
value={this.state.editValue?.pattern}
|
||||
pattern={REMOTE_MATCH_PATTERN_REGEX.source}
|
||||
onChange={ev => {
|
||||
this.setState(prevState => ({
|
||||
editValue: {
|
||||
...prevState.editValue,
|
||||
pattern: ev.target.value
|
||||
}
|
||||
}));
|
||||
}}
|
||||
onBlur={this.finishEditing}
|
||||
onKeyPress={ev => {
|
||||
if (ev.key === "Enter") {
|
||||
this.finishEditing();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
this.props.value?.pattern
|
||||
)}
|
||||
</div>
|
||||
|
||||
<label className="whitelist__user-agent">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={!this.props.value?.isUserAgentDisabled}
|
||||
onChange={ev => {
|
||||
if (!this.props.value) return;
|
||||
this.props.onEdit(this.props.value, {
|
||||
...this.props.value,
|
||||
isUserAgentDisabled: !ev.target.checked
|
||||
});
|
||||
}}
|
||||
/>
|
||||
{_("optionsSiteWhitelistUserAgent")}
|
||||
</label>
|
||||
|
||||
<button
|
||||
className="whitelist__edit-button ghost"
|
||||
title={_("optionsSiteWhitelistEditItem")}
|
||||
onClick={this.beginEditing}
|
||||
type="button"
|
||||
>
|
||||
<img src="assets/photon_edit.svg" alt="icon, edit" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
className="whitelist__remove-button ghost"
|
||||
title={_("optionsSiteWhitelistRemoveItem")}
|
||||
onClick={() => {
|
||||
this.props.onRemove(this.props.value);
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<img src="assets/photon_delete.svg" alt="icon, remove" />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,14 @@ import React, { Component } from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
|
||||
import defaultOptions from "../../defaultOptions";
|
||||
import type { WhitelistItemData } from "../../background/whitelist";
|
||||
|
||||
import Bridge from "./Bridge";
|
||||
import EditableList from "./EditableList";
|
||||
import Whitelist from "./Whitelist";
|
||||
|
||||
import bridge, { BridgeInfo, BridgeTimedOutError } from "../../lib/bridge";
|
||||
import logger from "../../lib/logger";
|
||||
import options, { Options } from "../../lib/options";
|
||||
import { REMOTE_MATCH_PATTERN_REGEX } from "../../lib/matchPattern";
|
||||
|
||||
const _ = browser.i18n.getMessage;
|
||||
|
||||
@@ -77,9 +77,6 @@ class OptionsApp extends Component<OptionsAppProps, OptionsAppState> {
|
||||
this.handleFormChange = this.handleFormChange.bind(this);
|
||||
this.handleInputChange = this.handleInputChange.bind(this);
|
||||
this.handleWhitelistChange = this.handleWhitelistChange.bind(this);
|
||||
|
||||
this.getWhitelistItemPatternError =
|
||||
this.getWhitelistItemPatternError.bind(this);
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
@@ -333,77 +330,43 @@ class OptionsApp extends Component<OptionsAppProps, OptionsAppState> {
|
||||
|
||||
<fieldset className="category">
|
||||
<legend className="category__name">
|
||||
<h2>
|
||||
{_("optionsUserAgentWhitelistCategoryName")}
|
||||
</h2>
|
||||
<h2>{_("optionsSiteWhitelistCategoryName")}</h2>
|
||||
</legend>
|
||||
<p className="category__description">
|
||||
{_("optionsUserAgentWhitelistCategoryDescription")}
|
||||
{_("optionsSiteWhitelistCategoryDescription")}
|
||||
</p>
|
||||
|
||||
<label className="option option--inline">
|
||||
<div className="option__control">
|
||||
<input
|
||||
name="userAgentWhitelistEnabled"
|
||||
name="siteWhitelistEnabled"
|
||||
type="checkbox"
|
||||
checked={
|
||||
this.state.options
|
||||
?.userAgentWhitelistEnabled
|
||||
this.state.options?.siteWhitelistEnabled
|
||||
}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="option__label">
|
||||
{_("optionsUserAgentWhitelistEnabled")}
|
||||
<span className="option__recommended">
|
||||
{_("optionsOptionRecommended")}
|
||||
</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<label className="option option--inline">
|
||||
<div className="option__control">
|
||||
<input
|
||||
name="userAgentWhitelistRestrictedEnabled"
|
||||
type="checkbox"
|
||||
checked={
|
||||
this.state.options
|
||||
?.userAgentWhitelistRestrictedEnabled
|
||||
}
|
||||
onChange={this.handleInputChange}
|
||||
/>
|
||||
</div>
|
||||
<div className="option__label">
|
||||
{_(
|
||||
"optionsUserAgentWhitelistRestrictedEnabled"
|
||||
)}
|
||||
{_("optionsSiteWhitelistEnabled")}
|
||||
<span className="option__recommended">
|
||||
{_("optionsOptionRecommended")}
|
||||
</span>
|
||||
</div>
|
||||
<div className="option__description">
|
||||
{_(
|
||||
"optionsUserAgentWhitelistRestrictedEnabledDescription"
|
||||
)}
|
||||
{_("optionsSiteWhitelistEnabledDescription")}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div className="option">
|
||||
<div className="option__label">
|
||||
{_("optionsUserAgentWhitelistContent")}
|
||||
{_("optionsSiteWhitelistContent")}
|
||||
</div>
|
||||
<div className="option__control">
|
||||
{this.state.options?.userAgentWhitelist && (
|
||||
<EditableList
|
||||
data={
|
||||
this.state.options
|
||||
.userAgentWhitelist
|
||||
}
|
||||
<Whitelist
|
||||
items={this.state.options.siteWhitelist}
|
||||
onChange={this.handleWhitelistChange}
|
||||
itemPattern={REMOTE_MATCH_PATTERN_REGEX}
|
||||
itemPatternError={
|
||||
this.getWhitelistItemPatternError
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -485,22 +448,15 @@ class OptionsApp extends Component<OptionsAppProps, OptionsAppState> {
|
||||
});
|
||||
}
|
||||
|
||||
private handleWhitelistChange(whitelist: string[]) {
|
||||
private handleWhitelistChange(whitelist: WhitelistItemData[]) {
|
||||
this.setState(currentState => {
|
||||
if (currentState.options) {
|
||||
currentState.options.userAgentWhitelist = whitelist;
|
||||
currentState.options.siteWhitelist = whitelist;
|
||||
}
|
||||
|
||||
return currentState;
|
||||
});
|
||||
}
|
||||
|
||||
private getWhitelistItemPatternError(info: string): string {
|
||||
return _("optionsUserAgentWhitelistInvalidMatchPattern", info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ReactDOM.render(
|
||||
<OptionsApp />
|
||||
, document.querySelector("#root"));
|
||||
ReactDOM.render(<OptionsApp />, document.querySelector("#root"));
|
||||
|
||||
@@ -316,7 +316,7 @@ button.ghost:not(:hover) {
|
||||
}
|
||||
|
||||
|
||||
.editable-list {
|
||||
.whitelist {
|
||||
background-color: var(--box-background);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--box-color);
|
||||
@@ -324,22 +324,18 @@ button.ghost:not(:hover) {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.editable-list__view-actions {
|
||||
.whitelist__view-actions {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.editable-list__save-raw-button {
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.editable-list hr {
|
||||
.whitelist hr {
|
||||
border: initial;
|
||||
border-top: 1px solid var(--border-color);
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.editable-list__items {
|
||||
.whitelist__items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: initial;
|
||||
@@ -348,22 +344,22 @@ button.ghost:not(:hover) {
|
||||
width: calc(100% + 10px);
|
||||
}
|
||||
|
||||
.editable-list__item {
|
||||
.whitelist__item {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: 34px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.editable-list__item:nth-child(odd) {
|
||||
.whitelist__item:nth-child(odd) {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.editable-list__item--selected {
|
||||
.whitelist__item--selected {
|
||||
background-color: var(--blue-50-a30) !important;
|
||||
}
|
||||
|
||||
.editable-list__title {
|
||||
.whitelist__title {
|
||||
flex: 1;
|
||||
font-family: monospace;
|
||||
overflow: hidden;
|
||||
@@ -372,28 +368,24 @@ button.ghost:not(:hover) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.editable-list__item:not(.editable-list__item--selected) > .editable-list__title {
|
||||
.whitelist__item:not(.whitelist__item--selected) > .whitelist__title {
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.editable-list__title + button {
|
||||
.whitelist__title + button {
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.editable-list__edit-field {
|
||||
.whitelist__input-pattern {
|
||||
font: inherit;
|
||||
margin-inline-end: 1em;
|
||||
width: -moz-available;
|
||||
}
|
||||
|
||||
.editable-list__raw-view {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
resize: vertical;
|
||||
width: 100%;
|
||||
.whitelist__user-agent {
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.editable-list__add-button {
|
||||
.whitelist__add-button {
|
||||
margin-inline-end: auto;
|
||||
}
|
||||
|
||||
@@ -410,7 +402,7 @@ button.ghost:not(:hover) {
|
||||
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.editable-list__item:nth-child(odd) {
|
||||
.whitelist__item:nth-child(odd) {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user