@arqel-dev/ui 0.8.1 → 0.9.1
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/dist/action.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pages.d.ts +13 -0
- package/dist/pages.js +115 -2
- package/dist/pages.js.map +1 -1
- package/package.json +6 -6
package/dist/pages.d.ts
CHANGED
|
@@ -29,6 +29,19 @@ declare function ArqelEditPage<TRecord extends RecordType = RecordType>(): JSX.E
|
|
|
29
29
|
* `<ResourceIndex>`. Apps can override per-resource by registering
|
|
30
30
|
* their own page component at `Pages/Arqel/{Slug}/Index.tsx` (the
|
|
31
31
|
* lookup falls through to user pages first inside `createArqelApp`).
|
|
32
|
+
*
|
|
33
|
+
* Wires the standard table interactions — search / filter / sort /
|
|
34
|
+
* pagination — into Inertia partial `router.get` visits so the
|
|
35
|
+
* server can re-run TableQueryBuilder with the new query string.
|
|
36
|
+
*
|
|
37
|
+
* Query-string contract (matches `Arqel\Table\TableQueryBuilder`):
|
|
38
|
+
* - `?search=foo` global search
|
|
39
|
+
* - `?sort=column&direction=asc|desc`
|
|
40
|
+
* - `?page=N&per_page=M`
|
|
41
|
+
* - `?filter[name]=value` one entry per filter
|
|
42
|
+
*
|
|
43
|
+
* `search` is debounced (300ms) so typing doesn't hammer the
|
|
44
|
+
* backend; the rest fire immediately.
|
|
32
45
|
*/
|
|
33
46
|
|
|
34
47
|
declare function ArqelIndexPage<TRecord extends RecordType = RecordType>(): JSX.Element;
|
package/dist/pages.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
import { useRef,
|
|
3
|
+
import { useMemo, useState, useRef, useEffect, useCallback, useId } from 'react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import { Slot } from 'radix-ui';
|
|
6
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
@@ -1566,10 +1566,123 @@ var ArqelIndexPage_exports = {};
|
|
|
1566
1566
|
__export(ArqelIndexPage_exports, {
|
|
1567
1567
|
default: () => ArqelIndexPage
|
|
1568
1568
|
});
|
|
1569
|
+
function pruneEmptyFilters(values) {
|
|
1570
|
+
const out = {};
|
|
1571
|
+
for (const [k, v] of Object.entries(values)) {
|
|
1572
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
1573
|
+
if (Array.isArray(v) && v.length === 0) continue;
|
|
1574
|
+
out[k] = v;
|
|
1575
|
+
}
|
|
1576
|
+
return out;
|
|
1577
|
+
}
|
|
1578
|
+
function buildQuery(params) {
|
|
1579
|
+
const data = {};
|
|
1580
|
+
const filters = pruneEmptyFilters(params.filters);
|
|
1581
|
+
if (Object.keys(filters).length > 0) data["filter"] = filters;
|
|
1582
|
+
if (params.search) data["search"] = params.search;
|
|
1583
|
+
if (params.sort?.column) {
|
|
1584
|
+
data["sort"] = params.sort.column;
|
|
1585
|
+
if (params.sort.direction) data["direction"] = params.sort.direction;
|
|
1586
|
+
}
|
|
1587
|
+
if (params.page && params.page > 1) data["page"] = params.page;
|
|
1588
|
+
if (params.perPage) data["per_page"] = params.perPage;
|
|
1589
|
+
return data;
|
|
1590
|
+
}
|
|
1569
1591
|
function ArqelIndexPage() {
|
|
1570
1592
|
const page = usePage();
|
|
1571
1593
|
const props = page.props;
|
|
1572
|
-
|
|
1594
|
+
const initialFilters = useMemo(() => {
|
|
1595
|
+
if (typeof window === "undefined") return {};
|
|
1596
|
+
const sp = new URLSearchParams(window.location.search);
|
|
1597
|
+
const fromUrl = {};
|
|
1598
|
+
for (const [key, value] of sp.entries()) {
|
|
1599
|
+
const m = key.match(/^filter\[([^\]]+)\]$/);
|
|
1600
|
+
if (m?.[1]) fromUrl[m[1]] = value;
|
|
1601
|
+
}
|
|
1602
|
+
return fromUrl;
|
|
1603
|
+
}, []);
|
|
1604
|
+
const [filterValues, setFilterValues] = useState(initialFilters);
|
|
1605
|
+
const [searchValue, setSearchValue] = useState(props.search ?? "");
|
|
1606
|
+
const searchDebounceRef = useRef(null);
|
|
1607
|
+
const visit = (data) => {
|
|
1608
|
+
const url = typeof window !== "undefined" ? window.location.pathname : page.url.split("?")[0];
|
|
1609
|
+
router.get(url ?? "/", data, {
|
|
1610
|
+
preserveState: true,
|
|
1611
|
+
preserveScroll: true,
|
|
1612
|
+
replace: true
|
|
1613
|
+
});
|
|
1614
|
+
};
|
|
1615
|
+
const currentSort = props.sort ?? null;
|
|
1616
|
+
const handleFilterChange = (name, value) => {
|
|
1617
|
+
const nextFilters = { ...filterValues };
|
|
1618
|
+
if (value === void 0 || value === null || value === "") {
|
|
1619
|
+
delete nextFilters[name];
|
|
1620
|
+
} else {
|
|
1621
|
+
nextFilters[name] = value;
|
|
1622
|
+
}
|
|
1623
|
+
setFilterValues(nextFilters);
|
|
1624
|
+
visit(buildQuery({ filters: nextFilters, search: searchValue, sort: currentSort }));
|
|
1625
|
+
};
|
|
1626
|
+
const handleClearFilters = () => {
|
|
1627
|
+
setFilterValues({});
|
|
1628
|
+
visit(buildQuery({ filters: {}, search: searchValue, sort: currentSort }));
|
|
1629
|
+
};
|
|
1630
|
+
const handleSearchChange = (value) => {
|
|
1631
|
+
setSearchValue(value);
|
|
1632
|
+
if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
|
|
1633
|
+
searchDebounceRef.current = setTimeout(() => {
|
|
1634
|
+
visit(buildQuery({ filters: filterValues, search: value, sort: currentSort }));
|
|
1635
|
+
}, 300);
|
|
1636
|
+
};
|
|
1637
|
+
useEffect(
|
|
1638
|
+
() => () => {
|
|
1639
|
+
if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
|
|
1640
|
+
},
|
|
1641
|
+
[]
|
|
1642
|
+
);
|
|
1643
|
+
const handleSortChange = (column, direction) => {
|
|
1644
|
+
visit(
|
|
1645
|
+
buildQuery({
|
|
1646
|
+
filters: filterValues,
|
|
1647
|
+
search: searchValue,
|
|
1648
|
+
sort: { column, direction }
|
|
1649
|
+
})
|
|
1650
|
+
);
|
|
1651
|
+
};
|
|
1652
|
+
const handlePageChange = (pageNum) => {
|
|
1653
|
+
visit(
|
|
1654
|
+
buildQuery({
|
|
1655
|
+
filters: filterValues,
|
|
1656
|
+
search: searchValue,
|
|
1657
|
+
sort: currentSort,
|
|
1658
|
+
page: pageNum
|
|
1659
|
+
})
|
|
1660
|
+
);
|
|
1661
|
+
};
|
|
1662
|
+
const handlePerPageChange = (perPage) => {
|
|
1663
|
+
visit(
|
|
1664
|
+
buildQuery({
|
|
1665
|
+
filters: filterValues,
|
|
1666
|
+
search: searchValue,
|
|
1667
|
+
sort: currentSort,
|
|
1668
|
+
perPage
|
|
1669
|
+
})
|
|
1670
|
+
);
|
|
1671
|
+
};
|
|
1672
|
+
return /* @__PURE__ */ jsx(
|
|
1673
|
+
ResourceIndex,
|
|
1674
|
+
{
|
|
1675
|
+
...props,
|
|
1676
|
+
filterValues,
|
|
1677
|
+
onFilterChange: handleFilterChange,
|
|
1678
|
+
onClearFilters: handleClearFilters,
|
|
1679
|
+
onSearchChange: handleSearchChange,
|
|
1680
|
+
onSortChange: handleSortChange,
|
|
1681
|
+
onPageChange: handlePageChange,
|
|
1682
|
+
onPerPageChange: handlePerPageChange,
|
|
1683
|
+
search: searchValue
|
|
1684
|
+
}
|
|
1685
|
+
);
|
|
1573
1686
|
}
|
|
1574
1687
|
var init_ArqelIndexPage = __esm({
|
|
1575
1688
|
"src/pages/ArqelIndexPage.tsx"() {
|