@olddevs/ui 4.0.3 → 4.1.1
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/CHANGELOG.md +26 -0
- package/dist/components/actions/Toggle.d.ts.map +1 -1
- package/dist/components/actions/Toggle.js +1 -1
- package/dist/components/actions/Toggle.js.map +1 -1
- package/dist/components/actions/Truncate.d.ts.map +1 -1
- package/dist/components/actions/Truncate.js +2 -2
- package/dist/components/actions/Truncate.js.map +1 -1
- package/dist/components/app-shell/MobileTabBar.d.ts.map +1 -1
- package/dist/components/app-shell/MobileTabBar.js +1 -1
- package/dist/components/app-shell/MobileTabBar.js.map +1 -1
- package/dist/components/app-shell/NavItem.d.ts.map +1 -1
- package/dist/components/app-shell/NavItem.js +3 -1
- package/dist/components/app-shell/NavItem.js.map +1 -1
- package/dist/components/app-shell/SideNav.d.ts +17 -0
- package/dist/components/app-shell/SideNav.d.ts.map +1 -1
- package/dist/components/app-shell/SideNav.js +2 -1
- package/dist/components/app-shell/SideNav.js.map +1 -1
- package/dist/components/content/AvatarGroup.d.ts.map +1 -1
- package/dist/components/content/AvatarGroup.js +2 -1
- package/dist/components/content/AvatarGroup.js.map +1 -1
- package/dist/components/content/Table.d.ts.map +1 -1
- package/dist/components/content/Table.js +2 -1
- package/dist/components/content/Table.js.map +1 -1
- package/dist/components/content/VirtualGrid.d.ts.map +1 -1
- package/dist/components/content/VirtualGrid.js +4 -1
- package/dist/components/content/VirtualGrid.js.map +1 -1
- package/dist/components/inputs/Checkbox.d.ts.map +1 -1
- package/dist/components/inputs/Checkbox.js +3 -0
- package/dist/components/inputs/Checkbox.js.map +1 -1
- package/dist/components/inputs/CheckboxCard.d.ts.map +1 -1
- package/dist/components/inputs/CheckboxCard.js +2 -2
- package/dist/components/inputs/CheckboxCard.js.map +1 -1
- package/dist/components/inputs/RadioGroup.d.ts.map +1 -1
- package/dist/components/inputs/RadioGroup.js +5 -3
- package/dist/components/inputs/RadioGroup.js.map +1 -1
- package/dist/components/inputs/Select.d.ts.map +1 -1
- package/dist/components/inputs/Select.js +6 -6
- package/dist/components/inputs/Select.js.map +1 -1
- package/dist/components/inputs/Slider.d.ts.map +1 -1
- package/dist/components/inputs/Slider.js +2 -2
- package/dist/components/inputs/Slider.js.map +1 -1
- package/dist/components/navigation/Accordion.d.ts.map +1 -1
- package/dist/components/navigation/Accordion.js +1 -1
- package/dist/components/navigation/Accordion.js.map +1 -1
- package/dist/components/navigation/TreeView.d.ts.map +1 -1
- package/dist/components/navigation/TreeView.js +6 -3
- package/dist/components/navigation/TreeView.js.map +1 -1
- package/dist/editor/RichTextEditor.d.ts.map +1 -1
- package/dist/editor/RichTextEditor.js +4 -2
- package/dist/editor/RichTextEditor.js.map +1 -1
- package/dist/editor/styles/prose.d.ts +7 -0
- package/dist/editor/styles/prose.d.ts.map +1 -1
- package/dist/editor/styles/prose.js +1 -1
- package/dist/editor/styles/prose.js.map +1 -1
- package/dist/editor/ui/FormatMenus.d.ts.map +1 -1
- package/dist/editor/ui/FormatMenus.js +2 -1
- package/dist/editor/ui/FormatMenus.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/choice-card.d.ts +13 -0
- package/dist/lib/choice-card.d.ts.map +1 -1
- package/dist/lib/choice-card.js +2 -0
- package/dist/lib/choice-card.js.map +1 -1
- package/docs/components/app-shell/mobile-tab-bar.en.md +3 -3
- package/docs/components/app-shell/mobile-tab-bar.md +2 -2
- package/docs/components/app-shell/side-nav.en.md +56 -6
- package/docs/components/app-shell/side-nav.md +55 -5
- package/docs/components/index.en.md +2 -2
- package/docs/components/index.md +1 -1
- package/docs/components/inputs/form.en.md +2 -2
- package/docs/components/inputs/form.md +1 -1
- package/docs/components/inputs/rich-text-editor.en.md +2 -2
- package/docs/components/inputs/rich-text-editor.md +1 -1
- package/docs/getting-started.en.md +3 -3
- package/docs/getting-started.md +2 -2
- package/package.json +1 -1
- package/src/components/actions/Toggle.tsx +10 -3
- package/src/components/actions/Truncate.tsx +5 -3
- package/src/components/app-shell/MobileTabBar.tsx +9 -1
- package/src/components/app-shell/NavItem.tsx +6 -1
- package/src/components/app-shell/SideNav.tsx +27 -2
- package/src/components/content/AvatarGroup.tsx +6 -2
- package/src/components/content/Table.tsx +15 -3
- package/src/components/content/VirtualGrid.tsx +11 -1
- package/src/components/inputs/Checkbox.tsx +5 -0
- package/src/components/inputs/CheckboxCard.tsx +2 -0
- package/src/components/inputs/RadioGroup.tsx +10 -1
- package/src/components/inputs/Select.tsx +113 -106
- package/src/components/inputs/Slider.tsx +6 -2
- package/src/components/navigation/Accordion.tsx +4 -1
- package/src/components/navigation/TreeView.tsx +70 -49
- package/src/editor/RichTextEditor.tsx +6 -3
- package/src/editor/styles/prose.ts +1 -1
- package/src/editor/ui/FormatMenus.tsx +16 -1
- package/src/index.ts +1 -1
- package/src/lib/choice-card.ts +17 -0
|
@@ -1123,7 +1123,7 @@ ARIA сетки — шаблон `grid` из APG: `role="grid"` → `row` → `g
|
|
|
1123
1123
|
|
|
1124
1124
|
**Что видно.** HTML отформатирован: Блок верхнего уровня — на своей строке, дети контейнеров Блоков (пункты списков, строки и ячейки таблицы, колонки Раскладки, части `figure`, пункт чек-листа) — с отступом в два пробела, содержимое Блока — на строке его тега (пробелы текста не меняются), содержимое `pre` — как есть. Форматирование без потерь: неизменённый текст при применении даёт тот же Документ. Пустой Документ открывается пустым полем, а не `<p></p>`. Подсветки синтаксиса нет.
|
|
1125
1125
|
|
|
1126
|
-
**Вид.** Поле **всегда тёмное, цветом кита**, а не палитрой подсветки синтаксиса Блока кода: в `dark` и `dim` — самая глубокая подложка темы с её текстом и рамкой, в `light` и на Листе `paper` — те же значения тёмной темы (переменные `--rte-source-background`, `--rte-source-foreground`, `--rte-source-border`, их можно перекрыть). Скругление — как у кода, шрифт — моноширинный 13 px, как у `CodeView`; отступы — как у области Документа. Высота при входе — не меньше высоты Документа, дальше поле растёт по содержимому и растягивается по вертикали вручную. С `maxHeight` или `resizable` поле живёт в той же области прокрутки, что и Документ (раздел «Высота поля»). Выделение текста в поле — тоже тёмной темы текущего акцента (`--selection-dark`) со светлым текстом поля: светлое выделение светлой темы на тёмном поле не читалось.
|
|
1126
|
+
**Вид.** Поле **всегда тёмное, цветом кита**, а не палитрой подсветки синтаксиса Блока кода: в `dark` и `dim` — самая глубокая подложка темы с её текстом и рамкой, в `light` и на Листе `paper` — те же значения тёмной темы (переменные `--rte-source-background`, `--rte-source-foreground`, `--rte-source-border`, их можно перекрыть). На Листе `paper` светлый лист остаётся под тёмным полем: меняется содержимое листа, а не сам лист. Скругление — как у кода, шрифт — моноширинный 13 px, как у `CodeView`; отступы — как у области Документа. Высота при входе — не меньше высоты Документа, дальше поле растёт по содержимому и растягивается по вертикали вручную. С `maxHeight` или `resizable` поле живёт в той же области прокрутки, что и Документ (раздел «Высота поля»). Выделение текста в поле — тоже тёмной темы текущего акцента (`--selection-dark`) со светлым текстом поля: светлое выделение светлой темы на тёмном поле не читалось.
|
|
1127
1127
|
|
|
1128
1128
|
**Применение — при выходе, уходе фокуса и чтении.** Текст поля проходит через Схему тем же разбором, что входящий `value` (всё, чего в Схеме нет, отбрасывается — `script`, обработчики `on*`, произвольные `style` и `class`, `javascript:`; разметка выключенных Инструментов снимается, текст остаётся), и заменяет Документ **одной правкой** — одна отмена `Ctrl/⌘+Z` в Документе возвращает его к состоянию до применения. Применение запускают:
|
|
1129
1129
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
sourceHash:
|
|
2
|
+
sourceHash: 6f3e6f49e2eb
|
|
3
3
|
---
|
|
4
4
|
|
|
5
5
|
# Setting up `@olddevs/ui`
|
|
@@ -132,10 +132,10 @@ peer dependencies are treated as external, and the metric is gzip:
|
|
|
132
132
|
| `cn` and `tableStateToParams` | 0.6 kB |
|
|
133
133
|
| one `Button` | 1.6 kB |
|
|
134
134
|
| `DataTable` | 9.0 kB |
|
|
135
|
-
| the whole kit (`import *`) | 71.
|
|
135
|
+
| the whole kit (`import *`) | 71.4 kB |
|
|
136
136
|
| `RichTextEditor` from `@olddevs/ui/editor`, without the engine, "start" | 70.4 kB |
|
|
137
137
|
| `RichTextEditor` with the Tiptap engine, "start" (static graph, `basic`) | 295.1 kB |
|
|
138
|
-
| `RichTextEditor` with the Tiptap engine, "full" (with all lazy chunks, `full`) |
|
|
138
|
+
| `RichTextEditor` with the Tiptap engine, "full" (with all lazy chunks, `full`) | 339.1 kB |
|
|
139
139
|
| `RichTextView` from `@olddevs/ui/editor`, Tiptap engine included in the measurement | 12.0 kB |
|
|
140
140
|
| `sanitizeRichText` and `richTextSanitizeConfig` from `@olddevs/ui/editor/schema`, Tiptap engine included | 3.6 kB |
|
|
141
141
|
| `highlightCodeBlocks` from `@olddevs/ui/editor/highlight`, with lowlight | 22.6 kB |
|
package/docs/getting-started.md
CHANGED
|
@@ -129,10 +129,10 @@ peer-зависимости внешние, метрика gzip:
|
|
|
129
129
|
| `cn` и `tableStateToParams` | 0,6 кБ |
|
|
130
130
|
| одна `Button` | 1,6 кБ |
|
|
131
131
|
| `DataTable` | 9,0 кБ |
|
|
132
|
-
| весь кит целиком (`import *`) | 71,
|
|
132
|
+
| весь кит целиком (`import *`) | 71,4 кБ |
|
|
133
133
|
| `RichTextEditor` из `@olddevs/ui/editor`, без движка, «старт» | 70,4 кБ |
|
|
134
134
|
| `RichTextEditor` вместе с движком Tiptap, «старт» (статический граф, `basic`) | 295,1 кБ |
|
|
135
|
-
| `RichTextEditor` вместе с движком Tiptap, «полный» (со всеми ленивыми чанками, `full`) |
|
|
135
|
+
| `RichTextEditor` вместе с движком Tiptap, «полный» (со всеми ленивыми чанками, `full`) | 339,1 кБ |
|
|
136
136
|
| `RichTextView` из `@olddevs/ui/editor`, движок Tiptap в замер включён | 12,0 кБ |
|
|
137
137
|
| `sanitizeRichText` и `richTextSanitizeConfig` из `@olddevs/ui/editor/schema`, движок Tiptap в замер включён | 3,6 кБ |
|
|
138
138
|
| `highlightCodeBlocks` из `@olddevs/ui/editor/highlight`, вместе с lowlight | 22,6 кБ |
|
package/package.json
CHANGED
|
@@ -194,10 +194,17 @@ export const Toggle = forwardRef<HTMLButtonElement, ToggleProps>(function Toggle
|
|
|
194
194
|
оно обёрнуто в свой `<label for>`.
|
|
195
195
|
*/
|
|
196
196
|
return (
|
|
197
|
-
<div className={cn("flex items-center justify-between gap-4", className)}>
|
|
197
|
+
<div className={cn("group/toggle flex items-center justify-between gap-4", className)}>
|
|
198
198
|
{/* Приглушается только текст: дорожка приглушает себя сама (`disabled:opacity-50`),
|
|
199
|
-
и общая прозрачность строки дала бы ей 25 % вместо 50 % — иначе, чем без подписи.
|
|
200
|
-
|
|
199
|
+
и общая прозрачность строки дала бы ей 25 % вместо 50 % — иначе, чем без подписи.
|
|
200
|
+
`group-has-[[role=switch]:disabled]/toggle:` — для кнопки, выключенной предком-`fieldset`:
|
|
201
|
+
свойство `disabled` у неё при этом ложно, и одно оно текст не погасило бы. */}
|
|
202
|
+
<div
|
|
203
|
+
className={cn(
|
|
204
|
+
"flex min-w-0 flex-col group-has-[[role=switch]:disabled]/toggle:opacity-50",
|
|
205
|
+
disabled && "opacity-50"
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
201
208
|
<label
|
|
202
209
|
id={labelId}
|
|
203
210
|
htmlFor={switchId}
|
|
@@ -68,10 +68,12 @@ export function Truncate({ children, lines = 1, side = "top", className, ...rest
|
|
|
68
68
|
// клавиатурой не нужен и мешал бы.
|
|
69
69
|
tabIndex={truncated ? 0 : undefined}
|
|
70
70
|
className={cn(
|
|
71
|
-
"focus-visible:outline-accent-strong
|
|
71
|
+
"focus-visible:outline-accent-strong min-w-0 focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
72
72
|
lines === 1
|
|
73
|
-
? "truncate"
|
|
74
|
-
: //
|
|
73
|
+
? "block truncate"
|
|
74
|
+
: // `block` сюда нельзя: в CSS он стоит позже `[display:-webkit-box]`,
|
|
75
|
+
// перебивает его, и `line-clamp` молча не работает.
|
|
76
|
+
// Многострочная обрезка: `line-clamp` считает строки сам, и число
|
|
75
77
|
// приходит переменной — иначе класс собирался бы конкатенацией, а
|
|
76
78
|
// такого текста в исходниках нет и Tailwind его не увидит.
|
|
77
79
|
"[display:-webkit-box] overflow-hidden [-webkit-box-orient:vertical]",
|
|
@@ -263,7 +263,15 @@ export const MobileTabBarItem = forwardRef<HTMLAnchorElement, MobileTabBarItemPr
|
|
|
263
263
|
{custom ? (
|
|
264
264
|
<span className="absolute top-[-2px] -right-1.5 flex">{custom}</span>
|
|
265
265
|
) : shown ? (
|
|
266
|
-
|
|
266
|
+
// Значок держится ЛЕВОЙ кромкой у правого верхнего угла иконки и растёт
|
|
267
|
+
// вправо: привязка правой кромкой (`-right-1.5`) растила широкий «99+» или
|
|
268
|
+
// «Beta» влево, на иконку. Сдвиг `-ml-*` повторяет прежнее положение точки
|
|
269
|
+
// (8px) и короткого числа (16px): они выглядят как раньше.
|
|
270
|
+
<Badge
|
|
271
|
+
overlay
|
|
272
|
+
dot={dot}
|
|
273
|
+
className={cn("top-[-2px] right-auto left-full", dot ? "-ml-0.5" : "-ml-2.5")}
|
|
274
|
+
>
|
|
267
275
|
{count ?? text}
|
|
268
276
|
</Badge>
|
|
269
277
|
) : null}
|
|
@@ -68,7 +68,12 @@ export interface NavItemProps extends Omit<ComponentPropsWithoutRef<"a">, "child
|
|
|
68
68
|
const BASE =
|
|
69
69
|
"focus-visible:outline-accent-strong flex items-center gap-3 rounded-control border " +
|
|
70
70
|
"px-3 py-[9px] text-[13px] leading-5 no-underline transition-colors " +
|
|
71
|
-
"focus-visible:outline-2 focus-visible:outline-offset-2"
|
|
71
|
+
"focus-visible:outline-2 focus-visible:outline-offset-2 " +
|
|
72
|
+
// Недоступный пункт красится по `aria-disabled`, как у `Button`: у ссылки нет
|
|
73
|
+
// родного `disabled`, и без этого правила пункт, выключенный атрибутом ARIA,
|
|
74
|
+
// выглядел бы рабочим — с тем же цветом и курсором-указателем.
|
|
75
|
+
"aria-disabled:opacity-50 aria-disabled:cursor-default " +
|
|
76
|
+
"aria-disabled:hover:bg-transparent aria-disabled:hover:text-fg-1";
|
|
72
77
|
|
|
73
78
|
const STATE = {
|
|
74
79
|
active: "border-line bg-bg-3 text-fg-0 font-semibold",
|
|
@@ -74,6 +74,24 @@ export const NavGroup = forwardRef<HTMLDivElement, NavGroupProps>(function NavGr
|
|
|
74
74
|
);
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* Схлопнута ли навигация — для содержимого слотов `header` и `footer`.
|
|
79
|
+
*
|
|
80
|
+
* Пункты и разделы кита подстраиваются под иконочный режим сами, а слоты
|
|
81
|
+
* принадлежат потребителю, и кит не знает, что в них лежит. Блоку пользователя
|
|
82
|
+
* в подвале нужно знать ширину колонки: в иконочном режиме место есть только
|
|
83
|
+
* для аватара, а имя и роль, оставленные как есть, переносятся по словам и
|
|
84
|
+
* вылезают за край.
|
|
85
|
+
*
|
|
86
|
+
* Хук, а не render-проп у слота: слот остаётся обычным `ReactNode`, и тот же
|
|
87
|
+
* компонент пользователя работает и вне `SideNav`. Там хук возвращает `false`
|
|
88
|
+
* — развёрнутое состояние по умолчанию, как у `NavItem`. В ящике мобильного
|
|
89
|
+
* режима навигация всегда развёрнута, и хук это повторяет.
|
|
90
|
+
*
|
|
91
|
+
* Для подстройки чистым CSS у `<nav>` есть атрибут `data-collapsed`.
|
|
92
|
+
*/
|
|
93
|
+
export const useNavCollapsed = (): boolean => useContext(NavCollapsedContext);
|
|
94
|
+
|
|
77
95
|
export interface SideNavProps extends Omit<ComponentPropsWithoutRef<"nav">, "children"> {
|
|
78
96
|
/** Содержимое колонки между шапкой и подвалом: `NavGroup` и `NavItem`; прокручивается, если не помещается. */
|
|
79
97
|
children: ReactNode;
|
|
@@ -181,12 +199,19 @@ export const SideNav = forwardRef<HTMLElement, SideNavProps>(function SideNav(
|
|
|
181
199
|
)}
|
|
182
200
|
{...rest}
|
|
183
201
|
>
|
|
184
|
-
{
|
|
202
|
+
{/*
|
|
203
|
+
* Шапка и подвал обрезают лишнее по ширине: слот потребителя может не
|
|
204
|
+
* знать про иконочный режим, и без обрезки его текст лёг бы поверх
|
|
205
|
+
* содержимого страницы. Обрезка только по горизонтали (`clip`, а не
|
|
206
|
+
* `hidden`): по вертикали слот растёт сам, а `hidden` по одной оси
|
|
207
|
+
* сделал бы другую прокручиваемой.
|
|
208
|
+
*/}
|
|
209
|
+
{header != null ? <div className="shrink-0 overflow-x-clip">{header}</div> : null}
|
|
185
210
|
|
|
186
211
|
<div className="flex flex-1 flex-col gap-0.5 overflow-y-auto px-2.5 py-1">{children}</div>
|
|
187
212
|
|
|
188
213
|
{footer != null || showToggle ? (
|
|
189
|
-
<div className="border-line-soft shrink-0 border-t p-2.5">
|
|
214
|
+
<div className="border-line-soft shrink-0 overflow-x-clip border-t p-2.5">
|
|
190
215
|
{footer}
|
|
191
216
|
{showToggle ? (
|
|
192
217
|
<div className={cn("flex", isCollapsed ? "justify-center" : "justify-end")}>
|
|
@@ -56,6 +56,9 @@ export function AvatarGroup({
|
|
|
56
56
|
const members = names.map((entry) => (typeof entry === "string" ? { name: entry } : entry));
|
|
57
57
|
const shown = members.slice(0, max);
|
|
58
58
|
const rest = members.length - shown.length;
|
|
59
|
+
// Перекрытие по размеру круга: на 28px прежние 8px плюс кольцо срезали
|
|
60
|
+
// инициалы слева и плюс у «+N»; на 64px они не мешают.
|
|
61
|
+
const overlap = size === "lg" ? "-ml-2" : "-ml-1";
|
|
59
62
|
|
|
60
63
|
return (
|
|
61
64
|
<span
|
|
@@ -67,7 +70,7 @@ export function AvatarGroup({
|
|
|
67
70
|
<span
|
|
68
71
|
key={`${name}-${index}`}
|
|
69
72
|
aria-hidden
|
|
70
|
-
className={cn("ring-bg-1 relative rounded-full ring-2", index > 0 &&
|
|
73
|
+
className={cn("ring-bg-1 relative rounded-full ring-2", index > 0 && overlap)}
|
|
71
74
|
style={{ zIndex: shown.length - index }}
|
|
72
75
|
>
|
|
73
76
|
{/* `alt=""`: круг скрыт, имя объявляет группа — подпись у фото была бы вторым чтением. */}
|
|
@@ -80,7 +83,8 @@ export function AvatarGroup({
|
|
|
80
83
|
className={cn(
|
|
81
84
|
// `text-fg-1`, а не `fg-2`: на подложке `bg-3` вторая ступень не держит 4.5 —
|
|
82
85
|
// поймал аудит axe. Остаток «+N» это содержимое, а не подсказка.
|
|
83
|
-
"bg-bg-3 text-fg-1 ring-bg-1 relative
|
|
86
|
+
"bg-bg-3 text-fg-1 ring-bg-1 relative flex items-center justify-center rounded-full font-semibold ring-2",
|
|
87
|
+
overlap,
|
|
84
88
|
size === "lg" ? "text-md h-16 w-16" : "text-caption h-7 w-7"
|
|
85
89
|
)}
|
|
86
90
|
>
|
|
@@ -298,6 +298,13 @@ export function Table<T extends { id: Key }>({
|
|
|
298
298
|
/** Сколько ячеек в строке — столько же обязана занять детальная. */
|
|
299
299
|
const span = columns.length + (selectable ? 1 : 0) + (expandable ? 1 : 0);
|
|
300
300
|
|
|
301
|
+
/*
|
|
302
|
+
Ширина служебных колонок (выбор, раскрытие) идёт тем же путём, что и
|
|
303
|
+
`column.width`: `width` действует только внутри `ResizableTableContainer`,
|
|
304
|
+
вне его колонка растягивается наравне с данными, поэтому нужен CSS-стиль.
|
|
305
|
+
*/
|
|
306
|
+
const serviceWidth = resizable ? { width: 40 } : { style: { width: 40 } };
|
|
307
|
+
|
|
301
308
|
const table = (
|
|
302
309
|
<AriaTable
|
|
303
310
|
{...rest}
|
|
@@ -339,7 +346,7 @@ export function Table<T extends { id: Key }>({
|
|
|
339
346
|
{selectable ? (
|
|
340
347
|
<Column
|
|
341
348
|
className="border-line-soft bg-bg-1 text-fg-2 border-b px-3 py-2 text-left font-semibold"
|
|
342
|
-
|
|
349
|
+
{...serviceWidth}
|
|
343
350
|
>
|
|
344
351
|
{/*
|
|
345
352
|
Флажок «выделить всё» — родной RAC (`slot="selection"`), а не наш
|
|
@@ -354,7 +361,7 @@ export function Table<T extends { id: Key }>({
|
|
|
354
361
|
{expandable ? (
|
|
355
362
|
<Column
|
|
356
363
|
id={`${COLUMN_KEY}od-expander`}
|
|
357
|
-
|
|
364
|
+
{...serviceWidth}
|
|
358
365
|
className="border-line-soft bg-bg-1 border-b px-3 py-2"
|
|
359
366
|
>
|
|
360
367
|
{/*
|
|
@@ -401,7 +408,12 @@ export function Table<T extends { id: Key }>({
|
|
|
401
408
|
/>
|
|
402
409
|
) : null}
|
|
403
410
|
{resizable ? (
|
|
404
|
-
<ColumnResizer
|
|
411
|
+
<ColumnResizer
|
|
412
|
+
className={cn(
|
|
413
|
+
"bg-line-soft hover:bg-accent-strong w-px cursor-col-resize",
|
|
414
|
+
column.align !== "end" && "ml-auto"
|
|
415
|
+
)}
|
|
416
|
+
/>
|
|
405
417
|
) : null}
|
|
406
418
|
</span>
|
|
407
419
|
)}
|
|
@@ -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
|
-
|
|
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
|
|
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} />
|