@thangph2146/lexical-editor 0.0.5 → 0.0.7

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 (40) hide show
  1. package/README.md +47 -0
  2. package/dist/editor-x/editor.cjs +610 -493
  3. package/dist/editor-x/editor.cjs.map +1 -1
  4. package/dist/editor-x/editor.css +157 -69
  5. package/dist/editor-x/editor.css.map +1 -1
  6. package/dist/editor-x/editor.d.cts +2 -1
  7. package/dist/editor-x/editor.d.ts +2 -1
  8. package/dist/editor-x/editor.js +350 -233
  9. package/dist/editor-x/editor.js.map +1 -1
  10. package/dist/index.cjs +620 -501
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.css +157 -69
  13. package/dist/index.css.map +1 -1
  14. package/dist/index.d.cts +1 -1
  15. package/dist/index.d.ts +1 -1
  16. package/dist/index.js +354 -235
  17. package/dist/index.js.map +1 -1
  18. package/package.json +3 -1
  19. package/src/components/lexical-editor.tsx +2 -0
  20. package/src/editor-x/editor.tsx +4 -2
  21. package/src/editor-x/plugins.tsx +7 -6
  22. package/src/plugins/images-plugin.tsx +3 -2
  23. package/src/plugins/layout-plugin.tsx +96 -61
  24. package/src/themes/_mixins.scss +12 -7
  25. package/src/themes/_variables.scss +2 -1
  26. package/src/themes/core/_reset.scss +10 -6
  27. package/src/themes/plugins/_color-picker.scss +1 -0
  28. package/src/themes/plugins/_layout.scss +3 -7
  29. package/src/themes/plugins/_list-color.scss +2 -0
  30. package/src/themes/plugins/_toolbar.scss +7 -7
  31. package/src/themes/ui-components/_button.scss +3 -3
  32. package/src/themes/ui-components/_flex.scss +2 -0
  33. package/src/themes/ui-components/_number-input.scss +81 -0
  34. package/src/themes/ui-components/_text-utilities.scss +1 -1
  35. package/src/themes/ui-components.scss +1 -0
  36. package/src/ui/flex.tsx +9 -2
  37. package/src/ui/number-input.tsx +104 -0
  38. package/src/themes/editor-theme copy.scss +0 -763
  39. package/src/themes/plugins copy.scss +0 -656
  40. package/src/themes/ui-components copy.scss +0 -1335
@@ -0,0 +1,81 @@
1
+ @use "../variables" as *;
2
+ @use "../mixins" as *;
3
+
4
+ .editor-number-input-container {
5
+ display: flex;
6
+ align-items: center;
7
+ border: $editor-border-width solid var(--border);
8
+ border-radius: $editor-border-radius;
9
+ background-color: var(--background);
10
+ height: $editor-control-size-xl; // h-11 to match color picker trigger
11
+ transition: border-color $editor-transition-normal;
12
+ padding: 2px; // Small padding to make inner buttons look nested
13
+ gap: 2px;
14
+
15
+ &:focus-within {
16
+ border-color: var(--ring);
17
+ }
18
+ }
19
+
20
+ .editor-number-input-btn {
21
+ height: 100% !important;
22
+ width: $editor-control-size-lg !important;
23
+ @include rounded-md; // Increase rounding to match container more closely
24
+ flex-shrink: 0;
25
+ color: var(--muted-foreground);
26
+ border: none !important;
27
+ box-shadow: none !important;
28
+
29
+ &:hover:not(:disabled) {
30
+ background-color: var(--accent) !important;
31
+ color: var(--accent-foreground) !important;
32
+ }
33
+
34
+ &:active:not(:disabled) {
35
+ background-color: var(--accent-muted) !important;
36
+ }
37
+ }
38
+
39
+ .editor-number-input-wrapper {
40
+ position: relative;
41
+ flex: 1;
42
+ display: flex;
43
+ align-items: center;
44
+ min-width: 0;
45
+ height: 100%;
46
+ @include rounded-md;
47
+ overflow: hidden;
48
+ }
49
+
50
+ .editor-number-input-field {
51
+ border: none !important;
52
+ border-radius: 0 !important;
53
+ height: 100% !important;
54
+ text-align: center;
55
+ padding-right: 28px !important; // space for unit
56
+ box-shadow: none !important;
57
+ background: transparent !important;
58
+
59
+ &:focus {
60
+ box-shadow: none !important; // Don't show inner focus ring, container handles it
61
+ }
62
+
63
+ &::-webkit-inner-spin-button,
64
+ &::-webkit-outer-spin-button {
65
+ -webkit-appearance: none;
66
+ margin: 0;
67
+ }
68
+
69
+ -moz-appearance: textfield;
70
+ }
71
+
72
+ .editor-number-input-unit {
73
+ position: absolute;
74
+ right: 10px;
75
+ top: 50%;
76
+ transform: translateY(-50%);
77
+ font-size: $editor-font-size-xs;
78
+ color: var(--muted-foreground);
79
+ pointer-events: none;
80
+ font-family: $editor-font-family-mono;
81
+ }
@@ -2,7 +2,7 @@
2
2
  @use "../mixins" as *;
3
3
 
4
4
  /* --- TEXT UTILITIES --- */
5
- .editor-text-muted-xs {
5
+ .editor-text-xs-muted {
6
6
  font-size: $editor-font-size-xs;
7
7
  color: $editor-muted-foreground-color;
8
8
  }
@@ -28,3 +28,4 @@
28
28
  @use "ui-components/popover";
29
29
  @use "ui-components/select";
30
30
  @use "ui-components/toggle";
31
+ @use "ui-components/number-input";
package/src/ui/flex.tsx CHANGED
@@ -11,10 +11,16 @@ export interface FlexProps extends React.HTMLAttributes<HTMLDivElement> {
11
11
 
12
12
  export const Flex = React.forwardRef<HTMLDivElement, FlexProps>(
13
13
  ({ className, align, justify, direction, wrap, gap, style, ...props }, ref) => {
14
+ const isStandardGap =
15
+ typeof gap === "number" && [0.5, 1, 1.5, 2, 3, 4, 5].includes(gap)
16
+ const gapClass = isStandardGap
17
+ ? `editor-gap-${gap.toString().replace(".", "-")}`
18
+ : undefined
19
+
14
20
  const gapStyle =
15
- typeof gap === "number"
21
+ !isStandardGap && typeof gap === "number"
16
22
  ? { gap: `${gap * 0.25}rem` }
17
- : typeof gap !== "undefined"
23
+ : !isStandardGap && typeof gap !== "undefined"
18
24
  ? { gap }
19
25
  : undefined
20
26
 
@@ -27,6 +33,7 @@ export const Flex = React.forwardRef<HTMLDivElement, FlexProps>(
27
33
  justify && `editor-justify-${justify}`,
28
34
  direction && `editor-flex-${direction}`,
29
35
  wrap && `editor-flex-${wrap}`,
36
+ gapClass,
30
37
  className
31
38
  )}
32
39
  style={{ ...gapStyle, ...style }}
@@ -0,0 +1,104 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Minus, Plus } from "lucide-react"
5
+ import { cn } from "../lib/utils"
6
+ import { Button } from "./button"
7
+ import { Input } from "./input"
8
+
9
+ export interface NumberInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> {
10
+ value: number
11
+ onValueChange: (value: number) => void
12
+ min?: number
13
+ max?: number
14
+ step?: number
15
+ unit?: string
16
+ }
17
+
18
+ export const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
19
+ ({ value, onValueChange, min = 0, max = 100, step = 1, unit = "px", className, ...props }, ref) => {
20
+ const timerRef = React.useRef<NodeJS.Timeout | null>(null)
21
+ const intervalRef = React.useRef<NodeJS.Timeout | null>(null)
22
+ const valueRef = React.useRef(value)
23
+
24
+ // Sync ref with current value for interval closure
25
+ React.useEffect(() => {
26
+ valueRef.current = value
27
+ }, [value])
28
+
29
+ const updateValue = React.useCallback((delta: number) => {
30
+ onValueChange(Math.min(Math.max(valueRef.current + delta, min), max))
31
+ }, [onValueChange, min, max])
32
+
33
+ const startLongPress = (delta: number) => {
34
+ updateValue(delta)
35
+ timerRef.current = setTimeout(() => {
36
+ intervalRef.current = setInterval(() => {
37
+ updateValue(delta)
38
+ }, 50)
39
+ }, 300) // Reduced initial delay to 300ms
40
+ }
41
+
42
+ const stopLongPress = React.useCallback(() => {
43
+ if (timerRef.current) clearTimeout(timerRef.current)
44
+ if (intervalRef.current) clearInterval(intervalRef.current)
45
+ }, [])
46
+
47
+ const handleBlur = () => {
48
+ onValueChange(Math.min(Math.max(value, min), max))
49
+ }
50
+
51
+ React.useEffect(() => {
52
+ return () => stopLongPress()
53
+ }, [stopLongPress])
54
+
55
+ return (
56
+ <div className={cn("editor-number-input-container", className)}>
57
+ <Button
58
+ variant="ghost"
59
+ size="icon"
60
+ className="editor-number-input-btn"
61
+ onMouseDown={() => startLongPress(-step)}
62
+ onMouseUp={stopLongPress}
63
+ onMouseLeave={stopLongPress}
64
+ onTouchStart={() => startLongPress(-step)}
65
+ onTouchEnd={stopLongPress}
66
+ tabIndex={-1} // Prevent tabbing into buttons
67
+ >
68
+ <Minus size={14} />
69
+ </Button>
70
+ <div className="editor-number-input-wrapper">
71
+ <Input
72
+ {...props}
73
+ ref={ref}
74
+ type="number"
75
+ value={value}
76
+ onBlur={handleBlur}
77
+ onChange={(e) => {
78
+ const val = parseInt(e.target.value, 10)
79
+ if (!isNaN(val)) onValueChange(val)
80
+ else onValueChange(0) // Default to 0 for invalid input while typing
81
+ }}
82
+ className="editor-number-input-field"
83
+ />
84
+ {unit && <span className="editor-number-input-unit">{unit}</span>}
85
+ </div>
86
+ <Button
87
+ variant="ghost"
88
+ size="icon"
89
+ className="editor-number-input-btn"
90
+ onMouseDown={() => startLongPress(step)}
91
+ onMouseUp={stopLongPress}
92
+ onMouseLeave={stopLongPress}
93
+ onTouchStart={() => startLongPress(step)}
94
+ onTouchEnd={stopLongPress}
95
+ tabIndex={-1}
96
+ >
97
+ <Plus size={14} />
98
+ </Button>
99
+ </div>
100
+ )
101
+ }
102
+ )
103
+
104
+ NumberInput.displayName = "NumberInput"