@octavius2929-personal/design-system 1.0.1 → 1.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
@@ -1297,6 +1297,10 @@ interface ColorTokens {
1297
1297
  sunken: string;
1298
1298
  inset: string;
1299
1299
  };
1300
+ /** `strong`/`muted` cumplen AA (≥4.5) sobre los 4 fondos. **`subtle` solo cumple AA sobre
1301
+ * `bg.page`/`bg.surface`**: sobre los hundidos (`bg.sunken`/`bg.inset`) queda en ~3–4.4, así
1302
+ * que ahí es válido únicamente para uso incidental (estados `:disabled`, que WCAG 1.4.3
1303
+ * exime). Para texto ACTIVO sobre fondos hundidos usá `muted`. Lo fija `contrast.test.ts`. */
1300
1304
  fg: {
1301
1305
  strong: string;
1302
1306
  muted: string;
@@ -1985,6 +1989,17 @@ interface SnackbarProps {
1985
1989
  message: ReactNode;
1986
1990
  action?: ReactNode;
1987
1991
  onClose?: () => void;
1992
+ /**
1993
+ * Tiempo en ms antes de auto-cerrarse (dispara `onClose`). Default: `5000`.
1994
+ * Pasá `null` para desactivarlo: el snackbar queda persistente y solo se
1995
+ * cierra por acción del usuario (p. ej. el botón de cerrar).
1996
+ *
1997
+ * A11y (WCAG 2.2.1 — Timing Adjustable): el timer se pausa mientras el
1998
+ * puntero está sobre el snackbar o el foco está dentro de él, y se reinicia
1999
+ * con la duración completa al terminar la interacción. Así no se cierra
2000
+ * algo que el usuario está leyendo o con lo que está interactuando.
2001
+ */
2002
+ duration?: number | null;
1988
2003
  /** testId de la instancia: compone `feedback__<contexto>__<testId>`. */
1989
2004
  testId?: string;
1990
2005
  }
package/dist/index.d.ts CHANGED
@@ -1297,6 +1297,10 @@ interface ColorTokens {
1297
1297
  sunken: string;
1298
1298
  inset: string;
1299
1299
  };
1300
+ /** `strong`/`muted` cumplen AA (≥4.5) sobre los 4 fondos. **`subtle` solo cumple AA sobre
1301
+ * `bg.page`/`bg.surface`**: sobre los hundidos (`bg.sunken`/`bg.inset`) queda en ~3–4.4, así
1302
+ * que ahí es válido únicamente para uso incidental (estados `:disabled`, que WCAG 1.4.3
1303
+ * exime). Para texto ACTIVO sobre fondos hundidos usá `muted`. Lo fija `contrast.test.ts`. */
1300
1304
  fg: {
1301
1305
  strong: string;
1302
1306
  muted: string;
@@ -1985,6 +1989,17 @@ interface SnackbarProps {
1985
1989
  message: ReactNode;
1986
1990
  action?: ReactNode;
1987
1991
  onClose?: () => void;
1992
+ /**
1993
+ * Tiempo en ms antes de auto-cerrarse (dispara `onClose`). Default: `5000`.
1994
+ * Pasá `null` para desactivarlo: el snackbar queda persistente y solo se
1995
+ * cierra por acción del usuario (p. ej. el botón de cerrar).
1996
+ *
1997
+ * A11y (WCAG 2.2.1 — Timing Adjustable): el timer se pausa mientras el
1998
+ * puntero está sobre el snackbar o el foco está dentro de él, y se reinicia
1999
+ * con la duración completa al terminar la interacción. Así no se cierra
2000
+ * algo que el usuario está leyendo o con lo que está interactuando.
2001
+ */
2002
+ duration?: number | null;
1988
2003
  /** testId de la instancia: compone `feedback__<contexto>__<testId>`. */
1989
2004
  testId?: string;
1990
2005
  }
package/dist/index.js CHANGED
@@ -1795,17 +1795,33 @@ var defaultIcons = {
1795
1795
  warn: TriangleAlertIcon,
1796
1796
  danger: CircleXIcon
1797
1797
  };
1798
+ var roleBySeverity = {
1799
+ info: "status",
1800
+ ok: "status",
1801
+ warn: "alert",
1802
+ danger: "alert"
1803
+ };
1798
1804
  var Alert = forwardRef19(function Alert2({ severity: severity2 = "info", title, icon, className, testId, children, ...rest }, ref) {
1799
1805
  const styles = useStyles17({ severity: severity2, className });
1800
1806
  const { testId: dataTestId, slot } = useTestId("feedback", testId);
1801
1807
  const IconComponent = icon ?? defaultIcons[severity2];
1802
- return /* @__PURE__ */ jsxs16("div", { ref, role: "alert", className: styles.root, "data-testid": dataTestId, ...rest, children: [
1803
- /* @__PURE__ */ jsx29("span", { className: styles.iconSlot, "data-testid": slot("icon"), children: /* @__PURE__ */ jsx29(IconComponent, {}) }),
1804
- /* @__PURE__ */ jsxs16("div", { className: styles.content, "data-testid": slot("content"), children: [
1805
- title != null && /* @__PURE__ */ jsx29(Typography, { variant: "h4", children: title }),
1806
- children != null && /* @__PURE__ */ jsx29(Typography, { variant: "body", color: "muted", children })
1807
- ] })
1808
- ] });
1808
+ return /* @__PURE__ */ jsxs16(
1809
+ "div",
1810
+ {
1811
+ ref,
1812
+ role: roleBySeverity[severity2],
1813
+ className: styles.root,
1814
+ "data-testid": dataTestId,
1815
+ ...rest,
1816
+ children: [
1817
+ /* @__PURE__ */ jsx29("span", { className: styles.iconSlot, "data-testid": slot("icon"), children: /* @__PURE__ */ jsx29(IconComponent, {}) }),
1818
+ /* @__PURE__ */ jsxs16("div", { className: styles.content, "data-testid": slot("content"), children: [
1819
+ title != null && /* @__PURE__ */ jsx29(Typography, { variant: "h4", children: title }),
1820
+ children != null && /* @__PURE__ */ jsx29(Typography, { variant: "body", color: "muted", children })
1821
+ ] })
1822
+ ]
1823
+ }
1824
+ );
1809
1825
  });
1810
1826
 
1811
1827
  // src/components/tooltip/index.tsx
@@ -2934,7 +2950,7 @@ var Dialog = forwardRef29(function Dialog2({ open, onClose, title, actions: acti
2934
2950
  });
2935
2951
 
2936
2952
  // src/components/snackbar/index.tsx
2937
- import { forwardRef as forwardRef30 } from "react";
2953
+ import { forwardRef as forwardRef30, useCallback as useCallback2, useEffect as useEffect8, useRef as useRef7 } from "react";
2938
2954
  import { createPortal as createPortal2 } from "react-dom";
2939
2955
 
2940
2956
  // src/components/snackbar/use-styles.ts
@@ -2960,26 +2976,80 @@ function useStyles28() {
2960
2976
 
2961
2977
  // src/components/snackbar/index.tsx
2962
2978
  import { jsx as jsx43, jsxs as jsxs27 } from "react/jsx-runtime";
2963
- var Snackbar = forwardRef30(function Snackbar2({ open, message: message2, action, onClose, testId }, ref) {
2979
+ var DEFAULT_DURATION = 5e3;
2980
+ var Snackbar = forwardRef30(function Snackbar2({ open, message: message2, action, onClose, duration = DEFAULT_DURATION, testId }, ref) {
2964
2981
  const { root: root24, message: messageClass, closeBtn: closeBtn2 } = useStyles28();
2965
2982
  const { testId: dataTestId, slot } = useTestId("feedback", testId);
2983
+ const onCloseRef = useRef7(onClose);
2984
+ useEffect8(() => {
2985
+ onCloseRef.current = onClose;
2986
+ }, [onClose]);
2987
+ const interactingRef = useRef7({ hover: false, focus: false });
2988
+ const timeoutRef = useRef7();
2989
+ const clearTimer = useCallback2(() => {
2990
+ if (timeoutRef.current !== void 0) {
2991
+ clearTimeout(timeoutRef.current);
2992
+ timeoutRef.current = void 0;
2993
+ }
2994
+ }, []);
2995
+ const scheduleTimer = useCallback2(() => {
2996
+ clearTimer();
2997
+ if (!open || duration == null || !onCloseRef.current) return;
2998
+ if (interactingRef.current.hover || interactingRef.current.focus) return;
2999
+ timeoutRef.current = setTimeout(() => {
3000
+ onCloseRef.current?.();
3001
+ }, duration);
3002
+ }, [open, duration, clearTimer]);
3003
+ useEffect8(() => {
3004
+ scheduleTimer();
3005
+ return clearTimer;
3006
+ }, [scheduleTimer, clearTimer]);
3007
+ const handleMouseEnter = () => {
3008
+ interactingRef.current.hover = true;
3009
+ clearTimer();
3010
+ };
3011
+ const handleMouseLeave = () => {
3012
+ interactingRef.current.hover = false;
3013
+ scheduleTimer();
3014
+ };
3015
+ const handleFocus = () => {
3016
+ interactingRef.current.focus = true;
3017
+ clearTimer();
3018
+ };
3019
+ const handleBlur = () => {
3020
+ interactingRef.current.focus = false;
3021
+ scheduleTimer();
3022
+ };
2966
3023
  if (!open || typeof document === "undefined") return null;
2967
3024
  return createPortal2(
2968
- /* @__PURE__ */ jsxs27("div", { ref, role: "status", className: root24, "data-testid": dataTestId, children: [
2969
- /* @__PURE__ */ jsx43("span", { className: messageClass, "data-testid": slot("message"), children: message2 }),
2970
- action,
2971
- onClose && /* @__PURE__ */ jsx43(
2972
- "button",
2973
- {
2974
- type: "button",
2975
- "aria-label": "Close",
2976
- className: closeBtn2,
2977
- "data-testid": slot("close"),
2978
- onClick: onClose,
2979
- children: /* @__PURE__ */ jsx43(XIcon, { size: 18 })
2980
- }
2981
- )
2982
- ] }),
3025
+ /* @__PURE__ */ jsxs27(
3026
+ "div",
3027
+ {
3028
+ ref,
3029
+ role: "status",
3030
+ className: root24,
3031
+ "data-testid": dataTestId,
3032
+ onMouseEnter: handleMouseEnter,
3033
+ onMouseLeave: handleMouseLeave,
3034
+ onFocus: handleFocus,
3035
+ onBlur: handleBlur,
3036
+ children: [
3037
+ /* @__PURE__ */ jsx43("span", { className: messageClass, "data-testid": slot("message"), children: message2 }),
3038
+ action,
3039
+ onClose && /* @__PURE__ */ jsx43(
3040
+ "button",
3041
+ {
3042
+ type: "button",
3043
+ "aria-label": "Close",
3044
+ className: closeBtn2,
3045
+ "data-testid": slot("close"),
3046
+ onClick: onClose,
3047
+ children: /* @__PURE__ */ jsx43(XIcon, { size: 18 })
3048
+ }
3049
+ )
3050
+ ]
3051
+ }
3052
+ ),
2983
3053
  document.body
2984
3054
  );
2985
3055
  });