@particle-academy/react-fancy 4.19.1 → 5.1.0

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.
package/dist/index.d.cts CHANGED
@@ -376,6 +376,23 @@ interface InputProps extends InputBaseProps, InputAffixProps, Omit<InputHTMLAttr
376
376
  onValueChange?: (value: string) => void;
377
377
  leading?: ReactNode;
378
378
  trailing?: ReactNode;
379
+ /**
380
+ * Show a toggle that reveals the value, for `type="password"`.
381
+ *
382
+ * Ignored on other input types — a reveal on a field that is already visible
383
+ * is a button that does nothing.
384
+ *
385
+ * It occupies the trailing slot, so it replaces `trailing` when both are set:
386
+ * the two would otherwise stack in the same corner, and the reveal is the one
387
+ * a person needs to click.
388
+ */
389
+ reveal?: boolean;
390
+ /** Controlled reveal state. Omit to let the input own it. */
391
+ revealed?: boolean;
392
+ onRevealedChange?: (revealed: boolean) => void;
393
+ /** Accessible label for the toggle. Defaults to "Show password" / "Hide password". */
394
+ revealLabel?: string;
395
+ hideLabel?: string;
379
396
  }
380
397
 
381
398
  declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
package/dist/index.d.ts CHANGED
@@ -376,6 +376,23 @@ interface InputProps extends InputBaseProps, InputAffixProps, Omit<InputHTMLAttr
376
376
  onValueChange?: (value: string) => void;
377
377
  leading?: ReactNode;
378
378
  trailing?: ReactNode;
379
+ /**
380
+ * Show a toggle that reveals the value, for `type="password"`.
381
+ *
382
+ * Ignored on other input types — a reveal on a field that is already visible
383
+ * is a button that does nothing.
384
+ *
385
+ * It occupies the trailing slot, so it replaces `trailing` when both are set:
386
+ * the two would otherwise stack in the same corner, and the reveal is the one
387
+ * a person needs to click.
388
+ */
389
+ reveal?: boolean;
390
+ /** Controlled reveal state. Omit to let the input own it. */
391
+ revealed?: boolean;
392
+ onRevealedChange?: (revealed: boolean) => void;
393
+ /** Accessible label for the toggle. Defaults to "Show password" / "Hide password". */
394
+ revealLabel?: string;
395
+ hideLabel?: string;
379
396
  }
380
397
 
381
398
  declare const Input: react.ForwardRefExoticComponent<InputProps & react.RefAttributes<HTMLInputElement>>;
package/dist/index.js CHANGED
@@ -3177,12 +3177,21 @@ var Input = forwardRef(
3177
3177
  mode,
3178
3178
  onValueChange,
3179
3179
  onChange,
3180
+ reveal,
3181
+ revealed,
3182
+ onRevealedChange,
3183
+ revealLabel = "Show password",
3184
+ hideLabel = "Hide password",
3180
3185
  ...props
3181
3186
  }, ref) => {
3182
3187
  const autoId = useId();
3183
3188
  const inputId = id ?? autoId;
3184
3189
  const resolvedMode = useFieldMode(mode);
3185
3190
  const { showControl, interactive, enterEdit, exitEdit } = useInlineEdit(resolvedMode, disabled);
3191
+ const canReveal = reveal === true && type === "password";
3192
+ const [isRevealed, setRevealed] = useControllableState(revealed, false, onRevealedChange);
3193
+ const shown = canReveal && isRevealed;
3194
+ const renderedType = shown ? "text" : type;
3186
3195
  const input = !showControl ? /* @__PURE__ */ jsx(
3187
3196
  DisplayValue,
3188
3197
  {
@@ -3208,7 +3217,7 @@ var Input = forwardRef(
3208
3217
  {
3209
3218
  ref,
3210
3219
  id: inputId,
3211
- type,
3220
+ type: renderedType,
3212
3221
  disabled,
3213
3222
  required,
3214
3223
  className: cn(
@@ -3218,7 +3227,7 @@ var Input = forwardRef(
3218
3227
  errorClasses(error),
3219
3228
  "w-full",
3220
3229
  leading && "pl-9",
3221
- trailing && "pr-9",
3230
+ (trailing || canReveal) && "pr-9",
3222
3231
  className
3223
3232
  ),
3224
3233
  onChange: (e) => {
@@ -3233,7 +3242,23 @@ var Input = forwardRef(
3233
3242
  }
3234
3243
  }
3235
3244
  ),
3236
- trailing && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-3 text-zinc-400 dark:text-zinc-500", children: trailing })
3245
+ canReveal ? /* @__PURE__ */ jsx(
3246
+ "button",
3247
+ {
3248
+ type: "button",
3249
+ id: `${inputId}-reveal`,
3250
+ "data-react-fancy-reveal": "",
3251
+ onClick: () => setRevealed((v) => !v),
3252
+ disabled,
3253
+ "aria-controls": inputId,
3254
+ "aria-pressed": shown,
3255
+ "aria-label": shown ? hideLabel : revealLabel,
3256
+ title: shown ? hideLabel : revealLabel,
3257
+ tabIndex: -1,
3258
+ className: "absolute right-2 inline-flex items-center justify-center rounded p-1 text-zinc-400 transition-colors hover:text-zinc-600 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500/40 disabled:cursor-not-allowed disabled:opacity-50 dark:text-zinc-500 dark:hover:text-zinc-300",
3259
+ children: shown ? /* @__PURE__ */ jsx(EyeOffIcon, {}) : /* @__PURE__ */ jsx(EyeIcon, {})
3260
+ }
3261
+ ) : trailing && /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-3 text-zinc-400 dark:text-zinc-500", children: trailing })
3237
3262
  ] })
3238
3263
  }
3239
3264
  );
@@ -3255,6 +3280,20 @@ var Input = forwardRef(
3255
3280
  }
3256
3281
  );
3257
3282
  Input.displayName = "Input";
3283
+ function EyeIcon() {
3284
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3285
+ /* @__PURE__ */ jsx("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }),
3286
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" })
3287
+ ] });
3288
+ }
3289
+ function EyeOffIcon() {
3290
+ return /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [
3291
+ /* @__PURE__ */ jsx("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }),
3292
+ /* @__PURE__ */ jsx("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }),
3293
+ /* @__PURE__ */ jsx("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }),
3294
+ /* @__PURE__ */ jsx("path", { d: "m2 2 20 20" })
3295
+ ] });
3296
+ }
3258
3297
  var Textarea = forwardRef(
3259
3298
  ({
3260
3299
  size = "md",