@jirawatpyk/aura-tokens 5.1.1 → 5.2.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 +14 -14
- package/aura-tailwind.css +16 -3
- package/aura-tailwind.prefixed.css +1 -1
- package/aura.css +1 -1
- package/components/aura.bundle.js +144 -35
- package/components/aura.components.css +7 -2
- package/components/index.d.ts +5 -0
- package/eslint-plugin-aura.d.ts +11 -0
- package/eslint-plugin-aura.js +112 -21
- package/package.json +22 -5
- package/scripts/a11y-check.js +3 -0
- package/scripts/lint-tokens.js +31 -4
- package/tailwind.tokens.d.cts +16 -0
- package/tokens.json +1 -1
package/README.md
CHANGED
|
@@ -4,20 +4,20 @@ Enterprise Standard, Human Creative. Tokens, Tailwind preset, Figma variables a
|
|
|
4
4
|
|
|
5
5
|
## What's inside
|
|
6
6
|
|
|
7
|
-
| File | What
|
|
8
|
-
| -------------------------------------------- |
|
|
9
|
-
| `tokens.json` | **Source of truth.** 3-tier: primitive → semantic (light/dark) → component, plus spacing, radius, shadow (per theme), size, motion, opacity, z-index, typography.
|
|
10
|
-
| `aura.css` | Every token as a CSS variable. Light on `:root` / `[data-theme="light"]`, dark on `.dark` / `[data-theme="dark"]`. **Generated.**
|
|
11
|
-
| `aura-fonts.css` | The Google Fonts `@import` (Fraunces, Inter, JetBrains Mono, Noto Sans Thai). Kept separate so a network that blocks Google can't break `aura.css`. **Generated.**
|
|
12
|
-
| `aura-fonts.local.css` + `fonts/` | The same four families self-hosted: `@font-face` rules over woff2 files in the package (4.16), for a `font-src 'self'` CSP. `npm run check:fonts` loads them under that CSP. **Generated.**
|
|
13
|
-
| `tailwind.config.ts` + `tailwind.tokens.cjs` | Tailwind v3 theme wired to the variables (`bg-bg-surface`, `text-fg-danger`, `rounded-aura-xl`, `shadow-aura-overlay` …). The `.cjs` is **generated**.
|
|
14
|
-
| `aura-tailwind.css` | The same class names for Tailwind v4 (`@theme inline`, CSS-first). **Generated**; exported as `@jirawatpyk/aura-tokens/tailwind.css`.
|
|
15
|
-
| `figma-variables.csv` | 204 variables: all colours in both modes (aliases kept), spacing, radius and sizes. **Generated.**
|
|
16
|
-
| `components/` | `aura.bundle.js` (sets `window.Aura`), `aura.components.css`, `index.d.ts`.
|
|
17
|
-
| `eslint-plugin-aura.js` | `aura/no-hardcoded-color`: hex, rgb
|
|
18
|
-
| `scripts/build-tokens.js` | Rebuilds the generated files from `tokens.json`.
|
|
19
|
-
| `scripts/a11y-check.js` | Checks
|
|
20
|
-
| `scripts/lint-tokens.js` | Fails on hard-coded colours (a file that must hold them says `aura-lint: allow-colours` with a reason).
|
|
7
|
+
| File | What |
|
|
8
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
9
|
+
| `tokens.json` | **Source of truth.** 3-tier: primitive → semantic (light/dark) → component, plus spacing, radius, shadow (per theme), size, motion, opacity, z-index, typography. |
|
|
10
|
+
| `aura.css` | Every token as a CSS variable. Light on `:root` / `[data-theme="light"]`, dark on `.dark` / `[data-theme="dark"]`. **Generated.** |
|
|
11
|
+
| `aura-fonts.css` | The Google Fonts `@import` (Fraunces, Inter, JetBrains Mono, Noto Sans Thai). Kept separate so a network that blocks Google can't break `aura.css`. **Generated.** |
|
|
12
|
+
| `aura-fonts.local.css` + `fonts/` | The same four families self-hosted: `@font-face` rules over woff2 files in the package (4.16), for a `font-src 'self'` CSP. `npm run check:fonts` loads them under that CSP. **Generated.** |
|
|
13
|
+
| `tailwind.config.ts` + `tailwind.tokens.cjs` | Tailwind v3 theme wired to the variables (`bg-bg-surface`, `text-fg-danger`, `rounded-aura-xl`, `shadow-aura-overlay` …). The `.cjs` is **generated**. |
|
|
14
|
+
| `aura-tailwind.css` | The same class names for Tailwind v4 (`@theme inline`, CSS-first). **Generated**; exported as `@jirawatpyk/aura-tokens/tailwind.css`. |
|
|
15
|
+
| `figma-variables.csv` | 204 variables: all colours in both modes (aliases kept), spacing, radius and sizes. **Generated.** |
|
|
16
|
+
| `components/` | `aura.bundle.js` (sets `window.Aura`), `aura.components.css`, `index.d.ts`. |
|
|
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.js` | Rebuilds the generated files from `tokens.json`. |
|
|
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
|
+
| `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
|
|
|
22
22
|
## ใช้งาน (5 นาที)
|
|
23
23
|
|
package/aura-tailwind.css
CHANGED
|
@@ -1,12 +1,25 @@
|
|
|
1
|
-
/* AURA v5.
|
|
1
|
+
/* AURA v5.2.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,
|
|
5
5
|
* ease-aura-ease, duration-aura-fast, z-aura-dialog, font-display … (the v3 preset's names). */
|
|
6
6
|
@import "./aura.css";
|
|
7
7
|
|
|
8
|
-
/* dark: follows .dark and data-theme="dark", the same switches aura.css uses
|
|
9
|
-
|
|
8
|
+
/* dark: follows .dark and data-theme="dark", the same switches aura.css uses; data-theme="system" when the OS is dark
|
|
9
|
+
* (5.2); and not inside a light island in a dark page (Surface sets data-theme="light"), where the tokens are light. */
|
|
10
|
+
@custom-variant dark {
|
|
11
|
+
&:where(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *):not(:where(.dark [data-theme="light"], .dark [data-theme="light"] *, [data-theme="dark"] [data-theme="light"], [data-theme="dark"] [data-theme="light"] *)) {
|
|
12
|
+
@slot;
|
|
13
|
+
}
|
|
14
|
+
&:where([data-theme="light"] [data-theme="dark"], [data-theme="light"] [data-theme="dark"] *):not(:where([data-theme="dark"] [data-theme="light"], [data-theme="dark"] [data-theme="light"] *)) {
|
|
15
|
+
@slot;
|
|
16
|
+
}
|
|
17
|
+
@media (prefers-color-scheme: dark) {
|
|
18
|
+
&:where([data-theme="system"], [data-theme="system"] *):not(:where([data-theme="system"] [data-theme="light"], [data-theme="system"] [data-theme="light"] *)) {
|
|
19
|
+
@slot;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
10
23
|
|
|
11
24
|
@theme inline {
|
|
12
25
|
--color-aura-violet-50: var(--aura-violet-50);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* AURA v5.
|
|
1
|
+
/* AURA v5.2.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.
|
|
1
|
+
/* AURA v5.2.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"] {
|
|
@@ -378,6 +378,12 @@ window.Aura = (() => {
|
|
|
378
378
|
page: function(p, total) {
|
|
379
379
|
return "Page " + p + " of " + total;
|
|
380
380
|
},
|
|
381
|
+
rangeOpen: function(a, b) {
|
|
382
|
+
return a + "\u2013" + b + " of many";
|
|
383
|
+
},
|
|
384
|
+
pageOpen: function(p) {
|
|
385
|
+
return "Page " + p;
|
|
386
|
+
},
|
|
381
387
|
prevPage: "Previous page",
|
|
382
388
|
nextPage: "Next page",
|
|
383
389
|
totals: "Totals",
|
|
@@ -391,6 +397,7 @@ window.Aura = (() => {
|
|
|
391
397
|
timeOutOfRange: function(a, b) {
|
|
392
398
|
return "Choose a time between " + a + " and " + b;
|
|
393
399
|
},
|
|
400
|
+
timeUnavailable: "That time isn't available. Choose another.",
|
|
394
401
|
dropFiles: "Drag files here or",
|
|
395
402
|
browse: "Choose files",
|
|
396
403
|
browseOne: "Choose a file",
|
|
@@ -492,6 +499,12 @@ window.Aura = (() => {
|
|
|
492
499
|
page: function(p, total) {
|
|
493
500
|
return "\u0E2B\u0E19\u0E49\u0E32 " + p + " / " + total;
|
|
494
501
|
},
|
|
502
|
+
rangeOpen: function(a, b) {
|
|
503
|
+
return a + "\u2013" + b + " \u0E08\u0E32\u0E01\u0E2B\u0E25\u0E32\u0E22\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
|
|
504
|
+
},
|
|
505
|
+
pageOpen: function(p) {
|
|
506
|
+
return "\u0E2B\u0E19\u0E49\u0E32 " + p;
|
|
507
|
+
},
|
|
495
508
|
prevPage: "\u0E2B\u0E19\u0E49\u0E32\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32",
|
|
496
509
|
nextPage: "\u0E2B\u0E19\u0E49\u0E32\u0E16\u0E31\u0E14\u0E44\u0E1B",
|
|
497
510
|
totals: "\u0E23\u0E27\u0E21",
|
|
@@ -505,6 +518,7 @@ window.Aura = (() => {
|
|
|
505
518
|
timeOutOfRange: function(a, b) {
|
|
506
519
|
return "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07 " + a + "\u2013" + b + " \u0E19.";
|
|
507
520
|
},
|
|
521
|
+
timeUnavailable: "\u0E40\u0E27\u0E25\u0E32\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E27\u0E48\u0E32\u0E07 \u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E2D\u0E37\u0E48\u0E19",
|
|
508
522
|
dropFiles: "\u0E25\u0E32\u0E01\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E27\u0E32\u0E07 \u0E2B\u0E23\u0E37\u0E2D",
|
|
509
523
|
browse: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
|
|
510
524
|
browseOne: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
|
|
@@ -606,6 +620,12 @@ window.Aura = (() => {
|
|
|
606
620
|
page: function(p, total) {
|
|
607
621
|
return "Sida " + p + " av " + total;
|
|
608
622
|
},
|
|
623
|
+
rangeOpen: function(a, b) {
|
|
624
|
+
return a + "\u2013" + b + " av m\xE5nga";
|
|
625
|
+
},
|
|
626
|
+
pageOpen: function(p) {
|
|
627
|
+
return "Sida " + p;
|
|
628
|
+
},
|
|
609
629
|
prevPage: "F\xF6reg\xE5ende sida",
|
|
610
630
|
nextPage: "N\xE4sta sida",
|
|
611
631
|
totals: "Summa",
|
|
@@ -619,6 +639,7 @@ window.Aura = (() => {
|
|
|
619
639
|
timeOutOfRange: function(a, b) {
|
|
620
640
|
return "V\xE4lj en tid mellan " + a + " och " + b;
|
|
621
641
|
},
|
|
642
|
+
timeUnavailable: "Den tiden \xE4r inte ledig. V\xE4lj en annan.",
|
|
622
643
|
dropFiles: "Dra filer hit eller",
|
|
623
644
|
browse: "V\xE4lj filer",
|
|
624
645
|
browseOne: "V\xE4lj en fil",
|
|
@@ -1690,20 +1711,29 @@ window.Aura = (() => {
|
|
|
1690
1711
|
return (n2 < 10 ? "0" : "") + n2;
|
|
1691
1712
|
};
|
|
1692
1713
|
function toISO(d) {
|
|
1693
|
-
|
|
1714
|
+
if (!d) return null;
|
|
1715
|
+
const y = String(d.getFullYear());
|
|
1716
|
+
return ("000" + y).slice(-Math.max(4, y.length)) + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
|
|
1717
|
+
}
|
|
1718
|
+
function makeDate(y, monthIndex2, day) {
|
|
1719
|
+
const d = new Date(2e3, 0, 1);
|
|
1720
|
+
d.setFullYear(y, monthIndex2, day);
|
|
1721
|
+
return d;
|
|
1694
1722
|
}
|
|
1695
1723
|
function fromISO(s) {
|
|
1696
1724
|
if (!s) return null;
|
|
1697
1725
|
const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
|
|
1698
|
-
|
|
1726
|
+
if (!m) return null;
|
|
1727
|
+
const d = makeDate(+m[1], +m[2] - 1, +m[3]);
|
|
1728
|
+
return d.getMonth() === +m[2] - 1 && d.getDate() === +m[3] ? d : null;
|
|
1699
1729
|
}
|
|
1700
1730
|
function addDays(d, n2) {
|
|
1701
|
-
return
|
|
1731
|
+
return makeDate(d.getFullYear(), d.getMonth(), d.getDate() + n2);
|
|
1702
1732
|
}
|
|
1703
1733
|
function addMonths(d, n2) {
|
|
1704
|
-
const t =
|
|
1705
|
-
const last =
|
|
1706
|
-
return
|
|
1734
|
+
const t = makeDate(d.getFullYear(), d.getMonth() + n2, 1);
|
|
1735
|
+
const last = makeDate(t.getFullYear(), t.getMonth() + 1, 0).getDate();
|
|
1736
|
+
return makeDate(t.getFullYear(), t.getMonth(), Math.min(d.getDate(), last));
|
|
1707
1737
|
}
|
|
1708
1738
|
function same(a, b) {
|
|
1709
1739
|
return !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
|
|
@@ -1811,7 +1841,15 @@ window.Aura = (() => {
|
|
|
1811
1841
|
return MONTHS[k] != null ? MONTHS[k] : -1;
|
|
1812
1842
|
}
|
|
1813
1843
|
function parseDate(text) {
|
|
1814
|
-
const
|
|
1844
|
+
const raw = String(text || "").trim();
|
|
1845
|
+
const gregorian = /ค\.ศ\.|\d\s*(AD|CE)\b|^\s*(AD|CE)\s/i.test(raw);
|
|
1846
|
+
let s = raw.replace(/พ\.ศ\.|ค\.ศ\./g, " ").replace(/(\d)\s*(BE|AD|CE)\b/gi, "$1 ").replace(/^\s*(BE|AD|CE)\s+/i, " ").replace(/\s+/g, " ").trim();
|
|
1847
|
+
const eight = /^(\d{2})(\d{2})(\d{4})$/.exec(s);
|
|
1848
|
+
if (eight) {
|
|
1849
|
+
const a = +eight[3] >= 1e3 ? parseDate(eight[1] + "/" + eight[2] + "/" + eight[3] + (gregorian ? " \u0E04.\u0E28." : "")) : null;
|
|
1850
|
+
if (a) return a;
|
|
1851
|
+
s = s.slice(0, 4) + "-" + s.slice(4, 6) + "-" + s.slice(6);
|
|
1852
|
+
}
|
|
1815
1853
|
let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(s), y, mo, d;
|
|
1816
1854
|
if (m) {
|
|
1817
1855
|
y = +m[1];
|
|
@@ -1821,13 +1859,13 @@ window.Aura = (() => {
|
|
|
1821
1859
|
d = +m[1];
|
|
1822
1860
|
mo = +m[2];
|
|
1823
1861
|
y = +m[3];
|
|
1824
|
-
} else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s
|
|
1862
|
+
} else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s)) && monthIndex(m[2]) >= 0) {
|
|
1825
1863
|
d = +m[1];
|
|
1826
1864
|
mo = monthIndex(m[2]) + 1;
|
|
1827
1865
|
y = +m[3];
|
|
1828
1866
|
} else return null;
|
|
1829
|
-
if (y >= 2400) y -= 543;
|
|
1830
|
-
const dt =
|
|
1867
|
+
if (y >= 2400 && !gregorian) y -= 543;
|
|
1868
|
+
const dt = makeDate(y, mo - 1, d);
|
|
1831
1869
|
return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
|
|
1832
1870
|
}
|
|
1833
1871
|
function todayIn(timeZone) {
|
|
@@ -2118,16 +2156,16 @@ window.Aura = (() => {
|
|
|
2118
2156
|
const pos = React17.useState(null), popRef = React17.useRef(null), mounted = useMounted();
|
|
2119
2157
|
function place() {
|
|
2120
2158
|
if (!boxRef.current) return;
|
|
2121
|
-
const r = boxRef.current.getBoundingClientRect(), H = 380;
|
|
2122
|
-
const up = window.innerHeight - r.bottom < H && r.top > H;
|
|
2123
|
-
const left = Math.max(8, Math.min(r.left, window.innerWidth -
|
|
2159
|
+
const el = popRef.current, r = boxRef.current.getBoundingClientRect(), W = el ? el.offsetWidth : 320, H = el ? el.offsetHeight : 380;
|
|
2160
|
+
const up = window.innerHeight - r.bottom < H + 12 && r.top > H + 12;
|
|
2161
|
+
const left = Math.max(8, Math.min(r.left, window.innerWidth - W - 8));
|
|
2124
2162
|
pos[1](up ? { left, bottom: window.innerHeight - r.top + 4 } : { left, top: r.bottom + 4 });
|
|
2125
2163
|
}
|
|
2126
2164
|
useIsoLayoutEffect(
|
|
2127
2165
|
function() {
|
|
2128
2166
|
if (open) place();
|
|
2129
2167
|
},
|
|
2130
|
-
[open]
|
|
2168
|
+
[open, !!pos[0]]
|
|
2131
2169
|
);
|
|
2132
2170
|
React17.useEffect(
|
|
2133
2171
|
function() {
|
|
@@ -3696,11 +3734,12 @@ window.Aura = (() => {
|
|
|
3696
3734
|
if (lv && boxWidth[0] == null) hideTerms += " + " + w + "px * var(--aura-h" + lv + "-on, 1)";
|
|
3697
3735
|
else fixedSum += w;
|
|
3698
3736
|
});
|
|
3699
|
-
let pinOffsets = {}, acc =
|
|
3737
|
+
let pinOffsets = {}, acc = "0px";
|
|
3700
3738
|
vis.forEach(function(c) {
|
|
3701
3739
|
if (isPinned(c)) {
|
|
3702
3740
|
pinOffsets[c.key] = acc;
|
|
3703
|
-
|
|
3741
|
+
const lv = hideLevel(c);
|
|
3742
|
+
acc += " + " + widthOf(c) + "px" + (lv && boxWidth[0] == null ? " * var(--aura-h" + lv + "-on, 1)" : "");
|
|
3704
3743
|
}
|
|
3705
3744
|
});
|
|
3706
3745
|
const selW = selectable ? " + var(--aura-table-select-width)" : "";
|
|
@@ -3715,7 +3754,7 @@ window.Aura = (() => {
|
|
|
3715
3754
|
s["--aura-cell-flex"] = !hasFlex && i === vis.length - 1 ? "1 0 auto" : "none";
|
|
3716
3755
|
s["--aura-cell-w"] = w + "px";
|
|
3717
3756
|
}
|
|
3718
|
-
if (isPinned(c)) s["--aura-cell-left"] = "calc(var(--aura-space-6)" + selW + " + " + pinOffsets[c.key] + "
|
|
3757
|
+
if (isPinned(c)) s["--aura-cell-left"] = "calc(var(--aura-space-6)" + selW + " + " + pinOffsets[c.key] + ")";
|
|
3719
3758
|
return s;
|
|
3720
3759
|
}
|
|
3721
3760
|
const sortCol = sort && sort.key ? byKey[sort.key] : void 0;
|
|
@@ -3761,6 +3800,20 @@ window.Aura = (() => {
|
|
|
3761
3800
|
const pageRows = pageSize && !manual ? view.slice(first, first + pageSize) : view;
|
|
3762
3801
|
const canSortAny = !busy && (manual ? total > 1 : rows.length > 1);
|
|
3763
3802
|
const shownTotal = manual && props.totalRows == null ? first + rows.length : total;
|
|
3803
|
+
const prevCount = React28.useRef(pageCount);
|
|
3804
|
+
React28.useEffect(
|
|
3805
|
+
function() {
|
|
3806
|
+
if (busy) return;
|
|
3807
|
+
const before = prevCount.current;
|
|
3808
|
+
prevCount.current = pageCount;
|
|
3809
|
+
const asked = pageState[0] || 1;
|
|
3810
|
+
if (asked === page || pageCount >= before || asked > before) return;
|
|
3811
|
+
if (props.getPageHref && !props.onPageChange && !props.onStateChange) return;
|
|
3812
|
+
pageState[1](page);
|
|
3813
|
+
emit(sort, page);
|
|
3814
|
+
},
|
|
3815
|
+
[pageState[0], page, busy, pageCount]
|
|
3816
|
+
);
|
|
3764
3817
|
const linkPaging = !!props.getPageHref && !props.onPageChange && !oneCallback;
|
|
3765
3818
|
const prevLink = React28.useRef(null), nextLink = React28.useRef(null), jumpLink = React28.useRef(null);
|
|
3766
3819
|
const jumpState = React28.useState(null);
|
|
@@ -3829,9 +3882,10 @@ window.Aura = (() => {
|
|
|
3829
3882
|
const visibleRows = Math.max(1, Math.floor(bodyH / ROW_H));
|
|
3830
3883
|
const nCols = vis.length + (selectable ? 1 : 0);
|
|
3831
3884
|
const nRows = loading ? 0 : pageRows.length;
|
|
3885
|
+
const hasTotal = !!(props.footer && rows.length && !loading), T = nRows + 1, lastR = nRows + (hasTotal ? 1 : 0);
|
|
3832
3886
|
const active = activeState[0];
|
|
3833
|
-
const ar = Math.min(active.r,
|
|
3834
|
-
const activeRendered = ar === 0 || ar - 1 >= start && ar - 1 < end;
|
|
3887
|
+
const ar = Math.min(active.r, lastR), ac = Math.min(active.c, nCols - 1);
|
|
3888
|
+
const activeRendered = ar === 0 || hasTotal && ar === T || ar - 1 >= start && ar - 1 < end;
|
|
3835
3889
|
function colAt(ci) {
|
|
3836
3890
|
return selectable ? ci === 0 ? null : vis[ci - 1] : vis[ci];
|
|
3837
3891
|
}
|
|
@@ -3862,12 +3916,33 @@ window.Aura = (() => {
|
|
|
3862
3916
|
}
|
|
3863
3917
|
});
|
|
3864
3918
|
function focusCell(r, c) {
|
|
3865
|
-
r = Math.max(0, Math.min(r,
|
|
3919
|
+
r = Math.max(0, Math.min(r, lastR));
|
|
3866
3920
|
c = Math.max(0, Math.min(c, nCols - 1));
|
|
3867
3921
|
if (stacked && r === 0 && nRows > 0 && !(selectable && c === 0)) r = 1;
|
|
3922
|
+
if (gridRef.current && r === T && hasTotal) {
|
|
3923
|
+
const shown2 = function(cc) {
|
|
3924
|
+
const e = gridRef.current.querySelector('[data-rc="' + r + ":" + cc + '"]');
|
|
3925
|
+
return !!e && e.getClientRects().length > 0;
|
|
3926
|
+
};
|
|
3927
|
+
if (!shown2(c))
|
|
3928
|
+
for (let d = 1; d < nCols; d++) {
|
|
3929
|
+
if (c + d < nCols && shown2(c + d)) {
|
|
3930
|
+
c = c + d;
|
|
3931
|
+
break;
|
|
3932
|
+
}
|
|
3933
|
+
if (c - d >= 0 && shown2(c - d)) {
|
|
3934
|
+
c = c - d;
|
|
3935
|
+
break;
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3868
3939
|
activeState[1]({ r, c });
|
|
3869
3940
|
pending.current = { r, c };
|
|
3870
|
-
if (virtual && r
|
|
3941
|
+
if (virtual && hasTotal && r === T && !props.stickyFooter) {
|
|
3942
|
+
const sc = scrollRef.current;
|
|
3943
|
+
sc.scrollTop = sc.scrollHeight;
|
|
3944
|
+
setScrollTop(sc.scrollTop);
|
|
3945
|
+
} else if (virtual && r > 0 && r <= nRows) {
|
|
3871
3946
|
const sc = scrollRef.current, top = (r - 1) * ROW_H;
|
|
3872
3947
|
if (top < sc.scrollTop) sc.scrollTop = top;
|
|
3873
3948
|
else if (top + ROW_H > sc.scrollTop + bodyH) sc.scrollTop = top + ROW_H - bodyH;
|
|
@@ -4110,7 +4185,7 @@ window.Aura = (() => {
|
|
|
4110
4185
|
return;
|
|
4111
4186
|
}
|
|
4112
4187
|
const p = rc.split(":"), r = +p[0], c = +p[1], k = e.key, col = colAt(c);
|
|
4113
|
-
const row = r > 0 ? pageRows[r - 1] : null;
|
|
4188
|
+
const row = r > 0 && r <= nRows ? pageRows[r - 1] : null, isTotal = hasTotal && r === T;
|
|
4114
4189
|
let handled = true;
|
|
4115
4190
|
if (r === 0 && col && e.altKey && (k === "ArrowLeft" || k === "ArrowRight") && canResize(col))
|
|
4116
4191
|
setW(col, widthOf(col) + (k === "ArrowLeft" ? -1 : 1) * (e.shiftKey ? 48 : 16), true);
|
|
@@ -4121,21 +4196,23 @@ window.Aura = (() => {
|
|
|
4121
4196
|
else if (k === "ArrowRight") focusCell(r, c + 1);
|
|
4122
4197
|
else if (k === "ArrowLeft") focusCell(r, c - 1);
|
|
4123
4198
|
else if (k === "ArrowDown") {
|
|
4124
|
-
if (r <
|
|
4199
|
+
if (r < lastR) focusCell(r + 1, c);
|
|
4125
4200
|
else if (pageSize && page < pageCount && goPage(page + 1, { r: 1, c })) activeState[1]({ r: 1, c });
|
|
4126
4201
|
} else if (k === "ArrowUp") {
|
|
4127
4202
|
if (r > 1 || r === 1 && !(pageSize && page > 1)) focusCell(r - 1, c);
|
|
4128
4203
|
else if (r === 1 && goPage(page - 1, { r: pageSize, c })) activeState[1]({ r: pageSize, c });
|
|
4129
4204
|
} else if (k === "Home") focusCell(e.ctrlKey ? 1 : r, 0);
|
|
4130
|
-
else if (k === "End") focusCell(e.ctrlKey ?
|
|
4205
|
+
else if (k === "End") focusCell(e.ctrlKey ? lastR : r, nCols - 1);
|
|
4131
4206
|
else if (k === "PageDown") {
|
|
4132
4207
|
if (pageSize) {
|
|
4133
4208
|
if (goPage(page + 1, { r: 1, c })) activeState[1]({ r: 1, c });
|
|
4134
|
-
} else focusCell(Math.min(
|
|
4209
|
+
} else focusCell(Math.min(lastR, r + visibleRows), c);
|
|
4135
4210
|
} else if (k === "PageUp") {
|
|
4136
4211
|
if (pageSize) {
|
|
4137
4212
|
if (goPage(page - 1, { r: 1, c })) activeState[1]({ r: 1, c });
|
|
4138
4213
|
} else focusCell(Math.max(1, r - visibleRows), c);
|
|
4214
|
+
} else if (isTotal && (k === " " || k === "Enter" || k === "F2")) {
|
|
4215
|
+
handled = false;
|
|
4139
4216
|
} else if (k === "F2" && r > 0) {
|
|
4140
4217
|
const inner = target.querySelector("button, a[href], input, select, textarea");
|
|
4141
4218
|
if (inner) inner.focus();
|
|
@@ -4506,9 +4583,10 @@ window.Aura = (() => {
|
|
|
4506
4583
|
}
|
|
4507
4584
|
);
|
|
4508
4585
|
}
|
|
4586
|
+
const openTotal = manual && props.totalRows == null && !!pageSize && rows.length >= pageSize;
|
|
4509
4587
|
if (pageSize && (rows.length || busy || page > 1)) {
|
|
4510
4588
|
const from = rows.length ? first + 1 : 0, to = manual ? first + rows.length : Math.min(first + pageSize, view.length);
|
|
4511
|
-
foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", { "aria-live": "polite" }, busy ? t.loading : t.range(from, to, shownTotal)), /* @__PURE__ */ React28.createElement("span", { className: "aura-table__pager" }, /* @__PURE__ */ React28.createElement("span", null, t.page(page, pageCount)), pagerButton(-1), pagerButton(1)));
|
|
4589
|
+
foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", { "aria-live": "polite" }, busy ? t.loading : openTotal ? t.rangeOpen(from, to) : t.range(from, to, shownTotal)), /* @__PURE__ */ React28.createElement("span", { className: "aura-table__pager" }, /* @__PURE__ */ React28.createElement("span", null, openTotal ? t.pageOpen(page) : t.page(page, pageCount)), pagerButton(-1), pagerButton(1)));
|
|
4512
4590
|
} else if (height && rows.length && !loading) {
|
|
4513
4591
|
foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React28.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React28.createElement("span", null));
|
|
4514
4592
|
}
|
|
@@ -4522,15 +4600,32 @@ window.Aura = (() => {
|
|
|
4522
4600
|
style: rowStyle
|
|
4523
4601
|
},
|
|
4524
4602
|
/* @__PURE__ */ React28.createElement("span", { className: "aura-table__gutter", "aria-hidden": true }),
|
|
4525
|
-
selectable ? /* @__PURE__ */ React28.createElement(
|
|
4603
|
+
selectable ? /* @__PURE__ */ React28.createElement(
|
|
4604
|
+
"span",
|
|
4605
|
+
{
|
|
4606
|
+
role: "gridcell",
|
|
4607
|
+
"aria-colindex": 1,
|
|
4608
|
+
className: cx("aura-table__sel", nPinned && "is-pinned"),
|
|
4609
|
+
tabIndex: tabFor(T, 0),
|
|
4610
|
+
"data-rc": T + ":0",
|
|
4611
|
+
onFocus: function() {
|
|
4612
|
+
activeState[1]({ r: T, c: 0 });
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
) : null,
|
|
4526
4616
|
vis.map(function(c, j) {
|
|
4527
|
-
const pin = isPinned(c);
|
|
4617
|
+
const pin = isPinned(c), ci = j + (selectable ? 1 : 0);
|
|
4528
4618
|
return /* @__PURE__ */ React28.createElement(
|
|
4529
4619
|
"span",
|
|
4530
4620
|
{
|
|
4531
4621
|
key: c.key,
|
|
4532
4622
|
role: "gridcell",
|
|
4533
4623
|
"aria-colindex": j + (selectable ? 2 : 1),
|
|
4624
|
+
tabIndex: tabFor(T, ci),
|
|
4625
|
+
"data-rc": T + ":" + ci,
|
|
4626
|
+
onFocus: function() {
|
|
4627
|
+
if (activeState[0].r !== T || activeState[0].c !== ci) activeState[1]({ r: T, c: ci });
|
|
4628
|
+
},
|
|
4534
4629
|
className: cx(
|
|
4535
4630
|
"aura-table__td",
|
|
4536
4631
|
c.mono && "aura-table__mono",
|
|
@@ -4556,7 +4651,7 @@ window.Aura = (() => {
|
|
|
4556
4651
|
)
|
|
4557
4652
|
) : null;
|
|
4558
4653
|
const scrollStyle = {
|
|
4559
|
-
scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "
|
|
4654
|
+
scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + ")",
|
|
4560
4655
|
scrollPaddingTop: ROW_H + "px",
|
|
4561
4656
|
scrollPaddingBottom: stickyTotal ? ROW_H + "px" : void 0
|
|
4562
4657
|
};
|
|
@@ -5468,6 +5563,7 @@ window.Aura = (() => {
|
|
|
5468
5563
|
const m = /^(\d{1,2})(?:[:.](\d{2}))?$/.exec(s) || /^(\d{1,2})(\d{2})$/.exec(s);
|
|
5469
5564
|
if (!m) return null;
|
|
5470
5565
|
let hh = +m[1], mm = m[2] ? +m[2] : 0;
|
|
5566
|
+
if ((am || pm) && (hh < 1 || hh > 12)) return null;
|
|
5471
5567
|
if (pm && hh < 12) hh += 12;
|
|
5472
5568
|
if (am && hh === 12) hh = 0;
|
|
5473
5569
|
if (hh > 23 || mm > 59) return null;
|
|
@@ -5582,7 +5678,8 @@ window.Aura = (() => {
|
|
|
5582
5678
|
return true;
|
|
5583
5679
|
}
|
|
5584
5680
|
if (blocked(v)) {
|
|
5585
|
-
|
|
5681
|
+
const n2 = toMin(v);
|
|
5682
|
+
errState[1](n2 != null && n2 >= lo && n2 <= hi ? t.timeUnavailable : t.timeOutOfRange(fromMin(lo), fromMin(hi)));
|
|
5586
5683
|
return false;
|
|
5587
5684
|
}
|
|
5588
5685
|
errState[1](null);
|
|
@@ -5841,6 +5938,7 @@ window.Aura = (() => {
|
|
|
5841
5938
|
[items2, props.name]
|
|
5842
5939
|
);
|
|
5843
5940
|
function thumb(it) {
|
|
5941
|
+
if (!it.file && it.url && /^image\//.test(it.type || "")) return it.url;
|
|
5844
5942
|
if (!it.file || !/^image\//.test(it.type || "") || typeof URL === "undefined" || !URL.createObjectURL) return null;
|
|
5845
5943
|
if (!urls.current[it.id]) urls.current[it.id] = URL.createObjectURL(it.file);
|
|
5846
5944
|
return urls.current[it.id];
|
|
@@ -5946,7 +6044,7 @@ window.Aura = (() => {
|
|
|
5946
6044
|
),
|
|
5947
6045
|
items2.length ? /* @__PURE__ */ React46.createElement("ul", { className: "aura-upload__list", "aria-live": "polite" }, items2.map(function(it) {
|
|
5948
6046
|
const src = thumb(it);
|
|
5949
|
-
return /* @__PURE__ */ React46.createElement("li", { key: it.id, className: cx("aura-upload__item", it.error && "is-error") }, src ? /* @__PURE__ */ React46.createElement("img", { className: "aura-upload__thumb", src, alt: "" }) : /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__thumb is-icon", "aria-hidden": true }, /* @__PURE__ */ React46.createElement(Icon, { name: /^image\//.test(it.type || "") ? "image" : "file", size: "md" })), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__meta" }, /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__name" }, it.name), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__sub" }, it.error ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-alert", size: 12 }), it.error) : it.status === "uploading" ? t.uploading + (it.progress != null ? " " + Math.round(it.progress) + "%" : "") : it.status === "done" ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-check", size: 12 }), formatBytes(it.size)) : formatBytes(it.size)), it.status === "uploading" ? /* @__PURE__ */ React46.createElement(
|
|
6047
|
+
return /* @__PURE__ */ React46.createElement("li", { key: it.id, className: cx("aura-upload__item", it.error && "is-error") }, src ? /* @__PURE__ */ React46.createElement("img", { className: "aura-upload__thumb", src, alt: "" }) : /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__thumb is-icon", "aria-hidden": true }, /* @__PURE__ */ React46.createElement(Icon, { name: /^image\//.test(it.type || "") ? "image" : "file", size: "md" })), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__meta" }, it.url && !it.error ? /* @__PURE__ */ React46.createElement("a", { className: "aura-upload__name", href: it.url, target: "_blank", rel: "noreferrer" }, it.name) : /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__name" }, it.name), /* @__PURE__ */ React46.createElement("span", { className: "aura-upload__sub" }, it.error ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-alert", size: 12 }), it.error) : it.status === "uploading" ? t.uploading + (it.progress != null ? " " + Math.round(it.progress) + "%" : "") : it.status === "done" ? /* @__PURE__ */ React46.createElement(React46.Fragment, null, /* @__PURE__ */ React46.createElement(Icon, { name: "circle-check", size: 12 }), formatBytes(it.size)) : formatBytes(it.size)), it.status === "uploading" ? /* @__PURE__ */ React46.createElement(
|
|
5950
6048
|
"span",
|
|
5951
6049
|
{
|
|
5952
6050
|
className: "aura-upload__bar",
|
|
@@ -6651,13 +6749,20 @@ window.Aura = (() => {
|
|
|
6651
6749
|
var React55 = __toESM(require_react(), 1);
|
|
6652
6750
|
var import_react_dom11 = __toESM(require_react_dom(), 1);
|
|
6653
6751
|
function position(anchor, pop, placement) {
|
|
6654
|
-
const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.
|
|
6752
|
+
const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.scrollHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
|
|
6655
6753
|
let side = (placement || "bottom-start").split("-")[0], align = (placement || "bottom-start").split("-")[1] || "start";
|
|
6656
6754
|
if (side === "bottom" && r.bottom + gap + ph > vh - 8 && r.top - gap - ph > 8) side = "top";
|
|
6657
6755
|
else if (side === "top" && r.top - gap - ph < 8 && r.bottom + gap + ph < vh - 8) side = "bottom";
|
|
6658
|
-
const
|
|
6756
|
+
const below2 = vh - r.bottom - gap - 8, above = r.top - gap - 8;
|
|
6757
|
+
let maxHeight;
|
|
6758
|
+
if (ph > below2 && ph > above) {
|
|
6759
|
+
side = above > below2 ? "top" : "bottom";
|
|
6760
|
+
maxHeight = Math.max(120, side === "top" ? above : below2);
|
|
6761
|
+
}
|
|
6762
|
+
const h9 = maxHeight ? Math.min(ph, maxHeight) : ph;
|
|
6763
|
+
const top = side === "top" ? r.top - gap - h9 : r.bottom + gap;
|
|
6659
6764
|
const left = align === "end" ? r.right - pw : align === "center" ? r.left + r.width / 2 - pw / 2 : r.left;
|
|
6660
|
-
return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side };
|
|
6765
|
+
return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side, maxHeight };
|
|
6661
6766
|
}
|
|
6662
6767
|
var Popover = React55.forwardRef(function Popover2(props, ref) {
|
|
6663
6768
|
const t = useStrings();
|
|
@@ -6737,7 +6842,11 @@ window.Aura = (() => {
|
|
|
6737
6842
|
},
|
|
6738
6843
|
className: cx("aura-popover", pos[0] && "is-" + pos[0].side, props.className),
|
|
6739
6844
|
style: Object.assign(
|
|
6740
|
-
{
|
|
6845
|
+
{
|
|
6846
|
+
top: pos[0] ? pos[0].top : -9999,
|
|
6847
|
+
left: pos[0] ? pos[0].left : -9999,
|
|
6848
|
+
maxHeight: pos[0] && pos[0].maxHeight ? pos[0].maxHeight : void 0
|
|
6849
|
+
},
|
|
6741
6850
|
props.width ? { width: props.width } : null
|
|
6742
6851
|
),
|
|
6743
6852
|
onKeyDown: function(e) {
|
|
@@ -679,6 +679,8 @@ a.aura-tab { text-decoration: none; }
|
|
|
679
679
|
.aura-avatar__status {
|
|
680
680
|
position: absolute; right: -1px; bottom: -1px; width: 30%; height: 30%; min-width: 8px; min-height: 8px;
|
|
681
681
|
border-radius: var(--aura-radius-full); background: var(--aura-accent-dot); box-shadow: 0 0 0 2px var(--aura-bg-surface);
|
|
682
|
+
/* 5.2: the lime fill is 1.2:1 on the white ring; the edge carries the 3:1 (WCAG 1.4.11). */
|
|
683
|
+
border: 1.5px solid var(--aura-fg-positive); box-sizing: border-box;
|
|
682
684
|
}
|
|
683
685
|
|
|
684
686
|
/* ---------- Combobox ---------- */
|
|
@@ -921,6 +923,8 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
|
|
|
921
923
|
.aura-upload__thumb.is-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--aura-fg-secondary); }
|
|
922
924
|
.aura-upload__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
|
|
923
925
|
.aura-upload__name { font-size: 13px; font-weight: 500; color: var(--aura-fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
926
|
+
a.aura-upload__name { color: var(--aura-fg-accent); text-decoration: underline; text-underline-offset: 2px; border-radius: var(--aura-radius-xs); }
|
|
927
|
+
a.aura-upload__name:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; }
|
|
924
928
|
.aura-upload__sub { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--aura-fg-secondary); }
|
|
925
929
|
.aura-upload__item.is-error .aura-upload__sub { color: var(--aura-fg-danger); }
|
|
926
930
|
.aura-upload__bar { display: block; height: 4px; border-radius: var(--aura-radius-full); background: var(--aura-progress-track); box-shadow: inset 0 0 0 1px var(--aura-progress-track-edge); overflow: hidden; }
|
|
@@ -1042,8 +1046,9 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
|
|
|
1042
1046
|
position: fixed; z-index: var(--aura-z-menu); box-sizing: border-box; width: max-content; min-width: 200px; max-width: min(360px, calc(100vw - 16px));
|
|
1043
1047
|
background: var(--aura-bg-surface); color: var(--aura-fg-primary); border: 1px solid var(--aura-border-default); border-radius: var(--aura-radius-lg);
|
|
1044
1048
|
box-shadow: var(--aura-shadow-overlay); font-family: var(--font-sans); font-size: 14px; outline: none; animation: aura-fade-in var(--aura-duration-fast) var(--aura-ease);
|
|
1049
|
+
overflow-y: auto; overscroll-behavior: contain;
|
|
1045
1050
|
}
|
|
1046
|
-
.aura-popover__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); padding: var(--aura-space-2) var(--aura-space-2) 0 var(--aura-space-4); }
|
|
1051
|
+
.aura-popover__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); padding: var(--aura-space-2) var(--aura-space-2) 0 var(--aura-space-4); position: sticky; top: 0; z-index: 1; background: var(--aura-bg-surface); } /* 5.2: stays while a tall popover scrolls */
|
|
1047
1052
|
.aura-popover__title { margin: 0; font-size: 14px; font-weight: 600; }
|
|
1048
1053
|
.aura-popover__body { padding: var(--aura-space-4); }
|
|
1049
1054
|
.aura-popover__head + .aura-popover__body { padding-top: var(--aura-space-2); }
|
|
@@ -1143,7 +1148,7 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
1143
1148
|
.aura-segmented.is-full .aura-segmented__option { flex: 1 1 0; min-width: 0; }
|
|
1144
1149
|
.aura-segmented__option.is-icon { padding: 0; width: 36px; }
|
|
1145
1150
|
.aura-segmented__option:hover:not(:disabled):not(.is-selected) { color: var(--aura-fg-primary); }
|
|
1146
|
-
.aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: var(--aura-shadow-enterprise); }
|
|
1151
|
+
.aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: inset 0 0 0 1px var(--aura-border-control), var(--aura-shadow-enterprise); } /* 5.2: the edge marks the choice at 3:1; the fill alone was 1.04:1 on the track */
|
|
1147
1152
|
.aura-segmented__option:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; }
|
|
1148
1153
|
.aura-segmented__option:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
|
|
1149
1154
|
.aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
|
package/components/index.d.ts
CHANGED
|
@@ -1381,6 +1381,9 @@ export interface AuraStrings {
|
|
|
1381
1381
|
loadingRows: string;
|
|
1382
1382
|
range: (a: number | string, b: number | string, total: number) => string;
|
|
1383
1383
|
page: (p: number, total: number) => string;
|
|
1384
|
+
/** Server paging without totalRows (5.2): the range and page when the total isn't known. */
|
|
1385
|
+
rangeOpen: (a: number | string, b: number | string) => string;
|
|
1386
|
+
pageOpen: (p: number) => string;
|
|
1384
1387
|
prevPage: string;
|
|
1385
1388
|
nextPage: string;
|
|
1386
1389
|
rowCount: (c: number) => string;
|
|
@@ -1390,6 +1393,8 @@ export interface AuraStrings {
|
|
|
1390
1393
|
timePlaceholder: string;
|
|
1391
1394
|
timeInvalid: string;
|
|
1392
1395
|
timeOutOfRange: (a: number | string, b: number | string) => string;
|
|
1396
|
+
/** A time inside the range that isTimeDisabled refuses (5.2). */
|
|
1397
|
+
timeUnavailable: string;
|
|
1393
1398
|
dropFiles: string;
|
|
1394
1399
|
browse: string;
|
|
1395
1400
|
browseOne: string;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Types for eslint-plugin-aura.js (5.2): import aura from '@jirawatpyk/aura-tokens/eslint-plugin';
|
|
2
|
+
* export default [aura.configs.recommended]; */
|
|
3
|
+
declare const plugin: {
|
|
4
|
+
meta: { name: string; version: string };
|
|
5
|
+
rules: Record<string, unknown>;
|
|
6
|
+
configs: {
|
|
7
|
+
recommended: { plugins: Record<string, unknown>; rules: Record<string, 'error' | 'warn' | 'off'> };
|
|
8
|
+
'recommended-legacy': { plugins: string[]; rules: Record<string, 'error' | 'warn' | 'off'> };
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
export = plugin;
|
package/eslint-plugin-aura.js
CHANGED
|
@@ -1,34 +1,125 @@
|
|
|
1
|
-
// eslint-plugin-aura.js — AURA
|
|
2
|
-
// Flags hard-coded colours in JS/TS: hex (#rgb, #rgba, #rrggbb, #rrggbbaa), rgb
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
// eslint-plugin-aura.js — AURA 5.2
|
|
2
|
+
// Flags hard-coded colours in JS/TS: hex (#rgb, #rgba, #rrggbb, #rrggbbaa), colour functions (rgb/hsl/hwb/lab/lch/
|
|
3
|
+
// oklab/oklch/color-mix), CSS named colours in a style value, Tailwind palette classes (bg-red-500, text-white) and
|
|
4
|
+
// arbitrary colour classes (bg-[#18181b]). Use semantic tokens instead.
|
|
5
|
+
//
|
|
6
|
+
// Flat config (ESLint 9): import aura from '@jirawatpyk/aura-tokens/eslint-plugin';
|
|
7
|
+
// export default [aura.configs.recommended];
|
|
8
|
+
// Legacy .eslintrc: plugins: ['aura'] needs the package installed as eslint-plugin-aura, so use the
|
|
9
|
+
// flat config, or aura.configs['recommended-legacy'] with a local plugin loader.
|
|
10
|
+
const PALETTE =
|
|
11
|
+
'slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose';
|
|
12
|
+
const UTIL =
|
|
13
|
+
'bg|text|border(?:-[trblxy])?|ring|ring-offset|outline|fill|stroke|from|to|via|shadow|decoration|divide|placeholder|caret|accent';
|
|
14
|
+
/* "#fff" but not "#add-user" or "#section_2": a hex is not followed by a word character or a hyphen. */
|
|
15
|
+
const HEX = /#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})(?![\w-])/i;
|
|
16
|
+
/* A colour function with a literal colour in it: a hex, or three numbers (rgb(1 2 3), oklch(.7 .1 200)), or an open
|
|
17
|
+
* call a template literal continues (`rgb(${r}, …)`). Token-based ones pass: rgb(var(--brand-rgb) / .5),
|
|
18
|
+
* color-mix(in srgb, var(--aura-accent) 20%, transparent). var() and url() are removed before any check. */
|
|
19
|
+
const FN_CALL = /\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color-mix)\(([^()]*)(\)|$)/gi;
|
|
20
|
+
function fnHit(v) {
|
|
21
|
+
FN_CALL.lastIndex = 0;
|
|
22
|
+
let m;
|
|
23
|
+
while ((m = FN_CALL.exec(v))) {
|
|
24
|
+
const args = m[1];
|
|
25
|
+
if (!m[2] || /#[0-9a-f]{3}/i.test(args) || (args.match(/-?\d*\.?\d+/g) || []).length >= 3) return m[0];
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const stripRefs = (v) => v.replace(/var\([^()]*(?:\([^()]*\)[^()]*)*\)/g, ' ').replace(/url\([^)]*\)/g, ' ');
|
|
30
|
+
const TW_ARB = new RegExp('(?:^|[\\s:"\'`])(?:' + UTIL + ')-\\[(?:#|rgb|hsl|oklch|color)', 'i');
|
|
31
|
+
const TW_PAL = new RegExp(
|
|
32
|
+
'(?:^|[\\s:"\'`!])(?:' + UTIL + ')-(?:(?:' + PALETTE + ')-\\d{2,3}|white|black)(?:\\/\\d+)?(?![\\w-])',
|
|
33
|
+
);
|
|
34
|
+
const NAMED =
|
|
35
|
+
'white|black|red|green|blue|yellow|orange|purple|pink|gray|grey|silver|maroon|navy|teal|olive|lime|aqua|fuchsia|brown|gold|indigo|violet|crimson|coral|salmon|tomato|khaki|beige|ivory|lavender|turquoise|tan|plum|orchid';
|
|
36
|
+
/* A whole value that is a named colour ('white'), or one after a colour property ("color: red", "background:#…"). */
|
|
37
|
+
const NAMED_WHOLE = new RegExp('^\\s*(?:' + NAMED + ')\\s*$', 'i');
|
|
38
|
+
/* Lower-case CSS only ("color: white"), so UI text like "Color: Red" isn't a declaration; the name stands alone
|
|
39
|
+
* (not --aura-white or white-space). */
|
|
40
|
+
const NAMED_DECL = new RegExp(
|
|
41
|
+
'(?:^|[;{\\s])(?:color|background(?:-color)?|border(?:-[a-z]+)?-color|border|fill|stroke|outline(?:-color)?)\\s*:\\s*[^;]*(?<![\\w-])(?:' +
|
|
42
|
+
NAMED +
|
|
43
|
+
')(?![\\w-])',
|
|
44
|
+
);
|
|
45
|
+
/* Properties whose values are colours when written as an object key (style={{ color: 'white' }}). */
|
|
46
|
+
const COLOUR_KEY =
|
|
47
|
+
/^(?:color|background|backgroundColor|borderColor|border(?:Top|Right|Bottom|Left)Color|fill|stroke|outlineColor|caretColor|accentColor|textDecorationColor)$/;
|
|
48
|
+
/* JSX attributes that hold ids and URLs, never colours ("#add-user", "#fff" as an anchor). */
|
|
49
|
+
const NOT_COLOUR_ATTR =
|
|
50
|
+
/^(?:href|to|id|htmlFor|for|src|action|formAction|xlinkHref|xlink:href|aria-[a-z]+|data-[\w-]+|name|key)$/;
|
|
7
51
|
|
|
8
|
-
function
|
|
9
|
-
if (typeof
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
52
|
+
function hitIn(raw, colourKey) {
|
|
53
|
+
if (typeof raw !== 'string') return null;
|
|
54
|
+
const value = stripRefs(raw);
|
|
55
|
+
const m = TW_ARB.exec(value) || TW_PAL.exec(value) || HEX.exec(value) || NAMED_DECL.exec(value);
|
|
56
|
+
if (m) return m[0].trim();
|
|
57
|
+
const f = fnHit(value);
|
|
58
|
+
if (f) return f;
|
|
59
|
+
if (colourKey && NAMED_WHOLE.test(value)) return value.trim();
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function skip(node) {
|
|
63
|
+
let p = node.parent;
|
|
64
|
+
if (!p) return false;
|
|
65
|
+
/* href={'#add'}, href={x ? '#a' : '#b'}, href={`#fff`}: look through the expression to the attribute. */
|
|
66
|
+
while (
|
|
67
|
+
p.parent &&
|
|
68
|
+
(p.type === 'JSXExpressionContainer' ||
|
|
69
|
+
p.type === 'ConditionalExpression' ||
|
|
70
|
+
p.type === 'LogicalExpression' ||
|
|
71
|
+
p.type === 'TemplateLiteral' ||
|
|
72
|
+
p.type === 'BinaryExpression')
|
|
73
|
+
)
|
|
74
|
+
p = p.parent;
|
|
75
|
+
if (p.type === 'JSXAttribute' && p.name) {
|
|
76
|
+
const n = p.name.type === 'JSXNamespacedName' ? p.name.namespace.name + ':' + p.name.name.name : p.name.name;
|
|
77
|
+
if (NOT_COLOUR_ATTR.test(n)) return true;
|
|
16
78
|
}
|
|
79
|
+
if (p.type === 'ImportDeclaration' || p.type === 'ExportNamedDeclaration' || p.type === 'ExportAllDeclaration')
|
|
80
|
+
return true;
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function colourKeyOf(node) {
|
|
84
|
+
const p = node.parent;
|
|
85
|
+
if (p && p.type === 'Property' && p.value === node && p.key) return COLOUR_KEY.test(p.key.name || p.key.value || '');
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
function report(context, node, hit) {
|
|
89
|
+
context.report({
|
|
90
|
+
node,
|
|
91
|
+
message: `Hard-coded colour "${hit}". Use a semantic token (bg-bg-surface, text-fg-primary, var(--aura-…)) instead.`,
|
|
92
|
+
});
|
|
17
93
|
}
|
|
18
94
|
|
|
19
|
-
|
|
95
|
+
const plugin = {
|
|
96
|
+
meta: { name: 'eslint-plugin-aura', version: '5.2.0' },
|
|
20
97
|
rules: {
|
|
21
98
|
'no-hardcoded-color': {
|
|
22
|
-
meta: {
|
|
99
|
+
meta: {
|
|
100
|
+
type: 'problem',
|
|
101
|
+
docs: { description: 'ห้ามใช้สี hardcode ให้ใช้ token — use AURA semantic tokens, never raw colours' },
|
|
102
|
+
schema: [],
|
|
103
|
+
},
|
|
23
104
|
create(context) {
|
|
24
105
|
return {
|
|
25
|
-
Literal(node) {
|
|
26
|
-
|
|
106
|
+
Literal(node) {
|
|
107
|
+
if (skip(node)) return;
|
|
108
|
+
const hit = hitIn(node.value, colourKeyOf(node));
|
|
109
|
+
if (hit) report(context, node, hit);
|
|
110
|
+
},
|
|
111
|
+
TemplateElement(node) {
|
|
112
|
+
if (skip(node)) return;
|
|
113
|
+
const hit = hitIn(node.value && node.value.raw, false);
|
|
114
|
+
if (hit) report(context, node, hit);
|
|
115
|
+
},
|
|
27
116
|
};
|
|
28
117
|
},
|
|
29
118
|
},
|
|
30
119
|
},
|
|
31
|
-
configs: {
|
|
32
|
-
recommended: { plugins: ['aura'], rules: { 'aura/no-hardcoded-color': 'error' } },
|
|
33
|
-
},
|
|
120
|
+
configs: {},
|
|
34
121
|
};
|
|
122
|
+
/* Flat config (ESLint 9, the default): the plugin object itself, not a name. */
|
|
123
|
+
plugin.configs.recommended = { plugins: { aura: plugin }, rules: { 'aura/no-hardcoded-color': 'error' } };
|
|
124
|
+
plugin.configs['recommended-legacy'] = { plugins: ['aura'], rules: { 'aura/no-hardcoded-color': 'error' } };
|
|
125
|
+
module.exports = plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jirawatpyk/aura-tokens",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.2.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",
|
|
@@ -12,10 +12,12 @@
|
|
|
12
12
|
"fonts",
|
|
13
13
|
"tailwind.config.ts",
|
|
14
14
|
"tailwind.tokens.cjs",
|
|
15
|
+
"tailwind.tokens.d.cts",
|
|
15
16
|
"aura-tailwind.css",
|
|
16
17
|
"aura-tailwind.prefixed.css",
|
|
17
18
|
"figma-variables.csv",
|
|
18
19
|
"eslint-plugin-aura.js",
|
|
20
|
+
"eslint-plugin-aura.d.ts",
|
|
19
21
|
"components/aura.bundle.js",
|
|
20
22
|
"components/aura.components.css",
|
|
21
23
|
"components/index.d.ts",
|
|
@@ -26,17 +28,31 @@
|
|
|
26
28
|
"LICENSE"
|
|
27
29
|
],
|
|
28
30
|
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./tailwind.tokens.d.cts",
|
|
33
|
+
"default": "./tailwind.tokens.cjs"
|
|
34
|
+
},
|
|
29
35
|
"./aura.css": "./aura.css",
|
|
30
36
|
"./aura-fonts.css": "./aura-fonts.css",
|
|
31
37
|
"./aura-fonts.local.css": "./aura-fonts.local.css",
|
|
32
38
|
"./fonts/*": "./fonts/*",
|
|
33
39
|
"./tokens.json": "./tokens.json",
|
|
34
|
-
"./tailwind":
|
|
40
|
+
"./tailwind": {
|
|
41
|
+
"types": "./tailwind.tokens.d.cts",
|
|
42
|
+
"default": "./tailwind.tokens.cjs"
|
|
43
|
+
},
|
|
35
44
|
"./tailwind.css": "./aura-tailwind.css",
|
|
36
45
|
"./tailwind.prefixed.css": "./aura-tailwind.prefixed.css",
|
|
37
|
-
"./components.js":
|
|
46
|
+
"./components.js": {
|
|
47
|
+
"types": "./components/index.d.ts",
|
|
48
|
+
"default": "./components/aura.bundle.js"
|
|
49
|
+
},
|
|
38
50
|
"./components.css": "./components/aura.components.css",
|
|
39
|
-
"./eslint-plugin":
|
|
51
|
+
"./eslint-plugin": {
|
|
52
|
+
"types": "./eslint-plugin-aura.d.ts",
|
|
53
|
+
"default": "./eslint-plugin-aura.js"
|
|
54
|
+
},
|
|
55
|
+
"./package.json": "./package.json"
|
|
40
56
|
},
|
|
41
57
|
"scripts": {
|
|
42
58
|
"build": "node scripts/build-tokens.js",
|
|
@@ -45,7 +61,8 @@
|
|
|
45
61
|
"lint:tokens": "node scripts/lint-tokens.js ../react/src && node scripts/lint-tokens.js ../react/styles",
|
|
46
62
|
"check:generated": "node scripts/build-tokens.js && 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",
|
|
47
63
|
"prepack": "npm run build",
|
|
48
|
-
"check:fonts": "node scripts/fonts-check.mjs"
|
|
64
|
+
"check:fonts": "node scripts/fonts-check.mjs",
|
|
65
|
+
"check:eslint": "node scripts/eslint-check.mjs"
|
|
49
66
|
},
|
|
50
67
|
"keywords": [
|
|
51
68
|
"design-system",
|
package/scripts/a11y-check.js
CHANGED
|
@@ -69,6 +69,9 @@ for (const bg of ['bg-surface', 'bg-canvas']) {
|
|
|
69
69
|
for (let i = 1; i < 8; i++) pairs.push([`chart-${i}`, `chart-${i + 1}`, NEIGHBOUR]);
|
|
70
70
|
/* Status fills that sit side by side in a column must also differ in lightness, not only hue (5.1: warning). */
|
|
71
71
|
pairs.push(['status-warning-bg', 'status-ready-bg', NEIGHBOUR], ['status-warning-bg', 'status-blocked-bg', NEIGHBOUR]);
|
|
72
|
+
/* 5.2: state marks that were fill-only — the Avatar online dot's edge on its ring, the selected segment's edge on the
|
|
73
|
+
* track and on its own fill. */
|
|
74
|
+
pairs.push(['fg-positive', 'bg-surface', UI], ['border-control', 'bg-surface-hover', UI], ['border-control', 'bg-surface', UI]);
|
|
72
75
|
/* Progress / upload tracks: the edge shows the empty part at 3:1; the bar colours also clear 3:1 on the track fill. */
|
|
73
76
|
for (const bg of ['bg-surface', 'bg-canvas']) pairs.push(['progress-track-edge', bg, UI]);
|
|
74
77
|
for (const fg of ['fg-accent', 'fg-positive', 'fg-danger', 'fg-secondary']) pairs.push([fg, 'progress-track', UI]);
|
package/scripts/lint-tokens.js
CHANGED
|
@@ -7,8 +7,30 @@ const fs = require('fs');
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
const dir = path.resolve(process.argv[2] || path.join(process.cwd(), 'src'));
|
|
9
9
|
const ALLOW = /(aura\.css|aura\.components\.css|aura\.bundle\.js|tokens\.json|tailwind\.tokens\.cjs|figma-variables\.csv)$/;
|
|
10
|
-
const EXT = /\.(tsx?|jsx?|css|scss|mdx)$/;
|
|
11
|
-
|
|
10
|
+
const EXT = /\.(tsx?|jsx?|mjs|cjs|mts|cts|css|scss|mdx|vue|svelte|astro|html)$/;
|
|
11
|
+
/* 5.2: the same patterns as eslint-plugin-aura — a hex not followed by a word character or hyphen ("#add-user" is an
|
|
12
|
+
* anchor), every colour function, Tailwind palette and arbitrary colour classes. */
|
|
13
|
+
const PALETTE = 'slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose';
|
|
14
|
+
const UTIL = 'bg|text|border(?:-[trblxy])?|ring|ring-offset|outline|fill|stroke|from|to|via|shadow|decoration|divide|placeholder|caret|accent';
|
|
15
|
+
const COLOUR = new RegExp(
|
|
16
|
+
'#(?:[0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3,4})(?![\\w-])' +
|
|
17
|
+
'|(?:^|[\\s:"\'`!])(?:' + UTIL + ')-\\[(?:#|rgb|hsl|oklch|color)' +
|
|
18
|
+
'|(?:^|[\\s:"\'`!])(?:' + UTIL + ')-(?:(?:' + PALETTE + ')-\\d{2,3}|white|black)(?:\\/\\d+)?(?![\\w-])',
|
|
19
|
+
'gi',
|
|
20
|
+
);
|
|
21
|
+
/* CSS only: a named colour as a colour property's value ("color: white"). System colours (CanvasText, Highlight) for
|
|
22
|
+
* forced-colors mode are allowed. */
|
|
23
|
+
const NAMED = 'white|black|red|green|blue|yellow|orange|purple|pink|gray|grey|silver|maroon|navy|teal|olive|lime|aqua|fuchsia|brown|gold|indigo|violet';
|
|
24
|
+
const CSS_NAMED = new RegExp('(?:^|[;{\\s])(?:color|background(?:-color)?|border(?:-[a-z]+)?-color|fill|stroke|outline-color)\\s*:\\s*[^;]*(?<![\\w-])(?:' + NAMED + ')(?![\\w-])');
|
|
25
|
+
/* Colour functions count only with a literal colour inside (a hex or three numbers), as in eslint-plugin-aura. */
|
|
26
|
+
const FN_CALL = /\b(?:rgba?|hsla?|hwb|lab|lch|oklab|oklch|color-mix)\(([^()]*)(\)|$)/gi;
|
|
27
|
+
function fnHit(v) {
|
|
28
|
+
FN_CALL.lastIndex = 0;
|
|
29
|
+
let m;
|
|
30
|
+
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
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const stripRefs = (v) => v.replace(/var\([^()]*(?:\([^()]*\)[^()]*)*\)/g, ' ').replace(/url\([^)]*\)/g, ' ');
|
|
12
34
|
const MOTION_PROP = /\b(?:transition|animation)(?:-duration|-timing-function|-delay)?\s*:/i;
|
|
13
35
|
const MOTION_RAW = /(?:^|[\s,:(])(?:\d*\.?\d+m?s)\b|cubic-bezier\(|\bease(?:-in-out|-in|-out)?\b/gi;
|
|
14
36
|
const violations = [];
|
|
@@ -23,8 +45,13 @@ function scan(d) {
|
|
|
23
45
|
/* A file that must hold raw colours (the theme generator) says so, with a reason, in its first lines. */
|
|
24
46
|
if (/aura-lint:\s*allow-colours/.test(text.slice(0, 600))) continue;
|
|
25
47
|
text.split('\n').forEach((line, i) => {
|
|
26
|
-
const
|
|
27
|
-
|
|
48
|
+
const plain = stripRefs(line);
|
|
49
|
+
const m = plain.match(COLOUR) || (fnHit(plain) ? [fnHit(plain)] : null);
|
|
50
|
+
if (m) violations.push(`${path.relative(process.cwd(), full)}:${i + 1} ${m.map((s) => s.trim().replace(/^[:"'`!]/, '')).join(', ')}`);
|
|
51
|
+
if (/\.s?css$/.test(f) && !/^\s*--/.test(line)) {
|
|
52
|
+
const n = CSS_NAMED.exec(stripRefs(line.replace(/\/\*.*?\*\//g, '')));
|
|
53
|
+
if (n) violations.push(`${path.relative(process.cwd(), full)}:${i + 1} named colour: ${n[0].trim()}`);
|
|
54
|
+
}
|
|
28
55
|
if (/\.s?css$/.test(f) && MOTION_PROP.test(line)) {
|
|
29
56
|
const raw = line.replace(/var\([^)]*\)/g, '').match(MOTION_RAW);
|
|
30
57
|
if (raw) violations.push(`${path.relative(process.cwd(), full)}:${i + 1} motion ${raw.map((s) => s.trim()).join(', ')} — use --aura-duration-* / --aura-ease`);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* Types for tailwind.tokens.cjs (5.2): the token maps a Tailwind v3 config spreads into theme.extend.
|
|
2
|
+
* import aura from '@jirawatpyk/aura-tokens/tailwind'; // or require()
|
|
3
|
+
* theme: { extend: { colors: aura.colors, spacing: aura.spacing, … } } */
|
|
4
|
+
type AuraColorMap = { [key: string]: string | AuraColorMap };
|
|
5
|
+
declare const aura: {
|
|
6
|
+
colors: AuraColorMap;
|
|
7
|
+
spacing: Record<string, string>;
|
|
8
|
+
borderRadius: Record<string, string>;
|
|
9
|
+
boxShadow: Record<string, string>;
|
|
10
|
+
transitionTimingFunction: Record<string, string>;
|
|
11
|
+
transitionDuration: Record<string, string>;
|
|
12
|
+
screens: Record<string, string>;
|
|
13
|
+
zIndex: Record<string, string>;
|
|
14
|
+
fontFamily: Record<string, string[]>;
|
|
15
|
+
};
|
|
16
|
+
export = aura;
|
package/tokens.json
CHANGED