@seekho/ui 0.1.0

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,40 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
5
+ type ButtonSize = "sm" | "md" | "lg";
6
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
7
+ variant?: ButtonVariant;
8
+ size?: ButtonSize;
9
+ loading?: boolean;
10
+ children: React.ReactNode;
11
+ }
12
+ declare function Button({ variant, size, loading, disabled, className, children, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
13
+
14
+ type BadgeVariant = "default" | "success" | "warning" | "danger" | "info";
15
+ interface BadgeProps {
16
+ variant?: BadgeVariant;
17
+ children: React.ReactNode;
18
+ className?: string;
19
+ }
20
+ declare function Badge({ variant, children, className }: BadgeProps): react_jsx_runtime.JSX.Element;
21
+
22
+ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
23
+ label?: string;
24
+ error?: string;
25
+ hint?: string;
26
+ }
27
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
28
+
29
+ /**
30
+ * useState backed by localStorage — syncs value on mount and persists on change.
31
+ */
32
+ declare function useLocalStorage<T>(key: string, initialValue: T): readonly [T, (value: T | ((prev: T) => T)) => void, () => void];
33
+
34
+ /**
35
+ * Merges class names, filtering out falsy values.
36
+ * Lightweight alternative to clsx for simple use cases.
37
+ */
38
+ declare function cn(...classes: (string | undefined | null | false)[]): string;
39
+
40
+ export { Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Input, type InputProps, cn, useLocalStorage };
@@ -0,0 +1,40 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React from 'react';
3
+
4
+ type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
5
+ type ButtonSize = "sm" | "md" | "lg";
6
+ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
7
+ variant?: ButtonVariant;
8
+ size?: ButtonSize;
9
+ loading?: boolean;
10
+ children: React.ReactNode;
11
+ }
12
+ declare function Button({ variant, size, loading, disabled, className, children, ...props }: ButtonProps): react_jsx_runtime.JSX.Element;
13
+
14
+ type BadgeVariant = "default" | "success" | "warning" | "danger" | "info";
15
+ interface BadgeProps {
16
+ variant?: BadgeVariant;
17
+ children: React.ReactNode;
18
+ className?: string;
19
+ }
20
+ declare function Badge({ variant, children, className }: BadgeProps): react_jsx_runtime.JSX.Element;
21
+
22
+ interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
23
+ label?: string;
24
+ error?: string;
25
+ hint?: string;
26
+ }
27
+ declare const Input: React.ForwardRefExoticComponent<InputProps & React.RefAttributes<HTMLInputElement>>;
28
+
29
+ /**
30
+ * useState backed by localStorage — syncs value on mount and persists on change.
31
+ */
32
+ declare function useLocalStorage<T>(key: string, initialValue: T): readonly [T, (value: T | ((prev: T) => T)) => void, () => void];
33
+
34
+ /**
35
+ * Merges class names, filtering out falsy values.
36
+ * Lightweight alternative to clsx for simple use cases.
37
+ */
38
+ declare function cn(...classes: (string | undefined | null | false)[]): string;
39
+
40
+ export { Badge, type BadgeProps, type BadgeVariant, Button, type ButtonProps, type ButtonSize, type ButtonVariant, Input, type InputProps, cn, useLocalStorage };
package/dist/index.js ADDED
@@ -0,0 +1,217 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var React = require('react');
5
+
6
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
7
+
8
+ var React__default = /*#__PURE__*/_interopDefault(React);
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __defProps = Object.defineProperties;
12
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
13
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
14
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
15
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
16
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17
+ var __spreadValues = (a, b) => {
18
+ for (var prop in b || (b = {}))
19
+ if (__hasOwnProp.call(b, prop))
20
+ __defNormalProp(a, prop, b[prop]);
21
+ if (__getOwnPropSymbols)
22
+ for (var prop of __getOwnPropSymbols(b)) {
23
+ if (__propIsEnum.call(b, prop))
24
+ __defNormalProp(a, prop, b[prop]);
25
+ }
26
+ return a;
27
+ };
28
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
29
+ var __objRest = (source, exclude) => {
30
+ var target = {};
31
+ for (var prop in source)
32
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
33
+ target[prop] = source[prop];
34
+ if (source != null && __getOwnPropSymbols)
35
+ for (var prop of __getOwnPropSymbols(source)) {
36
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
37
+ target[prop] = source[prop];
38
+ }
39
+ return target;
40
+ };
41
+ var variantClasses = {
42
+ primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
43
+ secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-400",
44
+ danger: "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
45
+ ghost: "bg-transparent text-gray-700 hover:bg-gray-100 focus:ring-gray-300"
46
+ };
47
+ var sizeClasses = {
48
+ sm: "px-3 py-1.5 text-sm",
49
+ md: "px-4 py-2 text-sm",
50
+ lg: "px-5 py-2.5 text-base"
51
+ };
52
+ function Button(_a) {
53
+ var _b = _a, {
54
+ variant = "primary",
55
+ size = "md",
56
+ loading = false,
57
+ disabled,
58
+ className = "",
59
+ children
60
+ } = _b, props = __objRest(_b, [
61
+ "variant",
62
+ "size",
63
+ "loading",
64
+ "disabled",
65
+ "className",
66
+ "children"
67
+ ]);
68
+ return /* @__PURE__ */ jsxRuntime.jsxs(
69
+ "button",
70
+ __spreadProps(__spreadValues({
71
+ disabled: disabled || loading,
72
+ className: [
73
+ "inline-flex items-center justify-center gap-2 rounded-md font-medium",
74
+ "focus:outline-none focus:ring-2 focus:ring-offset-2",
75
+ "transition-colors duration-150",
76
+ "disabled:opacity-50 disabled:cursor-not-allowed",
77
+ variantClasses[variant],
78
+ sizeClasses[size],
79
+ className
80
+ ].join(" ")
81
+ }, props), {
82
+ children: [
83
+ loading && /* @__PURE__ */ jsxRuntime.jsxs(
84
+ "svg",
85
+ {
86
+ className: "animate-spin h-4 w-4",
87
+ xmlns: "http://www.w3.org/2000/svg",
88
+ fill: "none",
89
+ viewBox: "0 0 24 24",
90
+ children: [
91
+ /* @__PURE__ */ jsxRuntime.jsx(
92
+ "circle",
93
+ {
94
+ className: "opacity-25",
95
+ cx: "12",
96
+ cy: "12",
97
+ r: "10",
98
+ stroke: "currentColor",
99
+ strokeWidth: "4"
100
+ }
101
+ ),
102
+ /* @__PURE__ */ jsxRuntime.jsx(
103
+ "path",
104
+ {
105
+ className: "opacity-75",
106
+ fill: "currentColor",
107
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
108
+ }
109
+ )
110
+ ]
111
+ }
112
+ ),
113
+ children
114
+ ]
115
+ })
116
+ );
117
+ }
118
+ var variantClasses2 = {
119
+ default: "bg-gray-100 text-gray-800",
120
+ success: "bg-green-100 text-green-800",
121
+ warning: "bg-yellow-100 text-yellow-800",
122
+ danger: "bg-red-100 text-red-800",
123
+ info: "bg-blue-100 text-blue-800"
124
+ };
125
+ function Badge({ variant = "default", children, className = "" }) {
126
+ return /* @__PURE__ */ jsxRuntime.jsx(
127
+ "span",
128
+ {
129
+ className: [
130
+ "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
131
+ variantClasses2[variant],
132
+ className
133
+ ].join(" "),
134
+ children
135
+ }
136
+ );
137
+ }
138
+ var Input = React__default.default.forwardRef(
139
+ (_a, ref) => {
140
+ var _b = _a, { label, error, hint, className = "", id } = _b, props = __objRest(_b, ["label", "error", "hint", "className", "id"]);
141
+ const inputId = id || (label == null ? void 0 : label.toLowerCase().replace(/\s+/g, "-"));
142
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
143
+ label && /* @__PURE__ */ jsxRuntime.jsx(
144
+ "label",
145
+ {
146
+ htmlFor: inputId,
147
+ className: "text-sm font-medium text-gray-700",
148
+ children: label
149
+ }
150
+ ),
151
+ /* @__PURE__ */ jsxRuntime.jsx(
152
+ "input",
153
+ __spreadValues({
154
+ ref,
155
+ id: inputId,
156
+ className: [
157
+ "block w-full rounded-md border px-3 py-2 text-sm shadow-sm",
158
+ "placeholder:text-gray-400",
159
+ "focus:outline-none focus:ring-2 focus:ring-offset-0",
160
+ error ? "border-red-400 focus:border-red-500 focus:ring-red-300" : "border-gray-300 focus:border-blue-500 focus:ring-blue-200",
161
+ "disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500",
162
+ className
163
+ ].join(" ")
164
+ }, props)
165
+ ),
166
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-red-600", children: error }),
167
+ hint && !error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-gray-500", children: hint })
168
+ ] });
169
+ }
170
+ );
171
+ Input.displayName = "Input";
172
+ function useLocalStorage(key, initialValue) {
173
+ const [storedValue, setStoredValue] = React.useState(() => {
174
+ if (typeof window === "undefined") return initialValue;
175
+ try {
176
+ const item = window.localStorage.getItem(key);
177
+ return item ? JSON.parse(item) : initialValue;
178
+ } catch (e) {
179
+ return initialValue;
180
+ }
181
+ });
182
+ const setValue = (value) => {
183
+ try {
184
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
185
+ setStoredValue(valueToStore);
186
+ if (typeof window !== "undefined") {
187
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
188
+ }
189
+ } catch (error) {
190
+ console.warn(`useLocalStorage: failed to set "${key}"`, error);
191
+ }
192
+ };
193
+ const remove = () => {
194
+ try {
195
+ setStoredValue(initialValue);
196
+ if (typeof window !== "undefined") {
197
+ window.localStorage.removeItem(key);
198
+ }
199
+ } catch (error) {
200
+ console.warn(`useLocalStorage: failed to remove "${key}"`, error);
201
+ }
202
+ };
203
+ return [storedValue, setValue, remove];
204
+ }
205
+
206
+ // src/utils/cn.ts
207
+ function cn(...classes) {
208
+ return classes.filter(Boolean).join(" ");
209
+ }
210
+
211
+ exports.Badge = Badge;
212
+ exports.Button = Button;
213
+ exports.Input = Input;
214
+ exports.cn = cn;
215
+ exports.useLocalStorage = useLocalStorage;
216
+ //# sourceMappingURL=index.js.map
217
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Button/Button.tsx","../src/components/Badge/Badge.tsx","../src/components/Input/Input.tsx","../src/hooks/useLocalStorage.ts","../src/utils/cn.ts"],"names":["jsxs","jsx","variantClasses","React","useState"],"mappings":"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","file":"index.js","sourcesContent":["import React from \"react\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\" | \"ghost\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n loading?: boolean;\n children: React.ReactNode;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: \"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500\",\n secondary: \"bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-400\",\n danger: \"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500\",\n ghost: \"bg-transparent text-gray-700 hover:bg-gray-100 focus:ring-gray-300\",\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: \"px-3 py-1.5 text-sm\",\n md: \"px-4 py-2 text-sm\",\n lg: \"px-5 py-2.5 text-base\",\n};\n\nexport function Button({\n variant = \"primary\",\n size = \"md\",\n loading = false,\n disabled,\n className = \"\",\n children,\n ...props\n}: ButtonProps) {\n return (\n <button\n disabled={disabled || loading}\n className={[\n \"inline-flex items-center justify-center gap-2 rounded-md font-medium\",\n \"focus:outline-none focus:ring-2 focus:ring-offset-2\",\n \"transition-colors duration-150\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n variantClasses[variant],\n sizeClasses[size],\n className,\n ].join(\" \")}\n {...props}\n >\n {loading && (\n <svg\n className=\"animate-spin h-4 w-4\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n )}\n {children}\n </button>\n );\n}\n","import React from \"react\";\n\nexport type BadgeVariant = \"default\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport interface BadgeProps {\n variant?: BadgeVariant;\n children: React.ReactNode;\n className?: string;\n}\n\nconst variantClasses: Record<BadgeVariant, string> = {\n default: \"bg-gray-100 text-gray-800\",\n success: \"bg-green-100 text-green-800\",\n warning: \"bg-yellow-100 text-yellow-800\",\n danger: \"bg-red-100 text-red-800\",\n info: \"bg-blue-100 text-blue-800\",\n};\n\nexport function Badge({ variant = \"default\", children, className = \"\" }: BadgeProps) {\n return (\n <span\n className={[\n \"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium\",\n variantClasses[variant],\n className,\n ].join(\" \")}\n >\n {children}\n </span>\n );\n}\n","import React from \"react\";\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n label?: string;\n error?: string;\n hint?: string;\n}\n\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ label, error, hint, className = \"\", id, ...props }, ref) => {\n const inputId = id || label?.toLowerCase().replace(/\\s+/g, \"-\");\n\n return (\n <div className=\"flex flex-col gap-1\">\n {label && (\n <label\n htmlFor={inputId}\n className=\"text-sm font-medium text-gray-700\"\n >\n {label}\n </label>\n )}\n <input\n ref={ref}\n id={inputId}\n className={[\n \"block w-full rounded-md border px-3 py-2 text-sm shadow-sm\",\n \"placeholder:text-gray-400\",\n \"focus:outline-none focus:ring-2 focus:ring-offset-0\",\n error\n ? \"border-red-400 focus:border-red-500 focus:ring-red-300\"\n : \"border-gray-300 focus:border-blue-500 focus:ring-blue-200\",\n \"disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500\",\n className,\n ].join(\" \")}\n {...props}\n />\n {error && <p className=\"text-xs text-red-600\">{error}</p>}\n {hint && !error && <p className=\"text-xs text-gray-500\">{hint}</p>}\n </div>\n );\n }\n);\n\nInput.displayName = \"Input\";\n","import { useState } from \"react\";\n\n/**\n * useState backed by localStorage — syncs value on mount and persists on change.\n */\nexport function useLocalStorage<T>(key: string, initialValue: T) {\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === \"undefined\") return initialValue;\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n });\n\n const setValue = (value: T | ((prev: T) => T)) => {\n try {\n const valueToStore = value instanceof Function ? value(storedValue) : value;\n setStoredValue(valueToStore);\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(key, JSON.stringify(valueToStore));\n }\n } catch (error) {\n console.warn(`useLocalStorage: failed to set \"${key}\"`, error);\n }\n };\n\n const remove = () => {\n try {\n setStoredValue(initialValue);\n if (typeof window !== \"undefined\") {\n window.localStorage.removeItem(key);\n }\n } catch (error) {\n console.warn(`useLocalStorage: failed to remove \"${key}\"`, error);\n }\n };\n\n return [storedValue, setValue, remove] as const;\n}\n","/**\n * Merges class names, filtering out falsy values.\n * Lightweight alternative to clsx for simple use cases.\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,207 @@
1
+ import { jsxs, jsx } from 'react/jsx-runtime';
2
+ import React, { useState } from 'react';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ var __objRest = (source, exclude) => {
24
+ var target = {};
25
+ for (var prop in source)
26
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
27
+ target[prop] = source[prop];
28
+ if (source != null && __getOwnPropSymbols)
29
+ for (var prop of __getOwnPropSymbols(source)) {
30
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
31
+ target[prop] = source[prop];
32
+ }
33
+ return target;
34
+ };
35
+ var variantClasses = {
36
+ primary: "bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500",
37
+ secondary: "bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-400",
38
+ danger: "bg-red-600 text-white hover:bg-red-700 focus:ring-red-500",
39
+ ghost: "bg-transparent text-gray-700 hover:bg-gray-100 focus:ring-gray-300"
40
+ };
41
+ var sizeClasses = {
42
+ sm: "px-3 py-1.5 text-sm",
43
+ md: "px-4 py-2 text-sm",
44
+ lg: "px-5 py-2.5 text-base"
45
+ };
46
+ function Button(_a) {
47
+ var _b = _a, {
48
+ variant = "primary",
49
+ size = "md",
50
+ loading = false,
51
+ disabled,
52
+ className = "",
53
+ children
54
+ } = _b, props = __objRest(_b, [
55
+ "variant",
56
+ "size",
57
+ "loading",
58
+ "disabled",
59
+ "className",
60
+ "children"
61
+ ]);
62
+ return /* @__PURE__ */ jsxs(
63
+ "button",
64
+ __spreadProps(__spreadValues({
65
+ disabled: disabled || loading,
66
+ className: [
67
+ "inline-flex items-center justify-center gap-2 rounded-md font-medium",
68
+ "focus:outline-none focus:ring-2 focus:ring-offset-2",
69
+ "transition-colors duration-150",
70
+ "disabled:opacity-50 disabled:cursor-not-allowed",
71
+ variantClasses[variant],
72
+ sizeClasses[size],
73
+ className
74
+ ].join(" ")
75
+ }, props), {
76
+ children: [
77
+ loading && /* @__PURE__ */ jsxs(
78
+ "svg",
79
+ {
80
+ className: "animate-spin h-4 w-4",
81
+ xmlns: "http://www.w3.org/2000/svg",
82
+ fill: "none",
83
+ viewBox: "0 0 24 24",
84
+ children: [
85
+ /* @__PURE__ */ jsx(
86
+ "circle",
87
+ {
88
+ className: "opacity-25",
89
+ cx: "12",
90
+ cy: "12",
91
+ r: "10",
92
+ stroke: "currentColor",
93
+ strokeWidth: "4"
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsx(
97
+ "path",
98
+ {
99
+ className: "opacity-75",
100
+ fill: "currentColor",
101
+ d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"
102
+ }
103
+ )
104
+ ]
105
+ }
106
+ ),
107
+ children
108
+ ]
109
+ })
110
+ );
111
+ }
112
+ var variantClasses2 = {
113
+ default: "bg-gray-100 text-gray-800",
114
+ success: "bg-green-100 text-green-800",
115
+ warning: "bg-yellow-100 text-yellow-800",
116
+ danger: "bg-red-100 text-red-800",
117
+ info: "bg-blue-100 text-blue-800"
118
+ };
119
+ function Badge({ variant = "default", children, className = "" }) {
120
+ return /* @__PURE__ */ jsx(
121
+ "span",
122
+ {
123
+ className: [
124
+ "inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium",
125
+ variantClasses2[variant],
126
+ className
127
+ ].join(" "),
128
+ children
129
+ }
130
+ );
131
+ }
132
+ var Input = React.forwardRef(
133
+ (_a, ref) => {
134
+ var _b = _a, { label, error, hint, className = "", id } = _b, props = __objRest(_b, ["label", "error", "hint", "className", "id"]);
135
+ const inputId = id || (label == null ? void 0 : label.toLowerCase().replace(/\s+/g, "-"));
136
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
137
+ label && /* @__PURE__ */ jsx(
138
+ "label",
139
+ {
140
+ htmlFor: inputId,
141
+ className: "text-sm font-medium text-gray-700",
142
+ children: label
143
+ }
144
+ ),
145
+ /* @__PURE__ */ jsx(
146
+ "input",
147
+ __spreadValues({
148
+ ref,
149
+ id: inputId,
150
+ className: [
151
+ "block w-full rounded-md border px-3 py-2 text-sm shadow-sm",
152
+ "placeholder:text-gray-400",
153
+ "focus:outline-none focus:ring-2 focus:ring-offset-0",
154
+ error ? "border-red-400 focus:border-red-500 focus:ring-red-300" : "border-gray-300 focus:border-blue-500 focus:ring-blue-200",
155
+ "disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500",
156
+ className
157
+ ].join(" ")
158
+ }, props)
159
+ ),
160
+ error && /* @__PURE__ */ jsx("p", { className: "text-xs text-red-600", children: error }),
161
+ hint && !error && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: hint })
162
+ ] });
163
+ }
164
+ );
165
+ Input.displayName = "Input";
166
+ function useLocalStorage(key, initialValue) {
167
+ const [storedValue, setStoredValue] = useState(() => {
168
+ if (typeof window === "undefined") return initialValue;
169
+ try {
170
+ const item = window.localStorage.getItem(key);
171
+ return item ? JSON.parse(item) : initialValue;
172
+ } catch (e) {
173
+ return initialValue;
174
+ }
175
+ });
176
+ const setValue = (value) => {
177
+ try {
178
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
179
+ setStoredValue(valueToStore);
180
+ if (typeof window !== "undefined") {
181
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
182
+ }
183
+ } catch (error) {
184
+ console.warn(`useLocalStorage: failed to set "${key}"`, error);
185
+ }
186
+ };
187
+ const remove = () => {
188
+ try {
189
+ setStoredValue(initialValue);
190
+ if (typeof window !== "undefined") {
191
+ window.localStorage.removeItem(key);
192
+ }
193
+ } catch (error) {
194
+ console.warn(`useLocalStorage: failed to remove "${key}"`, error);
195
+ }
196
+ };
197
+ return [storedValue, setValue, remove];
198
+ }
199
+
200
+ // src/utils/cn.ts
201
+ function cn(...classes) {
202
+ return classes.filter(Boolean).join(" ");
203
+ }
204
+
205
+ export { Badge, Button, Input, cn, useLocalStorage };
206
+ //# sourceMappingURL=index.mjs.map
207
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Button/Button.tsx","../src/components/Badge/Badge.tsx","../src/components/Input/Input.tsx","../src/hooks/useLocalStorage.ts","../src/utils/cn.ts"],"names":["variantClasses","jsx","jsxs"],"mappings":"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","file":"index.mjs","sourcesContent":["import React from \"react\";\n\nexport type ButtonVariant = \"primary\" | \"secondary\" | \"danger\" | \"ghost\";\nexport type ButtonSize = \"sm\" | \"md\" | \"lg\";\n\nexport interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n loading?: boolean;\n children: React.ReactNode;\n}\n\nconst variantClasses: Record<ButtonVariant, string> = {\n primary: \"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500\",\n secondary: \"bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-400\",\n danger: \"bg-red-600 text-white hover:bg-red-700 focus:ring-red-500\",\n ghost: \"bg-transparent text-gray-700 hover:bg-gray-100 focus:ring-gray-300\",\n};\n\nconst sizeClasses: Record<ButtonSize, string> = {\n sm: \"px-3 py-1.5 text-sm\",\n md: \"px-4 py-2 text-sm\",\n lg: \"px-5 py-2.5 text-base\",\n};\n\nexport function Button({\n variant = \"primary\",\n size = \"md\",\n loading = false,\n disabled,\n className = \"\",\n children,\n ...props\n}: ButtonProps) {\n return (\n <button\n disabled={disabled || loading}\n className={[\n \"inline-flex items-center justify-center gap-2 rounded-md font-medium\",\n \"focus:outline-none focus:ring-2 focus:ring-offset-2\",\n \"transition-colors duration-150\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n variantClasses[variant],\n sizeClasses[size],\n className,\n ].join(\" \")}\n {...props}\n >\n {loading && (\n <svg\n className=\"animate-spin h-4 w-4\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n className=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n strokeWidth=\"4\"\n />\n <path\n className=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z\"\n />\n </svg>\n )}\n {children}\n </button>\n );\n}\n","import React from \"react\";\n\nexport type BadgeVariant = \"default\" | \"success\" | \"warning\" | \"danger\" | \"info\";\n\nexport interface BadgeProps {\n variant?: BadgeVariant;\n children: React.ReactNode;\n className?: string;\n}\n\nconst variantClasses: Record<BadgeVariant, string> = {\n default: \"bg-gray-100 text-gray-800\",\n success: \"bg-green-100 text-green-800\",\n warning: \"bg-yellow-100 text-yellow-800\",\n danger: \"bg-red-100 text-red-800\",\n info: \"bg-blue-100 text-blue-800\",\n};\n\nexport function Badge({ variant = \"default\", children, className = \"\" }: BadgeProps) {\n return (\n <span\n className={[\n \"inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium\",\n variantClasses[variant],\n className,\n ].join(\" \")}\n >\n {children}\n </span>\n );\n}\n","import React from \"react\";\n\nexport interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {\n label?: string;\n error?: string;\n hint?: string;\n}\n\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ label, error, hint, className = \"\", id, ...props }, ref) => {\n const inputId = id || label?.toLowerCase().replace(/\\s+/g, \"-\");\n\n return (\n <div className=\"flex flex-col gap-1\">\n {label && (\n <label\n htmlFor={inputId}\n className=\"text-sm font-medium text-gray-700\"\n >\n {label}\n </label>\n )}\n <input\n ref={ref}\n id={inputId}\n className={[\n \"block w-full rounded-md border px-3 py-2 text-sm shadow-sm\",\n \"placeholder:text-gray-400\",\n \"focus:outline-none focus:ring-2 focus:ring-offset-0\",\n error\n ? \"border-red-400 focus:border-red-500 focus:ring-red-300\"\n : \"border-gray-300 focus:border-blue-500 focus:ring-blue-200\",\n \"disabled:cursor-not-allowed disabled:bg-gray-50 disabled:text-gray-500\",\n className,\n ].join(\" \")}\n {...props}\n />\n {error && <p className=\"text-xs text-red-600\">{error}</p>}\n {hint && !error && <p className=\"text-xs text-gray-500\">{hint}</p>}\n </div>\n );\n }\n);\n\nInput.displayName = \"Input\";\n","import { useState } from \"react\";\n\n/**\n * useState backed by localStorage — syncs value on mount and persists on change.\n */\nexport function useLocalStorage<T>(key: string, initialValue: T) {\n const [storedValue, setStoredValue] = useState<T>(() => {\n if (typeof window === \"undefined\") return initialValue;\n try {\n const item = window.localStorage.getItem(key);\n return item ? (JSON.parse(item) as T) : initialValue;\n } catch {\n return initialValue;\n }\n });\n\n const setValue = (value: T | ((prev: T) => T)) => {\n try {\n const valueToStore = value instanceof Function ? value(storedValue) : value;\n setStoredValue(valueToStore);\n if (typeof window !== \"undefined\") {\n window.localStorage.setItem(key, JSON.stringify(valueToStore));\n }\n } catch (error) {\n console.warn(`useLocalStorage: failed to set \"${key}\"`, error);\n }\n };\n\n const remove = () => {\n try {\n setStoredValue(initialValue);\n if (typeof window !== \"undefined\") {\n window.localStorage.removeItem(key);\n }\n } catch (error) {\n console.warn(`useLocalStorage: failed to remove \"${key}\"`, error);\n }\n };\n\n return [storedValue, setValue, remove] as const;\n}\n","/**\n * Merges class names, filtering out falsy values.\n * Lightweight alternative to clsx for simple use cases.\n */\nexport function cn(...classes: (string | undefined | null | false)[]): string {\n return classes.filter(Boolean).join(\" \");\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@seekho/ui",
3
+ "version": "0.1.0",
4
+ "description": "Seekho internal UI component library",
5
+ "keywords": ["seekho", "ui", "components", "react", "tailwind"],
6
+ "author": "Seekho",
7
+ "license": "MIT",
8
+ "publishConfig": {
9
+ "access": "public"
10
+ },
11
+ "main": "dist/index.js",
12
+ "module": "dist/index.mjs",
13
+ "types": "dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.mjs",
18
+ "require": "./dist/index.js"
19
+ }
20
+ },
21
+ "files": ["dist"],
22
+ "sideEffects": false,
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "build:watch": "tsup --watch",
26
+ "storybook": "storybook dev -p 6006",
27
+ "build-storybook": "storybook build",
28
+ "lint": "tsc --noEmit",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "peerDependencies": {
32
+ "react": ">=17",
33
+ "react-dom": ">=17"
34
+ },
35
+ "devDependencies": {
36
+ "@types/react": "^19.2.14",
37
+ "@types/react-dom": "^19.2.3",
38
+ "autoprefixer": "^10.4.27",
39
+ "postcss": "^8.5.8",
40
+ "react": "^19.2.4",
41
+ "react-dom": "^19.2.4",
42
+ "tailwindcss": "^4.2.2",
43
+ "tsup": "^8.5.1",
44
+ "typescript": "^6.0.2",
45
+ "storybook": "^10.3.3",
46
+ "@storybook/react-vite": "^10.3.3",
47
+ "@chromatic-com/storybook": "^5.1.1",
48
+ "@storybook/addon-vitest": "^10.3.3",
49
+ "@storybook/addon-a11y": "^10.3.3",
50
+ "@storybook/addon-docs": "^10.3.3",
51
+ "@storybook/addon-onboarding": "^10.3.3",
52
+ "vitest": "^4.1.2",
53
+ "playwright": "^1.58.2",
54
+ "@vitest/browser-playwright": "^4.1.2",
55
+ "@vitest/coverage-v8": "^4.1.2"
56
+ }
57
+ }
package/readme.md ADDED
@@ -0,0 +1,118 @@
1
+ # seekho-ui
2
+
3
+ Seekho internal UI component library — shared components, hooks, and utilities for internal tools built on React + Tailwind.
4
+
5
+ ## What's included
6
+
7
+ | Export | Description |
8
+ | ------ | ----------- |
9
+ | `<Button>` | Primary, secondary, danger, ghost variants with loading state |
10
+ | `<Badge>` | Status badges (success, warning, danger, info) |
11
+ | `<Input>` | Form input with label, hint, and error state |
12
+ | `useLocalStorage` | useState backed by localStorage |
13
+ | `cn()` | Utility to merge Tailwind class names |
14
+
15
+ ## Development
16
+
17
+ ```bash
18
+ npm install
19
+ npm run storybook # component playground at http://localhost:6006
20
+ npm run build # build dist/
21
+ npm run lint # type check
22
+ ```
23
+
24
+ ## Using in a consumer project
25
+
26
+ ### 1. Install
27
+
28
+ ```bash
29
+ # If published to npm:
30
+ npm install seekho-ui
31
+
32
+ # If using local path during development:
33
+ npm install ../seekho-ui
34
+ ```
35
+
36
+ ### 2. Add library source to your Tailwind content paths
37
+
38
+ In your app's `tailwind.config.js` (or `tailwind.css` for v4):
39
+
40
+ ```js
41
+ // tailwind.config.js
42
+ module.exports = {
43
+ content: [
44
+ "./src/**/*.{ts,tsx}",
45
+ "./node_modules/seekho-ui/dist/**/*.js", // <-- add this
46
+ ],
47
+ }
48
+ ```
49
+
50
+ ### 3. Import and use
51
+
52
+ ```tsx
53
+ import { Button, Badge, Input, useLocalStorage, cn } from "seekho-ui";
54
+
55
+ export function MyPage() {
56
+ return (
57
+ <div>
58
+ <Button variant="primary" onClick={() => {}}>Save</Button>
59
+ <Badge variant="success">Active</Badge>
60
+ <Input label="Email" placeholder="you@seekho.ai" />
61
+ </div>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ## Publishing
67
+
68
+ ### npm (recommended for internal use)
69
+
70
+ ```bash
71
+ # Login once
72
+ npm login
73
+
74
+ # Publish
75
+ npm publish
76
+
77
+ # Or bump version first
78
+ npm version patch # 0.1.0 → 0.1.1
79
+ npm publish
80
+ ```
81
+
82
+ ### GitHub Packages (free private hosting)
83
+
84
+ 1. Add to `package.json`: `"name": "@seekho/ui"`
85
+ 2. Create `.npmrc`:
86
+
87
+ ```ini
88
+ @seekho:registry=https://npm.pkg.github.com
89
+ //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
90
+ ```
91
+
92
+ 3. `npm publish`
93
+
94
+ ## Adding a new component
95
+
96
+ ```text
97
+ src/components/
98
+ └── MyComponent/
99
+ ├── MyComponent.tsx # component + types
100
+ ├── MyComponent.stories.tsx
101
+ └── index.ts # re-export
102
+ ```
103
+
104
+ Then add `export * from "./MyComponent"` to [src/components/index.ts](src/components/index.ts).
105
+
106
+ ## Project structure
107
+
108
+ ```text
109
+ seekho-ui/
110
+ ├── src/
111
+ │ ├── components/ # React components
112
+ │ ├── hooks/ # Shared React hooks
113
+ │ ├── utils/ # Pure utility functions
114
+ │ └── index.ts # Single entry point
115
+ ├── .storybook/ # Storybook config
116
+ ├── tsup.config.ts # Build config
117
+ └── tailwind.config.js # Tailwind (for Storybook)
118
+ ```