@jirawatpyk/aura-react 5.9.0 → 5.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/README.md +9 -0
- package/dist/aura.bundle.js +49 -19
- package/dist/cjs/index.cjs +49 -19
- package/dist/esm/DropdownMenu.js +1 -1
- package/dist/esm/Menu.js +35 -11
- package/dist/esm/Tabs.js +13 -7
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/styles.css +21 -1
- package/dist/styles.layer.css +21 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -186,6 +186,15 @@ DataTable with sort and page in the URL (4.17): `onStateChange={({ sort, page })
|
|
|
186
186
|
- **Time zone**: `timeZone` on AuraProvider, DatePicker, DateRangePicker and Calendar decides "today" (the marker, `min`/`max="today"`, the first month shown); or pass `today` as an ISO date. `todayIn('Asia/Bangkok')` is exported from the root and `/server`.
|
|
187
187
|
- **Toasts** queue past three instead of dropping: six in a row all show, in order, three at a time.
|
|
188
188
|
|
|
189
|
+
## 5.10 — Chamber-OS addendum 10 (adopting 5.9)
|
|
190
|
+
|
|
191
|
+
- **Tabs `variant="segmented"`** (item 72): the tab list as a pill track with the selected tab raised — SegmentedControl's look — keeping every Tabs behaviour (tab / tabpanel roles, `keepMounted`, `activation`, `tabProps`). `fullWidth` stretches the track and shares it equally. Tabs are 44px on touch screens, the list doesn't scroll so the focus ring is never clipped, and in forced colours the selected tab gets a `Highlight` outline. Default (`underline`) output is unchanged.
|
|
192
|
+
```tsx
|
|
193
|
+
<Tabs label="Payment method" variant="segmented" fullWidth keepMounted activation="manual" … />
|
|
194
|
+
```
|
|
195
|
+
- **Disabled menu items stay reachable** (item 73): a disabled `MenuItem` is `aria-disabled` rather than a disabled button, so the arrow keys reach it and screen readers announce it as dimmed (WAI-ARIA APG); choosing it does nothing and keeps the menu open. `disabledReason` shows in place of the hint and is read with the item ("Again in 5 min"). A menu with nothing to focus takes focus itself, so Escape and Tab still close it and focus returns to the trigger. Enabled items are unchanged; tests that expected the arrows to skip a disabled item now land on it.
|
|
196
|
+
- **SegmentedControl boundary** (item 74): measured again — the selected pill's inset edge (`aura-border-control`, zinc-500) is 4.6:1 against the light track (zinc-50) and 5.8:1 in dark; CI now measures the rendered edge in both themes (≥3:1). A value near 2.5:1 is zinc-400, dark mode's `aura-border-control`: check that the light page isn't picking up the dark tokens (a `.dark` / `data-theme="dark"` ancestor, or a theme override).
|
|
197
|
+
|
|
189
198
|
## 5.9 — Chamber-OS addendum 9 (pay sheet)
|
|
190
199
|
|
|
191
200
|
- **Drawer** (item 70): other attributes (`id`, `data-*`, `aria-*`) go on the `.aura-drawer` panel; `closeLabel` names the close button and its tooltip after what it closes ("Close payment drawer" — pass it in the page's language); `closeProps` puts attributes on that button (`{ 'data-testid': 'pay-sheet-close' }`; its `onClick` runs first and can `preventDefault()` to keep the drawer open). Without them the output is as before; focus trap, Escape, scrim and focus return are unchanged.
|
package/dist/aura.bundle.js
CHANGED
|
@@ -1233,13 +1233,14 @@ window.Aura = (() => {
|
|
|
1233
1233
|
// src/Menu.tsx
|
|
1234
1234
|
var React8 = __toESM(require_react(), 1);
|
|
1235
1235
|
var import_react_dom = __toESM(require_react_dom(), 1);
|
|
1236
|
+
var ITEMS = '[role^="menuitem"]';
|
|
1236
1237
|
var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
1237
1238
|
const own = React8.useRef(null), merged = useMergedRef(ref, own);
|
|
1238
1239
|
const posState = React8.useState(null);
|
|
1239
1240
|
const pos = posState[0], setPos = posState[1];
|
|
1240
1241
|
const items2 = props.items || [];
|
|
1241
1242
|
const mounted = useMounted();
|
|
1242
|
-
const headerId = uid();
|
|
1243
|
+
const headerId = uid(), itemId = uid();
|
|
1243
1244
|
const hasHeader = props.header != null && props.header !== false && props.header !== "";
|
|
1244
1245
|
const Link = useLinkComponent(props.linkComponent);
|
|
1245
1246
|
useIsoLayoutEffect(
|
|
@@ -1267,8 +1268,8 @@ window.Aura = (() => {
|
|
|
1267
1268
|
hadHeader.current = hasHeader;
|
|
1268
1269
|
const a = document.activeElement;
|
|
1269
1270
|
if (own.current && (!a || a === document.body)) {
|
|
1270
|
-
const first = own.current.querySelector(
|
|
1271
|
-
|
|
1271
|
+
const first = own.current.querySelector(ITEMS);
|
|
1272
|
+
(first || own.current.querySelector('[role="menu"]') || own.current).focus();
|
|
1272
1273
|
}
|
|
1273
1274
|
},
|
|
1274
1275
|
[hasHeader]
|
|
@@ -1276,8 +1277,11 @@ window.Aura = (() => {
|
|
|
1276
1277
|
React8.useEffect(
|
|
1277
1278
|
function() {
|
|
1278
1279
|
if (!mounted) return;
|
|
1279
|
-
const first = own.current && own.current.querySelector(
|
|
1280
|
-
if (
|
|
1280
|
+
const first = own.current && own.current.querySelector(ITEMS);
|
|
1281
|
+
if (props.autoFocus !== false) {
|
|
1282
|
+
if (first) first.focus();
|
|
1283
|
+
else if (own.current) (own.current.querySelector('[role="menu"]') || own.current).focus();
|
|
1284
|
+
}
|
|
1281
1285
|
function outside(e) {
|
|
1282
1286
|
if (own.current && !own.current.contains(e.target) && !(props.anchor && props.anchor.contains(e.target)))
|
|
1283
1287
|
props.onClose(false);
|
|
@@ -1297,16 +1301,18 @@ window.Aura = (() => {
|
|
|
1297
1301
|
[mounted]
|
|
1298
1302
|
);
|
|
1299
1303
|
function onKeyDown(e) {
|
|
1300
|
-
const list2 = Array.prototype.slice.call(
|
|
1301
|
-
own.current.querySelectorAll('[role^="menuitem"]:not([disabled])')
|
|
1302
|
-
);
|
|
1304
|
+
const list2 = Array.prototype.slice.call(own.current.querySelectorAll(ITEMS));
|
|
1303
1305
|
const i = list2.indexOf(document.activeElement);
|
|
1306
|
+
if (!list2.length && e.key !== "Escape" && e.key !== "Tab") {
|
|
1307
|
+
e.stopPropagation();
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1304
1310
|
if (e.key === "ArrowDown") {
|
|
1305
1311
|
e.preventDefault();
|
|
1306
1312
|
list2[(i + 1) % list2.length].focus();
|
|
1307
1313
|
} else if (e.key === "ArrowUp") {
|
|
1308
1314
|
e.preventDefault();
|
|
1309
|
-
list2[(i - 1 + list2.length) % list2.length].focus();
|
|
1315
|
+
list2[i < 0 ? list2.length - 1 : (i - 1 + list2.length) % list2.length].focus();
|
|
1310
1316
|
} else if (e.key === "Home") {
|
|
1311
1317
|
e.preventDefault();
|
|
1312
1318
|
list2[0].focus();
|
|
@@ -1345,6 +1351,10 @@ window.Aura = (() => {
|
|
|
1345
1351
|
it.hint ? /* @__PURE__ */ React8.createElement("span", { key: "h", className: "aura-menu__hint" }, it.hint) : null
|
|
1346
1352
|
];
|
|
1347
1353
|
const cls = cx("aura-menu__item", it.tone === "danger" && "aura-menu__item--danger");
|
|
1354
|
+
const reasonId = it.disabled && it.disabledReason ? itemId + "-r" + i : void 0;
|
|
1355
|
+
if (reasonId)
|
|
1356
|
+
body[2] = /* Hidden from the name (it would be read twice); aria-describedby still reads it. */
|
|
1357
|
+
/* @__PURE__ */ React8.createElement("span", { key: "h", id: reasonId, className: "aura-menu__hint aura-menu__reason", "aria-hidden": true }, it.disabledReason);
|
|
1348
1358
|
if (it.href && !it.disabled)
|
|
1349
1359
|
return /* @__PURE__ */ React8.createElement(
|
|
1350
1360
|
Link,
|
|
@@ -1369,11 +1379,13 @@ window.Aura = (() => {
|
|
|
1369
1379
|
key: i,
|
|
1370
1380
|
type: "button",
|
|
1371
1381
|
tabIndex: -1,
|
|
1372
|
-
disabled: it.disabled,
|
|
1382
|
+
"aria-disabled": it.disabled ? true : void 0,
|
|
1383
|
+
"aria-describedby": reasonId,
|
|
1373
1384
|
role: isRadio ? "menuitemradio" : isCheck ? "menuitemcheckbox" : "menuitem",
|
|
1374
1385
|
"aria-checked": isRadio || isCheck ? !!it.checked : void 0,
|
|
1375
1386
|
className: cls,
|
|
1376
1387
|
onClick: function() {
|
|
1388
|
+
if (it.disabled) return;
|
|
1377
1389
|
if (it.onSelect) it.onSelect();
|
|
1378
1390
|
if (!it.keepOpen) props.onClose(true);
|
|
1379
1391
|
}
|
|
@@ -1402,7 +1414,19 @@ window.Aura = (() => {
|
|
|
1402
1414
|
}
|
|
1403
1415
|
},
|
|
1404
1416
|
props.header
|
|
1405
|
-
), /* @__PURE__ */ React8.createElement("div", { role: "menu", "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React8.createElement(
|
|
1417
|
+
), /* @__PURE__ */ React8.createElement("div", { role: "menu", tabIndex: -1, "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React8.createElement(
|
|
1418
|
+
"div",
|
|
1419
|
+
{
|
|
1420
|
+
ref: merged,
|
|
1421
|
+
role: "menu",
|
|
1422
|
+
tabIndex: -1,
|
|
1423
|
+
"aria-label": props.label,
|
|
1424
|
+
className: "aura-menu",
|
|
1425
|
+
onKeyDown,
|
|
1426
|
+
style
|
|
1427
|
+
},
|
|
1428
|
+
list
|
|
1429
|
+
);
|
|
1406
1430
|
return mounted ? (0, import_react_dom.createPortal)(el, document.body) : null;
|
|
1407
1431
|
});
|
|
1408
1432
|
|
|
@@ -1417,7 +1441,7 @@ window.Aura = (() => {
|
|
|
1417
1441
|
function() {
|
|
1418
1442
|
if (!anchor || !toLast.current || !menuRef.current) return;
|
|
1419
1443
|
toLast.current = false;
|
|
1420
|
-
const list = menuRef.current.querySelectorAll('[role^="menuitem"]
|
|
1444
|
+
const list = menuRef.current.querySelectorAll('[role^="menuitem"]');
|
|
1421
1445
|
if (list.length) list[list.length - 1].focus();
|
|
1422
1446
|
},
|
|
1423
1447
|
[anchor]
|
|
@@ -5876,6 +5900,12 @@ window.Aura = (() => {
|
|
|
5876
5900
|
})[0] || firstOn || items2[0];
|
|
5877
5901
|
const Link = useLinkComponent(props.linkComponent);
|
|
5878
5902
|
const manual = props.activation === "manual";
|
|
5903
|
+
const seg = props.variant === "segmented";
|
|
5904
|
+
const listCls = cx("aura-tabs__list", seg && "aura-segmented", seg && props.fullWidth && "is-full");
|
|
5905
|
+
const tabCls = function(on, extra) {
|
|
5906
|
+
return cx("aura-tab", on && "is-active", seg && "aura-segmented__option", seg && on && "is-selected", extra);
|
|
5907
|
+
};
|
|
5908
|
+
const rootCls = cx("aura-tabs", seg && "aura-tabs--segmented");
|
|
5879
5909
|
const [focusId, setFocusId] = React32.useState(null);
|
|
5880
5910
|
items2.forEach(function(t) {
|
|
5881
5911
|
const al = t.tabProps && t.tabProps["aria-label"];
|
|
@@ -5891,9 +5921,9 @@ window.Aura = (() => {
|
|
|
5891
5921
|
{
|
|
5892
5922
|
ref,
|
|
5893
5923
|
"aria-label": props.label,
|
|
5894
|
-
className: cx("aura-tabs
|
|
5924
|
+
className: cx(rootCls, "aura-tabs--links", props.className)
|
|
5895
5925
|
},
|
|
5896
|
-
/* @__PURE__ */ React32.createElement("div", { className:
|
|
5926
|
+
/* @__PURE__ */ React32.createElement("div", { className: listCls }, items2.map(function(t) {
|
|
5897
5927
|
const on = t.id === cur;
|
|
5898
5928
|
const inner = [
|
|
5899
5929
|
t.icon ? /* @__PURE__ */ React32.createElement(Icon, { key: "i", name: t.icon }) : null,
|
|
@@ -5917,7 +5947,7 @@ window.Aura = (() => {
|
|
|
5917
5947
|
{
|
|
5918
5948
|
...omit(own, ["onClick"]),
|
|
5919
5949
|
key: t.id,
|
|
5920
|
-
className: cx("
|
|
5950
|
+
className: cx(tabCls(false), "is-disabled", own.className),
|
|
5921
5951
|
"aria-disabled": true
|
|
5922
5952
|
},
|
|
5923
5953
|
inner
|
|
@@ -5927,7 +5957,7 @@ window.Aura = (() => {
|
|
|
5927
5957
|
...own,
|
|
5928
5958
|
key: t.id,
|
|
5929
5959
|
href: t.href,
|
|
5930
|
-
className:
|
|
5960
|
+
className: tabCls(on, own.className),
|
|
5931
5961
|
"aria-current": on ? "page" : void 0,
|
|
5932
5962
|
onClick: function(e) {
|
|
5933
5963
|
if (own.onClick) own.onClick(e);
|
|
@@ -5951,12 +5981,12 @@ window.Aura = (() => {
|
|
|
5951
5981
|
const stop = manual && focusId && items2.filter(function(t) {
|
|
5952
5982
|
return t.id === focusId && !t.disabled;
|
|
5953
5983
|
})[0] || current2;
|
|
5954
|
-
return /* @__PURE__ */ React32.createElement("div", { ref, className: cx(
|
|
5984
|
+
return /* @__PURE__ */ React32.createElement("div", { ref, className: cx(rootCls, props.className) }, /* @__PURE__ */ React32.createElement(
|
|
5955
5985
|
"div",
|
|
5956
5986
|
{
|
|
5957
5987
|
role: "tablist",
|
|
5958
5988
|
"aria-label": props.label,
|
|
5959
|
-
className:
|
|
5989
|
+
className: listCls,
|
|
5960
5990
|
onBlur: manual ? function(e) {
|
|
5961
5991
|
if (!e.currentTarget.contains(e.relatedTarget)) setFocusId(null);
|
|
5962
5992
|
} : void 0,
|
|
@@ -5999,7 +6029,7 @@ window.Aura = (() => {
|
|
|
5999
6029
|
ref: function(el) {
|
|
6000
6030
|
refs.current[t.id] = el;
|
|
6001
6031
|
},
|
|
6002
|
-
className:
|
|
6032
|
+
className: tabCls(on, own.className),
|
|
6003
6033
|
onFocus: manual ? function(e) {
|
|
6004
6034
|
if (own.onFocus) own.onFocus(e);
|
|
6005
6035
|
setFocusId(t.id);
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -1215,13 +1215,14 @@ var IconButton = React7.forwardRef(function IconButton2(props, ref) {
|
|
|
1215
1215
|
// src/Menu.tsx
|
|
1216
1216
|
var React8 = __toESM(require("react"), 1);
|
|
1217
1217
|
var import_react_dom = require("react-dom");
|
|
1218
|
+
var ITEMS = '[role^="menuitem"]';
|
|
1218
1219
|
var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
1219
1220
|
const own = React8.useRef(null), merged = useMergedRef(ref, own);
|
|
1220
1221
|
const posState = React8.useState(null);
|
|
1221
1222
|
const pos = posState[0], setPos = posState[1];
|
|
1222
1223
|
const items2 = props.items || [];
|
|
1223
1224
|
const mounted = useMounted();
|
|
1224
|
-
const headerId = uid();
|
|
1225
|
+
const headerId = uid(), itemId = uid();
|
|
1225
1226
|
const hasHeader = props.header != null && props.header !== false && props.header !== "";
|
|
1226
1227
|
const Link = useLinkComponent(props.linkComponent);
|
|
1227
1228
|
useIsoLayoutEffect(
|
|
@@ -1249,8 +1250,8 @@ var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
|
1249
1250
|
hadHeader.current = hasHeader;
|
|
1250
1251
|
const a = document.activeElement;
|
|
1251
1252
|
if (own.current && (!a || a === document.body)) {
|
|
1252
|
-
const first = own.current.querySelector(
|
|
1253
|
-
|
|
1253
|
+
const first = own.current.querySelector(ITEMS);
|
|
1254
|
+
(first || own.current.querySelector('[role="menu"]') || own.current).focus();
|
|
1254
1255
|
}
|
|
1255
1256
|
},
|
|
1256
1257
|
[hasHeader]
|
|
@@ -1258,8 +1259,11 @@ var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
|
1258
1259
|
React8.useEffect(
|
|
1259
1260
|
function() {
|
|
1260
1261
|
if (!mounted) return;
|
|
1261
|
-
const first = own.current && own.current.querySelector(
|
|
1262
|
-
if (
|
|
1262
|
+
const first = own.current && own.current.querySelector(ITEMS);
|
|
1263
|
+
if (props.autoFocus !== false) {
|
|
1264
|
+
if (first) first.focus();
|
|
1265
|
+
else if (own.current) (own.current.querySelector('[role="menu"]') || own.current).focus();
|
|
1266
|
+
}
|
|
1263
1267
|
function outside(e) {
|
|
1264
1268
|
if (own.current && !own.current.contains(e.target) && !(props.anchor && props.anchor.contains(e.target)))
|
|
1265
1269
|
props.onClose(false);
|
|
@@ -1279,16 +1283,18 @@ var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
|
1279
1283
|
[mounted]
|
|
1280
1284
|
);
|
|
1281
1285
|
function onKeyDown(e) {
|
|
1282
|
-
const list2 = Array.prototype.slice.call(
|
|
1283
|
-
own.current.querySelectorAll('[role^="menuitem"]:not([disabled])')
|
|
1284
|
-
);
|
|
1286
|
+
const list2 = Array.prototype.slice.call(own.current.querySelectorAll(ITEMS));
|
|
1285
1287
|
const i = list2.indexOf(document.activeElement);
|
|
1288
|
+
if (!list2.length && e.key !== "Escape" && e.key !== "Tab") {
|
|
1289
|
+
e.stopPropagation();
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1286
1292
|
if (e.key === "ArrowDown") {
|
|
1287
1293
|
e.preventDefault();
|
|
1288
1294
|
list2[(i + 1) % list2.length].focus();
|
|
1289
1295
|
} else if (e.key === "ArrowUp") {
|
|
1290
1296
|
e.preventDefault();
|
|
1291
|
-
list2[(i - 1 + list2.length) % list2.length].focus();
|
|
1297
|
+
list2[i < 0 ? list2.length - 1 : (i - 1 + list2.length) % list2.length].focus();
|
|
1292
1298
|
} else if (e.key === "Home") {
|
|
1293
1299
|
e.preventDefault();
|
|
1294
1300
|
list2[0].focus();
|
|
@@ -1327,6 +1333,10 @@ var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
|
1327
1333
|
it.hint ? /* @__PURE__ */ React8.createElement("span", { key: "h", className: "aura-menu__hint" }, it.hint) : null
|
|
1328
1334
|
];
|
|
1329
1335
|
const cls = cx("aura-menu__item", it.tone === "danger" && "aura-menu__item--danger");
|
|
1336
|
+
const reasonId = it.disabled && it.disabledReason ? itemId + "-r" + i : void 0;
|
|
1337
|
+
if (reasonId)
|
|
1338
|
+
body[2] = /* Hidden from the name (it would be read twice); aria-describedby still reads it. */
|
|
1339
|
+
/* @__PURE__ */ React8.createElement("span", { key: "h", id: reasonId, className: "aura-menu__hint aura-menu__reason", "aria-hidden": true }, it.disabledReason);
|
|
1330
1340
|
if (it.href && !it.disabled)
|
|
1331
1341
|
return /* @__PURE__ */ React8.createElement(
|
|
1332
1342
|
Link,
|
|
@@ -1351,11 +1361,13 @@ var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
|
1351
1361
|
key: i,
|
|
1352
1362
|
type: "button",
|
|
1353
1363
|
tabIndex: -1,
|
|
1354
|
-
disabled: it.disabled,
|
|
1364
|
+
"aria-disabled": it.disabled ? true : void 0,
|
|
1365
|
+
"aria-describedby": reasonId,
|
|
1355
1366
|
role: isRadio ? "menuitemradio" : isCheck ? "menuitemcheckbox" : "menuitem",
|
|
1356
1367
|
"aria-checked": isRadio || isCheck ? !!it.checked : void 0,
|
|
1357
1368
|
className: cls,
|
|
1358
1369
|
onClick: function() {
|
|
1370
|
+
if (it.disabled) return;
|
|
1359
1371
|
if (it.onSelect) it.onSelect();
|
|
1360
1372
|
if (!it.keepOpen) props.onClose(true);
|
|
1361
1373
|
}
|
|
@@ -1384,7 +1396,19 @@ var Menu = React8.forwardRef(function Menu2(props, ref) {
|
|
|
1384
1396
|
}
|
|
1385
1397
|
},
|
|
1386
1398
|
props.header
|
|
1387
|
-
), /* @__PURE__ */ React8.createElement("div", { role: "menu", "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React8.createElement(
|
|
1399
|
+
), /* @__PURE__ */ React8.createElement("div", { role: "menu", tabIndex: -1, "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React8.createElement(
|
|
1400
|
+
"div",
|
|
1401
|
+
{
|
|
1402
|
+
ref: merged,
|
|
1403
|
+
role: "menu",
|
|
1404
|
+
tabIndex: -1,
|
|
1405
|
+
"aria-label": props.label,
|
|
1406
|
+
className: "aura-menu",
|
|
1407
|
+
onKeyDown,
|
|
1408
|
+
style
|
|
1409
|
+
},
|
|
1410
|
+
list
|
|
1411
|
+
);
|
|
1388
1412
|
return mounted ? (0, import_react_dom.createPortal)(el, document.body) : null;
|
|
1389
1413
|
});
|
|
1390
1414
|
|
|
@@ -1399,7 +1423,7 @@ var DropdownMenu = React9.forwardRef(function DropdownMenu2(props, ref) {
|
|
|
1399
1423
|
function() {
|
|
1400
1424
|
if (!anchor || !toLast.current || !menuRef.current) return;
|
|
1401
1425
|
toLast.current = false;
|
|
1402
|
-
const list = menuRef.current.querySelectorAll('[role^="menuitem"]
|
|
1426
|
+
const list = menuRef.current.querySelectorAll('[role^="menuitem"]');
|
|
1403
1427
|
if (list.length) list[list.length - 1].focus();
|
|
1404
1428
|
},
|
|
1405
1429
|
[anchor]
|
|
@@ -5858,6 +5882,12 @@ var Tabs = React32.forwardRef(function Tabs2(props, ref) {
|
|
|
5858
5882
|
})[0] || firstOn || items2[0];
|
|
5859
5883
|
const Link = useLinkComponent(props.linkComponent);
|
|
5860
5884
|
const manual = props.activation === "manual";
|
|
5885
|
+
const seg = props.variant === "segmented";
|
|
5886
|
+
const listCls = cx("aura-tabs__list", seg && "aura-segmented", seg && props.fullWidth && "is-full");
|
|
5887
|
+
const tabCls = function(on, extra) {
|
|
5888
|
+
return cx("aura-tab", on && "is-active", seg && "aura-segmented__option", seg && on && "is-selected", extra);
|
|
5889
|
+
};
|
|
5890
|
+
const rootCls = cx("aura-tabs", seg && "aura-tabs--segmented");
|
|
5861
5891
|
const [focusId, setFocusId] = React32.useState(null);
|
|
5862
5892
|
items2.forEach(function(t) {
|
|
5863
5893
|
const al = t.tabProps && t.tabProps["aria-label"];
|
|
@@ -5873,9 +5903,9 @@ var Tabs = React32.forwardRef(function Tabs2(props, ref) {
|
|
|
5873
5903
|
{
|
|
5874
5904
|
ref,
|
|
5875
5905
|
"aria-label": props.label,
|
|
5876
|
-
className: cx("aura-tabs
|
|
5906
|
+
className: cx(rootCls, "aura-tabs--links", props.className)
|
|
5877
5907
|
},
|
|
5878
|
-
/* @__PURE__ */ React32.createElement("div", { className:
|
|
5908
|
+
/* @__PURE__ */ React32.createElement("div", { className: listCls }, items2.map(function(t) {
|
|
5879
5909
|
const on = t.id === cur;
|
|
5880
5910
|
const inner = [
|
|
5881
5911
|
t.icon ? /* @__PURE__ */ React32.createElement(Icon, { key: "i", name: t.icon }) : null,
|
|
@@ -5899,7 +5929,7 @@ var Tabs = React32.forwardRef(function Tabs2(props, ref) {
|
|
|
5899
5929
|
{
|
|
5900
5930
|
...omit(own, ["onClick"]),
|
|
5901
5931
|
key: t.id,
|
|
5902
|
-
className: cx("
|
|
5932
|
+
className: cx(tabCls(false), "is-disabled", own.className),
|
|
5903
5933
|
"aria-disabled": true
|
|
5904
5934
|
},
|
|
5905
5935
|
inner
|
|
@@ -5909,7 +5939,7 @@ var Tabs = React32.forwardRef(function Tabs2(props, ref) {
|
|
|
5909
5939
|
...own,
|
|
5910
5940
|
key: t.id,
|
|
5911
5941
|
href: t.href,
|
|
5912
|
-
className:
|
|
5942
|
+
className: tabCls(on, own.className),
|
|
5913
5943
|
"aria-current": on ? "page" : void 0,
|
|
5914
5944
|
onClick: function(e) {
|
|
5915
5945
|
if (own.onClick) own.onClick(e);
|
|
@@ -5933,12 +5963,12 @@ var Tabs = React32.forwardRef(function Tabs2(props, ref) {
|
|
|
5933
5963
|
const stop = manual && focusId && items2.filter(function(t) {
|
|
5934
5964
|
return t.id === focusId && !t.disabled;
|
|
5935
5965
|
})[0] || current2;
|
|
5936
|
-
return /* @__PURE__ */ React32.createElement("div", { ref, className: cx(
|
|
5966
|
+
return /* @__PURE__ */ React32.createElement("div", { ref, className: cx(rootCls, props.className) }, /* @__PURE__ */ React32.createElement(
|
|
5937
5967
|
"div",
|
|
5938
5968
|
{
|
|
5939
5969
|
role: "tablist",
|
|
5940
5970
|
"aria-label": props.label,
|
|
5941
|
-
className:
|
|
5971
|
+
className: listCls,
|
|
5942
5972
|
onBlur: manual ? function(e) {
|
|
5943
5973
|
if (!e.currentTarget.contains(e.relatedTarget)) setFocusId(null);
|
|
5944
5974
|
} : void 0,
|
|
@@ -5981,7 +6011,7 @@ var Tabs = React32.forwardRef(function Tabs2(props, ref) {
|
|
|
5981
6011
|
ref: function(el) {
|
|
5982
6012
|
refs.current[t.id] = el;
|
|
5983
6013
|
},
|
|
5984
|
-
className:
|
|
6014
|
+
className: tabCls(on, own.className),
|
|
5985
6015
|
onFocus: manual ? function(e) {
|
|
5986
6016
|
if (own.onFocus) own.onFocus(e);
|
|
5987
6017
|
setFocusId(t.id);
|
package/dist/esm/DropdownMenu.js
CHANGED
|
@@ -11,7 +11,7 @@ const DropdownMenu = React.forwardRef(function DropdownMenu2(props, ref) {
|
|
|
11
11
|
function() {
|
|
12
12
|
if (!anchor || !toLast.current || !menuRef.current) return;
|
|
13
13
|
toLast.current = false;
|
|
14
|
-
const list = menuRef.current.querySelectorAll('[role^="menuitem"]
|
|
14
|
+
const list = menuRef.current.querySelectorAll('[role^="menuitem"]');
|
|
15
15
|
if (list.length) list[list.length - 1].focus();
|
|
16
16
|
},
|
|
17
17
|
[anchor]
|
package/dist/esm/Menu.js
CHANGED
|
@@ -5,13 +5,14 @@ import { cx, uid, useIsoLayoutEffect, useMounted, useMergedRef } from "./interna
|
|
|
5
5
|
import { Icon } from "./Icon.js";
|
|
6
6
|
import { useLinkComponent } from "./locale.js";
|
|
7
7
|
import { IconCheck } from "./icons.js";
|
|
8
|
+
const ITEMS = '[role^="menuitem"]';
|
|
8
9
|
const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
9
10
|
const own = React.useRef(null), merged = useMergedRef(ref, own);
|
|
10
11
|
const posState = React.useState(null);
|
|
11
12
|
const pos = posState[0], setPos = posState[1];
|
|
12
13
|
const items = props.items || [];
|
|
13
14
|
const mounted = useMounted();
|
|
14
|
-
const headerId = uid();
|
|
15
|
+
const headerId = uid(), itemId = uid();
|
|
15
16
|
const hasHeader = props.header != null && props.header !== false && props.header !== "";
|
|
16
17
|
const Link = useLinkComponent(props.linkComponent);
|
|
17
18
|
useIsoLayoutEffect(
|
|
@@ -39,8 +40,8 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
39
40
|
hadHeader.current = hasHeader;
|
|
40
41
|
const a = document.activeElement;
|
|
41
42
|
if (own.current && (!a || a === document.body)) {
|
|
42
|
-
const first = own.current.querySelector(
|
|
43
|
-
|
|
43
|
+
const first = own.current.querySelector(ITEMS);
|
|
44
|
+
(first || own.current.querySelector('[role="menu"]') || own.current).focus();
|
|
44
45
|
}
|
|
45
46
|
},
|
|
46
47
|
[hasHeader]
|
|
@@ -48,8 +49,11 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
48
49
|
React.useEffect(
|
|
49
50
|
function() {
|
|
50
51
|
if (!mounted) return;
|
|
51
|
-
const first = own.current && own.current.querySelector(
|
|
52
|
-
if (
|
|
52
|
+
const first = own.current && own.current.querySelector(ITEMS);
|
|
53
|
+
if (props.autoFocus !== false) {
|
|
54
|
+
if (first) first.focus();
|
|
55
|
+
else if (own.current) (own.current.querySelector('[role="menu"]') || own.current).focus();
|
|
56
|
+
}
|
|
53
57
|
function outside(e) {
|
|
54
58
|
if (own.current && !own.current.contains(e.target) && !(props.anchor && props.anchor.contains(e.target)))
|
|
55
59
|
props.onClose(false);
|
|
@@ -69,16 +73,18 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
69
73
|
[mounted]
|
|
70
74
|
);
|
|
71
75
|
function onKeyDown(e) {
|
|
72
|
-
const list2 = Array.prototype.slice.call(
|
|
73
|
-
own.current.querySelectorAll('[role^="menuitem"]:not([disabled])')
|
|
74
|
-
);
|
|
76
|
+
const list2 = Array.prototype.slice.call(own.current.querySelectorAll(ITEMS));
|
|
75
77
|
const i = list2.indexOf(document.activeElement);
|
|
78
|
+
if (!list2.length && e.key !== "Escape" && e.key !== "Tab") {
|
|
79
|
+
e.stopPropagation();
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
76
82
|
if (e.key === "ArrowDown") {
|
|
77
83
|
e.preventDefault();
|
|
78
84
|
list2[(i + 1) % list2.length].focus();
|
|
79
85
|
} else if (e.key === "ArrowUp") {
|
|
80
86
|
e.preventDefault();
|
|
81
|
-
list2[(i - 1 + list2.length) % list2.length].focus();
|
|
87
|
+
list2[i < 0 ? list2.length - 1 : (i - 1 + list2.length) % list2.length].focus();
|
|
82
88
|
} else if (e.key === "Home") {
|
|
83
89
|
e.preventDefault();
|
|
84
90
|
list2[0].focus();
|
|
@@ -117,6 +123,10 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
117
123
|
it.hint ? /* @__PURE__ */ React.createElement("span", { key: "h", className: "aura-menu__hint" }, it.hint) : null
|
|
118
124
|
];
|
|
119
125
|
const cls = cx("aura-menu__item", it.tone === "danger" && "aura-menu__item--danger");
|
|
126
|
+
const reasonId = it.disabled && it.disabledReason ? itemId + "-r" + i : void 0;
|
|
127
|
+
if (reasonId)
|
|
128
|
+
body[2] = /* Hidden from the name (it would be read twice); aria-describedby still reads it. */
|
|
129
|
+
/* @__PURE__ */ React.createElement("span", { key: "h", id: reasonId, className: "aura-menu__hint aura-menu__reason", "aria-hidden": true }, it.disabledReason);
|
|
120
130
|
if (it.href && !it.disabled)
|
|
121
131
|
return /* @__PURE__ */ React.createElement(
|
|
122
132
|
Link,
|
|
@@ -141,11 +151,13 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
141
151
|
key: i,
|
|
142
152
|
type: "button",
|
|
143
153
|
tabIndex: -1,
|
|
144
|
-
disabled: it.disabled,
|
|
154
|
+
"aria-disabled": it.disabled ? true : void 0,
|
|
155
|
+
"aria-describedby": reasonId,
|
|
145
156
|
role: isRadio ? "menuitemradio" : isCheck ? "menuitemcheckbox" : "menuitem",
|
|
146
157
|
"aria-checked": isRadio || isCheck ? !!it.checked : void 0,
|
|
147
158
|
className: cls,
|
|
148
159
|
onClick: function() {
|
|
160
|
+
if (it.disabled) return;
|
|
149
161
|
if (it.onSelect) it.onSelect();
|
|
150
162
|
if (!it.keepOpen) props.onClose(true);
|
|
151
163
|
}
|
|
@@ -174,7 +186,19 @@ const Menu = React.forwardRef(function Menu2(props, ref) {
|
|
|
174
186
|
}
|
|
175
187
|
},
|
|
176
188
|
props.header
|
|
177
|
-
), /* @__PURE__ */ React.createElement("div", { role: "menu", "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React.createElement(
|
|
189
|
+
), /* @__PURE__ */ React.createElement("div", { role: "menu", tabIndex: -1, "aria-label": props.label, "aria-describedby": headerId, className: "aura-menu__list" }, list)) : /* @__PURE__ */ React.createElement(
|
|
190
|
+
"div",
|
|
191
|
+
{
|
|
192
|
+
ref: merged,
|
|
193
|
+
role: "menu",
|
|
194
|
+
tabIndex: -1,
|
|
195
|
+
"aria-label": props.label,
|
|
196
|
+
className: "aura-menu",
|
|
197
|
+
onKeyDown,
|
|
198
|
+
style
|
|
199
|
+
},
|
|
200
|
+
list
|
|
201
|
+
);
|
|
178
202
|
return mounted ? createPortal(el, document.body) : null;
|
|
179
203
|
});
|
|
180
204
|
export {
|
package/dist/esm/Tabs.js
CHANGED
|
@@ -23,6 +23,12 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
|
|
|
23
23
|
})[0] || firstOn || items[0];
|
|
24
24
|
const Link = useLinkComponent(props.linkComponent);
|
|
25
25
|
const manual = props.activation === "manual";
|
|
26
|
+
const seg = props.variant === "segmented";
|
|
27
|
+
const listCls = cx("aura-tabs__list", seg && "aura-segmented", seg && props.fullWidth && "is-full");
|
|
28
|
+
const tabCls = function(on, extra) {
|
|
29
|
+
return cx("aura-tab", on && "is-active", seg && "aura-segmented__option", seg && on && "is-selected", extra);
|
|
30
|
+
};
|
|
31
|
+
const rootCls = cx("aura-tabs", seg && "aura-tabs--segmented");
|
|
26
32
|
const [focusId, setFocusId] = React.useState(null);
|
|
27
33
|
items.forEach(function(t) {
|
|
28
34
|
const al = t.tabProps && t.tabProps["aria-label"];
|
|
@@ -38,9 +44,9 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
|
|
|
38
44
|
{
|
|
39
45
|
ref,
|
|
40
46
|
"aria-label": props.label,
|
|
41
|
-
className: cx("aura-tabs
|
|
47
|
+
className: cx(rootCls, "aura-tabs--links", props.className)
|
|
42
48
|
},
|
|
43
|
-
/* @__PURE__ */ React.createElement("div", { className:
|
|
49
|
+
/* @__PURE__ */ React.createElement("div", { className: listCls }, items.map(function(t) {
|
|
44
50
|
const on = t.id === cur;
|
|
45
51
|
const inner = [
|
|
46
52
|
t.icon ? /* @__PURE__ */ React.createElement(Icon, { key: "i", name: t.icon }) : null,
|
|
@@ -64,7 +70,7 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
|
|
|
64
70
|
{
|
|
65
71
|
...omit(own, ["onClick"]),
|
|
66
72
|
key: t.id,
|
|
67
|
-
className: cx("
|
|
73
|
+
className: cx(tabCls(false), "is-disabled", own.className),
|
|
68
74
|
"aria-disabled": true
|
|
69
75
|
},
|
|
70
76
|
inner
|
|
@@ -74,7 +80,7 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
|
|
|
74
80
|
...own,
|
|
75
81
|
key: t.id,
|
|
76
82
|
href: t.href,
|
|
77
|
-
className:
|
|
83
|
+
className: tabCls(on, own.className),
|
|
78
84
|
"aria-current": on ? "page" : void 0,
|
|
79
85
|
onClick: function(e) {
|
|
80
86
|
if (own.onClick) own.onClick(e);
|
|
@@ -98,12 +104,12 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
|
|
|
98
104
|
const stop = manual && focusId && items.filter(function(t) {
|
|
99
105
|
return t.id === focusId && !t.disabled;
|
|
100
106
|
})[0] || current;
|
|
101
|
-
return /* @__PURE__ */ React.createElement("div", { ref, className: cx(
|
|
107
|
+
return /* @__PURE__ */ React.createElement("div", { ref, className: cx(rootCls, props.className) }, /* @__PURE__ */ React.createElement(
|
|
102
108
|
"div",
|
|
103
109
|
{
|
|
104
110
|
role: "tablist",
|
|
105
111
|
"aria-label": props.label,
|
|
106
|
-
className:
|
|
112
|
+
className: listCls,
|
|
107
113
|
onBlur: manual ? function(e) {
|
|
108
114
|
if (!e.currentTarget.contains(e.relatedTarget)) setFocusId(null);
|
|
109
115
|
} : void 0,
|
|
@@ -146,7 +152,7 @@ const Tabs = React.forwardRef(function Tabs2(props, ref) {
|
|
|
146
152
|
ref: function(el) {
|
|
147
153
|
refs.current[t.id] = el;
|
|
148
154
|
},
|
|
149
|
-
className:
|
|
155
|
+
className: tabCls(on, own.className),
|
|
150
156
|
onFocus: manual ? function(e) {
|
|
151
157
|
if (own.onFocus) own.onFocus(e);
|
|
152
158
|
setFocusId(t.id);
|
package/dist/index.d.cts
CHANGED
|
@@ -278,7 +278,10 @@ export interface MenuItem {
|
|
|
278
278
|
icon?: IconInput | undefined;
|
|
279
279
|
/** Present = a checkable item (menuitemcheckbox). */
|
|
280
280
|
checked?: boolean | undefined;
|
|
281
|
+
/** Dimmed and does nothing when chosen, but still reachable with the arrow keys and announced (5.10). */
|
|
281
282
|
disabled?: boolean | undefined;
|
|
283
|
+
/** Why it's disabled ("You can resend in 5 minutes"): shown in place of the hint and read with the item. (5.10) */
|
|
284
|
+
disabledReason?: string | undefined;
|
|
282
285
|
/** Keep the menu open after choosing (checkbox lists). */
|
|
283
286
|
keepOpen?: boolean | undefined;
|
|
284
287
|
/** Short right-aligned hint, e.g. a shortcut. */
|
|
@@ -617,6 +620,11 @@ export interface TabsProps {
|
|
|
617
620
|
/** `auto` (default): arrow keys select as they move. `manual`: arrows, Home and End only move focus; Enter, Space
|
|
618
621
|
* or a click selects — for tabs whose selection does work (loads data, starts a payment). (5.9) */
|
|
619
622
|
activation?: "auto" | "manual" | undefined;
|
|
623
|
+
/** `underline` (default) or `segmented`: the tabs as a pill track with the selected one raised, like
|
|
624
|
+
* SegmentedControl, keeping every Tabs behaviour (panels, keepMounted, activation, tabProps). (5.10, Chamber-OS 72) */
|
|
625
|
+
variant?: "underline" | "segmented" | undefined;
|
|
626
|
+
/** With `variant="segmented"`: stretch the track to the full width, tabs sharing it equally. (5.10) */
|
|
627
|
+
fullWidth?: boolean | undefined;
|
|
620
628
|
}
|
|
621
629
|
export interface NavItem {
|
|
622
630
|
id: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -278,7 +278,10 @@ export interface MenuItem {
|
|
|
278
278
|
icon?: IconInput | undefined;
|
|
279
279
|
/** Present = a checkable item (menuitemcheckbox). */
|
|
280
280
|
checked?: boolean | undefined;
|
|
281
|
+
/** Dimmed and does nothing when chosen, but still reachable with the arrow keys and announced (5.10). */
|
|
281
282
|
disabled?: boolean | undefined;
|
|
283
|
+
/** Why it's disabled ("You can resend in 5 minutes"): shown in place of the hint and read with the item. (5.10) */
|
|
284
|
+
disabledReason?: string | undefined;
|
|
282
285
|
/** Keep the menu open after choosing (checkbox lists). */
|
|
283
286
|
keepOpen?: boolean | undefined;
|
|
284
287
|
/** Short right-aligned hint, e.g. a shortcut. */
|
|
@@ -617,6 +620,11 @@ export interface TabsProps {
|
|
|
617
620
|
/** `auto` (default): arrow keys select as they move. `manual`: arrows, Home and End only move focus; Enter, Space
|
|
618
621
|
* or a click selects — for tabs whose selection does work (loads data, starts a payment). (5.9) */
|
|
619
622
|
activation?: "auto" | "manual" | undefined;
|
|
623
|
+
/** `underline` (default) or `segmented`: the tabs as a pill track with the selected one raised, like
|
|
624
|
+
* SegmentedControl, keeping every Tabs behaviour (panels, keepMounted, activation, tabProps). (5.10, Chamber-OS 72) */
|
|
625
|
+
variant?: "underline" | "segmented" | undefined;
|
|
626
|
+
/** With `variant="segmented"`: stretch the track to the full width, tabs sharing it equally. (5.10) */
|
|
627
|
+
fullWidth?: boolean | undefined;
|
|
620
628
|
}
|
|
621
629
|
export interface NavItem {
|
|
622
630
|
id: string;
|
package/dist/styles.css
CHANGED
|
@@ -226,7 +226,13 @@ a.aura-btn { text-decoration: none; }
|
|
|
226
226
|
}
|
|
227
227
|
.aura-menu__item:hover, .aura-menu__item:focus { background: var(--aura-bg-surface-hover); outline: none; }
|
|
228
228
|
.aura-menu__item:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
|
|
229
|
-
.aura-menu__item:disabled { opacity: var(--aura-disabled-opacity); cursor: default;
|
|
229
|
+
.aura-menu__item:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
|
|
230
|
+
/* 5.10: a disabled item is dimmed but focusable; its reason stays at full strength (it is the information). */
|
|
231
|
+
.aura-menu__item[aria-disabled="true"] { cursor: default; }
|
|
232
|
+
.aura-menu__item[aria-disabled="true"] > :not(.aura-menu__reason) { opacity: var(--aura-disabled-opacity); }
|
|
233
|
+
.aura-menu__reason { padding-left: var(--aura-space-4); }
|
|
234
|
+
.aura-menu__item:disabled, .aura-menu__item[aria-disabled="true"]:hover:not(:focus) { background: transparent; }
|
|
235
|
+
.aura-menu:focus, .aura-menu__list:focus { outline: none; }
|
|
230
236
|
.aura-menu__item .aura-icon { color: var(--aura-fg-secondary); }
|
|
231
237
|
.aura-menu__label { flex: 1; }
|
|
232
238
|
.aura-menu__blank { width: var(--aura-icon-sm); flex: none; }
|
|
@@ -1203,6 +1209,16 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1203
1209
|
.aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
|
|
1204
1210
|
.aura-segmented--sm .aura-segmented__option.is-icon { width: 28px; padding: 0; }
|
|
1205
1211
|
.aura-segmented.is-disabled { opacity: var(--aura-disabled-opacity); }
|
|
1212
|
+
/* 5.10 (Chamber-OS 72): Tabs variant="segmented" — the tablist wears SegmentedControl's track and pills. The focus
|
|
1213
|
+
* ring is drawn inside the pill, so neither the scrolling list (many tabs on a phone) nor the next pill can cover it;
|
|
1214
|
+
* full-width tabs share the track but never shrink below their label (the track scrolls instead). The phone rule's
|
|
1215
|
+
* wider tab gap doesn't apply. */
|
|
1216
|
+
.aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; }
|
|
1217
|
+
.aura-tabs--segmented .aura-tabs__list.aura-segmented.is-full .aura-tab { min-width: max-content; }
|
|
1218
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option { margin-bottom: 0; }
|
|
1219
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { border-radius: var(--aura-radius-full); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
|
|
1220
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option:disabled,
|
|
1221
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option.is-disabled { opacity: var(--aura-disabled-opacity); cursor: not-allowed; }
|
|
1206
1222
|
|
|
1207
1223
|
/* A field inside a row Stack takes the remaining width (a search box beside its button). */
|
|
1208
1224
|
.aura-stack > .aura-field { flex: 1 1 auto; min-width: 0; }
|
|
@@ -1590,4 +1606,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1590
1606
|
.aura-segmented__option.is-selected { outline: 2px solid Highlight; outline-offset: -2px; }
|
|
1591
1607
|
.aura-tab { border-bottom-color: Canvas; }
|
|
1592
1608
|
.aura-tab.is-active { border-bottom-color: Highlight; }
|
|
1609
|
+
/* 5.10: segmented tabs mark the selection with a border and focus with a dashed ring inside it, so a focused tab
|
|
1610
|
+
* and the selected one never look alike (manual activation moves focus without selecting). */
|
|
1611
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option.is-selected { outline: none; border: 2px solid Highlight; }
|
|
1612
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { outline: 2px dashed CanvasText; outline-offset: -5px; }
|
|
1593
1613
|
}
|
package/dist/styles.layer.css
CHANGED
|
@@ -228,7 +228,13 @@ a.aura-btn { text-decoration: none; }
|
|
|
228
228
|
}
|
|
229
229
|
.aura-menu__item:hover, .aura-menu__item:focus { background: var(--aura-bg-surface-hover); outline: none; }
|
|
230
230
|
.aura-menu__item:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
|
|
231
|
-
.aura-menu__item:disabled { opacity: var(--aura-disabled-opacity); cursor: default;
|
|
231
|
+
.aura-menu__item:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
|
|
232
|
+
/* 5.10: a disabled item is dimmed but focusable; its reason stays at full strength (it is the information). */
|
|
233
|
+
.aura-menu__item[aria-disabled="true"] { cursor: default; }
|
|
234
|
+
.aura-menu__item[aria-disabled="true"] > :not(.aura-menu__reason) { opacity: var(--aura-disabled-opacity); }
|
|
235
|
+
.aura-menu__reason { padding-left: var(--aura-space-4); }
|
|
236
|
+
.aura-menu__item:disabled, .aura-menu__item[aria-disabled="true"]:hover:not(:focus) { background: transparent; }
|
|
237
|
+
.aura-menu:focus, .aura-menu__list:focus { outline: none; }
|
|
232
238
|
.aura-menu__item .aura-icon { color: var(--aura-fg-secondary); }
|
|
233
239
|
.aura-menu__label { flex: 1; }
|
|
234
240
|
.aura-menu__blank { width: var(--aura-icon-sm); flex: none; }
|
|
@@ -1205,6 +1211,16 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1205
1211
|
.aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
|
|
1206
1212
|
.aura-segmented--sm .aura-segmented__option.is-icon { width: 28px; padding: 0; }
|
|
1207
1213
|
.aura-segmented.is-disabled { opacity: var(--aura-disabled-opacity); }
|
|
1214
|
+
/* 5.10 (Chamber-OS 72): Tabs variant="segmented" — the tablist wears SegmentedControl's track and pills. The focus
|
|
1215
|
+
* ring is drawn inside the pill, so neither the scrolling list (many tabs on a phone) nor the next pill can cover it;
|
|
1216
|
+
* full-width tabs share the track but never shrink below their label (the track scrolls instead). The phone rule's
|
|
1217
|
+
* wider tab gap doesn't apply. */
|
|
1218
|
+
.aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; }
|
|
1219
|
+
.aura-tabs--segmented .aura-tabs__list.aura-segmented.is-full .aura-tab { min-width: max-content; }
|
|
1220
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option { margin-bottom: 0; }
|
|
1221
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { border-radius: var(--aura-radius-full); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
|
|
1222
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option:disabled,
|
|
1223
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option.is-disabled { opacity: var(--aura-disabled-opacity); cursor: not-allowed; }
|
|
1208
1224
|
|
|
1209
1225
|
/* A field inside a row Stack takes the remaining width (a search box beside its button). */
|
|
1210
1226
|
.aura-stack > .aura-field { flex: 1 1 auto; min-width: 0; }
|
|
@@ -1592,6 +1608,10 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1592
1608
|
.aura-segmented__option.is-selected { outline: 2px solid Highlight; outline-offset: -2px; }
|
|
1593
1609
|
.aura-tab { border-bottom-color: Canvas; }
|
|
1594
1610
|
.aura-tab.is-active { border-bottom-color: Highlight; }
|
|
1611
|
+
/* 5.10: segmented tabs mark the selection with a border and focus with a dashed ring inside it, so a focused tab
|
|
1612
|
+
* and the selected one never look alike (manual activation moves focus without selecting). */
|
|
1613
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option.is-selected { outline: none; border: 2px solid Highlight; }
|
|
1614
|
+
.aura-tabs--segmented .aura-tab.aura-segmented__option:focus-visible { outline: 2px dashed CanvasText; outline-offset: -5px; }
|
|
1595
1615
|
}
|
|
1596
1616
|
|
|
1597
1617
|
}
|
package/package.json
CHANGED