@stackshift-ui/button 6.0.2 → 6.0.3

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.
@@ -0,0 +1 @@
1
+ import{a as f}from"./chunk-5BNLLVMY.mjs";import{Link as V}from"@stackshift-ui/link";import{useStackShiftUIComponents as _}from"@stackshift-ui/system";import d from"classnames";import{FaSpinner as E}from"react-icons/fa";import{jsx as u}from"react/jsx-runtime";var h="Button";function q({children:p,...o}){let{[h]:k="button"}=_(),L={xs:"py-1 px-3 text-xs",sm:"py-2 px-4 text-sm",default:"py-3 px-6 text-default",md:"py-3 px-6 text-base",lg:"py-4 px-7 text-lg"},$={none:"rounded-none",sm:"rounded-sm",md:"rounded-md",base:"rounded-base",lg:"rounded-lg",xl:"rounded-xl","2xl":"rounded-2xl",full:"rounded-full",global:"rounded-global"},{radius:m,size:r,variant:a,ariaLabel:c,className:b,isActive:y}=o,t=m?$[m]:"rounded-global",e=L[r!=null?r:"default"],n="inline-block font-default text-default transition duration-200",T=`${n} ${e} ${t} bg-primary hover:bg-primary/50 text-gray-50`,v=`inline-block bg-primary hover:bg-primary/50 ${e} ${t} text-gray-50 font-bold transition duration-200`,S=`${n} ${e} ${t} bg-white hover:bg-primary/50 outline outline-1 text-primary outline-primary`,B=`${n} ${t} ${e} bg-transparent hover:bg-primary/50 text-primary`,R=`transition-200 text-primary hover:text-primary/50 underline ${t} ${d(e,"px-0 py-0")}`,A="",w=`mr-1 ${y?"bg-primary":"bg-gray-200"} rounded-full p-1 focus:outline-none`,N=`mx-auto mb-1 w-auto px-4 py-2 rounded duration-200 transition focus:outline-none font-bold ${y?"bg-gray-50 text-primary shadow":"text-gray-700 hover:bg-secondary/50 hover:text-primary hover:shadow"}`,z=`${n} ${t} ${e} ml-auto sm:ml-0 flex-shrink-0 inline-flex items-center justify-center w-full rounded-md border hover:border-primary`,x={outline:S,ghost:B,link:R,custom:v,solid:T,addToWishlist:z,unstyled:A,swiper_pagination:w,tab:N}[a!=null?a:"solid"];if(o.as==="link"){let{link:l,...H}=o;return u(V,{className:d(x,b),"aria-label":c,href:f(l),target:l.linkTarget,rel:l.linkTarget==="_blank"?"noopener noreferrer":"",...H,children:p})}let{loadingComponent:i,onClick:C,loading:g,disabled:s,type:M}=o,P=i!=null?i:u(E,{className:"animate-spin",size:30});return u(k,{onClick:C,disabled:s!=null?s:g,className:d(x,b),"aria-label":c,"data-testid":h.toLowerCase(),type:M,children:g?P:p})}export{q as a};
@@ -0,0 +1 @@
1
+ import{a as f}from"./chunk-5BNLLVMY.mjs";import{Link as V}from"@stackshift-ui/link";import{useStackShiftUIComponents as _}from"@stackshift-ui/system";import r from"classnames";import{FaSpinner as E}from"react-icons/fa";import{jsx as u}from"react/jsx-runtime";var h="Button";function q({children:p,...o}){let{[h]:k="button"}=_(),L={xs:"py-1 px-3 text-xs",sm:"py-2 px-4 text-sm",default:"py-3 px-6 text-default",md:"py-3 px-6 text-base",lg:"py-4 px-7 text-lg"},$={none:"rounded-none",sm:"rounded-sm",md:"rounded-md",base:"rounded-base",lg:"rounded-lg",xl:"rounded-xl","2xl":"rounded-2xl",full:"rounded-full",global:"rounded-global"},{radius:m,size:a,variant:i,ariaLabel:c,className:b,isActive:y}=o,t=m?$[m]:"rounded-global",e=L[a!=null?a:"default"],n="inline-block font-default text-default transition duration-200",T=`${n} ${e} ${t} bg-primary hover:bg-primary/50 text-gray-50`,v=`inline-block bg-primary hover:bg-primary/50 ${e} ${t} text-gray-50 font-bold transition duration-200`,S=`${n} ${e} ${t} bg-white hover:bg-primary/50 outline outline-1 text-primary outline-primary`,B=`${n} ${t} ${e} bg-transparent hover:bg-primary/50 text-primary`,R=`transition-200 text-primary hover:text-primary/50 underline ${t} ${r(e,"px-0 py-0")}`,A="",w=`mr-1 ${y?"bg-primary":"bg-gray-200"} rounded-full p-1 focus:outline-none`,N=`mx-auto mb-1 w-auto px-4 py-2 rounded duration-200 transition focus:outline-none font-bold ${y?"bg-gray-50 text-primary shadow":"text-gray-700 hover:bg-secondary/50 hover:text-primary hover:shadow"}`,z=`${n} ${t} ${e} ml-auto sm:ml-0 flex-shrink-0 inline-flex items-center justify-center w-full rounded-md border hover:border-primary`,x=r({outline:S,ghost:B,link:R,custom:v,solid:T,addToWishlist:z,unstyled:A,swiper_pagination:w,tab:N}[i!=null?i:"solid"],t);if(o.as==="link"){let{link:d,...H}=o;return u(V,{className:r(b,x),"aria-label":c,href:f(d),target:d.linkTarget,rel:d.linkTarget==="_blank"?"noopener noreferrer":"",...H,children:p})}let{loadingComponent:s,onClick:C,loading:g,disabled:l,type:M}=o,P=s!=null?s:u(E,{className:"animate-spin",size:30});return u(k,{onClick:C,disabled:l!=null?l:g,className:r(x,b),"aria-label":c,"data-testid":h.toLowerCase(),type:M,children:g?P:p})}export{q as a};
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "@stackshift-ui/button",
3
- "version": "6.0.2",
3
+ "version": "6.0.3",
4
4
  "private": false,
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
7
7
  "module": "./dist/index.mjs",
8
8
  "types": "./dist/index.d.ts",
9
9
  "files": [
10
- "dist/**"
10
+ "dist/**",
11
+ "src"
11
12
  ],
12
13
  "devDependencies": {
13
14
  "@testing-library/react": "^16.0.1",
@@ -32,9 +33,9 @@
32
33
  "dependencies": {
33
34
  "classnames": "^2.5.1",
34
35
  "react-icons": "^5.3.0",
35
- "@stackshift-ui/system": "6.0.2",
36
- "@stackshift-ui/link": "6.0.2",
37
- "@stackshift-ui/scripts": "6.0.2"
36
+ "@stackshift-ui/link": "6.0.3",
37
+ "@stackshift-ui/scripts": "6.0.2",
38
+ "@stackshift-ui/system": "6.0.3"
38
39
  },
39
40
  "peerDependencies": {
40
41
  "@types/react": "16.8 - 19",
@@ -0,0 +1,13 @@
1
+ import { cleanup, render, screen } from "@testing-library/react";
2
+ import { afterEach, describe, test } from "vitest";
3
+ import { Button } from "./button";
4
+
5
+ describe.concurrent("button", () => {
6
+ afterEach(cleanup);
7
+
8
+ test("Common: Button - test if renders without errors", ({ expect }) => {
9
+ const clx = "button-class";
10
+ render(<Button className={clx} />);
11
+ expect(screen.getByTestId("button").classList).toContain(clx);
12
+ });
13
+ });
package/src/button.tsx ADDED
@@ -0,0 +1,135 @@
1
+ import { Link } from "@stackshift-ui/link";
2
+ import { useStackShiftUIComponents } from "@stackshift-ui/system";
3
+ import cn from "classnames";
4
+ import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from "react";
5
+ import { FaSpinner } from "react-icons/fa";
6
+ import { extractLink } from "./helper";
7
+ import { type LabeledRoute, type StyleVariants } from "./types";
8
+
9
+ type Variant =
10
+ | "outline"
11
+ | "ghost"
12
+ | "link"
13
+ | "custom"
14
+ | "solid"
15
+ | "addToWishlist"
16
+ | "unstyled"
17
+ | "swiper_pagination"
18
+ | "tab";
19
+ type TextSize = "xs" | "sm" | "md" | "lg";
20
+ type RadiusSize = "none" | "sm" | "md" | "base" | "lg" | "xl" | "2xl" | "full";
21
+
22
+ interface BaseType {
23
+ children?: ReactNode;
24
+ variant?: Variant;
25
+ radius?: RadiusSize;
26
+ size?: TextSize;
27
+ ariaLabel?: string;
28
+ isActive?: boolean;
29
+ }
30
+
31
+ interface LinkProps extends BaseType, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "type"> {
32
+ link: LabeledRoute;
33
+ as: "link";
34
+ }
35
+
36
+ interface ButtonProps extends BaseType, ButtonHTMLAttributes<HTMLButtonElement> {
37
+ as?: "button";
38
+ loading?: boolean;
39
+ disabled?: boolean;
40
+ loadingComponent?: React.ReactNode;
41
+ }
42
+
43
+ type Props = ButtonProps | LinkProps;
44
+
45
+ const displayName = "Button";
46
+
47
+ export function Button({ children, ...props }: Props) {
48
+ const { [displayName]: Component = "button" } = useStackShiftUIComponents();
49
+
50
+ const sizes = {
51
+ xs: "py-1 px-3 text-xs",
52
+ sm: "py-2 px-4 text-sm",
53
+ default: "py-3 px-6 text-default",
54
+ md: "py-3 px-6 text-base",
55
+ lg: "py-4 px-7 text-lg",
56
+ };
57
+
58
+ const borderRadiusMap = {
59
+ none: "rounded-none",
60
+ sm: "rounded-sm",
61
+ md: "rounded-md",
62
+ base: "rounded-base",
63
+ lg: "rounded-lg",
64
+ xl: "rounded-xl",
65
+ "2xl": "rounded-2xl",
66
+ full: "rounded-full",
67
+ global: "rounded-global",
68
+ };
69
+
70
+ const { radius, size, variant, ariaLabel, className, isActive } = props;
71
+
72
+ const buttonRadius = radius ? borderRadiusMap[radius] : "rounded-global";
73
+ const buttonSize = sizes[size ?? "default"];
74
+
75
+ const commonStyles = "inline-block font-default text-default transition duration-200";
76
+ const solid = `${commonStyles} ${buttonSize} ${buttonRadius} bg-primary hover:bg-primary/50 text-gray-50`;
77
+ const custom = `inline-block bg-primary hover:bg-primary/50 ${buttonSize} ${buttonRadius} text-gray-50 font-bold transition duration-200`;
78
+ const outline = `${commonStyles} ${buttonSize} ${buttonRadius} bg-white hover:bg-primary/50 outline outline-1 text-primary outline-primary`;
79
+ const ghost = `${commonStyles} ${buttonRadius} ${buttonSize} bg-transparent hover:bg-primary/50 text-primary`;
80
+ const linkType = `transition-200 text-primary hover:text-primary/50 underline ${buttonRadius} ${cn(buttonSize, "px-0 py-0")}`;
81
+ const unstyled = ``;
82
+ const swiper_pagination = `mr-1 ${isActive ? "bg-primary" : "bg-gray-200"} rounded-full p-1 focus:outline-none`;
83
+ const tab = `mx-auto mb-1 w-auto px-4 py-2 rounded duration-200 transition focus:outline-none font-bold ${
84
+ isActive
85
+ ? "bg-gray-50 text-primary shadow"
86
+ : "text-gray-700 hover:bg-secondary/50 hover:text-primary hover:shadow"
87
+ }`;
88
+ const addToWishlist = `${commonStyles} ${buttonRadius} ${buttonSize} ml-auto sm:ml-0 flex-shrink-0 inline-flex items-center justify-center w-full rounded-md border hover:border-primary`;
89
+
90
+ const variants: StyleVariants<Variant> = {
91
+ outline,
92
+ ghost,
93
+ link: linkType,
94
+ custom,
95
+ solid,
96
+ addToWishlist,
97
+ unstyled,
98
+ swiper_pagination,
99
+ tab,
100
+ };
101
+
102
+ const variantClass = variants[variant ?? "solid"];
103
+
104
+ if (props.as === "link") {
105
+ const { link, ...rest } = props as LinkProps;
106
+
107
+ return (
108
+ <Link
109
+ className={cn(variantClass, className)}
110
+ aria-label={ariaLabel}
111
+ href={extractLink(link)}
112
+ target={link.linkTarget}
113
+ rel={link.linkTarget === "_blank" ? "noopener noreferrer" : ""}
114
+ {...rest}>
115
+ {children}
116
+ </Link>
117
+ );
118
+ }
119
+
120
+ const { loadingComponent, onClick, loading, disabled, type } = props;
121
+
122
+ const Loader = loadingComponent ?? <FaSpinner className="animate-spin" size={30} />;
123
+
124
+ return (
125
+ <Component
126
+ onClick={onClick}
127
+ disabled={disabled ?? loading}
128
+ className={cn(variantClass, className)}
129
+ aria-label={ariaLabel}
130
+ data-testid={displayName.toLowerCase()}
131
+ type={type}>
132
+ {loading ? Loader : children}
133
+ </Component>
134
+ );
135
+ }
@@ -0,0 +1,25 @@
1
+ import { LabeledRoute } from "../types";
2
+
3
+ export const extractLink = (link: LabeledRoute) => {
4
+ //not found page
5
+ if (!link?.internalLink && !link?.externalLink) {
6
+ return "/page-not-found";
7
+ }
8
+
9
+ //home page
10
+ if (link?.type === "linkInternal" && link?.internalLink?.toLowerCase()?.includes("home")) {
11
+ return "/";
12
+ }
13
+
14
+ //internal link
15
+ if (link?.type === "linkInternal") {
16
+ return `/${link?.internalLink}`;
17
+ }
18
+
19
+ //external link
20
+ if (link?.type === "linkExternal") {
21
+ return `${link?.externalLink}`;
22
+ }
23
+
24
+ return "/";
25
+ };
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ "use client";
2
+
3
+ // component exports
4
+ export * from "./button";
package/src/types.ts ADDED
@@ -0,0 +1,16 @@
1
+ export type StyleVariants<T extends string> = Record<T, string>;
2
+
3
+ export interface LabeledRoute extends ConditionalLink {
4
+ ariaLabel?: string;
5
+ label?: string;
6
+ linkTarget?: string;
7
+ linkType?: string;
8
+ _type?: string;
9
+ linkInternal?: any;
10
+ }
11
+
12
+ export interface ConditionalLink {
13
+ type?: string;
14
+ internalLink?: string | null;
15
+ externalLink?: string | null;
16
+ }