@growgroup/visual-editor 0.1.2 → 0.1.4

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.
@@ -1,21 +1,31 @@
1
1
  "use client";
2
2
 
3
3
  /**
4
- * Figma風UIの左パネル(2段構成)。
4
+ * Figma風UIの左パネル。
5
+ *
6
+ * 既定は2段構成:
5
7
  * 上: ページ切替(PagesPanel。スライドのプレビュー付き)
6
8
  * 下: 現在ページのレイヤーツリー(EditorLayerPanel hideSlideList)
7
9
  *
10
+ * `pagesSlot` を渡すと、その中身と レイヤーツリー をタブで出し分ける。
11
+ * ページ一覧を利用側が持つ場合(Webページ・ページ数が固定のサイト等)に、
12
+ * 左端へ独自パネルをもう1枚並べずに済ませるための口。左に2枚パネルが並ぶと
13
+ * 「左=1枚 / 中央=キャンバス / 右=プロパティ」というFigmaの骨格が崩れる。
14
+ *
8
15
  * 幅はこのラッパーが1本のハンドルで持つ。段ごとに幅・ハンドルを持たせると
9
16
  * 上下で幅が食い違ったり、リサイズが二重になったりするため。
10
17
  */
11
18
 
19
+ import { useState, type ReactNode } from 'react';
12
20
  import { useEditorContext } from '../../EditorContext';
13
21
  import { useResizablePanel } from '../../hooks/useResizablePanel';
14
22
  import { EditorLayerPanel } from '../EditorLayerPanel';
15
23
  import { PagesPanel } from './PagesPanel';
16
24
  import { can } from '../../../io';
17
25
 
18
- export function LeftPanel({ page }: { page: number }) {
26
+ type LeftTab = 'pages' | 'layers';
27
+
28
+ export function LeftPanel({ page, pagesSlot }: { page: number; pagesSlot?: ReactNode }) {
19
29
  // 一覧のサムネイルは利用側が描く(io.renderContent)。描けないなら段ごと出さない。
20
30
  // Webページの用途ではページ切替を利用側のUIが持つことが多い
21
31
  const { editorMode } = useEditorContext();
@@ -28,16 +38,70 @@ export function LeftPanel({ page }: { page: number }) {
28
38
  storageKey: 'gg-editor:left-panel-width',
29
39
  });
30
40
 
41
+ /**
42
+ * 既定は「ページ」。レイヤーは要素を触りはじめてから使うもので、
43
+ * 開いた直後に知りたいのは「どのページを見ているか」のため
44
+ */
45
+ const [tab, setTab] = useState<LeftTab>('pages');
46
+
31
47
  return (
32
48
  <div
33
- className="relative flex shrink-0 flex-col overflow-hidden"
49
+ className="relative flex shrink-0 flex-col overflow-hidden border-r border-[#444444] bg-[#2c2c2c]"
34
50
  style={{ width: `${width}px` }}
35
51
  >
36
- {showPages && <PagesPanel page={page} />}
37
- {/* 残りの高さいっぱいにレイヤーツリー(自前でスクロールする) */}
38
- <div className="flex min-h-0 flex-1">
39
- <EditorLayerPanel hideSlideList />
40
- </div>
52
+ {pagesSlot ? (
53
+ <>
54
+ <div
55
+ role="tablist"
56
+ aria-label="左パネル"
57
+ className="flex shrink-0 items-stretch gap-1 border-b border-[#444444] px-1"
58
+ >
59
+ {([
60
+ ['pages', 'ページ'],
61
+ ['layers', 'レイヤー'],
62
+ ] as const).map(([key, label]) => {
63
+ const active = tab === key;
64
+ return (
65
+ <button
66
+ key={key}
67
+ type="button"
68
+ role="tab"
69
+ aria-selected={active}
70
+ data-left-tab={key}
71
+ onClick={() => setTab(key)}
72
+ className={`relative flex-1 px-2 py-2 text-[11px] font-medium transition-colors ${
73
+ active ? 'text-white' : 'text-gray-400 hover:text-gray-200'
74
+ }`}
75
+ >
76
+ {label}
77
+ {active && (
78
+ <span className="absolute inset-x-2 bottom-0 h-0.5 rounded-full bg-[#0d99ff]" />
79
+ )}
80
+ </button>
81
+ );
82
+ })}
83
+ </div>
84
+
85
+ {/*
86
+ 両方をマウントしたまま出し分ける。片方を外すと、戻ったときに
87
+ スクロール位置・検索語・展開状態が毎回初期化される
88
+ */}
89
+ <div className={`min-h-0 flex-1 flex-col ${tab === 'pages' ? 'flex' : 'hidden'}`}>
90
+ {pagesSlot}
91
+ </div>
92
+ <div className={`min-h-0 flex-1 ${tab === 'layers' ? 'flex' : 'hidden'}`}>
93
+ <EditorLayerPanel hideSlideList />
94
+ </div>
95
+ </>
96
+ ) : (
97
+ <>
98
+ {showPages && <PagesPanel page={page} />}
99
+ {/* 残りの高さいっぱいにレイヤーツリー(自前でスクロールする) */}
100
+ <div className="flex min-h-0 flex-1">
101
+ <EditorLayerPanel hideSlideList />
102
+ </div>
103
+ </>
104
+ )}
41
105
 
42
106
  {/* リサイズハンドル(全高。上下どちらの段からでも掴める) */}
43
107
  <div {...resizeHandleProps} />
package/src/index.ts CHANGED
@@ -27,6 +27,21 @@ export { WEBPAGE_WIDTH, WEBPAGE_MIN_HEIGHT, SLIDE_WIDTH, SLIDE_HEIGHT, BREAKPOIN
27
27
 
28
28
  export { registerAutoSaveFlush, flushAutoSave } from "./editor/autosave";
29
29
 
30
+ /**
31
+ * コメントの一覧(カンバン)。エディタとは別の画面に単独で置ける。
32
+ *
33
+ * - CommentBoardPanel … 配線済み。setEditorIO さえ渡してあれば props なしで動く
34
+ * - CommentBoard … 純UI。デッキも操作も自分で用意したい場合はこちら
35
+ */
36
+ export { CommentBoardPanel } from "./editor/components/comments/CommentBoardPanel";
37
+ export type { CommentBoardPanelProps } from "./editor/components/comments/CommentBoardPanel";
38
+ export { CommentBoard } from "./editor/components/comments/CommentBoard";
39
+ export type { CommentBoardProps } from "./editor/components/comments/CommentBoard";
40
+
41
+ /** 配色。CommentBoard の theme に渡す(initialPptTheme はOS設定に追従する) */
42
+ export { initialPptTheme } from "./editor/components/ppt/PptChrome";
43
+ export type { PptTheme } from "./editor/components/ppt/PptChrome";
44
+
30
45
  /** 置き場との境界。利用側が起動時に1回渡す */
31
46
  export { setEditorIO, io, can } from "./io";
32
47
  export type {