1
- import { ChevronDown , ChevronRight , HelpCircle , Check } from "lucide-react" ;
2
- import { useState , useRef , useEffect } from "react" ;
3
1
import { PluginSettings } from "types" ;
4
2
import FormField from "./CustomPrefixInput" ; // Still importing from the same file
5
3
6
- // Added InputGroup component
7
- interface InputGroupProps {
8
- label : string ;
9
- children : React . ReactNode ;
10
- }
11
-
12
- const InputGroup : React . FC < InputGroupProps > = ( { label, children } ) => (
13
- < div className = "mb-2" >
14
- < div className = "flex items-center gap-1.5 mb-1.5" >
15
- < label className = "text-xs font-medium text-gray-700 dark:text-gray-300" >
16
- { label }
17
- </ label >
18
-
19
- { /* This is where the success message will appear, rendered by the child component */ }
20
- </ div >
21
- { children }
22
- </ div >
23
- ) ;
24
-
25
- // Enhanced InputWithText component
26
- interface InputWithTextProps {
27
- value : string | number ;
28
- onChange : ( value : number ) => void ;
29
- placeholder ?: string ;
30
- suffix ?: string ;
31
- min ?: number ;
32
- max ?: number ;
33
- }
34
-
35
- const InputWithText : React . FC < InputWithTextProps > = ( {
36
- value,
37
- onChange,
38
- placeholder,
39
- suffix,
40
- min = 1 ,
41
- max = 100 ,
42
- } ) => {
43
- const [ inputValue , setInputValue ] = useState ( String ( value ) ) ;
44
- const [ isFocused , setIsFocused ] = useState ( false ) ;
45
- const [ hasChanges , setHasChanges ] = useState ( false ) ;
46
- const [ showSuccess , setShowSuccess ] = useState ( false ) ;
47
- const [ hasError , setHasError ] = useState ( false ) ;
48
- const [ errorMessage , setErrorMessage ] = useState ( "" ) ;
49
- const inputRef = useRef < HTMLInputElement > ( null ) ;
50
-
51
- // Update internal state when value changes (from parent)
52
- useEffect ( ( ) => {
53
- setInputValue ( String ( value ) ) ;
54
- setHasChanges ( false ) ;
55
- } , [ value ] ) ;
56
-
57
- const handleChange = ( e : React . ChangeEvent < HTMLInputElement > ) => {
58
- const newValue = e . target . value ;
59
- setInputValue ( newValue ) ;
60
-
61
- // Check for non-numeric characters
62
- if ( / [ ^ 0 - 9 ] / . test ( newValue ) ) {
63
- setHasError ( true ) ;
64
- setErrorMessage ( "Only numbers are allowed" ) ;
65
- setHasChanges ( newValue !== String ( value ) ) ;
66
- return ;
67
- }
68
-
69
- const numValue = parseInt ( newValue , 10 ) ;
70
-
71
- if ( isNaN ( numValue ) ) {
72
- setHasError ( true ) ;
73
- setErrorMessage ( "Please enter a valid number" ) ;
74
- } else if ( numValue < min ) {
75
- setHasError ( true ) ;
76
- setErrorMessage ( `Minimum value is ${ min } ` ) ;
77
- } else if ( numValue > max ) {
78
- setHasError ( true ) ;
79
- setErrorMessage ( `Maximum value is ${ max } ` ) ;
80
- } else {
81
- setHasError ( false ) ;
82
- setErrorMessage ( "" ) ;
83
- }
84
-
85
- setHasChanges ( newValue !== String ( value ) ) ;
86
- } ;
87
-
88
- const applyChanges = ( ) => {
89
- if ( hasError ) return ;
90
-
91
- const numValue = parseInt ( inputValue , 10 ) ;
92
- if ( ! isNaN ( numValue ) && numValue >= min && numValue <= max ) {
93
- onChange ( numValue ) ;
94
-
95
- // Show success indicator briefly
96
- setShowSuccess ( true ) ;
97
- setTimeout ( ( ) => setShowSuccess ( false ) , 1500 ) ;
98
- setHasChanges ( false ) ;
99
- }
100
- } ;
101
-
102
- const handleBlur = ( ) => {
103
- setIsFocused ( false ) ;
104
- } ;
105
-
106
- const handleKeyDown = ( e : React . KeyboardEvent < HTMLInputElement > ) => {
107
- if ( e . key === "Enter" ) {
108
- e . preventDefault ( ) ;
109
- applyChanges ( ) ;
110
- inputRef . current ?. blur ( ) ;
111
- }
112
- } ;
113
-
114
- return (
115
- < div className = "flex flex-col w-full" >
116
- { showSuccess && (
117
- < span className = "text-xs text-green-500 flex items-center gap-1 animate-fade-in-out ml-auto mb-1" >
118
- < Check className = "w-3 h-3" /> Applied
119
- </ span >
120
- ) }
121
-
122
- < div className = "flex items-start gap-2" >
123
- < div className = "flex-1 flex flex-col" >
124
- < div className = "flex items-center" >
125
- < input
126
- ref = { inputRef }
127
- type = "text"
128
- value = { inputValue }
129
- onChange = { handleChange }
130
- onFocus = { ( ) => setIsFocused ( true ) }
131
- onBlur = { handleBlur }
132
- onKeyDown = { handleKeyDown }
133
- placeholder = { placeholder }
134
- className = { `p-1.5 px-2.5 w-full transition-all focus:outline-hidden ${
135
- suffix ? "rounded-l-md" : "rounded-md"
136
- } ${
137
- hasError
138
- ? "border border-red-300 dark:border-red-700 bg-red-50 dark:bg-red-900/20"
139
- : isFocused
140
- ? "border border-green-400 dark:border-green-600 ring-1 ring-green-300 dark:ring-green-800 bg-white dark:bg-neutral-800"
141
- : "border border-gray-300 dark:border-gray-600 bg-white dark:bg-neutral-800 hover:border-gray-400 dark:hover:border-gray-500"
142
- } `}
143
- />
144
- { suffix && (
145
- < span
146
- className = "py-1.5 px-2.5 text-sm border border-l-0 border-gray-300 dark:border-gray-600
147
- bg-gray-100 dark:bg-gray-700 rounded-r-md text-gray-700 dark:text-gray-300"
148
- >
149
- { suffix }
150
- </ span >
151
- ) }
152
- </ div >
153
-
154
- { hasError && (
155
- < p className = "text-xs text-red-500 mt-1" > { errorMessage } </ p >
156
- ) }
157
- </ div >
158
-
159
- { hasChanges && (
160
- < button
161
- onClick = { applyChanges }
162
- disabled = { hasError }
163
- className = { `px-3 py-1.5 rounded-md text-sm font-medium transition-colors ${
164
- hasError
165
- ? "bg-gray-200 text-gray-500 dark:bg-gray-800 dark:text-gray-600 cursor-not-allowed"
166
- : "bg-green-100 text-green-700 hover:bg-green-200 dark:bg-green-900/30 dark:text-green-400 dark:hover:bg-green-900/50"
167
- } `}
168
- >
169
- Done
170
- </ button >
171
- ) }
172
- </ div >
173
- </ div >
174
- ) ;
175
- } ;
176
-
177
4
interface TailwindSettingsProps {
178
5
settings : PluginSettings | null ;
179
6
onPreferenceChanged : (
@@ -194,6 +21,12 @@ export const TailwindSettings: React.FC<TailwindSettingsProps> = ({
194
21
const handleBaseFontSizeChange = ( value : number ) => {
195
22
onPreferenceChanged ( "baseFontSize" , value ) ;
196
23
} ;
24
+ const handleThresholdPercentChange = ( value : number ) => {
25
+ onPreferenceChanged ( "thresholdPercent" , value ) ;
26
+ } ;
27
+ const handleBaseFontFamilyChange = ( newValue : string ) => {
28
+ onPreferenceChanged ( "baseFontFamily" , newValue ) ;
29
+ } ;
197
30
198
31
return (
199
32
< div className = "mt-2" >
@@ -239,6 +72,42 @@ export const TailwindSettings: React.FC<TailwindSettingsProps> = ({
239
72
Use this value to calculate rem values (default: 16px)
240
73
</ p >
241
74
</ div >
75
+
76
+ { /* Threshold percent setting */ }
77
+ < div className = "mb-3" >
78
+ < FormField
79
+ label = "Rounding Threshold"
80
+ initialValue = { settings . thresholdPercent || 15 }
81
+ onValueChange = { ( d ) => {
82
+ handleThresholdPercentChange ( d as any ) ;
83
+ } }
84
+ placeholder = "15"
85
+ suffix = "%"
86
+ type = "number"
87
+ min = { 0 }
88
+ max = { 50 }
89
+ />
90
+ < p className = "text-xs text-neutral-500 mt-1" >
91
+ Maximum allowed difference when rounding values (default: 15%)
92
+ </ p >
93
+ </ div >
94
+
95
+ { /* Base font family setting */ }
96
+ < div className = "mb-3" >
97
+ < FormField
98
+ label = "Base Font Family"
99
+ initialValue = { settings . baseFontFamily || '' }
100
+ onValueChange = { ( d ) => {
101
+ handleBaseFontFamilyChange ( String ( d ) ) ;
102
+ } }
103
+ placeholder = "sans-serif"
104
+ helpText = "Font family that won't be included in generated classes."
105
+ type = "text"
106
+ />
107
+ < p className = "text-xs text-neutral-500 mt-1" >
108
+ { `Elements with this font won't have "font-[<value>]" class added` }
109
+ </ p >
110
+ </ div >
242
111
</ div >
243
112
</ div >
244
113
) ;
0 commit comments