@firecms/core 3.0.0-tw4.2 → 3.0.0-tw4.3
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/hooks/useProjectLog.d.ts +1 -1
- package/dist/index.es.js +23 -6
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +23 -6
- package/dist/index.umd.js.map +1 -1
- package/dist/util/entity_cache.d.ts +0 -1
- package/dist/util/strings.d.ts +1 -0
- package/dist/util/strings.test.d.ts +1 -0
- package/package.json +5 -5
- package/src/core/SideDialogs.tsx +19 -8
- package/src/hooks/useProjectLog.tsx +1 -1
- package/src/util/entity_cache.ts +0 -5
- package/src/util/strings.test.ts +101 -0
- package/src/util/strings.ts +21 -0
|
@@ -12,7 +12,6 @@ export declare function hasEntityInCache(path: string): boolean;
|
|
|
12
12
|
* Retrieves an entity from the in-memory cache or `localStorage`.
|
|
13
13
|
* If the entity is not in the cache but exists in `localStorage`, it loads it into the cache.
|
|
14
14
|
* @param path - The unique path/key for the entity.
|
|
15
|
-
* @param useLocalStorage
|
|
16
15
|
* @returns The cached entity or `undefined` if not found.
|
|
17
16
|
*/
|
|
18
17
|
export declare function getEntityFromCache(path: string): object | undefined;
|
package/dist/util/strings.d.ts
CHANGED
|
@@ -4,3 +4,4 @@ export declare function randomString(strLength?: number): string;
|
|
|
4
4
|
export declare function randomColor(): string;
|
|
5
5
|
export declare function slugify(text?: string, separator?: string, lowercase?: boolean): string;
|
|
6
6
|
export declare function unslugify(slug?: string): string;
|
|
7
|
+
export declare function prettifyIdentifier(input: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@firecms/core",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.0.0-tw4.
|
|
4
|
+
"version": "3.0.0-tw4.3",
|
|
5
5
|
"description": "Awesome Firebase/Firestore-based headless open-source CMS",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/firecmsco"
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"@dnd-kit/core": "^6.3.1",
|
|
54
54
|
"@dnd-kit/modifiers": "^9.0.0",
|
|
55
55
|
"@dnd-kit/sortable": "^10.0.0",
|
|
56
|
-
"@firecms/editor": "^3.0.0-tw4.
|
|
57
|
-
"@firecms/formex": "^3.0.0-tw4.
|
|
58
|
-
"@firecms/ui": "^3.0.0-tw4.
|
|
56
|
+
"@firecms/editor": "^3.0.0-tw4.3",
|
|
57
|
+
"@firecms/formex": "^3.0.0-tw4.3",
|
|
58
|
+
"@firecms/ui": "^3.0.0-tw4.3",
|
|
59
59
|
"@radix-ui/react-portal": "^1.1.10",
|
|
60
60
|
"clsx": "^2.1.1",
|
|
61
61
|
"compressorjs": "^1.2.1",
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
"dist",
|
|
109
109
|
"src"
|
|
110
110
|
],
|
|
111
|
-
"gitHead": "
|
|
111
|
+
"gitHead": "cd0162fc6759a7e55df77471c3433d89894ce2a1",
|
|
112
112
|
"publishConfig": {
|
|
113
113
|
"access": "public"
|
|
114
114
|
},
|
package/src/core/SideDialogs.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useContext, useEffect, useState } from "react";
|
|
2
2
|
import { useSideDialogsController } from "../hooks";
|
|
3
3
|
import { SideDialogPanelProps } from "../types";
|
|
4
4
|
import { Sheet } from "@firecms/ui";
|
|
@@ -61,15 +61,15 @@ export function SideDialogs() {
|
|
|
61
61
|
<SideDialogView
|
|
62
62
|
key={`side_dialog_${index}`}
|
|
63
63
|
panel={panel}
|
|
64
|
-
offsetPosition={sidePanels.length - index - 1}/>)
|
|
64
|
+
offsetPosition={sidePanels.length - index - 1} />)
|
|
65
65
|
}
|
|
66
66
|
</>;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function SideDialogView({
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
offsetPosition,
|
|
71
|
+
panel
|
|
72
|
+
}: {
|
|
73
73
|
offsetPosition: number,
|
|
74
74
|
panel?: SideDialogPanelProps
|
|
75
75
|
}) {
|
|
@@ -134,7 +134,18 @@ function SideDialogView({
|
|
|
134
134
|
|
|
135
135
|
<Sheet
|
|
136
136
|
open={Boolean(panel)}
|
|
137
|
-
onOpenChange={(open) =>
|
|
137
|
+
onOpenChange={(open) => {
|
|
138
|
+
if (!open) {
|
|
139
|
+
// Check if any suggestion menu is visible in DOM
|
|
140
|
+
const suggestionMenu = document.querySelector("[data-suggestion-menu=\"true\"]");
|
|
141
|
+
if (suggestionMenu && window.getComputedStyle(suggestionMenu).visibility !== "hidden") {
|
|
142
|
+
// Don't close the sheet if a suggestion menu is visible
|
|
143
|
+
// Let Tiptap handle closing the menu first
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
onCloseRequest();
|
|
147
|
+
}
|
|
148
|
+
}}
|
|
138
149
|
title={"Side dialog " + panel?.key}
|
|
139
150
|
>
|
|
140
151
|
{panel &&
|
|
@@ -150,7 +161,7 @@ function SideDialogView({
|
|
|
150
161
|
</ErrorBoundary>
|
|
151
162
|
</div>}
|
|
152
163
|
|
|
153
|
-
{!panel && <div style={{ width }}/>}
|
|
164
|
+
{!panel && <div style={{ width }} />}
|
|
154
165
|
|
|
155
166
|
</Sheet>
|
|
156
167
|
|
|
@@ -158,7 +169,7 @@ function SideDialogView({
|
|
|
158
169
|
open={drawerCloseRequested}
|
|
159
170
|
handleOk={drawerCloseRequested ? handleDrawerCloseOk : handleNavigationOk}
|
|
160
171
|
handleCancel={drawerCloseRequested ? handleDrawerCloseCancel : handleNavigationCancel}
|
|
161
|
-
body={blockedNavigationMessage}/>
|
|
172
|
+
body={blockedNavigationMessage} />
|
|
162
173
|
|
|
163
174
|
</SideDialogContext.Provider>
|
|
164
175
|
|
|
@@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from "react";
|
|
|
2
2
|
import { AuthController, DataSourceDelegate, FireCMSPlugin } from "../types";
|
|
3
3
|
|
|
4
4
|
export const DEFAULT_SERVER_DEV = "https://api-kdoe6pj3qq-ey.a.run.app";
|
|
5
|
-
export const DEFAULT_SERVER = "https://api
|
|
5
|
+
export const DEFAULT_SERVER = "https://api.firecms.co";
|
|
6
6
|
|
|
7
7
|
export type AccessResponse = {
|
|
8
8
|
blocked?: boolean;
|
package/src/util/entity_cache.ts
CHANGED
|
@@ -122,7 +122,6 @@ export function hasEntityInCache(path: string): boolean {
|
|
|
122
122
|
* Retrieves an entity from the in-memory cache or `localStorage`.
|
|
123
123
|
* If the entity is not in the cache but exists in `localStorage`, it loads it into the cache.
|
|
124
124
|
* @param path - The unique path/key for the entity.
|
|
125
|
-
* @param useLocalStorage
|
|
126
125
|
* @returns The cached entity or `undefined` if not found.
|
|
127
126
|
*/
|
|
128
127
|
export function getEntityFromCache(path: string): object | undefined {
|
|
@@ -134,10 +133,6 @@ export function getEntityFromCache(path: string): object | undefined {
|
|
|
134
133
|
const entityString = localStorage.getItem(key);
|
|
135
134
|
if (entityString) {
|
|
136
135
|
const entity: object = JSON.parse(entityString, customReviver);
|
|
137
|
-
console.log("Loaded entity from localStorage:", {
|
|
138
|
-
key,
|
|
139
|
-
entity
|
|
140
|
-
});
|
|
141
136
|
return entity;
|
|
142
137
|
}
|
|
143
138
|
} catch (error) {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { prettifyIdentifier } from "./strings";
|
|
2
|
+
|
|
3
|
+
describe("prettifyIdentifier", () => {
|
|
4
|
+
it("should return empty string for empty input", () => {
|
|
5
|
+
expect(prettifyIdentifier("")).toBe("");
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it("should handle camelCase", () => {
|
|
9
|
+
expect(prettifyIdentifier("displayName")).toBe("Display Name");
|
|
10
|
+
expect(prettifyIdentifier("firstName")).toBe("First Name");
|
|
11
|
+
expect(prettifyIdentifier("lastName")).toBe("Last Name");
|
|
12
|
+
expect(prettifyIdentifier("emailAddress")).toBe("Email Address");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should handle PascalCase", () => {
|
|
16
|
+
expect(prettifyIdentifier("DisplayName")).toBe("Display Name");
|
|
17
|
+
expect(prettifyIdentifier("FirstName")).toBe("First Name");
|
|
18
|
+
expect(prettifyIdentifier("UserProfile")).toBe("User Profile");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("should handle snake_case", () => {
|
|
22
|
+
expect(prettifyIdentifier("display_name")).toBe("Display Name");
|
|
23
|
+
expect(prettifyIdentifier("first_name")).toBe("First Name");
|
|
24
|
+
expect(prettifyIdentifier("user_profile")).toBe("User Profile");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("should handle kebab-case", () => {
|
|
28
|
+
expect(prettifyIdentifier("display-name")).toBe("Display Name");
|
|
29
|
+
expect(prettifyIdentifier("first-name")).toBe("First Name");
|
|
30
|
+
expect(prettifyIdentifier("user-profile")).toBe("User Profile");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should handle mixed separators", () => {
|
|
34
|
+
expect(prettifyIdentifier("display_name-test")).toBe("Display Name Test");
|
|
35
|
+
expect(prettifyIdentifier("first-name_last")).toBe("First Name Last");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it("should handle acronyms correctly", () => {
|
|
39
|
+
expect(prettifyIdentifier("imageURL")).toBe("Image URL");
|
|
40
|
+
expect(prettifyIdentifier("XMLParser")).toBe("XML Parser");
|
|
41
|
+
expect(prettifyIdentifier("HTTPSConnection")).toBe("HTTPS Connection");
|
|
42
|
+
expect(prettifyIdentifier("parseHTML")).toBe("Parse HTML");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it("should handle consecutive uppercase letters", () => {
|
|
46
|
+
expect(prettifyIdentifier("URLParser")).toBe("URL Parser");
|
|
47
|
+
expect(prettifyIdentifier("HTMLElement")).toBe("HTML Element");
|
|
48
|
+
expect(prettifyIdentifier("APIKey")).toBe("API Key");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should handle single words", () => {
|
|
52
|
+
expect(prettifyIdentifier("name")).toBe("Name");
|
|
53
|
+
expect(prettifyIdentifier("title")).toBe("Title");
|
|
54
|
+
expect(prettifyIdentifier("description")).toBe("Description");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should handle all uppercase", () => {
|
|
58
|
+
expect(prettifyIdentifier("NAME")).toBe("NAME");
|
|
59
|
+
expect(prettifyIdentifier("TITLE")).toBe("TITLE");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should handle all lowercase", () => {
|
|
63
|
+
expect(prettifyIdentifier("name")).toBe("Name");
|
|
64
|
+
expect(prettifyIdentifier("title")).toBe("Title");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should handle multiple consecutive separators", () => {
|
|
68
|
+
expect(prettifyIdentifier("display__name")).toBe("Display Name");
|
|
69
|
+
expect(prettifyIdentifier("first--name")).toBe("First Name");
|
|
70
|
+
expect(prettifyIdentifier("user___profile")).toBe("User Profile");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("should trim whitespace", () => {
|
|
74
|
+
expect(prettifyIdentifier(" displayName ")).toBe("Display Name");
|
|
75
|
+
expect(prettifyIdentifier(" first_name ")).toBe("First Name");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("should handle numbers", () => {
|
|
79
|
+
expect(prettifyIdentifier("user123")).toBe("User123");
|
|
80
|
+
expect(prettifyIdentifier("item1Name")).toBe("Item1Name");
|
|
81
|
+
expect(prettifyIdentifier("version2Point0")).toBe("Version2Point0");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should handle complex combinations", () => {
|
|
85
|
+
expect(prettifyIdentifier("userProfileURLParser")).toBe("User Profile URL Parser");
|
|
86
|
+
expect(prettifyIdentifier("parse_HTML_document")).toBe("Parse HTML Document");
|
|
87
|
+
expect(prettifyIdentifier("API-key-validator")).toBe("API Key Validator");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("should handle edge cases with underscores and hyphens at boundaries", () => {
|
|
91
|
+
expect(prettifyIdentifier("_displayName")).toBe("Display Name");
|
|
92
|
+
expect(prettifyIdentifier("displayName_")).toBe("Display Name");
|
|
93
|
+
expect(prettifyIdentifier("-displayName-")).toBe("Display Name");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should handle already formatted strings", () => {
|
|
97
|
+
expect(prettifyIdentifier("Display Name")).toBe("Display Name");
|
|
98
|
+
expect(prettifyIdentifier("First Name")).toBe("First Name");
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
package/src/util/strings.ts
CHANGED
|
@@ -61,3 +61,24 @@ export function unslugify(slug?: string): string {
|
|
|
61
61
|
return slug.trim();
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
export function prettifyIdentifier(input: string) {
|
|
66
|
+
if (!input) return "";
|
|
67
|
+
|
|
68
|
+
let text = input;
|
|
69
|
+
|
|
70
|
+
// 1. Handle camelCase and Acronyms
|
|
71
|
+
// Group 1 ($1 $2): Lowercase followed by Uppercase (e.g., imageURL -> image URL)
|
|
72
|
+
// Group 2 ($3 $4): Uppercase followed by Uppercase+lowercase (e.g., XMLParser -> XML Parser)
|
|
73
|
+
text = text.replace(/([a-z])([A-Z])|([A-Z])([A-Z][a-z])/g, "$1$3 $2$4");
|
|
74
|
+
|
|
75
|
+
// 2. Replace hyphens/underscores with spaces
|
|
76
|
+
text = text.replace(/[_-]+/g, " ");
|
|
77
|
+
|
|
78
|
+
// 3. Capitalize first letter of each word (Title Case)
|
|
79
|
+
const s = text
|
|
80
|
+
.trim()
|
|
81
|
+
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
82
|
+
console.log("Prettified identifier:", { input,s });
|
|
83
|
+
return s;
|
|
84
|
+
}
|