@jirawatpyk/aura-tokens 5.2.0 → 5.4.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 +1 -1
- package/aura-tailwind.css +1 -1
- package/aura-tailwind.prefixed.css +1 -1
- package/aura.css +1 -1
- package/components/aura.bundle.js +503 -39
- package/components/aura.components.css +9 -0
- package/components/index.d.ts +29 -12
- package/eslint-plugin-aura.js +19 -4
- package/package.json +6 -6
- package/scripts/a11y-check.js +26 -5
- package/scripts/lint-tokens.js +8 -3
- package/tailwind.tokens.cjs +1 -1
- package/tokens.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Enterprise Standard, Human Creative. Tokens, Tailwind preset, Figma variables a
|
|
|
15
15
|
| `figma-variables.csv` | 204 variables: all colours in both modes (aliases kept), spacing, radius and sizes. **Generated.** |
|
|
16
16
|
| `components/` | `aura.bundle.js` (sets `window.Aura`), `aura.components.css`, `index.d.ts`. |
|
|
17
17
|
| `eslint-plugin-aura.js` | `aura/no-hardcoded-color`: hex, colour functions (rgb, hsl, oklch, color-mix…), named colours in style values and Tailwind palette / `bg-[#…]` classes. ESLint 9: `import aura from '@jirawatpyk/aura-tokens/eslint-plugin'; export default [aura.configs.recommended];` (5.2). |
|
|
18
|
-
| `scripts/build-tokens.
|
|
18
|
+
| `scripts/build-tokens.mts` | Rebuilds the generated files from `tokens.json`. |
|
|
19
19
|
| `scripts/a11y-check.js` | Checks 210 pairs in both themes: text on its grounds (WCAG AA), control edges and state marks at 3:1, neighbouring chart and status fills a lightness step apart. |
|
|
20
20
|
| `scripts/lint-tokens.js` | Fails on hard-coded colours (a file that must hold them says `aura-lint: allow-colours` with a reason). |
|
|
21
21
|
|
package/aura-tailwind.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* AURA v5.
|
|
1
|
+
/* AURA v5.4.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand.
|
|
2
2
|
* @import "tailwindcss";
|
|
3
3
|
* @import "@jirawatpyk/aura-tokens/tailwind.css";
|
|
4
4
|
* Gives bg-bg-surface, text-fg-primary, border-border-strong, p-aura-6, rounded-aura-xl, shadow-aura-overlay,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* AURA v5.
|
|
1
|
+
/* AURA v5.4.0 — Tailwind v4 theme with aura- prefixed names. GENERATED by scripts/build-tokens.mts. Do not edit by hand.
|
|
2
2
|
* For apps that already have a Tailwind theme (shadcn): nothing here redefines an existing name.
|
|
3
3
|
* @layer aura-tokens, theme, base, aura, components, utilities;
|
|
4
4
|
* @import "tailwindcss";
|
package/aura.css
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* AURA v5.
|
|
1
|
+
/* AURA v5.4.0 — GENERATED by scripts/build-tokens.mts from tokens.json. Do not edit by hand. */
|
|
2
2
|
/* Fonts: load aura-fonts.css (or the <link> tags in README) separately, so a blocked Google Fonts never breaks this file. */
|
|
3
3
|
|
|
4
4
|
:root, [data-theme="light"] {
|
|
@@ -1224,7 +1224,52 @@ window.Aura = (() => {
|
|
|
1224
1224
|
|
|
1225
1225
|
// src/Select.tsx
|
|
1226
1226
|
var React13 = __toESM(require_react(), 1);
|
|
1227
|
+
var import_react_dom2 = __toESM(require_react_dom(), 1);
|
|
1227
1228
|
var h3 = React13.createElement;
|
|
1229
|
+
var FORM_KEYS = ["name", "form", "value", "defaultValue", "disabled", "autoComplete", "onInput", "onInvalid"];
|
|
1230
|
+
var HANDLED_KEYS = FORM_KEYS.concat([
|
|
1231
|
+
"onChange",
|
|
1232
|
+
"onBlur",
|
|
1233
|
+
"onFocus",
|
|
1234
|
+
"onKeyDown",
|
|
1235
|
+
"onClick",
|
|
1236
|
+
"autoFocus",
|
|
1237
|
+
"tabIndex",
|
|
1238
|
+
"aria-label",
|
|
1239
|
+
"aria-labelledby",
|
|
1240
|
+
"aria-describedby",
|
|
1241
|
+
"multiple",
|
|
1242
|
+
"size"
|
|
1243
|
+
]);
|
|
1244
|
+
function pick(o, keys) {
|
|
1245
|
+
const r = {};
|
|
1246
|
+
for (const k of keys) if (k in o) r[k] = o[k];
|
|
1247
|
+
return r;
|
|
1248
|
+
}
|
|
1249
|
+
function readItems(el) {
|
|
1250
|
+
if (!el) return [];
|
|
1251
|
+
return Array.prototype.map.call(el.options, function(o, i) {
|
|
1252
|
+
const g = o.parentElement && o.parentElement.tagName === "OPTGROUP" ? o.parentElement : null;
|
|
1253
|
+
return {
|
|
1254
|
+
value: o.value,
|
|
1255
|
+
label: o.textContent || "",
|
|
1256
|
+
disabled: o.disabled || !!(g && g.disabled),
|
|
1257
|
+
group: g ? g.label : null,
|
|
1258
|
+
index: i
|
|
1259
|
+
};
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
function initialLabel(props) {
|
|
1263
|
+
const opts = (props.options || []).map(function(o) {
|
|
1264
|
+
return typeof o === "object" ? o : { value: o, label: o };
|
|
1265
|
+
});
|
|
1266
|
+
const v = props.value !== void 0 && props.value !== null ? String(props.value) : props.defaultValue !== void 0 && props.defaultValue !== null ? String(props.defaultValue) : props.placeholder ? "" : opts[0] ? opts[0].value : "";
|
|
1267
|
+
const hit = opts.filter(function(o) {
|
|
1268
|
+
return o.value === v;
|
|
1269
|
+
})[0];
|
|
1270
|
+
if (hit) return { label: hit.label, empty: false };
|
|
1271
|
+
return { label: props.placeholder || "", empty: true };
|
|
1272
|
+
}
|
|
1228
1273
|
var Select = React13.forwardRef(function Select2(props, ref) {
|
|
1229
1274
|
const auto = uid(), id = props.id || auto;
|
|
1230
1275
|
const rest = omit(props, FIELD_KEYS.concat(["children"]));
|
|
@@ -1237,10 +1282,267 @@ window.Aura = (() => {
|
|
|
1237
1282
|
/* @__PURE__ */ React13.createElement("option", { key: "__ph", value: "", disabled: true }, props.placeholder)
|
|
1238
1283
|
);
|
|
1239
1284
|
const extra = props.value === void 0 && props.defaultValue === void 0 && props.placeholder ? { defaultValue: "" } : {};
|
|
1240
|
-
|
|
1285
|
+
const native = !!props.multiple || props.size != null && props.size > 1;
|
|
1286
|
+
const mounted = useMounted();
|
|
1287
|
+
const live = !native && mounted;
|
|
1288
|
+
const density = useDensity();
|
|
1289
|
+
React13.useEffect(function() {
|
|
1290
|
+
if (props.label || props["aria-label"] || props["aria-labelledby"] || props.title) return;
|
|
1291
|
+
if (document.querySelector('label[for="' + id.replace(/["\\]/g, "\\$&") + '"]')) return;
|
|
1292
|
+
devWarnOnce(
|
|
1293
|
+
"select-name",
|
|
1294
|
+
"Select needs a label, aria-label or aria-labelledby: screen readers announce it with no name."
|
|
1295
|
+
);
|
|
1296
|
+
}, []);
|
|
1297
|
+
const selRef = React13.useRef(null), trigRef = React13.useRef(null), listRef = React13.useRef(null);
|
|
1298
|
+
const shown = React13.useState(function() {
|
|
1299
|
+
return initialLabel(props);
|
|
1300
|
+
});
|
|
1301
|
+
const openState = React13.useState(false), open = openState[0];
|
|
1302
|
+
const activeState = React13.useState(-1), active = activeState[0];
|
|
1303
|
+
const items2 = React13.useState([]);
|
|
1304
|
+
const pos = React13.useState(null);
|
|
1305
|
+
const typed = React13.useRef({ text: "", at: 0 });
|
|
1306
|
+
const sync = React13.useCallback(function() {
|
|
1307
|
+
const el = selRef.current;
|
|
1308
|
+
if (!el) return;
|
|
1309
|
+
const o = el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null;
|
|
1310
|
+
const next = o ? { label: o.textContent || "", empty: o.value === "" } : { label: "", empty: true };
|
|
1311
|
+
shown[1](function(cur) {
|
|
1312
|
+
return cur.label === next.label && cur.empty === next.empty ? cur : next;
|
|
1313
|
+
});
|
|
1314
|
+
}, []);
|
|
1315
|
+
const hook = React13.useCallback(
|
|
1316
|
+
function(el) {
|
|
1317
|
+
selRef.current = el;
|
|
1318
|
+
if (!el || native || el.__auraHooked) return;
|
|
1319
|
+
el.__auraHooked = true;
|
|
1320
|
+
const proto = HTMLSelectElement.prototype;
|
|
1321
|
+
const pv = Object.getOwnPropertyDescriptor(proto, "value"), pi = Object.getOwnPropertyDescriptor(proto, "selectedIndex");
|
|
1322
|
+
if (pv && pv.set && pv.get)
|
|
1323
|
+
Object.defineProperty(el, "value", {
|
|
1324
|
+
configurable: true,
|
|
1325
|
+
get: function() {
|
|
1326
|
+
return pv.get.call(el);
|
|
1327
|
+
},
|
|
1328
|
+
set: function(v) {
|
|
1329
|
+
pv.set.call(el, v);
|
|
1330
|
+
sync();
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
if (pi && pi.set && pi.get)
|
|
1334
|
+
Object.defineProperty(el, "selectedIndex", {
|
|
1335
|
+
configurable: true,
|
|
1336
|
+
get: function() {
|
|
1337
|
+
return pi.get.call(el);
|
|
1338
|
+
},
|
|
1339
|
+
set: function(v) {
|
|
1340
|
+
pi.set.call(el, v);
|
|
1341
|
+
sync();
|
|
1342
|
+
}
|
|
1343
|
+
});
|
|
1344
|
+
el.focus = function(o) {
|
|
1345
|
+
if (trigRef.current) trigRef.current.focus(o);
|
|
1346
|
+
else proto.focus.call(el, o);
|
|
1347
|
+
};
|
|
1348
|
+
},
|
|
1349
|
+
[native, sync]
|
|
1350
|
+
);
|
|
1351
|
+
const selMerged = useMergedRef(ref, hook);
|
|
1352
|
+
React13.useEffect(
|
|
1353
|
+
function() {
|
|
1354
|
+
const f = selRef.current && selRef.current.form;
|
|
1355
|
+
if (!f || native) return;
|
|
1356
|
+
function onReset() {
|
|
1357
|
+
setTimeout(sync, 0);
|
|
1358
|
+
}
|
|
1359
|
+
f.addEventListener("reset", onReset);
|
|
1360
|
+
return function() {
|
|
1361
|
+
f.removeEventListener("reset", onReset);
|
|
1362
|
+
};
|
|
1363
|
+
},
|
|
1364
|
+
[native, sync]
|
|
1365
|
+
);
|
|
1366
|
+
const focusedOnce = React13.useRef(false);
|
|
1367
|
+
React13.useEffect(
|
|
1368
|
+
function() {
|
|
1369
|
+
if (!live || focusedOnce.current || !trigRef.current) return;
|
|
1370
|
+
focusedOnce.current = true;
|
|
1371
|
+
if (props.autoFocus || selRef.current && document.activeElement === selRef.current) trigRef.current.focus();
|
|
1372
|
+
},
|
|
1373
|
+
[live]
|
|
1374
|
+
);
|
|
1375
|
+
useIsoLayoutEffect(function() {
|
|
1376
|
+
if (!live) return;
|
|
1377
|
+
sync();
|
|
1378
|
+
if (!open) return;
|
|
1379
|
+
const now = readItems(selRef.current), was = items2[0];
|
|
1380
|
+
const same2 = now.length === was.length && now.every(function(it, i) {
|
|
1381
|
+
const w = was[i];
|
|
1382
|
+
return w.value === it.value && w.label === it.label && w.disabled === it.disabled && w.group === it.group;
|
|
1383
|
+
});
|
|
1384
|
+
if (same2) return;
|
|
1385
|
+
items2[1](now);
|
|
1386
|
+
if (!choices(now).length) {
|
|
1387
|
+
close(false);
|
|
1388
|
+
return;
|
|
1389
|
+
}
|
|
1390
|
+
const hv = was[active] ? was[active].value : null;
|
|
1391
|
+
const again = now.filter(function(it) {
|
|
1392
|
+
return !it.disabled && it.value === hv;
|
|
1393
|
+
})[0];
|
|
1394
|
+
const cur = selRef.current ? selRef.current.selectedIndex : -1;
|
|
1395
|
+
activeState[1](again ? again.index : now[cur] && !now[cur].disabled ? cur : choices(now)[0].index);
|
|
1396
|
+
});
|
|
1397
|
+
function choices(list) {
|
|
1398
|
+
return list.filter(function(i) {
|
|
1399
|
+
return !i.disabled;
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1402
|
+
function choose(it) {
|
|
1403
|
+
const el = selRef.current;
|
|
1404
|
+
if (!el || it.disabled) return;
|
|
1405
|
+
if (el.value !== it.value) {
|
|
1406
|
+
el.value = it.value;
|
|
1407
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
1408
|
+
el.dispatchEvent(new Event("change", { bubbles: true }));
|
|
1409
|
+
}
|
|
1410
|
+
sync();
|
|
1411
|
+
close(true);
|
|
1412
|
+
}
|
|
1413
|
+
function openList(at) {
|
|
1414
|
+
if (props.disabled || trigRef.current && trigRef.current.disabled) return;
|
|
1415
|
+
const list = readItems(selRef.current);
|
|
1416
|
+
const ok = choices(list);
|
|
1417
|
+
if (!ok.length) return;
|
|
1418
|
+
items2[1](list);
|
|
1419
|
+
const cur = selRef.current ? selRef.current.selectedIndex : -1;
|
|
1420
|
+
let a = at === "first" ? ok[0].index : at === "last" ? ok[ok.length - 1].index : cur;
|
|
1421
|
+
if (typeof at === "number") a = at;
|
|
1422
|
+
if (a < 0 || !list[a] || list[a].disabled) a = ok[0].index;
|
|
1423
|
+
activeState[1](a);
|
|
1424
|
+
openState[1](true);
|
|
1425
|
+
}
|
|
1426
|
+
function close(focus) {
|
|
1427
|
+
openState[1](false);
|
|
1428
|
+
pos[1](null);
|
|
1429
|
+
if (focus && trigRef.current) trigRef.current.focus();
|
|
1430
|
+
}
|
|
1431
|
+
function step(from, dir, list) {
|
|
1432
|
+
for (let i = from + dir; i >= 0 && i < list.length; i += dir) if (!list[i].disabled) return i;
|
|
1433
|
+
return from;
|
|
1434
|
+
}
|
|
1435
|
+
function typing() {
|
|
1436
|
+
return !!typed.current.text && Date.now() - typed.current.at < 500;
|
|
1437
|
+
}
|
|
1438
|
+
function typeahead(ch, list, from) {
|
|
1439
|
+
const now = Date.now(), t = typed.current;
|
|
1440
|
+
t.text = now - t.at < 500 ? t.text + ch : ch;
|
|
1441
|
+
t.at = now;
|
|
1442
|
+
const q = t.text.toLocaleLowerCase();
|
|
1443
|
+
for (let k = 1; k <= list.length; k++) {
|
|
1444
|
+
const i = (from + (t.text.length > 1 ? 0 : 1) + k - 1 + list.length) % list.length;
|
|
1445
|
+
if (!list[i].disabled && list[i].label.toLocaleLowerCase().indexOf(q) === 0) return i;
|
|
1446
|
+
}
|
|
1447
|
+
t.text = "";
|
|
1448
|
+
return -1;
|
|
1449
|
+
}
|
|
1450
|
+
function onKeyDown(e) {
|
|
1451
|
+
const k = e.key;
|
|
1452
|
+
const printable = k.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey;
|
|
1453
|
+
const search = printable && (k !== " " || typing());
|
|
1454
|
+
if (!open) {
|
|
1455
|
+
if (search) {
|
|
1456
|
+
const list2 = readItems(selRef.current);
|
|
1457
|
+
const i = typeahead(k, list2, selRef.current ? selRef.current.selectedIndex : -1);
|
|
1458
|
+
e.preventDefault();
|
|
1459
|
+
if (i >= 0) openList(i);
|
|
1460
|
+
} else if (k === "ArrowDown" || k === "ArrowUp" || k === "Enter" || k === " ") {
|
|
1461
|
+
e.preventDefault();
|
|
1462
|
+
openList(k === "ArrowUp" && !(selRef.current && selRef.current.value) ? "last" : void 0);
|
|
1463
|
+
}
|
|
1464
|
+
return;
|
|
1465
|
+
}
|
|
1466
|
+
const list = items2[0];
|
|
1467
|
+
if (search) {
|
|
1468
|
+
e.preventDefault();
|
|
1469
|
+
const i = typeahead(k, list, active);
|
|
1470
|
+
if (i >= 0) activeState[1](i);
|
|
1471
|
+
} else if (k === "ArrowDown") {
|
|
1472
|
+
e.preventDefault();
|
|
1473
|
+
activeState[1](step(active, 1, list));
|
|
1474
|
+
} else if (k === "ArrowUp") {
|
|
1475
|
+
e.preventDefault();
|
|
1476
|
+
activeState[1](step(active, -1, list));
|
|
1477
|
+
} else if (k === "Home" || k === "PageUp") {
|
|
1478
|
+
e.preventDefault();
|
|
1479
|
+
activeState[1](k === "Home" ? step(-1, 1, list) : Math.max(step(-1, 1, list), step(active - 9, 1, list)));
|
|
1480
|
+
} else if (k === "End" || k === "PageDown") {
|
|
1481
|
+
e.preventDefault();
|
|
1482
|
+
activeState[1](
|
|
1483
|
+
k === "End" ? step(list.length, -1, list) : Math.min(step(list.length, -1, list), step(active + 9, -1, list))
|
|
1484
|
+
);
|
|
1485
|
+
} else if (k === "Enter" || k === " ") {
|
|
1486
|
+
e.preventDefault();
|
|
1487
|
+
if (list[active]) choose(list[active]);
|
|
1488
|
+
} else if (k === "Escape") {
|
|
1489
|
+
e.preventDefault();
|
|
1490
|
+
e.stopPropagation();
|
|
1491
|
+
close(true);
|
|
1492
|
+
} else if (k === "Tab") {
|
|
1493
|
+
close(false);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
useIsoLayoutEffect(
|
|
1497
|
+
function() {
|
|
1498
|
+
if (!open) return;
|
|
1499
|
+
function place() {
|
|
1500
|
+
const t = trigRef.current && trigRef.current.parentElement;
|
|
1501
|
+
if (!t) return;
|
|
1502
|
+
const r = t.getBoundingClientRect(), below2 = window.innerHeight - r.bottom - 8, above = r.top - 8, want = listRef.current ? listRef.current.scrollHeight : 320, up = below2 < Math.min(want, 200) && above > below2;
|
|
1503
|
+
pos[1]({
|
|
1504
|
+
left: r.left,
|
|
1505
|
+
width: r.width,
|
|
1506
|
+
top: up ? void 0 : r.bottom + 4,
|
|
1507
|
+
bottom: up ? window.innerHeight - r.top + 4 : void 0,
|
|
1508
|
+
maxHeight: Math.min(320, (up ? above : below2) - 4)
|
|
1509
|
+
});
|
|
1510
|
+
}
|
|
1511
|
+
place();
|
|
1512
|
+
function outside(e) {
|
|
1513
|
+
const n2 = e.target;
|
|
1514
|
+
if (listRef.current && listRef.current.contains(n2)) return;
|
|
1515
|
+
if (trigRef.current && trigRef.current.contains(n2)) return;
|
|
1516
|
+
close(false);
|
|
1517
|
+
}
|
|
1518
|
+
function onScroll(e) {
|
|
1519
|
+
if (!listRef.current || !listRef.current.contains(e.target)) place();
|
|
1520
|
+
}
|
|
1521
|
+
document.addEventListener("pointerdown", outside, true);
|
|
1522
|
+
window.addEventListener("scroll", onScroll, true);
|
|
1523
|
+
window.addEventListener("resize", place);
|
|
1524
|
+
return function() {
|
|
1525
|
+
document.removeEventListener("pointerdown", outside, true);
|
|
1526
|
+
window.removeEventListener("scroll", onScroll, true);
|
|
1527
|
+
window.removeEventListener("resize", place);
|
|
1528
|
+
};
|
|
1529
|
+
},
|
|
1530
|
+
[open]
|
|
1531
|
+
);
|
|
1532
|
+
React13.useEffect(
|
|
1533
|
+
function() {
|
|
1534
|
+
if (!open || !listRef.current || active < 0) return;
|
|
1535
|
+
const el = listRef.current.querySelector('[data-idx="' + active + '"]');
|
|
1536
|
+
if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
|
|
1537
|
+
},
|
|
1538
|
+
[open, active, pos[0] != null]
|
|
1539
|
+
);
|
|
1540
|
+
const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
|
|
1541
|
+
const field = (child) => /* @__PURE__ */ React13.createElement(
|
|
1241
1542
|
Field,
|
|
1242
1543
|
{
|
|
1243
1544
|
id,
|
|
1545
|
+
labelId: native ? void 0 : id + "-label",
|
|
1244
1546
|
label: props.label,
|
|
1245
1547
|
hint: props.hint,
|
|
1246
1548
|
error: props.error,
|
|
@@ -1249,19 +1551,180 @@ window.Aura = (() => {
|
|
|
1249
1551
|
disabled: props.disabled,
|
|
1250
1552
|
className: props.className
|
|
1251
1553
|
},
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1554
|
+
child
|
|
1555
|
+
);
|
|
1556
|
+
if (!live)
|
|
1557
|
+
return field(
|
|
1558
|
+
/* @__PURE__ */ React13.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React13.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h3(
|
|
1559
|
+
"select",
|
|
1560
|
+
Object.assign(extra, rest, {
|
|
1561
|
+
ref: native ? ref : selMerged,
|
|
1562
|
+
id,
|
|
1563
|
+
className: "aura-input__control",
|
|
1564
|
+
required: props.required,
|
|
1565
|
+
"aria-invalid": props.error ? true : void 0,
|
|
1566
|
+
"aria-describedby": described
|
|
1567
|
+
}),
|
|
1568
|
+
opts,
|
|
1569
|
+
props.children
|
|
1570
|
+
), /* @__PURE__ */ React13.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }))
|
|
1571
|
+
);
|
|
1572
|
+
const listId = id + "-list", labelId = id + "-label";
|
|
1573
|
+
const optId = function(i) {
|
|
1574
|
+
return id + "-opt-" + i;
|
|
1575
|
+
};
|
|
1576
|
+
const labelledBy = rest["aria-labelledby"] || (props.label ? labelId : void 0);
|
|
1577
|
+
const selIndex = selRef.current ? selRef.current.selectedIndex : -1;
|
|
1578
|
+
const option = function(it) {
|
|
1579
|
+
const sel = selIndex === it.index;
|
|
1580
|
+
return /* @__PURE__ */ React13.createElement(
|
|
1581
|
+
"div",
|
|
1582
|
+
{
|
|
1583
|
+
key: it.index,
|
|
1584
|
+
id: optId(it.index),
|
|
1585
|
+
role: "option",
|
|
1586
|
+
"data-idx": it.index,
|
|
1587
|
+
"aria-selected": sel,
|
|
1588
|
+
"aria-disabled": it.disabled || void 0,
|
|
1589
|
+
className: cx(
|
|
1590
|
+
"aura-combo__option",
|
|
1591
|
+
it.index === active && "is-active",
|
|
1592
|
+
sel && "is-selected",
|
|
1593
|
+
it.disabled && "is-disabled"
|
|
1594
|
+
),
|
|
1595
|
+
onPointerDown: function(e) {
|
|
1596
|
+
e.preventDefault();
|
|
1597
|
+
},
|
|
1598
|
+
onPointerMove: function() {
|
|
1599
|
+
if (!it.disabled && active !== it.index) activeState[1](it.index);
|
|
1600
|
+
},
|
|
1601
|
+
onClick: function() {
|
|
1602
|
+
choose(it);
|
|
1603
|
+
}
|
|
1604
|
+
},
|
|
1605
|
+
/* @__PURE__ */ React13.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React13.createElement("span", { className: "aura-combo__label" }, it.label)),
|
|
1606
|
+
sel ? /* @__PURE__ */ React13.createElement(Icon, { name: "check", className: "aura-combo__check" }) : null
|
|
1607
|
+
);
|
|
1608
|
+
};
|
|
1609
|
+
const runs = [];
|
|
1610
|
+
items2[0].forEach(function(it) {
|
|
1611
|
+
if (it.value === "" && it.disabled) return;
|
|
1612
|
+
const last = runs[runs.length - 1];
|
|
1613
|
+
if (last && last.group === it.group) last.items.push(it);
|
|
1614
|
+
else runs.push({ group: it.group, items: [it] });
|
|
1615
|
+
});
|
|
1616
|
+
const popup = open ? (0, import_react_dom2.createPortal)(
|
|
1617
|
+
/* @__PURE__ */ React13.createElement(
|
|
1618
|
+
"div",
|
|
1619
|
+
{
|
|
1620
|
+
ref: listRef,
|
|
1621
|
+
"data-density": density,
|
|
1622
|
+
className: "aura-combo__popover aura-select__popover",
|
|
1623
|
+
style: pos[0] ? {
|
|
1624
|
+
left: pos[0].left,
|
|
1625
|
+
minWidth: pos[0].width,
|
|
1626
|
+
top: pos[0].top,
|
|
1627
|
+
bottom: pos[0].bottom,
|
|
1628
|
+
maxHeight: pos[0].maxHeight
|
|
1629
|
+
} : { left: -9999, top: -9999 }
|
|
1630
|
+
},
|
|
1631
|
+
/* @__PURE__ */ React13.createElement(
|
|
1632
|
+
"div",
|
|
1633
|
+
{
|
|
1634
|
+
id: listId,
|
|
1635
|
+
role: "listbox",
|
|
1636
|
+
"aria-labelledby": labelledBy,
|
|
1637
|
+
"aria-label": labelledBy ? void 0 : rest["aria-label"],
|
|
1638
|
+
className: "aura-combo__list"
|
|
1639
|
+
},
|
|
1640
|
+
runs.map(function(r, n2) {
|
|
1641
|
+
if (r.group === null) return /* @__PURE__ */ React13.createElement(React13.Fragment, { key: "r" + n2 }, r.items.map(option));
|
|
1642
|
+
const gid = id + "-group-" + n2;
|
|
1643
|
+
return /* @__PURE__ */ React13.createElement("div", { key: "r" + n2, role: "group", "aria-labelledby": gid }, /* @__PURE__ */ React13.createElement("div", { id: gid, className: "aura-select__group" }, r.group), r.items.map(option));
|
|
1644
|
+
})
|
|
1645
|
+
)
|
|
1646
|
+
),
|
|
1647
|
+
document.body
|
|
1648
|
+
) : null;
|
|
1649
|
+
const userFocus = rest.onFocus, userBlur = rest.onBlur, userKey = rest.onKeyDown, userClick = rest.onClick;
|
|
1650
|
+
return field(
|
|
1651
|
+
/* @__PURE__ */ React13.createElement(
|
|
1652
|
+
"div",
|
|
1653
|
+
{
|
|
1654
|
+
className: cx("aura-input aura-select aura-select--custom", props.icon && "has-icon", open && "is-open"),
|
|
1655
|
+
"data-invalid": props.error ? "" : void 0
|
|
1656
|
+
},
|
|
1657
|
+
props.icon ? /* @__PURE__ */ React13.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null,
|
|
1658
|
+
h3(
|
|
1659
|
+
"select",
|
|
1660
|
+
Object.assign(extra, pick(rest, FORM_KEYS), {
|
|
1661
|
+
ref: selMerged,
|
|
1662
|
+
id: id + "-select",
|
|
1663
|
+
className: "aura-select__native",
|
|
1664
|
+
tabIndex: -1,
|
|
1665
|
+
"aria-hidden": true,
|
|
1666
|
+
required: props.required,
|
|
1667
|
+
onChange: function(e) {
|
|
1668
|
+
sync();
|
|
1669
|
+
if (props.onChange) props.onChange(e);
|
|
1670
|
+
},
|
|
1671
|
+
/* A browser focusing the <select> (the required bubble, a label click) hands focus to the button. */
|
|
1672
|
+
onFocus: function() {
|
|
1673
|
+
if (trigRef.current) trigRef.current.focus();
|
|
1674
|
+
},
|
|
1675
|
+
/* Only the blur the button reports (below): the <select>'s own, while it hands focus over, is not one. */
|
|
1676
|
+
onBlur: function(e) {
|
|
1677
|
+
if (!e.nativeEvent.isTrusted && userBlur) userBlur(e);
|
|
1678
|
+
}
|
|
1679
|
+
}),
|
|
1680
|
+
opts,
|
|
1681
|
+
props.children
|
|
1682
|
+
),
|
|
1683
|
+
/* @__PURE__ */ React13.createElement(
|
|
1684
|
+
"button",
|
|
1685
|
+
{
|
|
1686
|
+
...omit(rest, HANDLED_KEYS),
|
|
1687
|
+
ref: trigRef,
|
|
1688
|
+
type: "button",
|
|
1689
|
+
role: "combobox",
|
|
1690
|
+
form: id + "-no-form",
|
|
1691
|
+
className: "aura-input__control aura-select__trigger",
|
|
1692
|
+
disabled: props.disabled,
|
|
1693
|
+
tabIndex: rest.tabIndex,
|
|
1694
|
+
"aria-haspopup": "listbox",
|
|
1695
|
+
"aria-expanded": open,
|
|
1696
|
+
"aria-controls": open ? listId : void 0,
|
|
1697
|
+
"aria-activedescendant": open && active >= 0 ? optId(active) : void 0,
|
|
1698
|
+
id,
|
|
1699
|
+
"aria-label": rest["aria-label"],
|
|
1700
|
+
"aria-labelledby": rest["aria-labelledby"],
|
|
1701
|
+
"aria-required": props.required || void 0,
|
|
1702
|
+
"aria-invalid": props.error ? true : void 0,
|
|
1703
|
+
"aria-describedby": described,
|
|
1704
|
+
onFocus: function(e) {
|
|
1705
|
+
if (userFocus) userFocus(e);
|
|
1706
|
+
},
|
|
1707
|
+
onClick: function(e) {
|
|
1708
|
+
if (userClick) userClick(e);
|
|
1709
|
+
if (e.defaultPrevented) return;
|
|
1710
|
+
if (open) close(true);
|
|
1711
|
+
else openList();
|
|
1712
|
+
},
|
|
1713
|
+
onKeyDown: function(e) {
|
|
1714
|
+
if (userKey) userKey(e);
|
|
1715
|
+
if (!e.defaultPrevented) onKeyDown(e);
|
|
1716
|
+
},
|
|
1717
|
+
onBlur: function(e) {
|
|
1718
|
+
if (listRef.current && e.relatedTarget && listRef.current.contains(e.relatedTarget)) return;
|
|
1719
|
+
if (open) close(false);
|
|
1720
|
+
if (selRef.current) selRef.current.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
|
|
1721
|
+
}
|
|
1722
|
+
},
|
|
1723
|
+
/* @__PURE__ */ React13.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
|
|
1724
|
+
),
|
|
1725
|
+
/* @__PURE__ */ React13.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }),
|
|
1726
|
+
popup
|
|
1727
|
+
)
|
|
1265
1728
|
);
|
|
1266
1729
|
});
|
|
1267
1730
|
|
|
@@ -1360,7 +1823,7 @@ window.Aura = (() => {
|
|
|
1360
1823
|
|
|
1361
1824
|
// src/Combobox.tsx
|
|
1362
1825
|
var React16 = __toESM(require_react(), 1);
|
|
1363
|
-
var
|
|
1826
|
+
var import_react_dom3 = __toESM(require_react_dom(), 1);
|
|
1364
1827
|
function norm(s) {
|
|
1365
1828
|
return String(s == null ? "" : s).normalize("NFC").toLocaleLowerCase("th");
|
|
1366
1829
|
}
|
|
@@ -1380,6 +1843,7 @@ window.Aura = (() => {
|
|
|
1380
1843
|
const multi = all.multiple === true;
|
|
1381
1844
|
const mp = all;
|
|
1382
1845
|
const t = useStrings();
|
|
1846
|
+
const density = useDensity();
|
|
1383
1847
|
const auto = uid(), id = props.id || auto, listId = id + "-list";
|
|
1384
1848
|
const options = (props.options || []).map(toOpt);
|
|
1385
1849
|
const st = useMaybeControlled(
|
|
@@ -1542,8 +2006,8 @@ window.Aura = (() => {
|
|
|
1542
2006
|
const optId = function(i) {
|
|
1543
2007
|
return id + "-opt-" + i;
|
|
1544
2008
|
};
|
|
1545
|
-
const list = open && mounted && posState[0] ? (0,
|
|
1546
|
-
/* @__PURE__ */ React16.createElement("div", { ref: listRef, className: "aura-combo__popover", style: posState[0] }, live.length ? /* @__PURE__ */ React16.createElement(
|
|
2009
|
+
const list = open && mounted && posState[0] ? (0, import_react_dom3.createPortal)(
|
|
2010
|
+
/* @__PURE__ */ React16.createElement("div", { ref: listRef, "data-density": density, className: "aura-combo__popover", style: posState[0] }, live.length ? /* @__PURE__ */ React16.createElement(
|
|
1547
2011
|
"ul",
|
|
1548
2012
|
{
|
|
1549
2013
|
id: listId,
|
|
@@ -1703,7 +2167,7 @@ window.Aura = (() => {
|
|
|
1703
2167
|
|
|
1704
2168
|
// src/DatePicker.tsx
|
|
1705
2169
|
var React17 = __toESM(require_react(), 1);
|
|
1706
|
-
var
|
|
2170
|
+
var import_react_dom4 = __toESM(require_react_dom(), 1);
|
|
1707
2171
|
|
|
1708
2172
|
// src/dates.ts
|
|
1709
2173
|
var ERA = /^พ\.ศ\.\s?|\s?(BE|พ\.ศ\.)$/g;
|
|
@@ -2308,7 +2772,7 @@ window.Aura = (() => {
|
|
|
2308
2772
|
pop.setOpen(false);
|
|
2309
2773
|
if (inputRef.current) inputRef.current.focus();
|
|
2310
2774
|
}
|
|
2311
|
-
const cal = pop.open && pop.mounted && pop.pos ? (0,
|
|
2775
|
+
const cal = pop.open && pop.mounted && pop.pos ? (0, import_react_dom4.createPortal)(
|
|
2312
2776
|
/* @__PURE__ */ React17.createElement(
|
|
2313
2777
|
"div",
|
|
2314
2778
|
{
|
|
@@ -2433,7 +2897,7 @@ window.Aura = (() => {
|
|
|
2433
2897
|
draft[1](null);
|
|
2434
2898
|
if (inputRef.current) inputRef.current.focus();
|
|
2435
2899
|
}
|
|
2436
|
-
function
|
|
2900
|
+
function pick2(iso) {
|
|
2437
2901
|
if (!draft[0]) {
|
|
2438
2902
|
draft[1](iso);
|
|
2439
2903
|
return;
|
|
@@ -2443,7 +2907,7 @@ window.Aura = (() => {
|
|
|
2443
2907
|
st[1](a <= b ? { start: a, end: b } : { start: b, end: a });
|
|
2444
2908
|
close();
|
|
2445
2909
|
}
|
|
2446
|
-
const cal = pop.open && pop.mounted && pop.pos ? (0,
|
|
2910
|
+
const cal = pop.open && pop.mounted && pop.pos ? (0, import_react_dom4.createPortal)(
|
|
2447
2911
|
/* @__PURE__ */ React17.createElement(
|
|
2448
2912
|
"div",
|
|
2449
2913
|
{
|
|
@@ -2477,7 +2941,7 @@ window.Aura = (() => {
|
|
|
2477
2941
|
start: draft[0] || v.start,
|
|
2478
2942
|
end: draft[0] ? null : v.end,
|
|
2479
2943
|
focus: draft[0] || v.start,
|
|
2480
|
-
onSelect:
|
|
2944
|
+
onSelect: pick2
|
|
2481
2945
|
}
|
|
2482
2946
|
)
|
|
2483
2947
|
),
|
|
@@ -2547,7 +3011,7 @@ window.Aura = (() => {
|
|
|
2547
3011
|
|
|
2548
3012
|
// src/Toaster.tsx
|
|
2549
3013
|
var React19 = __toESM(require_react(), 1);
|
|
2550
|
-
var
|
|
3014
|
+
var import_react_dom5 = __toESM(require_react_dom(), 1);
|
|
2551
3015
|
var MAX_VISIBLE = 3;
|
|
2552
3016
|
var toastState = {
|
|
2553
3017
|
list: [],
|
|
@@ -2708,7 +3172,7 @@ window.Aura = (() => {
|
|
|
2708
3172
|
};
|
|
2709
3173
|
}, []);
|
|
2710
3174
|
if (!mounted) return null;
|
|
2711
|
-
return (0,
|
|
3175
|
+
return (0, import_react_dom5.createPortal)(
|
|
2712
3176
|
/* @__PURE__ */ React19.createElement(
|
|
2713
3177
|
"div",
|
|
2714
3178
|
{
|
|
@@ -2989,7 +3453,7 @@ window.Aura = (() => {
|
|
|
2989
3453
|
|
|
2990
3454
|
// src/Command.tsx
|
|
2991
3455
|
var React25 = __toESM(require_react(), 1);
|
|
2992
|
-
var
|
|
3456
|
+
var import_react_dom6 = __toESM(require_react_dom(), 1);
|
|
2993
3457
|
|
|
2994
3458
|
// src/useModal.tsx
|
|
2995
3459
|
var React24 = __toESM(require_react(), 1);
|
|
@@ -3193,7 +3657,7 @@ window.Aura = (() => {
|
|
|
3193
3657
|
}
|
|
3194
3658
|
if (!modal.ready) return null;
|
|
3195
3659
|
let i = -1;
|
|
3196
|
-
return (0,
|
|
3660
|
+
return (0, import_react_dom6.createPortal)(
|
|
3197
3661
|
/* @__PURE__ */ React25.createElement(
|
|
3198
3662
|
"div",
|
|
3199
3663
|
{
|
|
@@ -3302,7 +3766,7 @@ window.Aura = (() => {
|
|
|
3302
3766
|
|
|
3303
3767
|
// src/Tooltip.tsx
|
|
3304
3768
|
var React26 = __toESM(require_react(), 1);
|
|
3305
|
-
var
|
|
3769
|
+
var import_react_dom7 = __toESM(require_react_dom(), 1);
|
|
3306
3770
|
var Tooltip = React26.forwardRef(function Tooltip2(props, ref) {
|
|
3307
3771
|
const id = uid(), st = React26.useState(false), open = props.open !== void 0 ? props.open : st[0], set = st[1];
|
|
3308
3772
|
const anchor = React26.useRef(null), anchorMerged = useMergedRef(ref, anchor), tip = React26.useRef(null), timer = React26.useRef(void 0);
|
|
@@ -3400,7 +3864,7 @@ window.Aura = (() => {
|
|
|
3400
3864
|
},
|
|
3401
3865
|
React26.cloneElement(child, { "aria-describedby": open ? id : child.props["aria-describedby"] })
|
|
3402
3866
|
);
|
|
3403
|
-
return /* @__PURE__ */ React26.createElement(React26.Fragment, null, trigger, open && mounted ? (0,
|
|
3867
|
+
return /* @__PURE__ */ React26.createElement(React26.Fragment, null, trigger, open && mounted ? (0, import_react_dom7.createPortal)(
|
|
3404
3868
|
/* @__PURE__ */ React26.createElement(
|
|
3405
3869
|
"div",
|
|
3406
3870
|
{
|
|
@@ -3422,7 +3886,7 @@ window.Aura = (() => {
|
|
|
3422
3886
|
|
|
3423
3887
|
// src/Dialog.tsx
|
|
3424
3888
|
var React27 = __toESM(require_react(), 1);
|
|
3425
|
-
var
|
|
3889
|
+
var import_react_dom8 = __toESM(require_react_dom(), 1);
|
|
3426
3890
|
var Dialog = React27.forwardRef(function Dialog2(props, ref) {
|
|
3427
3891
|
const t = useStrings();
|
|
3428
3892
|
const density = useDensity();
|
|
@@ -3437,7 +3901,7 @@ window.Aura = (() => {
|
|
|
3437
3901
|
footSelector: ".aura-dialog__foot"
|
|
3438
3902
|
});
|
|
3439
3903
|
if (!modal.ready) return null;
|
|
3440
|
-
return (0,
|
|
3904
|
+
return (0, import_react_dom8.createPortal)(
|
|
3441
3905
|
/* @__PURE__ */ React27.createElement("div", { className: "aura-dialog-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React27.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React27.createElement(
|
|
3442
3906
|
"div",
|
|
3443
3907
|
{
|
|
@@ -3472,7 +3936,7 @@ window.Aura = (() => {
|
|
|
3472
3936
|
});
|
|
3473
3937
|
if (!modal.ready) return null;
|
|
3474
3938
|
const side = props.side === "left" ? "left" : "right";
|
|
3475
|
-
return (0,
|
|
3939
|
+
return (0, import_react_dom8.createPortal)(
|
|
3476
3940
|
/* @__PURE__ */ React27.createElement("div", { className: "aura-dialog-layer aura-drawer-layer", "data-density": density, onKeyDown: modal.onKeyDown }, /* @__PURE__ */ React27.createElement("div", { className: "aura-scrim", onClick: close, "aria-hidden": true }), /* @__PURE__ */ React27.createElement(
|
|
3477
3941
|
"div",
|
|
3478
3942
|
{
|
|
@@ -3496,7 +3960,7 @@ window.Aura = (() => {
|
|
|
3496
3960
|
|
|
3497
3961
|
// src/DataTable.tsx
|
|
3498
3962
|
var React28 = __toESM(require_react(), 1);
|
|
3499
|
-
var
|
|
3963
|
+
var import_react_dom9 = __toESM(require_react_dom(), 1);
|
|
3500
3964
|
var BP = { sm: 640, md: 768, lg: 1024, xl: 1280 };
|
|
3501
3965
|
var DEFAULT_COLUMNS = [
|
|
3502
3966
|
{ key: "id", label: "ID", width: 96, mono: true },
|
|
@@ -3653,7 +4117,7 @@ window.Aura = (() => {
|
|
|
3653
4117
|
function hideFull() {
|
|
3654
4118
|
setTip(null);
|
|
3655
4119
|
}
|
|
3656
|
-
const tipEl = tip && typeof document !== "undefined" ? (0,
|
|
4120
|
+
const tipEl = tip && typeof document !== "undefined" ? (0, import_react_dom9.createPortal)(
|
|
3657
4121
|
/* @__PURE__ */ React28.createElement(
|
|
3658
4122
|
"div",
|
|
3659
4123
|
{
|
|
@@ -4903,7 +5367,7 @@ window.Aura = (() => {
|
|
|
4903
5367
|
|
|
4904
5368
|
// src/SideNav.tsx
|
|
4905
5369
|
var React31 = __toESM(require_react(), 1);
|
|
4906
|
-
var
|
|
5370
|
+
var import_react_dom10 = __toESM(require_react_dom(), 1);
|
|
4907
5371
|
function contains(it, id) {
|
|
4908
5372
|
return !!id && !!it.children && it.children.some(function(c) {
|
|
4909
5373
|
return c.id === id || contains(c, id);
|
|
@@ -5096,7 +5560,7 @@ window.Aura = (() => {
|
|
|
5096
5560
|
}
|
|
5097
5561
|
}
|
|
5098
5562
|
)) : null,
|
|
5099
|
-
tip && typeof document !== "undefined" ? (0,
|
|
5563
|
+
tip && typeof document !== "undefined" ? (0, import_react_dom10.createPortal)(
|
|
5100
5564
|
/* @__PURE__ */ React31.createElement(
|
|
5101
5565
|
"div",
|
|
5102
5566
|
{
|
|
@@ -5540,7 +6004,7 @@ window.Aura = (() => {
|
|
|
5540
6004
|
|
|
5541
6005
|
// src/TimePicker.tsx
|
|
5542
6006
|
var React45 = __toESM(require_react(), 1);
|
|
5543
|
-
var
|
|
6007
|
+
var import_react_dom11 = __toESM(require_react_dom(), 1);
|
|
5544
6008
|
|
|
5545
6009
|
// src/text.ts
|
|
5546
6010
|
function formatBytes(n2) {
|
|
@@ -5756,7 +6220,7 @@ window.Aura = (() => {
|
|
|
5756
6220
|
}
|
|
5757
6221
|
}
|
|
5758
6222
|
const error = errState[0] || props.error;
|
|
5759
|
-
const list = open && mounted && pos[0] ? (0,
|
|
6223
|
+
const list = open && mounted && pos[0] ? (0, import_react_dom11.createPortal)(
|
|
5760
6224
|
/* @__PURE__ */ React45.createElement("div", { ref: listRef, className: "aura-combo__popover aura-time__popover", style: pos[0] }, /* @__PURE__ */ React45.createElement("ul", { id: listId, role: "listbox", "aria-label": props.label, className: "aura-combo__list" }, slots.map(function(s, i) {
|
|
5761
6225
|
const dis = blocked(s), sel = s === value;
|
|
5762
6226
|
return /* @__PURE__ */ React45.createElement(
|
|
@@ -6747,7 +7211,7 @@ window.Aura = (() => {
|
|
|
6747
7211
|
|
|
6748
7212
|
// src/Popover.tsx
|
|
6749
7213
|
var React55 = __toESM(require_react(), 1);
|
|
6750
|
-
var
|
|
7214
|
+
var import_react_dom12 = __toESM(require_react_dom(), 1);
|
|
6751
7215
|
function position(anchor, pop, placement) {
|
|
6752
7216
|
const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.scrollHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
|
|
6753
7217
|
let side = (placement || "bottom-start").split("-")[0], align = (placement || "bottom-start").split("-")[1] || "start";
|
|
@@ -6825,7 +7289,7 @@ window.Aura = (() => {
|
|
|
6825
7289
|
[open, mounted]
|
|
6826
7290
|
);
|
|
6827
7291
|
const child = React55.Children.only(props.trigger);
|
|
6828
|
-
const panel = open && mounted ? (0,
|
|
7292
|
+
const panel = open && mounted ? (0, import_react_dom12.createPortal)(
|
|
6829
7293
|
/* @__PURE__ */ React55.createElement(
|
|
6830
7294
|
"div",
|
|
6831
7295
|
{
|
|
@@ -424,7 +424,16 @@ a.aura-icon-btn { text-decoration: none; }
|
|
|
424
424
|
.aura-select .aura-input__control { padding-right: calc(var(--aura-space-3) + var(--aura-icon-sm) + var(--aura-space-2)); cursor: pointer; }
|
|
425
425
|
.aura-select__chevron { position: absolute; right: var(--aura-space-3); color: var(--aura-fg-secondary); pointer-events: none; }
|
|
426
426
|
.aura-select select:invalid, .aura-select select:has(option[value=""]:checked) { color: var(--aura-fg-tertiary); }
|
|
427
|
+
/* 5.3: the field is a button over an invisible <select> (forms, refs, events); the list is AURA's, like Combobox. */
|
|
427
428
|
.aura-select option { color: var(--aura-fg-primary); background: var(--aura-bg-input); }
|
|
429
|
+
.aura-select--custom { position: relative; }
|
|
430
|
+
.aura-select__native { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; padding: 0; border: 0; opacity: 0; pointer-events: none; appearance: none; }
|
|
431
|
+
.aura-select__trigger { display: flex; align-items: center; text-align: start; background: none; border: 0; color: inherit; font: inherit; width: 100%; min-width: 0; }
|
|
432
|
+
.aura-select__trigger:disabled { cursor: not-allowed; }
|
|
433
|
+
.aura-select__value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
434
|
+
.aura-select__value.is-placeholder { color: var(--aura-fg-tertiary); }
|
|
435
|
+
.aura-select__group { padding: var(--aura-space-2) var(--aura-space-2) var(--aura-space-1); font-family: var(--font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--aura-fg-tertiary); }
|
|
436
|
+
.aura-select.is-open .aura-select__chevron { transform: rotate(180deg); }
|
|
428
437
|
|
|
429
438
|
.aura-radio-group > legend { margin-bottom: 6px; }
|
|
430
439
|
.aura-radio-group__list { display: flex; flex-direction: column; gap: var(--aura-space-3); }
|
package/components/index.d.ts
CHANGED
|
@@ -46,6 +46,11 @@ export interface DataTableState {
|
|
|
46
46
|
sort: DataTableSort | null;
|
|
47
47
|
page: number;
|
|
48
48
|
}
|
|
49
|
+
/** A callback that gets one row. Written with your own row type (`(r: Order) => …`) it still fits (5.4):
|
|
50
|
+
* the row is checked the way method parameters are, so no cast from `Record<string, any>` is needed. */
|
|
51
|
+
export type RowCallback<R> = {
|
|
52
|
+
bivarianceHack(row: Record<string, any>): R;
|
|
53
|
+
}["bivarianceHack"];
|
|
49
54
|
export interface DataTableColumn {
|
|
50
55
|
/** Key into each row object. */
|
|
51
56
|
key: string;
|
|
@@ -62,9 +67,9 @@ export interface DataTableColumn {
|
|
|
62
67
|
/** Header becomes a button cycling asc → desc → unsorted. */
|
|
63
68
|
sortable?: boolean | undefined;
|
|
64
69
|
/** Value to sort by, when not the cell value (dates, amounts). */
|
|
65
|
-
sortValue?:
|
|
70
|
+
sortValue?: RowCallback<string | number | null> | undefined;
|
|
66
71
|
/** Custom cell content. */
|
|
67
|
-
render?:
|
|
72
|
+
render?: RowCallback<React$1.ReactNode> | undefined;
|
|
68
73
|
/** Set false to keep a sized column fixed when the table is `resizable`. */
|
|
69
74
|
resizable?: boolean | undefined;
|
|
70
75
|
/** Resize limits in px. Defaults 64 / 480. For the flexible column, minWidth defaults to 160. */
|
|
@@ -98,7 +103,9 @@ export interface DataTableEmpty {
|
|
|
98
103
|
export interface DataTableProps {
|
|
99
104
|
/** Defaults to ID · NAME · STATUS · OWNER (96 / 160 / 112 / auto px). */
|
|
100
105
|
columns?: DataTableColumn[] | undefined;
|
|
101
|
-
|
|
106
|
+
/** Row objects. Any typed interface works (5.4: before, `interface Order {…}` rows needed a cast); cells show
|
|
107
|
+
* the value at each column's key unless the column has `render`. */
|
|
108
|
+
rows: ReadonlyArray<Record<string, any>>;
|
|
102
109
|
/** Column key giving each row a unique React key. Default: the first column's key. */
|
|
103
110
|
rowKey?: string | undefined;
|
|
104
111
|
/** Accessible name for the table. */
|
|
@@ -145,7 +152,7 @@ export interface DataTableProps {
|
|
|
145
152
|
totalRows?: number | undefined;
|
|
146
153
|
/** Makes each row a link: the first column's content renders as the provider's linkComponent (or `<a>`), and a click
|
|
147
154
|
* or Enter anywhere on the row follows it. Ctrl/⌘-click opens a new tab as usual. */
|
|
148
|
-
getRowHref?:
|
|
155
|
+
getRowHref?: RowCallback<string> | undefined;
|
|
149
156
|
/** Pager arrows become links to these URLs (search-param paging). Without onPageChange the link navigates. */
|
|
150
157
|
getPageHref?: ((page: number) => string) | undefined;
|
|
151
158
|
/** Router link for getRowHref / getPageHref. Default: AuraProvider's linkComponent, else `<a>`. */
|
|
@@ -153,7 +160,7 @@ export interface DataTableProps {
|
|
|
153
160
|
/** Skeleton row count when there is no pageSize. Default 5. */
|
|
154
161
|
skeletonRows?: number | undefined;
|
|
155
162
|
/** Called on row click or Enter. */
|
|
156
|
-
onRowActivate?:
|
|
163
|
+
onRowActivate?: RowCallback<void> | undefined;
|
|
157
164
|
/** Fixed height in px: sticky header, and only the rows in view are rendered. */
|
|
158
165
|
height?: number | undefined;
|
|
159
166
|
/** Column menu on every header, drag-to-reorder, and the show/hide columns button. */
|
|
@@ -321,11 +328,14 @@ export interface FormErrorItem {
|
|
|
321
328
|
message: React$1.ReactNode;
|
|
322
329
|
}
|
|
323
330
|
/** The list of problems at the top of a form after a failed submit; each links to its field (GOV.UK pattern). */
|
|
331
|
+
/** react-hook-form's `formState.errors` (FieldErrors) or any object shaped like it: a `message` at any depth. */
|
|
332
|
+
export type FormErrorTree = {
|
|
333
|
+
readonly [field: string]: object | undefined;
|
|
334
|
+
};
|
|
324
335
|
export interface FormErrorSummaryProps {
|
|
325
|
-
/** A list, or react-hook-form's `formState.errors` as is
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
} | undefined>;
|
|
336
|
+
/** A list, or react-hook-form's `formState.errors` as is: `{ name: { message } }`, nested for `address.street`
|
|
337
|
+
* and field arrays (`items.0.name`). Empty → renders nothing. */
|
|
338
|
+
errors: FormErrorItem[] | FormErrorTree;
|
|
329
339
|
/** Default "Fix N fields to continue" in the provider's language. */
|
|
330
340
|
title?: React$1.ReactNode | undefined;
|
|
331
341
|
/** Called with the field when a link is followed, e.g. react-hook-form's `setFocus`. Default: focus the element whose id (or name) is the field. */
|
|
@@ -417,9 +427,15 @@ export type SelectOption = string | {
|
|
|
417
427
|
label: string;
|
|
418
428
|
disabled?: boolean | undefined;
|
|
419
429
|
};
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
430
|
+
/** 5.3: opens AURA's own list (light and dark alike) over a real `<select>`, which keeps `name`, `required`, the ref,
|
|
431
|
+
* `onChange`, react-hook-form `register` and form posts. `multiple` or `size > 1` keeps the native list box. */
|
|
432
|
+
export interface SelectProps extends Omit<FieldProps, "label">, Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "required"> {
|
|
433
|
+
/** Visible label; also the accessible name. Omit only with `aria-label` or `aria-labelledby` (a toolbar, a table
|
|
434
|
+
* cell); development builds warn when the field has no name at all. */
|
|
435
|
+
label?: string | undefined;
|
|
436
|
+
/** The choices; or pass `<option>` / `<optgroup>` children instead (5.3: optional). */
|
|
437
|
+
options?: SelectOption[] | undefined;
|
|
438
|
+
/** Shown in fg-tertiary on the closed field until something is chosen; not listed as a choice. */
|
|
423
439
|
placeholder?: string | undefined;
|
|
424
440
|
icon?: IconInput | undefined;
|
|
425
441
|
}
|
|
@@ -1217,6 +1233,7 @@ export interface FieldComponentProps extends Omit<FieldPropsPublic, "label"> {
|
|
|
1217
1233
|
export declare const Field: React$1.ForwardRefExoticComponent<FieldComponentProps & React$1.RefAttributes<HTMLDivElement>>;
|
|
1218
1234
|
export declare const TextField: React$1.ForwardRefExoticComponent<TextFieldProps & React$1.RefAttributes<HTMLInputElement>>;
|
|
1219
1235
|
export declare const Textarea: React$1.ForwardRefExoticComponent<TextareaProps & React$1.RefAttributes<HTMLTextAreaElement>>;
|
|
1236
|
+
/** A select field: a button that opens an AURA list (5.3), over a real <select> that keeps forms, refs and events. */
|
|
1220
1237
|
export declare const Select: React$1.ForwardRefExoticComponent<SelectProps & React$1.RefAttributes<HTMLSelectElement>>;
|
|
1221
1238
|
export declare const RadioGroup: React$1.ForwardRefExoticComponent<RadioGroupProps & React$1.RefAttributes<HTMLFieldSetElement>>;
|
|
1222
1239
|
export declare const Switch: React$1.ForwardRefExoticComponent<SwitchProps & React$1.RefAttributes<HTMLButtonElement>>;
|
package/eslint-plugin-aura.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
// @ts-check
|
|
1
2
|
// eslint-plugin-aura.js — AURA 5.2
|
|
2
3
|
// Flags hard-coded colours in JS/TS: hex (#rgb, #rgba, #rrggbb, #rrggbbaa), colour functions (rgb/hsl/hwb/lab/lch/
|
|
3
4
|
// oklab/oklch/color-mix), CSS named colours in a style value, Tailwind palette classes (bg-red-500, text-white) and
|
|
@@ -17,6 +18,7 @@ const HEX = /#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})(?![\w-])/i;
|
|
|
17
18
|
* call a template literal continues (`rgb(${r}, …)`). Token-based ones pass: rgb(var(--brand-rgb) / .5),
|
|
18
19
|
* color-mix(in srgb, var(--aura-accent) 20%, transparent). var() and url() are removed before any check. */
|
|
19
20
|
const FN_CALL = /\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color-mix)\(([^()]*)(\)|$)/gi;
|
|
21
|
+
/** @param {string} v */
|
|
20
22
|
function fnHit(v) {
|
|
21
23
|
FN_CALL.lastIndex = 0;
|
|
22
24
|
let m;
|
|
@@ -26,7 +28,7 @@ function fnHit(v) {
|
|
|
26
28
|
}
|
|
27
29
|
return null;
|
|
28
30
|
}
|
|
29
|
-
const stripRefs = (v) => v.replace(/var\([^()]*(?:\([^()]*\)[^()]*)*\)/g, ' ').replace(/url\([^)]*\)/g, ' ');
|
|
31
|
+
const stripRefs = (/** @type {string} */ v) => v.replace(/var\([^()]*(?:\([^()]*\)[^()]*)*\)/g, ' ').replace(/url\([^)]*\)/g, ' ');
|
|
30
32
|
const TW_ARB = new RegExp('(?:^|[\\s:"\'`])(?:' + UTIL + ')-\\[(?:#|rgb|hsl|oklch|color)', 'i');
|
|
31
33
|
const TW_PAL = new RegExp(
|
|
32
34
|
'(?:^|[\\s:"\'`!])(?:' + UTIL + ')-(?:(?:' + PALETTE + ')-\\d{2,3}|white|black)(?:\\/\\d+)?(?![\\w-])',
|
|
@@ -49,6 +51,10 @@ const COLOUR_KEY =
|
|
|
49
51
|
const NOT_COLOUR_ATTR =
|
|
50
52
|
/^(?:href|to|id|htmlFor|for|src|action|formAction|xlinkHref|xlink:href|aria-[a-z]+|data-[\w-]+|name|key)$/;
|
|
51
53
|
|
|
54
|
+
/**
|
|
55
|
+
* @param {unknown} raw
|
|
56
|
+
* @param {boolean} colourKey
|
|
57
|
+
*/
|
|
52
58
|
function hitIn(raw, colourKey) {
|
|
53
59
|
if (typeof raw !== 'string') return null;
|
|
54
60
|
const value = stripRefs(raw);
|
|
@@ -59,6 +65,8 @@ function hitIn(raw, colourKey) {
|
|
|
59
65
|
if (colourKey && NAMED_WHOLE.test(value)) return value.trim();
|
|
60
66
|
return null;
|
|
61
67
|
}
|
|
68
|
+
/** An ESTree / JSX AST node, loosely (the plugin runs on whatever parser the project uses). @typedef {any} Node */
|
|
69
|
+
/** @param {Node} node */
|
|
62
70
|
function skip(node) {
|
|
63
71
|
let p = node.parent;
|
|
64
72
|
if (!p) return false;
|
|
@@ -80,11 +88,17 @@ function skip(node) {
|
|
|
80
88
|
return true;
|
|
81
89
|
return false;
|
|
82
90
|
}
|
|
91
|
+
/** @param {Node} node */
|
|
83
92
|
function colourKeyOf(node) {
|
|
84
93
|
const p = node.parent;
|
|
85
94
|
if (p && p.type === 'Property' && p.value === node && p.key) return COLOUR_KEY.test(p.key.name || p.key.value || '');
|
|
86
95
|
return false;
|
|
87
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* @param {{ report(d: { node: Node, message: string }): void }} context
|
|
99
|
+
* @param {Node} node
|
|
100
|
+
* @param {string} hit
|
|
101
|
+
*/
|
|
88
102
|
function report(context, node, hit) {
|
|
89
103
|
context.report({
|
|
90
104
|
node,
|
|
@@ -101,14 +115,14 @@ const plugin = {
|
|
|
101
115
|
docs: { description: 'ห้ามใช้สี hardcode ให้ใช้ token — use AURA semantic tokens, never raw colours' },
|
|
102
116
|
schema: [],
|
|
103
117
|
},
|
|
104
|
-
create(context) {
|
|
118
|
+
create(/** @type {any} */ context) {
|
|
105
119
|
return {
|
|
106
|
-
Literal(node) {
|
|
120
|
+
Literal(/** @type {Node} */ node) {
|
|
107
121
|
if (skip(node)) return;
|
|
108
122
|
const hit = hitIn(node.value, colourKeyOf(node));
|
|
109
123
|
if (hit) report(context, node, hit);
|
|
110
124
|
},
|
|
111
|
-
TemplateElement(node) {
|
|
125
|
+
TemplateElement(/** @type {Node} */ node) {
|
|
112
126
|
if (skip(node)) return;
|
|
113
127
|
const hit = hitIn(node.value && node.value.raw, false);
|
|
114
128
|
if (hit) report(context, node, hit);
|
|
@@ -117,6 +131,7 @@ const plugin = {
|
|
|
117
131
|
},
|
|
118
132
|
},
|
|
119
133
|
},
|
|
134
|
+
/** @type {Record<string, { plugins: unknown, rules: Record<string, string> }>} */
|
|
120
135
|
configs: {},
|
|
121
136
|
};
|
|
122
137
|
/* Flat config (ESLint 9, the default): the plugin object itself, not a name. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jirawatpyk/aura-tokens",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.4.0",
|
|
4
4
|
"description": "AURA Design System — tokens (CSS variables, Tailwind preset, Figma variables), theme contrast checks and token lint.",
|
|
5
5
|
"main": "tailwind.tokens.cjs",
|
|
6
6
|
"types": "components/index.d.ts",
|
|
@@ -55,14 +55,14 @@
|
|
|
55
55
|
"./package.json": "./package.json"
|
|
56
56
|
},
|
|
57
57
|
"scripts": {
|
|
58
|
-
"build": "node scripts/build-tokens.
|
|
58
|
+
"build": "node scripts/build-tokens.mts",
|
|
59
59
|
"a11y": "node scripts/a11y-check.js",
|
|
60
|
-
"check:tailwind4": "node scripts/tailwind4-check.
|
|
60
|
+
"check:tailwind4": "node scripts/tailwind4-check.mts",
|
|
61
61
|
"lint:tokens": "node scripts/lint-tokens.js ../react/src && node scripts/lint-tokens.js ../react/styles",
|
|
62
|
-
"check:generated": "node scripts/build-tokens.
|
|
62
|
+
"check:generated": "node scripts/build-tokens.mts && git diff --exit-code -- aura.css aura-fonts.css aura-fonts.local.css tailwind.tokens.cjs aura-tailwind.css aura-tailwind.prefixed.css figma-variables.csv",
|
|
63
63
|
"prepack": "npm run build",
|
|
64
|
-
"check:fonts": "node scripts/fonts-check.
|
|
65
|
-
"check:eslint": "node scripts/eslint-check.
|
|
64
|
+
"check:fonts": "node scripts/fonts-check.mts",
|
|
65
|
+
"check:eslint": "node scripts/eslint-check.mts"
|
|
66
66
|
},
|
|
67
67
|
"keywords": [
|
|
68
68
|
"design-system",
|
package/scripts/a11y-check.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
2
3
|
/* AURA contrast check — resolves tokens.json and tests every documented text/ground pair in BOTH themes.
|
|
3
4
|
* Text needs 4.5:1, control edges and focus rings 3:1 (WCAG 2.1 AA). Writes a11y-report.json; exits 1 on any failure. */
|
|
4
5
|
const fs = require('fs');
|
|
@@ -6,6 +7,11 @@ const path = require('path');
|
|
|
6
7
|
const TOKENS = path.join(__dirname, '..', 'tokens.json');
|
|
7
8
|
const T = JSON.parse(fs.readFileSync(TOKENS, 'utf8'));
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @param {unknown} value
|
|
12
|
+
* @param {string} theme
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
9
15
|
function resolve(value, theme, depth = 0) {
|
|
10
16
|
if (depth > 16) throw new Error('alias loop at ' + value);
|
|
11
17
|
const m = /^\{(primitive|semantic|component)\.([a-z0-9-]+)(?:\.([a-z0-9]+))?\}$/.exec(String(value).trim());
|
|
@@ -16,24 +22,38 @@ function resolve(value, theme, depth = 0) {
|
|
|
16
22
|
if (v === undefined) throw new Error(`missing ${tier}.${a} (${theme})`);
|
|
17
23
|
return resolve(v, theme, depth + 1);
|
|
18
24
|
}
|
|
19
|
-
const tok = (name, theme) => resolve(T.semantic[theme][name] ?? T.component[theme][name], theme);
|
|
25
|
+
const tok = (/** @type {string} */ name, /** @type {string} */ theme) => resolve(T.semantic[theme][name] ?? T.component[theme][name], theme);
|
|
20
26
|
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} c
|
|
29
|
+
* @param {string} [ground]
|
|
30
|
+
* @returns {number[]}
|
|
31
|
+
*/
|
|
21
32
|
function rgb(c, ground) {
|
|
22
33
|
let m = /^#([0-9a-f]{6})$/i.exec(c);
|
|
23
|
-
|
|
34
|
+
const hex = m && m[1];
|
|
35
|
+
if (hex) return [0, 2, 4].map((i) => parseInt(hex.slice(i, i + 2), 16));
|
|
24
36
|
m = /^#([0-9a-f]{3})$/i.exec(c);
|
|
25
37
|
if (m) return m[1].split('').map((x) => parseInt(x + x, 16));
|
|
26
38
|
m = /^rgba?\(([^)]+)\)$/i.exec(c);
|
|
27
39
|
if (m) {
|
|
28
40
|
const [r, g, b, a = 1] = m[1].split(',').map(Number);
|
|
41
|
+
if ([r, g, b, a].some((x) => x === undefined || Number.isNaN(x))) throw new Error('unsupported colour ' + c);
|
|
29
42
|
if (a >= 1 || !ground) return [r, g, b];
|
|
30
43
|
const G = rgb(ground);
|
|
31
|
-
|
|
44
|
+
/** @type {(v: number, i: number) => number} */
|
|
45
|
+
const mix = (v, i) => Math.round(a * v + (1 - a) * (G[i] || 0));
|
|
46
|
+
return [r || 0, g || 0, b || 0].map(mix);
|
|
32
47
|
}
|
|
33
48
|
throw new Error('unsupported colour ' + c);
|
|
34
49
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
50
|
+
const W = [0.2126, 0.7152, 0.0722];
|
|
51
|
+
const lum = (/** @type {string} */ c) => rgb(c).map((v) => v / 255).map((v) => (v <= 0.03928 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4))
|
|
52
|
+
.reduce((s, v, i) => s + v * (W[i] || 0), 0);
|
|
53
|
+
/**
|
|
54
|
+
* @param {string} fg
|
|
55
|
+
* @param {string} bg
|
|
56
|
+
*/
|
|
37
57
|
function ratio(fg, bg) {
|
|
38
58
|
const B = '#' + rgb(bg).map((v) => v.toString(16).padStart(2, '0')).join('');
|
|
39
59
|
const F = '#' + rgb(fg, B).map((v) => v.toString(16).padStart(2, '0')).join('');
|
|
@@ -43,6 +63,7 @@ function ratio(fg, bg) {
|
|
|
43
63
|
|
|
44
64
|
const TEXT = 4.5, UI = 3;
|
|
45
65
|
const grounds = ['bg-surface', 'bg-canvas', 'bg-surface-hover'];
|
|
66
|
+
/** @type {[string, string, number][]} */
|
|
46
67
|
const pairs = [];
|
|
47
68
|
for (const fg of ['fg-primary', 'fg-secondary', 'fg-tertiary', 'fg-accent', 'fg-danger', 'fg-positive']) for (const bg of grounds) pairs.push([fg, bg, TEXT]);
|
|
48
69
|
pairs.push(['fg-primary', 'bg-selected', TEXT], ['fg-secondary', 'bg-selected', TEXT]);
|
package/scripts/lint-tokens.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// @ts-check
|
|
2
3
|
/* Token lint — fails when source files hard-code colours instead of AURA tokens, or (CSS) motion:
|
|
3
4
|
* a transition/animation must take its duration and easing from --aura-duration-* / --aura-ease / --aura-spring
|
|
4
5
|
* (`linear` is allowed: it's for spinners and progress, not a feel).
|
|
@@ -24,16 +25,19 @@ const NAMED = 'white|black|red|green|blue|yellow|orange|purple|pink|gray|grey|si
|
|
|
24
25
|
const CSS_NAMED = new RegExp('(?:^|[;{\\s])(?:color|background(?:-color)?|border(?:-[a-z]+)?-color|fill|stroke|outline-color)\\s*:\\s*[^;]*(?<![\\w-])(?:' + NAMED + ')(?![\\w-])');
|
|
25
26
|
/* Colour functions count only with a literal colour inside (a hex or three numbers), as in eslint-plugin-aura. */
|
|
26
27
|
const FN_CALL = /\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color-mix)\(([^()]*)(\)|$)/gi;
|
|
28
|
+
/** @param {string} v */
|
|
27
29
|
function fnHit(v) {
|
|
28
30
|
FN_CALL.lastIndex = 0;
|
|
29
31
|
let m;
|
|
30
32
|
while ((m = FN_CALL.exec(v))) if (!m[2] || /#[0-9a-f]{3}/i.test(m[1]) || (m[1].match(/-?\d*\.?\d+/g) || []).length >= 3) return m[0];
|
|
31
33
|
return null;
|
|
32
34
|
}
|
|
33
|
-
const stripRefs = (v) => v.replace(/var\([^()]*(?:\([^()]*\)[^()]*)*\)/g, ' ').replace(/url\([^)]*\)/g, ' ');
|
|
35
|
+
const stripRefs = (/** @type {string} */ v) => v.replace(/var\([^()]*(?:\([^()]*\)[^()]*)*\)/g, ' ').replace(/url\([^)]*\)/g, ' ');
|
|
34
36
|
const MOTION_PROP = /\b(?:transition|animation)(?:-duration|-timing-function|-delay)?\s*:/i;
|
|
35
37
|
const MOTION_RAW = /(?:^|[\s,:(])(?:\d*\.?\d+m?s)\b|cubic-bezier\(|\bease(?:-in-out|-in|-out)?\b/gi;
|
|
38
|
+
/** @type {string[]} */
|
|
36
39
|
const violations = [];
|
|
40
|
+
/** @param {string} d */
|
|
37
41
|
function scan(d) {
|
|
38
42
|
if (!fs.existsSync(d)) return;
|
|
39
43
|
for (const f of fs.readdirSync(d)) {
|
|
@@ -46,8 +50,9 @@ function scan(d) {
|
|
|
46
50
|
if (/aura-lint:\s*allow-colours/.test(text.slice(0, 600))) continue;
|
|
47
51
|
text.split('\n').forEach((line, i) => {
|
|
48
52
|
const plain = stripRefs(line);
|
|
49
|
-
const
|
|
50
|
-
|
|
53
|
+
const fn = fnHit(plain);
|
|
54
|
+
const m = plain.match(COLOUR) || (fn ? [fn] : null);
|
|
55
|
+
if (m) violations.push(`${path.relative(process.cwd(), full)}:${i + 1} ${m.map((/** @type {string} */ s) => s.trim().replace(/^[:"'`!]/, '')).join(', ')}`);
|
|
51
56
|
if (/\.s?css$/.test(f) && !/^\s*--/.test(line)) {
|
|
52
57
|
const n = CSS_NAMED.exec(stripRefs(line.replace(/\/\*.*?\*\//g, '')));
|
|
53
58
|
if (n) violations.push(`${path.relative(process.cwd(), full)}:${i + 1} named colour: ${n[0].trim()}`);
|
package/tailwind.tokens.cjs
CHANGED
package/tokens.json
CHANGED