@particle-academy/react-fancy 5.0.0 → 5.2.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>>;
@@ -826,8 +843,18 @@ declare function Portal({ children, container }: PortalProps): react.ReactPortal
826
843
  interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
827
844
  /** Which heading element to render */
828
845
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
829
- /** Text size */
830
- size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
846
+ /**
847
+ * Text size, on a SEMANTIC ramp the names are steps on this scale, not
848
+ * Tailwind class names. `xl` is `text-2xl` and `2xl` is `text-4xl`, and that
849
+ * offset has been true since the component shipped, so the display steps
850
+ * below continue the same ramp rather than re-basing it and silently
851
+ * resizing every existing heading.
852
+ *
853
+ * `3xl`–`7xl` are DISPLAY sizes: hero type, "typography at maximum volume".
854
+ * They carry tighter tracking, because letter-spacing tuned for body-adjacent
855
+ * headings reads loose and unset once type gets big.
856
+ */
857
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "7xl";
831
858
  /** Font weight */
832
859
  weight?: "normal" | "medium" | "semibold" | "bold";
833
860
  }
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>>;
@@ -826,8 +843,18 @@ declare function Portal({ children, container }: PortalProps): react.ReactPortal
826
843
  interface HeadingProps extends HTMLAttributes<HTMLHeadingElement> {
827
844
  /** Which heading element to render */
828
845
  as?: "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
829
- /** Text size */
830
- size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
846
+ /**
847
+ * Text size, on a SEMANTIC ramp the names are steps on this scale, not
848
+ * Tailwind class names. `xl` is `text-2xl` and `2xl` is `text-4xl`, and that
849
+ * offset has been true since the component shipped, so the display steps
850
+ * below continue the same ramp rather than re-basing it and silently
851
+ * resizing every existing heading.
852
+ *
853
+ * `3xl`–`7xl` are DISPLAY sizes: hero type, "typography at maximum volume".
854
+ * They carry tighter tracking, because letter-spacing tuned for body-adjacent
855
+ * headings reads loose and unset once type gets big.
856
+ */
857
+ size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl" | "4xl" | "5xl" | "6xl" | "7xl";
831
858
  /** Font weight */
832
859
  weight?: "normal" | "medium" | "semibold" | "bold";
833
860
  }
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",
@@ -6183,7 +6222,20 @@ var sizeClasses3 = {
6183
6222
  md: "text-base",
6184
6223
  lg: "text-lg",
6185
6224
  xl: "text-2xl",
6186
- "2xl": "text-4xl"
6225
+ "2xl": "text-4xl",
6226
+ // Display steps. Each is the next Tailwind size up, continuing the ramp.
6227
+ "3xl": "text-5xl",
6228
+ "4xl": "text-6xl",
6229
+ "5xl": "text-7xl",
6230
+ "6xl": "text-8xl",
6231
+ "7xl": "text-9xl"
6232
+ };
6233
+ var trackingClasses = {
6234
+ "3xl": "tracking-tight",
6235
+ "4xl": "tracking-tight",
6236
+ "5xl": "tracking-tighter",
6237
+ "6xl": "tracking-tighter",
6238
+ "7xl": "tracking-tighter"
6187
6239
  };
6188
6240
  var weightClasses = {
6189
6241
  normal: "font-normal",
@@ -6208,6 +6260,7 @@ var Heading = forwardRef(
6208
6260
  className: cn(
6209
6261
  "text-zinc-900 dark:text-zinc-100",
6210
6262
  sizeClasses3[size],
6263
+ trackingClasses[size],
6211
6264
  weightClasses[weight],
6212
6265
  className
6213
6266
  ),