@clicktap/ui 0.43.5 → 0.43.6

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.
@@ -1 +1 @@
1
- "use client";import{jsxs as s,jsx as e}from"react/jsx-runtime";import{Label as p,TextField as v,Input as k,Text as L,FieldError as C}from"react-aria-components";import{forwardRef as I,useState as N}from"react";import{cn as r}from"../../utils/cn.js";import{Skeleton as P}from"../Skeleton/Skeleton.js";import M from"../../hooks/useIsClient.js";function S({className:t}){return s("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:t,children:[e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.64 0 8.577 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.64 0-8.577-3.007-9.963-7.178Z"}),e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"})]})}function j({className:t}){return e("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:t,children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"})})}function y({className:t}){return e(P,{className:r("w-full h-10 rounded-md z-20 relative",t)})}const W=I(({inputProps:t,label:l,description:d,errorMessage:x,placeholder:m,className:u,classNames:o,disableSkeleton:h,type:c,...b},w)=>{const g=M(),i=c==="password",[f,n]=N(!1);return!g&&!h?s("div",{className:r("flex flex-col w-full text-fg",u),children:[l?e(p,{className:r("flex text-fg-muted text-xs",o?.label),children:l}):null,e(y,{className:o?.skeleton})]}):s(v,{className:r("flex flex-col w-full text-fg",u),type:i?f?"text":"password":c,...b,children:[e(p,{className:r("flex text-fg-muted text-xs",o?.label),children:l}),s("div",{className:"relative",children:[e(k,{ref:w,placeholder:m,className:r("border-solid border border-border rounded-md","text-sm text-fg placeholder-fg-subtle","h-10 px-2 py-0 m-0 w-full","bg-white","transition-all duration-200 ease-in-out","hover:border-border-emphasis","focus:outline-2 focus:outline focus:outline-border-subtle focus:border-border-emphasis","disabled:border-border-subtle disabled:bg-surface-subtle","invalid:border-danger-soft invalid:bg-danger-surface invalid:text-danger","invalid:hover:border-danger invalid:focus:border-danger invalid:focus:outline-danger-ring",i&&"pr-10",o?.input),...t}),i&&e("button",{type:"button",className:"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-fg-subtle hover:text-slate-600 bg-transparent border-none cursor-pointer select-none",onPointerDown:a=>{a.preventDefault(),n(!0)},onPointerUp:()=>n(!1),onPointerLeave:()=>n(!1),onPointerCancel:()=>n(!1),onContextMenu:a=>a.preventDefault(),"aria-label":"Show password while pressed",tabIndex:-1,children:f?e(j,{className:"w-5 h-5"}):e(S,{className:"w-5 h-5"})})]}),d&&e(L,{className:r("flex text-fg-muted text-sm",o?.description),slot:"description",children:d}),e(C,{className:r("flex text-danger-soft text-sm",o?.error),children:x})]})});export{W as Input,W as default};
1
+ "use client";import{jsxs as s,jsx as e}from"react/jsx-runtime";import{Label as f,TextField as v,Input as k,Text as L,FieldError as C}from"react-aria-components";import{forwardRef as I,useState as N}from"react";import{cn as r}from"../../utils/cn.js";import{Skeleton as M}from"../Skeleton/Skeleton.js";import S from"../../hooks/useIsClient.js";function j({className:t}){return s("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:t,children:[e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.64 0 8.577 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.64 0-8.577-3.007-9.963-7.178Z"}),e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"})]})}function y({className:t}){return e("svg",{xmlns:"http://www.w3.org/2000/svg",fill:"none",viewBox:"0 0 24 24",strokeWidth:1.5,stroke:"currentColor",className:t,children:e("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"})})}function E({className:t}){return e(M,{className:r("w-full h-10 rounded-md z-20 relative",t)})}const W=I(({inputProps:t,label:l,description:a,errorMessage:p,placeholder:m,className:u,classNames:o,disableSkeleton:x,type:c,...h},b)=>{const w=S(),i=c==="password",[n,g]=N(!1);return!w&&!x?s("div",{className:r("flex flex-col w-full text-fg",u),children:[l?e(f,{className:r("flex text-fg-muted text-xs",o?.label),children:l}):null,e(E,{className:o?.skeleton})]}):s(v,{className:r("flex flex-col w-full text-fg",u),type:i?n?"text":"password":c,...h,children:[e(f,{className:r("flex text-fg-muted text-xs",o?.label),children:l}),s("div",{className:"relative",children:[e(k,{ref:b,placeholder:m,className:r("border-solid border border-border rounded-md","text-sm text-fg placeholder-fg-subtle","h-10 px-2 py-0 m-0 w-full","bg-white","transition-all duration-200 ease-in-out","hover:border-border-emphasis","focus:outline-2 focus:outline focus:outline-border-subtle focus:border-border-emphasis","disabled:border-border-subtle disabled:bg-surface-subtle","invalid:border-danger-soft invalid:bg-danger-surface invalid:text-danger","invalid:hover:border-danger invalid:focus:border-danger invalid:focus:outline-danger-ring",i&&"pr-10",o?.input),...t}),i&&e("button",{type:"button",className:"absolute right-2 top-1/2 -translate-y-1/2 p-1 text-fg-subtle hover:text-slate-600 bg-transparent border-none cursor-pointer select-none",onMouseDown:d=>d.preventDefault(),onClick:()=>g(d=>!d),"aria-label":n?"Hide password":"Show password","aria-pressed":n,children:n?e(y,{className:"w-5 h-5"}):e(j,{className:"w-5 h-5"})})]}),a&&e(L,{className:r("flex text-fg-muted text-sm",o?.description),slot:"description",children:a}),e(C,{className:r("flex text-danger-soft text-sm",o?.error),children:p})]})});export{W as Input,W as default};
@@ -56,10 +56,10 @@ import { isDevelopment } from '../../../../../utils/env';
56
56
  ```
57
57
 
58
58
  **After:**
59
- **Formerly named:** `2026-02-16-env-utils-from-clicktap-ui.md` — renamed 2026-08-07 (CTAP-1143) for global filename uniqueness; an identically-named guide exists in another tree and mirrored folders overwrote each other. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
60
59
  ```ts
61
60
  import { isDevelopment } from '@clicktap/ui/utils/env';
62
61
  ```
62
+ **Formerly named:** `2026-02-16-env-utils-from-clicktap-ui.md` — renamed 2026-08-07 (CTAP-1143) for global filename uniqueness; an identically-named guide exists in another tree and mirrored folders overwrote each other. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
63
63
 
64
64
  ### Files changed
65
65
 
@@ -4,10 +4,10 @@
4
4
  **Revised:** 2026-08-08 — the test-runner half (CTAP-1195): every runtime subpath entry gains a `default` condition, so CJS resolvers (Jest) stop falling through to the `.d.ts`. See "Test runners (Jest)" below; reported by noco.
5
5
  **Package:** `@clicktap/ui`
6
6
  **Breaking:** No — additive to the `exports` map. Existing `moduleResolution: node` / `node10`
7
- **Formerly named:** `2026-07-31-ui-exports-bundler-resolution.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
8
7
  consumers are unaffected (that resolver ignores `exports`); this only *adds* working resolution under
9
8
  `bundler` / `node16` / `nodenext`.
10
9
  **Classification:** BUGFIX (packaging) — unblocks `moduleResolution: bundler`, and with it TypeScript 7
10
+ **Formerly named:** `2026-07-31-ui-exports-bundler-resolution.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
11
11
 
12
12
  ## What was wrong
13
13
 
@@ -5,9 +5,11 @@
5
5
  **Affects:** `@clicktap/ui` — `Input` with `type="password"`.
6
6
  **Classification:** IMPROVEMENT (security / UX)
7
7
  **Breaking:** Behavioural only — the reveal changed from a click-toggle to press-and-hold. No API/prop
8
- **Formerly named:** `2026-08-04-password-reveal-press-and-hold.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
9
8
  change; adoption is automatic.
10
9
  **Ticket:** CTAP-1128
10
+ **Status:** ⚠️ **Superseded — do not adopt.** (CTAP-1240)
11
+ **Superseded by:** [`ui-2026-08-11-password-reveal-toggle.md`](./ui-2026-08-11-password-reveal-toggle.md) — press-and-hold made a revealed password impossible to copy into a password manager, so the reveal went back to a click toggle. If you have not applied this guide yet, skip it and apply that one. If you have, that guide tells you how to reverse it.
12
+ **Formerly named:** `2026-08-04-password-reveal-press-and-hold.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
11
13
 
12
14
  ## Summary
13
15
 
@@ -6,8 +6,8 @@
6
6
  **Classification:** IMPROVEMENT (new capability)
7
7
  **Breaking:** No — additive exports.
8
8
  **Consumers:** the OTP resend timer — [wire it everywhere](../../../../../../apps/frontend/docs/migrations/2026/08/2026-08-04-otp-resend-timer.md) ·
9
- **Formerly named:** `2026-08-04-resend-code-control.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
10
9
  GraphQL contract — [resend mutations](../../../../../../libs/mezzio-modules/user/docs/migrations/2026/08/2026-08-04-otp-resend-mutations.md)
10
+ **Formerly named:** `2026-08-04-resend-code-control.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
11
11
 
12
12
  ## Summary
13
13
 
@@ -5,9 +5,9 @@
5
5
  **Affects:** `@clicktap/ui` — `Button`, `ButtonLoadingContent`, `Loader/CircularEasing`, `Loader/Pulse`
6
6
  **Breaking:** No API change. **One requirement to be aware of** — see below.
7
7
  **Requires:** `@import '@clicktap/ui/theme.css'` in your CSS entry
8
- **Formerly named:** `2026-08-06-clicktap-ui-loaders-drop-framer-motion.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
9
8
  ([tokens guide](./ui-2026-08-06-clicktap-ui-semantic-tokens.md))
10
9
  **Classification:** IMPROVEMENT
10
+ **Formerly named:** `2026-08-06-clicktap-ui-loaders-drop-framer-motion.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
11
11
 
12
12
  ## What changed
13
13
 
@@ -5,9 +5,9 @@
5
5
  **Affects:** `@clicktap/ui`, every app that renders its components
6
6
  **Breaking:** **Yes — one required import.** Components render *unstyled* without it.
7
7
  **Requires:** Tailwind v4 (`@import 'tailwindcss'` in your CSS entry). See
8
- **Formerly named:** `2026-08-06-clicktap-ui-semantic-tokens.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
9
8
  [the v4 upgrade guide](../../../../../../apps/frontend/docs/migrations/2026/07/2026-07-31-tailwind-3-to-4.md).
10
9
  **Classification:** COMPATIBILITY
10
+ **Formerly named:** `2026-08-06-clicktap-ui-semantic-tokens.md` — renamed 2026-08-10 (CTAP-1214) for global filename uniqueness; library guides carry their package slug so they can never collide with an app-level guide in a consumer's aggregated working set. **If you carry a `-applied` sibling under the old name, rename it to match — do NOT re-apply.**
11
11
 
12
12
  ## Do this
13
13
 
@@ -0,0 +1,75 @@
1
+ # Password reveal is a toggle again, not press-and-hold
2
+
3
+ **Date:** 2026-08-11
4
+ **Package:** `@clicktap/ui`
5
+ **Affects:** `@clicktap/ui` — `Input` with `type="password"`.
6
+ **Classification:** IMPROVEMENT (UX / accessibility)
7
+ **Breaking:** Behavioural only — the reveal changed from press-and-hold back to a click toggle, and the
8
+ control entered the tab order. No API/prop change; adoption is automatic.
9
+ **Ticket:** CTAP-1240
10
+ **Supersedes:** [`ui-2026-08-04-password-reveal-press-and-hold.md`](./ui-2026-08-04-password-reveal-press-and-hold.md) (CTAP-1128).
11
+ If you have not applied that guide yet, **skip it** and apply this one instead — the end state is the
12
+ same either way.
13
+
14
+ ## Summary
15
+
16
+ The password-field reveal (the eye button in `Input`) is a **toggle** again: one click shows the value
17
+ and it stays shown until you click again. CTAP-1128 had made it press-and-hold — visible only while the
18
+ control was physically held — to shrink the shoulder-surfing window. That traded away more than it
19
+ bought:
20
+
21
+ - **You cannot copy a password you cannot release.** Revealing a password so you can select it and
22
+ paste it into a password manager is a routine thing to do, and press-and-hold makes it impossible.
23
+ - **You cannot watch yourself type.** Holding the button with one hand while typing a long password
24
+ with the other is not a real workflow.
25
+ - **A toggle is the control users already know.** Edge's own built-in password reveal is a toggle, as
26
+ are the Chrome and Safari password managers and effectively every major sign-in form.
27
+
28
+ The reveal is still user-initiated and still resets to hidden on every mount, so a revealed password
29
+ never survives a page load.
30
+
31
+ ## What changed
32
+
33
+ The reveal button swapped its pointer press-and-hold handlers back for a click toggle, and picked up
34
+ proper toggle-button semantics:
35
+
36
+ ```tsx
37
+ // before (CTAP-1128) // after (CTAP-1240)
38
+ onPointerDown={(e) => { onMouseDown={(e) => e.preventDefault()}
39
+ e.preventDefault(); onClick={() => setShowPassword((shown) => !shown)}
40
+ setShowPassword(true); aria-label={showPassword ? 'Hide password' : 'Show password'}
41
+ }} aria-pressed={showPassword}
42
+ onPointerUp={() => setShowPassword(false)}
43
+ onPointerLeave={() => setShowPassword(false)}
44
+ onPointerCancel={() => setShowPassword(false)}
45
+ onContextMenu={(e) => e.preventDefault()}
46
+ aria-label="Show password while pressed"
47
+ tabIndex={-1}
48
+ ```
49
+
50
+ - **`preventDefault()` on mousedown** keeps the caret in the input when you click the eye, so you can
51
+ toggle visibility mid-typing without losing your place.
52
+ - **`tabIndex={-1}` is gone** — the button is now in the tab order and operable with Enter/Space. This
53
+ is the one behavioural change a consumer might notice beyond the reveal itself: **a password field
54
+ now has one extra tab stop between it and the next control.** If you have a test that tabs through a
55
+ sign-in form by counting stops, update it.
56
+ - **`aria-pressed`** exposes the on/off state to assistive tech, and the label swaps between
57
+ `Show password` and `Hide password` instead of the static "Show password while pressed".
58
+ - Press-and-hold was **not** kept as an opt-in mode — there is one reveal behaviour, no new prop.
59
+
60
+ Shipped in `@clicktap/ui` **0.43.6**.
61
+
62
+ ## Action required (consumer)
63
+
64
+ - **Using `@clicktap/ui` `Input`:** nothing — the behaviour ships with the component. Re-check any
65
+ keyboard-navigation test that asserts an exact tab order around a password field.
66
+ - **Rolled your own password reveal:** swap the pointer handlers back for the `onClick` toggle above,
67
+ and add `aria-pressed` + the state-dependent `aria-label` while you are in there. The reference
68
+ frontend also updated its one bespoke reveal (`UpdateEmailForm`) to match.
69
+ - **Applied the CTAP-1128 guide:** this reverses it. Nothing to un-install beyond the handler swap.
70
+
71
+ ## Verify
72
+
73
+ Click the eye control → the characters show and **stay** shown. Select the revealed text and copy it —
74
+ it lands on the clipboard. Click again → hidden. Tab to the control and press Enter → it toggles.
75
+ Reload the page → the field is hidden again.
@@ -28,7 +28,8 @@ For migrations affecting the frontend app, see [frontend migrations](../../../..
28
28
  | 2026-06-17 | [Select standardization — one `<Select searchable>`, `DropdownSelect` removed](./2026/06/ui-2026-06-17-select-standardization.md) ↔ | Yes | *None* |
29
29
  | 2026-06-18 | [Select (searchable): fix "setValue is not a function" crash on browser autofill](./2026/06/ui-2026-06-18-select-hiddenselect-setvalue-crash.md) | No | Select standardization |
30
30
  | 2026-08-04 | [`ResendCode` control + `useCooldown` hook](./2026/08/ui-2026-08-04-resend-code-control.md) ↔ | No | OTP resend timer (CTAP-1114) |
31
- | 2026-08-04 | [Password reveal is press-and-hold, not a toggle](./2026/08/ui-2026-08-04-password-reveal-press-and-hold.md) | Behavioural | *None* (CTAP-1128) |
31
+ | 2026-08-04 | [Password reveal is press-and-hold, not a toggle](./2026/08/ui-2026-08-04-password-reveal-press-and-hold.md) ⚠️ superseded | Behavioural | *None* (CTAP-1128) |
32
32
  | 2026-08-06 | [`Image` is framework-free; Next's optimizer moves to `@clicktap/ui/next`](./2026/08/ui-2026-08-06-clicktap-ui-image-next-subpath.md) ↔ | Yes (Next consumers) | *None* |
33
+ | 2026-08-11 | [Password reveal is a toggle again, not press-and-hold](./2026/08/ui-2026-08-11-password-reveal-toggle.md) | Behavioural | Supersedes CTAP-1128 |
33
34
 
34
35
  ↔ = cross-app migration (has counterpart in frontend app)
@@ -1,4 +1,14 @@
1
1
  [
2
+ {
3
+ "path": "2026/08/ui-2026-08-11-password-reveal-toggle.md",
4
+ "module": "libs/frontend-modules/ui",
5
+ "package": "@clicktap/ui",
6
+ "date": "2026-08-11",
7
+ "title": "Password reveal is a toggle again, not press-and-hold",
8
+ "classification": "IMPROVEMENT (UX / accessibility)",
9
+ "breaking": "Behavioural only — the reveal changed from press-and-hold back to a click toggle, and the",
10
+ "affects": "`@clicktap/ui` — `Input` with `type=\"password\"`."
11
+ },
2
12
  {
3
13
  "path": "2026/08/ui-2026-08-07-ui-peer-contract-form-graphql-optional-peers.md",
4
14
  "module": "libs/frontend-modules/ui",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clicktap/ui",
3
- "version": "0.43.5",
3
+ "version": "0.43.6",
4
4
  "private": false,
5
5
  "author": "Clicktap",
6
6
  "description": "A library of React UI components and low-level hooks.",