Skip to content

Commit 559ed86

Browse files
CopilotTechQuery
andauthored
[add] REST Table component based on Shadcn UI (#5)
Co-authored-by: TechQuery <[email protected]>
1 parent 9023a7c commit 559ed86

File tree

12 files changed

+858
-68
lines changed

12 files changed

+858
-68
lines changed

README.md

Lines changed: 215 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,229 @@
11
# MobX RESTful Shadcn
22

3-
A **Pagination Table** & **Scroll List** component suite for [CRUD operation][1], which is based on [MobX RESTful][2] & [React][3].
3+
A **Pagination Table** & **Scroll List** component suite for [CRUD operation][1], which is based on [MobX RESTful][2], [React][3] & [Shadcn UI][4].
44

5-
You can use the `shadcn` CLI to run your own component registry. Running your own
6-
component registry allows you to distribute your custom components, hooks, pages, and
7-
other files to any React project.
5+
[![MobX compatibility](https://img.shields.io/badge/Compatible-1?logo=mobx&label=MobX%206%2F7)][5]
6+
[![NPM Dependency](https://img.shields.io/librariesio/github/idea2app/MobX-RESTful-Shadcn.svg)][6]
7+
[![CI & CD](https://github.com/idea2app/MobX-RESTful-Shadcn/actions/workflows/main.yml/badge.svg)][7]
88

9-
> [!IMPORTANT]
10-
> This template uses Tailwind v4. For Tailwind v3, see [registry-template-v3](https://github.com/shadcn-ui/registry-template-v3).
9+
## Components
1110

12-
## Getting Started
11+
1. [Badge Bar](https://mobx-restful-shadcn.idea2.app/)
12+
2. [Badge Input](https://mobx-restful-shadcn.idea2.app/)
13+
3. [Image Preview](https://mobx-restful-shadcn.idea2.app/)
14+
4. [File Preview](https://mobx-restful-shadcn.idea2.app/)
15+
5. [File Picker](https://mobx-restful-shadcn.idea2.app/)
16+
6. [File Uploader](https://mobx-restful-shadcn.idea2.app/)
17+
7. [Form Field](https://mobx-restful-shadcn.idea2.app/)
18+
8. [Range Input](https://mobx-restful-shadcn.idea2.app/)
19+
9. [Array Field](https://mobx-restful-shadcn.idea2.app/)
20+
10. [REST Form](https://mobx-restful-shadcn.idea2.app/)
21+
11. [REST Form Modal](https://mobx-restful-shadcn.idea2.app/)
22+
12. [Pager](https://mobx-restful-shadcn.idea2.app/)
23+
13. [REST Table](https://mobx-restful-shadcn.idea2.app/)
24+
14. [Scroll Boundary](https://mobx-restful-shadcn.idea2.app/)
25+
15. [Scroll List](https://mobx-restful-shadcn.idea2.app/)
26+
16. [Searchable Input](https://mobx-restful-shadcn.idea2.app/)
1327

14-
This is a template for creating a custom registry using Next.js.
28+
## Installation
1529

16-
- The template uses a `registry.json` file to define components and their files.
17-
- The `shadcn build` command is used to build the registry.
18-
- The registry items are served as static files under `public/r/[name].json`.
19-
- The template also includes a route handler for serving registry items.
20-
- Every registry item are compatible with the `shadcn` CLI.
21-
- We have also added v0 integration using the `Open in v0` api.
30+
```shell
31+
npx shadcn-helper add https://mobx-restful-shadcn.idea2.app/r/rest-table.json
32+
```
33+
34+
Replace `rest-table` with any component name from the list above.
35+
36+
## Configuration
37+
38+
### Internationalization
39+
40+
Set up i18n translation model for UI text:
41+
42+
```typescript
43+
import { TranslationModel } from "mobx-i18n";
44+
import { IDType } from "mobx-restful";
45+
46+
export const i18n = new TranslationModel({
47+
en_US: {
48+
load_more: "Load more",
49+
no_more: "No more",
50+
create: "Create",
51+
view: "View",
52+
submit: "Submit",
53+
cancel: "Cancel",
54+
edit: "Edit",
55+
delete: "Delete",
56+
total_x_rows: ({ totalCount }: { totalCount: number }) =>
57+
`Total ${totalCount} rows`,
58+
sure_to_delete_x: ({ keys }: { keys: IDType[] }) =>
59+
`Are you sure to delete ${keys.join(", ")}?`,
60+
},
61+
});
62+
```
63+
64+
### Data Source
65+
66+
Set up HTTP client and implement Model class:
67+
68+
```typescript
69+
import { githubClient, RepositoryModel } from "mobx-github";
70+
71+
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
72+
73+
githubClient.use(({ request }, next) => {
74+
if (GITHUB_TOKEN)
75+
request.headers = {
76+
...request.headers,
77+
Authorization: `Bearer ${GITHUB_TOKEN}`,
78+
};
79+
return next();
80+
});
81+
82+
export const repositoryStore = new RepositoryModel("idea2app");
83+
```
84+
85+
## Usage
86+
87+
### Pagination Table
88+
89+
```tsx
90+
import { computed } from "mobx";
91+
import { observer } from "mobx-react";
92+
import { Component } from "react";
93+
94+
import { BadgeBar } from "@/components/ui/badge-bar";
95+
import { RestTable, Column } from "@/components/ui/rest-table";
96+
import repositoryStore, { Repository } from "@/models/Repository";
97+
import { i18n } from "@/models/Translation";
98+
99+
@observer
100+
export class RepositoryTable extends Component {
101+
@computed
102+
get columns() {
103+
return [
104+
{
105+
key: "full_name",
106+
renderHead: "Repository Name",
107+
renderBody: ({ html_url, full_name }) => (
108+
<a target="_blank" href={html_url}>
109+
{full_name}
110+
</a>
111+
),
112+
required: true,
113+
minLength: 3,
114+
invalidMessage: "Input 3 characters at least",
115+
},
116+
{ key: "homepage", type: "url", renderHead: "Home Page" },
117+
{ key: "language", renderHead: "Programming Language" },
118+
{
119+
key: "topics",
120+
renderHead: "Topic",
121+
renderBody: ({ topics }) => (
122+
<BadgeBar
123+
list={(topics || []).map((text) => ({
124+
text,
125+
link: `https://github.com/topics/${text}`,
126+
}))}
127+
/>
128+
),
129+
},
130+
{ key: "stargazers_count", type: "number", renderHead: "Star Count" },
131+
{ key: "description", renderHead: "Description", rows: 3 },
132+
];
133+
}
134+
135+
render() {
136+
return (
137+
<RestTable
138+
editable
139+
deletable
140+
columns={this.columns}
141+
store={repositoryStore}
142+
translator={i18n}
143+
onCheck={console.log}
144+
/>
145+
);
146+
}
147+
}
148+
```
149+
150+
### Scroll List
151+
152+
```tsx
153+
import { observer } from "mobx-react";
154+
155+
import { ScrollList } from "@/components/ui/scroll-list";
156+
import repositoryStore from "@/models/Repository";
157+
import { i18n } from "@/models/Translation";
158+
159+
export const ScrollListExample = () => (
160+
<ScrollList
161+
translator={i18n}
162+
store={repositoryStore}
163+
renderList={(allItems) => (
164+
<ul className="grid grid-cols-1 md:grid-cols-2 gap-4">
165+
{allItems.map(({ id, name, description }) => (
166+
<li key={id} className="p-4 border rounded">
167+
<h3>{name}</h3>
168+
<p>{description}</p>
169+
</li>
170+
))}
171+
</ul>
172+
)}
173+
/>
174+
);
175+
```
176+
177+
### File Uploader
178+
179+
```tsx
180+
import { FileModel, FileUploader } from "@/components/ui/file-uploader";
181+
182+
class MyFileModel extends FileModel {}
183+
184+
const store = new MyFileModel();
185+
186+
export const EditorPage = () => (
187+
<FileUploader
188+
store={store}
189+
accept="image/*"
190+
name="images"
191+
multiple
192+
required
193+
onChange={console.log}
194+
/>
195+
);
196+
```
197+
198+
## Development
199+
200+
This is a custom component registry built with Next.js and compatible with the `shadcn` CLI.
201+
202+
### Getting Started
203+
204+
1. Clone the repository
205+
2. Install dependencies: `pnpm install`
206+
3. Run development server: `pnpm dev`
207+
4. Build registry: `pnpm registry:build`
208+
5. Build project: `pnpm build`
209+
210+
### Registry Structure
211+
212+
- The `registry.json` file defines all components and their files
213+
- Components are located in `registry/new-york/blocks/`
214+
- Each component has its implementation and example files
215+
- The `shadcn build` command generates registry items in `public/r/`
22216

23217
## Documentation
24218

25-
Visit the [shadcn documentation](https://ui.shadcn.com/docs/registry) to view the full documentation.
219+
- [Shadcn UI Documentation](https://ui.shadcn.com/docs)
220+
- [Component Registry Documentation](https://ui.shadcn.com/docs/registry)
221+
- [MobX RESTful Documentation][2]
26222

27223
[1]: https://en.wikipedia.org/wiki/Create,_read,_update_and_delete
28224
[2]: https://github.com/idea2app/MobX-RESTful
29225
[3]: https://reactjs.org/
226+
[4]: https://ui.shadcn.com/
227+
[5]: https://mobx.js.org/
228+
[6]: https://libraries.io/npm/mobx-restful-shadcn
229+
[7]: https://github.com/idea2app/MobX-RESTful-Shadcn/actions/workflows/main.yml

app/globals.css

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
@import "tailwindcss";
22
@import "tw-animate-css";
33

4+
.overflow-x-auto {
5+
overflow-x: auto;
6+
}
7+
48
@custom-variant dark (&:is(.dark *));
59

610
@theme inline {
@@ -44,8 +48,9 @@
4448
}
4549

4650
:root {
47-
--font-geist-sans: 'Geist Sans', ui-sans-serif, system-ui, sans-serif;
48-
--font-geist-mono: 'Geist Mono', ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
51+
--font-geist-sans: "Geist Sans", ui-sans-serif, system-ui, sans-serif;
52+
--font-geist-mono: "Geist Mono", ui-monospace, "Cascadia Code",
53+
"Source Code Pro", Menlo, Consolas, "DejaVu Sans Mono", monospace;
4954
--radius: 0.625rem;
5055
--background: oklch(1 0 0);
5156
--foreground: oklch(0.145 0 0);

app/page.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { BadgeInputExample } from "@/registry/new-york/blocks/badge-input/exampl
1414
import { RangeInputExample } from "@/registry/new-york/blocks/range-input/example";
1515
import { FilePickerExample } from "@/registry/new-york/blocks/file-picker/example";
1616
import { FormFieldExample } from "@/registry/new-york/blocks/form-field/example";
17+
import { RestTableExample } from "@/registry/new-york/blocks/rest-table/example";
1718

1819
export default function Home() {
1920
return (
@@ -130,6 +131,14 @@ export default function Home() {
130131
>
131132
<FormFieldExample />
132133
</ComponentCard>
134+
135+
<ComponentCard
136+
name="rest-table"
137+
description="A comprehensive pagination table component for CRUD operations with MobX RESTful integration."
138+
minHeight="min-h-[600px]"
139+
>
140+
<RestTableExample />
141+
</ComponentCard>
133142
</main>
134143
</div>
135144
);

components/example/form.tsx

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
11
import { GitRepository } from "mobx-github";
22

33
import { i18n, topicStore } from "@/models/example";
4+
import { BadgeBar } from "@/registry/new-york/blocks/badge-bar/badge-bar";
45
import { Field } from "@/registry/new-york/blocks/rest-form/rest-form";
6+
import { Column } from "@/registry/new-york/blocks/rest-table/rest-table";
57
import { SearchableInput } from "@/registry/new-york/blocks/searchable-input/searchable-input";
68

7-
export const fields: Field<GitRepository>[] = [
9+
export const columns: Column<GitRepository>[] = [
810
{
911
key: "full_name",
10-
renderLabel: "Repository Name",
12+
renderHead: "Repository Name",
13+
renderBody: ({ html_url, full_name }) => (
14+
<a target="_blank" href={html_url} rel="noreferrer">
15+
{full_name}
16+
</a>
17+
),
1118
required: true,
1219
minLength: 3,
1320
invalidMessage: "Input 3 characters at least",
1421
},
15-
{ key: "homepage", type: "url", renderLabel: "Home Page" },
16-
{ key: "language", renderLabel: "Programming Language" },
22+
{ key: "homepage", type: "url", renderHead: "Home Page" },
23+
{ key: "language", renderHead: "Programming Language" },
1724
{
1825
key: "topics",
19-
renderLabel: "Topic",
26+
renderHead: "Topic",
27+
renderBody: ({ topics }) => (
28+
<BadgeBar
29+
list={(topics || []).map((text) => ({
30+
text,
31+
link: `https://github.com/topics/${text}`,
32+
}))}
33+
/>
34+
),
2035
renderInput: ({ topics }) => (
2136
<SearchableInput
2237
translator={i18n}
@@ -29,6 +44,13 @@ export const fields: Field<GitRepository>[] = [
2944
/>
3045
),
3146
},
32-
{ key: "stargazers_count", type: "number", renderLabel: "Star Count" },
33-
{ key: "description", renderLabel: "Description", rows: 3 },
47+
{ key: "stargazers_count", type: "number", renderHead: "Star Count" },
48+
{ key: "description", renderHead: "Description", rows: 3 },
3449
];
50+
51+
export const fields: Field<GitRepository>[] = columns.map(
52+
({ renderHead, renderBody, ...meta }) => ({
53+
...meta,
54+
renderLabel: renderHead,
55+
})
56+
);

0 commit comments

Comments
 (0)