@firecms/core 3.0.0-tw4.2 → 3.0.0-tw4.4

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.
@@ -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;
@@ -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.2",
4
+ "version": "3.0.0-tw4.4",
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.2",
57
- "@firecms/formex": "^3.0.0-tw4.2",
58
- "@firecms/ui": "^3.0.0-tw4.2",
56
+ "@firecms/editor": "^3.0.0-tw4.4",
57
+ "@firecms/formex": "^3.0.0-tw4.4",
58
+ "@firecms/ui": "^3.0.0-tw4.4",
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": "1292c52536d1485a6dd0803de81e05448cb0a36e",
111
+ "gitHead": "16f788a2cb4ff14ee9d1dd2440da0cde9ffca5a9",
112
112
  "publishConfig": {
113
113
  "access": "public"
114
114
  },
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useContext, useEffect, useRef, useState } from "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
- offsetPosition,
71
- panel
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) => !open && onCloseRequest()}
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-drplyi3b6q-ey.a.run.app";
5
+ export const DEFAULT_SERVER = "https://api.firecms.co";
6
6
 
7
7
  export type AccessResponse = {
8
8
  blocked?: boolean;
@@ -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
+
@@ -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
+ }