mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +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:
parent
98ac2f7e5b
commit
34708cd348
@ -223,9 +223,13 @@ export class ColumnTransform extends Disposable {
|
|||||||
[
|
[
|
||||||
'CopyFromColumn',
|
'CopyFromColumn',
|
||||||
this._tableData.tableId,
|
this._tableData.tableId,
|
||||||
this.transformColumn.colId(),
|
this.transformColumn.colId.peek(),
|
||||||
this.origColumn.colId(),
|
this.origColumn.colId.peek(),
|
||||||
JSON.stringify(this._fieldBuilder.options()),
|
// Get the options from builder rather the transforming columns.
|
||||||
|
// Those options are supposed to be set by prepTransformColInfo(TypeTransform) and
|
||||||
|
// adjusted by client.
|
||||||
|
// TODO: is this really needed? Aren't those options already in the data-engine?
|
||||||
|
JSON.stringify(this._fieldBuilder.options.peek()),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -15,12 +15,13 @@ import {dateTimeWidgetOptions, guessDateFormat} from 'app/common/parseDate';
|
|||||||
import {TableData} from 'app/common/TableData';
|
import {TableData} from 'app/common/TableData';
|
||||||
import {decodeObject} from 'app/plugin/objtypes';
|
import {decodeObject} from 'app/plugin/objtypes';
|
||||||
|
|
||||||
export interface ColInfo {
|
interface ColInfo {
|
||||||
type: string;
|
type: string;
|
||||||
isFormula: boolean;
|
isFormula: boolean;
|
||||||
formula: string;
|
formula: string;
|
||||||
visibleCol: number;
|
visibleCol: number;
|
||||||
widgetOptions?: string;
|
widgetOptions?: string;
|
||||||
|
rules: gristTypes.RefListValue
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,18 +88,21 @@ export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRe
|
|||||||
const toType = gristTypes.extractTypeFromColType(toTypeMaybeFull);
|
const toType = gristTypes.extractTypeFromColType(toTypeMaybeFull);
|
||||||
const tableData: TableData = docModel.docData.getTable(origCol.table().tableId())!;
|
const tableData: TableData = docModel.docData.getTable(origCol.table().tableId())!;
|
||||||
let widgetOptions: any = null;
|
let widgetOptions: any = null;
|
||||||
|
let rules: gristTypes.RefListValue = null;
|
||||||
|
|
||||||
const colInfo: ColInfo = {
|
const colInfo: ColInfo = {
|
||||||
type: addColTypeSuffix(toTypeMaybeFull, origCol, docModel),
|
type: addColTypeSuffix(toTypeMaybeFull, origCol, docModel),
|
||||||
isFormula: true,
|
isFormula: true,
|
||||||
visibleCol: 0,
|
visibleCol: 0,
|
||||||
formula: "CURRENT_CONVERSION(rec)",
|
formula: "CURRENT_CONVERSION(rec)",
|
||||||
|
rules,
|
||||||
};
|
};
|
||||||
|
|
||||||
const visibleCol = origCol.visibleColModel();
|
const visibleCol = origCol.visibleColModel();
|
||||||
// Column used to derive previous widget options and sample values for guessing
|
// Column used to derive previous widget options and sample values for guessing
|
||||||
const sourceCol = visibleCol.getRowId() !== 0 ? visibleCol : origCol;
|
const sourceCol = visibleCol.getRowId() !== 0 ? visibleCol : origCol;
|
||||||
const prevOptions = sourceCol.widgetOptionsJson.peek() || {};
|
const prevOptions = sourceCol.widgetOptionsJson.peek() || {};
|
||||||
|
const prevRules = sourceCol.rules.peek();
|
||||||
switch (toType) {
|
switch (toType) {
|
||||||
case 'Date':
|
case 'Date':
|
||||||
case 'DateTime': {
|
case 'DateTime': {
|
||||||
@ -112,12 +116,13 @@ export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRe
|
|||||||
}
|
}
|
||||||
case 'Numeric':
|
case 'Numeric':
|
||||||
case 'Int': {
|
case 'Int': {
|
||||||
if (["Numeric", "Int"].includes(sourceCol.type())) {
|
if (!["Numeric", "Int"].includes(sourceCol.type())) {
|
||||||
widgetOptions = prevOptions;
|
|
||||||
} else {
|
|
||||||
const numberParse = NumberParse.fromSettings(docModel.docData.docSettings());
|
const numberParse = NumberParse.fromSettings(docModel.docData.docSettings());
|
||||||
const colValues = tableData.getColValues(sourceCol.colId()) || [];
|
const colValues = tableData.getColValues(sourceCol.colId()) || [];
|
||||||
widgetOptions = numberParse.guessOptions(colValues.filter(isString));
|
widgetOptions = numberParse.guessOptions(colValues.filter(isString));
|
||||||
|
} else {
|
||||||
|
widgetOptions = prevOptions;
|
||||||
|
rules = prevRules;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -202,6 +207,9 @@ export async function prepTransformColInfo(docModel: DocModel, origCol: ColumnRe
|
|||||||
if (widgetOptions) {
|
if (widgetOptions) {
|
||||||
colInfo.widgetOptions = JSON.stringify(widgetOptions);
|
colInfo.widgetOptions = JSON.stringify(widgetOptions);
|
||||||
}
|
}
|
||||||
|
if (rules) {
|
||||||
|
colInfo.rules = rules;
|
||||||
|
}
|
||||||
return colInfo;
|
return colInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,11 +91,19 @@ export class TypeTransform extends ColumnTransform {
|
|||||||
protected async addTransformColumn(toType: string) {
|
protected async addTransformColumn(toType: string) {
|
||||||
const docModel = this.gristDoc.docModel;
|
const docModel = this.gristDoc.docModel;
|
||||||
const colInfo = await TypeConversion.prepTransformColInfo(docModel, this.origColumn, this.origDisplayCol, toType);
|
const colInfo = await TypeConversion.prepTransformColInfo(docModel, this.origColumn, this.origDisplayCol, toType);
|
||||||
|
// NOTE: We could add rules with AddColumn action, but there are some optimizations that converts array values.
|
||||||
|
const rules = colInfo.rules;
|
||||||
|
delete colInfo.rules;
|
||||||
const newColInfos = await this._tableData.sendTableActions([
|
const newColInfos = await this._tableData.sendTableActions([
|
||||||
['AddColumn', 'gristHelper_Converted', {...colInfo, isFormula: false, formula: ''}],
|
['AddColumn', 'gristHelper_Converted', {...colInfo, isFormula: false, formula: ''}],
|
||||||
['AddColumn', 'gristHelper_Transform', colInfo],
|
['AddColumn', 'gristHelper_Transform', colInfo],
|
||||||
]);
|
]);
|
||||||
const transformColRef = newColInfos[1].colRef;
|
const transformColRef = newColInfos[1].colRef;
|
||||||
|
if (rules) {
|
||||||
|
await this.gristDoc.docData.sendActions([
|
||||||
|
['UpdateRecord', '_grist_Tables_column', transformColRef, { rules }]
|
||||||
|
]);
|
||||||
|
}
|
||||||
this.transformColumn = docModel.columns.getRowModel(transformColRef);
|
this.transformColumn = docModel.columns.getRowModel(transformColRef);
|
||||||
await this.convertValues();
|
await this.convertValues();
|
||||||
return transformColRef;
|
return transformColRef;
|
||||||
|
@ -36,7 +36,7 @@ import {createValidationRec, ValidationRec} from 'app/client/models/entities/Val
|
|||||||
import {createViewFieldRec, ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
import {createViewFieldRec, ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||||
import {createViewRec, ViewRec} from 'app/client/models/entities/ViewRec';
|
import {createViewRec, ViewRec} from 'app/client/models/entities/ViewRec';
|
||||||
import {createViewSectionRec, ViewSectionRec} from 'app/client/models/entities/ViewSectionRec';
|
import {createViewSectionRec, ViewSectionRec} from 'app/client/models/entities/ViewSectionRec';
|
||||||
import {GristObjCode} from 'app/plugin/GristData';
|
import {RefListValue} from 'app/common/gristTypes';
|
||||||
import {decodeObject} from 'app/plugin/objtypes';
|
import {decodeObject} from 'app/plugin/objtypes';
|
||||||
|
|
||||||
// Re-export all the entity types available. The recommended usage is like this:
|
// Re-export all the entity types available. The recommended usage is like this:
|
||||||
@ -97,7 +97,7 @@ export function refRecord<TRow extends MetaRowModel>(
|
|||||||
return ko.pureComputed(() => tableModel.getRowModel(rowIdObs() || 0, true));
|
return ko.pureComputed(() => tableModel.getRowModel(rowIdObs() || 0, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
type RefListValue = [GristObjCode.List, ...number[]]|null;
|
|
||||||
/**
|
/**
|
||||||
* Returns an observable with a list of records from another table, selected using RefList column.
|
* Returns an observable with a list of records from another table, selected using RefList column.
|
||||||
* @param {TableModel} tableModel: The model for the table to return a record from.
|
* @param {TableModel} tableModel: The model for the table to return a record from.
|
||||||
|
@ -1,18 +1,34 @@
|
|||||||
export interface Style {
|
export interface Style {
|
||||||
textColor?: string;
|
textColor?: string;
|
||||||
fillColor?: string;
|
fillColor?: string;
|
||||||
|
fontBold?: boolean;
|
||||||
|
fontUnderline?: boolean;
|
||||||
|
fontItalic?: boolean;
|
||||||
|
fontStrikethrough?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CombinedStyle implements Style {
|
export class CombinedStyle implements Style {
|
||||||
public readonly textColor?: string;
|
public readonly textColor?: string;
|
||||||
public readonly fillColor?: string;
|
public readonly fillColor?: string;
|
||||||
constructor(rules: Style[], flags: any[]) {
|
public readonly fontBold?: boolean;
|
||||||
|
public readonly fontUnderline?: boolean;
|
||||||
|
public readonly fontItalic?: boolean;
|
||||||
|
public readonly fontStrikethrough?: boolean;
|
||||||
|
constructor(rules: (Style|undefined|null)[], flags: any[]) {
|
||||||
for (let i = 0; i < rules.length; i++) {
|
for (let i = 0; i < rules.length; i++) {
|
||||||
if (flags[i]) {
|
if (flags[i]) {
|
||||||
const textColor = rules[i].textColor;
|
const textColor = rules[i]?.textColor;
|
||||||
const fillColor = rules[i].fillColor;
|
const fillColor = rules[i]?.fillColor;
|
||||||
|
const fontBold = rules[i]?.fontBold;
|
||||||
|
const fontUnderline = rules[i]?.fontUnderline;
|
||||||
|
const fontItalic = rules[i]?.fontItalic;
|
||||||
|
const fontStrikethrough = rules[i]?.fontStrikethrough;
|
||||||
this.textColor = textColor || this.textColor;
|
this.textColor = textColor || this.textColor;
|
||||||
this.fillColor = fillColor || this.fillColor;
|
this.fillColor = fillColor || this.fillColor;
|
||||||
|
this.fontBold = fontBold || this.fontBold;
|
||||||
|
this.fontUnderline = fontUnderline || this.fontUnderline;
|
||||||
|
this.fontItalic = fontItalic || this.fontItalic;
|
||||||
|
this.fontStrikethrough = fontStrikethrough || this.fontStrikethrough;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,10 +67,11 @@ export interface ViewFieldRec extends IRowModel<"_grist_Views_section_field"> {
|
|||||||
disableEditData: ko.Computed<boolean>;
|
disableEditData: ko.Computed<boolean>;
|
||||||
|
|
||||||
textColor: modelUtil.KoSaveableObservable<string|undefined>;
|
textColor: modelUtil.KoSaveableObservable<string|undefined>;
|
||||||
fillColor: modelUtil.KoSaveableObservable<string>;
|
fillColor: modelUtil.KoSaveableObservable<string|undefined>;
|
||||||
|
fontBold: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||||
computedColor: ko.Computed<string|undefined>;
|
fontUnderline: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||||
computedFill: ko.Computed<string>;
|
fontItalic: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||||
|
fontStrikethrough: modelUtil.KoSaveableObservable<boolean|undefined>;
|
||||||
|
|
||||||
documentSettings: ko.PureComputed<DocumentSettings>;
|
documentSettings: ko.PureComputed<DocumentSettings>;
|
||||||
|
|
||||||
@ -227,16 +228,12 @@ export function createViewFieldRec(this: ViewFieldRec, docModel: DocModel): void
|
|||||||
this.disableModify = ko.pureComputed(() => this.column().disableModify());
|
this.disableModify = ko.pureComputed(() => this.column().disableModify());
|
||||||
this.disableEditData = ko.pureComputed(() => this.column().disableEditData());
|
this.disableEditData = ko.pureComputed(() => this.column().disableEditData());
|
||||||
|
|
||||||
this.textColor = this.widgetOptionsJson.prop('textColor') as modelUtil.KoSaveableObservable<string>;
|
this.textColor = this.widgetOptionsJson.prop('textColor');
|
||||||
|
this.fillColor = this.widgetOptionsJson.prop('fillColor');
|
||||||
const fillColorProp = modelUtil.fieldWithDefault(
|
this.fontBold = this.widgetOptionsJson.prop('fontBold');
|
||||||
this.widgetOptionsJson.prop('fillColor') as modelUtil.KoSaveableObservable<string>, "#FFFFFF00");
|
this.fontUnderline = this.widgetOptionsJson.prop('fontUnderline');
|
||||||
// Store empty string in place of the default white color, so that we can keep it transparent in
|
this.fontItalic = this.widgetOptionsJson.prop('fontItalic');
|
||||||
// GridView, to avoid interfering with zebra stripes.
|
this.fontStrikethrough = this.widgetOptionsJson.prop('fontStrikethrough');
|
||||||
this.fillColor = modelUtil.savingComputed({
|
|
||||||
read: () => fillColorProp(),
|
|
||||||
write: (setter, val) => setter(fillColorProp, val?.toUpperCase() === '#FFFFFF' ? '' : (val ?? '')),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.documentSettings = ko.pureComputed(() => docModel.docInfoRow.documentSettingsJson());
|
this.documentSettings = ko.pureComputed(() => docModel.docInfoRow.documentSettingsJson());
|
||||||
|
|
||||||
|
@ -378,6 +378,10 @@ function getRenderFunc(columnType: string, fieldOrColumn: ViewFieldRec|ColumnRec
|
|||||||
{
|
{
|
||||||
fillColor: choiceOptions[value.label]?.fillColor,
|
fillColor: choiceOptions[value.label]?.fillColor,
|
||||||
textColor: choiceOptions[value.label]?.textColor,
|
textColor: choiceOptions[value.label]?.textColor,
|
||||||
|
fontBold: choiceOptions[value.label]?.fontBold ?? false,
|
||||||
|
fontUnderline: choiceOptions[value.label]?.fontUnderline ?? false,
|
||||||
|
fontItalic: choiceOptions[value.label]?.fontItalic ?? false,
|
||||||
|
fontStrikethrough: choiceOptions[value.label]?.fontStrikethrough ?? false,
|
||||||
},
|
},
|
||||||
dom.cls(cssToken.className),
|
dom.cls(cssToken.className),
|
||||||
cssInvalidToken.cls('-invalid', !choiceSet.has(value.label)),
|
cssInvalidToken.cls('-invalid', !choiceSet.has(value.label)),
|
||||||
|
@ -1,68 +1,71 @@
|
|||||||
/*
|
/*
|
||||||
* The palettes were inspired by comparisons of a handful of popular services.
|
* The palettes were inspired by comparisons of a handful of popular services.
|
||||||
*/
|
*/
|
||||||
export const lighter = [
|
export const swatches = [
|
||||||
|
// white-black
|
||||||
"#FFFFFF",
|
"#FFFFFF",
|
||||||
"#DCDCDC",
|
"#DCDCDC",
|
||||||
"#B4B4B4",
|
"#888888",
|
||||||
|
"#000000",
|
||||||
|
|
||||||
|
// red
|
||||||
"#FECBCC",
|
"#FECBCC",
|
||||||
"#FD8182",
|
"#FD8182",
|
||||||
"#FC363B",
|
"#E00A17",
|
||||||
|
"#740206",
|
||||||
|
|
||||||
|
// brown
|
||||||
"#F3E1D2",
|
"#F3E1D2",
|
||||||
"#D6A77F",
|
"#D6A77F",
|
||||||
"#C37739",
|
"#AA632B",
|
||||||
|
"#653008",
|
||||||
|
|
||||||
|
// orange
|
||||||
"#FEE7C3",
|
"#FEE7C3",
|
||||||
"#FECC81",
|
"#FECC81",
|
||||||
"#FDA630",
|
"#FD9D28",
|
||||||
|
"#B36F19",
|
||||||
|
|
||||||
|
// yellow
|
||||||
"#FFFACD",
|
"#FFFACD",
|
||||||
"#FEF47A",
|
"#FEF47A",
|
||||||
"#FEEB36",
|
"#E8D62F",
|
||||||
|
"#928619",
|
||||||
|
|
||||||
|
// green
|
||||||
"#E1FEDE",
|
"#E1FEDE",
|
||||||
"#98FD90",
|
"#98FD90",
|
||||||
"#35FD31",
|
"#2AE028",
|
||||||
|
"#126E0E",
|
||||||
|
|
||||||
|
// light blue
|
||||||
"#CCFEFE",
|
"#CCFEFE",
|
||||||
"#8AFCFE",
|
"#8AFCFE",
|
||||||
"#2EF8FE",
|
"#24D6DB",
|
||||||
|
"#0C686A",
|
||||||
|
|
||||||
|
// dark blue
|
||||||
"#D3E7FE",
|
"#D3E7FE",
|
||||||
"#75B5FC",
|
"#75B5FC",
|
||||||
"#2486FB",
|
"#157AFB",
|
||||||
|
"#084794",
|
||||||
|
|
||||||
|
// violet
|
||||||
"#E8D0FE",
|
"#E8D0FE",
|
||||||
"#BC77FC",
|
"#BC77FC",
|
||||||
"#9633FB",
|
"#8725FB",
|
||||||
|
"#460D81",
|
||||||
|
|
||||||
|
// pink
|
||||||
"#FED6FB",
|
"#FED6FB",
|
||||||
"#FD79F4",
|
"#FD79F4",
|
||||||
"#FC2AED"
|
|
||||||
];
|
|
||||||
|
|
||||||
export const darker = [
|
|
||||||
"#888888",
|
|
||||||
"#414141",
|
|
||||||
"#000000",
|
|
||||||
"#E00A17",
|
|
||||||
"#B60610",
|
|
||||||
"#740206",
|
|
||||||
"#AA632B",
|
|
||||||
"#824617",
|
|
||||||
"#653008",
|
|
||||||
"#FD9D28",
|
|
||||||
"#E38D22",
|
|
||||||
"#B36F19",
|
|
||||||
"#E8D62F",
|
|
||||||
"#C0B225",
|
|
||||||
"#928619",
|
|
||||||
"#2AE028",
|
|
||||||
"#1FAA1C",
|
|
||||||
"#126E0E",
|
|
||||||
"#24D6DB",
|
|
||||||
"#189DA1",
|
|
||||||
"#0C686A",
|
|
||||||
"#157AFB",
|
|
||||||
"#0F64CF",
|
|
||||||
"#084794",
|
|
||||||
"#8725FB",
|
|
||||||
"#6318B8",
|
|
||||||
"#460D81",
|
|
||||||
"#E621D7",
|
"#E621D7",
|
||||||
"#B818AC",
|
|
||||||
"#760C6E"
|
"#760C6E"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells if swatch is a light color or dark (2 first are light 2 last are dark)
|
||||||
|
*/
|
||||||
|
export function isLight(index: number) {
|
||||||
|
return index % 4 <= 1;
|
||||||
|
}
|
||||||
|
@ -1,11 +1,39 @@
|
|||||||
import { darker, lighter } from "app/client/ui2018/ColorPalette";
|
import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
|
||||||
import { colors, testId, vars } from 'app/client/ui2018/cssVars';
|
import {isLight, swatches} from 'app/client/ui2018/ColorPalette';
|
||||||
import { textInput } from "app/client/ui2018/editableLabel";
|
import {colors, testId, vars} from 'app/client/ui2018/cssVars';
|
||||||
import { icon } from "app/client/ui2018/icons";
|
import {textInput} from 'app/client/ui2018/editableLabel';
|
||||||
import { isValidHex } from "app/common/gutil";
|
import {IconName} from 'app/client/ui2018/IconList';
|
||||||
import { cssSelectBtn } from 'app/client/ui2018/select';
|
import {icon} from 'app/client/ui2018/icons';
|
||||||
import { Computed, Disposable, dom, DomArg, Observable, onKeyDown, styled } from "grainjs";
|
import {cssSelectBtn} from 'app/client/ui2018/select';
|
||||||
import { defaultMenuOptions, IOpenController, setPopupToCreateDom } from "popweasel";
|
import {isValidHex} from 'app/common/gutil';
|
||||||
|
import {Computed, Disposable, dom, Observable, onKeyDown, styled} from 'grainjs';
|
||||||
|
import {defaultMenuOptions, IOpenController, setPopupToCreateDom} from 'popweasel';
|
||||||
|
|
||||||
|
export interface StyleOptions {
|
||||||
|
textColor: ColorOption,
|
||||||
|
fillColor: ColorOption,
|
||||||
|
fontBold: Observable<boolean|undefined>,
|
||||||
|
fontUnderline: Observable<boolean|undefined>,
|
||||||
|
fontItalic: Observable<boolean|undefined>,
|
||||||
|
fontStrikethrough: Observable<boolean|undefined>,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ColorOption {
|
||||||
|
constructor(
|
||||||
|
public color: Observable<string|undefined>,
|
||||||
|
// If the color accepts undefined/empty as a value. Controls empty selector in the picker.
|
||||||
|
public allowsNone: boolean = false,
|
||||||
|
// Default color to show when value is empty or undefined (itself can be empty).
|
||||||
|
public defaultColor: string = '',
|
||||||
|
// Text to be shown in the picker when color is not set.
|
||||||
|
public noneText: string = '',
|
||||||
|
// Preview color to show when value is undefined.
|
||||||
|
public previewNoneColor: string = '',) {
|
||||||
|
if (defaultColor && allowsNone) {
|
||||||
|
throw new Error("Allowing an empty value is not compatible with a default color");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* colorSelect allows to select color for both fill and text cell color. It allows for fast
|
* colorSelect allows to select color for both fill and text cell color. It allows for fast
|
||||||
@ -13,98 +41,118 @@ import { defaultMenuOptions, IOpenController, setPopupToCreateDom } from "popwea
|
|||||||
* native color picker. Pressing Escape reverts to the saved value. Caller is expected to handle
|
* native color picker. Pressing Escape reverts to the saved value. Caller is expected to handle
|
||||||
* logging of onSave() callback rejection. In case of rejection, values are reverted to their saved one.
|
* logging of onSave() callback rejection. In case of rejection, values are reverted to their saved one.
|
||||||
*/
|
*/
|
||||||
export function colorSelect(textColor: Observable<string|undefined>, fillColor: Observable<string|undefined>,
|
export function colorSelect(
|
||||||
onSave: () => Promise<void>, allowNone = false): Element {
|
styleOptions: StyleOptions,
|
||||||
|
onSave: () => Promise<void>,
|
||||||
|
placeholder = 'Default cell style'): Element {
|
||||||
|
const {
|
||||||
|
textColor,
|
||||||
|
fillColor,
|
||||||
|
} = styleOptions;
|
||||||
const selectBtn = cssSelectBtn(
|
const selectBtn = cssSelectBtn(
|
||||||
cssContent(
|
cssContent(
|
||||||
cssButtonIcon(
|
cssButtonIcon(
|
||||||
'T',
|
'T',
|
||||||
dom.style('color', use => use(textColor) || ''),
|
dom.style('color', use => use(textColor.color) || textColor.previewNoneColor),
|
||||||
dom.style('background-color', (use) => use(fillColor)?.slice(0, 7) || ''),
|
dom.style('background-color', (use) => use(fillColor.color)?.slice(0, 7) || fillColor.previewNoneColor),
|
||||||
|
dom.cls('font-bold', use => use(styleOptions.fontBold) ?? false),
|
||||||
|
dom.cls('font-italic', use => use(styleOptions.fontItalic) ?? false),
|
||||||
|
dom.cls('font-underline', use => use(styleOptions.fontUnderline) ?? false),
|
||||||
|
dom.cls('font-strikethrough', use => use(styleOptions.fontStrikethrough) ?? false),
|
||||||
cssLightBorder.cls(''),
|
cssLightBorder.cls(''),
|
||||||
testId('btn-icon'),
|
testId('btn-icon'),
|
||||||
),
|
),
|
||||||
'Cell Color',
|
placeholder,
|
||||||
),
|
),
|
||||||
icon('Dropdown'),
|
icon('Dropdown'),
|
||||||
testId('color-select'),
|
testId('color-select'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const domCreator = (ctl: IOpenController) => buildColorPicker(ctl, textColor, fillColor, onSave, allowNone);
|
const domCreator = (ctl: IOpenController) => buildColorPicker(ctl, styleOptions, onSave);
|
||||||
setPopupToCreateDom(selectBtn, domCreator, {...defaultMenuOptions, placement: 'bottom-end'});
|
setPopupToCreateDom(selectBtn, domCreator, {...defaultMenuOptions, placement: 'bottom-end'});
|
||||||
|
|
||||||
return selectBtn;
|
return selectBtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function colorButton(textColor: Observable<string|undefined>, fillColor: Observable<string|undefined>,
|
export function colorButton(
|
||||||
|
styleOptions: StyleOptions,
|
||||||
onSave: () => Promise<void>): Element {
|
onSave: () => Promise<void>): Element {
|
||||||
|
const { textColor, fillColor } = styleOptions;
|
||||||
const iconBtn = cssIconBtn(
|
const iconBtn = cssIconBtn(
|
||||||
icon(
|
'T',
|
||||||
'Dropdown',
|
dom.style('color', use => use(textColor.color) || textColor.previewNoneColor),
|
||||||
dom.style('background-color', use => use(textColor) || ''),
|
dom.style('background-color', (use) => use(fillColor.color)?.slice(0, 7) || fillColor.previewNoneColor),
|
||||||
testId('color-button-dropdown')
|
dom.cls('font-bold', use => use(styleOptions.fontBold) ?? false),
|
||||||
),
|
dom.cls('font-italic', use => use(styleOptions.fontItalic) ?? false),
|
||||||
dom.style('background-color', (use) => use(fillColor)?.slice(0, 7) || ''),
|
dom.cls('font-underline', use => use(styleOptions.fontUnderline) ?? false),
|
||||||
dom.on('click', (e) => { e.stopPropagation(); e.preventDefault(); }),
|
dom.cls('font-strikethrough', use => use(styleOptions.fontStrikethrough) ?? false),
|
||||||
testId('color-button'),
|
testId('color-button'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const domCreator = (ctl: IOpenController) => buildColorPicker(ctl, textColor, fillColor, onSave);
|
const domCreator = (ctl: IOpenController) => buildColorPicker(ctl, styleOptions, onSave);
|
||||||
setPopupToCreateDom(iconBtn, domCreator, { ...defaultMenuOptions, placement: 'bottom-end' });
|
setPopupToCreateDom(iconBtn, domCreator, { ...defaultMenuOptions, placement: 'bottom-end' });
|
||||||
|
|
||||||
return iconBtn;
|
return iconBtn;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildColorPicker(ctl: IOpenController, textColor: Observable<string|undefined>,
|
function buildColorPicker(ctl: IOpenController,
|
||||||
fillColor: Observable<string|undefined>,
|
{
|
||||||
onSave: () => Promise<void>,
|
textColor,
|
||||||
allowNone = false): Element {
|
fillColor,
|
||||||
const textColorModel = PickerModel.create(null, textColor);
|
fontBold,
|
||||||
const fillColorModel = PickerModel.create(null, fillColor);
|
fontUnderline,
|
||||||
|
fontItalic,
|
||||||
|
fontStrikethrough
|
||||||
|
}: StyleOptions,
|
||||||
|
onSave: () => Promise<void>): Element {
|
||||||
|
const textColorModel = ColorModel.create(null, textColor.color);
|
||||||
|
const fillColorModel = ColorModel.create(null, fillColor.color);
|
||||||
|
const fontBoldModel = BooleanModel.create(null, fontBold);
|
||||||
|
const fontUnderlineModel = BooleanModel.create(null, fontUnderline);
|
||||||
|
const fontItalicModel = BooleanModel.create(null, fontItalic);
|
||||||
|
const fontStrikethroughModel = BooleanModel.create(null, fontStrikethrough);
|
||||||
|
|
||||||
|
const models = [textColorModel, fillColorModel, fontBoldModel, fontUnderlineModel,
|
||||||
|
fontItalicModel, fontStrikethroughModel];
|
||||||
|
|
||||||
|
const notChanged = Computed.create(null, use => models.every(m => use(m.needsSaving) === false));
|
||||||
|
|
||||||
function revert() {
|
function revert() {
|
||||||
textColorModel.revert();
|
models.forEach(m => m.revert());
|
||||||
fillColorModel.revert();
|
|
||||||
ctl.close();
|
ctl.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctl.onDispose(async () => {
|
ctl.onDispose(async () => {
|
||||||
if (textColorModel.needsSaving() || fillColorModel.needsSaving()) {
|
if (!notChanged.get()) {
|
||||||
try {
|
try {
|
||||||
// TODO: disable the trigger btn while saving
|
// TODO: disable the trigger btn while saving
|
||||||
await onSave();
|
await onSave();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
/* Does no logging: onSave() callback is expected to handle their reporting */
|
/* Does no logging: onSave() callback is expected to handle their reporting */
|
||||||
textColorModel.revert();
|
models.forEach(m => m.revert());
|
||||||
fillColorModel.revert();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textColorModel.dispose();
|
models.forEach(m => m.dispose());
|
||||||
fillColorModel.dispose();
|
notChanged.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
const colorSquare = (...args: DomArg[]) => cssColorSquare(
|
|
||||||
...args,
|
|
||||||
dom.style('color', use => use(textColor) || ''),
|
|
||||||
dom.style('background-color', use => use(fillColor) || ''),
|
|
||||||
cssLightBorder.cls(''),
|
|
||||||
);
|
|
||||||
|
|
||||||
return cssContainer(
|
return cssContainer(
|
||||||
dom.create(PickerComponent, fillColorModel, {
|
dom.create(FontComponent, {
|
||||||
colorSquare: colorSquare(),
|
fontBoldModel,
|
||||||
title: 'fill',
|
fontUnderlineModel,
|
||||||
defaultMode: 'lighter',
|
fontItalicModel,
|
||||||
allowNone
|
fontStrikethroughModel,
|
||||||
}),
|
}),
|
||||||
cssVSpacer(),
|
cssVSpacer(),
|
||||||
dom.create(PickerComponent, textColorModel, {
|
dom.create(PickerComponent, textColorModel, {
|
||||||
colorSquare: colorSquare('T'),
|
|
||||||
title: 'text',
|
title: 'text',
|
||||||
defaultMode: 'darker',
|
...textColor
|
||||||
allowNone
|
}),
|
||||||
|
cssVSpacer(),
|
||||||
|
dom.create(PickerComponent, fillColorModel, {
|
||||||
|
title: 'fill',
|
||||||
|
...fillColor
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// gives focus and binds keydown events
|
// gives focus and binds keydown events
|
||||||
(elem: any) => { setTimeout(() => elem.focus(), 0); },
|
(elem: any) => { setTimeout(() => elem.focus(), 0); },
|
||||||
onKeyDown({
|
onKeyDown({
|
||||||
@ -112,36 +160,53 @@ function buildColorPicker(ctl: IOpenController, textColor: Observable<string|und
|
|||||||
Enter: () => { ctl.close(); },
|
Enter: () => { ctl.close(); },
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
cssButtonRow(
|
||||||
|
primaryButton('Apply',
|
||||||
|
dom.on('click', () => ctl.close()),
|
||||||
|
dom.boolAttr("disabled", notChanged),
|
||||||
|
testId('colors-save')
|
||||||
|
),
|
||||||
|
basicButton('Cancel',
|
||||||
|
dom.on('click', () => revert()),
|
||||||
|
testId('colors-cancel')
|
||||||
|
)
|
||||||
|
),
|
||||||
|
|
||||||
// Set focus when `focusout` is bubbling from a children element. This is to allow to receive
|
// Set focus when `focusout` is bubbling from a children element. This is to allow to receive
|
||||||
// keyboard event again after user interacted with the hex box text input.
|
// keyboard event again after user interacted with the hex box text input.
|
||||||
dom.on('focusout', (ev, elem) => (ev.target !== elem) && elem.focus()),
|
dom.on('focusout', (ev, elem) => (ev.target !== elem) && elem.focus()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PickerComponentOptions {
|
|
||||||
colorSquare: Element;
|
|
||||||
title: string;
|
|
||||||
defaultMode: 'darker'|'lighter';
|
|
||||||
allowNone?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// PickerModel is a helper model that helps keep track of the server value for an observable that
|
// PickerModel is a helper model that helps keep track of the server value for an observable that
|
||||||
// needs to be changed locally without saving. To use, you must call `model.setValue(...)` instead
|
// needs to be changed locally without saving. To use, you must call `model.setValue(...)` instead
|
||||||
// of `obs.set(...)`. Then it offers `model.needsSaving()` that tells you whether current value
|
// of `obs.set(...)`. Then it offers `model.needsSaving()` that tells you whether current value
|
||||||
// needs saving, and `model.revert()` that reverts obs to the its server value.
|
// needs saving, and `model.revert()` that reverts obs to the its server value.
|
||||||
class PickerModel extends Disposable {
|
class PickerModel<T extends boolean|string|undefined> extends Disposable {
|
||||||
private _serverValue = this.obs.get();
|
|
||||||
|
// Is current value different from the server value?
|
||||||
|
public needsSaving: Observable<boolean>;
|
||||||
|
private _serverValue: Observable<T>;
|
||||||
private _localChange: boolean = false;
|
private _localChange: boolean = false;
|
||||||
constructor(public obs: Observable<string|undefined>) {
|
constructor(public obs: Observable<T>) {
|
||||||
super();
|
super();
|
||||||
|
this._serverValue = Observable.create(this, this.obs.get());
|
||||||
|
this.needsSaving = Computed.create(this, use => {
|
||||||
|
const current = use(this.obs);
|
||||||
|
const server = use(this._serverValue);
|
||||||
|
// We support booleans and strings only for now, so if current is false and server
|
||||||
|
// is undefined, we assume they are the same.
|
||||||
|
// TODO: this probably should be a strategy method.
|
||||||
|
return current !== (typeof current === 'boolean' ? (server ?? false) : server);
|
||||||
|
});
|
||||||
this.autoDispose(this.obs.addListener((val) => {
|
this.autoDispose(this.obs.addListener((val) => {
|
||||||
if (this._localChange) { return; }
|
if (this._localChange) { return; }
|
||||||
this._serverValue = val;
|
this._serverValue.set(val);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the value picked by the user
|
// Set the value picked by the user
|
||||||
public setValue(val: string|undefined) {
|
public setValue(val: T) {
|
||||||
this._localChange = true;
|
this._localChange = true;
|
||||||
this.obs.set(val);
|
this.obs.set(val);
|
||||||
this._localChange = false;
|
this._localChange = false;
|
||||||
@ -149,31 +214,50 @@ class PickerModel extends Disposable {
|
|||||||
|
|
||||||
// Revert obs to its server value
|
// Revert obs to its server value
|
||||||
public revert() {
|
public revert() {
|
||||||
this.obs.set(this._serverValue);
|
this.obs.set(this._serverValue.get());
|
||||||
}
|
|
||||||
|
|
||||||
// Is current value different from the server value?
|
|
||||||
public needsSaving() {
|
|
||||||
return this.obs.get() !== this._serverValue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ColorModel extends PickerModel<string|undefined> {}
|
||||||
|
class BooleanModel extends PickerModel<boolean|undefined> {}
|
||||||
|
|
||||||
|
interface PickerComponentOptions {
|
||||||
|
title: string;
|
||||||
|
allowsNone: boolean;
|
||||||
|
// Default color to show when value is empty or undefined (itself can be empty).
|
||||||
|
defaultColor: string;
|
||||||
|
// Text to be shown in the picker when color is not set.
|
||||||
|
noneText: string;
|
||||||
|
// Preview color to show when value is undefined.
|
||||||
|
previewNoneColor: string;
|
||||||
|
}
|
||||||
class PickerComponent extends Disposable {
|
class PickerComponent extends Disposable {
|
||||||
|
|
||||||
private _color = Computed.create(this, this._model.obs, (use, val) => (val || '').toUpperCase().slice(0, 7));
|
private _color = Computed.create(this,
|
||||||
private _mode = Observable.create<'darker'|'lighter'>(this, this._guessMode());
|
this._model.obs,
|
||||||
|
(use, val) => (val || this._options.defaultColor).toUpperCase().slice(0, 7));
|
||||||
|
|
||||||
constructor(private _model: PickerModel, private _options: PickerComponentOptions) {
|
constructor(
|
||||||
|
private _model: PickerModel<string|undefined>,
|
||||||
|
private _options: PickerComponentOptions) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public buildDom() {
|
public buildDom() {
|
||||||
const title = this._options.title;
|
const title = this._options.title;
|
||||||
|
const colorText = Computed.create(null, use => use(this._color) || this._options.noneText);
|
||||||
return [
|
return [
|
||||||
cssHeaderRow(
|
cssHeaderRow(title),
|
||||||
|
cssControlRow(
|
||||||
cssColorPreview(
|
cssColorPreview(
|
||||||
dom.update(
|
dom.update(
|
||||||
this._options.colorSquare,
|
cssColorSquare(
|
||||||
|
cssLightBorder.cls(''),
|
||||||
|
dom.style('background-color', this._color),
|
||||||
|
cssNoneIcon('Empty',
|
||||||
|
dom.hide(use => Boolean(use(this._color)) === true)
|
||||||
|
),
|
||||||
|
),
|
||||||
cssColorInput(
|
cssColorInput(
|
||||||
{type: 'color'},
|
{type: 'color'},
|
||||||
dom.attr('value', this._color),
|
dom.attr('value', this._color),
|
||||||
@ -182,52 +266,38 @@ class PickerComponent extends Disposable {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
cssHexBox(
|
cssHexBox(
|
||||||
this._color,
|
colorText,
|
||||||
async (val) => {
|
async (val) => {
|
||||||
if ((this._options.allowNone && !val) || isValidHex(val)) {
|
if (!val || isValidHex(val)) {
|
||||||
this._model.setValue(val);
|
this._model.setValue(val || undefined);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
dom.autoDispose(colorText),
|
||||||
testId(`${title}-hex`),
|
testId(`${title}-hex`),
|
||||||
// select the hex value on click. Doing it using settimeout allows to avoid some
|
// select the hex value on click. Doing it using settimeout allows to avoid some
|
||||||
// sporadically losing the selection just after the click.
|
// sporadically losing the selection just after the click.
|
||||||
dom.on('click', (ev, elem) => setTimeout(() => elem.select(), 0)),
|
dom.on('click', (ev, elem) => setTimeout(() => elem.select(), 0)),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
title,
|
cssEmptyBox(
|
||||||
cssBrickToggle(
|
cssEmptyBox.cls('-selected', (use) => !use(this._color)),
|
||||||
cssBrick(
|
dom.on('click', () => this._setValue(undefined)),
|
||||||
cssBrick.cls('-selected', (use) => use(this._mode) === 'darker'),
|
dom.hide(!this._options.allowsNone),
|
||||||
dom.on('click', () => this._mode.set('darker')),
|
cssNoneIcon('Empty'),
|
||||||
testId(`${title}-darker-brick`),
|
testId(`${title}-empty`),
|
||||||
),
|
)
|
||||||
cssBrick(
|
|
||||||
cssBrick.cls('-lighter'),
|
|
||||||
cssBrick.cls('-selected', (use) => use(this._mode) === 'lighter'),
|
|
||||||
dom.on('click', () => this._mode.set('lighter')),
|
|
||||||
testId(`${title}-lighter-brick`),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
cssPalette(
|
cssPalette(
|
||||||
dom.domComputed(this._mode, (mode) => (mode === 'lighter' ? lighter : darker).map(color => (
|
swatches.map((color, index) => (
|
||||||
cssColorSquare(
|
cssColorSquare(
|
||||||
dom.style('background-color', color),
|
dom.style('background-color', color),
|
||||||
cssLightBorder.cls('', (use) => use(this._mode) === 'lighter'),
|
cssLightBorder.cls('', isLight(index)),
|
||||||
cssColorSquare.cls('-selected', (use) => use(this._color) === color),
|
cssColorSquare.cls('-selected', (use) => use(this._color) === color),
|
||||||
dom.style('outline-color', (use) => use(this._mode) === 'lighter' ? '' : color),
|
dom.style('outline-color', isLight(index) ? '' : color),
|
||||||
dom.on('click', () => {
|
dom.on('click', () => this._setValue(color)),
|
||||||
// Clicking same color twice - removes the selection.
|
|
||||||
if (this._model.obs.get() === color &&
|
|
||||||
this._options.allowNone) {
|
|
||||||
this._setValue(undefined);
|
|
||||||
} else {
|
|
||||||
this._setValue(color);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
testId(`color-${color}`),
|
testId(`color-${color}`),
|
||||||
)
|
)
|
||||||
))),
|
)),
|
||||||
testId(`${title}-palette`),
|
testId(`${title}-palette`),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@ -236,18 +306,61 @@ class PickerComponent extends Disposable {
|
|||||||
private _setValue(val: string|undefined) {
|
private _setValue(val: string|undefined) {
|
||||||
this._model.setValue(val);
|
this._model.setValue(val);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _guessMode(): 'darker'|'lighter' {
|
class FontComponent extends Disposable {
|
||||||
if (lighter.indexOf(this._color.get()) > -1) {
|
constructor(
|
||||||
return 'lighter';
|
private _options: {
|
||||||
|
fontBoldModel: BooleanModel,
|
||||||
|
fontUnderlineModel: BooleanModel,
|
||||||
|
fontItalicModel: BooleanModel,
|
||||||
|
fontStrikethroughModel: BooleanModel,
|
||||||
}
|
}
|
||||||
if (darker.indexOf(this._color.get()) > -1) {
|
) {
|
||||||
return 'darker';
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public buildDom() {
|
||||||
|
function option(iconName: IconName, model: BooleanModel) {
|
||||||
|
return cssFontOption(
|
||||||
|
cssFontIcon(iconName),
|
||||||
|
dom.on('click', () => model.setValue(!model.obs.get())),
|
||||||
|
cssFontOption.cls('-selected', use => use(model.obs) ?? false),
|
||||||
|
testId(`font-option-${iconName}`)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return this._options.defaultMode;
|
return cssFontOptions(
|
||||||
|
option('FontBold', this._options.fontBoldModel),
|
||||||
|
option('FontUnderline', this._options.fontUnderlineModel),
|
||||||
|
option('FontItalic', this._options.fontItalicModel),
|
||||||
|
option('FontStrikethrough', this._options.fontStrikethroughModel),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cssFontOptions = styled('div', `
|
||||||
|
display: flex;
|
||||||
|
gap: 1px;
|
||||||
|
background: ${colors.darkGrey};
|
||||||
|
border: 1px solid ${colors.darkGrey};
|
||||||
|
`);
|
||||||
|
|
||||||
|
const cssFontOption = styled('div', `
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
flex-grow: 1;
|
||||||
|
background: white;
|
||||||
|
height: 24px;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover:not(&-selected) {
|
||||||
|
background: ${colors.lightGrey};
|
||||||
|
}
|
||||||
|
&-selected {
|
||||||
|
background: ${colors.dark};
|
||||||
|
--icon-color: ${colors.light}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
const cssColorInput = styled('input', `
|
const cssColorInput = styled('input', `
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -259,39 +372,25 @@ const cssColorInput = styled('input', `
|
|||||||
border: none;
|
border: none;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssBrickToggle = styled('div', `
|
|
||||||
display: flex;
|
|
||||||
`);
|
|
||||||
|
|
||||||
const cssBrick = styled('div', `
|
|
||||||
height: 20px;
|
|
||||||
width: 34px;
|
|
||||||
background-color: #414141;
|
|
||||||
box-shadow: inset 0 0 0 2px #FFFFFF;
|
|
||||||
&-selected {
|
|
||||||
border: 1px solid #414141;
|
|
||||||
box-shadow: inset 0 0 0 1px #FFFFFF;
|
|
||||||
}
|
|
||||||
&-lighter {
|
|
||||||
background-color: #DCDCDC;
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
|
|
||||||
const cssColorPreview = styled('div', `
|
const cssColorPreview = styled('div', `
|
||||||
display: flex;
|
display: flex;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssHeaderRow = styled('div', `
|
const cssControlRow = styled('div', `
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
text-transform: capitalize;
|
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const cssHeaderRow = styled('div', `
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: ${vars.smallFontSize};
|
||||||
|
margin-bottom: 12px;
|
||||||
|
`);
|
||||||
|
|
||||||
const cssPalette = styled('div', `
|
const cssPalette = styled('div', `
|
||||||
width: 236px;
|
width: 236px;
|
||||||
height: 68px;
|
height: calc(4 * 20px + 3 * 4px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@ -300,7 +399,7 @@ const cssPalette = styled('div', `
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const cssVSpacer = styled('div', `
|
const cssVSpacer = styled('div', `
|
||||||
height: 12px;
|
height: 24px;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssContainer = styled('div', `
|
const cssContainer = styled('div', `
|
||||||
@ -320,7 +419,7 @@ const cssContent = styled('div', `
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const cssHexBox = styled(textInput, `
|
const cssHexBox = styled(textInput, `
|
||||||
border: 1px solid #D9D9D9;
|
border: 1px solid ${colors.darkGrey};
|
||||||
border-left: none;
|
border-left: none;
|
||||||
font-size: ${vars.smallFontSize};
|
font-size: ${vars.smallFontSize};
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -350,17 +449,42 @@ const cssColorSquare = styled('div', `
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const cssEmptyBox = styled(cssColorSquare, `
|
||||||
|
--icon-color: ${colors.error};
|
||||||
|
border: 1px solid #D9D9D9;
|
||||||
|
&-selected {
|
||||||
|
outline: 1px solid ${colors.dark};
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const cssFontIcon = styled(icon, `
|
||||||
|
height: 12px;
|
||||||
|
width: 12px;
|
||||||
|
`);
|
||||||
|
|
||||||
|
const cssNoneIcon = styled(icon, `
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
--icon-color: ${colors.error}
|
||||||
|
`);
|
||||||
|
|
||||||
const cssButtonIcon = styled(cssColorSquare, `
|
const cssButtonIcon = styled(cssColorSquare, `
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssIconBtn = styled('div', `
|
const cssIconBtn = styled(cssColorSquare, `
|
||||||
min-width: 18px;
|
min-width: 18px;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: grid;
|
||||||
align-items: center;
|
place-items: center;
|
||||||
justify-content: center;
|
`);
|
||||||
|
|
||||||
|
const cssButtonRow = styled('div', `
|
||||||
|
gap: 8px;
|
||||||
|
display: flex;
|
||||||
|
margin-top: 24px;
|
||||||
`);
|
`);
|
||||||
|
@ -51,6 +51,7 @@ export type IconName = "ChartArea" |
|
|||||||
"DragDrop" |
|
"DragDrop" |
|
||||||
"Dropdown" |
|
"Dropdown" |
|
||||||
"DropdownUp" |
|
"DropdownUp" |
|
||||||
|
"Empty" |
|
||||||
"Expand" |
|
"Expand" |
|
||||||
"EyeHide" |
|
"EyeHide" |
|
||||||
"EyeShow" |
|
"EyeShow" |
|
||||||
@ -58,6 +59,10 @@ export type IconName = "ChartArea" |
|
|||||||
"Filter" |
|
"Filter" |
|
||||||
"FilterSimple" |
|
"FilterSimple" |
|
||||||
"Folder" |
|
"Folder" |
|
||||||
|
"FontBold" |
|
||||||
|
"FontItalic" |
|
||||||
|
"FontStrikethrough" |
|
||||||
|
"FontUnderline" |
|
||||||
"FunctionResult" |
|
"FunctionResult" |
|
||||||
"Help" |
|
"Help" |
|
||||||
"Home" |
|
"Home" |
|
||||||
@ -168,6 +173,7 @@ export const IconList: IconName[] = ["ChartArea",
|
|||||||
"DragDrop",
|
"DragDrop",
|
||||||
"Dropdown",
|
"Dropdown",
|
||||||
"DropdownUp",
|
"DropdownUp",
|
||||||
|
"Empty",
|
||||||
"Expand",
|
"Expand",
|
||||||
"EyeHide",
|
"EyeHide",
|
||||||
"EyeShow",
|
"EyeShow",
|
||||||
@ -175,6 +181,10 @@ export const IconList: IconName[] = ["ChartArea",
|
|||||||
"Filter",
|
"Filter",
|
||||||
"FilterSimple",
|
"FilterSimple",
|
||||||
"Folder",
|
"Folder",
|
||||||
|
"FontBold",
|
||||||
|
"FontItalic",
|
||||||
|
"FontStrikethrough",
|
||||||
|
"FontUnderline",
|
||||||
"FunctionResult",
|
"FunctionResult",
|
||||||
"Help",
|
"Help",
|
||||||
"Home",
|
"Home",
|
||||||
|
@ -143,8 +143,27 @@ const cssInputFonts = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
// Font style classes used by style selector.
|
||||||
|
const cssFontStyles = `
|
||||||
|
.font-italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.font-bold {
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
.font-underline {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.font-strikethrough {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
.font-strikethrough.font-underline {
|
||||||
|
text-decoration: line-through underline;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
const cssVarsOnly = styled('div', cssColors + cssVars);
|
const cssVarsOnly = styled('div', cssColors + cssVars);
|
||||||
const cssBodyVars = styled('div', cssFontParams + cssColors + cssVars + cssBorderBox + cssInputFonts);
|
const cssBodyVars = styled('div', cssFontParams + cssColors + cssVars + cssBorderBox + cssInputFonts + cssFontStyles);
|
||||||
|
|
||||||
const cssBody = styled('body', `
|
const cssBody = styled('body', `
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -14,6 +14,7 @@ function AbstractWidget(field, opts = {}) {
|
|||||||
const {defaultTextColor = '#000000'} = opts;
|
const {defaultTextColor = '#000000'} = opts;
|
||||||
this.defaultTextColor = defaultTextColor;
|
this.defaultTextColor = defaultTextColor;
|
||||||
this.valueFormatter = this.field.visibleColFormatter;
|
this.valueFormatter = this.field.visibleColFormatter;
|
||||||
|
this.defaultTextColor = opts.defaultTextColor || '#000000';
|
||||||
}
|
}
|
||||||
dispose.makeDisposable(AbstractWidget);
|
dispose.makeDisposable(AbstractWidget);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import {Style} from 'app/client/models/Styles';
|
|||||||
import {cssFieldFormula} from 'app/client/ui/FieldConfig';
|
import {cssFieldFormula} from 'app/client/ui/FieldConfig';
|
||||||
import {cssIcon, cssLabel, cssRow} from 'app/client/ui/RightPanel';
|
import {cssIcon, cssLabel, cssRow} from 'app/client/ui/RightPanel';
|
||||||
import {textButton} from 'app/client/ui2018/buttons';
|
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 {colors} from 'app/client/ui2018/cssVars';
|
||||||
import {setupEditorCleanup} from 'app/client/widgets/FieldEditor';
|
import {setupEditorCleanup} from 'app/client/widgets/FieldEditor';
|
||||||
import {cssError, openFormulaEditor} from 'app/client/widgets/FormulaEditor';
|
import {cssError, openFormulaEditor} from 'app/client/widgets/FormulaEditor';
|
||||||
@ -18,8 +18,12 @@ import debounce = require('lodash/debounce');
|
|||||||
const testId = makeTestId('test-widget-style-');
|
const testId = makeTestId('test-widget-style-');
|
||||||
|
|
||||||
export class CellStyle extends Disposable {
|
export class CellStyle extends Disposable {
|
||||||
protected textColor: Observable<string>;
|
protected textColor: Observable<string|undefined>;
|
||||||
protected fillColor: Observable<string>;
|
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).
|
// Holds data from currently selected record (holds data only when this field has conditional styles).
|
||||||
protected currentRecord: Computed<RowRecord | undefined>;
|
protected currentRecord: Computed<RowRecord | undefined>;
|
||||||
// Helper field for refreshing current record data.
|
// Helper field for refreshing current record data.
|
||||||
@ -28,14 +32,15 @@ export class CellStyle extends Disposable {
|
|||||||
constructor(
|
constructor(
|
||||||
protected field: ViewFieldRec,
|
protected field: ViewFieldRec,
|
||||||
protected gristDoc: GristDoc,
|
protected gristDoc: GristDoc,
|
||||||
defaultTextColor: string = '#000000'
|
protected defaultTextColor: string
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.textColor = Computed.create(
|
this.textColor = fromKo(this.field.textColor);
|
||||||
this,
|
|
||||||
use => use(this.field.textColor) || defaultTextColor
|
|
||||||
).onWrite(val => this.field.textColor(val === defaultTextColor ? '' : val));
|
|
||||||
this.fillColor = fromKo(this.field.fillColor);
|
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 => {
|
this.currentRecord = Computed.create(this, use => {
|
||||||
if (!use(this.field.hasRules)) {
|
if (!use(this.field.hasRules)) {
|
||||||
return;
|
return;
|
||||||
@ -75,8 +80,14 @@ export class CellStyle extends Disposable {
|
|||||||
cssLabel('CELL STYLE', dom.autoDispose(holder)),
|
cssLabel('CELL STYLE', dom.autoDispose(holder)),
|
||||||
cssRow(
|
cssRow(
|
||||||
colorSelect(
|
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.
|
// Calling `field.widgetOptionsJson.save()` saves both fill and text color settings.
|
||||||
() => this.field.widgetOptionsJson.save()
|
() => this.field.widgetOptionsJson.save()
|
||||||
)
|
)
|
||||||
@ -98,6 +109,10 @@ export class CellStyle extends Disposable {
|
|||||||
...rules.map((column, ruleIndex) => {
|
...rules.map((column, ruleIndex) => {
|
||||||
const textColor = this._buildStyleOption(owner, ruleIndex, 'textColor');
|
const textColor = this._buildStyleOption(owner, ruleIndex, 'textColor');
|
||||||
const fillColor = this._buildStyleOption(owner, ruleIndex, 'fillColor');
|
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 () => {
|
const save = async () => {
|
||||||
// This will save both options.
|
// This will save both options.
|
||||||
await this.field.rulesStyles.save();
|
await this.field.rulesStyles.save();
|
||||||
@ -131,7 +146,18 @@ export class CellStyle extends Disposable {
|
|||||||
dom.show(hasError),
|
dom.show(hasError),
|
||||||
testId(`rule-error-${ruleIndex}`),
|
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(
|
cssRemoveButton(
|
||||||
'Remove',
|
'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]);
|
const obs = Computed.create(owner, use => use(this.field.rulesStyles)[index]?.[option]);
|
||||||
obs.onWrite(value => {
|
obs.onWrite(value => {
|
||||||
const list = Array.from(this.field.rulesStyles.peek() ?? []);
|
const list = Array.from(this.field.rulesStyles.peek() ?? []);
|
||||||
list[index] = list[index] ?? {};
|
list[index] = list[index] ?? {};
|
||||||
list[index][option] = value;
|
list[index][option] = value as any;
|
||||||
this.field.rulesStyles(list);
|
this.field.rulesStyles(list);
|
||||||
});
|
});
|
||||||
return obs;
|
return obs;
|
||||||
|
@ -12,7 +12,7 @@ import {csvEncodeRow} from 'app/common/csvFormat';
|
|||||||
import {CellValue} from "app/common/DocActions";
|
import {CellValue} from "app/common/DocActions";
|
||||||
import {decodeObject, encodeObject} from 'app/plugin/objtypes';
|
import {decodeObject, encodeObject} from 'app/plugin/objtypes';
|
||||||
import {dom, styled} from 'grainjs';
|
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 {choiceToken, cssChoiceACItem} from 'app/client/widgets/ChoiceToken';
|
||||||
import {icon} from 'app/client/ui2018/icons';
|
import {icon} from 'app/client/ui2018/icons';
|
||||||
|
|
||||||
@ -73,8 +73,12 @@ export class ChoiceListEditor extends NewBaseEditor {
|
|||||||
initialValue: startTokens,
|
initialValue: startTokens,
|
||||||
renderToken: item => [
|
renderToken: item => [
|
||||||
item.label,
|
item.label,
|
||||||
dom.style('background-color', getFillColor(this._choiceOptionsByName[item.label])),
|
dom.style('background-color', getRenderFillColor(this._choiceOptionsByName[item.label])),
|
||||||
dom.style('color', getTextColor(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)
|
cssInvalidToken.cls('-invalid', item.isInvalid)
|
||||||
],
|
],
|
||||||
createToken: label => new ChoiceItem(label, !choiceSet.has(label)),
|
createToken: label => new ChoiceItem(label, !choiceSet.has(label)),
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
import {IToken, TokenField} from 'app/client/lib/TokenField';
|
import {IToken, TokenField} from 'app/client/lib/TokenField';
|
||||||
import {cssBlockedCursor} from 'app/client/ui/RightPanel';
|
import {cssBlockedCursor} from 'app/client/ui/RightPanel';
|
||||||
import {basicButton, primaryButton} from 'app/client/ui2018/buttons';
|
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 {colors, testId} from 'app/client/ui2018/cssVars';
|
||||||
import {editableLabel} from 'app/client/ui2018/editableLabel';
|
import {editableLabel} from 'app/client/ui2018/editableLabel';
|
||||||
import {icon} from 'app/client/ui2018/icons';
|
import {icon} from 'app/client/ui2018/icons';
|
||||||
import {ChoiceOptionsByName, IChoiceOptions} from 'app/client/widgets/ChoiceTextBox';
|
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 {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 isEqual = require('lodash/isEqual');
|
||||||
import uniqBy = require('lodash/uniqBy');
|
import uniqBy = require('lodash/uniqBy');
|
||||||
|
|
||||||
@ -33,7 +32,7 @@ class ChoiceItem implements IToken {
|
|||||||
public label: string,
|
public label: string,
|
||||||
// We will keep the previous label value for a token, to tell us which token
|
// We will keep the previous label value for a token, to tell us which token
|
||||||
// was renamed. For new tokens this should be null.
|
// was renamed. For new tokens this should be null.
|
||||||
public readonly previousLabel: string | null,
|
public previousLabel: string | null,
|
||||||
public options?: IChoiceOptions
|
public options?: IChoiceOptions
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -41,19 +40,24 @@ class ChoiceItem implements IToken {
|
|||||||
return new ChoiceItem(label, this.previousLabel, this.options);
|
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});
|
return new ChoiceItem(this.label, this.previousLabel, {...this.options, ...options});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChoiceItemType = iface([], {
|
const ChoiceItemType = iface([], {
|
||||||
label: "string",
|
label: "string",
|
||||||
|
previousLabel: union("string", "null"),
|
||||||
options: opt("ChoiceOptionsType"),
|
options: opt("ChoiceOptionsType"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ChoiceOptionsType = iface([], {
|
const ChoiceOptionsType = iface([], {
|
||||||
textColor: "string",
|
textColor: opt("string"),
|
||||||
fillColor: "string",
|
fillColor: opt("string"),
|
||||||
|
fontBold: opt("boolean"),
|
||||||
|
fontUnderline: opt("boolean"),
|
||||||
|
fontItalic: opt("boolean"),
|
||||||
|
fontStrikethrough: opt("boolean"),
|
||||||
});
|
});
|
||||||
|
|
||||||
const choiceTypes: ITypeSuite = {
|
const choiceTypes: ITypeSuite = {
|
||||||
@ -63,8 +67,6 @@ const choiceTypes: ITypeSuite = {
|
|||||||
|
|
||||||
const {ChoiceItemType: ChoiceItemChecker} = createCheckers(choiceTypes);
|
const {ChoiceItemType: ChoiceItemChecker} = createCheckers(choiceTypes);
|
||||||
|
|
||||||
const UNSET_COLOR = '#ffffff';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ChoiceListEntry - Editor for choices and choice colors.
|
* ChoiceListEntry - Editor for choices and choice colors.
|
||||||
*
|
*
|
||||||
@ -166,17 +168,29 @@ export class ChoiceListEntry extends Disposable {
|
|||||||
dom.forEach(someValues, val => {
|
dom.forEach(someValues, val => {
|
||||||
return row(
|
return row(
|
||||||
cssTokenColorInactive(
|
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')
|
testId('choice-list-entry-color')
|
||||||
),
|
),
|
||||||
cssTokenLabel(val)
|
cssTokenLabel(
|
||||||
|
val,
|
||||||
|
testId('choice-list-entry-label')
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
// Show description row for any remaining rows
|
// Show description row for any remaining rows
|
||||||
dom.maybe(use => use(this._values).length > maxRows, () =>
|
dom.maybe(use => use(this._values).length > maxRows, () =>
|
||||||
row(
|
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()),
|
dom.on('click', () => this._startEditing()),
|
||||||
@ -215,12 +229,15 @@ export class ChoiceListEntry extends Disposable {
|
|||||||
const newTokens = uniqBy(tokens, t => t.label);
|
const newTokens = uniqBy(tokens, t => t.label);
|
||||||
const newValues = newTokens.map(t => t.label);
|
const newValues = newTokens.map(t => t.label);
|
||||||
const newOptions: ChoiceOptionsByName = new Map();
|
const newOptions: ChoiceOptionsByName = new Map();
|
||||||
|
const keys: Array<keyof IChoiceOptions> = [
|
||||||
|
'fillColor', 'textColor', 'fontBold', 'fontItalic', 'fontStrikethrough', 'fontUnderline'
|
||||||
|
];
|
||||||
for (const t of newTokens) {
|
for (const t of newTokens) {
|
||||||
if (t.options) {
|
if (t.options) {
|
||||||
newOptions.set(t.label, {
|
const options: IChoiceOptions = {};
|
||||||
fillColor: t.options.fillColor,
|
keys.filter(k => t.options![k] !== undefined)
|
||||||
textColor: t.options.textColor
|
.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) {
|
private _renderToken(token: ChoiceItem) {
|
||||||
const fillColorObs = Observable.create(null, getFillColor(token.options));
|
const fillColorObs = Observable.create(null, getFillColor(token.options));
|
||||||
const textColorObs = Observable.create(null, getTextColor(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 choiceText = Observable.create(null, token.label);
|
||||||
|
|
||||||
const rename = async (to: string) => {
|
const rename = async (to: string) => {
|
||||||
@ -275,15 +296,32 @@ export class ChoiceListEntry extends Disposable {
|
|||||||
dom.autoDispose(fillColorObs),
|
dom.autoDispose(fillColorObs),
|
||||||
dom.autoDispose(textColorObs),
|
dom.autoDispose(textColorObs),
|
||||||
dom.autoDispose(choiceText),
|
dom.autoDispose(choiceText),
|
||||||
colorButton(textColorObs,
|
colorButton({
|
||||||
fillColorObs,
|
textColor: new ColorOption(textColorObs, false, '#000000'),
|
||||||
|
fillColor: new ColorOption(fillColorObs, true, '', 'none', '#FFFFFF'),
|
||||||
|
fontBold: fontBoldObs,
|
||||||
|
fontItalic: fontItalicObs,
|
||||||
|
fontUnderline: fontUnderlineObs,
|
||||||
|
fontStrikethrough: fontStrikethroughObs
|
||||||
|
},
|
||||||
async () => {
|
async () => {
|
||||||
const tokenField = this._tokenFieldHolder.get();
|
const tokenField = this._tokenFieldHolder.get();
|
||||||
if (!tokenField) { return; }
|
if (!tokenField) { return; }
|
||||||
|
|
||||||
const fillColor = fillColorObs.get();
|
const fillColor = fillColorObs.get();
|
||||||
const textColor = textColorObs.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,
|
editableLabel(choiceText,
|
||||||
@ -320,11 +358,11 @@ function row(...domArgs: DomElementArg[]): Element {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getTextColor(choiceOptions?: IChoiceOptions) {
|
function getTextColor(choiceOptions?: IChoiceOptions) {
|
||||||
return choiceOptions?.textColor ?? DEFAULT_TEXT_COLOR;
|
return choiceOptions?.textColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFillColor(choiceOptions?: IChoiceOptions) {
|
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[] {
|
function clipboardToChoices(clipboard: DataTransfer): ChoiceItem[] {
|
||||||
const maybeTokens = clipboard.getData('application/json');
|
const maybeTokens = clipboard.getData('application/json');
|
||||||
if (maybeTokens && isJSON(maybeTokens)) {
|
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))) {
|
if (Array.isArray(tokens) && tokens.every((t): t is ChoiceItem => ChoiceItemChecker.test(t))) {
|
||||||
|
tokens.forEach(t => t.previousLabel = null);
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,6 +463,8 @@ const cssTokenColorInactive = styled('div', `
|
|||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
width: 18px;
|
width: 18px;
|
||||||
height: 18px;
|
height: 18px;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const cssTokenLabel = styled('span', `
|
const cssTokenLabel = styled('span', `
|
||||||
|
@ -1,26 +1,23 @@
|
|||||||
import {ChoiceListEntry} from 'app/client/widgets/ChoiceListEntry';
|
|
||||||
import {DataRowModel} from 'app/client/models/DataRowModel';
|
import {DataRowModel} from 'app/client/models/DataRowModel';
|
||||||
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
import {ViewFieldRec} from 'app/client/models/entities/ViewFieldRec';
|
||||||
import {KoSaveableObservable} from 'app/client/models/modelUtil';
|
import {KoSaveableObservable} from 'app/client/models/modelUtil';
|
||||||
|
import {Style} from 'app/client/models/Styles';
|
||||||
import {cssLabel, cssRow} from 'app/client/ui/RightPanel';
|
import {cssLabel, cssRow} from 'app/client/ui/RightPanel';
|
||||||
import {testId} from 'app/client/ui2018/cssVars';
|
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 {NTextBox} from 'app/client/widgets/NTextBox';
|
||||||
import {Computed, dom, fromKo, styled} from 'grainjs';
|
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 ChoiceOptions = Record<string, IChoiceOptions | undefined>;
|
||||||
export type ChoiceOptionsByName = Map<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;
|
return choiceOptions?.fillColor ?? DEFAULT_FILL_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTextColor(choiceOptions?: IChoiceOptions) {
|
export function getRenderTextColor(choiceOptions?: IChoiceOptions) {
|
||||||
return choiceOptions?.textColor ?? DEFAULT_TEXT_COLOR;
|
return choiceOptions?.textColor ?? DEFAULT_TEXT_COLOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
import {dom, DomContents, DomElementArg, styled} from "grainjs";
|
import {dom, DomContents, DomElementArg, styled} from "grainjs";
|
||||||
import {colors, vars} from "app/client/ui2018/cssVars";
|
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_FILL_COLOR = colors.mediumGreyOpaque.value;
|
||||||
export const DEFAULT_TEXT_COLOR = '#000000';
|
export const DEFAULT_TEXT_COLOR = '#000000';
|
||||||
|
|
||||||
export interface IChoiceTokenOptions {
|
export type IChoiceTokenOptions = Style;
|
||||||
fillColor?: string;
|
|
||||||
textColor?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a colored token representing a choice (e.g. Choice and Choice List values).
|
* Creates a colored token representing a choice (e.g. Choice and Choice List values).
|
||||||
@ -25,13 +23,17 @@ export interface IChoiceTokenOptions {
|
|||||||
*/
|
*/
|
||||||
export function choiceToken(
|
export function choiceToken(
|
||||||
label: DomElementArg,
|
label: DomElementArg,
|
||||||
{fillColor, textColor}: IChoiceTokenOptions,
|
{fillColor, textColor, fontBold, fontItalic, fontUnderline, fontStrikethrough}: IChoiceTokenOptions,
|
||||||
...args: DomElementArg[]
|
...args: DomElementArg[]
|
||||||
): DomContents {
|
): DomContents {
|
||||||
return cssChoiceToken(
|
return cssChoiceToken(
|
||||||
label,
|
label,
|
||||||
dom.style('background-color', fillColor ?? DEFAULT_FILL_COLOR),
|
dom.style('background-color', fillColor ?? DEFAULT_FILL_COLOR),
|
||||||
dom.style('color', textColor ?? DEFAULT_TEXT_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
|
...args
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,22 @@ function getTypeDefinition(type: string | false) {
|
|||||||
return UserType.typeDefs[type] || UserType.typeDefs.Text;
|
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,
|
* Creates an instance of FieldBuilder. Used to create all column configuration DOMs, cell DOMs,
|
||||||
* and cell editor DOMs for all Grist Types.
|
* 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 there is a brief moment that rule is still not evaluated
|
||||||
// (rules.length != value.length), in this case return last value
|
// (rules.length != value.length), in this case return last value
|
||||||
// and wait for the update.
|
// and wait for the update.
|
||||||
const computedRule = koUtil.withKoUtils(ko.pureComputed(() => {
|
const computedRule = koUtil.withKoUtils(ko.pureComputed<ComputedStyle>(() => {
|
||||||
if (this.isDisposed()) { return null; }
|
if (this.isDisposed()) { return null; }
|
||||||
const styles: Style[] = this.field.rulesStyles();
|
const styles: Style[] = this.field.rulesStyles();
|
||||||
// Make sure that rules where computed.
|
// Make sure that rules where computed.
|
||||||
@ -469,12 +485,21 @@ export class FieldBuilder extends Disposable {
|
|||||||
return fromRules || this.field.textColor() || '';
|
return fromRules || this.field.textColor() || '';
|
||||||
}, this)).onlyNotifyUnequal();
|
}, this)).onlyNotifyUnequal();
|
||||||
|
|
||||||
const background = koUtil.withKoUtils(ko.computed(function() {
|
const fillColor = koUtil.withKoUtils(ko.computed(function() {
|
||||||
if (this.isDisposed()) { return null; }
|
if (this.isDisposed()) { return null; }
|
||||||
const fromRules = computedRule()?.style?.fillColor;
|
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();
|
}, 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));
|
const errorInStyle = ko.pureComputed(() => Boolean(computedRule()?.error));
|
||||||
|
|
||||||
return (elem: Element) => {
|
return (elem: Element) => {
|
||||||
@ -485,7 +510,11 @@ export class FieldBuilder extends Disposable {
|
|||||||
dom.autoDispose(errorInStyle),
|
dom.autoDispose(errorInStyle),
|
||||||
dom.autoDispose(textColor),
|
dom.autoDispose(textColor),
|
||||||
dom.autoDispose(computedRule),
|
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),
|
this._options.isPreview ? null : kd.cssClass(this.field.formulaCssClass),
|
||||||
kd.toggleClass("readonly", toKo(ko, this._readonly)),
|
kd.toggleClass("readonly", toKo(ko, this._readonly)),
|
||||||
kd.maybe(isSelected, () => dom('div.selected_cursor',
|
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);
|
const cellDom = widget ? widget.buildDom(row) : buildErrorDom(row, this.field);
|
||||||
return dom(cellDom, kd.toggleClass('has_cursor', isActive),
|
return dom(cellDom, kd.toggleClass('has_cursor', isActive),
|
||||||
kd.toggleClass('field-error-from-style', errorInStyle),
|
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-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 = {}) {
|
constructor(protected field: ViewFieldRec, opts: Options = {}) {
|
||||||
super();
|
super();
|
||||||
const {defaultTextColor = '#000000'} = opts;
|
|
||||||
this.defaultTextColor = defaultTextColor;
|
|
||||||
this.options = field.widgetOptionsJson;
|
this.options = field.widgetOptionsJson;
|
||||||
this.valueFormatter = fromKo(field.formatter);
|
this.valueFormatter = fromKo(field.formatter);
|
||||||
|
this.defaultTextColor = opts?.defaultTextColor || '#000000';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,3 +346,5 @@ export function isValidRuleValue(value: CellValue|undefined) {
|
|||||||
// indicate other number in the future.
|
// indicate other number in the future.
|
||||||
return value === null || typeof value === 'boolean';
|
return value === null || typeof value === 'boolean';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RefListValue = [GristObjCode.List, ...number[]]|null;
|
||||||
|
@ -776,9 +776,10 @@ class UserActions(object):
|
|||||||
# Look at the actual data for that column (first 1000 values) to decide on the type.
|
# Look at the actual data for that column (first 1000 values) to decide on the type.
|
||||||
col_values['type'] = guess_type(self._get_column_values(col), convert=False)
|
col_values['type'] = guess_type(self._get_column_values(col), convert=False)
|
||||||
|
|
||||||
# If changing the type of a column, unset its widgetOptions and displayCol by default.
|
# If changing the type of a column, unset its widgetOptions, displayCol and rules by default.
|
||||||
if 'type' in col_values:
|
if 'type' in col_values:
|
||||||
col_values.setdefault('widgetOptions', '')
|
col_values.setdefault('widgetOptions', '')
|
||||||
|
col_values.setdefault('rules', None)
|
||||||
col_values.setdefault('displayCol', 0)
|
col_values.setdefault('displayCol', 0)
|
||||||
|
|
||||||
source_table = col.parentId.summarySourceTable
|
source_table = col.parentId.summarySourceTable
|
||||||
@ -1066,7 +1067,12 @@ class UserActions(object):
|
|||||||
for vf in self._docmodel.view_fields.lookupRecords(visibleCol=c.id)])
|
for vf in self._docmodel.view_fields.lookupRecords(visibleCol=c.id)])
|
||||||
|
|
||||||
# Remove also all autogenereted formula columns for conditional styles.
|
# Remove also all autogenereted formula columns for conditional styles.
|
||||||
more_removals.update([rule for col in col_recs
|
# But not from transform columns, as those columns borrow rules from original columns
|
||||||
|
more_removals.update([rule
|
||||||
|
for col in col_recs if not col.colId.startswith((
|
||||||
|
'gristHelper_Transform',
|
||||||
|
'gristHelper_Converted',
|
||||||
|
))
|
||||||
for rule in col.rules])
|
for rule in col.rules])
|
||||||
|
|
||||||
# Add any extra removals after removing the requested columns in the requested order.
|
# Add any extra removals after removing the requested columns in the requested order.
|
||||||
@ -1227,6 +1233,8 @@ class UserActions(object):
|
|||||||
'widgetOptions': col_info.get('widgetOptions', ''),
|
'widgetOptions': col_info.get('widgetOptions', ''),
|
||||||
'label': col_info.get('label', col_id),
|
'label': col_info.get('label', col_id),
|
||||||
})
|
})
|
||||||
|
if 'rules' in col_info:
|
||||||
|
values['rules'] = col_info['rules']
|
||||||
if 'recalcWhen' in col_info:
|
if 'recalcWhen' in col_info:
|
||||||
values['recalcWhen'] = col_info['recalcWhen']
|
values['recalcWhen'] = col_info['recalcWhen']
|
||||||
if 'recalcDeps' in col_info:
|
if 'recalcDeps' in col_info:
|
||||||
@ -1435,12 +1443,28 @@ class UserActions(object):
|
|||||||
if src_column.is_formula():
|
if src_column.is_formula():
|
||||||
self._engine.bring_col_up_to_date(src_column)
|
self._engine.bring_col_up_to_date(src_column)
|
||||||
|
|
||||||
# Update the destination column to match the source's type and options. Also unset displayCol,
|
# NOTE: This action is invoked only in a single place (during type/colum/data)
|
||||||
# except if src_col has a displayCol, then keep it unchanged until SetDisplayFormula below.
|
# transformation - where user has a chance to adjust some widgetOptions (though
|
||||||
|
# the UI is limited). Those widget options were already cleared (in js) and are either
|
||||||
|
# nullish (default ones) or are truly adjusted. As Grist doesn't know if the widgetOptions
|
||||||
|
# were adjusted or not - it will populate it on UI side and pass it here - so the code below
|
||||||
|
# is not used actually (widgetOptions are always set). But there are set with the things
|
||||||
|
# copied from dst_col or were cleared during typeConversion.
|
||||||
if widgetOptions is None:
|
if widgetOptions is None:
|
||||||
widgetOptions = src_col.widgetOptions
|
widgetOptions = src_col.widgetOptions
|
||||||
|
|
||||||
|
# Update the destination column to match the source's type and options. Also unset displayCol,
|
||||||
|
# except if src_col has a displayCol, then keep it unchanged until SetDisplayFormula below.
|
||||||
self._docmodel.update([dst_col], type=src_col.type, widgetOptions=[widgetOptions],
|
self._docmodel.update([dst_col], type=src_col.type, widgetOptions=[widgetOptions],
|
||||||
visibleCol=[src_col.visibleCol if src_col.visibleCol else 0],
|
visibleCol=[src_col.visibleCol if src_col.visibleCol else 0],
|
||||||
|
# TypeConversion (in js) has decided if rules should be copied or not. If yes, rules were
|
||||||
|
# copied to transforming column (it borrowed rules from us [us as dst_col]), in that case
|
||||||
|
# here is no-op. But it could also decide to clear rules, in that case here we will clear
|
||||||
|
# rules (as transforming column doesn't have it).
|
||||||
|
|
||||||
|
# RulesOptions (fonts, etc) are copied separately in the widgetOptions with the same
|
||||||
|
# logic (where removed or copied to the transforming column).
|
||||||
|
rules=[src_col.rules if src_col.rules else None],
|
||||||
displayCol=[dst_col.displayCol if src_col.displayCol else 0])
|
displayCol=[dst_col.displayCol if src_col.displayCol else 0])
|
||||||
|
|
||||||
# Copy over display column as well, if the source column has one.
|
# Copy over display column as well, if the source column has one.
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
--icon-DragDrop: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYuNSwzIEM2Ljc3NjE0MjM3LDMgNywzLjIyMzg1NzYzIDcsMy41IEw3LDEyLjUgQzcsMTIuNzc2MTQyNCA2Ljc3NjE0MjM3LDEzIDYuNSwxMyBDNi4yMjM4NTc2MywxMyA2LDEyLjc3NjE0MjQgNiwxMi41IEw2LDMuNSBDNiwzLjIyMzg1NzYzIDYuMjIzODU3NjMsMyA2LjUsMyBaIE05LjUsMyBDOS43NzYxNDIzNywzIDEwLDMuMjIzODU3NjMgMTAsMy41IEwxMCwxMi41IEMxMCwxMi43NzYxNDI0IDkuNzc2MTQyMzcsMTMgOS41LDEzIEM5LjIyMzg1NzYzLDEzIDksMTIuNzc2MTQyNCA5LDEyLjUgTDksMy41IEM5LDMuMjIzODU3NjMgOS4yMjM4NTc2MywzIDkuNSwzIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-DragDrop: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTYuNSwzIEM2Ljc3NjE0MjM3LDMgNywzLjIyMzg1NzYzIDcsMy41IEw3LDEyLjUgQzcsMTIuNzc2MTQyNCA2Ljc3NjE0MjM3LDEzIDYuNSwxMyBDNi4yMjM4NTc2MywxMyA2LDEyLjc3NjE0MjQgNiwxMi41IEw2LDMuNSBDNiwzLjIyMzg1NzYzIDYuMjIzODU3NjMsMyA2LjUsMyBaIE05LjUsMyBDOS43NzYxNDIzNywzIDEwLDMuMjIzODU3NjMgMTAsMy41IEwxMCwxMi41IEMxMCwxMi43NzYxNDI0IDkuNzc2MTQyMzcsMTMgOS41LDEzIEM5LjIyMzg1NzYzLDEzIDksMTIuNzc2MTQyNCA5LDEyLjUgTDksMy41IEM5LDMuMjIzODU3NjMgOS4yMjM4NTc2MywzIDkuNSwzIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
--icon-Dropdown: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsOS4xNzQ2MzA1MiBMMTAuOTIxODI3Myw2LjE4OTAyMzIgQzExLjE2ODQ3NDIsNS45MzY5OTIyNyAxMS41NjgzNjc5LDUuOTM2OTkyMjcgMTEuODE1MDE0OCw2LjE4OTAyMzIgQzEyLjA2MTY2MTcsNi40NDEwNTQxMyAxMi4wNjE2NjE3LDYuODQ5Njc3MDEgMTEuODE1MDE0OCw3LjEwMTcwNzk0IEw4LDExIEw0LjE4NDk4NTE5LDcuMTAxNzA3OTQgQzMuOTM4MzM4MjcsNi44NDk2NzcwMSAzLjkzODMzODI3LDYuNDQxMDU0MTMgNC4xODQ5ODUxOSw2LjE4OTAyMzIgQzQuNDMxNjMyMTEsNS45MzY5OTIyNyA0LjgzMTUyNTc4LDUuOTM2OTkyMjcgNS4wNzgxNzI3LDYuMTg5MDIzMiBMOCw5LjE3NDYzMDUyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-Dropdown: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsOS4xNzQ2MzA1MiBMMTAuOTIxODI3Myw2LjE4OTAyMzIgQzExLjE2ODQ3NDIsNS45MzY5OTIyNyAxMS41NjgzNjc5LDUuOTM2OTkyMjcgMTEuODE1MDE0OCw2LjE4OTAyMzIgQzEyLjA2MTY2MTcsNi40NDEwNTQxMyAxMi4wNjE2NjE3LDYuODQ5Njc3MDEgMTEuODE1MDE0OCw3LjEwMTcwNzk0IEw4LDExIEw0LjE4NDk4NTE5LDcuMTAxNzA3OTQgQzMuOTM4MzM4MjcsNi44NDk2NzcwMSAzLjkzODMzODI3LDYuNDQxMDU0MTMgNC4xODQ5ODUxOSw2LjE4OTAyMzIgQzQuNDMxNjMyMTEsNS45MzY5OTIyNyA0LjgzMTUyNTc4LDUuOTM2OTkyMjcgNS4wNzgxNzI3LDYuMTg5MDIzMiBMOCw5LjE3NDYzMDUyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
--icon-DropdownUp: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsOS4xNzQ2MzA1MiBMMTAuOTIxODI3Myw2LjE4OTAyMzIgQzExLjE2ODQ3NDIsNS45MzY5OTIyNyAxMS41NjgzNjc5LDUuOTM2OTkyMjcgMTEuODE1MDE0OCw2LjE4OTAyMzIgQzEyLjA2MTY2MTcsNi40NDEwNTQxMyAxMi4wNjE2NjE3LDYuODQ5Njc3MDEgMTEuODE1MDE0OCw3LjEwMTcwNzk0IEw4LDExIEw0LjE4NDk4NTE5LDcuMTAxNzA3OTQgQzMuOTM4MzM4MjcsNi44NDk2NzcwMSAzLjkzODMzODI3LDYuNDQxMDU0MTMgNC4xODQ5ODUxOSw2LjE4OTAyMzIgQzQuNDMxNjMyMTEsNS45MzY5OTIyNyA0LjgzMTUyNTc4LDUuOTM2OTkyMjcgNS4wNzgxNzI3LDYuMTg5MDIzMiBMOCw5LjE3NDYzMDUyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCAxNykiLz48L3N2Zz4=');
|
--icon-DropdownUp: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsOS4xNzQ2MzA1MiBMMTAuOTIxODI3Myw2LjE4OTAyMzIgQzExLjE2ODQ3NDIsNS45MzY5OTIyNyAxMS41NjgzNjc5LDUuOTM2OTkyMjcgMTEuODE1MDE0OCw2LjE4OTAyMzIgQzEyLjA2MTY2MTcsNi40NDEwNTQxMyAxMi4wNjE2NjE3LDYuODQ5Njc3MDEgMTEuODE1MDE0OCw3LjEwMTcwNzk0IEw4LDExIEw0LjE4NDk4NTE5LDcuMTAxNzA3OTQgQzMuOTM4MzM4MjcsNi44NDk2NzcwMSAzLjkzODMzODI3LDYuNDQxMDU0MTMgNC4xODQ5ODUxOSw2LjE4OTAyMzIgQzQuNDMxNjMyMTEsNS45MzY5OTIyNyA0LjgzMTUyNTc4LDUuOTM2OTkyMjcgNS4wNzgxNzI3LDYuMTg5MDIzMiBMOCw5LjE3NDYzMDUyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgdHJhbnNmb3JtPSJtYXRyaXgoMSAwIDAgLTEgMCAxNykiLz48L3N2Zz4=');
|
||||||
|
--icon-Empty: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMCIgaGVpZ2h0PSIyMCIgdmlld0JveD0iMCAwIDUuMjkyIDUuMjkyIj48cGF0aCBkPSJtIDAuMDMyNjIyNjMsMjkxLjcyMjI1IDUuMjMwMDMyMjcsNS4yNzY5IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAiIHN0cm9rZS13aWR0aD0iLjUyOSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAtMjkxLjcwOCkiLz48L3N2Zz4=');
|
||||||
--icon-Expand: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsOS4xNzQ2MzA1MiBMMTAuOTIxODI3Myw2LjE4OTAyMzIgQzExLjE2ODQ3NDIsNS45MzY5OTIyNyAxMS41NjgzNjc5LDUuOTM2OTkyMjcgMTEuODE1MDE0OCw2LjE4OTAyMzIgQzEyLjA2MTY2MTcsNi40NDEwNTQxMyAxMi4wNjE2NjE3LDYuODQ5Njc3MDEgMTEuODE1MDE0OCw3LjEwMTcwNzk0IEw4LDExIEw0LjE4NDk4NTE5LDcuMTAxNzA3OTQgQzMuOTM4MzM4MjcsNi44NDk2NzcwMSAzLjkzODMzODI3LDYuNDQxMDU0MTMgNC4xODQ5ODUxOSw2LjE4OTAyMzIgQzQuNDMxNjMyMTEsNS45MzY5OTIyNyA0LjgzMTUyNTc4LDUuOTM2OTkyMjcgNS4wNzgxNzI3LDYuMTg5MDIzMiBMOCw5LjE3NDYzMDUyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDggOC41KSIvPjwvc3ZnPg==');
|
--icon-Expand: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsOS4xNzQ2MzA1MiBMMTAuOTIxODI3Myw2LjE4OTAyMzIgQzExLjE2ODQ3NDIsNS45MzY5OTIyNyAxMS41NjgzNjc5LDUuOTM2OTkyMjcgMTEuODE1MDE0OCw2LjE4OTAyMzIgQzEyLjA2MTY2MTcsNi40NDEwNTQxMyAxMi4wNjE2NjE3LDYuODQ5Njc3MDEgMTEuODE1MDE0OCw3LjEwMTcwNzk0IEw4LDExIEw0LjE4NDk4NTE5LDcuMTAxNzA3OTQgQzMuOTM4MzM4MjcsNi44NDk2NzcwMSAzLjkzODMzODI3LDYuNDQxMDU0MTMgNC4xODQ5ODUxOSw2LjE4OTAyMzIgQzQuNDMxNjMyMTEsNS45MzY5OTIyNyA0LjgzMTUyNTc4LDUuOTM2OTkyMjcgNS4wNzgxNzI3LDYuMTg5MDIzMiBMOCw5LjE3NDYzMDUyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIgdHJhbnNmb3JtPSJyb3RhdGUoLTkwIDggOC41KSIvPjwvc3ZnPg==');
|
||||||
--icon-EyeHide: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzY3ODUwMDUsOC4zODc3NjE4NiBMOC40ODI0Nzg2NSwyLjk0NTI1ODQzIEM3LjY5MTM3ODkxLDIuMzM3Nzg0NzggNi44NTk1NjUyNywyIDYsMiBDNC42ODcxNTc3OSwyIDMuNDM5MDUzMDYsMi43ODc5NjU5MSAyLjMwMTg2ODgzLDQuMTIxMjgwMjIgQzEuODk1MDk5OTEsNC41OTgyMDQ1NSAxLjUzMjcyNzg0LDUuMTEwOTc5MTEgMS4yMjA1NDE3NCw1LjYyMzQxOTQ0IEMxLjEzMzk0MTAxLDUuNzY1NTcwOSAxLjA2MDI2MjE4LDUuODkyMzE1NDggMSw2IEMxLjA2MDI2MjE4LDYuMTA3Njg0NTIgMS4xMzM5NDEwMSw2LjIzNDQyOTEgMS4yMjA1NDE3NCw2LjM3NjU4MDU2IEMxLjUzMjcyNzg0LDYuODg5MDIwODkgMS44OTUwOTk5MSw3LjQwMTc5NTQ1IDIuMzAxODY4ODMsNy44Nzg3MTk3OCBDMi40NTUyMTY2Niw4LjA1ODUxNTQ5IDIuNjEwNTgxNDgsOC4yMjgzOTQ0OSAyLjc2Nzg1MDA1LDguMzg3NzYxODYgWiBNMy41MTc1MjEzNSw5LjA1NDc0MTU3IEM0LjMwODYyMTA5LDkuNjYyMjE1MjIgNS4xNDA0MzQ3MywxMCA2LDEwIEM3LjMxMjg0MjIxLDEwIDguNTYwOTQ2OTQsOS4yMTIwMzQwOSA5LjY5ODEzMTE3LDcuODc4NzE5NzggQzEwLjEwNDkwMDEsNy40MDE3OTU0NSAxMC40NjcyNzIyLDYuODg5MDIwODkgMTAuNzc5NDU4Myw2LjM3NjU4MDU2IEMxMC44NjYwNTksNi4yMzQ0MjkxIDEwLjkzOTczNzgsNi4xMDc2ODQ1MiAxMSw2IEMxMC45Mzk3Mzc4LDUuODkyMzE1NDggMTAuODY2MDU5LDUuNzY1NTcwOSAxMC43Nzk0NTgzLDUuNjIzNDE5NDQgQzEwLjQ2NzI3MjIsNS4xMTA5NzkxMSAxMC4xMDQ5MDAxLDQuNTk4MjA0NTUgOS42OTgxMzExNyw0LjEyMTI4MDIyIEM5LjU0NDc4MzM0LDMuOTQxNDg0NTEgOS4zODk0MTg1MiwzLjc3MTYwNTUxIDkuMjMyMTQ5OTUsMy42MTIyMzgxNCBMMy41MTc1MjEzNSw5LjA1NDc0MTU3IFogTTkuMTk4MDQ5MzEsMi4yNjM3NjI1NyBMMTAuOTA1MTcyNCwwLjYzNzkzMTAzNCBDMTEuMTA1MTM3NiwwLjQ0NzQ4ODAxNyAxMS40MjE2MjU5LDAuNDU1MjA3MjQ2IDExLjYxMjA2OSwwLjY1NTE3MjQxNCBDMTEuODAyNTEyLDAuODU1MTM3NTgyIDExLjc5NDc5MjgsMS4xNzE2MjU5NSAxMS41OTQ4Mjc2LDEuMzYyMDY4OTcgTDkuOTM1ODc1NzEsMi45NDIwMjMxMyBDMTAuMDcyNjIyNCwzLjA4MjQ0NjUzIDEwLjIwNzYxOTksMy4yMjk0MzM5IDEwLjM0MDg0MjYsMy4zODI2MzIwNSBDMTAuNzgzMjMyOSwzLjg5MTM1NDQ3IDExLjE3NDcyMzEsNC40MzQ2ODc1NSAxMS41MTI0MTU1LDQuOTc4MzQ0MDkgQzExLjcxNjM0MSw1LjMwNjY0NzE4IDExLjg1ODUzNjYsNS41NjUxMDg2MyAxMS45MzUyMjgsNS43MTk0NDIxMiBDMTIuMDIxNTkwNyw1Ljg5MzIzNzgyIDEyLjAyMTU5MDcsNi4xMDY3NjIxOCAxMS45MzUyMjgsNi4yODA1NTc4OCBDMTEuODU4NTM2Niw2LjQzNDg5MTM3IDExLjcxNjM0MSw2LjY5MzM1MjgyIDExLjUxMjQxNTUsNy4wMjE2NTU5MSBDMTEuMTc0NzIzMSw3LjU2NTMxMjQ1IDEwLjc4MzIzMjksOC4xMDg2NDU1MyAxMC4zNDA4NDI2LDguNjE3MzY3OTUgQzkuMDUyMTkwMjYsMTAuMDk5MjQxMiA3LjU5NzQ3MzE1LDExIDYsMTEgQzQuODU4MjE4ODYsMTEgMy43ODkzNjU1MiwxMC41Mzk4NDE5IDIuODAxOTUwNjksOS43MzYyMzc0MyBMMS4wOTQ4Mjc1OSwxMS4zNjIwNjkgQzAuODk0ODYyNDE4LDExLjU1MjUxMiAwLjU3ODM3NDA1MiwxMS41NDQ3OTI4IDAuMzg3OTMxMDM0LDExLjM0NDgyNzYgQzAuMTk3NDg4MDE3LDExLjE0NDg2MjQgMC4yMDUyMDcyNDYsMTAuODI4Mzc0MSAwLjQwNTE3MjQxNCwxMC42Mzc5MzEgTDIuMDY0MTI0MjksOS4wNTc5NzY4NyBDMS45MjczNzc2Myw4LjkxNzU1MzQ3IDEuNzkyMzgwMDcsOC43NzA1NjYxIDEuNjU5MTU3MzcsOC42MTczNjc5NSBDMS4yMTY3NjcwNyw4LjEwODY0NTUzIDAuODI1Mjc2OTI4LDcuNTY1MzEyNDUgMC40ODc1ODQ1MjEsNy4wMjE2NTU5MSBDMC4yODM2NTg5NjYsNi42OTMzNTI4MiAwLjE0MTQ2MzM3OCw2LjQzNDg5MTM3IDAuMDY0NzcxOTUwNCw2LjI4MDU1Nzg4IEMtMC4wMjE1OTA2NTAxLDYuMTA2NzYyMTggLTAuMDIxNTkwNjUwMSw1Ljg5MzIzNzgyIDAuMDY0NzcxOTUwNCw1LjcxOTQ0MjEyIEMwLjE0MTQ2MzM3OCw1LjU2NTEwODYzIDAuMjgzNjU4OTY2LDUuMzA2NjQ3MTggMC40ODc1ODQ1MjEsNC45NzgzNDQwOSBDMC44MjUyNzY5MjgsNC40MzQ2ODc1NSAxLjIxNjc2NzA3LDMuODkxMzU0NDcgMS42NTkxNTczNywzLjM4MjYzMjA1IEMyLjk0NzgwOTc0LDEuOTAwNzU4OCA0LjQwMjUyNjg1LDEgNiwxIEM3LjE0MTc4MTE0LDEgOC4yMTA2MzQ0OCwxLjQ2MDE1ODE0IDkuMTk4MDQ5MzEsMi4yNjM3NjI1NyBaIE00LjI1LDYgQzQuMjUsNi4yNzYxNDIzNyA0LjAyNjE0MjM3LDYuNSAzLjc1LDYuNSBDMy40NzM4NTc2Myw2LjUgMy4yNSw2LjI3NjE0MjM3IDMuMjUsNiBDMy4yNSw0LjUzNDY4MjU0IDQuNDg2NDE2NjcsMy4zNTcxNDI4NiA2LDMuMzU3MTQyODYgQzYuMjc2MTQyMzcsMy4zNTcxNDI4NiA2LjUsMy41ODEwMDA0OCA2LjUsMy44NTcxNDI4NiBDNi41LDQuMTMzMjg1MjMgNi4yNzYxNDIzNyw0LjM1NzE0Mjg2IDYsNC4zNTcxNDI4NiBDNS4wMjgwODMzMyw0LjM1NzE0Mjg2IDQuMjUsNS4wOTgxNzQ2IDQuMjUsNiBaIE03Ljc1LDYgQzcuNzUsNS43MjM4NTc2MyA3Ljk3Mzg1NzYzLDUuNSA4LjI1LDUuNSBDOC41MjYxNDIzNyw1LjUgOC43NSw1LjcyMzg1NzYzIDguNzUsNiBDOC43NSw3LjQ2NTMxNzQ2IDcuNTEzNTgzMzMsOC42NDI4NTcxNCA2LDguNjQyODU3MTQgQzUuNzIzODU3NjMsOC42NDI4NTcxNCA1LjUsOC40MTg5OTk1MiA1LjUsOC4xNDI4NTcxNCBDNS41LDcuODY2NzE0NzcgNS43MjM4NTc2Myw3LjY0Mjg1NzE0IDYsNy42NDI4NTcxNCBDNi45NzE5MTY2Nyw3LjY0Mjg1NzE0IDcuNzUsNi45MDE4MjU0IDcuNzUsNiBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyIDIpIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4=');
|
--icon-EyeHide: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzY3ODUwMDUsOC4zODc3NjE4NiBMOC40ODI0Nzg2NSwyLjk0NTI1ODQzIEM3LjY5MTM3ODkxLDIuMzM3Nzg0NzggNi44NTk1NjUyNywyIDYsMiBDNC42ODcxNTc3OSwyIDMuNDM5MDUzMDYsMi43ODc5NjU5MSAyLjMwMTg2ODgzLDQuMTIxMjgwMjIgQzEuODk1MDk5OTEsNC41OTgyMDQ1NSAxLjUzMjcyNzg0LDUuMTEwOTc5MTEgMS4yMjA1NDE3NCw1LjYyMzQxOTQ0IEMxLjEzMzk0MTAxLDUuNzY1NTcwOSAxLjA2MDI2MjE4LDUuODkyMzE1NDggMSw2IEMxLjA2MDI2MjE4LDYuMTA3Njg0NTIgMS4xMzM5NDEwMSw2LjIzNDQyOTEgMS4yMjA1NDE3NCw2LjM3NjU4MDU2IEMxLjUzMjcyNzg0LDYuODg5MDIwODkgMS44OTUwOTk5MSw3LjQwMTc5NTQ1IDIuMzAxODY4ODMsNy44Nzg3MTk3OCBDMi40NTUyMTY2Niw4LjA1ODUxNTQ5IDIuNjEwNTgxNDgsOC4yMjgzOTQ0OSAyLjc2Nzg1MDA1LDguMzg3NzYxODYgWiBNMy41MTc1MjEzNSw5LjA1NDc0MTU3IEM0LjMwODYyMTA5LDkuNjYyMjE1MjIgNS4xNDA0MzQ3MywxMCA2LDEwIEM3LjMxMjg0MjIxLDEwIDguNTYwOTQ2OTQsOS4yMTIwMzQwOSA5LjY5ODEzMTE3LDcuODc4NzE5NzggQzEwLjEwNDkwMDEsNy40MDE3OTU0NSAxMC40NjcyNzIyLDYuODg5MDIwODkgMTAuNzc5NDU4Myw2LjM3NjU4MDU2IEMxMC44NjYwNTksNi4yMzQ0MjkxIDEwLjkzOTczNzgsNi4xMDc2ODQ1MiAxMSw2IEMxMC45Mzk3Mzc4LDUuODkyMzE1NDggMTAuODY2MDU5LDUuNzY1NTcwOSAxMC43Nzk0NTgzLDUuNjIzNDE5NDQgQzEwLjQ2NzI3MjIsNS4xMTA5NzkxMSAxMC4xMDQ5MDAxLDQuNTk4MjA0NTUgOS42OTgxMzExNyw0LjEyMTI4MDIyIEM5LjU0NDc4MzM0LDMuOTQxNDg0NTEgOS4zODk0MTg1MiwzLjc3MTYwNTUxIDkuMjMyMTQ5OTUsMy42MTIyMzgxNCBMMy41MTc1MjEzNSw5LjA1NDc0MTU3IFogTTkuMTk4MDQ5MzEsMi4yNjM3NjI1NyBMMTAuOTA1MTcyNCwwLjYzNzkzMTAzNCBDMTEuMTA1MTM3NiwwLjQ0NzQ4ODAxNyAxMS40MjE2MjU5LDAuNDU1MjA3MjQ2IDExLjYxMjA2OSwwLjY1NTE3MjQxNCBDMTEuODAyNTEyLDAuODU1MTM3NTgyIDExLjc5NDc5MjgsMS4xNzE2MjU5NSAxMS41OTQ4Mjc2LDEuMzYyMDY4OTcgTDkuOTM1ODc1NzEsMi45NDIwMjMxMyBDMTAuMDcyNjIyNCwzLjA4MjQ0NjUzIDEwLjIwNzYxOTksMy4yMjk0MzM5IDEwLjM0MDg0MjYsMy4zODI2MzIwNSBDMTAuNzgzMjMyOSwzLjg5MTM1NDQ3IDExLjE3NDcyMzEsNC40MzQ2ODc1NSAxMS41MTI0MTU1LDQuOTc4MzQ0MDkgQzExLjcxNjM0MSw1LjMwNjY0NzE4IDExLjg1ODUzNjYsNS41NjUxMDg2MyAxMS45MzUyMjgsNS43MTk0NDIxMiBDMTIuMDIxNTkwNyw1Ljg5MzIzNzgyIDEyLjAyMTU5MDcsNi4xMDY3NjIxOCAxMS45MzUyMjgsNi4yODA1NTc4OCBDMTEuODU4NTM2Niw2LjQzNDg5MTM3IDExLjcxNjM0MSw2LjY5MzM1MjgyIDExLjUxMjQxNTUsNy4wMjE2NTU5MSBDMTEuMTc0NzIzMSw3LjU2NTMxMjQ1IDEwLjc4MzIzMjksOC4xMDg2NDU1MyAxMC4zNDA4NDI2LDguNjE3MzY3OTUgQzkuMDUyMTkwMjYsMTAuMDk5MjQxMiA3LjU5NzQ3MzE1LDExIDYsMTEgQzQuODU4MjE4ODYsMTEgMy43ODkzNjU1MiwxMC41Mzk4NDE5IDIuODAxOTUwNjksOS43MzYyMzc0MyBMMS4wOTQ4Mjc1OSwxMS4zNjIwNjkgQzAuODk0ODYyNDE4LDExLjU1MjUxMiAwLjU3ODM3NDA1MiwxMS41NDQ3OTI4IDAuMzg3OTMxMDM0LDExLjM0NDgyNzYgQzAuMTk3NDg4MDE3LDExLjE0NDg2MjQgMC4yMDUyMDcyNDYsMTAuODI4Mzc0MSAwLjQwNTE3MjQxNCwxMC42Mzc5MzEgTDIuMDY0MTI0MjksOS4wNTc5NzY4NyBDMS45MjczNzc2Myw4LjkxNzU1MzQ3IDEuNzkyMzgwMDcsOC43NzA1NjYxIDEuNjU5MTU3MzcsOC42MTczNjc5NSBDMS4yMTY3NjcwNyw4LjEwODY0NTUzIDAuODI1Mjc2OTI4LDcuNTY1MzEyNDUgMC40ODc1ODQ1MjEsNy4wMjE2NTU5MSBDMC4yODM2NTg5NjYsNi42OTMzNTI4MiAwLjE0MTQ2MzM3OCw2LjQzNDg5MTM3IDAuMDY0NzcxOTUwNCw2LjI4MDU1Nzg4IEMtMC4wMjE1OTA2NTAxLDYuMTA2NzYyMTggLTAuMDIxNTkwNjUwMSw1Ljg5MzIzNzgyIDAuMDY0NzcxOTUwNCw1LjcxOTQ0MjEyIEMwLjE0MTQ2MzM3OCw1LjU2NTEwODYzIDAuMjgzNjU4OTY2LDUuMzA2NjQ3MTggMC40ODc1ODQ1MjEsNC45NzgzNDQwOSBDMC44MjUyNzY5MjgsNC40MzQ2ODc1NSAxLjIxNjc2NzA3LDMuODkxMzU0NDcgMS42NTkxNTczNywzLjM4MjYzMjA1IEMyLjk0NzgwOTc0LDEuOTAwNzU4OCA0LjQwMjUyNjg1LDEgNiwxIEM3LjE0MTc4MTE0LDEgOC4yMTA2MzQ0OCwxLjQ2MDE1ODE0IDkuMTk4MDQ5MzEsMi4yNjM3NjI1NyBaIE00LjI1LDYgQzQuMjUsNi4yNzYxNDIzNyA0LjAyNjE0MjM3LDYuNSAzLjc1LDYuNSBDMy40NzM4NTc2Myw2LjUgMy4yNSw2LjI3NjE0MjM3IDMuMjUsNiBDMy4yNSw0LjUzNDY4MjU0IDQuNDg2NDE2NjcsMy4zNTcxNDI4NiA2LDMuMzU3MTQyODYgQzYuMjc2MTQyMzcsMy4zNTcxNDI4NiA2LjUsMy41ODEwMDA0OCA2LjUsMy44NTcxNDI4NiBDNi41LDQuMTMzMjg1MjMgNi4yNzYxNDIzNyw0LjM1NzE0Mjg2IDYsNC4zNTcxNDI4NiBDNS4wMjgwODMzMyw0LjM1NzE0Mjg2IDQuMjUsNS4wOTgxNzQ2IDQuMjUsNiBaIE03Ljc1LDYgQzcuNzUsNS43MjM4NTc2MyA3Ljk3Mzg1NzYzLDUuNSA4LjI1LDUuNSBDOC41MjYxNDIzNyw1LjUgOC43NSw1LjcyMzg1NzYzIDguNzUsNiBDOC43NSw3LjQ2NTMxNzQ2IDcuNTEzNTgzMzMsOC42NDI4NTcxNCA2LDguNjQyODU3MTQgQzUuNzIzODU3NjMsOC42NDI4NTcxNCA1LjUsOC40MTg5OTk1MiA1LjUsOC4xNDI4NTcxNCBDNS41LDcuODY2NzE0NzcgNS43MjM4NTc2Myw3LjY0Mjg1NzE0IDYsNy42NDI4NTcxNCBDNi45NzE5MTY2Nyw3LjY0Mjg1NzE0IDcuNzUsNi45MDE4MjU0IDcuNzUsNiBaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyIDIpIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4=');
|
||||||
--icon-EyeShow: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMuMjM1MDA0NzcsOC40MDA0MDAzNyBDMy41NDczMDQ3OSw4LjkxMDM0MTgyIDMuOTA5NzM5NSw5LjQyMDUxNzkxIDQuMzE2NDc5NzEsOS44OTQ5MDk5MyBDNS40NTE0MTkyNiwxMS4yMTg2MjA0IDYuNjk1NjEwNDMsMTIgOCwxMiBDOS4zMDQzODk1NywxMiAxMC41NDg1ODA3LDExLjIxODYyMDQgMTEuNjgzNTIwMyw5Ljg5NDkwOTkzIEMxMi4wOTAyNjA1LDkuNDIwNTE3OTEgMTIuNDUyNjk1Miw4LjkxMDM0MTgyIDEyLjc2NDk5NTIsOC40MDA0MDAzNyBDMTIuODU4NTc0Nyw4LjI0NzU5ODQ0IDEyLjkzNzExNjQsOC4xMTI2MTA2NCAxMyw4IEMxMi45MzcxMTY0LDcuODg3Mzg5MzYgMTIuODU4NTc0Nyw3Ljc1MjQwMTU2IDEyLjc2NDk5NTIsNy41OTk1OTk2MyBDMTIuNDUyNjk1Miw3LjA4OTY1ODE4IDEyLjA5MDI2MDUsNi41Nzk0ODIwOSAxMS42ODM1MjAzLDYuMTA1MDkwMDcgQzEwLjU0ODU4MDcsNC43ODEzNzk2MSA5LjMwNDM4OTU3LDQgOCw0IEM2LjY5NTYxMDQzLDQgNS40NTE0MTkyNiw0Ljc4MTM3OTYxIDQuMzE2NDc5NzEsNi4xMDUwOTAwNyBDMy45MDk3Mzk1LDYuNTc5NDgyMDkgMy41NDczMDQ3OSw3LjA4OTY1ODE4IDMuMjM1MDA0NzcsNy41OTk1OTk2MyBDMy4xNDE0MjUzMSw3Ljc1MjQwMTU2IDMuMDYyODgzNTgsNy44ODczODkzNiAzLDggQzMuMDYyODgzNTgsOC4xMTI2MTA2NCAzLjE0MTQyNTMxLDguMjQ3NTk4NDQgMy4yMzUwMDQ3Nyw4LjQwMDQwMDM3IFogTTIuMDU5ODA2MjUsNy43NDExNjEwMSBDMi4xMzYwNDgxMSw3LjU4NjkwMjg4IDIuMjc3NzU4NjcsNy4zMjc5MzA3MSAyLjQ4MTE1OTA2LDYuOTk4NzAzMDcgQzIuODE4MjI0ODgsNi40NTMxMjIwNSAzLjIwOTA1NjQ4LDUuOTA3NzcwOTIgMy42NTA4MDU0NSw1LjM5NzA0MDE0IEM0LjkzOTgwNDA1LDMuOTA2NzU2NDYgNi4zOTYzMzQxOSwzIDgsMyBDOS42MDM2NjU4MSwzIDExLjA2MDE5NTksMy45MDY3NTY0NiAxMi4zNDkxOTQ2LDUuMzk3MDQwMTQgQzEyLjc5MDk0MzUsNS45MDc3NzA5MiAxMy4xODE3NzUxLDYuNDUzMTIyMDUgMTMuNTE4ODQwOSw2Ljk5ODcwMzA3IEMxMy43MjIyNDEzLDcuMzI3OTMwNzEgMTMuODYzOTUxOSw3LjU4NjkwMjg4IDEzLjk0MDE5MzcsNy43NDExNjEwMSBDMTQuMDE5OTM1NCw3LjkwMjUwMDE5IDE0LjAxOTkzNTQsOC4wOTc0OTk4MSAxMy45NDAxOTM3LDguMjU4ODM4OTkgQzEzLjg2Mzk1MTksOC40MTMwOTcxMiAxMy43MjIyNDEzLDguNjcyMDY5MjkgMTMuNTE4ODQwOSw5LjAwMTI5NjkzIEMxMy4xODE3NzUxLDkuNTQ2ODc3OTUgMTIuNzkwOTQzNSwxMC4wOTIyMjkxIDEyLjM0OTE5NDYsMTAuNjAyOTU5OSBDMTEuMDYwMTk1OSwxMi4wOTMyNDM1IDkuNjAzNjY1ODEsMTMgOCwxMyBDNi4zOTYzMzQxOSwxMyA0LjkzOTgwNDA1LDEyLjA5MzI0MzUgMy42NTA4MDU0NSwxMC42MDI5NTk5IEMzLjIwOTA1NjQ4LDEwLjA5MjIyOTEgMi44MTgyMjQ4OCw5LjU0Njg3Nzk1IDIuNDgxMTU5MDYsOS4wMDEyOTY5MyBDMi4yNzc3NTg2Nyw4LjY3MjA2OTI5IDIuMTM2MDQ4MTEsOC40MTMwOTcxMiAyLjA1OTgwNjI1LDguMjU4ODM4OTkgQzEuOTgwMDY0NTgsOC4wOTc0OTk4MSAxLjk4MDA2NDU4LDcuOTAyNTAwMTkgMi4wNTk4MDYyNSw3Ljc0MTE2MTAxIFogTTguMDY0MjE5NjIsMTAuMjg3Mjc2NiBDNi43Mjc2MDM2MiwxMC4yODcyNzY2IDUuNjQ0MDYyMjYsOS4yNjMyMjc5NiA1LjY0NDA2MjI2LDggQzUuNjQ0MDYyMjYsNi43MzY3NzIwNCA2LjcyNzYwMzYyLDUuNzEyNzIzNDQgOC4wNjQyMTk2Miw1LjcxMjcyMzQ0IEM5LjQwMDgzNTYzLDUuNzEyNzIzNDQgMTAuNDg0Mzc3LDYuNzM2NzcyMDQgMTAuNDg0Mzc3LDggQzEwLjQ4NDM3Nyw5LjI2MzIyNzk2IDkuNDAwODM1NjMsMTAuMjg3Mjc2NiA4LjA2NDIxOTYyLDEwLjI4NzI3NjYgWiBNOC4wNjQyMTk2Miw5LjMyNDIxMjc1IEM4LjgzODA0OTk0LDkuMzI0MjEyNzUgOS40NjUzNjMzNiw4LjczMTM0MjUxIDkuNDY1MzYzMzYsOCBDOS40NjUzNjMzNiw3LjI2ODY1NzQ5IDguODM4MDQ5OTQsNi42NzU3ODcyNSA4LjA2NDIxOTYyLDYuNjc1Nzg3MjUgQzcuMjkwMzg5MzEsNi42NzU3ODcyNSA2LjY2MzA3NTg5LDcuMjY4NjU3NDkgNi42NjMwNzU4OSw4IEM2LjY2MzA3NTg5LDguNzMxMzQyNTEgNy4yOTAzODkzMSw5LjMyNDIxMjc1IDguMDY0MjE5NjIsOS4zMjQyMTI3NSBaIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4=');
|
--icon-EyeShow: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMuMjM1MDA0NzcsOC40MDA0MDAzNyBDMy41NDczMDQ3OSw4LjkxMDM0MTgyIDMuOTA5NzM5NSw5LjQyMDUxNzkxIDQuMzE2NDc5NzEsOS44OTQ5MDk5MyBDNS40NTE0MTkyNiwxMS4yMTg2MjA0IDYuNjk1NjEwNDMsMTIgOCwxMiBDOS4zMDQzODk1NywxMiAxMC41NDg1ODA3LDExLjIxODYyMDQgMTEuNjgzNTIwMyw5Ljg5NDkwOTkzIEMxMi4wOTAyNjA1LDkuNDIwNTE3OTEgMTIuNDUyNjk1Miw4LjkxMDM0MTgyIDEyLjc2NDk5NTIsOC40MDA0MDAzNyBDMTIuODU4NTc0Nyw4LjI0NzU5ODQ0IDEyLjkzNzExNjQsOC4xMTI2MTA2NCAxMyw4IEMxMi45MzcxMTY0LDcuODg3Mzg5MzYgMTIuODU4NTc0Nyw3Ljc1MjQwMTU2IDEyLjc2NDk5NTIsNy41OTk1OTk2MyBDMTIuNDUyNjk1Miw3LjA4OTY1ODE4IDEyLjA5MDI2MDUsNi41Nzk0ODIwOSAxMS42ODM1MjAzLDYuMTA1MDkwMDcgQzEwLjU0ODU4MDcsNC43ODEzNzk2MSA5LjMwNDM4OTU3LDQgOCw0IEM2LjY5NTYxMDQzLDQgNS40NTE0MTkyNiw0Ljc4MTM3OTYxIDQuMzE2NDc5NzEsNi4xMDUwOTAwNyBDMy45MDk3Mzk1LDYuNTc5NDgyMDkgMy41NDczMDQ3OSw3LjA4OTY1ODE4IDMuMjM1MDA0NzcsNy41OTk1OTk2MyBDMy4xNDE0MjUzMSw3Ljc1MjQwMTU2IDMuMDYyODgzNTgsNy44ODczODkzNiAzLDggQzMuMDYyODgzNTgsOC4xMTI2MTA2NCAzLjE0MTQyNTMxLDguMjQ3NTk4NDQgMy4yMzUwMDQ3Nyw4LjQwMDQwMDM3IFogTTIuMDU5ODA2MjUsNy43NDExNjEwMSBDMi4xMzYwNDgxMSw3LjU4NjkwMjg4IDIuMjc3NzU4NjcsNy4zMjc5MzA3MSAyLjQ4MTE1OTA2LDYuOTk4NzAzMDcgQzIuODE4MjI0ODgsNi40NTMxMjIwNSAzLjIwOTA1NjQ4LDUuOTA3NzcwOTIgMy42NTA4MDU0NSw1LjM5NzA0MDE0IEM0LjkzOTgwNDA1LDMuOTA2NzU2NDYgNi4zOTYzMzQxOSwzIDgsMyBDOS42MDM2NjU4MSwzIDExLjA2MDE5NTksMy45MDY3NTY0NiAxMi4zNDkxOTQ2LDUuMzk3MDQwMTQgQzEyLjc5MDk0MzUsNS45MDc3NzA5MiAxMy4xODE3NzUxLDYuNDUzMTIyMDUgMTMuNTE4ODQwOSw2Ljk5ODcwMzA3IEMxMy43MjIyNDEzLDcuMzI3OTMwNzEgMTMuODYzOTUxOSw3LjU4NjkwMjg4IDEzLjk0MDE5MzcsNy43NDExNjEwMSBDMTQuMDE5OTM1NCw3LjkwMjUwMDE5IDE0LjAxOTkzNTQsOC4wOTc0OTk4MSAxMy45NDAxOTM3LDguMjU4ODM4OTkgQzEzLjg2Mzk1MTksOC40MTMwOTcxMiAxMy43MjIyNDEzLDguNjcyMDY5MjkgMTMuNTE4ODQwOSw5LjAwMTI5NjkzIEMxMy4xODE3NzUxLDkuNTQ2ODc3OTUgMTIuNzkwOTQzNSwxMC4wOTIyMjkxIDEyLjM0OTE5NDYsMTAuNjAyOTU5OSBDMTEuMDYwMTk1OSwxMi4wOTMyNDM1IDkuNjAzNjY1ODEsMTMgOCwxMyBDNi4zOTYzMzQxOSwxMyA0LjkzOTgwNDA1LDEyLjA5MzI0MzUgMy42NTA4MDU0NSwxMC42MDI5NTk5IEMzLjIwOTA1NjQ4LDEwLjA5MjIyOTEgMi44MTgyMjQ4OCw5LjU0Njg3Nzk1IDIuNDgxMTU5MDYsOS4wMDEyOTY5MyBDMi4yNzc3NTg2Nyw4LjY3MjA2OTI5IDIuMTM2MDQ4MTEsOC40MTMwOTcxMiAyLjA1OTgwNjI1LDguMjU4ODM4OTkgQzEuOTgwMDY0NTgsOC4wOTc0OTk4MSAxLjk4MDA2NDU4LDcuOTAyNTAwMTkgMi4wNTk4MDYyNSw3Ljc0MTE2MTAxIFogTTguMDY0MjE5NjIsMTAuMjg3Mjc2NiBDNi43Mjc2MDM2MiwxMC4yODcyNzY2IDUuNjQ0MDYyMjYsOS4yNjMyMjc5NiA1LjY0NDA2MjI2LDggQzUuNjQ0MDYyMjYsNi43MzY3NzIwNCA2LjcyNzYwMzYyLDUuNzEyNzIzNDQgOC4wNjQyMTk2Miw1LjcxMjcyMzQ0IEM5LjQwMDgzNTYzLDUuNzEyNzIzNDQgMTAuNDg0Mzc3LDYuNzM2NzcyMDQgMTAuNDg0Mzc3LDggQzEwLjQ4NDM3Nyw5LjI2MzIyNzk2IDkuNDAwODM1NjMsMTAuMjg3Mjc2NiA4LjA2NDIxOTYyLDEwLjI4NzI3NjYgWiBNOC4wNjQyMTk2Miw5LjMyNDIxMjc1IEM4LjgzODA0OTk0LDkuMzI0MjEyNzUgOS40NjUzNjMzNiw4LjczMTM0MjUxIDkuNDY1MzYzMzYsOCBDOS40NjUzNjMzNiw3LjI2ODY1NzQ5IDguODM4MDQ5OTQsNi42NzU3ODcyNSA4LjA2NDIxOTYyLDYuNjc1Nzg3MjUgQzcuMjkwMzg5MzEsNi42NzU3ODcyNSA2LjY2MzA3NTg5LDcuMjY4NjU3NDkgNi42NjMwNzU4OSw4IEM2LjY2MzA3NTg5LDguNzMxMzQyNTEgNy4yOTAzODkzMSw5LjMyNDIxMjc1IDguMDY0MjE5NjIsOS4zMjQyMTI3NSBaIiBmaWxsPSIjMDAwIiBmaWxsLXJ1bGU9Im5vbnplcm8iLz48L3N2Zz4=');
|
||||||
@ -59,6 +60,10 @@
|
|||||||
--icon-Filter: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNSw0IEwxLjUsNCBDMS4yMjQsNCAxLDMuNzc2IDEsMy41IEMxLDMuMjI0IDEuMjI0LDMgMS41LDMgTDIuNSwzIEMyLjc3NiwzIDMsMy4yMjQgMywzLjUgQzMsMy43NzYgMi43NzYsNCAyLjUsNCBaIE00LjUsNyBMMS41LDcgQzEuMjI0LDcgMSw2Ljc3NiAxLDYuNSBDMSw2LjIyNCAxLjIyNCw2IDEuNSw2IEw0LjUsNiBDNC43NzYsNiA1LDYuMjI0IDUsNi41IEM1LDYuNzc2IDQuNzc2LDcgNC41LDcgWiBNNi41LDEwIEwxLjUsMTAgQzEuMjI0LDEwIDEsOS43NzYgMSw5LjUgQzEsOS4yMjQgMS4yMjQsOSAxLjUsOSBMNi41LDkgQzYuNzc2LDkgNyw5LjIyNCA3LDkuNSBDNyw5Ljc3NiA2Ljc3NiwxMCA2LjUsMTAgWiBNOC41LDEzIEwxLjUsMTMgQzEuMjI0LDEzIDEsMTIuNzc2IDEsMTIuNSBDMSwxMi4yMjQgMS4yMjQsMTIgMS41LDEyIEw4LjUsMTIgQzguNzc2LDEyIDksMTIuMjI0IDksMTIuNSBDOSwxMi43NzYgOC43NzYsMTMgOC41LDEzIFogTTE0Ljk3Mjg4OCwyLjI4NDkwODg1IEMxNS4wMjY1MzgyLDIuNDU3MzYxNzggMTQuOTk2ODg2NCwyLjY1NTk2NzI1IDE0Ljg5Nzg5MzIsMi43ODc4MDY4MyBMMTEuNjgxMzA2OCw3LjA3NjY2OTY1IEMxMS42MTE3MzQ3LDcuMTY5NTg4MDkgMTEuNTM4MDA5LDcuNDA2ODA3MDIgMTEuNTM4MDA5LDcuNTM4MTg1MDggTDExLjUzODAwOSwxMy41Mzc4ODU2IEMxMS41MzgwMDksMTMuNzI0NjQ1NSAxMS40NTM3ODQsMTMuODkyOTQ0OCAxMS4zMjQzMzE0LDEzLjk2NDE3MiBDMTEuMjgxNTI2NiwxMy45ODgwMTcgMTEuMjM2NTI5NywxMy45OTk0MDEgMTEuMTkxOTk0MywxMy45OTk0MDEgQzExLjEwMTg4NTEsMTMuOTk5NDAxIDExLjAxMzI3NTgsMTMuOTUyNDgwMyAxMC45NDcxNjUsMTMuODY0MTc3IEw5LjU2MjY0NDg2LDEyLjAxODExNTMgQzkuNDk3Njg3NzksMTEuOTMxNjU4MSA5LjQ2MTIyODc2LDExLjgxNDI3OTMgOS40NjEyMjg3NiwxMS42OTE4MjM5IEw5LjQ2MTIyODc2LDcuNTM4MTg1MDggQzkuNDYxMjI4NzYsNy40MDY4MDcwMiA5LjM4NzYxODQ0LDcuMTY5NTg4MDkgOS4zMTc5MzA5Miw3LjA3Njk3NzMzIEw2LjEwMTM0NDUyLDIuNzg3OTYwNjcgQzYuMDAyMzUxMzMsMi42NTU5NjcyNSA1Ljk3MjgxNDksMi40NTc1MTU2MiA2LjAyNjM0OTY4LDIuMjg0OTA4ODUgQzYuMDc5ODg0NDUsMi4xMTIzMDIwOSA2LjIwNjEwNjU0LDIgNi4zNDYwNTg0NSwyIEwxNC42NTMxNzkzLDIgQzE0Ljc5MzI0NjYsMiAxNC45MTk0Njg2LDIuMTEyMzAyMDkgMTQuOTcyODg4LDIuMjg0OTA4ODUgWiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+');
|
--icon-Filter: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNSw0IEwxLjUsNCBDMS4yMjQsNCAxLDMuNzc2IDEsMy41IEMxLDMuMjI0IDEuMjI0LDMgMS41LDMgTDIuNSwzIEMyLjc3NiwzIDMsMy4yMjQgMywzLjUgQzMsMy43NzYgMi43NzYsNCAyLjUsNCBaIE00LjUsNyBMMS41LDcgQzEuMjI0LDcgMSw2Ljc3NiAxLDYuNSBDMSw2LjIyNCAxLjIyNCw2IDEuNSw2IEw0LjUsNiBDNC43NzYsNiA1LDYuMjI0IDUsNi41IEM1LDYuNzc2IDQuNzc2LDcgNC41LDcgWiBNNi41LDEwIEwxLjUsMTAgQzEuMjI0LDEwIDEsOS43NzYgMSw5LjUgQzEsOS4yMjQgMS4yMjQsOSAxLjUsOSBMNi41LDkgQzYuNzc2LDkgNyw5LjIyNCA3LDkuNSBDNyw5Ljc3NiA2Ljc3NiwxMCA2LjUsMTAgWiBNOC41LDEzIEwxLjUsMTMgQzEuMjI0LDEzIDEsMTIuNzc2IDEsMTIuNSBDMSwxMi4yMjQgMS4yMjQsMTIgMS41LDEyIEw4LjUsMTIgQzguNzc2LDEyIDksMTIuMjI0IDksMTIuNSBDOSwxMi43NzYgOC43NzYsMTMgOC41LDEzIFogTTE0Ljk3Mjg4OCwyLjI4NDkwODg1IEMxNS4wMjY1MzgyLDIuNDU3MzYxNzggMTQuOTk2ODg2NCwyLjY1NTk2NzI1IDE0Ljg5Nzg5MzIsMi43ODc4MDY4MyBMMTEuNjgxMzA2OCw3LjA3NjY2OTY1IEMxMS42MTE3MzQ3LDcuMTY5NTg4MDkgMTEuNTM4MDA5LDcuNDA2ODA3MDIgMTEuNTM4MDA5LDcuNTM4MTg1MDggTDExLjUzODAwOSwxMy41Mzc4ODU2IEMxMS41MzgwMDksMTMuNzI0NjQ1NSAxMS40NTM3ODQsMTMuODkyOTQ0OCAxMS4zMjQzMzE0LDEzLjk2NDE3MiBDMTEuMjgxNTI2NiwxMy45ODgwMTcgMTEuMjM2NTI5NywxMy45OTk0MDEgMTEuMTkxOTk0MywxMy45OTk0MDEgQzExLjEwMTg4NTEsMTMuOTk5NDAxIDExLjAxMzI3NTgsMTMuOTUyNDgwMyAxMC45NDcxNjUsMTMuODY0MTc3IEw5LjU2MjY0NDg2LDEyLjAxODExNTMgQzkuNDk3Njg3NzksMTEuOTMxNjU4MSA5LjQ2MTIyODc2LDExLjgxNDI3OTMgOS40NjEyMjg3NiwxMS42OTE4MjM5IEw5LjQ2MTIyODc2LDcuNTM4MTg1MDggQzkuNDYxMjI4NzYsNy40MDY4MDcwMiA5LjM4NzYxODQ0LDcuMTY5NTg4MDkgOS4zMTc5MzA5Miw3LjA3Njk3NzMzIEw2LjEwMTM0NDUyLDIuNzg3OTYwNjcgQzYuMDAyMzUxMzMsMi42NTU5NjcyNSA1Ljk3MjgxNDksMi40NTc1MTU2MiA2LjAyNjM0OTY4LDIuMjg0OTA4ODUgQzYuMDc5ODg0NDUsMi4xMTIzMDIwOSA2LjIwNjEwNjU0LDIgNi4zNDYwNTg0NSwyIEwxNC42NTMxNzkzLDIgQzE0Ljc5MzI0NjYsMiAxNC45MTk0Njg2LDIuMTEyMzAyMDkgMTQuOTcyODg4LDIuMjg0OTA4ODUgWiIgZmlsbD0iIzAwMCIgZmlsbC1ydWxlPSJub256ZXJvIi8+PC9zdmc+');
|
||||||
--icon-FilterSimple: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkuOTUwODMzMzMsMC4yMiBDOS44NzgyODY2NywwLjA4NDQ5NzcxMDggOS43MzcwMzQsLTYuMTY5Njc5NTFlLTA1IDkuNTgzMzMzMzMsLTQuNjI1OTMyOGUtMTcgTDAuNDE2NjY2NjY3LC00LjYyNTkzMjhlLTE3IEMwLjI2MzEwMzQ1NCw3LjkzMjczNDM1ZS0wNSAwLjEyMjAzMTQsMC4wODQ2MTg0ODc1IDAuMDQ5NTQ5NDAxMiwwLjIxOTk5OTUyOSBDLTAuMDIyOTMyNTk3OCwwLjM1NTM4MDU3MSAtMC4wMTUwNzQwNTE3LDAuNTE5NjU2MDYyIDAuMDcsMC42NDc1IEwzLjMzMzMzMzMzLDUuNTQyNSBMMy4zMzMzMzMzMyw5LjU4MzMzMzMzIEMzLjMzMzMzMzMzLDkuODEzNDUyIDMuNTE5ODgxMzYsMTAgMy43NSwxMCBMNi4yNSwxMCBDNi40ODAxMTg2NCwxMCA2LjY2NjY2NjY3LDkuODEzNDUyIDYuNjY2NjY2NjcsOS41ODMzMzMzMyBMNi42NjY2NjY2Nyw1LjU0MjUgTDkuOTMsMC42NDc1IEMxMC4wMTUxOTU2LDAuNTE5NzI3OTkxIDEwLjAyMzIwMTMsMC4zNTU0NTA1NDcgOS45NTA4MzMzMywwLjIyIFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMgMykiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-FilterSimple: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTkuOTUwODMzMzMsMC4yMiBDOS44NzgyODY2NywwLjA4NDQ5NzcxMDggOS43MzcwMzQsLTYuMTY5Njc5NTFlLTA1IDkuNTgzMzMzMzMsLTQuNjI1OTMyOGUtMTcgTDAuNDE2NjY2NjY3LC00LjYyNTkzMjhlLTE3IEMwLjI2MzEwMzQ1NCw3LjkzMjczNDM1ZS0wNSAwLjEyMjAzMTQsMC4wODQ2MTg0ODc1IDAuMDQ5NTQ5NDAxMiwwLjIxOTk5OTUyOSBDLTAuMDIyOTMyNTk3OCwwLjM1NTM4MDU3MSAtMC4wMTUwNzQwNTE3LDAuNTE5NjU2MDYyIDAuMDcsMC42NDc1IEwzLjMzMzMzMzMzLDUuNTQyNSBMMy4zMzMzMzMzMyw5LjU4MzMzMzMzIEMzLjMzMzMzMzMzLDkuODEzNDUyIDMuNTE5ODgxMzYsMTAgMy43NSwxMCBMNi4yNSwxMCBDNi40ODAxMTg2NCwxMCA2LjY2NjY2NjY3LDkuODEzNDUyIDYuNjY2NjY2NjcsOS41ODMzMzMzMyBMNi42NjY2NjY2Nyw1LjU0MjUgTDkuOTMsMC42NDc1IEMxMC4wMTUxOTU2LDAuNTE5NzI3OTkxIDEwLjAyMzIwMTMsMC4zNTU0NTA1NDcgOS45NTA4MzMzMywwLjIyIFoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMgMykiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
--icon-Folder: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMsNSBMMTMsNSBDMTMuNTUyMjg0Nyw1IDE0LDUuNDQ3NzE1MjUgMTQsNiBMMTQsMTMgQzE0LDEzLjU1MjI4NDcgMTMuNTUyMjg0NywxNCAxMywxNCBMMywxNCBDMi40NDc3MTUyNSwxNCAyLDEzLjU1MjI4NDcgMiwxMyBMMiw2IEMyLDUuNDQ3NzE1MjUgMi40NDc3MTUyNSw1IDMsNSBaIE00LDIgTDEyLDIgTDEyLDQgTDQsNCBMNCwyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-Folder: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTMsNSBMMTMsNSBDMTMuNTUyMjg0Nyw1IDE0LDUuNDQ3NzE1MjUgMTQsNiBMMTQsMTMgQzE0LDEzLjU1MjI4NDcgMTMuNTUyMjg0NywxNCAxMywxNCBMMywxNCBDMi40NDc3MTUyNSwxNCAyLDEzLjU1MjI4NDcgMiwxMyBMMiw2IEMyLDUuNDQ3NzE1MjUgMi40NDc3MTUyNSw1IDMsNSBaIE00LDIgTDEyLDIgTDEyLDQgTDQsNCBMNCwyIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
|
--icon-FontBold: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTTMuMTk1MDk4OCAxLjgzNTQxOTloNS42OTAzODE4Yy43NTQ1OTUyIDAgMS40NzgyNzI0LjI5OTc1NjcgMi4wMTE4NTM0LjgzMzMzNzUuNTMzNTgxLjUzMzU4MDguODMzMzM3IDEuMjU3MjU4Mi44MzMzMzcgMi4wMTE4NTM1IDAgLjc1NDU5NTItLjI5OTc1NiAxLjQ3ODI3MjctLjgzMzMzNyAyLjAxMTg1MzVDMTAuMzYzNzUzIDcuMjI2MDQ1MSA5LjY0MDA3NTggNy41MjU4MDE4IDguODg1NDgwNiA3LjUyNTgwMThINS4wOTE4OTI3TTUuMDkxODkyNyA3LjUyNTgwMThoNS4yMTYxODMzYy44ODAzNTIgMCAxLjcyNDY1My4zNDk3MTgyIDIuMzQ3MTY5Ljk3MjIyMDcuNjIyNTAyLjYyMjUxNTIuOTcyMjIgMS40NjY4MTYxLjk3MjIyIDIuMzQ3MTY4NXYwYzAgLjg4MDM1My0uMzQ5NzE4IDEuNzI0NjU0LS45NzIyMiAyLjM0NzIxOS0uNjIyNTE2LjYyMjQwMi0xLjQ2NjgxNy45NzIxNy0yLjM0NzE2OS45NzIxN0gzLjE5NTA5ODhNNS4wOTE4OTI3IDEuODM1NDE5OVYxNC4xNjQ1OCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDMwMDAwIiBzdHJva2Utd2lkdGg9IjEuMjY1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48L3N2Zz4=');
|
||||||
|
--icon-FontItalic: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwSDEyVjEySDB6Ii8+PC9jbGlwUGF0aD48L2RlZnM+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4xMzU2IDAgMCAxLjEzNTYgLjk4MyAxLjM5KSIgY2xpcC1wYXRoPSJ1cmwoI2EpIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGZpbGw9Im5vbmUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0ibSA0Ljg3NSwwLjM3NSBoIDQuNSIgc3Ryb2tlPSIjMDAwIi8+PHBhdGggZD0ibSAyLjYyNSwxMS42MjUgaCA0LjUiIHN0cm9rZT0iIzA2MDAwMCIvPjxwYXRoIGQ9Im0gNy4xMjUsMC4zNzUgLTIuMjUsMTEuMjUiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLW9wYWNpdHk9Ii45OSIvPjwvZz48L3N2Zz4=');
|
||||||
|
--icon-FontStrikethrough: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggZD0iTSAxLjgwNTY1ODUsMy4zOTcxNzIgViAxLjczNTc0MzEgSCAxNC4xOTQzNDIgViAzLjM5NzE3MiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTI5Mjk5IiBzdHJva2Utd2lkdGg9IjEuMTQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNIDcuOTk5OTk5OSwxMC4wNDI4ODcgViAxNC4xOTY0NiIgc3Ryb2tlPSIjOTI5Mjk5IiBzdHJva2Utd2lkdGg9IjEuMTQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNIDcuOTk5OTk5OSwxLjczNTc0MzEgViA3LjU1MDc0NCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTI5Mjk5IiBzdHJva2Utd2lkdGg9IjEuMTQzIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBkPSJNNS4zNDUyODIyIDE0LjE5NjQ2SDEwLjY1NDcxOE0xLjgwNTY1ODUgNy41NTA3NDRIMTQuMTk0MzQyIiBzdHJva2U9IiM5MjkyOTkiIHN0cm9rZS13aWR0aD0iMS4xNDMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==');
|
||||||
|
--icon-FontUnderline: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PGRlZnM+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBmaWxsPSIjZmZmIiBkPSJNMCAwSDEyVjEySDB6Ii8+PC9jbGlwUGF0aD48L2RlZnM+PGcgdHJhbnNmb3JtPSJtYXRyaXgoMS4xNDI5NSAwIDAgMS4xNDI5NSAxLjE0MiAuNzIxKSIgY2xpcC1wYXRoPSJ1cmwoI2EpIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZT0iIzAwMCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIj48cGF0aCBkPSJNLjM3NSAxMS42MjVoMTEuMjVNOS4zNzUgMS4xMjVWNkM5LjM3NSA2Ljg5NTExIDkuMDE5NDIgNy43NTM1NSA4LjM4NjQ5IDguMzg2NDkgNy43NTM1NSA5LjAxOTQyIDYuODk1MTEgOS4zNzUgNiA5LjM3NXYwQzUuMTA0ODkgOS4zNzUgNC4yNDY0NSA5LjAxOTQyIDMuNjEzNTEgOC4zODY0OSAyLjk4MDU4IDcuNzUzNTUgMi42MjUgNi44OTUxMSAyLjYyNSA2VjEuMTI1TTEuMTI1IDEuMTI1aDNNNy44NzUgMS4xMjVoMyIvPjwvZz48L3N2Zz4=');
|
||||||
--icon-FunctionResult: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTQsMiBMMTIsMiBDMTMuMTA0NTY5NSwyIDE0LDIuODk1NDMwNSAxNCw0IEwxNCwxMiBDMTQsMTMuMTA0NTY5NSAxMy4xMDQ1Njk1LDE0IDEyLDE0IEw0LDE0IEMyLjg5NTQzMDUsMTQgMiwxMy4xMDQ1Njk1IDIsMTIgTDIsNCBDMiwyLjg5NTQzMDUgMi44OTU0MzA1LDIgNCwyIFogTTQuNSw2IEM0LjIyMzg1NzYzLDYgNCw2LjIyMzg1NzYzIDQsNi41IEM0LDYuNzc2MTQyMzcgNC4yMjM4NTc2Myw3IDQuNSw3IEwxMS41LDcgQzExLjc3NjE0MjQsNyAxMiw2Ljc3NjE0MjM3IDEyLDYuNSBDMTIsNi4yMjM4NTc2MyAxMS43NzYxNDI0LDYgMTEuNSw2IEw0LjUsNiBaIE00LjUsOSBDNC4yMjM4NTc2Myw5IDQsOS4yMjM4NTc2MyA0LDkuNSBDNCw5Ljc3NjE0MjM3IDQuMjIzODU3NjMsMTAgNC41LDEwIEwxMS41LDEwIEMxMS43NzYxNDI0LDEwIDEyLDkuNzc2MTQyMzcgMTIsOS41IEMxMiw5LjIyMzg1NzYzIDExLjc3NjE0MjQsOSAxMS41LDkgTDQuNSw5IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-FunctionResult: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTQsMiBMMTIsMiBDMTMuMTA0NTY5NSwyIDE0LDIuODk1NDMwNSAxNCw0IEwxNCwxMiBDMTQsMTMuMTA0NTY5NSAxMy4xMDQ1Njk1LDE0IDEyLDE0IEw0LDE0IEMyLjg5NTQzMDUsMTQgMiwxMy4xMDQ1Njk1IDIsMTIgTDIsNCBDMiwyLjg5NTQzMDUgMi44OTU0MzA1LDIgNCwyIFogTTQuNSw2IEM0LjIyMzg1NzYzLDYgNCw2LjIyMzg1NzYzIDQsNi41IEM0LDYuNzc2MTQyMzcgNC4yMjM4NTc2Myw3IDQuNSw3IEwxMS41LDcgQzExLjc3NjE0MjQsNyAxMiw2Ljc3NjE0MjM3IDEyLDYuNSBDMTIsNi4yMjM4NTc2MyAxMS43NzYxNDI0LDYgMTEuNSw2IEw0LjUsNiBaIE00LjUsOSBDNC4yMjM4NTc2Myw5IDQsOS4yMjM4NTc2MyA0LDkuNSBDNCw5Ljc3NjE0MjM3IDQuMjIzODU3NjMsMTAgNC41LDEwIEwxMS41LDEwIEMxMS43NzYxNDI0LDEwIDEyLDkuNzc2MTQyMzcgMTIsOS41IEMxMiw5LjIyMzg1NzYzIDExLjc3NjE0MjQsOSAxMS41LDkgTDQuNSw5IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
--icon-Help: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzA5MjY0NDMsMy40MTYzNzEyMSBDMS42NDQ0MDc2OSw0LjY0NDQzMjIyIDEsNi4yNDY5NjEzNyAxLDggQzEsOS43NTMwMzg2MyAxLjY0NDQwNzY5LDExLjM1NTU2NzggMi43MDkyNjQ0MywxMi41ODM2Mjg4IEw0Ljg0MDA3MTI5LDEwLjQ1MjgyMTkgQzQuMzEzNTQwOCw5Ljc3NTQ4MDA3IDQsOC45MjQzNTU3MyA0LDggQzQsNy4wNzU2NDQyNyA0LjMxMzU0MDgsNi4yMjQ1MTk5MyA0Ljg0MDA3MTI5LDUuNTQ3MTc4MDcgTDIuNzA5MjY0NDMsMy40MTYzNzEyMSBaIE0zLjQxNjM3MTIxLDIuNzA5MjY0NDMgTDUuNTQ3MTc4MDcsNC44NDAwNzEyOSBDNi4yMjQ1MTk5Myw0LjMxMzU0MDggNy4wNzU2NDQyNyw0IDgsNCBDOC45MjQzNTU3Myw0IDkuNzc1NDgwMDcsNC4zMTM1NDA4IDEwLjQ1MjgyMTksNC44NDAwNzEyOSBMMTIuNTgzNjI4OCwyLjcwOTI2NDQzIEMxMS4zNTU1Njc4LDEuNjQ0NDA3NjkgOS43NTMwMzg2MywxIDgsMSBDNi4yNDY5NjEzNywxIDQuNjQ0NDMyMjIsMS42NDQ0MDc2OSAzLjQxNjM3MTIxLDIuNzA5MjY0NDMgWiBNNS45MDE5ODczMSw1Ljg1NTYyMzc2IEM1Ljg5NDgyMDAxLDUuODYzNzgzMDcgNS44ODczNDIwNCw1Ljg3MTc2NDc0IDUuODc5NTUzMzksNS44Nzk1NTMzOSBDNS44NzE3NjQ3NCw1Ljg4NzM0MjA0IDUuODYzNzgzMDcsNS44OTQ4MjAwMSA1Ljg1NTYyMzc2LDUuOTAxOTg3MzEgQzUuMzI2Mjk1NSw2LjQ0MjkzOTMyIDUsNy4xODMzNjQ0NiA1LDggQzUsOC44MTY2MzU1NCA1LjMyNjI5NTUsOS41NTcwNjA2OCA1Ljg1NTYyMzc2LDEwLjA5ODAxMjcgQzUuODYzNzgzMDcsMTAuMTA1MTggNS44NzE3NjQ3NCwxMC4xMTI2NTggNS44Nzk1NTMzOSwxMC4xMjA0NDY2IEM1Ljg4NzM0MjA0LDEwLjEyODIzNTMgNS44OTQ4MjAwMSwxMC4xMzYyMTY5IDUuOTAxOTg3MzEsMTAuMTQ0Mzc2MiBDNi40NDI5MzkzMiwxMC42NzM3MDQ1IDcuMTgzMzY0NDYsMTEgOCwxMSBDOC44MTY2MzU1NCwxMSA5LjU1NzA2MDY4LDEwLjY3MzcwNDUgMTAuMDk4MDEyNywxMC4xNDQzNzYyIEMxMC4xMDUxOCwxMC4xMzYyMTY5IDEwLjExMjY1OCwxMC4xMjgyMzUzIDEwLjEyMDQ0NjYsMTAuMTIwNDQ2NiBDMTAuMTI4MjM1MywxMC4xMTI2NTggMTAuMTM2MjE2OSwxMC4xMDUxOCAxMC4xNDQzNzYyLDEwLjA5ODAxMjcgQzEwLjY3MzcwNDUsOS41NTcwNjA2OCAxMSw4LjgxNjYzNTU0IDExLDggQzExLDcuMTgzMzY0NDYgMTAuNjczNzA0NSw2LjQ0MjkzOTMyIDEwLjE0NDM3NjIsNS45MDE5ODczMSBDMTAuMTM2MjE2OSw1Ljg5NDgyMDAxIDEwLjEyODIzNTMsNS44ODczNDIwNCAxMC4xMjA0NDY2LDUuODc5NTUzMzkgQzEwLjExMjY1OCw1Ljg3MTc2NDc0IDEwLjEwNTE4LDUuODYzNzgzMDcgMTAuMDk4MDEyNyw1Ljg1NTYyMzc2IEM5LjU1NzA2MDY4LDUuMzI2Mjk1NSA4LjgxNjYzNTU0LDUgOCw1IEM3LjE4MzM2NDQ2LDUgNi40NDI5MzkzMiw1LjMyNjI5NTUgNS45MDE5ODczMSw1Ljg1NTYyMzc2IFogTTMuNDE2MzcxMjEsMTMuMjkwNzM1NiBDNC42NDQ0MzIyMiwxNC4zNTU1OTIzIDYuMjQ2OTYxMzcsMTUgOCwxNSBDOS43NTMwMzg2MywxNSAxMS4zNTU1Njc4LDE0LjM1NTU5MjMgMTIuNTgzNjI4OCwxMy4yOTA3MzU2IEwxMC40NTI4MjE5LDExLjE1OTkyODcgQzkuNzc1NDgwMDcsMTEuNjg2NDU5MiA4LjkyNDM1NTczLDEyIDgsMTIgQzcuMDc1NjQ0MjcsMTIgNi4yMjQ1MTk5MywxMS42ODY0NTkyIDUuNTQ3MTc4MDcsMTEuMTU5OTI4NyBMMy40MTYzNzEyMSwxMy4yOTA3MzU2IFogTTEzLjI5MDczNTYsMTIuNTgzNjI4OCBDMTQuMzU1NTkyMywxMS4zNTU1Njc4IDE1LDkuNzUzMDM4NjMgMTUsOCBDMTUsNi4yNDY5NjEzNyAxNC4zNTU1OTIzLDQuNjQ0NDMyMjIgMTMuMjkwNzM1NiwzLjQxNjM3MTIxIEwxMS4xNTk5Mjg3LDUuNTQ3MTc4MDcgQzExLjY4NjQ1OTIsNi4yMjQ1MTk5MyAxMiw3LjA3NTY0NDI3IDEyLDggQzEyLDguOTI0MzU1NzMgMTEuNjg2NDU5Miw5Ljc3NTQ4MDA3IDExLjE1OTkyODcsMTAuNDUyODIxOSBMMTMuMjkwNzM1NiwxMi41ODM2Mjg4IFogTTgsMTYgQzMuNTgxNzIyLDE2IDAsMTIuNDE4Mjc4IDAsOCBDMCwzLjU4MTcyMiAzLjU4MTcyMiwwIDgsMCBDMTIuNDE4Mjc4LDAgMTYsMy41ODE3MjIgMTYsOCBDMTYsMTIuNDE4Mjc4IDEyLjQxODI3OCwxNiA4LDE2IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-Help: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIuNzA5MjY0NDMsMy40MTYzNzEyMSBDMS42NDQ0MDc2OSw0LjY0NDQzMjIyIDEsNi4yNDY5NjEzNyAxLDggQzEsOS43NTMwMzg2MyAxLjY0NDQwNzY5LDExLjM1NTU2NzggMi43MDkyNjQ0MywxMi41ODM2Mjg4IEw0Ljg0MDA3MTI5LDEwLjQ1MjgyMTkgQzQuMzEzNTQwOCw5Ljc3NTQ4MDA3IDQsOC45MjQzNTU3MyA0LDggQzQsNy4wNzU2NDQyNyA0LjMxMzU0MDgsNi4yMjQ1MTk5MyA0Ljg0MDA3MTI5LDUuNTQ3MTc4MDcgTDIuNzA5MjY0NDMsMy40MTYzNzEyMSBaIE0zLjQxNjM3MTIxLDIuNzA5MjY0NDMgTDUuNTQ3MTc4MDcsNC44NDAwNzEyOSBDNi4yMjQ1MTk5Myw0LjMxMzU0MDggNy4wNzU2NDQyNyw0IDgsNCBDOC45MjQzNTU3Myw0IDkuNzc1NDgwMDcsNC4zMTM1NDA4IDEwLjQ1MjgyMTksNC44NDAwNzEyOSBMMTIuNTgzNjI4OCwyLjcwOTI2NDQzIEMxMS4zNTU1Njc4LDEuNjQ0NDA3NjkgOS43NTMwMzg2MywxIDgsMSBDNi4yNDY5NjEzNywxIDQuNjQ0NDMyMjIsMS42NDQ0MDc2OSAzLjQxNjM3MTIxLDIuNzA5MjY0NDMgWiBNNS45MDE5ODczMSw1Ljg1NTYyMzc2IEM1Ljg5NDgyMDAxLDUuODYzNzgzMDcgNS44ODczNDIwNCw1Ljg3MTc2NDc0IDUuODc5NTUzMzksNS44Nzk1NTMzOSBDNS44NzE3NjQ3NCw1Ljg4NzM0MjA0IDUuODYzNzgzMDcsNS44OTQ4MjAwMSA1Ljg1NTYyMzc2LDUuOTAxOTg3MzEgQzUuMzI2Mjk1NSw2LjQ0MjkzOTMyIDUsNy4xODMzNjQ0NiA1LDggQzUsOC44MTY2MzU1NCA1LjMyNjI5NTUsOS41NTcwNjA2OCA1Ljg1NTYyMzc2LDEwLjA5ODAxMjcgQzUuODYzNzgzMDcsMTAuMTA1MTggNS44NzE3NjQ3NCwxMC4xMTI2NTggNS44Nzk1NTMzOSwxMC4xMjA0NDY2IEM1Ljg4NzM0MjA0LDEwLjEyODIzNTMgNS44OTQ4MjAwMSwxMC4xMzYyMTY5IDUuOTAxOTg3MzEsMTAuMTQ0Mzc2MiBDNi40NDI5MzkzMiwxMC42NzM3MDQ1IDcuMTgzMzY0NDYsMTEgOCwxMSBDOC44MTY2MzU1NCwxMSA5LjU1NzA2MDY4LDEwLjY3MzcwNDUgMTAuMDk4MDEyNywxMC4xNDQzNzYyIEMxMC4xMDUxOCwxMC4xMzYyMTY5IDEwLjExMjY1OCwxMC4xMjgyMzUzIDEwLjEyMDQ0NjYsMTAuMTIwNDQ2NiBDMTAuMTI4MjM1MywxMC4xMTI2NTggMTAuMTM2MjE2OSwxMC4xMDUxOCAxMC4xNDQzNzYyLDEwLjA5ODAxMjcgQzEwLjY3MzcwNDUsOS41NTcwNjA2OCAxMSw4LjgxNjYzNTU0IDExLDggQzExLDcuMTgzMzY0NDYgMTAuNjczNzA0NSw2LjQ0MjkzOTMyIDEwLjE0NDM3NjIsNS45MDE5ODczMSBDMTAuMTM2MjE2OSw1Ljg5NDgyMDAxIDEwLjEyODIzNTMsNS44ODczNDIwNCAxMC4xMjA0NDY2LDUuODc5NTUzMzkgQzEwLjExMjY1OCw1Ljg3MTc2NDc0IDEwLjEwNTE4LDUuODYzNzgzMDcgMTAuMDk4MDEyNyw1Ljg1NTYyMzc2IEM5LjU1NzA2MDY4LDUuMzI2Mjk1NSA4LjgxNjYzNTU0LDUgOCw1IEM3LjE4MzM2NDQ2LDUgNi40NDI5MzkzMiw1LjMyNjI5NTUgNS45MDE5ODczMSw1Ljg1NTYyMzc2IFogTTMuNDE2MzcxMjEsMTMuMjkwNzM1NiBDNC42NDQ0MzIyMiwxNC4zNTU1OTIzIDYuMjQ2OTYxMzcsMTUgOCwxNSBDOS43NTMwMzg2MywxNSAxMS4zNTU1Njc4LDE0LjM1NTU5MjMgMTIuNTgzNjI4OCwxMy4yOTA3MzU2IEwxMC40NTI4MjE5LDExLjE1OTkyODcgQzkuNzc1NDgwMDcsMTEuNjg2NDU5MiA4LjkyNDM1NTczLDEyIDgsMTIgQzcuMDc1NjQ0MjcsMTIgNi4yMjQ1MTk5MywxMS42ODY0NTkyIDUuNTQ3MTc4MDcsMTEuMTU5OTI4NyBMMy40MTYzNzEyMSwxMy4yOTA3MzU2IFogTTEzLjI5MDczNTYsMTIuNTgzNjI4OCBDMTQuMzU1NTkyMywxMS4zNTU1Njc4IDE1LDkuNzUzMDM4NjMgMTUsOCBDMTUsNi4yNDY5NjEzNyAxNC4zNTU1OTIzLDQuNjQ0NDMyMjIgMTMuMjkwNzM1NiwzLjQxNjM3MTIxIEwxMS4xNTk5Mjg3LDUuNTQ3MTc4MDcgQzExLjY4NjQ1OTIsNi4yMjQ1MTk5MyAxMiw3LjA3NTY0NDI3IDEyLDggQzEyLDguOTI0MzU1NzMgMTEuNjg2NDU5Miw5Ljc3NTQ4MDA3IDExLjE1OTkyODcsMTAuNDUyODIxOSBMMTMuMjkwNzM1NiwxMi41ODM2Mjg4IFogTTgsMTYgQzMuNTgxNzIyLDE2IDAsMTIuNDE4Mjc4IDAsOCBDMCwzLjU4MTcyMiAzLjU4MTcyMiwwIDgsMCBDMTIuNDE4Mjc4LDAgMTYsMy41ODE3MjIgMTYsOCBDMTYsMTIuNDE4Mjc4IDEyLjQxODI3OCwxNiA4LDE2IFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
--icon-Home: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsMi4wODMwOTUxOSBMMS40NzE4MjUzMiw2IEw4LDkuOTE2OTA0ODEgTDE0LjUyODE3NDcsNiBMOCwyLjA4MzA5NTE5IFogTTguMjU3MjQ3ODgsMS4wNzEyNTM1NCBMMTUuNzU3MjQ3OSw1LjU3MTI1MzU0IEMxNi4wODA5MTc0LDUuNzY1NDU1MjMgMTYuMDgwOTE3NCw2LjIzNDU0NDc3IDE1Ljc1NzI0NzksNi40Mjg3NDY0NiBMOC4yNTcyNDc4OCwxMC45Mjg3NDY1IEM4LjA5ODkwNjY4LDExLjAyMzc1MTIgNy45MDEwOTMzMiwxMS4wMjM3NTEyIDcuNzQyNzUyMTIsMTAuOTI4NzQ2NSBMMC4yNDI3NTIxMjIsNi40Mjg3NDY0NiBDLTAuMDgwOTE3Mzc0MSw2LjIzNDU0NDc3IC0wLjA4MDkxNzM3NDEsNS43NjU0NTUyMyAwLjI0Mjc1MjEyMiw1LjU3MTI1MzU0IEw3Ljc0Mjc1MjEyLDEuMDcxMjUzNTQgQzcuOTAxMDkzMzIsMC45NzYyNDg4MjEgOC4wOTg5MDY2OCwwLjk3NjI0ODgyMSA4LjI1NzI0Nzg4LDEuMDcxMjUzNTQgWiBNMTQuNTI4MTc0NywxMCBMMTMuNzQyNzUyMSw5LjUyODc0NjQ2IEMxMy41MDU5NjIsOS4zODY2NzIzOCAxMy40MjkxNzk1LDkuMDc5NTQyMjYgMTMuNTcxMjUzNSw4Ljg0Mjc1MjEyIEMxMy43MTMzMjc2LDguNjA1OTYxOTkgMTQuMDIwNDU3Nyw4LjUyOTE3OTQ2IDE0LjI1NzI0NzksOC42NzEyNTM1NCBMMTUuNzU3MjQ3OSw5LjU3MTI1MzU0IEMxNi4wODA5MTc0LDkuNzY1NDU1MjMgMTYuMDgwOTE3NCwxMC4yMzQ1NDQ4IDE1Ljc1NzI0NzksMTAuNDI4NzQ2NSBMOC4yNTcyNDc4OCwxNC45Mjg3NDY1IEM4LjA5ODkwNjY4LDE1LjAyMzc1MTIgNy45MDEwOTMzMiwxNS4wMjM3NTEyIDcuNzQyNzUyMTIsMTQuOTI4NzQ2NSBMMC4yNDI3NTIxMjIsMTAuNDI4NzQ2NSBDLTAuMDgwOTE3Mzc0MSwxMC4yMzQ1NDQ4IC0wLjA4MDkxNzM3NDEsOS43NjU0NTUyMyAwLjI0Mjc1MjEyMiw5LjU3MTI1MzU0IEwxLjc0Mjc1MjEyLDguNjcxMjUzNTQgQzEuOTc5NTQyMjYsOC41MjkxNzk0NiAyLjI4NjY3MjM4LDguNjA1OTYxOTkgMi40Mjg3NDY0Niw4Ljg0Mjc1MjEyIEMyLjU3MDgyMDU0LDkuMDc5NTQyMjYgMi40OTQwMzgwMSw5LjM4NjY3MjM4IDIuMjU3MjQ3ODgsOS41Mjg3NDY0NiBMMS40NzE4MjUzMiwxMCBMOCwxMy45MTY5MDQ4IEwxNC41MjgxNzQ3LDEwIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
--icon-Home: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTgsMi4wODMwOTUxOSBMMS40NzE4MjUzMiw2IEw4LDkuOTE2OTA0ODEgTDE0LjUyODE3NDcsNiBMOCwyLjA4MzA5NTE5IFogTTguMjU3MjQ3ODgsMS4wNzEyNTM1NCBMMTUuNzU3MjQ3OSw1LjU3MTI1MzU0IEMxNi4wODA5MTc0LDUuNzY1NDU1MjMgMTYuMDgwOTE3NCw2LjIzNDU0NDc3IDE1Ljc1NzI0NzksNi40Mjg3NDY0NiBMOC4yNTcyNDc4OCwxMC45Mjg3NDY1IEM4LjA5ODkwNjY4LDExLjAyMzc1MTIgNy45MDEwOTMzMiwxMS4wMjM3NTEyIDcuNzQyNzUyMTIsMTAuOTI4NzQ2NSBMMC4yNDI3NTIxMjIsNi40Mjg3NDY0NiBDLTAuMDgwOTE3Mzc0MSw2LjIzNDU0NDc3IC0wLjA4MDkxNzM3NDEsNS43NjU0NTUyMyAwLjI0Mjc1MjEyMiw1LjU3MTI1MzU0IEw3Ljc0Mjc1MjEyLDEuMDcxMjUzNTQgQzcuOTAxMDkzMzIsMC45NzYyNDg4MjEgOC4wOTg5MDY2OCwwLjk3NjI0ODgyMSA4LjI1NzI0Nzg4LDEuMDcxMjUzNTQgWiBNMTQuNTI4MTc0NywxMCBMMTMuNzQyNzUyMSw5LjUyODc0NjQ2IEMxMy41MDU5NjIsOS4zODY2NzIzOCAxMy40MjkxNzk1LDkuMDc5NTQyMjYgMTMuNTcxMjUzNSw4Ljg0Mjc1MjEyIEMxMy43MTMzMjc2LDguNjA1OTYxOTkgMTQuMDIwNDU3Nyw4LjUyOTE3OTQ2IDE0LjI1NzI0NzksOC42NzEyNTM1NCBMMTUuNzU3MjQ3OSw5LjU3MTI1MzU0IEMxNi4wODA5MTc0LDkuNzY1NDU1MjMgMTYuMDgwOTE3NCwxMC4yMzQ1NDQ4IDE1Ljc1NzI0NzksMTAuNDI4NzQ2NSBMOC4yNTcyNDc4OCwxNC45Mjg3NDY1IEM4LjA5ODkwNjY4LDE1LjAyMzc1MTIgNy45MDEwOTMzMiwxNS4wMjM3NTEyIDcuNzQyNzUyMTIsMTQuOTI4NzQ2NSBMMC4yNDI3NTIxMjIsMTAuNDI4NzQ2NSBDLTAuMDgwOTE3Mzc0MSwxMC4yMzQ1NDQ4IC0wLjA4MDkxNzM3NDEsOS43NjU0NTUyMyAwLjI0Mjc1MjEyMiw5LjU3MTI1MzU0IEwxLjc0Mjc1MjEyLDguNjcxMjUzNTQgQzEuOTc5NTQyMjYsOC41MjkxNzk0NiAyLjI4NjY3MjM4LDguNjA1OTYxOTkgMi40Mjg3NDY0Niw4Ljg0Mjc1MjEyIEMyLjU3MDgyMDU0LDkuMDc5NTQyMjYgMi40OTQwMzgwMSw5LjM4NjY3MjM4IDIuMjU3MjQ3ODgsOS41Mjg3NDY0NiBMMS40NzE4MjUzMiwxMCBMOCwxMy45MTY5MDQ4IEwxNC41MjgxNzQ3LDEwIFoiIGZpbGw9IiMwMDAiIGZpbGwtcnVsZT0ibm9uemVybyIvPjwvc3ZnPg==');
|
||||||
|
17
static/ui-icons/UI/Empty.svg
Normal file
17
static/ui-icons/UI/Empty.svg
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 5.2916665 5.2916668">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<g
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-291.70832)">
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
|
||||||
|
d="m 0.03262263,291.72225 5.23003227,5.2769" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 560 B |
6
static/ui-icons/UI/FontBold.svg
Normal file
6
static/ui-icons/UI/FontBold.svg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 16 16" version="1.1">
|
||||||
|
<path d="m 3.1950988,1.8354199 h 5.6903818 c 0.7545952,0 1.4782724,0.2997567 2.0118534,0.8333375 0.533581,0.5335808 0.833337,1.2572582 0.833337,2.0118535 0,0.7545952 -0.299756,1.4782727 -0.833337,2.0118535 C 10.363753,7.2260451 9.6400758,7.5258018 8.8854806,7.5258018 H 5.0918927" id="path3414" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#030000;stroke-width:1.26452935;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
<path d="m 5.0918927,7.5258018 h 5.2161833 c 0.880352,0 1.724653,0.3497182 2.347169,0.9722207 0.622502,0.6225152 0.97222,1.4668161 0.97222,2.3471685 v 0 c 0,0.880353 -0.349718,1.724654 -0.97222,2.347219 -0.622516,0.622402 -1.466817,0.97217 -2.347169,0.97217 H 3.1950988" id="path3416" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#030000;stroke-width:1.26452935;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
<path d="M 5.0918927,1.8354199 V 14.16458" id="path3418" style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#030000;stroke-width:1.26452935;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
16
static/ui-icons/UI/FontItalic.svg
Normal file
16
static/ui-icons/UI/FontItalic.svg
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 16 16" version="1.1">
|
||||||
|
<defs id="defs5379">
|
||||||
|
<clipPath id="clip0_635_11941">
|
||||||
|
<rect style="fill:#ffffff" y="0" x="0" width="12" height="12" id="rect4748" />
|
||||||
|
</clipPath>
|
||||||
|
<clipPath id="clip0_635_11917">
|
||||||
|
<rect style="fill:#ffffff" y="0" x="0" width="12" height="12" id="rect6069" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g transform="matrix(1.1355932,0,0,1.1355932,0.98305088,1.3898305)" clip-path="url(#clip0_635_11917)" id="g6067" style="fill-rule:evenodd;fill:none">
|
||||||
|
<path style="stroke:#000000;stroke-linecap:round;stroke-linejoin:round;fill-rule:evenodd;fill:none;stroke-opacity:1" d="m 4.875,0.375 h 4.5" id="path6061" />
|
||||||
|
<path style="stroke:#060000;stroke-linecap:round;stroke-linejoin:round;fill-rule:evenodd;fill:none;stroke-opacity:1" d="m 2.625,11.625 h 4.5" id="path6063" />
|
||||||
|
<path style="stroke:#000000;stroke-linecap:round;stroke-linejoin:round;fill-rule:evenodd;fill:none;stroke-opacity:0.99024391" d="m 7.125,0.375 -2.25,11.25" id="path6065" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
9
static/ui-icons/UI/FontStrikethrough.svg
Normal file
9
static/ui-icons/UI/FontStrikethrough.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 16 16" version="1.1">
|
||||||
|
<defs id="defs2753" />
|
||||||
|
<path d="M 1.8056585,3.397172 V 1.7357431 H 14.194342 V 3.397172" id="path2655" style="fill:none;fill-rule:evenodd;stroke:#929299;stroke-width:1.1431762;stroke-linecap:round;stroke-linejoin:round" />
|
||||||
|
<path d="M 7.9999999,10.042887 V 14.19646" id="path2657" style="stroke:#929299;stroke-width:1.1431762;stroke-linecap:round;stroke-linejoin:round" />
|
||||||
|
<path d="M 7.9999999,1.7357431 V 7.550744" id="path2659" style="fill:none;fill-rule:evenodd;stroke:#929299;stroke-width:1.1431762;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path d="M 5.3452822,14.19646 H 10.654718" id="path2661" style="stroke:#929299;stroke-width:1.1431762;stroke-linecap:round;stroke-linejoin:round" />
|
||||||
|
<path d="M 1.8056585,7.550744 H 14.194342" id="path2663" style="stroke:#929299;stroke-width:1.1431762;stroke-linecap:round;stroke-linejoin:round" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
44
static/ui-icons/UI/FontUnderline.svg
Normal file
44
static/ui-icons/UI/FontUnderline.svg
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="16px"
|
||||||
|
height="16px"
|
||||||
|
viewBox="0 0 16 16"
|
||||||
|
version="1.1">
|
||||||
|
<defs
|
||||||
|
id="defs5379">
|
||||||
|
<clipPath
|
||||||
|
id="clip0_635_11941">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff"
|
||||||
|
y="0"
|
||||||
|
x="0"
|
||||||
|
width="12"
|
||||||
|
height="12"
|
||||||
|
id="rect4748" />
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
<g
|
||||||
|
transform="matrix(1.1429539,0,0,1.1429539,1.1422766,0.72137666)"
|
||||||
|
clip-path="url(#clip0_635_11941)"
|
||||||
|
id="g4746"
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-opacity:1">
|
||||||
|
<path
|
||||||
|
d="m 0.375,11.625 h 11.25"
|
||||||
|
id="path4738"
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="M 9.375,1.125 V 6 C 9.375,6.89511 9.01942,7.75355 8.38649,8.38649 7.75355,9.01942 6.89511,9.375 6,9.375 v 0 C 5.10489,9.375 4.24645,9.01942 3.61351,8.38649 2.98058,7.75355 2.625,6.89511 2.625,6 V 1.125"
|
||||||
|
id="path4740"
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 1.125,1.125 h 3"
|
||||||
|
id="path4742"
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
d="m 7.875,1.125 h 3"
|
||||||
|
id="path4744"
|
||||||
|
style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.6 KiB |
@ -1601,6 +1601,28 @@ export function session(): Session {
|
|||||||
return Session.default;
|
return Session.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets font style in opened color picker.
|
||||||
|
*/
|
||||||
|
export async function setFont(type: 'bold'|'underline'|'italic'|'strikethrough', onOff: boolean|number) {
|
||||||
|
const optionToClass = {
|
||||||
|
bold: '.test-font-option-FontBold',
|
||||||
|
italic: '.test-font-option-FontItalic',
|
||||||
|
underline: '.test-font-option-FontUnderline',
|
||||||
|
strikethrough: '.test-font-option-FontStrikethrough',
|
||||||
|
};
|
||||||
|
async function clickFontOption() {
|
||||||
|
await driver.find(optionToClass[type]).click();
|
||||||
|
}
|
||||||
|
async function isFontOption() {
|
||||||
|
return (await driver.findAll(`${optionToClass[type]}[class*=-selected]`)).length === 1;
|
||||||
|
}
|
||||||
|
const current = await isFontOption();
|
||||||
|
if (onOff && !current || !onOff && current) {
|
||||||
|
await clickFontOption();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Set the value of an `<input type="color">` element to `color` and trigger the `change`
|
// Set the value of an `<input type="color">` element to `color` and trigger the `change`
|
||||||
// event. Accepts `color` to be of following forms `rgb(120, 10, 3)` or '#780a03'.
|
// event. Accepts `color` to be of following forms `rgb(120, 10, 3)` or '#780a03'.
|
||||||
export async function setColor(colorInputEl: WebElement, color: string) {
|
export async function setColor(colorInputEl: WebElement, color: string) {
|
||||||
|
Loading…
Reference in New Issue
Block a user