@lazlon-platform/html-editor 0.4.0 → 0.6.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 (59) hide show
  1. package/lib/hooks/actions.ts +54 -26
  2. package/lib/hooks/batch.ts +17 -7
  3. package/lib/hooks/index.ts +1 -0
  4. package/lib/hooks/node.ts +14 -6
  5. package/lib/hooks/pointer/movePoint.ts +75 -0
  6. package/lib/hooks/pointer/moveable.ts +92 -57
  7. package/lib/hooks/pointer/pointer.ts +21 -11
  8. package/lib/hooks/pointer/resize.ts +176 -210
  9. package/lib/hooks/pointer/rotation.ts +89 -68
  10. package/lib/hooks/pointer/selectionFrame.ts +8 -11
  11. package/lib/hooks/pointer/selector.ts +62 -40
  12. package/lib/hooks/pointer/snap.ts +23 -23
  13. package/lib/hooks/textMarks.ts +1 -3
  14. package/lib/lib/googleFonts.ts +1 -5
  15. package/lib/model/editor.ts +13 -9
  16. package/lib/model/geometry/math.ts +623 -0
  17. package/lib/model/geometry/svg.ts +55 -0
  18. package/lib/model/history.ts +10 -13
  19. package/lib/model/index.ts +7 -10
  20. package/lib/model/node/{editable → editableNode}/index.ts +13 -29
  21. package/lib/model/node/{formattable.ts → formattableNode/index.ts} +5 -11
  22. package/lib/model/node/{group.ts → groupNode.ts} +9 -13
  23. package/lib/model/node/{image.ts → imageNode.ts} +5 -11
  24. package/lib/model/node/lineNode.ts +59 -0
  25. package/lib/model/node/{shape/shape.ts → shapeNode/index.ts} +30 -15
  26. package/lib/model/node/shapeNode/shape.ts +96 -0
  27. package/lib/model/node/{text.ts → textNode.ts} +19 -21
  28. package/lib/model/node.ts +11 -29
  29. package/lib/model/page.ts +4 -3
  30. package/lib/model/traversal.ts +1 -1
  31. package/lib/ui/extractor.ts +3 -3
  32. package/lib/ui/index.ts +2 -4
  33. package/lib/ui/node/{EditableContent.tsx → EditableContent/index.tsx} +4 -3
  34. package/lib/ui/node/GroupContent.tsx +1 -1
  35. package/lib/ui/node/ImageContent.tsx +1 -1
  36. package/lib/ui/node/LineContent.tsx +32 -0
  37. package/lib/ui/node/NodeView.tsx +1 -13
  38. package/lib/ui/node/ShapeContent/ArrowContent.tsx +57 -0
  39. package/lib/ui/node/ShapeContent/EllipseContent.tsx +37 -0
  40. package/lib/ui/node/ShapeContent/PolygonContent.tsx +62 -0
  41. package/lib/ui/node/ShapeContent/RectangleContent.tsx +35 -0
  42. package/lib/ui/node/ShapeContent/StarContent.tsx +75 -0
  43. package/lib/ui/node/ShapeContent/index.tsx +43 -0
  44. package/lib/ui/node/TextContent.tsx +1 -1
  45. package/lib/ui/selection.ts +9 -26
  46. package/package.json +34 -34
  47. package/lib/model/geometry.ts +0 -247
  48. package/lib/model/node/shape/arrow.ts +0 -50
  49. package/lib/model/node/shape/ellipse.ts +0 -26
  50. package/lib/model/node/shape/polygon.ts +0 -108
  51. package/lib/model/node/shape/star.ts +0 -63
  52. package/lib/ui/node/ArrowContent.tsx +0 -60
  53. package/lib/ui/node/EllipseContent.tsx +0 -49
  54. package/lib/ui/node/PolygonContent.tsx +0 -81
  55. package/lib/ui/node/StarContent.tsx +0 -60
  56. /package/lib/model/node/{editable → editableNode}/letterSpacing.ts +0 -0
  57. /package/lib/model/node/{editable → editableNode}/persistentMarks.ts +0 -0
  58. /package/lib/model/node/{editable → editableNode}/tiptapExtensions.ts +0 -0
  59. /package/lib/ui/node/{useDoubleClick.ts → EditableContent/useDoubleClick.ts} +0 -0
package/lib/model/page.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { state } from "react-bolt"
2
2
  import type { Editor } from "./editor"
3
3
  import { Node, type SerializedNode } from "./node"
4
- // import { createRef } from "react"
5
4
 
6
5
  export interface PageProps
7
6
  extends Partial<Pick<Page, "background" | "width" | "height">> {
@@ -15,6 +14,7 @@ export interface SerializedPage extends Omit<PageProps, "nodes"> {
15
14
 
16
15
  export class Page {
17
16
  readonly id: string
17
+ readonly editor: Editor
18
18
 
19
19
  ref: HTMLElement | null = null
20
20
 
@@ -28,7 +28,8 @@ export class Page {
28
28
  | { y: number; x?: number; w?: number; h?: never } // vertical
29
29
  > = []
30
30
 
31
- constructor(_: Editor, props: PageProps) {
31
+ constructor(editor: Editor, props: PageProps) {
32
+ this.editor = editor
32
33
  this.id = props.id
33
34
  this.background = props.background ?? "#ffffff"
34
35
  this.width = props.width ?? 841
@@ -44,7 +45,7 @@ export class Page {
44
45
  height: this.height,
45
46
  nodes: this.nodes
46
47
  .values()
47
- .map((node) => node.serialize())
48
+ .map((node) => this.editor.serializeNode(node))
48
49
  .toArray(),
49
50
  }
50
51
  }
@@ -1,5 +1,5 @@
1
1
  import { Node } from "./node"
2
- import { GroupNode } from "./node/group"
2
+ import { GroupNode } from "./node/groupNode"
3
3
  import { Page } from "./page"
4
4
 
5
5
  export function flattenNodes(root: Node | Page): Array<Node> {
@@ -1,9 +1,9 @@
1
1
  import type { JSONContent } from "@tiptap/core"
2
2
  import { uniq } from "es-toolkit"
3
3
  import { Node } from "../model/node"
4
- import { EditableNode } from "../model/node/editable"
5
- import { FormattableNode } from "../model/node/formattable"
6
- import { ShapeNode } from "../model/node/shape/shape"
4
+ import { EditableNode } from "../model/node/editableNode"
5
+ import { FormattableNode } from "../model/node/formattableNode"
6
+ import { ShapeNode } from "../model/node/shapeNode"
7
7
  import { Page } from "../model/page"
8
8
  import { flattenNodes } from "../model/traversal"
9
9
 
package/lib/ui/index.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  export { getReadableForeground } from "./colors"
2
2
  export { getColors, getFontFamilies } from "./extractor"
3
- export { ArrowContent } from "./node/ArrowContent"
4
3
  export { EditableContent } from "./node/EditableContent"
5
- export { EllipseContent } from "./node/EllipseContent"
6
4
  export { GroupContent } from "./node/GroupContent"
7
5
  export { ImageContent } from "./node/ImageContent"
6
+ export { LineContent } from "./node/LineContent"
8
7
  export { NodeView } from "./node/NodeView"
9
- export { PolygonContent } from "./node/PolygonContent"
10
- export { StarContent } from "./node/StarContent"
8
+ export { ShapeContent } from "./node/ShapeContent"
11
9
  export { TextContent } from "./node/TextContent"
@@ -3,8 +3,8 @@ import clsx from "clsx"
3
3
  import { debounce, isEqual } from "es-toolkit"
4
4
  import { useCallback, useEffect, useRef, useState } from "react"
5
5
  import { useStore } from "react-bolt"
6
- import { useEditor } from "../../hooks/editor"
7
- import type { EditableNode, EditableNodeProps } from "../../model/node/editable"
6
+ import { useEditor } from "../../../hooks/editor"
7
+ import type { EditableNode } from "../../../model"
8
8
  import { useDoubleClick } from "./useDoubleClick"
9
9
 
10
10
  export function EditableContent(props: {
@@ -38,6 +38,7 @@ export function EditableContent(props: {
38
38
  useEffect(() => {
39
39
  if (!selection.has(node)) {
40
40
  node.blur()
41
+ // eslint-disable-next-line react-hooks/set-state-in-effect
41
42
  setClicked(false)
42
43
  }
43
44
  }, [selection, node])
@@ -47,7 +48,7 @@ export function EditableContent(props: {
47
48
  const next = node.tiptap.getJSON()
48
49
  const prev = prevContent.current
49
50
  if (!isEqual(prev, next)) {
50
- editor.history.push<EditableNodeProps>({
51
+ editor.history.push<EditableNode>({
51
52
  undo: ["set-node-props", [node.id, { content: prev }]],
52
53
  redo: ["set-node-props", [node.id, { content: next }]],
53
54
  })
@@ -1,6 +1,6 @@
1
1
  import { useStore } from "react-bolt"
2
2
  import type { Node } from "../../model/node"
3
- import type { GroupNode } from "../../model/node/group"
3
+ import type { GroupNode } from "../../model"
4
4
 
5
5
  function GroupedNodeContent(props: { node: Node; children: React.ReactNode }) {
6
6
  const { node, children } = props
@@ -1,6 +1,6 @@
1
1
  import clsx from "clsx"
2
2
  import { useStore } from "react-bolt"
3
- import type { ImageNode } from "../../model/node/image"
3
+ import type { ImageNode } from "../../model"
4
4
 
5
5
  export function ImageContent(props: { node: ImageNode; fallback?: React.ReactNode }) {
6
6
  const { node, fallback } = props
@@ -0,0 +1,32 @@
1
+ import { useStore } from "react-bolt"
2
+ import type { LineNode } from "../../model/node/lineNode"
3
+
4
+ export function LineContent(props: { node: LineNode }) {
5
+ const [width, height, points, strokeWidth, strokeColor] = useStore(
6
+ props.node,
7
+ "width",
8
+ "height",
9
+ "points",
10
+ "strokeWidth",
11
+ "strokeColor",
12
+ )
13
+
14
+ const polylinePoints = points.map(({ x, y }) => `${x},${y}`).join(" ")
15
+
16
+ return (
17
+ <svg
18
+ className="absolute inset-0 overflow-visible"
19
+ width={width}
20
+ height={height}
21
+ fill="none"
22
+ >
23
+ <polyline
24
+ points={polylinePoints}
25
+ stroke={strokeColor}
26
+ strokeWidth={strokeWidth}
27
+ strokeLinecap="round"
28
+ strokeLinejoin="round"
29
+ />
30
+ </svg>
31
+ )
32
+ }
@@ -1,10 +1,9 @@
1
1
  import { useEffect } from "react"
2
2
  import { useStore } from "react-bolt"
3
3
  import { useEditor } from "../../hooks/editor"
4
+ import { isLoaded, loadFont } from "../../lib/googleFonts"
4
5
  import { Node } from "../../model/node"
5
6
  import { getFontFamilies } from "../../ui/extractor"
6
- import { isLoaded, loadFont } from "../../lib/googleFonts"
7
- import { isPointerInSelectionRect } from "../selection"
8
7
 
9
8
  export function NodeView(props: {
10
9
  node: Node
@@ -49,17 +48,6 @@ export function NodeView(props: {
49
48
  height: `${height}px`,
50
49
  transform: `translate(${x}px,${y}px) rotate(${rotation ?? 0}deg)`,
51
50
  }}
52
- onDragStart={(e) => e.preventDefault()}
53
- onPointerDown={(event) => {
54
- if (event.button !== 0) return
55
-
56
- const { selection } = editor
57
- if (selection.size === 0 || event.shiftKey) {
58
- editor.selection = new Set([...selection, node])
59
- } else if (!isPointerInSelectionRect(selection, event)) {
60
- editor.selection = new Set([node])
61
- }
62
- }}
63
51
  >
64
52
  {children}
65
53
  {css && <style>{`#${id} { ${css} }`}</style>}
@@ -0,0 +1,57 @@
1
+ import { useId } from "react"
2
+ import { useStore } from "react-bolt"
3
+ import { roundedPathData } from "../../../model/geometry/svg"
4
+ import type { ArrowShape, ShapeNode } from "../../../model/node/shapeNode"
5
+
6
+ function arrowPath(props: { height: number; width: number; roundness: number }) {
7
+ const { height, width, roundness } = props
8
+ const bodyRight = width * 0.68
9
+ const inset = height * 0.28
10
+
11
+ const arrowPoints = [
12
+ { x: 0, y: inset },
13
+ { x: bodyRight, y: inset },
14
+ { x: bodyRight, y: 0 },
15
+ { x: width, y: height / 2 },
16
+ { x: bodyRight, y: height },
17
+ { x: bodyRight, y: height - inset },
18
+ { x: 0, y: height - inset },
19
+ ]
20
+
21
+ return roundedPathData(arrowPoints, roundness)
22
+ }
23
+
24
+ export function ArrowContent(props: { node: ShapeNode; shape: ArrowShape["props"] }) {
25
+ const maskId = useId()
26
+ const { node, shape } = props
27
+
28
+ const [background, borderWidth, borderColor] = useStore(
29
+ node,
30
+ "background",
31
+ "borderWidth",
32
+ "borderColor",
33
+ )
34
+
35
+ const [width, height] = useStore(node, "width", "height")
36
+ const { roundness } = shape
37
+ const d = arrowPath({ width, height, roundness })
38
+
39
+ return (
40
+ <svg width={width} height={height} className="absolute inset-0">
41
+ <defs>
42
+ <mask id={maskId} maskUnits="userSpaceOnUse">
43
+ <path d={d} fill="white" />
44
+ </mask>
45
+ </defs>
46
+
47
+ <path d={d} fill={background} />
48
+ <path
49
+ d={d}
50
+ fill="none"
51
+ stroke={borderColor}
52
+ strokeWidth={borderWidth}
53
+ mask={`url(#${maskId})`}
54
+ />
55
+ </svg>
56
+ )
57
+ }
@@ -0,0 +1,37 @@
1
+ import { useStore } from "react-bolt"
2
+ import type { ShapeNode } from "../../../model/node/shapeNode"
3
+
4
+ export function EllipseContent(props: { node: ShapeNode }) {
5
+ const { node } = props
6
+
7
+ const [background, borderWidth, borderColor] = useStore(
8
+ node,
9
+ "background",
10
+ "borderWidth",
11
+ "borderColor",
12
+ )
13
+
14
+ return (
15
+ <svg
16
+ aria-hidden="true"
17
+ className="absolute"
18
+ onDragStart={(e) => e.preventDefault()}
19
+ style={{
20
+ inset: borderWidth / 2,
21
+ width: `calc(100% - ${borderWidth}px)`,
22
+ height: `calc(100% - ${borderWidth}px)`,
23
+ overflow: "visible",
24
+ }}
25
+ >
26
+ <ellipse
27
+ cx="50%"
28
+ cy="50%"
29
+ rx="50%"
30
+ ry="50%"
31
+ fill={background}
32
+ stroke={borderColor}
33
+ strokeWidth={borderWidth}
34
+ />
35
+ </svg>
36
+ )
37
+ }
@@ -0,0 +1,62 @@
1
+ import { useId } from "react"
2
+ import { useStore } from "react-bolt"
3
+ import type { Point } from "../../../model/geometry/math"
4
+ import { roundedPathData } from "../../../model/geometry/svg"
5
+ import type { PolygonShape, ShapeNode } from "../../../model/node/shapeNode"
6
+
7
+ function regularPolygonPoints(props: {
8
+ width: number
9
+ height: number
10
+ sides: number
11
+ }): Point[] {
12
+ const { width, height, sides } = props
13
+ const rotation = sides % 2 === 0 ? Math.PI / sides : 0
14
+ const cx = width / 2
15
+ const cy = height / 2
16
+ const rx = width / 2
17
+ const ry = height / 2
18
+
19
+ return Array.from({ length: sides }, (_, i) => {
20
+ const angle = (i * Math.PI * 2) / sides - Math.PI / 2 + rotation
21
+ return {
22
+ x: cx + rx * Math.cos(angle),
23
+ y: cy + ry * Math.sin(angle),
24
+ }
25
+ })
26
+ }
27
+
28
+ export function PolygonContent(props: { node: ShapeNode; shape: PolygonShape["props"] }) {
29
+ const maskId = useId()
30
+ const { node, shape } = props
31
+
32
+ const [background, borderWidth, borderColor] = useStore(
33
+ node,
34
+ "background",
35
+ "borderWidth",
36
+ "borderColor",
37
+ )
38
+
39
+ const [width, height] = useStore(node, "width", "height")
40
+ const { sides, roundness } = shape
41
+
42
+ const d = roundedPathData(regularPolygonPoints({ width, height, sides }), roundness)
43
+
44
+ return (
45
+ <svg width={width} height={height} className="absolute inset-0">
46
+ <defs>
47
+ <mask id={maskId} maskUnits="userSpaceOnUse">
48
+ <path d={d} fill="white" />
49
+ </mask>
50
+ </defs>
51
+
52
+ <path d={d} fill={background} />
53
+ <path
54
+ d={d}
55
+ fill="none"
56
+ stroke={borderColor}
57
+ strokeWidth={borderWidth}
58
+ mask={`url(#${maskId})`}
59
+ />
60
+ </svg>
61
+ )
62
+ }
@@ -0,0 +1,35 @@
1
+ import { useStore } from "react-bolt"
2
+ import type { RectangleShape, ShapeNode } from "../../../model/node/shapeNode"
3
+
4
+ export function RectangleShape(props: {
5
+ node: ShapeNode
6
+ shape: RectangleShape["props"]
7
+ }) {
8
+ const { node, shape } = props
9
+
10
+ const [background, borderWidth, borderColor] = useStore(
11
+ node,
12
+ "background",
13
+ "borderWidth",
14
+ "borderColor",
15
+ )
16
+
17
+ const radii = [
18
+ shape["cornerTopLeft"],
19
+ shape["cornerTopRight"],
20
+ shape["cornerBottomRight"],
21
+ shape["cornerBottomLeft"],
22
+ ]
23
+
24
+ return (
25
+ <div
26
+ className="absolute inset-0 size-full border-4"
27
+ onDragStart={(e) => e.preventDefault()}
28
+ style={{
29
+ borderRadius: radii.map((r) => `${r}px`).join(" "),
30
+ background: background,
31
+ border: `${borderWidth}px solid ${borderColor}`,
32
+ }}
33
+ />
34
+ )
35
+ }
@@ -0,0 +1,75 @@
1
+ import { useId } from "react"
2
+ import { useStore } from "react-bolt"
3
+ import type { Point } from "../../../model/geometry/math"
4
+ import { roundedPathData } from "../../../model/geometry/svg"
5
+ import type { ShapeNode, StarShape } from "../../../model/node/shapeNode"
6
+
7
+ function starPoints(props: {
8
+ width: number
9
+ height: number
10
+ corners: number
11
+ depth: number
12
+ }): Point[] {
13
+ const { width, height, corners, depth } = props
14
+ const cx = width / 2
15
+ const cy = height / 2
16
+ const outerRx = width / 2
17
+ const outerRy = height / 2
18
+ const innerRx = outerRx * depth
19
+ const innerRy = outerRy * depth
20
+
21
+ return Array.from({ length: corners * 2 }, (_, i) => {
22
+ const angle = (i * Math.PI) / corners - Math.PI / 2
23
+ const isOuter = i % 2 === 0
24
+ const rx = isOuter ? outerRx : innerRx
25
+ const ry = isOuter ? outerRy : innerRy
26
+
27
+ return {
28
+ x: cx + rx * Math.cos(angle),
29
+ y: cy + ry * Math.sin(angle),
30
+ }
31
+ })
32
+ }
33
+
34
+ export function StarContent(props: { node: ShapeNode; shape: StarShape["props"] }) {
35
+ const maskId = useId()
36
+ const { node, shape } = props
37
+
38
+ const [background, borderWidth, borderColor] = useStore(
39
+ node,
40
+ "background",
41
+ "borderWidth",
42
+ "borderColor",
43
+ )
44
+ const [width, height] = useStore(node, "width", "height")
45
+ const { corners, depth, roundness } = shape
46
+
47
+ const d = roundedPathData(
48
+ starPoints({
49
+ width,
50
+ height,
51
+ corners,
52
+ depth,
53
+ }),
54
+ roundness,
55
+ )
56
+
57
+ return (
58
+ <svg width={width} height={height} className="absolute inset-0">
59
+ <defs>
60
+ <mask id={maskId} maskUnits="userSpaceOnUse">
61
+ <path d={d} fill="white" />
62
+ </mask>
63
+ </defs>
64
+
65
+ <path d={d} fill={background} />
66
+ <path
67
+ d={d}
68
+ fill="none"
69
+ stroke={borderColor}
70
+ strokeWidth={borderWidth}
71
+ mask={`url(#${maskId})`}
72
+ />
73
+ </svg>
74
+ )
75
+ }
@@ -0,0 +1,43 @@
1
+ import clsx from "clsx"
2
+ import { useStore } from "react-bolt"
3
+ import type { ShapeNode } from "../../../model"
4
+ import { EditableContent } from "../EditableContent"
5
+ import { ArrowContent } from "./ArrowContent"
6
+ import { EllipseContent } from "./EllipseContent"
7
+ import { PolygonContent } from "./PolygonContent"
8
+ import { RectangleShape } from "./RectangleContent"
9
+ import { StarContent } from "./StarContent"
10
+
11
+ export function ShapeContent(props: { node: ShapeNode; isStatic?: boolean }) {
12
+ const { node, isStatic } = props
13
+ const [valign, halign, shape] = useStore(node, "valign", "halign", "shape")
14
+
15
+ return (
16
+ <div className="relative size-full">
17
+ {shape.name === "rectangle" && <RectangleShape node={node} shape={shape.props} />}
18
+ {shape.name === "polygon" && <PolygonContent node={node} shape={shape.props} />}
19
+ {shape.name === "star" && <StarContent node={node} shape={shape.props} />}
20
+ {shape.name === "ellipse" && <EllipseContent node={node} />}
21
+ {shape.name === "arrow" && <ArrowContent node={node} shape={shape.props} />}
22
+
23
+ <div
24
+ className={clsx(
25
+ "flex size-full",
26
+ valign === "top" && "items-start",
27
+ valign === "center" && "items-center",
28
+ valign === "bottom" && "items-end",
29
+ halign === "left" && "justify-start text-left",
30
+ halign === "center" && "justify-center text-center",
31
+ halign === "right" && "justify-end text-right",
32
+ halign === "justify" && "w-full text-justify",
33
+ )}
34
+ >
35
+ <EditableContent
36
+ isStatic={isStatic}
37
+ node={node}
38
+ className={clsx(halign === "justify" && "w-full")}
39
+ />
40
+ </div>
41
+ </div>
42
+ )
43
+ }
@@ -2,7 +2,7 @@ import clsx from "clsx"
2
2
  import { useEffect } from "react"
3
3
  import { useStore } from "react-bolt"
4
4
  import { useEditor } from "../../hooks/editor"
5
- import type { TextNode } from "../../model/node/text"
5
+ import type { TextNode } from "../../model"
6
6
  import { EditableContent } from "./EditableContent"
7
7
 
8
8
  export function TextContent(props: {
@@ -1,38 +1,21 @@
1
- import { boundingBox } from "../model/geometry"
1
+ import { box, boxBounds, rect, type Box, type Rect } from "../model/geometry/math"
2
2
  import type { Node } from "../model/node"
3
3
 
4
- export function getTargetDOMRect(selection: Set<Node>) {
4
+ export function selectionDOMRect(selection: Set<Node>): Rect {
5
5
  const rects = selection
6
6
  .values()
7
7
  .map((node) => node.ref)
8
8
  .filter((dom) => dom instanceof HTMLElement)
9
9
  .map((dom) => dom.getBoundingClientRect())
10
- .toArray()
11
10
 
12
- return boundingBox(rects)
11
+ return rect(...rects)
13
12
  }
14
13
 
15
- export function getTargetRect(selection: Set<Node>) {
16
- const rects = selection
17
- .values()
18
- .map((node) => node.boundingBox)
19
- .toArray()
20
-
21
- return boundingBox(rects)
22
- }
23
-
24
- export function isPointerInSelectionRect(
25
- selection: Set<Node>,
26
- event: React.PointerEvent,
27
- ) {
28
- const rect = getTargetDOMRect(selection)
29
- const right = rect.x + rect.width
30
- const bottom = rect.y + rect.height
14
+ export function selectionBox(selection: Set<Node>): Box {
15
+ if (selection.size === 1) {
16
+ const [node] = selection
17
+ return box(node)
18
+ }
31
19
 
32
- return (
33
- event.clientX >= rect.x &&
34
- event.clientX <= right &&
35
- event.clientY >= rect.y &&
36
- event.clientY <= bottom
37
- )
20
+ return box(boxBounds(...selection.values().map(box)))
38
21
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazlon-platform/html-editor",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "lib"
@@ -8,55 +8,55 @@
8
8
  "exports": {
9
9
  "./ui": "./lib/ui/index.ts",
10
10
  "./model": "./lib/model/index.ts",
11
- "./model/geometry": "./lib/model/geometry.ts",
11
+ "./model/geometry": "./lib/model/geometry/math.ts",
12
12
  "./hooks": "./lib/hooks/index.ts",
13
13
  "./googleFonts": "./lib/lib/googleFonts.ts"
14
14
  },
15
15
  "dependencies": {
16
- "@tiptap/core": "^3.20.0",
17
- "@tiptap/extension-bold": "^3.20.0",
18
- "@tiptap/extension-document": "^3.20.0",
19
- "@tiptap/extension-hard-break": "^3.20.0",
20
- "@tiptap/extension-italic": "^3.20.0",
21
- "@tiptap/extension-paragraph": "^3.20.0",
22
- "@tiptap/extension-strike": "^3.20.0",
23
- "@tiptap/extension-subscript": "^3.20.0",
24
- "@tiptap/extension-superscript": "^3.20.0",
25
- "@tiptap/extension-text": "^3.20.0",
26
- "@tiptap/extension-text-style": "^3.20.0",
27
- "@tiptap/extension-underline": "^3.20.0",
28
- "@tiptap/extensions": "^3.22.4",
29
- "@tiptap/pm": "^3.20.0",
30
- "@tiptap/react": "^3.20.0",
16
+ "@tiptap/core": "^3.23.1",
17
+ "@tiptap/extension-bold": "^3.23.1",
18
+ "@tiptap/extension-document": "^3.23.1",
19
+ "@tiptap/extension-hard-break": "^3.23.1",
20
+ "@tiptap/extension-italic": "^3.23.1",
21
+ "@tiptap/extension-paragraph": "^3.23.1",
22
+ "@tiptap/extension-strike": "^3.23.1",
23
+ "@tiptap/extension-subscript": "^3.23.1",
24
+ "@tiptap/extension-superscript": "^3.23.1",
25
+ "@tiptap/extension-text": "^3.23.1",
26
+ "@tiptap/extension-text-style": "^3.23.1",
27
+ "@tiptap/extension-underline": "^3.23.1",
28
+ "@tiptap/extensions": "^3.23.1",
29
+ "@tiptap/pm": "^3.23.1",
30
+ "@tiptap/react": "^3.23.1",
31
31
  "clsx": "^2.1.1",
32
- "es-toolkit": "^1.45.0",
33
- "react": "^19.2.0",
34
- "react-aria-components": "^1.15.1",
32
+ "es-toolkit": "^1.46.1",
33
+ "react": "^19.2.6",
34
+ "react-aria-components": "^1.17.0",
35
35
  "react-bolt": "^1.4.1",
36
- "react-dom": "^19.2.0",
37
- "react-hotkeys-hook": "^5.2.4",
38
- "tailwindcss": "^4.2.1"
36
+ "react-dom": "^19.2.6",
37
+ "react-hotkeys-hook": "^5.3.2",
38
+ "tailwindcss": "^4.3.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@eslint/js": "^9.39.1",
42
- "@tailwindcss/vite": "^4.2.1",
41
+ "@eslint/js": "^9.39.4",
42
+ "@tailwindcss/vite": "^4.3.0",
43
43
  "@testing-library/jest-dom": "^6.9.1",
44
44
  "@testing-library/react": "^16.3.2",
45
- "@types/node": "^24.10.1",
45
+ "@types/node": "^24.12.3",
46
46
  "@types/react": "^19.2.7",
47
47
  "@types/react-dom": "^19.2.3",
48
- "@vitejs/plugin-react": "^5.1.1",
49
- "@vitest/coverage-v8": "^4.1.0",
50
- "eslint": "^9.39.1",
51
- "eslint-plugin-react-hooks": "^7.0.1",
48
+ "@vitejs/plugin-react": "^5.2.0",
49
+ "@vitest/coverage-v8": "^4.1.5",
50
+ "eslint": "^9.39.4",
51
+ "eslint-plugin-react-hooks": "^7.1.1",
52
52
  "eslint-plugin-react-refresh": "^0.4.24",
53
53
  "globals": "^16.5.0",
54
- "happy-dom": "^20.8.3",
54
+ "happy-dom": "^20.9.0",
55
55
  "typescript": "~5.9.3",
56
- "typescript-eslint": "^8.48.0",
57
- "vite": "^7.3.1",
56
+ "typescript-eslint": "^8.59.2",
57
+ "vite": "^7.3.3",
58
58
  "vite-tsconfig-paths": "^6.1.1",
59
- "vitest": "^4.1.0"
59
+ "vitest": "^4.1.5"
60
60
  },
61
61
  "prettier": {
62
62
  "semi": false,