mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Add dark mode to user preferences
Summary: Adds initial implementation of dark mode. Preferences for dark mode are available on the account settings page. Dark mode is currently a beta feature as there are still some small bugs to squash and a few remaining UI elements to style. Test Plan: Browser tests. Reviewers: jarek Reviewed By: jarek Subscribers: paulfitz, jarek Differential Revision: https://phab.getgrist.com/D3587
This commit is contained in:
@@ -16,7 +16,7 @@ import {cssLabel, cssRow, cssSeparator} from 'app/client/ui/RightPanelStyles';
|
||||
import {cssFieldEntry, cssFieldLabel, IField, VisibleFieldsConfig } from 'app/client/ui/VisibleFieldsConfig';
|
||||
import {IconName} from 'app/client/ui2018/IconList';
|
||||
import {squareCheckbox} from 'app/client/ui2018/checkbox';
|
||||
import {colors, vars} from 'app/client/ui2018/cssVars';
|
||||
import {theme, vars} from 'app/client/ui2018/cssVars';
|
||||
import {cssDragger} from 'app/client/ui2018/draggableList';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
import {IOptionFull, linkSelect, menu, menuItem, menuText, select} from 'app/client/ui2018/menus';
|
||||
@@ -33,6 +33,7 @@ import debounce = require('lodash/debounce');
|
||||
import defaults = require('lodash/defaults');
|
||||
import defaultsDeep = require('lodash/defaultsDeep');
|
||||
import isNumber = require('lodash/isNumber');
|
||||
import merge = require('lodash/merge');
|
||||
import sum = require('lodash/sum');
|
||||
import union = require('lodash/union');
|
||||
import type {Annotations, Config, Datum, ErrorBar, Layout, LayoutAxis, Margin,
|
||||
@@ -221,6 +222,8 @@ export class ChartView extends Disposable {
|
||||
this.listenTo(this.sortedRows, 'rowNotify', this._update);
|
||||
this.autoDispose(this.sortedRows.getKoArray().subscribe(this._update));
|
||||
this.autoDispose(this._formatterComp.subscribe(this._update));
|
||||
this.autoDispose(this.gristDoc.docPageModel.appModel.currentTheme.addListener(() =>
|
||||
this._update()));
|
||||
}
|
||||
|
||||
public prepareToPrint(onOff: boolean) {
|
||||
@@ -333,7 +336,7 @@ export class ChartView extends Disposable {
|
||||
// meantime and cause error later. So let's check again.
|
||||
if (this.isDisposed()) { return; }
|
||||
|
||||
const layout: Partial<Layout> = defaultsDeep(plotData.layout, getPlotlyLayout(options));
|
||||
const layout: Partial<Layout> = defaultsDeep(plotData.layout, this._getPlotlyLayout(options));
|
||||
const config: Partial<Config> = {...plotData.config, displayModeBar: false};
|
||||
// react() can be used in place of newPlot(), and is faster when updating an existing plot.
|
||||
await Plotly.react(this._chartDom, plotData.data, layout, config);
|
||||
@@ -348,6 +351,50 @@ export class ChartView extends Disposable {
|
||||
private _isCompatibleSeries(col: ColumnRec) {
|
||||
return isNumericOnly(this._chartType.peek()) ? isNumericLike(col) : true;
|
||||
}
|
||||
|
||||
private _getPlotlyLayout(options: ChartOptions): Partial<Layout> {
|
||||
// Note that each call to getPlotlyLayout() creates a new layout object. We are intentionally
|
||||
// avoiding reuse because Plotly caches too many layout calculations when the object is reused.
|
||||
const yaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
|
||||
const xaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
|
||||
if (options.logYAxis) { yaxis.type = 'log'; }
|
||||
if (options.invertYAxis) { yaxis.autorange = 'reversed'; }
|
||||
const layout = {
|
||||
// Margins include labels, titles, legend, and may get auto-expanded beyond this.
|
||||
margin: {
|
||||
l: 50,
|
||||
r: 50,
|
||||
b: 40, // Space below chart which includes x-axis labels
|
||||
t: 30, // Space above the chart (doesn't include any text)
|
||||
pad: 4
|
||||
} as Margin,
|
||||
yaxis,
|
||||
xaxis,
|
||||
...(options.stacked ? {barmode: 'relative'} : {}),
|
||||
};
|
||||
return merge(layout, this._getPlotlyTheme());
|
||||
}
|
||||
|
||||
private _getPlotlyTheme(): Partial<Layout> {
|
||||
const appModel = this.gristDoc.docPageModel.appModel;
|
||||
const {colors} = appModel.currentTheme.get();
|
||||
return {
|
||||
paper_bgcolor: colors['chart-bg'],
|
||||
plot_bgcolor: colors['chart-bg'],
|
||||
xaxis: {
|
||||
color: colors['chart-x-axis'],
|
||||
},
|
||||
yaxis: {
|
||||
color: colors['chart-y-axis'],
|
||||
},
|
||||
font: {
|
||||
color: colors['chart-fg'],
|
||||
},
|
||||
legend: {
|
||||
bgcolor: colors['chart-legend-bg'],
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,32 +467,6 @@ function extractErrorBars(series: Series[], options: ChartOptions): Map<Series,
|
||||
defaults(ChartView.prototype, BaseView.prototype);
|
||||
Object.assign(ChartView.prototype, BackboneEvents);
|
||||
|
||||
function getPlotlyLayout(options: ChartOptions): Partial<Layout> {
|
||||
// Note that each call to getPlotlyLayout() creates a new layout object. We are intentionally
|
||||
// avoiding reuse because Plotly caches too many layout calculations when the object is reused.
|
||||
const yaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
|
||||
const xaxis: Partial<LayoutAxis> = {automargin: true, title: {standoff: 0}};
|
||||
if (options.logYAxis) { yaxis.type = 'log'; }
|
||||
if (options.invertYAxis) { yaxis.autorange = 'reversed'; }
|
||||
return {
|
||||
// Margins include labels, titles, legend, and may get auto-expanded beyond this.
|
||||
margin: {
|
||||
l: 50,
|
||||
r: 50,
|
||||
b: 40, // Space below chart which includes x-axis labels
|
||||
t: 30, // Space above the chart (doesn't include any text)
|
||||
pad: 4
|
||||
} as Margin,
|
||||
legend: {
|
||||
// Translucent background, so chart data is still visible if legend overlaps it.
|
||||
bgcolor: "#FFFFFF80",
|
||||
},
|
||||
yaxis,
|
||||
xaxis,
|
||||
...(options.stacked ? {barmode: 'relative'} : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The grainjs component for side-pane configuration options for a Chart section.
|
||||
*/
|
||||
@@ -1257,7 +1278,7 @@ const cssRowLabel = styled('div', `
|
||||
margin-right: 8px;
|
||||
|
||||
font-weight: initial; /* negate bootstrap */
|
||||
color: ${colors.dark};
|
||||
color: ${theme.text};
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
user-select: none;
|
||||
@@ -1265,7 +1286,7 @@ const cssRowLabel = styled('div', `
|
||||
|
||||
const cssRowHelp = styled(cssRow, `
|
||||
font-size: ${vars.smallFontSize};
|
||||
color: ${colors.slate};
|
||||
color: ${theme.lightText};
|
||||
`);
|
||||
|
||||
const cssAddIcon = styled(icon, `
|
||||
@@ -1275,15 +1296,15 @@ const cssAddIcon = styled(icon, `
|
||||
const cssAddYAxis = styled('div', `
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
color: ${colors.lightGreen};
|
||||
--icon-color: ${colors.lightGreen};
|
||||
color: ${theme.controlFg};
|
||||
--icon-color: ${theme.controlFg};
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 8px;
|
||||
}
|
||||
&:hover, &:focus, &:active {
|
||||
color: ${colors.darkGreen};
|
||||
--icon-color: ${colors.darkGreen};
|
||||
color: ${theme.controlHoverFg};
|
||||
--icon-color: ${theme.controlHoverFg};
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -1299,7 +1320,7 @@ const cssRemoveIcon = styled(icon, `
|
||||
|
||||
const cssHintRow = styled('div', `
|
||||
margin: -4px 16px 8px 16px;
|
||||
color: ${colors.slate};
|
||||
color: ${theme.lightText};
|
||||
`);
|
||||
|
||||
const cssRangeInput = styled('input', `
|
||||
|
||||
Reference in New Issue
Block a user