@apliteni/apliteni-ui 0.107.0 → 0.108.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/guidelines/dense-tables.md +12 -0
- package/guidelines/state-set.md +1 -1
- package/package.json +1 -1
- package/react/dist/index.css +9 -0
- package/react/dist/index.d.ts +10 -0
- package/react/dist/index.js +10 -5
- package/src/styles/table.css +139 -11
|
@@ -74,6 +74,18 @@
|
|
|
74
74
|
|
|
75
75
|
**Except:** On a phone, a row whose long column is a paragraph stacks instead of scrolling: the short values on one line, the paragraph under them, and the header kept for screen readers. No column is dropped.
|
|
76
76
|
|
|
77
|
+
## Keep identity sub-lines as text
|
|
78
|
+
|
|
79
|
+
<!-- rule: pinned-identity-sub-lines -->
|
|
80
|
+
|
|
81
|
+
**Why:** An ellipsis cannot cut a flex or grid box, so the name spills past the cell. No specimen draws it, because the cut needs a phone viewport.
|
|
82
|
+
|
|
83
|
+
**Rule:** A pinned identity sub-line is running text.
|
|
84
|
+
|
|
85
|
+
**Do:** Write it as a sentence, with any link inside.
|
|
86
|
+
|
|
87
|
+
**Don't:** Lay a badge, a control or a picture along it, or make it a flex or grid box.
|
|
88
|
+
|
|
77
89
|
## Keep updates stable
|
|
78
90
|
|
|
79
91
|
<!-- rule: states -->
|
package/guidelines/state-set.md
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
**Don't:** Give the input a separate muted outline while the button uses `--ring`.
|
|
14
14
|
|
|
15
|
-
**Except:** A
|
|
15
|
+
**Except:** A box whose own edges are a surface's — a scroll region, or a box filling a table cell — takes `--ring-scroll` with `--ring-scroll-offset`. A box that is the outermost surface, such as a panel or card, keeps `--ring`. Text-entry controls may match `:focus-visible` on mouse focus. Both bands are declared once at the root and recomposed nowhere, because an outline leaves its offset unpainted. Adjust width and colour there.
|
|
16
16
|
|
|
17
17
|
## Busy controls
|
|
18
18
|
|
package/package.json
CHANGED
package/react/dist/index.css
CHANGED
|
@@ -291,6 +291,15 @@
|
|
|
291
291
|
outline-offset: var(--ring-offset);
|
|
292
292
|
border-radius: var(--radius-xs);
|
|
293
293
|
}
|
|
294
|
+
.rx-sort {
|
|
295
|
+
max-width: 100%;
|
|
296
|
+
min-width: 0;
|
|
297
|
+
}
|
|
298
|
+
.rx-sort__label {
|
|
299
|
+
min-width: 0;
|
|
300
|
+
overflow: hidden;
|
|
301
|
+
text-overflow: ellipsis;
|
|
302
|
+
}
|
|
294
303
|
.rx-caret {
|
|
295
304
|
margin-left: 2px;
|
|
296
305
|
width: 16px;
|
package/react/dist/index.d.ts
CHANGED
|
@@ -207,6 +207,16 @@ type Column<T> = {
|
|
|
207
207
|
num?: boolean;
|
|
208
208
|
sortable?: boolean;
|
|
209
209
|
render?: (row: T) => ReactNode;
|
|
210
|
+
/**
|
|
211
|
+
* `true` marks this column's body cells as holding one link and nothing else, so the
|
|
212
|
+
* link fills its cell and its focus ring outlines the cell rather than the words. It is
|
|
213
|
+
* the consumer's to declare because CSS counts elements and not words: a cell reading
|
|
214
|
+
* "Invoice 1162" with the number linked looks the same to a selector, and filling that
|
|
215
|
+
* one would put the number on a line of its own. The header cell is never marked — it
|
|
216
|
+
* holds a label, or a sort button that keeps its own ring.
|
|
217
|
+
* why: docs/components.md#dense-financial-tables
|
|
218
|
+
*/
|
|
219
|
+
linked?: boolean;
|
|
210
220
|
};
|
|
211
221
|
type TableSort<T> = {
|
|
212
222
|
key: (keyof T & string) | undefined;
|
package/react/dist/index.js
CHANGED
|
@@ -1284,10 +1284,15 @@ function DataTable({
|
|
|
1284
1284
|
scope: "col",
|
|
1285
1285
|
className: [pinnedIdentity && columnIndex === 0 && "ui-table__identity", c.num && "ui-table__num", c.sortable && "rx-sortable"].filter(Boolean).join(" "),
|
|
1286
1286
|
"aria-sort": sort.key === c.key ? sort.dir === 1 ? "ascending" : "descending" : c.sortable && sortIsKnown ? "none" : void 0,
|
|
1287
|
-
children: c.sortable ?
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1287
|
+
children: c.sortable ? (
|
|
1288
|
+
// The label is its own box so it can truncate: in a pinned
|
|
1289
|
+
// identity column on a phone the header is capped, and a bare
|
|
1290
|
+
// text node would push the caret out of the cell instead.
|
|
1291
|
+
/* @__PURE__ */ jsxs13("button", { type: "button", className: "rx-sort", onClick: () => onSort(c.key), children: [
|
|
1292
|
+
/* @__PURE__ */ jsx17("span", { className: "rx-sort__label", children: c.label }),
|
|
1293
|
+
caret(c.key)
|
|
1294
|
+
] })
|
|
1295
|
+
) : c.label
|
|
1291
1296
|
},
|
|
1292
1297
|
c.key
|
|
1293
1298
|
)
|
|
@@ -1305,7 +1310,7 @@ function DataTable({
|
|
|
1305
1310
|
onChange: () => onToggle(r.name)
|
|
1306
1311
|
}
|
|
1307
1312
|
) }) : null,
|
|
1308
|
-
columns.map((c, columnIndex) => /* @__PURE__ */ jsx17("td", { className: [c.num && "ui-table__num", pinnedIdentity && columnIndex === 0 && "ui-table__identity"].filter(Boolean).join(" "), children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
|
|
1313
|
+
columns.map((c, columnIndex) => /* @__PURE__ */ jsx17("td", { className: [c.num && "ui-table__num", pinnedIdentity && columnIndex === 0 && "ui-table__identity", c.linked && "ui-table__linked"].filter(Boolean).join(" "), children: c.render ? c.render(r) : String(r[c.key]) }, c.key))
|
|
1309
1314
|
] }, r.name))
|
|
1310
1315
|
] })
|
|
1311
1316
|
]
|
package/src/styles/table.css
CHANGED
|
@@ -161,12 +161,42 @@
|
|
|
161
161
|
top: 50%;
|
|
162
162
|
translate: 0 -50%;
|
|
163
163
|
}
|
|
164
|
-
/* No focus rule here
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
164
|
+
/* No generic focus rule here. #510 gave a cell link the ring because `base.css`
|
|
165
|
+
reached no bare `a`; #587 put `a` on that shared list, so this file only keeps
|
|
166
|
+
the filled-cell override below. why: guidelines/state-set.md#focus-rings */
|
|
167
|
+
/* The handle: a cell MARKED `.ui-table__linked` holds one link and nothing else, so the
|
|
168
|
+
link fills the cell and its ring outlines the cell rather than the words. The cell hands
|
|
169
|
+
its padding over and nothing moves. `height: 1px` is the cell stating no height of its
|
|
170
|
+
own, which a percentage height on the link can resolve against; a cell is stretched to
|
|
171
|
+
its row either way, so no row changes.
|
|
172
|
+
The mark is the consumer's because a selector counts elements and not words: `:only-child`
|
|
173
|
+
reads `<td>Invoice <a>1162</a></td>` as a lone link too. The link is named rather than
|
|
174
|
+
denied — a denylist of the anchors that own their box went stale twice (#510).
|
|
175
|
+
why: docs/components.md#dense-financial-tables, #500
|
|
176
|
+
held by: src/styles/table-focus.test.js */
|
|
177
|
+
.ui-table td.ui-table__linked:has(> :is(a:not([class]), a.ui-identity):only-child) { padding: 0; height: 1px; }
|
|
178
|
+
.ui-table td.ui-table__linked > :is(a:not([class]), a.ui-identity):only-child {
|
|
179
|
+
box-sizing: border-box;
|
|
180
|
+
display: flex;
|
|
181
|
+
align-items: center;
|
|
182
|
+
width: 100%;
|
|
183
|
+
height: 100%;
|
|
184
|
+
padding: var(--space-4) var(--space-4) var(--space-4) 0;
|
|
185
|
+
border-radius: var(--radius-xs);
|
|
186
|
+
}
|
|
187
|
+
.ui-table--dense td.ui-table__linked > :is(a:not([class]), a.ui-identity):only-child { padding: var(--space-2) var(--space-3); }
|
|
188
|
+
.ui-table--compact td.ui-table__linked > :is(a:not([class]), a.ui-identity):only-child { padding: var(--space-1) var(--space-3); }
|
|
189
|
+
/* Zebra insets the row's END cells only, so the fill does too: a marked identity behind a
|
|
190
|
+
selection column is a middle cell, and a step it never gave up would move its name. */
|
|
191
|
+
.ui-table--zebra td.ui-table__linked:first-child > :is(a:not([class]), a.ui-identity):only-child { padding-left: var(--space-3); }
|
|
192
|
+
.ui-table--zebra td.ui-table__linked:last-child > :is(a:not([class]), a.ui-identity):only-child { padding-right: var(--space-3); }
|
|
193
|
+
/* The inward band, the one a scroll region takes: a filled cell's edges are the
|
|
194
|
+
table's, where the pinned column's border and the scroller's 4px side padding
|
|
195
|
+
cut an outset ring away. Its 1px gap is unpainted, and the ring is the outline. */
|
|
196
|
+
.ui-table td.ui-table__linked > :is(a:not([class]), a.ui-identity):only-child:focus-visible {
|
|
197
|
+
outline: var(--ring-scroll);
|
|
198
|
+
outline-offset: var(--ring-scroll-offset);
|
|
199
|
+
}
|
|
170
200
|
|
|
171
201
|
.ui-table__num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
172
202
|
/* A numeric column is sized to its content while .ui-table__title claims the rest,
|
|
@@ -356,14 +386,78 @@
|
|
|
356
386
|
a.ui-identity:hover { text-decoration: underline; }
|
|
357
387
|
.ui-identity:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); border-radius: var(--radius-xs); }
|
|
358
388
|
@media (max-width: 720px) {
|
|
359
|
-
|
|
389
|
+
/* One line wide: nothing wraps, and --ui-table-identity-max is the ceiling a consumer
|
|
390
|
+
retunes. The cut takes NO margin — the cell is sticky and the next column scrolls
|
|
391
|
+
under it, so whatever the cut lets out is painted over that column. A child's ring
|
|
392
|
+
needs 3px and the cell's padding is 12px, so the room comes from the padding.
|
|
393
|
+
why: docs/components.md#dense-financial-tables, #500, #513 */
|
|
394
|
+
.ui-table--pinned .ui-table__identity {
|
|
395
|
+
width: 1%;
|
|
396
|
+
max-width: var(--ui-table-identity-max);
|
|
397
|
+
white-space: nowrap;
|
|
398
|
+
overflow: clip;
|
|
399
|
+
text-overflow: ellipsis;
|
|
400
|
+
}
|
|
401
|
+
/* A sub-line is one line of running text and cuts itself: the cell's ellipsis cannot
|
|
402
|
+
reach into a child block. 4px is a focused link's ring, inside the cell's padding.
|
|
403
|
+
Nothing is capped here — block layout already holds a child to the cell. */
|
|
404
|
+
.ui-table--pinned .ui-table__identity > * {
|
|
405
|
+
overflow: clip;
|
|
406
|
+
overflow-clip-margin: var(--space-1);
|
|
407
|
+
text-overflow: ellipsis;
|
|
408
|
+
}
|
|
409
|
+
/* A picture has no shorter form, so it keeps its intrinsic size and the cell cuts it.
|
|
410
|
+
Nothing caps it, so write neither a cap nor a reset of one here. #513 */
|
|
411
|
+
.ui-table--pinned .ui-table__identity :is(svg, img, canvas, video, picture) {
|
|
412
|
+
overflow: visible;
|
|
413
|
+
text-overflow: clip;
|
|
414
|
+
}
|
|
415
|
+
.ui-table--pinned .ui-table__identity > *:has(:is(svg, img, canvas, video, picture)) {
|
|
416
|
+
text-overflow: clip;
|
|
417
|
+
}
|
|
418
|
+
/* A control's label is text, so it yields: held to the cell and cut at its END. A kit
|
|
419
|
+
control centres its label, and a capped one that centres loses the front of it. #513 */
|
|
420
|
+
.ui-table--pinned .ui-table__identity :is(.ui-btn, .ui-nav__item, .ui-badge, button, input, select, textarea) {
|
|
421
|
+
max-width: 100%;
|
|
422
|
+
min-width: auto;
|
|
423
|
+
overflow: hidden;
|
|
424
|
+
justify-content: flex-start;
|
|
425
|
+
text-align: start;
|
|
426
|
+
}
|
|
427
|
+
/* `a:not([class])` above is an inline-block so a WRAPPING title link paints one ring.
|
|
428
|
+
Nothing wraps here, and an inline-block is atomic: `text-overflow` cannot cut inside
|
|
429
|
+
one, so the link came out cut mid-glyph. Inline, it draws one fragment and one
|
|
430
|
+
ring, and the line's ellipsis reaches it. #513 */
|
|
431
|
+
.ui-table--pinned .ui-table__identity a:not([class]) { display: inline; }
|
|
432
|
+
/* Except where the fill gave the lone link the cell's box: it cuts as a block box. */
|
|
433
|
+
.ui-table--pinned .ui-table__identity.ui-table__linked > a:not([class]):only-child {
|
|
434
|
+
display: block;
|
|
435
|
+
align-content: center;
|
|
436
|
+
}
|
|
437
|
+
/* The ring goes on the LINE: a link longer than the cell runs past the cut, so a ring
|
|
438
|
+
around the link has no surface to draw its right side on. Inward, like every ring
|
|
439
|
+
the kit puts in a clipped region. why: docs/foundations.md#the-focus-ring, #513 */
|
|
440
|
+
.ui-table--pinned .ui-table__identity > *:has(a:focus-visible) {
|
|
441
|
+
outline: var(--ring-scroll);
|
|
442
|
+
outline-offset: var(--ring-scroll-offset);
|
|
443
|
+
border-radius: var(--radius-xs);
|
|
444
|
+
}
|
|
445
|
+
.ui-table--pinned .ui-table__identity > * a:focus-visible { outline: none; }
|
|
446
|
+
.ui-table--pinned .ui-table__identity:not(.ui-table__linked):has(> a:focus-visible) {
|
|
447
|
+
outline: var(--ring-scroll);
|
|
448
|
+
outline-offset: var(--ring-scroll-offset);
|
|
449
|
+
border-radius: var(--radius-xs);
|
|
450
|
+
}
|
|
451
|
+
.ui-table--pinned .ui-table__identity:not(.ui-table__linked) > a:focus-visible { outline: none; }
|
|
360
452
|
.ui-table--pinned .ui-identity__logo { display: none; }
|
|
361
|
-
|
|
362
|
-
|
|
453
|
+
/* An inline-flex box is the one child block layout does not hold to the cell, so the
|
|
454
|
+
cap belongs on it. The whole name stays in the DOM; only the drawn line is cut. */
|
|
455
|
+
.ui-table--pinned .ui-identity { max-width: 100%; }
|
|
456
|
+
.ui-table--pinned .ui-identity__name { min-width: 0; max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
363
457
|
}
|
|
364
458
|
|
|
365
459
|
/* One width reserves the selection track and offsets the adjacent identity. */
|
|
366
|
-
.ui-table--pinned { --ui-table-selection-width: var(--space-12); }
|
|
460
|
+
.ui-table--pinned { --ui-table-selection-width: var(--space-12); --ui-table-identity-max: min(var(--panel-sm), 50vw); }
|
|
367
461
|
.ui-table--pinned .ui-table__selection { box-sizing: border-box; width: var(--ui-table-selection-width); min-width: var(--ui-table-selection-width); max-width: var(--ui-table-selection-width); padding-inline: 0; text-align: center; }
|
|
368
462
|
.ui-table--pinned .ui-table__selection + .ui-table__identity { left: var(--ui-table-selection-width); }
|
|
369
463
|
|
|
@@ -413,10 +507,44 @@ a.ui-identity:hover { text-decoration: underline; }
|
|
|
413
507
|
.ui-table.ui-table--stack.ui-table--pinned > :where(thead, tbody) > tr > :where(.ui-table__identity, .ui-table__selection) {
|
|
414
508
|
position: static; width: auto; min-width: 0; max-width: none;
|
|
415
509
|
padding-inline: 0; border-right: 0; text-align: left;
|
|
510
|
+
/* The cap's cut goes with the cap: a card is as wide as the row, so there is
|
|
511
|
+
nothing to cut, and a clip box here would only reach a focused link's ring. */
|
|
512
|
+
overflow: visible; text-overflow: clip;
|
|
416
513
|
}
|
|
514
|
+
/* The name is what the card is about, and a card has the width to wrap it. #500 put
|
|
515
|
+
`nowrap` on it for the narrow column, so the stacked row takes that off too. */
|
|
417
516
|
.ui-table.ui-table--stack.ui-table--pinned .ui-identity__name {
|
|
418
517
|
position: static; width: auto; height: auto; max-width: none;
|
|
419
|
-
overflow: visible; clip-path: none;
|
|
518
|
+
overflow: visible; clip-path: none; white-space: normal; text-overflow: clip;
|
|
519
|
+
}
|
|
520
|
+
.ui-table.ui-table--stack.ui-table--pinned .ui-table__identity > * {
|
|
521
|
+
overflow: visible; text-overflow: clip;
|
|
522
|
+
}
|
|
523
|
+
.ui-table.ui-table--stack.ui-table--pinned .ui-table__identity > * a {
|
|
524
|
+
max-width: none; overflow: visible; text-overflow: clip;
|
|
525
|
+
}
|
|
526
|
+
.ui-table.ui-table--stack.ui-table--pinned .ui-table__identity > a:not([class]) {
|
|
527
|
+
display: inline-block;
|
|
528
|
+
}
|
|
529
|
+
.ui-table.ui-table--stack.ui-table--pinned .ui-table__identity:not(.ui-table__linked):has(> a:focus-visible) {
|
|
530
|
+
outline: none;
|
|
531
|
+
}
|
|
532
|
+
.ui-table.ui-table--stack.ui-table--pinned .ui-table__identity:not(.ui-table__linked) > a:focus-visible {
|
|
533
|
+
outline: var(--ring);
|
|
534
|
+
outline-offset: var(--ring-offset);
|
|
535
|
+
}
|
|
536
|
+
/* A card has no column to fill: every cell is a block on its own line and the ROW pays
|
|
537
|
+
the inset, so the fill's padding would stand the link a step right of the card's text
|
|
538
|
+
edge and the cell's `height: 1px` would hold a block cell to one pixel. Written through
|
|
539
|
+
the row like the resets above it, so it outranks the fill and its density. */
|
|
540
|
+
.ui-table.ui-table--stack > tbody > tr > td.ui-table__linked:has(> :is(a:not([class]), a.ui-identity):only-child) { height: auto; }
|
|
541
|
+
.ui-table.ui-table--stack > tbody > tr > td.ui-table__linked > :is(a:not([class]), a.ui-identity):only-child {
|
|
542
|
+
width: auto; height: auto; padding: 0;
|
|
543
|
+
}
|
|
544
|
+
/* The stacked card gives the link its own box again, so it takes the outset band. */
|
|
545
|
+
.ui-table.ui-table--stack > tbody > tr > td.ui-table__linked > a:only-child:focus-visible {
|
|
546
|
+
outline: var(--ring);
|
|
547
|
+
outline-offset: var(--ring-offset);
|
|
420
548
|
}
|
|
421
549
|
/* The bleed now pays for the row's inset instead of the end cells', so the cells still
|
|
422
550
|
start on the card's own text edge and the outline keeps its room on both sides. Same
|