@lazlon-platform/html-editor 0.3.0 → 0.3.2
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/lib/model/geometry.ts
CHANGED
|
@@ -218,3 +218,30 @@ export function regularPolygonPoints(props: {
|
|
|
218
218
|
}
|
|
219
219
|
})
|
|
220
220
|
}
|
|
221
|
+
|
|
222
|
+
export function starPoints(props: {
|
|
223
|
+
width: number
|
|
224
|
+
height: number
|
|
225
|
+
corners: number
|
|
226
|
+
depth: number
|
|
227
|
+
}): Point[] {
|
|
228
|
+
const { width, height, corners, depth } = props
|
|
229
|
+
const cx = width / 2
|
|
230
|
+
const cy = height / 2
|
|
231
|
+
const outerRx = width / 2
|
|
232
|
+
const outerRy = height / 2
|
|
233
|
+
const innerRx = outerRx * depth
|
|
234
|
+
const innerRy = outerRy * depth
|
|
235
|
+
|
|
236
|
+
return Array.from({ length: corners * 2 }, (_, i) => {
|
|
237
|
+
const angle = (i * Math.PI) / corners - Math.PI / 2
|
|
238
|
+
const isOuter = i % 2 === 0
|
|
239
|
+
const rx = isOuter ? outerRx : innerRx
|
|
240
|
+
const ry = isOuter ? outerRy : innerRy
|
|
241
|
+
|
|
242
|
+
return {
|
|
243
|
+
x: cx + rx * Math.cos(angle),
|
|
244
|
+
y: cy + ry * Math.sin(angle),
|
|
245
|
+
}
|
|
246
|
+
})
|
|
247
|
+
}
|
package/lib/model/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { ArrowNode, type ArrowNodeProps } from "./node/shape/arrow"
|
|
|
9
9
|
export { EllipseNode, type EllipseNodeProps } from "./node/shape/ellipse"
|
|
10
10
|
export { PolygonNode, type PolygonNodeProps } from "./node/shape/polygon"
|
|
11
11
|
export { ShapeNode, type ShapeNodeProps } from "./node/shape/shape"
|
|
12
|
+
export { StarNode, type StarNodeProps } from "./node/shape/star"
|
|
12
13
|
export { TextNode, type TextNodeProps } from "./node/text"
|
|
13
14
|
export { Page, type PageProps, type SerializedPage } from "./page"
|
|
14
15
|
export { flattenNodes } from "./traversal"
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { computed, state } from "react-bolt"
|
|
2
|
+
import { clamp, roundedPathData, starPoints } from "../../geometry"
|
|
3
|
+
import type { Editor } from "../../editor"
|
|
4
|
+
import type { SerializedNode } from "../../node"
|
|
5
|
+
import type { Page } from "../../page"
|
|
6
|
+
import { ShapeNode, type ShapeNodeProps } from "./shape"
|
|
7
|
+
|
|
8
|
+
export type StarNodeProps = ShapeNodeProps &
|
|
9
|
+
Partial<Pick<StarNode, "corners" | "roundness" | "depth">>
|
|
10
|
+
|
|
11
|
+
export class StarNode extends ShapeNode {
|
|
12
|
+
get name() {
|
|
13
|
+
return "star"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@state accessor corners: number
|
|
17
|
+
@state accessor roundness: number
|
|
18
|
+
@state private accessor _depth: number
|
|
19
|
+
|
|
20
|
+
@computed get depth() {
|
|
21
|
+
return this._depth
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
set depth(value: number) {
|
|
25
|
+
this._depth = clamp(value, 0, 1)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
constructor(
|
|
29
|
+
editor: Editor,
|
|
30
|
+
page: Page,
|
|
31
|
+
{ corners = 5, roundness = 0, depth = 0.4, ...props }: StarNodeProps,
|
|
32
|
+
) {
|
|
33
|
+
super(editor, page, props)
|
|
34
|
+
this.corners = corners
|
|
35
|
+
this.roundness = roundness
|
|
36
|
+
this._depth = clamp(depth, 0, 1)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
props(): StarNodeProps {
|
|
40
|
+
return {
|
|
41
|
+
...super.props(),
|
|
42
|
+
corners: this.corners,
|
|
43
|
+
roundness: this.roundness,
|
|
44
|
+
depth: this.depth,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
serialize(): SerializedNode<this["name"], StarNodeProps> {
|
|
49
|
+
return super.serialize()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@computed get svgPathData() {
|
|
53
|
+
return roundedPathData(
|
|
54
|
+
starPoints({
|
|
55
|
+
width: this.width,
|
|
56
|
+
height: this.height,
|
|
57
|
+
corners: this.corners,
|
|
58
|
+
depth: this.depth,
|
|
59
|
+
}),
|
|
60
|
+
this.roundness,
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
}
|
package/lib/ui/index.ts
CHANGED
|
@@ -7,4 +7,5 @@ export { GroupContent } from "./node/GroupContent"
|
|
|
7
7
|
export { ImageContent } from "./node/ImageContent"
|
|
8
8
|
export { NodeView } from "./node/NodeView"
|
|
9
9
|
export { PolygonContent } from "./node/PolygonContent"
|
|
10
|
+
export { StarContent } from "./node/StarContent"
|
|
10
11
|
export { TextContent } from "./node/TextContent"
|
|
@@ -22,11 +22,12 @@ export function EditableContent(props: {
|
|
|
22
22
|
const prevContent = useRef(node.tiptap.getJSON())
|
|
23
23
|
|
|
24
24
|
// start with selection.has(node) so that newly added editable nodes can be focused
|
|
25
|
-
const [clicked, setClicked] = useState(selection.has(node))
|
|
25
|
+
const [clicked, setClicked] = useState(() => selection.has(node))
|
|
26
26
|
|
|
27
27
|
const onDoubleClick = useDoubleClick(
|
|
28
28
|
useCallback(() => {
|
|
29
29
|
setClicked(true)
|
|
30
|
+
console.log("hello")
|
|
30
31
|
node.tiptap.commands.focus()
|
|
31
32
|
}, [node, setClicked]),
|
|
32
33
|
)
|
|
@@ -77,7 +78,7 @@ export function EditableContent(props: {
|
|
|
77
78
|
return (
|
|
78
79
|
<div
|
|
79
80
|
ref={(ref) => void (node.contentRef = ref)}
|
|
80
|
-
className={clsx("relative min-w-[min(80%,6ch)]", className)}
|
|
81
|
+
className={clsx("relative min-w-[min(80%,6ch)] min-h-3", className)}
|
|
81
82
|
onPointerDown={onDoubleClick}
|
|
82
83
|
style={{ ...style, ...(!isEmpty && { lineHeight }) }}
|
|
83
84
|
>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import clsx from "clsx"
|
|
2
|
+
import { useId } from "react"
|
|
3
|
+
import { useStore } from "react-bolt"
|
|
4
|
+
import { type StarNode } from "../../model/node/shape/star"
|
|
5
|
+
import { EditableContent } from "./EditableContent"
|
|
6
|
+
|
|
7
|
+
export function StarContent(props: { node: StarNode; isStatic?: boolean }) {
|
|
8
|
+
const maskId = useId()
|
|
9
|
+
const { node, isStatic } = props
|
|
10
|
+
|
|
11
|
+
const [valign, halign, background, borderWidth, borderColor] = useStore(
|
|
12
|
+
node,
|
|
13
|
+
"valign",
|
|
14
|
+
"halign",
|
|
15
|
+
"background",
|
|
16
|
+
"borderWidth",
|
|
17
|
+
"borderColor",
|
|
18
|
+
)
|
|
19
|
+
const [w, h, d] = useStore(node, "width", "height", "svgPathData")
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="relative size-full">
|
|
23
|
+
<svg width={w} height={h} className="absolute inset-0">
|
|
24
|
+
<defs>
|
|
25
|
+
<mask id={maskId} maskUnits="userSpaceOnUse">
|
|
26
|
+
<path d={d} fill="white" />
|
|
27
|
+
</mask>
|
|
28
|
+
</defs>
|
|
29
|
+
|
|
30
|
+
<path d={d} fill={background} />
|
|
31
|
+
<path
|
|
32
|
+
d={d}
|
|
33
|
+
fill="none"
|
|
34
|
+
stroke={borderColor}
|
|
35
|
+
strokeWidth={borderWidth}
|
|
36
|
+
mask={`url(#${maskId})`}
|
|
37
|
+
/>
|
|
38
|
+
</svg>
|
|
39
|
+
|
|
40
|
+
<div
|
|
41
|
+
className={clsx(
|
|
42
|
+
"flex size-full",
|
|
43
|
+
valign === "top" && "items-start",
|
|
44
|
+
valign === "center" && "items-center",
|
|
45
|
+
valign === "bottom" && "items-end",
|
|
46
|
+
halign === "left" && "justify-start text-left",
|
|
47
|
+
halign === "center" && "justify-center text-center",
|
|
48
|
+
halign === "right" && "justify-end text-right",
|
|
49
|
+
halign === "justify" && "w-full text-justify",
|
|
50
|
+
)}
|
|
51
|
+
>
|
|
52
|
+
<EditableContent
|
|
53
|
+
isStatic={isStatic}
|
|
54
|
+
node={node}
|
|
55
|
+
className={clsx(halign === "justify" && "w-full")}
|
|
56
|
+
/>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|