@rebasepro/app 0.13.0 → 0.13.1-canary.gcd6689e
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/collections/entity-display-cache.d.ts +50 -0
- package/dist/collections/entity-display.d.ts +35 -0
- package/dist/collections/entity_image_preview.d.ts +8 -0
- package/dist/collections/index.d.ts +3 -0
- package/dist/collections/property-path.d.ts +16 -0
- package/dist/index.es.js +214 -13
- package/dist/index.es.js.map +1 -1
- package/package.json +7 -7
- package/src/collections/entity-display-cache.ts +166 -0
- package/src/collections/entity-display.ts +105 -0
- package/src/collections/entity_image_preview.ts +13 -0
- package/src/collections/index.ts +3 -0
- package/src/collections/property-path.ts +30 -0
- package/src/collections/title-property.ts +12 -3
- package/src/components/common/useColumnsIds.tsx +1 -20
- package/src/locales/de.ts +1 -0
- package/src/locales/en.ts +1 -0
- package/src/locales/es.ts +1 -0
- package/src/locales/fr.ts +1 -0
- package/src/locales/hi.ts +1 -0
- package/src/locales/it.ts +1 -0
- package/src/locales/pt.ts +1 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/app",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.13.
|
|
4
|
+
"version": "0.13.1-canary.gcd6689e",
|
|
5
5
|
"description": "Rebase core — framework-agnostic runtime for data-driven admin panels",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
"magic-string": "^0.30.0",
|
|
53
53
|
"notistack": "^3.0.2",
|
|
54
54
|
"react-i18next": "^17.0.11",
|
|
55
|
-
"@rebasepro/
|
|
56
|
-
"@rebasepro/
|
|
57
|
-
"@rebasepro/forms": "0.13.
|
|
58
|
-
"@rebasepro/types": "0.13.
|
|
59
|
-
"@rebasepro/
|
|
60
|
-
"@rebasepro/
|
|
55
|
+
"@rebasepro/common": "0.13.1-canary.gcd6689e",
|
|
56
|
+
"@rebasepro/types": "0.13.1-canary.gcd6689e",
|
|
57
|
+
"@rebasepro/forms": "0.13.1-canary.gcd6689e",
|
|
58
|
+
"@rebasepro/admin-types": "0.13.1-canary.gcd6689e",
|
|
59
|
+
"@rebasepro/utils": "0.13.1-canary.gcd6689e",
|
|
60
|
+
"@rebasepro/ui": "0.13.1-canary.gcd6689e"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
63
|
"react": ">=19.2.7",
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The store behind a computed display value.
|
|
3
|
+
*
|
|
4
|
+
* A record's title is asked for far more often than it changes, and by many
|
|
5
|
+
* components at once: a list of fifty rows, each row's relation chips, the
|
|
6
|
+
* breadcrumb above them. Resolving per component is what makes an async display
|
|
7
|
+
* value a bad idea — fifty rows becomes fifty reads, then fifty more on the next
|
|
8
|
+
* render.
|
|
9
|
+
*
|
|
10
|
+
* So resolution is keyed by record *and* role, in-flight calls are shared, and
|
|
11
|
+
* results are kept until something says otherwise. Deliberately not a React
|
|
12
|
+
* thing: the same store answers an imperative caller (an export, a breadcrumb
|
|
13
|
+
* built outside the tree), and it is testable without a renderer.
|
|
14
|
+
*/
|
|
15
|
+
import type { EntityDisplayRole } from "@rebasepro/admin-types";
|
|
16
|
+
|
|
17
|
+
type CacheEntry = {
|
|
18
|
+
/** Set once resolved. `null` means "resolved to nothing", not "unknown". */
|
|
19
|
+
value: unknown;
|
|
20
|
+
} | {
|
|
21
|
+
/** Shared by every caller that arrives while the first one is in flight. */
|
|
22
|
+
promise: Promise<unknown>;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type EntityDisplayKey = string;
|
|
26
|
+
|
|
27
|
+
/** The identity of one role of one record, as a cache key. */
|
|
28
|
+
export function entityDisplayKey(
|
|
29
|
+
path: string,
|
|
30
|
+
entityId: string | number | undefined,
|
|
31
|
+
role: EntityDisplayRole
|
|
32
|
+
): EntityDisplayKey {
|
|
33
|
+
return `${role} ${path} ${entityId ?? ""}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class EntityDisplayCache {
|
|
37
|
+
|
|
38
|
+
private readonly entries = new Map<EntityDisplayKey, CacheEntry>();
|
|
39
|
+
private readonly listeners = new Set<() => void>();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The resolved value, or `undefined` when this pair has not been resolved
|
|
43
|
+
* yet. `null` is a resolved absence, and the two must stay distinct: a
|
|
44
|
+
* caller that reads "not yet" as "nothing" flickers its fallback in on every
|
|
45
|
+
* mount.
|
|
46
|
+
*/
|
|
47
|
+
peek(key: EntityDisplayKey): unknown | undefined {
|
|
48
|
+
const entry = this.entries.get(key);
|
|
49
|
+
if (!entry || "promise" in entry) return undefined;
|
|
50
|
+
return entry.value;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** True while a resolution for this pair is in flight. */
|
|
54
|
+
isLoading(key: EntityDisplayKey): boolean {
|
|
55
|
+
const entry = this.entries.get(key);
|
|
56
|
+
return Boolean(entry && "promise" in entry);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Resolve once per record and role. Concurrent callers share the first
|
|
61
|
+
* call's promise; later callers get the cached value with no promise at all.
|
|
62
|
+
*
|
|
63
|
+
* A resolver that throws is recorded as "nothing" rather than retried: the
|
|
64
|
+
* alternative is every render re-running a call that just failed. The caller
|
|
65
|
+
* that saw the rejection is the one that logs it.
|
|
66
|
+
*/
|
|
67
|
+
resolve(key: EntityDisplayKey, resolver: () => unknown): Promise<unknown> {
|
|
68
|
+
const entry = this.entries.get(key);
|
|
69
|
+
if (entry) {
|
|
70
|
+
return "promise" in entry ? entry.promise : Promise.resolve(entry.value);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
let produced: unknown;
|
|
74
|
+
try {
|
|
75
|
+
produced = resolver();
|
|
76
|
+
} catch {
|
|
77
|
+
this.set(key, null);
|
|
78
|
+
return Promise.resolve(null);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// A synchronous resolver never enters the loading state. Putting it
|
|
82
|
+
// through one would render every caller's fallback for a frame, for
|
|
83
|
+
// nothing.
|
|
84
|
+
if (!isPromise(produced)) {
|
|
85
|
+
const value = normalize(produced);
|
|
86
|
+
this.set(key, value);
|
|
87
|
+
return Promise.resolve(value);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const promise = produced
|
|
91
|
+
.then(resolved => {
|
|
92
|
+
const value = normalize(resolved);
|
|
93
|
+
this.set(key, value);
|
|
94
|
+
return value;
|
|
95
|
+
})
|
|
96
|
+
.catch(() => {
|
|
97
|
+
this.set(key, null);
|
|
98
|
+
return null;
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.entries.set(key, { promise });
|
|
102
|
+
return promise;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Drop what is known about a record, so the next ask resolves again. Called
|
|
107
|
+
* after a write: the row that just saved may be called something else now.
|
|
108
|
+
*/
|
|
109
|
+
invalidate(path: string, entityId?: string | number): void {
|
|
110
|
+
const suffix = ` ${path} ${entityId ?? ""}`;
|
|
111
|
+
let changed = false;
|
|
112
|
+
for (const key of [...this.entries.keys()]) {
|
|
113
|
+
// `undefined` id means the whole collection — after an import, or a
|
|
114
|
+
// locale switch that changes what every title in it reads.
|
|
115
|
+
const matches = entityId === undefined
|
|
116
|
+
? key.includes(` ${path} `) || key.endsWith(` ${path} `)
|
|
117
|
+
: key.endsWith(suffix);
|
|
118
|
+
if (matches) {
|
|
119
|
+
this.entries.delete(key);
|
|
120
|
+
changed = true;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (changed) this.emit();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Drop everything. The user signed out, or the app swapped datasource. */
|
|
127
|
+
clear(): void {
|
|
128
|
+
if (this.entries.size === 0) return;
|
|
129
|
+
this.entries.clear();
|
|
130
|
+
this.emit();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
subscribe(listener: () => void): () => void {
|
|
134
|
+
this.listeners.add(listener);
|
|
135
|
+
return () => {
|
|
136
|
+
this.listeners.delete(listener);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private set(key: EntityDisplayKey, value: unknown): void {
|
|
141
|
+
this.entries.set(key, { value });
|
|
142
|
+
this.emit();
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private emit(): void {
|
|
146
|
+
for (const listener of [...this.listeners]) listener();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Empty strings and empty arrays are absences, not values — a title of `" "`
|
|
152
|
+
* would otherwise beat the derived one and render as a blank heading.
|
|
153
|
+
*/
|
|
154
|
+
function normalize(value: unknown): unknown {
|
|
155
|
+
if (value === undefined || value === null) return null;
|
|
156
|
+
if (typeof value === "string") {
|
|
157
|
+
const trimmed = value.trim();
|
|
158
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
159
|
+
}
|
|
160
|
+
if (Array.isArray(value)) return value.length > 0 ? value : null;
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function isPromise(value: unknown): value is Promise<unknown> {
|
|
165
|
+
return typeof (value as Promise<unknown> | undefined)?.then === "function";
|
|
166
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a collection's `display` block.
|
|
3
|
+
*
|
|
4
|
+
* Two questions, kept apart because they are answered at different times: which
|
|
5
|
+
* *property* fills a role (readable from values already in hand) and which
|
|
6
|
+
* *resolver* fills it (may have to go to the network). A caller that cannot
|
|
7
|
+
* await — a sort comparator, an export column, a server render — uses the key
|
|
8
|
+
* and is documented to ignore resolvers.
|
|
9
|
+
*/
|
|
10
|
+
import type {
|
|
11
|
+
AdminCollection,
|
|
12
|
+
EntityDisplayResolver,
|
|
13
|
+
EntityDisplayRole
|
|
14
|
+
} from "@rebasepro/admin-types";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Collections that have already warned about a deprecated field, so a list of
|
|
18
|
+
* fifty rows produces one line in the console rather than fifty.
|
|
19
|
+
*/
|
|
20
|
+
const deprecationWarned = new Set<string>();
|
|
21
|
+
|
|
22
|
+
function collectionId(collection: AdminCollection<never> | AdminCollection<any>): string {
|
|
23
|
+
return (collection.slug ?? collection.name ?? "collection") as string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function warnOnce(id: string, message: string): void {
|
|
27
|
+
const key = `${id}:${message}`;
|
|
28
|
+
if (deprecationWarned.has(key)) return;
|
|
29
|
+
deprecationWarned.add(key);
|
|
30
|
+
console.warn(message);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* What the collection declares for a role, before deciding which form it is.
|
|
35
|
+
*
|
|
36
|
+
* `display.title` wins over the deprecated `titleProperty`: a collection setting
|
|
37
|
+
* both is mid-migration, and the new field is the one it means.
|
|
38
|
+
*/
|
|
39
|
+
function getDeclaredSource<M extends Record<string, unknown>>(
|
|
40
|
+
collection: AdminCollection<M>,
|
|
41
|
+
role: EntityDisplayRole
|
|
42
|
+
): string | EntityDisplayResolver<M, unknown> | undefined {
|
|
43
|
+
|
|
44
|
+
const display = collection.display as Record<string, unknown> | undefined;
|
|
45
|
+
const declared = display?.[role];
|
|
46
|
+
if (declared !== undefined) return declared as string | EntityDisplayResolver<M, unknown>;
|
|
47
|
+
|
|
48
|
+
if (role === "title" && collection.titleProperty) {
|
|
49
|
+
const id = collectionId(collection);
|
|
50
|
+
warnOnce(
|
|
51
|
+
id,
|
|
52
|
+
`[rebase] Collection "${id}" uses admin.titleProperty, which is deprecated. ` +
|
|
53
|
+
`Move it to admin.display.title — the same string works there, and display.title ` +
|
|
54
|
+
`also accepts a resolver for a title the record does not carry.`
|
|
55
|
+
);
|
|
56
|
+
return collection.titleProperty as string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The property path a role is declared to read, when it is declared as a path.
|
|
64
|
+
*
|
|
65
|
+
* Returns `undefined` for a role filled by a resolver — a resolver has no key —
|
|
66
|
+
* and for a role the collection says nothing about, which is then derived.
|
|
67
|
+
*
|
|
68
|
+
* @group Collections
|
|
69
|
+
*/
|
|
70
|
+
export function getDisplayPropertyKey<M extends Record<string, unknown>>(
|
|
71
|
+
collection: AdminCollection<M>,
|
|
72
|
+
role: EntityDisplayRole
|
|
73
|
+
): string | undefined {
|
|
74
|
+
const declared = getDeclaredSource(collection, role);
|
|
75
|
+
return typeof declared === "string" ? declared : undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The resolver a role is declared to use, when it is declared as one.
|
|
80
|
+
*
|
|
81
|
+
* @group Collections
|
|
82
|
+
*/
|
|
83
|
+
export function getDisplayResolver<M extends Record<string, unknown>>(
|
|
84
|
+
collection: AdminCollection<M>,
|
|
85
|
+
role: EntityDisplayRole
|
|
86
|
+
): EntityDisplayResolver<M, unknown> | undefined {
|
|
87
|
+
const declared = getDeclaredSource(collection, role);
|
|
88
|
+
return typeof declared === "function" ? declared : undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* True when the collection states this role at all, in either form.
|
|
93
|
+
*
|
|
94
|
+
* The derivation is a guess about what a collection probably means; a statement
|
|
95
|
+
* outranks it, and the heuristics that look for "the first enum" or "the leading
|
|
96
|
+
* relation" have to stand down when one exists.
|
|
97
|
+
*
|
|
98
|
+
* @group Collections
|
|
99
|
+
*/
|
|
100
|
+
export function hasDeclaredDisplay<M extends Record<string, unknown>>(
|
|
101
|
+
collection: AdminCollection<M>,
|
|
102
|
+
role: EntityDisplayRole
|
|
103
|
+
): boolean {
|
|
104
|
+
return getDeclaredSource(collection, role) !== undefined;
|
|
105
|
+
}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
import { CollectionConfig } from "@rebasepro/types";
|
|
2
|
+
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
3
|
+
import { getDisplayPropertyKey } from "./entity-display";
|
|
2
4
|
|
|
5
|
+
/**
|
|
6
|
+
* The property that fills a record's image slot.
|
|
7
|
+
*
|
|
8
|
+
* `admin.display.image` first, then six fallbacks in descending confidence —
|
|
9
|
+
* the first image-typed storage property, an array of them, a URL rendered as an
|
|
10
|
+
* image, and so on. The ladder stays for collections that say nothing; a
|
|
11
|
+
* collection that names its picture is not guessed at.
|
|
12
|
+
*/
|
|
3
13
|
export function getEntityImagePreviewPropertyKey<M extends Record<string, unknown>>(collection: CollectionConfig<M>): string | undefined {
|
|
4
14
|
|
|
15
|
+
const declared = getDisplayPropertyKey(collection as AdminCollection<M>, "image");
|
|
16
|
+
if (declared) return declared;
|
|
17
|
+
|
|
5
18
|
// find first storage property of type image
|
|
6
19
|
for (const key in collection.properties) {
|
|
7
20
|
const property = collection.properties[key];
|
package/src/collections/index.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
export * from "./collection_view_config";
|
|
2
2
|
export * from "./entity_image_preview";
|
|
3
|
+
export * from "./entity-display";
|
|
4
|
+
export * from "./entity-display-cache";
|
|
3
5
|
export * from "./filter-operator-resolution";
|
|
4
6
|
export * from "./form-layout";
|
|
5
7
|
export * from "./navigation_from_path";
|
|
6
8
|
export * from "./navigation_utils";
|
|
7
9
|
export * from "./parent_references_from_path";
|
|
10
|
+
export * from "./property-path";
|
|
8
11
|
export * from "./property_presentation";
|
|
9
12
|
export * from "./title-property";
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Properties, Property } from "@rebasepro/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The property at a dotted path, walking `map` children — `address.street`.
|
|
5
|
+
*
|
|
6
|
+
* The value counterpart is `getValueInPath` in `@rebasepro/utils`; this is the
|
|
7
|
+
* schema half, and the two have to be used together. Reading a dotted path off
|
|
8
|
+
* an entity while looking its property up with a flat `properties[path]` gives
|
|
9
|
+
* the value and `undefined` for how to render it, which is how a declared title
|
|
10
|
+
* on a nested field silently fell back to a derived one.
|
|
11
|
+
*
|
|
12
|
+
* There were three copies of this: one private to `useColumnsIds`, one exported
|
|
13
|
+
* from the admin layer, and the flat lookup in the title resolver that was not
|
|
14
|
+
* this function at all. This is the one, in the lowest layer that needs it —
|
|
15
|
+
* admin re-exports it under the name it already published.
|
|
16
|
+
*/
|
|
17
|
+
export function getPropertyInPath(properties: Properties, path: string): Property | undefined {
|
|
18
|
+
if (typeof properties !== "object" || !properties) return undefined;
|
|
19
|
+
if (path in properties) {
|
|
20
|
+
return (properties as Record<string, Property>)[path];
|
|
21
|
+
}
|
|
22
|
+
if (path.includes(".")) {
|
|
23
|
+
const pathSegments = path.split(".");
|
|
24
|
+
const childProperty = (properties as Record<string, Property>)[pathSegments[0]];
|
|
25
|
+
if (typeof childProperty === "object" && childProperty?.type === "map" && childProperty.properties) {
|
|
26
|
+
return getPropertyInPath(childProperty.properties, pathSegments.slice(1).join("."));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
@@ -2,6 +2,8 @@ import { isRelationalCollectionConfig, type Property, type Relation } from "@reb
|
|
|
2
2
|
import { generateForeignKeyName } from "@rebasepro/utils";
|
|
3
3
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
4
4
|
import { getPrimaryKeys, isPropertyBuilder } from "@rebasepro/common";
|
|
5
|
+
import { getPropertyInPath } from "./property-path";
|
|
6
|
+
import { getDisplayPropertyKey, hasDeclaredDisplay } from "./entity-display";
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* How good a property is as the human-readable title of an entity.
|
|
@@ -182,8 +184,13 @@ export function getTitlePropertyCandidates<M extends Record<string, unknown>>(
|
|
|
182
184
|
): string[] {
|
|
183
185
|
if (!collection.properties) return [];
|
|
184
186
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
+
// `getPropertyInPath`, not `properties[key]`: a declared title on a nested
|
|
188
|
+
// field — `profile.display_name` — was looked up flat, found nothing, and
|
|
189
|
+
// silently fell through to the derived ranking. The dotted form has been
|
|
190
|
+
// documented as valid the whole time.
|
|
191
|
+
const declared = getDisplayPropertyKey(collection, "title");
|
|
192
|
+
if (declared && getPropertyInPath(collection.properties, declared)) {
|
|
193
|
+
return [declared];
|
|
187
194
|
}
|
|
188
195
|
|
|
189
196
|
const idKeys = new Set<string>(getPrimaryKeys(collection) as string[]);
|
|
@@ -237,7 +244,9 @@ export function getLeadingRelationTitleKey<M extends Record<string, unknown>>(
|
|
|
237
244
|
collection: AdminCollection<M>
|
|
238
245
|
): string | undefined {
|
|
239
246
|
if (!collection.properties) return undefined;
|
|
240
|
-
|
|
247
|
+
// The collection states what it is called, in one form or the other; either
|
|
248
|
+
// way it is not asking for the leading-relation heuristic.
|
|
249
|
+
if (hasDeclaredDisplay(collection, "title")) return undefined;
|
|
241
250
|
|
|
242
251
|
const idKeys = new Set<string>(getPrimaryKeys(collection) as string[]);
|
|
243
252
|
const foreignKeys = getForeignKeyColumns(collection);
|
|
@@ -3,26 +3,7 @@ import { useMemo } from "react";
|
|
|
3
3
|
;
|
|
4
4
|
import { getSubcollections } from "@rebasepro/common";
|
|
5
5
|
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get a property in a property tree from a dot-path like `address.street`.
|
|
9
|
-
* Inlined here to avoid importing property-aware utilities from the admin layer.
|
|
10
|
-
*/
|
|
11
|
-
function getPropertyInPath(properties: Properties, path: string): Property | undefined {
|
|
12
|
-
if (typeof properties === "object") {
|
|
13
|
-
if (path in properties) {
|
|
14
|
-
return (properties as Record<string, Property>)[path];
|
|
15
|
-
}
|
|
16
|
-
if (path.includes(".")) {
|
|
17
|
-
const pathSegments = path.split(".");
|
|
18
|
-
const childProperty = (properties as Record<string, Property>)[pathSegments[0]];
|
|
19
|
-
if (typeof childProperty === "object" && childProperty.type === "map" && childProperty.properties) {
|
|
20
|
-
return getPropertyInPath(childProperty.properties, pathSegments.slice(1).join("."));
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
6
|
+
import { getPropertyInPath } from "../../collections/property-path";
|
|
26
7
|
|
|
27
8
|
export type PropertyColumnConfig = {
|
|
28
9
|
key: string;
|
package/src/locales/de.ts
CHANGED
|
@@ -77,6 +77,7 @@ export const de: RebaseTranslations = {
|
|
|
77
77
|
column_cannot_be_edited: "Diese Spalte kann nicht direkt bearbeitet werden",
|
|
78
78
|
close: "Schließen",
|
|
79
79
|
hide_list: "Liste ausblenden",
|
|
80
|
+
show_list: "Liste einblenden",
|
|
80
81
|
unsaved_local_changes: "Ungespeicherte lokale Änderungen",
|
|
81
82
|
unsaved_local_changes_description: "Dieses Dokument wurde lokal bearbeitet und weist ungespeicherte Änderungen auf. Diese lokalen Änderungen gehen verloren, wenn Sie sie nicht anwenden.",
|
|
82
83
|
preview_changes: "Änderungen in der Vorschau anzeigen",
|
package/src/locales/en.ts
CHANGED
|
@@ -87,6 +87,7 @@ export const en: RebaseTranslations = {
|
|
|
87
87
|
column_cannot_be_edited: "This column can't be edited directly",
|
|
88
88
|
close: "Close",
|
|
89
89
|
hide_list: "Hide list",
|
|
90
|
+
show_list: "Show list",
|
|
90
91
|
unsaved_local_changes: "Unsaved Local changes",
|
|
91
92
|
unsaved_local_changes_description: "This document was edited locally and has unsaved changes. These local changes will be lost if you don't apply them.",
|
|
92
93
|
preview_changes: "Preview Changes",
|
package/src/locales/es.ts
CHANGED
|
@@ -85,6 +85,7 @@ export const es: RebaseTranslations = {
|
|
|
85
85
|
column_cannot_be_edited: "Esta columna no se puede editar directamente",
|
|
86
86
|
close: "Cerrar",
|
|
87
87
|
hide_list: "Ocultar lista",
|
|
88
|
+
show_list: "Mostrar lista",
|
|
88
89
|
unsaved_local_changes: "Cambios locales sin guardar",
|
|
89
90
|
unsaved_local_changes_description: "Este documento ha sido editado localmente y tiene cambios sin guardar. Estos cambios locales se perderán si no los aplicas.",
|
|
90
91
|
preview_changes: "Previsualizar cambios",
|
package/src/locales/fr.ts
CHANGED
|
@@ -77,6 +77,7 @@ export const fr: RebaseTranslations = {
|
|
|
77
77
|
column_cannot_be_edited: "Cette colonne ne peut pas être modifiée directement",
|
|
78
78
|
close: "Fermer",
|
|
79
79
|
hide_list: "Masquer la liste",
|
|
80
|
+
show_list: "Afficher la liste",
|
|
80
81
|
unsaved_local_changes: "Modifications locales non enregistrées",
|
|
81
82
|
unsaved_local_changes_description: "Ce document a été modifié localement et contient des modifications non enregistrées. Ces modifications locales seront perdues si vous ne les appliquez pas.",
|
|
82
83
|
preview_changes: "Aperçu des modifications",
|
package/src/locales/hi.ts
CHANGED
|
@@ -77,6 +77,7 @@ export const hi: RebaseTranslations = {
|
|
|
77
77
|
column_cannot_be_edited: "इस कॉलम को सीधे संपादित नहीं किया जा सकता",
|
|
78
78
|
close: "बंद करें",
|
|
79
79
|
hide_list: "सूची छिपाएँ",
|
|
80
|
+
show_list: "सूची दिखाएँ",
|
|
80
81
|
unsaved_local_changes: "सहेजे नहीं गए स्थानीय परिवर्तन",
|
|
81
82
|
unsaved_local_changes_description: "इस दस्तावेज़ को स्थानीय स्तर पर संपादित किया गया था और इसमें सहेजे नहीं गए परिवर्तन हैं। यदि आप इन्हें लागू नहीं करते हैं तो ये स्थानीय परिवर्तन खो जाएंगे।",
|
|
82
83
|
preview_changes: "परिवर्तनों का पूर्वावलोकन करें",
|
package/src/locales/it.ts
CHANGED
|
@@ -77,6 +77,7 @@ export const it: RebaseTranslations = {
|
|
|
77
77
|
column_cannot_be_edited: "Questa colonna non può essere modificata direttamente",
|
|
78
78
|
close: "Chiudi",
|
|
79
79
|
hide_list: "Nascondi elenco",
|
|
80
|
+
show_list: "Mostra elenco",
|
|
80
81
|
unsaved_local_changes: "Modifiche locali non salvate",
|
|
81
82
|
unsaved_local_changes_description: "Questo documento è stato modificato localmente e ha modifiche non salvate. Queste modifiche locali andranno perse se non le applichi.",
|
|
82
83
|
preview_changes: "Anteprima modifiche",
|
package/src/locales/pt.ts
CHANGED
|
@@ -82,6 +82,7 @@ export const pt: RebaseTranslations = {
|
|
|
82
82
|
column_cannot_be_edited: "Esta coluna não pode ser editada diretamente",
|
|
83
83
|
close: "Fechar",
|
|
84
84
|
hide_list: "Ocultar lista",
|
|
85
|
+
show_list: "Mostrar lista",
|
|
85
86
|
unsaved_local_changes: "Alterações locais não guardadas",
|
|
86
87
|
unsaved_local_changes_description: "Este documento foi editado localmente e tem alterações não guardadas. Estas alterações locais serão perdidas se não as aplicar.",
|
|
87
88
|
preview_changes: "Pré-visualizar alterações",
|