@olddevs/ui 4.1.0 → 4.2.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.
Files changed (141) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/dist/components/actions/Toggle.d.ts.map +1 -1
  3. package/dist/components/actions/Toggle.js +1 -1
  4. package/dist/components/actions/Toggle.js.map +1 -1
  5. package/dist/components/actions/Truncate.d.ts.map +1 -1
  6. package/dist/components/actions/Truncate.js +2 -2
  7. package/dist/components/actions/Truncate.js.map +1 -1
  8. package/dist/components/app-shell/MobileTabBar.d.ts.map +1 -1
  9. package/dist/components/app-shell/MobileTabBar.js +1 -1
  10. package/dist/components/app-shell/MobileTabBar.js.map +1 -1
  11. package/dist/components/app-shell/NavItem.d.ts.map +1 -1
  12. package/dist/components/app-shell/NavItem.js +3 -1
  13. package/dist/components/app-shell/NavItem.js.map +1 -1
  14. package/dist/components/content/AvatarGroup.d.ts.map +1 -1
  15. package/dist/components/content/AvatarGroup.js +2 -1
  16. package/dist/components/content/AvatarGroup.js.map +1 -1
  17. package/dist/components/content/Table.d.ts.map +1 -1
  18. package/dist/components/content/Table.js +2 -1
  19. package/dist/components/content/Table.js.map +1 -1
  20. package/dist/components/content/VirtualGrid.d.ts.map +1 -1
  21. package/dist/components/content/VirtualGrid.js +4 -1
  22. package/dist/components/content/VirtualGrid.js.map +1 -1
  23. package/dist/components/inputs/Checkbox.d.ts.map +1 -1
  24. package/dist/components/inputs/Checkbox.js +3 -0
  25. package/dist/components/inputs/Checkbox.js.map +1 -1
  26. package/dist/components/inputs/CheckboxCard.d.ts.map +1 -1
  27. package/dist/components/inputs/CheckboxCard.js +2 -2
  28. package/dist/components/inputs/CheckboxCard.js.map +1 -1
  29. package/dist/components/inputs/RadioGroup.d.ts.map +1 -1
  30. package/dist/components/inputs/RadioGroup.js +5 -3
  31. package/dist/components/inputs/RadioGroup.js.map +1 -1
  32. package/dist/components/inputs/Select.d.ts.map +1 -1
  33. package/dist/components/inputs/Select.js +6 -6
  34. package/dist/components/inputs/Select.js.map +1 -1
  35. package/dist/components/inputs/Slider.d.ts.map +1 -1
  36. package/dist/components/inputs/Slider.js +2 -2
  37. package/dist/components/inputs/Slider.js.map +1 -1
  38. package/dist/components/navigation/Accordion.d.ts.map +1 -1
  39. package/dist/components/navigation/Accordion.js +1 -1
  40. package/dist/components/navigation/Accordion.js.map +1 -1
  41. package/dist/components/navigation/MegaMenu.d.ts +156 -0
  42. package/dist/components/navigation/MegaMenu.d.ts.map +1 -0
  43. package/dist/components/navigation/MegaMenu.js +512 -0
  44. package/dist/components/navigation/MegaMenu.js.map +1 -0
  45. package/dist/components/navigation/TreeView.d.ts.map +1 -1
  46. package/dist/components/navigation/TreeView.js +6 -3
  47. package/dist/components/navigation/TreeView.js.map +1 -1
  48. package/dist/components/navigation/useMenuAim.d.ts +41 -0
  49. package/dist/components/navigation/useMenuAim.d.ts.map +1 -0
  50. package/dist/components/navigation/useMenuAim.js +114 -0
  51. package/dist/components/navigation/useMenuAim.js.map +1 -0
  52. package/dist/components/overlays/Menu.d.ts +19 -1
  53. package/dist/components/overlays/Menu.d.ts.map +1 -1
  54. package/dist/components/overlays/Menu.js +16 -4
  55. package/dist/components/overlays/Menu.js.map +1 -1
  56. package/dist/editor/RichTextEditor.d.ts.map +1 -1
  57. package/dist/editor/RichTextEditor.js +4 -2
  58. package/dist/editor/RichTextEditor.js.map +1 -1
  59. package/dist/editor/features/table/TableMenuButton.js +1 -1
  60. package/dist/editor/features/table/TableMenuButton.js.map +1 -1
  61. package/dist/editor/styles/prose.d.ts +7 -0
  62. package/dist/editor/styles/prose.d.ts.map +1 -1
  63. package/dist/editor/styles/prose.js +1 -1
  64. package/dist/editor/styles/prose.js.map +1 -1
  65. package/dist/editor/ui/BlockHandle.js +1 -1
  66. package/dist/editor/ui/BlockHandle.js.map +1 -1
  67. package/dist/editor/ui/FormatMenus.d.ts.map +1 -1
  68. package/dist/editor/ui/FormatMenus.js +3 -2
  69. package/dist/editor/ui/FormatMenus.js.map +1 -1
  70. package/dist/{editor/hooks → hooks}/useSubmenuPlacement.d.ts +14 -1
  71. package/dist/hooks/useSubmenuPlacement.d.ts.map +1 -0
  72. package/dist/hooks/useSubmenuPlacement.js +10 -0
  73. package/dist/hooks/useSubmenuPlacement.js.map +1 -0
  74. package/dist/i18n/defaults.d.ts.map +1 -1
  75. package/dist/i18n/defaults.js +3 -0
  76. package/dist/i18n/defaults.js.map +1 -1
  77. package/dist/i18n/messages.d.ts +8 -0
  78. package/dist/i18n/messages.d.ts.map +1 -1
  79. package/dist/index.d.ts +2 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +1 -0
  82. package/dist/index.js.map +1 -1
  83. package/dist/lib/choice-card.d.ts +13 -0
  84. package/dist/lib/choice-card.d.ts.map +1 -1
  85. package/dist/lib/choice-card.js +2 -0
  86. package/dist/lib/choice-card.js.map +1 -1
  87. package/dist/locales/ru.d.ts.map +1 -1
  88. package/dist/locales/ru.js +3 -0
  89. package/dist/locales/ru.js.map +1 -1
  90. package/docs/api/messages.md +6 -0
  91. package/docs/components/app-shell/mobile-tab-bar.en.md +3 -3
  92. package/docs/components/app-shell/mobile-tab-bar.md +2 -2
  93. package/docs/components/app-shell/side-nav.en.md +7 -3
  94. package/docs/components/app-shell/side-nav.md +6 -2
  95. package/docs/components/index.en.md +2 -1
  96. package/docs/components/index.md +1 -0
  97. package/docs/components/inputs/form.en.md +2 -2
  98. package/docs/components/inputs/form.md +1 -1
  99. package/docs/components/inputs/rich-text-editor.en.md +2 -2
  100. package/docs/components/inputs/rich-text-editor.md +1 -1
  101. package/docs/components/navigation/mega-menu.en.md +273 -0
  102. package/docs/components/navigation/mega-menu.md +258 -0
  103. package/docs/components/overlays/menu.en.md +22 -4
  104. package/docs/components/overlays/menu.md +20 -4
  105. package/docs/getting-started.en.md +5 -5
  106. package/docs/getting-started.md +4 -4
  107. package/docs/glossary.en.md +6 -1
  108. package/docs/glossary.md +5 -0
  109. package/llms.txt +1 -0
  110. package/package.json +1 -1
  111. package/src/components/actions/Toggle.tsx +10 -3
  112. package/src/components/actions/Truncate.tsx +5 -3
  113. package/src/components/app-shell/MobileTabBar.tsx +9 -1
  114. package/src/components/app-shell/NavItem.tsx +6 -1
  115. package/src/components/content/AvatarGroup.tsx +6 -2
  116. package/src/components/content/Table.tsx +15 -3
  117. package/src/components/content/VirtualGrid.tsx +11 -1
  118. package/src/components/inputs/Checkbox.tsx +5 -0
  119. package/src/components/inputs/CheckboxCard.tsx +2 -0
  120. package/src/components/inputs/RadioGroup.tsx +10 -1
  121. package/src/components/inputs/Select.tsx +113 -106
  122. package/src/components/inputs/Slider.tsx +6 -2
  123. package/src/components/navigation/Accordion.tsx +4 -1
  124. package/src/components/navigation/MegaMenu.tsx +1223 -0
  125. package/src/components/navigation/TreeView.tsx +70 -49
  126. package/src/components/navigation/useMenuAim.ts +202 -0
  127. package/src/components/overlays/Menu.tsx +47 -4
  128. package/src/editor/RichTextEditor.tsx +6 -3
  129. package/src/editor/features/table/TableMenuButton.tsx +1 -1
  130. package/src/editor/styles/prose.ts +1 -1
  131. package/src/editor/ui/BlockHandle.tsx +1 -1
  132. package/src/editor/ui/FormatMenus.tsx +17 -2
  133. package/src/{editor/hooks → hooks}/useSubmenuPlacement.ts +18 -15
  134. package/src/i18n/defaults.ts +4 -0
  135. package/src/i18n/messages.ts +9 -0
  136. package/src/index.ts +17 -0
  137. package/src/lib/choice-card.ts +17 -0
  138. package/src/locales/ru.ts +4 -0
  139. package/dist/editor/hooks/useSubmenuPlacement.d.ts.map +0 -1
  140. package/dist/editor/hooks/useSubmenuPlacement.js +0 -20
  141. package/dist/editor/hooks/useSubmenuPlacement.js.map +0 -1
@@ -509,7 +509,17 @@ export function VirtualGrid({
509
509
  className="focus-visible:outline-accent-strong rounded-xl focus-visible:outline-2 focus-visible:outline-offset-2"
510
510
  >
511
511
  {/* `gridcell` у react-aria — `display: contents`: высоту держит обёртка. */}
512
- <div className="h-full" style={{ minHeight: minItemHeight }}>
512
+ {/* Обёртка — сетка из одной строки, а не блок с `min-height`: процентная высота
513
+ вложенного узла (`h-full`) от `min-height` не считается и схлопывается до
514
+ содержимого, а от строки сетки — считается. `items-start` оставляет ячейку без
515
+ `h-full` её естественной высоты. */}
516
+ <div
517
+ className="grid h-full grid-cols-1 items-start"
518
+ style={{
519
+ minHeight: minItemHeight,
520
+ gridTemplateRows: `minmax(${minItemHeight}px, 1fr)`,
521
+ }}
522
+ >
513
523
  {item.contextMenu != null ? (
514
524
  <CellContextMenu
515
525
  menu={item.contextMenu}
@@ -10,6 +10,7 @@ import {
10
10
  } from "react";
11
11
  import { CheckboxButton, CheckboxField, Text } from "react-aria-components";
12
12
  import { CheckIcon, MinusIcon } from "../../icons.js";
13
+ import { DIMMED_IN_DISABLED_FIELDSET } from "../../lib/choice-card.js";
13
14
  import { cn } from "../../lib/cn.js";
14
15
  import { hasContent } from "../../lib/empty.js";
15
16
  import { STATE_BORDER, TONE_TOKENS, type Size, type Tone } from "../../lib/variants.js";
@@ -239,6 +240,10 @@ export const Checkbox = forwardRef<HTMLDivElement, CheckboxProps>(function Check
239
240
  className={
240
241
  "hover:bg-bg-2 relative flex w-fit cursor-pointer items-center gap-2 rounded-sm px-1 py-0.5 " +
241
242
  "transition-colors disabled:cursor-default disabled:opacity-50 " +
243
+ // `disabled:` ловит только собственный `isDisabled`; выключенный предок-
244
+ // `fieldset` — см. `DIMMED_IN_DISABLED_FIELDSET`.
245
+ DIMMED_IN_DISABLED_FIELDSET +
246
+ " " +
242
247
  "before:absolute before:inset-x-0 before:bottom-full before:h-0.5 before:content-[''] after:absolute after:inset-x-0 after:top-full after:h-0.5 after:content-['']"
243
248
  }
244
249
  >
@@ -6,6 +6,7 @@ import { CheckIcon, MinusIcon } from "../../icons.js";
6
6
  import { cn } from "../../lib/cn.js";
7
7
  import {
8
8
  CHOICE_CARD_BASE,
9
+ CHOICE_CARD_DIMMED_IN_DISABLED_FIELDSET,
9
10
  CHOICE_CARD_FOCUS_RING,
10
11
  CHOICE_CARD_IDLE_BORDER,
11
12
  CHOICE_CARD_INVALID_BORDER,
@@ -82,6 +83,7 @@ export const CheckboxCard: CheckboxCardComponent = forwardRef<
82
83
  ? CHOICE_CARD_SELECTED_BORDER[tone]
83
84
  : CHOICE_CARD_IDLE_BORDER,
84
85
  isDisabled ? "opacity-50" : "hover:bg-bg-3",
86
+ CHOICE_CARD_DIMMED_IN_DISABLED_FIELDSET,
85
87
  orientation === "horizontal" && CHOICE_CARD_ROW,
86
88
  className
87
89
  )
@@ -14,6 +14,8 @@ import { hasContent } from "../../lib/empty.js";
14
14
  import { ChoiceCardBody } from "./ChoiceCardBody.js";
15
15
  import {
16
16
  CHOICE_CARD_BASE,
17
+ DIMMED_IN_DISABLED_FIELDSET,
18
+ CHOICE_CARD_DIMMED_IN_DISABLED_FIELDSET,
17
19
  CHOICE_CARD_FOCUS_RING,
18
20
  CHOICE_CARD_IDLE_BORDER,
19
21
  CHOICE_CARD_INVALID_BORDER,
@@ -214,6 +216,7 @@ export const Radio = forwardRef<HTMLDivElement, RadioProps>(function Radio(
214
216
  ? CHOICE_CARD_SELECTED_BORDER[tone]
215
217
  : CHOICE_CARD_IDLE_BORDER,
216
218
  isDisabled ? "opacity-50" : "hover:bg-bg-3",
219
+ CHOICE_CARD_DIMMED_IN_DISABLED_FIELDSET,
217
220
  orientation === "horizontal" && CHOICE_CARD_ROW,
218
221
  className
219
222
  )
@@ -247,7 +250,13 @@ export const Radio = forwardRef<HTMLDivElement, RadioProps>(function Radio(
247
250
  isDisabled={disabled}
248
251
  className={cn("flex flex-col", className)}
249
252
  >
250
- <RadioButton className="hover:bg-bg-2 flex w-fit cursor-pointer items-center gap-2 rounded-sm px-1 py-0.5 transition-colors disabled:cursor-default disabled:opacity-50">
253
+ <RadioButton
254
+ className={
255
+ "hover:bg-bg-2 flex w-fit cursor-pointer items-center gap-2 rounded-sm px-1 py-0.5 transition-colors " +
256
+ "disabled:cursor-default disabled:opacity-50 " +
257
+ DIMMED_IN_DISABLED_FIELDSET
258
+ }
259
+ >
251
260
  {({ isSelected, isInvalid, isFocusVisible }) => (
252
261
  <>
253
262
  <RadioBox selected={isSelected} invalid={isInvalid} focusVisible={isFocusVisible} />
@@ -15,7 +15,7 @@ import { LISTBOX_ITEM_CLASS } from "../../lib/menu.js";
15
15
  import { FIELD_CLEAR_CLASS } from "../../lib/field.js";
16
16
  import { useMessages } from "../../i18n/useMessages.js";
17
17
  import { CheckIcon, CloseIcon, SelectIcon } from "../../icons.js";
18
- import { Popover, PopoverContent, PopoverTrigger } from "../overlays/Popover.js";
18
+ import { Popover, PopoverAnchor, PopoverContent, PopoverTrigger } from "../overlays/Popover.js";
19
19
  import { VisuallyHidden } from "../content/VisuallyHidden.js";
20
20
 
21
21
  /** Минимум, который список требует от элемента. Остальные поля — ваши. */
@@ -502,41 +502,47 @@ export function Select<T extends SelectItem>(props: SelectProps<T>) {
502
502
  Кольцо фокуса рисуется на псевдоэлементе, а не на кнопке: обводить надо
503
503
  пилюлю целиком, иначе она у`же собственного контура.
504
504
  */}
505
- <span
506
- className={cn(
507
- "relative inline-flex items-center gap-2 rounded-full border px-3 py-[7px]",
508
- // `leading-5` задаёт высоту, а не наследует её: строчный бокс в 20px
509
- // плюс отступы дают ровно 36 — рост `Button` размера `md`, рядом с
510
- // которым пилюля и стоит в панели фильтров. С отступами формы
511
- // (`py-2.5` от потребителя) те же 20 дают 42 — рост `TextControl`
512
- // и `NumberInput` (см. `lib/field.ts`).
513
- //
514
- // Картинка выбранного значения тоже 20, и это условие, а не совпадение:
515
- // содержимое выше строчного бокса растягивало бы триггер. Пока она
516
- // была 22, пилюля с картинкой вырастала до 38, а триггер формы — до 44,
517
- // и контрол расходился и с соседним полем, и сам с собой в пустом
518
- // состоянии.
519
- // `transition-colors`, а не `transition-all`: на этом боксе меняются
520
- // только рамка, фон и цвет подписи — по ветке `hasSelection`. `all`
521
- // обещал переход всему, чего на пилюле нет, и заодно тому, что
522
- // потребитель пришлёт через `triggerClassName`. Заменено в волне 27
523
- // при ревизии движения: класс выглядел перемещающим, а перемещать
524
- // здесь нечего.
525
- "text-[13px] leading-5 font-semibold transition-colors",
526
- hasSelection && highlightSelected
527
- ? "border-accent-strong bg-accent-soft text-fg-0"
528
- : "border-line-soft text-fg-1 hover:border-line hover:bg-bg-2 bg-transparent",
529
- // Выбранное значение читается тёмным даже без акцентной подсветки:
530
- // подпись — это содержимое контрола, а не подсказка.
531
- hasSelection && !highlightSelected && "text-fg-0",
532
- // Гаснет ПИЛЮЛЯ, а не кнопка внутри неё: рамка и фон нарисованы здесь,
533
- // и приглушённая подпись рядом с яркой рамкой выглядела бы сбоем
534
- // отрисовки, а не выключенным полем.
535
- disabled && "cursor-not-allowed opacity-50",
536
- triggerClassName
537
- )}
538
- >
539
- {/*
505
+ {/*
506
+ Якорь панели — вся пилюля, а не кнопка-триггер внутри неё: кнопка отступает
507
+ от нижней кромки на рамку и `py-[7px]`, и панель с `sideOffset` в 4px
508
+ заходила на пилюлю вместо зазора.
509
+ */}
510
+ <PopoverAnchor asChild>
511
+ <span
512
+ className={cn(
513
+ "relative inline-flex items-center gap-2 rounded-full border px-3 py-[7px]",
514
+ // `leading-5` задаёт высоту, а не наследует её: строчный бокс в 20px
515
+ // плюс отступы дают ровно 36 — рост `Button` размера `md`, рядом с
516
+ // которым пилюля и стоит в панели фильтров. С отступами формы
517
+ // (`py-2.5` от потребителя) те же 20 дают 42 — рост `TextControl`
518
+ // и `NumberInput` (см. `lib/field.ts`).
519
+ //
520
+ // Картинка выбранного значения тоже 20, и это условие, а не совпадение:
521
+ // содержимое выше строчного бокса растягивало бы триггер. Пока она
522
+ // была 22, пилюля с картинкой вырастала до 38, а триггер формы — до 44,
523
+ // и контрол расходился и с соседним полем, и сам с собой в пустом
524
+ // состоянии.
525
+ // `transition-colors`, а не `transition-all`: на этом боксе меняются
526
+ // только рамка, фон и цвет подписи — по ветке `hasSelection`. `all`
527
+ // обещал переход всему, чего на пилюле нет, и заодно тому, что
528
+ // потребитель пришлёт через `triggerClassName`. Заменено в волне 27
529
+ // при ревизии движения: класс выглядел перемещающим, а перемещать
530
+ // здесь нечего.
531
+ "text-[13px] leading-5 font-semibold transition-colors",
532
+ hasSelection && highlightSelected
533
+ ? "border-accent-strong bg-accent-soft text-fg-0"
534
+ : "border-line-soft text-fg-1 hover:border-line hover:bg-bg-2 bg-transparent",
535
+ // Выбранное значение читается тёмным даже без акцентной подсветки:
536
+ // подпись — это содержимое контрола, а не подсказка.
537
+ hasSelection && !highlightSelected && "text-fg-0",
538
+ // Гаснет ПИЛЮЛЯ, а не кнопка внутри неё: рамка и фон нарисованы здесь,
539
+ // и приглушённая подпись рядом с яркой рамкой выглядела бы сбоем
540
+ // отрисовки, а не выключенным полем.
541
+ disabled && "cursor-not-allowed opacity-50",
542
+ triggerClassName
543
+ )}
544
+ >
545
+ {/*
540
546
  Подпись видна только скринридеру и стоит ВНЕ кнопки: внутри неё она
541
547
  попала бы в содержимое, то есть прочиталась бы дважды — один раз как
542
548
  имя, второй как значение.
@@ -544,46 +550,46 @@ export function Select<T extends SelectItem>(props: SelectProps<T>) {
544
550
  `elementType="span"`: по умолчанию react-aria рисует `<div>`, а здесь
545
551
  он оказался бы внутри строчной пилюли.
546
552
  */}
547
- {ariaLabel && !ariaLabelledBy ? (
548
- <VisuallyHidden elementType="span" id={labelId}>
549
- {ariaLabel}
550
- </VisuallyHidden>
551
- ) : null}
552
-
553
- <PopoverTrigger asChild>
554
- <button
555
- type="button"
556
- disabled={disabled}
557
- aria-haspopup="listbox"
558
- aria-expanded={open && !disabled}
559
- aria-labelledby={labelledBy ? `${labelledBy} ${valueId}` : undefined}
560
- aria-describedby={describedBy}
561
- className={cn(
562
- // `grow` + `justify-between`: когда потребитель растягивает пилюлю
563
- // (`w-full`, `min-w`), кнопка занимает всю ширину, и его
564
- // `justify-between` разводит иконку и подпись — как было до выноса.
565
- // На пилюле по размеру содержимого свободного места нет, и оба
566
- // класса ничего не меняют.
567
- "inline-flex min-w-0 grow cursor-pointer items-center justify-between gap-2 text-left",
568
- "disabled:cursor-not-allowed",
569
- "after:absolute after:inset-0 after:rounded-[inherit]",
570
- "focus-visible:after:outline-accent-strong focus-visible:after:outline-2 focus-visible:after:outline-offset-2",
571
- // Место под крестик — на кнопке, а не на пилюле: пилюля уже несёт
572
- // отступы потребителя, и добавка к ним сместила бы рамку.
573
- hasSelection && clearable && !disabled && "pr-6"
574
- )}
575
- >
576
- {/*
553
+ {ariaLabel && !ariaLabelledBy ? (
554
+ <VisuallyHidden elementType="span" id={labelId}>
555
+ {ariaLabel}
556
+ </VisuallyHidden>
557
+ ) : null}
558
+
559
+ <PopoverTrigger asChild>
560
+ <button
561
+ type="button"
562
+ disabled={disabled}
563
+ aria-haspopup="listbox"
564
+ aria-expanded={open && !disabled}
565
+ aria-labelledby={labelledBy ? `${labelledBy} ${valueId}` : undefined}
566
+ aria-describedby={describedBy}
567
+ className={cn(
568
+ // `grow` + `justify-between`: когда потребитель растягивает пилюлю
569
+ // (`w-full`, `min-w`), кнопка занимает всю ширину, и его
570
+ // `justify-between` разводит иконку и подпись — как было до выноса.
571
+ // На пилюле по размеру содержимого свободного места нет, и оба
572
+ // класса ничего не меняют.
573
+ "inline-flex min-w-0 grow cursor-pointer items-center justify-between gap-2 text-left",
574
+ "disabled:cursor-not-allowed",
575
+ "after:absolute after:inset-0 after:rounded-[inherit]",
576
+ "focus-visible:after:outline-accent-strong focus-visible:after:outline-2 focus-visible:after:outline-offset-2",
577
+ // Место под крестик — на кнопке, а не на пилюле: пилюля уже несёт
578
+ // отступы потребителя, и добавка к ним сместила бы рамку.
579
+ hasSelection && clearable && !disabled && "pr-6"
580
+ )}
581
+ >
582
+ {/*
577
583
  Содержимое собрано в одну группу, а не выложено соседями кнопки.
578
584
  Иначе `justify-between` разводит по краям именно их: в пилюле по
579
585
  ширине содержимого это незаметно, а в широком триггере (`w-full`
580
586
  в форме) картинка прилипает к левому краю, подпись — к правому,
581
587
  и между ними дыра во всю строку.
582
588
  */}
583
- {selected ? (
584
- <span id={valueId} className="inline-flex min-w-0 items-center gap-2">
585
- {renderMedia?.(selected, 20)}
586
- {/*
589
+ {selected ? (
590
+ <span id={valueId} className="inline-flex min-w-0 items-center gap-2">
591
+ {renderMedia?.(selected, 20)}
592
+ {/*
587
593
  Ширина подписи ограничена переменной, а не жёстким числом.
588
594
  В пилюле фильтра предел нужен: она растёт по содержимому, и
589
595
  «Абстрактные градиенты» растолкали бы соседние фильтры. В
@@ -592,10 +598,10 @@ export function Select<T extends SelectItem>(props: SelectProps<T>) {
592
598
  Снимается это классом на триггере:
593
599
  `triggerClassName="[--select-label-max:none]"`.
594
600
  */}
595
- <span className={LABEL_CLASS}>{selected.label}</span>
596
- </span>
597
- ) : hasSelection && props.multiple ? (
598
- /*
601
+ <span className={LABEL_CLASS}>{selected.label}</span>
602
+ </span>
603
+ ) : hasSelection && props.multiple ? (
604
+ /*
599
605
  Счётчик — СТРОГО за множественным выбором.
600
606
 
601
607
  Ветка ловила и одиночный `Select`, у которого значение есть, а
@@ -605,13 +611,13 @@ export function Select<T extends SelectItem>(props: SelectProps<T>) {
605
611
  нечего. Одиночный случай уходит теперь в ветку ниже и
606
612
  показывает сам ключ, как это делает `captionOf` в `TreeSelect`.
607
613
  */
608
- <span id={valueId} className={LABEL_CLASS}>
609
- {countLabel
610
- ? countLabel(selectedIds.length)
611
- : `${placeholder}: ${selectedIds.length}`}
612
- </span>
613
- ) : hasSelection ? (
614
- /*
614
+ <span id={valueId} className={LABEL_CLASS}>
615
+ {countLabel
616
+ ? countLabel(selectedIds.length)
617
+ : `${placeholder}: ${selectedIds.length}`}
618
+ </span>
619
+ ) : hasSelection ? (
620
+ /*
615
621
  Одиночный выбор, которому не нашлось элемента.
616
622
 
617
623
  Ключ ЧЕСТНЕЕ плейсхолдера, когда он человекочитаем
@@ -622,36 +628,37 @@ export function Select<T extends SelectItem>(props: SelectProps<T>) {
622
628
  умолчанию показывается ключ, как это делает `captionOf` в
623
629
  `TreeSelect`.
624
630
  */
625
- <span id={valueId} className={LABEL_CLASS}>
626
- {missingLabel ? missingLabel(selectedIds[0]!) : selectedIds[0]}
627
- </span>
628
- ) : (
629
- /*
631
+ <span id={valueId} className={LABEL_CLASS}>
632
+ {missingLabel ? missingLabel(selectedIds[0]!) : selectedIds[0]}
633
+ </span>
634
+ ) : (
635
+ /*
630
636
  `id` значения стоит на КОРНЕ КАЖДОЙ ветки, а не на общей обёртке
631
637
  вокруг них: обёртка — лишний бокс во флексе триггера, и она
632
638
  сместила бы содержимое там, где менять его никто не просил.
633
639
  */
634
- <span id={valueId} className="inline-flex min-w-0 items-center gap-2">
635
- <span aria-hidden className="inline-flex shrink-0 items-center opacity-60">
636
- {icon ?? <SelectIcon size={14} />}
640
+ <span id={valueId} className="inline-flex min-w-0 items-center gap-2">
641
+ <span aria-hidden className="inline-flex shrink-0 items-center opacity-60">
642
+ {icon ?? <SelectIcon size={14} />}
643
+ </span>
644
+ <span className="truncate">{placeholder}</span>
637
645
  </span>
638
- <span className="truncate">{placeholder}</span>
639
- </span>
640
- )}
641
- </button>
642
- </PopoverTrigger>
643
-
644
- {hasSelection && clearable && !disabled ? (
645
- <button
646
- type="button"
647
- aria-label={clearLabel ?? messages.common.clear}
648
- onClick={clear}
649
- className={FIELD_CLEAR_CLASS}
650
- >
651
- <CloseIcon size={10} aria-hidden />
652
- </button>
653
- ) : null}
654
- </span>
646
+ )}
647
+ </button>
648
+ </PopoverTrigger>
649
+
650
+ {hasSelection && clearable && !disabled ? (
651
+ <button
652
+ type="button"
653
+ aria-label={clearLabel ?? messages.common.clear}
654
+ onClick={clear}
655
+ className={FIELD_CLEAR_CLASS}
656
+ >
657
+ <CloseIcon size={10} aria-hidden />
658
+ </button>
659
+ ) : null}
660
+ </span>
661
+ </PopoverAnchor>
655
662
 
656
663
  <PopoverContent
657
664
  className={cn("w-[260px]", contentClassName)}
@@ -64,15 +64,19 @@ export type RangeSliderProps = SliderLookProps &
64
64
  `State=disabled` в Figma. `disabled:` здесь срабатывает на `data-disabled`,
65
65
  который react-aria ставит на корень. Раньше вида не было вовсе —
66
66
  отличался только курсор над бегунком.
67
+
68
+ `has-[input:disabled]:` — для ползунка внутри `<fieldset disabled>` (так `Form` с
69
+ `pending` гасит поля): react-aria о таком предке не знает и `data-disabled`
70
+ на корне не ставит, а нативное состояние видит только скрытый `input` бегунка.
67
71
  */
68
- const DIMMED_WHEN_DISABLED = "disabled:opacity-50";
72
+ const DIMMED_WHEN_DISABLED = "disabled:opacity-50 has-[input:disabled]:opacity-50";
69
73
  const ROOT_CLASS = `flex w-full flex-col gap-2 ${DIMMED_WHEN_DISABLED}`;
70
74
  const TRACK_CLASS = "bg-bg-3 relative h-1.5 w-full rounded-full";
71
75
  const FILL_CLASS = "bg-accent absolute h-full rounded-full";
72
76
  const THUMB_BASE_CLASS =
73
77
  "border-accent bg-bg-1 size-4 cursor-grab rounded-full border-2 shadow-[var(--shadow-1)] transition-shadow " +
74
78
  "dragging:cursor-grabbing focus-visible:outline-accent-strong focus-visible:outline-2 focus-visible:outline-offset-2 " +
75
- "disabled:cursor-not-allowed";
79
+ "disabled:cursor-not-allowed has-[input:disabled]:cursor-not-allowed";
76
80
  // Вдоль оси бегунок ставит react-aria (`left` или `top`), поперёк — мы.
77
81
  const THUMB_CLASS = `${THUMB_BASE_CLASS} top-1/2`;
78
82
 
@@ -91,7 +91,10 @@ export function AccordionItem({ id, title, disabled, className, children }: Acco
91
91
  />
92
92
  </AriaButton>
93
93
  </AriaHeading>
94
- <DisclosurePanel className="text-fg-1 pb-3 text-sm">{children}</DisclosurePanel>
94
+ {/* Отступ только у раскрытой панели: свёрнутая остаётся в потоке, и её `pb-3` добавлял 12px. */}
95
+ <DisclosurePanel className={cn("text-fg-1 text-sm", isExpanded && "pb-3")}>
96
+ {children}
97
+ </DisclosurePanel>
95
98
  </>
96
99
  )}
97
100
  </Disclosure>