@growgroup/visual-editor 0.3.0 → 0.4.0
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/README.md +10 -1
- package/dist/editor.css +1 -1
- package/package.json +3 -2
- package/src/editor/EditorContext.tsx +11 -2
- package/src/editor/EditorToolbar.tsx +8 -2
- package/src/editor/FrontendVisualEditor.tsx +128 -45
- package/src/editor/components/EditorCanvas.tsx +87 -15
- package/src/editor/components/EditorContextMenu.tsx +12 -3
- package/src/editor/components/EditorFooter.tsx +2 -0
- package/src/editor/components/EditorHeader.tsx +3 -2
- package/src/editor/components/EditorLayerPanel.tsx +28 -2
- package/src/editor/components/EditorPropertyPanel.tsx +54 -25
- package/src/editor/components/FigmaColorPicker.tsx +2 -1
- package/src/editor/components/JsEditorDialog.tsx +1 -0
- package/src/editor/components/PageSettingsDialog.tsx +1 -0
- package/src/editor/components/comments/CommentBoard.tsx +9 -6
- package/src/editor/components/multi-page/CanvasFrameDecor.tsx +172 -0
- package/src/editor/components/multi-page/MultiPageCanvasView.tsx +204 -58
- package/src/editor/components/multi-page/PageFramePreview.tsx +131 -22
- package/src/editor/components/multi-page/index.ts +1 -0
- package/src/editor/components/ppt/PptChrome.tsx +6 -2
- package/src/editor/components/ppt/PptComments.tsx +411 -71
- package/src/editor/constants.ts +44 -1
- package/src/editor/contexts/EditorArtboardContext.tsx +60 -1
- package/src/editor/contexts/EditorComponentsContext.tsx +48 -31
- package/src/editor/contexts/EditorUIStateContext.tsx +4 -3
- package/src/editor/contexts/EditorVariablesContext.tsx +5 -4
- package/src/editor/contexts/MultiPageCanvasContext.tsx +211 -34
- package/src/editor/hooks/useAiReplace.ts +4 -3
- package/src/editor/hooks/useBrowserZoomPrevention.ts +2 -1
- package/src/editor/hooks/useComponentEditMode.ts +27 -26
- package/src/editor/hooks/useEditorMessages.ts +3 -2
- package/src/editor/hooks/useElementActions.ts +27 -26
- package/src/editor/hooks/useElementSelection.ts +19 -16
- package/src/editor/hooks/useFocusManagement.ts +7 -6
- package/src/editor/hooks/useGoogleFonts.ts +3 -2
- package/src/editor/hooks/useIframeInitializer.ts +6 -3
- package/src/editor/hooks/useIframeSetup.ts +4 -3
- package/src/editor/hooks/useImageUpload.ts +4 -3
- package/src/editor/hooks/useMarqueeSelection.ts +2 -1
- package/src/editor/hooks/usePageSettingsManager.ts +24 -23
- package/src/editor/hooks/useRichPaste.ts +11 -10
- package/src/editor/parts.ts +7 -25
- package/src/editor/utils/component-renderer.ts +8 -7
- package/src/editor/utils/component-sync.ts +16 -15
- package/src/editor/utils/debug.ts +52 -0
- package/src/editor/utils/dom-utils.ts +27 -10
- package/src/editor/utils/drop-target.ts +135 -0
- package/src/editor/utils/eyedropper.ts +3 -1
- package/src/editor/utils/figma-export.ts +6 -5
- package/src/editor/utils/figma-kiwi-decoder.ts +28 -27
- package/src/editor/utils/figma-kiwi-encoder.ts +9 -8
- package/src/editor/utils/figma-paste.ts +57 -56
- package/src/editor/utils/flex-utils.ts +12 -10
- package/src/editor/utils/html-utils.ts +19 -6
- package/src/editor/utils/override-apply.ts +3 -2
- package/src/editor/utils/override-detection.ts +11 -10
- package/src/editor/utils/tailwind-utils.ts +4 -3
- package/src/index.ts +5 -0
- package/src/io.ts +22 -1
- package/src/lib/firebase/css-variables.ts +4 -3
- package/src/lib/firebase/storage.ts +3 -2
- package/src/styles/skin.css +63 -4
- package/src/types/editor.ts +6 -2
- package/src/version.ts +9 -0
package/src/editor/constants.ts
CHANGED
|
@@ -183,7 +183,22 @@ export const EDITOR_IFRAME_STYLES = `
|
|
|
183
183
|
body.text-mode [data-editable="true"] {
|
|
184
184
|
pointer-events: none;
|
|
185
185
|
}
|
|
186
|
-
|
|
186
|
+
/* コメントの範囲指定中: 要素は反応せず、紙面のドラッグが範囲になる */
|
|
187
|
+
body.comment-mode {
|
|
188
|
+
cursor: crosshair !important;
|
|
189
|
+
}
|
|
190
|
+
body.comment-mode [data-editable="true"] {
|
|
191
|
+
pointer-events: none;
|
|
192
|
+
}
|
|
193
|
+
body.comment-mode .selection-box,
|
|
194
|
+
body.comment-mode .hover-preview {
|
|
195
|
+
display: none !important;
|
|
196
|
+
}
|
|
197
|
+
/* 範囲の枠の上からも新しい範囲を描けるように(ピンはクリックでスレッドを開く) */
|
|
198
|
+
body.comment-mode .gg-comment-layer [data-comment-rect] {
|
|
199
|
+
pointer-events: none !important;
|
|
200
|
+
}
|
|
201
|
+
body:not(.draw-mode):not(.text-mode):not(.move-mode):not(.comment-mode) [data-editable="true"] {
|
|
187
202
|
pointer-events: auto !important;
|
|
188
203
|
cursor: pointer;
|
|
189
204
|
}
|
|
@@ -375,6 +390,34 @@ export const EDITOR_IFRAME_STYLES = `
|
|
|
375
390
|
.selection-box.selection-member .selection-outline {
|
|
376
391
|
box-shadow: 0 0 0 calc(1px * var(--ov)) rgba(13, 153, 255, 0.75);
|
|
377
392
|
}
|
|
393
|
+
/*
|
|
394
|
+
* 部品(data-part)のインスタンスは紫(Figma のコンポーネントと同じ色)。
|
|
395
|
+
* ルートを選んだら枠・ハンドル・サイズラベルが紫、中のスロットを選んだら枠は青のまま
|
|
396
|
+
* 「部品の中」の札だけ出す。札はパンくず(上 -22px)の左上に重ならないよう、その上に置く
|
|
397
|
+
*/
|
|
398
|
+
.selection-box.selection-part .selection-outline { box-shadow: 0 0 0 var(--outline-w) #7b61ff; }
|
|
399
|
+
.selection-box.selection-part .resize-handle::after { border-color: #7b61ff; }
|
|
400
|
+
.selection-box.selection-part .size-label { background: #7b61ff; }
|
|
401
|
+
.selection-box.selection-member.selection-part .selection-outline { box-shadow: 0 0 0 calc(1px * var(--ov)) rgba(123, 97, 255, 0.8); }
|
|
402
|
+
.selection-box .part-badge {
|
|
403
|
+
position: absolute;
|
|
404
|
+
top: calc(-44px * var(--ov));
|
|
405
|
+
left: 0;
|
|
406
|
+
padding: calc(2px * var(--ov)) calc(6px * var(--ov));
|
|
407
|
+
border-radius: calc(3px * var(--ov));
|
|
408
|
+
background: #7b61ff;
|
|
409
|
+
color: #fff;
|
|
410
|
+
font-size: calc(10px * var(--ov));
|
|
411
|
+
line-height: 1.4;
|
|
412
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Hiragino Sans', 'Noto Sans JP', sans-serif;
|
|
413
|
+
white-space: nowrap;
|
|
414
|
+
pointer-events: none;
|
|
415
|
+
z-index: 40;
|
|
416
|
+
}
|
|
417
|
+
.selection-box.selection-in-part .part-badge { background: rgba(123, 97, 255, 0.85); }
|
|
418
|
+
/* 選んでいる部品のスロット(編集できる範囲)は点線。中を選んでいるときは部品の外周を薄い点線で示す */
|
|
419
|
+
[data-part].selected [data-slot] { outline: calc(1px * var(--overlay-scale, 1)) dashed rgba(123, 97, 255, 0.75); outline-offset: calc(-1px * var(--overlay-scale, 1)); }
|
|
420
|
+
[data-part]:not(.selected):has(.selected) { outline: calc(1px * var(--overlay-scale, 1)) dashed rgba(123, 97, 255, 0.55); outline-offset: calc(2px * var(--overlay-scale, 1)); }
|
|
378
421
|
.size-label {
|
|
379
422
|
position: absolute;
|
|
380
423
|
bottom: calc(-22px * var(--ov));
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - contentList: コンテンツリスト(スライド、ページ等)
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import React, { createContext, useContext, useState, useCallback, useEffect } from 'react';
|
|
17
|
+
import React, { createContext, useContext, useState, useCallback, useEffect, useMemo } from 'react';
|
|
18
18
|
import type { DOMTreeNode } from '../types';
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -30,6 +30,56 @@ export interface ContentListItem {
|
|
|
30
30
|
/** 表示順序(スライド番号、ページ番号等) */
|
|
31
31
|
order: number;
|
|
32
32
|
thumbnailHtml?: string;
|
|
33
|
+
/**
|
|
34
|
+
* 親ページの id(サイトマップの階層)。渡すと、マルチフレームのキャンバスが
|
|
35
|
+
* フレームを階層のツリー(親の下に子を並べる)で配置し、左パネルの一覧も段を付ける。
|
|
36
|
+
* 無ければ(全ページ null / undefined)従来どおり行で折り返す
|
|
37
|
+
*/
|
|
38
|
+
parentId?: string | null;
|
|
39
|
+
/** 編集しない表示(ページそのもの・ビューア)の URL。渡すとフレーム名と一覧に「別タブで開く」が付く */
|
|
40
|
+
href?: string;
|
|
41
|
+
/**
|
|
42
|
+
* ページ全体を写した画像(PNG / JPEG)の URL。幅は 480px 以上、縦横比はページと同じ。
|
|
43
|
+
* マルチフレームのキャンバスが大きく縮小されている間(倍率 0.5 未満)は、見るだけの紙面を
|
|
44
|
+
* srcdoc の iframe ではなくこの画像で描く(26 枚の iframe をラスタライズし直さずに済む)。
|
|
45
|
+
* 内容が変わったら URL も変える(`&v=…`)。変わると紙面が読み直す。
|
|
46
|
+
* 渡さないページ・画像が読めなかったページは、今までどおり iframe で描く
|
|
47
|
+
*/
|
|
48
|
+
thumbnail?: string;
|
|
49
|
+
/**
|
|
50
|
+
* 本文の版。利用側でファイルが外から変わったとき(部品の一括反映・CLI・他の人の編集)に
|
|
51
|
+
* 数を進めると、キャンバスの見るだけの紙面がそのページを読み直す。編集中のページには効かない
|
|
52
|
+
* (編集中の本文は上書きしない)
|
|
53
|
+
*/
|
|
54
|
+
revision?: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 文書に付ける属性(`<html>` の属性)。生きているエディタと見るだけの紙面の両方の文書に、
|
|
59
|
+
* 読み直さずに反映する。利用側の CSS が `html[data-…]` で切り替える表示(構成ラフの
|
|
60
|
+
* 注釈カラムの表示・非表示など)に使う。値が null / undefined の属性は外す
|
|
61
|
+
*/
|
|
62
|
+
export type DocumentAttributes = Record<string, string | null | undefined>;
|
|
63
|
+
|
|
64
|
+
/** contentList の parentId から各ページの深さ(ルート = 0)を求める。親が一覧に無い・循環しているものはルート扱い */
|
|
65
|
+
export function computeContentDepths(list: readonly ContentListItem[]): Map<string, number> {
|
|
66
|
+
const byId = new Map(list.map((c) => [c.id, c] as const));
|
|
67
|
+
const depths = new Map<string, number>();
|
|
68
|
+
const depthOf = (id: string, seen: Set<string>): number => {
|
|
69
|
+
const cached = depths.get(id);
|
|
70
|
+
if (cached != null) return cached;
|
|
71
|
+
const item = byId.get(id);
|
|
72
|
+
const parent = item?.parentId;
|
|
73
|
+
let d = 0;
|
|
74
|
+
if (parent && parent !== id && byId.has(parent) && !seen.has(parent)) {
|
|
75
|
+
seen.add(id);
|
|
76
|
+
d = depthOf(parent, seen) + 1;
|
|
77
|
+
}
|
|
78
|
+
depths.set(id, d);
|
|
79
|
+
return d;
|
|
80
|
+
};
|
|
81
|
+
list.forEach((c) => depthOf(c.id, new Set()));
|
|
82
|
+
return depths;
|
|
33
83
|
}
|
|
34
84
|
|
|
35
85
|
// アートボード(スライド/ページ)ごとの状態
|
|
@@ -55,6 +105,8 @@ export interface EditorArtboardContextValue {
|
|
|
55
105
|
contentList: ContentListItem[];
|
|
56
106
|
currentContentId: string | null;
|
|
57
107
|
onContentChange: ContentChangeHandler | null;
|
|
108
|
+
/** 文書(`<html>`)に付ける属性。エディタと見るだけの紙面の両方に、読み直さずに反映する */
|
|
109
|
+
documentAttributes: DocumentAttributes;
|
|
58
110
|
|
|
59
111
|
// Deprecated aliases
|
|
60
112
|
/** @deprecated Use contentList instead */
|
|
@@ -79,6 +131,7 @@ interface EditorArtboardProviderProps {
|
|
|
79
131
|
contentList?: ContentListItem[];
|
|
80
132
|
currentContentId?: string;
|
|
81
133
|
onContentChange?: ContentChangeHandler;
|
|
134
|
+
documentAttributes?: DocumentAttributes;
|
|
82
135
|
// 状態同期用のコールバック(ファサードで設定)
|
|
83
136
|
onArtboardSwitch?: (
|
|
84
137
|
newId: string,
|
|
@@ -110,6 +163,7 @@ export function EditorArtboardProvider({
|
|
|
110
163
|
contentList: contentListProp,
|
|
111
164
|
currentContentId: currentContentIdProp,
|
|
112
165
|
onContentChange: onContentChangeProp,
|
|
166
|
+
documentAttributes: documentAttributesProp,
|
|
113
167
|
slides: slidesProp,
|
|
114
168
|
currentSlideId: currentSlideIdProp,
|
|
115
169
|
onSlideChange: onSlideChangeProp,
|
|
@@ -119,6 +173,10 @@ export function EditorArtboardProvider({
|
|
|
119
173
|
const contentList = contentListProp ?? slidesProp ?? [];
|
|
120
174
|
const currentContentId = currentContentIdProp ?? currentSlideIdProp ?? null;
|
|
121
175
|
const onContentChange = onContentChangeProp ?? onSlideChangeProp ?? null;
|
|
176
|
+
// 属性は中身で比べる(利用側が毎描画で新しいオブジェクトを渡しても、購読側を描き直さない)
|
|
177
|
+
const documentAttributesKey = JSON.stringify(documentAttributesProp ?? {});
|
|
178
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
179
|
+
const documentAttributes = useMemo<DocumentAttributes>(() => ({ ...(documentAttributesProp ?? {}) }), [documentAttributesKey]);
|
|
122
180
|
|
|
123
181
|
// Deprecated aliases
|
|
124
182
|
const slides = contentList;
|
|
@@ -221,6 +279,7 @@ export function EditorArtboardProvider({
|
|
|
221
279
|
contentList,
|
|
222
280
|
currentContentId,
|
|
223
281
|
onContentChange,
|
|
282
|
+
documentAttributes,
|
|
224
283
|
// Deprecated
|
|
225
284
|
slides,
|
|
226
285
|
currentSlideId,
|
|
@@ -45,6 +45,8 @@ import {
|
|
|
45
45
|
} from '../../lib/firebase/editor-components';
|
|
46
46
|
import { io } from '../../io';
|
|
47
47
|
import type { EditorPartDef, EditorPartsLibrary } from '../../io';
|
|
48
|
+
import { useMultiPageCanvasOptional } from './MultiPageCanvasContext';
|
|
49
|
+
import { debugLog, isDebugEnabled } from '../utils/debug';
|
|
48
50
|
import {
|
|
49
51
|
materializePart,
|
|
50
52
|
partDefFromElement,
|
|
@@ -210,7 +212,7 @@ export function htmlElementToComponentElement(element: HTMLElement, isRoot: bool
|
|
|
210
212
|
if (key && value) {
|
|
211
213
|
// Skip position-related styles for root element
|
|
212
214
|
if (isRoot && POSITION_STYLES.has(key)) {
|
|
213
|
-
|
|
215
|
+
debugLog(`[htmlElementToComponentElement] Skipping position style for root: ${key}`);
|
|
214
216
|
continue;
|
|
215
217
|
}
|
|
216
218
|
// Convert kebab-case to camelCase
|
|
@@ -237,7 +239,7 @@ export function htmlElementToComponentElement(element: HTMLElement, isRoot: bool
|
|
|
237
239
|
if (hasMixedContent) {
|
|
238
240
|
// Mixed content: use innerHTML to preserve all content
|
|
239
241
|
innerHTML = element.innerHTML;
|
|
240
|
-
|
|
242
|
+
debugLog('[htmlElementToComponentElement] Mixed content detected, using innerHTML');
|
|
241
243
|
} else if (hasElementChildren) {
|
|
242
244
|
// Only element children: recursively process (not root)
|
|
243
245
|
for (const child of Array.from(element.children)) {
|
|
@@ -260,17 +262,20 @@ export function htmlElementToComponentElement(element: HTMLElement, isRoot: bool
|
|
|
260
262
|
overridable: { ...defaultOverridable },
|
|
261
263
|
};
|
|
262
264
|
|
|
263
|
-
//
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
265
|
+
// 要素 1 つにつき 1 行出る。部品 89 個 + 26 ページで 2,000 件を超えるので、
|
|
266
|
+
// 出さないときは引数のオブジェクトを組み立てること自体をやめる
|
|
267
|
+
if (isDebugEnabled()) {
|
|
268
|
+
debugLog('[htmlElementToComponentElement] Converted:', {
|
|
269
|
+
tagName: result.tagName,
|
|
270
|
+
className: result.className,
|
|
271
|
+
hasTextContent: !!result.textContent,
|
|
272
|
+
hasInnerHTML: !!result.innerHTML,
|
|
273
|
+
textContentPreview: result.textContent?.substring(0, 50),
|
|
274
|
+
innerHTMLPreview: result.innerHTML?.substring(0, 50),
|
|
275
|
+
childrenCount: result.children.length,
|
|
276
|
+
stylesCount: Object.keys(result.styles).length,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
274
279
|
|
|
275
280
|
return result;
|
|
276
281
|
}
|
|
@@ -474,6 +479,13 @@ export function EditorComponentsProvider({
|
|
|
474
479
|
websiteId,
|
|
475
480
|
pageId,
|
|
476
481
|
}: EditorComponentsProviderProps) {
|
|
482
|
+
// マルチフレームのキャンバス(あれば)。部品の定義を更新したあと、他ページの紙面を読み直させる。
|
|
483
|
+
// ref で持つ: キャンバスの値は高さの実測のたびに変わるので、コールバックの依存に入れない
|
|
484
|
+
const canvas = useMultiPageCanvasOptional();
|
|
485
|
+
const canvasRef = useRef(canvas);
|
|
486
|
+
canvasRef.current = canvas;
|
|
487
|
+
const pageIdRef = useRef(pageId);
|
|
488
|
+
pageIdRef.current = pageId;
|
|
477
489
|
// State
|
|
478
490
|
const [masterComponents, setMasterComponents] = useState<
|
|
479
491
|
Map<string, MasterComponent>
|
|
@@ -578,7 +590,7 @@ export function EditorComponentsProvider({
|
|
|
578
590
|
setIsLoadingComponents(true);
|
|
579
591
|
setError(null);
|
|
580
592
|
try {
|
|
581
|
-
|
|
593
|
+
debugLog(
|
|
582
594
|
`[EditorComponentsContext] Loading components for website: ${targetWebsiteId}`
|
|
583
595
|
);
|
|
584
596
|
|
|
@@ -602,13 +614,13 @@ export function EditorComponentsProvider({
|
|
|
602
614
|
setMasterComponents(componentsMap);
|
|
603
615
|
setComponentLibrary(categoriesFromParts(lib, componentsMap));
|
|
604
616
|
setComponentInstances(new Map());
|
|
605
|
-
|
|
617
|
+
debugLog(`[EditorComponentsContext] Loaded ${defs.size} parts`);
|
|
606
618
|
return;
|
|
607
619
|
}
|
|
608
620
|
|
|
609
621
|
// Load master components from Firestore
|
|
610
622
|
const components = await getAllMasterComponents(targetWebsiteId);
|
|
611
|
-
|
|
623
|
+
debugLog(`[EditorComponentsContext] Loaded ${components.length} components`);
|
|
612
624
|
|
|
613
625
|
// Create Map from components
|
|
614
626
|
const componentsMap = new Map<string, MasterComponent>();
|
|
@@ -681,9 +693,9 @@ export function EditorComponentsProvider({
|
|
|
681
693
|
|
|
682
694
|
const loadInstances = async () => {
|
|
683
695
|
try {
|
|
684
|
-
|
|
696
|
+
debugLog(`[EditorComponentsContext] Loading instances for page: ${pageId}`);
|
|
685
697
|
const instances = await getPageComponentInstances(websiteId, pageId);
|
|
686
|
-
|
|
698
|
+
debugLog(`[EditorComponentsContext] Loaded ${instances.length} instances`);
|
|
687
699
|
|
|
688
700
|
// Create Map from instances (keyed by domElementId)
|
|
689
701
|
const instancesMap = new Map<string, ComponentInstance>();
|
|
@@ -758,7 +770,7 @@ export function EditorComponentsProvider({
|
|
|
758
770
|
});
|
|
759
771
|
}
|
|
760
772
|
|
|
761
|
-
|
|
773
|
+
debugLog(
|
|
762
774
|
`[EditorComponentsContext] Created master component: ${name} (${id})`
|
|
763
775
|
);
|
|
764
776
|
return masterComponent;
|
|
@@ -870,6 +882,9 @@ export function EditorComponentsProvider({
|
|
|
870
882
|
const saved = (save ? await save(next) : undefined) ?? next;
|
|
871
883
|
upsertPartDef(saved);
|
|
872
884
|
el.setAttribute('data-part-v', String(saved.version));
|
|
885
|
+
// 他ページのインスタンスは利用側(savePart)が書き換えている。キャンバスの見るだけの紙面にもその場で反映する
|
|
886
|
+
// (以前は読み直すまで前の姿のままだった)。編集中のページは今の DOM が正なので触らない
|
|
887
|
+
canvasRef.current?.invalidatePages({ except: [pageIdRef.current] });
|
|
873
888
|
return saved;
|
|
874
889
|
},
|
|
875
890
|
[partDefs, upsertPartDef]
|
|
@@ -930,10 +945,12 @@ export function EditorComponentsProvider({
|
|
|
930
945
|
m.set(id, next);
|
|
931
946
|
return m;
|
|
932
947
|
});
|
|
933
|
-
io().savePart?.(next)
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
948
|
+
io().savePart?.(next)
|
|
949
|
+
.then(() => canvasRef.current?.invalidatePages({ except: [pageIdRef.current] }))
|
|
950
|
+
.catch((err) => {
|
|
951
|
+
console.error('[EditorComponentsContext] 部品の保存に失敗:', err);
|
|
952
|
+
setError('部品の保存に失敗しました');
|
|
953
|
+
});
|
|
937
954
|
}
|
|
938
955
|
}
|
|
939
956
|
},
|
|
@@ -983,7 +1000,7 @@ export function EditorComponentsProvider({
|
|
|
983
1000
|
});
|
|
984
1001
|
}
|
|
985
1002
|
|
|
986
|
-
|
|
1003
|
+
debugLog(`[EditorComponentsContext] Deleted master component: ${id}`);
|
|
987
1004
|
}, [websiteId, partsMode]);
|
|
988
1005
|
|
|
989
1006
|
const getMasterComponent = useCallback(
|
|
@@ -1056,7 +1073,7 @@ export function EditorComponentsProvider({
|
|
|
1056
1073
|
});
|
|
1057
1074
|
}
|
|
1058
1075
|
|
|
1059
|
-
|
|
1076
|
+
debugLog(
|
|
1060
1077
|
`[EditorComponentsContext] Created instance: ${id} of master ${masterComponentId}`
|
|
1061
1078
|
);
|
|
1062
1079
|
return instance;
|
|
@@ -1144,7 +1161,7 @@ export function EditorComponentsProvider({
|
|
|
1144
1161
|
return next;
|
|
1145
1162
|
});
|
|
1146
1163
|
|
|
1147
|
-
|
|
1164
|
+
debugLog(`[EditorComponentsContext] Deleted instance: ${instanceId}`);
|
|
1148
1165
|
}, [websiteId, pageId]);
|
|
1149
1166
|
|
|
1150
1167
|
const getInstanceByDomId = useCallback(
|
|
@@ -1206,7 +1223,7 @@ export function EditorComponentsProvider({
|
|
|
1206
1223
|
return next;
|
|
1207
1224
|
});
|
|
1208
1225
|
|
|
1209
|
-
|
|
1226
|
+
debugLog(
|
|
1210
1227
|
`[EditorComponentsContext] Added override to instance: ${instanceId}`
|
|
1211
1228
|
);
|
|
1212
1229
|
},
|
|
@@ -1256,7 +1273,7 @@ export function EditorComponentsProvider({
|
|
|
1256
1273
|
return next;
|
|
1257
1274
|
});
|
|
1258
1275
|
|
|
1259
|
-
|
|
1276
|
+
debugLog(
|
|
1260
1277
|
`[EditorComponentsContext] Removed override ${overrideId} from instance: ${instanceId}`
|
|
1261
1278
|
);
|
|
1262
1279
|
},
|
|
@@ -1308,7 +1325,7 @@ export function EditorComponentsProvider({
|
|
|
1308
1325
|
return next;
|
|
1309
1326
|
});
|
|
1310
1327
|
|
|
1311
|
-
|
|
1328
|
+
debugLog(
|
|
1312
1329
|
`[EditorComponentsContext] Reset all overrides for instance: ${instanceId}`
|
|
1313
1330
|
);
|
|
1314
1331
|
}, [websiteId, pageId]);
|
|
@@ -1381,7 +1398,7 @@ export function EditorComponentsProvider({
|
|
|
1381
1398
|
return next;
|
|
1382
1399
|
});
|
|
1383
1400
|
|
|
1384
|
-
|
|
1401
|
+
debugLog(
|
|
1385
1402
|
`[EditorComponentsContext] Changed variant for instance ${instanceId} to ${variantId}`
|
|
1386
1403
|
);
|
|
1387
1404
|
},
|
|
@@ -1445,7 +1462,7 @@ export function EditorComponentsProvider({
|
|
|
1445
1462
|
});
|
|
1446
1463
|
}
|
|
1447
1464
|
|
|
1448
|
-
|
|
1465
|
+
debugLog(
|
|
1449
1466
|
`[EditorComponentsContext] Detached instance: ${instanceId}`
|
|
1450
1467
|
);
|
|
1451
1468
|
return htmlElement;
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import React, { createContext, useContext, useState, useCallback } from 'react';
|
|
19
19
|
import type { DOMTreeNode, PanelSections, MarqueeState } from '../types';
|
|
20
20
|
import { DEFAULT_PANEL_SECTIONS } from '../constants';
|
|
21
|
+
import { debugLog } from '../utils/debug';
|
|
21
22
|
|
|
22
23
|
export interface EditorUIStateContextValue {
|
|
23
24
|
// DOMツリー
|
|
@@ -81,7 +82,7 @@ export function EditorUIStateProvider({
|
|
|
81
82
|
const restoreFocus = useCallback(() => {
|
|
82
83
|
const iframeDoc = getIframeDoc();
|
|
83
84
|
if (!iframeDoc) {
|
|
84
|
-
|
|
85
|
+
debugLog('[EditorUIStateContext] restoreFocus: iframe doc not available');
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
87
88
|
|
|
@@ -89,14 +90,14 @@ export function EditorUIStateProvider({
|
|
|
89
90
|
const editingElement = iframeDoc.querySelector('[contenteditable="true"]') as HTMLElement;
|
|
90
91
|
if (editingElement) {
|
|
91
92
|
editingElement.focus();
|
|
92
|
-
|
|
93
|
+
debugLog('[EditorUIStateContext] restoreFocus: focused editing element');
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
97
|
// iframeのbodyにフォーカスを設定
|
|
97
98
|
if (iframeDoc.body) {
|
|
98
99
|
iframeDoc.body.focus();
|
|
99
|
-
|
|
100
|
+
debugLog('[EditorUIStateContext] restoreFocus: focused iframe body');
|
|
100
101
|
}
|
|
101
102
|
}, [getIframeDoc]);
|
|
102
103
|
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import React, { createContext, useContext, useState, useCallback, useMemo, useRef } from 'react';
|
|
25
|
+
import { debugLog } from '../utils/debug';
|
|
25
26
|
import type {
|
|
26
27
|
CSSVariableDefinition,
|
|
27
28
|
CSSVariableCategory,
|
|
@@ -264,7 +265,7 @@ export function EditorVariablesProvider({
|
|
|
264
265
|
await onSave(variables);
|
|
265
266
|
savedVariablesRef.current = [...variables];
|
|
266
267
|
setHasChanges(false);
|
|
267
|
-
|
|
268
|
+
debugLog('[EditorVariablesContext] Variables saved successfully');
|
|
268
269
|
} catch (error) {
|
|
269
270
|
console.error('[EditorVariablesContext] Failed to save variables:', error);
|
|
270
271
|
throw error;
|
|
@@ -286,7 +287,7 @@ export function EditorVariablesProvider({
|
|
|
286
287
|
setVariablesState(loaded);
|
|
287
288
|
savedVariablesRef.current = [...loaded];
|
|
288
289
|
setHasChanges(false);
|
|
289
|
-
|
|
290
|
+
debugLog('[EditorVariablesContext] Variables loaded:', loaded.length);
|
|
290
291
|
} catch (error) {
|
|
291
292
|
console.error('[EditorVariablesContext] Failed to load variables:', error);
|
|
292
293
|
throw error;
|
|
@@ -299,7 +300,7 @@ export function EditorVariablesProvider({
|
|
|
299
300
|
const syncFromTheme = useCallback((themeVariables: CSSVariableDefinition[]) => {
|
|
300
301
|
setVariablesState(themeVariables);
|
|
301
302
|
markAsChanged();
|
|
302
|
-
|
|
303
|
+
debugLog('[EditorVariablesContext] Synced from theme:', themeVariables.length, 'variables');
|
|
303
304
|
}, [markAsChanged]);
|
|
304
305
|
|
|
305
306
|
// バルク設定
|
|
@@ -327,7 +328,7 @@ export function EditorVariablesProvider({
|
|
|
327
328
|
}
|
|
328
329
|
|
|
329
330
|
styleEl.textContent = cssString;
|
|
330
|
-
|
|
331
|
+
debugLog('[EditorVariablesContext] Injected CSS variables to iframe');
|
|
331
332
|
}, [cssString]);
|
|
332
333
|
|
|
333
334
|
// Context値
|