{"version":3,"file":"transfer-state.cjs","names":[],"sources":["../../../src/components/Transfer/transfer-state.ts"],"sourcesContent":["import type { ReactNode } from \"react\";\n\n/** One movable entry. */\nexport interface TransferItem {\n    /** Stable identity. What `value`/`onChange` carry. */\n    id: string;\n    /** What the row shows. */\n    label: ReactNode;\n    /** Text the search box matches against. Falls back to `label` when it is a string. */\n    searchText?: string;\n    /** Row that cannot move — a mandatory permission, a locked seat. */\n    disabled?: boolean;\n    /** Anything the app wants back in `renderItem`. */\n    data?: Record<string, unknown>;\n}\n\n/** Which pane an operation is about. */\nexport type TransferSide = \"source\" | \"target\";\n\n/**\n * Split the catalogue into the two panes.\n *\n * The selected ids are the single source of truth — the panes are derived, never\n * stored. Two stored lists drift the moment the catalogue changes under them: a\n * permission that is removed upstream lingers in whichever pane held it, and an\n * id that appears in both is a bug nobody can see.\n *\n * Order follows `items`, so both panes read in the catalogue's order rather than\n * in the order somebody happened to click.\n *\n * @param items - The whole catalogue.\n * @param value - Ids currently on the target side.\n */\nexport function splitSides(\n    items: readonly TransferItem[],\n    value: readonly string[],\n): { source: TransferItem[]; target: TransferItem[] } {\n    const selected = new Set(value);\n    const source: TransferItem[] = [];\n    const target: TransferItem[] = [];\n    for (const item of items) {\n        (selected.has(item.id) ? target : source).push(item);\n    }\n    return { source, target };\n}\n\n/** Text a row is searched by. */\nexport function searchTextOf(item: TransferItem): string {\n    if (item.searchText !== undefined) return item.searchText;\n    return typeof item.label === \"string\" ? item.label : \"\";\n}\n\n/**\n * Filter a pane by a query, case- and accent-insensitively.\n *\n * Accent folding is not optional for a PT-BR audience: typing \"sao\" has to find\n * \"São Paulo\", and a plain `includes` would not.\n */\nexport function filterItems(items: readonly TransferItem[], query: string): TransferItem[] {\n    const needle = fold(query);\n    if (!needle) return [...items];\n    return items.filter((item) => fold(searchTextOf(item)).includes(needle));\n}\n\n/** Lowercase and strip diacritics. */\nfunction fold(text: string): string {\n    return text\n        .normalize(\"NFD\")\n        .replace(/\\p{Diacritic}/gu, \"\")\n        .toLowerCase()\n        .trim();\n}\n\n/**\n * Apply a move and return the next value.\n *\n * Disabled rows are dropped here rather than at the call site, so no caller can\n * move one by pressing the bulk button — the check has to live where the\n * decision is made, not in each of the four places that trigger one.\n *\n * @param params.value - Current target ids.\n * @param params.moving - Ids being moved.\n * @param params.to - Destination pane.\n * @param params.items - The catalogue, to look disabled rows up.\n * @param params.order - Ids in catalogue order, so the result stays stable.\n * @returns The next value, in catalogue order.\n */\nexport function applyMove({\n    value,\n    moving,\n    to,\n    items,\n}: {\n    value: readonly string[];\n    moving: readonly string[];\n    to: TransferSide;\n    items: readonly TransferItem[];\n}): string[] {\n    const byId = new Map(items.map((item) => [item.id, item]));\n    const allowed = moving.filter((id) => byId.has(id) && !byId.get(id)?.disabled);\n    const next = new Set(value);\n    for (const id of allowed) {\n        if (to === \"target\") next.add(id);\n        else next.delete(id);\n    }\n    return items.filter((item) => next.has(item.id)).map((item) => item.id);\n}\n\n/** Ids in `items` that a checkbox set may still act on. */\nexport function movableIds(items: readonly TransferItem[]): string[] {\n    return items.filter((item) => !item.disabled).map((item) => item.id);\n}\n\n/** Labels the component needs, per locale. */\ninterface TransferStrings {\n    sourceTitle: string;\n    targetTitle: string;\n    search: string;\n    toTarget: string;\n    toSource: string;\n    allToTarget: string;\n    allToSource: string;\n    empty: string;\n    noMatches: string;\n    selected: (n: number, total: number) => string;\n    moved: (n: number, side: string) => string;\n}\n\nconst PT_BR: TransferStrings = {\n    sourceTitle: \"Disponíveis\",\n    targetTitle: \"Selecionados\",\n    search: \"Buscar\",\n    toTarget: \"Mover selecionados para a direita\",\n    toSource: \"Mover selecionados para a esquerda\",\n    allToTarget: \"Mover todos para a direita\",\n    allToSource: \"Mover todos para a esquerda\",\n    empty: \"Nada aqui\",\n    noMatches: \"Nenhum resultado\",\n    selected: (n, total) => `${n} de ${total} marcados`,\n    moved: (n, side) => `${n} ${n === 1 ? \"item movido\" : \"itens movidos\"} para ${side}`,\n};\n\nconst EN: TransferStrings = {\n    sourceTitle: \"Available\",\n    targetTitle: \"Selected\",\n    search: \"Search\",\n    toTarget: \"Move checked to the right\",\n    toSource: \"Move checked to the left\",\n    allToTarget: \"Move all to the right\",\n    allToSource: \"Move all to the left\",\n    empty: \"Nothing here\",\n    noMatches: \"No matches\",\n    selected: (n, total) => `${n} of ${total} checked`,\n    moved: (n, side) => `${n} ${n === 1 ? \"item moved\" : \"items moved\"} to ${side}`,\n};\n\n/** Locale strings for the dual list. */\nexport function transferStrings(locale: \"pt-BR\" | \"en\"): TransferStrings {\n    return locale === \"en\" ? EN : PT_BR;\n}\n"],"mappings":"AAiCA,SAAgB,EACZ,EACA,EACkD,CAClD,IAAM,EAAW,IAAI,IAAI,CAAK,EACxB,EAAyB,CAAC,EAC1B,EAAyB,CAAC,EAChC,IAAK,IAAM,KAAQ,GACd,EAAS,IAAI,EAAK,EAAE,EAAI,EAAS,EAAA,CAAQ,KAAK,CAAI,EAEvD,MAAO,CAAE,SAAQ,QAAO,CAC5B,CAGA,SAAgB,EAAa,EAA4B,CAErD,OADI,EAAK,aAAe,IAAA,GACjB,OAAO,EAAK,OAAU,SAAW,EAAK,MAAQ,GADX,EAAK,UAEnD,CAQA,SAAgB,EAAY,EAAgC,EAA+B,CACvF,IAAM,EAAS,EAAK,CAAK,EAEzB,OADK,EACE,EAAM,OAAQ,GAAS,EAAK,EAAa,CAAI,CAAC,CAAC,CAAC,SAAS,CAAM,CAAC,EADnD,CAAC,GAAG,CAAK,CAEjC,CAGA,SAAS,EAAK,EAAsB,CAChC,OAAO,EACF,UAAU,KAAK,CAAC,CAChB,QAAQ,kBAAmB,EAAE,CAAC,CAC9B,YAAY,CAAC,CACb,KAAK,CACd,CAgBA,SAAgB,EAAU,CACtB,QACA,SACA,KACA,SAMS,CACT,IAAM,EAAO,IAAI,IAAI,EAAM,IAAK,GAAS,CAAC,EAAK,GAAI,CAAI,CAAC,CAAC,EACnD,EAAU,EAAO,OAAQ,GAAO,EAAK,IAAI,CAAE,GAAK,CAAC,EAAK,IAAI,CAAE,CAAC,EAAE,QAAQ,EACvE,EAAO,IAAI,IAAI,CAAK,EAC1B,IAAK,IAAM,KAAM,EACT,IAAO,SAAU,EAAK,IAAI,CAAE,EAC3B,EAAK,OAAO,CAAE,EAEvB,OAAO,EAAM,OAAQ,GAAS,EAAK,IAAI,EAAK,EAAE,CAAC,CAAC,CAAC,IAAK,GAAS,EAAK,EAAE,CAC1E,CAGA,SAAgB,EAAW,EAA0C,CACjE,OAAO,EAAM,OAAQ,GAAS,CAAC,EAAK,QAAQ,CAAC,CAAC,IAAK,GAAS,EAAK,EAAE,CACvE,CAiBA,IAAM,EAAyB,CAC3B,YAAa,cACb,YAAa,eACb,OAAQ,SACR,SAAU,oCACV,SAAU,qCACV,YAAa,6BACb,YAAa,8BACb,MAAO,YACP,UAAW,mBACX,UAAW,EAAG,IAAU,GAAG,EAAE,MAAM,EAAM,WACzC,OAAQ,EAAG,IAAS,GAAG,EAAE,GAAG,IAAM,EAAI,cAAgB,gBAAgB,QAAQ,GAClF,EAEM,EAAsB,CACxB,YAAa,YACb,YAAa,WACb,OAAQ,SACR,SAAU,4BACV,SAAU,2BACV,YAAa,wBACb,YAAa,uBACb,MAAO,eACP,UAAW,aACX,UAAW,EAAG,IAAU,GAAG,EAAE,MAAM,EAAM,UACzC,OAAQ,EAAG,IAAS,GAAG,EAAE,GAAG,IAAM,EAAI,aAAe,cAAc,MAAM,GAC7E,EAGA,SAAgB,EAAgB,EAAyC,CACrE,OAAO,IAAW,KAAO,EAAK,CAClC"}