Files
Oxicloud/static/js/components/pendingEmailVignette.js
T
Edouard Vanbelle 9fac34f91e feat(ui): add share modal and users
- fix(external users): fix app starting for external users
2026-06-03 00:31:59 +02:00

71 lines
2.8 KiB
JavaScript

// @ts-check
/**
* PendingEmailVignette — visual for an external invite *before* the
* server has resolved (or lazily created) the recipient user.
*
* Used by the share modal's email-input UX:
* 1. The user types an address in the search input.
* 2. When the address matches no existing contact but parses as an
* email, the dropdown surfaces an "Invite by email" suggestion
* rendered with this vignette.
* 3. Clicking the suggestion stages an email-typed chip that also
* uses this vignette.
* 4. On Apply, the modal POSTs `subject.type=email` and reloads the
* grant list — at which point the resolved real userId takes
* over via the regular `createUserVignette`, which paints the
* same `fa-building-circle-xmark` badge (PR 11.2). Visual
* continuity is intentional: the chip's look doesn't change
* across the commit boundary.
*
* Reuses the userVignette CSS so size variants (xs/sm/md/list/lg/menu/xl),
* colour palette, and the external-badge styling all apply unchanged.
* The external badge here is FORCED visible — by definition an email
* we don't recognise is going to mint an external user.
*/
import { _colorIndex, _initials } from './userVignette.js';
/** @typedef {'xs'|'sm'|'list'|'md'|'lg'|'menu'|'xl'} VignetteSize */
/**
* Build a transient vignette seeded from an email address (no UUID yet).
*
* @param {string} email
* @param {VignetteSize} [size='sm']
* @returns {HTMLElement}
*/
export function createPendingEmailVignette(email, size = 'sm') {
const trimmed = email.trim();
const colorIdx = _colorIndex(trimmed);
const wrapper = document.createElement('span');
wrapper.className = `user-vignette user-vignette--${size}`;
const avatar = document.createElement('span');
avatar.className = `user-vignette__avatar uv-color-${colorIdx}`;
// Synthesize initials: local-part initial + domain initial when
// possible, otherwise fall back to the first two chars.
const [local, domain] = trimmed.split('@');
const synthName = local && domain ? `${local[0]} ${domain[0]}` : trimmed.slice(0, 2);
avatar.textContent = _initials(synthName);
// Forced external badge — this is the whole point of the component.
const badge = document.createElement('i');
badge.className = 'user-vignette__origin user-vignette__origin--external fa-solid fa-building-circle-xmark';
badge.title = 'External invitation';
badge.setAttribute('aria-hidden', 'true');
avatar.appendChild(badge);
wrapper.appendChild(avatar);
// The "name" for a pending invite is just the email itself — there
// is no separate display name yet.
const nameEl = document.createElement('span');
nameEl.className = 'user-vignette__name';
nameEl.textContent = trimmed;
wrapper.appendChild(nameEl);
return wrapper;
}