@growgroup/visual-editor 0.1.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/LICENSE +21 -0
- package/NOTICE +52 -0
- package/README.md +143 -0
- package/dist/editor.css +2 -0
- package/package.json +94 -0
- package/src/components/DeckSlideRender.tsx +21 -0
- package/src/components/SaveNote.tsx +21 -0
- package/src/components/auth/AuthProvider.tsx +44 -0
- package/src/components/ui/accordion.tsx +57 -0
- package/src/components/ui/alert-dialog.tsx +141 -0
- package/src/components/ui/badge.tsx +36 -0
- package/src/components/ui/button.tsx +56 -0
- package/src/components/ui/card.tsx +76 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/context-menu.tsx +200 -0
- package/src/components/ui/dialog.tsx +122 -0
- package/src/components/ui/dropdown-menu.tsx +201 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/popover.tsx +33 -0
- package/src/components/ui/scroll-area.tsx +48 -0
- package/src/components/ui/scrubbable-label.tsx +95 -0
- package/src/components/ui/select.tsx +159 -0
- package/src/components/ui/separator.tsx +31 -0
- package/src/components/ui/slider.tsx +28 -0
- package/src/components/ui/switch.tsx +29 -0
- package/src/components/ui/tabs.tsx +55 -0
- package/src/components/ui/textarea.tsx +22 -0
- package/src/components/ui/tooltip.tsx +32 -0
- package/src/components/viewer/useDeck.ts +58 -0
- package/src/editor/EditorContext.tsx +632 -0
- package/src/editor/EditorToolbar.tsx +446 -0
- package/src/editor/FrontendVisualEditor.tsx +2595 -0
- package/src/editor/autosave.ts +34 -0
- package/src/editor/components/AiPromptPopover.tsx +378 -0
- package/src/editor/components/BreakpointGuides.tsx +323 -0
- package/src/editor/components/BreakpointSelector.tsx +180 -0
- package/src/editor/components/ComponentPropertyEditor.tsx +426 -0
- package/src/editor/components/CssEditorDialog.tsx +168 -0
- package/src/editor/components/EditorCanvas.tsx +987 -0
- package/src/editor/components/EditorContextMenu.tsx +515 -0
- package/src/editor/components/EditorFooter.tsx +81 -0
- package/src/editor/components/EditorHeader.tsx +524 -0
- package/src/editor/components/EditorLayerPanel.tsx +1197 -0
- package/src/editor/components/EditorPropertyPanel.tsx +2996 -0
- package/src/editor/components/EditorSidebar.tsx +226 -0
- package/src/editor/components/FigmaColorPicker.tsx +1577 -0
- package/src/editor/components/HtmlEditorDialog.tsx +71 -0
- package/src/editor/components/HtmlImportDialog.tsx +346 -0
- package/src/editor/components/JsEditorDialog.tsx +175 -0
- package/src/editor/components/LayerTreeItem.tsx +494 -0
- package/src/editor/components/MasterComponentEditor.tsx +846 -0
- package/src/editor/components/MediaLibraryDialog.tsx +447 -0
- package/src/editor/components/PageSettingsDialog.tsx +776 -0
- package/src/editor/components/VariablePicker.tsx +355 -0
- package/src/editor/components/VariablesDialog.tsx +564 -0
- package/src/editor/components/VariantEditor.tsx +687 -0
- package/src/editor/components/VariantMatrix.tsx +507 -0
- package/src/editor/components/component-panel/CategoryAccordion.tsx +139 -0
- package/src/editor/components/component-panel/ComponentGrid.tsx +51 -0
- package/src/editor/components/component-panel/ComponentItem.tsx +368 -0
- package/src/editor/components/component-panel/ComponentPanel.tsx +275 -0
- package/src/editor/components/component-panel/ComponentSearch.tsx +82 -0
- package/src/editor/components/component-panel/VariantList.tsx +260 -0
- package/src/editor/components/component-panel/index.ts +12 -0
- package/src/editor/components/index.ts +48 -0
- package/src/editor/components/multi-page/CanvasZoomControls.tsx +71 -0
- package/src/editor/components/multi-page/InfiniteCanvas.tsx +95 -0
- package/src/editor/components/multi-page/MultiPageCanvasView.tsx +1404 -0
- package/src/editor/components/multi-page/PageFrameOverlay.tsx +76 -0
- package/src/editor/components/multi-page/PageLabel.tsx +34 -0
- package/src/editor/components/multi-page/PageLivePreview.tsx +242 -0
- package/src/editor/components/multi-page/PageThumbnail.tsx +59 -0
- package/src/editor/components/multi-page/index.ts +7 -0
- package/src/editor/components/ppt/PptChrome.tsx +2508 -0
- package/src/editor/components/ppt/PptComments.tsx +589 -0
- package/src/editor/components/ppt/PptDesignProposals.tsx +275 -0
- package/src/editor/components/ppt/PptFormatPane.tsx +320 -0
- package/src/editor/components/ppt/PptNotes.tsx +246 -0
- package/src/editor/components/property-panel/AlignmentPanel.tsx +110 -0
- package/src/editor/components/property-panel/AutoLayoutPanel.tsx +615 -0
- package/src/editor/components/property-panel/BorderSection.tsx +104 -0
- package/src/editor/components/property-panel/CompactNumberInput.tsx +237 -0
- package/src/editor/components/property-panel/CompactSizeInput.tsx +148 -0
- package/src/editor/components/property-panel/EffectSection.tsx +157 -0
- package/src/editor/components/property-panel/FillSection.tsx +69 -0
- package/src/editor/components/property-panel/GoogleFontPicker.tsx +192 -0
- package/src/editor/components/property-panel/ImageSection.tsx +206 -0
- package/src/editor/components/property-panel/ImgSrcSection.tsx +125 -0
- package/src/editor/components/property-panel/InstanceOverrideSection.tsx +413 -0
- package/src/editor/components/property-panel/LayoutSection.tsx +105 -0
- package/src/editor/components/property-panel/LinkSection.tsx +103 -0
- package/src/editor/components/property-panel/PropertyVariablePicker.tsx +311 -0
- package/src/editor/components/property-panel/ScalePanel.tsx +212 -0
- package/src/editor/components/property-panel/TypographySection.tsx +230 -0
- package/src/editor/components/property-panel/UnitAwareNumberInput.tsx +238 -0
- package/src/editor/components/property-panel/VariableAwareColorInput.tsx +477 -0
- package/src/editor/components/property-panel/VariableAwareInput.tsx +606 -0
- package/src/editor/components/property-panel/VariableAwareSizeInput.tsx +588 -0
- package/src/editor/components/property-panel/VariableAwareUnitInput.tsx +546 -0
- package/src/editor/components/property-panel/index.ts +24 -0
- package/src/editor/components/property-panel/unit-utils.ts +261 -0
- package/src/editor/components/shell/EditorTopBar.tsx +392 -0
- package/src/editor/components/shell/LeftPanel.tsx +47 -0
- package/src/editor/components/shell/PagesPanel.tsx +118 -0
- package/src/editor/components/variables-panel/ColorValueCell.tsx +165 -0
- package/src/editor/components/variables-panel/TextValueCell.tsx +116 -0
- package/src/editor/components/variables-panel/VariablesPanel.tsx +518 -0
- package/src/editor/components/variables-panel/index.ts +7 -0
- package/src/editor/constants.ts +468 -0
- package/src/editor/contexts/EditorArtboardContext.tsx +229 -0
- package/src/editor/contexts/EditorComponentsContext.tsx +1320 -0
- package/src/editor/contexts/EditorDocumentContext.tsx +113 -0
- package/src/editor/contexts/EditorHistoryContext.tsx +516 -0
- package/src/editor/contexts/EditorRefsContext.tsx +204 -0
- package/src/editor/contexts/EditorSelectionContext.tsx +54 -0
- package/src/editor/contexts/EditorToolContext.tsx +58 -0
- package/src/editor/contexts/EditorUIStateContext.tsx +122 -0
- package/src/editor/contexts/EditorVariablesContext.tsx +397 -0
- package/src/editor/contexts/EditorViewContext.tsx +101 -0
- package/src/editor/contexts/MultiPageCanvasContext.tsx +498 -0
- package/src/editor/contexts/index.ts +17 -0
- package/src/editor/editor-skin.css +122 -0
- package/src/editor/hooks/index.ts +38 -0
- package/src/editor/hooks/useAiReplace.ts +397 -0
- package/src/editor/hooks/useAltMeasure.ts +67 -0
- package/src/editor/hooks/useBrowserZoomPrevention.ts +121 -0
- package/src/editor/hooks/useCanvasControls.ts +1222 -0
- package/src/editor/hooks/useComponentEditMode.ts +658 -0
- package/src/editor/hooks/useComponentInstances.ts +515 -0
- package/src/editor/hooks/useContextMenuHandler.ts +157 -0
- package/src/editor/hooks/useCoordinateTransform.ts +207 -0
- package/src/editor/hooks/useDragResize.ts +1317 -0
- package/src/editor/hooks/useDrawingMode.ts +472 -0
- package/src/editor/hooks/useEditorColors.ts +144 -0
- package/src/editor/hooks/useEditorMessages.ts +108 -0
- package/src/editor/hooks/useElementActions.ts +1754 -0
- package/src/editor/hooks/useElementSelection.ts +1573 -0
- package/src/editor/hooks/useFocusManagement.ts +249 -0
- package/src/editor/hooks/useGoogleFonts.ts +305 -0
- package/src/editor/hooks/useIframeInitializer.ts +584 -0
- package/src/editor/hooks/useIframeSetup.ts +360 -0
- package/src/editor/hooks/useImageUpload.ts +270 -0
- package/src/editor/hooks/useInfiniteCanvas.ts +301 -0
- package/src/editor/hooks/useKeyboardShortcuts.ts +205 -0
- package/src/editor/hooks/useMarqueeSelection.ts +388 -0
- package/src/editor/hooks/useMediaLibrary.ts +129 -0
- package/src/editor/hooks/usePageSettingsManager.ts +739 -0
- package/src/editor/hooks/useResizablePanel.ts +163 -0
- package/src/editor/hooks/useRichPaste.ts +255 -0
- package/src/editor/hooks/useTouchGestures.ts +197 -0
- package/src/editor/index.ts +27 -0
- package/src/editor/types/page-settings.ts +513 -0
- package/src/editor/types.ts +382 -0
- package/src/editor/utils/align-elements.ts +169 -0
- package/src/editor/utils/api-json.ts +21 -0
- package/src/editor/utils/aspect-lock.ts +50 -0
- package/src/editor/utils/component-renderer.ts +702 -0
- package/src/editor/utils/component-sync.ts +845 -0
- package/src/editor/utils/crop-mode.ts +265 -0
- package/src/editor/utils/dom-utils.ts +2238 -0
- package/src/editor/utils/element-effects.ts +218 -0
- package/src/editor/utils/eyedropper.ts +35 -0
- package/src/editor/utils/figma-export.ts +560 -0
- package/src/editor/utils/figma-kiwi-decoder.ts +722 -0
- package/src/editor/utils/figma-kiwi-encoder.ts +583 -0
- package/src/editor/utils/figma-paste.ts +2262 -0
- package/src/editor/utils/flex-reorder.ts +231 -0
- package/src/editor/utils/flex-utils.ts +1230 -0
- package/src/editor/utils/geometry.ts +232 -0
- package/src/editor/utils/grid-layout.ts +210 -0
- package/src/editor/utils/html-utils.ts +470 -0
- package/src/editor/utils/index.ts +11 -0
- package/src/editor/utils/ink-style.ts +57 -0
- package/src/editor/utils/inline-format.ts +236 -0
- package/src/editor/utils/measure-distance.ts +120 -0
- package/src/editor/utils/override-apply.ts +526 -0
- package/src/editor/utils/override-detection.ts +613 -0
- package/src/editor/utils/paste-processors.ts +609 -0
- package/src/editor/utils/paste-sanitizer.ts +244 -0
- package/src/editor/utils/restore-flow.ts +188 -0
- package/src/editor/utils/shape-library.ts +171 -0
- package/src/editor/utils/slide-root.ts +29 -0
- package/src/editor/utils/smart-guides.ts +218 -0
- package/src/editor/utils/style-utils.ts +685 -0
- package/src/editor/utils/table-edit.ts +151 -0
- package/src/editor/utils/tailwind-mappings.ts +778 -0
- package/src/editor/utils/tailwind-utils.ts +804 -0
- package/src/editor/utils/text-highlight.ts +216 -0
- package/src/editor/utils/theme-to-variables.ts +341 -0
- package/src/editor/utils/variant-resolver.ts +456 -0
- package/src/editor/utils/viewport-utils.ts +295 -0
- package/src/hooks/useEditorHistory.ts +195 -0
- package/src/hooks/useEditorShortcuts.ts +260 -0
- package/src/index.ts +40 -0
- package/src/io.ts +134 -0
- package/src/lib/agent/slide-agent/types.ts +547 -0
- package/src/lib/agent/website-agent/types.ts +805 -0
- package/src/lib/api/auth-fetch.ts +58 -0
- package/src/lib/deck.ts +69 -0
- package/src/lib/export.ts +47 -0
- package/src/lib/firebase/config.ts +29 -0
- package/src/lib/firebase/css-variables.ts +378 -0
- package/src/lib/firebase/editor-components.ts +677 -0
- package/src/lib/firebase/storage.ts +469 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles/editor.css +52 -0
- package/src/styles/skin.css +122 -0
- package/src/types/css-variables.ts +645 -0
- package/src/types/editor-components.ts +944 -0
- package/src/types/editor.ts +420 -0
- package/src/types/page-master.ts +197 -0
- package/src/types/page.ts +419 -0
- package/src/types/slide.ts +402 -0
- package/src/types/website-theme.ts +788 -0
- package/src/types/website.ts +374 -0
- package/src/vendor/firebase-firestore.ts +13 -0
- package/src/vendor/firebase-functions.ts +17 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 影・光彩の当て方を要素に合わせて切り替える。
|
|
3
|
+
*
|
|
4
|
+
* box-shadow は「ボックスの外側」に描かれるため、clip-path で切り抜いた図形
|
|
5
|
+
* (星・矢印・吹き出しなど)では影ごと切り落とされて何も見えない。
|
|
6
|
+
* 画像も同じで、透過PNGに box-shadow を掛けると絵ではなく矩形に影が出る。
|
|
7
|
+
*
|
|
8
|
+
* そこで clip-path のある要素と <img> は `filter: drop-shadow()` を使う。
|
|
9
|
+
* drop-shadow はアルファ形状に沿うので、PowerPointの見え方と一致する。
|
|
10
|
+
* それ以外(パネルなど普通の箱)は spread が使える box-shadow のままにする。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export type ShadowSpec = {
|
|
14
|
+
dx: number;
|
|
15
|
+
dy: number;
|
|
16
|
+
blur: number;
|
|
17
|
+
/** box-shadow の spread 相当。drop-shadow では二重掛けで近似する */
|
|
18
|
+
size: number;
|
|
19
|
+
color: string;
|
|
20
|
+
} | null;
|
|
21
|
+
|
|
22
|
+
export type GlowSpec = { size: number; color: string } | null;
|
|
23
|
+
|
|
24
|
+
/** filter 文字列から drop-shadow(...) だけを取り除く(明るさ等は残す) */
|
|
25
|
+
export function stripDropShadow(filter: string): string {
|
|
26
|
+
if (!filter) return '';
|
|
27
|
+
const out: string[] = [];
|
|
28
|
+
let i = 0;
|
|
29
|
+
while (i < filter.length) {
|
|
30
|
+
const rest = filter.slice(i);
|
|
31
|
+
const m = /^\s*drop-shadow\(/.exec(rest);
|
|
32
|
+
if (m) {
|
|
33
|
+
// 括弧の対応を数えて関数末尾まで飛ばす(rgba(...) の入れ子があるため)
|
|
34
|
+
let depth = 0;
|
|
35
|
+
let j = i + m[0].length - 1;
|
|
36
|
+
for (; j < filter.length; j++) {
|
|
37
|
+
if (filter[j] === '(') depth++;
|
|
38
|
+
else if (filter[j] === ')') {
|
|
39
|
+
depth--;
|
|
40
|
+
if (depth === 0) break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
i = j + 1;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const next = filter.indexOf('drop-shadow(', i);
|
|
47
|
+
const end = next === -1 ? filter.length : next;
|
|
48
|
+
out.push(filter.slice(i, end));
|
|
49
|
+
i = end;
|
|
50
|
+
}
|
|
51
|
+
return out.join(' ').replace(/\s+/g, ' ').trim();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** clip-path で切り抜かれているか(影・反射がブラウザに切られる要素) */
|
|
55
|
+
export function isClipped(el: HTMLElement): boolean {
|
|
56
|
+
if (el.style.clipPath && el.style.clipPath !== 'none') return true;
|
|
57
|
+
const cs = el.ownerDocument.defaultView?.getComputedStyle(el);
|
|
58
|
+
return !!cs && cs.clipPath !== 'none';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const FX_HOST_ATTR = 'data-gg-fx-host';
|
|
62
|
+
/** ラッパーへ移す(=図形本体ではなく枠が持つべき)スタイル */
|
|
63
|
+
const GEOMETRY_PROPS = [
|
|
64
|
+
'position', 'left', 'top', 'right', 'bottom', 'width', 'height',
|
|
65
|
+
'margin', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom',
|
|
66
|
+
'zIndex', 'transform', 'flex',
|
|
67
|
+
] as const;
|
|
68
|
+
/** ラッパーへ移す識別子(選択・ドラッグ・書き戻しの対象を枠に移すため) */
|
|
69
|
+
const IDENTITY_ATTRS = ['data-element-id', 'data-editable', 'data-gg-src', 'data-shape-type', 'data-shape-id'];
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 効果(影・反射)を載せる要素を返す。
|
|
73
|
+
*
|
|
74
|
+
* clip-path のある要素は、自分に付けた drop-shadow も -webkit-box-reflect も
|
|
75
|
+
* 切り抜きで消えてしまう(Chromeで実測)。そこで図形を枠<div>で包み、
|
|
76
|
+
* 効果は枠が持ち、切り抜きは中身が持つ形にする。枠は位置・大きさと
|
|
77
|
+
* data-element-id を引き継ぐので、選択・移動・リサイズは今までどおり動く。
|
|
78
|
+
*/
|
|
79
|
+
export function getFxHost(el: HTMLElement, create = false): HTMLElement {
|
|
80
|
+
const parent = el.parentElement;
|
|
81
|
+
if (el.hasAttribute(FX_HOST_ATTR)) return el;
|
|
82
|
+
if (parent?.hasAttribute(FX_HOST_ATTR)) return parent;
|
|
83
|
+
if (!isClipped(el) || !create) return el;
|
|
84
|
+
|
|
85
|
+
const doc = el.ownerDocument;
|
|
86
|
+
const win = doc.defaultView;
|
|
87
|
+
const cs = win?.getComputedStyle(el);
|
|
88
|
+
const wrap = doc.createElement('div');
|
|
89
|
+
wrap.setAttribute(FX_HOST_ATTR, '1');
|
|
90
|
+
|
|
91
|
+
wrap.style.position =
|
|
92
|
+
el.style.position || (cs && cs.position !== 'static' ? cs.position : 'relative');
|
|
93
|
+
for (const prop of GEOMETRY_PROPS) {
|
|
94
|
+
if (prop === 'position') continue;
|
|
95
|
+
const v = (el.style as unknown as Record<string, string>)[prop];
|
|
96
|
+
if (v) (wrap.style as unknown as Record<string, string>)[prop] = v;
|
|
97
|
+
}
|
|
98
|
+
if (!wrap.style.width && cs) wrap.style.width = `${el.offsetWidth}px`;
|
|
99
|
+
if (!wrap.style.height && cs) wrap.style.height = `${el.offsetHeight}px`;
|
|
100
|
+
|
|
101
|
+
el.parentElement?.insertBefore(wrap, el);
|
|
102
|
+
wrap.appendChild(el);
|
|
103
|
+
|
|
104
|
+
// 中身は枠いっぱいに置き直す
|
|
105
|
+
el.style.position = 'absolute';
|
|
106
|
+
el.style.left = '0';
|
|
107
|
+
el.style.top = '0';
|
|
108
|
+
el.style.right = '';
|
|
109
|
+
el.style.bottom = '';
|
|
110
|
+
el.style.width = '100%';
|
|
111
|
+
el.style.height = '100%';
|
|
112
|
+
el.style.margin = '0';
|
|
113
|
+
el.style.transform = '';
|
|
114
|
+
el.style.zIndex = '';
|
|
115
|
+
|
|
116
|
+
for (const name of IDENTITY_ATTRS) {
|
|
117
|
+
const v = el.getAttribute(name);
|
|
118
|
+
if (v !== null) {
|
|
119
|
+
wrap.setAttribute(name, v);
|
|
120
|
+
el.removeAttribute(name);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if (el.classList.contains('selected')) {
|
|
124
|
+
el.classList.remove('selected');
|
|
125
|
+
wrap.classList.add('selected');
|
|
126
|
+
}
|
|
127
|
+
return wrap;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** 効果が全部外れた枠は畳んで元の1要素に戻す */
|
|
131
|
+
export function unwrapFxHostIfEmpty(host: HTMLElement): void {
|
|
132
|
+
if (!host.hasAttribute(FX_HOST_ATTR)) return;
|
|
133
|
+
const hasFx =
|
|
134
|
+
!!stripDropShadow(host.style.filter) ||
|
|
135
|
+
/drop-shadow\(/.test(host.style.filter || '') ||
|
|
136
|
+
!!host.style.getPropertyValue('-webkit-box-reflect') ||
|
|
137
|
+
!!host.style.getPropertyValue('-webkit-mask-image');
|
|
138
|
+
if (hasFx) return;
|
|
139
|
+
|
|
140
|
+
const inner = host.firstElementChild as HTMLElement | null;
|
|
141
|
+
if (!inner || host.children.length !== 1) return;
|
|
142
|
+
|
|
143
|
+
for (const prop of GEOMETRY_PROPS) {
|
|
144
|
+
const v = (host.style as unknown as Record<string, string>)[prop];
|
|
145
|
+
if (v) (inner.style as unknown as Record<string, string>)[prop] = v;
|
|
146
|
+
}
|
|
147
|
+
for (const name of IDENTITY_ATTRS) {
|
|
148
|
+
const v = host.getAttribute(name);
|
|
149
|
+
if (v !== null) inner.setAttribute(name, v);
|
|
150
|
+
}
|
|
151
|
+
if (host.classList.contains('selected')) inner.classList.add('selected');
|
|
152
|
+
host.parentElement?.insertBefore(inner, host);
|
|
153
|
+
host.remove();
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** 塗り・枠線など「図形本体」に当てるべき要素(枠が選択されている場合は中身) */
|
|
157
|
+
export function paintTarget(el: HTMLElement): HTMLElement {
|
|
158
|
+
if (!el.hasAttribute(FX_HOST_ATTR)) return el;
|
|
159
|
+
const inner = el.firstElementChild as HTMLElement | null;
|
|
160
|
+
return inner && isClipped(inner) ? inner : el;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/** この要素(の中身)は形が矩形でないか = drop-shadow で影を付けるべきか */
|
|
164
|
+
export function usesDropShadow(el: HTMLElement): boolean {
|
|
165
|
+
if (el.tagName === 'IMG') return true;
|
|
166
|
+
if (isClipped(el)) return true;
|
|
167
|
+
if (el.hasAttribute(FX_HOST_ATTR)) return true;
|
|
168
|
+
const only = el.children.length === 1 ? (el.children[0] as HTMLElement) : null;
|
|
169
|
+
return !!only && (only.tagName === 'IMG' || isClipped(only));
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** 影と光彩をまとめて当てる(両方 null で解除)。戻り値は実際に効果を持った要素 */
|
|
173
|
+
export function applyShadowAndGlow(el: HTMLElement, shadow: ShadowSpec, glow: GlowSpec): HTMLElement {
|
|
174
|
+
// 切り抜き図形は枠に載せないと影が消える
|
|
175
|
+
const host = shadow || glow ? getFxHost(el, true) : getFxHost(el);
|
|
176
|
+
if (host !== el) {
|
|
177
|
+
// 図形本体に残っている古い影は消しておく
|
|
178
|
+
el.style.boxShadow = '';
|
|
179
|
+
el.style.filter = stripDropShadow(el.style.filter);
|
|
180
|
+
}
|
|
181
|
+
const target = host;
|
|
182
|
+
const base = stripDropShadow(target.style.filter);
|
|
183
|
+
|
|
184
|
+
if (usesDropShadow(target)) {
|
|
185
|
+
const parts: string[] = [];
|
|
186
|
+
if (shadow) {
|
|
187
|
+
parts.push(`drop-shadow(${shadow.dx}px ${shadow.dy}px ${shadow.blur}px ${shadow.color})`);
|
|
188
|
+
// drop-shadow に spread は無いので、0距離の影を重ねて太らせる
|
|
189
|
+
if (shadow.size > 0) parts.push(`drop-shadow(0 0 ${shadow.size}px ${shadow.color})`);
|
|
190
|
+
}
|
|
191
|
+
if (glow) parts.push(`drop-shadow(0 0 ${glow.size}px ${glow.color})`);
|
|
192
|
+
target.style.boxShadow = '';
|
|
193
|
+
target.style.filter = [base, ...parts].filter(Boolean).join(' ');
|
|
194
|
+
if (!shadow && !glow) unwrapFxHostIfEmpty(target);
|
|
195
|
+
return target;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const shadows: string[] = [];
|
|
199
|
+
if (shadow) shadows.push(`${shadow.dx}px ${shadow.dy}px ${shadow.blur}px ${shadow.size}px ${shadow.color}`);
|
|
200
|
+
if (glow) shadows.push(`0 0 ${glow.size}px ${Math.round(glow.size / 3)}px ${glow.color}`);
|
|
201
|
+
target.style.filter = base;
|
|
202
|
+
target.style.boxShadow = shadows.join(', ');
|
|
203
|
+
if (!shadow && !glow) unwrapFxHostIfEmpty(target);
|
|
204
|
+
return target;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** リボンの影プリセット(なし/弱/中/強)。切り抜き図形でも見えるようにする */
|
|
208
|
+
export function applyShadowPreset(el: HTMLElement, preset: 'none' | 'sm' | 'md' | 'lg'): HTMLElement {
|
|
209
|
+
const spec: ShadowSpec =
|
|
210
|
+
preset === 'none'
|
|
211
|
+
? null
|
|
212
|
+
: preset === 'sm'
|
|
213
|
+
? { dx: 0, dy: 1, blur: 3, size: 0, color: 'rgba(0,0,0,0.25)' }
|
|
214
|
+
: preset === 'md'
|
|
215
|
+
? { dx: 0, dy: 4, blur: 12, size: 0, color: 'rgba(0,0,0,0.3)' }
|
|
216
|
+
: { dx: 0, dy: 10, blur: 28, size: 0, color: 'rgba(0,0,0,0.4)' };
|
|
217
|
+
return applyShadowAndGlow(el, spec, null);
|
|
218
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* スポイト(画面から色を1点拾う)。ChromeのEyeDropper APIの薄いラッパー。
|
|
3
|
+
*
|
|
4
|
+
* 【呼び出しの決まり】
|
|
5
|
+
* EyeDropper.open() は**ユーザー操作(クリック等)から直接**呼ばないとブラウザに拒否される。
|
|
6
|
+
* setTimeout や await をはさんだ後から呼ばないこと。
|
|
7
|
+
*
|
|
8
|
+
* 【未対応ブラウザ】
|
|
9
|
+
* Safari / Firefox には無い。ボタン自体を出さないよう isEyeDropperSupported() で分岐する
|
|
10
|
+
* (押せるのに何も起きないボタンを置かない)。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/** この環境でスポイトが使えるか */
|
|
14
|
+
export function isEyeDropperSupported(): boolean {
|
|
15
|
+
return typeof window !== 'undefined' && 'EyeDropper' in window;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type EyeDropperLike = { open(): Promise<{ sRGBHex: string }> };
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 画面から色を1点拾って #rrggbb で返す。
|
|
22
|
+
* 未対応・ユーザーが Esc で取り消した場合は null(どちらも異常ではない)。
|
|
23
|
+
*/
|
|
24
|
+
export async function pickScreenColor(): Promise<string | null> {
|
|
25
|
+
if (!isEyeDropperSupported()) return null;
|
|
26
|
+
try {
|
|
27
|
+
const Ctor = (window as unknown as { EyeDropper: new () => EyeDropperLike }).EyeDropper;
|
|
28
|
+
const result = await new Ctor().open();
|
|
29
|
+
return result?.sRGBHex || null;
|
|
30
|
+
} catch (e) {
|
|
31
|
+
// Escでの取り消しもここに来るので、握りつぶしてよい
|
|
32
|
+
console.log('EyeDropper canceled or failed', e);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|