(core) Improve dark mode

Summary:
Enhances dark mode support for the formula editor, and adds support to
the color select popup. Also fixes some bugs with dark mode.

Test Plan: Tested manually.

Reviewers: jarek

Reviewed By: jarek

Differential Revision: https://phab.getgrist.com/D3847
This commit is contained in:
George Gevoian
2023-04-11 01:00:28 -04:00
parent 9d0e6694fc
commit 8a0bb4d4fe
27 changed files with 537 additions and 107 deletions

View File

@@ -1,10 +1,13 @@
import {setupAceEditorCompletions} from 'app/client/components/AceEditorCompletions';
import {colors} from 'app/client/ui2018/cssVars';
import {theme} from 'app/client/ui2018/cssVars';
import {Theme} from 'app/common/ThemePrefs';
import {getGristConfig} from 'app/common/urlUtils';
import * as ace from 'brace';
import {dom, DomArg, Observable, styled} from 'grainjs';
import {Computed, dom, DomArg, Listener, Observable, styled} from 'grainjs';
import debounce from 'lodash/debounce';
export interface ACLFormulaOptions {
gristTheme: Computed<Theme>;
initialValue: string;
readOnly: boolean;
placeholder: DomArg;
@@ -19,7 +22,19 @@ export function aclFormulaEditor(options: ACLFormulaOptions) {
const editor: ace.Editor = ace.edit(editorElem);
// Set various editor options.
editor.setTheme('ace/theme/chrome');
function setAceTheme(gristTheme: Theme) {
const {enableCustomCss} = getGristConfig();
const gristAppearance = gristTheme.appearance;
const aceTheme = gristAppearance === 'dark' && !enableCustomCss ? 'dracula' : 'chrome';
editor.setTheme(`ace/theme/${aceTheme}`);
}
setAceTheme(options.gristTheme.get());
let themeListener: Listener | undefined;
if (!getGristConfig().enableCustomCss) {
themeListener = options.gristTheme.addListener((gristTheme) => {
setAceTheme(gristTheme);
});
}
// ACE editor resizes automatically when maxLines is set.
editor.setOptions({enableLiveAutocompletion: true, maxLines: 10});
editor.renderer.setShowGutter(false); // Default line numbers to hidden
@@ -80,6 +95,7 @@ export function aclFormulaEditor(options: ACLFormulaOptions) {
}
return cssConditionInputAce(
dom.autoDispose(themeListener ?? null),
cssConditionInputAce.cls('-disabled', options.readOnly),
// ACE editor calls preventDefault on clicks into the scrollbar area, which prevents focus
// being set when the click happens to be into there. To ensure we can focus on such clicks
@@ -100,22 +116,25 @@ const cssConditionInputAce = styled('div', `
cursor: pointer;
&:hover {
border: 1px solid ${colors.darkGrey};
border: 1px solid ${theme.accessRulesFormulaEditorBorderHover};
}
&:not(&-disabled):focus-within {
box-shadow: inset 0 0 0 1px ${colors.cursor};
border-color: ${colors.cursor};
box-shadow: inset 0 0 0 1px ${theme.accessRulesFormulaEditorFocus};
border-color: ${theme.accessRulesFormulaEditorFocus};
}
&:not(:focus-within) .ace_scroller, &-disabled .ace_scroller {
cursor: unset;
}
&-disabled, &-disabled:hover {
background-color: ${colors.mediumGreyOpaque};
background-color: ${theme.accessRulesFormulaEditorBgDisabled};
box-shadow: unset;
border-color: transparent;
}
&-disabled .ace-chrome {
background-color: ${colors.mediumGreyOpaque};
& .ace-chrome, & .ace-dracula {
background-color: ${theme.accessRulesFormulaEditorBg};
}
&-disabled .ace-chrome, &-disabled .ace-dracula {
background-color: ${theme.accessRulesFormulaEditorBgDisabled};
}
& .ace_marker-layer, & .ace_cursor-layer {
display: none;

View File

@@ -123,9 +123,9 @@ export class AccessRules extends Disposable {
// Map of tableId to basic metadata for all tables in the document.
private _aclResources = new Map<string, AclTableDescription>();
private _aclUsersPopup = ACLUsersPopup.create(this, this._gristDoc.docPageModel);
private _aclUsersPopup = ACLUsersPopup.create(this, this.gristDoc.docPageModel);
constructor(private _gristDoc: GristDoc) {
constructor(public gristDoc: GristDoc) {
super();
this._ruleStatus = Computed.create(this, (use) => {
const defRuleSet = use(this._docDefaultRuleSet);
@@ -175,10 +175,10 @@ export class AccessRules extends Disposable {
// changes). Instead, react deliberately if rules change. Note that table/column renames would
// trigger changes to rules, so we don't need to listen for those separately.
for (const tableId of ['_grist_ACLResources', '_grist_ACLRules']) {
const tableData = this._gristDoc.docData.getTable(tableId)!;
const tableData = this.gristDoc.docData.getTable(tableId)!;
this.autoDispose(tableData.tableActionEmitter.addListener(this._onChange, this));
}
this.autoDispose(this._gristDoc.docPageModel.currentDoc.addListener(this._updateDocAccessData, this));
this.autoDispose(this.gristDoc.docPageModel.currentDoc.addListener(this._updateDocAccessData, this));
this.update().catch((e) => this._errorMessage.set(e.message));
}
@@ -202,9 +202,9 @@ export class AccessRules extends Disposable {
const rules = this._ruleCollection;
const [ , , aclResources] = await Promise.all([
rules.update(this._gristDoc.docData, {log: console, pullOutSchemaEdit: true}),
rules.update(this.gristDoc.docData, {log: console, pullOutSchemaEdit: true}),
this._updateDocAccessData(),
this._gristDoc.docComm.getAclResources(),
this.gristDoc.docComm.getAclResources(),
]);
this._aclResources = new Map(Object.entries(aclResources.tables));
this._ruleProblems.set(aclResources.problems);
@@ -244,7 +244,7 @@ export class AccessRules extends Disposable {
// Note that if anything has changed, we apply changes relative to the current state of the
// ACL tables (they may have changed by other users). So our changes will win.
const docData = this._gristDoc.docData;
const docData = this.gristDoc.docData;
const resourcesTable = docData.getMetaTable('_grist_ACLResources');
const rulesTable = docData.getMetaTable('_grist_ACLRules');
@@ -346,7 +346,7 @@ export class AccessRules extends Disposable {
public buildDom() {
return cssOuter(
dom('div', this._gristDoc.behavioralPromptsManager.attachTip('accessRules', {
dom('div', this.gristDoc.behavioralPromptsManager.attachTip('accessRules', {
hideArrow: true,
})),
cssAddTableRow(
@@ -485,7 +485,7 @@ export class AccessRules extends Disposable {
public async checkAclFormula(text: string): Promise<FormulaProperties> {
if (text) {
return this._gristDoc.docComm.checkAclFormula(text);
return this.gristDoc.docComm.checkAclFormula(text);
}
return {};
}
@@ -1433,6 +1433,7 @@ class ObsUserAttributeRule extends Disposable {
cssColumnGroup(
cssCell1(
aclFormulaEditor({
gristTheme: this._accessRules.gristDoc.currentTheme,
initialValue: this._charId.get(),
readOnly: false,
setValue: (text) => this._setUserAttr(text),
@@ -1655,6 +1656,7 @@ class ObsRulePart extends Disposable {
cssCell2(
wide ? cssCell4.cls('') : null,
aclFormulaEditor({
gristTheme: this._ruleSet.accessRules.gristDoc.currentTheme,
initialValue: this._aclFormula.get(),
readOnly: this.isBuiltIn(),
setValue: (value) => this._setAclFormula(value),