Skip to content

Commit 0c499eb

Browse files
committed
Text Case Tools
1 parent 0a1671f commit 0c499eb

File tree

7 files changed

+1185
-0
lines changed

7 files changed

+1185
-0
lines changed

TextHub/Pages/CamelCase.razor

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
@page "/camel-case"
2+
@using TextHub.Services
3+
@using TextHub.UIComponent
4+
@using Microsoft.JSInterop
5+
@using System.Text
6+
@inject JsonLdService JsonLdService
7+
@inject ToolDataService ToolDataService
8+
@inject IJSRuntime JSRuntime
9+
10+
<PageTitle>camelCase Converter - Text Hub</PageTitle>
11+
12+
<DynamicJsonLd JsonLdContent="@_jsonLdContent" Id="camelcase-jsonld" />
13+
14+
<main class="flex-1">
15+
<div class="container mx-auto px-4 py-8 md:py-12 animate-fade-in">
16+
<div class="max-w-4xl mx-auto">
17+
<div class="text-center mb-8 md:mb-10">
18+
<div class="inline-flex items-center justify-center p-2.5 sm:p-3 bg-primary/10 rounded-lg mb-3 md:mb-4">
19+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code w-6 h-6 sm:w-8 sm:h-8 text-primary">
20+
<polyline points="16 18 22 12 16 6"></polyline>
21+
<polyline points="8 6 2 12 8 18"></polyline>
22+
</svg>
23+
</div>
24+
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold mb-3 md:mb-4">
25+
<span class="text-gradient">camelCase Converter</span>
26+
</h1>
27+
<p class="text-base sm:text-lg text-muted-foreground px-4">Convert to camelCase for coding</p>
28+
</div>
29+
30+
<div class="grid gap-4 md:gap-6">
31+
<div class="animate-fade-in" style="animation-delay: 100ms;">
32+
<label class="block text-sm font-medium mb-2">Input Text</label>
33+
<textarea
34+
@bind="_inputText"
35+
@oninput="ConvertToCamelCase"
36+
placeholder="Enter or paste your text here..."
37+
class="input-area min-h-[150px] sm:min-h-[200px] text-sm sm:text-base"></textarea>
38+
</div>
39+
40+
<div class="flex flex-col sm:flex-row gap-3 sm:gap-4 justify-center animate-fade-in" style="animation-delay: 200ms;">
41+
<button @onclick="ConvertToCamelCase" class="btn-primary w-full sm:w-auto text-sm sm:text-base">Convert to camelCase</button>
42+
<button @onclick="ClearText" class="btn-secondary w-full sm:w-auto text-sm sm:text-base">
43+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw w-4 h-4 mr-2 inline">
44+
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
45+
<path d="M3 3v5h5"></path>
46+
</svg>
47+
Reset
48+
</button>
49+
</div>
50+
51+
@if (!string.IsNullOrEmpty(_outputText))
52+
{
53+
<div class="animate-fade-in" style="animation-delay: 300ms;">
54+
<label class="block text-sm font-medium mb-2">Result</label>
55+
<div class="relative">
56+
<textarea
57+
@bind="_outputText"
58+
class="input-area min-h-[150px] sm:min-h-[200px] text-sm sm:text-base bg-muted"
59+
readonly></textarea>
60+
<button @onclick="CopyResult"
61+
class="absolute top-2 right-2 p-2 bg-primary/10 hover:bg-primary/20 rounded-md transition-colors">
62+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy">
63+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect>
64+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path>
65+
</svg>
66+
</button>
67+
</div>
68+
</div>
69+
}
70+
</div>
71+
72+
<div class="mt-8 md:mt-12 p-4 md:p-6 bg-muted/30 rounded-lg animate-fade-in" style="animation-delay: 300ms;">
73+
<h2 class="text-lg md:text-xl font-semibold mb-2 md:mb-3">How it works</h2>
74+
<ul class="space-y-1.5 md:space-y-2 text-sm md:text-base text-muted-foreground">
75+
<li>• Simply paste or type your text in the input box</li>
76+
<li>• Click "Convert to camelCase" to transform your text</li>
77+
<li>• Copy the result to your clipboard with one click</li>
78+
<li>• Perfect for variable names, function names, and coding</li>
79+
</ul>
80+
</div>
81+
</div>
82+
</div>
83+
</main>
84+
85+
@code {
86+
private string _inputText = string.Empty;
87+
private string _outputText = string.Empty;
88+
private string _jsonLdContent = string.Empty;
89+
90+
protected override void OnInitialized()
91+
{
92+
var tool = ToolDataService.GetTextCaseTools().FirstOrDefault(t => t.Href == "/camel-case");
93+
if (tool != null)
94+
{
95+
_jsonLdContent = JsonLdService.GenerateToolPageJsonLd(tool, "Text Case Tools");
96+
}
97+
}
98+
99+
private void ConvertToCamelCase()
100+
{
101+
if (string.IsNullOrWhiteSpace(_inputText))
102+
{
103+
_outputText = string.Empty;
104+
return;
105+
}
106+
107+
var words = SplitIntoWords(_inputText);
108+
if (words.Count == 0)
109+
{
110+
_outputText = string.Empty;
111+
return;
112+
}
113+
114+
var result = new StringBuilder();
115+
116+
// First word is lowercase
117+
result.Append(words[0].ToLower());
118+
119+
// Subsequent words are capitalized
120+
for (int i = 1; i < words.Count; i++)
121+
{
122+
result.Append(CapitalizeWord(words[i]));
123+
}
124+
125+
_outputText = result.ToString();
126+
}
127+
128+
private List<string> SplitIntoWords(string text)
129+
{
130+
var words = new List<string>();
131+
var currentWord = new StringBuilder();
132+
133+
foreach (char c in text)
134+
{
135+
if (char.IsLetterOrDigit(c))
136+
{
137+
currentWord.Append(c);
138+
}
139+
else if (char.IsWhiteSpace(c) || c == '-' || c == '_')
140+
{
141+
if (currentWord.Length > 0)
142+
{
143+
words.Add(currentWord.ToString());
144+
currentWord.Clear();
145+
}
146+
}
147+
}
148+
149+
if (currentWord.Length > 0)
150+
{
151+
words.Add(currentWord.ToString());
152+
}
153+
154+
return words;
155+
}
156+
157+
private string CapitalizeWord(string word)
158+
{
159+
if (string.IsNullOrWhiteSpace(word))
160+
return word;
161+
162+
var chars = word.ToCharArray();
163+
if (chars.Length > 0)
164+
{
165+
chars[0] = char.ToUpper(chars[0]);
166+
for (int i = 1; i < chars.Length; i++)
167+
{
168+
chars[i] = char.ToLower(chars[i]);
169+
}
170+
}
171+
172+
return new string(chars);
173+
}
174+
175+
private void ClearText()
176+
{
177+
_inputText = string.Empty;
178+
_outputText = string.Empty;
179+
}
180+
181+
private async Task CopyResult()
182+
{
183+
if (!string.IsNullOrEmpty(_outputText))
184+
{
185+
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", _outputText);
186+
}
187+
}
188+
}

TextHub/Pages/KebabCase.razor

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
@page "/kebab-case"
2+
@using TextHub.Services
3+
@using TextHub.UIComponent
4+
@using Microsoft.JSInterop
5+
@using System.Text
6+
@inject JsonLdService JsonLdService
7+
@inject ToolDataService ToolDataService
8+
@inject IJSRuntime JSRuntime
9+
10+
<PageTitle>kebab-case Converter - Text Hub</PageTitle>
11+
12+
<DynamicJsonLd JsonLdContent="@_jsonLdContent" Id="kebabcase-jsonld" />
13+
14+
<main class="flex-1">
15+
<div class="container mx-auto px-4 py-8 md:py-12 animate-fade-in">
16+
<div class="max-w-4xl mx-auto">
17+
<div class="text-center mb-8 md:mb-10">
18+
<div class="inline-flex items-center justify-center p-2.5 sm:p-3 bg-primary/10 rounded-lg mb-3 md:mb-4">
19+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-code w-6 h-6 sm:w-8 sm:h-8 text-primary">
20+
<polyline points="16 18 22 12 16 6"></polyline>
21+
<polyline points="8 6 2 12 8 18"></polyline>
22+
</svg>
23+
</div>
24+
<h1 class="text-2xl sm:text-3xl md:text-4xl font-bold mb-3 md:mb-4">
25+
<span class="text-gradient">kebab-case Converter</span>
26+
</h1>
27+
<p class="text-base sm:text-lg text-muted-foreground px-4">Convert to kebab-case for URLs</p>
28+
</div>
29+
30+
<div class="grid gap-4 md:gap-6">
31+
<div class="animate-fade-in" style="animation-delay: 100ms;">
32+
<label class="block text-sm font-medium mb-2">Input Text</label>
33+
<textarea
34+
@bind="_inputText"
35+
@oninput="ConvertToKebabCase"
36+
placeholder="Enter or paste your text here..."
37+
class="input-area min-h-[150px] sm:min-h-[200px] text-sm sm:text-base"></textarea>
38+
</div>
39+
40+
<div class="flex flex-col sm:flex-row gap-3 sm:gap-4 justify-center animate-fade-in" style="animation-delay: 200ms;">
41+
<button @onclick="ConvertToKebabCase" class="btn-primary w-full sm:w-auto text-sm sm:text-base">Convert to kebab-case</button>
42+
<button @onclick="ClearText" class="btn-secondary w-full sm:w-auto text-sm sm:text-base">
43+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-rotate-ccw w-4 h-4 mr-2 inline">
44+
<path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
45+
<path d="M3 3v5h5"></path>
46+
</svg>
47+
Reset
48+
</button>
49+
</div>
50+
51+
@if (!string.IsNullOrEmpty(_outputText))
52+
{
53+
<div class="animate-fade-in" style="animation-delay: 300ms;">
54+
<label class="block text-sm font-medium mb-2">Result</label>
55+
<div class="relative">
56+
<textarea
57+
@bind="_outputText"
58+
class="input-area min-h-[150px] sm:min-h-[200px] text-sm sm:text-base bg-muted"
59+
readonly></textarea>
60+
<button @onclick="CopyResult"
61+
class="absolute top-2 right-2 p-2 bg-primary/10 hover:bg-primary/20 rounded-md transition-colors">
62+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy">
63+
<rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect>
64+
<path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path>
65+
</svg>
66+
</button>
67+
</div>
68+
</div>
69+
}
70+
</div>
71+
72+
<div class="mt-8 md:mt-12 p-4 md:p-6 bg-muted/30 rounded-lg animate-fade-in" style="animation-delay: 300ms;">
73+
<h2 class="text-lg md:text-xl font-semibold mb-2 md:mb-3">How it works</h2>
74+
<ul class="space-y-1.5 md:space-y-2 text-sm md:text-base text-muted-foreground">
75+
<li>• Simply paste or type your text in the input box</li>
76+
<li>• Click "Convert to kebab-case" to transform your text</li>
77+
<li>• Copy the result to your clipboard with one click</li>
78+
<li>• Perfect for URLs, CSS classes, and HTML attributes</li>
79+
</ul>
80+
</div>
81+
</div>
82+
</div>
83+
</main>
84+
85+
@code {
86+
private string _inputText = string.Empty;
87+
private string _outputText = string.Empty;
88+
private string _jsonLdContent = string.Empty;
89+
90+
protected override void OnInitialized()
91+
{
92+
var tool = ToolDataService.GetTextCaseTools().FirstOrDefault(t => t.Href == "/kebab-case");
93+
if (tool != null)
94+
{
95+
_jsonLdContent = JsonLdService.GenerateToolPageJsonLd(tool, "Text Case Tools");
96+
}
97+
}
98+
99+
private void ConvertToKebabCase()
100+
{
101+
if (string.IsNullOrWhiteSpace(_inputText))
102+
{
103+
_outputText = string.Empty;
104+
return;
105+
}
106+
107+
var words = SplitIntoWords(_inputText);
108+
if (words.Count == 0)
109+
{
110+
_outputText = string.Empty;
111+
return;
112+
}
113+
114+
var result = new StringBuilder();
115+
116+
// Join words with hyphens, all lowercase
117+
for (int i = 0; i < words.Count; i++)
118+
{
119+
if (i > 0)
120+
{
121+
result.Append("-");
122+
}
123+
result.Append(words[i].ToLower());
124+
}
125+
126+
_outputText = result.ToString();
127+
}
128+
129+
private List<string> SplitIntoWords(string text)
130+
{
131+
var words = new List<string>();
132+
var currentWord = new StringBuilder();
133+
134+
foreach (char c in text)
135+
{
136+
if (char.IsLetterOrDigit(c))
137+
{
138+
currentWord.Append(c);
139+
}
140+
else if (char.IsWhiteSpace(c) || c == '-' || c == '_')
141+
{
142+
if (currentWord.Length > 0)
143+
{
144+
words.Add(currentWord.ToString());
145+
currentWord.Clear();
146+
}
147+
}
148+
}
149+
150+
if (currentWord.Length > 0)
151+
{
152+
words.Add(currentWord.ToString());
153+
}
154+
155+
return words;
156+
}
157+
158+
private void ClearText()
159+
{
160+
_inputText = string.Empty;
161+
_outputText = string.Empty;
162+
}
163+
164+
private async Task CopyResult()
165+
{
166+
if (!string.IsNullOrEmpty(_outputText))
167+
{
168+
await JSRuntime.InvokeVoidAsync("navigator.clipboard.writeText", _outputText);
169+
}
170+
}
171+
}

0 commit comments

Comments
 (0)