@tinybigui/react 0.4.2 → 0.6.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.cjs +682 -365
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +324 -181
- package/dist/index.d.ts +324 -181
- package/dist/index.js +682 -365
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1113,81 +1113,113 @@ var IconButtonHeadless = forwardRef(
|
|
|
1113
1113
|
tabIndex = 0,
|
|
1114
1114
|
onMouseDown,
|
|
1115
1115
|
type,
|
|
1116
|
-
|
|
1116
|
+
isSelected,
|
|
1117
|
+
isToggle = false,
|
|
1118
|
+
isDisabled = false,
|
|
1117
1119
|
"aria-label": ariaLabel,
|
|
1118
1120
|
title,
|
|
1119
1121
|
...props
|
|
1120
1122
|
}, forwardedRef) => {
|
|
1121
1123
|
const internalRef = useRef(null);
|
|
1122
1124
|
const ref = forwardedRef ?? internalRef;
|
|
1123
|
-
const { buttonProps } = useButton(
|
|
1125
|
+
const { buttonProps, isPressed } = useButton(
|
|
1124
1126
|
{
|
|
1125
1127
|
...props,
|
|
1126
|
-
// Ensure element type is 'button' for proper semantics
|
|
1127
1128
|
elementType: "button",
|
|
1128
|
-
|
|
1129
|
-
|
|
1129
|
+
"aria-label": ariaLabel,
|
|
1130
|
+
isDisabled
|
|
1130
1131
|
},
|
|
1131
1132
|
ref
|
|
1132
1133
|
);
|
|
1134
|
+
const { isHovered, hoverProps } = useHover({ isDisabled });
|
|
1135
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
1133
1136
|
const domProps = filterDOMProps(props);
|
|
1134
|
-
const mergedProps = mergeProps(
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
+
const mergedProps = mergeProps(buttonProps, hoverProps, focusProps, domProps, {
|
|
1138
|
+
tabIndex,
|
|
1139
|
+
className,
|
|
1140
|
+
onMouseDown,
|
|
1141
|
+
type: type ?? "button",
|
|
1142
|
+
...title && { title },
|
|
1143
|
+
// aria-pressed only when acting as a toggle button
|
|
1144
|
+
...isToggle && { "aria-pressed": isSelected ?? false }
|
|
1145
|
+
});
|
|
1146
|
+
return /* @__PURE__ */ jsx(
|
|
1147
|
+
"button",
|
|
1137
1148
|
{
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1149
|
+
...mergedProps,
|
|
1150
|
+
ref,
|
|
1151
|
+
type: type === "submit" ? "submit" : type === "reset" ? "reset" : "button",
|
|
1152
|
+
...getInteractionDataAttributes({
|
|
1153
|
+
isHovered,
|
|
1154
|
+
isFocusVisible,
|
|
1155
|
+
isPressed,
|
|
1156
|
+
...isToggle ? { isSelected: isSelected ?? false } : {},
|
|
1157
|
+
isDisabled
|
|
1158
|
+
}),
|
|
1159
|
+
"data-toggle": isToggle ? "" : void 0,
|
|
1160
|
+
children
|
|
1146
1161
|
}
|
|
1147
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1148
|
-
);
|
|
1149
|
-
return (
|
|
1150
|
-
// eslint-disable-next-line react/button-has-type
|
|
1151
|
-
/* @__PURE__ */ jsx("button", { ...mergedProps, ref, type: type ?? "button", children })
|
|
1152
1162
|
);
|
|
1153
1163
|
}
|
|
1154
1164
|
);
|
|
1155
1165
|
IconButtonHeadless.displayName = "IconButtonHeadless";
|
|
1156
|
-
var
|
|
1166
|
+
var iconButtonRootVariants = cva(
|
|
1157
1167
|
[
|
|
1158
|
-
//
|
|
1159
|
-
"relative inline-flex items-center justify-center
|
|
1160
|
-
"
|
|
1161
|
-
|
|
1162
|
-
//
|
|
1163
|
-
//
|
|
1164
|
-
//
|
|
1168
|
+
// Layout
|
|
1169
|
+
"relative inline-flex items-center justify-center",
|
|
1170
|
+
"cursor-pointer select-none",
|
|
1171
|
+
"overflow-hidden",
|
|
1172
|
+
// Corner radius driven by CSS variable — set per shape×size in compoundVariants.
|
|
1173
|
+
// Fallback 9999px is only reached if both shape and size props are absent,
|
|
1174
|
+
// which cannot happen in normal usage.
|
|
1175
|
+
"rounded-[var(--ib-radius,9999px)]",
|
|
1176
|
+
// Split MD3 transition via the existing btn-transition utility:
|
|
1177
|
+
// border-radius → emphasized-decelerate (no overshoot, no sharp-corner flash)
|
|
1178
|
+
// color/bg/border/opacity → standard-fast-effects (no overshoot on effects)
|
|
1179
|
+
// This is identical to the approach used by Button/connected ButtonGroup and is
|
|
1180
|
+
// the standard fix for the 9999px overshoot problem documented in styles.css.
|
|
1165
1181
|
"btn-transition",
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
"
|
|
1169
|
-
"
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
"
|
|
1173
|
-
"
|
|
1182
|
+
// Background + border + text driven from CSS role variables
|
|
1183
|
+
"bg-[var(--ib-bg,transparent)]",
|
|
1184
|
+
"border border-[var(--ib-border,transparent)]",
|
|
1185
|
+
"text-[var(--ib-fg,currentColor)]",
|
|
1186
|
+
// Toggle: off state (data-toggle present but data-selected absent)
|
|
1187
|
+
// Uses doubly-chained selector to beat single-chain specificity of defaults
|
|
1188
|
+
"data-[toggle]:bg-[var(--ib-bg-off,var(--ib-bg,transparent))]",
|
|
1189
|
+
"data-[toggle]:text-[var(--ib-fg-off,var(--ib-fg,currentColor))]",
|
|
1190
|
+
// Selected state
|
|
1191
|
+
"data-[selected]:bg-[var(--ib-bg-on,var(--ib-bg,transparent))]",
|
|
1192
|
+
"data-[selected]:text-[var(--ib-fg-on,var(--ib-fg,currentColor))]",
|
|
1193
|
+
"data-[selected]:border-transparent",
|
|
1194
|
+
// Press shape-morph: radius collapses to --ib-radius-press on press
|
|
1195
|
+
// (only has visual effect when --ib-radius-press differs from --ib-radius)
|
|
1196
|
+
"data-[pressed]:rounded-[var(--ib-radius-press,var(--ib-radius,9999px))]",
|
|
1197
|
+
// Focus ring (outline, not a state layer — stays outside overflow-hidden
|
|
1198
|
+
// because it's drawn as outline on the root element itself)
|
|
1199
|
+
"outline-none",
|
|
1200
|
+
"group-data-[focus-visible]/icon-button:outline-2",
|
|
1201
|
+
"group-data-[focus-visible]/icon-button:outline-offset-2",
|
|
1202
|
+
"group-data-[focus-visible]/icon-button:outline-secondary",
|
|
1203
|
+
// Disabled — content opacity 38%, container handled per variant via CSS vars
|
|
1204
|
+
"data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none",
|
|
1205
|
+
"data-[disabled]:text-on-surface/38"
|
|
1206
|
+
// Filled/tonal/outlined-selected backgrounds collapse to on-surface/12 — set
|
|
1207
|
+
// via compoundVariants on the root for variants that have a container.
|
|
1208
|
+
// For variants with transparent bg (standard, outlined-unselected) we do nothing.
|
|
1174
1209
|
],
|
|
1175
1210
|
{
|
|
1176
1211
|
variants: {
|
|
1177
1212
|
/**
|
|
1178
|
-
*
|
|
1213
|
+
* Visual style variant (MD3 icon button types)
|
|
1179
1214
|
*/
|
|
1180
1215
|
variant: {
|
|
1181
|
-
standard: "
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
tonal: "",
|
|
1186
|
-
// Container background
|
|
1187
|
-
outlined: "bg-transparent border border-outline"
|
|
1216
|
+
standard: "",
|
|
1217
|
+
filled: "data-[disabled]:bg-on-surface/12",
|
|
1218
|
+
tonal: "data-[disabled]:bg-on-surface/12",
|
|
1219
|
+
outlined: ""
|
|
1188
1220
|
},
|
|
1189
1221
|
/**
|
|
1190
|
-
* Color scheme
|
|
1222
|
+
* Color scheme — sets CSS role variables via compoundVariants.
|
|
1191
1223
|
*/
|
|
1192
1224
|
color: {
|
|
1193
1225
|
primary: "",
|
|
@@ -1196,180 +1228,400 @@ var iconButtonVariants = cva(
|
|
|
1196
1228
|
error: ""
|
|
1197
1229
|
},
|
|
1198
1230
|
/**
|
|
1199
|
-
*
|
|
1231
|
+
* Size tier (M3 Expressive 5-tier)
|
|
1200
1232
|
*/
|
|
1201
1233
|
size: {
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
medium: "h-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
// 48×48px
|
|
1234
|
+
xsmall: "h-8",
|
|
1235
|
+
small: "h-10",
|
|
1236
|
+
medium: "h-14",
|
|
1237
|
+
large: "h-24",
|
|
1238
|
+
xlarge: "h-[8.5rem]"
|
|
1208
1239
|
},
|
|
1209
1240
|
/**
|
|
1210
|
-
*
|
|
1241
|
+
* Width variant — adjusts container width
|
|
1211
1242
|
*/
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1243
|
+
width: {
|
|
1244
|
+
narrow: "",
|
|
1245
|
+
default: "",
|
|
1246
|
+
wide: ""
|
|
1215
1247
|
},
|
|
1216
1248
|
/**
|
|
1217
|
-
*
|
|
1249
|
+
* Shape — base values only; per-size radii set via compoundVariants below.
|
|
1250
|
+
*
|
|
1251
|
+
* round: --ib-radius = half the container height (true circle), set per size.
|
|
1252
|
+
* --ib-radius-press = square corner for that size (set per size).
|
|
1253
|
+
* Morph distance is small (e.g. 28px → 16px for medium), so the
|
|
1254
|
+
* emphasized-decelerate curve from btn-transition produces a smooth,
|
|
1255
|
+
* non-overshooting transition. The old 9999px fallback caused the
|
|
1256
|
+
* spring to overshoot below 0 = sharp-corner flash.
|
|
1257
|
+
* square: --ib-radius = size-tiered MD3 corner, set per size. No press morph.
|
|
1218
1258
|
*/
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1259
|
+
shape: {
|
|
1260
|
+
round: [],
|
|
1261
|
+
square: []
|
|
1222
1262
|
}
|
|
1223
1263
|
},
|
|
1224
|
-
/**
|
|
1225
|
-
* Compound variants - combinations of variant + color + selected
|
|
1226
|
-
*/
|
|
1227
1264
|
compoundVariants: [
|
|
1228
|
-
//
|
|
1229
|
-
//
|
|
1230
|
-
//
|
|
1231
|
-
{
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
},
|
|
1265
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1266
|
+
// SIZE × WIDTH — container width
|
|
1267
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1268
|
+
{ size: "xsmall", width: "narrow", className: "w-6" },
|
|
1269
|
+
{ size: "xsmall", width: "default", className: "w-8" },
|
|
1270
|
+
{ size: "xsmall", width: "wide", className: "w-10" },
|
|
1271
|
+
{ size: "small", width: "narrow", className: "w-8" },
|
|
1272
|
+
{ size: "small", width: "default", className: "w-10" },
|
|
1273
|
+
{ size: "small", width: "wide", className: "w-13" },
|
|
1274
|
+
{ size: "medium", width: "narrow", className: "w-12" },
|
|
1275
|
+
{ size: "medium", width: "default", className: "w-14" },
|
|
1276
|
+
{ size: "medium", width: "wide", className: "w-18" },
|
|
1277
|
+
{ size: "large", width: "narrow", className: "w-18" },
|
|
1278
|
+
{ size: "large", width: "default", className: "w-24" },
|
|
1279
|
+
{ size: "large", width: "wide", className: "w-32" },
|
|
1280
|
+
{ size: "xlarge", width: "narrow", className: "w-24" },
|
|
1281
|
+
{ size: "xlarge", width: "default", className: "w-[8.5rem]" },
|
|
1282
|
+
{ size: "xlarge", width: "wide", className: "w-42" },
|
|
1283
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1284
|
+
// SHAPE × SIZE — corner radii for both round and square shapes
|
|
1285
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1286
|
+
//
|
|
1287
|
+
// Round rest radius = half container height (true circle).
|
|
1288
|
+
// Using the exact half-height keeps the morph distance small, so the
|
|
1289
|
+
// no-overshoot emphasized-decelerate curve in btn-transition produces a
|
|
1290
|
+
// smooth animation. Using 9999px was the original cause of the sharp-
|
|
1291
|
+
// corner flash (the spring overshoots below 0 before settling).
|
|
1292
|
+
//
|
|
1293
|
+
// xsmall h-8 = 32px → half = 16px = 1rem
|
|
1294
|
+
// small h-10 = 40px → half = 20px = 1.25rem
|
|
1295
|
+
// medium h-14 = 56px → half = 28px = 1.75rem
|
|
1296
|
+
// large h-24 = 96px → half = 48px = 3rem
|
|
1297
|
+
// xlarge h-34 = 136px → half = 68px = 4.25rem
|
|
1298
|
+
//
|
|
1299
|
+
// Round press-morph target = MD3 square corner for that size tier.
|
|
1300
|
+
// Square rest radius = same MD3 corner (no morph).
|
|
1301
|
+
// ── round: rest radius (half height) ──────────────────────────────────
|
|
1302
|
+
{ shape: "round", size: "xsmall", className: "[--ib-radius:1rem]" },
|
|
1303
|
+
{ shape: "round", size: "small", className: "[--ib-radius:1.25rem]" },
|
|
1304
|
+
{ shape: "round", size: "medium", className: "[--ib-radius:1.75rem]" },
|
|
1305
|
+
{ shape: "round", size: "large", className: "[--ib-radius:3rem]" },
|
|
1306
|
+
{ shape: "round", size: "xlarge", className: "[--ib-radius:4.25rem]" },
|
|
1307
|
+
// ── round: press-morph target (square corner for that size) ───────────
|
|
1308
|
+
{ shape: "round", size: "xsmall", className: "[--ib-radius-press:0.75rem]" },
|
|
1309
|
+
{ shape: "round", size: "small", className: "[--ib-radius-press:0.75rem]" },
|
|
1310
|
+
{ shape: "round", size: "medium", className: "[--ib-radius-press:1rem]" },
|
|
1311
|
+
{ shape: "round", size: "large", className: "[--ib-radius-press:1.75rem]" },
|
|
1312
|
+
{ shape: "round", size: "xlarge", className: "[--ib-radius-press:1.75rem]" },
|
|
1313
|
+
// ── square: rest radius (MD3 shape scale) ─────────────────────────────
|
|
1314
|
+
// xsmall / small → 12px (0.75rem), medium → 16px (1rem), large / xlarge → 28px (1.75rem)
|
|
1315
|
+
{ shape: "square", size: "xsmall", className: "[--ib-radius:0.75rem]" },
|
|
1316
|
+
{ shape: "square", size: "small", className: "[--ib-radius:0.75rem]" },
|
|
1317
|
+
{ shape: "square", size: "medium", className: "[--ib-radius:1rem]" },
|
|
1318
|
+
{ shape: "square", size: "large", className: "[--ib-radius:1.75rem]" },
|
|
1319
|
+
{ shape: "square", size: "xlarge", className: "[--ib-radius:1.75rem]" },
|
|
1320
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1321
|
+
// VARIANT × COLOR — CSS role variable assignments
|
|
1322
|
+
// Only variant × color (design-time decisions); no state variants here.
|
|
1323
|
+
// ══════════════════════════════════════════════════════════════════════
|
|
1324
|
+
// ── STANDARD ──────────────────────────────────────────────────────────
|
|
1325
|
+
// Non-toggle standard: transparent bg, on-surface-variant fg
|
|
1326
|
+
// Selected: primary fg
|
|
1327
|
+
// State layer: on-surface-variant (unselected) / primary (selected)
|
|
1236
1328
|
{
|
|
1237
1329
|
variant: "standard",
|
|
1238
|
-
selected: true,
|
|
1239
|
-
className: "text-primary"
|
|
1240
|
-
},
|
|
1241
|
-
// ====================
|
|
1242
|
-
// FILLED VARIANTS (UNSELECTED)
|
|
1243
|
-
// ====================
|
|
1244
|
-
{
|
|
1245
|
-
variant: "filled",
|
|
1246
1330
|
color: "primary",
|
|
1247
|
-
|
|
1248
|
-
|
|
1331
|
+
className: [
|
|
1332
|
+
"[--ib-bg:transparent]",
|
|
1333
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1334
|
+
"[--ib-fg-on:var(--color-primary)]",
|
|
1335
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1336
|
+
// toggle-off same as non-toggle
|
|
1337
|
+
"[--ib-bg-off:transparent]",
|
|
1338
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1339
|
+
// toggle-on: selected
|
|
1340
|
+
"[--ib-bg-on:transparent]"
|
|
1341
|
+
]
|
|
1249
1342
|
},
|
|
1250
1343
|
{
|
|
1251
|
-
variant: "
|
|
1344
|
+
variant: "standard",
|
|
1252
1345
|
color: "secondary",
|
|
1253
|
-
|
|
1254
|
-
|
|
1346
|
+
className: [
|
|
1347
|
+
"[--ib-bg:transparent]",
|
|
1348
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1349
|
+
"[--ib-fg-on:var(--color-secondary)]",
|
|
1350
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1351
|
+
"[--ib-bg-off:transparent]",
|
|
1352
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1353
|
+
"[--ib-bg-on:transparent]"
|
|
1354
|
+
]
|
|
1255
1355
|
},
|
|
1256
1356
|
{
|
|
1257
|
-
variant: "
|
|
1357
|
+
variant: "standard",
|
|
1258
1358
|
color: "tertiary",
|
|
1259
|
-
|
|
1260
|
-
|
|
1359
|
+
className: [
|
|
1360
|
+
"[--ib-bg:transparent]",
|
|
1361
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1362
|
+
"[--ib-fg-on:var(--color-tertiary)]",
|
|
1363
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1364
|
+
"[--ib-bg-off:transparent]",
|
|
1365
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1366
|
+
"[--ib-bg-on:transparent]"
|
|
1367
|
+
]
|
|
1261
1368
|
},
|
|
1262
1369
|
{
|
|
1263
|
-
variant: "
|
|
1370
|
+
variant: "standard",
|
|
1264
1371
|
color: "error",
|
|
1265
|
-
|
|
1266
|
-
|
|
1372
|
+
className: [
|
|
1373
|
+
"[--ib-bg:transparent]",
|
|
1374
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1375
|
+
"[--ib-fg-on:var(--color-error)]",
|
|
1376
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1377
|
+
"[--ib-bg-off:transparent]",
|
|
1378
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1379
|
+
"[--ib-bg-on:transparent]"
|
|
1380
|
+
]
|
|
1267
1381
|
},
|
|
1268
|
-
//
|
|
1269
|
-
//
|
|
1270
|
-
//
|
|
1382
|
+
// ── FILLED ────────────────────────────────────────────────────────────
|
|
1383
|
+
// Non-toggle: bg primary / fg on-primary
|
|
1384
|
+
// Toggle off: bg surface-container-highest / fg primary
|
|
1385
|
+
// Toggle on (selected): bg primary / fg on-primary
|
|
1386
|
+
// State layer: on-primary (non-toggle / selected), primary (toggle-off)
|
|
1271
1387
|
{
|
|
1272
1388
|
variant: "filled",
|
|
1273
1389
|
color: "primary",
|
|
1274
|
-
|
|
1275
|
-
|
|
1390
|
+
className: [
|
|
1391
|
+
"[--ib-bg:var(--color-primary)]",
|
|
1392
|
+
"[--ib-fg:var(--color-on-primary)]",
|
|
1393
|
+
"[--ib-sl:var(--color-on-primary)]",
|
|
1394
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1395
|
+
"[--ib-fg-off:var(--color-primary)]",
|
|
1396
|
+
"[--ib-bg-on:var(--color-primary)]",
|
|
1397
|
+
"[--ib-fg-on:var(--color-on-primary)]"
|
|
1398
|
+
]
|
|
1276
1399
|
},
|
|
1277
1400
|
{
|
|
1278
1401
|
variant: "filled",
|
|
1279
1402
|
color: "secondary",
|
|
1280
|
-
|
|
1281
|
-
|
|
1403
|
+
className: [
|
|
1404
|
+
"[--ib-bg:var(--color-secondary)]",
|
|
1405
|
+
"[--ib-fg:var(--color-on-secondary)]",
|
|
1406
|
+
"[--ib-sl:var(--color-on-secondary)]",
|
|
1407
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1408
|
+
"[--ib-fg-off:var(--color-secondary)]",
|
|
1409
|
+
"[--ib-bg-on:var(--color-secondary)]",
|
|
1410
|
+
"[--ib-fg-on:var(--color-on-secondary)]"
|
|
1411
|
+
]
|
|
1282
1412
|
},
|
|
1283
1413
|
{
|
|
1284
1414
|
variant: "filled",
|
|
1285
1415
|
color: "tertiary",
|
|
1286
|
-
|
|
1287
|
-
|
|
1416
|
+
className: [
|
|
1417
|
+
"[--ib-bg:var(--color-tertiary)]",
|
|
1418
|
+
"[--ib-fg:var(--color-on-tertiary)]",
|
|
1419
|
+
"[--ib-sl:var(--color-on-tertiary)]",
|
|
1420
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1421
|
+
"[--ib-fg-off:var(--color-tertiary)]",
|
|
1422
|
+
"[--ib-bg-on:var(--color-tertiary)]",
|
|
1423
|
+
"[--ib-fg-on:var(--color-on-tertiary)]"
|
|
1424
|
+
]
|
|
1288
1425
|
},
|
|
1289
1426
|
{
|
|
1290
1427
|
variant: "filled",
|
|
1291
1428
|
color: "error",
|
|
1292
|
-
|
|
1293
|
-
|
|
1429
|
+
className: [
|
|
1430
|
+
"[--ib-bg:var(--color-error)]",
|
|
1431
|
+
"[--ib-fg:var(--color-on-error)]",
|
|
1432
|
+
"[--ib-sl:var(--color-on-error)]",
|
|
1433
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1434
|
+
"[--ib-fg-off:var(--color-error)]",
|
|
1435
|
+
"[--ib-bg-on:var(--color-error)]",
|
|
1436
|
+
"[--ib-fg-on:var(--color-on-error)]"
|
|
1437
|
+
]
|
|
1294
1438
|
},
|
|
1295
|
-
//
|
|
1296
|
-
//
|
|
1297
|
-
//
|
|
1439
|
+
// ── TONAL ─────────────────────────────────────────────────────────────
|
|
1440
|
+
// Non-toggle: bg secondary-container / fg on-secondary-container
|
|
1441
|
+
// Toggle off: bg surface-container-highest / fg on-surface-variant
|
|
1442
|
+
// Toggle on (selected): bg secondary-container / fg on-secondary-container
|
|
1298
1443
|
{
|
|
1299
1444
|
variant: "tonal",
|
|
1300
1445
|
color: "primary",
|
|
1301
|
-
|
|
1302
|
-
|
|
1446
|
+
className: [
|
|
1447
|
+
"[--ib-bg:var(--color-secondary-container)]",
|
|
1448
|
+
"[--ib-fg:var(--color-on-secondary-container)]",
|
|
1449
|
+
"[--ib-sl:var(--color-on-secondary-container)]",
|
|
1450
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1451
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1452
|
+
"[--ib-bg-on:var(--color-secondary-container)]",
|
|
1453
|
+
"[--ib-fg-on:var(--color-on-secondary-container)]"
|
|
1454
|
+
]
|
|
1303
1455
|
},
|
|
1304
1456
|
{
|
|
1305
1457
|
variant: "tonal",
|
|
1306
1458
|
color: "secondary",
|
|
1307
|
-
|
|
1308
|
-
|
|
1459
|
+
className: [
|
|
1460
|
+
"[--ib-bg:var(--color-secondary-container)]",
|
|
1461
|
+
"[--ib-fg:var(--color-on-secondary-container)]",
|
|
1462
|
+
"[--ib-sl:var(--color-on-secondary-container)]",
|
|
1463
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1464
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1465
|
+
"[--ib-bg-on:var(--color-secondary-container)]",
|
|
1466
|
+
"[--ib-fg-on:var(--color-on-secondary-container)]"
|
|
1467
|
+
]
|
|
1309
1468
|
},
|
|
1310
1469
|
{
|
|
1311
1470
|
variant: "tonal",
|
|
1312
1471
|
color: "tertiary",
|
|
1313
|
-
|
|
1314
|
-
|
|
1472
|
+
className: [
|
|
1473
|
+
"[--ib-bg:var(--color-tertiary-container)]",
|
|
1474
|
+
"[--ib-fg:var(--color-on-tertiary-container)]",
|
|
1475
|
+
"[--ib-sl:var(--color-on-tertiary-container)]",
|
|
1476
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1477
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1478
|
+
"[--ib-bg-on:var(--color-tertiary-container)]",
|
|
1479
|
+
"[--ib-fg-on:var(--color-on-tertiary-container)]"
|
|
1480
|
+
]
|
|
1315
1481
|
},
|
|
1316
1482
|
{
|
|
1317
1483
|
variant: "tonal",
|
|
1318
1484
|
color: "error",
|
|
1319
|
-
|
|
1320
|
-
|
|
1485
|
+
className: [
|
|
1486
|
+
"[--ib-bg:var(--color-error-container)]",
|
|
1487
|
+
"[--ib-fg:var(--color-on-error-container)]",
|
|
1488
|
+
"[--ib-sl:var(--color-on-error-container)]",
|
|
1489
|
+
"[--ib-bg-off:var(--color-surface-container-highest)]",
|
|
1490
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1491
|
+
"[--ib-bg-on:var(--color-error-container)]",
|
|
1492
|
+
"[--ib-fg-on:var(--color-on-error-container)]"
|
|
1493
|
+
]
|
|
1321
1494
|
},
|
|
1322
|
-
//
|
|
1323
|
-
//
|
|
1324
|
-
//
|
|
1495
|
+
// ── OUTLINED ──────────────────────────────────────────────────────────
|
|
1496
|
+
// Non-toggle: transparent bg, border-outline, on-surface-variant fg
|
|
1497
|
+
// Toggle off: same as non-toggle
|
|
1498
|
+
// Toggle on (selected): inverse-surface bg, inverse-on-surface fg, no border
|
|
1499
|
+
// Disabled: border becomes on-surface/12 (set via Tailwind utility on root)
|
|
1325
1500
|
{
|
|
1326
|
-
variant: "
|
|
1327
|
-
|
|
1328
|
-
className:
|
|
1501
|
+
variant: "outlined",
|
|
1502
|
+
color: "primary",
|
|
1503
|
+
className: [
|
|
1504
|
+
"[--ib-bg:transparent]",
|
|
1505
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1506
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1507
|
+
"[--ib-border:var(--color-outline)]",
|
|
1508
|
+
"[--ib-bg-off:transparent]",
|
|
1509
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1510
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1511
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1512
|
+
// Disabled outlined border
|
|
1513
|
+
"data-[disabled]:border-on-surface/12"
|
|
1514
|
+
]
|
|
1329
1515
|
},
|
|
1330
|
-
// ====================
|
|
1331
|
-
// OUTLINED VARIANTS (UNSELECTED)
|
|
1332
|
-
// ====================
|
|
1333
1516
|
{
|
|
1334
1517
|
variant: "outlined",
|
|
1335
|
-
|
|
1336
|
-
className:
|
|
1518
|
+
color: "secondary",
|
|
1519
|
+
className: [
|
|
1520
|
+
"[--ib-bg:transparent]",
|
|
1521
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1522
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1523
|
+
"[--ib-border:var(--color-outline)]",
|
|
1524
|
+
"[--ib-bg-off:transparent]",
|
|
1525
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1526
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1527
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1528
|
+
"data-[disabled]:border-on-surface/12"
|
|
1529
|
+
]
|
|
1337
1530
|
},
|
|
1338
|
-
// ====================
|
|
1339
|
-
// OUTLINED VARIANTS (SELECTED - uses inverse colors)
|
|
1340
|
-
// ====================
|
|
1341
1531
|
{
|
|
1342
1532
|
variant: "outlined",
|
|
1343
|
-
|
|
1344
|
-
className:
|
|
1533
|
+
color: "tertiary",
|
|
1534
|
+
className: [
|
|
1535
|
+
"[--ib-bg:transparent]",
|
|
1536
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1537
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1538
|
+
"[--ib-border:var(--color-outline)]",
|
|
1539
|
+
"[--ib-bg-off:transparent]",
|
|
1540
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1541
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1542
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1543
|
+
"data-[disabled]:border-on-surface/12"
|
|
1544
|
+
]
|
|
1545
|
+
},
|
|
1546
|
+
{
|
|
1547
|
+
variant: "outlined",
|
|
1548
|
+
color: "error",
|
|
1549
|
+
className: [
|
|
1550
|
+
"[--ib-bg:transparent]",
|
|
1551
|
+
"[--ib-fg:var(--color-on-surface-variant)]",
|
|
1552
|
+
"[--ib-sl:var(--color-on-surface-variant)]",
|
|
1553
|
+
"[--ib-border:var(--color-outline)]",
|
|
1554
|
+
"[--ib-bg-off:transparent]",
|
|
1555
|
+
"[--ib-fg-off:var(--color-on-surface-variant)]",
|
|
1556
|
+
"[--ib-bg-on:var(--color-inverse-surface)]",
|
|
1557
|
+
"[--ib-fg-on:var(--color-inverse-on-surface)]",
|
|
1558
|
+
"data-[disabled]:border-on-surface/12"
|
|
1559
|
+
]
|
|
1345
1560
|
}
|
|
1346
1561
|
],
|
|
1347
|
-
/**
|
|
1348
|
-
* Default variants
|
|
1349
|
-
*/
|
|
1350
1562
|
defaultVariants: {
|
|
1351
1563
|
variant: "standard",
|
|
1352
1564
|
color: "primary",
|
|
1353
1565
|
size: "medium",
|
|
1354
|
-
|
|
1355
|
-
|
|
1566
|
+
width: "default",
|
|
1567
|
+
shape: "round"
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
);
|
|
1571
|
+
var iconButtonStateLayerVariants = cva([
|
|
1572
|
+
"absolute inset-0 rounded-[inherit] pointer-events-none opacity-0",
|
|
1573
|
+
"bg-[var(--ib-sl,currentColor)]",
|
|
1574
|
+
// Effects transition (opacity — no spatial overshoot)
|
|
1575
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
1576
|
+
// Interaction opacities (MD3: hover 8%, focus/pressed 10%)
|
|
1577
|
+
"group-data-[hovered]/icon-button:opacity-8",
|
|
1578
|
+
"group-data-[focus-visible]/icon-button:opacity-10",
|
|
1579
|
+
"group-data-[pressed]/icon-button:opacity-10",
|
|
1580
|
+
// No state layer when disabled
|
|
1581
|
+
"group-data-[disabled]/icon-button:hidden"
|
|
1582
|
+
]);
|
|
1583
|
+
var iconButtonIconVariants = cva(
|
|
1584
|
+
[
|
|
1585
|
+
"relative z-10 inline-flex shrink-0 items-center justify-center",
|
|
1586
|
+
"transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
|
|
1587
|
+
],
|
|
1588
|
+
{
|
|
1589
|
+
variants: {
|
|
1590
|
+
size: {
|
|
1591
|
+
xsmall: "size-5",
|
|
1592
|
+
// 20dp
|
|
1593
|
+
small: "size-6",
|
|
1594
|
+
// 24dp
|
|
1595
|
+
medium: "size-6",
|
|
1596
|
+
// 24dp
|
|
1597
|
+
large: "size-8",
|
|
1598
|
+
// 32dp
|
|
1599
|
+
xlarge: "size-10"
|
|
1600
|
+
// 40dp
|
|
1601
|
+
}
|
|
1602
|
+
},
|
|
1603
|
+
defaultVariants: {
|
|
1604
|
+
size: "medium"
|
|
1356
1605
|
}
|
|
1357
1606
|
}
|
|
1358
1607
|
);
|
|
1359
1608
|
var IconButton = forwardRef(
|
|
1360
1609
|
({
|
|
1361
|
-
// Variant props (CVA)
|
|
1610
|
+
// Variant props (CVA / design-time)
|
|
1362
1611
|
variant = "standard",
|
|
1363
1612
|
color = "primary",
|
|
1364
1613
|
size = "medium",
|
|
1614
|
+
width = "default",
|
|
1615
|
+
shape = "round",
|
|
1365
1616
|
// IconButton specific props
|
|
1366
1617
|
children,
|
|
1618
|
+
selectedIcon,
|
|
1367
1619
|
value,
|
|
1368
1620
|
selected,
|
|
1369
1621
|
disableRipple = false,
|
|
1370
1622
|
className,
|
|
1371
1623
|
// React Aria props
|
|
1372
|
-
isDisabled
|
|
1624
|
+
isDisabled = false,
|
|
1373
1625
|
onPress,
|
|
1374
1626
|
onMouseDown,
|
|
1375
1627
|
"aria-label": ariaLabel,
|
|
@@ -1388,7 +1640,8 @@ var IconButton = forwardRef(
|
|
|
1388
1640
|
console.warn("[IconButton] IconButton should have an icon as children.");
|
|
1389
1641
|
}
|
|
1390
1642
|
}
|
|
1391
|
-
const
|
|
1643
|
+
const isToggle = selected !== void 0;
|
|
1644
|
+
const isSelected = isToggle ? selected ?? false : false;
|
|
1392
1645
|
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
1393
1646
|
disabled: isDisabled || disableRipple
|
|
1394
1647
|
});
|
|
@@ -1406,32 +1659,37 @@ var IconButton = forwardRef(
|
|
|
1406
1659
|
...getConnectedRadiusClasses(groupCtx, value),
|
|
1407
1660
|
groupCtx.enforceMinWidth ? "min-w-12" : ""
|
|
1408
1661
|
] : [];
|
|
1662
|
+
const iconNode = isToggle && isSelected && selectedIcon ? selectedIcon : children;
|
|
1409
1663
|
return /* @__PURE__ */ jsxs(
|
|
1410
1664
|
IconButtonHeadless,
|
|
1411
1665
|
{
|
|
1412
1666
|
ref,
|
|
1413
1667
|
className: cn(
|
|
1414
|
-
// CVA
|
|
1415
|
-
|
|
1416
|
-
//
|
|
1417
|
-
|
|
1418
|
-
//
|
|
1419
|
-
// path, preventing the overshoot-to-0px sharp-corner flash.
|
|
1668
|
+
// Root CVA — sets CSS role variables, dimensions, shape, transitions
|
|
1669
|
+
iconButtonRootVariants({ variant, color, size, width, shape }),
|
|
1670
|
+
// Group scope for child slot selectors
|
|
1671
|
+
"group/icon-button",
|
|
1672
|
+
// ButtonGroup asymmetric border-radius easing (connected selection morph)
|
|
1420
1673
|
isGroupSelected ? "btn-transition-selected" : "",
|
|
1421
1674
|
...connectedClasses,
|
|
1422
|
-
//
|
|
1675
|
+
// Consumer custom classes
|
|
1423
1676
|
className
|
|
1424
1677
|
),
|
|
1425
1678
|
"aria-label": ariaLabel,
|
|
1679
|
+
isSelected,
|
|
1680
|
+
isToggle,
|
|
1426
1681
|
"data-variant": variant,
|
|
1427
1682
|
"data-color": color,
|
|
1683
|
+
"data-size": size,
|
|
1684
|
+
"data-width": width,
|
|
1685
|
+
"data-shape": shape,
|
|
1428
1686
|
"data-group-selected": isGroupSelected ? "" : void 0,
|
|
1429
|
-
...selected !== void 0 && { selected },
|
|
1430
1687
|
...title && { title },
|
|
1431
1688
|
...mergedPropsValue,
|
|
1432
1689
|
children: [
|
|
1690
|
+
/* @__PURE__ */ jsx("span", { className: iconButtonStateLayerVariants(), "aria-hidden": "true", "data-state-layer": "" }),
|
|
1433
1691
|
ripples,
|
|
1434
|
-
/* @__PURE__ */ jsx("span", { className:
|
|
1692
|
+
/* @__PURE__ */ jsx("span", { className: iconButtonIconVariants({ size }), "data-icon-slot": "", "aria-hidden": "true", children: iconNode })
|
|
1435
1693
|
]
|
|
1436
1694
|
}
|
|
1437
1695
|
);
|
|
@@ -1447,232 +1705,273 @@ var FABHeadless = forwardRef(
|
|
|
1447
1705
|
type,
|
|
1448
1706
|
"aria-label": ariaLabel,
|
|
1449
1707
|
title,
|
|
1450
|
-
...
|
|
1708
|
+
...restProps
|
|
1451
1709
|
}, forwardedRef) => {
|
|
1452
1710
|
const internalRef = useRef(null);
|
|
1453
1711
|
const ref = forwardedRef ?? internalRef;
|
|
1454
1712
|
const { buttonProps } = useButton(
|
|
1455
1713
|
{
|
|
1456
|
-
...
|
|
1714
|
+
...restProps,
|
|
1457
1715
|
elementType: "button"
|
|
1458
1716
|
},
|
|
1459
1717
|
ref
|
|
1460
1718
|
);
|
|
1461
|
-
const
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1719
|
+
const {
|
|
1720
|
+
isDisabled: _isDisabled,
|
|
1721
|
+
onPress: _onPress,
|
|
1722
|
+
onPressStart: _onPressStart,
|
|
1723
|
+
onPressEnd: _onPressEnd,
|
|
1724
|
+
onPressChange: _onPressChange,
|
|
1725
|
+
onPressUp: _onPressUp,
|
|
1726
|
+
...htmlAttrs
|
|
1727
|
+
} = restProps;
|
|
1728
|
+
const mergedProps = mergeProps(
|
|
1729
|
+
buttonProps,
|
|
1730
|
+
{
|
|
1731
|
+
tabIndex,
|
|
1732
|
+
className,
|
|
1733
|
+
onMouseDown,
|
|
1734
|
+
"aria-label": ariaLabel,
|
|
1735
|
+
...title !== void 0 && { title }
|
|
1736
|
+
},
|
|
1737
|
+
htmlAttrs
|
|
1738
|
+
);
|
|
1472
1739
|
return (
|
|
1473
|
-
// eslint-disable-next-line react/button-has-type
|
|
1474
|
-
/* @__PURE__ */ jsx("button", { ...mergedProps, ref, children })
|
|
1740
|
+
// eslint-disable-next-line react/button-has-type -- type is dynamically passed from props
|
|
1741
|
+
/* @__PURE__ */ jsx("button", { ...mergedProps, ref, type: type ?? "button", children })
|
|
1475
1742
|
);
|
|
1476
1743
|
}
|
|
1477
1744
|
);
|
|
1478
1745
|
FABHeadless.displayName = "FABHeadless";
|
|
1479
1746
|
var fabVariants = cva(
|
|
1480
1747
|
[
|
|
1481
|
-
//
|
|
1482
|
-
"relative inline-flex items-center justify-center cursor-pointer",
|
|
1483
|
-
"overflow-hidden",
|
|
1484
|
-
"transition-all duration-200",
|
|
1485
|
-
"focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
1748
|
+
// Layout — NO overflow-hidden here (focus ring must extend outside)
|
|
1749
|
+
"relative inline-flex items-center justify-center cursor-pointer select-none",
|
|
1486
1750
|
"shrink-0",
|
|
1487
|
-
//
|
|
1488
|
-
|
|
1489
|
-
"
|
|
1490
|
-
|
|
1491
|
-
"
|
|
1492
|
-
"
|
|
1493
|
-
"active:before:opacity-12",
|
|
1494
|
-
// Elevation (floating appearance)
|
|
1495
|
-
"shadow-elevation-3",
|
|
1496
|
-
// Default elevation
|
|
1497
|
-
"hover:shadow-elevation-4"
|
|
1498
|
-
// Hover elevation
|
|
1751
|
+
// Effects transition: color / bg / shadow — standard spring, no overshoot
|
|
1752
|
+
"transition-[color,background-color,box-shadow]",
|
|
1753
|
+
"duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
1754
|
+
// Disabled — self-targeting data-[x]: selectors (not group-data — these target the root itself)
|
|
1755
|
+
"data-[disabled]:bg-on-surface/12 data-[disabled]:text-on-surface/38",
|
|
1756
|
+
"data-[disabled]:shadow-none data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none"
|
|
1499
1757
|
],
|
|
1500
1758
|
{
|
|
1501
1759
|
variants: {
|
|
1502
1760
|
/**
|
|
1503
|
-
* FAB size
|
|
1761
|
+
* FAB size — controls container dimensions, corner radius, and icon slot size.
|
|
1504
1762
|
*/
|
|
1505
1763
|
size: {
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
"rounded-xl",
|
|
1512
|
-
// 12px corner radius (not fully rounded!)
|
|
1513
|
-
"m-1"
|
|
1514
|
-
// 4px margin for 48×48px touch target
|
|
1515
|
-
],
|
|
1516
|
-
medium: [
|
|
1764
|
+
/**
|
|
1765
|
+
* fab (default) — 56×56dp, 16dp corner radius.
|
|
1766
|
+
* MD3 standard FAB.
|
|
1767
|
+
*/
|
|
1768
|
+
fab: [
|
|
1517
1769
|
"h-14 w-14",
|
|
1518
|
-
//
|
|
1519
|
-
"p-4",
|
|
1520
|
-
// 16px padding for 24px icon
|
|
1770
|
+
// 56dp
|
|
1521
1771
|
"rounded-2xl"
|
|
1522
|
-
//
|
|
1772
|
+
// 16dp corner
|
|
1523
1773
|
],
|
|
1774
|
+
/**
|
|
1775
|
+
* medium — 80×80dp, 20dp corner radius.
|
|
1776
|
+
* M3 Expressive Medium FAB. Previously this value mapped to 56dp;
|
|
1777
|
+
* it is now remapped to the Expressive 80dp Medium FAB.
|
|
1778
|
+
*/
|
|
1779
|
+
medium: [
|
|
1780
|
+
"h-20 w-20",
|
|
1781
|
+
// 80dp
|
|
1782
|
+
"rounded-[20px]"
|
|
1783
|
+
// 20dp corner (large-increased shape token)
|
|
1784
|
+
],
|
|
1785
|
+
/**
|
|
1786
|
+
* large — 96×96dp, 28dp corner radius.
|
|
1787
|
+
*/
|
|
1524
1788
|
large: [
|
|
1525
1789
|
"h-24 w-24",
|
|
1526
|
-
//
|
|
1527
|
-
"p-[30px]",
|
|
1528
|
-
// 30px padding for 36px icon
|
|
1790
|
+
// 96dp
|
|
1529
1791
|
"rounded-[28px]"
|
|
1530
|
-
//
|
|
1792
|
+
// 28dp corner
|
|
1531
1793
|
],
|
|
1794
|
+
/**
|
|
1795
|
+
* extended — 56dp height, variable width, 16dp corner.
|
|
1796
|
+
* Icon + text label side by side.
|
|
1797
|
+
* Padding: 16dp leading (icon side), 20dp trailing (text side).
|
|
1798
|
+
* Gap: 12dp between icon and label (MD3 spec).
|
|
1799
|
+
*/
|
|
1532
1800
|
extended: [
|
|
1533
1801
|
"h-14",
|
|
1534
|
-
//
|
|
1802
|
+
// 56dp height
|
|
1535
1803
|
"rounded-2xl",
|
|
1536
|
-
//
|
|
1804
|
+
// 16dp corner
|
|
1537
1805
|
"pl-4 pr-5",
|
|
1538
|
-
//
|
|
1539
|
-
"gap-
|
|
1540
|
-
//
|
|
1806
|
+
// 16dp leading, 20dp trailing
|
|
1807
|
+
"gap-3"
|
|
1808
|
+
// 12dp gap between icon and label
|
|
1809
|
+
],
|
|
1810
|
+
/**
|
|
1811
|
+
* @deprecated Use `fab` (56dp) instead.
|
|
1812
|
+
* small — 40×40dp, 12dp corner radius. No longer recommended in M3 Expressive.
|
|
1813
|
+
* Kept functional for backward compatibility.
|
|
1814
|
+
*/
|
|
1815
|
+
small: [
|
|
1816
|
+
"h-10 w-10",
|
|
1817
|
+
// 40dp
|
|
1818
|
+
"rounded-xl",
|
|
1819
|
+
// 12dp corner
|
|
1820
|
+
"m-1"
|
|
1821
|
+
// 4dp margin for 48×48dp minimum touch target
|
|
1541
1822
|
]
|
|
1542
1823
|
},
|
|
1543
1824
|
/**
|
|
1544
|
-
*
|
|
1825
|
+
* FAB color — controls container + on-color.
|
|
1826
|
+
* State-layer color in fabStateLayerVariants must match icon/on-color.
|
|
1545
1827
|
*/
|
|
1546
1828
|
color: {
|
|
1547
|
-
primary:
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1829
|
+
"primary-container": [
|
|
1830
|
+
"bg-primary-container text-on-primary-container",
|
|
1831
|
+
// Elevation base=3, hover=4 (state driven), disabled handled by root data-[disabled]
|
|
1832
|
+
"shadow-elevation-3",
|
|
1833
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1834
|
+
// Focus / pressed: return to elevation-3
|
|
1835
|
+
// Doubled attribute selector gives higher specificity than single hover selector
|
|
1836
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1837
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1838
|
+
],
|
|
1839
|
+
"secondary-container": [
|
|
1840
|
+
"bg-secondary-container text-on-secondary-container",
|
|
1841
|
+
"shadow-elevation-3",
|
|
1842
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1843
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1844
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1845
|
+
],
|
|
1846
|
+
"tertiary-container": [
|
|
1847
|
+
"bg-tertiary-container text-on-tertiary-container",
|
|
1848
|
+
"shadow-elevation-3",
|
|
1849
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1850
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1851
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1852
|
+
],
|
|
1853
|
+
primary: [
|
|
1854
|
+
"bg-primary text-on-primary",
|
|
1855
|
+
"shadow-elevation-3",
|
|
1856
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1857
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1858
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1859
|
+
],
|
|
1860
|
+
secondary: [
|
|
1861
|
+
"bg-secondary text-on-secondary",
|
|
1862
|
+
"shadow-elevation-3",
|
|
1863
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1864
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1865
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1866
|
+
],
|
|
1867
|
+
tertiary: [
|
|
1868
|
+
"bg-tertiary text-on-tertiary",
|
|
1869
|
+
"shadow-elevation-3",
|
|
1870
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1871
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1872
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1873
|
+
],
|
|
1874
|
+
/** @deprecated Use `primary-container` instead. */
|
|
1875
|
+
surface: [
|
|
1876
|
+
"bg-surface-container-high text-primary",
|
|
1877
|
+
"shadow-elevation-3",
|
|
1878
|
+
"group-data-[hovered]/fab:shadow-elevation-4",
|
|
1879
|
+
"group-data-[focus-visible]/fab:shadow-elevation-3",
|
|
1880
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:shadow-elevation-3"
|
|
1881
|
+
]
|
|
1558
1882
|
}
|
|
1559
1883
|
},
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
{
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
{
|
|
1624
|
-
color: "tertiary",
|
|
1625
|
-
size: "large",
|
|
1626
|
-
className: "bg-tertiary-container text-on-tertiary-container"
|
|
1627
|
-
},
|
|
1628
|
-
{
|
|
1629
|
-
color: "tertiary",
|
|
1630
|
-
size: "extended",
|
|
1631
|
-
className: "bg-tertiary-container text-on-tertiary-container"
|
|
1632
|
-
},
|
|
1633
|
-
// ====================
|
|
1634
|
-
// SURFACE COLOR
|
|
1635
|
-
// ====================
|
|
1636
|
-
{
|
|
1637
|
-
color: "surface",
|
|
1638
|
-
size: "small",
|
|
1639
|
-
className: "bg-surface text-primary"
|
|
1640
|
-
},
|
|
1641
|
-
{
|
|
1642
|
-
color: "surface",
|
|
1643
|
-
size: "medium",
|
|
1644
|
-
className: "bg-surface text-primary"
|
|
1645
|
-
},
|
|
1646
|
-
{
|
|
1647
|
-
color: "surface",
|
|
1648
|
-
size: "large",
|
|
1649
|
-
className: "bg-surface text-primary"
|
|
1884
|
+
defaultVariants: {
|
|
1885
|
+
size: "fab",
|
|
1886
|
+
color: "primary-container"
|
|
1887
|
+
}
|
|
1888
|
+
}
|
|
1889
|
+
);
|
|
1890
|
+
var fabStateLayerVariants = cva(
|
|
1891
|
+
[
|
|
1892
|
+
"absolute inset-0 rounded-[inherit] overflow-hidden pointer-events-none opacity-0",
|
|
1893
|
+
// Effects transition — opacity must not overshoot
|
|
1894
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
1895
|
+
// Hover: 8%
|
|
1896
|
+
"group-data-[hovered]/fab:opacity-8",
|
|
1897
|
+
// Focus: 10%
|
|
1898
|
+
"group-data-[focus-visible]/fab:opacity-10",
|
|
1899
|
+
// Pressed: 10% — doubled selector wins over hover's 8%
|
|
1900
|
+
"group-data-[pressed]/fab:group-data-[pressed]/fab:opacity-10",
|
|
1901
|
+
// No state layer when disabled
|
|
1902
|
+
"group-data-[disabled]/fab:hidden"
|
|
1903
|
+
],
|
|
1904
|
+
{
|
|
1905
|
+
variants: {
|
|
1906
|
+
color: {
|
|
1907
|
+
"primary-container": "bg-on-primary-container",
|
|
1908
|
+
"secondary-container": "bg-on-secondary-container",
|
|
1909
|
+
"tertiary-container": "bg-on-tertiary-container",
|
|
1910
|
+
primary: "bg-on-primary",
|
|
1911
|
+
secondary: "bg-on-secondary",
|
|
1912
|
+
tertiary: "bg-on-tertiary",
|
|
1913
|
+
/** @deprecated */
|
|
1914
|
+
surface: "bg-primary"
|
|
1915
|
+
}
|
|
1916
|
+
},
|
|
1917
|
+
defaultVariants: { color: "primary-container" }
|
|
1918
|
+
}
|
|
1919
|
+
);
|
|
1920
|
+
var fabFocusRingVariants = cva([
|
|
1921
|
+
"pointer-events-none absolute inset-[-3px] rounded-[inherit]",
|
|
1922
|
+
"outline outline-2 outline-offset-0 outline-secondary",
|
|
1923
|
+
// Effects transition — opacity change must not overshoot
|
|
1924
|
+
"transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
|
|
1925
|
+
"opacity-0",
|
|
1926
|
+
"group-data-[focus-visible]/fab:opacity-100"
|
|
1927
|
+
]);
|
|
1928
|
+
var fabIconVariants = cva(
|
|
1929
|
+
[
|
|
1930
|
+
"relative z-10 inline-flex shrink-0 items-center justify-center",
|
|
1931
|
+
// Color transition uses effects token (no spatial overshoot on color)
|
|
1932
|
+
"transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects"
|
|
1933
|
+
],
|
|
1934
|
+
{
|
|
1935
|
+
variants: {
|
|
1936
|
+
size: {
|
|
1937
|
+
fab: "size-6",
|
|
1938
|
+
// 24dp
|
|
1939
|
+
medium: "size-7",
|
|
1940
|
+
// 28dp
|
|
1941
|
+
large: "size-9",
|
|
1942
|
+
// 36dp
|
|
1943
|
+
extended: "size-6",
|
|
1944
|
+
// 24dp
|
|
1945
|
+
small: "size-6"
|
|
1946
|
+
// 24dp
|
|
1650
1947
|
},
|
|
1651
|
-
{
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
className: "bg-surface text-primary"
|
|
1948
|
+
hidden: {
|
|
1949
|
+
true: "invisible",
|
|
1950
|
+
false: ""
|
|
1655
1951
|
}
|
|
1656
|
-
|
|
1657
|
-
/**
|
|
1658
|
-
* Default variants
|
|
1659
|
-
*/
|
|
1952
|
+
},
|
|
1660
1953
|
defaultVariants: {
|
|
1661
|
-
size: "
|
|
1662
|
-
|
|
1663
|
-
isDisabled: false
|
|
1954
|
+
size: "fab",
|
|
1955
|
+
hidden: false
|
|
1664
1956
|
}
|
|
1665
1957
|
}
|
|
1666
1958
|
);
|
|
1959
|
+
var fabLabelVariants = cva([
|
|
1960
|
+
"relative z-10 inline-flex items-center",
|
|
1961
|
+
"text-label-large tracking-[0.1px]"
|
|
1962
|
+
]);
|
|
1667
1963
|
var Spinner2 = () => /* @__PURE__ */ jsxs(
|
|
1668
1964
|
"svg",
|
|
1669
1965
|
{
|
|
1670
1966
|
role: "progressbar",
|
|
1671
1967
|
"aria-label": "Loading",
|
|
1672
|
-
className: "
|
|
1968
|
+
className: "relative z-10 animate-spin",
|
|
1673
1969
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1674
1970
|
fill: "none",
|
|
1675
1971
|
viewBox: "0 0 24 24",
|
|
1972
|
+
width: "1em",
|
|
1973
|
+
height: "1em",
|
|
1974
|
+
style: { fontSize: "inherit" },
|
|
1676
1975
|
children: [
|
|
1677
1976
|
/* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
1678
1977
|
/* @__PURE__ */ jsx(
|
|
@@ -1688,80 +1987,98 @@ var Spinner2 = () => /* @__PURE__ */ jsxs(
|
|
|
1688
1987
|
);
|
|
1689
1988
|
var FAB = forwardRef(
|
|
1690
1989
|
({
|
|
1691
|
-
// Variant props
|
|
1692
|
-
size = "
|
|
1693
|
-
color = "primary",
|
|
1694
|
-
//
|
|
1990
|
+
// Variant props
|
|
1991
|
+
size = "fab",
|
|
1992
|
+
color = "primary-container",
|
|
1993
|
+
// Content
|
|
1695
1994
|
icon,
|
|
1696
1995
|
children,
|
|
1697
|
-
|
|
1996
|
+
// State
|
|
1698
1997
|
loading = false,
|
|
1699
1998
|
disableRipple = false,
|
|
1999
|
+
isDisabled = false,
|
|
2000
|
+
// Styling
|
|
1700
2001
|
className,
|
|
1701
|
-
//
|
|
1702
|
-
|
|
1703
|
-
onPress,
|
|
1704
|
-
onMouseDown,
|
|
2002
|
+
// Accessibility
|
|
2003
|
+
"aria-label": ariaLabel,
|
|
1705
2004
|
title,
|
|
2005
|
+
// Passthrough — forwarded to FABHeadless
|
|
2006
|
+
tabIndex = 0,
|
|
2007
|
+
type = "button",
|
|
2008
|
+
// Passed through to FABHeadless → useButton
|
|
1706
2009
|
...props
|
|
1707
2010
|
}, ref) => {
|
|
2011
|
+
const fabRef = useRef(null);
|
|
2012
|
+
const resolvedRef = ref ?? fabRef;
|
|
2013
|
+
const isFABDisabled = isDisabled || loading;
|
|
2014
|
+
const [isPressed, setIsPressed] = useState(false);
|
|
2015
|
+
const handlePressStart = useCallback(() => setIsPressed(true), []);
|
|
2016
|
+
const handlePressEnd = useCallback(() => setIsPressed(false), []);
|
|
2017
|
+
const { isHovered, hoverProps } = useHover({ isDisabled: isFABDisabled });
|
|
2018
|
+
const { isFocusVisible, focusProps } = useFocusRing();
|
|
2019
|
+
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
2020
|
+
disabled: isFABDisabled || disableRipple
|
|
2021
|
+
});
|
|
1708
2022
|
if (process.env.NODE_ENV === "development") {
|
|
1709
2023
|
if (!icon) {
|
|
1710
|
-
console.warn("[FAB] FAB must have an icon. Please provide the icon prop.");
|
|
2024
|
+
console.warn("[FAB] FAB must have an icon. Please provide the `icon` prop.");
|
|
1711
2025
|
}
|
|
1712
2026
|
if (size === "extended" && !children) {
|
|
1713
|
-
console.warn("[FAB] Extended FAB requires text label as children
|
|
2027
|
+
console.warn("[FAB] Extended FAB requires a text label as `children`.");
|
|
1714
2028
|
}
|
|
1715
2029
|
if (size !== "extended" && children) {
|
|
1716
2030
|
console.warn(
|
|
1717
|
-
"[FAB]
|
|
2031
|
+
"[FAB] `children` (text label) is only rendered for `size='extended'`. For icon-only FABs, use the `icon` prop only."
|
|
2032
|
+
);
|
|
2033
|
+
}
|
|
2034
|
+
if (size === "small") {
|
|
2035
|
+
console.warn(
|
|
2036
|
+
"[FAB] `size='small'` is deprecated in M3 Expressive. Use `size='fab'` (56dp) instead."
|
|
2037
|
+
);
|
|
2038
|
+
}
|
|
2039
|
+
if (color === "surface") {
|
|
2040
|
+
console.warn(
|
|
2041
|
+
"[FAB] `color='surface'` is deprecated in M3 Expressive. Use `color='primary-container'` instead."
|
|
1718
2042
|
);
|
|
1719
2043
|
}
|
|
1720
2044
|
}
|
|
1721
|
-
const isDisabled = propIsDisabled || loading;
|
|
1722
|
-
const { onMouseDown: handleRipple, ripples } = useRipple({
|
|
1723
|
-
disabled: isDisabled || disableRipple
|
|
1724
|
-
});
|
|
1725
|
-
const mergedOnMouseDown = (e) => {
|
|
1726
|
-
onMouseDown?.(e);
|
|
1727
|
-
handleRipple(e);
|
|
1728
|
-
};
|
|
1729
|
-
const mergedPropsValue = mergeProps(props, {
|
|
1730
|
-
...onPress && { onPress },
|
|
1731
|
-
onMouseDown: mergedOnMouseDown,
|
|
1732
|
-
isDisabled
|
|
1733
|
-
});
|
|
1734
2045
|
return /* @__PURE__ */ jsxs(
|
|
1735
2046
|
FABHeadless,
|
|
1736
2047
|
{
|
|
1737
|
-
|
|
2048
|
+
...mergeProps$1(
|
|
2049
|
+
hoverProps,
|
|
2050
|
+
focusProps,
|
|
2051
|
+
{ onPressStart: handlePressStart, onPressEnd: handlePressEnd },
|
|
2052
|
+
props
|
|
2053
|
+
),
|
|
2054
|
+
ref: resolvedRef,
|
|
2055
|
+
type,
|
|
2056
|
+
isDisabled: isFABDisabled,
|
|
2057
|
+
tabIndex,
|
|
2058
|
+
onMouseDown: handleRipple,
|
|
2059
|
+
"aria-label": ariaLabel,
|
|
2060
|
+
...title !== void 0 && { title },
|
|
2061
|
+
...getInteractionDataAttributes({
|
|
2062
|
+
isHovered,
|
|
2063
|
+
isFocusVisible,
|
|
2064
|
+
isPressed,
|
|
2065
|
+
isDisabled: isFABDisabled
|
|
2066
|
+
}),
|
|
2067
|
+
"data-with-icon": icon ? "" : void 0,
|
|
2068
|
+
"data-loading": loading ? "" : void 0,
|
|
1738
2069
|
className: cn(
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
"
|
|
1742
|
-
"focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
1743
|
-
"shrink-0",
|
|
1744
|
-
// State layers (hover, focus, active)
|
|
1745
|
-
"before:absolute before:inset-0 before:rounded-[inherit] before:transition-opacity before:duration-200",
|
|
1746
|
-
"before:bg-current before:opacity-0",
|
|
1747
|
-
"hover:before:opacity-8",
|
|
1748
|
-
"focus-visible:before:opacity-12",
|
|
1749
|
-
"active:before:opacity-12",
|
|
1750
|
-
// Elevation
|
|
1751
|
-
"shadow-elevation-3 hover:shadow-elevation-4",
|
|
1752
|
-
// CVA variants
|
|
1753
|
-
fabVariants({ size, color, isDisabled }),
|
|
1754
|
-
// User custom classes
|
|
2070
|
+
fabVariants({ size, color }),
|
|
2071
|
+
// group/fab: enables group-data-[x]/fab child selectors in all slots
|
|
2072
|
+
"group/fab",
|
|
1755
2073
|
className
|
|
1756
2074
|
),
|
|
1757
|
-
"aria-label": ariaLabel,
|
|
1758
|
-
...title && { title },
|
|
1759
|
-
...mergedPropsValue,
|
|
1760
2075
|
children: [
|
|
1761
2076
|
ripples,
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
2077
|
+
/* @__PURE__ */ jsx("span", { className: cn(fabStateLayerVariants({ color })), "aria-hidden": "true" }),
|
|
2078
|
+
/* @__PURE__ */ jsx("span", { className: cn(fabFocusRingVariants()), "aria-hidden": "true" }),
|
|
2079
|
+
icon && /* @__PURE__ */ jsx("span", { className: cn(fabIconVariants({ size, hidden: loading })), children: icon }),
|
|
2080
|
+
loading && /* @__PURE__ */ jsx("span", { className: cn(fabIconVariants({ size })), children: /* @__PURE__ */ jsx(Spinner2, {}) }),
|
|
2081
|
+
size === "extended" && children && /* @__PURE__ */ jsx("span", { className: cn(fabLabelVariants()), children })
|
|
1765
2082
|
]
|
|
1766
2083
|
}
|
|
1767
2084
|
);
|