@notum-cz/strapi-plugin-tiptap-editor 1.2.0 → 1.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/dist/_chunks/{RichTextInput-EtL-yFqV.js → RichTextInput-1uZSm3Nc.js} +225 -18
- package/dist/_chunks/{RichTextInput-B8CLPOyo.mjs → RichTextInput-B4D0Djcf.mjs} +227 -20
- package/dist/_chunks/{index-sX8SY6P-.mjs → index-BVPd2eP4.mjs} +1 -1
- package/dist/_chunks/{index-yXpX_VsO.js → index-CTcKIvYv.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/ImageAltPopover.d.ts +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ const ReactDOM = require("react-dom");
|
|
|
8
8
|
const styled = require("styled-components");
|
|
9
9
|
const admin = require("@strapi/strapi/admin");
|
|
10
10
|
const icons = require("@strapi/icons");
|
|
11
|
-
const index = require("./index-
|
|
11
|
+
const index = require("./index-CTcKIvYv.js");
|
|
12
12
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
13
13
|
const React__default = /* @__PURE__ */ _interopDefault(React);
|
|
14
14
|
const ReactDOM__default = /* @__PURE__ */ _interopDefault(ReactDOM);
|
|
@@ -19909,19 +19909,53 @@ const TiptapInputStyles = styled__default.default.div`
|
|
|
19909
19909
|
margin: 0.75em 0;
|
|
19910
19910
|
}
|
|
19911
19911
|
|
|
19912
|
-
/* Image alignment —
|
|
19913
|
-
[data-align="center"]
|
|
19914
|
-
|
|
19915
|
-
margin-right: auto;
|
|
19912
|
+
/* Image alignment — text-align on the node wrapper centres the inline-block image container */
|
|
19913
|
+
[data-align="center"] {
|
|
19914
|
+
text-align: center;
|
|
19916
19915
|
}
|
|
19917
19916
|
|
|
19918
|
-
[data-align="right"]
|
|
19919
|
-
|
|
19920
|
-
margin-right: 0;
|
|
19917
|
+
[data-align="right"] {
|
|
19918
|
+
text-align: right;
|
|
19921
19919
|
}
|
|
19922
19920
|
|
|
19923
19921
|
/* data-align="left" and null: natural left flow, no rule needed */
|
|
19924
19922
|
|
|
19923
|
+
/* Inline-block wrapper for positioning the resize handle relative to the image */
|
|
19924
|
+
.image-wrapper {
|
|
19925
|
+
position: relative;
|
|
19926
|
+
display: inline-block;
|
|
19927
|
+
max-width: 100%;
|
|
19928
|
+
line-height: 0; /* prevent extra space below img */
|
|
19929
|
+
}
|
|
19930
|
+
|
|
19931
|
+
.image-wrapper img {
|
|
19932
|
+
display: block;
|
|
19933
|
+
max-width: 100%;
|
|
19934
|
+
height: auto;
|
|
19935
|
+
}
|
|
19936
|
+
|
|
19937
|
+
/* Resize handle — bottom-right corner */
|
|
19938
|
+
.image-resize-handle {
|
|
19939
|
+
position: absolute;
|
|
19940
|
+
right: -4px;
|
|
19941
|
+
bottom: -4px;
|
|
19942
|
+
width: 12px;
|
|
19943
|
+
height: 12px;
|
|
19944
|
+
background: #4945ff;
|
|
19945
|
+
border: 2px solid #fff;
|
|
19946
|
+
border-radius: 50%;
|
|
19947
|
+
cursor: nwse-resize;
|
|
19948
|
+
opacity: 0;
|
|
19949
|
+
transition: opacity 0.15s;
|
|
19950
|
+
z-index: 1;
|
|
19951
|
+
}
|
|
19952
|
+
|
|
19953
|
+
.image-wrapper:hover .image-resize-handle,
|
|
19954
|
+
.image-wrapper[data-selected] .image-resize-handle,
|
|
19955
|
+
.ProseMirror-selectednode .image-resize-handle {
|
|
19956
|
+
opacity: 1;
|
|
19957
|
+
}
|
|
19958
|
+
|
|
19925
19959
|
// Source: https://tiptap.dev/docs/editor/extensions/nodes/table
|
|
19926
19960
|
|
|
19927
19961
|
.ProseMirror {
|
|
@@ -20893,19 +20927,43 @@ function TextAlignRight(props) {
|
|
|
20893
20927
|
}
|
|
20894
20928
|
);
|
|
20895
20929
|
}
|
|
20896
|
-
function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode: deleteNode2 }) {
|
|
20930
|
+
function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode: deleteNode2, selected, extension }) {
|
|
20897
20931
|
const { formatMessage } = reactIntl.useIntl();
|
|
20898
20932
|
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false);
|
|
20899
20933
|
const [altText, setAltText] = React.useState(node.attrs.alt ?? "");
|
|
20934
|
+
const imgRef = React.useRef(null);
|
|
20935
|
+
const isResizingRef = React.useRef(false);
|
|
20936
|
+
const resizeCleanupRef = React.useRef(null);
|
|
20937
|
+
const rawResize = extension.options.resize;
|
|
20938
|
+
const resizeEnabled = typeof rawResize === "object" && !!rawResize ? rawResize.enabled : !!rawResize;
|
|
20939
|
+
const resizeOpts = resizeEnabled && typeof rawResize === "object" ? rawResize : void 0;
|
|
20940
|
+
const preserveAspectRatio = resizeOpts?.alwaysPreserveAspectRatio ?? true;
|
|
20941
|
+
const minWidth = resizeOpts?.minWidth ?? 50;
|
|
20942
|
+
const minHeight = resizeOpts?.minHeight ?? 50;
|
|
20943
|
+
const currentWidth = node.attrs.width;
|
|
20944
|
+
const currentHeight = node.attrs.height;
|
|
20945
|
+
const [widthInput, setWidthInput] = React.useState(currentWidth ? String(currentWidth) : "");
|
|
20946
|
+
const [heightInput, setHeightInput] = React.useState(currentHeight ? String(currentHeight) : "");
|
|
20900
20947
|
React.useEffect(() => {
|
|
20901
20948
|
setAltText(node.attrs.alt ?? "");
|
|
20902
20949
|
}, [node.attrs.alt]);
|
|
20950
|
+
React.useEffect(() => {
|
|
20951
|
+
setWidthInput(node.attrs.width ? String(node.attrs.width) : "");
|
|
20952
|
+
}, [node.attrs.width]);
|
|
20953
|
+
React.useEffect(() => {
|
|
20954
|
+
setHeightInput(node.attrs.height ? String(node.attrs.height) : "");
|
|
20955
|
+
}, [node.attrs.height]);
|
|
20903
20956
|
React.useEffect(() => {
|
|
20904
20957
|
if (!isPopoverOpen) return;
|
|
20905
20958
|
const handleScroll = () => setIsPopoverOpen(false);
|
|
20906
20959
|
document.addEventListener("scroll", handleScroll, true);
|
|
20907
20960
|
return () => document.removeEventListener("scroll", handleScroll, true);
|
|
20908
20961
|
}, [isPopoverOpen]);
|
|
20962
|
+
React.useEffect(() => {
|
|
20963
|
+
return () => {
|
|
20964
|
+
resizeCleanupRef.current?.();
|
|
20965
|
+
};
|
|
20966
|
+
}, []);
|
|
20909
20967
|
const currentAlign = node.attrs["data-align"];
|
|
20910
20968
|
function handleAlign(value) {
|
|
20911
20969
|
const current = node.attrs["data-align"];
|
|
@@ -20920,15 +20978,117 @@ function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode:
|
|
|
20920
20978
|
e.currentTarget.blur();
|
|
20921
20979
|
}
|
|
20922
20980
|
}
|
|
20981
|
+
function getAspectRatio() {
|
|
20982
|
+
const img = imgRef.current;
|
|
20983
|
+
if (!img || !img.naturalWidth || !img.naturalHeight) return null;
|
|
20984
|
+
return img.naturalWidth / img.naturalHeight;
|
|
20985
|
+
}
|
|
20986
|
+
function handleWidthCommit() {
|
|
20987
|
+
if (widthInput === "") {
|
|
20988
|
+
updateAttributes2({ width: null, height: null });
|
|
20989
|
+
return;
|
|
20990
|
+
}
|
|
20991
|
+
const num = parseInt(widthInput, 10);
|
|
20992
|
+
if (!isNaN(num) && num >= minWidth) {
|
|
20993
|
+
const ratio = getAspectRatio();
|
|
20994
|
+
if (preserveAspectRatio && ratio) {
|
|
20995
|
+
const newHeight = Math.round(num / ratio);
|
|
20996
|
+
updateAttributes2({ width: num, height: newHeight });
|
|
20997
|
+
} else {
|
|
20998
|
+
updateAttributes2({ width: num });
|
|
20999
|
+
}
|
|
21000
|
+
} else {
|
|
21001
|
+
setWidthInput(node.attrs.width ? String(node.attrs.width) : "");
|
|
21002
|
+
}
|
|
21003
|
+
}
|
|
21004
|
+
function handleHeightCommit() {
|
|
21005
|
+
if (heightInput === "") {
|
|
21006
|
+
updateAttributes2({ height: null, width: null });
|
|
21007
|
+
return;
|
|
21008
|
+
}
|
|
21009
|
+
const num = parseInt(heightInput, 10);
|
|
21010
|
+
if (!isNaN(num) && num >= minHeight) {
|
|
21011
|
+
const ratio = getAspectRatio();
|
|
21012
|
+
if (preserveAspectRatio && ratio) {
|
|
21013
|
+
const newWidth = Math.round(num * ratio);
|
|
21014
|
+
updateAttributes2({ width: newWidth, height: num });
|
|
21015
|
+
} else {
|
|
21016
|
+
updateAttributes2({ height: num });
|
|
21017
|
+
}
|
|
21018
|
+
} else {
|
|
21019
|
+
setHeightInput(node.attrs.height ? String(node.attrs.height) : "");
|
|
21020
|
+
}
|
|
21021
|
+
}
|
|
21022
|
+
const handleResizeStart = React.useCallback(
|
|
21023
|
+
(e) => {
|
|
21024
|
+
e.preventDefault();
|
|
21025
|
+
e.stopPropagation();
|
|
21026
|
+
isResizingRef.current = true;
|
|
21027
|
+
const startX = e.clientX;
|
|
21028
|
+
const img = imgRef.current;
|
|
21029
|
+
if (!img) return;
|
|
21030
|
+
const startWidth = img.offsetWidth;
|
|
21031
|
+
const ratio = img.naturalWidth && img.naturalHeight ? img.naturalWidth / img.naturalHeight : null;
|
|
21032
|
+
function onMouseMove(moveEvent) {
|
|
21033
|
+
const diff = moveEvent.clientX - startX;
|
|
21034
|
+
const newWidth = Math.max(minWidth, startWidth + diff);
|
|
21035
|
+
if (img) {
|
|
21036
|
+
img.style.width = `${newWidth}px`;
|
|
21037
|
+
if (preserveAspectRatio && ratio) {
|
|
21038
|
+
img.style.height = `${Math.round(newWidth / ratio)}px`;
|
|
21039
|
+
}
|
|
21040
|
+
}
|
|
21041
|
+
}
|
|
21042
|
+
function cleanup() {
|
|
21043
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
21044
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
21045
|
+
resizeCleanupRef.current = null;
|
|
21046
|
+
}
|
|
21047
|
+
function onMouseUp() {
|
|
21048
|
+
cleanup();
|
|
21049
|
+
if (img) {
|
|
21050
|
+
updateAttributes2({
|
|
21051
|
+
width: Math.round(img.offsetWidth),
|
|
21052
|
+
height: Math.round(img.offsetHeight)
|
|
21053
|
+
});
|
|
21054
|
+
}
|
|
21055
|
+
requestAnimationFrame(() => {
|
|
21056
|
+
isResizingRef.current = false;
|
|
21057
|
+
});
|
|
21058
|
+
}
|
|
21059
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
21060
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
21061
|
+
resizeCleanupRef.current = cleanup;
|
|
21062
|
+
},
|
|
21063
|
+
[updateAttributes2, preserveAspectRatio, minWidth]
|
|
21064
|
+
);
|
|
20923
21065
|
return /* @__PURE__ */ jsxRuntime.jsx(NodeViewWrapper, { "data-drag-handle": true, "data-align": node.attrs["data-align"] ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Popover.Root, { open: isPopoverOpen, onOpenChange: setIsPopoverOpen, children: [
|
|
20924
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Anchor, { children: /* @__PURE__ */ jsxRuntime.
|
|
20925
|
-
"
|
|
21066
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Popover.Anchor, { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
21067
|
+
"div",
|
|
20926
21068
|
{
|
|
20927
|
-
|
|
20928
|
-
|
|
20929
|
-
|
|
20930
|
-
|
|
20931
|
-
|
|
21069
|
+
className: "image-wrapper",
|
|
21070
|
+
"data-selected": selected || isPopoverOpen || void 0,
|
|
21071
|
+
children: [
|
|
21072
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
21073
|
+
"img",
|
|
21074
|
+
{
|
|
21075
|
+
ref: imgRef,
|
|
21076
|
+
src: node.attrs.src,
|
|
21077
|
+
alt: node.attrs.alt ?? "",
|
|
21078
|
+
style: {
|
|
21079
|
+
display: "block",
|
|
21080
|
+
maxWidth: "100%",
|
|
21081
|
+
width: currentWidth ? `${currentWidth}px` : void 0,
|
|
21082
|
+
height: currentHeight ? `${currentHeight}px` : "auto"
|
|
21083
|
+
},
|
|
21084
|
+
draggable: false,
|
|
21085
|
+
onClick: () => {
|
|
21086
|
+
if (!isResizingRef.current) setIsPopoverOpen(true);
|
|
21087
|
+
}
|
|
21088
|
+
}
|
|
21089
|
+
),
|
|
21090
|
+
resizeEnabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "image-resize-handle", onMouseDown: handleResizeStart })
|
|
21091
|
+
]
|
|
20932
21092
|
}
|
|
20933
21093
|
) }),
|
|
20934
21094
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Popover.Content, { side: "bottom", children: [
|
|
@@ -20967,6 +21127,43 @@ function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode:
|
|
|
20967
21127
|
}
|
|
20968
21128
|
)
|
|
20969
21129
|
] }),
|
|
21130
|
+
resizeEnabled && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px", padding: "8px", paddingBottom: "0" }, children: [
|
|
21131
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
21132
|
+
designSystem.TextInput,
|
|
21133
|
+
{
|
|
21134
|
+
type: "number",
|
|
21135
|
+
placeholder: "W",
|
|
21136
|
+
value: widthInput,
|
|
21137
|
+
onChange: (e) => setWidthInput(e.target.value),
|
|
21138
|
+
onBlur: handleWidthCommit,
|
|
21139
|
+
onKeyDown: handleKeyDown2,
|
|
21140
|
+
"aria-label": formatMessage({ id: "tiptap-editor.image.width", defaultMessage: "Width (px)" }),
|
|
21141
|
+
style: { minWidth: "62px", flexGrow: 1 }
|
|
21142
|
+
}
|
|
21143
|
+
),
|
|
21144
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "1.1rem", color: "#999" }, children: "×" }),
|
|
21145
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
21146
|
+
designSystem.TextInput,
|
|
21147
|
+
{
|
|
21148
|
+
type: "number",
|
|
21149
|
+
placeholder: "H",
|
|
21150
|
+
value: heightInput,
|
|
21151
|
+
onChange: (e) => setHeightInput(e.target.value),
|
|
21152
|
+
onBlur: handleHeightCommit,
|
|
21153
|
+
onKeyDown: handleKeyDown2,
|
|
21154
|
+
"aria-label": formatMessage({ id: "tiptap-editor.image.height", defaultMessage: "Height (px)" }),
|
|
21155
|
+
style: { minWidth: "62px", flexGrow: 1 }
|
|
21156
|
+
}
|
|
21157
|
+
),
|
|
21158
|
+
(currentWidth || currentHeight) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
21159
|
+
designSystem.IconButton,
|
|
21160
|
+
{
|
|
21161
|
+
onClick: () => updateAttributes2({ width: null, height: null }),
|
|
21162
|
+
label: formatMessage({ id: "tiptap-editor.image.resetSize", defaultMessage: "Reset size" }),
|
|
21163
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(icons.Cross, {})
|
|
21164
|
+
}
|
|
21165
|
+
)
|
|
21166
|
+
] }),
|
|
20970
21167
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", padding: "8px" }, children: [
|
|
20971
21168
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
20972
21169
|
designSystem.TextInput,
|
|
@@ -20992,12 +21189,21 @@ function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode:
|
|
|
20992
21189
|
] }) });
|
|
20993
21190
|
}
|
|
20994
21191
|
function ImageNodeViewReadOnly({ node }) {
|
|
21192
|
+
const rawWidth = node.attrs.width;
|
|
21193
|
+
const rawHeight = node.attrs.height;
|
|
21194
|
+
const width = typeof rawWidth === "number" ? rawWidth : null;
|
|
21195
|
+
const height = typeof rawHeight === "number" ? rawHeight : null;
|
|
20995
21196
|
return /* @__PURE__ */ jsxRuntime.jsx(NodeViewWrapper, { "data-drag-handle": true, "data-align": node.attrs["data-align"] ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
20996
21197
|
"img",
|
|
20997
21198
|
{
|
|
20998
21199
|
src: node.attrs.src,
|
|
20999
21200
|
alt: node.attrs.alt ?? "",
|
|
21000
|
-
style: {
|
|
21201
|
+
style: {
|
|
21202
|
+
maxWidth: width ? void 0 : "100%",
|
|
21203
|
+
display: "block",
|
|
21204
|
+
width: width ? `${width}px` : void 0,
|
|
21205
|
+
height: height ? `${height}px` : void 0
|
|
21206
|
+
},
|
|
21001
21207
|
draggable: false
|
|
21002
21208
|
}
|
|
21003
21209
|
) });
|
|
@@ -29500,7 +29706,8 @@ function buildExtensions(config) {
|
|
|
29500
29706
|
extensions.push(index_default.configure({ multicolor: true }));
|
|
29501
29707
|
}
|
|
29502
29708
|
if (isFeatureEnabled(config.mediaLibrary)) {
|
|
29503
|
-
|
|
29709
|
+
const mediaOpts = getFeatureOptions(config.mediaLibrary, {});
|
|
29710
|
+
extensions.push(StrapiImage.configure(mediaOpts ?? {}));
|
|
29504
29711
|
} else {
|
|
29505
29712
|
extensions.push(StrapiImage.configure({ enableContentCheck: true }));
|
|
29506
29713
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment as Fragment$1 } from "react/jsx-runtime";
|
|
2
|
-
import React, { useRef, useState, useDebugValue, useEffect, useLayoutEffect, forwardRef, createRef, memo, createElement, createContext, version, useContext, useMemo, Component } from "react";
|
|
2
|
+
import React, { useRef, useState, useDebugValue, useEffect, useLayoutEffect, forwardRef, createRef, memo, createElement, createContext, version, useContext, useMemo, Component, useCallback } from "react";
|
|
3
3
|
import { useIntl } from "react-intl";
|
|
4
4
|
import { Field, Box, Status, Typography, Flex, Button, Tooltip, Dialog, TextInput, SingleSelect, SingleSelectOption, Popover, IconButton } from "@strapi/design-system";
|
|
5
5
|
import ReactDOM, { flushSync } from "react-dom";
|
|
6
6
|
import styled from "styled-components";
|
|
7
7
|
import { useField, useFetchClient } from "@strapi/strapi/admin";
|
|
8
|
-
import { Quotes, Code as Code$1, NumberList, BulletList as BulletList$1, StrikeThrough, Underline as Underline$1, Italic as Italic$1, Bold as Bold$1, Link as Link$1, Trash, Image as Image$1, GridNine } from "@strapi/icons";
|
|
9
|
-
import { g as getMediaLibraryComponent, a as getThemeCache } from "./index-
|
|
8
|
+
import { Quotes, Code as Code$1, NumberList, BulletList as BulletList$1, StrikeThrough, Underline as Underline$1, Italic as Italic$1, Bold as Bold$1, Link as Link$1, Cross, Trash, Image as Image$1, GridNine } from "@strapi/icons";
|
|
9
|
+
import { g as getMediaLibraryComponent, a as getThemeCache } from "./index-BVPd2eP4.mjs";
|
|
10
10
|
var shim = { exports: {} };
|
|
11
11
|
var useSyncExternalStoreShim_production = {};
|
|
12
12
|
/**
|
|
@@ -19903,19 +19903,53 @@ const TiptapInputStyles = styled.div`
|
|
|
19903
19903
|
margin: 0.75em 0;
|
|
19904
19904
|
}
|
|
19905
19905
|
|
|
19906
|
-
/* Image alignment —
|
|
19907
|
-
[data-align="center"]
|
|
19908
|
-
|
|
19909
|
-
margin-right: auto;
|
|
19906
|
+
/* Image alignment — text-align on the node wrapper centres the inline-block image container */
|
|
19907
|
+
[data-align="center"] {
|
|
19908
|
+
text-align: center;
|
|
19910
19909
|
}
|
|
19911
19910
|
|
|
19912
|
-
[data-align="right"]
|
|
19913
|
-
|
|
19914
|
-
margin-right: 0;
|
|
19911
|
+
[data-align="right"] {
|
|
19912
|
+
text-align: right;
|
|
19915
19913
|
}
|
|
19916
19914
|
|
|
19917
19915
|
/* data-align="left" and null: natural left flow, no rule needed */
|
|
19918
19916
|
|
|
19917
|
+
/* Inline-block wrapper for positioning the resize handle relative to the image */
|
|
19918
|
+
.image-wrapper {
|
|
19919
|
+
position: relative;
|
|
19920
|
+
display: inline-block;
|
|
19921
|
+
max-width: 100%;
|
|
19922
|
+
line-height: 0; /* prevent extra space below img */
|
|
19923
|
+
}
|
|
19924
|
+
|
|
19925
|
+
.image-wrapper img {
|
|
19926
|
+
display: block;
|
|
19927
|
+
max-width: 100%;
|
|
19928
|
+
height: auto;
|
|
19929
|
+
}
|
|
19930
|
+
|
|
19931
|
+
/* Resize handle — bottom-right corner */
|
|
19932
|
+
.image-resize-handle {
|
|
19933
|
+
position: absolute;
|
|
19934
|
+
right: -4px;
|
|
19935
|
+
bottom: -4px;
|
|
19936
|
+
width: 12px;
|
|
19937
|
+
height: 12px;
|
|
19938
|
+
background: #4945ff;
|
|
19939
|
+
border: 2px solid #fff;
|
|
19940
|
+
border-radius: 50%;
|
|
19941
|
+
cursor: nwse-resize;
|
|
19942
|
+
opacity: 0;
|
|
19943
|
+
transition: opacity 0.15s;
|
|
19944
|
+
z-index: 1;
|
|
19945
|
+
}
|
|
19946
|
+
|
|
19947
|
+
.image-wrapper:hover .image-resize-handle,
|
|
19948
|
+
.image-wrapper[data-selected] .image-resize-handle,
|
|
19949
|
+
.ProseMirror-selectednode .image-resize-handle {
|
|
19950
|
+
opacity: 1;
|
|
19951
|
+
}
|
|
19952
|
+
|
|
19919
19953
|
// Source: https://tiptap.dev/docs/editor/extensions/nodes/table
|
|
19920
19954
|
|
|
19921
19955
|
.ProseMirror {
|
|
@@ -20887,19 +20921,43 @@ function TextAlignRight(props) {
|
|
|
20887
20921
|
}
|
|
20888
20922
|
);
|
|
20889
20923
|
}
|
|
20890
|
-
function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode: deleteNode2 }) {
|
|
20924
|
+
function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode: deleteNode2, selected, extension }) {
|
|
20891
20925
|
const { formatMessage } = useIntl();
|
|
20892
20926
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
20893
20927
|
const [altText, setAltText] = useState(node.attrs.alt ?? "");
|
|
20928
|
+
const imgRef = useRef(null);
|
|
20929
|
+
const isResizingRef = useRef(false);
|
|
20930
|
+
const resizeCleanupRef = useRef(null);
|
|
20931
|
+
const rawResize = extension.options.resize;
|
|
20932
|
+
const resizeEnabled = typeof rawResize === "object" && !!rawResize ? rawResize.enabled : !!rawResize;
|
|
20933
|
+
const resizeOpts = resizeEnabled && typeof rawResize === "object" ? rawResize : void 0;
|
|
20934
|
+
const preserveAspectRatio = resizeOpts?.alwaysPreserveAspectRatio ?? true;
|
|
20935
|
+
const minWidth = resizeOpts?.minWidth ?? 50;
|
|
20936
|
+
const minHeight = resizeOpts?.minHeight ?? 50;
|
|
20937
|
+
const currentWidth = node.attrs.width;
|
|
20938
|
+
const currentHeight = node.attrs.height;
|
|
20939
|
+
const [widthInput, setWidthInput] = useState(currentWidth ? String(currentWidth) : "");
|
|
20940
|
+
const [heightInput, setHeightInput] = useState(currentHeight ? String(currentHeight) : "");
|
|
20894
20941
|
useEffect(() => {
|
|
20895
20942
|
setAltText(node.attrs.alt ?? "");
|
|
20896
20943
|
}, [node.attrs.alt]);
|
|
20944
|
+
useEffect(() => {
|
|
20945
|
+
setWidthInput(node.attrs.width ? String(node.attrs.width) : "");
|
|
20946
|
+
}, [node.attrs.width]);
|
|
20947
|
+
useEffect(() => {
|
|
20948
|
+
setHeightInput(node.attrs.height ? String(node.attrs.height) : "");
|
|
20949
|
+
}, [node.attrs.height]);
|
|
20897
20950
|
useEffect(() => {
|
|
20898
20951
|
if (!isPopoverOpen) return;
|
|
20899
20952
|
const handleScroll = () => setIsPopoverOpen(false);
|
|
20900
20953
|
document.addEventListener("scroll", handleScroll, true);
|
|
20901
20954
|
return () => document.removeEventListener("scroll", handleScroll, true);
|
|
20902
20955
|
}, [isPopoverOpen]);
|
|
20956
|
+
useEffect(() => {
|
|
20957
|
+
return () => {
|
|
20958
|
+
resizeCleanupRef.current?.();
|
|
20959
|
+
};
|
|
20960
|
+
}, []);
|
|
20903
20961
|
const currentAlign = node.attrs["data-align"];
|
|
20904
20962
|
function handleAlign(value) {
|
|
20905
20963
|
const current = node.attrs["data-align"];
|
|
@@ -20914,15 +20972,117 @@ function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode:
|
|
|
20914
20972
|
e.currentTarget.blur();
|
|
20915
20973
|
}
|
|
20916
20974
|
}
|
|
20975
|
+
function getAspectRatio() {
|
|
20976
|
+
const img = imgRef.current;
|
|
20977
|
+
if (!img || !img.naturalWidth || !img.naturalHeight) return null;
|
|
20978
|
+
return img.naturalWidth / img.naturalHeight;
|
|
20979
|
+
}
|
|
20980
|
+
function handleWidthCommit() {
|
|
20981
|
+
if (widthInput === "") {
|
|
20982
|
+
updateAttributes2({ width: null, height: null });
|
|
20983
|
+
return;
|
|
20984
|
+
}
|
|
20985
|
+
const num = parseInt(widthInput, 10);
|
|
20986
|
+
if (!isNaN(num) && num >= minWidth) {
|
|
20987
|
+
const ratio = getAspectRatio();
|
|
20988
|
+
if (preserveAspectRatio && ratio) {
|
|
20989
|
+
const newHeight = Math.round(num / ratio);
|
|
20990
|
+
updateAttributes2({ width: num, height: newHeight });
|
|
20991
|
+
} else {
|
|
20992
|
+
updateAttributes2({ width: num });
|
|
20993
|
+
}
|
|
20994
|
+
} else {
|
|
20995
|
+
setWidthInput(node.attrs.width ? String(node.attrs.width) : "");
|
|
20996
|
+
}
|
|
20997
|
+
}
|
|
20998
|
+
function handleHeightCommit() {
|
|
20999
|
+
if (heightInput === "") {
|
|
21000
|
+
updateAttributes2({ height: null, width: null });
|
|
21001
|
+
return;
|
|
21002
|
+
}
|
|
21003
|
+
const num = parseInt(heightInput, 10);
|
|
21004
|
+
if (!isNaN(num) && num >= minHeight) {
|
|
21005
|
+
const ratio = getAspectRatio();
|
|
21006
|
+
if (preserveAspectRatio && ratio) {
|
|
21007
|
+
const newWidth = Math.round(num * ratio);
|
|
21008
|
+
updateAttributes2({ width: newWidth, height: num });
|
|
21009
|
+
} else {
|
|
21010
|
+
updateAttributes2({ height: num });
|
|
21011
|
+
}
|
|
21012
|
+
} else {
|
|
21013
|
+
setHeightInput(node.attrs.height ? String(node.attrs.height) : "");
|
|
21014
|
+
}
|
|
21015
|
+
}
|
|
21016
|
+
const handleResizeStart = useCallback(
|
|
21017
|
+
(e) => {
|
|
21018
|
+
e.preventDefault();
|
|
21019
|
+
e.stopPropagation();
|
|
21020
|
+
isResizingRef.current = true;
|
|
21021
|
+
const startX = e.clientX;
|
|
21022
|
+
const img = imgRef.current;
|
|
21023
|
+
if (!img) return;
|
|
21024
|
+
const startWidth = img.offsetWidth;
|
|
21025
|
+
const ratio = img.naturalWidth && img.naturalHeight ? img.naturalWidth / img.naturalHeight : null;
|
|
21026
|
+
function onMouseMove(moveEvent) {
|
|
21027
|
+
const diff = moveEvent.clientX - startX;
|
|
21028
|
+
const newWidth = Math.max(minWidth, startWidth + diff);
|
|
21029
|
+
if (img) {
|
|
21030
|
+
img.style.width = `${newWidth}px`;
|
|
21031
|
+
if (preserveAspectRatio && ratio) {
|
|
21032
|
+
img.style.height = `${Math.round(newWidth / ratio)}px`;
|
|
21033
|
+
}
|
|
21034
|
+
}
|
|
21035
|
+
}
|
|
21036
|
+
function cleanup() {
|
|
21037
|
+
document.removeEventListener("mousemove", onMouseMove);
|
|
21038
|
+
document.removeEventListener("mouseup", onMouseUp);
|
|
21039
|
+
resizeCleanupRef.current = null;
|
|
21040
|
+
}
|
|
21041
|
+
function onMouseUp() {
|
|
21042
|
+
cleanup();
|
|
21043
|
+
if (img) {
|
|
21044
|
+
updateAttributes2({
|
|
21045
|
+
width: Math.round(img.offsetWidth),
|
|
21046
|
+
height: Math.round(img.offsetHeight)
|
|
21047
|
+
});
|
|
21048
|
+
}
|
|
21049
|
+
requestAnimationFrame(() => {
|
|
21050
|
+
isResizingRef.current = false;
|
|
21051
|
+
});
|
|
21052
|
+
}
|
|
21053
|
+
document.addEventListener("mousemove", onMouseMove);
|
|
21054
|
+
document.addEventListener("mouseup", onMouseUp);
|
|
21055
|
+
resizeCleanupRef.current = cleanup;
|
|
21056
|
+
},
|
|
21057
|
+
[updateAttributes2, preserveAspectRatio, minWidth]
|
|
21058
|
+
);
|
|
20917
21059
|
return /* @__PURE__ */ jsx(NodeViewWrapper, { "data-drag-handle": true, "data-align": node.attrs["data-align"] ?? void 0, children: /* @__PURE__ */ jsxs(Popover.Root, { open: isPopoverOpen, onOpenChange: setIsPopoverOpen, children: [
|
|
20918
|
-
/* @__PURE__ */ jsx(Popover.Anchor, { children: /* @__PURE__ */
|
|
20919
|
-
"
|
|
21060
|
+
/* @__PURE__ */ jsx(Popover.Anchor, { children: /* @__PURE__ */ jsxs(
|
|
21061
|
+
"div",
|
|
20920
21062
|
{
|
|
20921
|
-
|
|
20922
|
-
|
|
20923
|
-
|
|
20924
|
-
|
|
20925
|
-
|
|
21063
|
+
className: "image-wrapper",
|
|
21064
|
+
"data-selected": selected || isPopoverOpen || void 0,
|
|
21065
|
+
children: [
|
|
21066
|
+
/* @__PURE__ */ jsx(
|
|
21067
|
+
"img",
|
|
21068
|
+
{
|
|
21069
|
+
ref: imgRef,
|
|
21070
|
+
src: node.attrs.src,
|
|
21071
|
+
alt: node.attrs.alt ?? "",
|
|
21072
|
+
style: {
|
|
21073
|
+
display: "block",
|
|
21074
|
+
maxWidth: "100%",
|
|
21075
|
+
width: currentWidth ? `${currentWidth}px` : void 0,
|
|
21076
|
+
height: currentHeight ? `${currentHeight}px` : "auto"
|
|
21077
|
+
},
|
|
21078
|
+
draggable: false,
|
|
21079
|
+
onClick: () => {
|
|
21080
|
+
if (!isResizingRef.current) setIsPopoverOpen(true);
|
|
21081
|
+
}
|
|
21082
|
+
}
|
|
21083
|
+
),
|
|
21084
|
+
resizeEnabled && /* @__PURE__ */ jsx("div", { className: "image-resize-handle", onMouseDown: handleResizeStart })
|
|
21085
|
+
]
|
|
20926
21086
|
}
|
|
20927
21087
|
) }),
|
|
20928
21088
|
/* @__PURE__ */ jsxs(Popover.Content, { side: "bottom", children: [
|
|
@@ -20961,6 +21121,43 @@ function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode:
|
|
|
20961
21121
|
}
|
|
20962
21122
|
)
|
|
20963
21123
|
] }),
|
|
21124
|
+
resizeEnabled && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "4px", padding: "8px", paddingBottom: "0" }, children: [
|
|
21125
|
+
/* @__PURE__ */ jsx(
|
|
21126
|
+
TextInput,
|
|
21127
|
+
{
|
|
21128
|
+
type: "number",
|
|
21129
|
+
placeholder: "W",
|
|
21130
|
+
value: widthInput,
|
|
21131
|
+
onChange: (e) => setWidthInput(e.target.value),
|
|
21132
|
+
onBlur: handleWidthCommit,
|
|
21133
|
+
onKeyDown: handleKeyDown2,
|
|
21134
|
+
"aria-label": formatMessage({ id: "tiptap-editor.image.width", defaultMessage: "Width (px)" }),
|
|
21135
|
+
style: { minWidth: "62px", flexGrow: 1 }
|
|
21136
|
+
}
|
|
21137
|
+
),
|
|
21138
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: "1.1rem", color: "#999" }, children: "×" }),
|
|
21139
|
+
/* @__PURE__ */ jsx(
|
|
21140
|
+
TextInput,
|
|
21141
|
+
{
|
|
21142
|
+
type: "number",
|
|
21143
|
+
placeholder: "H",
|
|
21144
|
+
value: heightInput,
|
|
21145
|
+
onChange: (e) => setHeightInput(e.target.value),
|
|
21146
|
+
onBlur: handleHeightCommit,
|
|
21147
|
+
onKeyDown: handleKeyDown2,
|
|
21148
|
+
"aria-label": formatMessage({ id: "tiptap-editor.image.height", defaultMessage: "Height (px)" }),
|
|
21149
|
+
style: { minWidth: "62px", flexGrow: 1 }
|
|
21150
|
+
}
|
|
21151
|
+
),
|
|
21152
|
+
(currentWidth || currentHeight) && /* @__PURE__ */ jsx(
|
|
21153
|
+
IconButton,
|
|
21154
|
+
{
|
|
21155
|
+
onClick: () => updateAttributes2({ width: null, height: null }),
|
|
21156
|
+
label: formatMessage({ id: "tiptap-editor.image.resetSize", defaultMessage: "Reset size" }),
|
|
21157
|
+
children: /* @__PURE__ */ jsx(Cross, {})
|
|
21158
|
+
}
|
|
21159
|
+
)
|
|
21160
|
+
] }),
|
|
20964
21161
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", padding: "8px" }, children: [
|
|
20965
21162
|
/* @__PURE__ */ jsx(
|
|
20966
21163
|
TextInput,
|
|
@@ -20986,12 +21183,21 @@ function ImageNodeView({ node, updateAttributes: updateAttributes2, deleteNode:
|
|
|
20986
21183
|
] }) });
|
|
20987
21184
|
}
|
|
20988
21185
|
function ImageNodeViewReadOnly({ node }) {
|
|
21186
|
+
const rawWidth = node.attrs.width;
|
|
21187
|
+
const rawHeight = node.attrs.height;
|
|
21188
|
+
const width = typeof rawWidth === "number" ? rawWidth : null;
|
|
21189
|
+
const height = typeof rawHeight === "number" ? rawHeight : null;
|
|
20989
21190
|
return /* @__PURE__ */ jsx(NodeViewWrapper, { "data-drag-handle": true, "data-align": node.attrs["data-align"] ?? void 0, children: /* @__PURE__ */ jsx(
|
|
20990
21191
|
"img",
|
|
20991
21192
|
{
|
|
20992
21193
|
src: node.attrs.src,
|
|
20993
21194
|
alt: node.attrs.alt ?? "",
|
|
20994
|
-
style: {
|
|
21195
|
+
style: {
|
|
21196
|
+
maxWidth: width ? void 0 : "100%",
|
|
21197
|
+
display: "block",
|
|
21198
|
+
width: width ? `${width}px` : void 0,
|
|
21199
|
+
height: height ? `${height}px` : void 0
|
|
21200
|
+
},
|
|
20995
21201
|
draggable: false
|
|
20996
21202
|
}
|
|
20997
21203
|
) });
|
|
@@ -29494,7 +29700,8 @@ function buildExtensions(config) {
|
|
|
29494
29700
|
extensions.push(index_default.configure({ multicolor: true }));
|
|
29495
29701
|
}
|
|
29496
29702
|
if (isFeatureEnabled(config.mediaLibrary)) {
|
|
29497
|
-
|
|
29703
|
+
const mediaOpts = getFeatureOptions(config.mediaLibrary, {});
|
|
29704
|
+
extensions.push(StrapiImage.configure(mediaOpts ?? {}));
|
|
29498
29705
|
} else {
|
|
29499
29706
|
extensions.push(StrapiImage.configure({ enableContentCheck: true }));
|
|
29500
29707
|
}
|
|
@@ -146,7 +146,7 @@ const richTextField = {
|
|
|
146
146
|
},
|
|
147
147
|
icon: Paragraph,
|
|
148
148
|
components: {
|
|
149
|
-
Input: async () => import("./RichTextInput-
|
|
149
|
+
Input: async () => import("./RichTextInput-B4D0Djcf.mjs").then((m) => ({ default: m.default }))
|
|
150
150
|
},
|
|
151
151
|
options: {
|
|
152
152
|
advanced: [
|
|
@@ -147,7 +147,7 @@ const richTextField = {
|
|
|
147
147
|
},
|
|
148
148
|
icon: icons.Paragraph,
|
|
149
149
|
components: {
|
|
150
|
-
Input: async () => Promise.resolve().then(() => require("./RichTextInput-
|
|
150
|
+
Input: async () => Promise.resolve().then(() => require("./RichTextInput-1uZSm3Nc.js")).then((m) => ({ default: m.default }))
|
|
151
151
|
},
|
|
152
152
|
options: {
|
|
153
153
|
advanced: [
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { NodeViewProps } from '@tiptap/react';
|
|
2
|
-
export declare function ImageNodeView({ node, updateAttributes, deleteNode }: NodeViewProps): import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
export declare function ImageNodeView({ node, updateAttributes, deleteNode, selected, extension }: NodeViewProps): import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export default ImageNodeView;
|
package/package.json
CHANGED