Skip to content

Commit de26f3f

Browse files
staredclaude
andcommitted
Improve CSV upload UI with modern drop zone design (v0.1.0)
- Redesign FileUpload component with dashed border drop zone - Add "Drop CSV here" text in black for better visibility - Replace emoji buttons with thin bordered buttons - Implement pattern: Drop CSV here [or upload file] [or load from URL] - Better visual hierarchy following modern UX patterns - Set version to 0.1.0 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent a1310dc commit de26f3f

File tree

3 files changed

+71
-106
lines changed

3 files changed

+71
-106
lines changed

CLAUDE.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
## Development Workflow
22

3-
- Run pnpm lint before any commit (and after major edits as well).
3+
- Run pnpm lint before any commit (and after major edits as well).
4+
- With each commit (and only commit) increment patch version by one. Unless there is a direct instruction to increase minor (or major) version.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "webr-ggplot2-demo",
33
"private": true,
4-
"version": "0.0.0",
4+
"version": "0.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src/components/FileUpload.vue

Lines changed: 68 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,29 @@ onUnmounted(() => {
142142

143143
<template>
144144
<div ref="dropdownRef" class="file-upload">
145-
<div v-if="!props.uploadedFile && !showUrlInput" class="upload-controls">
146-
<div
147-
class="upload-button"
148-
:class="{ 'dragging': isDragging }"
149-
@click="fileInputRef?.click()"
150-
@drop="handleDrop"
151-
@dragover="handleDragOver"
152-
@dragleave="handleDragLeave"
153-
>
145+
<div
146+
v-if="!props.uploadedFile" class="csv-drop-zone"
147+
:class="{ 'dragging': isDragging }"
148+
@drop="handleDrop"
149+
@dragover="handleDragOver"
150+
@dragleave="handleDragLeave"
151+
>
152+
153+
<!-- URL Input Section (when active) -->
154+
<div v-if="showUrlInput" class="url-input-section">
155+
<input
156+
v-model="urlInput"
157+
type="url"
158+
placeholder="Enter CSV URL (e.g., https://example.com/data.csv)"
159+
class="url-input"
160+
@keyup.enter="loadFromUrl"
161+
/>
162+
<button class="thin-btn primary" @click="loadFromUrl">Load</button>
163+
<button class="thin-btn" @click="showUrlInput = false">Cancel</button>
164+
</div>
165+
166+
<!-- Default CSV upload options -->
167+
<div v-else class="csv-options">
154168
<input
155169
ref="fileInputRef"
156170
type="file"
@@ -159,38 +173,21 @@ onUnmounted(() => {
159173
style="display: none;"
160174
@change="handleFileSelect"
161175
/>
162-
<span class="upload-icon">📁</span>
163-
<span class="upload-text">Upload CSV</span>
164-
<span class="drag-hint">or drop file here</span>
176+
<span class="drop-text">Drop CSV here</span>
177+
<button class="thin-btn" @click="fileInputRef?.click()">
178+
or upload file
179+
</button>
180+
<button class="thin-btn" @click="showUrlInput = true">
181+
or load from URL
182+
</button>
165183
</div>
166-
167-
<button
168-
class="url-toggle-btn"
169-
@click="showUrlInput = true"
170-
>
171-
🌐 Load from URL
172-
</button>
173-
</div>
174-
175-
<!-- URL Input Section (when active) -->
176-
<div v-else-if="!props.uploadedFile && showUrlInput" class="url-input-section">
177-
<input
178-
v-model="urlInput"
179-
type="url"
180-
placeholder="Enter CSV URL (e.g., https://example.com/data.csv)"
181-
class="url-input"
182-
@keyup.enter="loadFromUrl"
183-
/>
184-
<button class="url-load-btn" @click="loadFromUrl">Load</button>
185-
<button class="url-cancel-btn" @click="showUrlInput = false">Cancel</button>
186184
</div>
187185

188186
<div v-else class="csv-info-container">
189187
<button
190188
class="csv-button"
191189
@click="toggleDropdown"
192190
>
193-
<span class="csv-icon">📊</span>
194191
<span class="csv-text">
195192
{{ props.uploadedFile.name }} ({{ props.uploadedFile.rows }} × {{ props.uploadedFile.columns }})
196193
</span>
@@ -240,76 +237,75 @@ onUnmounted(() => {
240237
align-items: center;
241238
}
242239
243-
.upload-controls {
244-
display: flex;
245-
align-items: center;
246-
gap: 0.75rem;
247-
}
248-
249-
.upload-button {
250-
display: flex;
251-
align-items: center;
252-
gap: 0.5rem;
253-
background: #f3f4f6;
240+
.csv-drop-zone {
241+
background: #fafafa;
254242
border: 2px dashed #d1d5db;
255243
border-radius: 6px;
256244
padding: 0.5rem 0.75rem;
257-
font-size: 0.875rem;
258-
cursor: pointer;
259245
transition: all 0.3s ease;
260-
white-space: nowrap;
246+
min-height: 36px;
247+
display: flex;
248+
align-items: center;
249+
justify-content: center;
261250
}
262251
263-
.upload-button:hover {
264-
background: #e5e7eb;
252+
.csv-drop-zone:hover {
253+
background: #f3f4f6;
265254
border-color: #9ca3af;
266255
}
267256
268-
.upload-button.dragging {
257+
.csv-drop-zone.dragging {
269258
background: #dbeafe;
270259
border-color: #3b82f6;
271260
border-style: solid;
272261
}
273262
274-
.upload-icon {
263+
.csv-options {
264+
display: flex;
265+
align-items: center;
266+
gap: 0.375rem;
275267
font-size: 0.875rem;
276268
}
277269
278-
.upload-text {
279-
font-weight: 500;
280-
}
281-
282-
.drag-hint {
283-
font-size: 0.75rem;
284-
color: #6b7280;
285-
margin-left: 0.25rem;
270+
.drop-text {
271+
color: #000000;
272+
font-size: 0.875rem;
273+
margin-right: 0.25rem;
286274
}
287275
288-
.url-toggle-btn {
289-
background: #f8fafc;
290-
border: 1px solid #e2e8f0;
276+
.thin-btn {
277+
background: none;
278+
border: 1px solid #d1d5db;
291279
border-radius: 4px;
292-
padding: 0.375rem 0.75rem;
280+
padding: 0.25rem 0.5rem;
293281
font-size: 0.75rem;
294-
color: #475569;
282+
color: #3b82f6;
295283
cursor: pointer;
296284
transition: all 0.2s ease;
297-
align-self: flex-start;
285+
white-space: nowrap;
286+
}
287+
288+
.thin-btn:hover {
289+
background: #f3f4f6;
290+
border-color: #3b82f6;
291+
}
292+
293+
.thin-btn.primary {
294+
background: #3b82f6;
295+
color: white;
296+
border-color: #3b82f6;
298297
}
299298
300-
.url-toggle-btn:hover {
301-
background: #f1f5f9;
302-
border-color: #cbd5e1;
299+
.thin-btn.primary:hover {
300+
background: #2563eb;
301+
border-color: #2563eb;
303302
}
304303
305304
.url-input-section {
306305
display: flex;
307306
gap: 0.5rem;
308307
align-items: center;
309-
background: #f8fafc;
310-
padding: 0.5rem;
311-
border-radius: 4px;
312-
border: 1px solid #e2e8f0;
308+
width: 100%;
313309
}
314310
315311
.url-input {
@@ -327,35 +323,6 @@ onUnmounted(() => {
327323
box-shadow: 0 0 0 1px #3b82f6;
328324
}
329325
330-
.url-load-btn {
331-
background: #3b82f6;
332-
color: white;
333-
border: none;
334-
padding: 0.375rem 0.75rem;
335-
border-radius: 4px;
336-
font-size: 0.75rem;
337-
cursor: pointer;
338-
transition: background-color 0.2s ease;
339-
}
340-
341-
.url-load-btn:hover {
342-
background: #2563eb;
343-
}
344-
345-
.url-cancel-btn {
346-
background: #6b7280;
347-
color: white;
348-
border: none;
349-
padding: 0.375rem 0.75rem;
350-
border-radius: 4px;
351-
font-size: 0.75rem;
352-
cursor: pointer;
353-
transition: background-color 0.2s ease;
354-
}
355-
356-
.url-cancel-btn:hover {
357-
background: #4b5563;
358-
}
359326
360327
.csv-info-container {
361328
position: relative;
@@ -380,9 +347,6 @@ onUnmounted(() => {
380347
border-color: #9ca3af;
381348
}
382349
383-
.csv-icon {
384-
font-size: 0.875rem;
385-
}
386350
387351
.csv-text {
388352
font-weight: 500;

0 commit comments

Comments
 (0)