blob: 1d940c7b19f019b62f0eb9ac915dbeabda56cc08 [file] [log] [blame] [edit]
/**
* @license
* Copyright 2018 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import '../../shared/gr-button/gr-button';
import {EditPreferencesInfo} from '../../../types/common';
import {grFormStyles} from '../../../styles/gr-form-styles';
import {sharedStyles} from '../../../styles/shared-styles';
import {css, html, LitElement} from 'lit';
import {customElement, query, state} from 'lit/decorators.js';
import {convertToString} from '../../../utils/string-util';
import {subscribe} from '../../lit/subscription-controller';
import {resolve} from '../../../models/dependency';
import {userModelToken} from '../../../models/user/user-model';
import {fire} from '../../../utils/event-util';
import {ValueChangedEvent} from '../../../types/events';
import '@material/web/textfield/outlined-text-field';
import {materialStyles} from '../../../styles/gr-material-styles';
@customElement('gr-edit-preferences')
export class GrEditPreferences extends LitElement {
@query('#editTabWidth') private editTabWidth?: HTMLInputElement;
@query('#editColumns') private editColumns?: HTMLInputElement;
@query('#editIndentUnit') private editIndentUnit?: HTMLInputElement;
@query('#editSyntaxHighlighting')
private editSyntaxHighlighting?: HTMLInputElement;
@query('#showAutoCloseBrackets')
private showAutoCloseBrackets?: HTMLInputElement;
@query('#showIndentWithTabs') private showIndentWithTabs?: HTMLInputElement;
@query('#showMatchBrackets') private showMatchBrackets?: HTMLInputElement;
@query('#editShowLineWrapping')
private editShowLineWrapping?: HTMLInputElement;
@query('#editShowTabs') private editShowTabs?: HTMLInputElement;
@query('#editShowTrailingWhitespaceInput')
private editShowTrailingWhitespaceInput?: HTMLInputElement;
@state() editPrefs?: EditPreferencesInfo;
@state() private originalEditPrefs?: EditPreferencesInfo;
readonly getUserModel = resolve(this, userModelToken);
constructor() {
super();
subscribe(
this,
() => this.getUserModel().editPreferences$,
editPreferences => {
this.originalEditPrefs = editPreferences;
this.editPrefs = {...editPreferences};
}
);
}
static override get styles() {
return [
materialStyles,
sharedStyles,
grFormStyles,
css`
md-outlined-text-field {
max-width: 25em;
}
`,
];
}
override render() {
return html`
<div id="editPreferences" class="gr-form-styles">
<section>
<label for="editTabWidth" class="title">Tab width</label>
<span class="value">
<md-outlined-text-field
id="editTabWidth"
class="showBlueFocusBorder"
type="number"
step="1"
.value=${convertToString(this.editPrefs?.tab_size)}
@input=${this.handleEditTabWidthInput}
@beforeinput=${(e: InputEvent) => {
// In iron-input we had allowedPattern, but this is not supported
// in md-outlined-text-field. Which uses native input functionality.
// We workaround this.
const data = e.data;
if (data && !/^[0-9]*$/.test(data)) {
e.preventDefault();
}
}}
>
</md-outlined-text-field>
</span>
</section>
<section>
<label for="editColumns" class="title">Columns</label>
<span class="value">
<md-outlined-text-field
id="editColumns"
class="showBlueFocusBorder"
type="number"
step="1"
.value=${convertToString(this.editPrefs?.line_length)}
@input=${this.handleEditLineLengthInput}
@beforeinput=${(e: InputEvent) => {
// In iron-input we had allowedPattern, but this is not supported
// in md-outlined-text-field. Which uses native input functionality.
// We workaround this.
const data = e.data;
if (data && !/^[0-9]*$/.test(data)) {
e.preventDefault();
}
}}
>
</md-outlined-text-field>
</span>
</section>
<section>
<label for="editIndentUnit" class="title">Indent unit</label>
<span class="value">
<md-outlined-text-field
id="editIndentUnit"
class="showBlueFocusBorder"
type="number"
step="1"
.value=${convertToString(this.editPrefs?.indent_unit)}
@input=${this.handleEditIndentUnitInput}
@beforeinput=${(e: InputEvent) => {
// In iron-input we had allowedPattern, but this is not supported
// in md-outlined-text-field. Which uses native input functionality.
// We workaround this.
const data = e.data;
if (data && !/^[0-9]*$/.test(data)) {
e.preventDefault();
}
}}
>
</md-outlined-text-field>
</span>
</section>
<section>
<label for="editSyntaxHighlighting" class="title"
>Syntax highlighting</label
>
<span class="value">
<input
id="editSyntaxHighlighting"
type="checkbox"
?checked=${!!this.editPrefs?.syntax_highlighting}
@change=${this.handleEditSyntaxHighlightingChanged}
/>
</span>
</section>
<section>
<label for="editShowTabs" class="title">Show tabs</label>
<span class="value">
<input
id="editShowTabs"
type="checkbox"
?checked=${!!this.editPrefs?.show_tabs}
@change=${this.handleEditShowTabsChanged}
/>
</span>
</section>
<section>
<label for="showTrailingWhitespaceInput" class="title"
>Show trailing whitespace</label
>
<span class="value">
<input
id="editShowTrailingWhitespaceInput"
type="checkbox"
?checked=${!!this.editPrefs?.show_whitespace_errors}
@change=${this.handleEditShowTrailingWhitespaceTap}
/>
</span>
</section>
<section>
<label for="showMatchBrackets" class="title">Match brackets</label>
<span class="value">
<input
id="showMatchBrackets"
type="checkbox"
?checked=${!!this.editPrefs?.match_brackets}
@change=${this.handleMatchBracketsChanged}
/>
</span>
</section>
<section>
<label for="editShowLineWrapping" class="title">Line wrapping</label>
<span class="value">
<input
id="editShowLineWrapping"
type="checkbox"
?checked=${!!this.editPrefs?.line_wrapping}
@change=${this.handleEditLineWrappingChanged}
/>
</span>
</section>
<section>
<label for="showIndentWithTabs" class="title">Indent with tabs</label>
<span class="value">
<input
id="showIndentWithTabs"
type="checkbox"
?checked=${!!this.editPrefs?.indent_with_tabs}
@change=${this.handleIndentWithTabsChanged}
/>
</span>
</section>
<section>
<label for="showAutoCloseBrackets" class="title"
>Auto close brackets</label
>
<span class="value">
<input
id="showAutoCloseBrackets"
type="checkbox"
?checked=${!!this.editPrefs?.auto_close_brackets}
@change=${this.handleAutoCloseBracketsChanged}
/>
</span>
</section>
</div>
`;
}
private readonly handleEditTabWidthInput = () => {
this.editPrefs!.tab_size = Number(this.editTabWidth!.value);
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleEditLineLengthInput = () => {
this.editPrefs!.line_length = Number(this.editColumns!.value);
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleEditIndentUnitInput = () => {
this.editPrefs!.indent_unit = Number(this.editIndentUnit!.value);
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleEditSyntaxHighlightingChanged = () => {
this.editPrefs!.syntax_highlighting = this.editSyntaxHighlighting!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
// private but used in test
readonly handleEditShowTabsChanged = () => {
this.editPrefs!.show_tabs = this.editShowTabs!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleEditShowTrailingWhitespaceTap = () => {
this.editPrefs!.show_whitespace_errors =
this.editShowTrailingWhitespaceInput!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleMatchBracketsChanged = () => {
this.editPrefs!.match_brackets = this.showMatchBrackets!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleEditLineWrappingChanged = () => {
this.editPrefs!.line_wrapping = this.editShowLineWrapping!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleIndentWithTabsChanged = () => {
this.editPrefs!.indent_with_tabs = this.showIndentWithTabs!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
private readonly handleAutoCloseBracketsChanged = () => {
this.editPrefs!.auto_close_brackets = this.showAutoCloseBrackets!.checked;
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
};
// private but used in test
hasUnsavedChanges() {
// We have to wrap boolean values in Boolean() to ensure undefined values
// use false rather than undefined.
return (
this.originalEditPrefs?.tab_size !== this.editPrefs?.tab_size ||
this.originalEditPrefs?.line_length !== this.editPrefs?.line_length ||
this.originalEditPrefs?.indent_unit !== this.editPrefs?.indent_unit ||
Boolean(this.originalEditPrefs?.syntax_highlighting) !==
Boolean(this.editPrefs?.syntax_highlighting) ||
Boolean(this.originalEditPrefs?.show_tabs) !==
Boolean(this.editPrefs?.show_tabs) ||
Boolean(this.originalEditPrefs?.show_whitespace_errors) !==
Boolean(this.editPrefs?.show_whitespace_errors) ||
Boolean(this.originalEditPrefs?.match_brackets) !==
Boolean(this.editPrefs?.match_brackets) ||
Boolean(this.originalEditPrefs?.line_wrapping) !==
Boolean(this.editPrefs?.line_wrapping) ||
Boolean(this.originalEditPrefs?.indent_with_tabs) !==
Boolean(this.editPrefs?.indent_with_tabs) ||
Boolean(this.originalEditPrefs?.auto_close_brackets) !==
Boolean(this.editPrefs?.auto_close_brackets)
);
}
async save() {
if (!this.editPrefs) return;
await this.getUserModel().updateEditPreference(this.editPrefs);
fire(this, 'has-unsaved-changes-changed', {
value: this.hasUnsavedChanges(),
});
}
}
declare global {
interface HTMLElementEventMap {
'has-unsaved-changes-changed': ValueChangedEvent<boolean>;
}
interface HTMLElementTagNameMap {
'gr-edit-preferences': GrEditPreferences;
}
}