notionsoft-ui 1.0.36 → 1.0.39

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notionsoft-ui",
3
- "version": "1.0.36",
3
+ "version": "1.0.39",
4
4
  "description": "A React UI component installer (shadcn-style). Installs components directly into your project.",
5
5
  "bin": {
6
6
  "notionsoft-ui": "./cli/index.cjs"
@@ -0,0 +1,86 @@
1
+ // Breadcrumb.stories.tsx
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import {
4
+ Breadcrumb,
5
+ BreadcrumbItem,
6
+ BreadcrumbSeparator,
7
+ BreadcrumbHome,
8
+ } from "./breadcrumb";
9
+
10
+ const meta: Meta<typeof Breadcrumb> = {
11
+ title: "Components/Breadcrumb",
12
+ component: Breadcrumb,
13
+ parameters: {
14
+ layout: "centered",
15
+ },
16
+ };
17
+
18
+ export default meta;
19
+
20
+ type Story = StoryObj<typeof Breadcrumb>;
21
+
22
+ /**
23
+ * Basic breadcrumb with text items
24
+ */
25
+ export const Basic: Story = {
26
+ render: () => (
27
+ <Breadcrumb>
28
+ <BreadcrumbItem>dashboard</BreadcrumbItem>
29
+ <BreadcrumbSeparator />
30
+ <BreadcrumbItem>projects</BreadcrumbItem>
31
+ <BreadcrumbSeparator />
32
+ <BreadcrumbItem>storybook</BreadcrumbItem>
33
+ </Breadcrumb>
34
+ ),
35
+ };
36
+
37
+ /**
38
+ * Breadcrumb with home icon
39
+ */
40
+ export const WithHomeIcon: Story = {
41
+ render: () => (
42
+ <Breadcrumb>
43
+ <BreadcrumbHome />
44
+ <BreadcrumbSeparator />
45
+ <BreadcrumbItem>dashboard</BreadcrumbItem>
46
+ <BreadcrumbSeparator />
47
+ <BreadcrumbItem>settings</BreadcrumbItem>
48
+ </Breadcrumb>
49
+ ),
50
+ };
51
+
52
+ /**
53
+ * Long breadcrumb path to demonstrate horizontal scrolling
54
+ */
55
+ export const LongPath: Story = {
56
+ render: () => (
57
+ <Breadcrumb className="max-w-xs">
58
+ <BreadcrumbHome />
59
+ <BreadcrumbSeparator />
60
+ <BreadcrumbItem>dashboard</BreadcrumbItem>
61
+ <BreadcrumbSeparator />
62
+ <BreadcrumbItem>projects</BreadcrumbItem>
63
+ <BreadcrumbSeparator />
64
+ <BreadcrumbItem>frontend</BreadcrumbItem>
65
+ <BreadcrumbSeparator />
66
+ <BreadcrumbItem>components</BreadcrumbItem>
67
+ <BreadcrumbSeparator />
68
+ <BreadcrumbItem>breadcrumb</BreadcrumbItem>
69
+ </Breadcrumb>
70
+ ),
71
+ };
72
+
73
+ /**
74
+ * Custom styled breadcrumb
75
+ */
76
+ export const CustomStyled: Story = {
77
+ render: () => (
78
+ <Breadcrumb className="bg-muted border-dashed">
79
+ <BreadcrumbHome className="text-primary" />
80
+ <BreadcrumbSeparator className="text-primary/30" />
81
+ <BreadcrumbItem className="text-primary">home</BreadcrumbItem>
82
+ <BreadcrumbSeparator />
83
+ <BreadcrumbItem className="font-semibold">profile</BreadcrumbItem>
84
+ </Breadcrumb>
85
+ ),
86
+ };
@@ -0,0 +1,101 @@
1
+ import { cn } from "@/utils/cn";
2
+ import React from "react";
3
+
4
+ interface BreadcrumbProps extends React.HTMLAttributes<HTMLDivElement> {}
5
+
6
+ const Breadcrumb = React.forwardRef<HTMLDivElement, BreadcrumbProps>(
7
+ (props, ref) => {
8
+ const { className, children, ...rest } = props;
9
+
10
+ return (
11
+ <div
12
+ ref={ref}
13
+ {...rest}
14
+ className={cn(
15
+ "rounded-sm px-5 items-center border border-primary/15 bg-card w-full sm:w-fit overflow-x-auto flex gap-x-4",
16
+ className
17
+ )}
18
+ >
19
+ {children}
20
+ </div>
21
+ );
22
+ }
23
+ );
24
+
25
+ interface BreadcrumbSeparatorProps extends React.SVGProps<SVGSVGElement> {}
26
+
27
+ const BreadcrumbSeparator = React.forwardRef<
28
+ SVGSVGElement,
29
+ BreadcrumbSeparatorProps
30
+ >((props, ref) => {
31
+ const { className, children, ...rest } = props;
32
+
33
+ return (
34
+ <svg
35
+ ref={ref}
36
+ {...rest}
37
+ fill="currentColor"
38
+ viewBox="0 0 24 44"
39
+ preserveAspectRatio="none"
40
+ aria-hidden="true"
41
+ className={cn(
42
+ "text-primary/15 min-h-9 min-w-4 h-9 w-4 rtl:rotate-180",
43
+ className
44
+ )}
45
+ >
46
+ <path d="M.293 0l22 22-22 22h1.414l22-22-22-22H.293z"></path>
47
+ </svg>
48
+ );
49
+ });
50
+
51
+ interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLDivElement> {}
52
+
53
+ const BreadcrumbItem = React.forwardRef<HTMLDivElement, BreadcrumbItemProps>(
54
+ (props, ref) => {
55
+ const { className, children, ...rest } = props;
56
+
57
+ return (
58
+ <div
59
+ ref={ref}
60
+ {...rest}
61
+ className={cn(
62
+ "text-primary/70 rtl:pt-0.5 hover:text-primary text-nowrap capitalize cursor-pointer transition-colors duration-200 font-medium rtl:text-4 ltr:text-xs",
63
+ className
64
+ )}
65
+ >
66
+ {children}
67
+ </div>
68
+ );
69
+ }
70
+ );
71
+
72
+ interface BreadcrumbHomeProps extends React.SVGProps<SVGSVGElement> {}
73
+
74
+ const BreadcrumbHome = React.forwardRef<SVGSVGElement, BreadcrumbHomeProps>(
75
+ (props, ref) => {
76
+ const { className, children, ...rest } = props;
77
+
78
+ return (
79
+ <svg
80
+ ref={ref}
81
+ {...rest}
82
+ xmlns="http://www.w3.org/2000/svg"
83
+ viewBox="0 0 20 20"
84
+ // aria-hidden="true"
85
+ // data-slot="icon"
86
+ className={cn(
87
+ "text-primary/60 fill-primary/60 hover:scale-105 min-w-4 min-h-4 size-4 hover:fill-primary/90 transition-[fill] duration-300 cursor-pointer",
88
+ className
89
+ )}
90
+ >
91
+ <path
92
+ fillRule="evenodd"
93
+ d="M9.293 2.293a1 1 0 0 1 1.414 0l7 7A1 1 0 0 1 17 11h-1v6a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-6H3a1 1 0 0 1-.707-1.707l7-7Z"
94
+ clipRule="evenodd"
95
+ ></path>
96
+ </svg>
97
+ );
98
+ }
99
+ );
100
+
101
+ export { Breadcrumb, BreadcrumbSeparator, BreadcrumbItem, BreadcrumbHome };
@@ -0,0 +1,59 @@
1
+ // Pagination.stories.tsx
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import Pagination from "./pagination";
4
+
5
+ const meta: Meta<typeof Pagination> = {
6
+ title: "Components/Pagination",
7
+ component: Pagination,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ argTypes: {
12
+ lastPage: {
13
+ control: { type: "number", min: 1 },
14
+ },
15
+ onPageChange: {
16
+ action: "page changed",
17
+ },
18
+ },
19
+ };
20
+
21
+ export default meta;
22
+
23
+ type Story = StoryObj<typeof Pagination>;
24
+
25
+ /**
26
+ * Default pagination
27
+ */
28
+ export const Default: Story = {
29
+ args: {
30
+ lastPage: 10,
31
+ },
32
+ };
33
+
34
+ /**
35
+ * Small number of pages (no ellipsis)
36
+ */
37
+ export const SmallDataset: Story = {
38
+ args: {
39
+ lastPage: 5,
40
+ },
41
+ };
42
+
43
+ /**
44
+ * Large dataset with ellipsis behavior
45
+ */
46
+ export const LargeDataset: Story = {
47
+ args: {
48
+ lastPage: 50,
49
+ },
50
+ };
51
+
52
+ /**
53
+ * Single page (navigation disabled)
54
+ */
55
+ export const SinglePage: Story = {
56
+ args: {
57
+ lastPage: 1,
58
+ },
59
+ };
@@ -0,0 +1,109 @@
1
+ import { ChevronLeft, ChevronRight } from "lucide-react";
2
+ import { useState } from "react";
3
+
4
+ interface PaginationProps {
5
+ lastPage: number;
6
+ onPageChange: (page: number) => void;
7
+ }
8
+ const Pagination: React.FC<PaginationProps> = ({ lastPage, onPageChange }) => {
9
+ const [currentPage, setCurrentPage] = useState<number>(1);
10
+ const handlePrevPage = () => {
11
+ if (currentPage > 1) {
12
+ setCurrentPage(currentPage - 1);
13
+ onPageChange(currentPage - 1);
14
+ }
15
+ };
16
+
17
+ const handleNextPage = () => {
18
+ if (currentPage < lastPage) {
19
+ setCurrentPage(currentPage + 1);
20
+ onPageChange(currentPage + 1);
21
+ }
22
+ };
23
+
24
+ const handlePageClick = (page: number) => {
25
+ setCurrentPage(page);
26
+ onPageChange(page);
27
+ };
28
+
29
+ // Function to generate pagination numbers with ellipsis for large datasets
30
+ const generatePageNumbers = () => {
31
+ const pages: (number | string)[] = [];
32
+ const siblingCount = 1; // Number of pages to show around current page
33
+
34
+ if (lastPage <= 7) {
35
+ // If less than 7 pages, show all pages
36
+ for (let i = 1; i <= lastPage; i++) {
37
+ pages.push(i);
38
+ }
39
+ } else {
40
+ // Always show first, last, and nearby pages
41
+ pages.push(1);
42
+ if (currentPage > siblingCount + 2) pages.push("...");
43
+ for (
44
+ let i = Math.max(2, currentPage - siblingCount);
45
+ i <= Math.min(lastPage - 1, currentPage + siblingCount);
46
+ i++
47
+ ) {
48
+ pages.push(i);
49
+ }
50
+ if (currentPage < lastPage - siblingCount - 1) pages.push("...");
51
+ pages.push(lastPage);
52
+ }
53
+
54
+ return pages;
55
+ };
56
+
57
+ const pageNumbers = generatePageNumbers();
58
+
59
+ return (
60
+ <div className="flex justify-center items-center select-none h-7 text-sm gap-x-1 text-primary-foreground">
61
+ {/* Previous Button */}
62
+ <ChevronLeft
63
+ onClick={() => {
64
+ if (currentPage !== 1) handlePrevPage();
65
+ }}
66
+ className={`size-8 rounded transition-[color,background-color,transform] cursor-pointer rtl:rotate-180 self-center p-2 ${
67
+ currentPage === 1
68
+ ? "text-primary/50"
69
+ : "text-primary hover:scale-110 hover:bg-fourth/10"
70
+ }`}
71
+ />
72
+ {/* Page Numbers */}
73
+ {pageNumbers.map((page, index) =>
74
+ typeof page === "number" ? (
75
+ <button
76
+ key={index}
77
+ onClick={() => handlePageClick(page)}
78
+ className={`size-8 cursor-pointer rounded transition-colors ${
79
+ currentPage === page
80
+ ? "bg-fourth text-primary-foreground text-sm"
81
+ : "text-secondary-foreground hover:bg-fourth/10 transition-transform hover:scale-105 text-xs font-semibold"
82
+ }`}
83
+ >
84
+ {page}
85
+ </button>
86
+ ) : (
87
+ <span key={index} className="px-4 py-2 text-primary">
88
+ ...
89
+ </span>
90
+ )
91
+ )}
92
+
93
+ {/* Next Button */}
94
+
95
+ <ChevronRight
96
+ onClick={() => {
97
+ if (currentPage !== lastPage) handleNextPage();
98
+ }}
99
+ className={`size-8 rounded transition-[color,background-color,transform] cursor-pointer rtl:rotate-180 self-center p-2 ${
100
+ currentPage !== lastPage
101
+ ? "text-primary hover:scale-110 hover:bg-fourth/10"
102
+ : "text-primary/50"
103
+ }`}
104
+ />
105
+ </div>
106
+ );
107
+ };
108
+
109
+ export default Pagination;