mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
8be920dd25
Summary: Creator panel allows now to edit multiple columns at once for some options that are common for them. Options that are not common are disabled. List of options that can be edited for multiple columns: - Column behavior (but limited to empty/formula columns) - Alignment and wrapping - Default style - Number options (for numeric columns) - Column types (but only for empty/formula columns) If multiple columns of the same type are selected, most of the options are available to change, except formula, trigger formula and conditional styles. Editing column label or column id is disabled by default for multiple selection. Not related: some tests were fixed due to the change in the column label and id widget in grist-core (disabled attribute was replaced by readonly). Test Plan: Updated and new tests. Reviewers: georgegevoian Reviewed By: georgegevoian Differential Revision: https://phab.getgrist.com/D3598
93 lines
3.9 KiB
TypeScript
93 lines
3.9 KiB
TypeScript
/**
|
|
* Here are the most relevant formats we want to support.
|
|
* -1234.56 Plain
|
|
* -1,234.56 Number (with separators)
|
|
* 12.34% Percent
|
|
* 1.23E3 Scientific
|
|
* $(1,234.56) Accounting
|
|
* (1,234.56) Financial
|
|
* -$1,234.56 Currency
|
|
*
|
|
* We implement a button-based UI, using one selector button to choose mode:
|
|
* none = NumMode undefined (plain number, no thousand separators)
|
|
* `$` = NumMode 'currency'
|
|
* `,` = NumMode 'decimal' (plain number, with thousand separators)
|
|
* `%` = NumMode 'percent'
|
|
* `Exp` = NumMode 'scientific'
|
|
* A second toggle button is `(-)` for Sign, to use parentheses rather than "-" for negative
|
|
* numbers. It is Ignored and disabled when mode is 'scientific'.
|
|
*/
|
|
|
|
import {clamp} from 'app/common/gutil';
|
|
import {StringUnion} from 'app/common/StringUnion';
|
|
import * as LocaleCurrency from "locale-currency";
|
|
import {FormatOptions} from 'app/common/ValueFormatter';
|
|
import {DocumentSettings} from 'app/common/DocumentSettings';
|
|
|
|
// Options for number formatting.
|
|
export const NumMode = StringUnion('currency', 'decimal', 'percent', 'scientific');
|
|
export type NumMode = typeof NumMode.type;
|
|
export type NumSign = 'parens';
|
|
|
|
export interface NumberFormatOptions extends FormatOptions {
|
|
numMode?: NumMode|null;
|
|
numSign?: NumSign|null;
|
|
decimals?: number|null; // aka minimum fraction digits
|
|
maxDecimals?: number|null;
|
|
currency?: string|null;
|
|
}
|
|
|
|
export function getCurrency(options: NumberFormatOptions, docSettings: DocumentSettings): string {
|
|
return options.currency || docSettings.currency || LocaleCurrency.getCurrency(docSettings.locale ?? 'en-US');
|
|
}
|
|
|
|
export function buildNumberFormat(options: NumberFormatOptions, docSettings: DocumentSettings): Intl.NumberFormat {
|
|
const currency = getCurrency(options, docSettings);
|
|
const nfOptions: Intl.NumberFormatOptions = parseNumMode(options.numMode, currency);
|
|
// numSign is implemented outside of Intl.NumberFormat since the latter's similar 'currencySign'
|
|
// option is not well-supported, and doesn't apply to non-currency formats.
|
|
|
|
if (options.decimals !== undefined && options.decimals !== null) {
|
|
// Should be at least 0
|
|
nfOptions.minimumFractionDigits = clamp(Number(options.decimals), 0, 20);
|
|
}
|
|
|
|
// maximumFractionDigits must not be less than the minimum, so we need to know the minimum
|
|
// implied by numMode.
|
|
const tmp = new Intl.NumberFormat(docSettings.locale, nfOptions).resolvedOptions();
|
|
|
|
if (options.maxDecimals !== undefined && options.maxDecimals !== null) {
|
|
// Should be at least 0 and at least minimumFractionDigits.
|
|
nfOptions.maximumFractionDigits = clamp(Number(options.maxDecimals), tmp.minimumFractionDigits || 0, 20);
|
|
} else if (!options.numMode) {
|
|
// For the default format, keep max digits at 10 as we had before.
|
|
nfOptions.maximumFractionDigits = clamp(10, tmp.minimumFractionDigits || 0, 20);
|
|
}
|
|
|
|
return new Intl.NumberFormat(docSettings.locale, nfOptions);
|
|
}
|
|
|
|
// Safari 13 and some other browsers don't support narrowSymbol option:
|
|
// https://github.com/mdn/browser-compat-data/issues/8985
|
|
// https://caniuse.com/?search=currencyDisplay
|
|
const currencyDisplay = (function(){
|
|
try {
|
|
new Intl.NumberFormat('en-US', {style: 'currency', currency: 'USD', currencyDisplay: 'narrowSymbol'});
|
|
return 'narrowSymbol';
|
|
} catch(err) {
|
|
return 'symbol';
|
|
}
|
|
})();
|
|
|
|
export function parseNumMode(numMode?: NumMode|null, currency?: string): Intl.NumberFormatOptions {
|
|
switch (numMode) {
|
|
case 'currency': return {style: 'currency', currency, currencyDisplay};
|
|
case 'decimal': return {useGrouping: true};
|
|
case 'percent': return {style: 'percent'};
|
|
// TODO 'notation' option (and therefore numMode 'scientific') works on recent Firefox and
|
|
// Chrome, not on Safari or Node 10.
|
|
case 'scientific': return {notation: 'scientific'} as Intl.NumberFormatOptions;
|
|
default: return {useGrouping: false};
|
|
}
|
|
}
|