@tapcart/mobile-components 0.1.1

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,191 @@
1
+ "use client"
2
+
3
+ // Inspired by react-hot-toast library
4
+ import * as React from "react"
5
+
6
+ import type { ToastActionElement, ToastProps } from "@/components/ui/toast"
7
+
8
+ const TOAST_LIMIT = 1
9
+ const TOAST_REMOVE_DELAY = 1000000
10
+
11
+ type ToasterToast = ToastProps & {
12
+ id: string
13
+ title?: React.ReactNode
14
+ description?: React.ReactNode
15
+ action?: ToastActionElement
16
+ }
17
+
18
+ const actionTypes = {
19
+ ADD_TOAST: "ADD_TOAST",
20
+ UPDATE_TOAST: "UPDATE_TOAST",
21
+ DISMISS_TOAST: "DISMISS_TOAST",
22
+ REMOVE_TOAST: "REMOVE_TOAST",
23
+ } as const
24
+
25
+ let count = 0
26
+
27
+ function genId() {
28
+ count = (count + 1) % Number.MAX_SAFE_INTEGER
29
+ return count.toString()
30
+ }
31
+
32
+ type ActionType = typeof actionTypes
33
+
34
+ type Action =
35
+ | {
36
+ type: ActionType["ADD_TOAST"]
37
+ toast: ToasterToast
38
+ }
39
+ | {
40
+ type: ActionType["UPDATE_TOAST"]
41
+ toast: Partial<ToasterToast>
42
+ }
43
+ | {
44
+ type: ActionType["DISMISS_TOAST"]
45
+ toastId?: ToasterToast["id"]
46
+ }
47
+ | {
48
+ type: ActionType["REMOVE_TOAST"]
49
+ toastId?: ToasterToast["id"]
50
+ }
51
+
52
+ interface State {
53
+ toasts: ToasterToast[]
54
+ }
55
+
56
+ const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
57
+
58
+ const addToRemoveQueue = (toastId: string) => {
59
+ if (toastTimeouts.has(toastId)) {
60
+ return
61
+ }
62
+
63
+ const timeout = setTimeout(() => {
64
+ toastTimeouts.delete(toastId)
65
+ dispatch({
66
+ type: "REMOVE_TOAST",
67
+ toastId: toastId,
68
+ })
69
+ }, TOAST_REMOVE_DELAY)
70
+
71
+ toastTimeouts.set(toastId, timeout)
72
+ }
73
+
74
+ export const reducer = (state: State, action: Action): State => {
75
+ switch (action.type) {
76
+ case "ADD_TOAST":
77
+ return {
78
+ ...state,
79
+ toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
80
+ }
81
+
82
+ case "UPDATE_TOAST":
83
+ return {
84
+ ...state,
85
+ toasts: state.toasts.map((t) =>
86
+ t.id === action.toast.id ? { ...t, ...action.toast } : t
87
+ ),
88
+ }
89
+
90
+ case "DISMISS_TOAST": {
91
+ const { toastId } = action
92
+
93
+ // ! Side effects ! - This could be extracted into a dismissToast() action,
94
+ // but I'll keep it here for simplicity
95
+ if (toastId) {
96
+ addToRemoveQueue(toastId)
97
+ } else {
98
+ state.toasts.forEach((toast) => {
99
+ addToRemoveQueue(toast.id)
100
+ })
101
+ }
102
+
103
+ return {
104
+ ...state,
105
+ toasts: state.toasts.map((t) =>
106
+ t.id === toastId || toastId === undefined
107
+ ? {
108
+ ...t,
109
+ open: false,
110
+ }
111
+ : t
112
+ ),
113
+ }
114
+ }
115
+ case "REMOVE_TOAST":
116
+ if (action.toastId === undefined) {
117
+ return {
118
+ ...state,
119
+ toasts: [],
120
+ }
121
+ }
122
+ return {
123
+ ...state,
124
+ toasts: state.toasts.filter((t) => t.id !== action.toastId),
125
+ }
126
+ }
127
+ }
128
+
129
+ const listeners: Array<(state: State) => void> = []
130
+
131
+ let memoryState: State = { toasts: [] }
132
+
133
+ function dispatch(action: Action) {
134
+ memoryState = reducer(memoryState, action)
135
+ listeners.forEach((listener) => {
136
+ listener(memoryState)
137
+ })
138
+ }
139
+
140
+ type Toast = Omit<ToasterToast, "id">
141
+
142
+ function toast({ ...props }: Toast) {
143
+ const id = genId()
144
+
145
+ const update = (props: ToasterToast) =>
146
+ dispatch({
147
+ type: "UPDATE_TOAST",
148
+ toast: { ...props, id },
149
+ })
150
+ const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
151
+
152
+ dispatch({
153
+ type: "ADD_TOAST",
154
+ toast: {
155
+ ...props,
156
+ id,
157
+ open: true,
158
+ onOpenChange: (open) => {
159
+ if (!open) dismiss()
160
+ },
161
+ },
162
+ })
163
+
164
+ return {
165
+ id: id,
166
+ dismiss,
167
+ update,
168
+ }
169
+ }
170
+
171
+ function useToast() {
172
+ const [state, setState] = React.useState<State>(memoryState)
173
+
174
+ React.useEffect(() => {
175
+ listeners.push(setState)
176
+ return () => {
177
+ const index = listeners.indexOf(setState)
178
+ if (index > -1) {
179
+ listeners.splice(index, 1)
180
+ }
181
+ }
182
+ }, [state])
183
+
184
+ return {
185
+ ...state,
186
+ toast,
187
+ dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
188
+ }
189
+ }
190
+
191
+ export { useToast, toast }
@@ -0,0 +1,27 @@
1
+ import * as React from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+
4
+ import { cn } from "@/lib/utils"
5
+
6
+ const videoVariants = cva("w-full", {
7
+ variants: {},
8
+ })
9
+
10
+ export interface VideoProps
11
+ extends React.HTMLAttributes<HTMLVideoElement>,
12
+ VariantProps<typeof videoVariants> {}
13
+
14
+ function Video({ className, ...props }: VideoProps) {
15
+ return (
16
+ <video
17
+ className={cn(videoVariants({}), className)}
18
+ {...props}
19
+ autoPlay
20
+ playsInline
21
+ muted
22
+ loop
23
+ />
24
+ )
25
+ }
26
+
27
+ export { Video, videoVariants }
@@ -0,0 +1,14 @@
1
+ {
2
+ "style": "default",
3
+ "rsc": true,
4
+ "tailwind": {
5
+ "config": "tailwind.config.js",
6
+ "css": "styles/globals.css",
7
+ "baseColor": "slate",
8
+ "cssVariables": true
9
+ },
10
+ "aliases": {
11
+ "components": "@/components",
12
+ "utils": "@/lib/utils"
13
+ }
14
+ }
package/index.tsx ADDED
@@ -0,0 +1,28 @@
1
+ import * as React from "react"
2
+
3
+ // component exports
4
+ export * from "@/components/ui/button"
5
+ export * from "@/components/ui/input"
6
+ export * from "@/components/ui/aspect-ratio"
7
+ export * from "@/components/ui/accordion"
8
+ export * from "@/components/ui/icon"
9
+ export * from "@/components/ui/carousel"
10
+ export * from "@/components/ui/container"
11
+ export * from "@/components/ui/grid"
12
+ export * from "@/components/ui/label"
13
+ export * from "@/components/ui/separator"
14
+ export * from "@/components/ui/badge"
15
+ export * from "@/components/ui/video"
16
+ export * from "@/components/ui/money"
17
+ export * from "@/components/ui/skeleton"
18
+ export * from "@/components/ui/text"
19
+ export * from "@/components/ui/toggle"
20
+ export * from "@/components/ui/toggle-group"
21
+ export * from "@/components/ui/switch"
22
+ export * from "@/components/ui/scroll-area"
23
+ export * from "@/components/ui/toast"
24
+ export * from "@/components/ui/toaster"
25
+ export * from "@/components/ui/use-toast"
26
+ export * from "@/components/ThemeToggle"
27
+ export { ThemeProvider } from "@/components/ThemeProvider"
28
+ export { cn } from "@/lib/utils"
package/lib/utils.ts ADDED
@@ -0,0 +1,6 @@
1
+ import { ClassValue, clsx } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@tapcart/mobile-components",
3
+ "version": "0.1.1",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "license": "SEE LICENSE IN LICENSE.md",
7
+ "author": "Tapcart Inc.",
8
+ "homepage": "https://tapcart.com",
9
+ "scripts": {
10
+ "lint": "eslint \"**/*.ts*\"",
11
+ "ui:add": "pnpm dlx shadcn-ui@latest add",
12
+ "build": "tsc -p tsconfig.json"
13
+ },
14
+ "devDependencies": {
15
+ "@types/react": "^18.2.0",
16
+ "@types/react-dom": "^18.2.0",
17
+ "autoprefixer": "^10.4.14",
18
+ "eslint": "^7.32.0",
19
+ "eslint-config-custom": "workspace:*",
20
+ "postcss": "^8.4.24",
21
+ "react": "^17.0.2",
22
+ "tailwindcss": "^3.3.2",
23
+ "tsconfig": "workspace:*",
24
+ "typescript": "^4.5.2"
25
+ },
26
+ "dependencies": {
27
+ "@radix-ui/react-accordion": "^1.1.2",
28
+ "@radix-ui/react-alert-dialog": "^1.0.5",
29
+ "@radix-ui/react-aspect-ratio": "^1.0.3",
30
+ "@radix-ui/react-label": "^2.0.2",
31
+ "@radix-ui/react-scroll-area": "^1.0.5",
32
+ "@radix-ui/react-separator": "^1.0.3",
33
+ "@radix-ui/react-slot": "^1.0.2",
34
+ "@radix-ui/react-switch": "^1.0.3",
35
+ "@radix-ui/react-toast": "^1.1.5",
36
+ "@radix-ui/react-toggle": "^1.0.3",
37
+ "@radix-ui/react-toggle-group": "^1.0.4",
38
+ "@tabler/icons-react": "^3.2.0",
39
+ "class-variance-authority": "^0.6.0",
40
+ "clsx": "^1.2.1",
41
+ "embla-carousel-react": "^8.0.2",
42
+ "lucide-react": "^0.248.0",
43
+ "next-themes": "^0.2.1",
44
+ "tailwind-merge": "^1.13.2",
45
+ "tailwindcss-animate": "^1.0.6"
46
+ }
47
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ }
@@ -0,0 +1,146 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ *::-webkit-scrollbar {
7
+ display: none;
8
+ }
9
+ * {
10
+ -ms-overflow-style: none; /* IE and Edge */
11
+ scrollbar-width: none; /* Firefox */
12
+ }
13
+
14
+ :root {
15
+ --background: 0 0% 100%;
16
+ --foreground: 222.2 47.4% 11.2%;
17
+
18
+ --muted: 210 40% 96.1%;
19
+ --muted-foreground: 215.4 16.3% 46.9%;
20
+
21
+ --popover: 0 0% 100%;
22
+ --popover-foreground: 222.2 47.4% 11.2%;
23
+
24
+ --card: 0 0% 100%;
25
+ --card-foreground: 222.2 47.4% 11.2%;
26
+
27
+ --border: 214.3 31.8% 91.4%;
28
+ --input: 214.3 31.8% 91.4%;
29
+
30
+ --primary: 222.2 47.4% 11.2%;
31
+ --primary-foreground: 210 40% 98%;
32
+
33
+ --secondary: 210 40% 96.1%;
34
+ --secondary-foreground: 222.2 47.4% 11.2%;
35
+
36
+ --accent: 210 40% 96.1%;
37
+ --accent-foreground: 222.2 47.4% 11.2%;
38
+
39
+ --destructive: 0 100% 50%;
40
+ --destructive-foreground: 210 40% 98%;
41
+
42
+ --ring: 215 20.2% 65.1%;
43
+
44
+ --radius: 0.5rem;
45
+
46
+ --coreColors-pageColor: #ffffff;
47
+ --coreColors-shadow: #000000;
48
+ --coreColors-brandColorPrimary: #000000;
49
+ --coreColors-headerBackground: #ffffffff;
50
+ --coreColors-inputBackground: #ffffffff;
51
+ --coreColors-modalBackground: #ffffffff;
52
+ --coreColors-tabBar: #ffffffff;
53
+ --coreColors-dividingLines: #e3e3e3ff;
54
+ --coreColors-shadowsEnabled: "true";
55
+ --coreColors-primaryIcon: #121212ff;
56
+ --coreColors-secondaryIcon: #727272ff;
57
+ --coreColors-headerIcon: #121212ff;
58
+
59
+ --textColors-primaryColor: #121212ff;
60
+ --textColors-secondaryColor: #727272ff;
61
+ --textColors-pageTitle: #121212ff;
62
+ --textColors-legalText: #727272;
63
+ --textColors-productTitle: #727272;
64
+ --textColors-priceText: #121212ff;
65
+ --textColors-strikethroughPriceText: #727272ff;
66
+ --textColors-salePriceText: #d91e18ff;
67
+
68
+ --buttonColors-primaryText: #ffffff;
69
+ --buttonColors-primaryFill: #000000;
70
+ --buttonColors-primaryOutline: #000000;
71
+ --buttonColors-primaryShadow: #ffffff;
72
+ --buttonColors-disabled: #707070;
73
+ --buttonColors-secondaryText: #000000;
74
+ --buttonColors-secondaryFill: #ffffff;
75
+ --buttonColors-secondaryOutline: #000000;
76
+ --buttonColors-secondaryShadow: #ffffff;
77
+
78
+ --stateColors-disabled: #707070;
79
+ --stateColors-error: #d91e18ff;
80
+
81
+ --stateColors-subscriptions: #008000ff;
82
+ --stateColors-favorites: #d91e18ff;
83
+ --stateColors-reviews: #ffaf02ff;
84
+ --stateColors-success: #008000ff;
85
+ --stateColors-warning: #ffaf02ff;
86
+ --stateColors-skeleton: #e3e3e3ff;
87
+
88
+ --productImage-aspectRatio: "2:3";
89
+ --productImage-scaling: cover;
90
+ --productImage-isCustom: "false";
91
+ }
92
+ .productImages-scaling {
93
+ object-fit: var(--productImage-scaling);
94
+ }
95
+
96
+ /*.dark {*/
97
+ /* --background: 222.2 84% 4.9%;*/
98
+ /* --foreground: 210 40% 98%;*/
99
+ /* --card: 222.2 84% 4.9%;*/
100
+ /* --card-foreground: 210 40% 98%;*/
101
+ /* --popover: 222.2 84% 4.9%;*/
102
+ /* --popover-foreground: 210 40% 98%;*/
103
+ /* --primary: 210 40% 98%;*/
104
+ /* --primary-foreground: 222.2 47.4% 11.2%;*/
105
+ /* --secondary: 217.2 32.6% 17.5%;*/
106
+ /* --secondary-foreground: 210 40% 98%;*/
107
+ /* --muted: 217.2 32.6% 17.5%;*/
108
+ /* --muted-foreground: 215 20.2% 65.1%;*/
109
+ /* --accent: 217.2 32.6% 17.5%;*/
110
+ /* --accent-foreground: 210 40% 98%;*/
111
+ /* --destructive: 0 62.8% 30.6%;*/
112
+ /* --destructive-foreground: 210 40% 98%;*/
113
+ /* --border: 217.2 32.6% 17.5%;*/
114
+ /* --input: 217.2 32.6% 17.5%;*/
115
+ /* --ring: 212.7 26.8% 83.9;*/
116
+ /*}*/
117
+ }
118
+
119
+ @layer base {
120
+ * {
121
+ @apply border-border;
122
+ }
123
+ body {
124
+ @apply bg-background text-foreground;
125
+ font-feature-settings: "rlig" 1, "calt" 1;
126
+ }
127
+ }
128
+
129
+ @layer utilities {
130
+ /* Hide scrollbar for Chrome, Safari and Opera */
131
+ .no-scrollbar *::-webkit-scrollbar {
132
+ display: none;
133
+ }
134
+ /* Hide scrollbar for IE, Edge and Firefox */
135
+ .no-scrollbar * {
136
+ -ms-overflow-style: none; /* IE and Edge */
137
+ scrollbar-width: none; /* Firefox */
138
+ }
139
+ .container {
140
+ padding-right: 16px;
141
+ padding-left: 16px;
142
+ }
143
+ *:hover {
144
+ text-decoration-line: unset !important;
145
+ }
146
+ }
@@ -0,0 +1,141 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ module.exports = {
3
+ darkMode: ["class"],
4
+ content: [
5
+ "./pages/**/*.{ts,tsx}",
6
+ "./components/**/*.{ts,tsx}",
7
+ "./app/**/*.{ts,tsx}",
8
+ "./src/**/*.{ts,tsx}",
9
+ "../../packages/tapcart-mobile-components/components/**/*.{ts,tsx}",
10
+ ],
11
+ theme: {
12
+ container: {
13
+ center: true,
14
+ padding: "2rem",
15
+ screens: {
16
+ "2xl": "1400px",
17
+ },
18
+ fontSize: {
19
+ xs: ["10px", "1.3"], //label
20
+ sm: ["12px", "1.3"], //paragraph
21
+ base: ["15px", "1.6"], //Body
22
+ lg: ["20px", "28px"],
23
+ xl: ["18px", "1.3"], //H2
24
+ "2xl": ["23px", "1.3"], //H1
25
+ },
26
+ },
27
+ extend: {
28
+ colors: {
29
+ coreColors: {
30
+ brandColorPrimary: "var(--coreColors-brandColorPrimary)",
31
+ pageColor: "var(--coreColors-pageColor)",
32
+ headerBackground: "var(--coreColors-headerBackground)",
33
+ inputBackground: "var(--coreColors-inputBackground)",
34
+ modalBackground: "var(--coreColors-modalBackground)",
35
+ tabBar: "var(--coreColors-tabBar)",
36
+ dividingLines: "var(--coreColors-dividingLines)",
37
+ shadowsEnabled: "var(--coreColors-shadowsEnabled)",
38
+ primaryIcon: "var(--coreColors-primaryIcon)",
39
+ secondaryIcon: "var(--coreColors-secondaryIcon)",
40
+ headerIcon: "var(--coreColors-headerIcon)",
41
+ shadow: "var(--coreColors-shadow)", // TODO: add to themesV2 API
42
+ },
43
+ buttonColors: {
44
+ primaryFill: "var(--buttonColors-primaryFill)",
45
+ primaryText: "var(--buttonColors-primaryText)",
46
+ primaryOutlineEnabled: "var(--buttonColors-primaryOutlineEnabled)",
47
+ primaryOutlineColor: "var(--buttonColors-primaryOutlineColor)",
48
+ primaryShadowEnabled: "var(--buttonColors-primaryShadowEnabled)",
49
+ secondaryFill: "var(--buttonColors-secondaryFill)",
50
+ secondaryText: "var(--buttonColors-secondaryText)",
51
+ secondaryOutlineEnabled:
52
+ "var(--buttonColors-secondaryOutlineEnabled)",
53
+ secondaryOutlineColor: "var(--buttonColors-secondaryOutlineColor)",
54
+ secondaryShadowEnabled: "var(--buttonColors-secondaryShadowEnabled)",
55
+ primaryOutline: "var(--buttonColors-primaryOutline)", // TODO: add to themesV2 API
56
+ primaryShadow: "var(--buttonColors-primaryShadow)", // TODO: add to themesV2 API
57
+ disabled: "var(--buttonColors-disabled)", // TODO: add to themesV2 API
58
+ destructiveText: "var(--buttonColors-destructiveText)", // TODO: add to themesV2 API
59
+ },
60
+ textColors: {
61
+ primaryColor: "var(--textColors-primaryColor)",
62
+ secondaryColor: "var(--textColors-secondaryColor)",
63
+ pageTitle: "var(--textColors-pageTitle)",
64
+ legalText: "var(--textColors-legalText)",
65
+ productTitle: "var(--textColors-productTitle)",
66
+ priceText: "var(--textColors-priceText)",
67
+ strikethroughPriceText: "var(--textColors-strikethroughPriceText)",
68
+ salePriceText: "var(--textColors-salePriceText)",
69
+ },
70
+ stateColors: {
71
+ subscriptions: "var(--stateColors-subscriptions)",
72
+ favorites: "var(--stateColors-favorites)",
73
+ reviews: "var(--stateColors-reviews)",
74
+ success: "var(--stateColors-success)",
75
+ error: "var(--stateColors-error)",
76
+ warning: "var(--stateColors-warning)",
77
+ disabled: "var(--stateColors-disabled)",
78
+ skeleton: "var(--stateColors-skeleton)",
79
+ error: "var(--stateColors-error)", // TODO: add to themesV2 API
80
+ },
81
+ productImages: {
82
+ aspectRatio: "var(--productImages-aspectRatio)",
83
+ scaling: "var(--productImages-scaling)",
84
+ },
85
+ border: "hsl(var(--border))",
86
+ input: "hsl(var(--input))",
87
+ ring: "hsl(var(--ring))",
88
+ background: "hsl(var(--background))",
89
+ foreground: "hsl(var(--foreground))",
90
+ primary: {
91
+ DEFAULT: "hsl(var(--primary))",
92
+ foreground: "hsl(var(--primary-foreground))",
93
+ },
94
+ secondary: {
95
+ DEFAULT: "hsl(var(--secondary))",
96
+ foreground: "hsl(var(--secondary-foreground))",
97
+ },
98
+ destructive: {
99
+ DEFAULT: "hsl(var(--destructive))",
100
+ foreground: "hsl(var(--destructive-foreground))",
101
+ },
102
+ muted: {
103
+ DEFAULT: "hsl(var(--muted))",
104
+ foreground: "hsl(var(--muted-foreground))",
105
+ },
106
+ accent: {
107
+ DEFAULT: "hsl(var(--accent))",
108
+ foreground: "hsl(var(--accent-foreground))",
109
+ },
110
+ popover: {
111
+ DEFAULT: "hsl(var(--popover))",
112
+ foreground: "hsl(var(--popover-foreground))",
113
+ },
114
+ card: {
115
+ DEFAULT: "hsl(var(--card))",
116
+ foreground: "hsl(var(--card-foreground))",
117
+ },
118
+ },
119
+ borderRadius: {
120
+ lg: "var(--radius)",
121
+ md: "calc(var(--radius) - 2px)",
122
+ sm: "calc(var(--radius) - 4px)",
123
+ },
124
+ keyframes: {
125
+ "accordion-down": {
126
+ from: { height: 0 },
127
+ to: { height: "var(--radix-accordion-content-height)" },
128
+ },
129
+ "accordion-up": {
130
+ from: { height: "var(--radix-accordion-content-height)" },
131
+ to: { height: 0 },
132
+ },
133
+ },
134
+ animation: {
135
+ "accordion-down": "accordion-down 0.2s ease-out",
136
+ "accordion-up": "accordion-up 0.2s ease-out",
137
+ },
138
+ },
139
+ },
140
+ plugins: [require("tailwindcss-animate")],
141
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "tsconfig/react-library.json",
3
+ "include": [".", "components/ThemeProvider.tsx"],
4
+ "exclude": ["dist", "build", "node_modules"],
5
+ "compilerOptions": {
6
+ "outDir": "./dist",
7
+ "declaration": true,
8
+ "baseUrl": ".",
9
+ "paths": {
10
+ "@/*": ["./*"]
11
+ }
12
+ }
13
+ }