@thangph2146/lexical-editor 0.0.6 → 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.
@@ -8,11 +8,11 @@
8
8
  @include flex-center;
9
9
  flex-wrap: wrap;
10
10
  gap: $editor-gap-1;
11
- border-bottom: $editor-border-width solid var(--border);
12
- border-top-left-radius: $editor-border-radius;
13
- border-top-right-radius: $editor-border-radius;
11
+ border-bottom: $editor-border-width solid var(--border) !important;
12
+ border-top-left-radius: $editor-border-radius !important;
13
+ border-top-right-radius: $editor-border-radius !important;
14
14
  @include backdrop-blur($editor-dialog-overlay-blur, rgba(255, 255, 255, 0.8));
15
- padding: $editor-gap-1;
15
+ padding: $editor-gap-2 !important;
16
16
  @include shadow-sm;
17
17
  width: 100%;
18
18
  overflow-x: visible;
@@ -23,7 +23,7 @@
23
23
  .editor-toolbar-group {
24
24
  @include flex-center;
25
25
  gap: $editor-gap-1;
26
- padding: $editor-gap-1;
26
+ padding: $editor-gap-1 !important;
27
27
  @include rounded-md;
28
28
  }
29
29
 
@@ -31,6 +31,6 @@
31
31
 
32
32
  .editor-toolbar-separator {
33
33
  height: $editor-icon-size-lg !important; // h-6 (standard separator height)
34
- margin-left: $editor-gap-1;
35
- margin-right: $editor-gap-1;
34
+ margin-left: $editor-gap-1 !important;
35
+ margin-right: $editor-gap-1 !important;
36
36
  }
@@ -17,6 +17,7 @@
17
17
  position: relative;
18
18
  padding: $editor-gap-2 $editor-gap-4;
19
19
  box-sizing: border-box; // Ensure consistent sizing
20
+ @include shadow-sm;
20
21
  @include editor-button-interactive;
21
22
 
22
23
  &:disabled {
@@ -53,7 +54,6 @@
53
54
  background-color: var(--primary);
54
55
  color: var(--primary-foreground);
55
56
  border-color: var(--primary);
56
- @include shadow-sm;
57
57
 
58
58
  &:hover {
59
59
  background-color: color-mix(in srgb, var(--primary), black 10%);
@@ -65,7 +65,6 @@
65
65
  background-color: var(--destructive);
66
66
  color: var(--destructive-foreground);
67
67
  border-color: var(--destructive);
68
- @include shadow-sm;
69
68
 
70
69
  &:hover {
71
70
  background-color: color-mix(in srgb, var(--destructive), black 10%);
@@ -114,11 +113,12 @@
114
113
  border: none;
115
114
  padding: 0;
116
115
  height: auto;
116
+ box-shadow: none !important;
117
117
 
118
118
  &:hover {
119
119
  text-decoration-line: underline;
120
120
  transform: none;
121
- box-shadow: none;
121
+ box-shadow: none !important;
122
122
  }
123
123
  }
124
124
 
@@ -66,6 +66,7 @@
66
66
  flex-direction: row;
67
67
  }
68
68
 
69
+ .editor-flex-column,
69
70
  .editor-flex-col {
70
71
  flex-direction: column;
71
72
  }
@@ -74,6 +75,7 @@
74
75
  flex-direction: row-reverse;
75
76
  }
76
77
 
78
+ .editor-flex-column-reverse,
77
79
  .editor-flex-col-reverse {
78
80
  flex-direction: column-reverse;
79
81
  }
@@ -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"