@chelone/core 0.7.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/auth-options.d.ts +2 -0
- package/dist/auth/auth-options.js +3 -1
- package/dist/block-editor/block-document.d.ts +4 -0
- package/dist/block-editor/block-document.js +5 -1
- package/dist/command-menu/recents.d.ts +4 -0
- package/dist/command-menu/recents.js +5 -1
- package/dist/controller/calendar-dates.d.ts +12 -0
- package/dist/controller/calendar-dates.js +13 -1
- package/dist/controller/in-place-editor.d.ts +9 -8
- package/dist/controller/in-place-editor.js +3 -2
- package/dist/controller/inspector.d.ts +6 -0
- package/dist/controller/inspector.js +7 -1
- package/dist/controller/job-monitor.d.ts +8 -1
- package/dist/controller/job-monitor.js +11 -2
- package/dist/controller/kanban-board.d.ts +3 -2
- package/dist/controller/kanban-board.js +3 -1
- package/dist/controller/list-controller.d.ts +3 -0
- package/dist/controller/list-controller.js +5 -2
- package/dist/controller/pivot-grid.d.ts +4 -4
- package/dist/controller/prev-next.js +2 -2
- package/dist/controller/record-diff.d.ts +2 -2
- package/dist/controller/record-timeline.d.ts +5 -5
- package/dist/controller/reference-many-controller.d.ts +2 -0
- package/dist/controller/reference-many-controller.js +4 -3
- package/dist/csv/export.d.ts +2 -2
- package/dist/csv/import-controller.d.ts +4 -0
- package/dist/csv/import-controller.js +5 -1
- package/dist/data/fake-provider.js +7 -1
- package/dist/data/with-lifecycle-callbacks.d.ts +2 -2
- package/dist/form/choices-controller.d.ts +19 -0
- package/dist/form/choices-controller.js +18 -5
- package/dist/form/input-binding.js +3 -1
- package/dist/form/input-values.d.ts +10 -0
- package/dist/form/input-values.js +30 -1
- package/dist/form/json-text.d.ts +11 -0
- package/dist/form/json-text.js +12 -1
- package/dist/geo/coordinates.d.ts +10 -0
- package/dist/geo/coordinates.js +14 -1
- package/dist/geo/index.d.ts +2 -2
- package/dist/geo/map-style.d.ts +12 -0
- package/dist/geo/map-style.js +13 -1
- package/dist/guards.d.ts +7 -0
- package/dist/guards.js +16 -1
- package/dist/i18n/english-messages.d.ts +172 -0
- package/dist/i18n/english-messages.js +173 -1
- package/dist/i18n/missing-keys.d.ts +4 -0
- package/dist/i18n/missing-keys.js +5 -1
- package/dist/index.d.ts +22 -21
- package/dist/index.js +21 -20
- package/dist/onboarding-tour/placement.d.ts +2 -0
- package/dist/onboarding-tour/placement.js +3 -1
- package/dist/resource/describe-resource.js +26 -11
- package/dist/resource/source-path.d.ts +7 -1
- package/dist/rich-text/rich-text-extras.d.ts +2 -0
- package/dist/rich-text/rich-text-extras.js +4 -2
- package/dist/router/location.d.ts +2 -0
- package/dist/router/location.js +3 -1
- package/dist/ui-defaults.d.ts +15 -0
- package/dist/ui-defaults.js +17 -0
- package/package.json +1 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type SkipToken } from "@tanstack/query-core";
|
|
2
2
|
import type { AuthProvider, CanAccessParams, UserIdentity } from "./types.js";
|
|
3
|
+
/** The path a logged-out session is sent to when no `redirectTo` is given. */
|
|
4
|
+
export declare const DEFAULT_LOGIN_PATH = "/login";
|
|
3
5
|
/** The option shape of the factories that skip with no provider. */
|
|
4
6
|
interface SkippableQueryOptions<TKey, TData> {
|
|
5
7
|
queryKey: TKey;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isRecord } from "../guards.js";
|
|
2
2
|
import { skipToken } from "@tanstack/query-core";
|
|
3
3
|
// src/auth/auth-options.ts
|
|
4
|
+
/** The path a logged-out session is sent to when no `redirectTo` is given. */
|
|
5
|
+
var DEFAULT_LOGIN_PATH = "/login";
|
|
4
6
|
/** `skipToken` (the query never runs) with no provider, else `read` bound to it. */
|
|
5
7
|
function queryFnFor(authProvider, read) {
|
|
6
8
|
return authProvider ? () => read(authProvider) : skipToken;
|
|
@@ -89,4 +91,4 @@ async function checkError(authProvider, error) {
|
|
|
89
91
|
};
|
|
90
92
|
}
|
|
91
93
|
}
|
|
92
|
-
export { authStateQueryOptions, canAccessQueryOptions, checkError, identityQueryOptions, permissionsQueryOptions };
|
|
94
|
+
export { DEFAULT_LOGIN_PATH, authStateQueryOptions, canAccessQueryOptions, checkError, identityQueryOptions, permissionsQueryOptions };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from "@tanstack/form-core";
|
|
2
2
|
import { type FieldDescriptor } from "../resource/describe-resource.js";
|
|
3
|
+
/** The dnd-kit id of the block editor's drag handle, shared by both adapters. */
|
|
4
|
+
export declare const HANDLE_ID = "block-drag-handle";
|
|
5
|
+
/** Prefix of the dnd-kit id of each drop gap, followed by the gap's index. */
|
|
6
|
+
export declare const GAP_PREFIX = "block-gap-";
|
|
3
7
|
export type Attrs = Record<string, unknown>;
|
|
4
8
|
/** The shape of tiptap JSON, with no tiptap import. */
|
|
5
9
|
export interface BlockDocNode {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { columnLabelMatches } from "../controller/data-table-columns.js";
|
|
2
2
|
import { describeField } from "../resource/describe-resource.js";
|
|
3
3
|
// src/block-editor/block-document.ts
|
|
4
|
+
/** The dnd-kit id of the block editor's drag handle, shared by both adapters. */
|
|
5
|
+
var HANDLE_ID = "block-drag-handle";
|
|
6
|
+
/** Prefix of the dnd-kit id of each drop gap, followed by the gap's index. */
|
|
7
|
+
var GAP_PREFIX = "block-gap-";
|
|
4
8
|
var unknownBlockType = "unknownBlock";
|
|
5
9
|
var builtInNodes = /* @__PURE__ */ new Set([
|
|
6
10
|
"doc",
|
|
@@ -140,4 +144,4 @@ function toEmbedSrc(url) {
|
|
|
140
144
|
}
|
|
141
145
|
return null;
|
|
142
146
|
}
|
|
143
|
-
export { createBlockRegistry, describeBlockAttrs, emptyBlockDocument, getBlockDefaultAttrs, isBlockDoc, isEmptyBlockDocument, matchBlocks, toEmbedSrc, unknownBlockType, unwrapUnknownBlocks, wrapUnknownBlocks };
|
|
147
|
+
export { GAP_PREFIX, HANDLE_ID, createBlockRegistry, describeBlockAttrs, emptyBlockDocument, getBlockDefaultAttrs, isBlockDoc, isEmptyBlockDocument, matchBlocks, toEmbedSrc, unknownBlockType, unwrapUnknownBlocks, wrapUnknownBlocks };
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/** Where the command menu keeps its recents in a `CheloneStore`. */
|
|
2
2
|
export declare const recentsKey = "command-menu.recents";
|
|
3
|
+
/** Most recents the command menu keeps when `recentsLimit` is not set. */
|
|
4
|
+
export declare const DEFAULT_RECENTS_LIMIT = 10;
|
|
5
|
+
/** Wait after the last keystroke, in milliseconds, when `searchDebounceMs` is not set. */
|
|
6
|
+
export declare const DEFAULT_SEARCH_DEBOUNCE_MS = 250;
|
|
3
7
|
/**
|
|
4
8
|
* A page the user visited. It holds no `path`: the link is rebuilt from the
|
|
5
9
|
* resource and id, so a basename change cannot strand a saved entry.
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
// src/command-menu/recents.ts
|
|
2
2
|
/** Where the command menu keeps its recents in a `CheloneStore`. */
|
|
3
3
|
var recentsKey = "command-menu.recents";
|
|
4
|
+
/** Most recents the command menu keeps when `recentsLimit` is not set. */
|
|
5
|
+
var DEFAULT_RECENTS_LIMIT = 10;
|
|
6
|
+
/** Wait after the last keystroke, in milliseconds, when `searchDebounceMs` is not set. */
|
|
7
|
+
var DEFAULT_SEARCH_DEBOUNCE_MS = 250;
|
|
4
8
|
function sameEntry(a, b) {
|
|
5
9
|
return a.type === b.type && a.resource === b.resource && (a.id === void 0 ? void 0 : String(a.id)) === (b.id === void 0 ? void 0 : String(b.id));
|
|
6
10
|
}
|
|
@@ -9,4 +13,4 @@ function rememberRecent(recents, entry, limit) {
|
|
|
9
13
|
if (limit < 1) return [];
|
|
10
14
|
return [entry, ...recents.filter((other) => !sameEntry(other, entry))].slice(0, limit);
|
|
11
15
|
}
|
|
12
|
-
export { recentsKey, rememberRecent };
|
|
16
|
+
export { DEFAULT_RECENTS_LIMIT, DEFAULT_SEARCH_DEBOUNCE_MS, recentsKey, rememberRecent };
|
|
@@ -15,6 +15,18 @@ export declare function parseCalendarDate(value: unknown): Date | undefined;
|
|
|
15
15
|
export declare function calendarRange(view: CalendarView, anchor: Date, weekStartsOn: CalendarWeekStart): CalendarRange;
|
|
16
16
|
/** One local midnight per day: adding to the day of the month never skips or repeats a day when the clocks change. */
|
|
17
17
|
export declare function calendarDays(range: CalendarRange): Date[];
|
|
18
|
+
/** A selected day cell: the whole local day, as ISO strings. */
|
|
19
|
+
export interface CalendarDaySlot {
|
|
20
|
+
startISO: string;
|
|
21
|
+
endISO: string;
|
|
22
|
+
allDay: true;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The slot a whole day is: its local midnight to its last millisecond, the same
|
|
26
|
+
* inclusive end {@link calendarRange} gives a range. A day carrying a time is
|
|
27
|
+
* read as its day, not that instant.
|
|
28
|
+
*/
|
|
29
|
+
export declare function calendarDaySlot(day: Date): CalendarDaySlot;
|
|
18
30
|
export declare function shiftCalendarAnchor(view: CalendarView, anchor: Date, direction: 1 | -1): Date;
|
|
19
31
|
export declare const calendarDayKey: (date: Date) => string;
|
|
20
32
|
export interface CalendarLabels {
|
|
@@ -46,6 +46,18 @@ function calendarDays(range) {
|
|
|
46
46
|
for (let day = first; day.getTime() <= range.end.getTime(); day = atMidnight(first, days.length)) days.push(day);
|
|
47
47
|
return days;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* The slot a whole day is: its local midnight to its last millisecond, the same
|
|
51
|
+
* inclusive end {@link calendarRange} gives a range. A day carrying a time is
|
|
52
|
+
* read as its day, not that instant.
|
|
53
|
+
*/
|
|
54
|
+
function calendarDaySlot(day) {
|
|
55
|
+
return {
|
|
56
|
+
startISO: atMidnight(day).toISOString(),
|
|
57
|
+
endISO: atEndOfDay(day).toISOString(),
|
|
58
|
+
allDay: true
|
|
59
|
+
};
|
|
60
|
+
}
|
|
49
61
|
function shiftCalendarAnchor(view, anchor, direction) {
|
|
50
62
|
const time = [
|
|
51
63
|
anchor.getHours(),
|
|
@@ -89,4 +101,4 @@ function calendarLabels(locale) {
|
|
|
89
101
|
agendaDay: (date) => agendaDay.format(date)
|
|
90
102
|
};
|
|
91
103
|
}
|
|
92
|
-
export { calendarDayKey, calendarDays, calendarLabels, calendarRange, parseCalendarDate, shiftCalendarAnchor };
|
|
104
|
+
export { calendarDayKey, calendarDaySlot, calendarDays, calendarLabels, calendarRange, parseCalendarDate, shiftCalendarAnchor };
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
* Pure rules for an in-place editor. No react, no vue: both InPlaceEditor
|
|
3
3
|
* adapters step through the same states and send the same fields on a save.
|
|
4
4
|
*/
|
|
5
|
-
import type { ResourceRecord } from "../resource/record.js";
|
|
6
|
-
export type InPlaceEditorState = {
|
|
5
|
+
import type { LooseRecord, ResourceRecord } from "../resource/record.js";
|
|
6
|
+
export type InPlaceEditorState<R extends ResourceRecord = LooseRecord> = {
|
|
7
7
|
status: "reading";
|
|
8
8
|
} | {
|
|
9
9
|
status: "editing";
|
|
10
10
|
} | {
|
|
11
11
|
status: "saving";
|
|
12
|
-
preview:
|
|
12
|
+
preview: R;
|
|
13
13
|
};
|
|
14
|
-
export type InPlaceEditorEvent = {
|
|
14
|
+
export type InPlaceEditorEvent<R extends ResourceRecord = LooseRecord> = {
|
|
15
15
|
type: "edit";
|
|
16
16
|
} | {
|
|
17
17
|
type: "cancel";
|
|
18
18
|
} | {
|
|
19
19
|
type: "save";
|
|
20
|
-
preview:
|
|
20
|
+
preview: R;
|
|
21
21
|
} | {
|
|
22
22
|
type: "settled";
|
|
23
23
|
};
|
|
@@ -26,12 +26,13 @@ export type InPlaceEditorEvent = {
|
|
|
26
26
|
* `cancel`, editing to saving on `save`, saving to reading on `settled`. Any
|
|
27
27
|
* other pair returns the same state object, so a click while saving does nothing.
|
|
28
28
|
*/
|
|
29
|
-
export declare function nextInPlaceEditorState(state: InPlaceEditorState
|
|
29
|
+
export declare function nextInPlaceEditorState<R extends ResourceRecord = LooseRecord>(state: InPlaceEditorState<R>, event: InPlaceEditorEvent<R>): InPlaceEditorState<R>;
|
|
30
30
|
export interface InPlaceSave<R extends ResourceRecord> {
|
|
31
31
|
id: R["id"];
|
|
32
|
-
data:
|
|
32
|
+
data: Partial<R>;
|
|
33
33
|
previousData: R;
|
|
34
|
-
|
|
34
|
+
/** The record with the changed fields over it. */
|
|
35
|
+
preview: R;
|
|
35
36
|
}
|
|
36
37
|
/**
|
|
37
38
|
* Plans a save from the record and the dirty form values. `data` keeps the
|
|
@@ -27,8 +27,9 @@ function nextInPlaceEditorState(state, event) {
|
|
|
27
27
|
*/
|
|
28
28
|
function planInPlaceSave(record, dirty) {
|
|
29
29
|
if (!isLooseRecord(record)) return null;
|
|
30
|
-
const
|
|
31
|
-
if (
|
|
30
|
+
const changed = Object.entries(dirty).filter(([key, value]) => !deepEqual(record[key], value));
|
|
31
|
+
if (changed.length === 0) return null;
|
|
32
|
+
const data = Object.fromEntries(changed);
|
|
32
33
|
return {
|
|
33
34
|
id: record.id,
|
|
34
35
|
data,
|
|
@@ -26,6 +26,12 @@ export interface Inspector<TEditor> {
|
|
|
26
26
|
}
|
|
27
27
|
export declare function createInspector<TEditor = unknown>(): Inspector<TEditor>;
|
|
28
28
|
export declare const inspectorPositionKey = "chelone.inspector.position";
|
|
29
|
+
/** The panel's width in pixels. */
|
|
30
|
+
export declare const PANEL_WIDTH = 320;
|
|
31
|
+
/** How far one arrow key moves the panel, in pixels. */
|
|
32
|
+
export declare const INSPECTOR_KEY_STEP = 16;
|
|
33
|
+
/** The message key a `<Configurable>`'s open button falls back to. */
|
|
34
|
+
export declare const INSPECTOR_OPEN_LABEL = "chelone.inspector.customize";
|
|
29
35
|
export interface InspectorPosition {
|
|
30
36
|
x: number;
|
|
31
37
|
y: number;
|
|
@@ -30,6 +30,12 @@ function createInspector() {
|
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
var inspectorPositionKey = "chelone.inspector.position";
|
|
33
|
+
/** The panel's width in pixels. */
|
|
34
|
+
var PANEL_WIDTH = 320;
|
|
35
|
+
/** How far one arrow key moves the panel, in pixels. */
|
|
36
|
+
var INSPECTOR_KEY_STEP = 16;
|
|
37
|
+
/** The message key a `<Configurable>`'s open button falls back to. */
|
|
38
|
+
var INSPECTOR_OPEN_LABEL = "chelone.inspector.customize";
|
|
33
39
|
var clamp = (value, min, max) => Math.min(Math.max(value, min), Math.max(min, max));
|
|
34
40
|
/**
|
|
35
41
|
* Keeps the panel's drag handle on screen. A missing or non-finite position
|
|
@@ -61,4 +67,4 @@ function configurableInputColumns(inputs) {
|
|
|
61
67
|
return column;
|
|
62
68
|
});
|
|
63
69
|
}
|
|
64
|
-
export { clampInspectorPosition, configurableInputColumns, createInspector, defaultConfigurableInputsStoreKey, inspectorPositionKey };
|
|
70
|
+
export { INSPECTOR_KEY_STEP, INSPECTOR_OPEN_LABEL, PANEL_WIDTH, clampInspectorPosition, configurableInputColumns, createInspector, defaultConfigurableInputsStoreKey, inspectorPositionKey };
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Identifier } from "../resource/identifier.js";
|
|
10
10
|
import type { ResourceRecord } from "../resource/record.js";
|
|
11
|
+
import type { SourceKey } from "../resource/source-path.js";
|
|
11
12
|
/** The monitor's tabs, in order. One `buildKanbanBuckets` column per status. */
|
|
12
13
|
export declare const DEFAULT_JOB_TABS: readonly ["running", "queued", "failed", "done"];
|
|
13
14
|
export interface JobActionOptions<TRecord extends object> {
|
|
@@ -31,9 +32,15 @@ export interface JobActions {
|
|
|
31
32
|
/** The arguments of one `update` call, ready to hand to a data provider. */
|
|
32
33
|
export interface JobPlan<TRecord> {
|
|
33
34
|
id: Identifier;
|
|
34
|
-
data:
|
|
35
|
+
data: Partial<TRecord>;
|
|
35
36
|
previousData: TRecord;
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The status key a monitor reads: `source`, or `"status"`. `SourceKey` is
|
|
40
|
+
* `keyof TRecord & string` for a typed record and `string` for a loose one,
|
|
41
|
+
* whose keys are every string, so it is always a key of `TRecord`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function jobStatusSource<TRecord extends object>(source?: SourceKey<TRecord>): keyof TRecord & string;
|
|
37
44
|
export declare function getJobActions<TRecord extends object>(record: TRecord & object, options?: JobActionOptions<TRecord>): JobActions;
|
|
38
45
|
/** The `update` that retries the job, or `null` when it cannot be retried. */
|
|
39
46
|
export declare function planJobRetry<TRecord extends ResourceRecord = ResourceRecord>(record: TRecord, options?: JobActionOptions<TRecord>): JobPlan<TRecord> | null;
|
|
@@ -18,6 +18,14 @@ function canMove(record, source, from, to) {
|
|
|
18
18
|
allowed: allowed.includes(to)
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* The status key a monitor reads: `source`, or `"status"`. `SourceKey` is
|
|
23
|
+
* `keyof TRecord & string` for a typed record and `string` for a loose one,
|
|
24
|
+
* whose keys are every string, so it is always a key of `TRecord`.
|
|
25
|
+
*/
|
|
26
|
+
function jobStatusSource(source) {
|
|
27
|
+
return source ?? "status";
|
|
28
|
+
}
|
|
21
29
|
function getJobActions(record, options = {}) {
|
|
22
30
|
const { statusSource = "status", retryFrom = ["failed"], cancelFrom = ["running", "queued"], retryValue = "queued", cancelValue = "cancelled" } = options;
|
|
23
31
|
const retry = canMove(record, statusSource, retryFrom, retryValue);
|
|
@@ -30,9 +38,10 @@ function getJobActions(record, options = {}) {
|
|
|
30
38
|
}
|
|
31
39
|
function plan(record, source, value, allowed) {
|
|
32
40
|
if (!allowed) return null;
|
|
41
|
+
const data = { [source]: value };
|
|
33
42
|
return {
|
|
34
43
|
id: record.id,
|
|
35
|
-
data
|
|
44
|
+
data,
|
|
36
45
|
previousData: record
|
|
37
46
|
};
|
|
38
47
|
}
|
|
@@ -46,4 +55,4 @@ function planJobCancel(record, options = {}) {
|
|
|
46
55
|
const { statusSource = "status", cancelValue = "cancelled" } = options;
|
|
47
56
|
return plan(record, statusSource, cancelValue, getJobActions(record, options).canCancel);
|
|
48
57
|
}
|
|
49
|
-
export { DEFAULT_JOB_TABS, getJobActions, planJobCancel, planJobRetry };
|
|
58
|
+
export { DEFAULT_JOB_TABS, getJobActions, jobStatusSource, planJobCancel, planJobRetry };
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Identifier } from "../resource/identifier.js";
|
|
11
11
|
import type { ResourceRecord } from "../resource/record.js";
|
|
12
|
+
import type { SourceKey } from "../resource/source-path.js";
|
|
12
13
|
export interface KanbanColumnDef {
|
|
13
14
|
/** Value written to `groupSource` by a drop into this column. */
|
|
14
15
|
id: string;
|
|
@@ -35,11 +36,11 @@ export interface KanbanMove<R> {
|
|
|
35
36
|
* is in no bucket, and records keep list order inside a bucket. A repeated
|
|
36
37
|
* column id keeps the first column's records and leaves the later one empty.
|
|
37
38
|
*/
|
|
38
|
-
export declare function buildKanbanBuckets<R extends ResourceRecord>(records: readonly R[], groupSource:
|
|
39
|
+
export declare function buildKanbanBuckets<R extends ResourceRecord>(records: readonly R[], groupSource: SourceKey<R>, columns: readonly KanbanColumnDef[]): KanbanBucket<R>[];
|
|
39
40
|
/**
|
|
40
41
|
* The update a drop into `targetColumnId` asks for, or `null` when there is
|
|
41
42
|
* nothing to write: the target is not a column, or the record already sits in
|
|
42
43
|
* it. A drop that writes nothing still fires a data-provider `update` on every
|
|
43
44
|
* board that re-renders on a dropped drag, so the adapters ask here first.
|
|
44
45
|
*/
|
|
45
|
-
export declare function planKanbanMove<R extends ResourceRecord>(record: R, groupSource:
|
|
46
|
+
export declare function planKanbanMove<R extends ResourceRecord>(record: R, groupSource: SourceKey<R>, columns: readonly KanbanColumnDef[], targetColumnId: string): KanbanMove<R> | null;
|
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
* The column key a record sits under: its group value as a string, with a
|
|
4
4
|
* missing or null value reading as `""`. A top-level key, because a move can
|
|
5
5
|
* only write `{ [source]: value }` — as in {@link getAllowedTransitions}.
|
|
6
|
+
* Read through `Reflect.get`: `SourceKey<R>` of a generic record is a
|
|
7
|
+
* deferred type, so `record[groupSource]` would not typecheck.
|
|
6
8
|
*/
|
|
7
9
|
function bucketKey(record, groupSource) {
|
|
8
|
-
return String(record
|
|
10
|
+
return String(Reflect.get(record, groupSource) ?? "");
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* One bucket per column, in column order, empty columns included. A record
|
|
@@ -76,6 +76,9 @@ export interface ListControllerOptions {
|
|
|
76
76
|
storeKey?: string | false;
|
|
77
77
|
enabled?: boolean;
|
|
78
78
|
}
|
|
79
|
+
/** The `perPage` a list starts at when nothing is persisted or in the URL. The single definition: `prev-next` and both adapters read this. */
|
|
80
|
+
export declare const DEFAULT_PER_PAGE = 10;
|
|
81
|
+
/** Debounce delay for `setFilters(..., debounced=true)` when no `debounceMs` option is given. Shared with the reference-many and choices controllers. */
|
|
79
82
|
export declare const DEFAULT_DEBOUNCE_MS = 500;
|
|
80
83
|
export declare const DEFAULT_SELECT_ALL_LIMIT = 250;
|
|
81
84
|
/**
|
|
@@ -4,7 +4,10 @@ import { isRecord } from "../guards.js";
|
|
|
4
4
|
import { createMicroStore } from "../store/micro-store.js";
|
|
5
5
|
import { QueryObserver } from "@tanstack/query-core";
|
|
6
6
|
// src/controller/list-controller.ts
|
|
7
|
+
/** The `perPage` a list starts at when nothing is persisted or in the URL. The single definition: `prev-next` and both adapters read this. */
|
|
7
8
|
var DEFAULT_PER_PAGE = 10;
|
|
9
|
+
/** Debounce delay for `setFilters(..., debounced=true)` when no `debounceMs` option is given. Shared with the reference-many and choices controllers. */
|
|
10
|
+
var DEFAULT_DEBOUNCE_MS = 500;
|
|
8
11
|
var DEFAULT_SORT = [{
|
|
9
12
|
field: "id",
|
|
10
13
|
order: "ASC"
|
|
@@ -188,7 +191,7 @@ function createListController(ctx, resource, options = {}) {
|
|
|
188
191
|
const kv = ctx.store;
|
|
189
192
|
const defaults = {
|
|
190
193
|
page: 1,
|
|
191
|
-
perPage: options.perPage ??
|
|
194
|
+
perPage: options.perPage ?? 10,
|
|
192
195
|
sort: normalizeSort(options.sort, DEFAULT_SORT),
|
|
193
196
|
filter: { ...options.filterDefaultValues ?? {} },
|
|
194
197
|
displayedFilters: {}
|
|
@@ -466,4 +469,4 @@ function createListController(ctx, resource, options = {}) {
|
|
|
466
469
|
refetch
|
|
467
470
|
};
|
|
468
471
|
}
|
|
469
|
-
export { createErrorAnnouncer, createListController, errorMessageKey, listControllerActions, listParamsFromPatch, notifyFetchError, notifySelectAllLimit, pageFlags, removeFilterPath, resetPageTarget, runSelectAll };
|
|
472
|
+
export { DEFAULT_DEBOUNCE_MS, DEFAULT_PER_PAGE, createErrorAnnouncer, createListController, errorMessageKey, listControllerActions, listParamsFromPatch, notifyFetchError, notifySelectAllLimit, pageFlags, removeFilterPath, resetPageTarget, runSelectAll };
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* react, no vue: both PivotGrid adapters build the same table from the same
|
|
4
4
|
* records and print numbers the same way.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { SourceProp } from "../resource/source-path.js";
|
|
7
7
|
export type PivotAggregator = "count" | "sum" | "avg" | "min" | "max";
|
|
8
8
|
export interface PivotOptions<R> {
|
|
9
|
-
rowSource:
|
|
10
|
-
columnSource:
|
|
9
|
+
rowSource: SourceProp<R>;
|
|
10
|
+
columnSource: SourceProp<R>;
|
|
11
11
|
/** Read by every aggregator but `count`. */
|
|
12
|
-
valueSource?:
|
|
12
|
+
valueSource?: SourceProp<R>;
|
|
13
13
|
/** Defaults to `count`. */
|
|
14
14
|
aggregator?: PivotAggregator;
|
|
15
15
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import "./list-controller.js";
|
|
1
2
|
// src/controller/prev-next.ts
|
|
2
3
|
var DEFAULT_PREV_NEXT_LIMIT = 1e3;
|
|
3
|
-
var DEFAULT_PER_PAGE = 10;
|
|
4
4
|
var DEFAULT_SORT = {
|
|
5
5
|
field: "id",
|
|
6
6
|
order: "ASC"
|
|
@@ -19,7 +19,7 @@ function toListSort(sort) {
|
|
|
19
19
|
function getPrevNextParams(stored, options = {}) {
|
|
20
20
|
const persisted = options.storeKey === false ? void 0 : stored;
|
|
21
21
|
const page = persisted?.page ?? 1;
|
|
22
|
-
const perPage = persisted?.perPage ??
|
|
22
|
+
const perPage = persisted?.perPage ?? 10;
|
|
23
23
|
const sort = toListSort(persisted?.sort && (!Array.isArray(persisted.sort) || persisted.sort.length) ? persisted.sort : options.sort);
|
|
24
24
|
const filter = {
|
|
25
25
|
...persisted?.filter ?? options.filterDefaultValues,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Pure rules for a record diff. No react, no vue: both DiffViewer adapters
|
|
3
3
|
* mark the same field the same way and print the same default text.
|
|
4
4
|
*/
|
|
5
|
-
import type {
|
|
5
|
+
import type { SourceProp } from "../resource/source-path.js";
|
|
6
6
|
export type DiffStatus = "unchanged" | "added" | "removed" | "changed";
|
|
7
7
|
export interface RecordDiffRow {
|
|
8
8
|
source: string;
|
|
@@ -16,6 +16,6 @@ export interface RecordDiffRow {
|
|
|
16
16
|
* `null` and `undefined` both count as missing; an empty string is a value.
|
|
17
17
|
* Neither record is changed.
|
|
18
18
|
*/
|
|
19
|
-
export declare function diffRecords<R>(before: R, after: R, sources?: readonly
|
|
19
|
+
export declare function diffRecords<R>(before: R, after: R, sources?: readonly SourceProp<R>[]): RecordDiffRow[];
|
|
20
20
|
/** The text a viewer shows for a value when the app gives no renderer. */
|
|
21
21
|
export declare function formatDiffValue(value: unknown): string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* relative time text.
|
|
5
5
|
*/
|
|
6
6
|
import type { Identifier } from "../resource/identifier.js";
|
|
7
|
-
import type {
|
|
7
|
+
import type { SourceProp } from "../resource/source-path.js";
|
|
8
8
|
export interface RecordTimelineEntry<R> {
|
|
9
9
|
id: Identifier;
|
|
10
10
|
record: R;
|
|
@@ -15,10 +15,10 @@ export interface RecordTimelineEntry<R> {
|
|
|
15
15
|
icon: string | undefined;
|
|
16
16
|
}
|
|
17
17
|
export interface ToTimelineEntriesOptions<R> {
|
|
18
|
-
messageSource:
|
|
19
|
-
timestampSource:
|
|
20
|
-
userSource?:
|
|
21
|
-
iconSource?:
|
|
18
|
+
messageSource: SourceProp<R>;
|
|
19
|
+
timestampSource: SourceProp<R>;
|
|
20
|
+
userSource?: SourceProp<R>;
|
|
21
|
+
iconSource?: SourceProp<R>;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* One entry per record, in list order: the data provider's sort decides the
|
|
@@ -29,6 +29,8 @@ export interface ReferenceManyControllerOptions {
|
|
|
29
29
|
}
|
|
30
30
|
/** Same state and actions as {@link ListController}, so it drops into a list context. */
|
|
31
31
|
export type ReferenceManyController<TRecord> = ListController<TRecord>;
|
|
32
|
+
/** The `perPage` a reference list starts at when nothing is persisted. The single definition: the choices controller and Vue's `<ReferenceInput>` read this. */
|
|
33
|
+
export declare const DEFAULT_REFERENCE_PER_PAGE = 25;
|
|
32
34
|
/**
|
|
33
35
|
* A server-paged list of the rows whose `target` equals `id`, read through
|
|
34
36
|
* `getManyReference`. Params and selection live in an inner list controller
|
|
@@ -3,7 +3,8 @@ import { createMicroStore } from "../store/micro-store.js";
|
|
|
3
3
|
import { createErrorAnnouncer, createListController, pageFlags, resetPageTarget, runSelectAll } from "./list-controller.js";
|
|
4
4
|
import { QueryObserver } from "@tanstack/query-core";
|
|
5
5
|
// src/controller/reference-many-controller.ts
|
|
6
|
-
|
|
6
|
+
/** The `perPage` a reference list starts at when nothing is persisted. The single definition: the choices controller and Vue's `<ReferenceInput>` read this. */
|
|
7
|
+
var DEFAULT_REFERENCE_PER_PAGE = 25;
|
|
7
8
|
/**
|
|
8
9
|
* A server-paged list of the rows whose `target` equals `id`, read through
|
|
9
10
|
* `getManyReference`. Params and selection live in an inner list controller
|
|
@@ -24,7 +25,7 @@ function createReferenceManyController(ctx, reference, options) {
|
|
|
24
25
|
...ctx,
|
|
25
26
|
notifications: void 0
|
|
26
27
|
}, reference, {
|
|
27
|
-
perPage: options.perPage ??
|
|
28
|
+
perPage: options.perPage ?? 25,
|
|
28
29
|
sort: options.sort,
|
|
29
30
|
filterDefaultValues: options.filterDefaultValues,
|
|
30
31
|
storeKey,
|
|
@@ -199,4 +200,4 @@ function createReferenceManyController(ctx, reference, options) {
|
|
|
199
200
|
refetch
|
|
200
201
|
};
|
|
201
202
|
}
|
|
202
|
-
export { createReferenceManyController };
|
|
203
|
+
export { DEFAULT_REFERENCE_PER_PAGE, createReferenceManyController };
|
package/dist/csv/export.d.ts
CHANGED
|
@@ -18,14 +18,14 @@
|
|
|
18
18
|
* passes its own `exporter` prop — the button never assumes the default.
|
|
19
19
|
*/
|
|
20
20
|
import type { DataProvider } from "../data/types.js";
|
|
21
|
-
import type { LooseRecord } from "../resource/record.js";
|
|
21
|
+
import type { LooseRecord, ResourceRecord } from "../resource/record.js";
|
|
22
22
|
import type { CsvRow } from "./types.js";
|
|
23
23
|
/**
|
|
24
24
|
* Turns a page of records into a downloaded file. Called by the export buttons
|
|
25
25
|
* with the fetched rows, the raw dataProvider (for follow-up fetches), and the
|
|
26
26
|
* resource name.
|
|
27
27
|
*/
|
|
28
|
-
export type Exporter<TRecord extends
|
|
28
|
+
export type Exporter<TRecord extends ResourceRecord = LooseRecord> = (data: TRecord[], dataProvider: DataProvider, resource: string) => void | Promise<void>;
|
|
29
29
|
/**
|
|
30
30
|
* Serialize records to CSV text. Columns are the UNION of every record's keys,
|
|
31
31
|
* in first-seen order — so a sparse record still lines up under the right
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { type MicroStore } from "../store/micro-store.js";
|
|
2
2
|
import type { CsvImportOptions, CsvImportState, ParsedCsv } from "./types.js";
|
|
3
|
+
/** How many rows either adapter's preview step lists. */
|
|
4
|
+
export declare const PREVIEW_ROWS = 50;
|
|
5
|
+
/** What `readAsText` writes for each byte it cannot read as UTF-8. */
|
|
6
|
+
export declare const REPLACEMENT_CHAR = "\uFFFD";
|
|
3
7
|
export interface CsvImportController {
|
|
4
8
|
store: MicroStore<CsvImportState>;
|
|
5
9
|
/** Takes the file the adapter parsed. Does nothing while an import runs. */
|
|
@@ -9,6 +9,10 @@ var STEPS = [
|
|
|
9
9
|
"preview",
|
|
10
10
|
"commit"
|
|
11
11
|
];
|
|
12
|
+
/** How many rows either adapter's preview step lists. */
|
|
13
|
+
var PREVIEW_ROWS = 50;
|
|
14
|
+
/** What `readAsText` writes for each byte it cannot read as UTF-8. */
|
|
15
|
+
var REPLACEMENT_CHAR = "�";
|
|
12
16
|
var asError = (error) => error instanceof Error ? error : new Error(String(error));
|
|
13
17
|
function createCsvImportController(options) {
|
|
14
18
|
const fromSchema = csvSchemaFields(options.schema);
|
|
@@ -188,4 +192,4 @@ function createCsvImportController(options) {
|
|
|
188
192
|
}
|
|
189
193
|
};
|
|
190
194
|
}
|
|
191
|
-
export { createCsvImportController };
|
|
195
|
+
export { PREVIEW_ROWS, REPLACEMENT_CHAR, createCsvImportController };
|
|
@@ -85,7 +85,13 @@ function fakeDataProvider(initialData) {
|
|
|
85
85
|
return row;
|
|
86
86
|
};
|
|
87
87
|
let nextId = 1e6;
|
|
88
|
-
const listLike = (rows, params) =>
|
|
88
|
+
const listLike = (rows, params) => {
|
|
89
|
+
const page = paginate(applySort(applyFilter(rows, params.filter), params.sort), params.pagination);
|
|
90
|
+
return {
|
|
91
|
+
...page,
|
|
92
|
+
data: structuredClone(page.data)
|
|
93
|
+
};
|
|
94
|
+
};
|
|
89
95
|
return {
|
|
90
96
|
async getList(resource, params) {
|
|
91
97
|
return listLike(table(resource), params);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ResourceRecord } from "../resource/record.js";
|
|
2
2
|
import type { RecordOf, ResourceMap } from "../resource/registry.js";
|
|
3
3
|
import type { CreateParams, CreateResult, DataProvider, DeleteManyParams, DeleteManyResult, DeleteParams, DeleteResult, GetListParams, GetListResult, GetManyParams, GetManyReferenceParams, GetManyReferenceResult, GetManyResult, GetOneParams, GetOneResult, UpdateManyParams, UpdateManyResult, UpdateParams, UpdateResult } from "./types.js";
|
|
4
4
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -21,7 +21,7 @@ interface RecordFreeCallbacks<TResources extends ResourceMap> {
|
|
|
21
21
|
* `dataProvider` is the provider passed to `withLifecycleCallbacks`, so
|
|
22
22
|
* calling it never re-enters the callbacks.
|
|
23
23
|
*/
|
|
24
|
-
export interface LifecycleCallbacks<TResources extends ResourceMap, TRecord extends
|
|
24
|
+
export interface LifecycleCallbacks<TResources extends ResourceMap, TRecord extends ResourceRecord> extends RecordFreeCallbacks<TResources> {
|
|
25
25
|
afterGetList?(result: GetListResult<TRecord>, dataProvider: DataProvider<TResources>): MaybePromise<GetListResult<TRecord>>;
|
|
26
26
|
afterGetOne?(result: GetOneResult<TRecord>, dataProvider: DataProvider<TResources>): MaybePromise<GetOneResult<TRecord>>;
|
|
27
27
|
beforeCreate?(params: CreateParams<TRecord>, dataProvider: DataProvider<TResources>): MaybePromise<CreateParams<TRecord>>;
|
|
@@ -4,6 +4,12 @@ import type { NotificationStore } from "../notification/notification-store.js";
|
|
|
4
4
|
import type { RecordOf, ResourceMap } from "../resource/registry.js";
|
|
5
5
|
import type { SourceKey } from "../resource/source-path.js";
|
|
6
6
|
import { type MicroStore } from "../store/micro-store.js";
|
|
7
|
+
/**
|
|
8
|
+
* An erased choice record: choices carry arbitrary shapes (`{id, name, ...}`),
|
|
9
|
+
* and an adapter's choice inputs are generic over one. Both adapters re-export
|
|
10
|
+
* this as their own `ChoiceRecord`.
|
|
11
|
+
*/
|
|
12
|
+
export type ChoiceRecord = Record<string, unknown>;
|
|
7
13
|
/**
|
|
8
14
|
* The choices-context state (§0b parity). A list-like base (page/perPage/sort/
|
|
9
15
|
* filter) plus a status union over three record views:
|
|
@@ -94,3 +100,16 @@ export interface ChoicesControllerOptions<TResources extends ResourceMap = Resou
|
|
|
94
100
|
* observer result subscription — the subscription only maps results into state.
|
|
95
101
|
*/
|
|
96
102
|
export declare function createChoicesController<TResources extends ResourceMap, TName extends keyof TResources & string>(ctx: ChoicesControllerContext<TResources>, options: ChoicesControllerOptions<TResources, TName>): ChoicesController<RecordOf<TResources, TName>>;
|
|
103
|
+
/** The three choice views of a state, erased to `ChoiceRecord[]`. */
|
|
104
|
+
export interface ChoiceLists {
|
|
105
|
+
allChoices: ChoiceRecord[];
|
|
106
|
+
availableChoices: ChoiceRecord[];
|
|
107
|
+
selectedChoices: ChoiceRecord[];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The state's three choice views as the erased lists an adapter's
|
|
111
|
+
* `ChoicesContextValue` carries — the one place a record becomes a
|
|
112
|
+
* `ChoiceRecord`, so neither adapter writes the cast. The arrays are the
|
|
113
|
+
* state's own, not copies.
|
|
114
|
+
*/
|
|
115
|
+
export declare function choiceListsOf<TRecord>(state: ChoicesControllerState<TRecord>): ChoiceLists;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { notifyFetchError } from "../controller/list-controller.js";
|
|
2
|
+
import "../controller/reference-many-controller.js";
|
|
2
3
|
import { getListOptions, getManyOptions } from "../data/query-options.js";
|
|
3
4
|
import { removeEmpty } from "../data/remove-empty.js";
|
|
4
5
|
import { createMicroStore } from "../store/micro-store.js";
|
|
5
6
|
import { QueryObserver, keepPreviousData } from "@tanstack/query-core";
|
|
6
7
|
// src/form/choices-controller.ts
|
|
7
8
|
var DEFAULT_PAGE = 1;
|
|
8
|
-
var DEFAULT_PER_PAGE = 25;
|
|
9
|
-
var DEFAULT_DEBOUNCE_MS = 500;
|
|
10
9
|
var DEFAULT_SORT = {
|
|
11
10
|
field: "id",
|
|
12
11
|
order: "DESC"
|
|
@@ -38,10 +37,10 @@ function createChoicesController(ctx, options) {
|
|
|
38
37
|
const lookupKey = options.lookupKey ?? "id";
|
|
39
38
|
const isIdLookup = lookupKey === "id";
|
|
40
39
|
const permanentFilter = options.filter ?? {};
|
|
41
|
-
const debounceMs = options.debounceMs ??
|
|
40
|
+
const debounceMs = options.debounceMs ?? 500;
|
|
42
41
|
const store = createMicroStore({
|
|
43
42
|
page: options.page ?? DEFAULT_PAGE,
|
|
44
|
-
perPage: options.perPage ??
|
|
43
|
+
perPage: options.perPage ?? 25,
|
|
45
44
|
sort: { ...options.sort ?? DEFAULT_SORT },
|
|
46
45
|
filter: {},
|
|
47
46
|
allChoices: [],
|
|
@@ -305,4 +304,18 @@ function createChoicesController(ctx, options) {
|
|
|
305
304
|
refetch
|
|
306
305
|
};
|
|
307
306
|
}
|
|
308
|
-
|
|
307
|
+
/**
|
|
308
|
+
* The state's three choice views as the erased lists an adapter's
|
|
309
|
+
* `ChoicesContextValue` carries — the one place a record becomes a
|
|
310
|
+
* `ChoiceRecord`, so neither adapter writes the cast. The arrays are the
|
|
311
|
+
* state's own, not copies.
|
|
312
|
+
*/
|
|
313
|
+
function choiceListsOf(state) {
|
|
314
|
+
const { allChoices, availableChoices, selectedChoices } = state;
|
|
315
|
+
return {
|
|
316
|
+
allChoices,
|
|
317
|
+
availableChoices,
|
|
318
|
+
selectedChoices
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
export { choiceListsOf, createChoicesController };
|
|
@@ -146,7 +146,9 @@ function createFieldBinding(form, options) {
|
|
|
146
146
|
store: field.store,
|
|
147
147
|
getValue: formatValue,
|
|
148
148
|
setValue: (input) => field.handleChange(parseInput(input)),
|
|
149
|
-
blur: () =>
|
|
149
|
+
blur: () => {
|
|
150
|
+
if (unmountField !== void 0) field.handleBlur();
|
|
151
|
+
},
|
|
150
152
|
isRequired: () => isRequired(current.validate),
|
|
151
153
|
update(next) {
|
|
152
154
|
current = {
|