/// /** * Phone Module - Pattern for a single phone number with customizable label * * A composable pattern that can be used standalone or embedded in containers * like Record. Stores one phone number with a label (Mobile, Home, Work, etc.) */ import { computed, type Default, NAME, recipe, UI } from "commontools"; import type { ModuleMetadata } from "./container-protocol.ts"; // ===== Standard Labels ===== export const STANDARD_LABELS = ["Mobile", "Home", "Work", "Other"]; // ===== Self-Describing Metadata ===== export const MODULE_METADATA: ModuleMetadata = { type: "phone", label: "Phone", icon: "\u{1F4F1}", // 📱 mobile phone emoji allowMultiple: true, // Show "add another" button for multiple phones schema: { phone: { type: "string", description: "Phone number" }, phoneLabel: { type: "string", enum: STANDARD_LABELS, description: "Phone label (Mobile, Home, Work, etc.)", }, }, fieldMapping: ["phone", "phoneNumber"], }; // ===== Types ===== export interface PhoneModuleInput { /** Label for this phone (Mobile, Home, Work, etc.) */ label: Default; /** Phone number (preserve original formatting) */ number: Default; } // ===== The Pattern ===== export const PhoneModule = recipe( "PhoneModule", ({ label, number }) => { // Build display text const displayText = computed(() => { const num = number?.trim(); return num || "Not set"; }); // Build autocomplete items from standard labels const labelItems = STANDARD_LABELS.map((l) => ({ value: l, label: l })); return { [NAME]: computed( () => `${MODULE_METADATA.icon} ${label}: ${displayText}`, ), [UI]: ( ), label, number, }; }, ); export default PhoneModule;