tag picker

This commit is contained in:
2025-11-11 15:59:17 +01:00
parent cd9a8a0204
commit 1193c71579
5 changed files with 53 additions and 35 deletions
+2 -1
View File
@@ -84,12 +84,13 @@ const createDesktopSurface = ({
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
const detail = detailOpen && detailProps const detail = detailOpen && detailProps
? (() => { ? (() => {
const { onClose, onOpenPreview, ...restDetailProps } = detailProps; const { onClose, onOpenPreview, tags: tagOptions, ...restDetailProps } = detailProps;
return ( return (
<DocumentViewerPanel <DocumentViewerPanel
variant="sidebar" variant="sidebar"
onCollapsePanel={onClose} onCollapsePanel={onClose}
onMaximizePanel={onOpenPreview} onMaximizePanel={onOpenPreview}
tagOptions={tagOptions}
{...restDetailProps} {...restDetailProps}
/> />
); );
@@ -1,10 +1,6 @@
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import useFloatingMenu from '../ui/useFloatingMenu'; import useFloatingMenu from '../ui/useFloatingMenu';
import { import { CheckIcon, CircleDashedCheckIcon, PlusIcon } from '../ui/icons';
PlusIcon,
CheckIcon,
CircleDashedCheckIcon,
} from '../ui/icons';
const STATE_ORDER = { const STATE_ORDER = {
all: 0, all: 0,
@@ -117,7 +113,8 @@ const SelectionAssignmentMenu = ({
); );
const handleCreate = useCallback( const handleCreate = useCallback(
async () => { async (event) => {
event?.preventDefault?.();
if (typeof onCreate !== 'function') { if (typeof onCreate !== 'function') {
return; return;
} }
@@ -144,9 +141,12 @@ const SelectionAssignmentMenu = ({
); );
const canCreate = Boolean(onCreate); const canCreate = Boolean(onCreate);
const showCreateOption = canCreate const trimmedQuery = query.trim();
&& query.trim().length > 0 const queryKey = trimmedQuery.toLowerCase();
&& !existingLabels.has(query.trim().toLowerCase()); const canSubmitCreate = canCreate
&& trimmedQuery.length > 0
&& !existingLabels.has(queryKey)
&& !pending;
const handleTriggerClick = useCallback(() => { const handleTriggerClick = useCallback(() => {
if (disabled) { if (disabled) {
@@ -184,15 +184,28 @@ const SelectionAssignmentMenu = ({
data-floating-position data-floating-position
> >
<div className="selection-assignment__header"> <div className="selection-assignment__header">
<input <form className="selection-assignment__form" onSubmit={handleCreate}>
ref={inputRef} <input
type="text" ref={inputRef}
value={query} type="text"
onChange={(event) => setQuery(event.target.value)} value={query}
placeholder={placeholder} onChange={(event) => setQuery(event.target.value)}
aria-label={placeholder} placeholder={placeholder}
disabled={pending} aria-label={placeholder}
/> disabled={pending}
/>
{canCreate ? (
<button
type="submit"
className="icon-button selection-assignment__add"
disabled={!canSubmitCreate}
aria-label={createLabel || 'Add'}
title={createLabel || 'Add'}
>
<PlusIcon aria-hidden="true" />
</button>
) : null}
</form>
</div> </div>
<div className="selection-assignment__list" role="presentation"> <div className="selection-assignment__list" role="presentation">
{filteredItems.length ? ( {filteredItems.length ? (
@@ -237,19 +250,6 @@ const SelectionAssignmentMenu = ({
<div className="menu__empty selection-assignment__empty">{emptyMessage}</div> <div className="menu__empty selection-assignment__empty">{emptyMessage}</div>
)} )}
</div> </div>
{showCreateOption ? (
<button
type="button"
className="menu__item selection-assignment__create"
onClick={handleCreate}
disabled={pending}
>
<PlusIcon className="selection-assignment__icon" aria-hidden="true" />
<span className="selection-assignment__label">
{createLabel ? `${createLabel}${query.trim()}` : `Create “${query.trim()}`}
</span>
</button>
) : null}
</div> </div>
) : null} ) : null}
</div> </div>
@@ -93,12 +93,13 @@ const createDocumentsSurface = ({
const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null; const sidebarToggle = renderSidebarToggle ? renderSidebarToggle() : null;
const detail = detailOpen && detailProps const detail = detailOpen && detailProps
? (() => { ? (() => {
const { onClose, onOpenPreview, ...restDetailProps } = detailProps; const { onClose, onOpenPreview, tags: tagOptions, ...restDetailProps } = detailProps;
return ( return (
<DocumentViewerPanel <DocumentViewerPanel
variant="sidebar" variant="sidebar"
onCollapsePanel={onClose} onCollapsePanel={onClose}
onMaximizePanel={onOpenPreview} onMaximizePanel={onOpenPreview}
tagOptions={tagOptions}
{...restDetailProps} {...restDetailProps}
/> />
); );
+17 -2
View File
@@ -191,7 +191,14 @@
border-bottom: 1px solid var(--border-subtle); border-bottom: 1px solid var(--border-subtle);
} }
.selection-assignment__header input { .selection-assignment__form {
display: flex;
gap: 0.4rem;
align-items: stretch;
}
.selection-assignment__form input {
flex: 1 1 auto;
width: 100%; width: 100%;
padding: 0.35rem 0.6rem; padding: 0.35rem 0.6rem;
border: 1px solid var(--border-subtle); border: 1px solid var(--border-subtle);
@@ -201,12 +208,20 @@
font-size: 0.95rem; font-size: 0.95rem;
} }
.selection-assignment__header input:focus-visible { .selection-assignment__form input:focus-visible {
outline: none; outline: none;
border-color: color-mix(in oklch, var(--accent) 60%, transparent); border-color: color-mix(in oklch, var(--accent) 60%, transparent);
box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent); box-shadow: 0 0 0 1px color-mix(in oklch, var(--accent) 35%, transparent);
} }
.selection-assignment__add {
align-self: stretch;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 2rem;
}
.selection-assignment__list { .selection-assignment__list {
max-height: max(240px, 50vh); max-height: max(240px, 50vh);
overflow-y: auto; overflow-y: auto;
+1
View File
@@ -247,6 +247,7 @@
.menu__list { .menu__list {
overflow-y: auto; overflow-y: auto;
max-height: min(20rem, 60vh);
padding: 0.25rem; padding: 0.25rem;
display: flex; display: flex;
flex-direction: column; flex-direction: column;