@atproto/oauth-provider-ui 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/dist/authorization-page-Cms-rcBA.js +3 -0
  2. package/dist/authorization-page-Cms-rcBA.js.map +1 -0
  3. package/dist/bundle-manifest.json +630 -0
  4. package/dist/error-page-DC6Vc-cv.js +2 -0
  5. package/dist/error-page-DC6Vc-cv.js.map +1 -0
  6. package/dist/error-view-CRGNTAn2.css +1 -0
  7. package/dist/error-view-MVy7C9l0.js +59 -0
  8. package/dist/error-view-MVy7C9l0.js.map +1 -0
  9. package/dist/index-CHPoD7Rp.js +20 -0
  10. package/dist/index-CHPoD7Rp.js.map +1 -0
  11. package/dist/messages-B0mgsxS-.js +2 -0
  12. package/dist/messages-B0mgsxS-.js.map +1 -0
  13. package/dist/messages-B5g8Fkio.js +2 -0
  14. package/dist/messages-B5g8Fkio.js.map +1 -0
  15. package/dist/messages-BCMss-Kt.js +2 -0
  16. package/dist/messages-BCMss-Kt.js.map +1 -0
  17. package/dist/messages-BGUrKgyK.js +2 -0
  18. package/dist/messages-BGUrKgyK.js.map +1 -0
  19. package/dist/messages-BjxAnLDp.js +2 -0
  20. package/dist/messages-BjxAnLDp.js.map +1 -0
  21. package/dist/messages-Bjysz3rI.js +2 -0
  22. package/dist/messages-Bjysz3rI.js.map +1 -0
  23. package/dist/messages-BvvEr3UX.js +2 -0
  24. package/dist/messages-BvvEr3UX.js.map +1 -0
  25. package/dist/messages-Bz6JOhJf.js +2 -0
  26. package/dist/messages-Bz6JOhJf.js.map +1 -0
  27. package/dist/messages-BzL3D1EU.js +2 -0
  28. package/dist/messages-BzL3D1EU.js.map +1 -0
  29. package/dist/messages-CAvN5UoW.js +2 -0
  30. package/dist/messages-CAvN5UoW.js.map +1 -0
  31. package/dist/messages-CEmswT1Q.js +2 -0
  32. package/dist/messages-CEmswT1Q.js.map +1 -0
  33. package/dist/messages-CHYqz0q6.js +2 -0
  34. package/dist/messages-CHYqz0q6.js.map +1 -0
  35. package/dist/messages-CRmpdijj.js +2 -0
  36. package/dist/messages-CRmpdijj.js.map +1 -0
  37. package/dist/messages-Cdb79R6S.js +2 -0
  38. package/dist/messages-Cdb79R6S.js.map +1 -0
  39. package/dist/messages-ChkJ_0WT.js +2 -0
  40. package/dist/messages-ChkJ_0WT.js.map +1 -0
  41. package/dist/messages-CqiEX6JJ.js +2 -0
  42. package/dist/messages-CqiEX6JJ.js.map +1 -0
  43. package/dist/messages-CxkHjJSR.js +2 -0
  44. package/dist/messages-CxkHjJSR.js.map +1 -0
  45. package/dist/messages-D0-cWoJ9.js +2 -0
  46. package/dist/messages-D0-cWoJ9.js.map +1 -0
  47. package/dist/messages-D2MnAxYY.js +2 -0
  48. package/dist/messages-D2MnAxYY.js.map +1 -0
  49. package/dist/messages-D5TZVsui.js +2 -0
  50. package/dist/messages-D5TZVsui.js.map +1 -0
  51. package/dist/messages-DBdV4-iw.js +2 -0
  52. package/dist/messages-DBdV4-iw.js.map +1 -0
  53. package/dist/messages-DEK3zybC.js +2 -0
  54. package/dist/messages-DEK3zybC.js.map +1 -0
  55. package/dist/messages-DGSM5jkd.js +2 -0
  56. package/dist/messages-DGSM5jkd.js.map +1 -0
  57. package/dist/messages-DJgAnSTQ.js +2 -0
  58. package/dist/messages-DJgAnSTQ.js.map +1 -0
  59. package/dist/messages-DK7O7sb_.js +2 -0
  60. package/dist/messages-DK7O7sb_.js.map +1 -0
  61. package/dist/messages-DRp7qc3j.js +2 -0
  62. package/dist/messages-DRp7qc3j.js.map +1 -0
  63. package/dist/messages-DT6xRw0m.js +2 -0
  64. package/dist/messages-DT6xRw0m.js.map +1 -0
  65. package/dist/messages-LnzLtU0L.js +2 -0
  66. package/dist/messages-LnzLtU0L.js.map +1 -0
  67. package/dist/messages-_Nk2qNGw.js +2 -0
  68. package/dist/messages-_Nk2qNGw.js.map +1 -0
  69. package/dist/messages-eHH6nZyF.js +2 -0
  70. package/dist/messages-eHH6nZyF.js.map +1 -0
  71. package/dist/messages-iNw8zY2C.js +2 -0
  72. package/dist/messages-iNw8zY2C.js.map +1 -0
  73. package/dist/messages-ipc0L8yF.js +2 -0
  74. package/dist/messages-ipc0L8yF.js.map +1 -0
  75. package/dist/messages-j7LsWm2F.js +2 -0
  76. package/dist/messages-j7LsWm2F.js.map +1 -0
  77. package/dist/messages-mgE_5UEw.js +2 -0
  78. package/dist/messages-mgE_5UEw.js.map +1 -0
  79. package/dist/messages-oRd-J5--.js +2 -0
  80. package/dist/messages-oRd-J5--.js.map +1 -0
  81. package/package.json +10 -8
  82. package/.linguirc +0 -57
  83. package/CHANGELOG.md +0 -17
  84. package/CONTRIBUTING.md +0 -6
  85. package/authorization-page.html +0 -186
  86. package/error-page.html +0 -118
  87. package/index.html +0 -13
  88. package/src/authorization-page.tsx +0 -49
  89. package/src/components/forms/button-toggle-visibility.tsx +0 -43
  90. package/src/components/forms/button.tsx +0 -60
  91. package/src/components/forms/fieldset.tsx +0 -55
  92. package/src/components/forms/form-card-async.tsx +0 -103
  93. package/src/components/forms/form-card.tsx +0 -49
  94. package/src/components/forms/input-checkbox.tsx +0 -78
  95. package/src/components/forms/input-container.tsx +0 -107
  96. package/src/components/forms/input-email-address.tsx +0 -65
  97. package/src/components/forms/input-new-password.tsx +0 -62
  98. package/src/components/forms/input-password.tsx +0 -87
  99. package/src/components/forms/input-text.tsx +0 -82
  100. package/src/components/forms/input-token.tsx +0 -94
  101. package/src/components/forms/wizard-card.tsx +0 -116
  102. package/src/components/layouts/layout-title-page.tsx +0 -78
  103. package/src/components/layouts/layout-welcome.tsx +0 -78
  104. package/src/components/utils/account-identifier.tsx +0 -23
  105. package/src/components/utils/account-image.tsx +0 -33
  106. package/src/components/utils/admonition.tsx +0 -52
  107. package/src/components/utils/client-name.tsx +0 -71
  108. package/src/components/utils/error-card.tsx +0 -93
  109. package/src/components/utils/error-message.tsx +0 -88
  110. package/src/components/utils/help-card.tsx +0 -46
  111. package/src/components/utils/icons.tsx +0 -88
  112. package/src/components/utils/link-anchor.tsx +0 -28
  113. package/src/components/utils/link-title.tsx +0 -26
  114. package/src/components/utils/multi-lang-string.tsx +0 -62
  115. package/src/components/utils/password-strength-label.tsx +0 -37
  116. package/src/components/utils/password-strength-meter.tsx +0 -58
  117. package/src/components/utils/url-viewer.tsx +0 -73
  118. package/src/error-page.tsx +0 -23
  119. package/src/hooks/use-api.ts +0 -202
  120. package/src/hooks/use-async-action.ts +0 -120
  121. package/src/hooks/use-bound-dispatch.ts +0 -5
  122. package/src/hooks/use-browser-color-scheme.ts +0 -31
  123. package/src/hooks/use-random-string.ts +0 -37
  124. package/src/hooks/use-stepper.ts +0 -87
  125. package/src/lib/api.ts +0 -225
  126. package/src/lib/cookies.ts +0 -17
  127. package/src/lib/json-client.ts +0 -141
  128. package/src/lib/password.ts +0 -98
  129. package/src/lib/ref.ts +0 -17
  130. package/src/lib/util.ts +0 -14
  131. package/src/locales/an/messages.po +0 -494
  132. package/src/locales/ast/messages.po +0 -494
  133. package/src/locales/ca/messages.po +0 -494
  134. package/src/locales/da/messages.po +0 -494
  135. package/src/locales/de/messages.po +0 -494
  136. package/src/locales/el/messages.po +0 -494
  137. package/src/locales/en/messages.po +0 -494
  138. package/src/locales/en-GB/messages.po +0 -494
  139. package/src/locales/es/messages.po +0 -494
  140. package/src/locales/eu/messages.po +0 -494
  141. package/src/locales/fi/messages.po +0 -494
  142. package/src/locales/fr/messages.po +0 -494
  143. package/src/locales/ga/messages.po +0 -494
  144. package/src/locales/gl/messages.po +0 -494
  145. package/src/locales/hi/messages.po +0 -494
  146. package/src/locales/hu/messages.po +0 -494
  147. package/src/locales/ia/messages.po +0 -494
  148. package/src/locales/id/messages.po +0 -494
  149. package/src/locales/it/messages.po +0 -494
  150. package/src/locales/ja/messages.po +0 -494
  151. package/src/locales/km/messages.po +0 -494
  152. package/src/locales/ko/messages.po +0 -494
  153. package/src/locales/load.ts +0 -8
  154. package/src/locales/locale-provider.tsx +0 -108
  155. package/src/locales/locale-selector.tsx +0 -57
  156. package/src/locales/locales.ts +0 -183
  157. package/src/locales/ne/messages.po +0 -494
  158. package/src/locales/nl/messages.po +0 -494
  159. package/src/locales/pl/messages.po +0 -494
  160. package/src/locales/pt-BR/messages.po +0 -494
  161. package/src/locales/ro/messages.po +0 -494
  162. package/src/locales/ru/messages.po +0 -494
  163. package/src/locales/sv/messages.po +0 -494
  164. package/src/locales/th/messages.po +0 -494
  165. package/src/locales/tr/messages.po +0 -494
  166. package/src/locales/uk/messages.po +0 -494
  167. package/src/locales/vi/messages.po +0 -494
  168. package/src/locales/zh-CN/messages.po +0 -494
  169. package/src/locales/zh-HK/messages.po +0 -494
  170. package/src/locales/zh-TW/messages.po +0 -494
  171. package/src/style.css +0 -219
  172. package/src/views/authorize/accept/accept-form.tsx +0 -155
  173. package/src/views/authorize/accept/accept-view.tsx +0 -70
  174. package/src/views/authorize/authorize-view.tsx +0 -186
  175. package/src/views/authorize/reset-password/reset-password-confirm-form.tsx +0 -88
  176. package/src/views/authorize/reset-password/reset-password-request-form.tsx +0 -80
  177. package/src/views/authorize/reset-password/reset-password-view.tsx +0 -127
  178. package/src/views/authorize/sign-in/sign-in-form.tsx +0 -240
  179. package/src/views/authorize/sign-in/sign-in-picker.tsx +0 -116
  180. package/src/views/authorize/sign-in/sign-in-view.tsx +0 -145
  181. package/src/views/authorize/sign-up/sign-up-account-form.tsx +0 -142
  182. package/src/views/authorize/sign-up/sign-up-disclaimer.tsx +0 -51
  183. package/src/views/authorize/sign-up/sign-up-handle-form.tsx +0 -287
  184. package/src/views/authorize/sign-up/sign-up-hcaptcha-form.tsx +0 -108
  185. package/src/views/authorize/sign-up/sign-up-view.tsx +0 -158
  186. package/src/views/authorize/welcome/welcome-view.tsx +0 -56
  187. package/src/views/error/error-view.tsx +0 -31
  188. package/tsconfig.json +0 -7
  189. package/tsconfig.src.json +0 -13
  190. package/tsconfig.src.tsbuildinfo +0 -1
  191. package/tsconfig.tools.json +0 -8
  192. package/tsconfig.tools.tsbuildinfo +0 -1
  193. package/vite.config.mjs +0 -47
  194. /package/{src/hydration-data.d.ts → hydration-data.d.ts} +0 -0
@@ -1,78 +0,0 @@
1
- import { clsx } from 'clsx'
2
- import { JSX, ReactNode, useContext, useRef } from 'react'
3
- import { useRandomString } from '../../hooks/use-random-string.ts'
4
- import { mergeRefs } from '../../lib/ref.ts'
5
- import { Override } from '../../lib/util.ts'
6
- import { FieldsetContext } from './fieldset.tsx'
7
- import { InputContainer } from './input-container.tsx'
8
-
9
- export type InputCheckboxProps = Override<
10
- Omit<JSX.IntrinsicElements['input'], 'className' | 'type' | 'children'>,
11
- {
12
- className?: string
13
- children?: ReactNode
14
- }
15
- >
16
-
17
- export function InputCheckbox({
18
- className,
19
- children,
20
-
21
- // input
22
- id,
23
- ref,
24
- disabled,
25
- title,
26
- 'aria-label': ariaLabel = title,
27
- 'aria-labelledby': ariaLabelledBy,
28
- ...props
29
- }: InputCheckboxProps) {
30
- const htmlFor = useRandomString('input-checkbox-')
31
- const labelRef = useRef<HTMLLabelElement>(null)
32
- const inputRef = useRef<HTMLInputElement>(null)
33
- const ctx = useContext(FieldsetContext)
34
-
35
- const inputId = id ?? htmlFor
36
-
37
- return (
38
- <InputContainer
39
- className={clsx('cursor-pointer', className)}
40
- icon={
41
- <input
42
- {...props}
43
- disabled={disabled ?? ctx.disabled}
44
- title={title}
45
- aria-label={ariaLabel}
46
- aria-labelledby={
47
- children
48
- ? // Prefer the local "<label>" element (through "htmlFor") over the wrapping "<fieldset>" to describe the checkbox.
49
- undefined
50
- : ariaLabelledBy ?? ctx.labelId
51
- }
52
- ref={mergeRefs([ref, inputRef])}
53
- id={inputId}
54
- className="accent-primary outline-hidden"
55
- type="checkbox"
56
- />
57
- }
58
- tabIndex={-1}
59
- onClick={({ target }) => {
60
- // Native behavior of clicking the label should toggle the checkbox.
61
- if (target === labelRef.current) return
62
- if (target === inputRef.current) return
63
-
64
- inputRef.current?.click()
65
- }}
66
- >
67
- {children && (
68
- <label
69
- ref={labelRef}
70
- htmlFor={inputId}
71
- className="block w-full cursor-pointer select-none leading-[1.6]"
72
- >
73
- {children}
74
- </label>
75
- )}
76
- </InputContainer>
77
- )
78
- }
@@ -1,107 +0,0 @@
1
- import { clsx } from 'clsx'
2
- import { JSX, ReactNode, useState } from 'react'
3
- import { Override } from '../../lib/util.ts'
4
-
5
- export type InputContainerProps = Override<
6
- JSX.IntrinsicElements['div'],
7
- {
8
- icon: ReactNode
9
- append?: ReactNode
10
- bellow?: ReactNode
11
- }
12
- >
13
-
14
- export function InputContainer({
15
- icon,
16
- append,
17
- bellow,
18
-
19
- // div
20
- className,
21
- children,
22
- onFocus,
23
- onBlur,
24
- ...props
25
- }: InputContainerProps) {
26
- const [hasFocus, setHasFocus] = useState(false)
27
-
28
- return (
29
- <div
30
- {...props}
31
- onFocus={(event) => {
32
- onFocus?.(event)
33
- if (!event.defaultPrevented) setHasFocus(true)
34
- }}
35
- onBlur={(event) => {
36
- onBlur?.(event)
37
- if (!event.defaultPrevented) setHasFocus(false)
38
- }}
39
- className={clsx(
40
- // Layout
41
- 'min-h-12',
42
- 'max-w-full',
43
- 'overflow-hidden',
44
- // Border
45
- 'rounded-lg',
46
- className,
47
- )}
48
- >
49
- <div
50
- className={clsx(
51
- // Layout
52
- 'px-1',
53
- 'min-h-12 w-full',
54
- 'flex items-center justify-stretch',
55
- // Border
56
- 'rounded-lg',
57
- bellow ? 'rounded-bl-none rounded-br-none' : undefined,
58
- 'outline-hidden',
59
- 'border-2 border-solid border-transparent',
60
- 'focus:border-primary has-focus:border-primary',
61
- 'hover:border-gray-400 hover:focus:border-gray-400',
62
- 'dark:hover:border-gray-500 dark:hover:focus:border-gray-500',
63
- // Background
64
- 'has-focus:bg-slate-200 bg-gray-100 focus:bg-slate-200',
65
- 'dark:has-focus:bg-slate-700 dark:bg-slate-800 dark:focus:bg-slate-700',
66
- // Font
67
- 'text-slate-600 dark:text-slate-300',
68
- 'accent-primary',
69
- )}
70
- >
71
- {icon && (
72
- <div
73
- className={clsx(
74
- 'shrink-0 grow-0',
75
- 'mx-1',
76
- hasFocus ? 'text-primary' : 'text-slate-500',
77
- )}
78
- >
79
- {icon}
80
- </div>
81
- )}
82
-
83
- {children}
84
-
85
- <div className="ml-1 flex shrink-0 grow-0 items-center">{append}</div>
86
- </div>
87
- {bellow && (
88
- <div
89
- className={clsx(
90
- // Layout
91
- 'space-x-2 px-3 py-2',
92
- 'flex flex-row items-center gap-1',
93
- // Border
94
- 'rounded-br-2 rounded-bl-2',
95
- // Background
96
- 'bg-gray-200 dark:bg-slate-700',
97
- // Font
98
- 'text-gray-700 dark:text-gray-300',
99
- 'text-sm italic',
100
- )}
101
- >
102
- {bellow}
103
- </div>
104
- )}
105
- </div>
106
- )
107
- }
@@ -1,65 +0,0 @@
1
- import { useLingui } from '@lingui/react/macro'
2
- import { ChangeEvent, useCallback, useState } from 'react'
3
- import { Override } from '../../lib/util.ts'
4
- import { AtSymbolIcon } from '../utils/icons.tsx'
5
- import { InputText, InputTextProps } from './input-text.tsx'
6
-
7
- export type InputEmailAddressProps = Override<
8
- Omit<InputTextProps, 'type'>,
9
- {
10
- onEmail?: (email: string | undefined) => void
11
- }
12
- >
13
-
14
- export function InputEmailAddress({
15
- onEmail,
16
-
17
- // InputTextProps
18
- autoCapitalize = 'none',
19
- autoComplete = 'email',
20
- autoCorrect = 'off',
21
- dir = 'auto',
22
- icon = <AtSymbolIcon className="w-5" />,
23
- onBlur,
24
- onChange,
25
- pattern = '^[^@]+@[^@]+\\.[^@]+$',
26
- spellCheck = 'false',
27
- value,
28
- defaultValue = value,
29
- title,
30
- ...props
31
- }: InputEmailAddressProps) {
32
- const { t } = useLingui()
33
- const [email, setEmail] = useState<string>(
34
- typeof defaultValue === 'string' ? defaultValue : '',
35
- )
36
-
37
- const doChange = useCallback(
38
- (event: ChangeEvent<HTMLInputElement>) => {
39
- const email = event.target.value.toLowerCase()
40
-
41
- setEmail(email)
42
- onChange?.(event)
43
- onEmail?.(event.target.validity.valid ? email : undefined)
44
- },
45
- [onChange, onEmail],
46
- )
47
-
48
- return (
49
- <InputText
50
- {...props}
51
- title={title ?? t`Email address`}
52
- type="email"
53
- autoCapitalize={autoCapitalize}
54
- autoCorrect={autoCorrect}
55
- dir={dir}
56
- spellCheck={spellCheck}
57
- icon={icon}
58
- pattern={pattern}
59
- autoComplete={autoComplete}
60
- value={email}
61
- onChange={doChange}
62
- onBlur={onBlur}
63
- />
64
- )
65
- }
@@ -1,62 +0,0 @@
1
- import { useLingui } from '@lingui/react/macro'
2
- import { ChangeEvent, useCallback, useState } from 'react'
3
- import { MIN_PASSWORD_LENGTH } from '../../lib/password.ts'
4
- import { Override } from '../../lib/util.ts'
5
- import { PasswordStrengthLabel } from '../utils/password-strength-label.tsx'
6
- import { PasswordStrengthMeter } from '../utils/password-strength-meter.tsx'
7
- import { InputPassword, InputPasswordProps } from './input-password.tsx'
8
-
9
- export type InputNewPasswordProps = Override<
10
- Omit<InputPasswordProps, 'value' | 'defaultValue'>,
11
- {
12
- password?: string
13
- onPassword?: (password: undefined | string) => void
14
- }
15
- >
16
-
17
- export function InputNewPassword({
18
- password: passwordInit = '',
19
- onPassword,
20
-
21
- // InputPasswordProps
22
- onChange,
23
- autoComplete = 'new-password',
24
- minLength = MIN_PASSWORD_LENGTH,
25
- ...props
26
- }: InputNewPasswordProps) {
27
- const { t } = useLingui()
28
- const [password, setPassword] = useState<string>(passwordInit)
29
-
30
- const doChange = useCallback(
31
- (event: ChangeEvent<HTMLInputElement>) => {
32
- const { value } = event.target
33
- onChange?.(event)
34
- if (event.defaultPrevented) return
35
- setPassword(value)
36
- onPassword?.(event.target.validity.valid ? value : undefined)
37
- },
38
- [onChange, onPassword],
39
- )
40
-
41
- return (
42
- <InputPassword
43
- {...props}
44
- placeholder={t`Enter a password`}
45
- aria-label={t`Enter your new password`}
46
- title={t`Password with at least ${MIN_PASSWORD_LENGTH} characters`}
47
- minLength={minLength}
48
- onChange={doChange}
49
- value={password}
50
- autoComplete={autoComplete}
51
- bellow={
52
- <>
53
- <PasswordStrengthMeter password={password} />
54
- <PasswordStrengthLabel
55
- className="grow-1 min-w-max text-xs text-gray-500 dark:text-gray-400"
56
- password={password}
57
- />
58
- </>
59
- }
60
- />
61
- )
62
- }
@@ -1,87 +0,0 @@
1
- import { useLingui } from '@lingui/react/macro'
2
- import { ChangeEvent, useCallback, useRef, useState } from 'react'
3
- import { mergeRefs } from '../../lib/ref.ts'
4
- import { Override } from '../../lib/util.ts'
5
- import { LockIcon } from '../utils/icons.tsx'
6
- import { ButtonToggleVisibility } from './button-toggle-visibility.tsx'
7
- import { InputText, InputTextProps } from './input-text.tsx'
8
-
9
- export type InputPasswordProps = Override<
10
- Omit<InputTextProps, 'type' | 'children'>,
11
- {
12
- autoHide?: boolean
13
- }
14
- >
15
-
16
- export function InputPassword({
17
- autoHide = true,
18
-
19
- // InputTextProps
20
- onBlur,
21
- onChange,
22
- append,
23
- autoComplete = 'current-password',
24
- icon = <LockIcon className="w-5" />,
25
- value,
26
- defaultValue = value,
27
- ref,
28
- title,
29
- dir = 'auto',
30
- autoCapitalize = 'none',
31
- autoCorrect = 'off',
32
- spellCheck = 'false',
33
- ...props
34
- }: InputPasswordProps) {
35
- const { t } = useLingui()
36
- const inputRef = useRef<HTMLInputElement>(null)
37
- const [visible, setVisible] = useState<boolean>(false)
38
- const [password, setPassword] = useState<string>(
39
- typeof defaultValue === 'string' ? defaultValue : '',
40
- )
41
-
42
- const doChange = useCallback(
43
- (event: ChangeEvent<HTMLInputElement>) => {
44
- onChange?.(event)
45
- setPassword(event.target.value)
46
- },
47
- [onChange],
48
- )
49
-
50
- return (
51
- <InputText
52
- {...props}
53
- title={title ?? t`Password`}
54
- ref={mergeRefs([ref, inputRef])}
55
- dir={dir}
56
- autoCapitalize={autoCapitalize}
57
- autoCorrect={autoCorrect}
58
- spellCheck={spellCheck}
59
- icon={icon}
60
- onBlur={
61
- autoHide
62
- ? (event) => {
63
- onBlur?.(event)
64
- if (!event.defaultPrevented) setVisible(false)
65
- }
66
- : onBlur
67
- }
68
- value={password}
69
- onChange={doChange}
70
- type={visible ? 'text' : 'password'}
71
- autoComplete={autoComplete}
72
- append={
73
- <>
74
- <ButtonToggleVisibility
75
- className="m-1"
76
- visible={visible}
77
- toggleVisible={() => {
78
- setVisible((prev) => !prev)
79
- inputRef.current?.focus()
80
- }}
81
- />
82
- {append}
83
- </>
84
- }
85
- />
86
- )
87
- }
@@ -1,82 +0,0 @@
1
- import { clsx } from 'clsx'
2
- import { JSX, ReactNode, useContext, useRef } from 'react'
3
- import { mergeRefs } from '../../lib/ref.ts'
4
- import { Override } from '../../lib/util.ts'
5
- import { FieldsetContext } from './fieldset.tsx'
6
- import { InputContainer } from './input-container.tsx'
7
-
8
- export type InputTextProps = Override<
9
- Omit<JSX.IntrinsicElements['input'], 'children'>,
10
- {
11
- icon?: ReactNode
12
- append?: ReactNode
13
- bellow?: ReactNode
14
- className?: string
15
- }
16
- >
17
-
18
- export function InputText({
19
- icon,
20
- append,
21
- bellow,
22
- className,
23
-
24
- // input
25
- onFocus,
26
- onBlur,
27
- ref,
28
- disabled,
29
- title,
30
- 'aria-label': ariaLabel = title,
31
- 'aria-labelledby': ariaLabelledBy,
32
- placeholder = ariaLabel,
33
- ...props
34
- }: InputTextProps) {
35
- const ctx = useContext(FieldsetContext)
36
-
37
- const inputRef = useRef<HTMLInputElement>(null)
38
- const focusedRef = useRef(false) // ref instead of state to avoid re-renders
39
-
40
- return (
41
- <InputContainer
42
- icon={icon}
43
- append={append}
44
- bellow={bellow}
45
- className={clsx('cursor-text', className)}
46
- tabIndex={-1}
47
- onClick={(event) => {
48
- if (inputRef.current !== event.target) {
49
- event.preventDefault()
50
- event.stopPropagation()
51
- inputRef.current?.focus()
52
- }
53
- }}
54
- onMouseDown={(event) => {
55
- if (focusedRef.current && event.target !== inputRef.current) {
56
- // Prevent "blur" event from firing when clicking outside the input
57
- event.preventDefault()
58
- event.stopPropagation()
59
- }
60
- }}
61
- >
62
- <input
63
- {...props}
64
- disabled={disabled ?? ctx.disabled}
65
- title={title}
66
- placeholder={placeholder}
67
- aria-label={ariaLabel}
68
- aria-labelledby={ariaLabelledBy ?? ctx.labelId}
69
- ref={mergeRefs([ref, inputRef])}
70
- className="outline-hidden w-full text-ellipsis bg-transparent bg-clip-padding text-base text-inherit dark:placeholder-gray-500"
71
- onFocus={(event) => {
72
- onFocus?.(event)
73
- if (!event.defaultPrevented) focusedRef.current = true
74
- }}
75
- onBlur={(event) => {
76
- onBlur?.(event)
77
- if (!event.defaultPrevented) focusedRef.current = false
78
- }}
79
- />
80
- </InputContainer>
81
- )
82
- }
@@ -1,94 +0,0 @@
1
- import { useLingui } from '@lingui/react/macro'
2
- import { ChangeEvent, useState } from 'react'
3
- import { Override } from '../../lib/util.ts'
4
- import { TokenIcon } from '../utils/icons.tsx'
5
- import { InputText, InputTextProps } from './input-text.tsx'
6
-
7
- export type InputTokenProps = Override<
8
- Omit<
9
- InputTextProps,
10
- | 'type'
11
- | 'pattern'
12
- | 'autoCapitalize'
13
- | 'autoCorrect'
14
- | 'autoComplete'
15
- | 'spellCheck'
16
- | 'minLength'
17
- | 'maxLength'
18
- | 'placeholder'
19
- | 'dir'
20
- >,
21
- {
22
- example?: string
23
- onToken?: (code: string | null) => void
24
- }
25
- >
26
-
27
- export const OTP_CODE_EXAMPLE = 'XXXXX-XXXXX'
28
-
29
- export function InputToken({
30
- example = OTP_CODE_EXAMPLE,
31
- onToken,
32
-
33
- // InputTextProps
34
- icon = <TokenIcon className="w-5" />,
35
- title = example,
36
- onChange,
37
- value,
38
- defaultValue = value,
39
- ...props
40
- }: InputTokenProps) {
41
- const { t } = useLingui()
42
- const [token, setToken] = useState<string>(
43
- typeof defaultValue === 'string' ? defaultValue : '',
44
- )
45
-
46
- return (
47
- <InputText
48
- {...props}
49
- type="text"
50
- autoCapitalize="none"
51
- autoCorrect="off"
52
- autoComplete="off"
53
- spellCheck="false"
54
- minLength={11}
55
- maxLength={11}
56
- dir="auto"
57
- icon={icon}
58
- pattern="^[A-Z2-7]{5}-[A-Z2-7]{5}$"
59
- placeholder={t`Looks like ${example}`}
60
- title={title}
61
- value={token}
62
- onChange={(event: ChangeEvent<HTMLInputElement>) => {
63
- const { value, selectionEnd, selectionStart } = event.currentTarget
64
-
65
- const fixedValue = fix(value)
66
-
67
- event.currentTarget.value = fixedValue
68
-
69
- // Move the cursor back where it was relative to the original value
70
- const pos = selectionEnd ?? selectionStart
71
- if (pos != null) {
72
- const fixedSlicedValue = fix(value.slice(0, pos))
73
- event.currentTarget.selectionStart =
74
- event.currentTarget.selectionEnd = fixedSlicedValue.length
75
- }
76
-
77
- setToken(fixedValue)
78
- onChange?.(event)
79
-
80
- if (!event.isDefaultPrevented()) {
81
- onToken?.(fixedValue.length === 11 ? fixedValue : null)
82
- }
83
- }}
84
- />
85
- )
86
- }
87
-
88
- function fix(value: string) {
89
- const normalized = value.toUpperCase().replaceAll(/[^A-Z2-7]/g, '')
90
-
91
- if (normalized.length <= 5) return normalized
92
-
93
- return `${normalized.slice(0, 5)}-${normalized.slice(5, 10)}`
94
- }
@@ -1,116 +0,0 @@
1
- import { Trans } from '@lingui/react/macro'
2
- import { clsx } from 'clsx'
3
- import { JSX, ReactNode, useCallback } from 'react'
4
- import { DisabledStep, Step, useStepper } from '../../hooks/use-stepper.ts'
5
- import { Override } from '../../lib/util.ts'
6
-
7
- export type DoneFn = (...a: any) => unknown
8
-
9
- export type WizardRenderProps<TDone extends DoneFn> = {
10
- /**
11
- * Indicates wether the render function being invoked corresponds to the step
12
- * currently active. The steps titles could, for example, be rendered in a
13
- * list of links, where the current step is highlighted (based on `current`).
14
- *
15
- * Another use for this is to render the next/previous steps in order to
16
- * provide animated transitions between steps. In this case, `current` would
17
- * be used to disable any form interaction with the form transitioning in/out.
18
- */
19
- current: boolean
20
- invalid: boolean
21
-
22
- prev?: () => void
23
- prevLabel: ReactNode
24
-
25
- // On the last step, the "next()" function will actually be the done function
26
- next: (() => void) | TDone
27
- nextLabel: ReactNode
28
- }
29
-
30
- export type WizardRenderFn<TDone extends DoneFn> = (
31
- data: WizardRenderProps<TDone>,
32
- ) => ReactNode
33
-
34
- export type WizardStep<TDone extends DoneFn> = Step & {
35
- titleRender?: WizardRenderFn<TDone>
36
- contentRender: WizardRenderFn<TDone>
37
- }
38
-
39
- export type WizardCardProps<TDone extends DoneFn> = Override<
40
- Omit<JSX.IntrinsicElements['div'], 'children'>,
41
- {
42
- prevLabel?: ReactNode
43
- nextLabel?: ReactNode
44
-
45
- onBack?: () => void
46
- backLabel?: ReactNode
47
-
48
- onDone: TDone
49
- doneLabel?: ReactNode
50
-
51
- steps: readonly (WizardStep<TDone> | DisabledStep)[]
52
- }
53
- >
54
-
55
- export function WizardCard<TDone extends DoneFn>({
56
- prevLabel,
57
- nextLabel,
58
-
59
- onBack,
60
- backLabel,
61
-
62
- onDone,
63
- doneLabel,
64
-
65
- steps,
66
- className,
67
-
68
- ...props
69
- }: WizardCardProps<TDone>) {
70
- const {
71
- atFirst,
72
- atLast,
73
- count,
74
- current,
75
- currentPosition,
76
- completed,
77
- toNext,
78
- toPrev,
79
- toRequired,
80
- } = useStepper(steps)
81
-
82
- // Memoized to avoid re-renders in child (rendered) components
83
- const onNext = useCallback(() => {
84
- // If already at last step, go to the first incomplete (required) step
85
- if (!toNext()) toRequired()
86
- }, [toNext, toRequired])
87
-
88
- const data: WizardRenderProps<TDone> = {
89
- // The current UI only displays the current title & content.
90
- current: true,
91
- invalid: current ? current.invalid : false,
92
-
93
- prevLabel: (atFirst && backLabel) || prevLabel || <Trans>Back</Trans>,
94
- prev: atFirst ? onBack : toPrev,
95
-
96
- nextLabel: (atLast && doneLabel) || nextLabel || <Trans>Next</Trans>,
97
- next: atLast && completed ? onDone : onNext,
98
- }
99
-
100
- const stepTitle = current?.titleRender?.(data)
101
- const stepContent = current?.contentRender?.(data)
102
-
103
- return (
104
- <div className={clsx(className, 'flex flex-col')} {...props}>
105
- <p className="text-slate-500 dark:text-slate-400">
106
- <Trans>
107
- Step {currentPosition} of {count}
108
- </Trans>
109
- </p>
110
-
111
- {stepTitle && <h2 className="mb-4 text-xl font-medium">{stepTitle}</h2>}
112
-
113
- {stepContent}
114
- </div>
115
- )
116
- }