@growgroup/visual-editor 0.1.6 → 0.2.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.
Files changed (42) hide show
  1. package/README.md +59 -2
  2. package/dist/editor.css +1 -1
  3. package/package.json +7 -3
  4. package/src/components/ui/alert-dialog.tsx +18 -4
  5. package/src/components/ui/context-menu.tsx +18 -4
  6. package/src/components/ui/dialog.tsx +19 -5
  7. package/src/components/ui/dropdown-menu.tsx +18 -4
  8. package/src/components/ui/popover.tsx +10 -2
  9. package/src/components/ui/select.tsx +10 -2
  10. package/src/components/ui/tooltip.tsx +10 -2
  11. package/src/editor/EditorToolbar.tsx +10 -20
  12. package/src/editor/FrontendVisualEditor.tsx +157 -43
  13. package/src/editor/components/EditorCanvas.tsx +1 -1
  14. package/src/editor/components/EditorContextMenu.tsx +53 -71
  15. package/src/editor/components/EditorFooter.tsx +36 -70
  16. package/src/editor/components/EditorHeader.tsx +37 -20
  17. package/src/editor/components/EditorLayerPanel.tsx +7 -9
  18. package/src/editor/components/EditorPropertyPanel.tsx +84 -8
  19. package/src/editor/components/LayerTreeItem.tsx +28 -5
  20. package/src/editor/components/component-panel/ComponentPanel.tsx +4 -3
  21. package/src/editor/components/ppt/PptChrome.tsx +156 -344
  22. package/src/editor/components/ppt/PptComments.tsx +135 -87
  23. package/src/editor/components/ppt/PptDesignProposals.tsx +21 -26
  24. package/src/editor/components/ppt/PptNotes.tsx +15 -15
  25. package/src/editor/components/shell/CanvasAppearance.tsx +19 -0
  26. package/src/editor/components/shell/ConfirmDialog.tsx +15 -0
  27. package/src/editor/components/shell/EditorTopBar.tsx +29 -68
  28. package/src/editor/components/shell/LeftPanel.tsx +20 -94
  29. package/src/editor/components/shell/PagesPanel.tsx +4 -2
  30. package/src/editor/contexts/EditorAppearanceContext.tsx +44 -0
  31. package/src/editor/contexts/EditorComponentsContext.tsx +278 -16
  32. package/src/editor/editor-skin.css +2 -122
  33. package/src/editor/hooks/useComponentEditMode.ts +95 -1
  34. package/src/editor/hooks/useIframeSetup.ts +10 -0
  35. package/src/editor/hooks/useMediaLibrary.ts +4 -5
  36. package/src/editor/hooks/useResizablePanel.ts +40 -11
  37. package/src/editor/parts.ts +321 -0
  38. package/src/editor/utils/dom-utils.ts +5 -4
  39. package/src/index.ts +25 -0
  40. package/src/io.ts +54 -0
  41. package/src/styles/skin.css +159 -0
  42. package/src/types/editor-components.ts +23 -1
@@ -4,6 +4,8 @@ import * as React from "react"
4
4
  import * as DialogPrimitive from "@radix-ui/react-dialog"
5
5
  import { X } from "lucide-react"
6
6
 
7
+ import { useEditorAppearance } from "../../editor/contexts/EditorAppearanceContext"
8
+
7
9
  import { cn } from "../../lib/utils"
8
10
 
9
11
  const Dialog = DialogPrimitive.Root
@@ -17,27 +19,38 @@ const DialogClose = DialogPrimitive.Close
17
19
  const DialogOverlay = React.forwardRef<
18
20
  React.ElementRef<typeof DialogPrimitive.Overlay>,
19
21
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay>
20
- >(({ className, ...props }, ref) => (
22
+ >(({ className, ...props }, ref) => {
23
+ const appearance = useEditorAppearance();
24
+ return (
21
25
  <DialogPrimitive.Overlay
26
+ data-editor-theme={appearance["data-editor-theme"]}
27
+ data-editor-surface="overlay"
22
28
  ref={ref}
23
29
  className={cn(
30
+ appearance.className,
24
31
  "fixed inset-0 z-[100] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
25
32
  className
26
33
  )}
27
34
  {...props}
28
35
  />
29
- ))
36
+ );
37
+ })
30
38
  DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
31
39
 
32
40
  const DialogContent = React.forwardRef<
33
41
  React.ElementRef<typeof DialogPrimitive.Content>,
34
42
  React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
35
- >(({ className, children, ...props }, ref) => (
43
+ >(({ className, children, ...props }, ref) => {
44
+ const appearance = useEditorAppearance();
45
+ return (
36
46
  <DialogPortal>
37
47
  <DialogOverlay />
38
48
  <DialogPrimitive.Content
49
+ data-editor-theme={appearance["data-editor-theme"]}
50
+ data-editor-surface="dialog"
39
51
  ref={ref}
40
52
  className={cn(
53
+ appearance.className,
41
54
  "fixed left-[50%] top-[50%] z-[100] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
42
55
  className
43
56
  )}
@@ -46,11 +59,12 @@ const DialogContent = React.forwardRef<
46
59
  {children}
47
60
  <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
48
61
  <X className="h-4 w-4" />
49
- <span className="sr-only">Close</span>
62
+ <span className="sr-only">閉じる</span>
50
63
  </DialogPrimitive.Close>
51
64
  </DialogPrimitive.Content>
52
65
  </DialogPortal>
53
- ))
66
+ );
67
+ })
54
68
  DialogContent.displayName = DialogPrimitive.Content.displayName
55
69
 
56
70
  const DialogHeader = ({
@@ -4,6 +4,8 @@ import * as React from "react"
4
4
  import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
5
5
  import { Check, ChevronRight, Circle } from "lucide-react"
6
6
 
7
+ import { useEditorAppearance } from "../../editor/contexts/EditorAppearanceContext"
8
+
7
9
  import { cn } from "../../lib/utils"
8
10
 
9
11
  const DropdownMenu = DropdownMenuPrimitive.Root
@@ -43,28 +45,39 @@ DropdownMenuSubTrigger.displayName =
43
45
  const DropdownMenuSubContent = React.forwardRef<
44
46
  React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,
45
47
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>
46
- >(({ className, ...props }, ref) => (
48
+ >(({ className, ...props }, ref) => {
49
+ const appearance = useEditorAppearance();
50
+ return (
47
51
  <DropdownMenuPrimitive.SubContent
52
+ data-editor-theme={appearance["data-editor-theme"]}
53
+ data-editor-surface="popup"
48
54
  ref={ref}
49
55
  className={cn(
56
+ appearance.className,
50
57
  "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
51
58
  className
52
59
  )}
53
60
  {...props}
54
61
  />
55
- ))
62
+ );
63
+ })
56
64
  DropdownMenuSubContent.displayName =
57
65
  DropdownMenuPrimitive.SubContent.displayName
58
66
 
59
67
  const DropdownMenuContent = React.forwardRef<
60
68
  React.ElementRef<typeof DropdownMenuPrimitive.Content>,
61
69
  React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>
62
- >(({ className, sideOffset = 4, ...props }, ref) => (
70
+ >(({ className, sideOffset = 4, ...props }, ref) => {
71
+ const appearance = useEditorAppearance();
72
+ return (
63
73
  <DropdownMenuPrimitive.Portal>
64
74
  <DropdownMenuPrimitive.Content
75
+ data-editor-theme={appearance["data-editor-theme"]}
76
+ data-editor-surface="popup"
65
77
  ref={ref}
66
78
  sideOffset={sideOffset}
67
79
  className={cn(
80
+ appearance.className,
68
81
  "z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
69
82
  "data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
70
83
  className
@@ -72,7 +85,8 @@ const DropdownMenuContent = React.forwardRef<
72
85
  {...props}
73
86
  />
74
87
  </DropdownMenuPrimitive.Portal>
75
- ))
88
+ );
89
+ })
76
90
  DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName
77
91
 
78
92
  const DropdownMenuItem = React.forwardRef<
@@ -3,6 +3,8 @@
3
3
  import * as React from "react"
4
4
  import * as PopoverPrimitive from "@radix-ui/react-popover"
5
5
 
6
+ import { useEditorAppearance } from "../../editor/contexts/EditorAppearanceContext"
7
+
6
8
  import { cn } from "../../lib/utils"
7
9
 
8
10
  const Popover = PopoverPrimitive.Root
@@ -14,20 +16,26 @@ const PopoverAnchor = PopoverPrimitive.Anchor
14
16
  const PopoverContent = React.forwardRef<
15
17
  React.ElementRef<typeof PopoverPrimitive.Content>,
16
18
  React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>
17
- >(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
19
+ >(({ className, align = "center", sideOffset = 4, ...props }, ref) => {
20
+ const appearance = useEditorAppearance();
21
+ return (
18
22
  <PopoverPrimitive.Portal>
19
23
  <PopoverPrimitive.Content
24
+ data-editor-theme={appearance["data-editor-theme"]}
25
+ data-editor-surface="popup"
20
26
  ref={ref}
21
27
  align={align}
22
28
  sideOffset={sideOffset}
23
29
  className={cn(
30
+ appearance.className,
24
31
  "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-popover-content-transform-origin]",
25
32
  className
26
33
  )}
27
34
  {...props}
28
35
  />
29
36
  </PopoverPrimitive.Portal>
30
- ))
37
+ );
38
+ })
31
39
  PopoverContent.displayName = PopoverPrimitive.Content.displayName
32
40
 
33
41
  export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }
@@ -4,6 +4,8 @@ import * as React from "react"
4
4
  import * as SelectPrimitive from "@radix-ui/react-select"
5
5
  import { Check, ChevronDown, ChevronUp } from "lucide-react"
6
6
 
7
+ import { useEditorAppearance } from "../../editor/contexts/EditorAppearanceContext"
8
+
7
9
  import { cn } from "../../lib/utils"
8
10
 
9
11
  const Select = SelectPrimitive.Root
@@ -70,11 +72,16 @@ SelectScrollDownButton.displayName =
70
72
  const SelectContent = React.forwardRef<
71
73
  React.ElementRef<typeof SelectPrimitive.Content>,
72
74
  React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
73
- >(({ className, children, position = "popper", ...props }, ref) => (
75
+ >(({ className, children, position = "popper", ...props }, ref) => {
76
+ const appearance = useEditorAppearance();
77
+ return (
74
78
  <SelectPrimitive.Portal>
75
79
  <SelectPrimitive.Content
80
+ data-editor-theme={appearance["data-editor-theme"]}
81
+ data-editor-surface="popup"
76
82
  ref={ref}
77
83
  className={cn(
84
+ appearance.className,
78
85
  "relative z-50 max-h-[--radix-select-content-available-height] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-select-content-transform-origin]",
79
86
  position === "popper" &&
80
87
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
@@ -96,7 +103,8 @@ const SelectContent = React.forwardRef<
96
103
  <SelectScrollDownButton />
97
104
  </SelectPrimitive.Content>
98
105
  </SelectPrimitive.Portal>
99
- ))
106
+ );
107
+ })
100
108
  SelectContent.displayName = SelectPrimitive.Content.displayName
101
109
 
102
110
  const SelectLabel = React.forwardRef<
@@ -3,6 +3,8 @@
3
3
  import * as React from "react"
4
4
  import * as TooltipPrimitive from "@radix-ui/react-tooltip"
5
5
 
6
+ import { useEditorAppearance } from "../../editor/contexts/EditorAppearanceContext"
7
+
6
8
  import { cn } from "../../lib/utils"
7
9
 
8
10
  const TooltipProvider = TooltipPrimitive.Provider
@@ -14,19 +16,25 @@ const TooltipTrigger = TooltipPrimitive.Trigger
14
16
  const TooltipContent = React.forwardRef<
15
17
  React.ElementRef<typeof TooltipPrimitive.Content>,
16
18
  React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
17
- >(({ className, sideOffset = 4, ...props }, ref) => (
19
+ >(({ className, sideOffset = 4, ...props }, ref) => {
20
+ const appearance = useEditorAppearance();
21
+ return (
18
22
  <TooltipPrimitive.Portal>
19
23
  <TooltipPrimitive.Content
24
+ data-editor-theme={appearance["data-editor-theme"]}
25
+ data-editor-surface="tooltip"
20
26
  ref={ref}
21
27
  sideOffset={sideOffset}
22
28
  className={cn(
29
+ appearance.className,
23
30
  "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-tooltip-content-transform-origin]",
24
31
  className
25
32
  )}
26
33
  {...props}
27
34
  />
28
35
  </TooltipPrimitive.Portal>
29
- ))
36
+ );
37
+ })
30
38
  TooltipContent.displayName = TooltipPrimitive.Content.displayName
31
39
 
32
40
  export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }
@@ -232,7 +232,7 @@ export const EditorToolbar = memo(function EditorToolbar({
232
232
  選択中の副次操作(重ね順・グループ)は…メニューへ畳む。配色もビューアに合わせる。
233
233
  */}
234
234
  <div className="absolute bottom-5 left-1/2 z-50 max-w-[calc(100%-5rem)] -translate-x-1/2 px-0">
235
- <div className="no-scrollbar flex items-center gap-0.5 overflow-x-auto rounded-xl border border-[#444444] bg-[#2c2c2c]/95 px-1.5 py-1.5 text-gray-200 shadow-2xl shadow-black/60 backdrop-blur"
235
+ <div className="ed-toolbar no-scrollbar flex items-center gap-1 overflow-x-auto"
236
236
  style={{ scrollbarWidth: 'none', msOverflowStyle: 'none' }}>
237
237
  {/* 取り消し/やり直し */}
238
238
  <ActionButton icon={Undo2} onClick={onUndo} disabled={!canUndo} tooltip="元に戻す" shortcut={`${cmdKey}Z`} />
@@ -359,26 +359,16 @@ export const EditorToolbar = memo(function EditorToolbar({
359
359
  </>
360
360
  )}
361
361
 
362
- {/* パネル(CSS変数・コンポーネント) */}
363
- {(onOpenVariables || onOpenComponents) && (
362
+ {(onOpenVariables || onOpenComponents) && <>
364
363
  <Separator orientation="vertical" className="mx-1 h-6 bg-white/15" />
365
- )}
366
- {onOpenVariables && (
367
- <ActionButton
368
- icon={Paintbrush}
369
- onClick={onOpenVariables}
370
- tooltip="CSS変数"
371
- className={hasVariables ? 'text-sky-400' : undefined}
372
- />
373
- )}
374
- {onOpenComponents && (
375
- <ActionButton
376
- icon={Component}
377
- onClick={onOpenComponents}
378
- tooltip="コンポーネント"
379
- className={hasComponents ? 'text-sky-400' : undefined}
380
- />
381
- )}
364
+ <DropdownMenu>
365
+ <DropdownMenuTrigger asChild><Button variant="ghost" size="sm" className="h-9 gap-2 px-2 text-xs"><Component className="h-4 w-4" />パネル<ChevronDown className="h-3 w-3" /></Button></DropdownMenuTrigger>
366
+ <DropdownMenuContent side="top" align="end" className="w-52">
367
+ {onOpenComponents && <DropdownMenuItem onSelect={onOpenComponents} className="gap-2"><Component className="h-4 w-4" />コンポーネント</DropdownMenuItem>}
368
+ {onOpenVariables && <DropdownMenuItem onSelect={onOpenVariables} className="gap-2"><Paintbrush className="h-4 w-4" />CSS変数</DropdownMenuItem>}
369
+ </DropdownMenuContent>
370
+ </DropdownMenu>
371
+ </>}
382
372
 
383
373
  {/* 選択中のみ: よく使う3つを出し、残りは…メニュー */}
384
374
  {selectedCount > 0 && (