cartly 1.0.2

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/README.md ADDED
@@ -0,0 +1,167 @@
1
+ # cartly πŸ›’
2
+
3
+ **cartly** is a lightweight, type-safe React Cart Context library built with modern hooks.
4
+ It’s designed to be **generic**, **framework-agnostic**, and easy to integrate into any React application β€” from e‑commerce stores to internal tools.
5
+
6
+ - No external state libraries.
7
+ - Strong TypeScript support.
8
+ - Optional persistence.
9
+ - Safe for SSR.
10
+
11
+ ## ✨ Features
12
+
13
+ - βœ… Generic `createCartContext<T>()` API
14
+ - βœ… `CartProvider` + `useCart` hook
15
+ - βœ… Cart actions: add, update, remove, clear
16
+ - βœ… Derived state (total item count)
17
+ - βœ… Optional persistence (`localStorage`, `sessionStorage`, or disabled)
18
+ - βœ… SSR-safe (no `window` access at import time)
19
+ - βœ… Works with Material UI, Vite, Next.js, CRA
20
+ - βœ… React as peer dependency (no duplicate React issues)
21
+
22
+ ---
23
+
24
+ ## πŸ“¦ Installation
25
+
26
+ ```bash
27
+ npm install cartly
28
+ ```
29
+
30
+ ## πŸ“¦ Peer Dependencies
31
+
32
+ ```bash
33
+ npm install react react-dom
34
+ ```
35
+
36
+ ## πŸš€ Quick Start
37
+
38
+ ### 1. Create a typed cart context (once per app)
39
+
40
+ ```Typescript
41
+ import { createCartContext } from "cartly";
42
+
43
+ export type Product = {
44
+ id: string;
45
+ name: string;
46
+ price: number;
47
+ };
48
+
49
+ export const { CartProvider, useCart } = createCartContext<Product>();
50
+ ```
51
+
52
+ βœ… This should be done once and re-exported.
53
+ Do not call createCartContext in every component.
54
+
55
+ ### 2. Wrap your application
56
+
57
+ ```Typescript
58
+ import { StrictMode } from "react";
59
+ import { createRoot } from "react-dom/client";
60
+ import { CartProvider } from "./cart/cart";
61
+ import App from "./App.tsx";
62
+
63
+ createRoot(document.getElementById("root")!).render(
64
+ <StrictMode>
65
+ <CartProvider storageKey="cart" storage={localStorage}>
66
+ <App />
67
+ </CartProvider>
68
+ </StrictMode>,
69
+ );
70
+ ```
71
+
72
+ ### 3. Use the cart anywhere
73
+
74
+ ```Typescript
75
+ import { useCart } from "./cart/cart";
76
+
77
+ export function AddToCartButton({ product }: { product: Product }) {
78
+ const { addItemToCart, cartCount } = useCart();
79
+
80
+ return (
81
+ <button onClick={() => addItemToCart(product)}>
82
+ Add to cart ({cartCount})
83
+ </button>
84
+ );
85
+ }
86
+ ```
87
+
88
+ ## 🧠 API
89
+
90
+ ### createCartContext<T>()
91
+
92
+ Creates a typed cart context for your application.
93
+
94
+ ```TypeScript
95
+ const { CartProvider, useCart } = createCartContext<T>();
96
+ ```
97
+
98
+ Call this once per app, export the result, and reuse it everywhere.
99
+
100
+ ### &lt;CartProvider&gt;
101
+
102
+ ```TypeScript
103
+ <CartProvider storageKey="cart" storage={localStorage}>
104
+ {children}
105
+ </CartProvider>
106
+ ```
107
+
108
+ #### Props
109
+
110
+ | Prop | Type | Required | Description |
111
+ | ---------- | -------------- | -------- | ------------------------------------- |
112
+ | children | ReactNode | βœ… | App tree |
113
+ | storageKey | string | ❌ | Storage key (default: "cart") |
114
+ | storage | Storage / null | ❌ | localStorage, sessionStorage, or null |
115
+
116
+ πŸ’‘ Pass storage={null} to disable persistence (recommended for SSR/tests).
117
+
118
+ ### useCart()
119
+
120
+ ```TypeScript
121
+ const {
122
+ cartItems,
123
+ addItemToCart,
124
+ updateItemQuantity,
125
+ removeItemFromCart,
126
+ clearCart,
127
+ cartCount
128
+ } = useCart();
129
+ ```
130
+
131
+ ### 🧾 Cart Item Shape
132
+
133
+ ```TypeScript
134
+ export type CartItem<T> = {
135
+ id: string | number;
136
+ item: T;
137
+ quantity: number;
138
+ };
139
+ ```
140
+
141
+ This avoids collisions if your product model already contains a quantity field.
142
+
143
+ ## πŸ“ Repository Structure
144
+
145
+ ```Plain Text
146
+ cartly/
147
+ β”œβ”€ src/ # Library source
148
+ β”œβ”€ samples/ # Example apps
149
+ β”‚ └─ react-vite-demo/
150
+ β”œβ”€ dist/ # Build output (npm only)
151
+ β”œβ”€ package.json
152
+ β”œβ”€ vite.config.ts
153
+ β”œβ”€ tsconfig.json
154
+ β”œβ”€ README.md
155
+ └─ LICENSE
156
+ ```
157
+
158
+ ## πŸ“„ License
159
+
160
+ MIT
161
+
162
+ ## 🀝 Contributing
163
+
164
+ Issues and PRs are welcome.
165
+ The library intentionally keeps a small, focused API surface.
166
+
167
+ Built with React, TypeScript, and Vite.
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("react/jsx-runtime"),i=require("react");function w(u,s,d){try{const t=u.getItem(s);return t?JSON.parse(t):d}catch(t){return console.log(t),u.removeItem(s),d}}function O(){const u=i.createContext(void 0);function s({children:t,storageKey:m="cart",storage:f=localStorage}){const[a,C]=i.useState(()=>w(f,m,[]));i.useEffect(()=>{f.setItem(m,JSON.stringify(a))},[a,f,m]);const x=(e,r=1)=>{C(n=>n.find(c=>c.id==e.id)?n.map(c=>c.id==e.id?{...c,quantity:c.quantity+r}:c):[...n,{...e,quantity:r}])},l=(e,r)=>{C(n=>n.map(o=>o.id==e?{...o,quantity:r}:o).filter(o=>o.quantity>0))},I=e=>{C(r=>r.filter(n=>n.id!==e))},S=()=>{C([])},v=i.useMemo(()=>a.reduce((e,r)=>e+r.quantity,0),[a]),q={cartItems:a,addItemToCart:x,updateItemQuantity:l,removeItemFromCart:I,clearCart:S,cartCount:v};return y.jsx(u.Provider,{value:q,children:t})}function d(){const t=i.useContext(u);if(!t)throw new Error("useCart must be used within a CartProvider.");return t}return{CartProvider:s,useCart:d}}exports.createCartContext=O;
@@ -0,0 +1,2 @@
1
+ export * from './src/index'
2
+ export {}
package/dist/index.js ADDED
@@ -0,0 +1,59 @@
1
+ import { jsx as S } from "react/jsx-runtime";
2
+ import { createContext as w, useContext as h, useState as q, useEffect as y, useMemo as J } from "react";
3
+ function N(c, i, s) {
4
+ try {
5
+ const t = c.getItem(i);
6
+ return t ? JSON.parse(t) : s;
7
+ } catch (t) {
8
+ return console.log(t), c.removeItem(i), s;
9
+ }
10
+ }
11
+ function E() {
12
+ const c = w(void 0);
13
+ function i({
14
+ children: t,
15
+ storageKey: m = "cart",
16
+ storage: C = localStorage
17
+ }) {
18
+ const [u, d] = q(
19
+ () => N(C, m, [])
20
+ );
21
+ y(() => {
22
+ C.setItem(m, JSON.stringify(u));
23
+ }, [u, C, m]);
24
+ const f = (r, e = 1) => {
25
+ d((n) => n.find((a) => a.id == r.id) ? n.map(
26
+ (a) => a.id == r.id ? { ...a, quantity: a.quantity + e } : a
27
+ ) : [...n, { ...r, quantity: e }]);
28
+ }, x = (r, e) => {
29
+ d(
30
+ (n) => n.map((o) => o.id == r ? { ...o, quantity: e } : o).filter((o) => o.quantity > 0)
31
+ );
32
+ }, I = (r) => {
33
+ d((e) => e.filter((n) => n.id !== r));
34
+ }, l = () => {
35
+ d([]);
36
+ }, p = J(
37
+ () => u.reduce((r, e) => r + e.quantity, 0),
38
+ [u]
39
+ ), v = {
40
+ cartItems: u,
41
+ addItemToCart: f,
42
+ updateItemQuantity: x,
43
+ removeItemFromCart: I,
44
+ clearCart: l,
45
+ cartCount: p
46
+ };
47
+ return /* @__PURE__ */ S(c.Provider, { value: v, children: t });
48
+ }
49
+ function s() {
50
+ const t = h(c);
51
+ if (!t)
52
+ throw new Error("useCart must be used within a CartProvider.");
53
+ return t;
54
+ }
55
+ return { CartProvider: i, useCart: s };
56
+ }
57
+ export {
58
+ E as createCartContext
59
+ };
@@ -0,0 +1,13 @@
1
+ import { CartContextType, CartStorage } from './types';
2
+ type Props = {
3
+ children: React.ReactNode;
4
+ storageKey?: string;
5
+ storage?: CartStorage;
6
+ };
7
+ export declare function createCartContext<T extends {
8
+ id: string | number;
9
+ }>(): {
10
+ CartProvider: ({ children, storageKey, storage, }: Props) => import("react/jsx-runtime").JSX.Element;
11
+ useCart: () => CartContextType<T>;
12
+ };
13
+ export {};
@@ -0,0 +1,2 @@
1
+ export { createCartContext } from './CartProvider';
2
+ export type { CartContextType, CartItem, CartStorage } from './types';
@@ -0,0 +1,13 @@
1
+ import { CartStorage } from './types';
2
+ /**
3
+ * Safely read JSON from storage, returning a fallback value if parsing fails or if the item does not exist
4
+ *
5
+ * @remarks
6
+ * This function attempts to read a JSON string from the provided storage using the specified key.
7
+ *
8
+ * @param storage - The storage object to read from.
9
+ * @param key - The key to read the JSON string from.
10
+ * @param fallback - The fallback value to return if parsing fails or the item does not exist.
11
+ * @returns The parsed JSON value or the fallback value.
12
+ */
13
+ export declare function safeReadJSON<T>(storage: CartStorage, key: string, fallback: T): T;
@@ -0,0 +1,22 @@
1
+ export type CartItem<T extends {
2
+ id: string | number;
3
+ }> = T & {
4
+ quantity: number;
5
+ };
6
+ export type CartContextType<T extends {
7
+ id: string | number;
8
+ }> = {
9
+ cartItems: CartItem<T>[];
10
+ addItemToCart: (item: T, quantity?: number) => void;
11
+ updateItemQuantity: (id: string | number, quantity: number) => void;
12
+ removeItemFromCart: (id: string | number) => void;
13
+ clearCart: () => void;
14
+ cartCount: number;
15
+ };
16
+ /**
17
+ * A simplified interface for storage objects, focusing on essential methods for cart management.
18
+ *
19
+ * @remarks
20
+ * This type includes only the `getItem`, `setItem`, and `removeItem` methods from the standard Storage interface.
21
+ */
22
+ export type CartStorage = Pick<Storage, "getItem" | "setItem" | "removeItem">;
@@ -0,0 +1,2 @@
1
+ declare const _default: import('vite').UserConfig;
2
+ export default _default;
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "cartly",
3
+ "version": "1.0.2",
4
+ "description": "Generic, type-safe Cart Context & hooks for React",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "vite build",
13
+ "typecheck": "tsc -p tsconfig.build.json --noEmit",
14
+ "prepublishOnly": "npm run typecheck && npm run build"
15
+ },
16
+ "keywords": [],
17
+ "author": "",
18
+ "license": "MIT",
19
+ "type": "module",
20
+ "peerDependencies": {
21
+ "react": ">=18",
22
+ "react-dom": ">=18"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^25.1.0",
26
+ "@types/react": "^19.2.10",
27
+ "@types/react-dom": "^19.2.3",
28
+ "@vitejs/plugin-react-swc": "^4.2.2",
29
+ "react": "^19.2.4",
30
+ "react-dom": "^19.2.4",
31
+ "typescript": "^5.9.3",
32
+ "vite": "^7.3.1",
33
+ "vite-plugin-dts": "^4.5.4"
34
+ }
35
+ }