@thoughtspot/ts-chart-sdk 0.0.2-alpha.2 → 0.0.2-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 (107) hide show
  1. package/README.md +126 -50
  2. package/dist/ts-chart-sdk.d.ts +940 -0
  3. package/lib/index.d.ts +4 -0
  4. package/lib/index.d.ts.map +1 -1
  5. package/lib/index.js +4 -0
  6. package/lib/index.js.map +1 -1
  7. package/lib/main/custom-chart-context.d.ts +22 -7
  8. package/lib/main/custom-chart-context.d.ts.map +1 -1
  9. package/lib/main/custom-chart-context.js +59 -4
  10. package/lib/main/custom-chart-context.js.map +1 -1
  11. package/lib/main/custom-chart-context.spec.js +402 -5
  12. package/lib/main/custom-chart-context.spec.js.map +1 -1
  13. package/lib/main/post-message-event-bridge.js +2 -2
  14. package/lib/main/post-message-event-bridge.js.map +1 -1
  15. package/lib/main/util.spec.d.ts +2 -0
  16. package/lib/main/util.spec.d.ts.map +1 -0
  17. package/lib/main/util.spec.js +31 -0
  18. package/lib/main/util.spec.js.map +1 -0
  19. package/lib/react/use-custom-chart-context.spec.js +54 -3
  20. package/lib/react/use-custom-chart-context.spec.js.map +1 -1
  21. package/lib/test/test-conditional-formatting-utils.d.ts +5 -0
  22. package/lib/test/test-conditional-formatting-utils.d.ts.map +1 -0
  23. package/lib/test/test-conditional-formatting-utils.js +43 -0
  24. package/lib/test/test-conditional-formatting-utils.js.map +1 -0
  25. package/lib/types/answer-column.types.d.ts +57 -1
  26. package/lib/types/answer-column.types.d.ts.map +1 -1
  27. package/lib/types/answer-column.types.js +47 -0
  28. package/lib/types/answer-column.types.js.map +1 -1
  29. package/lib/types/chart-to-ts-event.types.d.ts +2 -0
  30. package/lib/types/chart-to-ts-event.types.d.ts.map +1 -1
  31. package/lib/types/chart-to-ts-event.types.js +1 -0
  32. package/lib/types/chart-to-ts-event.types.js.map +1 -1
  33. package/lib/types/common.types.d.ts +103 -4
  34. package/lib/types/common.types.d.ts.map +1 -1
  35. package/lib/types/common.types.js +15 -1
  36. package/lib/types/common.types.js.map +1 -1
  37. package/lib/types/conditional-formatting.types.d.ts +140 -0
  38. package/lib/types/conditional-formatting.types.d.ts.map +1 -0
  39. package/lib/types/conditional-formatting.types.js +60 -0
  40. package/lib/types/conditional-formatting.types.js.map +1 -0
  41. package/lib/types/configurator.types.d.ts +5 -0
  42. package/lib/types/configurator.types.d.ts.map +1 -1
  43. package/lib/types/number-formatting.types.d.ts +55 -0
  44. package/lib/types/number-formatting.types.d.ts.map +1 -0
  45. package/lib/types/number-formatting.types.js +23 -0
  46. package/lib/types/number-formatting.types.js.map +1 -0
  47. package/lib/types/ts-to-chart-event.types.d.ts +10 -5
  48. package/lib/types/ts-to-chart-event.types.d.ts.map +1 -1
  49. package/lib/types/ts-to-chart-event.types.js.map +1 -1
  50. package/lib/types/visual-prop.types.d.ts +21 -0
  51. package/lib/types/visual-prop.types.d.ts.map +1 -1
  52. package/lib/utils/conditional-formatting/conditional-formatting.d.ts +10 -0
  53. package/lib/utils/conditional-formatting/conditional-formatting.d.ts.map +1 -0
  54. package/lib/utils/conditional-formatting/conditional-formatting.js +103 -0
  55. package/lib/utils/conditional-formatting/conditional-formatting.js.map +1 -0
  56. package/lib/utils/conditional-formatting/conditional-formatting.spec.d.ts +2 -0
  57. package/lib/utils/conditional-formatting/conditional-formatting.spec.d.ts.map +1 -0
  58. package/lib/utils/conditional-formatting/conditional-formatting.spec.js +142 -0
  59. package/lib/utils/conditional-formatting/conditional-formatting.spec.js.map +1 -0
  60. package/lib/utils/date-formatting.d.ts +103 -0
  61. package/lib/utils/date-formatting.d.ts.map +1 -0
  62. package/lib/utils/date-formatting.js +389 -0
  63. package/lib/utils/date-formatting.js.map +1 -0
  64. package/lib/utils/date-formatting.spec.d.ts +2 -0
  65. package/lib/utils/date-formatting.spec.d.ts.map +1 -0
  66. package/lib/utils/date-formatting.spec.js +394 -0
  67. package/lib/utils/date-formatting.spec.js.map +1 -0
  68. package/lib/utils/date-utils.d.ts +6 -0
  69. package/lib/utils/date-utils.d.ts.map +1 -0
  70. package/lib/utils/date-utils.js +20 -0
  71. package/lib/utils/date-utils.js.map +1 -0
  72. package/lib/utils/date-utils.spec.d.ts +2 -0
  73. package/lib/utils/date-utils.spec.d.ts.map +1 -0
  74. package/lib/utils/date-utils.spec.js +63 -0
  75. package/lib/utils/date-utils.spec.js.map +1 -0
  76. package/lib/utils/formatting-util.d.ts +10 -0
  77. package/lib/utils/formatting-util.d.ts.map +1 -0
  78. package/lib/utils/formatting-util.js +78 -0
  79. package/lib/utils/formatting-util.js.map +1 -0
  80. package/lib/utils/formatting-util.spec.d.ts +2 -0
  81. package/lib/utils/formatting-util.spec.d.ts.map +1 -0
  82. package/lib/utils/formatting-util.spec.js +247 -0
  83. package/lib/utils/formatting-util.spec.js.map +1 -0
  84. package/package.json +6 -4
  85. package/src/index.ts +4 -0
  86. package/src/main/custom-chart-context.spec.ts +474 -9
  87. package/src/main/custom-chart-context.ts +246 -12
  88. package/src/main/post-message-event-bridge.ts +1 -1
  89. package/src/main/util.spec.ts +41 -0
  90. package/src/react/use-custom-chart-context.spec.tsx +65 -3
  91. package/src/test/test-conditional-formatting-utils.ts +95 -0
  92. package/src/types/answer-column.types.ts +102 -1
  93. package/src/types/chart-to-ts-event.types.ts +7 -0
  94. package/src/types/common.types.ts +165 -10
  95. package/src/types/conditional-formatting.types.ts +157 -0
  96. package/src/types/configurator.types.ts +21 -0
  97. package/src/types/number-formatting.types.ts +80 -0
  98. package/src/types/ts-to-chart-event.types.ts +38 -6
  99. package/src/types/visual-prop.types.ts +75 -1
  100. package/src/utils/conditional-formatting/conditional-formatting.spec.ts +469 -0
  101. package/src/utils/conditional-formatting/conditional-formatting.ts +229 -0
  102. package/src/utils/date-formatting.spec.ts +511 -0
  103. package/src/utils/date-formatting.ts +679 -0
  104. package/src/utils/date-utils.spec.ts +93 -0
  105. package/src/utils/date-utils.ts +69 -0
  106. package/src/utils/formatting-util.spec.ts +312 -0
  107. package/src/utils/formatting-util.ts +234 -0
@@ -0,0 +1,679 @@
1
+ /**
2
+ * @file: Date Formatting Utils
3
+ *
4
+ * @author Yashvardhan Nehra <yashvardhan.nehra@thoughtspot.com>
5
+ *
6
+ * Copyright: ThoughtSpot Inc. 2023
7
+ */
8
+
9
+ import _ from 'lodash';
10
+ import { DateTime } from 'luxon';
11
+ import {
12
+ ChartColumn,
13
+ ColumnTimeBucket,
14
+ ColumnType,
15
+ DataType,
16
+ } from '../types/answer-column.types';
17
+
18
+ export interface CustomCalendarDate {
19
+ v: {
20
+ s: number;
21
+ e: number;
22
+ };
23
+ d: string;
24
+ }
25
+
26
+ export const dateFormatPresets = {
27
+ DATE_SHORT: 'DATE_SHORT',
28
+ DATE_SHORT_WITH_HOUR: 'DATE_SHORT_WITH_HOUR',
29
+ DATE_SHORT_WITH_HOUR_WITHOUT_YEAR: 'DATE_SHORT_WITH_HOUR_WITHOUT_YEAR',
30
+ DATETIME_SHORT: 'DATETIME_SHORT',
31
+ DATETIME_SHORT_WITHOUT_YEAR: 'DATETIME_SHORT_WITHOUT_YEAR',
32
+ DATETIME_SHORT_WITH_SECONDS: 'DATETIME_SHORT_WITH_SECONDS',
33
+ DATETIME_SHORT_WITH_MILLIS: 'DATETIME_SHORT_WITH_MILLIS',
34
+ MONTH_WITH_YEAR: 'MONTH_WITH_YEAR',
35
+ QUARTER_WITH_YEAR: 'QUARTER_WITH_YEAR',
36
+ QUARTER_WITH_2_DIGIT_YEAR: 'QUARTER_WITH_2_DIGIT_YEAR',
37
+ DEFAULT_TIME_FORMAT: 'DEFAULT_TIME_FORMAT',
38
+ TIME_24_WITH_SECONDS: 'TIME_24_WITH_SECONDS',
39
+ DATE_SHORT_2_DIGIT_YEAR: 'DATE_SHORT_2_DIGIT_YEAR',
40
+ DATETIME_24_SHORT_WITHOUT_YEAR: 'DATETIME_24_SHORT_WITHOUT_YEAR',
41
+ DATETIME_24_SHORT: 'DATETIME_24_SHORT',
42
+ DATETIME_SHORT_WITH_SECONDS_WITHOUT_YEAR:
43
+ 'DATETIME_SHORT_WITH_SECONDS_WITHOUT_YEAR',
44
+ DATETIME_SHORT_WITH_MILLIS_WITHOUT_YEAR:
45
+ 'DATETIME_SHORT_WITH_MILLIS_WITHOUT_YEAR',
46
+ DATETIME_24_SHORT_WITH_MILLIS_WITHOUT_YEAR:
47
+ 'DATETIME_24_SHORT_WITH_MILLIS_WITHOUT_YEAR',
48
+ DATETIME_24_SHORT_WITH_MILLIS: 'DATETIME_24_SHORT_WITH_MILLIS',
49
+ MONTH_WITH_DAY_AND_YEAR: 'MONTH_WITH_DAY_AND_YEAR',
50
+ MONTH_WITH_2_DIGIT_YEAR: 'MONTH_WITH_2_DIGIT_YEAR',
51
+ DAY_WITH_MONTH_NUM: 'DAY_WITH_MONTH_NUM',
52
+ DATE_SHORT_WITH_HOUR_24_WITHOUT_YEAR:
53
+ 'DATE_SHORT_WITH_HOUR_24_WITHOUT_YEAR',
54
+ DATE_SHORT_WITH_HOUR_24: 'DATE_SHORT_WITH_HOUR_24',
55
+ QUARTER: 'QUARTER',
56
+ MONTH_ONLY: 'MONTH_ONLY',
57
+ DATETIME_WITH_SHORT_OFFSET: 'DATETIME_WITH_SHORT_OFFSET',
58
+ };
59
+ const yearlessFormats = {
60
+ DATE_SHORT: 'DAY_WITH_MONTH',
61
+ DATE_SHORT_WITH_HOUR: 'DATE_SHORT_WITH_HOUR_WITHOUT_YEAR',
62
+ DATETIME_SHORT: 'DATETIME_SHORT_WITHOUT_YEAR',
63
+ DATETIME_SHORT_WITH_SECONDS: 'DATETIME_SHORT_WITH_SECONDS_WITHOUT_YEAR',
64
+ DATETIME_SHORT_WITH_MILLIS: 'DATETIME_SHORT_WITH_MILLIS_WITHOUT_YEAR',
65
+ MONTH_WITH_YEAR: 'MONTH_ONLY',
66
+ QUARTER_WITH_YEAR: 'QUARTER',
67
+ QUARTER_WITH_2_DIGIT_YEAR: 'QUARTER',
68
+ DATE_SHORT_2_DIGIT_YEAR: 'DAY_WITH_MONTH',
69
+ DATETIME_24_SHORT: 'DATETIME_24_SHORT_WITHOUT_YEAR',
70
+ DATETIME_24_SHORT_WITH_MILLIS: 'DATETIME_24_SHORT_WITH_MILLIS_WITHOUT_YEAR',
71
+ MONTH_WITH_DAY_AND_YEAR: 'DAY_WITH_MONTH',
72
+ MONTH_WITH_2_DIGIT_YEAR: 'DAY_WITH_MONTH',
73
+ DATE_SHORT_WITH_HOUR_24: 'DATE_SHORT_WITH_HOUR_24_WITHOUT_YEAR',
74
+ };
75
+
76
+ export const dateNumTypes = {
77
+ DATE_NUM_ABS_DAY: 'DATE_NUM_ABS_DAY',
78
+ DATE_NUM_ABS_MONTH: 'DATE_NUM_ABS_MONTH',
79
+ DATE_NUM_ABS_QUARTER: 'DATE_NUM_ABS_QUARTER',
80
+ DATE_NUM_ABS_YEAR: 'DATE_NUM_ABS_YEAR',
81
+ DATE_NUM_DAY_IN_MONTH: 'DATE_NUM_DAY_IN_MONTH',
82
+ DATE_NUM_DAY_IN_QUARTER: 'DATE_NUM_DAY_IN_QUARTER',
83
+ DATE_NUM_DAY_IN_YEAR: 'DATE_NUM_DAY_IN_YEAR',
84
+ DATE_NUM_DAY_OF_WEEK: 'DATE_NUM_DAY_OF_WEEK',
85
+ DATE_NUM_MONTH_IN_QUARTER: 'DATE_NUM_MONTH_IN_QUARTER',
86
+ DATE_NUM_MONTH_IN_YEAR: 'DATE_NUM_MONTH_IN_YEAR',
87
+ DATE_NUM_QUARTER_IN_YEAR: 'DATE_NUM_QUARTER_IN_YEAR',
88
+ DATE_NUM_WEEK_IN_YEAR: 'DATE_NUM_WEEK_IN_YEAR',
89
+ DATE_NUM_WEEK_IN_QUARTER: 'DATE_NUM_WEEK_IN_QUARTER',
90
+ DATE_NUM_WEEK_IN_MONTH: 'DATE_NUM_WEEK_IN_MONTH',
91
+ DATE_NUM_HOUR_IN_DAY: 'DATE_NUM_HOUR_IN_DAY',
92
+ };
93
+
94
+ const weekdays = [
95
+ 'Sunday',
96
+ 'Monday',
97
+ 'Tuesday',
98
+ 'Wednesday',
99
+ 'Thursday',
100
+ 'Friday',
101
+ 'Saturday',
102
+ ];
103
+
104
+ const months = [
105
+ 'January',
106
+ 'February',
107
+ 'March',
108
+ 'April',
109
+ 'May',
110
+ 'June',
111
+ 'July',
112
+ 'August',
113
+ 'September',
114
+ 'October',
115
+ 'November',
116
+ 'December',
117
+ ];
118
+
119
+ export const bucketizationToDatePreset = {
120
+ HOURLY: dateFormatPresets.DATE_SHORT_WITH_HOUR, // hourly
121
+ DAILY: dateFormatPresets.DATE_SHORT, // daily
122
+ WEEKLY: dateFormatPresets.DATE_SHORT, // weekly
123
+ MONTHLY: dateFormatPresets.MONTH_WITH_YEAR,
124
+ QUARTERLY: dateFormatPresets.QUARTER_WITH_YEAR, // quarterly
125
+ YEARLY: 'yyyy', // yearly
126
+ };
127
+ const dateFormatPresetsToLuxonPresets = {
128
+ [dateFormatPresets.TIME_24_WITH_SECONDS]: DateTime.TIME_24_WITH_SECONDS,
129
+ };
130
+
131
+ export const timeBuckets = {
132
+ NO_BUCKET: 'ms',
133
+ HOURLY: 'h',
134
+ DAILY: 'd',
135
+ WEEKLY: 'w',
136
+ MONTHLY: 'M',
137
+ QUARTERLY: 'Q',
138
+ YEARLY: 'y',
139
+ DAY_OF_WEEK: 'dow',
140
+ DAY_OF_MONTH: 'dom',
141
+ DAY_OF_QUARTER: 'doq',
142
+ DAY_OF_YEAR: 'doy',
143
+ WEEK_OF_MONTH: 'wom',
144
+ WEEK_OF_QUARTER: 'woq',
145
+ WEEK_OF_YEAR: 'woy',
146
+ MONTH_OF_QUARTER: 'moq',
147
+ MONTH_OF_YEAR: 'moy',
148
+ QUARTER_OF_YEAR: 'qoy',
149
+ };
150
+
151
+ const DEFAULT_QUARTER_START_MONTH = 1;
152
+
153
+ /**
154
+ * Checks if a specified column has a data type of 'DATE' or 'DATE_TIME'.
155
+ *
156
+ * @param col - The column to check, represented as a `ChartColumn`.
157
+ * @returns True if the column's data type is either 'DATE' or 'DATE_TIME'; otherwise, false.
158
+ */
159
+
160
+ export const isDateColumn = (col: ChartColumn) =>
161
+ DataType[col.dataType] === 'DATE' || DataType[col.dataType] === 'DATE_TIME';
162
+
163
+ /**
164
+ * Determines if a specified column is of type 'ATTRIBUTE'.
165
+ *
166
+ * @param col - The column to check, represented as a `ChartColumn`.
167
+ * @returns True if the column's type is `ATTRIBUTE`; otherwise, false.
168
+ */
169
+
170
+ export const isAttribute = (col: ChartColumn) =>
171
+ col.type === ColumnType.ATTRIBUTE;
172
+
173
+ /**
174
+ * Checks if a specified column has a data type of 'DATE_TIME'.
175
+ *
176
+ * @param col - The column to check, represented as a `ChartColumn`.
177
+ * @returns True if the column's data type is `DATE_TIME`; otherwise, false.
178
+ */
179
+
180
+ export const isDateTimeColumn = (col: ChartColumn) =>
181
+ DataType[col.dataType] === 'DATE_TIME';
182
+
183
+ /**
184
+ * Retrieves the custom calendar GUID associated with a specified column.
185
+ *
186
+ * @param col - The column from which to retrieve the calendar GUID, represented as a `ChartColumn`.
187
+ * @returns The custom calendar GUID of the column, or undefined if not available.
188
+ */
189
+ export const getCustomCalendarGuidFromColumn = (col: ChartColumn) =>
190
+ col.calenderGuid;
191
+
192
+ /**
193
+ * Determines if a specified column's time bucket is one of the predefined numeric time buckets.
194
+ *
195
+ * This function checks if the column's `timeBucket` is one of the following:
196
+ * `HOUR_OF_DAY`, `DAY_OF_WEEK`, `DAY_OF_MONTH`, `DAY_OF_QUARTER`, `DAY_OF_YEAR`,
197
+ * `WEEK_OF_MONTH`, `WEEK_OF_QUARTER`, `WEEK_OF_YEAR`, `MONTH_OF_QUARTER`,
198
+ * `MONTH_OF_YEAR`, or `QUARTER_OF_YEAR`.
199
+ *
200
+ * @param col - The column to check, represented as a `ChartColumn`.
201
+ * @returns True if the column's time bucket is one of the specified date-related numeric time buckets; otherwise, false.
202
+ */
203
+
204
+ const isDateNumTimeBucket = (col: ChartColumn): boolean => {
205
+ return [
206
+ ColumnTimeBucket.HOUR_OF_DAY,
207
+ ColumnTimeBucket.DAY_OF_WEEK,
208
+ ColumnTimeBucket.DAY_OF_MONTH,
209
+ ColumnTimeBucket.DAY_OF_QUARTER,
210
+ ColumnTimeBucket.DAY_OF_YEAR,
211
+ ColumnTimeBucket.WEEK_OF_MONTH,
212
+ ColumnTimeBucket.WEEK_OF_QUARTER,
213
+ ColumnTimeBucket.WEEK_OF_YEAR,
214
+ ColumnTimeBucket.MONTH_OF_QUARTER,
215
+ ColumnTimeBucket.MONTH_OF_YEAR,
216
+ ColumnTimeBucket.QUARTER_OF_YEAR,
217
+ ].includes(col.timeBucket);
218
+ };
219
+
220
+ /**
221
+ * Determines if a specified column is both an attribute and has a date-related numeric time bucket.
222
+ *
223
+ * This function checks if the column is of type `ATTRIBUTE` and has a time bucket that is one of
224
+ * the predefined date-related numeric time buckets, as determined by `isDateNumTimeBucket`.
225
+ *
226
+ * @param col - The column to check, represented as a `ChartColumn`.
227
+ * @returns True if the column is an attribute and has a date-related numeric time bucket; otherwise, false.
228
+ */
229
+
230
+ export const isDateNumColumn = (col: ChartColumn): boolean => {
231
+ return isAttribute(col) && isDateNumTimeBucket(col);
232
+ };
233
+
234
+ /**
235
+ * Determines if a specified column belongs to the "date family," meaning it is either a date column
236
+ * or a date-related numeric column.
237
+ *
238
+ * This function checks if the column is a date column, as determined by `isDateColumn`, or a
239
+ * date-related numeric column, as determined by `isDateNumColumn`.
240
+ *
241
+ * @param col - The column to check, represented as a `ChartColumn`.
242
+ * @returns True if the column is either a date column or a date-related numeric column; otherwise, false.
243
+ */
244
+
245
+ export const isDateFamilyColumn = (col: ChartColumn): boolean => {
246
+ return isDateColumn(col) || isDateNumColumn(col);
247
+ };
248
+
249
+ /**
250
+ * Checks if a specified column has a data type of 'TIME'.
251
+ *
252
+ * @param col - The column to check, represented as a `ChartColumn`.
253
+ * @returns True if the column's data type is `TIME`; otherwise, false.
254
+ */
255
+
256
+ export const isTimeColumn = (col: ChartColumn) => {
257
+ return DataType[col.dataType] === 'TIME';
258
+ };
259
+ /**
260
+ * Retrieves the effective date numeric data type based on the column's time bucket.
261
+ *
262
+ * This function maps a column's `timeBucket` to a corresponding date numeric data type, such as
263
+ * `DATE_NUM_DAY_OF_WEEK`, `DATE_NUM_DAY_IN_MONTH`, etc. If the `timeBucket` does not match any
264
+ * predefined value, the function returns `undefined`.
265
+ *
266
+ * @param col - The column to check, represented as a `ChartColumn`.
267
+ * @returns The corresponding date numeric data type, or `undefined` if the `timeBucket` is not recognized.
268
+ */
269
+
270
+ export const getEffectiveDateNumDataType = (col: ChartColumn) => {
271
+ switch (col.timeBucket) {
272
+ case ColumnTimeBucket.DAY_OF_WEEK:
273
+ return dateNumTypes.DATE_NUM_DAY_OF_WEEK;
274
+ case ColumnTimeBucket.DAY_OF_MONTH:
275
+ return dateNumTypes.DATE_NUM_DAY_IN_MONTH;
276
+ case ColumnTimeBucket.DAY_OF_QUARTER:
277
+ return dateNumTypes.DATE_NUM_DAY_IN_QUARTER;
278
+ case ColumnTimeBucket.DAY_OF_YEAR:
279
+ return dateNumTypes.DATE_NUM_DAY_IN_YEAR;
280
+ case ColumnTimeBucket.WEEK_OF_MONTH:
281
+ return dateNumTypes.DATE_NUM_WEEK_IN_MONTH;
282
+ case ColumnTimeBucket.WEEK_OF_QUARTER:
283
+ return dateNumTypes.DATE_NUM_WEEK_IN_QUARTER;
284
+ case ColumnTimeBucket.WEEK_OF_YEAR:
285
+ return dateNumTypes.DATE_NUM_WEEK_IN_YEAR;
286
+ case ColumnTimeBucket.MONTH_OF_QUARTER:
287
+ return dateNumTypes.DATE_NUM_MONTH_IN_QUARTER;
288
+ case ColumnTimeBucket.MONTH_OF_YEAR:
289
+ return dateNumTypes.DATE_NUM_MONTH_IN_YEAR;
290
+ case ColumnTimeBucket.QUARTER_OF_YEAR:
291
+ return dateNumTypes.DATE_NUM_QUARTER_IN_YEAR;
292
+ default:
293
+ return undefined;
294
+ }
295
+ };
296
+
297
+ /**
298
+ * Determines if the given column has a custom calendar.
299
+ *
300
+ * @param col - The chart column to check.
301
+ * @returns True if the column is a date family column and has a custom calendar GUID, false otherwise.
302
+ */
303
+ export const hasCustomCalendar = (col: ChartColumn): boolean =>
304
+ isDateFamilyColumn(col) && !!getCustomCalendarGuidFromColumn(col);
305
+
306
+ /**
307
+ * Retrieves the start epoch from a custom calendar date.
308
+ *
309
+ * @param date - The custom calendar date object.
310
+ * @returns The start epoch time if present, null otherwise.
311
+ */
312
+ export function getStartEpoch(date: CustomCalendarDate): number | null {
313
+ if (_.has(date, 'v') && _.has(date.v, 's')) {
314
+ return date.v.s;
315
+ }
316
+ return null;
317
+ }
318
+
319
+ export const assignQuarterValueToString = (
320
+ quarter_of_year: any,
321
+ value: any,
322
+ ) => {
323
+ return quarter_of_year.replace(/\{.*?\}/, value);
324
+ };
325
+
326
+ export function getMonthOfYear(
327
+ num: any,
328
+ quarterStartMonth: any,
329
+ monthOfYear: any,
330
+ ) {
331
+ let monthNum = num + quarterStartMonth - 1;
332
+ monthNum = monthNum > 12 ? monthNum - 12 : monthNum;
333
+
334
+ return monthOfYear[months[monthNum - 1]]; // -1 as monthNum is 1 indexed
335
+ }
336
+
337
+ export function getDisplayString(date: CustomCalendarDate): string | null {
338
+ if (_.has(date, 'd')) {
339
+ return date.d;
340
+ }
341
+ return null;
342
+ }
343
+
344
+ export const useQuarterStart = (luxonDate: any, quarterStartMonth: any) => {
345
+ const newLuxonDate = luxonDate;
346
+ newLuxonDate.quarterStartMonth = quarterStartMonth;
347
+ return newLuxonDate;
348
+ };
349
+
350
+ export const getCustomCalendarValueFromEpoch = (
351
+ col: ChartColumn,
352
+ dateEpoch: number,
353
+ displayToCustomCalendarValueMap: any,
354
+ ) => {
355
+ if (
356
+ hasCustomCalendar(col) &&
357
+ _.has(displayToCustomCalendarValueMap, dateEpoch)
358
+ ) {
359
+ return displayToCustomCalendarValueMap[dateEpoch];
360
+ }
361
+ return null;
362
+ };
363
+ const parseDate = (dateString: string, format: string, options: any) => {
364
+ return DateTime.fromFormat(
365
+ dateString,
366
+ options.tsLocaleBasedDateFormats[format] || format,
367
+ ).toJSDate();
368
+ };
369
+
370
+ export function getSpecialFormatData(value: string | number, options: any) {
371
+ if (
372
+ value ===
373
+ options.tsLocaleBasedStringsFormats.null_value_placeholder_label ||
374
+ value ===
375
+ options.tsLocaleBasedStringsFormats.empty_value_placeholder_label
376
+ ) {
377
+ return value;
378
+ }
379
+
380
+ if (value === options.tsDateConstants.special_value_unavailable) {
381
+ return options.tsLocaleBasedStringsFormats
382
+ .unavailabe_column_sample_value;
383
+ }
384
+
385
+ // This (==) checks for both null and undefined
386
+ if (value === null || value === undefined) {
387
+ return options.tsLocaleBasedStringsFormats.null_value_placeholder_label;
388
+ }
389
+ // {Empty} placeholder is set for empty string or no characters
390
+ // other than spaces.
391
+ if (value === '') {
392
+ return options.tsLocaleBasedStringsFormats
393
+ .empty_value_placeholder_label;
394
+ }
395
+ return null;
396
+ }
397
+
398
+ export function sanitizeDate(
399
+ inputDate: string | number,
400
+ format: string,
401
+ options: any,
402
+ ) {
403
+ const specialVal = getSpecialFormatData(inputDate, options);
404
+ if (specialVal) {
405
+ return specialVal;
406
+ }
407
+ if (typeof inputDate === 'string') {
408
+ if (!_.isNaN(Number(inputDate))) {
409
+ return parseInt(inputDate, 10);
410
+ }
411
+ return parseDate(inputDate, format, options);
412
+ }
413
+ return inputDate;
414
+ }
415
+
416
+ /**
417
+ * Get the formatted date based on the format tokens
418
+ * @param {number} epochMillis
419
+ * @param {string} format: use dateFormatPresets to get localized formatted date
420
+ * or pass the format pattern for non localized results
421
+ * @returns {string}
422
+ */
423
+ export const formatDateTime = (
424
+ epochMillis: number,
425
+ format: string,
426
+ useSystemCalendar?: boolean,
427
+ options?: any,
428
+ ) => {
429
+ let newFormat = format;
430
+ let luxonDate;
431
+ try {
432
+ luxonDate = DateTime.fromMillis(epochMillis * 1000);
433
+ } catch (e) {
434
+ return 'Invalid Date';
435
+ }
436
+ if (options.tsLocaleBasedDateFormats[newFormat]) {
437
+ if (_.get(options, 'omitYear')) {
438
+ if (yearlessFormats[newFormat as keyof typeof yearlessFormats]) {
439
+ newFormat =
440
+ yearlessFormats[newFormat as keyof typeof yearlessFormats];
441
+ }
442
+ }
443
+ newFormat = options.tsLocaleBasedDateFormats[newFormat];
444
+ }
445
+ const customCalendarOverridesFiscalOffset = _.get(
446
+ options,
447
+ 'customCalendarOverridesFiscalOffset',
448
+ );
449
+ // if format preset is a luxon preset
450
+ if (!newFormat || dateFormatPresetsToLuxonPresets[newFormat]) {
451
+ // Note: this will not add FY to the year in case of custom
452
+ // quarterStartMonth but the year would be the correct fiscal year
453
+ return luxonDate.toLocaleString(
454
+ dateFormatPresetsToLuxonPresets[newFormat],
455
+ );
456
+ }
457
+ // qqq is not supported in luxon
458
+ newFormat = newFormat.replace(/qqq/, "'Q'q");
459
+ // support YYYY and YY
460
+ newFormat = newFormat.replace(/(YYYY|YY)/, _.lowerCase);
461
+ const quarterStartMonth = options.quarterStartMonth;
462
+ if (
463
+ quarterStartMonth > 1 &&
464
+ !useSystemCalendar &&
465
+ !customCalendarOverridesFiscalOffset
466
+ ) {
467
+ newFormat = newFormat.replace(/[yyyy||yy]/, "'FY' $&");
468
+ }
469
+ return useQuarterStart(
470
+ luxonDate,
471
+ useSystemCalendar ? DEFAULT_QUARTER_START_MONTH : quarterStartMonth,
472
+ ).toFormat(newFormat);
473
+ };
474
+
475
+ function getDayOfWeek(num: any, weekOfDay: any) {
476
+ const new_num = num % 7;
477
+ return weekOfDay[weekdays[new_num]];
478
+ }
479
+ /**
480
+ * Converts a number or string into its corresponding ordinal suffixed value.
481
+ *
482
+ * This function takes a number (or string that can be parsed as a number) and returns it as a
483
+ * string with the appropriate ordinal suffix (`st`, `nd`, `rd`, or `th`). The function accounts for
484
+ * exceptions in English grammar, such as 11th, 12th, and 13th, which do not follow the standard
485
+ * suffix rules.
486
+ *
487
+ * @param i - The number (or string) to convert into an ordinal suffixed value.
488
+ * @returns A string representing the number with its ordinal suffix (e.g., "1st", "2nd", "3rd", "4th").
489
+ */
490
+ export function getOrdinalSuffixedValue(i: number | string): string {
491
+ let ni = i;
492
+ // eslint-disable-next-line radix
493
+ ni = parseInt(ni.toString());
494
+ const j = ni % 10;
495
+ const k = ni % 100;
496
+ // eslint-disable-next-line eqeqeq
497
+ if (j == 1 && k != 11) {
498
+ return `${ni}st`;
499
+ }
500
+ // eslint-disable-next-line eqeqeq
501
+ if (j == 2 && k != 12) {
502
+ return `${ni}nd`;
503
+ }
504
+ // eslint-disable-next-line eqeqeq
505
+ if (j == 3 && k != 13) {
506
+ return `${ni}rd`;
507
+ }
508
+ return `${ni}th`;
509
+ }
510
+ /**
511
+ * Formats a date-related numeric value based on its effective data type, a given format pattern,
512
+ * and options.
513
+ *
514
+ * This function is used to format date-related numeric values (such as days of the month, weeks of
515
+ * the year, or hours of the day) according to the specified `effectiveDataType`, `formatPattern`,
516
+ * and other options. It supports custom formatting for various date-related values and can handle
517
+ * special cases like null values.
518
+ *
519
+ * @param effectiveDataType - The effective data type to format, which determines how the value will be interpreted
520
+ * (e.g., `DATE_NUM_ABS_DAY`, `DATE_NUM_DAY_IN_MONTH`).
521
+ * @param value - The numeric value (or string) to format. This value is typically a date-related number.
522
+ * @param formatPattern - The pattern to use for formatting the value (e.g., for day or month formatting).
523
+ * @param options - An object containing various formatting options, such as locale-based string formats, date constants,
524
+ * and settings for the quarter start month and special value handling.
525
+ *
526
+ * @returns A string representing the formatted date-related numeric value, or a placeholder if the value is null.
527
+ *
528
+ * @example
529
+ * // Example usage
530
+ * const formattedDate = formatDateNum(
531
+ * dateNumTypes.DATE_NUM_DAY_IN_MONTH,
532
+ * 15,
533
+ * 'm',
534
+ * options // get from {@link generateMapOptions}
535
+ * );
536
+ * console.log(formattedDate); // Output could be '15th day of month' depending on the format
537
+ */
538
+
539
+ export function formatDateNum(
540
+ effectiveDataType: string | undefined,
541
+ value: number | string,
542
+ formatPattern: string,
543
+ options: any,
544
+ ) {
545
+ let newValue = value;
546
+ if (_.isString(newValue)) {
547
+ // eslint-disable-next-line radix
548
+ newValue = parseInt(value.toString());
549
+ }
550
+
551
+ if (!value && value !== 0) {
552
+ return options.tsLocaleBasedStringsFormats.null_value_placeholder_label;
553
+ }
554
+ const specialVal = getSpecialFormatData(value, options);
555
+ if (specialVal) {
556
+ return specialVal;
557
+ }
558
+ switch (effectiveDataType) {
559
+ case dateNumTypes.DATE_NUM_ABS_DAY:
560
+ case dateNumTypes.DATE_NUM_ABS_MONTH:
561
+ case dateNumTypes.DATE_NUM_ABS_QUARTER:
562
+ case dateNumTypes.DATE_NUM_ABS_YEAR:
563
+ return `${value}`;
564
+ case dateNumTypes.DATE_NUM_DAY_IN_MONTH:
565
+ return formatPattern === options.tsDateConstants.day_in_month_format
566
+ ? `${value}`
567
+ : `${getOrdinalSuffixedValue(value)} day of month`;
568
+ case dateNumTypes.DATE_NUM_DAY_IN_QUARTER:
569
+ return formatPattern ===
570
+ options.tsDateConstants.day_in_quarter_format
571
+ ? `${value}`
572
+ : `${getOrdinalSuffixedValue(value)} day of quarter`;
573
+ case dateNumTypes.DATE_NUM_DAY_IN_YEAR:
574
+ return formatPattern === options.tsDateConstants.day_in_year_format
575
+ ? `${value}`
576
+ : `${getOrdinalSuffixedValue(value)} day of year`;
577
+ case dateNumTypes.DATE_NUM_DAY_OF_WEEK:
578
+ return formatPattern === options.tsDateConstants.day_of_week_format
579
+ ? // eslint-disable-next-line @typescript-eslint/no-use-before-define
580
+ getDayOfWeek(
581
+ value,
582
+ options.tsLocaleBasedStringsFormats.weekOfDay,
583
+ )
584
+ : `${value}`;
585
+ case dateNumTypes.DATE_NUM_MONTH_IN_QUARTER:
586
+ return formatPattern ===
587
+ options.tsDateConstants.month_in_quarter_format
588
+ ? `${value}`
589
+ : `${getOrdinalSuffixedValue(value)} month of quarter`;
590
+ case dateNumTypes.DATE_NUM_MONTH_IN_YEAR:
591
+ return formatPattern ===
592
+ options.tsDateConstants.month_in_year_format
593
+ ? // eslint-disable-next-line @typescript-eslint/no-use-before-define
594
+ getMonthOfYear(
595
+ value,
596
+ options.quarterStartMonth,
597
+ options.tsLocaleBasedStringsFormats.monthOfYear,
598
+ )
599
+ : `${value}`;
600
+ case dateNumTypes.DATE_NUM_QUARTER_IN_YEAR:
601
+ // Falcon returns quarter as 1 indexed, which is passed on by
602
+ // callosum to blink, so we can use value instead of value + 1.
603
+ return assignQuarterValueToString(
604
+ options.tsLocaleBasedStringsFormats.quarter_of_year,
605
+ value,
606
+ );
607
+ case dateNumTypes.DATE_NUM_WEEK_IN_YEAR:
608
+ // +1 to value as Falcon values start with 0.
609
+ return formatPattern === options.tsDateConstants.week_in_year_format
610
+ ? `${value}`
611
+ : `${getOrdinalSuffixedValue(value)} week of year`;
612
+ case dateNumTypes.DATE_NUM_WEEK_IN_QUARTER:
613
+ case dateNumTypes.DATE_NUM_WEEK_IN_MONTH:
614
+ return `${value}`;
615
+ case dateNumTypes.DATE_NUM_HOUR_IN_DAY:
616
+ return `${value}`;
617
+ default:
618
+ console.log(
619
+ 'unknown effectiveDataType for date num',
620
+ effectiveDataType,
621
+ );
622
+ return value;
623
+ }
624
+ }
625
+
626
+ /**
627
+ *
628
+ * @param {number|string} inputDate Can be either a parseable format of date or an epoch value.
629
+ * @param {string} format a dateFormatPresets or a format pattern string.
630
+ * @param {boolean} useSystemCalendar If any custom calendar setting (e.g. quarterStartMonth)
631
+ * is to be ignored.
632
+ * @return {string} Returns the formatted date per the format.
633
+ */
634
+ export function formatDate(
635
+ inputDate: number | string,
636
+ format: string,
637
+ useSystemCalendar: boolean,
638
+ options: any,
639
+ ): string {
640
+ let formatPattern = format;
641
+ let newInputDate: any = inputDate;
642
+ if (newInputDate === undefined || newInputDate === null) {
643
+ return options.tsLocaleBasedStringsFormats.null_value_placeholder_label;
644
+ }
645
+ if (!formatPattern) {
646
+ formatPattern = dateFormatPresets.DATE_SHORT;
647
+ }
648
+ if (
649
+ newInputDate ===
650
+ options.tsLocaleBasedStringsFormats.null_value_placeholder_label ||
651
+ newInputDate ===
652
+ options.tsLocaleBasedStringsFormats.empty_value_placeholder_label ||
653
+ newInputDate ===
654
+ options.tsLocaleBasedStringsFormats.other_value_placeholder_label
655
+ ) {
656
+ return newInputDate;
657
+ }
658
+ newInputDate = sanitizeDate(newInputDate, formatPattern, options);
659
+ if (_.isNaN(newInputDate)) {
660
+ return options.tsLocaleBasedStringsFormats.null_value_placeholder_label;
661
+ }
662
+ let epochMillis = newInputDate;
663
+ if (_.isDate(epochMillis)) {
664
+ epochMillis = newInputDate.getTime();
665
+ }
666
+ if (!_.isNumber(epochMillis)) {
667
+ console.log(
668
+ 'formatDate could not convert input date to a timestamp',
669
+ inputDate,
670
+ );
671
+ return `${inputDate}`;
672
+ }
673
+ return formatDateTime(
674
+ epochMillis,
675
+ formatPattern,
676
+ useSystemCalendar,
677
+ options,
678
+ );
679
+ }