@tinacms/app 0.0.0-003e348-20251023020516

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.
@@ -0,0 +1,53 @@
1
+ export type PostMessage =
2
+ | {
3
+ type: 'open' | 'close' | 'isEditMode';
4
+ id: string;
5
+ data: object;
6
+ }
7
+ | { type: 'field:selected'; fieldName: string }
8
+ | { type: 'quick-edit'; value: boolean }
9
+ | { type: 'user-select-form'; formId: string }
10
+ | { type: 'url-changed' };
11
+
12
+ export type Payload = {
13
+ id: string;
14
+ variables: object;
15
+ query: string;
16
+ data: object;
17
+ expandedQuery?: string;
18
+ expandedData?: object;
19
+ expandedQueryForResolver?: string;
20
+ };
21
+
22
+ export type SystemInfo = {
23
+ breadcrumbs: string[];
24
+ basename: string;
25
+ filename: string;
26
+ path: string;
27
+ extension: string;
28
+ relativePath: string;
29
+ title?: string | null | undefined;
30
+ template: string;
31
+ // __typename: string
32
+ collection: {
33
+ name: string;
34
+ slug: string;
35
+ label: string;
36
+ path: string;
37
+ format?: string | null | undefined;
38
+ matches?: string | null | undefined;
39
+ // templates?: object
40
+ // fields?: object
41
+ // __typename: string
42
+ };
43
+ };
44
+
45
+ export type Document = {
46
+ _values: Record<string, unknown>;
47
+ _sys: SystemInfo;
48
+ };
49
+
50
+ export type ResolvedDocument = {
51
+ _internalValues: Record<string, unknown>;
52
+ _internalSys: SystemInfo;
53
+ };
@@ -0,0 +1,129 @@
1
+ const charCodeOfDot = '.'.charCodeAt(0);
2
+ const reEscapeChar = /\\(\\)?/g;
3
+ const rePropName = RegExp(
4
+ // Match anything that isn't a dot or bracket.
5
+ '[^.[\\]]+' +
6
+ '|' +
7
+ // Or match property names within brackets.
8
+ '\\[(?:' +
9
+ // Match a non-string expression.
10
+ '([^"\'][^[]*)' +
11
+ '|' +
12
+ // Or match strings (supports escaping characters).
13
+ '(["\'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2' +
14
+ ')\\]' +
15
+ '|' +
16
+ // Or match "" as the space between consecutive dots or empty brackets.
17
+ '(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))',
18
+ 'g'
19
+ );
20
+
21
+ /**
22
+ * Converts `string` to a property path array.
23
+ *
24
+ * @private
25
+ * @param {string} string The string to convert.
26
+ * @returns {Array} Returns the property path array.
27
+ */
28
+ const stringToPath = (string: string) => {
29
+ const result = [];
30
+ if (string.charCodeAt(0) === charCodeOfDot) {
31
+ result.push('');
32
+ }
33
+ string.replace(rePropName, (match, expression, quote, subString) => {
34
+ let key = match;
35
+ if (quote) {
36
+ key = subString.replace(reEscapeChar, '$1');
37
+ } else if (expression) {
38
+ key = expression.trim();
39
+ }
40
+ result.push(key);
41
+ });
42
+ return result;
43
+ };
44
+
45
+ const keysCache: { [key: string]: string[] } = {};
46
+ const keysRegex = /[.[\]]+/;
47
+
48
+ const toPath = (key: string): string[] => {
49
+ if (key === null || key === undefined || !key.length) {
50
+ return [];
51
+ }
52
+ if (typeof key !== 'string') {
53
+ throw new Error('toPath() expects a string');
54
+ }
55
+ if (keysCache[key] == null) {
56
+ /**
57
+ * The following patch fixes issue 456, introduced since v4.20.3:
58
+ *
59
+ * Before v4.20.3, i.e. in v4.20.2, a `key` like 'choices[]' would map to ['choices']
60
+ * (e.g. an array of choices used where 'choices[]' is name attribute of an input of type checkbox).
61
+ *
62
+ * Since v4.20.3, a `key` like 'choices[]' would map to ['choices', ''] which is wrong and breaks
63
+ * this kind of inputs e.g. in React.
64
+ *
65
+ * v4.20.3 introduced an unwanted breaking change, this patch fixes it, see the issue at the link below.
66
+ *
67
+ * @see https://github.com/final-form/final-form/issues/456
68
+ */
69
+ if (key.endsWith('[]')) {
70
+ // v4.20.2 (a `key` like 'choices[]' should map to ['choices'], which is fine).
71
+ keysCache[key] = key.split(keysRegex).filter(Boolean);
72
+ } else {
73
+ // v4.20.3 (a `key` like 'choices[]' maps to ['choices', ''], which breaks applications relying on inputs like `<input type="checkbox" name="choices[]" />`).
74
+ keysCache[key] = stringToPath(key);
75
+ }
76
+ }
77
+ return keysCache[key];
78
+ };
79
+ export const getDeepestMetadata = (state: Object, complexKey: string): any => {
80
+ // Intentionally using iteration rather than recursion
81
+ const path = toPath(complexKey);
82
+ let current: any = state;
83
+ let metadata: any;
84
+ for (let i = 0; i < path.length; i++) {
85
+ const key = path[i];
86
+ if (
87
+ current === undefined ||
88
+ current === null ||
89
+ typeof current !== 'object' ||
90
+ (Array.isArray(current) && isNaN(Number(key)))
91
+ ) {
92
+ return undefined;
93
+ }
94
+ const value = current[key];
95
+ if (value?._tina_metadata) {
96
+ // We're at a reference field, we don't want to select the
97
+ // reference form, just the reference select field
98
+ if (complexKey === value._tina_metadata?.prefix && metadata) {
99
+ } else {
100
+ metadata = value._tina_metadata;
101
+ }
102
+ }
103
+ current = value;
104
+ }
105
+ return metadata;
106
+ };
107
+ export const getFormAndFieldNameFromMetadata = (
108
+ object: object,
109
+ eventFieldName: string
110
+ ) => {
111
+ const metadata = getDeepestMetadata(object, eventFieldName);
112
+
113
+ if (!metadata) {
114
+ console.warn(
115
+ '[getFormAndFieldNameFromMetadata] No metadata found for:',
116
+ eventFieldName
117
+ );
118
+ return { formId: undefined, fieldName: undefined };
119
+ }
120
+
121
+ const { id: formId, prefix } = metadata;
122
+ const prefixLength = prefix?.length ?? 0;
123
+ const localFieldName = eventFieldName.slice(prefixLength + 1);
124
+
125
+ return {
126
+ formId,
127
+ fieldName: localFieldName,
128
+ };
129
+ };
package/src/main.tsx ADDED
@@ -0,0 +1,12 @@
1
+ import React from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App';
4
+ import './index.css';
5
+
6
+ const container = document.getElementById('root');
7
+ const root = createRoot(container!);
8
+ root.render(
9
+ <React.StrictMode>
10
+ <App />
11
+ </React.StrictMode>
12
+ );
@@ -0,0 +1,233 @@
1
+ body {
2
+ line-height: 1.5;
3
+ -webkit-text-size-adjust: 100%;
4
+ -moz-tab-size: 4;
5
+ tab-size: 4;
6
+
7
+ *,
8
+ ::before,
9
+ ::after {
10
+ box-sizing: border-box;
11
+ border-width: 0;
12
+ border-style: solid;
13
+ border-color: transparent;
14
+ }
15
+
16
+ ::before,
17
+ ::after {
18
+ --tw-content: "";
19
+ }
20
+
21
+ hr {
22
+ height: 0; /* 1 */
23
+ color: inherit; /* 2 */
24
+ border-top-width: 1px; /* 3 */
25
+ }
26
+
27
+ abbr:where([title]) {
28
+ text-decoration: underline dotted;
29
+ }
30
+
31
+ h1,
32
+ h2,
33
+ h3,
34
+ h4,
35
+ h5,
36
+ h6 {
37
+ font-size: inherit;
38
+ font-weight: inherit;
39
+ }
40
+
41
+ a {
42
+ color: inherit;
43
+ text-decoration: inherit;
44
+ }
45
+
46
+ b,
47
+ strong {
48
+ font-weight: bolder;
49
+ }
50
+
51
+ code,
52
+ kbd,
53
+ samp,
54
+ pre {
55
+ font-family: theme(
56
+ "fontFamily.mono",
57
+ ui-monospace,
58
+ SFMono-Regular,
59
+ Menlo,
60
+ Monaco,
61
+ Consolas,
62
+ "Liberation Mono",
63
+ "Courier New",
64
+ monospace
65
+ ); /* 1 */
66
+ font-size: 1em; /* 2 */
67
+ }
68
+
69
+ small {
70
+ font-size: 80%;
71
+ }
72
+
73
+ sub,
74
+ sup {
75
+ font-size: 75%;
76
+ line-height: 0;
77
+ position: relative;
78
+ vertical-align: baseline;
79
+ }
80
+
81
+ sub {
82
+ bottom: -0.25em;
83
+ }
84
+
85
+ sup {
86
+ top: -0.5em;
87
+ }
88
+
89
+ table {
90
+ text-indent: 0; /* 1 */
91
+ border-color: inherit; /* 2 */
92
+ border-collapse: collapse; /* 3 */
93
+ }
94
+
95
+ button,
96
+ input,
97
+ optgroup,
98
+ select,
99
+ textarea {
100
+ font-family: inherit; /* 1 */
101
+ font-size: 100%; /* 1 */
102
+ line-height: inherit; /* 1 */
103
+ color: inherit; /* 1 */
104
+ margin: 0; /* 2 */
105
+ padding: 0; /* 3 */
106
+ }
107
+
108
+ button,
109
+ select {
110
+ text-transform: none;
111
+ }
112
+
113
+ button,
114
+ [type="button"],
115
+ [type="reset"],
116
+ [type="submit"] {
117
+ -webkit-appearance: button; /* 1 */
118
+ background-image: none; /* 2 */
119
+ }
120
+
121
+ :-moz-focusring {
122
+ outline: auto;
123
+ }
124
+
125
+ :-moz-ui-invalid {
126
+ box-shadow: none;
127
+ }
128
+
129
+ progress {
130
+ vertical-align: baseline;
131
+ }
132
+
133
+ ::-webkit-inner-spin-button,
134
+ ::-webkit-outer-spin-button {
135
+ height: auto;
136
+ }
137
+
138
+ [type="search"] {
139
+ -webkit-appearance: textfield; /* 1 */
140
+ outline-offset: -2px; /* 2 */
141
+ }
142
+
143
+ ::-webkit-search-decoration {
144
+ -webkit-appearance: none;
145
+ }
146
+
147
+ ::-webkit-file-upload-button {
148
+ -webkit-appearance: button; /* 1 */
149
+ font: inherit; /* 2 */
150
+ }
151
+
152
+ summary {
153
+ display: list-item;
154
+ }
155
+
156
+ blockquote,
157
+ dl,
158
+ dd,
159
+ h1,
160
+ h2,
161
+ h3,
162
+ h4,
163
+ h5,
164
+ h6,
165
+ hr,
166
+ figure,
167
+ p,
168
+ pre {
169
+ margin: 0;
170
+ }
171
+
172
+ fieldset {
173
+ margin: 0;
174
+ padding: 0;
175
+ }
176
+
177
+ legend {
178
+ padding: 0;
179
+ }
180
+
181
+ ol,
182
+ ul,
183
+ menu {
184
+ list-style: none;
185
+ margin: 0;
186
+ padding: 0;
187
+ }
188
+
189
+ li:before {
190
+ display: none;
191
+ }
192
+
193
+ textarea {
194
+ resize: vertical;
195
+ }
196
+
197
+ input::placeholder,
198
+ textarea::placeholder {
199
+ opacity: 1; /* 1 */
200
+ color: theme("colors.gray.400", #9ca3af); /* 2 */
201
+ }
202
+
203
+ button,
204
+ [role="button"] {
205
+ cursor: pointer;
206
+ }
207
+
208
+ :disabled {
209
+ cursor: default;
210
+ }
211
+
212
+ img,
213
+ svg,
214
+ video,
215
+ canvas,
216
+ audio,
217
+ iframe,
218
+ embed,
219
+ object {
220
+ display: block; /* 1 */
221
+ vertical-align: middle; /* 2 */
222
+ }
223
+
224
+ img,
225
+ video {
226
+ max-width: 100%;
227
+ height: auto;
228
+ }
229
+
230
+ [hidden] {
231
+ display: none;
232
+ }
233
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+
3
+ */
4
+ import React from 'react';
5
+ import { defineConfig } from 'tinacms';
6
+ import { useGraphQLReducer } from './lib/graphql-reducer';
7
+
8
+ type Config = Parameters<typeof defineConfig>[0];
9
+
10
+ export const Preview = (
11
+ props: Config & {
12
+ url: string;
13
+ iframeRef: React.MutableRefObject<HTMLIFrameElement>;
14
+ }
15
+ ) => {
16
+ useGraphQLReducer(props.iframeRef, props.url);
17
+
18
+ return (
19
+ <iframe
20
+ data-test='tina-iframe'
21
+ id='tina-iframe'
22
+ ref={props.iframeRef}
23
+ className='h-screen w-full bg-white'
24
+ src={props.url}
25
+ />
26
+ );
27
+ };
@@ -0,0 +1,8 @@
1
+ /**
2
+
3
+ */
4
+
5
+ /// <reference types="vite/client" />
6
+ declare const __API_URL__: string;
7
+ declare const __BASE_PATH__: string;
8
+ declare const __TINA_GRAPHQL_VERSION__: string;
package/tsconfig.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "useDefineForClassFields": true,
5
+ "lib": [
6
+ "DOM",
7
+ "DOM.Iterable",
8
+ "ESNext"
9
+ ],
10
+ "allowJs": false,
11
+ "skipLibCheck": true,
12
+ "esModuleInterop": false,
13
+ "allowSyntheticDefaultImports": true,
14
+ "strict": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "module": "ESNext",
17
+ "moduleResolution": "Node",
18
+ "resolveJsonModule": true,
19
+ "isolatedModules": true,
20
+ "noEmit": true,
21
+ "jsx": "react-jsx"
22
+ },
23
+ "exclude": [
24
+ "node_modules",
25
+ "**/node_modules/*"
26
+ ],
27
+ "references": [
28
+ {
29
+ "path": "./tsconfig.node.json"
30
+ }
31
+ ]
32
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "allowSyntheticDefaultImports": true
7
+ },
8
+ "include": ["vite.config.ts"],
9
+ "exclude": ["node_modules", "**/node_modules/*"]
10
+ }