Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
seancolsen committed Jul 25, 2024
1 parent 093952e commit 292db28
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 183 deletions.
206 changes: 100 additions & 106 deletions mathesar_ui/src/api/rpc/columns.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-builder';

type BooleanInputType = 'checkbox' | 'dropdown';

/**
* | value | example locale | example format |
* | --------- | -------------- | -------------- |
Expand All @@ -12,109 +14,131 @@ import { rpcMethodTypeContainer } from '@mathesar/packages/json-rpc-client-build
export type NumberFormat = 'english' | 'german' | 'french' | 'hindi' | 'swiss';

/**
* This common for both Number and Money types
* | value | formatting pattern |
* | ---------------- | -------------------------------------------- |
* | 'after-minus' | {minus_sign}{currency_symbol}{number} |
* | 'end-with-space' | {minus_sign}{number}{space}{currency_symbol} |
*/
interface FormattedNumberDisplayOptions {
/** When `null`, the browser's locale will be used. */
number_format: NumberFormat | null;
export type CurrencyLocation = 'after-minus' | 'end-with-space';

export type DurationUnit = 'd' | 'h' | 'm' | 's' | 'ms';

export type DateFormat = 'none' | 'us' | 'eu' | 'friendly' | 'iso';

export type TimeFormat = '24hr' | '12hr' | '24hrLong' | '12hrLong';

/**
* See the [Postgres docs][1] for an explanation of `scale` and `precision`.
*
* [1]: https://www.postgresql.org/docs/current/datatype-numeric.html
*/
interface ColumnTypeOptions {
/**
* - "true": display grouping separators even if the locale prefers otherwise.
* - "false": do not display grouping separators.
* For numeric types, the number of significant digits. For date/time types,
* the number of fractional digits.
*/
use_grouping: 'true' | 'false';
precision?: number | null;

/** For numeric types, the number of fractional digits. */
scale?: number | null;

/** Which time fields are stored. See Postgres docs. */
fields?: string | null;

/** The maximum length of a character-type field. */
length?: number | null;

minimum_fraction_digits: number | null;
maximum_fraction_digits: number | null;
/** The member type for arrays. */
item_type?: string | null;
}

export interface NumberDisplayOptions
extends Record<string, unknown>,
FormattedNumberDisplayOptions {}
/**
* The column display options values, typed as we need in order to render them
* in the UI.
*/
interface RequiredColumnDisplayOptions {
/** The type of input used for boolean values */
bool_input: BooleanInputType;

export interface MoneyDisplayOptions extends FormattedNumberDisplayOptions {
/**
* e.g. "$", "€", "NZD", etc.
*/
currency_symbol: string;
/** The text to display for a boolean `true` value */
bool_true: string;

/**
* | value | formatting pattern |
* | ---------------- | -------------------------------------------- |
* | 'after-minus' | {minus_sign}{currency_symbol}{number} |
* | 'end-with-space' | {minus_sign}{number}{space}{currency_symbol} |
*/
currency_symbol_location: 'after-minus' | 'end-with-space';
/** The text to display for a boolean `false` value */
bool_false: string;

/** The minimum number of fraction digits to display for a number */
num_min_frac_digits: number;

/** The maximum number of fraction digits to display for a number */
num_max_frac_digits: number;

/** When `null`, the browser's locale will be used. */
num_format: NumberFormat | null;

/**
* PLANNED FOR FUTURE IMPLEMENTATION POST-ALPHA.
* - `null`: display grouping separators if the locale prefers it.
* - `true`: display grouping separators.
* - `false`: do not display grouping separators.
*/
// use_accounting_notation: boolean;
}
num_grouping: boolean | null;

export interface TextTypeOptions extends Record<string, unknown> {
length: number | null;
}
/** The currency symbol to show for a money type e.g. "$", "€", "NZD", etc. */
mon_currency_symbol: string;

export interface BooleanDisplayOptions extends Record<string, unknown> {
input: 'checkbox' | 'dropdown' | null;
custom_labels: {
TRUE: string;
FALSE: string;
} | null;
}
mon_currency_location: CurrencyLocation;

export type DurationUnit = 'd' | 'h' | 'm' | 's' | 'ms';
time_format: TimeFormat;

export interface DurationDisplayOptions extends Record<string, unknown> {
min: DurationUnit | null;
max: DurationUnit | null;
show_units: boolean | null;
}
date_format: DateFormat;

export type DateFormat = 'none' | 'us' | 'eu' | 'friendly' | 'iso';
duration_min: DurationUnit;

export interface DateDisplayOptions extends Record<string, unknown> {
format: DateFormat | null;
}

export type TimeFormat = '24hr' | '12hr' | '24hrLong' | '12hrLong';
duration_max: DurationUnit;

export interface TimeDisplayOptions extends Record<string, unknown> {
format: TimeFormat | null;
duration_show_units: boolean;
}

export interface TimeStampDisplayOptions extends Record<string, unknown> {
date_format: DateDisplayOptions['format'];
time_format: TimeDisplayOptions['format'];
}
/** The column display options values, types as we get them from the API. */
type ColumnDisplayOptions = {
[K in keyof RequiredColumnDisplayOptions]?:
| RequiredColumnDisplayOptions[K]
| null;
};

// TODO_BETA: evaluate whether these are the appropriate defaults
const defaultColumnDisplayOptions: RequiredColumnDisplayOptions = {
bool_input: 'checkbox',
bool_true: 'true',
bool_false: 'false',
num_min_frac_digits: 0,
num_max_frac_digits: 20,
num_format: null,
num_grouping: null,
mon_currency_symbol: '$',
mon_currency_location: 'after-minus',
time_format: '24hr',
date_format: 'none',
duration_min: 's',
duration_max: 'm',
duration_show_units: true,
};

/**
* See the [Postgres docs][1] for an explanation of `scale` and `precision`.
*
* [1]: https://www.postgresql.org/docs/current/datatype-numeric.html
* Gets a display option value from a column, if present. Otherwise returns the
* default value for that display option.
*/
interface ColumnTypeOptions {
/**
* For numeric types, the number of significant digits. For date/time types,
* the number of fractional digits.
*/
precision?: number;
/** For numeric types, the number of fractional digits. */
scale?: number;
/** Which time fields are stored. See Postgres docs. */
fields?: string;
/** The maximum length of a character-type field. */
length?: number;
/** The member type for arrays. */
item_type?: string;
export function getColumnDisplayOption<
Option extends keyof RequiredColumnDisplayOptions,
>(column: Pick<Column, 'display_options'>, opt: Option) {
return column.display_options?.[opt] ?? defaultColumnDisplayOptions[opt];
}

interface ColumnDefault {
value: string;
is_dynamic: boolean;
}

/** The raw column data, from the user database only */
interface RawColumn {
/** The PostgreSQL attnum of the column */
id: number;
Expand All @@ -130,41 +154,11 @@ interface RawColumn {
valid_target_types: string[];
}

/**
* The raw column data from the user database combined with Mathesar's metadata
*/
export interface Column extends RawColumn {
display_options: Record<string, unknown> | null; // TODO_BETA
}

export interface MathesarMoneyColumn extends Column {
type: 'MATHESAR_TYPES.MATHESAR_MONEY';
display_options: Partial<MoneyDisplayOptions> | null;
}

export interface PostgresMoneyColumn extends Column {
type: 'MONEY';
type_options: null;
display_options: Partial<MoneyDisplayOptions> | null;
}

export type MoneyColumn = MathesarMoneyColumn | PostgresMoneyColumn;

// TODO: Remove specification of DB types here
export interface NumberColumn extends Column {
type:
| 'BIGINT'
| 'BIGSERIAL'
| 'DECIMAL'
| 'DOUBLE PRECISION'
| 'INTEGER'
| 'NUMERIC'
| 'REAL'
| 'SERIAL'
| 'SMALLINT'
| 'SMALLSERIAL';
display_options: Partial<NumberDisplayOptions> | null;
}

export interface ArrayTypeOptions extends Record<string, unknown> {
item_type: string;
display_options: ColumnDisplayOptions | null;
}

export interface ColumnCreationSpec {
Expand Down
25 changes: 9 additions & 16 deletions mathesar_ui/src/components/cell-fabric/data-types/datetime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { TimeStampDisplayOptions } from '@mathesar/api/rpc/columns';
import type { Column } from '@mathesar/api/rpc/columns';
import {
DateTimeFormatter,
DateTimeSpecification,
Expand All @@ -9,19 +9,15 @@ import type { ComponentAndProps } from '@mathesar-component-library/types';
import DateTimeCell from './components/date-time/DateTimeCell.svelte';
import DateTimeInput from './components/date-time/DateTimeInput.svelte';
import type { DateTimeCellExternalProps } from './components/typeDefinitions';
import type { CellColumnLike, CellComponentFactory } from './typeDefinitions';

export interface DateLikeColumn extends CellColumnLike {
display_options: Partial<TimeStampDisplayOptions> | null;
}
import type { CellComponentFactory } from './typeDefinitions';

function getProps(
column: DateLikeColumn,
column: Column,
supportTimeZone: boolean,
): DateTimeCellExternalProps {
const displayOptions = column.display_options ?? {};
const dateFormat = displayOptions.date_format ?? 'none';
const timeFormat = displayOptions.time_format ?? '24hr';
const displayOptions = column.display_options;
const dateFormat = displayOptions?.date_format ?? 'none';
const timeFormat = displayOptions?.time_format ?? '24hr';
const specification = new DateTimeSpecification({
type: supportTimeZone ? 'timestampWithTZ' : 'timestamp',
dateFormat,
Expand All @@ -47,14 +43,14 @@ function getProps(

const datetimeType: CellComponentFactory = {
get: (
column: DateLikeColumn,
column: Column,
config?: { supportTimeZone?: boolean },
): ComponentAndProps<DateTimeCellExternalProps> => ({
component: DateTimeCell,
props: getProps(column, config?.supportTimeZone ?? false),
}),
getInput: (
column: DateLikeColumn,
column: Column,
config?: { supportTimeZone?: boolean },
): ComponentAndProps<
Omit<DateTimeCellExternalProps, 'formatForDisplay'>
Expand All @@ -65,10 +61,7 @@ const datetimeType: CellComponentFactory = {
allowRelativePresets: true,
},
}),
getDisplayFormatter(
column: DateLikeColumn,
config?: { supportTimeZone?: boolean },
) {
getDisplayFormatter(column: Column, config?: { supportTimeZone?: boolean }) {
return (v) =>
getProps(column, config?.supportTimeZone ?? false).formatForDisplay(
String(v),
Expand Down
16 changes: 8 additions & 8 deletions mathesar_ui/src/components/cell-fabric/data-types/money.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MoneyColumn, NumberFormat } from '@mathesar/api/rpc/columns';
import type { Column, NumberFormat } from '@mathesar/api/rpc/columns';
import {
StringifiedNumberFormatter,
isDefinedNonNullable,
Expand All @@ -24,19 +24,19 @@ const localeMap = new Map<NumberFormat, string>([
['swiss' , 'de-CH' ],
]);

function moneyColumnIsInteger(column: MoneyColumn): boolean {
function ColumnIsInteger(column: Column): boolean {
return (column.type_options?.scale ?? Infinity) === 0;
}

function getFormatterOptions(
column: MoneyColumn,
column: Column,
): MoneyCellExternalProps['formatterOptions'] {
const displayOptions = column.display_options;
const format = displayOptions?.number_format ?? null;
return {
locale: (format && localeMap.get(format)) ?? undefined,
useGrouping: getUseGrouping(displayOptions?.use_grouping ?? 'true'),
allowFloat: !moneyColumnIsInteger(column),
allowFloat: !ColumnIsInteger(column),
allowNegative: true,
minimumFractionDigits: displayOptions?.minimum_fraction_digits ?? undefined,
maximumFractionDigits: displayOptions?.maximum_fraction_digits ?? undefined,
Expand All @@ -47,7 +47,7 @@ function getFormatterOptions(
};
}

function getProps(column: MoneyColumn): MoneyCellExternalProps {
function getProps(column: Column): MoneyCellExternalProps {
const formatterOptions = getFormatterOptions(column);
const displayFormatter = new StringifiedNumberFormatter(formatterOptions);
const insertCurrencySymbol = (() => {
Expand Down Expand Up @@ -75,15 +75,15 @@ function getProps(column: MoneyColumn): MoneyCellExternalProps {
}

const moneyType: CellComponentFactory = {
get(column: MoneyColumn): ComponentAndProps<MoneyCellExternalProps> {
get(column: Column): ComponentAndProps<MoneyCellExternalProps> {
return {
component: MoneyCell,
props: getProps(column),
};
},

getInput(
column: MoneyColumn,
column: Column,
): ComponentAndProps<MoneyCellExternalProps['formatterOptions']> {
return {
component: MoneyCellInput,
Expand All @@ -94,7 +94,7 @@ const moneyType: CellComponentFactory = {
};
},

getDisplayFormatter(column: MoneyColumn) {
getDisplayFormatter(column: Column) {
return (v) => getProps(column).formatForDisplay(String(v));
},
};
Expand Down
Loading

0 comments on commit 292db28

Please sign in to comment.