diff --git a/.changeset/api-keys-search-clear-button.md b/.changeset/api-keys-search-clear-button.md new file mode 100644 index 00000000000..f60a7bab5e8 --- /dev/null +++ b/.changeset/api-keys-search-clear-button.md @@ -0,0 +1,9 @@ +--- +'@clerk/localizations': patch +'@clerk/shared': patch +'@clerk/ui': patch +--- + +Add a clear button to search inputs for quickly resetting the current query. It appears in the `` search and the `` members search. + +Search inputs now expose a shared `searchInput` appearance element (layered alongside any existing component-specific element), and the clear button is themeable via the new shared `searchInputClearButton` element. The clear button's label can be customized with the new shared `searchInput.action__clear` localization key. diff --git a/packages/localizations/src/ar-SA.ts b/packages/localizations/src/ar-SA.ts index de94ee932ae..9b70f8d0a2d 100644 --- a/packages/localizations/src/ar-SA.ts +++ b/packages/localizations/src/ar-SA.ts @@ -1216,6 +1216,9 @@ export const arSA: LocalizationResource = { title: undefined, }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'إضافة حساب', diff --git a/packages/localizations/src/be-BY.ts b/packages/localizations/src/be-BY.ts index 2203157ce23..3fb72ae5d54 100644 --- a/packages/localizations/src/be-BY.ts +++ b/packages/localizations/src/be-BY.ts @@ -1223,6 +1223,9 @@ export const beBY: LocalizationResource = { title: 'Код TOTP', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Дадаць уліковы запіс', diff --git a/packages/localizations/src/bg-BG.ts b/packages/localizations/src/bg-BG.ts index 8b6d0093b09..4e14def42ab 100644 --- a/packages/localizations/src/bg-BG.ts +++ b/packages/localizations/src/bg-BG.ts @@ -1220,6 +1220,9 @@ export const bgBG: LocalizationResource = { title: 'Authenticator App MFA', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Add account', diff --git a/packages/localizations/src/bn-IN.ts b/packages/localizations/src/bn-IN.ts index bf5acdc03a2..d8a33bdfa92 100644 --- a/packages/localizations/src/bn-IN.ts +++ b/packages/localizations/src/bn-IN.ts @@ -1228,6 +1228,9 @@ export const bnIN: LocalizationResource = { title: 'যাচাইকরণ প্রয়োজন', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'অ্যাকাউন্ট যোগ করুন', diff --git a/packages/localizations/src/ca-ES.ts b/packages/localizations/src/ca-ES.ts index a768b80edd1..fe24667d6a5 100644 --- a/packages/localizations/src/ca-ES.ts +++ b/packages/localizations/src/ca-ES.ts @@ -1228,6 +1228,9 @@ export const caES: LocalizationResource = { title: 'Verificació per TOTP (2FA)', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Afegeix compte', diff --git a/packages/localizations/src/cs-CZ.ts b/packages/localizations/src/cs-CZ.ts index e5566d4bce0..40ca97e4f47 100644 --- a/packages/localizations/src/cs-CZ.ts +++ b/packages/localizations/src/cs-CZ.ts @@ -1226,6 +1226,9 @@ export const csCZ: LocalizationResource = { title: 'Vyžadováno ověření', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Přidat účet', diff --git a/packages/localizations/src/da-DK.ts b/packages/localizations/src/da-DK.ts index d6a111c43a7..8f6cd90e1ce 100644 --- a/packages/localizations/src/da-DK.ts +++ b/packages/localizations/src/da-DK.ts @@ -1218,6 +1218,9 @@ export const daDK: LocalizationResource = { title: undefined, }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Tilføj konto', diff --git a/packages/localizations/src/de-DE.ts b/packages/localizations/src/de-DE.ts index 970558a0f4f..f7c181cf898 100644 --- a/packages/localizations/src/de-DE.ts +++ b/packages/localizations/src/de-DE.ts @@ -1235,6 +1235,9 @@ export const deDE: LocalizationResource = { title: 'TOTP-Verifizierung (MFA)', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Konto hinzufügen', diff --git a/packages/localizations/src/el-GR.ts b/packages/localizations/src/el-GR.ts index 156e7e56130..fbc71c2473a 100644 --- a/packages/localizations/src/el-GR.ts +++ b/packages/localizations/src/el-GR.ts @@ -1226,6 +1226,9 @@ export const elGR: LocalizationResource = { title: 'Επαλήθευση δύο παραγόντων', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Προσθήκη λογαριασμού', diff --git a/packages/localizations/src/en-GB.ts b/packages/localizations/src/en-GB.ts index 27e0239b6ea..74fa7ea9e73 100644 --- a/packages/localizations/src/en-GB.ts +++ b/packages/localizations/src/en-GB.ts @@ -1219,6 +1219,9 @@ export const enGB: LocalizationResource = { title: 'Verification required', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Add account', diff --git a/packages/localizations/src/en-US.ts b/packages/localizations/src/en-US.ts index 6f4c008a9ae..d766d4ce383 100644 --- a/packages/localizations/src/en-US.ts +++ b/packages/localizations/src/en-US.ts @@ -1247,6 +1247,9 @@ export const enUS: LocalizationResource = { title: 'Verification required', }, }, + searchInput: { + action__clear: 'Clear search', + }, signIn: { accountSwitcher: { action__addAccount: 'Add account', diff --git a/packages/localizations/src/es-CR.ts b/packages/localizations/src/es-CR.ts index f48e0d8dab4..6c465f680a0 100644 --- a/packages/localizations/src/es-CR.ts +++ b/packages/localizations/src/es-CR.ts @@ -1224,6 +1224,9 @@ export const esCR: LocalizationResource = { title: 'Verificación requerida', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Agregar cuenta', diff --git a/packages/localizations/src/es-ES.ts b/packages/localizations/src/es-ES.ts index 7d08afe08cd..2d6895e5822 100644 --- a/packages/localizations/src/es-ES.ts +++ b/packages/localizations/src/es-ES.ts @@ -1229,6 +1229,9 @@ export const esES: LocalizationResource = { title: 'Verificación por TOTP (2FA)', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Añadir cuenta', diff --git a/packages/localizations/src/es-MX.ts b/packages/localizations/src/es-MX.ts index 18d06e8dc88..64b90aa3da2 100644 --- a/packages/localizations/src/es-MX.ts +++ b/packages/localizations/src/es-MX.ts @@ -1225,6 +1225,9 @@ export const esMX: LocalizationResource = { title: 'Verificación requerida', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Agregar cuenta', diff --git a/packages/localizations/src/es-UY.ts b/packages/localizations/src/es-UY.ts index 45a1012d1a7..b027e07069f 100644 --- a/packages/localizations/src/es-UY.ts +++ b/packages/localizations/src/es-UY.ts @@ -1223,6 +1223,9 @@ export const esUY: LocalizationResource = { title: 'Verificación requerida', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Agregar cuenta', diff --git a/packages/localizations/src/fa-IR.ts b/packages/localizations/src/fa-IR.ts index 2a28fa818b2..1cf0c875abf 100644 --- a/packages/localizations/src/fa-IR.ts +++ b/packages/localizations/src/fa-IR.ts @@ -1228,6 +1228,9 @@ export const faIR: LocalizationResource = { title: 'تایید هویت الزامی است', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'افزودن حساب', diff --git a/packages/localizations/src/fi-FI.ts b/packages/localizations/src/fi-FI.ts index 8f9779263f9..d02b810a53a 100644 --- a/packages/localizations/src/fi-FI.ts +++ b/packages/localizations/src/fi-FI.ts @@ -1230,6 +1230,9 @@ export const fiFI: LocalizationResource = { title: 'Vahvistus vaaditaan', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Lisää tili', diff --git a/packages/localizations/src/fr-FR.ts b/packages/localizations/src/fr-FR.ts index 0c8fc0af507..3ef7465f900 100644 --- a/packages/localizations/src/fr-FR.ts +++ b/packages/localizations/src/fr-FR.ts @@ -1236,6 +1236,9 @@ export const frFR: LocalizationResource = { title: 'Vérification par application d’authentification', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Ajouter un compte', diff --git a/packages/localizations/src/he-IL.ts b/packages/localizations/src/he-IL.ts index 991eba72e8e..b1763ec3337 100644 --- a/packages/localizations/src/he-IL.ts +++ b/packages/localizations/src/he-IL.ts @@ -1213,6 +1213,9 @@ export const heIL: LocalizationResource = { title: 'אימות דו-שלבי', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'הוסף חשבון', diff --git a/packages/localizations/src/hi-IN.ts b/packages/localizations/src/hi-IN.ts index 10ad49c33fd..98812de0410 100644 --- a/packages/localizations/src/hi-IN.ts +++ b/packages/localizations/src/hi-IN.ts @@ -1228,6 +1228,9 @@ export const hiIN: LocalizationResource = { title: 'सत्यापन आवश्यक है', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'खाता जोड़ें', diff --git a/packages/localizations/src/hr-HR.ts b/packages/localizations/src/hr-HR.ts index 9911ad376c8..db93f008960 100644 --- a/packages/localizations/src/hr-HR.ts +++ b/packages/localizations/src/hr-HR.ts @@ -1229,6 +1229,9 @@ export const hrHR: LocalizationResource = { title: 'Potrebna verifikacija', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Dodaj račun', diff --git a/packages/localizations/src/hu-HU.ts b/packages/localizations/src/hu-HU.ts index f0d727933c6..3a3aff4cfe8 100644 --- a/packages/localizations/src/hu-HU.ts +++ b/packages/localizations/src/hu-HU.ts @@ -1231,6 +1231,9 @@ export const huHU: LocalizationResource = { title: 'Ellenőrzés szükséges', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Fiók hozzáadása', diff --git a/packages/localizations/src/id-ID.ts b/packages/localizations/src/id-ID.ts index 0e8ad38dd4a..df03c826a49 100644 --- a/packages/localizations/src/id-ID.ts +++ b/packages/localizations/src/id-ID.ts @@ -1222,6 +1222,9 @@ export const idID: LocalizationResource = { title: 'Verifikasi diperlukan', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Tambah akun', diff --git a/packages/localizations/src/is-IS.ts b/packages/localizations/src/is-IS.ts index 0ca57d76698..a2f5087a28e 100644 --- a/packages/localizations/src/is-IS.ts +++ b/packages/localizations/src/is-IS.ts @@ -1230,6 +1230,9 @@ export const isIS: LocalizationResource = { title: 'Staðfesting nauðsynleg', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Bæta við reikningi', diff --git a/packages/localizations/src/it-IT.ts b/packages/localizations/src/it-IT.ts index 96a6a24fba0..305b7cc39d0 100644 --- a/packages/localizations/src/it-IT.ts +++ b/packages/localizations/src/it-IT.ts @@ -1228,6 +1228,9 @@ export const itIT: LocalizationResource = { title: 'Verifica TOTP (MFA)', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Aggiungi account', diff --git a/packages/localizations/src/ja-JP.ts b/packages/localizations/src/ja-JP.ts index 8eb8b07dfcf..ad9f53c787f 100644 --- a/packages/localizations/src/ja-JP.ts +++ b/packages/localizations/src/ja-JP.ts @@ -1229,6 +1229,9 @@ export const jaJP: LocalizationResource = { title: '確認が必要です', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'アカウントを追加', diff --git a/packages/localizations/src/kk-KZ.ts b/packages/localizations/src/kk-KZ.ts index 4978a338c80..f2ecf7e21fe 100644 --- a/packages/localizations/src/kk-KZ.ts +++ b/packages/localizations/src/kk-KZ.ts @@ -1212,6 +1212,9 @@ export const kkKZ: LocalizationResource = { title: 'Растау қажет', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Есептік жазба қосу', diff --git a/packages/localizations/src/ko-KR.ts b/packages/localizations/src/ko-KR.ts index 12aed128ad1..0bb35f7cee4 100644 --- a/packages/localizations/src/ko-KR.ts +++ b/packages/localizations/src/ko-KR.ts @@ -1217,6 +1217,9 @@ export const koKR: LocalizationResource = { title: '인증이 필요해요', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: '계정 추가', diff --git a/packages/localizations/src/mn-MN.ts b/packages/localizations/src/mn-MN.ts index 1c05a448f19..0d25f3cea12 100644 --- a/packages/localizations/src/mn-MN.ts +++ b/packages/localizations/src/mn-MN.ts @@ -1221,6 +1221,9 @@ export const mnMN: LocalizationResource = { title: undefined, }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Бүртгэл нэмэх', diff --git a/packages/localizations/src/ms-MY.ts b/packages/localizations/src/ms-MY.ts index 9e015ceee8e..72e32d5764c 100644 --- a/packages/localizations/src/ms-MY.ts +++ b/packages/localizations/src/ms-MY.ts @@ -1232,6 +1232,9 @@ export const msMY: LocalizationResource = { title: 'Pengesahan diperlukan', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Tambah akaun', diff --git a/packages/localizations/src/nb-NO.ts b/packages/localizations/src/nb-NO.ts index 55692054367..edd764c32d5 100644 --- a/packages/localizations/src/nb-NO.ts +++ b/packages/localizations/src/nb-NO.ts @@ -1231,6 +1231,9 @@ export const nbNO: LocalizationResource = { title: 'Verifisering påkrevd', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Legg til konto', diff --git a/packages/localizations/src/nl-BE.ts b/packages/localizations/src/nl-BE.ts index 74cde0f3c28..4107509c7d1 100644 --- a/packages/localizations/src/nl-BE.ts +++ b/packages/localizations/src/nl-BE.ts @@ -1222,6 +1222,9 @@ export const nlBE: LocalizationResource = { title: 'Tweestapsverificatie', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Account toevoegen', diff --git a/packages/localizations/src/nl-NL.ts b/packages/localizations/src/nl-NL.ts index cb89363c510..b54ddc59780 100644 --- a/packages/localizations/src/nl-NL.ts +++ b/packages/localizations/src/nl-NL.ts @@ -1222,6 +1222,9 @@ export const nlNL: LocalizationResource = { title: 'Tweestapsverificatie', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Account toevoegen', diff --git a/packages/localizations/src/pl-PL.ts b/packages/localizations/src/pl-PL.ts index 6fede4db9eb..92adfd113a1 100644 --- a/packages/localizations/src/pl-PL.ts +++ b/packages/localizations/src/pl-PL.ts @@ -1220,6 +1220,9 @@ export const plPL: LocalizationResource = { title: 'Wymagana weryfikacja', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Dodaj konto', diff --git a/packages/localizations/src/pt-BR.ts b/packages/localizations/src/pt-BR.ts index ae61bfe88c4..0b9478c97dd 100644 --- a/packages/localizations/src/pt-BR.ts +++ b/packages/localizations/src/pt-BR.ts @@ -1230,6 +1230,9 @@ export const ptBR: LocalizationResource = { title: 'Autenticação TOTP', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Adicionar conta', diff --git a/packages/localizations/src/pt-PT.ts b/packages/localizations/src/pt-PT.ts index 7754da6a7fb..f378392dcb4 100644 --- a/packages/localizations/src/pt-PT.ts +++ b/packages/localizations/src/pt-PT.ts @@ -1231,6 +1231,9 @@ export const ptPT: LocalizationResource = { title: 'Verificação necessária', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Adicionar conta', diff --git a/packages/localizations/src/ro-RO.ts b/packages/localizations/src/ro-RO.ts index dec56853bd8..6a91ac6bbd0 100644 --- a/packages/localizations/src/ro-RO.ts +++ b/packages/localizations/src/ro-RO.ts @@ -1231,6 +1231,9 @@ export const roRO: LocalizationResource = { title: 'Verificare în doi pași', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Adaugă cont', diff --git a/packages/localizations/src/ru-RU.ts b/packages/localizations/src/ru-RU.ts index 201b23e3775..545061ad0e5 100644 --- a/packages/localizations/src/ru-RU.ts +++ b/packages/localizations/src/ru-RU.ts @@ -1227,6 +1227,9 @@ export const ruRU: LocalizationResource = { title: 'Двухфакторная верификация', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Добавить учетную запись', diff --git a/packages/localizations/src/sk-SK.ts b/packages/localizations/src/sk-SK.ts index c81c0238ae1..0c34697e47f 100644 --- a/packages/localizations/src/sk-SK.ts +++ b/packages/localizations/src/sk-SK.ts @@ -1221,6 +1221,9 @@ export const skSK: LocalizationResource = { title: 'Vyžaduje sa overenie', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Pridať účet', diff --git a/packages/localizations/src/sr-RS.ts b/packages/localizations/src/sr-RS.ts index 1544a7ae617..92aee810ba2 100644 --- a/packages/localizations/src/sr-RS.ts +++ b/packages/localizations/src/sr-RS.ts @@ -1218,6 +1218,9 @@ export const srRS: LocalizationResource = { title: undefined, }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Dodaj nalog', diff --git a/packages/localizations/src/sv-SE.ts b/packages/localizations/src/sv-SE.ts index 8240c4fae21..56d3da38257 100644 --- a/packages/localizations/src/sv-SE.ts +++ b/packages/localizations/src/sv-SE.ts @@ -1220,6 +1220,9 @@ export const svSE: LocalizationResource = { title: 'Tvåstegsverifiering', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Lägg till konto', diff --git a/packages/localizations/src/ta-IN.ts b/packages/localizations/src/ta-IN.ts index a209297f22c..587a71d59e5 100644 --- a/packages/localizations/src/ta-IN.ts +++ b/packages/localizations/src/ta-IN.ts @@ -1234,6 +1234,9 @@ export const taIN: LocalizationResource = { title: 'சரிபார்ப்பு தேவை', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'கணக்கைச் சேர்', diff --git a/packages/localizations/src/te-IN.ts b/packages/localizations/src/te-IN.ts index d209049d175..de16deaba50 100644 --- a/packages/localizations/src/te-IN.ts +++ b/packages/localizations/src/te-IN.ts @@ -1231,6 +1231,9 @@ export const teIN: LocalizationResource = { title: 'ధృవీకరణ అవసరం', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'ఖాతాను జోడించండి', diff --git a/packages/localizations/src/th-TH.ts b/packages/localizations/src/th-TH.ts index 3db92e6126c..8c8b34e16ba 100644 --- a/packages/localizations/src/th-TH.ts +++ b/packages/localizations/src/th-TH.ts @@ -1220,6 +1220,9 @@ export const thTH: LocalizationResource = { title: 'ต้องการการยืนยัน', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'เพิ่มบัญชี', diff --git a/packages/localizations/src/tr-TR.ts b/packages/localizations/src/tr-TR.ts index 85ee6d51781..98e08e93e4a 100644 --- a/packages/localizations/src/tr-TR.ts +++ b/packages/localizations/src/tr-TR.ts @@ -1219,6 +1219,9 @@ export const trTR: LocalizationResource = { title: 'TOTP Kodunu Girin', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Hesap ekle', diff --git a/packages/localizations/src/uk-UA.ts b/packages/localizations/src/uk-UA.ts index cf4b56558fb..dc6f645e1c5 100644 --- a/packages/localizations/src/uk-UA.ts +++ b/packages/localizations/src/uk-UA.ts @@ -1218,6 +1218,9 @@ export const ukUA: LocalizationResource = { title: undefined, }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Add account', diff --git a/packages/localizations/src/vi-VN.ts b/packages/localizations/src/vi-VN.ts index 2496a6447e9..4d61f937845 100644 --- a/packages/localizations/src/vi-VN.ts +++ b/packages/localizations/src/vi-VN.ts @@ -1228,6 +1228,9 @@ export const viVN: LocalizationResource = { title: 'Xác minh yêu cầu', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: 'Thêm tài khoản', diff --git a/packages/localizations/src/zh-CN.ts b/packages/localizations/src/zh-CN.ts index 4caee7c1c85..50fd9254b16 100644 --- a/packages/localizations/src/zh-CN.ts +++ b/packages/localizations/src/zh-CN.ts @@ -1209,6 +1209,9 @@ export const zhCN: LocalizationResource = { title: undefined, }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: '添加账户', diff --git a/packages/localizations/src/zh-TW.ts b/packages/localizations/src/zh-TW.ts index eb79aa38554..b79689fb355 100644 --- a/packages/localizations/src/zh-TW.ts +++ b/packages/localizations/src/zh-TW.ts @@ -1212,6 +1212,9 @@ export const zhTW: LocalizationResource = { title: '輸入驗證碼', }, }, + searchInput: { + action__clear: undefined, + }, signIn: { accountSwitcher: { action__addAccount: '新增帳戶', diff --git a/packages/shared/src/types/localization.ts b/packages/shared/src/types/localization.ts index 8c8f12fb467..9ca7ecee35e 100644 --- a/packages/shared/src/types/localization.ts +++ b/packages/shared/src/types/localization.ts @@ -87,6 +87,10 @@ export type __internal_LocalizationResource = { /** Label for the “Last used” badge on authentication strategies. */ lastAuthenticationStrategy: LocalizationValue; dividerText: LocalizationValue; + /** Shared strings for search inputs across the components. */ + searchInput: { + action__clear: LocalizationValue; + }; formFieldLabel__emailAddress: LocalizationValue; formFieldLabel__emailAddresses: LocalizationValue; formFieldLabel__phoneNumber: LocalizationValue; diff --git a/packages/ui/src/baseTheme.ts b/packages/ui/src/baseTheme.ts index fef86197013..4cb07b6fc57 100644 --- a/packages/ui/src/baseTheme.ts +++ b/packages/ui/src/baseTheme.ts @@ -75,6 +75,18 @@ const checkboxBaseStyles = (theme: InternalTheme) => ({ }, }); +// Clerk theme tightens the trailing-button geometry (see `common.inputTrailingButton` for the +// simple-theme base) so it sits flush inside the denser default-theme input. Apply to every +// button anchored to the trailing edge of an input. +const inputTrailingButtonStyles = (theme: InternalTheme) => ({ + insetInlineEnd: theme.space.$0x75, + insetBlock: theme.space.$0x75, + borderRadius: `calc(${theme.radii.$md} - ${theme.space.$0x75})`, + '&::before': { + inset: `calc(${theme.space.$0x75} * -1)`, + }, +}); + const inputStyles = (theme: InternalTheme) => ({ borderWidth: 0, ...inputShadowStyles(theme, { @@ -305,14 +317,8 @@ const clerkTheme: Appearance = { borderWidth: 0, boxShadow: `0px 0px 2px 0px rgba(0, 0, 0, 0.08), 0px 1px 2px 0px rgba(25, 28, 33, 0.12), 0px 0px 0px 1px ${theme.colors.$borderAlpha100}`, }, - formFieldInputShowPasswordButton: { - insetInlineEnd: theme.space.$0x75, - insetBlock: theme.space.$0x75, - borderRadius: `calc(${theme.radii.$md} - ${theme.space.$0x75})`, - '&::before': { - inset: `calc(${theme.space.$0x75} * -1)`, - }, - }, + formFieldInputShowPasswordButton: inputTrailingButtonStyles(theme), + searchInputClearButton: inputTrailingButtonStyles(theme), }; }, } satisfies Appearance; diff --git a/packages/ui/src/components/APIKeys/APIKeys.tsx b/packages/ui/src/components/APIKeys/APIKeys.tsx index bf018663e22..64a16abc1a3 100644 --- a/packages/ui/src/components/APIKeys/APIKeys.tsx +++ b/packages/ui/src/components/APIKeys/APIKeys.tsx @@ -1,30 +1,19 @@ import { isClerkAPIResponseError } from '@clerk/shared/error'; import { isOrganizationId } from '@clerk/shared/internal/clerk-js/organization'; -import { useAPIKeys, __internal_useOrganizationBase, useClerk, useUser } from '@clerk/shared/react'; +import { __internal_useOrganizationBase, useAPIKeys, useClerk, useUser } from '@clerk/shared/react'; import type { APIKeyResource } from '@clerk/shared/types'; import { lazy, useState } from 'react'; import { useProtect } from '@/ui/common'; import { useAPIKeysContext, withCoreUserGuard } from '@/ui/contexts'; -import { - Box, - Button, - Col, - descriptors, - Flex, - Flow, - Icon, - localizationKeys, - useLocalizations, -} from '@/ui/customizables'; +import { Box, Button, Col, descriptors, Flex, Flow, localizationKeys, useLocalizations } from '@/ui/customizables'; import { Action } from '@/ui/elements/Action'; import { useCardState, withCardStateProvider } from '@/ui/elements/contexts'; -import { InputWithIcon } from '@/ui/elements/InputWithIcon'; import { Pagination } from '@/ui/elements/Pagination'; +import { SearchInput } from '@/ui/elements/SearchInput'; import { useDebounce } from '@/ui/hooks'; -import { handleError } from '@/ui/utils/errorHandler'; -import { MagnifyingGlass } from '@/ui/icons'; import { mqu } from '@/ui/styledSystem'; +import { handleError } from '@/ui/utils/errorHandler'; import { APIKeysTable } from './ApiKeysTable'; import type { OnCreateParams } from './CreateAPIKeyForm'; @@ -161,22 +150,13 @@ export const APIKeysPage = ({ subject, perPage, revokeModalRoot }: APIKeysPagePr elementDescriptor={descriptors.apiKeysHeader} > - ({ color: t.colors.$colorMutedForeground })} - /> - } value={searchValue} - type='search' - autoComplete='off' - autoCapitalize='none' - spellCheck={false} onChange={e => setSearchValue(e.target.value)} + onClear={() => setSearchValue('')} elementDescriptor={descriptors.apiKeysSearchInput} /> diff --git a/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx b/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx index a882e3982c2..bcb7195e189 100644 --- a/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx +++ b/packages/ui/src/components/OrganizationProfile/MembersSearch.tsx @@ -2,12 +2,10 @@ import type { useOrganization } from '@clerk/shared/react'; import type { GetMembersParams } from '@clerk/shared/types'; import { useEffect, useRef } from 'react'; -import { descriptors, Flex, Icon, localizationKeys, useLocalizations } from '@/customizables'; -import { MagnifyingGlass } from '@/icons'; -import { Spinner } from '@/primitives'; +import { descriptors, Flex, localizationKeys, useLocalizations } from '@/customizables'; import { mqu } from '@/styledSystem'; import { Animated } from '@/ui/elements/Animated'; -import { InputWithIcon } from '@/ui/elements/InputWithIcon'; +import { SearchInput } from '@/ui/elements/SearchInput'; import { ACTIVE_MEMBERS_PAGE_SIZE } from './OrganizationMembers'; @@ -63,6 +61,15 @@ export const MembersSearch = ({ query, value, memberships, onSearchChange, onQue }, membersSearchDebounceMs); } + const handleClear = () => { + // Cancel any pending debounce so it can't re-trigger the query with the stale value. + if (debounceTimer.current) { + clearTimeout(debounceTimer.current); + } + onSearchChange(''); + onQueryTrigger(''); + }; + // If search is not performed on a initial page, resets pagination offset // based on the response count useEffect(() => { @@ -88,26 +95,15 @@ export const MembersSearch = ({ query, value, memberships, onSearchChange, onQue }, }} > - - ) : ( - ({ color: t.colors.$colorMutedForeground })} - /> - ) - } + leftIconElementDescriptor={descriptors.organizationProfileMembersSearchInputIcon} onKeyUp={handleKeyUp} onChange={handleChange} + onClear={handleClear} elementDescriptor={descriptors.organizationProfileMembersSearchInput} /> diff --git a/packages/ui/src/customizables/elementDescriptors.ts b/packages/ui/src/customizables/elementDescriptors.ts index 69eca6184fe..0e8f3dc9d92 100644 --- a/packages/ui/src/customizables/elementDescriptors.ts +++ b/packages/ui/src/customizables/elementDescriptors.ts @@ -177,6 +177,8 @@ export const APPEARANCE_KEYS = containsAllElementsConfigKeys([ 'formFieldInputShowPasswordIcon', 'formFieldInputCopyToClipboardButton', 'formFieldInputCopyToClipboardIcon', + 'searchInput', + 'searchInputClearButton', 'phoneInputBox', 'formInputGroup', diff --git a/packages/ui/src/elements/InputWithIcon.tsx b/packages/ui/src/elements/InputWithIcon.tsx index 09e7d5329f1..3d4c1f20199 100644 --- a/packages/ui/src/elements/InputWithIcon.tsx +++ b/packages/ui/src/elements/InputWithIcon.tsx @@ -1,12 +1,45 @@ +import { inertProps } from '@clerk/shared/inert'; +import { isVirtualClick } from '@floating-ui/react/utils'; import React from 'react'; import { Box, Flex, Input } from '../customizables'; -import type { PropsOfComponent } from '../styledSystem'; +import type { ElementDescriptor } from '../customizables/elementDescriptors'; +import { Close } from '../icons'; +import { common, type PropsOfComponent } from '../styledSystem'; +import { mergeRefs } from '../utils/mergeRefs'; +import { IconButton } from './IconButton'; -type InputWithIcon = PropsOfComponent & { leftIcon?: React.ReactElement }; +type InputWithIcon = PropsOfComponent & { + leftIcon?: React.ReactElement; + /** + * When provided, renders a clear button anchored to the trailing edge of the input. The button + * stays mounted (fading via opacity + `inert`) to avoid layout shift, and calls `onClear` on + * activation — the parent is responsible for resetting `value`. Keyboard activation returns focus + * to the input. + */ + onClear?: () => void; + clearButtonLabel?: string; + clearButtonElementDescriptor?: ElementDescriptor; +}; export const InputWithIcon = React.forwardRef((props, ref) => { - const { leftIcon, sx, ...rest } = props; + const { leftIcon, sx, onClear, clearButtonLabel, clearButtonElementDescriptor, ...rest } = props; + const internalRef = React.useRef(null); + + const hasClearButton = Boolean(onClear); + const hasValue = rest.value != null && rest.value !== ''; + + const handleClear = (event: React.MouseEvent) => { + onClear?.(); + // The button is about to go inert and drop out of the tab order. On keyboard or assistive-tech + // activation, return focus to the input; a real pointer or touch click leaves focus alone so the + // on-screen keyboard is not forced open. `isVirtualClick` guards `detail === 0` with a pointer + // check plus Firefox/Android quirks, which a bare `detail === 0` check misses on touch devices. + if (isVirtualClick(event.nativeEvent)) { + internalRef.current?.focus({ preventScroll: true }); + } + }; + return ( (( width: '100%', paddingInlineStart: theme.space.$10, }), + hasClearButton + ? theme => ({ + paddingInlineEnd: theme.space.$10, + '::-webkit-search-cancel-button': { + display: 'none', + }, + }) + : undefined, sx, ]} - ref={ref} + // @ts-expect-error Type mismatch between ForwardRef and RefObject due to null + ref={hasClearButton ? mergeRefs(ref, internalRef) : ref} /> + {hasClearButton ? ( + ({ + ...common.inputTrailingButton(theme), + opacity: hasValue ? 1 : 0, + transition: `opacity ${theme.transitionDuration.$fast} ${theme.transitionTiming.$common}`, + })} + /> + ) : null} ); }); diff --git a/packages/ui/src/elements/PasswordInput.tsx b/packages/ui/src/elements/PasswordInput.tsx index 719f0897563..93c1c2be5a1 100644 --- a/packages/ui/src/elements/PasswordInput.tsx +++ b/packages/ui/src/elements/PasswordInput.tsx @@ -7,7 +7,7 @@ import { useEnvironment } from '../contexts'; import { descriptors, Flex, Input, localizationKeys, useLocalizations } from '../customizables'; import { usePassword } from '../hooks/usePassword'; import { Eye, EyeSlash } from '../icons'; -import type { PropsOfComponent } from '../styledSystem'; +import { common, type PropsOfComponent } from '../styledSystem'; import { mergeRefs } from '../utils/mergeRefs'; import { IconButton } from './IconButton'; @@ -112,21 +112,7 @@ export const PasswordInput = forwardRef((p size='xs' tabIndex={tabIndex} onClick={() => setHidden(s => !s)} - sx={theme => ({ - position: 'absolute', - insetInlineEnd: theme.space.$1, - insetBlock: theme.space.$1, - borderRadius: `calc(${theme.radii.$md} - ${theme.space.$1})`, - color: theme.colors.$neutralAlpha400, - paddingInline: theme.space.$2, - '&::before': { - content: '""', - position: 'absolute', - inset: `calc(${theme.space.$1} * -1)`, - display: 'block', - borderRadius: theme.radii.$md, - }, - })} + sx={theme => common.inputTrailingButton(theme)} icon={hidden ? Eye : EyeSlash} /> diff --git a/packages/ui/src/elements/SearchInput.tsx b/packages/ui/src/elements/SearchInput.tsx new file mode 100644 index 00000000000..40ecdd8a8f9 --- /dev/null +++ b/packages/ui/src/elements/SearchInput.tsx @@ -0,0 +1,70 @@ +import React from 'react'; + +import { descriptors, Icon, localizationKeys, useLocalizations } from '../customizables'; +import type { ElementDescriptor } from '../customizables/elementDescriptors'; +import { MagnifyingGlass } from '../icons'; +import { Spinner } from '../primitives'; +import type { PropsOfComponent } from '../styledSystem'; +import { InputWithIcon } from './InputWithIcon'; + +type SearchInputProps = Omit< + PropsOfComponent, + 'leftIcon' | 'type' | 'clearButtonElementDescriptor' +> & { + /** + * Called when the user clears the input. The parent is responsible for resetting `value`. + */ + onClear: () => void; + /** + * Renders a spinner in place of the search icon, e.g. while results are loading. + */ + isLoading?: boolean; + /** + * Descriptor for the leading search icon. + */ + leftIconElementDescriptor?: ElementDescriptor; +}; + +/** + * A search input preset: a magnifying-glass icon (or spinner while loading) and a trailing clear + * button that all search inputs share. The clear button uses the single `searchInputClearButton` + * appearance element and a shared clear label, so new search inputs don't introduce their own. + */ +export const SearchInput = React.forwardRef(function SearchInput( + { isLoading, leftIconElementDescriptor, clearButtonLabel, elementDescriptor, ...rest }, + ref, +) { + const { t } = useLocalizations(); + + // Every search input shares the `searchInput` element, layered with any consumer-specific + // descriptor so both `cl-searchInput` and e.g. `cl-apiKeysSearchInput` are themeable. + const inputDescriptors = [ + descriptors.searchInput, + ...(Array.isArray(elementDescriptor) ? elementDescriptor : [elementDescriptor]), + ]; + + return ( + + ) : ( + ({ color: theme.colors.$colorMutedForeground })} + /> + ) + } + clearButtonLabel={clearButtonLabel ?? t(localizationKeys('searchInput.action__clear'))} + clearButtonElementDescriptor={descriptors.searchInputClearButton} + {...rest} + /> + ); +}); diff --git a/packages/ui/src/internal/appearance.ts b/packages/ui/src/internal/appearance.ts index 71d20488744..bb64ee12425 100644 --- a/packages/ui/src/internal/appearance.ts +++ b/packages/ui/src/internal/appearance.ts @@ -306,6 +306,8 @@ export type ElementsConfig = { formFieldInputShowPasswordIcon: WithOptions; formFieldInputCopyToClipboardButton: WithOptions; formFieldInputCopyToClipboardIcon: WithOptions; + searchInput: WithOptions; + searchInputClearButton: WithOptions; phoneInputBox: WithOptions; formInputGroup: WithOptions; diff --git a/packages/ui/src/styledSystem/common.ts b/packages/ui/src/styledSystem/common.ts index 9abc7b7173c..6ffa85d2f2a 100644 --- a/packages/ui/src/styledSystem/common.ts +++ b/packages/ui/src/styledSystem/common.ts @@ -188,6 +188,26 @@ const disabled = (t: InternalTheme) => { } as const; }; +// Shared chrome for a button anchored to the trailing edge of an input (e.g. password reveal, +// search clear). The clerk theme tightens the geometry in `baseTheme.ts`; the simple theme uses +// these values as-is. Callers layer on their own icon/visibility styles. +const inputTrailingButton = (t: InternalTheme) => + ({ + position: 'absolute', + insetInlineEnd: t.space.$1, + insetBlock: t.space.$1, + borderRadius: `calc(${t.radii.$md} - ${t.space.$1})`, + color: t.colors.$neutralAlpha400, + paddingInline: t.space.$2, + '&::before': { + content: '""', + position: 'absolute', + inset: `calc(${t.space.$1} * -1)`, + display: 'block', + borderRadius: t.radii.$md, + }, + }) as const; + const centeredFlex = (display: 'flex' | 'inline-flex' = 'flex') => ({ display: display, justifyContent: 'center', @@ -264,6 +284,7 @@ export const common = { focusRing, disabled, borderColor, + inputTrailingButton, centeredFlex, maxHeightScroller, mutedBackground,