@extrachill/components 0.2.0 → 1.0.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 +19 -33
- package/dist/index.d.mts +91 -0
- package/dist/index.d.ts +88 -8
- package/dist/index.js +213 -10
- package/dist/index.mjs +183 -0
- package/package.json +23 -35
- package/{styles → src/styles}/components.scss +0 -54
- package/CHANGELOG.md +0 -14
- package/dist/DataTable.d.ts +0 -19
- package/dist/DataTable.d.ts.map +0 -1
- package/dist/DataTable.js +0 -27
- package/dist/Modal.d.ts +0 -10
- package/dist/Modal.d.ts.map +0 -1
- package/dist/Modal.js +0 -19
- package/dist/Pagination.d.ts +0 -8
- package/dist/Pagination.d.ts.map +0 -1
- package/dist/Pagination.js +0 -7
- package/dist/SearchBox.d.ts +0 -8
- package/dist/SearchBox.d.ts.map +0 -1
- package/dist/SearchBox.js +0 -23
- package/dist/Tabs.d.ts +0 -14
- package/dist/Tabs.d.ts.map +0 -1
- package/dist/Tabs.js +0 -11
- package/dist/index.d.ts.map +0 -1
- package/src/DataTable.tsx +0 -109
- package/src/Modal.tsx +0 -52
- package/src/Pagination.tsx +0 -41
- package/src/SearchBox.tsx +0 -56
- package/src/Tabs.tsx +0 -51
- package/src/index.tsx +0 -11
package/README.md
CHANGED
|
@@ -2,45 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
Shared React components for the Extra Chill Platform ecosystem.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm install @extrachill/components
|
|
9
|
+
```
|
|
8
10
|
|
|
9
11
|
## Components
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
From a plugin within the Extra Chill Platform:
|
|
20
|
-
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"@extrachill/components": "file:../../extrachill-components"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
```
|
|
13
|
+
| Component | Description |
|
|
14
|
+
|-----------|-------------|
|
|
15
|
+
| **DataTable** | Admin table with optional row selection, loading states |
|
|
16
|
+
| **Pagination** | Previous/Next page navigation |
|
|
17
|
+
| **SearchBox** | Search input with clear button |
|
|
18
|
+
| **Modal** | Accessible modal dialog wrapping `@wordpress/components` |
|
|
28
19
|
|
|
29
20
|
## Usage
|
|
30
21
|
|
|
31
|
-
```
|
|
32
|
-
import { DataTable, Pagination, SearchBox, Modal
|
|
22
|
+
```tsx
|
|
23
|
+
import { DataTable, Pagination, SearchBox, Modal } from '@extrachill/components';
|
|
33
24
|
import '@extrachill/components/styles/components.scss';
|
|
34
25
|
|
|
35
|
-
function
|
|
26
|
+
function UsersTable() {
|
|
36
27
|
return (
|
|
37
28
|
<DataTable
|
|
38
29
|
columns={[
|
|
39
|
-
{ key: 'name', label: 'Name'
|
|
40
|
-
{ key: 'email', label: 'Email' }
|
|
30
|
+
{ key: 'name', label: 'Name' },
|
|
31
|
+
{ key: 'email', label: 'Email' },
|
|
41
32
|
]}
|
|
42
33
|
data={users}
|
|
43
|
-
|
|
34
|
+
isLoading={isLoading}
|
|
44
35
|
/>
|
|
45
36
|
);
|
|
46
37
|
}
|
|
@@ -48,17 +39,12 @@ function MyComponent() {
|
|
|
48
39
|
|
|
49
40
|
## Peer Dependencies
|
|
50
41
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- `@wordpress/components` ^28.0.0
|
|
54
|
-
- `@wordpress/element` ^6.0.0
|
|
55
|
-
- `react` ^18.0.0
|
|
42
|
+
Requires `@wordpress/components`, `@wordpress/element`, and `react` — all provided by `@wordpress/scripts` in WordPress plugin builds.
|
|
56
43
|
|
|
57
44
|
## Used By
|
|
58
45
|
|
|
59
|
-
- `extrachill-admin-tools`
|
|
60
|
-
- `extrachill-
|
|
61
|
-
- `extrachill-studio` - Team collaboration workspace
|
|
46
|
+
- `extrachill-admin-tools` — Network admin tools
|
|
47
|
+
- `extrachill-seo` — SEO audit dashboard
|
|
62
48
|
|
|
63
49
|
## License
|
|
64
50
|
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DataTable Component
|
|
5
|
+
*
|
|
6
|
+
* Reusable table for admin interfaces with optional row selection.
|
|
7
|
+
*/
|
|
8
|
+
interface DataTableColumn<T = Record<string, unknown>> {
|
|
9
|
+
/** Unique key matching a field in the data row. */
|
|
10
|
+
key: string;
|
|
11
|
+
/** Display label for the column header. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Fixed column width (CSS value). */
|
|
14
|
+
width?: string;
|
|
15
|
+
/** Custom render function for the cell. */
|
|
16
|
+
render?: (value: unknown, row: T) => React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
interface DataTableProps<T = Record<string, unknown>> {
|
|
19
|
+
/** Column definitions. */
|
|
20
|
+
columns: DataTableColumn<T>[];
|
|
21
|
+
/** Row data. */
|
|
22
|
+
data: T[];
|
|
23
|
+
/** Show a loading spinner instead of the table. */
|
|
24
|
+
isLoading?: boolean;
|
|
25
|
+
/** Enable row selection checkboxes. */
|
|
26
|
+
selectable?: boolean;
|
|
27
|
+
/** Currently selected row IDs. */
|
|
28
|
+
selectedIds?: Array<string | number>;
|
|
29
|
+
/** Callback when selection changes. */
|
|
30
|
+
onSelectChange?: (ids: Array<string | number>) => void;
|
|
31
|
+
/** Message shown when data is empty. */
|
|
32
|
+
emptyMessage?: string;
|
|
33
|
+
/** Key used to identify each row. */
|
|
34
|
+
rowKey?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function DataTable<T extends Record<string, unknown>>({ columns, data, isLoading, selectable, selectedIds, onSelectChange, emptyMessage, rowKey, }: DataTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Pagination Component
|
|
40
|
+
*
|
|
41
|
+
* Page navigation for admin interfaces.
|
|
42
|
+
*/
|
|
43
|
+
interface PaginationProps {
|
|
44
|
+
/** Current page number (1-indexed). */
|
|
45
|
+
currentPage: number;
|
|
46
|
+
/** Total number of pages. */
|
|
47
|
+
totalPages: number;
|
|
48
|
+
/** Total number of items across all pages. */
|
|
49
|
+
totalItems: number;
|
|
50
|
+
/** Callback when page changes. */
|
|
51
|
+
onPageChange: (page: number) => void;
|
|
52
|
+
}
|
|
53
|
+
declare function Pagination({ currentPage, totalPages, totalItems, onPageChange, }: PaginationProps): react_jsx_runtime.JSX.Element | null;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* SearchBox Component
|
|
57
|
+
*
|
|
58
|
+
* Search input with clear button for admin interfaces.
|
|
59
|
+
*/
|
|
60
|
+
interface SearchBoxProps {
|
|
61
|
+
/** Current search value. */
|
|
62
|
+
value?: string;
|
|
63
|
+
/** Callback when search is submitted. */
|
|
64
|
+
onSearch: (value: string) => void;
|
|
65
|
+
/** Placeholder text for the input. */
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
/** Callback when search is cleared. Falls back to onSearch('') if not provided. */
|
|
68
|
+
onClear?: () => void;
|
|
69
|
+
}
|
|
70
|
+
declare function SearchBox({ value, onSearch, placeholder, onClear, }: SearchBoxProps): react_jsx_runtime.JSX.Element;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Modal Component
|
|
74
|
+
*
|
|
75
|
+
* Accessible modal dialog wrapping @wordpress/components Modal.
|
|
76
|
+
*/
|
|
77
|
+
interface ModalProps {
|
|
78
|
+
/** Modal title displayed in the header. */
|
|
79
|
+
title: string;
|
|
80
|
+
/** Whether the modal is currently visible. */
|
|
81
|
+
isOpen: boolean;
|
|
82
|
+
/** Callback when the modal is closed. */
|
|
83
|
+
onClose: () => void;
|
|
84
|
+
/** Modal content. */
|
|
85
|
+
children: React.ReactNode;
|
|
86
|
+
/** Additional CSS class name. */
|
|
87
|
+
className?: string;
|
|
88
|
+
}
|
|
89
|
+
declare function Modal({ title, isOpen, onClose, children, className, }: ModalProps): react_jsx_runtime.JSX.Element | null;
|
|
90
|
+
|
|
91
|
+
export { DataTable, type DataTableColumn, type DataTableProps, Modal, type ModalProps, Pagination, type PaginationProps, SearchBox, type SearchBoxProps };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,91 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* DataTable Component
|
|
3
5
|
*
|
|
4
|
-
*
|
|
6
|
+
* Reusable table for admin interfaces with optional row selection.
|
|
5
7
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
interface DataTableColumn<T = Record<string, unknown>> {
|
|
9
|
+
/** Unique key matching a field in the data row. */
|
|
10
|
+
key: string;
|
|
11
|
+
/** Display label for the column header. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Fixed column width (CSS value). */
|
|
14
|
+
width?: string;
|
|
15
|
+
/** Custom render function for the cell. */
|
|
16
|
+
render?: (value: unknown, row: T) => React.ReactNode;
|
|
17
|
+
}
|
|
18
|
+
interface DataTableProps<T = Record<string, unknown>> {
|
|
19
|
+
/** Column definitions. */
|
|
20
|
+
columns: DataTableColumn<T>[];
|
|
21
|
+
/** Row data. */
|
|
22
|
+
data: T[];
|
|
23
|
+
/** Show a loading spinner instead of the table. */
|
|
24
|
+
isLoading?: boolean;
|
|
25
|
+
/** Enable row selection checkboxes. */
|
|
26
|
+
selectable?: boolean;
|
|
27
|
+
/** Currently selected row IDs. */
|
|
28
|
+
selectedIds?: Array<string | number>;
|
|
29
|
+
/** Callback when selection changes. */
|
|
30
|
+
onSelectChange?: (ids: Array<string | number>) => void;
|
|
31
|
+
/** Message shown when data is empty. */
|
|
32
|
+
emptyMessage?: string;
|
|
33
|
+
/** Key used to identify each row. */
|
|
34
|
+
rowKey?: string;
|
|
35
|
+
}
|
|
36
|
+
declare function DataTable<T extends Record<string, unknown>>({ columns, data, isLoading, selectable, selectedIds, onSelectChange, emptyMessage, rowKey, }: DataTableProps<T>): react_jsx_runtime.JSX.Element;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Pagination Component
|
|
40
|
+
*
|
|
41
|
+
* Page navigation for admin interfaces.
|
|
42
|
+
*/
|
|
43
|
+
interface PaginationProps {
|
|
44
|
+
/** Current page number (1-indexed). */
|
|
45
|
+
currentPage: number;
|
|
46
|
+
/** Total number of pages. */
|
|
47
|
+
totalPages: number;
|
|
48
|
+
/** Total number of items across all pages. */
|
|
49
|
+
totalItems: number;
|
|
50
|
+
/** Callback when page changes. */
|
|
51
|
+
onPageChange: (page: number) => void;
|
|
52
|
+
}
|
|
53
|
+
declare function Pagination({ currentPage, totalPages, totalItems, onPageChange, }: PaginationProps): react_jsx_runtime.JSX.Element | null;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* SearchBox Component
|
|
57
|
+
*
|
|
58
|
+
* Search input with clear button for admin interfaces.
|
|
59
|
+
*/
|
|
60
|
+
interface SearchBoxProps {
|
|
61
|
+
/** Current search value. */
|
|
62
|
+
value?: string;
|
|
63
|
+
/** Callback when search is submitted. */
|
|
64
|
+
onSearch: (value: string) => void;
|
|
65
|
+
/** Placeholder text for the input. */
|
|
66
|
+
placeholder?: string;
|
|
67
|
+
/** Callback when search is cleared. Falls back to onSearch('') if not provided. */
|
|
68
|
+
onClear?: () => void;
|
|
69
|
+
}
|
|
70
|
+
declare function SearchBox({ value, onSearch, placeholder, onClear, }: SearchBoxProps): react_jsx_runtime.JSX.Element;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Modal Component
|
|
74
|
+
*
|
|
75
|
+
* Accessible modal dialog wrapping @wordpress/components Modal.
|
|
76
|
+
*/
|
|
77
|
+
interface ModalProps {
|
|
78
|
+
/** Modal title displayed in the header. */
|
|
79
|
+
title: string;
|
|
80
|
+
/** Whether the modal is currently visible. */
|
|
81
|
+
isOpen: boolean;
|
|
82
|
+
/** Callback when the modal is closed. */
|
|
83
|
+
onClose: () => void;
|
|
84
|
+
/** Modal content. */
|
|
85
|
+
children: React.ReactNode;
|
|
86
|
+
/** Additional CSS class name. */
|
|
87
|
+
className?: string;
|
|
88
|
+
}
|
|
89
|
+
declare function Modal({ title, isOpen, onClose, children, className, }: ModalProps): react_jsx_runtime.JSX.Element | null;
|
|
90
|
+
|
|
91
|
+
export { DataTable, type DataTableColumn, type DataTableProps, Modal, type ModalProps, Pagination, type PaginationProps, SearchBox, type SearchBoxProps };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,213 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
DataTable: () => DataTable,
|
|
24
|
+
Modal: () => Modal,
|
|
25
|
+
Pagination: () => Pagination,
|
|
26
|
+
SearchBox: () => SearchBox
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(index_exports);
|
|
29
|
+
|
|
30
|
+
// src/components/DataTable.tsx
|
|
31
|
+
var import_components = require("@wordpress/components");
|
|
32
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
33
|
+
function DataTable({
|
|
34
|
+
columns,
|
|
35
|
+
data,
|
|
36
|
+
isLoading = false,
|
|
37
|
+
selectable = false,
|
|
38
|
+
selectedIds = [],
|
|
39
|
+
onSelectChange = () => {
|
|
40
|
+
},
|
|
41
|
+
emptyMessage = "No data found.",
|
|
42
|
+
rowKey = "id"
|
|
43
|
+
}) {
|
|
44
|
+
const allSelected = data.length > 0 && selectedIds.length === data.length;
|
|
45
|
+
const someSelected = selectedIds.length > 0 && selectedIds.length < data.length;
|
|
46
|
+
const handleSelectAll = (checked) => {
|
|
47
|
+
if (checked) {
|
|
48
|
+
onSelectChange(data.map((row) => row[rowKey]));
|
|
49
|
+
} else {
|
|
50
|
+
onSelectChange([]);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
const handleSelectRow = (id, checked) => {
|
|
54
|
+
if (checked) {
|
|
55
|
+
onSelectChange([...selectedIds, id]);
|
|
56
|
+
} else {
|
|
57
|
+
onSelectChange(selectedIds.filter((sid) => sid !== id));
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
if (isLoading) {
|
|
61
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "ec-data-table__loading", children: [
|
|
62
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {}),
|
|
63
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Loading..." })
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
if (data.length === 0) {
|
|
67
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "ec-data-table__empty", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: emptyMessage }) });
|
|
68
|
+
}
|
|
69
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("table", { className: "ec-data-table wp-list-table widefat fixed striped", children: [
|
|
70
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
71
|
+
selectable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: "ec-data-table__check-column", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
72
|
+
import_components.CheckboxControl,
|
|
73
|
+
{
|
|
74
|
+
checked: allSelected,
|
|
75
|
+
indeterminate: someSelected,
|
|
76
|
+
onChange: handleSelectAll,
|
|
77
|
+
__nextHasNoMarginBottom: true
|
|
78
|
+
}
|
|
79
|
+
) }),
|
|
80
|
+
columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { style: col.width ? { width: col.width } : {}, children: col.label }, col.key))
|
|
81
|
+
] }) }),
|
|
82
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: data.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
|
|
83
|
+
selectable && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: "ec-data-table__check-column", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
84
|
+
import_components.CheckboxControl,
|
|
85
|
+
{
|
|
86
|
+
checked: selectedIds.includes(row[rowKey]),
|
|
87
|
+
onChange: (checked) => handleSelectRow(row[rowKey], checked),
|
|
88
|
+
__nextHasNoMarginBottom: true
|
|
89
|
+
}
|
|
90
|
+
) }),
|
|
91
|
+
columns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { children: col.render ? col.render(row[col.key], row) : String(row[col.key] ?? "") }, col.key))
|
|
92
|
+
] }, String(row[rowKey]))) })
|
|
93
|
+
] });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/components/Pagination.tsx
|
|
97
|
+
var import_components2 = require("@wordpress/components");
|
|
98
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
99
|
+
function Pagination({
|
|
100
|
+
currentPage,
|
|
101
|
+
totalPages,
|
|
102
|
+
totalItems,
|
|
103
|
+
onPageChange
|
|
104
|
+
}) {
|
|
105
|
+
if (totalPages <= 1) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ec-pagination", children: [
|
|
109
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "ec-pagination__info", children: [
|
|
110
|
+
"Page ",
|
|
111
|
+
currentPage,
|
|
112
|
+
" of ",
|
|
113
|
+
totalPages,
|
|
114
|
+
" (",
|
|
115
|
+
totalItems,
|
|
116
|
+
" items)"
|
|
117
|
+
] }),
|
|
118
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ec-pagination__buttons", children: [
|
|
119
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
120
|
+
import_components2.Button,
|
|
121
|
+
{
|
|
122
|
+
variant: "secondary",
|
|
123
|
+
disabled: currentPage <= 1,
|
|
124
|
+
onClick: () => onPageChange(currentPage - 1),
|
|
125
|
+
children: "Previous"
|
|
126
|
+
}
|
|
127
|
+
),
|
|
128
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
129
|
+
import_components2.Button,
|
|
130
|
+
{
|
|
131
|
+
variant: "secondary",
|
|
132
|
+
disabled: currentPage >= totalPages,
|
|
133
|
+
onClick: () => onPageChange(currentPage + 1),
|
|
134
|
+
children: "Next"
|
|
135
|
+
}
|
|
136
|
+
)
|
|
137
|
+
] })
|
|
138
|
+
] });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/components/SearchBox.tsx
|
|
142
|
+
var import_element = require("@wordpress/element");
|
|
143
|
+
var import_components3 = require("@wordpress/components");
|
|
144
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
145
|
+
function SearchBox({
|
|
146
|
+
value = "",
|
|
147
|
+
onSearch,
|
|
148
|
+
placeholder = "Search...",
|
|
149
|
+
onClear
|
|
150
|
+
}) {
|
|
151
|
+
const [inputValue, setInputValue] = (0, import_element.useState)(value);
|
|
152
|
+
const handleSearch = () => {
|
|
153
|
+
onSearch(inputValue);
|
|
154
|
+
};
|
|
155
|
+
const handleClear = () => {
|
|
156
|
+
setInputValue("");
|
|
157
|
+
if (onClear) {
|
|
158
|
+
onClear();
|
|
159
|
+
} else {
|
|
160
|
+
onSearch("");
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const handleKeyDown = (e) => {
|
|
164
|
+
if (e.key === "Enter") {
|
|
165
|
+
handleSearch();
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ec-search-box", children: [
|
|
169
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
170
|
+
import_components3.TextControl,
|
|
171
|
+
{
|
|
172
|
+
value: inputValue,
|
|
173
|
+
onChange: setInputValue,
|
|
174
|
+
placeholder,
|
|
175
|
+
onKeyDown: handleKeyDown,
|
|
176
|
+
__nextHasNoMarginBottom: true
|
|
177
|
+
}
|
|
178
|
+
),
|
|
179
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_components3.Button, { variant: "secondary", onClick: handleSearch, children: "Search" }),
|
|
180
|
+
inputValue && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_components3.Button, { variant: "tertiary", onClick: handleClear, children: "Clear" })
|
|
181
|
+
] });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/components/Modal.tsx
|
|
185
|
+
var import_components4 = require("@wordpress/components");
|
|
186
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
187
|
+
function Modal({
|
|
188
|
+
title,
|
|
189
|
+
isOpen,
|
|
190
|
+
onClose,
|
|
191
|
+
children,
|
|
192
|
+
className = ""
|
|
193
|
+
}) {
|
|
194
|
+
if (!isOpen) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
198
|
+
import_components4.Modal,
|
|
199
|
+
{
|
|
200
|
+
title,
|
|
201
|
+
onRequestClose: onClose,
|
|
202
|
+
className: `ec-modal ${className}`.trim(),
|
|
203
|
+
children
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
208
|
+
0 && (module.exports = {
|
|
209
|
+
DataTable,
|
|
210
|
+
Modal,
|
|
211
|
+
Pagination,
|
|
212
|
+
SearchBox
|
|
213
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
// src/components/DataTable.tsx
|
|
2
|
+
import { CheckboxControl, Spinner } from "@wordpress/components";
|
|
3
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
function DataTable({
|
|
5
|
+
columns,
|
|
6
|
+
data,
|
|
7
|
+
isLoading = false,
|
|
8
|
+
selectable = false,
|
|
9
|
+
selectedIds = [],
|
|
10
|
+
onSelectChange = () => {
|
|
11
|
+
},
|
|
12
|
+
emptyMessage = "No data found.",
|
|
13
|
+
rowKey = "id"
|
|
14
|
+
}) {
|
|
15
|
+
const allSelected = data.length > 0 && selectedIds.length === data.length;
|
|
16
|
+
const someSelected = selectedIds.length > 0 && selectedIds.length < data.length;
|
|
17
|
+
const handleSelectAll = (checked) => {
|
|
18
|
+
if (checked) {
|
|
19
|
+
onSelectChange(data.map((row) => row[rowKey]));
|
|
20
|
+
} else {
|
|
21
|
+
onSelectChange([]);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const handleSelectRow = (id, checked) => {
|
|
25
|
+
if (checked) {
|
|
26
|
+
onSelectChange([...selectedIds, id]);
|
|
27
|
+
} else {
|
|
28
|
+
onSelectChange(selectedIds.filter((sid) => sid !== id));
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
if (isLoading) {
|
|
32
|
+
return /* @__PURE__ */ jsxs("div", { className: "ec-data-table__loading", children: [
|
|
33
|
+
/* @__PURE__ */ jsx(Spinner, {}),
|
|
34
|
+
/* @__PURE__ */ jsx("span", { children: "Loading..." })
|
|
35
|
+
] });
|
|
36
|
+
}
|
|
37
|
+
if (data.length === 0) {
|
|
38
|
+
return /* @__PURE__ */ jsx("div", { className: "ec-data-table__empty", children: /* @__PURE__ */ jsx("p", { children: emptyMessage }) });
|
|
39
|
+
}
|
|
40
|
+
return /* @__PURE__ */ jsxs("table", { className: "ec-data-table wp-list-table widefat fixed striped", children: [
|
|
41
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
42
|
+
selectable && /* @__PURE__ */ jsx("th", { className: "ec-data-table__check-column", children: /* @__PURE__ */ jsx(
|
|
43
|
+
CheckboxControl,
|
|
44
|
+
{
|
|
45
|
+
checked: allSelected,
|
|
46
|
+
indeterminate: someSelected,
|
|
47
|
+
onChange: handleSelectAll,
|
|
48
|
+
__nextHasNoMarginBottom: true
|
|
49
|
+
}
|
|
50
|
+
) }),
|
|
51
|
+
columns.map((col) => /* @__PURE__ */ jsx("th", { style: col.width ? { width: col.width } : {}, children: col.label }, col.key))
|
|
52
|
+
] }) }),
|
|
53
|
+
/* @__PURE__ */ jsx("tbody", { children: data.map((row) => /* @__PURE__ */ jsxs("tr", { children: [
|
|
54
|
+
selectable && /* @__PURE__ */ jsx("td", { className: "ec-data-table__check-column", children: /* @__PURE__ */ jsx(
|
|
55
|
+
CheckboxControl,
|
|
56
|
+
{
|
|
57
|
+
checked: selectedIds.includes(row[rowKey]),
|
|
58
|
+
onChange: (checked) => handleSelectRow(row[rowKey], checked),
|
|
59
|
+
__nextHasNoMarginBottom: true
|
|
60
|
+
}
|
|
61
|
+
) }),
|
|
62
|
+
columns.map((col) => /* @__PURE__ */ jsx("td", { children: col.render ? col.render(row[col.key], row) : String(row[col.key] ?? "") }, col.key))
|
|
63
|
+
] }, String(row[rowKey]))) })
|
|
64
|
+
] });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/components/Pagination.tsx
|
|
68
|
+
import { Button } from "@wordpress/components";
|
|
69
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
70
|
+
function Pagination({
|
|
71
|
+
currentPage,
|
|
72
|
+
totalPages,
|
|
73
|
+
totalItems,
|
|
74
|
+
onPageChange
|
|
75
|
+
}) {
|
|
76
|
+
if (totalPages <= 1) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return /* @__PURE__ */ jsxs2("div", { className: "ec-pagination", children: [
|
|
80
|
+
/* @__PURE__ */ jsxs2("span", { className: "ec-pagination__info", children: [
|
|
81
|
+
"Page ",
|
|
82
|
+
currentPage,
|
|
83
|
+
" of ",
|
|
84
|
+
totalPages,
|
|
85
|
+
" (",
|
|
86
|
+
totalItems,
|
|
87
|
+
" items)"
|
|
88
|
+
] }),
|
|
89
|
+
/* @__PURE__ */ jsxs2("div", { className: "ec-pagination__buttons", children: [
|
|
90
|
+
/* @__PURE__ */ jsx2(
|
|
91
|
+
Button,
|
|
92
|
+
{
|
|
93
|
+
variant: "secondary",
|
|
94
|
+
disabled: currentPage <= 1,
|
|
95
|
+
onClick: () => onPageChange(currentPage - 1),
|
|
96
|
+
children: "Previous"
|
|
97
|
+
}
|
|
98
|
+
),
|
|
99
|
+
/* @__PURE__ */ jsx2(
|
|
100
|
+
Button,
|
|
101
|
+
{
|
|
102
|
+
variant: "secondary",
|
|
103
|
+
disabled: currentPage >= totalPages,
|
|
104
|
+
onClick: () => onPageChange(currentPage + 1),
|
|
105
|
+
children: "Next"
|
|
106
|
+
}
|
|
107
|
+
)
|
|
108
|
+
] })
|
|
109
|
+
] });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// src/components/SearchBox.tsx
|
|
113
|
+
import { useState } from "@wordpress/element";
|
|
114
|
+
import { TextControl, Button as Button2 } from "@wordpress/components";
|
|
115
|
+
import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
116
|
+
function SearchBox({
|
|
117
|
+
value = "",
|
|
118
|
+
onSearch,
|
|
119
|
+
placeholder = "Search...",
|
|
120
|
+
onClear
|
|
121
|
+
}) {
|
|
122
|
+
const [inputValue, setInputValue] = useState(value);
|
|
123
|
+
const handleSearch = () => {
|
|
124
|
+
onSearch(inputValue);
|
|
125
|
+
};
|
|
126
|
+
const handleClear = () => {
|
|
127
|
+
setInputValue("");
|
|
128
|
+
if (onClear) {
|
|
129
|
+
onClear();
|
|
130
|
+
} else {
|
|
131
|
+
onSearch("");
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
const handleKeyDown = (e) => {
|
|
135
|
+
if (e.key === "Enter") {
|
|
136
|
+
handleSearch();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ec-search-box", children: [
|
|
140
|
+
/* @__PURE__ */ jsx3(
|
|
141
|
+
TextControl,
|
|
142
|
+
{
|
|
143
|
+
value: inputValue,
|
|
144
|
+
onChange: setInputValue,
|
|
145
|
+
placeholder,
|
|
146
|
+
onKeyDown: handleKeyDown,
|
|
147
|
+
__nextHasNoMarginBottom: true
|
|
148
|
+
}
|
|
149
|
+
),
|
|
150
|
+
/* @__PURE__ */ jsx3(Button2, { variant: "secondary", onClick: handleSearch, children: "Search" }),
|
|
151
|
+
inputValue && /* @__PURE__ */ jsx3(Button2, { variant: "tertiary", onClick: handleClear, children: "Clear" })
|
|
152
|
+
] });
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/components/Modal.tsx
|
|
156
|
+
import { Modal as WPModal } from "@wordpress/components";
|
|
157
|
+
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
158
|
+
function Modal({
|
|
159
|
+
title,
|
|
160
|
+
isOpen,
|
|
161
|
+
onClose,
|
|
162
|
+
children,
|
|
163
|
+
className = ""
|
|
164
|
+
}) {
|
|
165
|
+
if (!isOpen) {
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
168
|
+
return /* @__PURE__ */ jsx4(
|
|
169
|
+
WPModal,
|
|
170
|
+
{
|
|
171
|
+
title,
|
|
172
|
+
onRequestClose: onClose,
|
|
173
|
+
className: `ec-modal ${className}`.trim(),
|
|
174
|
+
children
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
export {
|
|
179
|
+
DataTable,
|
|
180
|
+
Modal,
|
|
181
|
+
Pagination,
|
|
182
|
+
SearchBox
|
|
183
|
+
};
|