@axzydev/axzy_ui_system 1.5.2 → 1.5.3

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/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "packageManager": "pnpm@10.34.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
- "version": "1.5.2",
7
+ "version": "1.5.3",
8
8
  "type": "module",
9
9
  "sideEffects": [
10
10
  "*.css"
@@ -7,15 +7,15 @@ export interface ITButtonProps {
7
7
  label?: string
8
8
  /** Custom content to render inside the button. Takes precedence over `label`. */
9
9
  children?: React.ReactNode;
10
- /** Icon element rendered before the label. */
10
+ /** Icon element rendered before the label. With `variant="icon-only"` and no `label`/`children` it becomes the only content and is centered in the square button. */
11
11
  icon?: React.ReactNode;
12
12
  /** Click handler for the button. */
13
13
  onClick?: () => void;
14
14
  /** Color theme key. Values come from the semantic color palette (e.g. `"primary"`, `"secondary"`, `"success"`, `"danger"`, `"warning"`, `"info"`, `"purple"`, `"error"`, `"gray"`). @default "primary" */
15
15
  color?: ColorsTypes;
16
- /** Button size. Valid values: `"sm"`, `"md"`, `"lg"`. @default "md" */
16
+ /** Button size. Valid values: `"sm"`, `"md"`, `"lg"`. In `variant="icon-only"` it also scales the square padding (`buttonIconPadding`: `sm` 0.5rem, `md` 0.625rem, `lg` 0.75rem). @default "md" */
17
17
  size?: SizesTypes;
18
- /** Button visual style. Valid values: `"filled"`, `"outlined"`, `"raised"`, `"rounded"`, `"text"`, `"raised-text"`, `"icon-only"`, `"link"`. @default "filled" */
18
+ /** Button visual style. Valid values: `"filled"`, `"outlined"`, `"raised"`, `"rounded"`, `"text"`, `"raised-text"`, `"icon-only"`, `"link"`. `"icon-only"` renders a centered square icon button; combine it with `icon` and `ariaLabel`. @default "filled" */
19
19
  variant?: keyof typeof buttonVariants;
20
20
  /** Disables the button interaction and applies reduced opacity. @default false */
21
21
  disabled?: boolean;
@@ -1,4 +1,6 @@
1
1
  import {
2
+ buttonIconPadding,
3
+ buttonIconSizes,
2
4
  buttonSizes,
3
5
  buttonVariants,
4
6
  } from "@/types/button.types";
@@ -35,6 +37,16 @@ export default function ITButton({
35
37
  }: ITButtonProps) {
36
38
  const [isHovered, setIsHovered] = useState(false);
37
39
  const [isFocused, setIsFocused] = useState(false);
40
+
41
+ /** La variante `icon-only` siempre es un cuadrado con padding simétrico. */
42
+ const isIconOnly = variant === "icon-only";
43
+
44
+ /**
45
+ * Contenido "solo icono": hay `icon` y no hay `children`.
46
+ * En ese caso NO se envuelve el icono en el wrapper con `gap-1.5` ni en un `ITText` vacío,
47
+ * que lo desplazaban a la izquierda del centro.
48
+ */
49
+ const isIconOnlyContent = isIconOnly && Boolean(icon) && !children;
38
50
 
39
51
  // Get specific button config from theme
40
52
  const themeBtn = theme.button as any;
@@ -64,8 +76,11 @@ export default function ITButton({
64
76
  style.borderRadius = "9999px";
65
77
  }
66
78
 
67
- if (variant === "icon-only") {
68
- style.padding = "0.5rem"; // Square padding override
79
+ if (isIconOnly) {
80
+ // Padding simétrico explícito: con `aspect-square` define un cuadrado exacto
81
+ // y evita que las utilidades `px-*`/`py-*` de `buttonSizes` desalineen el icono.
82
+ style.padding = buttonIconPadding[size] ?? "0.5rem";
83
+ style.lineHeight = 1;
69
84
  }
70
85
 
71
86
  if (isSolid) {
@@ -103,7 +118,7 @@ export default function ITButton({
103
118
  // Apply variant base styles (border, shadow, rounded-full)
104
119
  buttonVariants[variant || "filled"],
105
120
  // Apply size classes (padding/font-size)
106
- buttonSizes[size],
121
+ isIconOnly ? buttonIconSizes[size] : buttonSizes[size],
107
122
  className,
108
123
  { "opacity-50 cursor-not-allowed": disabled }
109
124
  )}
@@ -118,10 +133,15 @@ export default function ITButton({
118
133
  title={title || ariaLabel || label}
119
134
  >
120
135
  {children || (
121
- <span className={clsx("inline-flex items-center gap-1.5 font-semibold", { "opacity-50": disabled })}>
122
- {icon && <span className="flex-shrink-0">{icon}</span>}
123
- <ITText as="span">{label}</ITText>
124
- </span>
136
+ isIconOnlyContent ? (
137
+ // Icono solo: sin wrapper con `gap` ni `ITText` vacío, que lo desplazaban a la izquierda.
138
+ <span className="inline-flex items-center justify-center leading-none">{icon}</span>
139
+ ) : (
140
+ <span className={clsx("inline-flex items-center gap-1.5 font-semibold", { "opacity-50": disabled })}>
141
+ {icon && <span className="flex-shrink-0">{icon}</span>}
142
+ <ITText as="span">{label}</ITText>
143
+ </span>
144
+ )
125
145
  )}
126
146
  </button>
127
147
  );
@@ -772,6 +772,7 @@ export const sandboxMessages: ITMessagesByLocale = {
772
772
  "showcase.forms.clicksRegistered": "Clicks registrados: {count}",
773
773
  "showcase.forms.withIcon": "Con Icono",
774
774
  "showcase.forms.galleryWithIcon": "Con Icono",
775
+ "showcase.forms.galleryIconOnly": "Solo Icono (centrado)",
775
776
  "showcase.forms.galleryVariants": "Variantes",
776
777
  "showcase.forms.gallerySemanticColors": "Colores Semánticos",
777
778
  "showcase.forms.sync": "Sincronizar",
@@ -1714,6 +1715,7 @@ export const sandboxMessages: ITMessagesByLocale = {
1714
1715
  "showcase.forms.clicksRegistered": "Clicks registered: {count}",
1715
1716
  "showcase.forms.withIcon": "With icon",
1716
1717
  "showcase.forms.galleryWithIcon": "With icon",
1718
+ "showcase.forms.galleryIconOnly": "Icon only (centered)",
1717
1719
  "showcase.forms.galleryVariants": "Variants",
1718
1720
  "showcase.forms.gallerySemanticColors": "Semantic colors",
1719
1721
  "showcase.forms.sync": "Sync",
@@ -158,6 +158,14 @@ export const ButtonShowcase = () => {
158
158
  <ITButton label={t("showcase.forms.sync")} icon={<FaSync />} variant="text" />
159
159
  </div>
160
160
  </div>
161
+ <div>
162
+ <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.forms.galleryIconOnly")}</h3>
163
+ <div className="flex flex-wrap items-center gap-3">
164
+ <ITButton variant="icon-only" size="sm" color="primary" icon={<FaEdit />} ariaLabel={t("showcase.common.edit")} />
165
+ <ITButton variant="icon-only" size="md" color="success" icon={<FaSave />} ariaLabel={t("showcase.common.save")} />
166
+ <ITButton variant="icon-only" size="lg" color="danger" icon={<FaTrash />} ariaLabel={t("showcase.common.delete")} />
167
+ </div>
168
+ </div>
161
169
  <div>
162
170
  <h3 className="mb-3 w-fit border-l-2 border-primary-700 pl-2.5 font-mono text-[11px] font-medium uppercase tracking-[0.14em] text-slate-700 dark:border-primary-500 dark:text-slate-200">{t("showcase.forms.galleryVariants")}</h3>
163
171
  <div className="flex flex-wrap gap-3">
@@ -210,6 +218,7 @@ export const ButtonShowcase = () => {
210
218
  notes: [
211
219
  "Con children no se renderizan label ni icon.",
212
220
  'Usa variant="icon-only" junto a ariaLabel cuando el botón no tenga texto visible.',
221
+ 'Con variant="icon-only" + icon (sin label) el botón es cuadrado y el icono queda centrado; el tamaño del cuadrado sale de buttonIconPadding (sm 0.5rem, md 0.625rem, lg 0.75rem).',
213
222
  ],
214
223
  }}
215
224
  />
@@ -5,7 +5,8 @@ export const buttonVariants: Record<string, string> = {
5
5
  rounded: "border-transparent shadow-sm rounded-full",
6
6
  text: "bg-transparent border-transparent shadow-none hover:bg-black/5 dark:hover:bg-white/10",
7
7
  "raised-text": "bg-white border border-gray-200 shadow-sm hover:shadow-md",
8
- "icon-only": "p-2 aspect-square flex items-center justify-center border-transparent shadow-sm",
8
+ // No lleva utilidades de padding: el cuadrado lo define `buttonIconPadding` por tamaño.
9
+ "icon-only": "aspect-square flex items-center justify-center border-transparent shadow-sm",
9
10
  link: "bg-transparent border-transparent shadow-none hover:underline px-0",
10
11
  };
11
12
 
@@ -14,3 +15,20 @@ export const buttonSizes: Record<string, string> = {
14
15
  md: "text-[13px] px-3.5 py-1.5",
15
16
  lg: "text-sm px-4 py-2",
16
17
  };
18
+
19
+ /**
20
+ * Tipografía de `icon-only`: solo el tamaño de fuente, sin padding horizontal/vertical,
21
+ * para que el padding simétrico de `buttonIconPadding` no compita con `buttonSizes`.
22
+ */
23
+ export const buttonIconSizes: Record<string, string> = {
24
+ sm: "text-xs",
25
+ md: "text-[13px]",
26
+ lg: "text-sm",
27
+ };
28
+
29
+ /** Padding simétrico (px) del botón `icon-only` por tamaño; define el cuadrado con `aspect-square`. */
30
+ export const buttonIconPadding: Record<string, string> = {
31
+ sm: "0.5rem",
32
+ md: "0.625rem",
33
+ lg: "0.75rem",
34
+ };