@tinybigui/react 0.8.1 → 0.10.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.js CHANGED
@@ -206,65 +206,111 @@ function useScrollElevation(options = {}) {
206
206
  };
207
207
  }
208
208
  var AppBarHeadless = forwardRef(
209
- ({ className, children, scrolled: scrolledProp, onScrollStateChange }, ref) => {
209
+ ({ className, children, scrolled: scrolledProp, onScrollStateChange, ...htmlProps }, ref) => {
210
210
  const { isScrolled } = useScrollElevation({
211
211
  scrolled: scrolledProp,
212
212
  onScrollStateChange
213
213
  });
214
- return /* @__PURE__ */ jsx("header", { ref, role: "banner", className, "data-scrolled": isScrolled, children });
214
+ return /* @__PURE__ */ jsx(
215
+ "header",
216
+ {
217
+ ...htmlProps,
218
+ ref,
219
+ role: "banner",
220
+ className,
221
+ "data-scrolled": isScrolled ? "" : void 0,
222
+ children
223
+ }
224
+ );
215
225
  }
216
226
  );
217
227
  AppBarHeadless.displayName = "AppBarHeadless";
218
228
  var appBarVariants = cva(
219
229
  [
220
- // Base classes (always applied)
221
- "w-full",
230
+ // Layout
231
+ "w-full flex flex-col",
232
+ // Color (base — at rest)
222
233
  "bg-surface text-on-surface",
223
- "flex flex-col",
224
- // Elevation transition using MD3 motion tokens
225
- "transition-shadow duration-medium2 ease-standard"
234
+ // Elevation base
235
+ "shadow-elevation-0",
236
+ // Scroll state — effects properties animated with standard spring (no overshoot)
237
+ "transition-[background-color,box-shadow]",
238
+ "duration-spring-standard-default-effects",
239
+ "ease-spring-standard-default-effects",
240
+ // On scroll: surface-container background + elevation-2
241
+ "group-data-[scrolled]/appbar:bg-surface-container",
242
+ "group-data-[scrolled]/appbar:shadow-elevation-2"
226
243
  ],
227
244
  {
228
245
  variants: {
229
246
  /**
230
- * Size variant (MD3 specification)
231
- * Controls bar height, title placement, and type scale
247
+ * Size variant controls bar height and which title row is shown.
248
+ * With-subtitle height growth is handled via group-data-[with-subtitle]/appbar below.
232
249
  */
233
250
  variant: {
234
- /** 64dp, title left-aligned, title-large */
251
+ /** 64dp fixed — title left-aligned in top row */
235
252
  small: "h-appbar-small",
236
- /** 64dp, title centered, title-large */
237
- "center-aligned": "h-appbar-small variant-center-aligned",
238
- /** min 112dp, title bottom-left, headline-medium (grows with subtitle) */
239
- medium: "min-h-appbar-medium",
240
- /** min 120dp, title bottom-left, display-small (grows with subtitle) */
241
- large: "min-h-appbar-large"
242
- },
243
- /**
244
- * Scroll state — controls surface elevation and background color
245
- * MD3: flat surface at rest → surface-container background + elevation-2 on scroll
246
- */
247
- scrolled: {
248
- false: "shadow-elevation-0",
249
- true: "bg-surface-container shadow-elevation-2"
253
+ /** 64dp fixed — title centered in top row */
254
+ "center-aligned": "h-appbar-small",
255
+ /**
256
+ * 112dp no-subtitle / 136dp with-subtitle — title in expanded bottom row.
257
+ * group-data-[with-subtitle]/appbar switches to the taller token.
258
+ */
259
+ medium: [
260
+ "min-h-appbar-medium",
261
+ "group-data-[with-subtitle]/appbar:min-h-appbar-medium-subtitle"
262
+ ],
263
+ /**
264
+ * 120dp no-subtitle / 152dp with-subtitle — title in expanded bottom row.
265
+ * group-data-[with-subtitle]/appbar switches to the taller token.
266
+ */
267
+ large: [
268
+ "min-h-appbar-large",
269
+ "group-data-[with-subtitle]/appbar:min-h-appbar-large-subtitle"
270
+ ]
250
271
  }
251
272
  },
252
273
  defaultVariants: {
253
- variant: "small",
254
- scrolled: false
274
+ variant: "small"
275
+ }
276
+ }
277
+ );
278
+ var appBarTopRowVariants = cva(["flex items-center justify-between", "px-1"], {
279
+ variants: {
280
+ variant: {
281
+ small: "flex-1",
282
+ "center-aligned": "flex-1",
283
+ medium: "h-16 shrink-0",
284
+ large: "h-16 shrink-0"
285
+ }
286
+ },
287
+ defaultVariants: {
288
+ variant: "small"
289
+ }
290
+ });
291
+ var appBarLeadingVariants = cva(["flex shrink-0 items-center", "text-on-surface"]);
292
+ var appBarHeadlineBlockVariants = cva(
293
+ ["flex min-w-0 flex-1 flex-col justify-center", "px-1"],
294
+ {
295
+ variants: {
296
+ variant: {
297
+ small: "",
298
+ "center-aligned": "items-center text-center",
299
+ medium: "",
300
+ large: ""
301
+ }
302
+ },
303
+ defaultVariants: {
304
+ variant: "small"
255
305
  }
256
306
  }
257
307
  );
258
308
  var appBarTitleVariants = cva("text-on-surface font-normal", {
259
309
  variants: {
260
310
  variant: {
261
- /** title-large: 22px / 28px line-height */
262
311
  small: "text-title-large truncate",
263
- /** title-large: 22px / 28px line-height, centered */
264
312
  "center-aligned": "text-title-large truncate",
265
- /** headline-medium: 28px / 36px line-height */
266
313
  medium: "text-headline-medium",
267
- /** display-small: 36px / 44px line-height */
268
314
  large: "text-display-small"
269
315
  }
270
316
  },
@@ -272,23 +318,27 @@ var appBarTitleVariants = cva("text-on-surface font-normal", {
272
318
  variant: "small"
273
319
  }
274
320
  });
275
- var appBarSubtitleVariants = cva("font-normal", {
321
+ var appBarSubtitleVariants = cva("text-on-surface-variant font-normal", {
276
322
  variants: {
277
323
  variant: {
278
- /** title-medium: 16px / 24px, on-surface-variant color */
279
- small: "text-title-medium text-on-surface-variant truncate",
280
- /** title-medium: 16px / 24px, centered, on-surface-variant color */
281
- "center-aligned": "text-title-medium text-on-surface-variant truncate",
282
- /** title-large: 22px / 28px, on-surface color */
283
- medium: "text-title-large text-on-surface",
284
- /** headline-small: 24px / 32px, on-surface color */
285
- large: "text-headline-small text-on-surface"
324
+ small: "text-label-medium truncate",
325
+ "center-aligned": "text-label-medium truncate",
326
+ medium: "text-label-large",
327
+ large: "text-title-medium"
286
328
  }
287
329
  },
288
330
  defaultVariants: {
289
331
  variant: "small"
290
332
  }
291
333
  });
334
+ var appBarTrailingVariants = cva([
335
+ "flex shrink-0 items-center gap-0.5",
336
+ "text-on-surface-variant"
337
+ ]);
338
+ var appBarExpandedTitleVariants = cva([
339
+ "flex flex-1 flex-col justify-end",
340
+ "gap-0.5 px-4 pb-4"
341
+ ]);
292
342
  var AppBar = forwardRef(
293
343
  ({
294
344
  variant = "small",
@@ -305,76 +355,54 @@ var AppBar = forwardRef(
305
355
  onScrollStateChange
306
356
  });
307
357
  const isExpandedVariant = variant === "medium" || variant === "large";
358
+ const hasSubtitle = subtitle != null;
308
359
  return /* @__PURE__ */ jsxs(
309
360
  AppBarHeadless,
310
361
  {
311
362
  ref,
312
363
  scrolled: isScrolled,
313
- className: cn(appBarVariants({ variant, scrolled: isScrolled }), className),
364
+ className: cn(
365
+ appBarVariants({ variant }),
366
+ // group/appbar: enables group-data-[scrolled]/appbar and
367
+ // group-data-[with-subtitle]/appbar child selectors in all slots
368
+ "group/appbar",
369
+ className
370
+ ),
371
+ "data-with-subtitle": hasSubtitle ? "" : void 0,
314
372
  children: [
315
- /* @__PURE__ */ jsxs(
316
- "div",
317
- {
318
- "data-slot": "top-row",
319
- className: cn(
320
- "flex items-center justify-between",
321
- "px-1",
322
- // Small and center-aligned: fill the full bar height
323
- !isExpandedVariant && "flex-1",
324
- // Expanded variants: fixed height for the top row (64dp)
325
- isExpandedVariant && "h-16 shrink-0"
326
- ),
327
- children: [
328
- navigationIcon != null && /* @__PURE__ */ jsx("div", { "data-slot": "navigation", className: "flex shrink-0 items-center", children: navigationIcon }),
329
- !isExpandedVariant && /* @__PURE__ */ jsxs(
330
- "div",
331
- {
332
- className: cn(
333
- "flex min-w-0 flex-1 flex-col justify-center px-1",
334
- variant === "center-aligned" && "text-center"
335
- ),
336
- children: [
337
- /* @__PURE__ */ jsx("span", { "data-testid": "appbar-title", className: cn(appBarTitleVariants({ variant })), children: title }),
338
- subtitle != null && /* @__PURE__ */ jsx(
339
- "span",
340
- {
341
- "data-testid": "appbar-subtitle",
342
- className: cn(appBarSubtitleVariants({ variant })),
343
- children: subtitle
344
- }
345
- )
346
- ]
347
- }
348
- ),
349
- actions != null && /* @__PURE__ */ jsx("div", { "data-slot": "actions", className: "flex shrink-0 items-center gap-0.5", children: actions })
350
- ]
351
- }
352
- ),
353
- isExpandedVariant && /* @__PURE__ */ jsxs(
354
- "div",
355
- {
356
- "data-slot": "expanded-title",
357
- className: cn("flex flex-1 flex-col justify-end", "gap-0.5 px-4 pb-4"),
358
- children: [
359
- /* @__PURE__ */ jsx(
360
- "span",
361
- {
362
- "data-testid": "appbar-title",
363
- className: cn("min-w-0", appBarTitleVariants({ variant })),
364
- children: title
365
- }
366
- ),
367
- subtitle != null && /* @__PURE__ */ jsx(
368
- "span",
369
- {
370
- "data-testid": "appbar-subtitle",
371
- className: cn("min-w-0", appBarSubtitleVariants({ variant })),
372
- children: subtitle
373
- }
374
- )
375
- ]
376
- }
377
- )
373
+ /* @__PURE__ */ jsxs("div", { "data-slot": "top-row", className: cn(appBarTopRowVariants({ variant })), children: [
374
+ navigationIcon != null && /* @__PURE__ */ jsx("div", { "data-slot": "navigation", className: cn(appBarLeadingVariants()), children: navigationIcon }),
375
+ !isExpandedVariant && /* @__PURE__ */ jsxs("div", { className: cn(appBarHeadlineBlockVariants({ variant })), children: [
376
+ /* @__PURE__ */ jsx("span", { "data-testid": "appbar-title", className: cn(appBarTitleVariants({ variant })), children: title }),
377
+ hasSubtitle && /* @__PURE__ */ jsx(
378
+ "span",
379
+ {
380
+ "data-testid": "appbar-subtitle",
381
+ className: cn(appBarSubtitleVariants({ variant })),
382
+ children: subtitle
383
+ }
384
+ )
385
+ ] }),
386
+ actions != null && /* @__PURE__ */ jsx("div", { "data-slot": "actions", className: cn(appBarTrailingVariants()), children: actions })
387
+ ] }),
388
+ isExpandedVariant && /* @__PURE__ */ jsxs("div", { "data-slot": "expanded-title", className: cn(appBarExpandedTitleVariants()), children: [
389
+ /* @__PURE__ */ jsx(
390
+ "span",
391
+ {
392
+ "data-testid": "appbar-title",
393
+ className: cn("min-w-0", appBarTitleVariants({ variant })),
394
+ children: title
395
+ }
396
+ ),
397
+ hasSubtitle && /* @__PURE__ */ jsx(
398
+ "span",
399
+ {
400
+ "data-testid": "appbar-subtitle",
401
+ className: cn("min-w-0", appBarSubtitleVariants({ variant })),
402
+ children: subtitle
403
+ }
404
+ )
405
+ ] })
378
406
  ]
379
407
  }
380
408
  );
@@ -6463,18 +6491,43 @@ function SnackbarProvider({ children, maxVisible = 5 }) {
6463
6491
  )
6464
6492
  ] });
6465
6493
  }
6494
+ function stripAriaProps(handlers) {
6495
+ const {
6496
+ isDisabled: _isDisabled,
6497
+ onPress: _onPress,
6498
+ onPressStart: _onPressStart,
6499
+ onPressEnd: _onPressEnd,
6500
+ onPressChange: _onPressChange,
6501
+ onPressUp: _onPressUp,
6502
+ ...html
6503
+ } = handlers;
6504
+ return html;
6505
+ }
6506
+ function buildPressCallbacks(onPressStart, onPressEnd) {
6507
+ return {
6508
+ ...onPressStart !== void 0 && { onPressStart },
6509
+ ...onPressEnd !== void 0 && { onPressEnd }
6510
+ };
6511
+ }
6466
6512
  var AssistChipImpl = forwardRef(
6467
- ({ label, onPress, isDisabled, className, onMouseDown, children }, forwardedRef) => {
6513
+ ({ label, onPress, isDisabled, className, onMouseDown, children, bodyPassthrough }, forwardedRef) => {
6468
6514
  const internalRef = useRef(null);
6469
6515
  const ref = forwardedRef ?? internalRef;
6470
6516
  const { buttonProps } = useButton(
6471
6517
  {
6472
6518
  ...onPress !== void 0 && { onPress },
6473
- ...isDisabled !== void 0 && { isDisabled }
6519
+ ...isDisabled !== void 0 && { isDisabled },
6520
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6474
6521
  },
6475
6522
  ref
6476
6523
  );
6477
- const mergedProps = mergeProps(buttonProps, { onMouseDown });
6524
+ const htmlPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6525
+ const mergedProps = mergeProps(
6526
+ buttonProps,
6527
+ { onMouseDown },
6528
+ bodyPassthrough?.dataAttributes ?? {},
6529
+ htmlPassthrough
6530
+ );
6478
6531
  return /* @__PURE__ */ jsx("button", { ...mergedProps, type: "button", ref, className, children: children ?? label });
6479
6532
  }
6480
6533
  );
@@ -6488,7 +6541,8 @@ var FilterChipImpl = forwardRef(
6488
6541
  isDisabled,
6489
6542
  className,
6490
6543
  onMouseDown,
6491
- children
6544
+ children,
6545
+ bodyPassthrough
6492
6546
  }, forwardedRef) => {
6493
6547
  const internalRef = useRef(null);
6494
6548
  const ref = forwardedRef ?? internalRef;
@@ -6496,11 +6550,18 @@ var FilterChipImpl = forwardRef(
6496
6550
  ...selected !== void 0 && { isSelected: selected },
6497
6551
  ...defaultSelected !== void 0 && { defaultSelected },
6498
6552
  ...onSelectionChange !== void 0 && { onChange: onSelectionChange },
6499
- ...isDisabled !== void 0 && { isDisabled }
6553
+ ...isDisabled !== void 0 && { isDisabled },
6554
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6500
6555
  };
6501
6556
  const state = useToggleState(toggleProps);
6502
6557
  const { buttonProps } = useToggleButton(toggleProps, state, ref);
6503
- const mergedProps = mergeProps(buttonProps, { onMouseDown });
6558
+ const htmlPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6559
+ const mergedProps = mergeProps(
6560
+ buttonProps,
6561
+ { onMouseDown },
6562
+ bodyPassthrough?.dataAttributes ?? {},
6563
+ htmlPassthrough
6564
+ );
6504
6565
  return /* @__PURE__ */ jsx("button", { ...mergedProps, type: "button", ref, className, children: children ?? label });
6505
6566
  }
6506
6567
  );
@@ -6514,7 +6575,9 @@ var InputChipImpl = forwardRef(
6514
6575
  onMouseDown,
6515
6576
  removeIcon,
6516
6577
  removeButtonClassName,
6517
- children
6578
+ children,
6579
+ bodyPassthrough,
6580
+ removePassthrough
6518
6581
  }, forwardedRef) => {
6519
6582
  const chipRef = useRef(null);
6520
6583
  const ref = forwardedRef ?? chipRef;
@@ -6522,7 +6585,8 @@ var InputChipImpl = forwardRef(
6522
6585
  const { buttonProps: chipButtonProps } = useButton(
6523
6586
  {
6524
6587
  "aria-label": label,
6525
- ...isDisabled !== void 0 && { isDisabled }
6588
+ ...isDisabled !== void 0 && { isDisabled },
6589
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6526
6590
  },
6527
6591
  ref
6528
6592
  );
@@ -6530,7 +6594,8 @@ var InputChipImpl = forwardRef(
6530
6594
  {
6531
6595
  "aria-label": `Remove ${label}`,
6532
6596
  onPress: () => onRemove?.(),
6533
- ...isDisabled !== void 0 && { isDisabled }
6597
+ ...isDisabled !== void 0 && { isDisabled },
6598
+ ...buildPressCallbacks(removePassthrough?.onPressStart, removePassthrough?.onPressEnd)
6534
6599
  },
6535
6600
  removeRef
6536
6601
  );
@@ -6540,13 +6605,25 @@ var InputChipImpl = forwardRef(
6540
6605
  onRemove?.();
6541
6606
  }
6542
6607
  };
6543
- const mergedChipProps = mergeProps(chipButtonProps, { onKeyDown: handleKeyDown, onMouseDown });
6608
+ const htmlBodyPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6609
+ const htmlRemovePassthrough = stripAriaProps(removePassthrough?.eventHandlers ?? {});
6610
+ const mergedChipProps = mergeProps(
6611
+ chipButtonProps,
6612
+ { onKeyDown: handleKeyDown, onMouseDown },
6613
+ bodyPassthrough?.dataAttributes ?? {},
6614
+ htmlBodyPassthrough
6615
+ );
6616
+ const mergedRemoveProps = mergeProps(
6617
+ removeButtonProps,
6618
+ removePassthrough?.dataAttributes ?? {},
6619
+ htmlRemovePassthrough
6620
+ );
6544
6621
  return /* @__PURE__ */ jsxs("span", { className, children: [
6545
6622
  /* @__PURE__ */ jsx("button", { ...mergedChipProps, type: "button", ref, children: children ?? label }),
6546
6623
  /* @__PURE__ */ jsx(
6547
6624
  "button",
6548
6625
  {
6549
- ...removeButtonProps,
6626
+ ...mergedRemoveProps,
6550
6627
  type: "button",
6551
6628
  ref: removeRef,
6552
6629
  className: removeButtonClassName,
@@ -6558,17 +6635,24 @@ var InputChipImpl = forwardRef(
6558
6635
  );
6559
6636
  InputChipImpl.displayName = "InputChipImpl";
6560
6637
  var SuggestionChipImpl = forwardRef(
6561
- ({ label, onPress, isDisabled, className, onMouseDown, children }, forwardedRef) => {
6638
+ ({ label, onPress, isDisabled, className, onMouseDown, children, bodyPassthrough }, forwardedRef) => {
6562
6639
  const internalRef = useRef(null);
6563
6640
  const ref = forwardedRef ?? internalRef;
6564
6641
  const { buttonProps } = useButton(
6565
6642
  {
6566
6643
  ...onPress !== void 0 && { onPress },
6567
- ...isDisabled !== void 0 && { isDisabled }
6644
+ ...isDisabled !== void 0 && { isDisabled },
6645
+ ...buildPressCallbacks(bodyPassthrough?.onPressStart, bodyPassthrough?.onPressEnd)
6568
6646
  },
6569
6647
  ref
6570
6648
  );
6571
- const mergedProps = mergeProps(buttonProps, { onMouseDown });
6649
+ const htmlPassthrough = stripAriaProps(bodyPassthrough?.eventHandlers ?? {});
6650
+ const mergedProps = mergeProps(
6651
+ buttonProps,
6652
+ { onMouseDown },
6653
+ bodyPassthrough?.dataAttributes ?? {},
6654
+ htmlPassthrough
6655
+ );
6572
6656
  return /* @__PURE__ */ jsx("button", { ...mergedProps, type: "button", ref, className, children: children ?? label });
6573
6657
  }
6574
6658
  );
@@ -6588,123 +6672,246 @@ var ChipHeadless = forwardRef((props, ref) => {
6588
6672
  ChipHeadless.displayName = "ChipHeadless";
6589
6673
  var chipVariants = cva(
6590
6674
  [
6591
- // Base layout — always applied
6592
- "relative inline-flex items-center overflow-hidden rounded-sm h-8",
6593
- "text-label-large cursor-pointer gap-1 group px-4",
6594
- // Focus ring
6595
- "focus-visible:outline-primary focus-visible:outline-2 focus-visible:outline-offset-2"
6675
+ // Layout + shape
6676
+ "relative inline-flex items-center justify-center",
6677
+ "h-8 rounded-sm cursor-pointer select-none",
6678
+ "text-label-large gap-2",
6679
+ // Base padding (no icons)
6680
+ "px-4",
6681
+ // Content-flag padding overrides (self-targeting)
6682
+ "data-[with-leading]:pl-2 data-[with-leading]:pr-4",
6683
+ "data-[with-trailing]:pr-2",
6684
+ // Effects transition (color/bg/shadow/border)
6685
+ "transition-[background-color,border-color,box-shadow,color]",
6686
+ "duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6687
+ // Disabled self-targeting
6688
+ "data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none",
6689
+ "data-[disabled]:text-on-surface/38 data-[disabled]:border-on-surface/12",
6690
+ "data-[disabled]:bg-transparent data-[disabled]:shadow-none"
6596
6691
  ],
6597
6692
  {
6598
6693
  variants: {
6599
6694
  /**
6600
- * MD3 chip type — determines interaction model and default styling.
6695
+ * MD3 chip type — determines base color tokens.
6601
6696
  */
6602
6697
  chipType: {
6603
- assist: "",
6604
- // Filter and Input chips have a fixed tonal surface style.
6605
- // The transition is on the base class so deselection also animates
6606
- // (adding it only in the selected compound variant would mean the
6607
- // transition property disappears at the same moment the color reverts,
6608
- // causing an instant jump back to the unselected color).
6609
- filter: "bg-surface-container-low text-on-surface border border-outline transition-[background-color,color] duration-short4 ease-standard",
6610
- input: "bg-surface-container-low text-on-surface border border-outline",
6611
- suggestion: ""
6698
+ /**
6699
+ * Assist transparent + outline border; label on-surface; leading icon primary.
6700
+ * State layer color: on-surface.
6701
+ */
6702
+ assist: ["bg-transparent border border-outline text-on-surface"],
6703
+ /**
6704
+ * Filter transparent + outline border; label on-surface-variant.
6705
+ * Selected state via group-data selectors (no CVA variant key).
6706
+ * State layer color: on-surface-variant → on-secondary-container when selected.
6707
+ */
6708
+ filter: [
6709
+ "bg-transparent border border-outline text-on-surface-variant",
6710
+ // Selected: secondary-container fill, no border, on-secondary-container label
6711
+ "group-data-[selected]/chip:bg-secondary-container",
6712
+ "group-data-[selected]/chip:border-0",
6713
+ "group-data-[selected]/chip:text-on-secondary-container",
6714
+ // Disabled + selected overrides (self-targeting, doubled to win over singly-chained selected)
6715
+ "data-[selected]:data-[disabled]:bg-on-surface/12",
6716
+ "data-[selected]:data-[disabled]:border-0"
6717
+ ],
6718
+ /**
6719
+ * Input — transparent + outline-variant border; label/icons on-surface-variant.
6720
+ * State layer color: on-surface-variant.
6721
+ */
6722
+ input: ["bg-transparent border border-outline-variant text-on-surface-variant"],
6723
+ /**
6724
+ * Suggestion — transparent + outline border; label on-surface-variant.
6725
+ * State layer color: on-surface-variant.
6726
+ */
6727
+ suggestion: ["bg-transparent border border-outline text-on-surface-variant"]
6612
6728
  },
6613
6729
  /**
6614
- * Surface style for Assist and Suggestion chips only.
6615
- * Applied via compound variants so it has no effect on Filter/Input.
6730
+ * Surface style elevated adds shadow and surface-container-low background.
6731
+ * All four chip types support elevated.
6732
+ * Note: "flat" and the deprecated "tonal" both resolve to the same base style
6733
+ * (transparent + border from chipType). The elevated compound variant overrides.
6616
6734
  */
6617
6735
  surface: {
6618
- tonal: "",
6619
- elevated: ""
6620
- },
6621
- /**
6622
- * Selected state — only meaningful for Filter chips.
6623
- * Applied via compound variant.
6624
- */
6625
- selected: {
6626
- true: "",
6627
- false: ""
6628
- },
6629
- /**
6630
- * MD3 disabled state: content 38% opacity, border 12% opacity, no background.
6631
- * Kept here only for `pointer-events-none`; color/bg overrides live in the
6632
- * disabled compound variants at the bottom so they win over surface compounds.
6633
- */
6634
- isDisabled: {
6635
- true: "pointer-events-none",
6636
- false: ""
6637
- },
6638
- /**
6639
- * Adjusts leading padding when a leading icon is present.
6640
- * Overrides the base `px-4` → `pl-3 pr-4`.
6641
- */
6642
- hasLeadingIcon: {
6643
- true: "pl-3 pr-4",
6644
- false: ""
6645
- },
6646
- /**
6647
- * Adjusts trailing padding for Input chips with a remove button.
6648
- * Takes precedence over hasLeadingIcon via tailwind-merge: `pl-3 pr-3`.
6649
- */
6650
- hasRemoveButton: {
6651
- true: "pl-3 pr-3",
6652
- false: ""
6736
+ flat: "",
6737
+ elevated: "",
6738
+ /** @deprecated Use `flat` instead. */
6739
+ tonal: ""
6653
6740
  }
6654
6741
  },
6655
6742
  compoundVariants: [
6656
- // ── Assist chip surfaces ───────────────────────────────────────────────
6743
+ // ── Elevated surface (all chip types) ─────────────────────────────────
6744
+ // Shared elevated base: surface-container-low fill, no border, level-1 elevation
6657
6745
  {
6746
+ surface: "elevated",
6658
6747
  chipType: "assist",
6659
- surface: "tonal",
6660
- className: "bg-surface-container-low text-on-surface border border-outline"
6748
+ className: [
6749
+ "bg-surface-container-low border-0 shadow-elevation-1",
6750
+ "data-[hovered]:shadow-elevation-2",
6751
+ "data-[focus-visible]:shadow-elevation-1",
6752
+ "data-[pressed]:data-[pressed]:shadow-elevation-1"
6753
+ ]
6661
6754
  },
6662
6755
  {
6663
- chipType: "assist",
6664
6756
  surface: "elevated",
6757
+ chipType: "filter",
6665
6758
  className: [
6666
- "bg-surface-container-low text-on-surface shadow-elevation-1",
6667
- "hover:shadow-elevation-2 transition-shadow duration-short2 ease-standard"
6759
+ "bg-surface-container-low border-0 shadow-elevation-1",
6760
+ "data-[hovered]:shadow-elevation-2",
6761
+ "data-[focus-visible]:shadow-elevation-1",
6762
+ "data-[pressed]:data-[pressed]:shadow-elevation-1",
6763
+ // Selected overrides bg; elevation stays
6764
+ "group-data-[selected]/chip:bg-secondary-container"
6668
6765
  ]
6669
6766
  },
6670
- // ── Suggestion chip surfaces ───────────────────────────────────────────
6671
6767
  {
6672
- chipType: "suggestion",
6673
- surface: "tonal",
6674
- className: "bg-surface-container-low text-on-surface border border-outline"
6768
+ surface: "elevated",
6769
+ chipType: "input",
6770
+ className: [
6771
+ "bg-surface-container-low border-0 shadow-elevation-1",
6772
+ "data-[hovered]:shadow-elevation-2",
6773
+ "data-[focus-visible]:shadow-elevation-1",
6774
+ "data-[pressed]:data-[pressed]:shadow-elevation-1"
6775
+ ]
6675
6776
  },
6676
6777
  {
6677
- chipType: "suggestion",
6678
6778
  surface: "elevated",
6779
+ chipType: "suggestion",
6679
6780
  className: [
6680
- "bg-surface-container-low text-on-surface shadow-elevation-1",
6681
- "hover:shadow-elevation-2 transition-shadow duration-short2 ease-standard"
6781
+ "bg-surface-container-low border-0 shadow-elevation-1",
6782
+ "data-[hovered]:shadow-elevation-2",
6783
+ "data-[focus-visible]:shadow-elevation-1",
6784
+ "data-[pressed]:data-[pressed]:shadow-elevation-1"
6682
6785
  ]
6683
6786
  },
6684
- // ── Filter chip selected state ─────────────────────────────────────────
6787
+ // Deprecated tonal maps to flat (same as no override)
6685
6788
  {
6789
+ surface: "tonal",
6790
+ chipType: "assist",
6791
+ className: ""
6792
+ },
6793
+ {
6794
+ surface: "tonal",
6686
6795
  chipType: "filter",
6687
- selected: true,
6688
- className: "bg-secondary-container text-on-secondary-container border-0"
6796
+ className: ""
6689
6797
  },
6690
- // ── Disabled overrides — placed last so they always win ────────────────
6691
- // These must follow all surface/selection compound variants to ensure
6692
- // tailwind-merge keeps the disabled classes over any surface classes.
6693
6798
  {
6694
- isDisabled: true,
6695
- className: "text-on-surface/38 border-on-surface/12 bg-transparent"
6799
+ surface: "tonal",
6800
+ chipType: "input",
6801
+ className: ""
6802
+ },
6803
+ {
6804
+ surface: "tonal",
6805
+ chipType: "suggestion",
6806
+ className: ""
6696
6807
  }
6697
6808
  ],
6698
6809
  defaultVariants: {
6699
6810
  chipType: "assist",
6700
- surface: "tonal",
6701
- selected: false,
6702
- isDisabled: false,
6703
- hasLeadingIcon: false,
6704
- hasRemoveButton: false
6811
+ surface: "flat"
6705
6812
  }
6706
6813
  }
6707
6814
  );
6815
+ var chipStateLayerVariants = cva(
6816
+ [
6817
+ "absolute inset-0 rounded-[inherit] overflow-hidden pointer-events-none opacity-0",
6818
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6819
+ // Hover: 8%
6820
+ "group-data-[hovered]/chip:opacity-8",
6821
+ // Focus: 10%
6822
+ "group-data-[focus-visible]/chip:opacity-10",
6823
+ // Pressed: 10%, doubled selector wins over hover
6824
+ "group-data-[pressed]/chip:group-data-[pressed]/chip:opacity-10",
6825
+ // No state layer when disabled
6826
+ "group-data-[disabled]/chip:hidden"
6827
+ ],
6828
+ {
6829
+ variants: {
6830
+ chipType: {
6831
+ assist: "bg-on-surface",
6832
+ filter: [
6833
+ "bg-on-surface-variant",
6834
+ // Selected: switch to on-secondary-container color
6835
+ "group-data-[selected]/chip:bg-on-secondary-container"
6836
+ ],
6837
+ input: "bg-on-surface-variant",
6838
+ suggestion: "bg-on-surface-variant"
6839
+ }
6840
+ },
6841
+ defaultVariants: { chipType: "assist" }
6842
+ }
6843
+ );
6844
+ var chipFocusRingVariants = cva([
6845
+ "pointer-events-none absolute inset-[-3px] rounded-sm",
6846
+ "outline outline-2 outline-offset-0 outline-secondary",
6847
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6848
+ "opacity-0",
6849
+ "group-data-[focus-visible]/chip:opacity-100"
6850
+ ]);
6851
+ var chipLeadingIconVariants = cva(
6852
+ [
6853
+ "relative z-10 inline-flex shrink-0 items-center justify-center size-[18px]",
6854
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6855
+ "group-data-[disabled]/chip:text-on-surface/38"
6856
+ ],
6857
+ {
6858
+ variants: {
6859
+ chipType: {
6860
+ assist: "text-primary",
6861
+ filter: [
6862
+ "text-on-surface-variant",
6863
+ "group-data-[selected]/chip:text-on-secondary-container"
6864
+ ],
6865
+ input: "text-on-surface-variant",
6866
+ suggestion: "text-on-surface-variant"
6867
+ }
6868
+ },
6869
+ defaultVariants: { chipType: "assist" }
6870
+ }
6871
+ );
6872
+ var chipTrailingIconVariants = cva([
6873
+ "relative z-10 inline-flex shrink-0 items-center justify-center size-[18px]",
6874
+ "text-on-surface-variant",
6875
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6876
+ "group-data-[disabled]/chip:text-on-surface/38"
6877
+ ]);
6878
+ var chipCheckmarkVariants = cva([
6879
+ "inline-flex overflow-hidden shrink-0",
6880
+ // Spatial spring for width (moves adjacent text — this is a spatial animation)
6881
+ "transition-[width] duration-spring-standard-fast-spatial ease-spring-standard-fast-spatial",
6882
+ // Collapsed state
6883
+ "w-0",
6884
+ // Expanded state when selected
6885
+ "group-data-[selected]/chip:w-[18px]"
6886
+ ]);
6887
+ var chipCheckmarkIconVariants = cva([
6888
+ "inline-flex items-center justify-center size-[18px] shrink-0",
6889
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6890
+ "opacity-0",
6891
+ "group-data-[selected]/chip:opacity-100",
6892
+ // Color: on-secondary-container when selected; inherits on-surface-variant at rest (invisible)
6893
+ "text-on-secondary-container",
6894
+ "group-data-[disabled]/chip:text-on-surface/38"
6895
+ ]);
6896
+ var chipLabelVariants = cva(["relative z-10 inline-flex items-center"]);
6897
+ var chipRemoveButtonVariants = cva([
6898
+ "relative z-10 inline-flex size-[18px] shrink-0 items-center justify-center",
6899
+ "rounded-full cursor-pointer",
6900
+ "text-on-surface-variant",
6901
+ "group/chip-remove",
6902
+ "transition-colors duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6903
+ "data-[disabled]:text-on-surface/38 data-[disabled]:cursor-not-allowed data-[disabled]:pointer-events-none"
6904
+ ]);
6905
+ var chipRemoveStateLayerVariants = cva([
6906
+ // Slightly larger circle (32dp touch target centered on 18dp icon)
6907
+ "absolute inset-[-7px] rounded-full pointer-events-none opacity-0",
6908
+ "bg-on-surface-variant",
6909
+ "transition-opacity duration-spring-standard-fast-effects ease-spring-standard-fast-effects",
6910
+ "group-data-[hovered]/chip-remove:opacity-8",
6911
+ "group-data-[focus-visible]/chip-remove:opacity-10",
6912
+ "group-data-[pressed]/chip-remove:group-data-[pressed]/chip-remove:opacity-10",
6913
+ "group-data-[disabled]/chip-remove:hidden"
6914
+ ]);
6708
6915
  var CloseIcon2 = () => /* @__PURE__ */ jsx(
6709
6916
  "svg",
6710
6917
  {
@@ -6729,22 +6936,11 @@ var CheckIcon2 = () => /* @__PURE__ */ jsx(
6729
6936
  children: /* @__PURE__ */ jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" })
6730
6937
  }
6731
6938
  );
6732
- var StateLayer = () => /* @__PURE__ */ jsx(
6733
- "span",
6734
- {
6735
- "aria-hidden": "true",
6736
- className: cn(
6737
- "bg-on-surface pointer-events-none absolute inset-0 rounded-sm opacity-0",
6738
- "duration-spring-standard-fast-effects ease-spring-standard-fast-effects transition-opacity",
6739
- "group-focus-within:opacity-12 group-hover:opacity-8 group-active:opacity-12"
6740
- )
6741
- }
6742
- );
6743
6939
  var Chip = forwardRef(
6744
6940
  ({
6745
6941
  type,
6746
6942
  label,
6747
- surface = "tonal",
6943
+ surface: surfaceProp = "flat",
6748
6944
  selected,
6749
6945
  defaultSelected,
6750
6946
  onSelectionChange,
@@ -6755,6 +6951,17 @@ var Chip = forwardRef(
6755
6951
  isDisabled = false,
6756
6952
  className
6757
6953
  }, ref) => {
6954
+ const surface = (() => {
6955
+ if (surfaceProp === "tonal") {
6956
+ if (process.env.NODE_ENV !== "production") {
6957
+ console.warn(
6958
+ '[Chip] surface="tonal" is deprecated. Use surface="flat" instead. "tonal" will be removed in a future minor version.'
6959
+ );
6960
+ }
6961
+ return "flat";
6962
+ }
6963
+ return surfaceProp;
6964
+ })();
6758
6965
  const [localSelected, setLocalSelected] = useState(defaultSelected ?? false);
6759
6966
  const isControlled = selected !== void 0;
6760
6967
  const effectiveSelected = type === "filter" ? isControlled ? selected : localSelected : false;
@@ -6767,8 +6974,16 @@ var Chip = forwardRef(
6767
6974
  },
6768
6975
  [isControlled, onSelectionChange]
6769
6976
  );
6977
+ const [isPressed, setIsPressed] = useState(false);
6978
+ const handlePressStart = useCallback(() => setIsPressed(true), []);
6979
+ const handlePressEnd = useCallback(() => setIsPressed(false), []);
6980
+ const { isHovered, hoverProps } = useHover({ isDisabled });
6981
+ const { isFocusVisible, focusProps } = useFocusRing();
6982
+ const [isRemovePressed, setIsRemovePressed] = useState(false);
6983
+ const { isHovered: isRemoveHovered, hoverProps: removeHoverProps } = useHover({ isDisabled });
6984
+ const { isFocusVisible: isRemoveFocusVisible, focusProps: removeFocusProps } = useFocusRing();
6770
6985
  const { onMouseDown: handleRipple, ripples } = useRipple({ disabled: isDisabled });
6771
- const hasLeadingIcon = Boolean(leadingIcon);
6986
+ const hasLeadingIcon = Boolean(leadingIcon) || type === "filter";
6772
6987
  const [isRemoving, setIsRemoving] = useState(false);
6773
6988
  const handleRemove = useCallback(() => {
6774
6989
  setIsRemoving(true);
@@ -6778,35 +6993,38 @@ var Chip = forwardRef(
6778
6993
  onRemove?.();
6779
6994
  }
6780
6995
  }, [isRemoving, onRemove]);
6781
- const chipClass = () => cn(
6782
- chipVariants({
6783
- chipType: type,
6784
- surface: type === "assist" || type === "suggestion" ? surface : void 0,
6785
- selected: type === "filter" ? effectiveSelected : void 0,
6786
- isDisabled,
6787
- hasLeadingIcon,
6788
- hasRemoveButton: false
6789
- }),
6790
- className
6791
- );
6996
+ const bodyDataAttrs = getInteractionDataAttributes({
6997
+ isHovered,
6998
+ isFocusVisible,
6999
+ isPressed,
7000
+ ...type === "filter" && { isSelected: effectiveSelected },
7001
+ isDisabled
7002
+ });
7003
+ const rootClass = cn(chipVariants({ chipType: type, surface }), "group/chip", className);
6792
7004
  if (type === "input") {
7005
+ const removeDataAttrs = getInteractionDataAttributes({
7006
+ isHovered: isRemoveHovered,
7007
+ isFocusVisible: isRemoveFocusVisible,
7008
+ isPressed: isRemovePressed,
7009
+ isDisabled
7010
+ });
6793
7011
  return /* @__PURE__ */ jsxs(
6794
7012
  "span",
6795
7013
  {
6796
7014
  className: cn(
6797
- chipVariants({
6798
- chipType: "input",
6799
- isDisabled,
6800
- hasLeadingIcon,
6801
- hasRemoveButton: true
6802
- }),
7015
+ chipVariants({ chipType: "input", surface }),
7016
+ "group/chip",
6803
7017
  isRemoving && "animate-md-fade-out",
6804
7018
  className
6805
7019
  ),
7020
+ ...bodyDataAttrs,
7021
+ "data-with-leading": hasLeadingIcon ? "" : void 0,
7022
+ "data-with-trailing": "",
6806
7023
  onAnimationEnd: handleAnimationEnd,
6807
7024
  children: [
6808
7025
  ripples,
6809
- /* @__PURE__ */ jsx(StateLayer, {}),
7026
+ /* @__PURE__ */ jsx("span", { className: cn(chipStateLayerVariants({ chipType: "input" })), "aria-hidden": "true" }),
7027
+ /* @__PURE__ */ jsx("span", { className: cn(chipFocusRingVariants()), "aria-hidden": "true" }),
6810
7028
  /* @__PURE__ */ jsxs(
6811
7029
  ChipHeadless,
6812
7030
  {
@@ -6815,20 +7033,40 @@ var Chip = forwardRef(
6815
7033
  isDisabled,
6816
7034
  onRemove: handleRemove,
6817
7035
  onMouseDown: handleRipple,
6818
- removeIcon: /* @__PURE__ */ jsx(CloseIcon2, {}),
6819
- removeButtonClassName: "relative z-10 inline-flex size-4.5 shrink-0 items-center text-on-surface-variant",
7036
+ removeButtonClassName: cn(chipRemoveButtonVariants()),
7037
+ removeIcon: /* @__PURE__ */ jsxs(Fragment, { children: [
7038
+ /* @__PURE__ */ jsx(
7039
+ "span",
7040
+ {
7041
+ className: cn(chipRemoveStateLayerVariants()),
7042
+ "aria-hidden": "true",
7043
+ ...{ "data-remove-state-layer": "" }
7044
+ }
7045
+ ),
7046
+ /* @__PURE__ */ jsx(CloseIcon2, {})
7047
+ ] }),
6820
7048
  ref,
6821
7049
  className: "contents",
7050
+ bodyPassthrough: {
7051
+ onPressStart: handlePressStart,
7052
+ onPressEnd: handlePressEnd
7053
+ },
7054
+ removePassthrough: {
7055
+ dataAttributes: removeDataAttrs,
7056
+ eventHandlers: mergeProps$1(removeHoverProps, removeFocusProps),
7057
+ onPressStart: () => setIsRemovePressed(true),
7058
+ onPressEnd: () => setIsRemovePressed(false)
7059
+ },
6822
7060
  children: [
6823
7061
  leadingIcon && /* @__PURE__ */ jsx(
6824
7062
  "span",
6825
7063
  {
6826
7064
  "aria-hidden": "true",
6827
- className: "relative z-10 inline-flex size-4.5 shrink-0 items-center",
7065
+ className: cn(chipLeadingIconVariants({ chipType: "input" })),
6828
7066
  children: leadingIcon
6829
7067
  }
6830
7068
  ),
6831
- /* @__PURE__ */ jsx("span", { className: "relative z-10", children: label })
7069
+ /* @__PURE__ */ jsx("span", { className: cn(chipLabelVariants()), children: label })
6832
7070
  ]
6833
7071
  }
6834
7072
  )
@@ -6849,37 +7087,21 @@ var Chip = forwardRef(
6849
7087
  ...type !== "filter" && onPress !== void 0 && { onPress },
6850
7088
  isDisabled,
6851
7089
  onMouseDown: handleRipple,
6852
- className: chipClass(),
7090
+ className: cn(rootClass),
7091
+ bodyPassthrough: {
7092
+ dataAttributes: bodyDataAttrs,
7093
+ onPressStart: handlePressStart,
7094
+ onPressEnd: handlePressEnd,
7095
+ eventHandlers: mergeProps$1(hoverProps, focusProps)
7096
+ },
6853
7097
  children: [
6854
7098
  ripples,
6855
- /* @__PURE__ */ jsx(StateLayer, {}),
6856
- type === "filter" && /* @__PURE__ */ jsx(
6857
- "span",
6858
- {
6859
- className: cn(
6860
- "duration-short4 ease-emphasized-decelerate inline-flex overflow-hidden transition-[width,opacity]",
6861
- effectiveSelected ? "w-4.5 opacity-100" : "w-0 opacity-0"
6862
- ),
6863
- children: /* @__PURE__ */ jsx(CheckIcon2, {})
6864
- }
6865
- ),
6866
- leadingIcon && /* @__PURE__ */ jsx(
6867
- "span",
6868
- {
6869
- "aria-hidden": "true",
6870
- className: "relative z-10 inline-flex size-4.5 shrink-0 items-center",
6871
- children: leadingIcon
6872
- }
6873
- ),
6874
- /* @__PURE__ */ jsx("span", { className: "relative z-10", children: label }),
6875
- trailingIcon && /* @__PURE__ */ jsx(
6876
- "span",
6877
- {
6878
- "aria-hidden": "true",
6879
- className: "relative z-10 inline-flex size-4.5 shrink-0 items-center",
6880
- children: trailingIcon
6881
- }
6882
- )
7099
+ /* @__PURE__ */ jsx("span", { className: cn(chipStateLayerVariants({ chipType: type })), "aria-hidden": "true" }),
7100
+ /* @__PURE__ */ jsx("span", { className: cn(chipFocusRingVariants()), "aria-hidden": "true" }),
7101
+ type === "filter" && /* @__PURE__ */ jsx("span", { className: cn(chipCheckmarkVariants()), "aria-hidden": "true", children: /* @__PURE__ */ jsx("span", { className: cn(chipCheckmarkIconVariants()), children: /* @__PURE__ */ jsx(CheckIcon2, {}) }) }),
7102
+ leadingIcon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn(chipLeadingIconVariants({ chipType: type })), children: leadingIcon }),
7103
+ /* @__PURE__ */ jsx("span", { className: cn(chipLabelVariants()), children: label }),
7104
+ trailingIcon && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: cn(chipTrailingIconVariants()), children: trailingIcon })
6883
7105
  ]
6884
7106
  }
6885
7107
  );