@vuu-ui/vuu-utils 4.0.0-alpha.2 → 4.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/package.json +9 -8
  2. package/src/array-utils.js +17 -5
  3. package/src/column-utils.js +28 -10
  4. package/src/component-registry.js +2 -1
  5. package/src/data-utils.js +1 -0
  6. package/src/datasource/BaseDataSource.js +5 -3
  7. package/src/date/EpochTimestamp.js +85 -0
  8. package/src/date/date-utils.js +50 -18
  9. package/src/date/dateTimePattern.js +33 -5
  10. package/src/date/formatter.js +54 -16
  11. package/src/date/index.js +5 -2
  12. package/src/date/temporal.js +131 -0
  13. package/src/date/time-zone.js +56 -0
  14. package/src/feature-utils.js +7 -1
  15. package/src/filters/filter-utils.js +13 -7
  16. package/src/filters/filterAsQuery.js +107 -45
  17. package/src/filters/index.js +1 -0
  18. package/src/filters/temporal-cell-filter.js +63 -0
  19. package/src/form-utils.js +44 -6
  20. package/src/formatting-utils.js +46 -25
  21. package/src/html-utils.js +35 -1
  22. package/src/keyset.js +15 -9
  23. package/src/useResizeObserver.js +3 -3
  24. package/src/vendor/@preact/signals-core/dist/signals-core.js +1 -1
  25. package/src/vendor/@preact/signals-core/dist/signals-core.js.map +1 -1
  26. package/types/ThemeProvider.d.ts +1 -1
  27. package/types/array-utils.d.ts +7 -4
  28. package/types/column-utils.d.ts +43 -40
  29. package/types/component-registry.d.ts +4 -4
  30. package/types/context-definitions/DataProvider.d.ts +1 -1
  31. package/types/context-definitions/DataSourceProvider.d.ts +1 -1
  32. package/types/datasource/BaseDataSource.d.ts +2 -2
  33. package/types/date/EpochTimestamp.d.ts +66 -0
  34. package/types/date/date-utils.d.ts +34 -9
  35. package/types/date/dateTimePattern.d.ts +20 -2
  36. package/types/date/formatter.d.ts +26 -1
  37. package/types/date/index.d.ts +6 -2
  38. package/types/date/temporal.d.ts +68 -0
  39. package/types/date/time-zone.d.ts +52 -0
  40. package/types/feature-utils.d.ts +15 -1
  41. package/types/filters/filter-utils.d.ts +6 -1
  42. package/types/filters/filterAsQuery.d.ts +41 -0
  43. package/types/filters/index.d.ts +1 -0
  44. package/types/filters/temporal-cell-filter.d.ts +27 -0
  45. package/types/form-utils.d.ts +19 -0
  46. package/types/formatting-utils.d.ts +23 -1
  47. package/types/html-utils.d.ts +16 -0
  48. package/types/keyset.d.ts +11 -1
  49. package/types/tree-types.d.ts +2 -0
@@ -1,4 +1,4 @@
1
- import type { DataSource, DataSourceConstructorProps, DataSourceEvents, DataSourceFilter, DataSourceSubscribeCallback, DataSourceSubscribeProps, DataSourceSuspenseProps, WithBaseFilter, WithFullConfig } from "@vuu-ui/vuu-data-types";
1
+ import type { DataSource, DataSourceConstructorProps, DataSourceEvents, DataSourceFilter, DataSourceSubscribeCallback, DataSourceSubscribeProps, DataSourceSuspenseProps, SetFilterOptions, WithBaseFilter, WithFullConfig } from "@vuu-ui/vuu-data-types";
2
2
  import { LinkDescriptorWithLabel, VuuAggregation, VuuRange, VuuSort } from "@vuu-ui/vuu-protocol-types";
3
3
  import { EventEmitter } from "../event-emitter";
4
4
  import { Range } from "../range-utils";
@@ -28,7 +28,7 @@ export declare abstract class BaseDataSource extends EventEmitter<DataSourceEven
28
28
  set columns(columns: string[]);
29
29
  get filter(): DataSourceFilter;
30
30
  set filter(filter: DataSourceFilter);
31
- setFilter(filter: Filter): void;
31
+ setFilter(filter: Filter, options?: SetFilterOptions): void;
32
32
  clearFilter(): void;
33
33
  get isAwaitingConfirmationOfConfigChange(): boolean;
34
34
  protected confirmConfigChange(): void;
@@ -0,0 +1,66 @@
1
+ import { type DateTimeFields, type TimeZoneSpec } from "./time-zone";
2
+ /**
3
+ * How a temporal value is encoded on the wire (i.e in rows received from,
4
+ * and values sent to, the server).
5
+ * - epochMillis, a number (epochtimestamp, or legacy long with a date/time type)
6
+ * - epochNanos, a string of digits (epochtimestampnano). Nanosecond values
7
+ * cannot be represented safely as JavaScript numbers. A bigint is also
8
+ * accepted where values are generated locally.
9
+ */
10
+ export type TemporalEncoding = "epochMillis" | "epochNanos";
11
+ /**
12
+ * A timestamp with nanosecond precision. Internally represented as epoch
13
+ * millis (floored) plus nanos within the milli, so the common millisecond
14
+ * case requires no bigint arithmetic.
15
+ *
16
+ * Note: an empty value (null, undefined, '', 0, '0') is treated as 'no value'
17
+ * by fromWire. The Vuu server uses 0 to represent an unset timestamp, so
18
+ * the epoch itself (1970-01-01T00:00:00.000Z) cannot be represented.
19
+ */
20
+ export declare class EpochTimestamp {
21
+ readonly epochMillis: number;
22
+ /** nanoseconds within the millisecond, 0 - 999,999 */
23
+ readonly subMilliNanos: number;
24
+ private constructor();
25
+ static fromMillis(epochMillis: number, subMilliNanos?: number): EpochTimestamp;
26
+ static fromNanos(epochNanos: bigint | string | number): EpochTimestamp;
27
+ static fromDate(date: Date): EpochTimestamp;
28
+ static fromFields(fields: Partial<DateTimeFields> & Pick<DateTimeFields, "year" | "month" | "day">, timeZone?: TimeZoneSpec, subMilliNanos?: number): EpochTimestamp;
29
+ /**
30
+ * Returns true if value represents 'no value'.
31
+ */
32
+ static isEmpty(value: unknown): value is "" | "0" | 0 | 0n | null | undefined;
33
+ /**
34
+ * Decode a value as received from the server, or held in a filter,
35
+ * in the given encoding. Returns undefined for an empty or invalid value,
36
+ * including a value outside the range of a JavaScript Date (e.g. a nanos
37
+ * value decoded as millis).
38
+ */
39
+ static fromWire(value: unknown, encoding: TemporalEncoding): EpochTimestamp | undefined;
40
+ private static decode;
41
+ get epochNanos(): bigint;
42
+ toDate(): Date;
43
+ /**
44
+ * Fraction of second, truncated to the requested number of digits
45
+ */
46
+ fractionalSecond(digits: number): string;
47
+ getFields(timeZone?: TimeZoneSpec): DateTimeFields;
48
+ /**
49
+ * Encode for the wire. epochMillis => number, epochNanos => string
50
+ */
51
+ toWire(encoding: "epochMillis"): number;
52
+ toWire(encoding: "epochNanos"): string;
53
+ toWire(encoding: TemporalEncoding): number | string;
54
+ /**
55
+ * Encode as a literal in a filter query (never quoted)
56
+ */
57
+ toLiteral(encoding: TemporalEncoding): string;
58
+ /**
59
+ * Add a number of the smallest unit for the encoding, i.e 1 milli or 1 nano.
60
+ * Useful to convert inclusive to exclusive bounds.
61
+ */
62
+ addUnits(units: number, encoding: TemporalEncoding): EpochTimestamp;
63
+ compare(other: EpochTimestamp): number;
64
+ equals(other?: EpochTimestamp): boolean;
65
+ toString(): string;
66
+ }
@@ -5,35 +5,60 @@ export type zeroToFive = 0 | oneToFive;
5
5
  export type sixToNine = 6 | 7 | 8 | 9;
6
6
  export type zeroToNine = zeroToFive | sixToNine;
7
7
  export type oneToNine = oneToFive | sixToNine;
8
- export type TimeUnit = "hours" | "minutes" | "seconds";
8
+ export type TimeUnit = "hours" | "minutes" | "seconds" | "milliseconds";
9
9
  export type Hours = `${0 | 1}${zeroToNine}` | `2${0 | 1 | 2 | 3}`;
10
10
  export type Minutes = `${zeroToFive}${zeroToNine}`;
11
11
  export type Seconds = `${zeroToFive}${zeroToNine}`;
12
- export type TimeUnitValue<T extends TimeUnit> = T extends "hours" ? Hours : T extends "minutes" ? Minutes : Seconds;
12
+ export type Milliseconds = `${zeroToNine}${zeroToNine}${zeroToNine}`;
13
+ export type TimeUnitValue<T extends TimeUnit> = T extends "hours" ? Hours : T extends "minutes" ? Minutes : T extends "seconds" ? Seconds : Milliseconds;
13
14
  export type TimeString = `${number}${number}:${number}${number}:${number}${number}`;
15
+ /**
16
+ * A TimeString with millisecond precision, hh:mm:ss.SSS
17
+ */
18
+ export type TimeStringMillis = `${TimeString}.${number}${number}${number}`;
14
19
  type YYYY = `19${zeroToNine}${zeroToNine}` | `20${zeroToNine}${zeroToNine}`;
15
20
  type MM = `0${oneToNine}` | `1${0 | 1 | 2}`;
16
21
  type DD = `${0}${oneToNine}` | `${1 | 2}${zeroToNine}` | `3${0 | 1}`;
17
22
  export type DateStringISO = `${YYYY}-${MM}-${DD}`;
18
23
  export declare const zeroTime: TimeString;
24
+ export declare const zeroTimeMillis: TimeStringMillis;
19
25
  export declare const zeroTimeUnit: TimeUnitValue<TimeUnit>;
20
- export declare function incrementTimeUnitValue<T extends TimeUnit>(unit: T, value: TimeUnitValue<T>): TimeUnitValue<T> | Hours;
21
- export declare function decrementTimeUnitValue<T extends TimeUnit>(unit: T, value: TimeUnitValue<T>): TimeUnitValue<T> | Hours;
22
- export declare function updateTimeString<T extends TimeUnit>(timeString: TimeString, unit: T, value: TimeUnitValue<T>): TimeString;
26
+ export declare function incrementTimeUnitValue<T extends TimeUnit>(unit: T, value: TimeUnitValue<T>): TimeUnitValue<T>;
27
+ export declare function decrementTimeUnitValue<T extends TimeUnit>(unit: T, value: TimeUnitValue<T>): TimeUnitValue<T>;
28
+ export declare function updateTimeString<S extends TimeString | TimeStringMillis, T extends TimeUnit>(timeString: S, unit: T, value: TimeUnitValue<T>): S;
23
29
  export declare const isValidTimeString: (value: unknown) => value is TimeString;
30
+ export declare const isValidTimeStringMillis: (value: unknown) => value is TimeStringMillis;
31
+ /**
32
+ * Convert a valid TimeString or TimeStringMillis to the required precision.
33
+ * Milliseconds are added (as 000) or removed as necessary. Returns undefined
34
+ * if value is not a valid time.
35
+ */
36
+ export declare function normaliseTimeString(value: unknown, milliseconds: true): TimeStringMillis | undefined;
37
+ export declare function normaliseTimeString(value: unknown, milliseconds?: false): TimeString | undefined;
38
+ export declare function normaliseTimeString(value: unknown, milliseconds?: boolean): TimeString | TimeStringMillis | undefined;
24
39
  export declare function asTimeString(value: unknown, allowUndefined: false): TimeString;
25
40
  export declare function asTimeString(value: unknown, allowUndefined?: true): TimeString | undefined;
41
+ /**
42
+ * Millisecond precision equivalent of asTimeString. Accepts a TimeString,
43
+ * TimeStringMillis or a timestamp (millis since epoch, number or string).
44
+ */
45
+ export declare function asTimeStringMillis(value: unknown, allowUndefined: false): TimeStringMillis;
46
+ export declare function asTimeStringMillis(value: unknown, allowUndefined?: true): TimeStringMillis | undefined;
26
47
  export interface Time {
27
48
  hours: number;
28
49
  minutes: number;
29
50
  seconds: number;
51
+ milliseconds: number;
30
52
  asDate: (date?: Date | DateStringISO) => Date;
31
53
  }
32
- export declare function Time(timeString: TimeString): Time;
54
+ export declare function Time(timeString: TimeString | TimeStringMillis): Time;
33
55
  export declare namespace Time {
34
- export var millisToTimeString: (timestamp: number) => TimeString;
35
- var _a: (hours: number, minutes: number, seconds: number) => TimeString;
36
- export { _a as toString };
56
+ var millisToTimeString: (timestamp: number) => TimeString;
57
+ var millisToTimeStringMillis: (timestamp: number) => TimeStringMillis;
58
+ export { timeToString as toString };
37
59
  export var isDateInMillis: (dtInMillis: string | number) => boolean;
38
60
  }
61
+ declare function timeToString(hours: number, minutes: number, seconds: number): TimeString;
62
+ declare function timeToString(hours: number, minutes: number, seconds: number, milliseconds: number): TimeStringMillis;
63
+ declare function timeToString(hours: number, minutes: number, seconds: number, milliseconds?: number): TimeString | TimeStringMillis;
39
64
  export {};
@@ -1,8 +1,26 @@
1
- import { DateTimeDataValueDescriptor } from "@vuu-ui/vuu-data-types";
1
+ import type { DataValueDescriptor, DataValueType, DateTimeDataValueDescriptor } from "@vuu-ui/vuu-data-types";
2
+ import { type TemporalKind } from "./temporal";
2
3
  import { DateTimePattern } from "./types";
3
4
  export declare const defaultPatternsByType: {
4
5
  readonly time: "hh:mm:ss";
5
6
  readonly date: "dd.mm.yyyy";
6
7
  };
7
8
  export declare const fallbackDateTimePattern: DateTimePattern;
8
- export declare function dateTimePattern(type: DateTimeDataValueDescriptor["type"]): DateTimePattern;
9
+ export declare const defaultDateTimePatternByKind: Record<TemporalKind, DateTimePattern>;
10
+ /**
11
+ * Returns the DateTimePattern configured on the type (formatting.pattern), if
12
+ * there is one. Otherwise, the default pattern for the temporal kind. If kind
13
+ * is not provided, it is inferred from type name.
14
+ */
15
+ export declare function dateTimePattern(type?: DataValueType | DateTimeDataValueDescriptor["type"], kind?: TemporalKind | undefined): DateTimePattern;
16
+ /**
17
+ * Returns a copy of a temporal descriptor, with its type replaced so that the
18
+ * value is displayed (or edited) using the given pattern. The type name follows
19
+ * the pattern: 'date/time' if both date and time are specified, else 'date' or
20
+ * 'time'. Other formatting (e.g. timeZone, locale) is preserved. The descriptor
21
+ * is returned unchanged if no pattern is provided or the descriptor is not
22
+ * temporal.
23
+ * Use this to present a column differently in different contexts, e.g. a
24
+ * ColumnFilter using a different pattern to the Table.
25
+ */
26
+ export declare const withDateTimePattern: <T extends DataValueDescriptor>(descriptor: T, pattern?: DateTimePattern) => T;
@@ -1,9 +1,34 @@
1
1
  import { DateFormatter } from "@internationalized/date";
2
+ import type { FractionalSecondDigits } from "@vuu-ui/vuu-table-types";
3
+ import { EpochTimestamp } from "./EpochTimestamp";
4
+ import { type TimeZoneSpec } from "./time-zone";
2
5
  import { DateTimePattern } from "./types";
3
6
  type DateTimeFormatConfig = {
4
7
  locale: string;
5
8
  options: Intl.DateTimeFormatOptions;
6
9
  };
10
+ export interface DateTimeFormatOptions {
11
+ /**
12
+ * Number of sub-second digits to display, when pattern includes time. If
13
+ * not specified, 3 is used for the 'hh:mm:ss.ms' pattern, otherwise 0.
14
+ */
15
+ fractionalSecondDigits?: FractionalSecondDigits;
16
+ /**
17
+ * Override the locale implied by the pattern. Pattern then determines the
18
+ * style of each field, locale determines order and language.
19
+ */
20
+ locale?: string;
21
+ timeZone?: TimeZoneSpec;
22
+ }
23
+ type TimestampFormatter = (ts: EpochTimestamp) => string;
24
+ /**
25
+ * Create a formatter for EpochTimestamp values. This is the most general
26
+ * formatter, it supports nanosecond precision.
27
+ */
28
+ export declare function formatTimestamp({ date, time }: DateTimePattern, { fractionalSecondDigits, locale, timeZone, }?: DateTimeFormatOptions): TimestampFormatter;
29
+ /**
30
+ * Create a formatter for Date values.
31
+ */
32
+ export declare function formatDate(pattern: DateTimePattern, options?: DateTimeFormatOptions): (d: Date) => string;
7
33
  export declare function getDateFormatter({ locale, options }: DateTimeFormatConfig): DateFormatter;
8
- export declare function formatDate(pattern: DateTimePattern): (d: Date) => string;
9
34
  export {};
@@ -1,4 +1,8 @@
1
- export { asTimeString, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, Time, toCalendarDate, type DateStringISO, type Hours, type Minutes, type Seconds, type TimeString, type TimeUnit, type TimeUnitValue, updateTimeString, zeroTime, zeroTimeUnit, } from "./date-utils";
2
- export { dateTimePattern, defaultPatternsByType, fallbackDateTimePattern, } from "./dateTimePattern";
1
+ export { asTimeString, asTimeStringMillis, decrementTimeUnitValue, incrementTimeUnitValue, isValidTimeString, isValidTimeStringMillis, normaliseTimeString, Time, toCalendarDate, type DateStringISO, type Hours, type Milliseconds, type Minutes, type Seconds, type TimeString, type TimeStringMillis, type TimeUnit, type TimeUnitValue, updateTimeString, zeroTime, zeroTimeMillis, zeroTimeUnit, } from "./date-utils";
2
+ export { dateTimePattern, defaultPatternsByType, fallbackDateTimePattern, withDateTimePattern, } from "./dateTimePattern";
3
3
  export * from "./formatter";
4
4
  export { dateTimeLabelByType, isDatePattern, isTimePattern, supportedDateTimePatterns, type DatePattern, type DateTimePattern, type TimePattern, } from "./types";
5
+ export { defaultDateTimePatternByKind } from "./dateTimePattern";
6
+ export { EpochTimestamp, type TemporalEncoding } from "./EpochTimestamp";
7
+ export * from "./temporal";
8
+ export * from "./time-zone";
@@ -0,0 +1,68 @@
1
+ import type { DataValueDescriptor, TemporalDataValueTypeSimple } from "@vuu-ui/vuu-data-types";
2
+ import type { FractionalSecondDigits } from "@vuu-ui/vuu-table-types";
3
+ import { EpochTimestamp, type TemporalEncoding } from "./EpochTimestamp";
4
+ import { type TimeZoneSpec } from "./time-zone";
5
+ /**
6
+ * What a temporal value represents, from the user's perspective
7
+ * - datetime: a point in time, date and time of day are both meaningful
8
+ * - date: a calendar date, time of day is not meaningful
9
+ * - time: a time of day, the date is not meaningful (typically 'today')
10
+ */
11
+ export type TemporalKind = "datetime" | "date" | "time";
12
+ /**
13
+ * The finest resolution that the encoding can represent.
14
+ */
15
+ export type TemporalPrecision = "ms" | "ns";
16
+ export interface TemporalInfo {
17
+ kind: TemporalKind;
18
+ encoding: TemporalEncoding;
19
+ precision: TemporalPrecision;
20
+ timeZone: TimeZoneSpec;
21
+ }
22
+ export declare const isTemporalTypeName: (typeName?: string) => typeName is TemporalDataValueTypeSimple;
23
+ type TemporalDescriptor = Pick<DataValueDescriptor, "serverDataType" | "type">;
24
+ /**
25
+ * The single source of truth for whether (and how) a column or form field
26
+ * holds a temporal value. Rules:
27
+ *
28
+ * - encoding is determined by serverDataType
29
+ * - epochtimestamp => epochMillis
30
+ * - epochtimestampnano => epochNanos
31
+ * - long, int, double (or unspecified) => epochMillis, but only when the
32
+ * 'type' is a temporal type. This legacy usage (pre epochtimestamp) is
33
+ * DEPRECATED and supported only for backward compatibility. Timestamps
34
+ * should be described by the server as epochtimestamp or epochtimestampnano.
35
+ * - kind is determined by 'type' ('date/time', 'date', 'time'), default 'datetime'.
36
+ * An explicit type of 'number' opts an epochtimestamp column out of temporal
37
+ * treatment altogether.
38
+ * - timeZone from type.formatting.timeZone, falling back to the default time zone
39
+ *
40
+ * @returns TemporalInfo or undefined if value is not temporal
41
+ */
42
+ export declare const getTemporalInfo: (descriptor?: TemporalDescriptor) => TemporalInfo | undefined;
43
+ export declare const isTemporal: (descriptor?: TemporalDescriptor) => boolean;
44
+ export declare const defaultFractionalSecondDigits: (info: Pick<TemporalInfo, "precision">) => FractionalSecondDigits;
45
+ /**
46
+ * Format a timestamp as a canonical, locale independent, editable string:
47
+ * - date: yyyy-mm-dd
48
+ * - datetime: yyyy-mm-dd hh:mm:ss[.fffffffff]
49
+ * - time: hh:mm:ss[.fffffffff]
50
+ * Sub-second digits are included only when non zero, up to the precision of
51
+ * the encoding, so they round-trip unchanged through an edit.
52
+ */
53
+ export declare const formatTemporalInput: (value: EpochTimestamp | undefined, { kind, precision, timeZone }: Omit<TemporalInfo, "encoding">) => string;
54
+ /**
55
+ * Parse user input. Accepts the canonical formats produced by
56
+ * formatTemporalInput as well as raw epoch values (in the encoding of the
57
+ * column). Where the input does not include a component (e.g the date for a
58
+ * 'time' value), it is taken from baseValue if provided, otherwise today
59
+ * (date) or midnight (time).
60
+ *
61
+ * @returns EpochTimestamp, or undefined if input is not valid
62
+ */
63
+ export declare const parseTemporalInput: (input: string, { kind, encoding, precision, timeZone }: TemporalInfo, baseValue?: EpochTimestamp) => EpochTimestamp | undefined;
64
+ /**
65
+ * Placeholder text describing the canonical input format for a kind.
66
+ */
67
+ export declare const temporalInputPlaceholder: (kind: TemporalKind) => "hh:mm:ss" | "yyyy-mm-dd" | "yyyy-mm-dd hh:mm:ss";
68
+ export {};
@@ -0,0 +1,52 @@
1
+ import { CalendarDate } from "@internationalized/date";
2
+ import type { DateStringISO, TimeString } from "./date-utils";
3
+ /**
4
+ * 'local' (browser time zone), 'UTC' or any IANA time zone identifier.
5
+ */
6
+ export type TimeZoneSpec = "local" | "UTC" | (string & {});
7
+ /**
8
+ * Set the time zone used for temporal values where a column does not
9
+ * specify one explicitly (formatting.timeZone). Default is 'local'.
10
+ */
11
+ export declare const setDefaultTimeZone: (timeZone: TimeZoneSpec) => void;
12
+ export declare const getDefaultTimeZone: () => TimeZoneSpec;
13
+ /**
14
+ * Resolve a TimeZoneSpec to an IANA time zone identifier.
15
+ */
16
+ export declare const resolveTimeZone: (timeZone?: TimeZoneSpec) => string;
17
+ export interface DateTimeFields {
18
+ year: number;
19
+ month: number;
20
+ day: number;
21
+ hour: number;
22
+ minute: number;
23
+ second: number;
24
+ millisecond: number;
25
+ }
26
+ export declare const getDateTimeFields: (epochMillis: number, timeZone?: TimeZoneSpec) => DateTimeFields;
27
+ /**
28
+ * Convert wall clock fields in the given time zone to epoch millis. Where the
29
+ * wall clock time is ambiguous or does not exist (DST transitions), the
30
+ * 'compatible' disambiguation strategy is used (same as Temporal / JS Date).
31
+ */
32
+ export declare const fromDateTimeFields: ({ year, month, day, hour, minute, second, millisecond, }: Partial<DateTimeFields> & Pick<DateTimeFields, "year" | "month" | "day">, timeZone?: TimeZoneSpec) => number;
33
+ /**
34
+ * Epoch millis of the start of the calendar day (in timeZone) that contains epochMillis.
35
+ */
36
+ export declare const startOfDay: (epochMillis: number, timeZone?: TimeZoneSpec) => number;
37
+ /**
38
+ * Epoch millis of the start of the calendar day (in timeZone) following the day that
39
+ * contains epochMillis. This is DST safe, the day may be 23, 24 or 25 hours long.
40
+ */
41
+ export declare const startOfNextDay: (epochMillis: number, timeZone?: TimeZoneSpec) => number;
42
+ export declare const isStartOfDay: (epochMillis: number, timeZone?: TimeZoneSpec) => boolean;
43
+ /**
44
+ * A date to which a time of day can be applied. 'today' and 'yesterday' are
45
+ * resolved at the point of use, so filters that use them remain relative.
46
+ */
47
+ export type RelativeDate = "today" | "yesterday" | DateStringISO;
48
+ export declare const resolveRelativeDate: (date?: RelativeDate | Date, timeZone?: TimeZoneSpec) => CalendarDate;
49
+ /**
50
+ * Epoch millis of the given time of day on the given date, in timeZone.
51
+ */
52
+ export declare const timeOfDayToEpochMillis: (timeString: TimeString | `${TimeString}.${string}`, date?: RelativeDate | Date, timeZone?: TimeZoneSpec) => number;
@@ -1,7 +1,7 @@
1
1
  import type { RemoteModuleConnection, TableSchema } from "@vuu-ui/vuu-data-types";
2
2
  import type { VuuTable } from "@vuu-ui/vuu-protocol-types";
3
3
  import type { ListOption } from "@vuu-ui/vuu-table-types";
4
- import { type ReactElement } from "react";
4
+ import React, { type ReactElement } from "react";
5
5
  export type PathMap = {
6
6
  [key: string]: string;
7
7
  };
@@ -28,6 +28,20 @@ export interface DynamicFeatureProps<P extends object | undefined = object> {
28
28
  title?: string;
29
29
  width?: number;
30
30
  }
31
+ export type FeatureModule = {
32
+ default: React.ComponentType<object>;
33
+ };
34
+ export type FeatureImporter = (url: string) => Promise<FeatureModule>;
35
+ declare const importFeatureByUrl: FeatureImporter;
36
+ /**
37
+ * By default, a dynamic feature is loaded from its url with a native
38
+ * dynamic import. An application that bundles its features can install
39
+ * an importer that resolves feature urls to bundled modules. Use
40
+ * `importFeatureByUrl` as a fallback for unknown urls.
41
+ */
42
+ export declare const setFeatureImporter: (importer: FeatureImporter) => void;
43
+ export declare const importFeature: (url: string) => Promise<FeatureModule>;
44
+ export { importFeatureByUrl };
31
45
  declare global {
32
46
  const vuuConfig: Promise<VuuConfig>;
33
47
  }
@@ -26,7 +26,8 @@ export declare const stripFilterFromColumns: (columns: RuntimeColumnDescriptor[]
26
26
  allowColumnHeaderMenu?: false;
27
27
  colHeaderContentRenderer?: string;
28
28
  colHeaderLabelRenderer?: string;
29
- exportFormatter?: (value: unknown) => string;
29
+ exportable?: boolean;
30
+ exportFormatter?: (value: unknown, row: import("@vuu-ui/vuu-table-types").RowAccessor) => string;
30
31
  getIcon?: (row: import("@vuu-ui/vuu-table-types").DataRow) => string | undefined;
31
32
  groupable?: boolean;
32
33
  hidden?: boolean;
@@ -71,5 +72,9 @@ export declare const stripFilterFromColumns: (columns: RuntimeColumnDescriptor[]
71
72
  type?: import("@vuu-ui/vuu-data-types").DataValueType;
72
73
  }[];
73
74
  export declare const extractFilterForColumn: (filter: Filter | undefined, columnName: string) => Filter | undefined;
75
+ /**
76
+ * The full-day range used as default value of a time range filter.
77
+ */
78
+ export declare const getDefaultTimeRange: (column: ColumnDescriptor) => [string, string];
74
79
  export declare const getColumnValueFromFilter: (column: ColumnDescriptor, operator: ColumnFilterOp, filter?: FilterContainerFilter) => ColumnFilterValue;
75
80
  export declare const filtersAreEqual: (f1: FilterContainerFilter | null | undefined, f2: FilterContainerFilter | null | undefined) => boolean;
@@ -1,8 +1,49 @@
1
+ import type { TableSchema } from "@vuu-ui/vuu-data-types";
2
+ import type { ColumnDescriptor } from "@vuu-ui/vuu-table-types";
1
3
  import type { ColumnDescriptorsByName, Filter, SingleValueFilterClause } from "@vuu-ui/vuu-filter-types";
4
+ import { type TemporalInfo } from "../date/temporal";
5
+ import { type RelativeDate } from "../date/time-zone";
2
6
  export declare const filterAsQuery: (f: Filter, opts?: {
3
7
  columnsByName?: ColumnDescriptorsByName;
4
8
  }) => string;
5
9
  export declare const ONE_DAY_IN_MILLIS: number;
10
+ export interface TemporalFilterAsQueryOptions {
11
+ /**
12
+ * The date against which time of day (TimeString) values are resolved.
13
+ * Default is 'today', resolved at the time the query is created.
14
+ */
15
+ date?: RelativeDate;
16
+ }
17
+ /**
18
+ * Serialize a filter clause on a temporal column. Values are interpreted at
19
+ * the granularity implied by the value and the column 'kind':
20
+ * - day: column kind is 'date', or a 'datetime' value at the exact start of a
21
+ * day (in the column time zone), e.g as selected from a DatePicker. The clause
22
+ * applies to the whole day: '=' becomes a [startOfDay, startOfNextDay) range,
23
+ * '>' becomes '>= startOfNextDay' etc. Day boundaries are DST safe.
24
+ * - period: time of day values (TimeString or whole second 'time' values). A
25
+ * TimeString represents the period implied by its precision, e.g hh:mm:ss is
26
+ * the whole second, hh:mm:ss.fff the whole millisecond (on a nanosecond
27
+ * column), and is treated like the day granularity above.
28
+ * - exact: all other values
29
+ * Values are encoded according to the column encoding (millis or nanos).
30
+ */
31
+ export declare function temporalFilterAsQuery(f: SingleValueFilterClause, temporalInfo: TemporalInfo, options?: TemporalFilterAsQueryOptions): string;
32
+ /**
33
+ * @deprecated use temporalFilterAsQuery
34
+ */
6
35
  export declare function dateFilterAsQuery(f: SingleValueFilterClause<number>, opts?: {
7
36
  columnsByName?: ColumnDescriptorsByName;
8
37
  }): string;
38
+ export declare const getColumnsByName: (columns: readonly ColumnDescriptor[]) => ColumnDescriptorsByName;
39
+ /**
40
+ * Build the columnsByName used to serialize a filter. Column descriptors
41
+ * provided by client take precedence over (are merged with) the schema columns.
42
+ */
43
+ export declare const getColumnsByNameForFilter: (tableSchema?: Pick<TableSchema, "columns">, columnsByName?: ColumnDescriptorsByName) => ColumnDescriptorsByName | undefined;
44
+ /**
45
+ * Returns true if the filter includes clauses whose meaning depends on the
46
+ * column type (temporal columns) or which serialize themselves. Such filters
47
+ * must be resolved (see resolveFilterQuery) before they can be evaluated locally.
48
+ */
49
+ export declare const filterRequiresResolution: (filter: Filter, columnsByName?: ColumnDescriptorsByName) => boolean;
@@ -1,2 +1,3 @@
1
1
  export * from "./filter-utils";
2
2
  export * from "./filterAsQuery";
3
+ export * from "./temporal-cell-filter";
@@ -0,0 +1,27 @@
1
+ import type { DataValueDescriptor } from "@vuu-ui/vuu-data-types";
2
+ export interface TemporalCellFilter {
3
+ /** The value, formatted as displayed */
4
+ label: string;
5
+ op: "=" | "between-inclusive";
6
+ value: string | [string, string];
7
+ }
8
+ /**
9
+ * Describes a filter that matches a temporal value at the precision with which
10
+ * it is displayed by the given column descriptor, e.g. where the descriptor
11
+ * displays milliseconds, the filter matches the whole millisecond.
12
+ * - date => the whole day, as a start of day value
13
+ * - time => a time of day (TimeString), truncated to the displayed precision
14
+ * - datetime => a range, unless the displayed precision is that of the encoding
15
+ * The filter is suitable for FilterAggregator.add (with the same descriptor) and
16
+ * matches the value a ColumnFilter with this descriptor would commit.
17
+ *
18
+ * @param column the descriptor of the filter (or table cell) displaying the value
19
+ * @param value a value as received from the server
20
+ * @param columnFilter the value is displayed by a ColumnFilter, rather than a
21
+ * table cell. A ColumnFilter displays a 'datetime' value as a date (DatePicker,
22
+ * so the filter is the whole day) and a 'time' value to the second, or to the
23
+ * millisecond with the 'hh:mm:ss.ms' pattern (TimeInput supports no finer
24
+ * precision, whereas a table cell displays nanos by default).
25
+ * @returns undefined if value is empty or column is not temporal
26
+ */
27
+ export declare const getTemporalCellFilter: (column: DataValueDescriptor, value: unknown, columnFilter?: boolean) => TemporalCellFilter | undefined;
@@ -1,7 +1,11 @@
1
1
  import { DataValueTypeSimple } from "@vuu-ui/vuu-data-types";
2
2
  import { VuuColumnDataType, VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
3
3
  import { KeyboardEvent, SyntheticEvent } from "react";
4
+ import { EpochTimestamp } from "./date/EpochTimestamp";
5
+ import { type TemporalInfo } from "./date/temporal";
6
+ import type { DataValueDescriptor } from "@vuu-ui/vuu-data-types";
4
7
  import { ExtendedFilterOptions } from "@vuu-ui/vuu-filter-types";
8
+ import { type ScaledDecimal } from "./ScaledDecimal";
5
9
  export declare class DataValidationError extends Error {
6
10
  actualType: string;
7
11
  expectedType: string;
@@ -35,3 +39,18 @@ export declare function getTypedRange([value1, value2]: [string, string], dataTy
35
39
  */
36
40
  export declare function getTypedValue(value: string, type: VuuColumnDataType | DataValueTypeSimple, throwIfInvalid?: false, options?: ExtendedFilterOptions): VuuRowDataItemType | undefined;
37
41
  export declare function getTypedValue(value: string, type: VuuColumnDataType | DataValueTypeSimple, throwIfInvalid: true, options?: ExtendedFilterOptions): VuuRowDataItemType;
42
+ /**
43
+ * Convert a string value (from user input or a filter control) to the wire
44
+ * representation of a temporal value, using the column's TemporalInfo.
45
+ * - time kind: a TimeString is applied to today's date (in the column time zone)
46
+ * - any kind: canonical input (see parseTemporalInput) or raw epoch digits
47
+ * Values are returned in the column encoding (number for epochMillis, string
48
+ * for epochNanos).
49
+ */
50
+ export declare function getTypedTemporalValue(value: string, temporalInfo: TemporalInfo, throwIfInvalid?: boolean, baseValue?: EpochTimestamp): number | string | undefined;
51
+ /**
52
+ * Like getTypedValue, but takes a column (or form field) descriptor. Temporal
53
+ * values are handled according to getTemporalInfo, other values according to
54
+ * the type name, if there is one, otherwise the serverDataType.
55
+ */
56
+ export declare function getTypedValueForDescriptor(value: string, descriptor: DataValueDescriptor, throwIfInvalid?: boolean): VuuRowDataItemType | ScaledDecimal | bigint | undefined;
@@ -1,8 +1,30 @@
1
- import { ColumnDescriptor, ValueFormatter } from "@vuu-ui/vuu-table-types";
1
+ import { DataValueDescriptor } from "@vuu-ui/vuu-data-types";
2
+ import { ColumnDescriptor, FractionalSecondDigits, ValueFormatter } from "@vuu-ui/vuu-table-types";
2
3
  export type ValueFormatters = {
3
4
  [key: string]: ValueFormatter;
4
5
  };
5
6
  export declare const defaultValueFormatter: (value: unknown) => string;
7
+ /**
8
+ * The number of fractional second digits displayed for a temporal value:
9
+ * type.formatting.fractionalSecondDigits if set, otherwise 3 for an
10
+ * 'hh:mm:ss.ms' time pattern, 9 for a nanosecond column with any other time
11
+ * pattern, else 0.
12
+ */
13
+ export declare const getFractionalSecondDigits: (column: DataValueDescriptor, temporalInfo?: import("./date").TemporalInfo | undefined) => FractionalSecondDigits;
14
+ /**
15
+ * Creates a formatter for a temporal value (see getTemporalInfo). Handles
16
+ * millisecond and nanosecond encodings, the configured (or default) pattern,
17
+ * time zone, locale and fractional second digits.
18
+ * Empty values (null, undefined, '', 0) are rendered as empty string.
19
+ */
20
+ export declare const temporalFormatter: (column: DataValueDescriptor, temporalInfo?: import("./date").TemporalInfo | undefined) => ValueFormatter;
21
+ /**
22
+ * Format a filter value on a temporal column for display (e.g. in a FilterPill
23
+ * tooltip or a list of filter clauses). Time of day (TimeString) values are
24
+ * displayed as is. A 'datetime' value at the start of a day (which the filter
25
+ * applies to the whole day, see temporalFilterAsQuery) is displayed as a date.
26
+ */
27
+ export declare const formatTemporalFilterValue: (value: unknown, column: DataValueDescriptor) => string;
6
28
  export declare const numericFormatter: ({ align, serverDataType, type, }: Partial<ColumnDescriptor>) => typeof defaultValueFormatter;
7
29
  export declare const getValueFormatter: (column: ColumnDescriptor) => ValueFormatter;
8
30
  /**
@@ -13,6 +13,22 @@ export declare const isInputElement: (el: HTMLElement | EventTarget | null) => e
13
13
  export declare const isDateInput: (el: HTMLElement | EventTarget | null) => el is HTMLInputElement;
14
14
  export declare const hasOpenOptionList: (el: HTMLElement | EventTarget | null) => boolean | undefined;
15
15
  export declare function getScrollbarSize(): number;
16
+ /**
17
+ * Used when the max scroll height cannot be measured, e.g. outside a browser.
18
+ */
19
+ export declare const DEFAULT_MAX_SCROLL_HEIGHT = 33000000;
20
+ /**
21
+ * Measure the largest scrollHeight (in CSS pixels) the browser supports for a
22
+ * scrollable element. Engines have an internal layout limit, which shrinks in
23
+ * CSS pixels as browser zoom increases, e.g.
24
+ * Chromium/WebKit: ~33.5M at 100%, ~11.18M at 300%
25
+ * Firefox: ~17.9M at 100%, ~5.97M at 300%
26
+ * Chromium/WebKit clamp heights above the limit, Firefox ignores them entirely,
27
+ * so a single oversized probe cannot be used. Instead we binary search for the
28
+ * largest height that is honoured. Browser zoom changes devicePixelRatio, so
29
+ * results are cached per ratio.
30
+ */
31
+ export declare function getMaxScrollHeight(doc?: Document | undefined): number;
16
32
  export type MouseEventTypes = "dblclick" | "click";
17
33
  export type KeyboardEventTypes = "keydown" | "keypress" | "keyup";
18
34
  export declare const dispatchMouseEvent: (el: HTMLElement, type: MouseEventTypes) => void;
package/types/keyset.d.ts CHANGED
@@ -5,12 +5,22 @@ export interface IKeySet {
5
5
  }
6
6
  export declare class KeySet implements IKeySet {
7
7
  private keys;
8
+ private freeKeys;
8
9
  private nextKeyValue;
9
10
  private range;
10
11
  constructor(range: VuuRange);
11
12
  next(free?: number[]): number;
12
13
  private init;
13
- reset(range: VuuRange): boolean;
14
+ /**
15
+ * Keys assigned to rows that remain within range are never changed, keys
16
+ * released by rows leaving the range are recycled for rows entering the
17
+ * range. Keys are never re-sequenced, clients may retain rows (and their
18
+ * keys) that remain within range, so re-assigning keys to those rows would
19
+ * risk duplicate keys on the client.
20
+ * Note: range is copied, callers may mutate the range object they pass.
21
+ * Returns false, keys are never resequenced.
22
+ */
23
+ reset({ from, to }: VuuRange): boolean;
14
24
  keyFor(rowIndex: number): number;
15
25
  toDebugString(): string;
16
26
  }
@@ -5,5 +5,7 @@ export interface TreeSourceNode<T = unknown> {
5
5
  header?: boolean;
6
6
  label: string;
7
7
  childNodes?: TreeSourceNode<T>[];
8
+ /** node is rendered expanded initially */
9
+ expanded?: boolean;
8
10
  tags?: string[];
9
11
  }