1- import { ChevronDown , ChevronRight , HelpCircle , Check } from "lucide-react" ;
2- import { useState , useRef , useEffect } from "react" ;
31import { PluginSettings } from "types" ;
42import FormField from "./CustomPrefixInput" ; // Still importing from the same file
53
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-
1774interface TailwindSettingsProps {
1785 settings : PluginSettings | null ;
1796 onPreferenceChanged : (
@@ -194,6 +21,12 @@ export const TailwindSettings: React.FC<TailwindSettingsProps> = ({
19421 const handleBaseFontSizeChange = ( value : number ) => {
19522 onPreferenceChanged ( "baseFontSize" , value ) ;
19623 } ;
24+ const handleThresholdPercentChange = ( value : number ) => {
25+ onPreferenceChanged ( "thresholdPercent" , value ) ;
26+ } ;
27+ const handleBaseFontFamilyChange = ( newValue : string ) => {
28+ onPreferenceChanged ( "baseFontFamily" , newValue ) ;
29+ } ;
19730
19831 return (
19932 < div className = "mt-2" >
@@ -239,6 +72,42 @@ export const TailwindSettings: React.FC<TailwindSettingsProps> = ({
23972 Use this value to calculate rem values (default: 16px)
24073 </ p >
24174 </ 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 >
242111 </ div >
243112 </ div >
244113 ) ;
0 commit comments