mirror of
https://github.com/gristlabs/grist-core.git
synced 2026-03-02 04:09:24 +00:00
(core) Adding font options to the style picker
Summary: Redesigning color picker: - Single color palette (no light/dark switch) - Ability to remove color (new empty button) New font options in the color picker. Font options are available on: - Default cell style - Conditional rules styles - Choice/ChoiceList editor and token field - Filters for Choice/ChoiceList columns Design document: https://www.figma.com/file/bRTsb47VIOVBfJPj0qF3C9/Grist-Updates?node-id=415%3A8135 Test Plan: new and updated tests Reviewers: georgegevoian, alexmojaki Reviewed By: georgegevoian, alexmojaki Subscribers: alexmojaki Differential Revision: https://phab.getgrist.com/D3335
This commit is contained in:
@@ -14,6 +14,7 @@ function AbstractWidget(field, opts = {}) {
|
||||
const {defaultTextColor = '#000000'} = opts;
|
||||
this.defaultTextColor = defaultTextColor;
|
||||
this.valueFormatter = this.field.visibleColFormatter;
|
||||
this.defaultTextColor = opts.defaultTextColor || '#000000';
|
||||
}
|
||||
dispose.makeDisposable(AbstractWidget);
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import {Style} from 'app/client/models/Styles';
|
||||
import {cssFieldFormula} from 'app/client/ui/FieldConfig';
|
||||
import {cssIcon, cssLabel, cssRow} from 'app/client/ui/RightPanel';
|
||||
import {textButton} from 'app/client/ui2018/buttons';
|
||||
import {colorSelect} from 'app/client/ui2018/ColorSelect';
|
||||
import {ColorOption, colorSelect} from 'app/client/ui2018/ColorSelect';
|
||||
import {colors} from 'app/client/ui2018/cssVars';
|
||||
import {setupEditorCleanup} from 'app/client/widgets/FieldEditor';
|
||||
import {cssError, openFormulaEditor} from 'app/client/widgets/FormulaEditor';
|
||||
@@ -18,8 +18,12 @@ import debounce = require('lodash/debounce');
|
||||
const testId = makeTestId('test-widget-style-');
|
||||
|
||||
export class CellStyle extends Disposable {
|
||||
protected textColor: Observable<string>;
|
||||
protected fillColor: Observable<string>;
|
||||
protected textColor: Observable<string|undefined>;
|
||||
protected fillColor: Observable<string|undefined>;
|
||||
protected fontBold: Observable<boolean|undefined>;
|
||||
protected fontUnderline: Observable<boolean|undefined>;
|
||||
protected fontItalic: Observable<boolean|undefined>;
|
||||
protected fontStrikethrough: Observable<boolean|undefined>;
|
||||
// Holds data from currently selected record (holds data only when this field has conditional styles).
|
||||
protected currentRecord: Computed<RowRecord | undefined>;
|
||||
// Helper field for refreshing current record data.
|
||||
@@ -28,14 +32,15 @@ export class CellStyle extends Disposable {
|
||||
constructor(
|
||||
protected field: ViewFieldRec,
|
||||
protected gristDoc: GristDoc,
|
||||
defaultTextColor: string = '#000000'
|
||||
protected defaultTextColor: string
|
||||
) {
|
||||
super();
|
||||
this.textColor = Computed.create(
|
||||
this,
|
||||
use => use(this.field.textColor) || defaultTextColor
|
||||
).onWrite(val => this.field.textColor(val === defaultTextColor ? '' : val));
|
||||
this.textColor = fromKo(this.field.textColor);
|
||||
this.fillColor = fromKo(this.field.fillColor);
|
||||
this.fontBold = fromKo(this.field.fontBold);
|
||||
this.fontUnderline = fromKo(this.field.fontUnderline);
|
||||
this.fontItalic = fromKo(this.field.fontItalic);
|
||||
this.fontStrikethrough = fromKo(this.field.fontStrikethrough);
|
||||
this.currentRecord = Computed.create(this, use => {
|
||||
if (!use(this.field.hasRules)) {
|
||||
return;
|
||||
@@ -75,8 +80,14 @@ export class CellStyle extends Disposable {
|
||||
cssLabel('CELL STYLE', dom.autoDispose(holder)),
|
||||
cssRow(
|
||||
colorSelect(
|
||||
this.textColor,
|
||||
this.fillColor,
|
||||
{
|
||||
textColor: new ColorOption(this.textColor, false, this.defaultTextColor),
|
||||
fillColor: new ColorOption(this.fillColor, true, '', 'none', '#FFFFFF'),
|
||||
fontBold: this.fontBold,
|
||||
fontItalic: this.fontItalic,
|
||||
fontUnderline: this.fontUnderline,
|
||||
fontStrikethrough: this.fontStrikethrough
|
||||
},
|
||||
// Calling `field.widgetOptionsJson.save()` saves both fill and text color settings.
|
||||
() => this.field.widgetOptionsJson.save()
|
||||
)
|
||||
@@ -98,6 +109,10 @@ export class CellStyle extends Disposable {
|
||||
...rules.map((column, ruleIndex) => {
|
||||
const textColor = this._buildStyleOption(owner, ruleIndex, 'textColor');
|
||||
const fillColor = this._buildStyleOption(owner, ruleIndex, 'fillColor');
|
||||
const fontBold = this._buildStyleOption(owner, ruleIndex, 'fontBold');
|
||||
const fontItalic = this._buildStyleOption(owner, ruleIndex, 'fontItalic');
|
||||
const fontUnderline = this._buildStyleOption(owner, ruleIndex, 'fontUnderline');
|
||||
const fontStrikethrough = this._buildStyleOption(owner, ruleIndex, 'fontStrikethrough');
|
||||
const save = async () => {
|
||||
// This will save both options.
|
||||
await this.field.rulesStyles.save();
|
||||
@@ -131,7 +146,18 @@ export class CellStyle extends Disposable {
|
||||
dom.show(hasError),
|
||||
testId(`rule-error-${ruleIndex}`),
|
||||
),
|
||||
colorSelect(textColor, fillColor, save, true)
|
||||
colorSelect(
|
||||
{
|
||||
textColor: new ColorOption(textColor, true, '', 'default'),
|
||||
fillColor: new ColorOption(fillColor, true, '', 'none'),
|
||||
fontBold,
|
||||
fontItalic,
|
||||
fontUnderline,
|
||||
fontStrikethrough
|
||||
},
|
||||
save,
|
||||
'Cell style'
|
||||
)
|
||||
),
|
||||
cssRemoveButton(
|
||||
'Remove',
|
||||
@@ -152,12 +178,12 @@ export class CellStyle extends Disposable {
|
||||
];
|
||||
}
|
||||
|
||||
private _buildStyleOption(owner: Disposable, index: number, option: keyof Style) {
|
||||
private _buildStyleOption<T extends keyof Style>(owner: Disposable, index: number, option: T) {
|
||||
const obs = Computed.create(owner, use => use(this.field.rulesStyles)[index]?.[option]);
|
||||
obs.onWrite(value => {
|
||||
const list = Array.from(this.field.rulesStyles.peek() ?? []);
|
||||
list[index] = list[index] ?? {};
|
||||
list[index][option] = value;
|
||||
list[index][option] = value as any;
|
||||
this.field.rulesStyles(list);
|
||||
});
|
||||
return obs;
|
||||
|
||||
@@ -12,7 +12,7 @@ import {csvEncodeRow} from 'app/common/csvFormat';
|
||||
import {CellValue} from "app/common/DocActions";
|
||||
import {decodeObject, encodeObject} from 'app/plugin/objtypes';
|
||||
import {dom, styled} from 'grainjs';
|
||||
import {ChoiceOptions, getFillColor, getTextColor} from 'app/client/widgets/ChoiceTextBox';
|
||||
import {ChoiceOptions, getRenderFillColor, getRenderTextColor} from 'app/client/widgets/ChoiceTextBox';
|
||||
import {choiceToken, cssChoiceACItem} from 'app/client/widgets/ChoiceToken';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
|
||||
@@ -73,8 +73,12 @@ export class ChoiceListEditor extends NewBaseEditor {
|
||||
initialValue: startTokens,
|
||||
renderToken: item => [
|
||||
item.label,
|
||||
dom.style('background-color', getFillColor(this._choiceOptionsByName[item.label])),
|
||||
dom.style('color', getTextColor(this._choiceOptionsByName[item.label])),
|
||||
dom.style('background-color', getRenderFillColor(this._choiceOptionsByName[item.label])),
|
||||
dom.style('color', getRenderTextColor(this._choiceOptionsByName[item.label])),
|
||||
dom.cls('font-bold', this._choiceOptionsByName[item.label]?.fontBold ?? false),
|
||||
dom.cls('font-underline', this._choiceOptionsByName[item.label]?.fontUnderline ?? false),
|
||||
dom.cls('font-italic', this._choiceOptionsByName[item.label]?.fontItalic ?? false),
|
||||
dom.cls('font-strikethrough', this._choiceOptionsByName[item.label]?.fontStrikethrough ?? false),
|
||||
cssInvalidToken.cls('-invalid', item.isInvalid)
|
||||
],
|
||||
createToken: label => new ChoiceItem(label, !choiceSet.has(label)),
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import {IToken, TokenField} from 'app/client/lib/TokenField';
|
||||
import {cssBlockedCursor} from 'app/client/ui/RightPanel';
|
||||
import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
|
||||
import {colorButton} from 'app/client/ui2018/ColorSelect';
|
||||
import {colorButton, ColorOption} from 'app/client/ui2018/ColorSelect';
|
||||
import {colors, testId} from 'app/client/ui2018/cssVars';
|
||||
import {editableLabel} from 'app/client/ui2018/editableLabel';
|
||||
import {icon} from 'app/client/ui2018/icons';
|
||||
import {ChoiceOptionsByName, IChoiceOptions} from 'app/client/widgets/ChoiceTextBox';
|
||||
import {DEFAULT_TEXT_COLOR} from 'app/client/widgets/ChoiceToken';
|
||||
import {Computed, Disposable, dom, DomContents, DomElementArg, Holder, Observable, styled} from 'grainjs';
|
||||
import {createCheckers, iface, ITypeSuite, opt} from 'ts-interface-checker';
|
||||
import {createCheckers, iface, ITypeSuite, opt, union} from 'ts-interface-checker';
|
||||
import isEqual = require('lodash/isEqual');
|
||||
import uniqBy = require('lodash/uniqBy');
|
||||
|
||||
@@ -33,7 +32,7 @@ class ChoiceItem implements IToken {
|
||||
public label: string,
|
||||
// We will keep the previous label value for a token, to tell us which token
|
||||
// was renamed. For new tokens this should be null.
|
||||
public readonly previousLabel: string | null,
|
||||
public previousLabel: string | null,
|
||||
public options?: IChoiceOptions
|
||||
) {}
|
||||
|
||||
@@ -41,19 +40,24 @@ class ChoiceItem implements IToken {
|
||||
return new ChoiceItem(label, this.previousLabel, this.options);
|
||||
}
|
||||
|
||||
public changeColors(options: IChoiceOptions) {
|
||||
public changeStyle(options: IChoiceOptions) {
|
||||
return new ChoiceItem(this.label, this.previousLabel, {...this.options, ...options});
|
||||
}
|
||||
}
|
||||
|
||||
const ChoiceItemType = iface([], {
|
||||
label: "string",
|
||||
previousLabel: union("string", "null"),
|
||||
options: opt("ChoiceOptionsType"),
|
||||
});
|
||||
|
||||
const ChoiceOptionsType = iface([], {
|
||||
textColor: "string",
|
||||
fillColor: "string",
|
||||
textColor: opt("string"),
|
||||
fillColor: opt("string"),
|
||||
fontBold: opt("boolean"),
|
||||
fontUnderline: opt("boolean"),
|
||||
fontItalic: opt("boolean"),
|
||||
fontStrikethrough: opt("boolean"),
|
||||
});
|
||||
|
||||
const choiceTypes: ITypeSuite = {
|
||||
@@ -63,8 +67,6 @@ const choiceTypes: ITypeSuite = {
|
||||
|
||||
const {ChoiceItemType: ChoiceItemChecker} = createCheckers(choiceTypes);
|
||||
|
||||
const UNSET_COLOR = '#ffffff';
|
||||
|
||||
/**
|
||||
* ChoiceListEntry - Editor for choices and choice colors.
|
||||
*
|
||||
@@ -166,17 +168,29 @@ export class ChoiceListEntry extends Disposable {
|
||||
dom.forEach(someValues, val => {
|
||||
return row(
|
||||
cssTokenColorInactive(
|
||||
dom.style('background-color', getFillColor(choiceOptions.get(val))),
|
||||
dom.style('background-color', getFillColor(choiceOptions.get(val)) || '#FFFFFF'),
|
||||
dom.style('color', getTextColor(choiceOptions.get(val)) || '#000000'),
|
||||
dom.cls('font-bold', choiceOptions.get(val)?.fontBold ?? false),
|
||||
dom.cls('font-underline', choiceOptions.get(val)?.fontUnderline ?? false),
|
||||
dom.cls('font-italic', choiceOptions.get(val)?.fontItalic ?? false),
|
||||
dom.cls('font-strikethrough', choiceOptions.get(val)?.fontStrikethrough ?? false),
|
||||
'T',
|
||||
testId('choice-list-entry-color')
|
||||
),
|
||||
cssTokenLabel(val)
|
||||
cssTokenLabel(
|
||||
val,
|
||||
testId('choice-list-entry-label')
|
||||
)
|
||||
);
|
||||
}),
|
||||
),
|
||||
// Show description row for any remaining rows
|
||||
dom.maybe(use => use(this._values).length > maxRows, () =>
|
||||
row(
|
||||
dom.text((use) => `+${use(this._values).length - (maxRows - 1)} more`)
|
||||
dom('span',
|
||||
testId('choice-list-entry-label'),
|
||||
dom.text((use) => `+${use(this._values).length - (maxRows - 1)} more`)
|
||||
)
|
||||
)
|
||||
),
|
||||
dom.on('click', () => this._startEditing()),
|
||||
@@ -215,12 +229,15 @@ export class ChoiceListEntry extends Disposable {
|
||||
const newTokens = uniqBy(tokens, t => t.label);
|
||||
const newValues = newTokens.map(t => t.label);
|
||||
const newOptions: ChoiceOptionsByName = new Map();
|
||||
const keys: Array<keyof IChoiceOptions> = [
|
||||
'fillColor', 'textColor', 'fontBold', 'fontItalic', 'fontStrikethrough', 'fontUnderline'
|
||||
];
|
||||
for (const t of newTokens) {
|
||||
if (t.options) {
|
||||
newOptions.set(t.label, {
|
||||
fillColor: t.options.fillColor,
|
||||
textColor: t.options.textColor
|
||||
});
|
||||
const options: IChoiceOptions = {};
|
||||
keys.filter(k => t.options![k] !== undefined)
|
||||
.forEach(k => options[k] = t.options![k] as any);
|
||||
newOptions.set(t.label, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,6 +262,10 @@ export class ChoiceListEntry extends Disposable {
|
||||
private _renderToken(token: ChoiceItem) {
|
||||
const fillColorObs = Observable.create(null, getFillColor(token.options));
|
||||
const textColorObs = Observable.create(null, getTextColor(token.options));
|
||||
const fontBoldObs = Observable.create(null, token.options?.fontBold);
|
||||
const fontItalicObs = Observable.create(null, token.options?.fontItalic);
|
||||
const fontUnderlineObs = Observable.create(null, token.options?.fontUnderline);
|
||||
const fontStrikethroughObs = Observable.create(null, token.options?.fontStrikethrough);
|
||||
const choiceText = Observable.create(null, token.label);
|
||||
|
||||
const rename = async (to: string) => {
|
||||
@@ -275,15 +296,32 @@ export class ChoiceListEntry extends Disposable {
|
||||
dom.autoDispose(fillColorObs),
|
||||
dom.autoDispose(textColorObs),
|
||||
dom.autoDispose(choiceText),
|
||||
colorButton(textColorObs,
|
||||
fillColorObs,
|
||||
colorButton({
|
||||
textColor: new ColorOption(textColorObs, false, '#000000'),
|
||||
fillColor: new ColorOption(fillColorObs, true, '', 'none', '#FFFFFF'),
|
||||
fontBold: fontBoldObs,
|
||||
fontItalic: fontItalicObs,
|
||||
fontUnderline: fontUnderlineObs,
|
||||
fontStrikethrough: fontStrikethroughObs
|
||||
},
|
||||
async () => {
|
||||
const tokenField = this._tokenFieldHolder.get();
|
||||
if (!tokenField) { return; }
|
||||
|
||||
const fillColor = fillColorObs.get();
|
||||
const textColor = textColorObs.get();
|
||||
tokenField.replaceToken(token.label, ChoiceItem.from(token).changeColors({fillColor, textColor}));
|
||||
const fontBold = fontBoldObs.get();
|
||||
const fontItalic = fontItalicObs.get();
|
||||
const fontUnderline = fontUnderlineObs.get();
|
||||
const fontStrikethrough = fontStrikethroughObs.get();
|
||||
tokenField.replaceToken(token.label, ChoiceItem.from(token).changeStyle({
|
||||
fillColor,
|
||||
textColor,
|
||||
fontBold,
|
||||
fontItalic,
|
||||
fontUnderline,
|
||||
fontStrikethrough,
|
||||
}));
|
||||
}
|
||||
),
|
||||
editableLabel(choiceText,
|
||||
@@ -320,11 +358,11 @@ function row(...domArgs: DomElementArg[]): Element {
|
||||
}
|
||||
|
||||
function getTextColor(choiceOptions?: IChoiceOptions) {
|
||||
return choiceOptions?.textColor ?? DEFAULT_TEXT_COLOR;
|
||||
return choiceOptions?.textColor;
|
||||
}
|
||||
|
||||
function getFillColor(choiceOptions?: IChoiceOptions) {
|
||||
return choiceOptions?.fillColor ?? UNSET_COLOR;
|
||||
return choiceOptions?.fillColor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,8 +374,9 @@ function getFillColor(choiceOptions?: IChoiceOptions) {
|
||||
function clipboardToChoices(clipboard: DataTransfer): ChoiceItem[] {
|
||||
const maybeTokens = clipboard.getData('application/json');
|
||||
if (maybeTokens && isJSON(maybeTokens)) {
|
||||
const tokens = JSON.parse(maybeTokens);
|
||||
const tokens: ChoiceItem[] = JSON.parse(maybeTokens);
|
||||
if (Array.isArray(tokens) && tokens.every((t): t is ChoiceItem => ChoiceItemChecker.test(t))) {
|
||||
tokens.forEach(t => t.previousLabel = null);
|
||||
return tokens;
|
||||
}
|
||||
}
|
||||
@@ -424,6 +463,8 @@ const cssTokenColorInactive = styled('div', `
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
`);
|
||||
|
||||
const cssTokenLabel = styled('span', `
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import {ChoiceListEntry} from 'app/client/widgets/ChoiceListEntry';
|
||||
import {DataRowModel} from 'app/client/models/DataRowModel';
|
||||
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||
import {KoSaveableObservable} from 'app/client/models/modelUtil';
|
||||
import {Style} from 'app/client/models/Styles';
|
||||
import {cssLabel, cssRow} from 'app/client/ui/RightPanel';
|
||||
import {testId} from 'app/client/ui2018/cssVars';
|
||||
import {ChoiceListEntry} from 'app/client/widgets/ChoiceListEntry';
|
||||
import {choiceToken, DEFAULT_FILL_COLOR, DEFAULT_TEXT_COLOR} from 'app/client/widgets/ChoiceToken';
|
||||
import {NTextBox} from 'app/client/widgets/NTextBox';
|
||||
import {Computed, dom, fromKo, styled} from 'grainjs';
|
||||
import {choiceToken, DEFAULT_FILL_COLOR, DEFAULT_TEXT_COLOR} from 'app/client/widgets/ChoiceToken';
|
||||
|
||||
export interface IChoiceOptions {
|
||||
textColor: string;
|
||||
fillColor: string;
|
||||
}
|
||||
|
||||
export type IChoiceOptions = Style
|
||||
export type ChoiceOptions = Record<string, IChoiceOptions | undefined>;
|
||||
export type ChoiceOptionsByName = Map<string, IChoiceOptions | undefined>;
|
||||
|
||||
export function getFillColor(choiceOptions?: IChoiceOptions) {
|
||||
export function getRenderFillColor(choiceOptions?: IChoiceOptions) {
|
||||
return choiceOptions?.fillColor ?? DEFAULT_FILL_COLOR;
|
||||
}
|
||||
|
||||
export function getTextColor(choiceOptions?: IChoiceOptions) {
|
||||
export function getRenderTextColor(choiceOptions?: IChoiceOptions) {
|
||||
return choiceOptions?.textColor ?? DEFAULT_TEXT_COLOR;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import {dom, DomContents, DomElementArg, styled} from "grainjs";
|
||||
import {colors, vars} from "app/client/ui2018/cssVars";
|
||||
import {Style} from 'app/client/models/Styles';
|
||||
|
||||
export const DEFAULT_FILL_COLOR = colors.mediumGreyOpaque.value;
|
||||
export const DEFAULT_TEXT_COLOR = '#000000';
|
||||
|
||||
export interface IChoiceTokenOptions {
|
||||
fillColor?: string;
|
||||
textColor?: string;
|
||||
}
|
||||
export type IChoiceTokenOptions = Style;
|
||||
|
||||
/**
|
||||
* Creates a colored token representing a choice (e.g. Choice and Choice List values).
|
||||
@@ -25,13 +23,17 @@ export interface IChoiceTokenOptions {
|
||||
*/
|
||||
export function choiceToken(
|
||||
label: DomElementArg,
|
||||
{fillColor, textColor}: IChoiceTokenOptions,
|
||||
{fillColor, textColor, fontBold, fontItalic, fontUnderline, fontStrikethrough}: IChoiceTokenOptions,
|
||||
...args: DomElementArg[]
|
||||
): DomContents {
|
||||
return cssChoiceToken(
|
||||
label,
|
||||
dom.style('background-color', fillColor ?? DEFAULT_FILL_COLOR),
|
||||
dom.style('color', textColor ?? DEFAULT_TEXT_COLOR),
|
||||
dom.cls('font-bold', fontBold ?? false),
|
||||
dom.cls('font-underline', fontUnderline ?? false),
|
||||
dom.cls('font-italic', fontItalic ?? false),
|
||||
dom.cls('font-strikethrough', fontStrikethrough ?? false),
|
||||
...args
|
||||
);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,22 @@ function getTypeDefinition(type: string | false) {
|
||||
return UserType.typeDefs[type] || UserType.typeDefs.Text;
|
||||
}
|
||||
|
||||
type ComputedStyle = {style?: Style; error?: true} | null | undefined;
|
||||
/**
|
||||
* Builds a font option computed property.
|
||||
*/
|
||||
function buildFontOptions(
|
||||
builder: FieldBuilder,
|
||||
computedRule: ko.Computed<ComputedStyle>,
|
||||
optionName: keyof Style) {
|
||||
return koUtil.withKoUtils(ko.computed(function() {
|
||||
if (builder.isDisposed()) { return false; }
|
||||
const style = computedRule()?.style;
|
||||
const styleFlag = style?.[optionName] || this.field[optionName]();
|
||||
return styleFlag;
|
||||
}, builder)).onlyNotifyUnequal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of FieldBuilder. Used to create all column configuration DOMs, cell DOMs,
|
||||
* and cell editor DOMs for all Grist Types.
|
||||
@@ -427,7 +443,7 @@ export class FieldBuilder extends Disposable {
|
||||
// rules there is a brief moment that rule is still not evaluated
|
||||
// (rules.length != value.length), in this case return last value
|
||||
// and wait for the update.
|
||||
const computedRule = koUtil.withKoUtils(ko.pureComputed(() => {
|
||||
const computedRule = koUtil.withKoUtils(ko.pureComputed<ComputedStyle>(() => {
|
||||
if (this.isDisposed()) { return null; }
|
||||
const styles: Style[] = this.field.rulesStyles();
|
||||
// Make sure that rules where computed.
|
||||
@@ -469,12 +485,21 @@ export class FieldBuilder extends Disposable {
|
||||
return fromRules || this.field.textColor() || '';
|
||||
}, this)).onlyNotifyUnequal();
|
||||
|
||||
const background = koUtil.withKoUtils(ko.computed(function() {
|
||||
const fillColor = koUtil.withKoUtils(ko.computed(function() {
|
||||
if (this.isDisposed()) { return null; }
|
||||
const fromRules = computedRule()?.style?.fillColor;
|
||||
return fromRules || this.field.fillColor();
|
||||
let fill = fromRules || this.field.fillColor();
|
||||
// If user set white color - remove it to play nice with zebra strips.
|
||||
// If there is no color we are using fully transparent white color (for tests mainly).
|
||||
fill = fill ? fill.toUpperCase() : fill;
|
||||
return (fill === '#FFFFFF' ? '' : fill) || '#FFFFFF00';
|
||||
}, this)).onlyNotifyUnequal();
|
||||
|
||||
const fontBold = buildFontOptions(this, computedRule, 'fontBold');
|
||||
const fontItalic = buildFontOptions(this, computedRule, 'fontItalic');
|
||||
const fontUnderline = buildFontOptions(this, computedRule, 'fontUnderline');
|
||||
const fontStrikethrough = buildFontOptions(this, computedRule, 'fontStrikethrough');
|
||||
|
||||
const errorInStyle = ko.pureComputed(() => Boolean(computedRule()?.error));
|
||||
|
||||
return (elem: Element) => {
|
||||
@@ -485,7 +510,11 @@ export class FieldBuilder extends Disposable {
|
||||
dom.autoDispose(errorInStyle),
|
||||
dom.autoDispose(textColor),
|
||||
dom.autoDispose(computedRule),
|
||||
dom.autoDispose(background),
|
||||
dom.autoDispose(fillColor),
|
||||
dom.autoDispose(fontBold),
|
||||
dom.autoDispose(fontItalic),
|
||||
dom.autoDispose(fontUnderline),
|
||||
dom.autoDispose(fontStrikethrough),
|
||||
this._options.isPreview ? null : kd.cssClass(this.field.formulaCssClass),
|
||||
kd.toggleClass("readonly", toKo(ko, this._readonly)),
|
||||
kd.maybe(isSelected, () => dom('div.selected_cursor',
|
||||
@@ -496,8 +525,12 @@ export class FieldBuilder extends Disposable {
|
||||
const cellDom = widget ? widget.buildDom(row) : buildErrorDom(row, this.field);
|
||||
return dom(cellDom, kd.toggleClass('has_cursor', isActive),
|
||||
kd.toggleClass('field-error-from-style', errorInStyle),
|
||||
kd.toggleClass('font-bold', fontBold),
|
||||
kd.toggleClass('font-underline', fontUnderline),
|
||||
kd.toggleClass('font-italic', fontItalic),
|
||||
kd.toggleClass('font-strikethrough', fontStrikethrough),
|
||||
kd.style('--grist-cell-color', textColor),
|
||||
kd.style('--grist-cell-background-color', background));
|
||||
kd.style('--grist-cell-background-color', fillColor));
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
@@ -42,10 +42,9 @@ export abstract class NewAbstractWidget extends Disposable {
|
||||
|
||||
constructor(protected field: ViewFieldRec, opts: Options = {}) {
|
||||
super();
|
||||
const {defaultTextColor = '#000000'} = opts;
|
||||
this.defaultTextColor = defaultTextColor;
|
||||
this.options = field.widgetOptionsJson;
|
||||
this.valueFormatter = fromKo(field.formatter);
|
||||
this.defaultTextColor = opts?.defaultTextColor || '#000000';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user