@lattice-php/lattice 0.5.0 → 0.6.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.
@@ -73,6 +73,7 @@ function CartesianChart({ props }) {
73
73
  return /* @__PURE__ */ jsx(ResponsiveContainer, {
74
74
  width: "100%",
75
75
  height: props.height,
76
+ debounce: 100,
76
77
  children: /* @__PURE__ */ jsxs(RechartsChart, {
77
78
  data: props.data,
78
79
  margin: chartMargin,
@@ -136,6 +137,7 @@ function PieChartView({ props, series }) {
136
137
  return /* @__PURE__ */ jsx(ResponsiveContainer, {
137
138
  width: "100%",
138
139
  height: props.height,
140
+ debounce: 100,
139
141
  children: /* @__PURE__ */ jsxs(PieChart, {
140
142
  margin: chartMargin,
141
143
  children: [
@@ -1 +1 @@
1
- {"version":3,"file":"chart.js","names":[],"sources":["../../../resources/js/core/components/chart.tsx"],"sourcesContent":["import {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { PropsOf, RendererComponent } from \"@lattice-php/lattice/core/types\";\n\ntype ChartProps = PropsOf<\"chart\">;\ntype ChartSeries = ChartProps[\"series\"][number];\ntype ChartDatum = ChartProps[\"data\"][number];\ntype CartesianSeries = ChartSeries & { type: \"area\" | \"bar\" | \"line\" };\ntype ChartMargin = { bottom: number; left: number; right: number; top: number };\ntype CartesianChartComponent = ComponentType<{\n children: ReactNode;\n data: ChartProps[\"data\"];\n margin?: ChartMargin;\n}>;\n\nconst chartMargin: ChartMargin = { bottom: 0, left: 0, right: 16, top: 8 };\nconst compactLegendProps = {\n align: \"center\" as const,\n height: 24,\n iconSize: 7,\n verticalAlign: \"top\" as const,\n wrapperStyle: { fontSize: 11, lineHeight: \"14px\", paddingBottom: 6 },\n};\nconst axisTick = { fontSize: 10 };\n\nconst palette = [\n \"var(--lt-primary)\",\n \"var(--lt-success)\",\n \"var(--lt-info)\",\n \"var(--lt-warning)\",\n \"var(--lt-danger)\",\n \"var(--lt-muted-fg)\",\n];\n\nfunction colorAt(index: number): string {\n return palette[index % palette.length] ?? \"var(--lt-primary)\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction datumColor(datum: ChartDatum, series: ChartSeries, index: number): string {\n if (isRecord(datum) && typeof datum.color === \"string\") {\n return datum.color;\n }\n\n return series.color ?? colorAt(index);\n}\n\nfunction isCartesianSeries(series: ChartSeries): series is CartesianSeries {\n return series.type === \"area\" || series.type === \"bar\" || series.type === \"line\";\n}\n\nfunction cartesianChartFor(series: CartesianSeries[]): CartesianChartComponent {\n const types = new Set(series.map((item) => item.type));\n const firstSeries = series[0];\n\n if (firstSeries === undefined || types.size !== 1) {\n return ComposedChart;\n }\n\n switch (firstSeries.type) {\n case \"area\":\n return AreaChart;\n case \"bar\":\n return BarChart;\n case \"line\":\n return LineChart;\n }\n}\n\nfunction ChartFrame({\n children,\n description,\n id,\n title,\n}: {\n children: ReactNode;\n description: string | null;\n id?: string;\n title: string | null;\n}) {\n const hasHeader = title !== null || description !== null;\n\n return (\n <div\n className=\"flex flex-col gap-3 rounded-lt border border-lt-border bg-lt-surface p-4 text-lt-surface-fg shadow-lt-sm\"\n data-lattice-component={id}\n >\n {hasHeader && (\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n {title !== null && <div className=\"text-sm font-semibold leading-tight\">{title}</div>}\n {description !== null && (\n <div className=\"text-xs leading-5 text-lt-muted-fg\">{description}</div>\n )}\n </div>\n )}\n {children}\n </div>\n );\n}\n\nfunction CartesianChart({ props }: { props: ChartProps }) {\n const series = props.series.filter(isCartesianSeries);\n const RechartsChart = cartesianChartFor(series);\n\n return (\n <ResponsiveContainer width=\"100%\" height={props.height}>\n <RechartsChart data={props.data} margin={chartMargin}>\n {props.grid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--lt-border)\" />}\n {props.xAxis && props.categoryKey !== null && (\n <XAxis\n dataKey={props.categoryKey}\n stroke=\"var(--lt-muted-fg)\"\n tick={axisTick}\n tickLine={false}\n />\n )}\n {props.yAxis && (\n <YAxis stroke=\"var(--lt-muted-fg)\" tick={axisTick} tickLine={false} width={42} />\n )}\n {props.tooltip && <Tooltip />}\n {props.legend && <Legend {...compactLegendProps} />}\n {series.map((item, index) => {\n const color = item.color ?? colorAt(index);\n const key = `${item.type}:${item.dataKey}`;\n\n if (item.type === \"area\") {\n return (\n <Area\n key={key}\n dataKey={item.dataKey}\n fill={color}\n fillOpacity={0.16}\n name={item.name ?? undefined}\n stackId={item.stackId ?? undefined}\n stroke={color}\n type=\"monotone\"\n />\n );\n }\n\n if (item.type === \"bar\") {\n return (\n <Bar\n key={key}\n dataKey={item.dataKey}\n fill={color}\n name={item.name ?? undefined}\n radius={[4, 4, 0, 0]}\n stackId={item.stackId ?? undefined}\n />\n );\n }\n\n return (\n <Line\n key={key}\n dataKey={item.dataKey}\n dot={false}\n name={item.name ?? undefined}\n stroke={color}\n strokeWidth={2}\n type=\"monotone\"\n />\n );\n })}\n </RechartsChart>\n </ResponsiveContainer>\n );\n}\n\nfunction PieChartView({ props, series }: { props: ChartProps; series: ChartSeries }) {\n return (\n <ResponsiveContainer width=\"100%\" height={props.height}>\n <PieChart margin={chartMargin}>\n {props.tooltip && <Tooltip />}\n {props.legend && <Legend {...compactLegendProps} />}\n <Pie\n data={props.data}\n dataKey={series.dataKey}\n name={series.name ?? undefined}\n nameKey={series.nameKey ?? undefined}\n outerRadius=\"68%\"\n >\n {props.data.map((datum, index) => (\n <Cell key={index} fill={datumColor(datum, series, index)} />\n ))}\n </Pie>\n </PieChart>\n </ResponsiveContainer>\n );\n}\n\nconst ChartComponent: RendererComponent<\"chart\"> = ({ node }) => {\n const props = node.props;\n const pieSeries = props.series.find((series) => series.type === \"pie\");\n const hasCartesianSeries = props.series.some(isCartesianSeries);\n\n return (\n <ChartFrame description={props.description} id={nodeIdentity(node)} title={props.title}>\n <div className=\"min-h-0 w-full\">\n {pieSeries && !hasCartesianSeries ? (\n <PieChartView props={props} series={pieSeries} />\n ) : (\n <CartesianChart props={props} />\n )}\n </div>\n </ChartFrame>\n );\n};\n\nexport default ChartComponent;\n"],"mappings":"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"}
1
+ {"version":3,"file":"chart.js","names":[],"sources":["../../../resources/js/core/components/chart.tsx"],"sourcesContent":["import {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from \"recharts\";\nimport type { ComponentType, ReactNode } from \"react\";\nimport { nodeIdentity } from \"@lattice-php/lattice/core/test-id\";\nimport type { PropsOf, RendererComponent } from \"@lattice-php/lattice/core/types\";\n\ntype ChartProps = PropsOf<\"chart\">;\ntype ChartSeries = ChartProps[\"series\"][number];\ntype ChartDatum = ChartProps[\"data\"][number];\ntype CartesianSeries = ChartSeries & { type: \"area\" | \"bar\" | \"line\" };\ntype ChartMargin = { bottom: number; left: number; right: number; top: number };\ntype CartesianChartComponent = ComponentType<{\n children: ReactNode;\n data: ChartProps[\"data\"];\n margin?: ChartMargin;\n}>;\n\nconst chartMargin: ChartMargin = { bottom: 0, left: 0, right: 16, top: 8 };\nconst compactLegendProps = {\n align: \"center\" as const,\n height: 24,\n iconSize: 7,\n verticalAlign: \"top\" as const,\n wrapperStyle: { fontSize: 11, lineHeight: \"14px\", paddingBottom: 6 },\n};\nconst axisTick = { fontSize: 10 };\n\nconst palette = [\n \"var(--lt-primary)\",\n \"var(--lt-success)\",\n \"var(--lt-info)\",\n \"var(--lt-warning)\",\n \"var(--lt-danger)\",\n \"var(--lt-muted-fg)\",\n];\n\nfunction colorAt(index: number): string {\n return palette[index % palette.length] ?? \"var(--lt-primary)\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction datumColor(datum: ChartDatum, series: ChartSeries, index: number): string {\n if (isRecord(datum) && typeof datum.color === \"string\") {\n return datum.color;\n }\n\n return series.color ?? colorAt(index);\n}\n\nfunction isCartesianSeries(series: ChartSeries): series is CartesianSeries {\n return series.type === \"area\" || series.type === \"bar\" || series.type === \"line\";\n}\n\nfunction cartesianChartFor(series: CartesianSeries[]): CartesianChartComponent {\n const types = new Set(series.map((item) => item.type));\n const firstSeries = series[0];\n\n if (firstSeries === undefined || types.size !== 1) {\n return ComposedChart;\n }\n\n switch (firstSeries.type) {\n case \"area\":\n return AreaChart;\n case \"bar\":\n return BarChart;\n case \"line\":\n return LineChart;\n }\n}\n\nfunction ChartFrame({\n children,\n description,\n id,\n title,\n}: {\n children: ReactNode;\n description: string | null;\n id?: string;\n title: string | null;\n}) {\n const hasHeader = title !== null || description !== null;\n\n return (\n <div\n className=\"flex flex-col gap-3 rounded-lt border border-lt-border bg-lt-surface p-4 text-lt-surface-fg shadow-lt-sm\"\n data-lattice-component={id}\n >\n {hasHeader && (\n <div className=\"flex min-w-0 flex-col gap-1.5\">\n {title !== null && <div className=\"text-sm font-semibold leading-tight\">{title}</div>}\n {description !== null && (\n <div className=\"text-xs leading-5 text-lt-muted-fg\">{description}</div>\n )}\n </div>\n )}\n {children}\n </div>\n );\n}\n\nfunction CartesianChart({ props }: { props: ChartProps }) {\n const series = props.series.filter(isCartesianSeries);\n const RechartsChart = cartesianChartFor(series);\n\n return (\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <RechartsChart data={props.data} margin={chartMargin}>\n {props.grid && <CartesianGrid strokeDasharray=\"3 3\" stroke=\"var(--lt-border)\" />}\n {props.xAxis && props.categoryKey !== null && (\n <XAxis\n dataKey={props.categoryKey}\n stroke=\"var(--lt-muted-fg)\"\n tick={axisTick}\n tickLine={false}\n />\n )}\n {props.yAxis && (\n <YAxis stroke=\"var(--lt-muted-fg)\" tick={axisTick} tickLine={false} width={42} />\n )}\n {props.tooltip && <Tooltip />}\n {props.legend && <Legend {...compactLegendProps} />}\n {series.map((item, index) => {\n const color = item.color ?? colorAt(index);\n const key = `${item.type}:${item.dataKey}`;\n\n if (item.type === \"area\") {\n return (\n <Area\n key={key}\n dataKey={item.dataKey}\n fill={color}\n fillOpacity={0.16}\n name={item.name ?? undefined}\n stackId={item.stackId ?? undefined}\n stroke={color}\n type=\"monotone\"\n />\n );\n }\n\n if (item.type === \"bar\") {\n return (\n <Bar\n key={key}\n dataKey={item.dataKey}\n fill={color}\n name={item.name ?? undefined}\n radius={[4, 4, 0, 0]}\n stackId={item.stackId ?? undefined}\n />\n );\n }\n\n return (\n <Line\n key={key}\n dataKey={item.dataKey}\n dot={false}\n name={item.name ?? undefined}\n stroke={color}\n strokeWidth={2}\n type=\"monotone\"\n />\n );\n })}\n </RechartsChart>\n </ResponsiveContainer>\n );\n}\n\nfunction PieChartView({ props, series }: { props: ChartProps; series: ChartSeries }) {\n return (\n <ResponsiveContainer width=\"100%\" height={props.height} debounce={100}>\n <PieChart margin={chartMargin}>\n {props.tooltip && <Tooltip />}\n {props.legend && <Legend {...compactLegendProps} />}\n <Pie\n data={props.data}\n dataKey={series.dataKey}\n name={series.name ?? undefined}\n nameKey={series.nameKey ?? undefined}\n outerRadius=\"68%\"\n >\n {props.data.map((datum, index) => (\n <Cell key={index} fill={datumColor(datum, series, index)} />\n ))}\n </Pie>\n </PieChart>\n </ResponsiveContainer>\n );\n}\n\nconst ChartComponent: RendererComponent<\"chart\"> = ({ node }) => {\n const props = node.props;\n const pieSeries = props.series.find((series) => series.type === \"pie\");\n const hasCartesianSeries = props.series.some(isCartesianSeries);\n\n return (\n <ChartFrame description={props.description} id={nodeIdentity(node)} title={props.title}>\n <div className=\"min-h-0 w-full\">\n {pieSeries && !hasCartesianSeries ? (\n <PieChartView props={props} series={pieSeries} />\n ) : (\n <CartesianChart props={props} />\n )}\n </div>\n </ChartFrame>\n );\n};\n\nexport default ChartComponent;\n"],"mappings":"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"}
@@ -16,6 +16,7 @@ export declare const LATTICE_EVENT: {
16
16
  readonly toggleSidebar: "lattice:toggle-sidebar";
17
17
  readonly appearanceChange: "lattice:appearance-change";
18
18
  readonly localeChange: "lattice:locale-change";
19
+ readonly timezoneChange: "lattice:timezone-change";
19
20
  readonly actionError: "lattice:action-error";
20
21
  };
21
22
  export type ReloadComponentEvent = CustomEvent<{
@@ -17,6 +17,7 @@ var LATTICE_EVENT = {
17
17
  toggleSidebar: "lattice:toggle-sidebar",
18
18
  appearanceChange: "lattice:appearance-change",
19
19
  localeChange: "lattice:locale-change",
20
+ timezoneChange: "lattice:timezone-change",
20
21
  actionError: "lattice:action-error"
21
22
  };
22
23
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"event-names.js","names":[],"sources":["../../resources/js/events/event-names.ts"],"sourcesContent":["/**\n * Single source of truth for the `lattice:*` DOM events the runtime dispatches\n * and listens for. The built-in effect handlers in effects/registry.ts bridge\n * effects to these events; the rest are framework events with no PHP counterpart.\n */\nexport const LATTICE_EVENT = {\n callout: \"lattice:callout\",\n toast: \"lattice:toast\",\n reloadComponent: \"lattice:reload-component\",\n reloadPage: \"lattice:reload-page\",\n redirect: \"lattice:redirect\",\n download: \"lattice:download\",\n openModal: \"lattice:open-modal\",\n closeModal: \"lattice:close-modal\",\n resetForm: \"lattice:reset-form\",\n toggleSidebar: \"lattice:toggle-sidebar\",\n appearanceChange: \"lattice:appearance-change\",\n localeChange: \"lattice:locale-change\",\n actionError: \"lattice:action-error\",\n} as const;\n\nexport type ReloadComponentEvent = CustomEvent<{\n component?: string;\n type?: string;\n}>;\n"],"mappings":";;;;;;AAKA,IAAa,gBAAgB;CAC3B,SAAS;CACT,OAAO;CACP,iBAAiB;CACjB,YAAY;CACZ,UAAU;CACV,UAAU;CACV,WAAW;CACX,YAAY;CACZ,WAAW;CACX,eAAe;CACf,kBAAkB;CAClB,cAAc;CACd,aAAa;AACf"}
1
+ {"version":3,"file":"event-names.js","names":[],"sources":["../../resources/js/events/event-names.ts"],"sourcesContent":["/**\n * Single source of truth for the `lattice:*` DOM events the runtime dispatches\n * and listens for. The built-in effect handlers in effects/registry.ts bridge\n * effects to these events; the rest are framework events with no PHP counterpart.\n */\nexport const LATTICE_EVENT = {\n callout: \"lattice:callout\",\n toast: \"lattice:toast\",\n reloadComponent: \"lattice:reload-component\",\n reloadPage: \"lattice:reload-page\",\n redirect: \"lattice:redirect\",\n download: \"lattice:download\",\n openModal: \"lattice:open-modal\",\n closeModal: \"lattice:close-modal\",\n resetForm: \"lattice:reset-form\",\n toggleSidebar: \"lattice:toggle-sidebar\",\n appearanceChange: \"lattice:appearance-change\",\n localeChange: \"lattice:locale-change\",\n timezoneChange: \"lattice:timezone-change\",\n actionError: \"lattice:action-error\",\n} as const;\n\nexport type ReloadComponentEvent = CustomEvent<{\n component?: string;\n type?: string;\n}>;\n"],"mappings":";;;;;;AAKA,IAAa,gBAAgB;CAC3B,SAAS;CACT,OAAO;CACP,iBAAiB;CACjB,YAAY;CACZ,UAAU;CACV,UAAU;CACV,WAAW;CACX,YAAY;CACZ,WAAW;CACX,eAAe;CACf,kBAAkB;CAClB,cAAc;CACd,gBAAgB;CAChB,aAAa;AACf"}
@@ -1,6 +1,9 @@
1
1
  import { I18nConfig } from '../types/generated';
2
2
  export type Config = {
3
3
  readonly locales: readonly string[];
4
+ readonly timezone: string | null;
4
5
  };
5
6
  export declare function setConfig(config: I18nConfig | undefined): void;
7
+ export declare function configTimezone(): string | null;
8
+ export declare function subscribeConfig(callback: () => void): () => void;
6
9
  export declare function useConfig(): Config;
@@ -1,6 +1,9 @@
1
1
  import { useSyncExternalStore } from "react";
2
2
  //#region resources/js/i18n/config.ts
3
- var fallback = { locales: [] };
3
+ var fallback = {
4
+ locales: [],
5
+ timezone: null
6
+ };
4
7
  var listeners = /* @__PURE__ */ new Set();
5
8
  var active = fallback;
6
9
  function normalizeLocales(locales) {
@@ -23,14 +26,27 @@ function notify() {
23
26
  }
24
27
  function setConfig(config) {
25
28
  const locales = normalizeLocales(config?.locales);
26
- if (sameLocales(active.locales, locales)) return;
27
- active = { locales };
29
+ const timezone = config?.timezone ?? null;
30
+ if (sameLocales(active.locales, locales) && active.timezone === timezone) return;
31
+ active = {
32
+ locales,
33
+ timezone
34
+ };
28
35
  notify();
29
36
  }
37
+ function configTimezone() {
38
+ return active.timezone;
39
+ }
40
+ function subscribeConfig(callback) {
41
+ listeners.add(callback);
42
+ return () => {
43
+ listeners.delete(callback);
44
+ };
45
+ }
30
46
  function useConfig() {
31
47
  return useSyncExternalStore(subscribe, snapshot, () => fallback);
32
48
  }
33
49
  //#endregion
34
- export { setConfig, useConfig };
50
+ export { configTimezone, setConfig, subscribeConfig, useConfig };
35
51
 
36
52
  //# sourceMappingURL=config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../../resources/js/i18n/config.ts"],"sourcesContent":["import type { I18nConfig } from \"@lattice-php/lattice/types/generated\";\nimport { useSyncExternalStore } from \"react\";\n\nexport type Config = {\n readonly locales: readonly string[];\n};\n\nconst fallback: Config = { locales: [] };\nconst listeners = new Set<() => void>();\n\nlet active: Config = fallback;\n\nfunction normalizeLocales(locales: readonly string[] | undefined): string[] {\n return Array.from(new Set((locales ?? []).map((locale) => locale.trim()).filter(Boolean)));\n}\n\nfunction sameLocales(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((locale, index) => locale === right[index]);\n}\n\nfunction snapshot(): Config {\n return active;\n}\n\nfunction subscribe(callback: () => void): () => void {\n listeners.add(callback);\n\n return () => {\n listeners.delete(callback);\n };\n}\n\nfunction notify(): void {\n listeners.forEach((listener) => listener());\n}\n\nexport function setConfig(config: I18nConfig | undefined): void {\n const locales = normalizeLocales(config?.locales);\n\n if (sameLocales(active.locales, locales)) {\n return;\n }\n\n active = { locales };\n notify();\n}\n\nexport function useConfig(): Config {\n return useSyncExternalStore(subscribe, snapshot, () => fallback);\n}\n"],"mappings":";;AAOA,IAAM,WAAmB,EAAE,SAAS,CAAC,EAAE;AACvC,IAAM,4BAAY,IAAI,IAAgB;AAEtC,IAAI,SAAiB;AAErB,SAAS,iBAAiB,SAAkD;CAC1E,OAAO,MAAM,KAAK,IAAI,KAAK,WAAW,CAAC,GAAG,KAAK,WAAW,OAAO,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AAC3F;AAEA,SAAS,YAAY,MAAyB,OAAmC;CAC/E,OAAO,KAAK,WAAW,MAAM,UAAU,KAAK,OAAO,QAAQ,UAAU,WAAW,MAAM,MAAM;AAC9F;AAEA,SAAS,WAAmB;CAC1B,OAAO;AACT;AAEA,SAAS,UAAU,UAAkC;CACnD,UAAU,IAAI,QAAQ;CAEtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;AAEA,SAAS,SAAe;CACtB,UAAU,SAAS,aAAa,SAAS,CAAC;AAC5C;AAEA,SAAgB,UAAU,QAAsC;CAC9D,MAAM,UAAU,iBAAiB,QAAQ,OAAO;CAEhD,IAAI,YAAY,OAAO,SAAS,OAAO,GACrC;CAGF,SAAS,EAAE,QAAQ;CACnB,OAAO;AACT;AAEA,SAAgB,YAAoB;CAClC,OAAO,qBAAqB,WAAW,gBAAgB,QAAQ;AACjE"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../../resources/js/i18n/config.ts"],"sourcesContent":["import type { I18nConfig } from \"@lattice-php/lattice/types/generated\";\nimport { useSyncExternalStore } from \"react\";\n\nexport type Config = {\n readonly locales: readonly string[];\n readonly timezone: string | null;\n};\n\nconst fallback: Config = { locales: [], timezone: null };\nconst listeners = new Set<() => void>();\n\nlet active: Config = fallback;\n\nfunction normalizeLocales(locales: readonly string[] | undefined): string[] {\n return Array.from(new Set((locales ?? []).map((locale) => locale.trim()).filter(Boolean)));\n}\n\nfunction sameLocales(left: readonly string[], right: readonly string[]): boolean {\n return left.length === right.length && left.every((locale, index) => locale === right[index]);\n}\n\nfunction snapshot(): Config {\n return active;\n}\n\nfunction subscribe(callback: () => void): () => void {\n listeners.add(callback);\n\n return () => {\n listeners.delete(callback);\n };\n}\n\nfunction notify(): void {\n listeners.forEach((listener) => listener());\n}\n\nexport function setConfig(config: I18nConfig | undefined): void {\n const locales = normalizeLocales(config?.locales);\n const timezone = config?.timezone ?? null;\n\n if (sameLocales(active.locales, locales) && active.timezone === timezone) {\n return;\n }\n\n active = { locales, timezone };\n notify();\n}\n\nexport function configTimezone(): string | null {\n return active.timezone;\n}\n\nexport function subscribeConfig(callback: () => void): () => void {\n listeners.add(callback);\n\n return () => {\n listeners.delete(callback);\n };\n}\n\nexport function useConfig(): Config {\n return useSyncExternalStore(subscribe, snapshot, () => fallback);\n}\n"],"mappings":";;AAQA,IAAM,WAAmB;CAAE,SAAS,CAAC;CAAG,UAAU;AAAK;AACvD,IAAM,4BAAY,IAAI,IAAgB;AAEtC,IAAI,SAAiB;AAErB,SAAS,iBAAiB,SAAkD;CAC1E,OAAO,MAAM,KAAK,IAAI,KAAK,WAAW,CAAC,GAAG,KAAK,WAAW,OAAO,KAAK,CAAC,EAAE,OAAO,OAAO,CAAC,CAAC;AAC3F;AAEA,SAAS,YAAY,MAAyB,OAAmC;CAC/E,OAAO,KAAK,WAAW,MAAM,UAAU,KAAK,OAAO,QAAQ,UAAU,WAAW,MAAM,MAAM;AAC9F;AAEA,SAAS,WAAmB;CAC1B,OAAO;AACT;AAEA,SAAS,UAAU,UAAkC;CACnD,UAAU,IAAI,QAAQ;CAEtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;AAEA,SAAS,SAAe;CACtB,UAAU,SAAS,aAAa,SAAS,CAAC;AAC5C;AAEA,SAAgB,UAAU,QAAsC;CAC9D,MAAM,UAAU,iBAAiB,QAAQ,OAAO;CAChD,MAAM,WAAW,QAAQ,YAAY;CAErC,IAAI,YAAY,OAAO,SAAS,OAAO,KAAK,OAAO,aAAa,UAC9D;CAGF,SAAS;EAAE;EAAS;CAAS;CAC7B,OAAO;AACT;AAEA,SAAgB,iBAAgC;CAC9C,OAAO,OAAO;AAChB;AAEA,SAAgB,gBAAgB,UAAkC;CAChE,UAAU,IAAI,QAAQ;CAEtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;AAEA,SAAgB,YAAoB;CAClC,OAAO,qBAAqB,WAAW,gBAAgB,QAAQ;AACjE"}
@@ -0,0 +1,7 @@
1
+ import { ReactNode } from 'react';
2
+ export type DateTimeProps = {
3
+ value: unknown;
4
+ dateStyle?: string | null;
5
+ timeStyle?: string | null;
6
+ };
7
+ export declare function DateTime({ value, dateStyle, timeStyle, }: DateTimeProps): ReactNode;
@@ -0,0 +1,33 @@
1
+ import { useLocale } from "./locale.js";
2
+ import { useTimezone } from "./timezone.js";
3
+ import { formatDateValue, preciseDateTime } from "../table/format.js";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region resources/js/i18n/date-time.tsx
6
+ function DateTime({ value, dateStyle = "medium", timeStyle = "short" }) {
7
+ const { locale } = useLocale();
8
+ const { timezone } = useTimezone();
9
+ if (value === null || value === void 0 || value === "") return null;
10
+ const options = {
11
+ locale,
12
+ timeZone: timezone
13
+ };
14
+ const text = formatDateValue(value, {
15
+ dateStyle,
16
+ timeStyle
17
+ }, options);
18
+ const iso = isoOrNull(value);
19
+ const title = preciseDateTime(value, options);
20
+ return /* @__PURE__ */ jsx("time", {
21
+ dateTime: iso ?? void 0,
22
+ title: title || void 0,
23
+ children: text
24
+ });
25
+ }
26
+ function isoOrNull(value) {
27
+ const date = new Date(String(value));
28
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
29
+ }
30
+ //#endregion
31
+ export { DateTime };
32
+
33
+ //# sourceMappingURL=date-time.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"date-time.js","names":[],"sources":["../../resources/js/i18n/date-time.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { type FormatOptions, formatDateValue, preciseDateTime } from \"../table/format\";\nimport { useLocale } from \"./locale\";\nimport { useTimezone } from \"./timezone\";\n\nexport type DateTimeProps = {\n value: unknown;\n dateStyle?: string | null;\n timeStyle?: string | null;\n};\n\nexport function DateTime({\n value,\n dateStyle = \"medium\",\n timeStyle = \"short\",\n}: DateTimeProps): ReactNode {\n const { locale } = useLocale();\n const { timezone } = useTimezone();\n\n if (value === null || value === undefined || value === \"\") {\n return null;\n }\n\n const options: FormatOptions = { locale, timeZone: timezone };\n const text = formatDateValue(value, { dateStyle, timeStyle }, options);\n const iso = isoOrNull(value);\n const title = preciseDateTime(value, options);\n\n return (\n <time dateTime={iso ?? undefined} title={title || undefined}>\n {text}\n </time>\n );\n}\n\nfunction isoOrNull(value: unknown): string | null {\n const date = new Date(String(value));\n\n return Number.isNaN(date.getTime()) ? null : date.toISOString();\n}\n"],"mappings":";;;;;AAWA,SAAgB,SAAS,EACvB,OACA,YAAY,UACZ,YAAY,WACe;CAC3B,MAAM,EAAE,WAAW,UAAU;CAC7B,MAAM,EAAE,aAAa,YAAY;CAEjC,IAAI,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,IACrD,OAAO;CAGT,MAAM,UAAyB;EAAE;EAAQ,UAAU;CAAS;CAC5D,MAAM,OAAO,gBAAgB,OAAO;EAAE;EAAW;CAAU,GAAG,OAAO;CACrE,MAAM,MAAM,UAAU,KAAK;CAC3B,MAAM,QAAQ,gBAAgB,OAAO,OAAO;CAE5C,OACE,oBAAC,QAAD;EAAM,UAAU,OAAO,KAAA;EAAW,OAAO,SAAS,KAAA;YAC/C;CACG,CAAA;AAEV;AAEA,SAAS,UAAU,OAA+B;CAChD,MAAM,OAAO,IAAI,KAAK,OAAO,KAAK,CAAC;CAEnC,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,IAAI,OAAO,KAAK,YAAY;AAChE"}
@@ -7,3 +7,7 @@ export { configureI18nFromPageProps, i18nConfigFromPageProps } from './page-prop
7
7
  export type { BackendOptions, ConfigureI18nOptions, I18nConfig } from './backend';
8
8
  export type { LocaleOption, LocaleSwitcherProps, UseLocaleOptionsOptions, UseLocaleOptionsReturn, } from './locale-switcher';
9
9
  export type { UseLocaleReturn } from './locale';
10
+ export { currentTimezone, setTimezone, useTimezone } from './timezone';
11
+ export type { UseTimezoneReturn } from './timezone';
12
+ export { DateTime } from './date-time';
13
+ export type { DateTimeProps } from './date-time';
@@ -4,4 +4,6 @@ import { configureI18n, enableBackend } from "./backend.js";
4
4
  import { LocaleReload } from "./locale-reload.js";
5
5
  import { LocaleSwitcher, useLocaleOptions } from "./locale-switcher.js";
6
6
  import { configureI18nFromPageProps, i18nConfigFromPageProps } from "./page-props.js";
7
- export { LocaleReload, LocaleSwitcher, configureI18n, configureI18nFromPageProps, currentLocale, enableBackend, i18n, i18nConfigFromPageProps, localeHeader, setLocale, translate, useLocale, useLocaleOptions, useT };
7
+ import { currentTimezone, setTimezone, useTimezone } from "./timezone.js";
8
+ import { DateTime } from "./date-time.js";
9
+ export { DateTime, LocaleReload, LocaleSwitcher, configureI18n, configureI18nFromPageProps, currentLocale, currentTimezone, enableBackend, i18n, i18nConfigFromPageProps, localeHeader, setLocale, setTimezone, translate, useLocale, useLocaleOptions, useT, useTimezone };
@@ -7,7 +7,7 @@ function isStringArray(value) {
7
7
  return Array.isArray(value) && value.every((item) => typeof item === "string");
8
8
  }
9
9
  function isI18nConfig(value) {
10
- return isRecord(value) && typeof value.enabled === "boolean" && typeof value.saveMissing === "boolean" && isStringArray(value.locales) && isStringArray(value.preloadLocales);
10
+ return isRecord(value) && typeof value.enabled === "boolean" && typeof value.saveMissing === "boolean" && isStringArray(value.locales) && isStringArray(value.preloadLocales) && (value.timezone === void 0 || value.timezone === null || typeof value.timezone === "string");
11
11
  }
12
12
  function i18nConfigFromPageProps(props) {
13
13
  if (!isRecord(props)) return;
@@ -1 +1 @@
1
- {"version":3,"file":"page-props.js","names":[],"sources":["../../resources/js/i18n/page-props.ts"],"sourcesContent":["import { configureI18n, type ConfigureI18nOptions, type I18nConfig } from \"./backend\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\nfunction isI18nConfig(value: unknown): value is I18nConfig {\n return (\n isRecord(value) &&\n typeof value.enabled === \"boolean\" &&\n typeof value.saveMissing === \"boolean\" &&\n isStringArray(value.locales) &&\n isStringArray(value.preloadLocales)\n );\n}\n\nexport function i18nConfigFromPageProps(props: unknown): I18nConfig | undefined {\n if (!isRecord(props)) {\n return undefined;\n }\n\n const shared = props.lattice;\n const config = isRecord(shared) ? shared.i18n : undefined;\n\n return isI18nConfig(config) ? config : undefined;\n}\n\nexport function configureI18nFromPageProps(\n props: unknown,\n options: ConfigureI18nOptions = {},\n): Promise<void> {\n return configureI18n(i18nConfigFromPageProps(props), options);\n}\n"],"mappings":";;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,cAAc,OAAmC;CACxD,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,SAAS,OAAO,SAAS,QAAQ;AAC/E;AAEA,SAAS,aAAa,OAAqC;CACzD,OACE,SAAS,KAAK,KACd,OAAO,MAAM,YAAY,aACzB,OAAO,MAAM,gBAAgB,aAC7B,cAAc,MAAM,OAAO,KAC3B,cAAc,MAAM,cAAc;AAEtC;AAEA,SAAgB,wBAAwB,OAAwC;CAC9E,IAAI,CAAC,SAAS,KAAK,GACjB;CAGF,MAAM,SAAS,MAAM;CACrB,MAAM,SAAS,SAAS,MAAM,IAAI,OAAO,OAAO,KAAA;CAEhD,OAAO,aAAa,MAAM,IAAI,SAAS,KAAA;AACzC;AAEA,SAAgB,2BACd,OACA,UAAgC,CAAC,GAClB;CACf,OAAO,cAAc,wBAAwB,KAAK,GAAG,OAAO;AAC9D"}
1
+ {"version":3,"file":"page-props.js","names":[],"sources":["../../resources/js/i18n/page-props.ts"],"sourcesContent":["import { configureI18n, type ConfigureI18nOptions, type I18nConfig } from \"./backend\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null;\n}\n\nfunction isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.every((item) => typeof item === \"string\");\n}\n\nfunction isI18nConfig(value: unknown): value is I18nConfig {\n return (\n isRecord(value) &&\n typeof value.enabled === \"boolean\" &&\n typeof value.saveMissing === \"boolean\" &&\n isStringArray(value.locales) &&\n isStringArray(value.preloadLocales) &&\n (value.timezone === undefined || value.timezone === null || typeof value.timezone === \"string\")\n );\n}\n\nexport function i18nConfigFromPageProps(props: unknown): I18nConfig | undefined {\n if (!isRecord(props)) {\n return undefined;\n }\n\n const shared = props.lattice;\n const config = isRecord(shared) ? shared.i18n : undefined;\n\n return isI18nConfig(config) ? config : undefined;\n}\n\nexport function configureI18nFromPageProps(\n props: unknown,\n options: ConfigureI18nOptions = {},\n): Promise<void> {\n return configureI18n(i18nConfigFromPageProps(props), options);\n}\n"],"mappings":";;AAEA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,cAAc,OAAmC;CACxD,OAAO,MAAM,QAAQ,KAAK,KAAK,MAAM,OAAO,SAAS,OAAO,SAAS,QAAQ;AAC/E;AAEA,SAAS,aAAa,OAAqC;CACzD,OACE,SAAS,KAAK,KACd,OAAO,MAAM,YAAY,aACzB,OAAO,MAAM,gBAAgB,aAC7B,cAAc,MAAM,OAAO,KAC3B,cAAc,MAAM,cAAc,MACjC,MAAM,aAAa,KAAA,KAAa,MAAM,aAAa,QAAQ,OAAO,MAAM,aAAa;AAE1F;AAEA,SAAgB,wBAAwB,OAAwC;CAC9E,IAAI,CAAC,SAAS,KAAK,GACjB;CAGF,MAAM,SAAS,MAAM;CACrB,MAAM,SAAS,SAAS,MAAM,IAAI,OAAO,OAAO,KAAA;CAEhD,OAAO,aAAa,MAAM,IAAI,SAAS,KAAA;AACzC;AAEA,SAAgB,2BACd,OACA,UAAgC,CAAC,GAClB;CACf,OAAO,cAAc,wBAAwB,KAAK,GAAG,OAAO;AAC9D"}
@@ -0,0 +1,7 @@
1
+ export type UseTimezoneReturn = {
2
+ readonly timezone: string;
3
+ readonly setTimezone: (timezone: string) => void;
4
+ };
5
+ export declare function currentTimezone(): string;
6
+ export declare function setTimezone(timezone: string): void;
7
+ export declare function useTimezone(): UseTimezoneReturn;
@@ -0,0 +1,53 @@
1
+ import { LATTICE_EVENT } from "../events/event-names.js";
2
+ import { configTimezone, subscribeConfig } from "./config.js";
3
+ import { useSyncExternalStore } from "react";
4
+ //#region resources/js/i18n/timezone.ts
5
+ var fallback = "UTC";
6
+ var listeners = /* @__PURE__ */ new Set();
7
+ var override;
8
+ function detectedTimezone() {
9
+ if (typeof Intl === "undefined") return fallback;
10
+ try {
11
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || fallback;
12
+ } catch {
13
+ return fallback;
14
+ }
15
+ }
16
+ function snapshot() {
17
+ return override ?? configTimezone() ?? detectedTimezone();
18
+ }
19
+ function subscribe(callback) {
20
+ listeners.add(callback);
21
+ return () => {
22
+ listeners.delete(callback);
23
+ };
24
+ }
25
+ function notify() {
26
+ listeners.forEach((listener) => listener());
27
+ }
28
+ subscribeConfig(() => notify());
29
+ function dispatch(timezone) {
30
+ if (typeof window === "undefined") return;
31
+ window.dispatchEvent(new CustomEvent(LATTICE_EVENT.timezoneChange, { detail: { timezone } }));
32
+ }
33
+ function currentTimezone() {
34
+ return snapshot();
35
+ }
36
+ function setTimezone(timezone) {
37
+ const previous = currentTimezone();
38
+ const next = timezone.trim();
39
+ override = next === "" ? void 0 : next;
40
+ if (currentTimezone() === previous) return;
41
+ notify();
42
+ dispatch(currentTimezone());
43
+ }
44
+ function useTimezone() {
45
+ return {
46
+ timezone: useSyncExternalStore(subscribe, snapshot, () => fallback),
47
+ setTimezone
48
+ };
49
+ }
50
+ //#endregion
51
+ export { currentTimezone, setTimezone, useTimezone };
52
+
53
+ //# sourceMappingURL=timezone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timezone.js","names":[],"sources":["../../resources/js/i18n/timezone.ts"],"sourcesContent":["import { useSyncExternalStore } from \"react\";\nimport { LATTICE_EVENT } from \"../events/event-names\";\nimport { configTimezone, subscribeConfig } from \"./config\";\n\nexport type UseTimezoneReturn = {\n readonly timezone: string;\n readonly setTimezone: (timezone: string) => void;\n};\n\nconst fallback = \"UTC\";\nconst listeners = new Set<() => void>();\n\nlet override: string | undefined;\n\nfunction detectedTimezone(): string {\n if (typeof Intl === \"undefined\") {\n return fallback;\n }\n\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || fallback;\n } catch {\n return fallback;\n }\n}\n\nfunction snapshot(): string {\n return override ?? configTimezone() ?? detectedTimezone();\n}\n\nfunction subscribe(callback: () => void): () => void {\n listeners.add(callback);\n\n return () => {\n listeners.delete(callback);\n };\n}\n\nfunction notify(): void {\n listeners.forEach((listener) => listener());\n}\n\nsubscribeConfig(() => notify());\n\nfunction dispatch(timezone: string): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n window.dispatchEvent(new CustomEvent(LATTICE_EVENT.timezoneChange, { detail: { timezone } }));\n}\n\nexport function currentTimezone(): string {\n return snapshot();\n}\n\nexport function setTimezone(timezone: string): void {\n const previous = currentTimezone();\n const next = timezone.trim();\n\n override = next === \"\" ? undefined : next;\n\n if (currentTimezone() === previous) {\n return;\n }\n\n notify();\n dispatch(currentTimezone());\n}\n\nexport function useTimezone(): UseTimezoneReturn {\n const timezone = useSyncExternalStore(subscribe, snapshot, () => fallback);\n\n return { timezone, setTimezone } as const;\n}\n"],"mappings":";;;;AASA,IAAM,WAAW;AACjB,IAAM,4BAAY,IAAI,IAAgB;AAEtC,IAAI;AAEJ,SAAS,mBAA2B;CAClC,IAAI,OAAO,SAAS,aAClB,OAAO;CAGT,IAAI;EACF,OAAO,KAAK,eAAe,EAAE,gBAAgB,EAAE,YAAY;CAC7D,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,WAAmB;CAC1B,OAAO,YAAY,eAAe,KAAK,iBAAiB;AAC1D;AAEA,SAAS,UAAU,UAAkC;CACnD,UAAU,IAAI,QAAQ;CAEtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;AAEA,SAAS,SAAe;CACtB,UAAU,SAAS,aAAa,SAAS,CAAC;AAC5C;AAEA,sBAAsB,OAAO,CAAC;AAE9B,SAAS,SAAS,UAAwB;CACxC,IAAI,OAAO,WAAW,aACpB;CAGF,OAAO,cAAc,IAAI,YAAY,cAAc,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;AAC9F;AAEA,SAAgB,kBAA0B;CACxC,OAAO,SAAS;AAClB;AAEA,SAAgB,YAAY,UAAwB;CAClD,MAAM,WAAW,gBAAgB;CACjC,MAAM,OAAO,SAAS,KAAK;CAE3B,WAAW,SAAS,KAAK,KAAA,IAAY;CAErC,IAAI,gBAAgB,MAAM,UACxB;CAGF,OAAO;CACP,SAAS,gBAAgB,CAAC;AAC5B;AAEA,SAAgB,cAAiC;CAG/C,OAAO;EAAE,UAFQ,qBAAqB,WAAW,gBAAgB,QAExD;EAAU;CAAY;AACjC"}
@@ -1,7 +1,8 @@
1
1
  import { cn } from "../../../lib/utils.js";
2
2
  import { Icon } from "../../../icons/sprite.js";
3
- import { copyToClipboard } from "../../../clipboard/index.js";
4
3
  import { formatCell, resolveLink } from "../../format.js";
4
+ import { DateTime } from "../../../i18n/date-time.js";
5
+ import { copyToClipboard } from "../../../clipboard/index.js";
5
6
  import { useEffect, useState } from "react";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  //#region resources/js/table/components/cells/text-cell.tsx
@@ -40,9 +41,10 @@ function SingleBadgeCell({ column, props, row, value }) {
40
41
  });
41
42
  }
42
43
  function PlainTextCell({ column, props, row, value }) {
44
+ const dateProps = column.props?.date;
45
+ const href = resolveLink(column, row, value);
43
46
  const text = formatCell(value, column);
44
47
  const [copied, setCopied] = useState(false);
45
- const href = resolveLink(column, row, value);
46
48
  const content = href ? /* @__PURE__ */ jsx("a", {
47
49
  className: "underline underline-offset-2",
48
50
  href,
@@ -59,6 +61,11 @@ function PlainTextCell({ column, props, row, value }) {
59
61
  copyToClipboard(text);
60
62
  setCopied(true);
61
63
  }
64
+ if (dateProps && !href && !props.copyable && value !== null && value !== void 0) return /* @__PURE__ */ jsx(DateTime, {
65
+ value,
66
+ dateStyle: dateProps.dateStyle,
67
+ timeStyle: dateProps.timeStyle
68
+ });
62
69
  if (!props.copyable) return content;
63
70
  return /* @__PURE__ */ jsxs("span", {
64
71
  className: "inline-flex items-center gap-2",
@@ -1 +1 @@
1
- {"version":3,"file":"text-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/text-cell.tsx"],"sourcesContent":["import { copyToClipboard } from \"@lattice-php/lattice/clipboard\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { type ReactNode, useEffect, useState } from \"react\";\nimport { formatCell, resolveLink } from \"../../format\";\nimport type { ColumnCellArgs, ColumnCellComponent } from \"../../registry\";\n\ntype TextProps = ColumnCellArgs<\"column.text\">[\"props\"];\n\nexport const TextCell: ColumnCellComponent<\"column.text\"> = (args) => {\n if (args.props.multiple) {\n return <MultipleCell {...args} />;\n }\n\n if (args.props.badge) {\n return <SingleBadgeCell {...args} />;\n }\n\n return <PlainTextCell {...args} />;\n};\n\nfunction Badge({ label, color }: { label: string; color?: string | null }): ReactNode {\n if (label === \"\") {\n return null;\n }\n\n return <span className={cn(\"lt-cell-badge\", `lt-cell-tone-${color || \"gray\"}`)}>{label}</span>;\n}\n\nfunction MultipleCell({ column, props, value }: ColumnCellArgs<\"column.text\">): ReactNode {\n const items = Array.isArray(value) ? value : [];\n\n if (items.length === 0) {\n return null;\n }\n\n if (!props.badge) {\n return <span>{items.map((item) => formatCell(item, column)).join(\", \")}</span>;\n }\n\n return (\n <div className=\"flex flex-wrap gap-1\">\n {items.map((item, index) => {\n const chip = item as { value: unknown; color?: string };\n\n return <Badge key={index} label={formatCell(chip.value, column)} color={chip.color} />;\n })}\n </div>\n );\n}\n\nfunction SingleBadgeCell({ column, props, row, value }: ColumnCellArgs<\"column.text\">): ReactNode {\n const colorKey = (props.badge as NonNullable<TextProps[\"badge\"]>).colorKey;\n\n return <Badge label={formatCell(value, column)} color={String(row[colorKey] ?? \"\")} />;\n}\n\nfunction PlainTextCell({ column, props, row, value }: ColumnCellArgs<\"column.text\">): ReactNode {\n const text = formatCell(value, column);\n const [copied, setCopied] = useState(false);\n const href = resolveLink(column, row, value);\n const content = href ? (\n <a\n className=\"underline underline-offset-2\"\n href={href}\n rel={props.link?.external ? \"noreferrer\" : undefined}\n target={props.link?.external ? \"_blank\" : undefined}\n >\n {text}\n </a>\n ) : (\n text\n );\n\n useEffect(() => {\n if (!copied) {\n return;\n }\n\n const timeout = window.setTimeout(() => setCopied(false), 1500);\n\n return () => window.clearTimeout(timeout);\n }, [copied]);\n\n function handleCopy(): void {\n void copyToClipboard(text);\n setCopied(true);\n }\n\n if (!props.copyable) {\n return content;\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <span>{content}</span>\n <button\n type=\"button\"\n data-test={`copy-${column.key}`}\n className=\"inline-flex items-center gap-1 rounded-lt-sm border border-lt-border px-2 py-1 text-xs\"\n aria-label={`${copied ? \"Copied\" : \"Copy\"} ${column.label}`}\n onClick={handleCopy}\n >\n {copied ? (\n <Icon name=\"check\" className=\"size-lt-icon-xs\" />\n ) : (\n <Icon name=\"copy\" className=\"size-lt-icon-xs\" />\n )}\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </span>\n );\n}\n"],"mappings":";;;;;;;AASA,IAAa,YAAgD,SAAS;CACpE,IAAI,KAAK,MAAM,UACb,OAAO,oBAAC,cAAD,EAAc,GAAI,KAAO,CAAA;CAGlC,IAAI,KAAK,MAAM,OACb,OAAO,oBAAC,iBAAD,EAAiB,GAAI,KAAO,CAAA;CAGrC,OAAO,oBAAC,eAAD,EAAe,GAAI,KAAO,CAAA;AACnC;AAEA,SAAS,MAAM,EAAE,OAAO,SAA8D;CACpF,IAAI,UAAU,IACZ,OAAO;CAGT,OAAO,oBAAC,QAAD;EAAM,WAAW,GAAG,iBAAiB,gBAAgB,SAAS,QAAQ;YAAI;CAAY,CAAA;AAC/F;AAEA,SAAS,aAAa,EAAE,QAAQ,OAAO,SAAmD;CACxF,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;CAE9C,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,IAAI,CAAC,MAAM,OACT,OAAO,oBAAC,QAAD,EAAA,UAAO,MAAM,KAAK,SAAS,WAAW,MAAM,MAAM,CAAC,EAAE,KAAK,IAAI,EAAQ,CAAA;CAG/E,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,MAAM,KAAK,MAAM,UAAU;GAC1B,MAAM,OAAO;GAEb,OAAO,oBAAC,OAAD;IAAmB,OAAO,WAAW,KAAK,OAAO,MAAM;IAAG,OAAO,KAAK;GAAQ,GAAlE,KAAkE;EACvF,CAAC;CACE,CAAA;AAET;AAEA,SAAS,gBAAgB,EAAE,QAAQ,OAAO,KAAK,SAAmD;CAChG,MAAM,WAAY,MAAM,MAA0C;CAElE,OAAO,oBAAC,OAAD;EAAO,OAAO,WAAW,OAAO,MAAM;EAAG,OAAO,OAAO,IAAI,aAAa,EAAE;CAAI,CAAA;AACvF;AAEA,SAAS,cAAc,EAAE,QAAQ,OAAO,KAAK,SAAmD;CAC9F,MAAM,OAAO,WAAW,OAAO,MAAM;CACrC,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,OAAO,YAAY,QAAQ,KAAK,KAAK;CAC3C,MAAM,UAAU,OACd,oBAAC,KAAD;EACE,WAAU;EACJ;EACN,KAAK,MAAM,MAAM,WAAW,eAAe,KAAA;EAC3C,QAAQ,MAAM,MAAM,WAAW,WAAW,KAAA;YAEzC;CACA,CAAA,IAEH;CAGF,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,MAAM,UAAU,OAAO,iBAAiB,UAAU,KAAK,GAAG,IAAI;EAE9D,aAAa,OAAO,aAAa,OAAO;CAC1C,GAAG,CAAC,MAAM,CAAC;CAEX,SAAS,aAAmB;EAC1B,gBAAqB,IAAI;EACzB,UAAU,IAAI;CAChB;CAEA,IAAI,CAAC,MAAM,UACT,OAAO;CAGT,OACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,QAAD,EAAA,UAAO,QAAc,CAAA,GACrB,qBAAC,UAAD;GACE,MAAK;GACL,aAAW,QAAQ,OAAO;GAC1B,WAAU;GACV,cAAY,GAAG,SAAS,WAAW,OAAO,GAAG,OAAO;GACpD,SAAS;aALX,CAOG,SACC,oBAAC,MAAD;IAAM,MAAK;IAAQ,WAAU;GAAmB,CAAA,IAEhD,oBAAC,MAAD;IAAM,MAAK;IAAO,WAAU;GAAmB,CAAA,GAEhD,SAAS,WAAW,MACf;IACJ;;AAEV"}
1
+ {"version":3,"file":"text-cell.js","names":[],"sources":["../../../../resources/js/table/components/cells/text-cell.tsx"],"sourcesContent":["import { copyToClipboard } from \"@lattice-php/lattice/clipboard\";\nimport { DateTime } from \"@lattice-php/lattice/i18n\";\nimport { Icon } from \"@lattice-php/lattice/icons\";\nimport { cn } from \"@lattice-php/lattice/lib/utils\";\nimport { type ReactNode, useEffect, useState } from \"react\";\nimport { formatCell, resolveLink } from \"../../format\";\nimport type { ColumnCellArgs, ColumnCellComponent } from \"../../registry\";\nimport type { ColumnPropsOf } from \"../../types\";\n\ntype TextProps = ColumnCellArgs<\"column.text\">[\"props\"];\n\nexport const TextCell: ColumnCellComponent<\"column.text\"> = (args) => {\n if (args.props.multiple) {\n return <MultipleCell {...args} />;\n }\n\n if (args.props.badge) {\n return <SingleBadgeCell {...args} />;\n }\n\n return <PlainTextCell {...args} />;\n};\n\nfunction Badge({ label, color }: { label: string; color?: string | null }): ReactNode {\n if (label === \"\") {\n return null;\n }\n\n return <span className={cn(\"lt-cell-badge\", `lt-cell-tone-${color || \"gray\"}`)}>{label}</span>;\n}\n\nfunction MultipleCell({ column, props, value }: ColumnCellArgs<\"column.text\">): ReactNode {\n const items = Array.isArray(value) ? value : [];\n\n if (items.length === 0) {\n return null;\n }\n\n if (!props.badge) {\n return <span>{items.map((item) => formatCell(item, column)).join(\", \")}</span>;\n }\n\n return (\n <div className=\"flex flex-wrap gap-1\">\n {items.map((item, index) => {\n const chip = item as { value: unknown; color?: string };\n\n return <Badge key={index} label={formatCell(chip.value, column)} color={chip.color} />;\n })}\n </div>\n );\n}\n\nfunction SingleBadgeCell({ column, props, row, value }: ColumnCellArgs<\"column.text\">): ReactNode {\n const colorKey = (props.badge as NonNullable<TextProps[\"badge\"]>).colorKey;\n\n return <Badge label={formatCell(value, column)} color={String(row[colorKey] ?? \"\")} />;\n}\n\nfunction PlainTextCell({ column, props, row, value }: ColumnCellArgs<\"column.text\">): ReactNode {\n const dateProps = (column.props as ColumnPropsOf<\"column.text\"> | null)?.date;\n const href = resolveLink(column, row, value);\n const text = formatCell(value, column);\n const [copied, setCopied] = useState(false);\n\n const content = href ? (\n <a\n className=\"underline underline-offset-2\"\n href={href}\n rel={props.link?.external ? \"noreferrer\" : undefined}\n target={props.link?.external ? \"_blank\" : undefined}\n >\n {text}\n </a>\n ) : (\n text\n );\n\n useEffect(() => {\n if (!copied) {\n return;\n }\n\n const timeout = window.setTimeout(() => setCopied(false), 1500);\n\n return () => window.clearTimeout(timeout);\n }, [copied]);\n\n function handleCopy(): void {\n void copyToClipboard(text);\n setCopied(true);\n }\n\n if (dateProps && !href && !props.copyable && value !== null && value !== undefined) {\n return (\n <DateTime value={value} dateStyle={dateProps.dateStyle} timeStyle={dateProps.timeStyle} />\n );\n }\n\n if (!props.copyable) {\n return content;\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <span>{content}</span>\n <button\n type=\"button\"\n data-test={`copy-${column.key}`}\n className=\"inline-flex items-center gap-1 rounded-lt-sm border border-lt-border px-2 py-1 text-xs\"\n aria-label={`${copied ? \"Copied\" : \"Copy\"} ${column.label}`}\n onClick={handleCopy}\n >\n {copied ? (\n <Icon name=\"check\" className=\"size-lt-icon-xs\" />\n ) : (\n <Icon name=\"copy\" className=\"size-lt-icon-xs\" />\n )}\n {copied ? \"Copied\" : \"Copy\"}\n </button>\n </span>\n );\n}\n"],"mappings":";;;;;;;;AAWA,IAAa,YAAgD,SAAS;CACpE,IAAI,KAAK,MAAM,UACb,OAAO,oBAAC,cAAD,EAAc,GAAI,KAAO,CAAA;CAGlC,IAAI,KAAK,MAAM,OACb,OAAO,oBAAC,iBAAD,EAAiB,GAAI,KAAO,CAAA;CAGrC,OAAO,oBAAC,eAAD,EAAe,GAAI,KAAO,CAAA;AACnC;AAEA,SAAS,MAAM,EAAE,OAAO,SAA8D;CACpF,IAAI,UAAU,IACZ,OAAO;CAGT,OAAO,oBAAC,QAAD;EAAM,WAAW,GAAG,iBAAiB,gBAAgB,SAAS,QAAQ;YAAI;CAAY,CAAA;AAC/F;AAEA,SAAS,aAAa,EAAE,QAAQ,OAAO,SAAmD;CACxF,MAAM,QAAQ,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC;CAE9C,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,IAAI,CAAC,MAAM,OACT,OAAO,oBAAC,QAAD,EAAA,UAAO,MAAM,KAAK,SAAS,WAAW,MAAM,MAAM,CAAC,EAAE,KAAK,IAAI,EAAQ,CAAA;CAG/E,OACE,oBAAC,OAAD;EAAK,WAAU;YACZ,MAAM,KAAK,MAAM,UAAU;GAC1B,MAAM,OAAO;GAEb,OAAO,oBAAC,OAAD;IAAmB,OAAO,WAAW,KAAK,OAAO,MAAM;IAAG,OAAO,KAAK;GAAQ,GAAlE,KAAkE;EACvF,CAAC;CACE,CAAA;AAET;AAEA,SAAS,gBAAgB,EAAE,QAAQ,OAAO,KAAK,SAAmD;CAChG,MAAM,WAAY,MAAM,MAA0C;CAElE,OAAO,oBAAC,OAAD;EAAO,OAAO,WAAW,OAAO,MAAM;EAAG,OAAO,OAAO,IAAI,aAAa,EAAE;CAAI,CAAA;AACvF;AAEA,SAAS,cAAc,EAAE,QAAQ,OAAO,KAAK,SAAmD;CAC9F,MAAM,YAAa,OAAO,OAA+C;CACzE,MAAM,OAAO,YAAY,QAAQ,KAAK,KAAK;CAC3C,MAAM,OAAO,WAAW,OAAO,MAAM;CACrC,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAE1C,MAAM,UAAU,OACd,oBAAC,KAAD;EACE,WAAU;EACJ;EACN,KAAK,MAAM,MAAM,WAAW,eAAe,KAAA;EAC3C,QAAQ,MAAM,MAAM,WAAW,WAAW,KAAA;YAEzC;CACA,CAAA,IAEH;CAGF,gBAAgB;EACd,IAAI,CAAC,QACH;EAGF,MAAM,UAAU,OAAO,iBAAiB,UAAU,KAAK,GAAG,IAAI;EAE9D,aAAa,OAAO,aAAa,OAAO;CAC1C,GAAG,CAAC,MAAM,CAAC;CAEX,SAAS,aAAmB;EAC1B,gBAAqB,IAAI;EACzB,UAAU,IAAI;CAChB;CAEA,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,YAAY,UAAU,QAAQ,UAAU,KAAA,GACvE,OACE,oBAAC,UAAD;EAAiB;EAAO,WAAW,UAAU;EAAW,WAAW,UAAU;CAAY,CAAA;CAI7F,IAAI,CAAC,MAAM,UACT,OAAO;CAGT,OACE,qBAAC,QAAD;EAAM,WAAU;YAAhB,CACE,oBAAC,QAAD,EAAA,UAAO,QAAc,CAAA,GACrB,qBAAC,UAAD;GACE,MAAK;GACL,aAAW,QAAQ,OAAO;GAC1B,WAAU;GACV,cAAY,GAAG,SAAS,WAAW,OAAO,GAAG,OAAO;GACpD,SAAS;aALX,CAOG,SACC,oBAAC,MAAD;IAAM,MAAK;IAAQ,WAAU;GAAmB,CAAA,IAEhD,oBAAC,MAAD;IAAM,MAAK;IAAO,WAAU;GAAmB,CAAA,GAEhD,SAAS,WAAW,MACf;IACJ;;AAEV"}
@@ -1,3 +1,13 @@
1
1
  import { TableColumn, TableRow } from './types';
2
- export declare function formatCell(value: unknown, column?: TableColumn): string;
2
+ export type FormatOptions = {
3
+ locale?: string;
4
+ timeZone?: string;
5
+ };
6
+ export type DateConfig = {
7
+ dateStyle: string | null;
8
+ timeStyle: string | null;
9
+ };
10
+ export declare function formatCell(value: unknown, column?: TableColumn, options?: FormatOptions): string;
11
+ export declare function formatDateValue(value: unknown, date: DateConfig, options?: FormatOptions): string;
12
+ export declare function preciseDateTime(value: unknown, options?: FormatOptions): string;
3
13
  export declare function resolveLink(column: TableColumn, row: TableRow, value: unknown): string | null;
@@ -1,31 +1,33 @@
1
1
  //#region resources/js/table/format.ts
2
- function formatCell(value, column) {
2
+ function formatCell(value, column, options) {
3
3
  if (value === null || value === void 0) return "";
4
4
  const date = (column?.props)?.date;
5
- if (date) return formatDate(value, date.format ?? null);
5
+ if (date) return formatDateValue(value, date, options);
6
6
  if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return String(value);
7
7
  return JSON.stringify(value);
8
8
  }
9
- function formatDate(value, format) {
9
+ function formatDateValue(value, date, options) {
10
+ const parsed = new Date(String(value));
11
+ if (Number.isNaN(parsed.getTime())) return String(value ?? "");
12
+ const intl = { timeZone: options?.timeZone };
13
+ if (date.dateStyle) intl.dateStyle = date.dateStyle;
14
+ if (date.timeStyle) intl.timeStyle = date.timeStyle;
15
+ return new Intl.DateTimeFormat(options?.locale, intl).format(parsed);
16
+ }
17
+ function preciseDateTime(value, options) {
10
18
  const date = new Date(String(value));
11
- if (Number.isNaN(date.getTime())) return formatCell(value);
12
- if (!format) return new Intl.DateTimeFormat(void 0, {
13
- dateStyle: "medium",
14
- timeStyle: "short"
19
+ if (Number.isNaN(date.getTime())) return "";
20
+ const formatted = new Intl.DateTimeFormat(options?.locale, {
21
+ year: "numeric",
22
+ month: "short",
23
+ day: "numeric",
24
+ hour: "2-digit",
25
+ minute: "2-digit",
26
+ second: "2-digit",
27
+ timeZone: options?.timeZone,
28
+ timeZoneName: "short"
15
29
  }).format(date);
16
- const replacements = {
17
- Y: String(date.getFullYear()),
18
- y: String(date.getFullYear()).slice(-2),
19
- m: String(date.getMonth() + 1).padStart(2, "0"),
20
- n: String(date.getMonth() + 1),
21
- d: String(date.getDate()).padStart(2, "0"),
22
- j: String(date.getDate()),
23
- H: String(date.getHours()).padStart(2, "0"),
24
- G: String(date.getHours()),
25
- i: String(date.getMinutes()).padStart(2, "0"),
26
- s: String(date.getSeconds()).padStart(2, "0")
27
- };
28
- return format.replace(/[YymndjHGis]/g, (token) => replacements[token] ?? token);
30
+ return options?.timeZone ? `${formatted} (${options.timeZone})` : formatted;
29
31
  }
30
32
  function resolveLink(column, row, value) {
31
33
  const link = column.props?.link;
@@ -38,6 +40,6 @@ function resolveLink(column, row, value) {
38
40
  });
39
41
  }
40
42
  //#endregion
41
- export { formatCell, resolveLink };
43
+ export { formatCell, formatDateValue, preciseDateTime, resolveLink };
42
44
 
43
45
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../../resources/js/table/format.ts"],"sourcesContent":["import type { ColumnPropsOf, TableColumn, TableRow } from \"./types\";\n\nexport function formatCell(value: unknown, column?: TableColumn): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n const date = (column?.props as ColumnPropsOf<\"column.text\"> | null)?.date;\n\n if (date) {\n return formatDate(value, date.format ?? null);\n }\n\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n\n return JSON.stringify(value);\n}\n\nfunction formatDate(value: unknown, format: string | null): string {\n const date = new Date(String(value));\n\n if (Number.isNaN(date.getTime())) {\n return formatCell(value);\n }\n\n if (!format) {\n return new Intl.DateTimeFormat(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n }).format(date);\n }\n\n const replacements: Record<string, string> = {\n Y: String(date.getFullYear()),\n y: String(date.getFullYear()).slice(-2),\n m: String(date.getMonth() + 1).padStart(2, \"0\"),\n n: String(date.getMonth() + 1),\n d: String(date.getDate()).padStart(2, \"0\"),\n j: String(date.getDate()),\n H: String(date.getHours()).padStart(2, \"0\"),\n G: String(date.getHours()),\n i: String(date.getMinutes()).padStart(2, \"0\"),\n s: String(date.getSeconds()).padStart(2, \"0\"),\n };\n\n return format.replace(/[YymndjHGis]/g, (token) => replacements[token] ?? token);\n}\n\nexport function resolveLink(column: TableColumn, row: TableRow, value: unknown): string | null {\n const link = (column.props as ColumnPropsOf<\"column.text\"> | null)?.link;\n\n if (!link) {\n return null;\n }\n\n const href = link.href ?? String(value ?? \"\");\n\n if (href === \"\") {\n return null;\n }\n\n return href.replace(/\\{([^}]+)\\}/g, (_, key: string) => {\n if (key === \"value\") {\n return encodeURIComponent(String(value ?? \"\"));\n }\n\n return encodeURIComponent(String(row[key] ?? \"\"));\n });\n}\n"],"mappings":";AAEA,SAAgB,WAAW,OAAgB,QAA8B;CACvE,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;CAGT,MAAM,QAAQ,QAAQ,QAA+C;CAErE,IAAI,MACF,OAAO,WAAW,OAAO,KAAK,UAAU,IAAI;CAG9C,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAC7E,OAAO,OAAO,KAAK;CAGrB,OAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAS,WAAW,OAAgB,QAA+B;CACjE,MAAM,OAAO,IAAI,KAAK,OAAO,KAAK,CAAC;CAEnC,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,OAAO,WAAW,KAAK;CAGzB,IAAI,CAAC,QACH,OAAO,IAAI,KAAK,eAAe,KAAA,GAAW;EACxC,WAAW;EACX,WAAW;CACb,CAAC,EAAE,OAAO,IAAI;CAGhB,MAAM,eAAuC;EAC3C,GAAG,OAAO,KAAK,YAAY,CAAC;EAC5B,GAAG,OAAO,KAAK,YAAY,CAAC,EAAE,MAAM,EAAE;EACtC,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;EAC9C,GAAG,OAAO,KAAK,SAAS,IAAI,CAAC;EAC7B,GAAG,OAAO,KAAK,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;EACzC,GAAG,OAAO,KAAK,QAAQ,CAAC;EACxB,GAAG,OAAO,KAAK,SAAS,CAAC,EAAE,SAAS,GAAG,GAAG;EAC1C,GAAG,OAAO,KAAK,SAAS,CAAC;EACzB,GAAG,OAAO,KAAK,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;EAC5C,GAAG,OAAO,KAAK,WAAW,CAAC,EAAE,SAAS,GAAG,GAAG;CAC9C;CAEA,OAAO,OAAO,QAAQ,kBAAkB,UAAU,aAAa,UAAU,KAAK;AAChF;AAEA,SAAgB,YAAY,QAAqB,KAAe,OAA+B;CAC7F,MAAM,OAAQ,OAAO,OAA+C;CAEpE,IAAI,CAAC,MACH,OAAO;CAGT,MAAM,OAAO,KAAK,QAAQ,OAAO,SAAS,EAAE;CAE5C,IAAI,SAAS,IACX,OAAO;CAGT,OAAO,KAAK,QAAQ,iBAAiB,GAAG,QAAgB;EACtD,IAAI,QAAQ,SACV,OAAO,mBAAmB,OAAO,SAAS,EAAE,CAAC;EAG/C,OAAO,mBAAmB,OAAO,IAAI,QAAQ,EAAE,CAAC;CAClD,CAAC;AACH"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../../resources/js/table/format.ts"],"sourcesContent":["import type { ColumnPropsOf, TableColumn, TableRow } from \"./types\";\n\nexport type FormatOptions = {\n locale?: string;\n timeZone?: string;\n};\n\nexport type DateConfig = { dateStyle: string | null; timeStyle: string | null };\n\nexport function formatCell(value: unknown, column?: TableColumn, options?: FormatOptions): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n\n const date = (column?.props as ColumnPropsOf<\"column.text\"> | null)?.date;\n\n if (date) {\n return formatDateValue(value, date, options);\n }\n\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return String(value);\n }\n\n return JSON.stringify(value);\n}\n\nexport function formatDateValue(value: unknown, date: DateConfig, options?: FormatOptions): string {\n const parsed = new Date(String(value));\n\n if (Number.isNaN(parsed.getTime())) {\n return String(value ?? \"\");\n }\n\n const intl: Intl.DateTimeFormatOptions = { timeZone: options?.timeZone };\n\n if (date.dateStyle) {\n intl.dateStyle = date.dateStyle as Intl.DateTimeFormatOptions[\"dateStyle\"];\n }\n\n if (date.timeStyle) {\n intl.timeStyle = date.timeStyle as Intl.DateTimeFormatOptions[\"timeStyle\"];\n }\n\n return new Intl.DateTimeFormat(options?.locale, intl).format(parsed);\n}\n\nexport function preciseDateTime(value: unknown, options?: FormatOptions): string {\n const date = new Date(String(value));\n\n if (Number.isNaN(date.getTime())) {\n return \"\";\n }\n\n const formatted = new Intl.DateTimeFormat(options?.locale, {\n year: \"numeric\",\n month: \"short\",\n day: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n timeZone: options?.timeZone,\n timeZoneName: \"short\",\n }).format(date);\n\n return options?.timeZone ? `${formatted} (${options.timeZone})` : formatted;\n}\n\nexport function resolveLink(column: TableColumn, row: TableRow, value: unknown): string | null {\n const link = (column.props as ColumnPropsOf<\"column.text\"> | null)?.link;\n\n if (!link) {\n return null;\n }\n\n const href = link.href ?? String(value ?? \"\");\n\n if (href === \"\") {\n return null;\n }\n\n return href.replace(/\\{([^}]+)\\}/g, (_, key: string) => {\n if (key === \"value\") {\n return encodeURIComponent(String(value ?? \"\"));\n }\n\n return encodeURIComponent(String(row[key] ?? \"\"));\n });\n}\n"],"mappings":";AASA,SAAgB,WAAW,OAAgB,QAAsB,SAAiC;CAChG,IAAI,UAAU,QAAQ,UAAU,KAAA,GAC9B,OAAO;CAGT,MAAM,QAAQ,QAAQ,QAA+C;CAErE,IAAI,MACF,OAAO,gBAAgB,OAAO,MAAM,OAAO;CAG7C,IAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,WAC7E,OAAO,OAAO,KAAK;CAGrB,OAAO,KAAK,UAAU,KAAK;AAC7B;AAEA,SAAgB,gBAAgB,OAAgB,MAAkB,SAAiC;CACjG,MAAM,SAAS,IAAI,KAAK,OAAO,KAAK,CAAC;CAErC,IAAI,OAAO,MAAM,OAAO,QAAQ,CAAC,GAC/B,OAAO,OAAO,SAAS,EAAE;CAG3B,MAAM,OAAmC,EAAE,UAAU,SAAS,SAAS;CAEvE,IAAI,KAAK,WACP,KAAK,YAAY,KAAK;CAGxB,IAAI,KAAK,WACP,KAAK,YAAY,KAAK;CAGxB,OAAO,IAAI,KAAK,eAAe,SAAS,QAAQ,IAAI,EAAE,OAAO,MAAM;AACrE;AAEA,SAAgB,gBAAgB,OAAgB,SAAiC;CAC/E,MAAM,OAAO,IAAI,KAAK,OAAO,KAAK,CAAC;CAEnC,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,GAC7B,OAAO;CAGT,MAAM,YAAY,IAAI,KAAK,eAAe,SAAS,QAAQ;EACzD,MAAM;EACN,OAAO;EACP,KAAK;EACL,MAAM;EACN,QAAQ;EACR,QAAQ;EACR,UAAU,SAAS;EACnB,cAAc;CAChB,CAAC,EAAE,OAAO,IAAI;CAEd,OAAO,SAAS,WAAW,GAAG,UAAU,IAAI,QAAQ,SAAS,KAAK;AACpE;AAEA,SAAgB,YAAY,QAAqB,KAAe,OAA+B;CAC7F,MAAM,OAAQ,OAAO,OAA+C;CAEpE,IAAI,CAAC,MACH,OAAO;CAGT,MAAM,OAAO,KAAK,QAAQ,OAAO,SAAS,EAAE;CAE5C,IAAI,SAAS,IACX,OAAO;CAGT,OAAO,KAAK,QAAQ,iBAAiB,GAAG,QAAgB;EACtD,IAAI,QAAQ,SACV,OAAO,mBAAmB,OAAO,SAAS,EAAE,CAAC;EAG/C,OAAO,mBAAmB,OAAO,IAAI,QAAQ,EAAE,CAAC;CAClD,CAAC;AACH"}
@@ -393,6 +393,7 @@ export type DateInput = {
393
393
  tooltip: string | null;
394
394
  value: unknown;
395
395
  };
396
+ export type DateTimeStyle = "full" | "long" | "medium" | "short";
396
397
  export type DownloadEffect = {
397
398
  readonly url: string;
398
399
  };
@@ -611,6 +612,7 @@ export type I18nConfig = {
611
612
  readonly saveMissing: boolean;
612
613
  readonly locales: string[];
613
614
  readonly preloadLocales: string[];
615
+ readonly timezone: string | null;
614
616
  };
615
617
  export type Icon = {
616
618
  class: string | null;
@@ -1047,7 +1049,8 @@ export type TextColumn = {
1047
1049
  } | null;
1048
1050
  copyable: boolean;
1049
1051
  date: {
1050
- format: string | null;
1052
+ dateStyle: string | null;
1053
+ timeStyle: string | null;
1051
1054
  } | null;
1052
1055
  link: {
1053
1056
  href: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lattice-php/lattice",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "description": "Server-driven React components for Laravel and Inertia.",
5
5
  "license": "MIT",
6
6
  "author": "Manuel Christlieb <manuel@christlieb.eu>",