@krak-stack/registry 0.1.40 → 0.1.42
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/README.md +179 -34
- package/dist/components/ui/data-table.d.ts +27 -10
- package/dist/components/ui/data-table.js +81 -36
- package/dist/components/ui/google-map.d.ts +14 -1
- package/dist/components/ui/google-map.js +97 -18
- package/dist/services/opentelemetry/api.d.ts +1 -1
- package/dist/services/opentelemetry/api.js +165 -13
- package/dist/services/opentelemetry/browser.d.ts +21 -3
- package/dist/services/opentelemetry/browser.js +267 -21
- package/dist/services/opentelemetry/events.d.ts +37 -0
- package/dist/services/opentelemetry/schema.d.ts +44 -0
- package/dist/services/opentelemetry/server.d.ts +20 -0
- package/dist/services/opentelemetry/server.js +154 -0
- package/package.json +7 -4
- package/patches/@lucas-barake%2Feffect-form-react@0.26.0-beta.5.patch +0 -66
- package/patches/@lucas-barake%2Feffect-form@0.25.0-beta.6.patch +0 -86
- package/patches/README.md +0 -31
package/README.md
CHANGED
|
@@ -6,41 +6,19 @@ Tree-shakable runtime components and Effect services from the KrakStack shadcn r
|
|
|
6
6
|
bun add @krak-stack/registry
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
##
|
|
9
|
+
## Effect forms
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
compatibility patches
|
|
14
|
-
does not apply them automatically.
|
|
15
|
-
|
|
16
|
-
After installing Registry, copy its bundled patches into your application:
|
|
11
|
+
Applications using Effect forms need `@lucas-barake/effect-form@1.1.0` and
|
|
12
|
+
`@lucas-barake/effect-form-react@1.1.0`, which support stable Effect 4 without
|
|
13
|
+
compatibility patches:
|
|
17
14
|
|
|
18
15
|
```sh
|
|
19
|
-
|
|
20
|
-
cp node_modules/@krak-stack/registry/patches/*.patch patches/
|
|
16
|
+
bun add @lucas-barake/effect-form@1.1.0 @lucas-barake/effect-form-react@1.1.0
|
|
21
17
|
```
|
|
22
18
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
```json
|
|
27
|
-
{
|
|
28
|
-
"overrides": {
|
|
29
|
-
"@lucas-barake/effect-form": "0.25.0-beta.6",
|
|
30
|
-
"@lucas-barake/effect-form-react": "0.26.0-beta.5"
|
|
31
|
-
},
|
|
32
|
-
"patchedDependencies": {
|
|
33
|
-
"@lucas-barake/effect-form@0.25.0-beta.6": "patches/@lucas-barake%2Feffect-form@0.25.0-beta.6.patch",
|
|
34
|
-
"@lucas-barake/effect-form-react@0.26.0-beta.5": "patches/@lucas-barake%2Feffect-form-react@0.26.0-beta.5.patch"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
Run `bun install` and commit the patches, manifest, and lockfile. Other package
|
|
40
|
-
managers require equivalent patch configuration. See the bundled
|
|
41
|
-
`patches/README.md` for provenance and removal instructions after
|
|
42
|
-
[upstream release PR #111](https://github.com/lucas-barake/effect-form/pull/111)
|
|
43
|
-
is published.
|
|
19
|
+
When upgrading from the patched betas, remove their `patchedDependencies`
|
|
20
|
+
entries and patch files, update any form version overrides to `1.1.0`, and run
|
|
21
|
+
`bun install`. Preserve unrelated dependency patches.
|
|
44
22
|
|
|
45
23
|
## Usage
|
|
46
24
|
|
|
@@ -79,25 +57,192 @@ import {
|
|
|
79
57
|
import { NotificationService } from "@krak-stack/registry/service-notification";
|
|
80
58
|
```
|
|
81
59
|
|
|
82
|
-
|
|
60
|
+
## OpenTelemetry
|
|
61
|
+
|
|
62
|
+
| Export | Purpose |
|
|
63
|
+
| --------------------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
64
|
+
| `HttpApiOtlp.layer` from `opentelemetry/api` | Server exporters and same-origin browser ingestion routes. Includes `ServerOtlp.layer`. |
|
|
65
|
+
| `ServerOtlp.layer` from `opentelemetry/server` | Exporters for workers, workflows, and CLIs without an HTTP server. |
|
|
66
|
+
| `BrowserOtlp.layer(options)` from `opentelemetry/browser` | Browser tracing and optional events, logs, and metrics. |
|
|
67
|
+
|
|
68
|
+
### Setup
|
|
69
|
+
|
|
70
|
+
Provide the HTTP layer once at the application composition root:
|
|
83
71
|
|
|
84
72
|
```ts
|
|
73
|
+
import { Layer } from "effect";
|
|
74
|
+
import { HttpApiOtlp } from "@krak-stack/registry/opentelemetry/api";
|
|
75
|
+
|
|
85
76
|
const appLayer = appRoutes.pipe(Layer.provideMerge(HttpApiOtlp.layer));
|
|
86
77
|
```
|
|
87
78
|
|
|
88
|
-
|
|
79
|
+
Configure an OTLP/HTTP collector on the server:
|
|
80
|
+
|
|
81
|
+
```dotenv
|
|
82
|
+
OTEL_SERVICE_NAME=my-app-api
|
|
83
|
+
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
|
|
84
|
+
OTEL_TRACES_EXPORTER=otlp
|
|
85
|
+
OTEL_METRICS_EXPORTER=otlp
|
|
86
|
+
OTEL_LOGS_EXPORTER=otlp
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Browser events require the logs signal. The proxy forwards enabled signals from
|
|
90
|
+
`/api/otel/v1/{traces,metrics,logs}` to the collector. Keep collector credentials
|
|
91
|
+
server-side. Server log endpoint/header overrides use
|
|
92
|
+
`OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` / `OTEL_EXPORTER_OTLP_LOGS_HEADERS`, falling
|
|
93
|
+
back to the shared endpoint/headers; these overrides do not change proxy routing.
|
|
94
|
+
`OTEL_SDK_DISABLED=true` disables server export, not browser collection.
|
|
95
|
+
|
|
96
|
+
Install one browser layer on the Atom context passed to `AtomHttpApi.Service`:
|
|
89
97
|
|
|
90
98
|
```ts
|
|
91
99
|
import { Atom } from "effect/reactivity";
|
|
92
100
|
import { BrowserOtlp } from "@krak-stack/registry/opentelemetry/browser";
|
|
93
101
|
|
|
94
102
|
const apiRuntime = Atom.context();
|
|
95
|
-
|
|
96
103
|
if (!import.meta.env.SSR) {
|
|
97
|
-
apiRuntime.addGlobalLayer(
|
|
104
|
+
apiRuntime.addGlobalLayer(
|
|
105
|
+
BrowserOtlp.layer({ serviceName: "my-app-web", events: {} }),
|
|
106
|
+
);
|
|
98
107
|
}
|
|
99
108
|
```
|
|
100
109
|
|
|
110
|
+
Mount/read `ApiClient.runtime` in the root layout to start it. Browser methods
|
|
111
|
+
target the active layer even when run from another Effect runtime. Calls before
|
|
112
|
+
startup or after disposal are no-ops, not buffered. Dispose long-lived server and
|
|
113
|
+
browser runtimes on shutdown to flush pending data.
|
|
114
|
+
|
|
115
|
+
### Browser API
|
|
116
|
+
|
|
117
|
+
All methods return Effects. Use `yield*` inside Effect code or
|
|
118
|
+
`Effect.runPromise(...)` / `Effect.runFork(...)` from application callbacks.
|
|
119
|
+
|
|
120
|
+
| Method | Behavior |
|
|
121
|
+
| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
122
|
+
| `identify({ userId?, organizationId? })` | Replace identity for future events; omitted fields clear old values. Emits no event. |
|
|
123
|
+
| `identify(null)` | Clear identity, for example on logout. |
|
|
124
|
+
| `track(name, properties?, options?)` | Record a named event. |
|
|
125
|
+
| `page(options?)` | Record `browser.page_view`. Accepts `path`, `name`, `category`, `properties`, and event `options`. `pageView` is an alias. |
|
|
126
|
+
|
|
127
|
+
`identify` has no typed failure: invalid input warns without including supplied
|
|
128
|
+
values and leaves identity unchanged. `track` and `page` fail with an Effect
|
|
129
|
+
schema error on invalid input. Identity is in memory only, applies to events
|
|
130
|
+
rather than automatic traces/logs, and never rewrites queued events.
|
|
131
|
+
|
|
132
|
+
### Auth example
|
|
133
|
+
|
|
134
|
+
Mount this once under the application's Atom registry. `ApiClient` and
|
|
135
|
+
`authSessionAtom` are application-owned; the telemetry package has no auth
|
|
136
|
+
dependency. The session atom in this example exposes KrakStack Auth session data.
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import { useAtomSubscribe, useAtomValue } from "@effect/atom-react";
|
|
140
|
+
import { BrowserOtlp } from "@krak-stack/registry/opentelemetry/browser";
|
|
141
|
+
import { Effect } from "effect";
|
|
142
|
+
import { AsyncResult } from "effect/reactivity";
|
|
143
|
+
import { ApiClient } from "@/lib/api-client";
|
|
144
|
+
import { authSessionAtom } from "@/services/auth/client";
|
|
145
|
+
|
|
146
|
+
export const AuthTelemetry = () => {
|
|
147
|
+
const runtime = useAtomValue(ApiClient.runtime);
|
|
148
|
+
|
|
149
|
+
useAtomSubscribe(
|
|
150
|
+
authSessionAtom,
|
|
151
|
+
(session) => {
|
|
152
|
+
if (!AsyncResult.isSuccess(runtime)) return;
|
|
153
|
+
|
|
154
|
+
Effect.runFork(
|
|
155
|
+
BrowserOtlp.identify(
|
|
156
|
+
AsyncResult.isSuccess(session) && !session.waiting && session.value
|
|
157
|
+
? {
|
|
158
|
+
userId: session.value.user.id,
|
|
159
|
+
organizationId:
|
|
160
|
+
session.value.session.activeOrganizationId ?? undefined,
|
|
161
|
+
}
|
|
162
|
+
: null,
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
},
|
|
166
|
+
{ immediate: true },
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
return null;
|
|
170
|
+
};
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
This identifies after startup and clears identity during auth loading, failure,
|
|
174
|
+
and logout. The first page view may be anonymous. For other auth libraries, pass
|
|
175
|
+
the same two IDs or `null` when auth state changes. Apply your own consent policy;
|
|
176
|
+
do not send names, emails, or session tokens as identity.
|
|
177
|
+
|
|
178
|
+
### Page views and browser options
|
|
179
|
+
|
|
180
|
+
`events: {}` enables custom events and one page view on startup, then on pathname
|
|
181
|
+
changes through `pushState`, `replaceState`, and back/forward navigation.
|
|
182
|
+
Same-path and query/hash-only changes are ignored. Tracking follows URL changes,
|
|
183
|
+
not loader completion. Paths exclude query strings/fragments but may still
|
|
184
|
+
contain personal IDs.
|
|
185
|
+
|
|
186
|
+
For sanitized route templates or custom timing, use
|
|
187
|
+
`events: { pageViews: false }` and call `page({ path: "/orders/:id" })` manually.
|
|
188
|
+
Do not combine automatic and manual tracking for the same view.
|
|
189
|
+
|
|
190
|
+
| Layer option | Default |
|
|
191
|
+
| ----------------------------------------- | --------------------------------------------------------------- |
|
|
192
|
+
| `serviceName` | Required |
|
|
193
|
+
| `traces` | `{}`; includes global exception capture. `false` disables both. |
|
|
194
|
+
| `events`, `logs`, `metrics` | `false`; enable each independently with `{}`. |
|
|
195
|
+
| `baseUrl` | `/api/otel` |
|
|
196
|
+
| `serviceVersion`, `attributes`, `headers` | Optional resource/transport metadata. |
|
|
197
|
+
|
|
198
|
+
Browser events batch every 30 seconds, up to 100 records by default. Override
|
|
199
|
+
`exportInterval`, `maxBatchSize`, and `shutdownTimeout` under `events`.
|
|
200
|
+
|
|
201
|
+
### Server events and shared options
|
|
202
|
+
|
|
203
|
+
Define server events as Effects and run them inside the runtime with
|
|
204
|
+
`ServerOtlp.layer` (or the HTTP layer):
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
import { Effect } from "effect";
|
|
208
|
+
import { ServerOtlp } from "@krak-stack/registry/opentelemetry/server";
|
|
209
|
+
|
|
210
|
+
const orderCompleted = Effect.gen(function* () {
|
|
211
|
+
yield* ServerOtlp.track("app.order.completed", { "order.total": 49 });
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Authenticated handlers inherit `user.id` and `organization.id` from
|
|
216
|
+
`AuthMiddleware` log annotations. Server identity is fiber-scoped; there is no
|
|
217
|
+
server `identify` method. Background jobs can supply identity per event.
|
|
218
|
+
|
|
219
|
+
Both `track` methods accept `options`:
|
|
220
|
+
|
|
221
|
+
| Option | OTLP attribute / meaning |
|
|
222
|
+
| ------------------------------------------------- | ------------------------------------------------------------------- |
|
|
223
|
+
| `userId`, `anonymousId`, `sessionId`, `messageId` | `user.id`, `analytics.anonymous_id`, `session.id`, `log.record.uid` |
|
|
224
|
+
| `context` | Additional flat attributes |
|
|
225
|
+
| `timestamp` | Effect `DateTime.Utc` occurrence time; otherwise recorded now |
|
|
226
|
+
|
|
227
|
+
Attribute precedence, lowest to highest: context, properties, explicit identity
|
|
228
|
+
options, browser identified identity, scoped log annotations. Values support
|
|
229
|
+
strings, finite numbers, booleans, and homogeneous arrays, not nested objects.
|
|
230
|
+
Use stable event names and put dynamic values in attributes. Message IDs support
|
|
231
|
+
correlation, not guaranteed deduplication.
|
|
232
|
+
|
|
233
|
+
Events are OTLP log records with `eventName`, INFO severity, and active trace/span
|
|
234
|
+
IDs when available; they do not create spans. `browser.page_view`, `url.path`,
|
|
235
|
+
`analytics.page.name`, and `analytics.page.category` describe page events; the
|
|
236
|
+
analytics names are project conventions, not standardized OTel semantics.
|
|
237
|
+
|
|
238
|
+
Configure collector/storage separately. Browser identity is untrusted analytics
|
|
239
|
+
metadata, not authorization or an audit trail. No visitor/session IDs or cookies
|
|
240
|
+
are generated. Browser flushing on hidden/pagehide is best-effort. Apply payload
|
|
241
|
+
and rate limits at ingestion, and keep sensitive data out of events and
|
|
242
|
+
high-cardinality fields out of Loki labels.
|
|
243
|
+
|
|
244
|
+
## Other services
|
|
245
|
+
|
|
101
246
|
`@krak-stack/registry/service-notification` is the source-compatible direct
|
|
102
247
|
dispatcher. Use the separate `@krak-stack/notifications` package for a durable
|
|
103
248
|
inbox, package-owned migrations, persisted delivery jobs, retries, workers, and
|
|
@@ -32,10 +32,10 @@ export interface DataTableListItemContext<TData> {
|
|
|
32
32
|
}
|
|
33
33
|
export interface DataTableListOptions {
|
|
34
34
|
emptyLabel: ReactNode;
|
|
35
|
-
display?: "list" | "icon";
|
|
36
|
-
locale?: string;
|
|
37
|
-
messages?: DataTableMessageTranslations;
|
|
38
|
-
visibleCount?: number;
|
|
35
|
+
display?: "list" | "icon" | undefined;
|
|
36
|
+
locale?: string | undefined;
|
|
37
|
+
messages?: DataTableMessageTranslations | undefined;
|
|
38
|
+
visibleCount?: number | undefined;
|
|
39
39
|
}
|
|
40
40
|
export type DataTableListColumn<TData> = DataTableListOptions & {
|
|
41
41
|
/** Includes items not loaded in the preview; the dropdown only shows supplied items. */
|
|
@@ -76,8 +76,8 @@ interface EditableDataTableListCellProps extends DataTableListOptions {
|
|
|
76
76
|
totalCount?: number | undefined;
|
|
77
77
|
}
|
|
78
78
|
interface DataTableListCellActionsProps {
|
|
79
|
-
actionsLabel?: string;
|
|
80
|
-
itemActions?: readonly DataTableItemAction<DataTableListItem>[];
|
|
79
|
+
actionsLabel?: string | undefined;
|
|
80
|
+
itemActions?: readonly DataTableItemAction<DataTableListItem>[] | undefined;
|
|
81
81
|
}
|
|
82
82
|
export type DataTableListCellProps = DataTableListCellActionsProps & (EditableDataTableListCellProps | (DataTableListSummaryProps & {
|
|
83
83
|
options?: never;
|
|
@@ -206,6 +206,23 @@ export type DataTablePaginationFeature = false | {
|
|
|
206
206
|
rowCount: number;
|
|
207
207
|
pageSizes?: readonly number[];
|
|
208
208
|
};
|
|
209
|
+
export interface DataTableExportAction {
|
|
210
|
+
id: string;
|
|
211
|
+
label: string;
|
|
212
|
+
icon?: ReactNode;
|
|
213
|
+
disabled?: boolean;
|
|
214
|
+
/** The app owns fetching, authorization, field selection, and downloading. */
|
|
215
|
+
onExport: () => void | Promise<void>;
|
|
216
|
+
}
|
|
217
|
+
export interface DataTableExportFeature {
|
|
218
|
+
baseName: string;
|
|
219
|
+
/** Both scopes operate only on rows already supplied to the table. */
|
|
220
|
+
scope?: "currentPage" | "filteredRows";
|
|
221
|
+
/** Set false to offer only app-provided actions. Defaults to true. */
|
|
222
|
+
builtIn?: boolean;
|
|
223
|
+
actions?: readonly DataTableExportAction[];
|
|
224
|
+
onError?: (error: unknown, action: DataTableExportAction) => void;
|
|
225
|
+
}
|
|
209
226
|
export interface DataTableFeatures<TData> {
|
|
210
227
|
toolbar?: false | {
|
|
211
228
|
/** Additional `MenubarMenu` controls rendered at the start of the toolbar. */
|
|
@@ -228,10 +245,7 @@ export interface DataTableFeatures<TData> {
|
|
|
228
245
|
label?: string;
|
|
229
246
|
items: readonly DataTableRowAction<TData>[];
|
|
230
247
|
};
|
|
231
|
-
export?: false |
|
|
232
|
-
baseName: string;
|
|
233
|
-
scope?: "currentPage" | "filteredRows";
|
|
234
|
-
};
|
|
248
|
+
export?: false | DataTableExportFeature;
|
|
235
249
|
gallery?: false | {
|
|
236
250
|
name: string;
|
|
237
251
|
description?: string;
|
|
@@ -255,6 +269,7 @@ export type DataTableMessages = PaginationMessages & {
|
|
|
255
269
|
export: string;
|
|
256
270
|
exportCsv: string;
|
|
257
271
|
exportJson: string;
|
|
272
|
+
exportFailed?: string;
|
|
258
273
|
refresh: string;
|
|
259
274
|
view: string;
|
|
260
275
|
tableView: string;
|
|
@@ -295,6 +310,7 @@ export declare const dataTableMessages: (locale?: string, translations?: DataTab
|
|
|
295
310
|
export: string;
|
|
296
311
|
exportCsv: string;
|
|
297
312
|
exportJson: string;
|
|
313
|
+
exportFailed: string;
|
|
298
314
|
refresh: string;
|
|
299
315
|
view: string;
|
|
300
316
|
tableView: string;
|
|
@@ -332,6 +348,7 @@ export declare const dataTableMessages: (locale?: string, translations?: DataTab
|
|
|
332
348
|
export: string;
|
|
333
349
|
exportCsv: string;
|
|
334
350
|
exportJson: string;
|
|
351
|
+
exportFailed: string;
|
|
335
352
|
refresh: string;
|
|
336
353
|
view: string;
|
|
337
354
|
tableView: string;
|
|
@@ -993,6 +993,7 @@ var messages2 = {
|
|
|
993
993
|
export: "Export",
|
|
994
994
|
exportCsv: "CSV",
|
|
995
995
|
exportJson: "JSON",
|
|
996
|
+
exportFailed: "Export failed. Please try again.",
|
|
996
997
|
refresh: "Refresh",
|
|
997
998
|
view: "View",
|
|
998
999
|
tableView: "Table",
|
|
@@ -1024,6 +1025,7 @@ var messages2 = {
|
|
|
1024
1025
|
export: "Exporter",
|
|
1025
1026
|
exportCsv: "CSV",
|
|
1026
1027
|
exportJson: "JSON",
|
|
1028
|
+
exportFailed: "L’exportation a échoué. Veuillez réessayer.",
|
|
1027
1029
|
refresh: "Rafraîchir",
|
|
1028
1030
|
view: "Affichage",
|
|
1029
1031
|
tableView: "Tableau",
|
|
@@ -1586,7 +1588,7 @@ var DataTableRowActions = ({
|
|
|
1586
1588
|
event.stopPropagation();
|
|
1587
1589
|
action.onClick(row);
|
|
1588
1590
|
},
|
|
1589
|
-
variant: action.variant,
|
|
1591
|
+
variant: action.variant ?? "default",
|
|
1590
1592
|
children: [
|
|
1591
1593
|
action.icon,
|
|
1592
1594
|
action.name
|
|
@@ -1641,6 +1643,9 @@ var DataTableToolbar = ({
|
|
|
1641
1643
|
tableAtom
|
|
1642
1644
|
}) => {
|
|
1643
1645
|
const [store, setStore] = useTableAtom(tableAtom);
|
|
1646
|
+
const exportPending = useRef(false);
|
|
1647
|
+
const [exporting, setExporting] = useState(false);
|
|
1648
|
+
const [exportFailed, setExportFailed] = useState(false);
|
|
1644
1649
|
const { features, labels } = store.config;
|
|
1645
1650
|
const model = deriveModel(store);
|
|
1646
1651
|
const selectedRows = getSelectedRows(store, model);
|
|
@@ -1653,6 +1658,27 @@ var DataTableToolbar = ({
|
|
|
1653
1658
|
const update = (next) => changeState(setStore, next);
|
|
1654
1659
|
const visibleBulkActions = bulkActions ? bulkActions.items.filter((action) => !action.visible || action.visible(selectedRows)) : [];
|
|
1655
1660
|
const exportRows = features.export && features.export.scope === "filteredRows" ? model.filteredRows : model.pageRows;
|
|
1661
|
+
const exportFeature = features.export;
|
|
1662
|
+
const exportActions = exportFeature ? exportFeature.actions ?? [] : [];
|
|
1663
|
+
const builtInExport = exportFeature && exportFeature.builtIn !== false;
|
|
1664
|
+
const canExport = builtInExport && exportRows.length > 0 || exportActions.some((action) => !action.disabled);
|
|
1665
|
+
const runExport = async (action) => {
|
|
1666
|
+
if (exportPending.current || action.disabled)
|
|
1667
|
+
return;
|
|
1668
|
+
exportPending.current = true;
|
|
1669
|
+
setExporting(true);
|
|
1670
|
+
setExportFailed(false);
|
|
1671
|
+
try {
|
|
1672
|
+
await action.onExport();
|
|
1673
|
+
} catch (error) {
|
|
1674
|
+
setExportFailed(true);
|
|
1675
|
+
if (exportFeature)
|
|
1676
|
+
exportFeature.onError?.(error, action);
|
|
1677
|
+
} finally {
|
|
1678
|
+
exportPending.current = false;
|
|
1679
|
+
setExporting(false);
|
|
1680
|
+
}
|
|
1681
|
+
};
|
|
1656
1682
|
return /* @__PURE__ */ jsxs8("div", {
|
|
1657
1683
|
className: "flex flex-col gap-2 pb-4",
|
|
1658
1684
|
children: [
|
|
@@ -1716,7 +1742,7 @@ var DataTableToolbar = ({
|
|
|
1716
1742
|
/* @__PURE__ */ jsx14(MenubarSeparator, {}),
|
|
1717
1743
|
visibleBulkActions.map((action) => /* @__PURE__ */ jsxs8(MenubarItem, {
|
|
1718
1744
|
onClick: () => action.onClick(selectedRows),
|
|
1719
|
-
variant: action.variant,
|
|
1745
|
+
variant: action.variant ?? "default",
|
|
1720
1746
|
children: [
|
|
1721
1747
|
action.icon,
|
|
1722
1748
|
action.name
|
|
@@ -1786,12 +1812,15 @@ var DataTableToolbar = ({
|
|
|
1786
1812
|
children: [
|
|
1787
1813
|
/* @__PURE__ */ jsxs8(MenubarTrigger, {
|
|
1788
1814
|
"aria-label": labels.export,
|
|
1789
|
-
|
|
1815
|
+
"aria-busy": exporting,
|
|
1816
|
+
disabled: exporting || !canExport,
|
|
1790
1817
|
children: [
|
|
1791
|
-
/* @__PURE__ */ jsx14(
|
|
1818
|
+
exporting ? /* @__PURE__ */ jsx14(RefreshCw, {
|
|
1819
|
+
className: "animate-spin"
|
|
1820
|
+
}) : /* @__PURE__ */ jsx14(Download, {}),
|
|
1792
1821
|
/* @__PURE__ */ jsx14("span", {
|
|
1793
1822
|
className: "hidden sm:inline",
|
|
1794
|
-
children: labels.export
|
|
1823
|
+
children: exporting ? labels.loading : labels.export
|
|
1795
1824
|
})
|
|
1796
1825
|
]
|
|
1797
1826
|
}),
|
|
@@ -1803,23 +1832,38 @@ var DataTableToolbar = ({
|
|
|
1803
1832
|
children: labels.export
|
|
1804
1833
|
}),
|
|
1805
1834
|
/* @__PURE__ */ jsx14(MenubarSeparator, {}),
|
|
1806
|
-
/* @__PURE__ */ jsxs8(
|
|
1807
|
-
onClick: () => downloadCsv(model.visibleColumns.map(columnLabel), exportRows.map((row) => model.visibleColumns.map((column) => String(getDataTableExportValue(row, column, store.config.locale) ?? ""))), features.export ? features.export.baseName : "table"),
|
|
1835
|
+
builtInExport ? /* @__PURE__ */ jsxs8(Fragment, {
|
|
1808
1836
|
children: [
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
|
|
1837
|
+
/* @__PURE__ */ jsxs8(MenubarItem, {
|
|
1838
|
+
disabled: exporting || !exportRows.length,
|
|
1839
|
+
onClick: () => downloadCsv(model.visibleColumns.map(columnLabel), exportRows.map((row) => model.visibleColumns.map((column) => String(getDataTableExportValue(row, column, store.config.locale) ?? ""))), features.export ? features.export.baseName : "table"),
|
|
1840
|
+
children: [
|
|
1841
|
+
/* @__PURE__ */ jsx14(FileText, {}),
|
|
1842
|
+
labels.exportCsv
|
|
1843
|
+
]
|
|
1844
|
+
}),
|
|
1845
|
+
/* @__PURE__ */ jsxs8(MenubarItem, {
|
|
1846
|
+
disabled: exporting || !exportRows.length,
|
|
1847
|
+
onClick: () => downloadJson(exportRows.map((row) => Object.fromEntries(model.visibleColumns.map((column) => [
|
|
1848
|
+
column.id,
|
|
1849
|
+
getDataTableExportValue(row, column, store.config.locale)
|
|
1850
|
+
]))), features.export ? features.export.baseName : "table"),
|
|
1851
|
+
children: [
|
|
1852
|
+
/* @__PURE__ */ jsx14(FileJson, {}),
|
|
1853
|
+
labels.exportJson
|
|
1854
|
+
]
|
|
1855
|
+
})
|
|
1811
1856
|
]
|
|
1812
|
-
}),
|
|
1813
|
-
/* @__PURE__ */
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
]))), features.export ? features.export.baseName : "table"),
|
|
1857
|
+
}) : null,
|
|
1858
|
+
builtInExport && exportActions.length ? /* @__PURE__ */ jsx14(MenubarSeparator, {}) : null,
|
|
1859
|
+
exportActions.map((action) => /* @__PURE__ */ jsxs8(MenubarItem, {
|
|
1860
|
+
disabled: exporting || action.disabled,
|
|
1861
|
+
onClick: () => void runExport(action),
|
|
1818
1862
|
children: [
|
|
1819
|
-
|
|
1820
|
-
|
|
1863
|
+
action.icon,
|
|
1864
|
+
action.label
|
|
1821
1865
|
]
|
|
1822
|
-
})
|
|
1866
|
+
}, action.id))
|
|
1823
1867
|
]
|
|
1824
1868
|
})
|
|
1825
1869
|
})
|
|
@@ -1827,7 +1871,12 @@ var DataTableToolbar = ({
|
|
|
1827
1871
|
}) : null
|
|
1828
1872
|
]
|
|
1829
1873
|
})
|
|
1830
|
-
})
|
|
1874
|
+
}),
|
|
1875
|
+
exportFeature && exportFailed ? /* @__PURE__ */ jsx14("p", {
|
|
1876
|
+
role: "alert",
|
|
1877
|
+
className: "text-destructive text-sm",
|
|
1878
|
+
children: labels.exportFailed ?? dataTableMessages(store.config.locale).exportFailed
|
|
1879
|
+
}) : null
|
|
1831
1880
|
]
|
|
1832
1881
|
});
|
|
1833
1882
|
};
|
|
@@ -2293,7 +2342,7 @@ var GroupActions = ({
|
|
|
2293
2342
|
children: /* @__PURE__ */ jsx14(DropdownMenuGroup, {
|
|
2294
2343
|
children: visible.map((action) => /* @__PURE__ */ jsxs8(DropdownMenuItem, {
|
|
2295
2344
|
onClick: () => action.onClick(groupId),
|
|
2296
|
-
variant: action.variant,
|
|
2345
|
+
variant: action.variant ?? "default",
|
|
2297
2346
|
children: [
|
|
2298
2347
|
action.icon,
|
|
2299
2348
|
" ",
|
|
@@ -2796,23 +2845,21 @@ var HydratedDataTable = ({
|
|
|
2796
2845
|
setPersistedUiState,
|
|
2797
2846
|
...props
|
|
2798
2847
|
}) => {
|
|
2799
|
-
const
|
|
2800
|
-
if (!transientUiAtomRef.current) {
|
|
2848
|
+
const [transientUiAtom] = useState(() => {
|
|
2801
2849
|
const grouping = props.features?.grouping;
|
|
2802
|
-
|
|
2850
|
+
return Atom.make({
|
|
2803
2851
|
...transientUiFromState(DEFAULT_UI_STATE),
|
|
2804
2852
|
grouping: grouping && grouping.initial ? grouping.initial : []
|
|
2805
2853
|
});
|
|
2806
|
-
}
|
|
2807
|
-
const [transientUiState, setTransientUiState] = useAtom(
|
|
2854
|
+
});
|
|
2855
|
+
const [transientUiState, setTransientUiState] = useAtom(transientUiAtom);
|
|
2808
2856
|
const uiState = useMemo(() => ({ ...persistedUiState, ...transientUiState }), [persistedUiState, transientUiState]);
|
|
2809
2857
|
const setUiState = useCallback((state) => {
|
|
2810
2858
|
setPersistedUiState(persistedUiFromState(state));
|
|
2811
2859
|
setTransientUiState(transientUiFromState(state));
|
|
2812
2860
|
}, [setPersistedUiState, setTransientUiState]);
|
|
2813
2861
|
const validatedConfig = resolveConfig(props, (state) => setUiState(state));
|
|
2814
|
-
const
|
|
2815
|
-
if (!atomRef.current) {
|
|
2862
|
+
const [tableAtom] = useState(() => {
|
|
2816
2863
|
const initial = {
|
|
2817
2864
|
rowData: props.rowData,
|
|
2818
2865
|
columnDefs: props.columnDefs,
|
|
@@ -2820,12 +2867,11 @@ var HydratedDataTable = ({
|
|
|
2820
2867
|
state: initialPublicState(props, uiState),
|
|
2821
2868
|
controlledState: props.state
|
|
2822
2869
|
};
|
|
2823
|
-
|
|
2870
|
+
return Atom.make({
|
|
2824
2871
|
...initial,
|
|
2825
2872
|
model: buildDataTableModel(initial)
|
|
2826
2873
|
});
|
|
2827
|
-
}
|
|
2828
|
-
const tableAtom = atomRef.current;
|
|
2874
|
+
});
|
|
2829
2875
|
const setStore = useAtomSet(tableAtom);
|
|
2830
2876
|
useLayoutEffect(() => {
|
|
2831
2877
|
setStore((store) => {
|
|
@@ -2886,20 +2932,19 @@ var HydratedDataTable = ({
|
|
|
2886
2932
|
};
|
|
2887
2933
|
var DataTable = (props) => {
|
|
2888
2934
|
const locale = useKrakstackLocale3(props.locale);
|
|
2889
|
-
const
|
|
2890
|
-
if (!persistedUiAtomRef.current) {
|
|
2935
|
+
const [persistedUiAtom] = useState(() => {
|
|
2891
2936
|
const pathname = globalThis.window?.location.pathname ?? "/";
|
|
2892
2937
|
const columnIds = normalizeDataTableColumns(props.columnDefs).map(({ id }) => id).join(",");
|
|
2893
2938
|
const viewCapability = props.features?.gallery ? "gallery" : "table";
|
|
2894
|
-
|
|
2939
|
+
return Atom.kvs({
|
|
2895
2940
|
mode: "async",
|
|
2896
2941
|
runtime: dataTableStorageRuntime,
|
|
2897
2942
|
key: `data-table:${pathname}:${columnIds}:${viewCapability}:ui`,
|
|
2898
2943
|
schema: DataTablePersistedUiStateSchema,
|
|
2899
2944
|
defaultValue: () => persistedUiFromState(DEFAULT_UI_STATE)
|
|
2900
2945
|
});
|
|
2901
|
-
}
|
|
2902
|
-
const [persistedUiResult, setPersistedUiState] = useAtom(
|
|
2946
|
+
});
|
|
2947
|
+
const [persistedUiResult, setPersistedUiState] = useAtom(persistedUiAtom);
|
|
2903
2948
|
if (!AsyncResult.isSuccess(persistedUiResult) || AsyncResult.isWaiting(persistedUiResult)) {
|
|
2904
2949
|
return /* @__PURE__ */ jsx14("div", {
|
|
2905
2950
|
"aria-busy": "true",
|
|
@@ -3139,7 +3184,7 @@ var DataTableListMenu = ({
|
|
|
3139
3184
|
event.stopPropagation();
|
|
3140
3185
|
action.onClick(item);
|
|
3141
3186
|
},
|
|
3142
|
-
variant: action.variant,
|
|
3187
|
+
variant: action.variant ?? "default",
|
|
3143
3188
|
children: [
|
|
3144
3189
|
action.icon,
|
|
3145
3190
|
action.name
|
|
@@ -22,6 +22,7 @@ export type GoogleMapMessages = {
|
|
|
22
22
|
errorBody: string;
|
|
23
23
|
zoomIn: string;
|
|
24
24
|
zoomOut: string;
|
|
25
|
+
showArea: string;
|
|
25
26
|
};
|
|
26
27
|
export type GoogleMapMessageTranslations = Partial<Record<string, Partial<GoogleMapMessages>>>;
|
|
27
28
|
export type GoogleMapProps = {
|
|
@@ -32,6 +33,8 @@ export type GoogleMapProps = {
|
|
|
32
33
|
radius?: GoogleMapRadiusOptions | undefined;
|
|
33
34
|
interactive?: boolean | undefined;
|
|
34
35
|
zoomControls?: boolean | undefined;
|
|
36
|
+
fitRadius?: boolean | undefined;
|
|
37
|
+
fitPadding?: number | undefined;
|
|
35
38
|
minZoom?: number | undefined;
|
|
36
39
|
maxZoom?: number | undefined;
|
|
37
40
|
className?: string | undefined;
|
|
@@ -55,11 +58,18 @@ type GoogleMapOptions = {
|
|
|
55
58
|
zoomControl: boolean;
|
|
56
59
|
};
|
|
57
60
|
type GoogleMapInstance = {
|
|
61
|
+
fitBounds: (bounds: GoogleMapBounds, padding: number) => void;
|
|
58
62
|
getZoom: () => number | undefined;
|
|
59
63
|
setCenter: (center: GoogleMapPoint) => void;
|
|
60
64
|
setOptions: (options: Partial<GoogleMapOptions>) => void;
|
|
61
65
|
setZoom: (zoom: number) => void;
|
|
62
66
|
};
|
|
67
|
+
type GoogleMapBounds = {
|
|
68
|
+
north: number;
|
|
69
|
+
south: number;
|
|
70
|
+
east: number;
|
|
71
|
+
west: number;
|
|
72
|
+
};
|
|
63
73
|
type GoogleMarkerInstance = {
|
|
64
74
|
setIcon: (icon?: string) => void;
|
|
65
75
|
setMap: (map: GoogleMapInstance | null) => void;
|
|
@@ -67,6 +77,9 @@ type GoogleMarkerInstance = {
|
|
|
67
77
|
};
|
|
68
78
|
type GoogleCircleStyleOptions = Omit<GoogleMapRadiusOptions, "radiusMeters">;
|
|
69
79
|
type GoogleCircleInstance = {
|
|
80
|
+
getBounds: () => {
|
|
81
|
+
toJSON: () => GoogleMapBounds;
|
|
82
|
+
} | undefined;
|
|
70
83
|
setCenter: (center: GoogleMapPoint) => void;
|
|
71
84
|
setMap: (map: GoogleMapInstance | null) => void;
|
|
72
85
|
setOptions: (options: GoogleCircleStyleOptions) => void;
|
|
@@ -96,5 +109,5 @@ export type GoogleMapsApi = {
|
|
|
96
109
|
};
|
|
97
110
|
};
|
|
98
111
|
};
|
|
99
|
-
export declare function GoogleMap({ apiKey, center, zoom, marker, radius, interactive, zoomControls, minZoom, maxZoom, className, messages: messageOverrides, locale: localeOverride, }: GoogleMapProps): import("react").JSX.Element;
|
|
112
|
+
export declare function GoogleMap({ apiKey, center, zoom, marker, radius, interactive, zoomControls, fitRadius, fitPadding, minZoom, maxZoom, className, messages: messageOverrides, locale: localeOverride, }: GoogleMapProps): import("react").JSX.Element;
|
|
100
113
|
export {};
|