@arqel-dev/ui 0.16.0 → 0.17.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.
Files changed (38) hide show
  1. package/dist/ArqelEditPage-7OFWPULE.js +12 -0
  2. package/dist/{ArqelEditPage-RRYFYOTC.js.map → ArqelEditPage-7OFWPULE.js.map} +1 -1
  3. package/dist/ArqelIndexPage-ZAVCCM6V.js +14 -0
  4. package/dist/{ArqelIndexPage-J2EUV2PI.js.map → ArqelIndexPage-ZAVCCM6V.js.map} +1 -1
  5. package/dist/action.js +2 -1
  6. package/dist/{chunk-SK6H7ARE.js → chunk-3E66JNQO.js} +3 -18
  7. package/dist/chunk-3E66JNQO.js.map +1 -0
  8. package/dist/chunk-53J74IYR.js +22 -0
  9. package/dist/chunk-53J74IYR.js.map +1 -0
  10. package/dist/{chunk-SLLTFOOS.js → chunk-7NGUPQON.js} +5 -192
  11. package/dist/chunk-7NGUPQON.js.map +1 -0
  12. package/dist/chunk-DO3PRGVV.js +198 -0
  13. package/dist/chunk-DO3PRGVV.js.map +1 -0
  14. package/dist/{chunk-X4ZFKOPX.js → chunk-DOQUHQND.js} +4 -4
  15. package/dist/{chunk-X4ZFKOPX.js.map → chunk-DOQUHQND.js.map} +1 -1
  16. package/dist/{chunk-S5UNPCMQ.js → chunk-JKD6SE75.js} +3 -269
  17. package/dist/chunk-JKD6SE75.js.map +1 -0
  18. package/dist/{chunk-EG2DASKR.js → chunk-KGAY2AZH.js} +4 -3
  19. package/dist/chunk-KGAY2AZH.js.map +1 -0
  20. package/dist/chunk-WKIYS2FQ.js +509 -0
  21. package/dist/chunk-WKIYS2FQ.js.map +1 -0
  22. package/dist/chunk-YEPQWAD6.js +275 -0
  23. package/dist/chunk-YEPQWAD6.js.map +1 -0
  24. package/dist/index.js +7 -4
  25. package/dist/pages.js +10 -7
  26. package/dist/pages.js.map +1 -1
  27. package/dist/resource.js +3 -2
  28. package/dist/table.js +2 -1
  29. package/dist/utility.js +2 -1
  30. package/package.json +6 -6
  31. package/dist/ArqelEditPage-RRYFYOTC.js +0 -8
  32. package/dist/ArqelIndexPage-J2EUV2PI.js +0 -12
  33. package/dist/chunk-EG2DASKR.js.map +0 -1
  34. package/dist/chunk-S5UNPCMQ.js.map +0 -1
  35. package/dist/chunk-SK6H7ARE.js.map +0 -1
  36. package/dist/chunk-SLLTFOOS.js.map +0 -1
  37. package/dist/chunk-YQ6KXR3U.js +0 -55
  38. package/dist/chunk-YQ6KXR3U.js.map +0 -1
@@ -0,0 +1,275 @@
1
+ import { Button } from './chunk-H23VX3RR.js';
2
+ import { cn } from './chunk-B7ULUJVO.js';
3
+ import { useArqelTranslations, useArqelLocale } from '@arqel-dev/react/utils';
4
+ import { jsxs, jsx } from 'react/jsx-runtime';
5
+ import { useMemo } from 'react';
6
+
7
+ function TableFilters({
8
+ filters,
9
+ values,
10
+ onChange,
11
+ onClearAll,
12
+ className
13
+ }) {
14
+ const t = useArqelTranslations();
15
+ if (filters.length === 0) return null;
16
+ const activeCount = filters.reduce((acc, f) => {
17
+ const v = values[f.name];
18
+ return v !== void 0 && v !== null && v !== "" ? acc + 1 : acc;
19
+ }, 0);
20
+ return /* @__PURE__ */ jsxs("fieldset", { className: cn("flex flex-wrap items-end gap-2 border-0 p-0", className), children: [
21
+ /* @__PURE__ */ jsx("legend", { className: "sr-only", children: t("table.filters.legend", "Filters") }),
22
+ filters.map((filter) => /* @__PURE__ */ jsx(
23
+ FilterControl,
24
+ {
25
+ filter,
26
+ value: values[filter.name],
27
+ onChange: (v) => onChange(filter.name, v)
28
+ },
29
+ filter.name
30
+ )),
31
+ activeCount > 0 && onClearAll && /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClearAll, children: t("table.filters.clear", `Clear filters (${activeCount})`, { count: activeCount }) })
32
+ ] });
33
+ }
34
+ function FilterControl({
35
+ filter,
36
+ value,
37
+ onChange
38
+ }) {
39
+ switch (filter.type) {
40
+ case "select":
41
+ return /* @__PURE__ */ jsx(SelectFilter, { filter, value, onChange });
42
+ case "multiSelect":
43
+ return /* @__PURE__ */ jsx(MultiSelectFilter, { filter, value, onChange });
44
+ case "text":
45
+ return /* @__PURE__ */ jsx(TextFilter, { filter, value, onChange });
46
+ case "ternary":
47
+ return /* @__PURE__ */ jsx(TernaryFilter, { filter, value, onChange });
48
+ case "dateRange":
49
+ case "scope":
50
+ return null;
51
+ }
52
+ }
53
+ function controlClasses() {
54
+ return cn(
55
+ // 44px touch target on mobile (WCAG 2.5.5); dense 36px on >=md.
56
+ "h-11 rounded-sm border border-[var(--input)] md:h-9",
57
+ "bg-background px-3 text-sm",
58
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
59
+ );
60
+ }
61
+ function SelectFilter({
62
+ filter,
63
+ value,
64
+ onChange
65
+ }) {
66
+ const t = useArqelTranslations();
67
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
68
+ filter.label ?? filter.name,
69
+ /* @__PURE__ */ jsxs(
70
+ "select",
71
+ {
72
+ className: controlClasses(),
73
+ value: value === void 0 || value === null ? "" : String(value),
74
+ onChange: (e) => onChange(e.target.value === "" ? null : e.target.value),
75
+ children: [
76
+ /* @__PURE__ */ jsx("option", { value: "", children: t("table.filters.all", "All") }),
77
+ filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
78
+ ]
79
+ }
80
+ )
81
+ ] });
82
+ }
83
+ function MultiSelectFilter({
84
+ filter,
85
+ value,
86
+ onChange
87
+ }) {
88
+ const arr = Array.isArray(value) ? value : [];
89
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
90
+ filter.label ?? filter.name,
91
+ /* @__PURE__ */ jsx(
92
+ "select",
93
+ {
94
+ multiple: true,
95
+ className: cn(controlClasses(), "h-auto min-h-11 py-1 md:min-h-[2.25rem]"),
96
+ value: arr.map(String),
97
+ onChange: (e) => {
98
+ const next = Array.from(e.target.selectedOptions, (o) => o.value);
99
+ onChange(next.length === 0 ? null : next);
100
+ },
101
+ children: filter.props.options.map((opt) => /* @__PURE__ */ jsx("option", { value: opt.value, children: opt.label }, opt.value))
102
+ }
103
+ )
104
+ ] });
105
+ }
106
+ function TextFilter({
107
+ filter,
108
+ value,
109
+ onChange
110
+ }) {
111
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
112
+ filter.label ?? filter.name,
113
+ /* @__PURE__ */ jsx(
114
+ "input",
115
+ {
116
+ type: "text",
117
+ className: controlClasses(),
118
+ value: typeof value === "string" ? value : "",
119
+ onChange: (e) => onChange(e.target.value === "" ? null : e.target.value)
120
+ }
121
+ )
122
+ ] });
123
+ }
124
+ function TernaryFilter({
125
+ filter,
126
+ value,
127
+ onChange
128
+ }) {
129
+ const t = useArqelTranslations();
130
+ const current = value === void 0 || value === null ? "all" : String(value);
131
+ return /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
132
+ filter.label ?? filter.name,
133
+ /* @__PURE__ */ jsxs(
134
+ "select",
135
+ {
136
+ className: controlClasses(),
137
+ value: current,
138
+ onChange: (e) => {
139
+ const v = e.target.value;
140
+ onChange(v === "all" ? null : v);
141
+ },
142
+ children: [
143
+ /* @__PURE__ */ jsx("option", { value: "all", children: filter.props.allLabel ?? t("table.filters.all", "All") }),
144
+ /* @__PURE__ */ jsx("option", { value: "true", children: filter.props.trueLabel ?? t("table.filters.yes", "Yes") }),
145
+ /* @__PURE__ */ jsx("option", { value: "false", children: filter.props.falseLabel ?? t("table.filters.no", "No") })
146
+ ]
147
+ }
148
+ )
149
+ ] });
150
+ }
151
+ function TablePagination({
152
+ meta,
153
+ onPageChange,
154
+ onPerPageChange,
155
+ perPageOptions = [10, 25, 50, 100],
156
+ className
157
+ }) {
158
+ const t = useArqelTranslations();
159
+ const locale = useArqelLocale();
160
+ const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);
161
+ const fromRaw = (meta.currentPage - 1) * meta.perPage + 1;
162
+ const toRaw = Math.min(meta.currentPage * meta.perPage, meta.total);
163
+ const from = numberFormatter.format(fromRaw);
164
+ const to = numberFormatter.format(toRaw);
165
+ const total = numberFormatter.format(meta.total);
166
+ const isFirst = meta.currentPage <= 1;
167
+ const isLast = meta.currentPage >= meta.lastPage;
168
+ return /* @__PURE__ */ jsxs(
169
+ "nav",
170
+ {
171
+ className: cn(
172
+ "flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm",
173
+ className
174
+ ),
175
+ "aria-label": t("table.pagination.label", "Pagination"),
176
+ children: [
177
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: meta.total === 0 ? t("table.empty", "No records found.") : t(
178
+ "table.pagination.showing",
179
+ // Fallback is rendered verbatim when the key is missing, so it must
180
+ // already carry the interpolated values (the replacements map only
181
+ // applies to a resolved dictionary string).
182
+ `Showing ${from} to ${to} of ${total} results`,
183
+ { from, to, total }
184
+ ) }),
185
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
186
+ onPerPageChange && /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-1 text-xs text-muted-foreground", children: [
187
+ t("table.per_page", "Per page"),
188
+ /* @__PURE__ */ jsx(
189
+ "select",
190
+ {
191
+ className: "h-11 rounded-sm border border-[var(--input)] bg-background px-2 md:h-8",
192
+ value: meta.perPage,
193
+ onChange: (e) => onPerPageChange(Number(e.target.value)),
194
+ children: perPageOptions.map((n) => /* @__PURE__ */ jsx("option", { value: n, children: n }, n))
195
+ }
196
+ )
197
+ ] }),
198
+ /* @__PURE__ */ jsx(
199
+ Button,
200
+ {
201
+ variant: "outline",
202
+ size: "sm",
203
+ className: "h-11 md:h-8",
204
+ disabled: isFirst,
205
+ onClick: () => onPageChange(meta.currentPage - 1),
206
+ "aria-label": t("table.pagination.previous_page", "Previous page"),
207
+ children: t("table.pagination.previous", "Prev")
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
211
+ numberFormatter.format(meta.currentPage),
212
+ " / ",
213
+ numberFormatter.format(meta.lastPage)
214
+ ] }),
215
+ /* @__PURE__ */ jsx(
216
+ Button,
217
+ {
218
+ variant: "outline",
219
+ size: "sm",
220
+ className: "h-11 md:h-8",
221
+ disabled: isLast,
222
+ onClick: () => onPageChange(meta.currentPage + 1),
223
+ "aria-label": t("table.pagination.next_page", "Next page"),
224
+ children: t("table.pagination.next", "Next")
225
+ }
226
+ )
227
+ ] })
228
+ ]
229
+ }
230
+ );
231
+ }
232
+ function TableToolbar({
233
+ search,
234
+ filters,
235
+ selectedCount = 0,
236
+ bulkActions,
237
+ onClearSelection,
238
+ className
239
+ }) {
240
+ const t = useArqelTranslations();
241
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col gap-3 px-3 py-3", className), children: [
242
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [
243
+ search,
244
+ filters
245
+ ] }),
246
+ selectedCount > 0 && /* @__PURE__ */ jsxs(
247
+ "section",
248
+ {
249
+ className: "flex flex-wrap items-center gap-2 rounded-sm border border-border bg-muted px-3 py-2",
250
+ "aria-label": t("table.bulk.label", "Bulk actions"),
251
+ children: [
252
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: t("table.bulk.selected", "{one} :count selected|{other} :count selected", {
253
+ count: selectedCount
254
+ }) }),
255
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex flex-wrap items-center gap-2", children: [
256
+ bulkActions,
257
+ onClearSelection && /* @__PURE__ */ jsx(
258
+ "button",
259
+ {
260
+ type: "button",
261
+ className: "text-sm text-muted-foreground hover:underline",
262
+ onClick: onClearSelection,
263
+ children: t("table.bulk.clear", "Clear")
264
+ }
265
+ )
266
+ ] })
267
+ ]
268
+ }
269
+ )
270
+ ] });
271
+ }
272
+
273
+ export { TableFilters, TablePagination, TableToolbar };
274
+ //# sourceMappingURL=chunk-YEPQWAD6.js.map
275
+ //# sourceMappingURL=chunk-YEPQWAD6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/table/TableFilters.tsx","../src/table/TablePagination.tsx","../src/table/TableToolbar.tsx"],"names":["useArqelTranslations","jsxs","jsx"],"mappings":"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","file":"chunk-YEPQWAD6.js","sourcesContent":["/**\n * `<TableFilters>` — renders one input per `FilterSchema` and emits\n * a single `onChange(name, value)` callback. Active values render as\n * dismissible chips so users can read state without re-opening menus.\n *\n * Phase 1 covers the four most-used filter types (select, multiSelect,\n * text, ternary). dateRange / scope land alongside FieldRenderer in\n * UI-004, where the date primitives ship.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type {\n FilterSchema,\n MultiSelectFilterSchema,\n SelectFilterSchema,\n TernaryFilterSchema,\n TextFilterSchema,\n} from '@arqel-dev/types/tables';\nimport { Button } from '../action/Button.js';\nimport { cn } from '../utils/cn.js';\n\nexport interface TableFiltersProps {\n filters: FilterSchema[];\n values: Record<string, unknown>;\n onChange: (name: string, value: unknown) => void;\n onClearAll?: (() => void) | undefined;\n className?: string | undefined;\n}\n\nexport function TableFilters({\n filters,\n values,\n onChange,\n onClearAll,\n className,\n}: TableFiltersProps) {\n const t = useArqelTranslations();\n if (filters.length === 0) return null;\n\n const activeCount = filters.reduce((acc, f) => {\n const v = values[f.name];\n return v !== undefined && v !== null && v !== '' ? acc + 1 : acc;\n }, 0);\n\n return (\n <fieldset className={cn('flex flex-wrap items-end gap-2 border-0 p-0', className)}>\n <legend className=\"sr-only\">{t('table.filters.legend', 'Filters')}</legend>\n {filters.map((filter) => (\n <FilterControl\n key={filter.name}\n filter={filter}\n value={values[filter.name]}\n onChange={(v) => onChange(filter.name, v)}\n />\n ))}\n {activeCount > 0 && onClearAll && (\n <Button variant=\"ghost\" size=\"sm\" onClick={onClearAll}>\n {t('table.filters.clear', `Clear filters (${activeCount})`, { count: activeCount })}\n </Button>\n )}\n </fieldset>\n );\n}\n\nfunction FilterControl({\n filter,\n value,\n onChange,\n}: {\n filter: FilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n switch (filter.type) {\n case 'select':\n return <SelectFilter filter={filter} value={value} onChange={onChange} />;\n case 'multiSelect':\n return <MultiSelectFilter filter={filter} value={value} onChange={onChange} />;\n case 'text':\n return <TextFilter filter={filter} value={value} onChange={onChange} />;\n case 'ternary':\n return <TernaryFilter filter={filter} value={value} onChange={onChange} />;\n case 'dateRange':\n case 'scope':\n return null;\n }\n}\n\nfunction controlClasses() {\n return cn(\n // 44px touch target on mobile (WCAG 2.5.5); dense 36px on >=md.\n 'h-11 rounded-sm border border-[var(--input)] md:h-9',\n 'bg-background px-3 text-sm',\n 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring',\n );\n}\n\nfunction SelectFilter({\n filter,\n value,\n onChange,\n}: {\n filter: SelectFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const t = useArqelTranslations();\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <select\n className={controlClasses()}\n value={value === undefined || value === null ? '' : String(value)}\n onChange={(e) => onChange(e.target.value === '' ? null : e.target.value)}\n >\n <option value=\"\">{t('table.filters.all', 'All')}</option>\n {filter.props.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction MultiSelectFilter({\n filter,\n value,\n onChange,\n}: {\n filter: MultiSelectFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const arr = Array.isArray(value) ? (value as Array<string | number>) : [];\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <select\n multiple\n className={cn(controlClasses(), 'h-auto min-h-11 py-1 md:min-h-[2.25rem]')}\n value={arr.map(String)}\n onChange={(e) => {\n const next = Array.from(e.target.selectedOptions, (o) => o.value);\n onChange(next.length === 0 ? null : next);\n }}\n >\n {filter.props.options.map((opt) => (\n <option key={opt.value} value={opt.value}>\n {opt.label}\n </option>\n ))}\n </select>\n </label>\n );\n}\n\nfunction TextFilter({\n filter,\n value,\n onChange,\n}: {\n filter: TextFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <input\n type=\"text\"\n className={controlClasses()}\n value={typeof value === 'string' ? value : ''}\n onChange={(e) => onChange(e.target.value === '' ? null : e.target.value)}\n />\n </label>\n );\n}\n\nfunction TernaryFilter({\n filter,\n value,\n onChange,\n}: {\n filter: TernaryFilterSchema;\n value: unknown;\n onChange: (value: unknown) => void;\n}) {\n const t = useArqelTranslations();\n const current = value === undefined || value === null ? 'all' : String(value);\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {filter.label ?? filter.name}\n <select\n className={controlClasses()}\n value={current}\n onChange={(e) => {\n const v = e.target.value;\n onChange(v === 'all' ? null : v);\n }}\n >\n <option value=\"all\">{filter.props.allLabel ?? t('table.filters.all', 'All')}</option>\n <option value=\"true\">{filter.props.trueLabel ?? t('table.filters.yes', 'Yes')}</option>\n <option value=\"false\">{filter.props.falseLabel ?? t('table.filters.no', 'No')}</option>\n </select>\n </label>\n );\n}\n","/**\n * `<TablePagination>` — prev/next + range + per-page picker.\n *\n * Stateless: parent owns `currentPage` / `perPage` and handles transitions\n * (typically via Inertia `router.get` with `preserveState: true`).\n */\n\nimport { useArqelLocale, useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { PaginationMeta } from '@arqel-dev/types/resources';\nimport { useMemo } from 'react';\nimport { Button } from '../action/Button.js';\nimport { cn } from '../utils/cn.js';\n\nexport interface TablePaginationProps {\n meta: PaginationMeta;\n onPageChange: (page: number) => void;\n onPerPageChange?: ((perPage: number) => void) | undefined;\n perPageOptions?: number[];\n className?: string;\n}\n\nexport function TablePagination({\n meta,\n onPageChange,\n onPerPageChange,\n perPageOptions = [10, 25, 50, 100],\n className,\n}: TablePaginationProps) {\n const t = useArqelTranslations();\n const locale = useArqelLocale();\n const numberFormatter = useMemo(() => new Intl.NumberFormat(locale), [locale]);\n const fromRaw = (meta.currentPage - 1) * meta.perPage + 1;\n const toRaw = Math.min(meta.currentPage * meta.perPage, meta.total);\n // Interpolated counts are locale number-grouped (12,345 / 12.345) before they\n // reach the translator, whose applyReplacements does plain String() substitution.\n const from = numberFormatter.format(fromRaw);\n const to = numberFormatter.format(toRaw);\n const total = numberFormatter.format(meta.total);\n const isFirst = meta.currentPage <= 1;\n const isLast = meta.currentPage >= meta.lastPage;\n\n return (\n <nav\n className={cn(\n 'flex flex-wrap items-center justify-between gap-2 border-t border-border px-3 py-2 text-sm',\n className,\n )}\n aria-label={t('table.pagination.label', 'Pagination')}\n >\n <span className=\"text-muted-foreground\">\n {meta.total === 0\n ? t('table.empty', 'No records found.')\n : t(\n 'table.pagination.showing',\n // Fallback is rendered verbatim when the key is missing, so it must\n // already carry the interpolated values (the replacements map only\n // applies to a resolved dictionary string).\n `Showing ${from} to ${to} of ${total} results`,\n { from, to, total },\n )}\n </span>\n <div className=\"flex items-center gap-2\">\n {onPerPageChange && (\n <label className=\"flex items-center gap-1 text-xs text-muted-foreground\">\n {t('table.per_page', 'Per page')}\n {/* 44px touch target on mobile (WCAG 2.5.5); dense 32px on >=md. */}\n <select\n className=\"h-11 rounded-sm border border-[var(--input)] bg-background px-2 md:h-8\"\n value={meta.perPage}\n onChange={(e) => onPerPageChange(Number(e.target.value))}\n >\n {perPageOptions.map((n) => (\n <option key={n} value={n}>\n {n}\n </option>\n ))}\n </select>\n </label>\n )}\n {/* Buttons use size=\"sm\" (h-8) for the dense desktop bar; the responsive\n h-11 override lifts them to a 44px touch target below md. */}\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-11 md:h-8\"\n disabled={isFirst}\n onClick={() => onPageChange(meta.currentPage - 1)}\n aria-label={t('table.pagination.previous_page', 'Previous page')}\n >\n {t('table.pagination.previous', 'Prev')}\n </Button>\n <span className=\"text-xs text-muted-foreground\">\n {numberFormatter.format(meta.currentPage)} / {numberFormatter.format(meta.lastPage)}\n </span>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-11 md:h-8\"\n disabled={isLast}\n onClick={() => onPageChange(meta.currentPage + 1)}\n aria-label={t('table.pagination.next_page', 'Next page')}\n >\n {t('table.pagination.next', 'Next')}\n </Button>\n </div>\n </nav>\n );\n}\n","/**\n * `<TableToolbar>` — search input + filters + bulk-action bar slot.\n *\n * The bulk-action bar appears only when `selectedCount > 0`. ResourceIndex\n * passes `bulkActions` rendered as a row of buttons; the toolbar just\n * positions them.\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { ReactNode } from 'react';\nimport { cn } from '../utils/cn.js';\n\nexport interface TableToolbarProps {\n search?: ReactNode;\n filters?: ReactNode;\n selectedCount?: number;\n bulkActions?: ReactNode;\n onClearSelection?: (() => void) | undefined;\n className?: string;\n}\n\nexport function TableToolbar({\n search,\n filters,\n selectedCount = 0,\n bulkActions,\n onClearSelection,\n className,\n}: TableToolbarProps) {\n const t = useArqelTranslations();\n return (\n <div className={cn('flex flex-col gap-3 px-3 py-3', className)}>\n <div className=\"flex flex-wrap items-end gap-2\">\n {search}\n {filters}\n </div>\n {selectedCount > 0 && (\n <section\n className=\"flex flex-wrap items-center gap-2 rounded-sm border border-border bg-muted px-3 py-2\"\n aria-label={t('table.bulk.label', 'Bulk actions')}\n >\n <span className=\"text-sm font-medium\">\n {t('table.bulk.selected', '{one} :count selected|{other} :count selected', {\n count: selectedCount,\n })}\n </span>\n <div className=\"ml-auto flex flex-wrap items-center gap-2\">\n {bulkActions}\n {onClearSelection && (\n <button\n type=\"button\"\n className=\"text-sm text-muted-foreground hover:underline\"\n onClick={onClearSelection}\n >\n {t('table.bulk.clear', 'Clear')}\n </button>\n )}\n </div>\n </section>\n )}\n </div>\n );\n}\n"]}
package/dist/index.js CHANGED
@@ -1,14 +1,17 @@
1
- export { Breadcrumbs, EmptyState, ErrorState, LoadingSkeleton } from './chunk-SK6H7ARE.js';
1
+ export { Breadcrumbs, EmptyState, LoadingSkeleton } from './chunk-3E66JNQO.js';
2
+ export { ErrorState } from './chunk-53J74IYR.js';
2
3
  export { StatCard, WidgetWrapper } from './chunk-WMOBLT3Z.js';
3
4
  export { Alert, AlertDescription, AlertTitle, Badge, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Field, FieldContent, FieldDescription, FieldError, FieldGroup, FieldLabel, FieldLegend, FieldSeparator, FieldSet, FieldTitle, Label, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectSeparator, SelectTrigger, SelectValue, Textarea, badgeVariants } from './chunk-K7QY56XN.js';
4
5
  export { PageHeader } from './chunk-MXUXMHOK.js';
5
6
  export { AppShell, Footer, MainContent, Sidebar, Topbar, UserMenu } from './chunk-X2X6YDOX.js';
6
7
  export { CommandPalette } from './chunk-HNUUZEOL.js';
7
8
  export { Input, Separator, Skeleton } from './chunk-R4BOUU6Y.js';
8
- export { ResourceIndex } from './chunk-EG2DASKR.js';
9
- export { DataTable, TableFilters, TablePagination, TableToolbar } from './chunk-S5UNPCMQ.js';
9
+ export { ResourceIndex } from './chunk-KGAY2AZH.js';
10
+ export { TableFilters, TablePagination, TableToolbar } from './chunk-YEPQWAD6.js';
11
+ export { DataTable } from './chunk-JKD6SE75.js';
10
12
  import './chunk-RMPOR2YZ.js';
11
- export { ActionButton, ActionFormModal, ActionMenu, ConfirmDialog } from './chunk-SLLTFOOS.js';
13
+ export { ActionButton, ActionFormModal, ActionMenu } from './chunk-7NGUPQON.js';
14
+ export { ConfirmDialog } from './chunk-DO3PRGVV.js';
12
15
  import './chunk-VKZY6J6G.js';
13
16
  export { FormActions } from './chunk-MZMYV43C.js';
14
17
  export { FieldRenderer, FormRenderer, clearFieldRegistry, getFieldComponent, registerField } from './chunk-CMNZLA32.js';
package/dist/pages.js CHANGED
@@ -1,15 +1,18 @@
1
1
  export { ArqelShowPage } from './chunk-Q7WGPKUN.js';
2
2
  export { ArqelDashboardPage } from './chunk-3VSDQCFO.js';
3
- export { ArqelEditPage } from './chunk-YQ6KXR3U.js';
4
- export { ArqelIndexPage } from './chunk-X4ZFKOPX.js';
3
+ export { ArqelEditPage } from './chunk-WKIYS2FQ.js';
4
+ export { ArqelIndexPage } from './chunk-DOQUHQND.js';
5
5
  import './chunk-YYQ45W25.js';
6
6
  export { ArqelCreatePage } from './chunk-IO6RMVUF.js';
7
+ import './chunk-53J74IYR.js';
7
8
  import './chunk-WMOBLT3Z.js';
8
9
  import './chunk-MXUXMHOK.js';
9
- import './chunk-EG2DASKR.js';
10
- import './chunk-S5UNPCMQ.js';
10
+ import './chunk-KGAY2AZH.js';
11
+ import './chunk-YEPQWAD6.js';
12
+ import './chunk-JKD6SE75.js';
11
13
  import './chunk-RMPOR2YZ.js';
12
- import './chunk-SLLTFOOS.js';
14
+ import './chunk-7NGUPQON.js';
15
+ import './chunk-DO3PRGVV.js';
13
16
  import './chunk-VKZY6J6G.js';
14
17
  import './chunk-MZMYV43C.js';
15
18
  import './chunk-CMNZLA32.js';
@@ -18,9 +21,9 @@ import './chunk-B7ULUJVO.js';
18
21
 
19
22
  // src/pages/index.ts
20
23
  var arqelPages = {
21
- "arqel::index": () => import('./ArqelIndexPage-J2EUV2PI.js'),
24
+ "arqel::index": () => import('./ArqelIndexPage-ZAVCCM6V.js'),
22
25
  "arqel::create": () => import('./ArqelCreatePage-UZAHV4DK.js'),
23
- "arqel::edit": () => import('./ArqelEditPage-RRYFYOTC.js'),
26
+ "arqel::edit": () => import('./ArqelEditPage-7OFWPULE.js'),
24
27
  "arqel::show": () => import('./ArqelShowPage-TVCJAV2F.js'),
25
28
  "arqel::dashboard": () => import('./ArqelDashboardPage-Z5R4XSH4.js')
26
29
  };
package/dist/pages.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/pages/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AA0BO,IAAM,UAAA,GAAuC;AAAA,EAClD,cAAA,EAAgB,MACd,OAAO,8BAAqB,CAAA;AAAA,EAC9B,eAAA,EAAiB,MACf,OAAO,+BAAsB,CAAA;AAAA,EAC/B,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,kBAAA,EAAoB,MAClB,OAAO,kCAAyB;AACpC","file":"pages.js","sourcesContent":["/**\n * Built-in Inertia page registry for `arqel::*`.\n *\n * `createArqelApp({ pages })` from `@arqel-dev/react/inertia` accepts\n * one or more `PageRegistry` records — merge `arqelPages` first so\n * user-defined pages can override per-resource:\n *\n * ```ts\n * import { arqelPages } from '@arqel-dev/ui/pages';\n * import { createArqelApp } from '@arqel-dev/react/inertia';\n *\n * const userPages = import.meta.glob('./Pages/**\\/*.tsx');\n *\n * createArqelApp({\n * pages: { ...arqelPages, ...userPages },\n * });\n * ```\n *\n * Names match what `ResourceController` emits via\n * `Inertia::render('arqel::index', ...)`.\n */\n\nimport type { ComponentType } from 'react';\n\ntype LazyPage = () => Promise<{ default: ComponentType<unknown> }>;\n\nexport const arqelPages: Record<string, LazyPage> = {\n 'arqel::index': () =>\n import('./ArqelIndexPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::create': () =>\n import('./ArqelCreatePage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::edit': () => import('./ArqelEditPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::show': () => import('./ArqelShowPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::dashboard': () =>\n import('./ArqelDashboardPage.js') as Promise<{ default: ComponentType<unknown> }>,\n};\n\nexport { default as ArqelCreatePage } from './ArqelCreatePage.js';\nexport { default as ArqelDashboardPage } from './ArqelDashboardPage.js';\nexport { default as ArqelEditPage } from './ArqelEditPage.js';\nexport { default as ArqelIndexPage } from './ArqelIndexPage.js';\nexport { default as ArqelShowPage } from './ArqelShowPage.js';\n"]}
1
+ {"version":3,"sources":["../src/pages/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AA0BO,IAAM,UAAA,GAAuC;AAAA,EAClD,cAAA,EAAgB,MACd,OAAO,8BAAqB,CAAA;AAAA,EAC9B,eAAA,EAAiB,MACf,OAAO,+BAAsB,CAAA;AAAA,EAC/B,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,aAAA,EAAe,MAAM,OAAO,6BAAoB,CAAA;AAAA,EAChD,kBAAA,EAAoB,MAClB,OAAO,kCAAyB;AACpC","file":"pages.js","sourcesContent":["/**\n * Built-in Inertia page registry for `arqel::*`.\n *\n * `createArqelApp({ pages })` from `@arqel-dev/react/inertia` accepts\n * one or more `PageRegistry` records — merge `arqelPages` first so\n * user-defined pages can override per-resource:\n *\n * ```ts\n * import { arqelPages } from '@arqel-dev/ui/pages';\n * import { createArqelApp } from '@arqel-dev/react/inertia';\n *\n * const userPages = import.meta.glob('./Pages/**\\/*.tsx');\n *\n * createArqelApp({\n * pages: { ...arqelPages, ...userPages },\n * });\n * ```\n *\n * Names match what `ResourceController` emits via\n * `Inertia::render('arqel::index', ...)`.\n */\n\nimport type { ComponentType } from 'react';\n\ntype LazyPage = () => Promise<{ default: ComponentType<unknown> }>;\n\nexport const arqelPages: Record<string, LazyPage> = {\n 'arqel::index': () =>\n import('./ArqelIndexPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::create': () =>\n import('./ArqelCreatePage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::edit': () => import('./ArqelEditPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::show': () => import('./ArqelShowPage.js') as Promise<{ default: ComponentType<unknown> }>,\n 'arqel::dashboard': () =>\n import('./ArqelDashboardPage.js') as Promise<{ default: ComponentType<unknown> }>,\n};\n\nexport { default as ArqelCreatePage } from './ArqelCreatePage.js';\nexport { default as ArqelDashboardPage } from './ArqelDashboardPage.js';\nexport { default as ArqelEditPage } from './ArqelEditPage.js';\nexport { default as ArqelIndexPage } from './ArqelIndexPage.js';\nexport { default as ArqelShowPage } from './ArqelShowPage.js';\n"]}
package/dist/resource.js CHANGED
@@ -1,5 +1,6 @@
1
- export { ResourceIndex } from './chunk-EG2DASKR.js';
2
- import './chunk-S5UNPCMQ.js';
1
+ export { ResourceIndex } from './chunk-KGAY2AZH.js';
2
+ import './chunk-YEPQWAD6.js';
3
+ import './chunk-JKD6SE75.js';
3
4
  import './chunk-RMPOR2YZ.js';
4
5
  import './chunk-H23VX3RR.js';
5
6
  import './chunk-B7ULUJVO.js';
package/dist/table.js CHANGED
@@ -1,4 +1,5 @@
1
- export { DataTable, TableCell, TableFilters, TablePagination, TableToolbar } from './chunk-S5UNPCMQ.js';
1
+ export { TableFilters, TablePagination, TableToolbar } from './chunk-YEPQWAD6.js';
2
+ export { DataTable, TableCell } from './chunk-JKD6SE75.js';
2
3
  import './chunk-RMPOR2YZ.js';
3
4
  import './chunk-H23VX3RR.js';
4
5
  import './chunk-B7ULUJVO.js';
package/dist/utility.js CHANGED
@@ -1,4 +1,5 @@
1
- export { Breadcrumbs, EmptyState, ErrorState, LoadingSkeleton } from './chunk-SK6H7ARE.js';
1
+ export { Breadcrumbs, EmptyState, LoadingSkeleton } from './chunk-3E66JNQO.js';
2
+ export { ErrorState } from './chunk-53J74IYR.js';
2
3
  export { PageHeader } from './chunk-MXUXMHOK.js';
3
4
  import './chunk-B7ULUJVO.js';
4
5
  //# sourceMappingURL=utility.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arqel-dev/ui",
3
- "version": "0.16.0",
3
+ "version": "0.17.0",
4
4
  "description": "Structural React components (AppShell, DataTable, FormRenderer, etc.) for Arqel admin panels.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://arqel.dev",
@@ -92,7 +92,7 @@
92
92
  "clsx": "^2.1.1",
93
93
  "radix-ui": "^1.4.3",
94
94
  "tailwind-merge": "^2.5.5",
95
- "@arqel-dev/types": "0.16.0"
95
+ "@arqel-dev/types": "0.17.0"
96
96
  },
97
97
  "peerDependencies": {
98
98
  "@inertiajs/react": "^3.0.0",
@@ -102,8 +102,8 @@
102
102
  "react-dom": "^19.2.0",
103
103
  "recharts": "^2.15.0",
104
104
  "tailwindcss": "^4.0.0",
105
- "@arqel-dev/hooks": "0.16.0",
106
- "@arqel-dev/react": "0.16.0"
105
+ "@arqel-dev/hooks": "0.17.0",
106
+ "@arqel-dev/react": "0.17.0"
107
107
  },
108
108
  "devDependencies": {
109
109
  "@base-ui-components/react": "^1.0.0-alpha.0",
@@ -125,8 +125,8 @@
125
125
  "tsup": "^8.5.0",
126
126
  "typescript": "^5.6.3",
127
127
  "vitest": "^4.1.0",
128
- "@arqel-dev/hooks": "0.16.0",
129
- "@arqel-dev/react": "0.16.0"
128
+ "@arqel-dev/hooks": "0.17.0",
129
+ "@arqel-dev/react": "0.17.0"
130
130
  },
131
131
  "publishConfig": {
132
132
  "access": "public"
@@ -1,8 +0,0 @@
1
- export { ArqelEditPage as default } from './chunk-YQ6KXR3U.js';
2
- import './chunk-MXUXMHOK.js';
3
- import './chunk-MZMYV43C.js';
4
- import './chunk-CMNZLA32.js';
5
- import './chunk-H23VX3RR.js';
6
- import './chunk-B7ULUJVO.js';
7
- //# sourceMappingURL=ArqelEditPage-RRYFYOTC.js.map
8
- //# sourceMappingURL=ArqelEditPage-RRYFYOTC.js.map
@@ -1,12 +0,0 @@
1
- export { ArqelIndexPage as default } from './chunk-X4ZFKOPX.js';
2
- import './chunk-EG2DASKR.js';
3
- import './chunk-S5UNPCMQ.js';
4
- import './chunk-RMPOR2YZ.js';
5
- import './chunk-SLLTFOOS.js';
6
- import './chunk-VKZY6J6G.js';
7
- import './chunk-MZMYV43C.js';
8
- import './chunk-CMNZLA32.js';
9
- import './chunk-H23VX3RR.js';
10
- import './chunk-B7ULUJVO.js';
11
- //# sourceMappingURL=ArqelIndexPage-J2EUV2PI.js.map
12
- //# sourceMappingURL=ArqelIndexPage-J2EUV2PI.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/resource/ResourceIndex.tsx"],"names":[],"mappings":"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","file":"chunk-EG2DASKR.js","sourcesContent":["/**\n * `<ResourceIndex>` — page-level wrapper for index/listing routes.\n *\n * Wires `ResourceIndexProps` (server payload) into the table stack:\n * header (title + toolbar actions + create button)\n * → TableToolbar (search + filters + bulk bar)\n * → DataTable (with selection, sorting, row actions)\n * → TablePagination\n *\n * The component is presentational — selection / sort / page transitions\n * are lifted to props so the same shell can be reused by ResourceIndex\n * (Inertia-driven) and dashboard widgets (memory-driven).\n */\n\nimport { useArqelTranslations } from '@arqel-dev/react/utils';\nimport type { RecordType, ResourceIndexProps as ServerProps } from '@arqel-dev/types/resources';\nimport type { SortDirection, TableSort } from '@arqel-dev/types/tables';\nimport type { ReactNode } from 'react';\nimport { DataTable } from '../table/DataTable.js';\nimport { TableFilters } from '../table/TableFilters.js';\nimport { TablePagination } from '../table/TablePagination.js';\nimport { TableToolbar } from '../table/TableToolbar.js';\nimport { cn } from '../utils/cn.js';\n\ntype RowId = string | number;\n\nexport interface ResourceIndexUIProps<TRecord extends RecordType = RecordType>\n extends ServerProps<TRecord> {\n selectedIds?: ReadonlyArray<RowId>;\n onSelectionChange?: (ids: RowId[]) => void;\n onSortChange?: (column: string, direction: SortDirection) => void;\n onPageChange?: (page: number) => void;\n onPerPageChange?: (perPage: number) => void;\n onSearchChange?: (search: string) => void;\n onFilterChange?: (name: string, value: unknown) => void;\n onClearFilters?: () => void;\n\n filterValues?: Record<string, unknown>;\n\n searchSlot?: ReactNode;\n toolbarActions?: ReactNode;\n rowActions?: (record: TRecord) => ReactNode;\n bulkActions?: ReactNode;\n emptyState?: ReactNode;\n\n loading?: boolean;\n className?: string;\n}\n\nexport function ResourceIndex<TRecord extends RecordType = RecordType>({\n resource,\n records,\n pagination,\n columns,\n filters,\n search,\n sort,\n selectedIds = [],\n filterValues = {},\n onSelectionChange,\n onSortChange,\n onPageChange,\n onPerPageChange,\n onSearchChange,\n onFilterChange,\n onClearFilters,\n searchSlot,\n toolbarActions,\n rowActions,\n bulkActions,\n emptyState,\n loading = false,\n className,\n}: ResourceIndexUIProps<TRecord>) {\n const t = useArqelTranslations();\n const enableSelection = Boolean(onSelectionChange);\n\n const renderSearch = (): ReactNode => {\n if (searchSlot) return searchSlot;\n if (!onSearchChange) return null;\n return (\n <label className=\"flex flex-col gap-1 text-xs text-muted-foreground\">\n {t('table.search.label', 'Search')}\n <input\n type=\"search\"\n placeholder={t(\n 'table.search.placeholder_for',\n `Search ${resource.pluralLabel.toLowerCase()}…`,\n { resource: resource.pluralLabel.toLowerCase() },\n )}\n // 44px touch target on mobile (WCAG 2.5.5); dense 36px on >=md.\n className=\"h-11 w-64 rounded-sm border border-[var(--input)] bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring md:h-9\"\n value={search ?? ''}\n onChange={(e) => onSearchChange(e.target.value)}\n />\n </label>\n );\n };\n\n const normalisedSort: TableSort | null = sort?.column\n ? {\n column: sort.column,\n direction: sort.direction === 'desc' ? 'desc' : 'asc',\n }\n : null;\n\n return (\n <section\n className={cn(\n 'flex min-w-0 flex-col rounded-md border border-border bg-background',\n className,\n )}\n aria-label={resource.pluralLabel}\n >\n <header className=\"flex flex-wrap items-center gap-3 border-b border-border px-4 py-3\">\n <h1 className=\"text-lg font-semibold\">{resource.pluralLabel}</h1>\n <div className=\"ml-auto flex flex-wrap items-center gap-2\">{toolbarActions}</div>\n </header>\n\n <TableToolbar\n search={renderSearch()}\n filters={\n filters.length > 0 ? (\n <TableFilters\n filters={filters}\n values={filterValues}\n onChange={(name, value) => onFilterChange?.(name, value)}\n onClearAll={onClearFilters}\n />\n ) : null\n }\n selectedCount={selectedIds.length}\n bulkActions={bulkActions}\n onClearSelection={onSelectionChange ? () => onSelectionChange([]) : undefined}\n />\n\n <DataTable\n columns={columns}\n records={records as unknown as Array<{ id: RowId } & TRecord>}\n enableSelection={enableSelection}\n selectedIds={selectedIds}\n onSelectionChange={onSelectionChange}\n sort={normalisedSort}\n onSortChange={onSortChange}\n rowActions={rowActions ? (record) => rowActions(record as TRecord) : undefined}\n loading={loading}\n emptyState={emptyState}\n />\n\n {pagination && (\n <TablePagination\n meta={pagination}\n onPageChange={(page) => onPageChange?.(page)}\n onPerPageChange={onPerPageChange}\n />\n )}\n </section>\n );\n}\n"]}