@einblick/sdk 0.5.3 → 0.5.5
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/client.d.ts +18 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -1
- package/dist/client.test.js +18 -0
- package/dist/client.test.js.map +1 -1
- package/dist/codegen.d.ts.map +1 -1
- package/dist/codegen.js +52 -2
- package/dist/codegen.js.map +1 -1
- package/dist/codegen.test.js +49 -0
- package/dist/codegen.test.js.map +1 -1
- package/dist/react/i18n.d.ts +5 -0
- package/dist/react/i18n.d.ts.map +1 -1
- package/dist/react/i18n.js +10 -0
- package/dist/react/i18n.js.map +1 -1
- package/dist/react/runtime.d.ts.map +1 -1
- package/dist/react/runtime.js +723 -357
- package/dist/react/runtime.js.map +1 -1
- package/dist/react/shared.d.ts +2 -0
- package/dist/react/shared.d.ts.map +1 -1
- package/dist/react/shared.js +9 -5
- package/dist/react/shared.js.map +1 -1
- package/package.json +3 -3
package/dist/react/runtime.js
CHANGED
|
@@ -1,98 +1,10 @@
|
|
|
1
|
-
import { EINBLICK_AUTH_SOURCE, EINBLICK_BRIDGE_SOURCE, EINBLICK_EDITOR_SOURCE, EINBLICK_PARENT_SOURCE, formatValueForText, isEditSessionExpired, readBindingFromElement, resolveAppOrigin, serializeBindingValue, shouldUseInlineEditor, } from './shared.js';
|
|
1
|
+
import { EINBLICK_AUTH_SOURCE, EINBLICK_BRIDGE_SOURCE, EINBLICK_EDITOR_SOURCE, EINBLICK_PARENT_SOURCE, formatValueForText, isMediaEditableBinding, isEditSessionExpired, readBindingFromElement, resolveAppOrigin, serializeBindingValue, shouldUseInlineEditor, } from './shared.js';
|
|
2
2
|
import { getEinblickSdkMessages, resolveEinblickSdkLocale, } from './i18n.js';
|
|
3
3
|
const RUNTIME_STYLE_ID = 'einblick-edit-runtime-styles';
|
|
4
|
-
const RELATIVE_POSITION_SENTINEL = '__einblick_runtime_original_static__';
|
|
5
4
|
const EINBLICK_ACCENT_RGB = '250, 105, 62';
|
|
6
5
|
const EINBLICK_ACCENT_COLOR = `rgb(${EINBLICK_ACCENT_RGB})`;
|
|
7
6
|
const EINBLICK_ACCENT_BORDER = `rgba(${EINBLICK_ACCENT_RGB}, 0.92)`;
|
|
8
7
|
const CONTROL_BAR_HEIGHT = 44;
|
|
9
|
-
const INLINE_EDITOR_SHADOW_STYLES = `
|
|
10
|
-
:host {
|
|
11
|
-
display: block;
|
|
12
|
-
width: 100%;
|
|
13
|
-
color: rgb(15, 23, 42);
|
|
14
|
-
font-family: system-ui, sans-serif;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
*, *::before, *::after {
|
|
18
|
-
box-sizing: border-box;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
[data-einblick-inline-shell] {
|
|
22
|
-
display: flex;
|
|
23
|
-
flex-direction: column;
|
|
24
|
-
gap: 0.75rem;
|
|
25
|
-
width: 100%;
|
|
26
|
-
color: rgb(15, 23, 42);
|
|
27
|
-
font-family: system-ui, sans-serif;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
[data-einblick-inline-control],
|
|
31
|
-
[data-einblick-inline-textarea],
|
|
32
|
-
[data-einblick-inline-select] {
|
|
33
|
-
appearance: none;
|
|
34
|
-
width: 100%;
|
|
35
|
-
min-width: 0;
|
|
36
|
-
margin: 0;
|
|
37
|
-
border-radius: 0.9rem;
|
|
38
|
-
border: 1px solid rgba(148, 163, 184, 0.6);
|
|
39
|
-
background: rgb(255, 255, 255);
|
|
40
|
-
padding: 0.75rem 0.9rem;
|
|
41
|
-
font: 400 0.95rem/1.5 system-ui, sans-serif;
|
|
42
|
-
color: rgb(15, 23, 42);
|
|
43
|
-
box-shadow: none;
|
|
44
|
-
letter-spacing: normal;
|
|
45
|
-
text-transform: none;
|
|
46
|
-
text-indent: 0;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
[data-einblick-inline-control]:focus,
|
|
50
|
-
[data-einblick-inline-textarea]:focus,
|
|
51
|
-
[data-einblick-inline-select]:focus {
|
|
52
|
-
outline: 2px solid rgba(${EINBLICK_ACCENT_RGB}, 0.3);
|
|
53
|
-
outline-offset: 2px;
|
|
54
|
-
border-color: ${EINBLICK_ACCENT_COLOR};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
[data-einblick-inline-textarea] {
|
|
58
|
-
min-height: 8rem;
|
|
59
|
-
resize: vertical;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
[data-einblick-inline-actions] {
|
|
63
|
-
display: flex;
|
|
64
|
-
flex-wrap: wrap;
|
|
65
|
-
justify-content: flex-end;
|
|
66
|
-
gap: 0.5rem;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
[data-einblick-edit-action] {
|
|
70
|
-
appearance: none;
|
|
71
|
-
border: 1px solid rgba(148, 163, 184, 0.6);
|
|
72
|
-
background: rgb(255, 255, 255);
|
|
73
|
-
color: rgb(15, 23, 42);
|
|
74
|
-
border-radius: 999px;
|
|
75
|
-
padding: 0.65rem 1rem;
|
|
76
|
-
font: 600 0.9rem/1 system-ui, sans-serif;
|
|
77
|
-
cursor: pointer;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
[data-einblick-edit-action]:disabled {
|
|
81
|
-
cursor: wait;
|
|
82
|
-
opacity: 0.72;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
[data-einblick-edit-action="primary"] {
|
|
86
|
-
border-color: ${EINBLICK_ACCENT_COLOR};
|
|
87
|
-
background: ${EINBLICK_ACCENT_COLOR};
|
|
88
|
-
color: rgb(255, 255, 255);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
[data-einblick-inline-error] {
|
|
92
|
-
font: 400 0.85rem/1.5 system-ui, sans-serif;
|
|
93
|
-
color: rgb(185, 28, 28);
|
|
94
|
-
}
|
|
95
|
-
`;
|
|
96
8
|
function getBrandBadgeSvg(size) {
|
|
97
9
|
return `
|
|
98
10
|
<svg viewBox="0 0 40 40" width="${size}" height="${size}" aria-hidden="true" focusable="false">
|
|
@@ -135,6 +47,32 @@ function createRandomId() {
|
|
|
135
47
|
}
|
|
136
48
|
return Math.random().toString(36).slice(2);
|
|
137
49
|
}
|
|
50
|
+
function stopEvent(event) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
event.stopPropagation();
|
|
53
|
+
}
|
|
54
|
+
function clamp(value, min, max) {
|
|
55
|
+
return Math.min(Math.max(value, min), max);
|
|
56
|
+
}
|
|
57
|
+
function isTextInputEvent(event) {
|
|
58
|
+
return (event.key.length === 1 ||
|
|
59
|
+
event.key === 'Enter' ||
|
|
60
|
+
event.key === 'Backspace' ||
|
|
61
|
+
event.key === 'Delete');
|
|
62
|
+
}
|
|
63
|
+
function fieldTypeAllowsMultiline(binding) {
|
|
64
|
+
return binding.fieldType === 'text' || binding.fieldType === 'markdown';
|
|
65
|
+
}
|
|
66
|
+
function getMediaAcceptValue(binding) {
|
|
67
|
+
return binding.fieldType === 'image' || binding.fieldType === 'images'
|
|
68
|
+
? 'image/*'
|
|
69
|
+
: '';
|
|
70
|
+
}
|
|
71
|
+
function getMediaActionLabel(binding, messages) {
|
|
72
|
+
return binding.fieldType === 'image' || binding.fieldType === 'images'
|
|
73
|
+
? messages.actions.replaceImage
|
|
74
|
+
: messages.actions.replaceFile;
|
|
75
|
+
}
|
|
138
76
|
function injectRuntimeStyles() {
|
|
139
77
|
if (typeof document === 'undefined') {
|
|
140
78
|
return;
|
|
@@ -149,15 +87,27 @@ function injectRuntimeStyles() {
|
|
|
149
87
|
scroll-margin-top: 96px;
|
|
150
88
|
}
|
|
151
89
|
|
|
152
|
-
[data-einblick-
|
|
153
|
-
|
|
154
|
-
|
|
90
|
+
[data-einblick-edit-outline],
|
|
91
|
+
[data-einblick-edit-field-outline] {
|
|
92
|
+
position: fixed;
|
|
93
|
+
z-index: 2147483643;
|
|
94
|
+
box-sizing: border-box;
|
|
95
|
+
pointer-events: none;
|
|
96
|
+
border-radius: var(--einblick-overlay-radius, 0.35rem);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
[data-einblick-edit-outline] {
|
|
100
|
+
border: 1px solid ${EINBLICK_ACCENT_BORDER};
|
|
101
|
+
box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.7);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
[data-einblick-edit-field-outline] {
|
|
105
|
+
border: 1px dashed rgba(${EINBLICK_ACCENT_RGB}, 0.78);
|
|
106
|
+
background: rgba(${EINBLICK_ACCENT_RGB}, 0.04);
|
|
155
107
|
}
|
|
156
108
|
|
|
157
109
|
[data-einblick-edit-controls] {
|
|
158
|
-
position:
|
|
159
|
-
top: 0.5rem;
|
|
160
|
-
right: 0.5rem;
|
|
110
|
+
position: fixed;
|
|
161
111
|
z-index: 2147483644;
|
|
162
112
|
display: inline-flex;
|
|
163
113
|
align-items: center;
|
|
@@ -165,7 +115,8 @@ function injectRuntimeStyles() {
|
|
|
165
115
|
}
|
|
166
116
|
|
|
167
117
|
[data-einblick-edit-button],
|
|
168
|
-
[data-einblick-create-button]
|
|
118
|
+
[data-einblick-create-button],
|
|
119
|
+
[data-einblick-field-button] {
|
|
169
120
|
border: 1px solid ${EINBLICK_ACCENT_COLOR};
|
|
170
121
|
border-radius: 999px;
|
|
171
122
|
padding: 0.3rem 0.7rem;
|
|
@@ -175,22 +126,46 @@ function injectRuntimeStyles() {
|
|
|
175
126
|
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.16);
|
|
176
127
|
}
|
|
177
128
|
|
|
178
|
-
[data-einblick-edit-button]
|
|
129
|
+
[data-einblick-edit-button],
|
|
130
|
+
[data-einblick-field-button="media"] {
|
|
179
131
|
background: ${EINBLICK_ACCENT_COLOR};
|
|
180
132
|
color: rgb(255, 255, 255);
|
|
181
133
|
}
|
|
182
134
|
|
|
183
|
-
[data-einblick-
|
|
135
|
+
[data-einblick-edit-button] {
|
|
136
|
+
padding: 0.42rem 0.85rem;
|
|
137
|
+
font-size: 13px;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
[data-einblick-create-button],
|
|
141
|
+
[data-einblick-field-button="text"] {
|
|
184
142
|
background: rgb(255, 255, 255);
|
|
185
143
|
color: ${EINBLICK_ACCENT_COLOR};
|
|
186
144
|
}
|
|
187
145
|
|
|
188
146
|
[data-einblick-edit-button]:hover,
|
|
189
|
-
[data-einblick-create-button]:hover
|
|
147
|
+
[data-einblick-create-button]:hover,
|
|
148
|
+
[data-einblick-field-button]:hover {
|
|
190
149
|
background: rgb(255, 255, 255);
|
|
191
150
|
color: ${EINBLICK_ACCENT_COLOR};
|
|
192
151
|
}
|
|
193
152
|
|
|
153
|
+
[data-einblick-field-overlays] {
|
|
154
|
+
position: fixed;
|
|
155
|
+
inset: 0;
|
|
156
|
+
z-index: 2147483644;
|
|
157
|
+
pointer-events: none;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
[data-einblick-field-button] {
|
|
161
|
+
position: fixed;
|
|
162
|
+
z-index: 2147483644;
|
|
163
|
+
pointer-events: auto;
|
|
164
|
+
padding: 0.24rem 0.58rem;
|
|
165
|
+
font-size: 11px;
|
|
166
|
+
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.12);
|
|
167
|
+
}
|
|
168
|
+
|
|
194
169
|
[data-einblick-brand-badge] {
|
|
195
170
|
position: fixed;
|
|
196
171
|
right: 1rem;
|
|
@@ -340,64 +315,11 @@ function injectRuntimeStyles() {
|
|
|
340
315
|
}
|
|
341
316
|
|
|
342
317
|
[data-einblick-inline-editing="true"] {
|
|
343
|
-
|
|
344
|
-
outline-offset: 4px;
|
|
345
|
-
border-radius: 1rem;
|
|
346
|
-
background: rgba(${EINBLICK_ACCENT_RGB}, 0.08);
|
|
347
|
-
padding: 0.8rem;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
[data-einblick-inline-shell] {
|
|
351
|
-
display: flex;
|
|
352
|
-
flex-direction: column;
|
|
353
|
-
gap: 0.75rem;
|
|
354
|
-
width: 100%;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
[data-einblick-inline-control],
|
|
358
|
-
[data-einblick-inline-textarea],
|
|
359
|
-
[data-einblick-inline-select] {
|
|
360
|
-
width: 100%;
|
|
361
|
-
min-width: 0;
|
|
362
|
-
border-radius: 0.9rem;
|
|
363
|
-
border: 1px solid rgba(148, 163, 184, 0.6);
|
|
364
|
-
background: rgb(255, 255, 255);
|
|
365
|
-
padding: 0.75rem 0.9rem;
|
|
366
|
-
font: 400 0.95rem/1.5 system-ui, sans-serif;
|
|
367
|
-
color: inherit;
|
|
368
|
-
box-sizing: border-box;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
[data-einblick-inline-textarea] {
|
|
372
|
-
min-height: 8rem;
|
|
373
|
-
resize: vertical;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
[data-einblick-inline-actions] {
|
|
377
|
-
display: flex;
|
|
378
|
-
flex-wrap: wrap;
|
|
379
|
-
justify-content: flex-end;
|
|
380
|
-
gap: 0.5rem;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
[data-einblick-edit-action] {
|
|
384
|
-
border-radius: 999px;
|
|
385
|
-
border: 1px solid rgba(148, 163, 184, 0.6);
|
|
386
|
-
background: rgb(255, 255, 255);
|
|
387
|
-
padding: 0.65rem 1rem;
|
|
388
|
-
font: 600 0.9rem/1 system-ui, sans-serif;
|
|
389
|
-
cursor: pointer;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
[data-einblick-edit-action="primary"] {
|
|
393
|
-
border-color: ${EINBLICK_ACCENT_COLOR};
|
|
394
|
-
background: ${EINBLICK_ACCENT_COLOR};
|
|
395
|
-
color: rgb(255, 255, 255);
|
|
318
|
+
cursor: text;
|
|
396
319
|
}
|
|
397
320
|
|
|
398
|
-
[data-einblick-inline-
|
|
399
|
-
|
|
400
|
-
color: rgb(185, 28, 28);
|
|
321
|
+
[data-einblick-inline-editing="true"]:focus {
|
|
322
|
+
outline: none;
|
|
401
323
|
}
|
|
402
324
|
|
|
403
325
|
[data-einblick-editor-backdrop] {
|
|
@@ -485,9 +407,12 @@ class EinblickEditRuntime {
|
|
|
485
407
|
bridgeFrame = null;
|
|
486
408
|
bridgePingInterval = null;
|
|
487
409
|
activeElement = null;
|
|
410
|
+
activeOutline = null;
|
|
488
411
|
hoverButtonContainer = null;
|
|
489
412
|
hoverButton = null;
|
|
490
413
|
hoverCreateButton = null;
|
|
414
|
+
quickFieldOverlayContainer = null;
|
|
415
|
+
quickFieldOverlays = [];
|
|
491
416
|
brandBadge = null;
|
|
492
417
|
brandPopover = null;
|
|
493
418
|
brandPopoverSiteName = null;
|
|
@@ -510,6 +435,7 @@ class EinblickEditRuntime {
|
|
|
510
435
|
currentInlineTarget = null;
|
|
511
436
|
currentInlineBinding = null;
|
|
512
437
|
currentInlineRestoreState = null;
|
|
438
|
+
inlineSaveInFlight = null;
|
|
513
439
|
editorBackdrop = null;
|
|
514
440
|
editorNonce = null;
|
|
515
441
|
editorTarget = null;
|
|
@@ -519,7 +445,9 @@ class EinblickEditRuntime {
|
|
|
519
445
|
navigatorNonce = null;
|
|
520
446
|
navigatorOpen = false;
|
|
521
447
|
pendingInlineSaves = new Map();
|
|
448
|
+
pendingInlineMediaSaves = new Map();
|
|
522
449
|
pendingLogin = null;
|
|
450
|
+
pendingLogout = null;
|
|
523
451
|
constructor(options) {
|
|
524
452
|
this.siteKey = options.siteKey;
|
|
525
453
|
this.appOrigin = resolveAppOrigin(options.appOrigin, options.appUrl);
|
|
@@ -603,15 +531,23 @@ class EinblickEditRuntime {
|
|
|
603
531
|
this.mutationObserver?.disconnect();
|
|
604
532
|
this.mutationObserver = null;
|
|
605
533
|
this.clearBridgePing();
|
|
534
|
+
if (this.pendingLogout) {
|
|
535
|
+
window.clearTimeout(this.pendingLogout.timeoutId);
|
|
536
|
+
this.pendingLogout.resolve(false);
|
|
537
|
+
this.pendingLogout = null;
|
|
538
|
+
}
|
|
606
539
|
this.bridgeFrame?.remove();
|
|
607
540
|
this.bridgeFrame = null;
|
|
608
541
|
this.setActiveElement(null);
|
|
542
|
+
this.activeOutline?.remove();
|
|
543
|
+
this.activeOutline = null;
|
|
609
544
|
this.hoverButtonContainer?.remove();
|
|
610
545
|
this.hoverButtonContainer = null;
|
|
611
546
|
this.hoverButton?.remove();
|
|
612
547
|
this.hoverButton = null;
|
|
613
548
|
this.hoverCreateButton?.remove();
|
|
614
549
|
this.hoverCreateButton = null;
|
|
550
|
+
this.clearQuickFieldOverlays();
|
|
615
551
|
this.brandBadge?.remove();
|
|
616
552
|
this.brandBadge = null;
|
|
617
553
|
this.brandPopover?.remove();
|
|
@@ -645,6 +581,11 @@ class EinblickEditRuntime {
|
|
|
645
581
|
pending.reject(new Error(this.messages.errors.editingInterrupted));
|
|
646
582
|
}
|
|
647
583
|
this.pendingInlineSaves.clear();
|
|
584
|
+
for (const pending of this.pendingInlineMediaSaves.values()) {
|
|
585
|
+
window.clearTimeout(pending.timeoutId);
|
|
586
|
+
pending.reject(new Error(this.messages.errors.editingInterrupted));
|
|
587
|
+
}
|
|
588
|
+
this.pendingInlineMediaSaves.clear();
|
|
648
589
|
this.setState({
|
|
649
590
|
enabled: true,
|
|
650
591
|
active: false,
|
|
@@ -656,7 +597,7 @@ class EinblickEditRuntime {
|
|
|
656
597
|
}
|
|
657
598
|
refreshLocalizedUi() {
|
|
658
599
|
if (this.hoverButton) {
|
|
659
|
-
this.
|
|
600
|
+
this.updateHoverButtonLabel();
|
|
660
601
|
}
|
|
661
602
|
if (this.hoverCreateButton) {
|
|
662
603
|
this.hoverCreateButton.textContent = this.messages.actions.addRecord;
|
|
@@ -677,6 +618,7 @@ class EinblickEditRuntime {
|
|
|
677
618
|
}
|
|
678
619
|
this.updateBrandPopover();
|
|
679
620
|
this.updateBottomBar();
|
|
621
|
+
this.updateHoverChrome();
|
|
680
622
|
}
|
|
681
623
|
async login(options) {
|
|
682
624
|
if (typeof window === 'undefined') {
|
|
@@ -738,13 +680,24 @@ class EinblickEditRuntime {
|
|
|
738
680
|
this.setActiveElement(null);
|
|
739
681
|
return;
|
|
740
682
|
}
|
|
683
|
+
if (this.isSdkChromeTarget(event.target)) {
|
|
684
|
+
this.updateHoverChrome();
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
741
687
|
const target = this.findEditableTarget(event.target);
|
|
742
688
|
this.setActiveElement(target);
|
|
743
689
|
};
|
|
744
690
|
handleViewportChange = () => {
|
|
691
|
+
if (this.currentInlineTarget?.isConnected) {
|
|
692
|
+
this.ensureActiveOutline();
|
|
693
|
+
this.positionOutline(this.activeOutline, this.currentInlineTarget);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
745
696
|
if (this.activeElement && !this.activeElement.isConnected) {
|
|
746
697
|
this.setActiveElement(null);
|
|
698
|
+
return;
|
|
747
699
|
}
|
|
700
|
+
this.updateHoverChrome();
|
|
748
701
|
};
|
|
749
702
|
handleWindowBlur = () => {
|
|
750
703
|
this.setBrandPopoverOpen(false);
|
|
@@ -785,8 +738,7 @@ class EinblickEditRuntime {
|
|
|
785
738
|
this.setActiveElement(null);
|
|
786
739
|
};
|
|
787
740
|
handleHoverButtonClick = (event) => {
|
|
788
|
-
event
|
|
789
|
-
event.stopPropagation();
|
|
741
|
+
stopEvent(event);
|
|
790
742
|
if (!this.activeElement) {
|
|
791
743
|
return;
|
|
792
744
|
}
|
|
@@ -794,11 +746,14 @@ class EinblickEditRuntime {
|
|
|
794
746
|
if (!binding) {
|
|
795
747
|
return;
|
|
796
748
|
}
|
|
749
|
+
if (binding.fieldKey && isMediaEditableBinding(binding)) {
|
|
750
|
+
this.openMediaPicker(this.activeElement, binding, this.hoverButton);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
797
753
|
this.openEditor(binding, this.activeElement);
|
|
798
754
|
};
|
|
799
755
|
handleCreateButtonClick = (event) => {
|
|
800
|
-
event
|
|
801
|
-
event.stopPropagation();
|
|
756
|
+
stopEvent(event);
|
|
802
757
|
if (!this.activeElement) {
|
|
803
758
|
return;
|
|
804
759
|
}
|
|
@@ -815,6 +770,16 @@ class EinblickEditRuntime {
|
|
|
815
770
|
this.setBottomBarMenuOpen(false);
|
|
816
771
|
return;
|
|
817
772
|
}
|
|
773
|
+
if (this.currentInlineTarget) {
|
|
774
|
+
if (this.currentInlineTarget.contains(target) ||
|
|
775
|
+
this.isSdkChromeTarget(target)) {
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
event.preventDefault();
|
|
779
|
+
event.stopImmediatePropagation();
|
|
780
|
+
void this.commitInlineEditor();
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
818
783
|
if (this.isBrandPopoverOpen() &&
|
|
819
784
|
!this.brandBadge?.contains(target) &&
|
|
820
785
|
!this.brandPopover?.contains(target)) {
|
|
@@ -889,6 +854,35 @@ class EinblickEditRuntime {
|
|
|
889
854
|
}
|
|
890
855
|
return;
|
|
891
856
|
}
|
|
857
|
+
if (message.type === 'logout-result') {
|
|
858
|
+
const pending = this.pendingLogout;
|
|
859
|
+
if (!pending || pending.requestId !== message.requestId) {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
window.clearTimeout(pending.timeoutId);
|
|
863
|
+
this.pendingLogout = null;
|
|
864
|
+
if (!message.ok && message.message) {
|
|
865
|
+
this.callbacks.onError?.(message.message);
|
|
866
|
+
}
|
|
867
|
+
pending.resolve(message.ok);
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
if (message.type === 'inline-media-result') {
|
|
871
|
+
const pending = this.pendingInlineMediaSaves.get(message.requestId);
|
|
872
|
+
if (!pending) {
|
|
873
|
+
return;
|
|
874
|
+
}
|
|
875
|
+
this.pendingInlineMediaSaves.delete(message.requestId);
|
|
876
|
+
window.clearTimeout(pending.timeoutId);
|
|
877
|
+
if (message.ok) {
|
|
878
|
+
pending.resolve({ fileId: message.fileId, value: message.value });
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
const error = new Error(message.error || this.messages.errors.saveFieldFailed);
|
|
882
|
+
pending.reject(error);
|
|
883
|
+
this.callbacks.onError?.(error.message);
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
892
886
|
const pending = this.pendingInlineSaves.get(message.requestId);
|
|
893
887
|
if (!pending) {
|
|
894
888
|
return;
|
|
@@ -1060,16 +1054,23 @@ class EinblickEditRuntime {
|
|
|
1060
1054
|
container.hidden = true;
|
|
1061
1055
|
const editButton = document.createElement('button');
|
|
1062
1056
|
editButton.type = 'button';
|
|
1063
|
-
editButton.textContent = this.messages.actions.
|
|
1057
|
+
editButton.textContent = this.messages.actions.editElement;
|
|
1064
1058
|
editButton.setAttribute('data-einblick-edit-button', 'true');
|
|
1059
|
+
editButton.addEventListener('pointerdown', (event) => {
|
|
1060
|
+
event.stopPropagation();
|
|
1061
|
+
});
|
|
1065
1062
|
editButton.addEventListener('click', this.handleHoverButtonClick);
|
|
1066
1063
|
const createButton = document.createElement('button');
|
|
1067
1064
|
createButton.type = 'button';
|
|
1068
1065
|
createButton.textContent = this.messages.actions.addRecord;
|
|
1069
1066
|
createButton.hidden = true;
|
|
1070
1067
|
createButton.setAttribute('data-einblick-create-button', 'true');
|
|
1068
|
+
createButton.addEventListener('pointerdown', (event) => {
|
|
1069
|
+
event.stopPropagation();
|
|
1070
|
+
});
|
|
1071
1071
|
createButton.addEventListener('click', this.handleCreateButtonClick);
|
|
1072
1072
|
container.append(editButton, createButton);
|
|
1073
|
+
document.body.append(container);
|
|
1073
1074
|
this.hoverButtonContainer = container;
|
|
1074
1075
|
this.hoverButton = editButton;
|
|
1075
1076
|
this.hoverCreateButton = createButton;
|
|
@@ -1621,6 +1622,48 @@ class EinblickEditRuntime {
|
|
|
1621
1622
|
siteName: this.state.siteName,
|
|
1622
1623
|
errorMessage: undefined,
|
|
1623
1624
|
});
|
|
1625
|
+
const loggedOutViaBridge = await this.requestBridgeLogout();
|
|
1626
|
+
if (!loggedOutViaBridge) {
|
|
1627
|
+
await this.logoutViaPopup(sessionToken);
|
|
1628
|
+
}
|
|
1629
|
+
this.reloadBridge();
|
|
1630
|
+
}
|
|
1631
|
+
requestBridgeLogout() {
|
|
1632
|
+
if (typeof window === 'undefined' || this.pendingLogout) {
|
|
1633
|
+
return Promise.resolve(false);
|
|
1634
|
+
}
|
|
1635
|
+
const target = this.bridgeFrame?.contentWindow;
|
|
1636
|
+
if (!target) {
|
|
1637
|
+
return Promise.resolve(false);
|
|
1638
|
+
}
|
|
1639
|
+
const requestId = createRandomId();
|
|
1640
|
+
return new Promise((resolve) => {
|
|
1641
|
+
const timeoutId = window.setTimeout(() => {
|
|
1642
|
+
if (this.pendingLogout?.requestId !== requestId) {
|
|
1643
|
+
return;
|
|
1644
|
+
}
|
|
1645
|
+
this.pendingLogout = null;
|
|
1646
|
+
resolve(false);
|
|
1647
|
+
}, 2500);
|
|
1648
|
+
this.pendingLogout = { requestId, resolve, timeoutId };
|
|
1649
|
+
try {
|
|
1650
|
+
target.postMessage({
|
|
1651
|
+
source: EINBLICK_PARENT_SOURCE,
|
|
1652
|
+
type: 'logout',
|
|
1653
|
+
nonce: this.bridgeNonce,
|
|
1654
|
+
requestId,
|
|
1655
|
+
}, this.appOrigin);
|
|
1656
|
+
}
|
|
1657
|
+
catch {
|
|
1658
|
+
window.clearTimeout(timeoutId);
|
|
1659
|
+
if (this.pendingLogout?.requestId === requestId) {
|
|
1660
|
+
this.pendingLogout = null;
|
|
1661
|
+
}
|
|
1662
|
+
resolve(false);
|
|
1663
|
+
}
|
|
1664
|
+
});
|
|
1665
|
+
}
|
|
1666
|
+
async logoutViaPopup(sessionToken) {
|
|
1624
1667
|
const logoutUrl = this.buildBridgeUrl({
|
|
1625
1668
|
mode: 'logout',
|
|
1626
1669
|
nonce: createRandomId(),
|
|
@@ -1647,28 +1690,11 @@ class EinblickEditRuntime {
|
|
|
1647
1690
|
}
|
|
1648
1691
|
}, 400);
|
|
1649
1692
|
});
|
|
1650
|
-
this.reloadBridge();
|
|
1651
1693
|
}
|
|
1652
1694
|
setActiveElement(element) {
|
|
1653
1695
|
if (this.activeElement === element) {
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
!element.contains(this.hoverButtonContainer)) {
|
|
1657
|
-
element.append(this.hoverButtonContainer);
|
|
1658
|
-
}
|
|
1659
|
-
this.updateHoverButtonVisibility(element);
|
|
1660
|
-
return;
|
|
1661
|
-
}
|
|
1662
|
-
if (this.activeElement) {
|
|
1663
|
-
this.activeElement.removeAttribute('data-einblick-editable-active');
|
|
1664
|
-
const originalPosition = this.activeElement.dataset.einblickRuntimeOriginalPosition;
|
|
1665
|
-
if (originalPosition !== undefined) {
|
|
1666
|
-
this.activeElement.style.position =
|
|
1667
|
-
originalPosition === RELATIVE_POSITION_SENTINEL
|
|
1668
|
-
? ''
|
|
1669
|
-
: originalPosition;
|
|
1670
|
-
delete this.activeElement.dataset.einblickRuntimeOriginalPosition;
|
|
1671
|
-
}
|
|
1696
|
+
this.updateHoverChrome();
|
|
1697
|
+
return;
|
|
1672
1698
|
}
|
|
1673
1699
|
this.activeElement = element;
|
|
1674
1700
|
if (!element ||
|
|
@@ -1677,22 +1703,176 @@ class EinblickEditRuntime {
|
|
|
1677
1703
|
!this.state.isAuthenticated) {
|
|
1678
1704
|
if (this.hoverButtonContainer) {
|
|
1679
1705
|
this.hoverButtonContainer.hidden = true;
|
|
1680
|
-
this.hoverButtonContainer.remove();
|
|
1681
1706
|
}
|
|
1707
|
+
this.activeOutline?.remove();
|
|
1708
|
+
this.activeOutline = null;
|
|
1709
|
+
this.clearQuickFieldOverlays();
|
|
1682
1710
|
if (this.hoverCreateButton) {
|
|
1683
1711
|
this.hoverCreateButton.hidden = true;
|
|
1684
1712
|
}
|
|
1685
1713
|
return;
|
|
1686
1714
|
}
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
element.style.position || RELATIVE_POSITION_SENTINEL;
|
|
1691
|
-
element.style.position = 'relative';
|
|
1692
|
-
}
|
|
1715
|
+
this.ensureActiveOutline();
|
|
1716
|
+
this.hoverButtonContainer.hidden = false;
|
|
1717
|
+
this.updateHoverButtonLabel();
|
|
1693
1718
|
this.updateHoverButtonVisibility(element);
|
|
1719
|
+
this.renderQuickFieldOverlays(element);
|
|
1720
|
+
this.updateHoverChrome();
|
|
1721
|
+
}
|
|
1722
|
+
ensureActiveOutline() {
|
|
1723
|
+
if (this.activeOutline || typeof document === 'undefined') {
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
const outline = document.createElement('div');
|
|
1727
|
+
outline.setAttribute('data-einblick-edit-outline', 'true');
|
|
1728
|
+
document.body.append(outline);
|
|
1729
|
+
this.activeOutline = outline;
|
|
1730
|
+
}
|
|
1731
|
+
updateHoverChrome() {
|
|
1732
|
+
if (!this.activeElement || !this.state.isAuthenticated) {
|
|
1733
|
+
this.activeOutline?.remove();
|
|
1734
|
+
this.activeOutline = null;
|
|
1735
|
+
if (this.hoverButtonContainer) {
|
|
1736
|
+
this.hoverButtonContainer.hidden = true;
|
|
1737
|
+
}
|
|
1738
|
+
this.clearQuickFieldOverlays();
|
|
1739
|
+
return;
|
|
1740
|
+
}
|
|
1741
|
+
if (!this.activeElement.isConnected) {
|
|
1742
|
+
this.setActiveElement(null);
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
this.ensureActiveOutline();
|
|
1746
|
+
const rect = this.activeElement.getBoundingClientRect();
|
|
1747
|
+
const isVisible = this.positionOutline(this.activeOutline, this.activeElement);
|
|
1748
|
+
if (!isVisible || rect.width <= 0 || rect.height <= 0) {
|
|
1749
|
+
if (this.hoverButtonContainer) {
|
|
1750
|
+
this.hoverButtonContainer.hidden = true;
|
|
1751
|
+
}
|
|
1752
|
+
this.clearQuickFieldOverlays();
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
this.updateHoverButtonLabel();
|
|
1756
|
+
this.updateHoverButtonVisibility(this.activeElement);
|
|
1757
|
+
this.positionHoverControls(rect);
|
|
1758
|
+
this.updateQuickFieldOverlayPositions();
|
|
1759
|
+
}
|
|
1760
|
+
positionOutline(outline, target) {
|
|
1761
|
+
if (!outline) {
|
|
1762
|
+
return false;
|
|
1763
|
+
}
|
|
1764
|
+
const rect = target.getBoundingClientRect();
|
|
1765
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
1766
|
+
outline.hidden = true;
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
const style = window.getComputedStyle(target);
|
|
1770
|
+
outline.hidden = false;
|
|
1771
|
+
outline.style.setProperty('--einblick-overlay-radius', style.borderRadius || '0.35rem');
|
|
1772
|
+
outline.style.left = `${Math.round(rect.left)}px`;
|
|
1773
|
+
outline.style.top = `${Math.round(rect.top)}px`;
|
|
1774
|
+
outline.style.width = `${Math.round(rect.width)}px`;
|
|
1775
|
+
outline.style.height = `${Math.round(rect.height)}px`;
|
|
1776
|
+
return true;
|
|
1777
|
+
}
|
|
1778
|
+
positionHoverControls(rect) {
|
|
1779
|
+
if (!this.hoverButtonContainer) {
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1694
1782
|
this.hoverButtonContainer.hidden = false;
|
|
1695
|
-
|
|
1783
|
+
const margin = 8;
|
|
1784
|
+
const width = this.hoverButtonContainer.offsetWidth || 180;
|
|
1785
|
+
const height = this.hoverButtonContainer.offsetHeight || 32;
|
|
1786
|
+
const left = clamp(rect.right - width - margin, margin, Math.max(margin, window.innerWidth - width - margin));
|
|
1787
|
+
const top = clamp(rect.top + margin, margin, Math.max(margin, window.innerHeight - height - margin));
|
|
1788
|
+
this.hoverButtonContainer.style.left = `${Math.round(left)}px`;
|
|
1789
|
+
this.hoverButtonContainer.style.top = `${Math.round(top)}px`;
|
|
1790
|
+
}
|
|
1791
|
+
clearQuickFieldOverlays() {
|
|
1792
|
+
this.quickFieldOverlayContainer?.remove();
|
|
1793
|
+
this.quickFieldOverlayContainer = null;
|
|
1794
|
+
this.quickFieldOverlays = [];
|
|
1795
|
+
}
|
|
1796
|
+
renderQuickFieldOverlays(element) {
|
|
1797
|
+
this.clearQuickFieldOverlays();
|
|
1798
|
+
const targets = this.getQuickEditableTargets(element);
|
|
1799
|
+
if (targets.length === 0 || typeof document === 'undefined') {
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
const container = document.createElement('div');
|
|
1803
|
+
container.setAttribute('data-einblick-field-overlays', 'true');
|
|
1804
|
+
this.quickFieldOverlays = targets.map((target) => {
|
|
1805
|
+
const binding = readBindingFromElement(target);
|
|
1806
|
+
const outline = document.createElement('div');
|
|
1807
|
+
outline.setAttribute('data-einblick-edit-field-outline', 'true');
|
|
1808
|
+
const button = document.createElement('button');
|
|
1809
|
+
button.type = 'button';
|
|
1810
|
+
button.setAttribute('data-einblick-field-button', binding && isMediaEditableBinding(binding) ? 'media' : 'text');
|
|
1811
|
+
button.textContent =
|
|
1812
|
+
binding && isMediaEditableBinding(binding)
|
|
1813
|
+
? getMediaActionLabel(binding, this.messages)
|
|
1814
|
+
: this.messages.actions.editText;
|
|
1815
|
+
button.addEventListener('pointerdown', (event) => {
|
|
1816
|
+
event.stopPropagation();
|
|
1817
|
+
});
|
|
1818
|
+
button.addEventListener('click', (event) => {
|
|
1819
|
+
stopEvent(event);
|
|
1820
|
+
const nextBinding = readBindingFromElement(target);
|
|
1821
|
+
if (!nextBinding) {
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
if (isMediaEditableBinding(nextBinding)) {
|
|
1825
|
+
this.openMediaPicker(target, nextBinding, button);
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
this.openInlineEditor(target, nextBinding);
|
|
1829
|
+
});
|
|
1830
|
+
container.append(outline, button);
|
|
1831
|
+
return { target, outline, button };
|
|
1832
|
+
});
|
|
1833
|
+
document.body.append(container);
|
|
1834
|
+
this.quickFieldOverlayContainer = container;
|
|
1835
|
+
this.updateQuickFieldOverlayPositions();
|
|
1836
|
+
}
|
|
1837
|
+
updateQuickFieldOverlayPositions() {
|
|
1838
|
+
if (!this.quickFieldOverlayContainer) {
|
|
1839
|
+
return;
|
|
1840
|
+
}
|
|
1841
|
+
for (const overlay of this.quickFieldOverlays) {
|
|
1842
|
+
if (!overlay.target.isConnected) {
|
|
1843
|
+
overlay.outline.hidden = true;
|
|
1844
|
+
overlay.button.hidden = true;
|
|
1845
|
+
continue;
|
|
1846
|
+
}
|
|
1847
|
+
const rect = overlay.target.getBoundingClientRect();
|
|
1848
|
+
const isVisible = this.positionOutline(overlay.outline, overlay.target);
|
|
1849
|
+
overlay.button.hidden = !isVisible;
|
|
1850
|
+
if (!isVisible) {
|
|
1851
|
+
continue;
|
|
1852
|
+
}
|
|
1853
|
+
const margin = 4;
|
|
1854
|
+
const width = overlay.button.offsetWidth || 96;
|
|
1855
|
+
const height = overlay.button.offsetHeight || 26;
|
|
1856
|
+
const left = clamp(rect.left + margin, margin, Math.max(margin, window.innerWidth - width - margin));
|
|
1857
|
+
const top = clamp(rect.top + margin, margin, Math.max(margin, window.innerHeight - height - margin));
|
|
1858
|
+
overlay.button.style.left = `${Math.round(left)}px`;
|
|
1859
|
+
overlay.button.style.top = `${Math.round(top)}px`;
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
updateHoverButtonLabel() {
|
|
1863
|
+
if (!this.hoverButton || !this.activeElement) {
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
const binding = readBindingFromElement(this.activeElement);
|
|
1867
|
+
if (!binding?.fieldKey) {
|
|
1868
|
+
this.hoverButton.textContent = this.messages.actions.editElement;
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
this.hoverButton.textContent = isMediaEditableBinding(binding)
|
|
1872
|
+
? getMediaActionLabel(binding, this.messages)
|
|
1873
|
+
: shouldUseInlineEditor(binding)
|
|
1874
|
+
? this.messages.actions.editText
|
|
1875
|
+
: this.messages.actions.edit;
|
|
1696
1876
|
}
|
|
1697
1877
|
reloadBridge() {
|
|
1698
1878
|
this.clearBridgePing();
|
|
@@ -1845,7 +2025,67 @@ class EinblickEditRuntime {
|
|
|
1845
2025
|
return null;
|
|
1846
2026
|
}
|
|
1847
2027
|
const editableTarget = target.closest('[data-einblick-editable="true"]');
|
|
1848
|
-
|
|
2028
|
+
if (!editableTarget) {
|
|
2029
|
+
return null;
|
|
2030
|
+
}
|
|
2031
|
+
const binding = readBindingFromElement(editableTarget);
|
|
2032
|
+
if (!binding?.fieldKey) {
|
|
2033
|
+
return editableTarget;
|
|
2034
|
+
}
|
|
2035
|
+
const entryTarget = this.findEntryTargetForField(editableTarget, binding);
|
|
2036
|
+
return entryTarget ?? editableTarget;
|
|
2037
|
+
}
|
|
2038
|
+
findEntryTargetForField(fieldElement, binding) {
|
|
2039
|
+
let parent = fieldElement.parentElement;
|
|
2040
|
+
while (parent) {
|
|
2041
|
+
const parentBinding = readBindingFromElement(parent);
|
|
2042
|
+
if (parentBinding &&
|
|
2043
|
+
!parentBinding.fieldKey &&
|
|
2044
|
+
parentBinding.resourceSlug === binding.resourceSlug &&
|
|
2045
|
+
parentBinding.recordId === binding.recordId) {
|
|
2046
|
+
return parent;
|
|
2047
|
+
}
|
|
2048
|
+
parent = parent.parentElement;
|
|
2049
|
+
}
|
|
2050
|
+
return null;
|
|
2051
|
+
}
|
|
2052
|
+
getQuickEditableTargets(element) {
|
|
2053
|
+
const binding = readBindingFromElement(element);
|
|
2054
|
+
if (!binding) {
|
|
2055
|
+
return [];
|
|
2056
|
+
}
|
|
2057
|
+
if (binding.fieldKey) {
|
|
2058
|
+
return shouldUseInlineEditor(binding) || isMediaEditableBinding(binding)
|
|
2059
|
+
? [element]
|
|
2060
|
+
: [];
|
|
2061
|
+
}
|
|
2062
|
+
const selector = [
|
|
2063
|
+
'[data-einblick-editable="true"]',
|
|
2064
|
+
`[data-einblick-resource="${CSS.escape(binding.resourceSlug)}"]`,
|
|
2065
|
+
`[data-einblick-record-id="${CSS.escape(binding.recordId)}"]`,
|
|
2066
|
+
'[data-einblick-field-key]',
|
|
2067
|
+
].join('');
|
|
2068
|
+
const fields = Array.from(element.querySelectorAll(selector));
|
|
2069
|
+
return fields
|
|
2070
|
+
.filter((candidate) => {
|
|
2071
|
+
const candidateBinding = readBindingFromElement(candidate);
|
|
2072
|
+
return (!!candidateBinding &&
|
|
2073
|
+
(shouldUseInlineEditor(candidateBinding) ||
|
|
2074
|
+
isMediaEditableBinding(candidateBinding)));
|
|
2075
|
+
})
|
|
2076
|
+
.slice(0, 24);
|
|
2077
|
+
}
|
|
2078
|
+
isSdkChromeTarget(target) {
|
|
2079
|
+
if (!(target instanceof Node)) {
|
|
2080
|
+
return false;
|
|
2081
|
+
}
|
|
2082
|
+
return (!!this.hoverButtonContainer?.contains(target) ||
|
|
2083
|
+
!!this.quickFieldOverlayContainer?.contains(target) ||
|
|
2084
|
+
!!this.activeOutline?.contains(target) ||
|
|
2085
|
+
!!this.brandBadge?.contains(target) ||
|
|
2086
|
+
!!this.brandPopover?.contains(target) ||
|
|
2087
|
+
!!this.bottomBarHost?.contains(target) ||
|
|
2088
|
+
!!this.bottomBarMoreMenu?.contains(target));
|
|
1849
2089
|
}
|
|
1850
2090
|
findElementForBinding(binding) {
|
|
1851
2091
|
const selector = [
|
|
@@ -1874,216 +2114,342 @@ class EinblickEditRuntime {
|
|
|
1874
2114
|
this.closeDrawer();
|
|
1875
2115
|
this.closeInlineEditor();
|
|
1876
2116
|
this.setActiveElement(null);
|
|
1877
|
-
const
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
gap: target.style.gap,
|
|
1889
|
-
minWidth: target.style.minWidth,
|
|
2117
|
+
const restoreState = {
|
|
2118
|
+
childNodes: Array.from(target.childNodes).map((node) => node.cloneNode(true)),
|
|
2119
|
+
contentEditable: target.getAttribute('contenteditable'),
|
|
2120
|
+
spellcheck: target.getAttribute('spellcheck'),
|
|
2121
|
+
tabIndex: target.tabIndex,
|
|
2122
|
+
hadTabIndex: target.hasAttribute('tabindex'),
|
|
2123
|
+
role: target.getAttribute('role'),
|
|
2124
|
+
ariaMultiline: target.getAttribute('aria-multiline'),
|
|
2125
|
+
title: target.getAttribute('title'),
|
|
2126
|
+
value: binding.value,
|
|
2127
|
+
cleanup: [],
|
|
1890
2128
|
};
|
|
2129
|
+
this.currentInlineRestoreState = restoreState;
|
|
1891
2130
|
target.dataset.einblickInlineEditing = 'true';
|
|
1892
|
-
target.
|
|
1893
|
-
target.
|
|
1894
|
-
target.
|
|
1895
|
-
target.
|
|
1896
|
-
target.
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
const errorMessage = document.createElement('div');
|
|
1908
|
-
errorMessage.setAttribute('data-einblick-inline-error', 'true');
|
|
1909
|
-
errorMessage.hidden = true;
|
|
1910
|
-
const actions = document.createElement('div');
|
|
1911
|
-
actions.setAttribute('data-einblick-inline-actions', 'true');
|
|
1912
|
-
const cancelButton = document.createElement('button');
|
|
1913
|
-
cancelButton.type = 'button';
|
|
1914
|
-
cancelButton.textContent = this.messages.actions.cancel;
|
|
1915
|
-
cancelButton.setAttribute('data-einblick-edit-action', 'secondary');
|
|
1916
|
-
cancelButton.addEventListener('click', () => this.closeInlineEditor());
|
|
1917
|
-
const saveButton = document.createElement('button');
|
|
1918
|
-
saveButton.type = 'button';
|
|
1919
|
-
saveButton.textContent = this.messages.actions.save;
|
|
1920
|
-
saveButton.setAttribute('data-einblick-edit-action', 'primary');
|
|
1921
|
-
const runSave = async () => {
|
|
1922
|
-
errorMessage.hidden = true;
|
|
1923
|
-
errorMessage.textContent = '';
|
|
1924
|
-
saveButton.disabled = true;
|
|
1925
|
-
saveButton.textContent = this.messages.actions.saving;
|
|
1926
|
-
cancelButton.disabled = true;
|
|
1927
|
-
try {
|
|
1928
|
-
const nextValue = this.getControlValue(control, binding);
|
|
1929
|
-
const savedValue = await this.saveInlineField(binding, nextValue);
|
|
1930
|
-
this.closeInlineEditor();
|
|
1931
|
-
this.applyValueToElement(target, binding, savedValue);
|
|
1932
|
-
await this.callbacks.onSave?.({
|
|
1933
|
-
binding,
|
|
1934
|
-
value: savedValue,
|
|
1935
|
-
source: 'inline',
|
|
1936
|
-
});
|
|
1937
|
-
}
|
|
1938
|
-
catch (error) {
|
|
1939
|
-
errorMessage.hidden = false;
|
|
1940
|
-
errorMessage.textContent =
|
|
1941
|
-
error instanceof Error
|
|
1942
|
-
? error.message
|
|
1943
|
-
: this.messages.errors.saveFieldFailed;
|
|
1944
|
-
saveButton.disabled = false;
|
|
1945
|
-
saveButton.textContent = this.messages.actions.save;
|
|
1946
|
-
cancelButton.disabled = false;
|
|
1947
|
-
}
|
|
2131
|
+
target.setAttribute('contenteditable', 'plaintext-only');
|
|
2132
|
+
target.setAttribute('spellcheck', 'true');
|
|
2133
|
+
target.setAttribute('role', 'textbox');
|
|
2134
|
+
target.setAttribute('aria-multiline', fieldTypeAllowsMultiline(binding) ? 'true' : 'false');
|
|
2135
|
+
if (!target.hasAttribute('tabindex')) {
|
|
2136
|
+
target.tabIndex = 0;
|
|
2137
|
+
}
|
|
2138
|
+
if ((target.textContent ?? '').trim().length === 0 &&
|
|
2139
|
+
binding.value !== undefined &&
|
|
2140
|
+
binding.value !== null) {
|
|
2141
|
+
target.textContent = formatValueForText(binding.value);
|
|
2142
|
+
}
|
|
2143
|
+
const handleClick = (event) => {
|
|
2144
|
+
event.preventDefault();
|
|
2145
|
+
event.stopPropagation();
|
|
1948
2146
|
};
|
|
1949
|
-
|
|
1950
|
-
void runSave();
|
|
1951
|
-
});
|
|
1952
|
-
control.addEventListener('keydown', (event) => {
|
|
1953
|
-
const keyboardEvent = event;
|
|
2147
|
+
const handleKeyDown = (keyboardEvent) => {
|
|
1954
2148
|
if (keyboardEvent.key === 'Escape') {
|
|
1955
2149
|
keyboardEvent.preventDefault();
|
|
2150
|
+
keyboardEvent.stopPropagation();
|
|
1956
2151
|
this.closeInlineEditor();
|
|
1957
2152
|
return;
|
|
1958
2153
|
}
|
|
1959
2154
|
if (keyboardEvent.key === 'Enter' &&
|
|
1960
|
-
(!(
|
|
2155
|
+
(!fieldTypeAllowsMultiline(binding) ||
|
|
1961
2156
|
keyboardEvent.metaKey ||
|
|
1962
2157
|
keyboardEvent.ctrlKey)) {
|
|
1963
2158
|
keyboardEvent.preventDefault();
|
|
1964
|
-
|
|
2159
|
+
keyboardEvent.stopPropagation();
|
|
2160
|
+
void this.commitInlineEditor();
|
|
2161
|
+
return;
|
|
1965
2162
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
2163
|
+
if (isTextInputEvent(keyboardEvent)) {
|
|
2164
|
+
keyboardEvent.stopPropagation();
|
|
2165
|
+
}
|
|
2166
|
+
};
|
|
2167
|
+
const handlePaste = (event) => {
|
|
2168
|
+
const text = event.clipboardData?.getData('text/plain');
|
|
2169
|
+
if (text === undefined) {
|
|
2170
|
+
return;
|
|
2171
|
+
}
|
|
2172
|
+
event.preventDefault();
|
|
2173
|
+
event.stopPropagation();
|
|
2174
|
+
this.insertPlainTextAtSelection(text);
|
|
2175
|
+
};
|
|
2176
|
+
const handleBlur = () => {
|
|
2177
|
+
window.setTimeout(() => {
|
|
2178
|
+
if (this.currentInlineTarget === target) {
|
|
2179
|
+
void this.commitInlineEditor();
|
|
2180
|
+
}
|
|
2181
|
+
}, 0);
|
|
2182
|
+
};
|
|
2183
|
+
target.addEventListener('click', handleClick, true);
|
|
2184
|
+
target.addEventListener('keydown', handleKeyDown, true);
|
|
2185
|
+
target.addEventListener('paste', handlePaste, true);
|
|
2186
|
+
target.addEventListener('blur', handleBlur, true);
|
|
2187
|
+
restoreState.cleanup.push(() => target.removeEventListener('click', handleClick, true), () => target.removeEventListener('keydown', handleKeyDown, true), () => target.removeEventListener('paste', handlePaste, true), () => target.removeEventListener('blur', handleBlur, true));
|
|
1971
2188
|
this.currentInlineTarget = target;
|
|
1972
2189
|
this.currentInlineBinding = binding;
|
|
2190
|
+
this.ensureActiveOutline();
|
|
2191
|
+
this.activeOutline?.setAttribute('data-einblick-inline-active', 'true');
|
|
2192
|
+
this.positionOutline(this.activeOutline, target);
|
|
1973
2193
|
window.setTimeout(() => {
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
}
|
|
2194
|
+
target.focus({ preventScroll: true });
|
|
2195
|
+
this.placeCaretAtEnd(target);
|
|
1977
2196
|
}, 0);
|
|
1978
2197
|
}
|
|
1979
|
-
closeInlineEditor() {
|
|
2198
|
+
closeInlineEditor(options = {}) {
|
|
2199
|
+
const restore = options.restore ?? true;
|
|
1980
2200
|
if (this.currentInlineTarget && this.currentInlineRestoreState) {
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
this.currentInlineRestoreState.
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
this.currentInlineTarget.
|
|
1990
|
-
this.currentInlineRestoreState.minWidth;
|
|
2201
|
+
for (const cleanup of this.currentInlineRestoreState.cleanup) {
|
|
2202
|
+
cleanup();
|
|
2203
|
+
}
|
|
2204
|
+
if (restore) {
|
|
2205
|
+
this.currentInlineTarget.replaceChildren(...this.currentInlineRestoreState.childNodes.map((node) => node.cloneNode(true)));
|
|
2206
|
+
this.currentInlineTarget.dataset.einblickValue =
|
|
2207
|
+
serializeBindingValue(this.currentInlineRestoreState.value) ?? '';
|
|
2208
|
+
}
|
|
2209
|
+
this.restoreInlineTargetAttributes(this.currentInlineTarget, this.currentInlineRestoreState);
|
|
1991
2210
|
delete this.currentInlineTarget.dataset.einblickInlineEditing;
|
|
2211
|
+
delete this.currentInlineTarget.dataset.einblickInlineState;
|
|
2212
|
+
this.currentInlineTarget.removeAttribute('aria-busy');
|
|
1992
2213
|
}
|
|
1993
2214
|
this.currentInlineRestoreState = null;
|
|
1994
2215
|
this.currentInlineTarget = null;
|
|
1995
2216
|
this.currentInlineBinding = null;
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
const textarea = document.createElement('textarea');
|
|
2002
|
-
textarea.setAttribute('data-einblick-inline-textarea', 'true');
|
|
2003
|
-
return textarea;
|
|
2004
|
-
}
|
|
2005
|
-
case 'boolean': {
|
|
2006
|
-
const select = document.createElement('select');
|
|
2007
|
-
select.setAttribute('data-einblick-inline-select', 'true');
|
|
2008
|
-
const trueOption = document.createElement('option');
|
|
2009
|
-
trueOption.value = 'true';
|
|
2010
|
-
trueOption.textContent = this.messages.values.true;
|
|
2011
|
-
const falseOption = document.createElement('option');
|
|
2012
|
-
falseOption.value = 'false';
|
|
2013
|
-
falseOption.textContent = this.messages.values.false;
|
|
2014
|
-
select.append(trueOption, falseOption);
|
|
2015
|
-
return select;
|
|
2016
|
-
}
|
|
2017
|
-
default: {
|
|
2018
|
-
const textValue = typeof binding.value === 'string'
|
|
2019
|
-
? binding.value
|
|
2020
|
-
: (target?.textContent ?? '');
|
|
2021
|
-
if (binding.fieldType === 'string' &&
|
|
2022
|
-
typeof textValue === 'string' &&
|
|
2023
|
-
textValue.includes('\n')) {
|
|
2024
|
-
const textarea = document.createElement('textarea');
|
|
2025
|
-
textarea.setAttribute('data-einblick-inline-textarea', 'true');
|
|
2026
|
-
return textarea;
|
|
2027
|
-
}
|
|
2028
|
-
const input = document.createElement('input');
|
|
2029
|
-
input.setAttribute('data-einblick-inline-control', 'true');
|
|
2030
|
-
if (binding.fieldType === 'number') {
|
|
2031
|
-
input.type = 'number';
|
|
2032
|
-
}
|
|
2033
|
-
else if (binding.fieldType === 'date') {
|
|
2034
|
-
input.type = 'date';
|
|
2035
|
-
}
|
|
2036
|
-
else {
|
|
2037
|
-
input.type = 'text';
|
|
2038
|
-
}
|
|
2039
|
-
return input;
|
|
2040
|
-
}
|
|
2217
|
+
this.inlineSaveInFlight = null;
|
|
2218
|
+
this.activeOutline?.removeAttribute('data-einblick-inline-active');
|
|
2219
|
+
if (!this.activeElement) {
|
|
2220
|
+
this.activeOutline?.remove();
|
|
2221
|
+
this.activeOutline = null;
|
|
2041
2222
|
}
|
|
2042
2223
|
}
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
control.value = currentValue === true ? 'true' : 'false';
|
|
2047
|
-
return;
|
|
2224
|
+
restoreInlineTargetAttributes(target, restoreState) {
|
|
2225
|
+
if (restoreState.contentEditable === null) {
|
|
2226
|
+
target.removeAttribute('contenteditable');
|
|
2048
2227
|
}
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
? currentValue
|
|
2052
|
-
.map((entry) => (typeof entry === 'string' ? entry : String(entry)))
|
|
2053
|
-
.join(', ')
|
|
2054
|
-
: currentValue === undefined || currentValue === null
|
|
2055
|
-
? ''
|
|
2056
|
-
: String(currentValue);
|
|
2057
|
-
return;
|
|
2228
|
+
else {
|
|
2229
|
+
target.setAttribute('contenteditable', restoreState.contentEditable);
|
|
2058
2230
|
}
|
|
2059
|
-
if (
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2231
|
+
if (restoreState.spellcheck === null) {
|
|
2232
|
+
target.removeAttribute('spellcheck');
|
|
2233
|
+
}
|
|
2234
|
+
else {
|
|
2235
|
+
target.setAttribute('spellcheck', restoreState.spellcheck);
|
|
2236
|
+
}
|
|
2237
|
+
if (restoreState.role === null) {
|
|
2238
|
+
target.removeAttribute('role');
|
|
2239
|
+
}
|
|
2240
|
+
else {
|
|
2241
|
+
target.setAttribute('role', restoreState.role);
|
|
2242
|
+
}
|
|
2243
|
+
if (restoreState.ariaMultiline === null) {
|
|
2244
|
+
target.removeAttribute('aria-multiline');
|
|
2245
|
+
}
|
|
2246
|
+
else {
|
|
2247
|
+
target.setAttribute('aria-multiline', restoreState.ariaMultiline);
|
|
2248
|
+
}
|
|
2249
|
+
if (restoreState.title === null) {
|
|
2250
|
+
target.removeAttribute('title');
|
|
2251
|
+
}
|
|
2252
|
+
else {
|
|
2253
|
+
target.setAttribute('title', restoreState.title);
|
|
2254
|
+
}
|
|
2255
|
+
if (restoreState.hadTabIndex) {
|
|
2256
|
+
target.tabIndex = restoreState.tabIndex;
|
|
2257
|
+
}
|
|
2258
|
+
else {
|
|
2259
|
+
target.removeAttribute('tabindex');
|
|
2063
2260
|
}
|
|
2064
|
-
control.value =
|
|
2065
|
-
currentValue === undefined || currentValue === null
|
|
2066
|
-
? ''
|
|
2067
|
-
: typeof currentValue === 'string'
|
|
2068
|
-
? currentValue
|
|
2069
|
-
: String(currentValue);
|
|
2070
2261
|
}
|
|
2071
|
-
|
|
2072
|
-
const rawValue =
|
|
2262
|
+
getInlineEditableValue(target, binding) {
|
|
2263
|
+
const rawValue = fieldTypeAllowsMultiline(binding)
|
|
2264
|
+
? target.innerText.replace(/\n$/, '')
|
|
2265
|
+
: (target.textContent ?? '').replace(/\s+/g, ' ').trim();
|
|
2073
2266
|
switch (binding.fieldType) {
|
|
2074
2267
|
case 'number':
|
|
2075
2268
|
return rawValue.trim().length === 0 ? null : Number(rawValue);
|
|
2076
|
-
case 'boolean':
|
|
2077
|
-
return rawValue === 'true';
|
|
2078
2269
|
case 'tags':
|
|
2079
2270
|
return rawValue
|
|
2080
2271
|
.split(',')
|
|
2081
2272
|
.map((entry) => entry.trim())
|
|
2082
2273
|
.filter(Boolean);
|
|
2274
|
+
case 'date':
|
|
2275
|
+
return normalizeDateValue(rawValue.trim());
|
|
2083
2276
|
default:
|
|
2084
2277
|
return rawValue;
|
|
2085
2278
|
}
|
|
2086
2279
|
}
|
|
2280
|
+
async commitInlineEditor() {
|
|
2281
|
+
if (this.inlineSaveInFlight) {
|
|
2282
|
+
return await this.inlineSaveInFlight;
|
|
2283
|
+
}
|
|
2284
|
+
const target = this.currentInlineTarget;
|
|
2285
|
+
const binding = this.currentInlineBinding;
|
|
2286
|
+
if (!target || !binding) {
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
this.inlineSaveInFlight = (async () => {
|
|
2290
|
+
target.setAttribute('aria-busy', 'true');
|
|
2291
|
+
target.dataset.einblickInlineState = 'saving';
|
|
2292
|
+
try {
|
|
2293
|
+
const nextValue = this.getInlineEditableValue(target, binding);
|
|
2294
|
+
const savedValue = await this.saveInlineField(binding, nextValue);
|
|
2295
|
+
this.closeInlineEditor({ restore: false });
|
|
2296
|
+
this.applyValueToElement(target, binding, savedValue);
|
|
2297
|
+
await this.callbacks.onSave?.({
|
|
2298
|
+
binding,
|
|
2299
|
+
value: savedValue,
|
|
2300
|
+
source: 'inline',
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
catch (error) {
|
|
2304
|
+
target.dataset.einblickInlineState = 'error';
|
|
2305
|
+
target.removeAttribute('aria-busy');
|
|
2306
|
+
this.inlineSaveInFlight = null;
|
|
2307
|
+
const message = error instanceof Error
|
|
2308
|
+
? error.message
|
|
2309
|
+
: this.messages.errors.saveFieldFailed;
|
|
2310
|
+
target.title = message;
|
|
2311
|
+
this.callbacks.onError?.(message);
|
|
2312
|
+
}
|
|
2313
|
+
})();
|
|
2314
|
+
await this.inlineSaveInFlight;
|
|
2315
|
+
}
|
|
2316
|
+
insertPlainTextAtSelection(text) {
|
|
2317
|
+
const selection = window.getSelection();
|
|
2318
|
+
if (!selection || selection.rangeCount === 0) {
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
selection.deleteFromDocument();
|
|
2322
|
+
selection.getRangeAt(0).insertNode(document.createTextNode(text));
|
|
2323
|
+
selection.collapseToEnd();
|
|
2324
|
+
}
|
|
2325
|
+
placeCaretAtEnd(target) {
|
|
2326
|
+
const selection = window.getSelection();
|
|
2327
|
+
if (!selection) {
|
|
2328
|
+
return;
|
|
2329
|
+
}
|
|
2330
|
+
const range = document.createRange();
|
|
2331
|
+
range.selectNodeContents(target);
|
|
2332
|
+
range.collapse(false);
|
|
2333
|
+
selection.removeAllRanges();
|
|
2334
|
+
selection.addRange(range);
|
|
2335
|
+
}
|
|
2336
|
+
openMediaPicker(target, binding, button) {
|
|
2337
|
+
if (!this.state.isAuthenticated) {
|
|
2338
|
+
this.callbacks.onError?.(this.messages.errors.signInToEditField);
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
if (!binding.fieldKey) {
|
|
2342
|
+
this.callbacks.onError?.(this.messages.errors.fieldBindingRequired);
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
const input = document.createElement('input');
|
|
2346
|
+
input.type = 'file';
|
|
2347
|
+
input.accept = getMediaAcceptValue(binding);
|
|
2348
|
+
input.style.position = 'fixed';
|
|
2349
|
+
input.style.left = '-9999px';
|
|
2350
|
+
input.style.top = '0';
|
|
2351
|
+
const previousText = button?.textContent ?? '';
|
|
2352
|
+
const resetButton = () => {
|
|
2353
|
+
if (button) {
|
|
2354
|
+
button.disabled = false;
|
|
2355
|
+
button.textContent =
|
|
2356
|
+
previousText || getMediaActionLabel(binding, this.messages);
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
input.addEventListener('change', () => {
|
|
2360
|
+
const file = input.files?.[0];
|
|
2361
|
+
input.remove();
|
|
2362
|
+
if (!file) {
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
if (button) {
|
|
2366
|
+
button.disabled = true;
|
|
2367
|
+
button.textContent = this.messages.actions.uploading;
|
|
2368
|
+
}
|
|
2369
|
+
void (async () => {
|
|
2370
|
+
try {
|
|
2371
|
+
const result = await this.saveInlineMediaField(binding, file);
|
|
2372
|
+
this.applyMediaPreviewToElement(target, file);
|
|
2373
|
+
await this.callbacks.onSave?.({
|
|
2374
|
+
binding,
|
|
2375
|
+
value: result.value,
|
|
2376
|
+
source: 'inline',
|
|
2377
|
+
});
|
|
2378
|
+
}
|
|
2379
|
+
catch (error) {
|
|
2380
|
+
const message = error instanceof Error
|
|
2381
|
+
? error.message
|
|
2382
|
+
: this.messages.errors.saveFieldFailed;
|
|
2383
|
+
this.callbacks.onError?.(message);
|
|
2384
|
+
}
|
|
2385
|
+
finally {
|
|
2386
|
+
resetButton();
|
|
2387
|
+
}
|
|
2388
|
+
})();
|
|
2389
|
+
});
|
|
2390
|
+
input.addEventListener('cancel', () => {
|
|
2391
|
+
input.remove();
|
|
2392
|
+
});
|
|
2393
|
+
document.body.append(input);
|
|
2394
|
+
input.click();
|
|
2395
|
+
}
|
|
2396
|
+
async saveInlineMediaField(binding, file) {
|
|
2397
|
+
if (!this.bridgeFrame?.contentWindow) {
|
|
2398
|
+
throw new Error(this.messages.errors.bridgeNotConnected);
|
|
2399
|
+
}
|
|
2400
|
+
if (!this.state.isAuthenticated) {
|
|
2401
|
+
throw new Error(this.messages.errors.signInToEditField);
|
|
2402
|
+
}
|
|
2403
|
+
if (!binding.fieldKey) {
|
|
2404
|
+
throw new Error(this.messages.errors.fieldBindingRequired);
|
|
2405
|
+
}
|
|
2406
|
+
const requestId = createRandomId();
|
|
2407
|
+
const promise = new Promise((resolve, reject) => {
|
|
2408
|
+
const timeoutId = window.setTimeout(() => {
|
|
2409
|
+
this.pendingInlineMediaSaves.delete(requestId);
|
|
2410
|
+
reject(new Error(this.messages.errors.savingTimedOut));
|
|
2411
|
+
}, 60000);
|
|
2412
|
+
this.pendingInlineMediaSaves.set(requestId, {
|
|
2413
|
+
resolve,
|
|
2414
|
+
reject,
|
|
2415
|
+
timeoutId,
|
|
2416
|
+
});
|
|
2417
|
+
});
|
|
2418
|
+
this.bridgeFrame.contentWindow.postMessage({
|
|
2419
|
+
source: EINBLICK_PARENT_SOURCE,
|
|
2420
|
+
type: 'replace-inline-media',
|
|
2421
|
+
nonce: this.bridgeNonce,
|
|
2422
|
+
requestId,
|
|
2423
|
+
payload: {
|
|
2424
|
+
resourceSlug: binding.resourceSlug,
|
|
2425
|
+
recordId: binding.recordId,
|
|
2426
|
+
fieldKey: binding.fieldKey,
|
|
2427
|
+
file,
|
|
2428
|
+
},
|
|
2429
|
+
}, this.appOrigin);
|
|
2430
|
+
return await promise;
|
|
2431
|
+
}
|
|
2432
|
+
applyMediaPreviewToElement(element, file) {
|
|
2433
|
+
if (!file.type.startsWith('image/')) {
|
|
2434
|
+
return;
|
|
2435
|
+
}
|
|
2436
|
+
const image = element instanceof HTMLImageElement
|
|
2437
|
+
? element
|
|
2438
|
+
: element.querySelector('img');
|
|
2439
|
+
if (!image) {
|
|
2440
|
+
return;
|
|
2441
|
+
}
|
|
2442
|
+
const objectUrl = URL.createObjectURL(file);
|
|
2443
|
+
const previousUrl = image.src;
|
|
2444
|
+
image.addEventListener('load', () => {
|
|
2445
|
+
URL.revokeObjectURL(objectUrl);
|
|
2446
|
+
}, { once: true });
|
|
2447
|
+
image.addEventListener('error', () => {
|
|
2448
|
+
image.src = previousUrl;
|
|
2449
|
+
URL.revokeObjectURL(objectUrl);
|
|
2450
|
+
}, { once: true });
|
|
2451
|
+
image.src = objectUrl;
|
|
2452
|
+
}
|
|
2087
2453
|
async saveInlineField(binding, value) {
|
|
2088
2454
|
if (!this.bridgeFrame?.contentWindow) {
|
|
2089
2455
|
throw new Error(this.messages.errors.bridgeNotConnected);
|