@adia-ai/web-components 0.0.16 → 0.0.18

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.
@@ -296,14 +296,13 @@
296
296
  [slot="panel"] > [slot="body"][padding] { background: var(--a-canvas-0-scrim); }
297
297
 
298
298
  /* ═══════ Footer ═══════
299
- Flex row for actions. [slot="description"] + [slot="action"] = space-between.
300
- Mirrors card-ui footer semantics. */
299
+ Block default single non-slotted child (e.g. <grid-ui>, <col-ui>, raw
300
+ paragraph) stretches full-width naturally. Activate flex-row only when a
301
+ direct slotted child is present, or there are 2+ children — that's the
302
+ action-row case. Mirrors card-ui footer semantics. */
301
303
 
302
304
  [slot="panel"] > [slot="footer"] {
303
- display: flex;
304
- flex-wrap: wrap;
305
- align-items: center;
306
- gap: var(--drawer-footer-gap);
305
+ display: block;
307
306
  padding: var(--drawer-footer-pad);
308
307
  border-top: 1px solid var(--drawer-divider);
309
308
  flex-shrink: 0;
@@ -314,6 +313,14 @@
314
313
  z-index: 1;
315
314
  }
316
315
 
316
+ [slot="panel"] > [slot="footer"]:has(> [slot]),
317
+ [slot="panel"] > [slot="footer"]:has(> :nth-child(2)) {
318
+ display: flex;
319
+ flex-wrap: wrap;
320
+ align-items: center;
321
+ gap: var(--drawer-footer-gap);
322
+ }
323
+
317
324
  [slot="panel"] > [slot="footer"][justify="end"] {
318
325
  justify-content: flex-end;
319
326
  }
@@ -38,6 +38,7 @@ export { AdiaCommand } from './command/command.js';
38
38
  export { AdiaColorPicker } from './color-picker/color-picker.js';
39
39
  export { AdiaNoodles } from './noodles/noodles.js';
40
40
  export { AdiaTable } from './table/table.js';
41
+ export { AdiaTableToolbar } from './table-toolbar/table-toolbar.js';
41
42
  export { AdiaTimeline, AdiaTimelineItem } from './timeline/timeline.js';
42
43
  export { AdiaStepper, AdiaStepperItem } from './stepper/stepper.js';
43
44
  export { AdiaSwiper } from './swiper/swiper.js';
@@ -123,12 +123,21 @@
123
123
  pointer-events: none;
124
124
  }
125
125
 
126
- /* Prefix + Suffix */
126
+ /* Prefix + Suffix — inline-flex so icon-ui (or any non-text affix
127
+ content) centers vertically within the slot wrapper. Without
128
+ this, an icon inside a default <span slot="prefix"> sits at the
129
+ baseline of the field's 1.4 line-box and renders visually below
130
+ the field's center. With inline-flex + align-items: center the
131
+ icon centers in the slot box, which is itself centered in the
132
+ field via the field's align-items: center. */
127
133
  [slot="prefix"],
128
134
  [slot="suffix"] {
135
+ display: inline-flex;
136
+ align-items: center;
129
137
  flex-shrink: 0;
130
138
  color: var(--input-affix-fg);
131
139
  font-size: var(--input-font-size);
140
+ line-height: 1;
132
141
  user-select: none;
133
142
  pointer-events: none;
134
143
  }
@@ -241,12 +241,26 @@ class AdiaNoodles extends AdiaElement {
241
241
  }
242
242
 
243
243
  // ── Port position computation ──────────────────────────────
244
-
244
+ // Uses bounding client rects so nested port-bearing descendants
245
+ // (e.g. inside an absolutely-positioned card wrapper) project
246
+ // correctly into the noodles-ui coordinate system.
247
+ //
248
+ // When an ancestor applies a CSS transform (zoom/pan in a graph
249
+ // editor), bounding rects are in *screen* pixels — but SVG paths
250
+ // and port-indicator dots are positioned in *local* (untransformed)
251
+ // pixels. We detect any ancestor scale by comparing this element's
252
+ // visible width to its layout width and divide deltas accordingly.
253
+ // No transform → ratio is 1, no-op.
245
254
  #getPortPosition(el, side) {
246
- const left = el.offsetLeft;
247
- const top = el.offsetTop;
248
- const w = el.offsetWidth;
249
- const h = el.offsetHeight;
255
+ const elRect = el.getBoundingClientRect();
256
+ const myRect = this.getBoundingClientRect();
257
+ const localScale = (this.offsetWidth && myRect.width)
258
+ ? (myRect.width / this.offsetWidth)
259
+ : 1;
260
+ const left = (elRect.left - myRect.left) / localScale;
261
+ const top = (elRect.top - myRect.top) / localScale;
262
+ const w = elRect.width / localScale;
263
+ const h = elRect.height / localScale;
250
264
 
251
265
  switch (side) {
252
266
  case 'left': return { x: left, y: top + h / 2 };
@@ -22,7 +22,11 @@
22
22
  --option-card-radio-bg: var(--a-bg);
23
23
  --option-card-radio-border: var(--a-border);
24
24
  --option-card-radio-fill: var(--a-accent);
25
- --option-card-radio-dot: var(--a-accent-fg);
25
+ /* Mirror radio.css's --radio-fg-checked recipe: an always-light
26
+ token rather than --a-accent-fg, which can flip dark depending
27
+ on theme/scheme combos and produce a near-black inner dot
28
+ against the accent disc. */
29
+ --option-card-radio-dot: var(--a-chrome-light);
26
30
 
27
31
  /* ── Typography ── */
28
32
  --option-card-heading-color: var(--a-fg);
@@ -57,8 +57,20 @@
57
57
  background var(--segment-duration) var(--segment-easing);
58
58
  }
59
59
 
60
+ /* Text rendered via ::after so consumers can drive label via [text].
61
+ Under the parent's `display: flex`, ::after becomes a flex item with
62
+ `min-width: auto` (= min-content), which would force the segment to
63
+ overflow when the label is wider than the available track. Setting
64
+ `min-width: 0` re-enables shrinking, and `text-overflow: ellipsis`
65
+ has to live here (not on :scope) because text-overflow does not
66
+ inherit and only applies to the box that owns the inline text. */
60
67
  :scope::after {
61
68
  content: attr(text);
69
+ min-width: 0;
70
+ max-width: 100%;
71
+ overflow: hidden;
72
+ text-overflow: ellipsis;
73
+ white-space: nowrap;
62
74
  }
63
75
 
64
76
  /* States */
@@ -110,9 +110,13 @@
110
110
  color: var(--select-fg-subtle);
111
111
  }
112
112
 
113
- /* Leading icon / avatar */
113
+ /* Leading icon / avatar — inline-flex centers icon content vertically
114
+ within the slot box (same recipe as input-ui's prefix/suffix). */
114
115
  [slot="leading"] {
116
+ display: inline-flex;
117
+ align-items: center;
115
118
  flex-shrink: 0;
119
+ line-height: 1;
116
120
  --a-icon-size: var(--select-font-size);
117
121
  color: var(--select-fg-muted);
118
122
  }
@@ -204,7 +208,7 @@ select-ui [role="option"] {
204
208
  select-ui [role="option"]:hover,
205
209
  select-ui [role="option"][data-focused] {
206
210
  background: var(--a-bg-hover);
207
- color: var(--a-fg);
211
+ color: var(--a-fg-hover);
208
212
  }
209
213
  select-ui [role="option"][aria-selected="true"] {
210
214
  color: var(--select-fg-selected);
@@ -0,0 +1,212 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://adiaui.dev/a2ui/v0_9/components/TableToolbar.json",
4
+ "title": "TableToolbar",
5
+ "description": "Header / companion bar for a sibling table-ui. Renders title + count badge, filter / sort / columns popovers, and a search input — all wired to the target table via an [for] id-ref. Modeled on chart-legend-ui's [for] binding pattern. Drop next to (or above) any table-ui to add the standard data-grid toolbar without re-implementing search, filter, sort, or column visibility.",
6
+ "type": "object",
7
+ "allOf": [
8
+ {
9
+ "$ref": "common_types.json#/$defs/ComponentCommon"
10
+ },
11
+ {
12
+ "$ref": "common_types.json#/$defs/CatalogComponentCommon"
13
+ }
14
+ ],
15
+ "properties": {
16
+ "columns": {
17
+ "description": "Show the Columns visibility popover button.",
18
+ "type": "boolean",
19
+ "default": true
20
+ },
21
+ "component": {
22
+ "const": "TableToolbar"
23
+ },
24
+ "count": {
25
+ "description": "Optional count badge value shown next to the title. When unset, falls back to the row count of the bound table.",
26
+ "type": "string",
27
+ "default": ""
28
+ },
29
+ "filterable": {
30
+ "description": "Show the Filter popover button.",
31
+ "type": "boolean",
32
+ "default": true
33
+ },
34
+ "for": {
35
+ "description": "id-ref of the table-ui to control. Falls back to the first sibling table-ui within the same parent when omitted.",
36
+ "type": "string",
37
+ "default": ""
38
+ },
39
+ "placeholder": {
40
+ "description": "Placeholder text for the search input.",
41
+ "type": "string",
42
+ "default": "Search..."
43
+ },
44
+ "searchable": {
45
+ "description": "Show the search input.",
46
+ "type": "boolean",
47
+ "default": true
48
+ },
49
+ "sortable": {
50
+ "description": "Show the Sort popover button.",
51
+ "type": "boolean",
52
+ "default": true
53
+ },
54
+ "text": {
55
+ "description": "Title text shown on the left. Alternative to slotted heading content.",
56
+ "type": "string",
57
+ "default": ""
58
+ },
59
+ "variant": {
60
+ "description": "Toolbar visual variant. `default` renders bare on parent surface; `card` adds the same chrome as a card-ui header.",
61
+ "type": "string",
62
+ "enum": [
63
+ "default",
64
+ "card"
65
+ ],
66
+ "default": "default"
67
+ }
68
+ },
69
+ "required": [
70
+ "component"
71
+ ],
72
+ "unevaluatedProperties": false,
73
+ "x-adiaui": {
74
+ "anti_patterns": [],
75
+ "category": "agent",
76
+ "events": {
77
+ "columns-change": {
78
+ "description": "Column visibility changed. Detail: { hiddenColumns }."
79
+ },
80
+ "filter-change": {
81
+ "description": "Filter set changed. Detail: { filters }."
82
+ },
83
+ "search": {
84
+ "description": "Debounced search query change. Detail: { value }."
85
+ },
86
+ "sort-change": {
87
+ "description": "Sort state changed. Detail: { sortState }."
88
+ }
89
+ },
90
+ "examples": [
91
+ {
92
+ "description": "Members table with filter/sort/columns/search wired to a sibling table-ui.",
93
+ "a2ui": "[\n {\"id\": \"root\", \"component\": \"Column\", \"gap\": \"3\", \"children\": [\"bar\", \"card\"]},\n {\"id\": \"bar\", \"component\": \"TableToolbar\", \"for\": \"members\", \"text\": \"All Employees\", \"count\": \"32\"},\n {\"id\": \"card\", \"component\": \"Card\", \"children\": [\"sec\"]},\n {\"id\": \"sec\", \"component\": \"Section\", \"bleed\": true, \"children\": [\"tbl\"]},\n {\"id\": \"tbl\", \"component\": \"Table\", \"id\": \"members\", \"sortable\": true, \"raw\": true}\n]",
94
+ "name": "members-toolbar"
95
+ }
96
+ ],
97
+ "keywords": [
98
+ "table-toolbar",
99
+ "data-grid",
100
+ "data-grid-toolbar",
101
+ "filter",
102
+ "sort",
103
+ "columns",
104
+ "search",
105
+ "directory",
106
+ "admin",
107
+ "backoffice",
108
+ "listing",
109
+ "records"
110
+ ],
111
+ "name": "AdiaTableToolbar",
112
+ "related": [
113
+ "table",
114
+ "search",
115
+ "button",
116
+ "badge",
117
+ "popover"
118
+ ],
119
+ "slots": {
120
+ "default": {
121
+ "description": "Optional title content. Used when [text] is empty."
122
+ },
123
+ "actions": {
124
+ "description": "Trailing action area — primary buttons (e.g. \"New row\") rendered after the search input."
125
+ }
126
+ },
127
+ "states": [
128
+ {
129
+ "description": "Default, ready for interaction.",
130
+ "name": "idle"
131
+ }
132
+ ],
133
+ "synonyms": {
134
+ "columns": [
135
+ "table-toolbar",
136
+ "table"
137
+ ],
138
+ "data-grid": [
139
+ "table-toolbar",
140
+ "table"
141
+ ],
142
+ "data-grid-toolbar": [
143
+ "table-toolbar",
144
+ "table"
145
+ ],
146
+ "filter": [
147
+ "table-toolbar",
148
+ "table"
149
+ ],
150
+ "sort": [
151
+ "table-toolbar",
152
+ "table"
153
+ ]
154
+ },
155
+ "tag": "table-toolbar-ui",
156
+ "tokens": {
157
+ "--table-toolbar-bg": {
158
+ "description": "Toolbar background (variant=card)"
159
+ },
160
+ "--table-toolbar-border": {
161
+ "description": "Toolbar border color (variant=card)"
162
+ },
163
+ "--table-toolbar-gap": {
164
+ "description": "Gap between toolbar clusters"
165
+ },
166
+ "--table-toolbar-popover-bg": {
167
+ "description": "Popover background"
168
+ },
169
+ "--table-toolbar-popover-border": {
170
+ "description": "Popover border"
171
+ },
172
+ "--table-toolbar-popover-fg": {
173
+ "description": "Popover text color"
174
+ },
175
+ "--table-toolbar-popover-gap": {
176
+ "description": "Popover content gap"
177
+ },
178
+ "--table-toolbar-popover-min": {
179
+ "description": "Popover minimum width"
180
+ },
181
+ "--table-toolbar-popover-pad": {
182
+ "description": "Popover padding"
183
+ },
184
+ "--table-toolbar-popover-radius": {
185
+ "description": "Popover radius"
186
+ },
187
+ "--table-toolbar-popover-shadow": {
188
+ "description": "Popover shadow"
189
+ },
190
+ "--table-toolbar-px": {
191
+ "description": "Horizontal padding"
192
+ },
193
+ "--table-toolbar-py": {
194
+ "description": "Vertical padding"
195
+ },
196
+ "--table-toolbar-radius": {
197
+ "description": "Toolbar corner radius (variant=card)"
198
+ },
199
+ "--table-toolbar-title-fg": {
200
+ "description": "Title text color"
201
+ },
202
+ "--table-toolbar-title-size": {
203
+ "description": "Title font size"
204
+ },
205
+ "--table-toolbar-title-weight": {
206
+ "description": "Title font weight"
207
+ }
208
+ },
209
+ "traits": [],
210
+ "version": 1
211
+ }
212
+ }
@@ -0,0 +1,269 @@
1
+ @scope (table-toolbar-ui) {
2
+ :where(:scope) {
3
+ /* ── Layout ── */
4
+ --table-toolbar-gap: var(--a-space-3);
5
+ --table-toolbar-py: var(--a-space-2);
6
+ --table-toolbar-px: 0;
7
+ --table-toolbar-cluster-gap: var(--a-space-2);
8
+
9
+ /* ── Surface ── */
10
+ --table-toolbar-bg: transparent;
11
+ --table-toolbar-border: transparent;
12
+ --table-toolbar-radius: var(--a-radius-lg);
13
+
14
+ /* ── Title ── */
15
+ --table-toolbar-title-fg: var(--a-fg-strong);
16
+ --table-toolbar-title-size: var(--a-ui-lg);
17
+ --table-toolbar-title-weight: var(--a-weight-medium);
18
+ --table-toolbar-title-gap: var(--a-space-2);
19
+
20
+ /* ── Search ── */
21
+ --table-toolbar-search-min: 14rem;
22
+ --table-toolbar-search-max: 22rem;
23
+
24
+ /* ── Popover ── */
25
+ --table-toolbar-popover-bg: var(--a-canvas);
26
+ --table-toolbar-popover-fg: var(--a-canvas-text);
27
+ --table-toolbar-popover-border: var(--a-border);
28
+ --table-toolbar-popover-radius: var(--a-radius-lg);
29
+ --table-toolbar-popover-shadow: var(--a-shadow-lg);
30
+ --table-toolbar-popover-pad: var(--a-space-2);
31
+ --table-toolbar-popover-gap: var(--a-space-1);
32
+ --table-toolbar-popover-min: 16rem;
33
+ --table-toolbar-popover-head-fg: var(--a-fg-muted);
34
+ --table-toolbar-popover-head-size: var(--a-ui-tiny);
35
+ --table-toolbar-popover-head-pad: var(--a-space-2);
36
+ --table-toolbar-popover-row-pad: var(--a-space-2);
37
+ --table-toolbar-popover-row-radius: var(--a-radius-sm);
38
+ --table-toolbar-popover-row-bg-hover: var(--a-bg-hover);
39
+ --table-toolbar-popover-input-bg: var(--a-bg-subtle);
40
+ --table-toolbar-popover-input-border: var(--a-border-subtle);
41
+ --table-toolbar-popover-input-radius: var(--a-radius-sm);
42
+ --table-toolbar-popover-input-py: var(--a-space-1);
43
+ --table-toolbar-popover-input-px: var(--a-space-2);
44
+ --table-toolbar-popover-input-size: var(--a-ui-sm);
45
+ --table-toolbar-popover-action-fg: var(--a-accent);
46
+ --table-toolbar-popover-action-size: var(--a-ui-sm);
47
+ }
48
+
49
+ /* ═══════ Base ═══════ */
50
+
51
+ :scope {
52
+ box-sizing: border-box;
53
+ display: block;
54
+ color: var(--a-fg);
55
+ }
56
+
57
+ [data-toolbar] {
58
+ display: flex;
59
+ flex-direction: row;
60
+ align-items: center;
61
+ gap: var(--table-toolbar-gap);
62
+ padding: var(--table-toolbar-py) var(--table-toolbar-px);
63
+ min-width: 0;
64
+ }
65
+
66
+ /* ═══════ Variant: card ═══════ */
67
+
68
+ :scope[variant="card"] [data-toolbar] {
69
+ background: var(--table-toolbar-bg);
70
+ border: 1px solid var(--table-toolbar-border);
71
+ border-radius: var(--table-toolbar-radius);
72
+ padding: var(--a-space-3) var(--a-space-4);
73
+ }
74
+
75
+ /* ═══════ Title cluster ═══════ */
76
+
77
+ [data-title] {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ gap: var(--table-toolbar-title-gap);
81
+ flex: 0 0 auto;
82
+ min-width: 0;
83
+ }
84
+
85
+ [data-heading] {
86
+ font-size: var(--table-toolbar-title-size);
87
+ font-weight: var(--table-toolbar-title-weight);
88
+ color: var(--table-toolbar-title-fg);
89
+ line-height: 1.2;
90
+ }
91
+
92
+ [data-count-badge][hidden] { display: none; }
93
+
94
+ /* ═══════ Controls cluster ═══════ */
95
+
96
+ [data-controls] {
97
+ display: inline-flex;
98
+ align-items: center;
99
+ gap: var(--table-toolbar-cluster-gap);
100
+ flex: 0 0 auto;
101
+ }
102
+
103
+ [data-toolbar-btn][hidden] { display: none; }
104
+
105
+ /* ═══════ Search ═══════ */
106
+
107
+ [data-search] {
108
+ flex: 1 1 var(--table-toolbar-search-min);
109
+ min-width: 0;
110
+ max-width: var(--table-toolbar-search-max);
111
+ margin-inline-start: auto;
112
+ }
113
+
114
+ [data-search][hidden] { display: none; }
115
+
116
+ /* ═══════ Actions slot ═══════ */
117
+
118
+ [data-actions] {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ gap: var(--table-toolbar-cluster-gap);
122
+ flex: 0 0 auto;
123
+ }
124
+
125
+ [data-actions]:empty { display: none; }
126
+ }
127
+
128
+ /* ═══════ Popover (top layer — outside @scope) ═══════
129
+ Popovers escape to the top layer and cannot inherit --table-toolbar-* tokens
130
+ from the @scope block. Style with raw --a-* tokens — same pattern used by
131
+ tooltip-ui and toolbar-ui's spillover menu. */
132
+
133
+ [data-toolbar-popover]:not(:popover-open) {
134
+ display: none !important;
135
+ }
136
+
137
+ [data-toolbar-popover]:popover-open {
138
+ margin: 0;
139
+ padding: var(--a-space-1);
140
+ background: var(--a-canvas-bright);
141
+ color: var(--a-fg);
142
+ border: 1px solid var(--a-ui-border);
143
+ border-radius: var(--a-radius);
144
+ box-shadow: var(--a-shadow-lg);
145
+ min-width: 16rem;
146
+ font-family: inherit;
147
+ font-size: var(--a-ui-size);
148
+ display: flex;
149
+ flex-direction: column;
150
+ gap: var(--a-space-1);
151
+ }
152
+
153
+ [data-toolbar-popover] [data-popover-head] {
154
+ font-size: var(--a-ui-tiny);
155
+ text-transform: uppercase;
156
+ letter-spacing: 0.06em;
157
+ color: var(--a-fg-muted);
158
+ padding: var(--a-space-1) var(--a-ui-px, var(--a-space-2));
159
+ }
160
+
161
+ [data-toolbar-popover] [data-popover-list] {
162
+ display: flex;
163
+ flex-direction: column;
164
+ gap: var(--a-space-1);
165
+ max-height: 22rem;
166
+ overflow-y: auto;
167
+ }
168
+
169
+ [data-toolbar-popover] [data-popover-empty] {
170
+ display: block;
171
+ padding: var(--a-space-3) var(--a-space-2);
172
+ text-align: center;
173
+ }
174
+
175
+ [data-toolbar-popover] [data-popover-action] {
176
+ margin-top: var(--a-space-1);
177
+ border-top: 1px solid var(--a-border-subtle);
178
+ padding-top: var(--a-space-1);
179
+ }
180
+
181
+ /* ── Filter rows ── */
182
+
183
+ [data-toolbar-popover] [data-filter-row] {
184
+ display: grid;
185
+ grid-template-columns: 1fr minmax(8rem, 1.2fr);
186
+ align-items: center;
187
+ gap: var(--a-space-2);
188
+ padding: var(--a-space-1) var(--a-space-2);
189
+ border-radius: var(--a-radius-sm);
190
+ color: var(--a-fg-subtle);
191
+ transition:
192
+ background var(--a-duration-fast) var(--a-easing),
193
+ color var(--a-duration-fast) var(--a-easing);
194
+ }
195
+
196
+ [data-toolbar-popover] [data-filter-row]:hover {
197
+ background: var(--a-bg-hover);
198
+ color: var(--a-fg-hover);
199
+ }
200
+
201
+ [data-toolbar-popover] [data-filter-label] {
202
+ font-size: var(--a-ui-sm);
203
+ color: inherit;
204
+ white-space: nowrap;
205
+ overflow: hidden;
206
+ text-overflow: ellipsis;
207
+ }
208
+
209
+ [data-toolbar-popover] [data-filter-input] {
210
+ min-width: 0;
211
+ }
212
+
213
+ /* ── Sort rows ── */
214
+
215
+ [data-toolbar-popover] [data-sort-row] {
216
+ all: unset;
217
+ cursor: pointer;
218
+ display: flex;
219
+ align-items: center;
220
+ justify-content: space-between;
221
+ gap: var(--a-space-2);
222
+ padding: var(--a-space-1) var(--a-space-2);
223
+ font-size: var(--a-ui-sm);
224
+ color: var(--a-fg-subtle);
225
+ border-radius: var(--a-radius-sm);
226
+ transition:
227
+ background var(--a-duration-fast) var(--a-easing),
228
+ color var(--a-duration-fast) var(--a-easing);
229
+ }
230
+
231
+ [data-toolbar-popover] [data-sort-row]:hover {
232
+ background: var(--a-bg-hover);
233
+ color: var(--a-fg-hover);
234
+ }
235
+
236
+ [data-toolbar-popover] [data-sort-row][data-active] {
237
+ color: var(--a-fg);
238
+ background: var(--a-bg-selected);
239
+ font-weight: var(--a-ui-weight, var(--a-weight-medium));
240
+ }
241
+
242
+ [data-toolbar-popover] [data-sort-indicator] {
243
+ color: var(--a-fg-muted);
244
+ }
245
+
246
+ [data-toolbar-popover] [data-sort-row][data-active] [data-sort-indicator] {
247
+ color: var(--a-fg);
248
+ }
249
+
250
+ /* ── Columns rows ── */
251
+
252
+ [data-toolbar-popover] [data-columns-row] {
253
+ display: flex;
254
+ align-items: center;
255
+ gap: var(--a-space-2);
256
+ padding: var(--a-space-1) var(--a-space-2);
257
+ font-size: var(--a-ui-sm);
258
+ color: var(--a-fg-subtle);
259
+ border-radius: var(--a-radius-sm);
260
+ cursor: pointer;
261
+ transition:
262
+ background var(--a-duration-fast) var(--a-easing),
263
+ color var(--a-duration-fast) var(--a-easing);
264
+ }
265
+
266
+ [data-toolbar-popover] [data-columns-row]:hover {
267
+ background: var(--a-bg-hover);
268
+ color: var(--a-fg-hover);
269
+ }