@growgroup/visual-editor 0.1.6 → 0.2.1
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 +59 -2
- package/dist/editor.css +1 -1
- package/package.json +7 -3
- package/src/components/ui/alert-dialog.tsx +18 -4
- package/src/components/ui/context-menu.tsx +18 -4
- package/src/components/ui/dialog.tsx +19 -5
- package/src/components/ui/dropdown-menu.tsx +18 -4
- package/src/components/ui/popover.tsx +10 -2
- package/src/components/ui/select.tsx +10 -2
- package/src/components/ui/tooltip.tsx +10 -2
- package/src/editor/EditorToolbar.tsx +10 -20
- package/src/editor/FrontendVisualEditor.tsx +157 -43
- package/src/editor/components/EditorCanvas.tsx +1 -1
- package/src/editor/components/EditorContextMenu.tsx +53 -71
- package/src/editor/components/EditorFooter.tsx +36 -70
- package/src/editor/components/EditorHeader.tsx +37 -20
- package/src/editor/components/EditorLayerPanel.tsx +7 -9
- package/src/editor/components/EditorPropertyPanel.tsx +84 -8
- package/src/editor/components/LayerTreeItem.tsx +28 -5
- package/src/editor/components/component-panel/ComponentPanel.tsx +4 -3
- package/src/editor/components/ppt/PptChrome.tsx +156 -344
- package/src/editor/components/ppt/PptComments.tsx +135 -87
- package/src/editor/components/ppt/PptDesignProposals.tsx +21 -26
- package/src/editor/components/ppt/PptNotes.tsx +15 -15
- package/src/editor/components/shell/CanvasAppearance.tsx +19 -0
- package/src/editor/components/shell/ConfirmDialog.tsx +15 -0
- package/src/editor/components/shell/EditorTopBar.tsx +29 -68
- package/src/editor/components/shell/LeftPanel.tsx +20 -94
- package/src/editor/components/shell/PagesPanel.tsx +4 -2
- package/src/editor/contexts/EditorAppearanceContext.tsx +44 -0
- package/src/editor/contexts/EditorComponentsContext.tsx +278 -16
- package/src/editor/editor-skin.css +2 -122
- package/src/editor/hooks/useComponentEditMode.ts +93 -1
- package/src/editor/hooks/useIframeSetup.ts +10 -0
- package/src/editor/hooks/useMediaLibrary.ts +4 -5
- package/src/editor/hooks/useResizablePanel.ts +40 -11
- package/src/editor/parts.ts +321 -0
- package/src/editor/utils/dom-utils.ts +5 -4
- package/src/index.ts +25 -0
- package/src/io.ts +54 -0
- package/src/styles/skin.css +159 -0
- package/src/types/editor-components.ts +23 -1
package/src/styles/skin.css
CHANGED
|
@@ -120,3 +120,162 @@
|
|
|
120
120
|
outline: 2px solid var(--ed-accent);
|
|
121
121
|
outline-offset: 1px;
|
|
122
122
|
}
|
|
123
|
+
|
|
124
|
+
/* 2026-09: 新しいエディタとそのPortalだけに適用。利用側のレールは対象外。
|
|
125
|
+
層の名前を base にしない: 利用側が Tailwind v3(PostCSS)だと dist/editor.css の
|
|
126
|
+
`@layer base` を自分の指令と誤認して「@tailwind base が無い」とビルドを止める。
|
|
127
|
+
独自名の層なら素通しされ、層なしの utilities が上書きする関係も保てる。 */
|
|
128
|
+
@layer gg-editor-defaults {
|
|
129
|
+
.gg-editor-ui { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Sans", Meiryo, sans-serif; font-size: 13px; line-height: 1.5; text-align: left; }
|
|
130
|
+
.gg-editor-ui, .gg-editor-ui :where(*, *::before, *::after) { box-sizing: border-box; border-width: 0; border-style: solid; }
|
|
131
|
+
.gg-editor-ui :where(button, input, textarea, select) { font: inherit; color: inherit; margin: 0; }
|
|
132
|
+
.gg-editor-ui :where(button) { background: transparent; cursor: pointer; }
|
|
133
|
+
.gg-editor-ui :where(button:disabled) { cursor: default; }
|
|
134
|
+
.gg-editor-ui :where(p, h1, h2, h3, h4, ul, ol) { margin: 0; padding: 0; }
|
|
135
|
+
.gg-editor-ui :where(svg) { display: block; flex-shrink: 0; }
|
|
136
|
+
.gg-editor-ui :where(input, textarea) { min-width: 0; }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.gg-editor-ui {
|
|
140
|
+
color-scheme: light;
|
|
141
|
+
--ed-bg: #eef1f5;
|
|
142
|
+
--ed-panel: #ffffff;
|
|
143
|
+
--ed-panel-raised: #ffffff;
|
|
144
|
+
--ed-surface: #f3f5f8;
|
|
145
|
+
--ed-surface-hover: #e8edf4;
|
|
146
|
+
--ed-text: #202938;
|
|
147
|
+
--ed-muted: #566477;
|
|
148
|
+
--ed-line: #dce2ea;
|
|
149
|
+
--ed-line-strong: #7b889a;
|
|
150
|
+
--ed-accent: #2459c4;
|
|
151
|
+
--ed-accent-strong: #2459c4;
|
|
152
|
+
--ed-accent-hover: #1c48a2;
|
|
153
|
+
--ed-accent-soft: #eaf0ff;
|
|
154
|
+
--ed-success: #216346;
|
|
155
|
+
--ed-warning: #885500;
|
|
156
|
+
--ed-danger: #b42332;
|
|
157
|
+
--ed-shadow: 0 8px 32px #20293814, 0 2px 8px #2029380a;
|
|
158
|
+
--ed-radius: 8px;
|
|
159
|
+
--ed-focus: 0 0 0 2px var(--ed-panel), 0 0 0 4px var(--ed-accent);
|
|
160
|
+
--color-background: var(--ed-panel);
|
|
161
|
+
--color-foreground: var(--ed-text);
|
|
162
|
+
--color-card: var(--ed-panel);
|
|
163
|
+
--color-card-foreground: var(--ed-text);
|
|
164
|
+
--color-popover: var(--ed-panel-raised);
|
|
165
|
+
--color-popover-foreground: var(--ed-text);
|
|
166
|
+
--color-primary: var(--ed-accent-strong);
|
|
167
|
+
--color-primary-foreground: #fff;
|
|
168
|
+
--color-secondary: var(--ed-surface);
|
|
169
|
+
--color-secondary-foreground: var(--ed-text);
|
|
170
|
+
--color-muted: var(--ed-surface);
|
|
171
|
+
--color-muted-foreground: var(--ed-muted);
|
|
172
|
+
--color-accent: var(--ed-surface-hover);
|
|
173
|
+
--color-accent-foreground: var(--ed-text);
|
|
174
|
+
--color-border: var(--ed-line);
|
|
175
|
+
--color-input: var(--ed-line-strong);
|
|
176
|
+
--color-ring: var(--ed-accent);
|
|
177
|
+
--color-destructive: var(--ed-danger);
|
|
178
|
+
color: var(--ed-text);
|
|
179
|
+
accent-color: var(--ed-accent-strong);
|
|
180
|
+
scrollbar-color: var(--ed-line-strong) transparent;
|
|
181
|
+
}
|
|
182
|
+
.gg-editor-ui[data-editor-theme="dark"] {
|
|
183
|
+
color-scheme: dark;
|
|
184
|
+
--ed-bg: #191d24;
|
|
185
|
+
--ed-panel: #242932;
|
|
186
|
+
--ed-panel-raised: #2b313c;
|
|
187
|
+
--ed-surface: #303744;
|
|
188
|
+
--ed-surface-hover: #3c4554;
|
|
189
|
+
--ed-text: #f0f3f8;
|
|
190
|
+
--ed-muted: #b4bfce;
|
|
191
|
+
--ed-line: #3b4554;
|
|
192
|
+
--ed-line-strong: #7e8ca0;
|
|
193
|
+
--ed-accent: #9bbcff;
|
|
194
|
+
--ed-accent-strong: #3168d5;
|
|
195
|
+
--ed-accent-hover: #2859bc;
|
|
196
|
+
--ed-accent-soft: #303e59;
|
|
197
|
+
--ed-success: #8bddb0;
|
|
198
|
+
--ed-warning: #f3cc80;
|
|
199
|
+
--ed-danger: #ffa6ad;
|
|
200
|
+
--ed-shadow: 0 8px 32px #00000040, 0 2px 8px #00000026;
|
|
201
|
+
}
|
|
202
|
+
/* 古い固定色の部品も同じトークンへ移行。塗り見本・紙面のコンテンツは対象外。 */
|
|
203
|
+
.gg-editor-ui :is(.text-white, .text-gray-100, .text-gray-200, .text-gray-300):not([data-editor-preview] *) { color: var(--ed-text); }
|
|
204
|
+
.gg-editor-ui :is(.text-gray-400, .text-gray-500, .text-gray-600):not([data-editor-preview] *) { color: var(--ed-muted); }
|
|
205
|
+
.gg-editor-ui :is(.hover\:text-white:hover, .hover\:text-gray-200:hover):not([data-editor-preview] *) { color: var(--ed-text); }
|
|
206
|
+
.gg-editor-ui :is(.hover\:bg-\[\#444444\]:hover, .hover\:bg-\[\#383838\]:hover, .hover\:bg-white\/10:hover):not([data-editor-preview] *) { background-color: var(--ed-surface-hover); }
|
|
207
|
+
.gg-editor-ui :is(.text-sky-400, .text-\[\#7cc4ff\]):not([data-editor-preview] *) { color: var(--ed-accent); }
|
|
208
|
+
.gg-editor-ui :is(.text-red-400, .text-red-500):not([data-editor-preview] *) { color: var(--ed-danger); }
|
|
209
|
+
.gg-editor-ui :is(.bg-blue-500, .bg-blue-600, .bg-\[\#0d99ff\], .bg-green-600, .bg-purple-600, .bg-red-500, .bg-black\/90).text-white,
|
|
210
|
+
.gg-editor-ui :is(.bg-blue-500, .bg-blue-600, .bg-\[\#0d99ff\], .bg-green-600, .bg-purple-600, .bg-red-500, .bg-black\/90) .text-white:not([data-editor-preview] *) { color: #fff; }
|
|
211
|
+
.gg-editor-ui :is(.bg-\[\#0d99ff\]\/20, .bg-\[\#0d99ff\]\/15, .bg-\[\#0d99ff\]\/30):not([data-editor-preview] *) { background-color: var(--ed-accent-soft); }
|
|
212
|
+
.gg-editor-ui :is(.bg-white\/15, .bg-white\/10):not([data-editor-preview] *) { background-color: var(--ed-line); }
|
|
213
|
+
.gg-editor-ui :is(.shadow-lg, .shadow-xl, .shadow-2xl):not([data-editor-preview] *) { box-shadow: var(--ed-shadow); }
|
|
214
|
+
.gg-editor-ui :is([class*="text-[6px]"], [class*="text-[7px]"], [class*="text-[8px]"], [class*="text-[9px]"], [class*="text-[9.5px]"], [class*="text-[10px]"], [class*="text-[10.5px]"], [class*="text-[11px]"], [class*="text-[11.5px]"]):not([data-editor-preview] *) { font-size: 12px; }
|
|
215
|
+
.gg-editor-ui :is(button, input, select, textarea, a, [tabindex]):focus-visible {
|
|
216
|
+
outline: 2px solid var(--ed-accent); outline-offset: 2px; box-shadow: none;
|
|
217
|
+
}
|
|
218
|
+
.gg-editor-ui :is(input, textarea):not([data-editor-preview] *)::placeholder { color: var(--ed-muted); opacity: 1; }
|
|
219
|
+
.gg-editor-ui .ed-icon-button, .gg-editor-ui .ed-button {
|
|
220
|
+
display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; gap: 8px;
|
|
221
|
+
height: 36px; padding: 0 12px; border: 1px solid var(--ed-line); border-radius: var(--ed-radius);
|
|
222
|
+
color: var(--ed-text); background: var(--ed-panel); font-size: 13px; font-weight: 500; white-space: nowrap;
|
|
223
|
+
}
|
|
224
|
+
.gg-editor-ui .ed-icon-button { width: 32px; height: 32px; padding: 0; border-color: transparent; background: transparent; }
|
|
225
|
+
.gg-editor-ui :is(.ed-button, .ed-icon-button):hover:not([data-editor-preview] *) { background: var(--ed-surface-hover); }
|
|
226
|
+
.gg-editor-ui :is(.ed-button, .ed-icon-button)[aria-pressed="true"]:not([data-editor-preview] *) { background: var(--ed-accent-soft); color: var(--ed-accent); border-color: var(--ed-accent); }
|
|
227
|
+
.gg-editor-ui .ed-button-primary { background: var(--ed-accent-strong); color: #fff; border-color: transparent; }
|
|
228
|
+
.gg-editor-ui .ed-button-primary:hover { background: var(--ed-accent-hover); }
|
|
229
|
+
.gg-editor-ui button:disabled { opacity: .45; }
|
|
230
|
+
.gg-editor-ui [data-editor-topbar] { height: 56px; gap: 12px; padding-inline: 16px; }
|
|
231
|
+
.gg-editor-ui [data-topbar="title"] { font-size: 14px; }
|
|
232
|
+
.gg-editor-ui .ed-save-status { display: inline-flex; align-items: center; gap: 8px; font-size: 12px; white-space: nowrap; color: var(--ed-muted); }
|
|
233
|
+
.gg-editor-ui .ed-save-status[data-save-status="dirty"] { color: var(--ed-warning); }
|
|
234
|
+
.gg-editor-ui .ed-save-status[data-save-status="error"] { color: var(--ed-danger); }
|
|
235
|
+
.gg-editor-ui .ed-save-status[data-save-status="saved"] svg { color: var(--ed-success); }
|
|
236
|
+
.gg-editor-ui .ed-panel-heading { height: 48px; display: flex; align-items: center; gap: 8px; padding: 0 16px; border-bottom: 1px solid var(--ed-line); flex-shrink: 0; font-size: 14px; font-weight: 600; }
|
|
237
|
+
.gg-editor-ui .ed-toolbar { border: 1px solid var(--ed-line); border-radius: 12px; padding: 8px; background: var(--ed-panel-raised); color: var(--ed-text); box-shadow: var(--ed-shadow); }
|
|
238
|
+
.gg-editor-ui .ed-footer { height: 36px; display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 0 16px; border-top: 1px solid var(--ed-line); background: var(--ed-panel); color: var(--ed-muted); font-size: 12px; flex-shrink: 0; }
|
|
239
|
+
.gg-editor-ui .ed-footer-hint { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
240
|
+
.gg-editor-ui .ed-empty { display: flex; flex-direction: column; align-items: flex-start; gap: 12px; padding: 24px; color: var(--ed-muted); font-size: 13px; line-height: 1.7; }
|
|
241
|
+
.gg-editor-ui .ed-empty strong { color: var(--ed-text); font-size: 14px; }
|
|
242
|
+
@media (prefers-reduced-motion: reduce) {
|
|
243
|
+
.gg-editor-ui *, .gg-editor-ui *::before, .gg-editor-ui *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; scroll-behavior: auto !important; }
|
|
244
|
+
}
|
|
245
|
+
.gg-editor-ui .ed-panel-tabs { display: flex; height: 48px; width: 100%; flex-shrink: 0; gap: 4px; padding: 8px; border-bottom: 1px solid var(--ed-line); border-radius: 0; background: var(--ed-panel); }
|
|
246
|
+
.gg-editor-ui .ed-panel-tabs > button { flex: 1; font-size: 12px; height: 32px; }
|
|
247
|
+
.gg-editor-ui .ed-panel-tabs > [data-state="active"] { background: var(--ed-accent-soft); color: var(--ed-accent); box-shadow: none; }
|
|
248
|
+
.gg-editor-ui .ed-panel-tab-content { margin: 0; min-height: 0; flex: 1; display: flex; }
|
|
249
|
+
.gg-editor-ui .ed-panel-tab-content[data-state="inactive"] { display: none; }
|
|
250
|
+
.gg-editor-ui .ed-resize-handle { width: 6px; background-color: transparent; }
|
|
251
|
+
.gg-editor-ui .ed-resize-handle:hover, .gg-editor-ui .ed-resize-handle:focus-visible { background-color: var(--ed-accent); }
|
|
252
|
+
.gg-editor-ui .ed-layer-selected { box-shadow: inset 3px 0 var(--ed-accent); }
|
|
253
|
+
.gg-editor-ui .ed-layer-selected :is(.text-white, .text-blue-100, .text-blue-200) { color: var(--ed-accent); }
|
|
254
|
+
.gg-editor-ui [data-property-panel] input:not([type="color"]), .gg-editor-ui [data-property-panel] select { min-height: 28px; }
|
|
255
|
+
.gg-editor-ui [data-property-panel] button[data-state] { min-height: 32px; }
|
|
256
|
+
.gg-editor-ui [data-property-panel] .ed-panel-heading { position: sticky; top: 0; z-index: 1; }
|
|
257
|
+
/* ポータル自身も配色スコープ。利用側にToasterがある場合もその設定を奪わない。 */
|
|
258
|
+
.gg-editor-ui[data-editor-surface="popup"], .gg-editor-ui[data-editor-surface="dialog"], .gg-editor-ui[data-editor-surface="tooltip"] {
|
|
259
|
+
color: var(--ed-text); background-color: var(--ed-panel-raised); border-color: var(--ed-line); border-radius: 12px; box-shadow: var(--ed-shadow); z-index: 10000;
|
|
260
|
+
}
|
|
261
|
+
.gg-editor-ui[data-editor-surface="popup"] { max-width: calc(100vw - 24px); }
|
|
262
|
+
.gg-editor-ui[data-editor-surface="dialog"] { max-width: min(var(--container-7xl, 80rem), calc(100vw - 48px)); max-height: calc(100dvh - 48px); overflow-y: auto; border-radius: 16px; }
|
|
263
|
+
.gg-editor-ui[data-editor-surface="dialog"].max-w-lg { max-width: min(32rem, calc(100vw - 48px)); }
|
|
264
|
+
.gg-editor-ui[data-editor-surface="overlay"] { background: #10182766; z-index: 9999; }
|
|
265
|
+
.gg-editor-ui[data-editor-surface="tooltip"] { border: 1px solid var(--ed-line); font-size: 12px; }
|
|
266
|
+
.gg-editor-ui [role="menuitem"], .gg-editor-ui [role="menuitemcheckbox"], .gg-editor-ui [role="menuitemradio"] { min-height: 32px; font-size: 13px; }
|
|
267
|
+
.gg-editor-ui :is([role="menuitem"], [role="menuitemcheckbox"], [role="menuitemradio"], [role="option"])[data-highlighted]:not([data-editor-preview] *) { background-color: var(--ed-accent-soft); color: var(--ed-accent); }
|
|
268
|
+
.gg-editor-ui[data-editor-surface="dialog"] > button:last-child { min-width: 28px; min-height: 28px; display: flex; align-items: center; justify-content: center; }
|
|
269
|
+
.gg-editor-ui .ed-comments-panel { background: var(--ed-surface); }
|
|
270
|
+
.gg-editor-ui .ed-comment-thread p { overflow-wrap: anywhere; }
|
|
271
|
+
.gg-editor-ui .ed-comment-thread button { min-height: 28px; }
|
|
272
|
+
.gg-editor-ui .ed-comments-panel textarea { padding: 12px; border-radius: 8px; min-height: 88px; line-height: 1.6; }
|
|
273
|
+
.gg-editor-ui .ed-comment-thread > div:first-child > button { min-width: 28px; }
|
|
274
|
+
.gg-editor-ui .ed-ribbon [role="tab"] { height: 40px; font-size: 13px; padding-inline: 12px; }
|
|
275
|
+
.gg-editor-ui .ed-ribbon [role="tabpanel"] { padding-inline: 16px; gap: 4px; }
|
|
276
|
+
.gg-editor-ui .ed-ribbon [role="tabpanel"] button { min-height: 28px; }
|
|
277
|
+
.gg-editor-ui .ed-ribbon [role="tabpanel"] input:not([type="color"]), .gg-editor-ui .ed-ribbon [role="tabpanel"] select { min-height: 28px; }
|
|
278
|
+
.gg-editor-ui .ed-ribbon [role="tab"][data-state="active"] { color: var(--ed-accent); }
|
|
279
|
+
|
|
280
|
+
/* 入力の識別に必要な境界は、レイアウトの薄い区切り線とは分ける。 */
|
|
281
|
+
.gg-editor-ui :is(input:not([type="color"]), textarea, select):not([data-editor-preview] *) { border-color: var(--ed-line-strong) !important; }
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* Firestore Timestamps are converted on read/write operations.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import type { EditorPartDef } from '../io';
|
|
12
|
+
|
|
11
13
|
// =============================================================================
|
|
12
14
|
// 1. Overridable Properties
|
|
13
15
|
// =============================================================================
|
|
@@ -928,11 +930,31 @@ export interface EditorComponentsActions {
|
|
|
928
930
|
clearNavigationRequest: () => void;
|
|
929
931
|
}
|
|
930
932
|
|
|
933
|
+
/**
|
|
934
|
+
* 部品(HTML の template)モードの操作。io.loadParts が渡されているときだけ意味を持つ。
|
|
935
|
+
* 詳細は src/editor/parts.ts。
|
|
936
|
+
*/
|
|
937
|
+
export interface EditorPartsActions {
|
|
938
|
+
/** io.loadParts が渡されているか。true のとき部品パネルは HTML 部品を使い、挿入は実体化になる */
|
|
939
|
+
partsMode: boolean;
|
|
940
|
+
getPartDef: (id: string) => EditorPartDef | null;
|
|
941
|
+
/** 定義を実体化した要素(まだページには入っていない)。定義が無ければ null */
|
|
942
|
+
materializePartInstance: (id: string, doc: Document) => HTMLElement | null;
|
|
943
|
+
/** 選択要素を新しい部品として保存し、その要素をその場でインスタンスにする */
|
|
944
|
+
savePartFromElement: (
|
|
945
|
+
el: HTMLElement,
|
|
946
|
+
meta: { name: string; category?: string; description?: string }
|
|
947
|
+
) => Promise<EditorPartDef>;
|
|
948
|
+
/** インスタンスの今の姿で定義を更新する(版 +1)。インスタンスでなければ null */
|
|
949
|
+
updatePartFromElement: (el: HTMLElement) => Promise<EditorPartDef | null>;
|
|
950
|
+
}
|
|
951
|
+
|
|
931
952
|
/**
|
|
932
953
|
* Combined context value type.
|
|
933
954
|
*/
|
|
934
955
|
export type EditorComponentsContextValue = EditorComponentsState &
|
|
935
|
-
EditorComponentsActions
|
|
956
|
+
EditorComponentsActions &
|
|
957
|
+
EditorPartsActions;
|
|
936
958
|
|
|
937
959
|
// =============================================================================
|
|
938
960
|
// Alias for backward compatibility
|