@jirawatpyk/aura-tokens 5.2.0 → 5.3.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/aura-tailwind.css CHANGED
@@ -1,4 +1,4 @@
1
- /* AURA v5.2.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.js from tokens.json. Do not edit by hand.
1
+ /* AURA v5.3.0 — Tailwind v4 theme. GENERATED by scripts/build-tokens.js 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.2.0 — Tailwind v4 theme with aura- prefixed names. GENERATED by scripts/build-tokens.js. Do not edit by hand.
1
+ /* AURA v5.3.0 — Tailwind v4 theme with aura- prefixed names. GENERATED by scripts/build-tokens.js. 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.2.0 — GENERATED by scripts/build-tokens.js from tokens.json. Do not edit by hand. */
1
+ /* AURA v5.3.0 — GENERATED by scripts/build-tokens.js 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,259 @@ 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
- return /* @__PURE__ */ React13.createElement(
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
+ const selRef = React13.useRef(null), trigRef = React13.useRef(null), listRef = React13.useRef(null);
1290
+ const shown = React13.useState(function() {
1291
+ return initialLabel(props);
1292
+ });
1293
+ const openState = React13.useState(false), open = openState[0];
1294
+ const activeState = React13.useState(-1), active = activeState[0];
1295
+ const items2 = React13.useState([]);
1296
+ const pos = React13.useState(null);
1297
+ const typed = React13.useRef({ text: "", at: 0 });
1298
+ const sync = React13.useCallback(function() {
1299
+ const el = selRef.current;
1300
+ if (!el) return;
1301
+ const o = el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null;
1302
+ const next = o ? { label: o.textContent || "", empty: o.value === "" } : { label: "", empty: true };
1303
+ shown[1](function(cur) {
1304
+ return cur.label === next.label && cur.empty === next.empty ? cur : next;
1305
+ });
1306
+ }, []);
1307
+ const hook = React13.useCallback(
1308
+ function(el) {
1309
+ selRef.current = el;
1310
+ if (!el || native || el.__auraHooked) return;
1311
+ el.__auraHooked = true;
1312
+ const proto = HTMLSelectElement.prototype;
1313
+ const pv = Object.getOwnPropertyDescriptor(proto, "value"), pi = Object.getOwnPropertyDescriptor(proto, "selectedIndex");
1314
+ if (pv && pv.set && pv.get)
1315
+ Object.defineProperty(el, "value", {
1316
+ configurable: true,
1317
+ get: function() {
1318
+ return pv.get.call(el);
1319
+ },
1320
+ set: function(v) {
1321
+ pv.set.call(el, v);
1322
+ sync();
1323
+ }
1324
+ });
1325
+ if (pi && pi.set && pi.get)
1326
+ Object.defineProperty(el, "selectedIndex", {
1327
+ configurable: true,
1328
+ get: function() {
1329
+ return pi.get.call(el);
1330
+ },
1331
+ set: function(v) {
1332
+ pi.set.call(el, v);
1333
+ sync();
1334
+ }
1335
+ });
1336
+ el.focus = function(o) {
1337
+ if (trigRef.current) trigRef.current.focus(o);
1338
+ else proto.focus.call(el, o);
1339
+ };
1340
+ },
1341
+ [native, sync]
1342
+ );
1343
+ const selMerged = useMergedRef(ref, hook);
1344
+ React13.useEffect(
1345
+ function() {
1346
+ const f = selRef.current && selRef.current.form;
1347
+ if (!f || native) return;
1348
+ function onReset() {
1349
+ setTimeout(sync, 0);
1350
+ }
1351
+ f.addEventListener("reset", onReset);
1352
+ return function() {
1353
+ f.removeEventListener("reset", onReset);
1354
+ };
1355
+ },
1356
+ [native, sync]
1357
+ );
1358
+ const focusedOnce = React13.useRef(false);
1359
+ React13.useEffect(
1360
+ function() {
1361
+ if (!live || focusedOnce.current || !trigRef.current) return;
1362
+ focusedOnce.current = true;
1363
+ if (props.autoFocus || selRef.current && document.activeElement === selRef.current) trigRef.current.focus();
1364
+ },
1365
+ [live]
1366
+ );
1367
+ useIsoLayoutEffect(function() {
1368
+ if (!live) return;
1369
+ sync();
1370
+ if (!open) return;
1371
+ const now = readItems(selRef.current), was = items2[0];
1372
+ const same2 = now.length === was.length && now.every(function(it, i) {
1373
+ const w = was[i];
1374
+ return w.value === it.value && w.label === it.label && w.disabled === it.disabled && w.group === it.group;
1375
+ });
1376
+ if (same2) return;
1377
+ items2[1](now);
1378
+ if (!choices(now).length) {
1379
+ close(false);
1380
+ return;
1381
+ }
1382
+ const hv = was[active] ? was[active].value : null;
1383
+ const again = now.filter(function(it) {
1384
+ return !it.disabled && it.value === hv;
1385
+ })[0];
1386
+ const cur = selRef.current ? selRef.current.selectedIndex : -1;
1387
+ activeState[1](again ? again.index : now[cur] && !now[cur].disabled ? cur : choices(now)[0].index);
1388
+ });
1389
+ function choices(list) {
1390
+ return list.filter(function(i) {
1391
+ return !i.disabled;
1392
+ });
1393
+ }
1394
+ function choose(it) {
1395
+ const el = selRef.current;
1396
+ if (!el || it.disabled) return;
1397
+ if (el.value !== it.value) {
1398
+ el.value = it.value;
1399
+ el.dispatchEvent(new Event("input", { bubbles: true }));
1400
+ el.dispatchEvent(new Event("change", { bubbles: true }));
1401
+ }
1402
+ sync();
1403
+ close(true);
1404
+ }
1405
+ function openList(at) {
1406
+ if (props.disabled || trigRef.current && trigRef.current.disabled) return;
1407
+ const list = readItems(selRef.current);
1408
+ const ok = choices(list);
1409
+ if (!ok.length) return;
1410
+ items2[1](list);
1411
+ const cur = selRef.current ? selRef.current.selectedIndex : -1;
1412
+ let a = at === "first" ? ok[0].index : at === "last" ? ok[ok.length - 1].index : cur;
1413
+ if (typeof at === "number") a = at;
1414
+ if (a < 0 || !list[a] || list[a].disabled) a = ok[0].index;
1415
+ activeState[1](a);
1416
+ openState[1](true);
1417
+ }
1418
+ function close(focus) {
1419
+ openState[1](false);
1420
+ pos[1](null);
1421
+ if (focus && trigRef.current) trigRef.current.focus();
1422
+ }
1423
+ function step(from, dir, list) {
1424
+ for (let i = from + dir; i >= 0 && i < list.length; i += dir) if (!list[i].disabled) return i;
1425
+ return from;
1426
+ }
1427
+ function typing() {
1428
+ return !!typed.current.text && Date.now() - typed.current.at < 500;
1429
+ }
1430
+ function typeahead(ch, list, from) {
1431
+ const now = Date.now(), t = typed.current;
1432
+ t.text = now - t.at < 500 ? t.text + ch : ch;
1433
+ t.at = now;
1434
+ const q = t.text.toLocaleLowerCase();
1435
+ for (let k = 1; k <= list.length; k++) {
1436
+ const i = (from + (t.text.length > 1 ? 0 : 1) + k - 1 + list.length) % list.length;
1437
+ if (!list[i].disabled && list[i].label.toLocaleLowerCase().indexOf(q) === 0) return i;
1438
+ }
1439
+ t.text = "";
1440
+ return -1;
1441
+ }
1442
+ function onKeyDown(e) {
1443
+ const k = e.key;
1444
+ const printable = k.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey;
1445
+ const search = printable && (k !== " " || typing());
1446
+ if (!open) {
1447
+ if (search) {
1448
+ const list2 = readItems(selRef.current);
1449
+ const i = typeahead(k, list2, selRef.current ? selRef.current.selectedIndex : -1);
1450
+ e.preventDefault();
1451
+ if (i >= 0) openList(i);
1452
+ } else if (k === "ArrowDown" || k === "ArrowUp" || k === "Enter" || k === " ") {
1453
+ e.preventDefault();
1454
+ openList(k === "ArrowUp" && !(selRef.current && selRef.current.value) ? "last" : void 0);
1455
+ }
1456
+ return;
1457
+ }
1458
+ const list = items2[0];
1459
+ if (search) {
1460
+ e.preventDefault();
1461
+ const i = typeahead(k, list, active);
1462
+ if (i >= 0) activeState[1](i);
1463
+ } else if (k === "ArrowDown") {
1464
+ e.preventDefault();
1465
+ activeState[1](step(active, 1, list));
1466
+ } else if (k === "ArrowUp") {
1467
+ e.preventDefault();
1468
+ activeState[1](step(active, -1, list));
1469
+ } else if (k === "Home" || k === "PageUp") {
1470
+ e.preventDefault();
1471
+ activeState[1](k === "Home" ? step(-1, 1, list) : Math.max(step(-1, 1, list), step(active - 9, 1, list)));
1472
+ } else if (k === "End" || k === "PageDown") {
1473
+ e.preventDefault();
1474
+ activeState[1](
1475
+ k === "End" ? step(list.length, -1, list) : Math.min(step(list.length, -1, list), step(active + 9, -1, list))
1476
+ );
1477
+ } else if (k === "Enter" || k === " ") {
1478
+ e.preventDefault();
1479
+ if (list[active]) choose(list[active]);
1480
+ } else if (k === "Escape") {
1481
+ e.preventDefault();
1482
+ e.stopPropagation();
1483
+ close(true);
1484
+ } else if (k === "Tab") {
1485
+ close(false);
1486
+ }
1487
+ }
1488
+ useIsoLayoutEffect(
1489
+ function() {
1490
+ if (!open) return;
1491
+ function place() {
1492
+ const t = trigRef.current && trigRef.current.parentElement;
1493
+ if (!t) return;
1494
+ 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;
1495
+ pos[1]({
1496
+ left: r.left,
1497
+ width: r.width,
1498
+ top: up ? void 0 : r.bottom + 4,
1499
+ bottom: up ? window.innerHeight - r.top + 4 : void 0,
1500
+ maxHeight: Math.min(320, (up ? above : below2) - 4)
1501
+ });
1502
+ }
1503
+ place();
1504
+ function outside(e) {
1505
+ const n2 = e.target;
1506
+ if (listRef.current && listRef.current.contains(n2)) return;
1507
+ if (trigRef.current && trigRef.current.contains(n2)) return;
1508
+ close(false);
1509
+ }
1510
+ function onScroll(e) {
1511
+ if (!listRef.current || !listRef.current.contains(e.target)) place();
1512
+ }
1513
+ document.addEventListener("pointerdown", outside, true);
1514
+ window.addEventListener("scroll", onScroll, true);
1515
+ window.addEventListener("resize", place);
1516
+ return function() {
1517
+ document.removeEventListener("pointerdown", outside, true);
1518
+ window.removeEventListener("scroll", onScroll, true);
1519
+ window.removeEventListener("resize", place);
1520
+ };
1521
+ },
1522
+ [open]
1523
+ );
1524
+ React13.useEffect(
1525
+ function() {
1526
+ if (!open || !listRef.current || active < 0) return;
1527
+ const el = listRef.current.querySelector('[data-idx="' + active + '"]');
1528
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
1529
+ },
1530
+ [open, active, pos[0] != null]
1531
+ );
1532
+ const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
1533
+ const field = (child) => /* @__PURE__ */ React13.createElement(
1241
1534
  Field,
1242
1535
  {
1243
1536
  id,
1537
+ labelId: native ? void 0 : id + "-label",
1244
1538
  label: props.label,
1245
1539
  hint: props.hint,
1246
1540
  error: props.error,
@@ -1249,19 +1543,180 @@ window.Aura = (() => {
1249
1543
  disabled: props.disabled,
1250
1544
  className: props.className
1251
1545
  },
1252
- /* @__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(
1253
- "select",
1254
- Object.assign(extra, rest, {
1255
- ref,
1256
- id,
1257
- className: "aura-input__control",
1258
- required: props.required,
1259
- "aria-invalid": props.error ? true : void 0,
1260
- "aria-describedby": describedBy(id, props)
1261
- }),
1262
- opts,
1263
- props.children
1264
- ), /* @__PURE__ */ React13.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }))
1546
+ child
1547
+ );
1548
+ if (!live)
1549
+ return field(
1550
+ /* @__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(
1551
+ "select",
1552
+ Object.assign(extra, rest, {
1553
+ ref: native ? ref : selMerged,
1554
+ id,
1555
+ className: "aura-input__control",
1556
+ required: props.required,
1557
+ "aria-invalid": props.error ? true : void 0,
1558
+ "aria-describedby": described
1559
+ }),
1560
+ opts,
1561
+ props.children
1562
+ ), /* @__PURE__ */ React13.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }))
1563
+ );
1564
+ const listId = id + "-list", labelId = id + "-label";
1565
+ const optId = function(i) {
1566
+ return id + "-opt-" + i;
1567
+ };
1568
+ const labelledBy = rest["aria-labelledby"] || (props.label ? labelId : void 0);
1569
+ const selIndex = selRef.current ? selRef.current.selectedIndex : -1;
1570
+ const option = function(it) {
1571
+ const sel = selIndex === it.index;
1572
+ return /* @__PURE__ */ React13.createElement(
1573
+ "div",
1574
+ {
1575
+ key: it.index,
1576
+ id: optId(it.index),
1577
+ role: "option",
1578
+ "data-idx": it.index,
1579
+ "aria-selected": sel,
1580
+ "aria-disabled": it.disabled || void 0,
1581
+ className: cx(
1582
+ "aura-combo__option",
1583
+ it.index === active && "is-active",
1584
+ sel && "is-selected",
1585
+ it.disabled && "is-disabled"
1586
+ ),
1587
+ onPointerDown: function(e) {
1588
+ e.preventDefault();
1589
+ },
1590
+ onPointerMove: function() {
1591
+ if (!it.disabled && active !== it.index) activeState[1](it.index);
1592
+ },
1593
+ onClick: function() {
1594
+ choose(it);
1595
+ }
1596
+ },
1597
+ /* @__PURE__ */ React13.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React13.createElement("span", { className: "aura-combo__label" }, it.label)),
1598
+ sel ? /* @__PURE__ */ React13.createElement(Icon, { name: "check", className: "aura-combo__check" }) : null
1599
+ );
1600
+ };
1601
+ const runs = [];
1602
+ items2[0].forEach(function(it) {
1603
+ if (it.value === "" && it.disabled) return;
1604
+ const last = runs[runs.length - 1];
1605
+ if (last && last.group === it.group) last.items.push(it);
1606
+ else runs.push({ group: it.group, items: [it] });
1607
+ });
1608
+ const popup = open ? (0, import_react_dom2.createPortal)(
1609
+ /* @__PURE__ */ React13.createElement(
1610
+ "div",
1611
+ {
1612
+ ref: listRef,
1613
+ "data-density": density,
1614
+ className: "aura-combo__popover aura-select__popover",
1615
+ style: pos[0] ? {
1616
+ left: pos[0].left,
1617
+ minWidth: pos[0].width,
1618
+ top: pos[0].top,
1619
+ bottom: pos[0].bottom,
1620
+ maxHeight: pos[0].maxHeight
1621
+ } : { left: -9999, top: -9999 }
1622
+ },
1623
+ /* @__PURE__ */ React13.createElement(
1624
+ "div",
1625
+ {
1626
+ id: listId,
1627
+ role: "listbox",
1628
+ "aria-labelledby": labelledBy,
1629
+ "aria-label": labelledBy ? void 0 : rest["aria-label"],
1630
+ className: "aura-combo__list"
1631
+ },
1632
+ runs.map(function(r, n2) {
1633
+ if (r.group === null) return /* @__PURE__ */ React13.createElement(React13.Fragment, { key: "r" + n2 }, r.items.map(option));
1634
+ const gid = id + "-group-" + n2;
1635
+ 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));
1636
+ })
1637
+ )
1638
+ ),
1639
+ document.body
1640
+ ) : null;
1641
+ const userFocus = rest.onFocus, userBlur = rest.onBlur, userKey = rest.onKeyDown, userClick = rest.onClick;
1642
+ return field(
1643
+ /* @__PURE__ */ React13.createElement(
1644
+ "div",
1645
+ {
1646
+ className: cx("aura-input aura-select aura-select--custom", props.icon && "has-icon", open && "is-open"),
1647
+ "data-invalid": props.error ? "" : void 0
1648
+ },
1649
+ props.icon ? /* @__PURE__ */ React13.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null,
1650
+ h3(
1651
+ "select",
1652
+ Object.assign(extra, pick(rest, FORM_KEYS), {
1653
+ ref: selMerged,
1654
+ id: id + "-select",
1655
+ className: "aura-select__native",
1656
+ tabIndex: -1,
1657
+ "aria-hidden": true,
1658
+ required: props.required,
1659
+ onChange: function(e) {
1660
+ sync();
1661
+ if (props.onChange) props.onChange(e);
1662
+ },
1663
+ /* A browser focusing the <select> (the required bubble, a label click) hands focus to the button. */
1664
+ onFocus: function() {
1665
+ if (trigRef.current) trigRef.current.focus();
1666
+ },
1667
+ /* Only the blur the button reports (below): the <select>'s own, while it hands focus over, is not one. */
1668
+ onBlur: function(e) {
1669
+ if (!e.nativeEvent.isTrusted && userBlur) userBlur(e);
1670
+ }
1671
+ }),
1672
+ opts,
1673
+ props.children
1674
+ ),
1675
+ /* @__PURE__ */ React13.createElement(
1676
+ "button",
1677
+ {
1678
+ ...omit(rest, HANDLED_KEYS),
1679
+ ref: trigRef,
1680
+ type: "button",
1681
+ role: "combobox",
1682
+ form: id + "-no-form",
1683
+ className: "aura-input__control aura-select__trigger",
1684
+ disabled: props.disabled,
1685
+ tabIndex: rest.tabIndex,
1686
+ "aria-haspopup": "listbox",
1687
+ "aria-expanded": open,
1688
+ "aria-controls": open ? listId : void 0,
1689
+ "aria-activedescendant": open && active >= 0 ? optId(active) : void 0,
1690
+ id,
1691
+ "aria-label": rest["aria-label"],
1692
+ "aria-labelledby": rest["aria-labelledby"],
1693
+ "aria-required": props.required || void 0,
1694
+ "aria-invalid": props.error ? true : void 0,
1695
+ "aria-describedby": described,
1696
+ onFocus: function(e) {
1697
+ if (userFocus) userFocus(e);
1698
+ },
1699
+ onClick: function(e) {
1700
+ if (userClick) userClick(e);
1701
+ if (e.defaultPrevented) return;
1702
+ if (open) close(true);
1703
+ else openList();
1704
+ },
1705
+ onKeyDown: function(e) {
1706
+ if (userKey) userKey(e);
1707
+ if (!e.defaultPrevented) onKeyDown(e);
1708
+ },
1709
+ onBlur: function(e) {
1710
+ if (listRef.current && e.relatedTarget && listRef.current.contains(e.relatedTarget)) return;
1711
+ if (open) close(false);
1712
+ if (selRef.current) selRef.current.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
1713
+ }
1714
+ },
1715
+ /* @__PURE__ */ React13.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
1716
+ ),
1717
+ /* @__PURE__ */ React13.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }),
1718
+ popup
1719
+ )
1265
1720
  );
1266
1721
  });
1267
1722
 
@@ -1360,7 +1815,7 @@ window.Aura = (() => {
1360
1815
 
1361
1816
  // src/Combobox.tsx
1362
1817
  var React16 = __toESM(require_react(), 1);
1363
- var import_react_dom2 = __toESM(require_react_dom(), 1);
1818
+ var import_react_dom3 = __toESM(require_react_dom(), 1);
1364
1819
  function norm(s) {
1365
1820
  return String(s == null ? "" : s).normalize("NFC").toLocaleLowerCase("th");
1366
1821
  }
@@ -1380,6 +1835,7 @@ window.Aura = (() => {
1380
1835
  const multi = all.multiple === true;
1381
1836
  const mp = all;
1382
1837
  const t = useStrings();
1838
+ const density = useDensity();
1383
1839
  const auto = uid(), id = props.id || auto, listId = id + "-list";
1384
1840
  const options = (props.options || []).map(toOpt);
1385
1841
  const st = useMaybeControlled(
@@ -1542,8 +1998,8 @@ window.Aura = (() => {
1542
1998
  const optId = function(i) {
1543
1999
  return id + "-opt-" + i;
1544
2000
  };
1545
- const list = open && mounted && posState[0] ? (0, import_react_dom2.createPortal)(
1546
- /* @__PURE__ */ React16.createElement("div", { ref: listRef, className: "aura-combo__popover", style: posState[0] }, live.length ? /* @__PURE__ */ React16.createElement(
2001
+ const list = open && mounted && posState[0] ? (0, import_react_dom3.createPortal)(
2002
+ /* @__PURE__ */ React16.createElement("div", { ref: listRef, "data-density": density, className: "aura-combo__popover", style: posState[0] }, live.length ? /* @__PURE__ */ React16.createElement(
1547
2003
  "ul",
1548
2004
  {
1549
2005
  id: listId,
@@ -1703,7 +2159,7 @@ window.Aura = (() => {
1703
2159
 
1704
2160
  // src/DatePicker.tsx
1705
2161
  var React17 = __toESM(require_react(), 1);
1706
- var import_react_dom3 = __toESM(require_react_dom(), 1);
2162
+ var import_react_dom4 = __toESM(require_react_dom(), 1);
1707
2163
 
1708
2164
  // src/dates.ts
1709
2165
  var ERA = /^พ\.ศ\.\s?|\s?(BE|พ\.ศ\.)$/g;
@@ -2308,7 +2764,7 @@ window.Aura = (() => {
2308
2764
  pop.setOpen(false);
2309
2765
  if (inputRef.current) inputRef.current.focus();
2310
2766
  }
2311
- const cal = pop.open && pop.mounted && pop.pos ? (0, import_react_dom3.createPortal)(
2767
+ const cal = pop.open && pop.mounted && pop.pos ? (0, import_react_dom4.createPortal)(
2312
2768
  /* @__PURE__ */ React17.createElement(
2313
2769
  "div",
2314
2770
  {
@@ -2433,7 +2889,7 @@ window.Aura = (() => {
2433
2889
  draft[1](null);
2434
2890
  if (inputRef.current) inputRef.current.focus();
2435
2891
  }
2436
- function pick(iso) {
2892
+ function pick2(iso) {
2437
2893
  if (!draft[0]) {
2438
2894
  draft[1](iso);
2439
2895
  return;
@@ -2443,7 +2899,7 @@ window.Aura = (() => {
2443
2899
  st[1](a <= b ? { start: a, end: b } : { start: b, end: a });
2444
2900
  close();
2445
2901
  }
2446
- const cal = pop.open && pop.mounted && pop.pos ? (0, import_react_dom3.createPortal)(
2902
+ const cal = pop.open && pop.mounted && pop.pos ? (0, import_react_dom4.createPortal)(
2447
2903
  /* @__PURE__ */ React17.createElement(
2448
2904
  "div",
2449
2905
  {
@@ -2477,7 +2933,7 @@ window.Aura = (() => {
2477
2933
  start: draft[0] || v.start,
2478
2934
  end: draft[0] ? null : v.end,
2479
2935
  focus: draft[0] || v.start,
2480
- onSelect: pick
2936
+ onSelect: pick2
2481
2937
  }
2482
2938
  )
2483
2939
  ),
@@ -2547,7 +3003,7 @@ window.Aura = (() => {
2547
3003
 
2548
3004
  // src/Toaster.tsx
2549
3005
  var React19 = __toESM(require_react(), 1);
2550
- var import_react_dom4 = __toESM(require_react_dom(), 1);
3006
+ var import_react_dom5 = __toESM(require_react_dom(), 1);
2551
3007
  var MAX_VISIBLE = 3;
2552
3008
  var toastState = {
2553
3009
  list: [],
@@ -2708,7 +3164,7 @@ window.Aura = (() => {
2708
3164
  };
2709
3165
  }, []);
2710
3166
  if (!mounted) return null;
2711
- return (0, import_react_dom4.createPortal)(
3167
+ return (0, import_react_dom5.createPortal)(
2712
3168
  /* @__PURE__ */ React19.createElement(
2713
3169
  "div",
2714
3170
  {
@@ -2989,7 +3445,7 @@ window.Aura = (() => {
2989
3445
 
2990
3446
  // src/Command.tsx
2991
3447
  var React25 = __toESM(require_react(), 1);
2992
- var import_react_dom5 = __toESM(require_react_dom(), 1);
3448
+ var import_react_dom6 = __toESM(require_react_dom(), 1);
2993
3449
 
2994
3450
  // src/useModal.tsx
2995
3451
  var React24 = __toESM(require_react(), 1);
@@ -3193,7 +3649,7 @@ window.Aura = (() => {
3193
3649
  }
3194
3650
  if (!modal.ready) return null;
3195
3651
  let i = -1;
3196
- return (0, import_react_dom5.createPortal)(
3652
+ return (0, import_react_dom6.createPortal)(
3197
3653
  /* @__PURE__ */ React25.createElement(
3198
3654
  "div",
3199
3655
  {
@@ -3302,7 +3758,7 @@ window.Aura = (() => {
3302
3758
 
3303
3759
  // src/Tooltip.tsx
3304
3760
  var React26 = __toESM(require_react(), 1);
3305
- var import_react_dom6 = __toESM(require_react_dom(), 1);
3761
+ var import_react_dom7 = __toESM(require_react_dom(), 1);
3306
3762
  var Tooltip = React26.forwardRef(function Tooltip2(props, ref) {
3307
3763
  const id = uid(), st = React26.useState(false), open = props.open !== void 0 ? props.open : st[0], set = st[1];
3308
3764
  const anchor = React26.useRef(null), anchorMerged = useMergedRef(ref, anchor), tip = React26.useRef(null), timer = React26.useRef(void 0);
@@ -3400,7 +3856,7 @@ window.Aura = (() => {
3400
3856
  },
3401
3857
  React26.cloneElement(child, { "aria-describedby": open ? id : child.props["aria-describedby"] })
3402
3858
  );
3403
- return /* @__PURE__ */ React26.createElement(React26.Fragment, null, trigger, open && mounted ? (0, import_react_dom6.createPortal)(
3859
+ return /* @__PURE__ */ React26.createElement(React26.Fragment, null, trigger, open && mounted ? (0, import_react_dom7.createPortal)(
3404
3860
  /* @__PURE__ */ React26.createElement(
3405
3861
  "div",
3406
3862
  {
@@ -3422,7 +3878,7 @@ window.Aura = (() => {
3422
3878
 
3423
3879
  // src/Dialog.tsx
3424
3880
  var React27 = __toESM(require_react(), 1);
3425
- var import_react_dom7 = __toESM(require_react_dom(), 1);
3881
+ var import_react_dom8 = __toESM(require_react_dom(), 1);
3426
3882
  var Dialog = React27.forwardRef(function Dialog2(props, ref) {
3427
3883
  const t = useStrings();
3428
3884
  const density = useDensity();
@@ -3437,7 +3893,7 @@ window.Aura = (() => {
3437
3893
  footSelector: ".aura-dialog__foot"
3438
3894
  });
3439
3895
  if (!modal.ready) return null;
3440
- return (0, import_react_dom7.createPortal)(
3896
+ return (0, import_react_dom8.createPortal)(
3441
3897
  /* @__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
3898
  "div",
3443
3899
  {
@@ -3472,7 +3928,7 @@ window.Aura = (() => {
3472
3928
  });
3473
3929
  if (!modal.ready) return null;
3474
3930
  const side = props.side === "left" ? "left" : "right";
3475
- return (0, import_react_dom7.createPortal)(
3931
+ return (0, import_react_dom8.createPortal)(
3476
3932
  /* @__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
3933
  "div",
3478
3934
  {
@@ -3496,7 +3952,7 @@ window.Aura = (() => {
3496
3952
 
3497
3953
  // src/DataTable.tsx
3498
3954
  var React28 = __toESM(require_react(), 1);
3499
- var import_react_dom8 = __toESM(require_react_dom(), 1);
3955
+ var import_react_dom9 = __toESM(require_react_dom(), 1);
3500
3956
  var BP = { sm: 640, md: 768, lg: 1024, xl: 1280 };
3501
3957
  var DEFAULT_COLUMNS = [
3502
3958
  { key: "id", label: "ID", width: 96, mono: true },
@@ -3653,7 +4109,7 @@ window.Aura = (() => {
3653
4109
  function hideFull() {
3654
4110
  setTip(null);
3655
4111
  }
3656
- const tipEl = tip && typeof document !== "undefined" ? (0, import_react_dom8.createPortal)(
4112
+ const tipEl = tip && typeof document !== "undefined" ? (0, import_react_dom9.createPortal)(
3657
4113
  /* @__PURE__ */ React28.createElement(
3658
4114
  "div",
3659
4115
  {
@@ -4903,7 +5359,7 @@ window.Aura = (() => {
4903
5359
 
4904
5360
  // src/SideNav.tsx
4905
5361
  var React31 = __toESM(require_react(), 1);
4906
- var import_react_dom9 = __toESM(require_react_dom(), 1);
5362
+ var import_react_dom10 = __toESM(require_react_dom(), 1);
4907
5363
  function contains(it, id) {
4908
5364
  return !!id && !!it.children && it.children.some(function(c) {
4909
5365
  return c.id === id || contains(c, id);
@@ -5096,7 +5552,7 @@ window.Aura = (() => {
5096
5552
  }
5097
5553
  }
5098
5554
  )) : null,
5099
- tip && typeof document !== "undefined" ? (0, import_react_dom9.createPortal)(
5555
+ tip && typeof document !== "undefined" ? (0, import_react_dom10.createPortal)(
5100
5556
  /* @__PURE__ */ React31.createElement(
5101
5557
  "div",
5102
5558
  {
@@ -5540,7 +5996,7 @@ window.Aura = (() => {
5540
5996
 
5541
5997
  // src/TimePicker.tsx
5542
5998
  var React45 = __toESM(require_react(), 1);
5543
- var import_react_dom10 = __toESM(require_react_dom(), 1);
5999
+ var import_react_dom11 = __toESM(require_react_dom(), 1);
5544
6000
 
5545
6001
  // src/text.ts
5546
6002
  function formatBytes(n2) {
@@ -5756,7 +6212,7 @@ window.Aura = (() => {
5756
6212
  }
5757
6213
  }
5758
6214
  const error = errState[0] || props.error;
5759
- const list = open && mounted && pos[0] ? (0, import_react_dom10.createPortal)(
6215
+ const list = open && mounted && pos[0] ? (0, import_react_dom11.createPortal)(
5760
6216
  /* @__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
6217
  const dis = blocked(s), sel = s === value;
5762
6218
  return /* @__PURE__ */ React45.createElement(
@@ -6747,7 +7203,7 @@ window.Aura = (() => {
6747
7203
 
6748
7204
  // src/Popover.tsx
6749
7205
  var React55 = __toESM(require_react(), 1);
6750
- var import_react_dom11 = __toESM(require_react_dom(), 1);
7206
+ var import_react_dom12 = __toESM(require_react_dom(), 1);
6751
7207
  function position(anchor, pop, placement) {
6752
7208
  const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.scrollHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
6753
7209
  let side = (placement || "bottom-start").split("-")[0], align = (placement || "bottom-start").split("-")[1] || "start";
@@ -6825,7 +7281,7 @@ window.Aura = (() => {
6825
7281
  [open, mounted]
6826
7282
  );
6827
7283
  const child = React55.Children.only(props.trigger);
6828
- const panel = open && mounted ? (0, import_react_dom11.createPortal)(
7284
+ const panel = open && mounted ? (0, import_react_dom12.createPortal)(
6829
7285
  /* @__PURE__ */ React55.createElement(
6830
7286
  "div",
6831
7287
  {
@@ -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); }
@@ -417,9 +417,12 @@ export type SelectOption = string | {
417
417
  label: string;
418
418
  disabled?: boolean | undefined;
419
419
  };
420
+ /** 5.3: opens AURA's own list (light and dark alike) over a real `<select>`, which keeps `name`, `required`, the ref,
421
+ * `onChange`, react-hook-form `register` and form posts. `multiple` or `size > 1` keeps the native list box. */
420
422
  export interface SelectProps extends FieldProps, Omit<React$1.SelectHTMLAttributes<HTMLSelectElement>, "required"> {
421
- options: SelectOption[];
422
- /** A disabled first option shown in fg-tertiary until something is chosen. */
423
+ /** The choices; or pass `<option>` / `<optgroup>` children instead (5.3: optional). */
424
+ options?: SelectOption[] | undefined;
425
+ /** Shown in fg-tertiary on the closed field until something is chosen; not listed as a choice. */
423
426
  placeholder?: string | undefined;
424
427
  icon?: IconInput | undefined;
425
428
  }
@@ -1217,6 +1220,7 @@ export interface FieldComponentProps extends Omit<FieldPropsPublic, "label"> {
1217
1220
  export declare const Field: React$1.ForwardRefExoticComponent<FieldComponentProps & React$1.RefAttributes<HTMLDivElement>>;
1218
1221
  export declare const TextField: React$1.ForwardRefExoticComponent<TextFieldProps & React$1.RefAttributes<HTMLInputElement>>;
1219
1222
  export declare const Textarea: React$1.ForwardRefExoticComponent<TextareaProps & React$1.RefAttributes<HTMLTextAreaElement>>;
1223
+ /** A select field: a button that opens an AURA list (5.3), over a real <select> that keeps forms, refs and events. */
1220
1224
  export declare const Select: React$1.ForwardRefExoticComponent<SelectProps & React$1.RefAttributes<HTMLSelectElement>>;
1221
1225
  export declare const RadioGroup: React$1.ForwardRefExoticComponent<RadioGroupProps & React$1.RefAttributes<HTMLFieldSetElement>>;
1222
1226
  export declare const Switch: React$1.ForwardRefExoticComponent<SwitchProps & React$1.RefAttributes<HTMLButtonElement>>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-tokens",
3
- "version": "5.2.0",
3
+ "version": "5.3.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",
package/tokens.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "5.2.0",
2
+ "version": "5.3.0",
3
3
  "meta": {
4
4
  "name": "AURA Design System",
5
5
  "tier": "3-tier",