Auto-growing name box for the new-project tile
Replace the plain <input> with a rows=1 <textarea> that grows to fit its content on input, so a long or wrapped name stays fully visible instead of scrolling. Enter still submits rather than adding a newline, and the box resets to one row after a successful create. Also make the tile's background transparent (dashed border stays) and drop its 'Create a project' title, keeping just the Name field -- accessible label moved to aria-label since the placeholder now stands alone. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+9
-1
@@ -361,6 +361,7 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.project-card--new {
|
.project-card--new {
|
||||||
|
background: transparent;
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,13 +371,20 @@ h1 {
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.project-card--new input {
|
.project-card__name-input {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
padding: 0.55rem 0.7rem;
|
padding: 0.55rem 0.7rem;
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: var(--bg);
|
background: var(--bg);
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
|
font: inherit;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
|
/* Grown in JS to fit its content -- no manual resize handle, no scrollbar. */
|
||||||
|
resize: none;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- project detail: cards ------------------------------------------ */
|
/* --- project detail: cards ------------------------------------------ */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, nextTick, onMounted, ref } from 'vue'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import { ApiError } from '../lib/api'
|
import { ApiError } from '../lib/api'
|
||||||
import { MAX_PROJECTS, useProjectsStore } from '../stores/projects'
|
import { MAX_PROJECTS, useProjectsStore } from '../stores/projects'
|
||||||
@@ -11,6 +11,25 @@ const loadError = ref<string | null>(null)
|
|||||||
const title = ref('')
|
const title = ref('')
|
||||||
const createError = ref<ApiError | null>(null)
|
const createError = ref<ApiError | null>(null)
|
||||||
const submitting = ref(false)
|
const submitting = ref(false)
|
||||||
|
const titleInput = ref<HTMLTextAreaElement | null>(null)
|
||||||
|
|
||||||
|
// Grows the textarea to fit its content instead of scrolling, so a name
|
||||||
|
// that wraps stays fully visible.
|
||||||
|
function resizeTitleInput() {
|
||||||
|
const el = titleInput.value
|
||||||
|
if (!el) return
|
||||||
|
el.style.height = 'auto'
|
||||||
|
el.style.height = `${el.scrollHeight}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matches a plain <input>'s behaviour: Enter submits rather than inserting
|
||||||
|
// a newline (the box only grows from wrapping, e.g. a long pasted name).
|
||||||
|
function onTitleKeydown(e: KeyboardEvent) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault()
|
||||||
|
onCreate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const atLimit = computed(() => projects.projects.length >= MAX_PROJECTS)
|
const atLimit = computed(() => projects.projects.length >= MAX_PROJECTS)
|
||||||
|
|
||||||
@@ -31,6 +50,8 @@ async function onCreate() {
|
|||||||
try {
|
try {
|
||||||
await projects.createProject(title.value)
|
await projects.createProject(title.value)
|
||||||
title.value = ''
|
title.value = ''
|
||||||
|
await nextTick()
|
||||||
|
resizeTitleInput()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
createError.value = e instanceof ApiError ? e : new ApiError('Could not create the project.', 0)
|
createError.value = e instanceof ApiError ? e : new ApiError('Could not create the project.', 0)
|
||||||
} finally {
|
} finally {
|
||||||
@@ -59,8 +80,19 @@ async function onCreate() {
|
|||||||
|
|
||||||
<form class="project-card project-card--new" @submit.prevent="onCreate">
|
<form class="project-card project-card--new" @submit.prevent="onCreate">
|
||||||
<label>
|
<label>
|
||||||
<span class="project-card__title">Create a project</span>
|
<textarea
|
||||||
<input v-model="title" type="text" maxlength="255" placeholder="Name" required :disabled="atLimit" />
|
ref="titleInput"
|
||||||
|
v-model="title"
|
||||||
|
class="project-card__name-input"
|
||||||
|
rows="1"
|
||||||
|
maxlength="255"
|
||||||
|
placeholder="Name"
|
||||||
|
aria-label="Name"
|
||||||
|
required
|
||||||
|
:disabled="atLimit"
|
||||||
|
@input="resizeTitleInput"
|
||||||
|
@keydown="onTitleKeydown"
|
||||||
|
></textarea>
|
||||||
<small v-if="createError?.fieldError('title')" class="field-error">
|
<small v-if="createError?.fieldError('title')" class="field-error">
|
||||||
{{ createError.fieldError('title') }}
|
{{ createError.fieldError('title') }}
|
||||||
</small>
|
</small>
|
||||||
|
|||||||
Reference in New Issue
Block a user