@asteby/metacore-runtime-react 18.0.0 → 18.2.0
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.
- package/CHANGELOG.md +47 -0
- package/dist/dialogs/dynamic-record.d.ts +86 -2
- package/dist/dialogs/dynamic-record.d.ts.map +1 -1
- package/dist/dialogs/dynamic-record.js +306 -88
- package/dist/dynamic-columns-shim.d.ts +1 -1
- package/dist/dynamic-columns-shim.d.ts.map +1 -1
- package/dist/dynamic-columns.d.ts +11 -1
- package/dist/dynamic-columns.d.ts.map +1 -1
- package/dist/dynamic-columns.js +46 -3
- package/dist/dynamic-select-field.d.ts +21 -0
- package/dist/dynamic-select-field.d.ts.map +1 -1
- package/dist/dynamic-select-field.js +2 -2
- package/dist/dynamic-table.d.ts +8 -1
- package/dist/dynamic-table.d.ts.map +1 -1
- package/dist/dynamic-table.js +3 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/__tests__/format-date-cell.test.ts +27 -0
- package/src/dialogs/dynamic-record.tsx +476 -114
- package/src/dynamic-columns-shim.ts +1 -0
- package/src/dynamic-columns.tsx +46 -1
- package/src/dynamic-select-field.tsx +2 -2
- package/src/dynamic-table.tsx +10 -2
- package/src/index.ts +2 -1
|
@@ -30,6 +30,7 @@ export type GetDynamicColumns = (
|
|
|
30
30
|
t: (key: string, options?: any) => string,
|
|
31
31
|
language: string,
|
|
32
32
|
columnFilterConfigs: Map<string, ColumnFilterConfig>,
|
|
33
|
+
timeZone?: string,
|
|
33
34
|
) => ColumnDef<any>[]
|
|
34
35
|
|
|
35
36
|
/** Signature for the host-provided `DynamicIcon` renderer. */
|
package/src/dynamic-columns.tsx
CHANGED
|
@@ -369,16 +369,60 @@ export const DATE_CELL_TYPES = ['date', 'datetime', 'timestamp', 'timestamptz']
|
|
|
369
369
|
* - `date`: day only (`PPP`), no tooltip.
|
|
370
370
|
* - `datetime`/`timestamp(tz)`: day + time (`Pp`) with a full-precision
|
|
371
371
|
* tooltip (`PPpp`) — the 7Leguas pattern.
|
|
372
|
+
*
|
|
373
|
+
* When a `timeZone` (IANA, e.g. the org's `America/Mexico_City`) is provided,
|
|
374
|
+
* instants are rendered in that zone via the native `Intl.DateTimeFormat` so
|
|
375
|
+
* the displayed day/time never shifts with the viewer's browser timezone:
|
|
376
|
+
* - instant (datetime/timestamp(tz)): `dateStyle:'medium' timeStyle:'short'`
|
|
377
|
+
* in the org zone, with a `dateStyle:'long' timeStyle:'medium'` +
|
|
378
|
+
* `timeZoneName:'short'` tooltip.
|
|
379
|
+
* - `date` (pure calendar day): rendered pinned to UTC so it never rolls to
|
|
380
|
+
* the previous/next day, no tooltip.
|
|
381
|
+
* Without a `timeZone`, the exact date-fns behavior is preserved (back-compat).
|
|
372
382
|
*/
|
|
373
383
|
export function formatDateCell(
|
|
374
384
|
value: unknown,
|
|
375
385
|
renderAs: string | undefined,
|
|
376
386
|
locale: Locale,
|
|
387
|
+
timeZone?: string,
|
|
377
388
|
): { display: string; title?: string } | null {
|
|
378
389
|
if (value === null || value === undefined || value === '') return null
|
|
379
390
|
const date = new Date(value as any)
|
|
380
391
|
if (isNaN(date.getTime()) || date.getFullYear() <= 1) return null
|
|
381
392
|
const withTime = renderAs !== 'date'
|
|
393
|
+
if (timeZone) {
|
|
394
|
+
// `locale.code` is the BCP-47 tag date-fns ships (e.g. 'es', 'en-US').
|
|
395
|
+
const localeTag = locale?.code || undefined
|
|
396
|
+
if (withTime) {
|
|
397
|
+
return {
|
|
398
|
+
display: new Intl.DateTimeFormat(localeTag, {
|
|
399
|
+
timeZone,
|
|
400
|
+
dateStyle: 'medium',
|
|
401
|
+
timeStyle: 'short',
|
|
402
|
+
}).format(date),
|
|
403
|
+
// `dateStyle`/`timeStyle` can't be combined with explicit
|
|
404
|
+
// component options like `timeZoneName`, so spell the tooltip
|
|
405
|
+
// out: long date + seconds + the zone abbreviation.
|
|
406
|
+
title: new Intl.DateTimeFormat(localeTag, {
|
|
407
|
+
timeZone,
|
|
408
|
+
year: 'numeric',
|
|
409
|
+
month: 'long',
|
|
410
|
+
day: 'numeric',
|
|
411
|
+
hour: '2-digit',
|
|
412
|
+
minute: '2-digit',
|
|
413
|
+
second: '2-digit',
|
|
414
|
+
timeZoneName: 'short',
|
|
415
|
+
}).format(date),
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
// Pure calendar date: pin to UTC so it never shifts across zones.
|
|
419
|
+
return {
|
|
420
|
+
display: new Intl.DateTimeFormat(localeTag, {
|
|
421
|
+
timeZone: 'UTC',
|
|
422
|
+
dateStyle: 'long',
|
|
423
|
+
}).format(date),
|
|
424
|
+
}
|
|
425
|
+
}
|
|
382
426
|
if (withTime) {
|
|
383
427
|
return {
|
|
384
428
|
display: format(date, 'Pp', { locale }),
|
|
@@ -514,6 +558,7 @@ export function makeDefaultGetDynamicColumns(
|
|
|
514
558
|
t?: (key: string, options?: any) => string,
|
|
515
559
|
currentLanguage?: string,
|
|
516
560
|
filterConfigs?: Map<string, ColumnFilterConfig>,
|
|
561
|
+
timeZone?: string,
|
|
517
562
|
): ColumnDef<any>[] {
|
|
518
563
|
const dateLocale = currentLanguage === 'en' ? enUS : es
|
|
519
564
|
const columns: ColumnDef<any>[] = [
|
|
@@ -665,7 +710,7 @@ export function makeDefaultGetDynamicColumns(
|
|
|
665
710
|
case 'datetime':
|
|
666
711
|
case 'timestamp':
|
|
667
712
|
case 'timestamptz': {
|
|
668
|
-
const formatted = formatDateCell(value, renderAs, dateLocale)
|
|
713
|
+
const formatted = formatDateCell(value, renderAs, dateLocale, timeZone)
|
|
669
714
|
if (!formatted)
|
|
670
715
|
return <span className="text-muted-foreground">-</span>
|
|
671
716
|
return (
|
|
@@ -48,7 +48,7 @@ import type { ActionFieldDef } from './types'
|
|
|
48
48
|
* not a gallery). Inline style for the box dimensions: arbitrary Tailwind
|
|
49
49
|
* classes from a federated addon don't always survive the host's class scan.
|
|
50
50
|
*/
|
|
51
|
-
function OptionThumb({ image, size = 20 }: { image?: string | null; size?: number }) {
|
|
51
|
+
export function OptionThumb({ image, size = 20 }: { image?: string | null; size?: number }) {
|
|
52
52
|
const box = { width: size, height: size }
|
|
53
53
|
if (!image) {
|
|
54
54
|
return (
|
|
@@ -83,7 +83,7 @@ function OptionThumb({ image, size = 20 }: { image?: string | null; size?: numbe
|
|
|
83
83
|
* else a declared icon, else a color dot (enum/status options with a color).
|
|
84
84
|
* Returns null when the option carries none, so plain text options stay plain.
|
|
85
85
|
*/
|
|
86
|
-
function OptionLead({
|
|
86
|
+
export function OptionLead({
|
|
87
87
|
option,
|
|
88
88
|
size = 20,
|
|
89
89
|
}: {
|
package/src/dynamic-table.tsx
CHANGED
|
@@ -90,6 +90,13 @@ interface DynamicTableProps {
|
|
|
90
90
|
* Optional — a sensible default maps each column to { accessorKey, header }.
|
|
91
91
|
*/
|
|
92
92
|
getDynamicColumns?: GetDynamicColumns
|
|
93
|
+
/**
|
|
94
|
+
* IANA timezone (e.g. the org's `America/Mexico_City`) used to render
|
|
95
|
+
* datetime/timestamp cells. When provided, instants are displayed in this
|
|
96
|
+
* zone instead of the viewer's browser zone, so the day/time never shifts.
|
|
97
|
+
* Optional — omitting it preserves the legacy browser-local formatting.
|
|
98
|
+
*/
|
|
99
|
+
timeZone?: string
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
export function DynamicTable({
|
|
@@ -102,6 +109,7 @@ export function DynamicTable({
|
|
|
102
109
|
defaultFilters,
|
|
103
110
|
extraColumns = [],
|
|
104
111
|
getDynamicColumns = defaultGetDynamicColumns,
|
|
112
|
+
timeZone,
|
|
105
113
|
}: DynamicTableProps) {
|
|
106
114
|
const { t, i18n } = useTranslation()
|
|
107
115
|
const api = useApi()
|
|
@@ -590,12 +598,12 @@ export function DynamicTable({
|
|
|
590
598
|
const rowMetadata = metadata.actions?.some((a) => a.placement === 'table' || a.placement === 'create')
|
|
591
599
|
? { ...metadata, actions: metadata.actions.filter((a) => !a.placement || a.placement === 'row') }
|
|
592
600
|
: metadata
|
|
593
|
-
const baseColumns = getDynamicColumns(rowMetadata, handleInternalAction, t, i18n.language, columnFilterConfigs)
|
|
601
|
+
const baseColumns = getDynamicColumns(rowMetadata, handleInternalAction, t, i18n.language, columnFilterConfigs, timeZone)
|
|
594
602
|
const filteredBase = baseColumns.filter((col: ColumnDef<any>) => !hiddenColumns.includes(col.id as string))
|
|
595
603
|
const actionsCol = filteredBase.find((c: ColumnDef<any>) => c.id === 'actions')
|
|
596
604
|
const otherCols = filteredBase.filter((c: ColumnDef<any>) => c.id !== 'actions')
|
|
597
605
|
return [...otherCols, ...extraColumns, ...(actionsCol ? [actionsCol] : [])]
|
|
598
|
-
}, [metadata, handleInternalAction, hiddenColumns, extraColumns, t, i18n.language, columnFilterConfigs, getDynamicColumns])
|
|
606
|
+
}, [metadata, handleInternalAction, hiddenColumns, extraColumns, t, i18n.language, columnFilterConfigs, getDynamicColumns, timeZone])
|
|
599
607
|
|
|
600
608
|
const filters = useMemo(() => [], [])
|
|
601
609
|
|
package/src/index.ts
CHANGED
|
@@ -68,7 +68,8 @@ export {
|
|
|
68
68
|
} from './dynamic-columns'
|
|
69
69
|
export { humanizeToken } from './dynamic-columns-helpers'
|
|
70
70
|
export { NIL_UUID, isNilUuid, normalizeNilUuid } from './nil-uuid'
|
|
71
|
-
export { DynamicRecordDialog } from './dialogs/dynamic-record'
|
|
71
|
+
export { DynamicRecordDialog, ViewValue } from './dialogs/dynamic-record'
|
|
72
|
+
export type { DynamicRecordDialogProps, FieldDef, FieldOption, GetImageUrl } from './dialogs/dynamic-record'
|
|
72
73
|
export { CreateRecordDialog } from './dialogs/create-record-dialog'
|
|
73
74
|
export { ViewRecordDialog } from './dialogs/view-record-dialog'
|
|
74
75
|
export type {
|