Skip to content

Commit fe1af35

Browse files
CopilotTechQuery
andauthored
[add] Spinner & Loading components (#8)
Co-authored-by: TechQuery <[email protected]>
1 parent 09a9302 commit fe1af35

File tree

11 files changed

+185
-31
lines changed

11 files changed

+185
-31
lines changed

README.md

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,25 @@ A **Pagination Table** & **Scroll List** component suite for [CRUD operation][1]
88

99
## Components
1010

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/)
27-
17. [Editor](https://mobx-restful-shadcn.idea2.app/)
11+
1. [Spinner](https://mobx-restful-shadcn.idea2.app/r/spinner.json)
12+
2. [Loading](https://mobx-restful-shadcn.idea2.app/r/loading.json)
13+
3. [Badge Bar](https://mobx-restful-shadcn.idea2.app/r/badge-bar.json)
14+
4. [Badge Input](https://mobx-restful-shadcn.idea2.app/r/badge-input.json)
15+
5. [Image Preview](https://mobx-restful-shadcn.idea2.app/r/image-preview.json)
16+
6. [File Preview](https://mobx-restful-shadcn.idea2.app/r/file-preview.json)
17+
7. [File Picker](https://mobx-restful-shadcn.idea2.app/r/file-picker.json)
18+
8. [File Uploader](https://mobx-restful-shadcn.idea2.app/r/file-uploader.json)
19+
9. [Form Field](https://mobx-restful-shadcn.idea2.app/r/form-field.json)
20+
10. [Range Input](https://mobx-restful-shadcn.idea2.app/r/range-input.json)
21+
11. [Array Field](https://mobx-restful-shadcn.idea2.app/r/array-field.json)
22+
12. [REST Form](https://mobx-restful-shadcn.idea2.app/r/rest-form.json)
23+
13. [REST Form Modal](https://mobx-restful-shadcn.idea2.app/r/rest-form-modal.json)
24+
14. [Pager](https://mobx-restful-shadcn.idea2.app/r/pager.json)
25+
15. [REST Table](https://mobx-restful-shadcn.idea2.app/r/rest-table.json)
26+
16. [Scroll Boundary](https://mobx-restful-shadcn.idea2.app/r/scroll-boundary.json)
27+
17. [Scroll List](https://mobx-restful-shadcn.idea2.app/r/scroll-list.json)
28+
18. [Searchable Input](https://mobx-restful-shadcn.idea2.app/r/searchable-input.json)
29+
19. [Editor](https://mobx-restful-shadcn.idea2.app/r/editor.json)
2830

2931
## Installation
3032

app/page.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ import { FilePickerExample } from "@/registry/new-york/blocks/file-picker/exampl
77
import { FilePreviewExample } from "@/registry/new-york/blocks/file-preview/example";
88
import { FormFieldExample } from "@/registry/new-york/blocks/form-field/example";
99
import { ImagePreviewExample } from "@/registry/new-york/blocks/image-preview/example";
10+
import { LoadingExample } from "@/registry/new-york/blocks/loading/example";
1011
import { PagerExample } from "@/registry/new-york/blocks/pager/example";
1112
import { RangeInputExample } from "@/registry/new-york/blocks/range-input/example";
1213
import { RestTableExample } from "@/registry/new-york/blocks/rest-table/example";
1314
import { ScrollBoundaryExample } from "@/registry/new-york/blocks/scroll-boundary/example";
1415
import { ScrollListExample } from "@/registry/new-york/blocks/scroll-list/example";
16+
import { SpinnerExample } from "@/registry/new-york/blocks/spinner/example";
1517

1618
export default function Home() {
1719
return (
@@ -25,6 +27,20 @@ export default function Home() {
2527
</p>
2628
</header>
2729
<main className="flex flex-col flex-1 gap-8">
30+
<ComponentCard
31+
name="spinner"
32+
description="A loading spinner component with customizable size."
33+
>
34+
<SpinnerExample />
35+
</ComponentCard>
36+
37+
<ComponentCard
38+
name="loading"
39+
description="A full-screen loading overlay component with spinner and customizable message."
40+
>
41+
<LoadingExample />
42+
</ComponentCard>
43+
2844
<ComponentCard
2945
name="badge-bar"
3046
description="A component for displaying a list of badges with optional click and delete handlers."

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "mobx-restful-shadcn",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"private": true,
55
"scripts": {
66
"prepare": "husky",

registry.json

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,33 @@
33
"name": "mobx-restful-shadcn",
44
"homepage": "https://mobx-restful-shadcn.idea2.app",
55
"items": [
6+
{
7+
"name": "spinner",
8+
"type": "registry:component",
9+
"title": "Spinner",
10+
"description": "A loading spinner component with customizable size.",
11+
"files": [
12+
{
13+
"type": "registry:component",
14+
"path": "registry/new-york/blocks/spinner/index.tsx",
15+
"target": "components/ui/mobx-restful-shadcn/spinner/index.tsx"
16+
}
17+
]
18+
},
19+
{
20+
"name": "loading",
21+
"type": "registry:component",
22+
"title": "Loading",
23+
"description": "A full-screen loading overlay component with spinner and customizable message.",
24+
"registryDependencies": ["@mobx-restful-shadcn/spinner"],
25+
"files": [
26+
{
27+
"type": "registry:component",
28+
"path": "registry/new-york/blocks/loading/index.tsx",
29+
"target": "components/ui/mobx-restful-shadcn/loading/index.tsx"
30+
}
31+
]
32+
},
633
{
734
"name": "badge-bar",
835
"type": "registry:component",
@@ -38,13 +65,8 @@
3865
"type": "registry:component",
3966
"title": "Image Preview",
4067
"description": "An image preview component with modal viewing and download functionality.",
41-
"registryDependencies": ["dialog"],
42-
"dependencies": [
43-
"lucide-react",
44-
"mobx",
45-
"mobx-react",
46-
"mobx-react-helper"
47-
],
68+
"registryDependencies": ["dialog", "@mobx-restful-shadcn/spinner"],
69+
"dependencies": ["mobx", "mobx-react", "mobx-react-helper"],
4870
"files": [
4971
{
5072
"type": "registry:component",
@@ -266,7 +288,8 @@
266288
"@mobx-restful-shadcn/badge-bar",
267289
"@mobx-restful-shadcn/badge-input",
268290
"@mobx-restful-shadcn/rest-form-modal",
269-
"@mobx-restful-shadcn/scroll-list"
291+
"@mobx-restful-shadcn/scroll-list",
292+
"@mobx-restful-shadcn/spinner"
270293
],
271294
"dependencies": [
272295
"lodash.debounce",
@@ -298,7 +321,8 @@
298321
"@mobx-restful-shadcn/file-preview",
299322
"@mobx-restful-shadcn/pager",
300323
"@mobx-restful-shadcn/rest-form",
301-
"@mobx-restful-shadcn/rest-form-modal"
324+
"@mobx-restful-shadcn/rest-form-modal",
325+
"@mobx-restful-shadcn/spinner"
302326
],
303327
"dependencies": [
304328
"lodash.debounce",

registry/new-york/blocks/image-preview/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
"use client";
22

3-
import { Loader2 } from "lucide-react";
43
import { observable } from "mobx";
54
import { observer } from "mobx-react";
65
import { ObservedComponent, reaction } from "mobx-react-helper";
@@ -13,6 +12,7 @@ import {
1312
DialogTitle,
1413
} from "@/components/ui/dialog";
1514
import { cn } from "@/lib/utils";
15+
import { Spinner } from "../spinner";
1616

1717
export interface ImagePreviewProps extends ImgHTMLAttributes<HTMLImageElement> {
1818
src: string;
@@ -77,7 +77,7 @@ export class ImagePreview extends ObservedComponent<ImagePreviewProps> {
7777
{...props}
7878
>
7979
{downloading ? (
80-
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
80+
<Spinner className="text-muted-foreground" />
8181
) : (
8282
loadedPath && (
8383
<img
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
5+
import { Button } from "@/components/ui/button";
6+
import { Loading } from "./index";
7+
8+
export function LoadingExample() {
9+
const [isLoading, setIsLoading] = useState(false);
10+
11+
return (
12+
<div className="flex flex-col gap-4 items-center p-8">
13+
<Button onClick={() => setIsLoading(!isLoading)}>
14+
{isLoading ? "Hide Loading" : "Show Loading"}
15+
</Button>
16+
17+
<p className="text-sm text-muted-foreground">
18+
Click the button to toggle the full-screen loading overlay
19+
</p>
20+
21+
{isLoading && <Loading>Please wait...</Loading>}
22+
</div>
23+
);
24+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { FC, HTMLAttributes } from "react";
2+
3+
import { cn } from "@/lib/utils";
4+
import { Spinner } from "../spinner";
5+
6+
export type LoadingProps = HTMLAttributes<HTMLDivElement>;
7+
8+
export const Loading: FC<LoadingProps> = ({
9+
className,
10+
children = "Loading...",
11+
...props
12+
}) => (
13+
<div
14+
className={cn(
15+
"fixed inset-0 z-50 flex h-full w-full items-center justify-center bg-black/25",
16+
className,
17+
)}
18+
{...props}
19+
>
20+
<div className="flex items-center gap-3">
21+
<Spinner className="text-primary" />
22+
23+
{children && <span className="text-white">{children}</span>}
24+
</div>
25+
</div>
26+
);
27+
28+
Loading.displayName = "Loading";

registry/new-york/blocks/rest-table/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { FilePreview } from "../file-preview";
2525
import { Pager } from "../pager";
2626
import { Field, RestForm, RestFormProps } from "../rest-form";
2727
import { RestFormModal } from "../rest-form-modal";
28+
import { Spinner } from "../spinner";
2829

2930
export interface Column<T extends DataObject>
3031
extends Omit<Field<T>, "renderLabel"> {
@@ -301,7 +302,7 @@ export class RestTable<
301302
<TableRow>
302303
<TableCell className="text-center p-3" colSpan={columns.length}>
303304
<div className="flex justify-center">
304-
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary" />
305+
<Spinner />
305306
</div>
306307
</TableCell>
307308
</TableRow>

registry/new-york/blocks/searchable-input/index.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { TextInputType } from "../badge-input";
1515
import { RestFormProps } from "../rest-form";
1616
import { RestFormModal } from "../rest-form-modal";
1717
import { ScrollList, ScrollListProps } from "../scroll-list";
18+
import { Spinner } from "../spinner";
1819

1920
export type OptionData = Record<"label" | "value", string>;
2021

@@ -112,7 +113,7 @@ export class SearchableInput<
112113
) : (
113114
this.observedProps.store.downloading > 0 && (
114115
<div className="text-center my-3">
115-
<div className="inline-block h-4 w-4 animate-spin rounded-full border-2 border-solid border-current border-r-transparent" />
116+
<Spinner size="sm" />
116117
</div>
117118
)
118119
);
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { Spinner } from "./index";
2+
3+
export const SpinnerExample = () => (
4+
<div className="flex flex-col gap-8 items-center p-8">
5+
<div className="flex gap-4 items-center">
6+
<Spinner size="sm" />
7+
<span className="text-sm">Small Spinner</span>
8+
</div>
9+
<div className="flex gap-4 items-center">
10+
<Spinner size="md" />
11+
<span className="text-sm">Medium Spinner (Default)</span>
12+
</div>
13+
<div className="flex gap-4 items-center">
14+
<Spinner size="lg" />
15+
<span className="text-sm">Large Spinner</span>
16+
</div>
17+
<div className="flex gap-4 items-center">
18+
<Spinner className="text-primary" />
19+
<span className="text-sm">Custom Color (Primary)</span>
20+
</div>
21+
<div className="flex gap-4 items-center">
22+
<Spinner className="text-destructive" size="lg" />
23+
<span className="text-sm">Custom Color (Destructive)</span>
24+
</div>
25+
</div>
26+
);

0 commit comments

Comments
 (0)