tinacms 0.0.0-af203c6-20251216034843 → 0.0.0-b0f55b5-20251228232811

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.
@@ -173,30 +173,17 @@ export declare class LocalClient extends Client {
173
173
  export declare class TinaCMSSearchClient implements SearchClient {
174
174
  private client;
175
175
  private tinaSearchConfig?;
176
- private fuzzyEnabled;
177
- private defaultFuzzyOptions?;
178
176
  constructor(client: Client, tinaSearchConfig?: {
179
177
  stopwordLanguages?: string[];
180
- fuzzyEnabled?: boolean;
181
- fuzzyOptions?: {
182
- maxDistance?: number;
183
- minSimilarity?: number;
184
- maxResults?: number;
185
- useTranspositions?: boolean;
186
- caseSensitive?: boolean;
187
- };
188
178
  });
189
179
  query(query: string, options?: {
190
180
  limit?: number;
191
181
  cursor?: string;
192
- fuzzy?: boolean;
193
- fuzzyOptions?: any;
194
182
  }): Promise<{
195
183
  results: any[];
196
184
  nextCursor: string | null;
197
185
  total: number;
198
186
  prevCursor: string | null;
199
- fuzzyMatches?: Record<string, any[]>;
200
187
  }>;
201
188
  del(ids: string[]): Promise<any>;
202
189
  put(docs: any[]): Promise<any>;
@@ -204,30 +191,15 @@ export declare class TinaCMSSearchClient implements SearchClient {
204
191
  }
205
192
  export declare class LocalSearchClient implements SearchClient {
206
193
  private client;
207
- private tinaSearchConfig?;
208
- private fuzzyEnabled;
209
- private defaultFuzzyOptions?;
210
- constructor(client: Client, tinaSearchConfig?: {
211
- fuzzyEnabled?: boolean;
212
- fuzzyOptions?: {
213
- maxDistance?: number;
214
- minSimilarity?: number;
215
- maxResults?: number;
216
- useTranspositions?: boolean;
217
- caseSensitive?: boolean;
218
- };
219
- });
194
+ constructor(client: Client);
220
195
  query(query: string, options?: {
221
196
  limit?: number;
222
197
  cursor?: string;
223
- fuzzy?: boolean;
224
- fuzzyOptions?: any;
225
198
  }): Promise<{
226
199
  results: any[];
227
200
  nextCursor: string | null;
228
201
  total: number;
229
202
  prevCursor: string | null;
230
- fuzzyMatches?: Record<string, any[]>;
231
203
  }>;
232
204
  del(ids: string[]): Promise<any>;
233
205
  put(docs: any[]): Promise<any>;
package/dist/react.js CHANGED
@@ -54,6 +54,15 @@ function useTina(props) {
54
54
  }
55
55
  }
56
56
  if (fieldName) {
57
+ if (lastHoveredField !== null) {
58
+ lastHoveredField = null;
59
+ if (isInTinaIframe) {
60
+ parent.postMessage(
61
+ { type: "field:hovered", fieldName: null },
62
+ window.location.origin
63
+ );
64
+ }
65
+ }
57
66
  if (isInTinaIframe) {
58
67
  parent.postMessage(
59
68
  { type: "field:selected", fieldName },
@@ -61,6 +70,40 @@ function useTina(props) {
61
70
  );
62
71
  }
63
72
  }
73
+ }, mouseEnterHandler = function(e) {
74
+ if (!(e.target instanceof Element)) {
75
+ return;
76
+ }
77
+ const attributeNames = e.target.getAttributeNames();
78
+ const tinaAttribute = attributeNames.find(
79
+ (name) => name.startsWith("data-tina-field")
80
+ );
81
+ let fieldName;
82
+ if (tinaAttribute) {
83
+ fieldName = e.target.getAttribute(tinaAttribute);
84
+ } else {
85
+ const ancestor = e.target.closest(
86
+ "[data-tina-field], [data-tina-field-overlay]"
87
+ );
88
+ if (ancestor) {
89
+ const attributeNames2 = ancestor.getAttributeNames();
90
+ const tinaAttribute2 = attributeNames2.find(
91
+ (name) => name.startsWith("data-tina-field")
92
+ );
93
+ if (tinaAttribute2) {
94
+ fieldName = ancestor.getAttribute(tinaAttribute2);
95
+ }
96
+ }
97
+ }
98
+ if (fieldName && fieldName !== lastHoveredField) {
99
+ lastHoveredField = fieldName;
100
+ if (isInTinaIframe) {
101
+ parent.postMessage(
102
+ { type: "field:hovered", fieldName },
103
+ window.location.origin
104
+ );
105
+ }
106
+ }
64
107
  };
65
108
  const style = document.createElement("style");
66
109
  style.type = "text/css";
@@ -74,6 +117,15 @@ function useTina(props) {
74
117
  outline: 2px solid rgba(34,150,254,1);
75
118
  cursor: pointer;
76
119
  }
120
+ [data-tina-field-focused] {
121
+ outline: 2px dashed #C2410C !important;
122
+ box-shadow: none !important;
123
+ }
124
+ [data-tina-field-focused]:hover {
125
+ box-shadow: inset 100vi 100vh rgba(194, 65, 12, 0.3) !important;
126
+ outline: 2px solid #C2410C !important;
127
+ cursor: pointer;
128
+ }
77
129
  [data-tina-field-overlay] {
78
130
  outline: 2px dashed rgba(34,150,254,0.5);
79
131
  position: relative;
@@ -94,12 +146,19 @@ function useTina(props) {
94
146
  [data-tina-field-overlay]:hover::after {
95
147
  opacity: 1;
96
148
  }
149
+ [data-tina-field-overlay][data-tina-field-focused]::after {
150
+ background-color: rgba(194, 65, 12, 0.3);
151
+ opacity: 1;
152
+ }
97
153
  `;
98
154
  document.head.appendChild(style);
99
155
  document.body.classList.add("__tina-quick-editing-enabled");
156
+ let lastHoveredField = null;
100
157
  document.addEventListener("click", mouseDownHandler, true);
158
+ document.addEventListener("mouseenter", mouseEnterHandler, true);
101
159
  return () => {
102
160
  document.removeEventListener("click", mouseDownHandler, true);
161
+ document.removeEventListener("mouseenter", mouseEnterHandler, true);
103
162
  document.body.classList.remove("__tina-quick-editing-enabled");
104
163
  style.remove();
105
164
  };
@@ -113,6 +172,7 @@ function useTina(props) {
113
172
  });
114
173
  }
115
174
  }, [id]);
175
+ const lastFocusedFieldRef = React.useRef(null);
116
176
  React.useEffect(() => {
117
177
  const { experimental___selectFormByFormId, ...rest } = props;
118
178
  parent.postMessage({ type: "open", ...rest, id }, window.location.origin);
@@ -142,6 +202,40 @@ function useTina(props) {
142
202
  );
143
203
  }
144
204
  }
205
+ if (event.data.type === "field:set-focused") {
206
+ const newFieldName = event.data.fieldName;
207
+ if (newFieldName === lastFocusedFieldRef.current) {
208
+ return;
209
+ }
210
+ lastFocusedFieldRef.current = newFieldName;
211
+ const allTinaFields = document.querySelectorAll("[data-tina-field]");
212
+ allTinaFields.forEach((el) => {
213
+ el.removeAttribute("data-tina-field-focused");
214
+ });
215
+ if (newFieldName) {
216
+ let targetElement = document.querySelector(
217
+ `[data-tina-field="${newFieldName}"]`
218
+ );
219
+ if (!targetElement) {
220
+ const allFields = Array.from(allTinaFields);
221
+ targetElement = allFields.find((el) => {
222
+ const fieldValue = el.getAttribute("data-tina-field");
223
+ return fieldValue && fieldValue.endsWith(newFieldName.split("---")[1]);
224
+ });
225
+ }
226
+ if (targetElement) {
227
+ targetElement.setAttribute("data-tina-field-focused", "true");
228
+ const rect = targetElement.getBoundingClientRect();
229
+ const isInViewport = rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth;
230
+ if (!isInViewport) {
231
+ targetElement.scrollIntoView({
232
+ behavior: "smooth",
233
+ block: "center"
234
+ });
235
+ }
236
+ }
237
+ }
238
+ }
145
239
  };
146
240
  window.addEventListener("message", handleMessage);
147
241
  return () => {
@@ -1,2 +1,3 @@
1
1
  import * as React from 'react';
2
2
  export declare const ActiveFieldIndicator: () => React.JSX.Element;
3
+ export declare const HoveredFieldIndicator: () => React.JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import 'moment-timezone';
2
2
  import * as React from 'react';
3
- import { type DayPickerProps } from 'react-day-picker';
3
+ import { DayPickerProps } from 'react-day-picker';
4
4
  type TimePickerType = 'minutes' | 'seconds' | 'hours' | '12hours';
5
5
  type Period = 'AM' | 'PM';
6
6
  declare const formatCurrentDate: ({ dateFormat, timeFormat, displayDate, }: {
@@ -4,6 +4,6 @@ export interface PasswordFieldProps extends a {
4
4
  error?: boolean;
5
5
  ref?: any;
6
6
  }
7
- export declare const passwordFieldClasses = "shadow-inner focus:shadow-outline focus:border-blue-500 focus:outline-none block text-base placeholder:text-gray-300 px-3 py-2 text-gray-600 w-full bg-white border border-gray-200 transition-all ease-out duration-150 focus:text-gray-900 rounded";
7
+ export declare const passwordFieldClasses = "shadow-inner focus:shadow-outline focus:border-tina-orange-dark focus:outline-none block text-base placeholder:text-gray-300 px-3 py-2 text-gray-600 w-full bg-white border border-gray-200 transition-all ease-out duration-150 focus:text-gray-900 rounded";
8
8
  export declare const BasePasswordField: React.ForwardRefExoticComponent<Omit<PasswordFieldProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
9
9
  export {};
@@ -18,6 +18,6 @@ export interface SelectProps {
18
18
  options?: (Option | string)[];
19
19
  className?: string;
20
20
  }
21
- export declare const selectFieldClasses = "shadow appearance-none h-full bg-white block pl-3 pr-8 py-2 truncate w-full text-base cursor-pointer border border-gray-200 focus:outline-none focus:shadow-outline focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded";
21
+ export declare const selectFieldClasses = "shadow appearance-none h-full bg-white block pl-3 pr-8 py-2 truncate w-full text-base cursor-pointer border border-gray-200 focus:outline-none focus:shadow-outline focus:ring-tina-orange-dark focus:border-tina-orange-dark sm:text-sm rounded";
22
22
  export declare const Select: React.FC<SelectProps>;
23
23
  export {};
@@ -4,6 +4,6 @@ export interface TextFieldProps extends a {
4
4
  error?: boolean;
5
5
  ref?: any;
6
6
  }
7
- export declare const textFieldClasses = "shadow-inner focus:shadow-outline focus:border-blue-500 focus:outline-none block text-base placeholder:text-gray-300 px-3 py-2 text-gray-600 w-full bg-white border border-gray-200 transition-all ease-out duration-150 focus:text-gray-900 rounded";
7
+ export declare const textFieldClasses = "shadow-inner focus:shadow-outline focus:border-tina-orange-dark focus-visible:ring-0 focus:outline-none focus:ring-2 block text-base placeholder:text-gray-300 px-3 py-2 text-gray-600 w-full bg-white border border-gray-200 transition-all ease-out duration-150 focus:text-gray-900 rounded";
8
8
  export declare const BaseTextField: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & React.RefAttributes<HTMLInputElement>>;
9
9
  export {};
@@ -1,11 +1,10 @@
1
- import { CodeLinePlugin } from '@udecode/plate-code-block/react';
2
1
  import React from 'react';
3
2
  import { CodeLeaf } from '../../components/plate-ui/code-leaf';
4
3
  import { CodeSyntaxLeaf } from '../../components/plate-ui/code-syntax-leaf';
5
4
  import { HrElement } from '../../components/plate-ui/hr-element';
6
5
  import { LinkElement } from '../../components/plate-ui/link-element';
7
6
  export declare const Components: () => {
8
- [CodeLinePlugin.key]: React.ForwardRefExoticComponent<Omit<Omit<import("@udecode/plate/react").PlateElementProps<import("@udecode/plate").TElement, import("@udecode/plate").AnyPluginConfig>, keyof {
7
+ [x: number]: React.ForwardRefExoticComponent<Omit<Omit<import("@udecode/plate/react").PlateElementProps<import("@udecode/plate").TElement, import("@udecode/plate").AnyPluginConfig>, keyof {
9
8
  className?: string;
10
9
  style?: React.CSSProperties;
11
10
  }> & import("@udecode/plate").BasePluginContext<import("@udecode/plate").AnyPluginConfig> & {
@@ -21,11 +21,15 @@ interface FieldMetaProps extends React.HTMLAttributes<HTMLElement> {
21
21
  margin?: boolean;
22
22
  index?: number;
23
23
  tinaForm: Form;
24
+ focusIntent?: boolean;
25
+ hoverIntent?: boolean;
24
26
  }
25
- export declare const FieldMeta: ({ name, label, description, error, margin, children, index, tinaForm, ...props }: FieldMetaProps) => React.JSX.Element;
26
- export declare const FieldWrapper: ({ margin, children, ...props }: {
27
+ export declare const FieldMeta: ({ name, label, description, error, margin, children, index, tinaForm, focusIntent, hoverIntent, ...props }: FieldMetaProps) => React.JSX.Element;
28
+ export declare const FieldWrapper: ({ margin, children, "data-tina-field-active": dataActive, "data-tina-field-hovering": dataHovering, ...restProps }: {
27
29
  margin: boolean;
28
30
  children: React.ReactNode;
31
+ "data-tina-field-active"?: string;
32
+ "data-tina-field-hovering"?: string;
29
33
  } & Partial<React.ComponentPropsWithoutRef<"div">>) => React.JSX.Element;
30
34
  export interface FieldLabel extends React.HTMLAttributes<HTMLLabelElement> {
31
35
  children?: any | any[];
@@ -3,10 +3,11 @@ import * as React from 'react';
3
3
  export interface FieldsBuilderProps {
4
4
  form: Form;
5
5
  activeFieldName?: string;
6
+ hoveringFieldName?: string;
6
7
  fields: Field[];
7
8
  padding?: boolean;
8
9
  }
9
- export declare function FieldsBuilder({ form, fields, activeFieldName, padding, }: FieldsBuilderProps): React.JSX.Element;
10
+ export declare function FieldsBuilder({ form, fields, activeFieldName, hoveringFieldName, padding, }: FieldsBuilderProps): React.JSX.Element;
10
11
  export declare const FieldsGroup: ({ padding, children, }: {
11
12
  padding?: boolean;
12
13
  children?: any | any[];
@@ -5,6 +5,7 @@ export interface FormBuilderProps {
5
5
  form: {
6
6
  tinaForm: Form;
7
7
  activeFieldName?: string;
8
+ hoveringFieldName?: string;
8
9
  };
9
10
  hideFooter?: boolean;
10
11
  label?: string;
@@ -20,9 +20,10 @@ export interface Field<F extends Field = AnyField> {
20
20
  * a guarantee that the given field will receive focus since that functionality
21
21
  * needs to be built on a per-component basis.
22
22
  *
23
- * This is also a one-way stree. The "active field" for a given form isn't
23
+ * This is also a one-way street. The "active field" for a given form isn't
24
24
  * necessarily updated when a user clicks on a new field. So you can have a
25
25
  * field which is marked as the active field, and have focus on another field
26
26
  */
27
- experimental_focusIntent?: boolean;
27
+ focusIntent?: boolean;
28
+ hoverIntent?: boolean;
28
29
  }
@@ -1,5 +1,24 @@
1
1
  import { Form } from './react-tinacms';
2
2
  import { TinaCMS } from './tina-cms';
3
+ export declare const ACTION_TYPES: {
4
+ readonly FORMS_ADD: "forms:add";
5
+ readonly FORMS_REMOVE: "forms:remove";
6
+ readonly FORMS_CLEAR: "forms:clear";
7
+ readonly FORMS_SET_ACTIVE_FORM_ID: "forms:set-active-form-id";
8
+ readonly FORMS_SET_ACTIVE_FIELD_NAME: "forms:set-active-field-name";
9
+ readonly FORMS_SET_HOVERED_FIELD_NAME: "forms:set-hovered-field-name";
10
+ readonly FORM_LISTS_ADD: "form-lists:add";
11
+ readonly FORM_LISTS_REMOVE: "form-lists:remove";
12
+ readonly FORM_LISTS_CLEAR: "form-lists:clear";
13
+ readonly SET_EDIT_MODE: "set-edit-mode";
14
+ readonly INCREMENT_OPERATION_INDEX: "increment-operation-index";
15
+ readonly SET_QUICK_EDITING_SUPPORTED: "set-quick-editing-supported";
16
+ readonly SET_QUICK_EDITING_ENABLED: "set-quick-editing-enabled";
17
+ readonly TOGGLE_QUICK_EDITING_ENABLED: "toggle-quick-editing-enabled";
18
+ readonly TOGGLE_EDIT_STATE: "toggle-edit-state";
19
+ readonly SIDEBAR_SET_DISPLAY_STATE: "sidebar:set-display-state";
20
+ readonly SIDEBAR_SET_LOADING_STATE: "sidebar:set-loading-state";
21
+ };
3
22
  type FormListItem = {
4
23
  type: 'document';
5
24
  path: string;
@@ -22,50 +41,56 @@ type Breadcrumb = {
22
41
  namespace: string[];
23
42
  };
24
43
  export type TinaAction = {
25
- type: 'forms:add';
44
+ type: typeof ACTION_TYPES.FORMS_ADD;
26
45
  value: Form;
27
46
  } | {
28
- type: 'forms:remove';
47
+ type: typeof ACTION_TYPES.FORMS_REMOVE;
29
48
  value: string;
30
49
  } | {
31
- type: 'forms:clear';
50
+ type: typeof ACTION_TYPES.FORMS_CLEAR;
32
51
  } | {
33
- type: 'form-lists:add';
52
+ type: typeof ACTION_TYPES.FORM_LISTS_ADD;
34
53
  value: FormList;
35
54
  } | {
36
- type: 'form-lists:remove';
55
+ type: typeof ACTION_TYPES.FORM_LISTS_REMOVE;
37
56
  value: string;
38
57
  } | {
39
- type: 'forms:set-active-form-id';
58
+ type: typeof ACTION_TYPES.FORMS_SET_ACTIVE_FORM_ID;
40
59
  value: string;
41
60
  } | {
42
- type: 'forms:set-active-field-name';
61
+ type: typeof ACTION_TYPES.FORMS_SET_ACTIVE_FIELD_NAME;
43
62
  value: {
44
63
  formId: string;
45
64
  fieldName: string;
46
65
  };
47
66
  } | {
48
- type: 'form-lists:clear';
67
+ type: typeof ACTION_TYPES.FORMS_SET_HOVERED_FIELD_NAME;
68
+ value: {
69
+ formId: string;
70
+ fieldName: string | null;
71
+ };
72
+ } | {
73
+ type: typeof ACTION_TYPES.FORM_LISTS_CLEAR;
49
74
  } | {
50
- type: 'set-edit-mode';
75
+ type: typeof ACTION_TYPES.SET_EDIT_MODE;
51
76
  value: 'visual' | 'basic';
52
77
  } | {
53
- type: 'increment-operation-index';
78
+ type: typeof ACTION_TYPES.INCREMENT_OPERATION_INDEX;
54
79
  } | {
55
- type: 'set-quick-editing-supported';
80
+ type: typeof ACTION_TYPES.SET_QUICK_EDITING_SUPPORTED;
56
81
  value: boolean;
57
82
  } | {
58
- type: 'set-quick-editing-enabled';
83
+ type: typeof ACTION_TYPES.SET_QUICK_EDITING_ENABLED;
59
84
  value?: boolean;
60
85
  } | {
61
- type: 'toggle-quick-editing-enabled';
86
+ type: typeof ACTION_TYPES.TOGGLE_QUICK_EDITING_ENABLED;
62
87
  } | {
63
- type: 'toggle-edit-state';
88
+ type: typeof ACTION_TYPES.TOGGLE_EDIT_STATE;
64
89
  } | {
65
- type: 'sidebar:set-display-state';
90
+ type: typeof ACTION_TYPES.SIDEBAR_SET_DISPLAY_STATE;
66
91
  value: TinaState['sidebarDisplayState'] | 'openOrFull';
67
92
  } | {
68
- type: 'sidebar:set-loading-state';
93
+ type: typeof ACTION_TYPES.SIDEBAR_SET_LOADING_STATE;
69
94
  value: boolean;
70
95
  };
71
96
  export interface TinaState {
@@ -83,6 +108,7 @@ export interface TinaState {
83
108
  forms: {
84
109
  activeFieldName?: string | null;
85
110
  tinaForm: Form;
111
+ hoveringFieldName?: string | null;
86
112
  }[];
87
113
  formLists: FormList[];
88
114
  editingMode: 'visual' | 'basic';
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tinacms",
3
3
  "type": "module",
4
4
  "typings": "dist/index.d.ts",
5
- "version": "0.0.0-af203c6-20251216034843",
5
+ "version": "0.0.0-b0f55b5-20251228232811",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -83,7 +83,7 @@
83
83
  "cmdk": "^1.0.4",
84
84
  "color-string": "^1.9.1",
85
85
  "crypto-js": "^4.2.0",
86
- "date-fns": "2.30.0",
86
+ "date-fns": "4.1.0",
87
87
  "es-toolkit": "^1.42.0",
88
88
  "final-form": "4.20.10",
89
89
  "final-form-arrays": "^3.1.0",
@@ -100,7 +100,7 @@
100
100
  "prop-types": "15.7.2",
101
101
  "react-colorful": "^5.6.1",
102
102
  "react-datetime": "^3.3.1",
103
- "react-day-picker": "^9.11.1",
103
+ "react-day-picker": "^9.13.0",
104
104
  "react-dropzone": "14.2.3",
105
105
  "react-final-form": "^6.5.9",
106
106
  "react-icons": "^5.4.0",
@@ -112,7 +112,7 @@
112
112
  "zod": "^3.24.2",
113
113
  "@tinacms/mdx": "2.0.1",
114
114
  "@tinacms/schema-tools": "2.1.0",
115
- "@tinacms/search": "0.0.0-af203c6-20251216034843"
115
+ "@tinacms/search": "0.0.0-b0f55b5-20251228232811"
116
116
  },
117
117
  "devDependencies": {
118
118
  "@graphql-tools/utils": "^10.8.1",