@jirawatpyk/aura-react 5.79.0 → 5.80.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 +63 -1
- package/dist/aura.bundle.js +6 -0
- package/dist/cjs/index.cjs +6 -0
- package/dist/esm/DataTable.js +6 -0
- package/dist/styles.css +5 -0
- package/dist/styles.layer.css +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -100,7 +100,69 @@ export function Providers({ children, locale }: { children: React.ReactNode; loc
|
|
|
100
100
|
// app/layout.tsx (a Server Component): <body><Providers locale="th">{children}</Providers></body>
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
`linkComponent` is used by Button `href`, Breadcrumb, Tabs and Menu items with `href`, BottomNav, Pagination `getHref` (page numbers and, since 4.17, the previous / next arrows), Stat `href`, SideNav and DataTable row/pager links. Each of them also takes its own `linkComponent`, which wins over the provider's. Pass `getHref` from a client component (it's a function too). The Next.js starter does all of this, and CI clicks every AURA link in it to check none triggers a full page load. Only `th` shows Buddhist-era years; `en` and `sv` are Gregorian (`sv` weeks start Monday). **Without a provider, components are English with Gregorian dates** — wrap Thai apps in `<AuraProvider locale="th">`. For dates in your own components use `useFormatDate()` — it follows the provider (`const fmt = useFormatDate(); fmt(iso, { format: 'long' })`). Plain `formatDate()` has no provider to read: since 5.0 it is English and Gregorian unless you pass `locale`, the same function from the package root and from `@jirawatpyk/aura-react/server`. Values are always Gregorian ISO dates.
|
|
103
|
+
`linkComponent` is used by Button `href`, Breadcrumb, Tabs and Menu items with `href`, BottomNav, Pagination `getHref` (page numbers and, since 4.17, the previous / next arrows), Stat and StatusTile `href`, Th `sortHref`, toast action `href`, SideNav and DataTable row/pager links. Each of them also takes its own `linkComponent`, which wins over the provider's. Pass `getHref` from a client component (it's a function too). The Next.js starter does all of this, and CI clicks every AURA link in it to check none triggers a full page load. Only `th` shows Buddhist-era years; `en` and `sv` are Gregorian (`sv` weeks start Monday). **Without a provider, components are English with Gregorian dates** — wrap Thai apps in `<AuraProvider locale="th">`. For dates in your own components use `useFormatDate()` — it follows the provider (`const fmt = useFormatDate(); fmt(iso, { format: 'long' })`). Plain `formatDate()` has no provider to read: since 5.0 it is English and Gregorian unless you pass `locale`, the same function from the package root and from `@jirawatpyk/aura-react/server`. Values are always Gregorian ISO dates.
|
|
104
|
+
|
|
105
|
+
TanStack Start / TanStack Router (5.80): AURA gives `linkComponent` a plain `href`, so pass a small `<a>` that hands same-origin clicks to the router — not TanStack's `<Link>`, which adds `aria-current="page"` to every link its own matcher calls active (on `/members?page=3`, the page 1 link and the "All" tab as well), over AURA's own:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
// src/components/aura-link.tsx
|
|
109
|
+
import * as React from 'react';
|
|
110
|
+
import { useRouter } from '@tanstack/react-router';
|
|
111
|
+
|
|
112
|
+
function routerHref(href?: string): string | null {
|
|
113
|
+
if (href == null || typeof window === 'undefined') return null;
|
|
114
|
+
let url: URL;
|
|
115
|
+
try {
|
|
116
|
+
url = new URL(href, window.location.href); // also '?page=2' and 'members'
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
const here = window.location;
|
|
121
|
+
if (url.origin !== here.origin) return null; // other sites, mailto:, tel:
|
|
122
|
+
if (url.hash && url.pathname === here.pathname && url.search === here.search) return null; // #fragment here
|
|
123
|
+
return url.pathname + url.search + url.hash;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export const AuraLink = React.forwardRef<HTMLAnchorElement, React.AnchorHTMLAttributes<HTMLAnchorElement>>(
|
|
127
|
+
function AuraLink({ href, onClick, onMouseEnter, onFocus, ...rest }, ref) {
|
|
128
|
+
const router = useRouter();
|
|
129
|
+
const preload = () => {
|
|
130
|
+
const to = routerHref(href);
|
|
131
|
+
if (to) router.preloadRoute({ href: to } as Parameters<typeof router.preloadRoute>[0]).catch(() => undefined);
|
|
132
|
+
};
|
|
133
|
+
return (
|
|
134
|
+
<a ref={ref} href={href} {...rest}
|
|
135
|
+
onClick={(e) => {
|
|
136
|
+
onClick?.(e); // AURA's own handler first (a DataTable pager with onPageChange, a toast action)
|
|
137
|
+
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
138
|
+
const target = e.currentTarget.getAttribute('target');
|
|
139
|
+
if ((target && target !== '_self') || e.currentTarget.hasAttribute('download')) return;
|
|
140
|
+
const to = routerHref(href);
|
|
141
|
+
if (!to) return;
|
|
142
|
+
e.preventDefault();
|
|
143
|
+
void router.navigate({ href: to });
|
|
144
|
+
}}
|
|
145
|
+
onMouseEnter={(e) => { onMouseEnter?.(e); preload(); }}
|
|
146
|
+
onFocus={(e) => { onFocus?.(e); preload(); }}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
149
|
+
},
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
// src/routes/__root.tsx — the provider inside the router (the root route's component), the document in shellComponent
|
|
153
|
+
export const Route = createRootRoute({
|
|
154
|
+
head: () => ({ links: [{ rel: 'stylesheet', href: tokensCss }, { rel: 'stylesheet', href: auraCss }] }), // '…css?url' imports
|
|
155
|
+
shellComponent: RootDocument, // <html><head><HeadContent /><ColorSchemeScript /></head><body>{children}<Scripts /></body></html>
|
|
156
|
+
component: () => (
|
|
157
|
+
<AuraProvider locale="th" strings={th} linkComponent={AuraLink}>
|
|
158
|
+
<Outlet />
|
|
159
|
+
<Toaster />
|
|
160
|
+
</AuraProvider>
|
|
161
|
+
),
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Write hrefs as URLs (`getHref={(p) => '/members?page=' + p}`) and read them back with the route's `validateSearch`. A client navigation passes the query through the router's search parser and serializer, so keep params single-valued (`?a=1&a=2` comes back as JSON, `?q=1e3` as `1000`) or set the router's `parseSearch` / `stringifySearch`. With a `basepath`, write hrefs with it; a same-origin href outside it goes to the router's not-found route. DataTable keyboard paging (PageDown / PageUp, arrows past the page) clicks the pager links, and since 5.80 the focus moves to the new page's row. The TanStack Start starter (`templates/tanstack-start-starter`) has all of this; CI builds it against the packed packages and clicks every AURA link in it — Button, Breadcrumb, link Tabs, DropdownMenu items, BottomNav, Pagination, Th `sortHref`, Stat, StatusTile, SideNav, DataTable rows and pager, toast actions — checking that none loads a full page, that `aria-current` is AURA's and that Ctrl/⌘-click still opens a tab.
|
|
104
166
|
|
|
105
167
|
## Compact density
|
|
106
168
|
|
package/dist/aura.bundle.js
CHANGED
|
@@ -6548,6 +6548,7 @@ window.Aura = (() => {
|
|
|
6548
6548
|
if (next === page) return false;
|
|
6549
6549
|
if (!quiet) emit(sort, next);
|
|
6550
6550
|
if (linkPaging) {
|
|
6551
|
+
if (focus) pending.current = { ...focus, page: next, at: Date.now() };
|
|
6551
6552
|
const a = next === page - 1 ? prevLink.current : next === page + 1 ? nextLink.current : null;
|
|
6552
6553
|
if (a) a.click();
|
|
6553
6554
|
else jumpState[1](next);
|
|
@@ -6646,6 +6647,11 @@ window.Aura = (() => {
|
|
|
6646
6647
|
React34.useEffect(function() {
|
|
6647
6648
|
const p = pending.current;
|
|
6648
6649
|
if (!p || !gridRef.current) return;
|
|
6650
|
+
if (p.at != null && Date.now() - p.at > 1e4) {
|
|
6651
|
+
pending.current = null;
|
|
6652
|
+
return;
|
|
6653
|
+
}
|
|
6654
|
+
if (p.page != null && p.page !== page) return;
|
|
6649
6655
|
let c = p.c;
|
|
6650
6656
|
if (p.key) {
|
|
6651
6657
|
let at = -1;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -6530,6 +6530,7 @@ var DataTableImpl = React34.forwardRef(function DataTable(props, ref) {
|
|
|
6530
6530
|
if (next === page) return false;
|
|
6531
6531
|
if (!quiet) emit(sort, next);
|
|
6532
6532
|
if (linkPaging) {
|
|
6533
|
+
if (focus) pending.current = { ...focus, page: next, at: Date.now() };
|
|
6533
6534
|
const a = next === page - 1 ? prevLink.current : next === page + 1 ? nextLink.current : null;
|
|
6534
6535
|
if (a) a.click();
|
|
6535
6536
|
else jumpState[1](next);
|
|
@@ -6628,6 +6629,11 @@ var DataTableImpl = React34.forwardRef(function DataTable(props, ref) {
|
|
|
6628
6629
|
React34.useEffect(function() {
|
|
6629
6630
|
const p = pending.current;
|
|
6630
6631
|
if (!p || !gridRef.current) return;
|
|
6632
|
+
if (p.at != null && Date.now() - p.at > 1e4) {
|
|
6633
|
+
pending.current = null;
|
|
6634
|
+
return;
|
|
6635
|
+
}
|
|
6636
|
+
if (p.page != null && p.page !== page) return;
|
|
6631
6637
|
let c = p.c;
|
|
6632
6638
|
if (p.key) {
|
|
6633
6639
|
let at = -1;
|
package/dist/esm/DataTable.js
CHANGED
|
@@ -487,6 +487,7 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
487
487
|
if (next === page) return false;
|
|
488
488
|
if (!quiet) emit(sort, next);
|
|
489
489
|
if (linkPaging) {
|
|
490
|
+
if (focus) pending.current = { ...focus, page: next, at: Date.now() };
|
|
490
491
|
const a = next === page - 1 ? prevLink.current : next === page + 1 ? nextLink.current : null;
|
|
491
492
|
if (a) a.click();
|
|
492
493
|
else jumpState[1](next);
|
|
@@ -585,6 +586,11 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
|
|
|
585
586
|
React.useEffect(function() {
|
|
586
587
|
const p = pending.current;
|
|
587
588
|
if (!p || !gridRef.current) return;
|
|
589
|
+
if (p.at != null && Date.now() - p.at > 1e4) {
|
|
590
|
+
pending.current = null;
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
if (p.page != null && p.page !== page) return;
|
|
588
594
|
let c = p.c;
|
|
589
595
|
if (p.key) {
|
|
590
596
|
let at = -1;
|
package/dist/styles.css
CHANGED
|
@@ -2153,6 +2153,11 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
2153
2153
|
.aura-filterbar--fill .aura-filterbar__controls > .aura-filterselect .aura-filterselect__chevron { margin-inline-start: auto; }
|
|
2154
2154
|
/* Compact filters sit closer together than full fields. */
|
|
2155
2155
|
.aura-filterbar__controls:has(> .aura-filterselect) { gap: var(--aura-space-2); }
|
|
2156
|
+
/* 5.80: on a phone 6px, so three filters and a Tag keep one 375px row with either font source — the self-hosted
|
|
2157
|
+
* Inter (aura-fonts.local.css) measured 2.3px over at 8px. */
|
|
2158
|
+
@media (max-width: 639.98px) {
|
|
2159
|
+
.aura-filterbar__controls:has(> .aura-filterselect) { gap: 6px; }
|
|
2160
|
+
}
|
|
2156
2161
|
/* 5.42 (Chamber-OS 146): collapseBelow — below the breakpoint the controls fold into a "Filters" sheet; the button
|
|
2157
2162
|
* shares a row with the search, at its end, as tall as the search (the count and actions wrap below). From the
|
|
2158
2163
|
* breakpoint up the search and the hidden button sit in a display: contents box, so the bar lays out as before. */
|
package/dist/styles.layer.css
CHANGED
|
@@ -2155,6 +2155,11 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
|
|
|
2155
2155
|
.aura-filterbar--fill .aura-filterbar__controls > .aura-filterselect .aura-filterselect__chevron { margin-inline-start: auto; }
|
|
2156
2156
|
/* Compact filters sit closer together than full fields. */
|
|
2157
2157
|
.aura-filterbar__controls:has(> .aura-filterselect) { gap: var(--aura-space-2); }
|
|
2158
|
+
/* 5.80: on a phone 6px, so three filters and a Tag keep one 375px row with either font source — the self-hosted
|
|
2159
|
+
* Inter (aura-fonts.local.css) measured 2.3px over at 8px. */
|
|
2160
|
+
@media (max-width: 639.98px) {
|
|
2161
|
+
.aura-filterbar__controls:has(> .aura-filterselect) { gap: 6px; }
|
|
2162
|
+
}
|
|
2158
2163
|
/* 5.42 (Chamber-OS 146): collapseBelow — below the breakpoint the controls fold into a "Filters" sheet; the button
|
|
2159
2164
|
* shares a row with the search, at its end, as tall as the search (the count and actions wrap below). From the
|
|
2160
2165
|
* breakpoint up the search and the hidden button sit in a display: contents box, so the bar lays out as before. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jirawatpyk/aura-react",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.80.0",
|
|
4
4
|
"description": "AURA Design System — React components (Enterprise Standard, Human Creative). Thai-first: Buddhist-era dates, Thai collation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|