@xsolla/xui-button 0.191.0 → 0.192.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.
@@ -170,7 +170,14 @@ interface IconButtonProps extends ThemeOverrideProps {
170
170
  declare const IconButton: React.FC<IconButtonProps>;
171
171
 
172
172
  interface FlexButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type"> {
173
- /** Button label. Omit for icon-only buttons (provide `aria-label`). */
173
+ /**
174
+ * Button label. Omit for icon-only buttons (provide `aria-label`).
175
+ *
176
+ * An icon may also be passed directly as `children` instead of via
177
+ * `iconLeft`/`iconRight`. When `children` carries no text, the button is
178
+ * treated as icon-only and renders as a square sized from the design-system
179
+ * height token for `size`.
180
+ */
174
181
  children?: ReactNode;
175
182
  /** Visual variant of the button */
176
183
  variant?: "brand" | "primary" | "secondary" | "tertiary" | "brandExtra" | "inverse";
@@ -190,6 +197,9 @@ interface FlexButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElem
190
197
  * The component applies padding via an inline style, which overrides any CSS
191
198
  * class or `style` prop a consumer passes, so this prop is the supported way
192
199
  * to render a zero-padding FlexButton (e.g. an inline text action).
200
+ *
201
+ * Icon-only buttons keep their square dimensions when `noPadding` is set —
202
+ * only the inner padding is removed.
193
203
  * @default false
194
204
  */
195
205
  noPadding?: boolean;
@@ -224,6 +234,19 @@ interface FlexButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElem
224
234
  *
225
235
  * Renders as a semantic `<button>` element with full ARIA support.
226
236
  *
237
+ * ## Icon-only buttons
238
+ *
239
+ * A FlexButton is icon-only when it has no text label and an icon is supplied
240
+ * either as `children` or via `iconLeft`/`iconRight`. Icon-only buttons render
241
+ * as a square whose side equals `theme.sizing.flexButton(size).height`, so they
242
+ * line up with the Modal header icon slots (36x36 at `size="xl"`).
243
+ *
244
+ * ```tsx
245
+ * <FlexButton variant="secondary" size="xl" aria-label="Go back">
246
+ * <BackwardAlt variant="line" />
247
+ * </FlexButton>
248
+ * ```
249
+ *
227
250
  * ## Accessibility Features
228
251
  *
229
252
  * - **Semantic HTML**: Renders as a native `<button>` element
package/native/index.d.ts CHANGED
@@ -170,7 +170,14 @@ interface IconButtonProps extends ThemeOverrideProps {
170
170
  declare const IconButton: React.FC<IconButtonProps>;
171
171
 
172
172
  interface FlexButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type"> {
173
- /** Button label. Omit for icon-only buttons (provide `aria-label`). */
173
+ /**
174
+ * Button label. Omit for icon-only buttons (provide `aria-label`).
175
+ *
176
+ * An icon may also be passed directly as `children` instead of via
177
+ * `iconLeft`/`iconRight`. When `children` carries no text, the button is
178
+ * treated as icon-only and renders as a square sized from the design-system
179
+ * height token for `size`.
180
+ */
174
181
  children?: ReactNode;
175
182
  /** Visual variant of the button */
176
183
  variant?: "brand" | "primary" | "secondary" | "tertiary" | "brandExtra" | "inverse";
@@ -190,6 +197,9 @@ interface FlexButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElem
190
197
  * The component applies padding via an inline style, which overrides any CSS
191
198
  * class or `style` prop a consumer passes, so this prop is the supported way
192
199
  * to render a zero-padding FlexButton (e.g. an inline text action).
200
+ *
201
+ * Icon-only buttons keep their square dimensions when `noPadding` is set —
202
+ * only the inner padding is removed.
193
203
  * @default false
194
204
  */
195
205
  noPadding?: boolean;
@@ -224,6 +234,19 @@ interface FlexButtonProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElem
224
234
  *
225
235
  * Renders as a semantic `<button>` element with full ARIA support.
226
236
  *
237
+ * ## Icon-only buttons
238
+ *
239
+ * A FlexButton is icon-only when it has no text label and an icon is supplied
240
+ * either as `children` or via `iconLeft`/`iconRight`. Icon-only buttons render
241
+ * as a square whose side equals `theme.sizing.flexButton(size).height`, so they
242
+ * line up with the Modal header icon slots (36x36 at `size="xl"`).
243
+ *
244
+ * ```tsx
245
+ * <FlexButton variant="secondary" size="xl" aria-label="Go back">
246
+ * <BackwardAlt variant="line" />
247
+ * </FlexButton>
248
+ * ```
249
+ *
227
250
  * ## Accessibility Features
228
251
  *
229
252
  * - **Semantic HTML**: Renders as a native `<button>` element
package/native/index.js CHANGED
@@ -808,15 +808,25 @@ var IconButton = ({
808
808
  IconButton.displayName = "IconButton";
809
809
 
810
810
  // src/FlexButton.tsx
811
- var import_react4 = require("react");
811
+ var import_react4 = __toESM(require("react"));
812
812
  var import_xui_core3 = require("@xsolla/xui-core");
813
813
  var import_jsx_runtime7 = require("react/jsx-runtime");
814
814
  var FLEX_BUTTON_PADDING = 4;
815
815
  var hasVisibleLabel = (node) => {
816
- if (node == null || node === false) return false;
816
+ if (node == null || node === false || node === true) return false;
817
817
  if (typeof node === "string") return node.trim().length > 0;
818
818
  if (typeof node === "number") return true;
819
819
  if (Array.isArray(node)) return node.some(hasVisibleLabel);
820
+ if (import_react4.default.isValidElement(node)) {
821
+ const { children: elementChildren } = node.props ?? {};
822
+ return hasVisibleLabel(elementChildren);
823
+ }
824
+ return true;
825
+ };
826
+ var hasRenderableContent = (node) => {
827
+ if (node == null || node === false || node === true) return false;
828
+ if (typeof node === "string") return node.trim().length > 0;
829
+ if (Array.isArray(node)) return node.some(hasRenderableContent);
820
830
  return true;
821
831
  };
822
832
  var ICON_SIZES = {
@@ -1018,8 +1028,10 @@ var FlexButton = ({
1018
1028
  const fontSize = FONT_SIZES[size];
1019
1029
  const borderRadius = BORDER_RADIUS[size];
1020
1030
  const lineHeight = LINE_HEIGHTS[size];
1021
- const isIconOnly = !loading && !hasVisibleLabel(children) && Boolean(iconLeft || iconRight);
1022
- const iconOnlyHitArea = iconSize + FLEX_BUTTON_PADDING * 2;
1031
+ const showTextLabel = hasVisibleLabel(children);
1032
+ const hasIconChildren = !showTextLabel && hasRenderableContent(children);
1033
+ const isIconOnly = !loading && !showTextLabel && Boolean(iconLeft || iconRight || hasIconChildren);
1034
+ const iconOnlyHitArea = theme.sizing.flexButton(size).height;
1023
1035
  const handleMouseEnter = () => {
1024
1036
  if (!isDisabled) {
1025
1037
  isMouseOverRef.current = true;
@@ -1159,7 +1171,8 @@ var FlexButton = ({
1159
1171
  "data-testid": testID || "flex-button",
1160
1172
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: contentStyle, children: loading ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { style: spinnerStyle, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Spinner, { size: spinnerSize, color: spinnerColor }) }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(import_jsx_runtime7.Fragment, { children: [
1161
1173
  iconLeft && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { size: iconSize, color: colors.text, children: iconLeft }),
1162
- hasVisibleLabel(children) && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children }),
1174
+ showTextLabel && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { children }),
1175
+ hasIconChildren && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { size: iconSize, color: colors.text, children }),
1163
1176
  iconRight && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { size: iconSize, color: colors.text, children: iconRight })
1164
1177
  ] }) })
1165
1178
  }