@seekho/ui 0.1.6 → 0.1.9

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/dist/index.d.mts CHANGED
@@ -15,6 +15,19 @@ interface ModalProps {
15
15
  }
16
16
  declare function Modal({ show, title, className, children, isLoading, onHide, isWarning, isButtonsRequired, handleConfirmation, handleConfirmationBtnText, }: ModalProps): react_jsx_runtime.JSX.Element | null;
17
17
 
18
+ interface ReadMoreProps {
19
+ readMore: Record<string | number, boolean>;
20
+ index: string | number;
21
+ displayText: string;
22
+ className?: string;
23
+ numberOfCharactersToShow?: number;
24
+ handleCopyDisplayText?: (args: {
25
+ displayText: string;
26
+ }) => void;
27
+ handleToggleReadMore: () => void;
28
+ }
29
+ declare function ReadMore({ readMore, index, displayText, className, numberOfCharactersToShow, handleCopyDisplayText, handleToggleReadMore, }: ReadMoreProps): react_jsx_runtime.JSX.Element;
30
+
18
31
  /**
19
32
  * useState backed by localStorage — syncs value on mount and persists on change.
20
33
  */
@@ -26,4 +39,4 @@ declare function useLocalStorage<T>(key: string, initialValue: T): readonly [T,
26
39
  */
27
40
  declare function cn(...classes: (string | undefined | null | false)[]): string;
28
41
 
29
- export { Modal, type ModalProps, cn, useLocalStorage };
42
+ export { Modal, type ModalProps, ReadMore, type ReadMoreProps, cn, useLocalStorage };
package/dist/index.d.ts CHANGED
@@ -15,6 +15,19 @@ interface ModalProps {
15
15
  }
16
16
  declare function Modal({ show, title, className, children, isLoading, onHide, isWarning, isButtonsRequired, handleConfirmation, handleConfirmationBtnText, }: ModalProps): react_jsx_runtime.JSX.Element | null;
17
17
 
18
+ interface ReadMoreProps {
19
+ readMore: Record<string | number, boolean>;
20
+ index: string | number;
21
+ displayText: string;
22
+ className?: string;
23
+ numberOfCharactersToShow?: number;
24
+ handleCopyDisplayText?: (args: {
25
+ displayText: string;
26
+ }) => void;
27
+ handleToggleReadMore: () => void;
28
+ }
29
+ declare function ReadMore({ readMore, index, displayText, className, numberOfCharactersToShow, handleCopyDisplayText, handleToggleReadMore, }: ReadMoreProps): react_jsx_runtime.JSX.Element;
30
+
18
31
  /**
19
32
  * useState backed by localStorage — syncs value on mount and persists on change.
20
33
  */
@@ -26,4 +39,4 @@ declare function useLocalStorage<T>(key: string, initialValue: T): readonly [T,
26
39
  */
27
40
  declare function cn(...classes: (string | undefined | null | false)[]): string;
28
41
 
29
- export { Modal, type ModalProps, cn, useLocalStorage };
42
+ export { Modal, type ModalProps, ReadMore, type ReadMoreProps, cn, useLocalStorage };
package/dist/index.js CHANGED
@@ -154,6 +154,68 @@ function Modal({
154
154
  }
155
155
  ) });
156
156
  }
157
+ function ReadMore({
158
+ readMore,
159
+ index,
160
+ displayText,
161
+ className,
162
+ numberOfCharactersToShow = 300,
163
+ handleCopyDisplayText,
164
+ handleToggleReadMore
165
+ }) {
166
+ const isExpanded = readMore == null ? void 0 : readMore[index];
167
+ const isLong = (displayText == null ? void 0 : displayText.length) > numberOfCharactersToShow;
168
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(
169
+ "p",
170
+ {
171
+ className: [
172
+ "text-[14px] mt-1 break-all text-[#333333] leading-[20px] tracking-wide overflow-hidden max-h-full",
173
+ className || ""
174
+ ].join(" "),
175
+ children: [
176
+ isExpanded ? displayText : displayText == null ? void 0 : displayText.slice(0, numberOfCharactersToShow),
177
+ " ",
178
+ isLong && /* @__PURE__ */ jsxRuntime.jsx(
179
+ "button",
180
+ {
181
+ onClick: handleToggleReadMore,
182
+ className: "text-basePurple cursor-pointer font-semibold text-[11px]",
183
+ children: isExpanded ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
184
+ handleCopyDisplayText && /* @__PURE__ */ jsxRuntime.jsx(
185
+ "span",
186
+ {
187
+ onClick: (e) => {
188
+ e.stopPropagation();
189
+ handleCopyDisplayText({ displayText });
190
+ },
191
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
192
+ "svg",
193
+ {
194
+ xmlns: "http://www.w3.org/2000/svg",
195
+ className: "text-gray-500 w-[14px] h-[14px]",
196
+ viewBox: "0 0 24 24",
197
+ fill: "none",
198
+ stroke: "currentColor",
199
+ strokeWidth: "2",
200
+ strokeLinecap: "round",
201
+ strokeLinejoin: "round",
202
+ children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
204
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
205
+ ]
206
+ }
207
+ )
208
+ }
209
+ ),
210
+ " ",
211
+ "...Read Less"
212
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("span", { children: "...Read More" })
213
+ }
214
+ )
215
+ ]
216
+ }
217
+ ) });
218
+ }
157
219
  function useLocalStorage(key, initialValue) {
158
220
  const [storedValue, setStoredValue] = react.useState(() => {
159
221
  if (typeof window === "undefined") return initialValue;
@@ -194,6 +256,7 @@ function cn(...classes) {
194
256
  }
195
257
 
196
258
  exports.Modal = Modal;
259
+ exports.ReadMore = ReadMore;
197
260
  exports.cn = cn;
198
261
  exports.useLocalStorage = useLocalStorage;
199
262
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/modalStack.ts","../src/components/Modal/Modal.tsx","../src/hooks/useLocalStorage.ts","../src/utils/cn.ts"],"names":["useId","useEffect","useHotkeys","jsx","jsxs","useState"],"mappings":"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","file":"index.js","sourcesContent":["type ModalEntry = { id: string };\n\nconst stack: ModalEntry[] = [];\n\nexport function handlePushModal({ id }: ModalEntry) {\n if (!stack.find((m) => m.id === id)) {\n stack.push({ id });\n }\n}\n\nexport function handlePopModal({ id }: ModalEntry) {\n const idx = stack.findIndex((m) => m.id === id);\n if (idx !== -1) stack.splice(idx, 1);\n}\n\nexport function handleIsTopModal({ id }: ModalEntry): boolean {\n return stack.length > 0 && stack[stack.length - 1].id === id;\n}\n","import React, { useEffect, useId } from \"react\";\nimport { useHotkeys } from \"react-hotkeys-hook\";\nimport {\n handleIsTopModal,\n handlePopModal,\n handlePushModal,\n} from \"../../utils/modalStack\";\n\nexport interface ModalProps {\n show: boolean;\n title?: string;\n className?: string;\n children?: React.ReactNode;\n isLoading?: boolean;\n onHide?: () => void;\n isWarning?: boolean;\n isButtonsRequired?: boolean;\n handleConfirmation?: () => void;\n handleConfirmationBtnText?: string;\n}\n\nexport function Modal({\n show,\n title = \"\",\n className,\n children,\n isLoading = false,\n onHide,\n isWarning = false,\n isButtonsRequired = true,\n handleConfirmation,\n handleConfirmationBtnText = \"Confirm\",\n}: ModalProps) {\n const modalId = useId();\n\n useEffect(() => {\n if (show) handlePushModal({ id: modalId });\n return () => handlePopModal({ id: modalId });\n }, [show, modalId]);\n\n useHotkeys(\n \"enter\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && handleConfirmation)\n handleConfirmation();\n },\n { enabled: show && !isLoading }\n );\n\n useHotkeys(\n \"esc\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && onHide) onHide();\n },\n { enabled: show && !isLoading }\n );\n\n if (!show) return null;\n\n return (\n <div className=\"fixed inset-0 z-[999] bg-gray-800/50 flex items-center justify-center\">\n <div\n className={[\n \"w-[95%] bg-white rounded-lg shadow-lg flex flex-col\",\n \"md:w-[60%] lg:w-[30%] max-h-[95vh]\",\n className || \"\",\n ].join(\" \")}\n >\n {/* Header */}\n {title && (\n <div className=\"px-3.5 py-3 border-b border-gray-200 flex justify-between items-start gap-x-3\">\n <h2 className=\"text-[17px] font-semibold line-clamp-2 capitalize flex gap-x-2 items-center\">\n {isWarning && (\n <svg\n className=\"text-red-500 w-6 h-6 shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12 2a10 10 0 100 20A10 10 0 0012 2zm0 5a1 1 0 011 1v4a1 1 0 11-2 0V8a1 1 0 011-1zm0 8a1.25 1.25 0 110 2.5A1.25 1.25 0 0112 15z\"\n clipRule=\"evenodd\"\n />\n </svg>\n )}\n {title}\n </h2>\n {onHide && (\n <button\n onClick={onHide}\n className=\"text-gray-400 hover:text-gray-600 transition-colors shrink-0\"\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Body */}\n <div className=\"flex-1 overflow-y-auto px-3.5 py-3\">{children}</div>\n\n {/* Footer */}\n {isButtonsRequired && (\n <div className=\"px-6 py-3 bg-gray-100 flex justify-end space-x-3 rounded-b-md\">\n {onHide && (\n <button\n onClick={onHide}\n className=\"px-4 py-2 bg-white rounded-md font-semibold text-[14px] hover:bg-gray-50 transition-colors\"\n >\n Cancel\n </button>\n )}\n <button\n onClick={handleConfirmation}\n disabled={isLoading}\n className={[\n \"px-4 py-2 text-white rounded-md text-[14px] font-semibold\",\n \"disabled:opacity-50 disabled:cursor-not-allowed transition-colors\",\n isWarning\n ? \"bg-red-600 hover:bg-red-700\"\n : \"bg-basePurple hover:bg-basePurple/80\",\n ].join(\" \")}\n >\n {isLoading ? (\n <span className=\"inline-flex items-center gap-2\">\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 {handleConfirmationBtnText}\n </span>\n ) : (\n handleConfirmationBtnText\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\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"]}
1
+ {"version":3,"sources":["../src/utils/modalStack.ts","../src/components/Modal/Modal.tsx","../src/components/ReadMore/ReadMore.tsx","../src/hooks/useLocalStorage.ts","../src/utils/cn.ts"],"names":["useId","useEffect","useHotkeys","jsx","jsxs","useState"],"mappings":"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","file":"index.js","sourcesContent":["type ModalEntry = { id: string };\n\nconst stack: ModalEntry[] = [];\n\nexport function handlePushModal({ id }: ModalEntry) {\n if (!stack.find((m) => m.id === id)) {\n stack.push({ id });\n }\n}\n\nexport function handlePopModal({ id }: ModalEntry) {\n const idx = stack.findIndex((m) => m.id === id);\n if (idx !== -1) stack.splice(idx, 1);\n}\n\nexport function handleIsTopModal({ id }: ModalEntry): boolean {\n return stack.length > 0 && stack[stack.length - 1].id === id;\n}\n","import React, { useEffect, useId } from \"react\";\nimport { useHotkeys } from \"react-hotkeys-hook\";\nimport {\n handleIsTopModal,\n handlePopModal,\n handlePushModal,\n} from \"../../utils/modalStack\";\n\nexport interface ModalProps {\n show: boolean;\n title?: string;\n className?: string;\n children?: React.ReactNode;\n isLoading?: boolean;\n onHide?: () => void;\n isWarning?: boolean;\n isButtonsRequired?: boolean;\n handleConfirmation?: () => void;\n handleConfirmationBtnText?: string;\n}\n\nexport function Modal({\n show,\n title = \"\",\n className,\n children,\n isLoading = false,\n onHide,\n isWarning = false,\n isButtonsRequired = true,\n handleConfirmation,\n handleConfirmationBtnText = \"Confirm\",\n}: ModalProps) {\n const modalId = useId();\n\n useEffect(() => {\n if (show) handlePushModal({ id: modalId });\n return () => handlePopModal({ id: modalId });\n }, [show, modalId]);\n\n useHotkeys(\n \"enter\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && handleConfirmation)\n handleConfirmation();\n },\n { enabled: show && !isLoading }\n );\n\n useHotkeys(\n \"esc\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && onHide) onHide();\n },\n { enabled: show && !isLoading }\n );\n\n if (!show) return null;\n\n return (\n <div className=\"fixed inset-0 z-[999] bg-gray-800/50 flex items-center justify-center\">\n <div\n className={[\n \"w-[95%] bg-white rounded-lg shadow-lg flex flex-col\",\n \"md:w-[60%] lg:w-[30%] max-h-[95vh]\",\n className || \"\",\n ].join(\" \")}\n >\n {/* Header */}\n {title && (\n <div className=\"px-3.5 py-3 border-b border-gray-200 flex justify-between items-start gap-x-3\">\n <h2 className=\"text-[17px] font-semibold line-clamp-2 capitalize flex gap-x-2 items-center\">\n {isWarning && (\n <svg\n className=\"text-red-500 w-6 h-6 shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12 2a10 10 0 100 20A10 10 0 0012 2zm0 5a1 1 0 011 1v4a1 1 0 11-2 0V8a1 1 0 011-1zm0 8a1.25 1.25 0 110 2.5A1.25 1.25 0 0112 15z\"\n clipRule=\"evenodd\"\n />\n </svg>\n )}\n {title}\n </h2>\n {onHide && (\n <button\n onClick={onHide}\n className=\"text-gray-400 hover:text-gray-600 transition-colors shrink-0\"\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Body */}\n <div className=\"flex-1 overflow-y-auto px-3.5 py-3\">{children}</div>\n\n {/* Footer */}\n {isButtonsRequired && (\n <div className=\"px-6 py-3 bg-gray-100 flex justify-end space-x-3 rounded-b-md\">\n {onHide && (\n <button\n onClick={onHide}\n className=\"px-4 py-2 bg-white rounded-md font-semibold text-[14px] hover:bg-gray-50 transition-colors\"\n >\n Cancel\n </button>\n )}\n <button\n onClick={handleConfirmation}\n disabled={isLoading}\n className={[\n \"px-4 py-2 text-white rounded-md text-[14px] font-semibold\",\n \"disabled:opacity-50 disabled:cursor-not-allowed transition-colors\",\n isWarning\n ? \"bg-red-600 hover:bg-red-700\"\n : \"bg-basePurple hover:bg-basePurple/80\",\n ].join(\" \")}\n >\n {isLoading ? (\n <span className=\"inline-flex items-center gap-2\">\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 {handleConfirmationBtnText}\n </span>\n ) : (\n handleConfirmationBtnText\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n","import React from \"react\";\n\nexport interface ReadMoreProps {\n readMore: Record<string | number, boolean>;\n index: string | number;\n displayText: string;\n className?: string;\n numberOfCharactersToShow?: number;\n handleCopyDisplayText?: (args: { displayText: string }) => void;\n handleToggleReadMore: () => void;\n}\n\nexport function ReadMore({\n readMore,\n index,\n displayText,\n className,\n numberOfCharactersToShow = 300,\n handleCopyDisplayText,\n handleToggleReadMore,\n}: ReadMoreProps) {\n const isExpanded = readMore?.[index];\n const isLong = displayText?.length > numberOfCharactersToShow;\n\n return (\n <div>\n <p\n className={[\n \"text-[14px] mt-1 break-all text-[#333333] leading-[20px] tracking-wide overflow-hidden max-h-full\",\n className || \"\",\n ].join(\" \")}\n >\n {isExpanded ? displayText : displayText?.slice(0, numberOfCharactersToShow)}{\" \"}\n {isLong && (\n <button\n onClick={handleToggleReadMore}\n className=\"text-basePurple cursor-pointer font-semibold text-[11px]\"\n >\n {isExpanded ? (\n <span className=\"inline-flex items-center gap-1\">\n {handleCopyDisplayText && (\n <span\n onClick={(e) => {\n e.stopPropagation();\n handleCopyDisplayText({ displayText });\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"text-gray-500 w-[14px] h-[14px]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n </span>\n )}{\" \"}\n ...Read Less\n </span>\n ) : (\n <span>...Read More</span>\n )}\n </button>\n )}\n </p>\n </div>\n );\n}\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 CHANGED
@@ -152,6 +152,68 @@ function Modal({
152
152
  }
153
153
  ) });
154
154
  }
155
+ function ReadMore({
156
+ readMore,
157
+ index,
158
+ displayText,
159
+ className,
160
+ numberOfCharactersToShow = 300,
161
+ handleCopyDisplayText,
162
+ handleToggleReadMore
163
+ }) {
164
+ const isExpanded = readMore == null ? void 0 : readMore[index];
165
+ const isLong = (displayText == null ? void 0 : displayText.length) > numberOfCharactersToShow;
166
+ return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
167
+ "p",
168
+ {
169
+ className: [
170
+ "text-[14px] mt-1 break-all text-[#333333] leading-[20px] tracking-wide overflow-hidden max-h-full",
171
+ className || ""
172
+ ].join(" "),
173
+ children: [
174
+ isExpanded ? displayText : displayText == null ? void 0 : displayText.slice(0, numberOfCharactersToShow),
175
+ " ",
176
+ isLong && /* @__PURE__ */ jsx(
177
+ "button",
178
+ {
179
+ onClick: handleToggleReadMore,
180
+ className: "text-basePurple cursor-pointer font-semibold text-[11px]",
181
+ children: isExpanded ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
182
+ handleCopyDisplayText && /* @__PURE__ */ jsx(
183
+ "span",
184
+ {
185
+ onClick: (e) => {
186
+ e.stopPropagation();
187
+ handleCopyDisplayText({ displayText });
188
+ },
189
+ children: /* @__PURE__ */ jsxs(
190
+ "svg",
191
+ {
192
+ xmlns: "http://www.w3.org/2000/svg",
193
+ className: "text-gray-500 w-[14px] h-[14px]",
194
+ viewBox: "0 0 24 24",
195
+ fill: "none",
196
+ stroke: "currentColor",
197
+ strokeWidth: "2",
198
+ strokeLinecap: "round",
199
+ strokeLinejoin: "round",
200
+ children: [
201
+ /* @__PURE__ */ jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
202
+ /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
203
+ ]
204
+ }
205
+ )
206
+ }
207
+ ),
208
+ " ",
209
+ "...Read Less"
210
+ ] }) : /* @__PURE__ */ jsx("span", { children: "...Read More" })
211
+ }
212
+ )
213
+ ]
214
+ }
215
+ ) });
216
+ }
155
217
  function useLocalStorage(key, initialValue) {
156
218
  const [storedValue, setStoredValue] = useState(() => {
157
219
  if (typeof window === "undefined") return initialValue;
@@ -191,6 +253,6 @@ function cn(...classes) {
191
253
  return classes.filter(Boolean).join(" ");
192
254
  }
193
255
 
194
- export { Modal, cn, useLocalStorage };
256
+ export { Modal, ReadMore, cn, useLocalStorage };
195
257
  //# sourceMappingURL=index.mjs.map
196
258
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/modalStack.ts","../src/components/Modal/Modal.tsx","../src/hooks/useLocalStorage.ts","../src/utils/cn.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["type ModalEntry = { id: string };\n\nconst stack: ModalEntry[] = [];\n\nexport function handlePushModal({ id }: ModalEntry) {\n if (!stack.find((m) => m.id === id)) {\n stack.push({ id });\n }\n}\n\nexport function handlePopModal({ id }: ModalEntry) {\n const idx = stack.findIndex((m) => m.id === id);\n if (idx !== -1) stack.splice(idx, 1);\n}\n\nexport function handleIsTopModal({ id }: ModalEntry): boolean {\n return stack.length > 0 && stack[stack.length - 1].id === id;\n}\n","import React, { useEffect, useId } from \"react\";\nimport { useHotkeys } from \"react-hotkeys-hook\";\nimport {\n handleIsTopModal,\n handlePopModal,\n handlePushModal,\n} from \"../../utils/modalStack\";\n\nexport interface ModalProps {\n show: boolean;\n title?: string;\n className?: string;\n children?: React.ReactNode;\n isLoading?: boolean;\n onHide?: () => void;\n isWarning?: boolean;\n isButtonsRequired?: boolean;\n handleConfirmation?: () => void;\n handleConfirmationBtnText?: string;\n}\n\nexport function Modal({\n show,\n title = \"\",\n className,\n children,\n isLoading = false,\n onHide,\n isWarning = false,\n isButtonsRequired = true,\n handleConfirmation,\n handleConfirmationBtnText = \"Confirm\",\n}: ModalProps) {\n const modalId = useId();\n\n useEffect(() => {\n if (show) handlePushModal({ id: modalId });\n return () => handlePopModal({ id: modalId });\n }, [show, modalId]);\n\n useHotkeys(\n \"enter\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && handleConfirmation)\n handleConfirmation();\n },\n { enabled: show && !isLoading }\n );\n\n useHotkeys(\n \"esc\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && onHide) onHide();\n },\n { enabled: show && !isLoading }\n );\n\n if (!show) return null;\n\n return (\n <div className=\"fixed inset-0 z-[999] bg-gray-800/50 flex items-center justify-center\">\n <div\n className={[\n \"w-[95%] bg-white rounded-lg shadow-lg flex flex-col\",\n \"md:w-[60%] lg:w-[30%] max-h-[95vh]\",\n className || \"\",\n ].join(\" \")}\n >\n {/* Header */}\n {title && (\n <div className=\"px-3.5 py-3 border-b border-gray-200 flex justify-between items-start gap-x-3\">\n <h2 className=\"text-[17px] font-semibold line-clamp-2 capitalize flex gap-x-2 items-center\">\n {isWarning && (\n <svg\n className=\"text-red-500 w-6 h-6 shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12 2a10 10 0 100 20A10 10 0 0012 2zm0 5a1 1 0 011 1v4a1 1 0 11-2 0V8a1 1 0 011-1zm0 8a1.25 1.25 0 110 2.5A1.25 1.25 0 0112 15z\"\n clipRule=\"evenodd\"\n />\n </svg>\n )}\n {title}\n </h2>\n {onHide && (\n <button\n onClick={onHide}\n className=\"text-gray-400 hover:text-gray-600 transition-colors shrink-0\"\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Body */}\n <div className=\"flex-1 overflow-y-auto px-3.5 py-3\">{children}</div>\n\n {/* Footer */}\n {isButtonsRequired && (\n <div className=\"px-6 py-3 bg-gray-100 flex justify-end space-x-3 rounded-b-md\">\n {onHide && (\n <button\n onClick={onHide}\n className=\"px-4 py-2 bg-white rounded-md font-semibold text-[14px] hover:bg-gray-50 transition-colors\"\n >\n Cancel\n </button>\n )}\n <button\n onClick={handleConfirmation}\n disabled={isLoading}\n className={[\n \"px-4 py-2 text-white rounded-md text-[14px] font-semibold\",\n \"disabled:opacity-50 disabled:cursor-not-allowed transition-colors\",\n isWarning\n ? \"bg-red-600 hover:bg-red-700\"\n : \"bg-basePurple hover:bg-basePurple/80\",\n ].join(\" \")}\n >\n {isLoading ? (\n <span className=\"inline-flex items-center gap-2\">\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 {handleConfirmationBtnText}\n </span>\n ) : (\n handleConfirmationBtnText\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\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"]}
1
+ {"version":3,"sources":["../src/utils/modalStack.ts","../src/components/Modal/Modal.tsx","../src/components/ReadMore/ReadMore.tsx","../src/hooks/useLocalStorage.ts","../src/utils/cn.ts"],"names":["jsx","jsxs"],"mappings":"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","file":"index.mjs","sourcesContent":["type ModalEntry = { id: string };\n\nconst stack: ModalEntry[] = [];\n\nexport function handlePushModal({ id }: ModalEntry) {\n if (!stack.find((m) => m.id === id)) {\n stack.push({ id });\n }\n}\n\nexport function handlePopModal({ id }: ModalEntry) {\n const idx = stack.findIndex((m) => m.id === id);\n if (idx !== -1) stack.splice(idx, 1);\n}\n\nexport function handleIsTopModal({ id }: ModalEntry): boolean {\n return stack.length > 0 && stack[stack.length - 1].id === id;\n}\n","import React, { useEffect, useId } from \"react\";\nimport { useHotkeys } from \"react-hotkeys-hook\";\nimport {\n handleIsTopModal,\n handlePopModal,\n handlePushModal,\n} from \"../../utils/modalStack\";\n\nexport interface ModalProps {\n show: boolean;\n title?: string;\n className?: string;\n children?: React.ReactNode;\n isLoading?: boolean;\n onHide?: () => void;\n isWarning?: boolean;\n isButtonsRequired?: boolean;\n handleConfirmation?: () => void;\n handleConfirmationBtnText?: string;\n}\n\nexport function Modal({\n show,\n title = \"\",\n className,\n children,\n isLoading = false,\n onHide,\n isWarning = false,\n isButtonsRequired = true,\n handleConfirmation,\n handleConfirmationBtnText = \"Confirm\",\n}: ModalProps) {\n const modalId = useId();\n\n useEffect(() => {\n if (show) handlePushModal({ id: modalId });\n return () => handlePopModal({ id: modalId });\n }, [show, modalId]);\n\n useHotkeys(\n \"enter\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && handleConfirmation)\n handleConfirmation();\n },\n { enabled: show && !isLoading }\n );\n\n useHotkeys(\n \"esc\",\n (e) => {\n e.preventDefault();\n if (show && handleIsTopModal({ id: modalId }) && onHide) onHide();\n },\n { enabled: show && !isLoading }\n );\n\n if (!show) return null;\n\n return (\n <div className=\"fixed inset-0 z-[999] bg-gray-800/50 flex items-center justify-center\">\n <div\n className={[\n \"w-[95%] bg-white rounded-lg shadow-lg flex flex-col\",\n \"md:w-[60%] lg:w-[30%] max-h-[95vh]\",\n className || \"\",\n ].join(\" \")}\n >\n {/* Header */}\n {title && (\n <div className=\"px-3.5 py-3 border-b border-gray-200 flex justify-between items-start gap-x-3\">\n <h2 className=\"text-[17px] font-semibold line-clamp-2 capitalize flex gap-x-2 items-center\">\n {isWarning && (\n <svg\n className=\"text-red-500 w-6 h-6 shrink-0\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12 2a10 10 0 100 20A10 10 0 0012 2zm0 5a1 1 0 011 1v4a1 1 0 11-2 0V8a1 1 0 011-1zm0 8a1.25 1.25 0 110 2.5A1.25 1.25 0 0112 15z\"\n clipRule=\"evenodd\"\n />\n </svg>\n )}\n {title}\n </h2>\n {onHide && (\n <button\n onClick={onHide}\n className=\"text-gray-400 hover:text-gray-600 transition-colors shrink-0\"\n aria-label=\"Close\"\n >\n ✕\n </button>\n )}\n </div>\n )}\n\n {/* Body */}\n <div className=\"flex-1 overflow-y-auto px-3.5 py-3\">{children}</div>\n\n {/* Footer */}\n {isButtonsRequired && (\n <div className=\"px-6 py-3 bg-gray-100 flex justify-end space-x-3 rounded-b-md\">\n {onHide && (\n <button\n onClick={onHide}\n className=\"px-4 py-2 bg-white rounded-md font-semibold text-[14px] hover:bg-gray-50 transition-colors\"\n >\n Cancel\n </button>\n )}\n <button\n onClick={handleConfirmation}\n disabled={isLoading}\n className={[\n \"px-4 py-2 text-white rounded-md text-[14px] font-semibold\",\n \"disabled:opacity-50 disabled:cursor-not-allowed transition-colors\",\n isWarning\n ? \"bg-red-600 hover:bg-red-700\"\n : \"bg-basePurple hover:bg-basePurple/80\",\n ].join(\" \")}\n >\n {isLoading ? (\n <span className=\"inline-flex items-center gap-2\">\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 {handleConfirmationBtnText}\n </span>\n ) : (\n handleConfirmationBtnText\n )}\n </button>\n </div>\n )}\n </div>\n </div>\n );\n}\n","import React from \"react\";\n\nexport interface ReadMoreProps {\n readMore: Record<string | number, boolean>;\n index: string | number;\n displayText: string;\n className?: string;\n numberOfCharactersToShow?: number;\n handleCopyDisplayText?: (args: { displayText: string }) => void;\n handleToggleReadMore: () => void;\n}\n\nexport function ReadMore({\n readMore,\n index,\n displayText,\n className,\n numberOfCharactersToShow = 300,\n handleCopyDisplayText,\n handleToggleReadMore,\n}: ReadMoreProps) {\n const isExpanded = readMore?.[index];\n const isLong = displayText?.length > numberOfCharactersToShow;\n\n return (\n <div>\n <p\n className={[\n \"text-[14px] mt-1 break-all text-[#333333] leading-[20px] tracking-wide overflow-hidden max-h-full\",\n className || \"\",\n ].join(\" \")}\n >\n {isExpanded ? displayText : displayText?.slice(0, numberOfCharactersToShow)}{\" \"}\n {isLong && (\n <button\n onClick={handleToggleReadMore}\n className=\"text-basePurple cursor-pointer font-semibold text-[11px]\"\n >\n {isExpanded ? (\n <span className=\"inline-flex items-center gap-1\">\n {handleCopyDisplayText && (\n <span\n onClick={(e) => {\n e.stopPropagation();\n handleCopyDisplayText({ displayText });\n }}\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n className=\"text-gray-500 w-[14px] h-[14px]\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\" />\n <path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\" />\n </svg>\n </span>\n )}{\" \"}\n ...Read Less\n </span>\n ) : (\n <span>...Read More</span>\n )}\n </button>\n )}\n </p>\n </div>\n );\n}\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/styles.css CHANGED
@@ -1 +1 @@
1
- *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.inset-0{inset:0}.z-\[999\]{z-index:999}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.flex{display:flex}.inline-flex{display:inline-flex}.h-4{height:1rem}.h-6{height:1.5rem}.max-h-\[95vh\]{max-height:95vh}.w-4{width:1rem}.w-6{width:1.5rem}.w-\[95\%\]{width:95%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-2{gap:.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.overflow-y-auto{overflow-y:auto}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.border-b{border-bottom-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.bg-basePurple{--tw-bg-opacity:1;background-color:rgb(83 79 143/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.text-\[14px\]{font-size:14px}.text-\[17px\]{font-size:17px}.text-sm{font-size:.875rem;line-height:1.25rem}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-basePurple\/80:hover{background-color:rgba(83,79,143,.8)}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:768px){.md\:w-\[60\%\]{width:60%}}@media (min-width:1024px){.lg\:w-\[30\%\]{width:30%}}
1
+ *,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.19 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.fixed{position:fixed}.inset-0{inset:0}.z-\[999\]{z-index:999}.mt-1{margin-top:.25rem}.line-clamp-2{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}.flex{display:flex}.inline-flex{display:inline-flex}.h-4{height:1rem}.h-6{height:1.5rem}.h-\[14px\]{height:14px}.max-h-\[95vh\]{max-height:95vh}.max-h-full{max-height:100%}.w-4{width:1rem}.w-6{width:1.5rem}.w-80{width:20rem}.w-\[14px\]{width:14px}.w-\[95\%\]{width:95%}.flex-1{flex:1 1 0%}.shrink-0{flex-shrink:0}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-x-2{-moz-column-gap:.5rem;column-gap:.5rem}.gap-x-3{-moz-column-gap:.75rem;column-gap:.75rem}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.break-all{word-break:break-all}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-b-md{border-bottom-right-radius:.375rem;border-bottom-left-radius:.375rem}.border-b{border-bottom-width:1px}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.bg-basePurple{--tw-bg-opacity:1;background-color:rgb(83 79 143/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.text-\[11px\]{font-size:11px}.text-\[14px\]{font-size:14px}.text-\[17px\]{font-size:17px}.text-sm{font-size:.875rem;line-height:1.25rem}.font-semibold{font-weight:600}.capitalize{text-transform:capitalize}.leading-\[20px\]{line-height:20px}.tracking-wide{letter-spacing:.025em}.text-\[\#333333\]{--tw-text-opacity:1;color:rgb(51 51 51/var(--tw-text-opacity,1))}.text-basePurple{--tw-text-opacity:1;color:rgb(83 79 143/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.opacity-25{opacity:.25}.opacity-75{opacity:.75}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.hover\:bg-basePurple\/80:hover{background-color:rgba(83,79,143,.8)}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}@media (min-width:768px){.md\:w-\[60\%\]{width:60%}}@media (min-width:1024px){.lg\:w-\[30\%\]{width:30%}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seekho/ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.9",
4
4
  "description": "Seekho internal UI component library",
5
5
  "keywords": [
6
6
  "seekho",
package/readme.md CHANGED
@@ -6,9 +6,7 @@ Seekho internal UI component library — shared components, hooks, and utilities
6
6
 
7
7
  | Export | Description |
8
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 |
9
+ | `<Modal>` | Confirmation/dialog modal with hotkey support (Enter to confirm, Esc to close), loading state, warning variant, and stacked modal management |
12
10
  | `useLocalStorage` | useState backed by localStorage |
13
11
  | `cn()` | Utility to merge Tailwind class names |
14
12
 
@@ -26,79 +24,65 @@ npm run lint # type check
26
24
  ### 1. Install
27
25
 
28
26
  ```bash
29
- # If published to npm:
30
- npm install seekho-ui
27
+ npm install @seekho/ui
28
+ ```
29
+
30
+ ### 2. Install peer dependencies
31
31
 
32
- # If using local path during development:
33
- npm install ../seekho-ui
32
+ ```bash
33
+ npm install react-hotkeys-hook
34
34
  ```
35
35
 
36
- ### 2. Add library source to your Tailwind content paths
36
+ ### 3. Import styles once
37
37
 
38
- In your app's `tailwind.config.js` (or `tailwind.css` for v4):
38
+ In your app's entry file (`src/index.tsx`, `src/main.tsx`, `app/layout.tsx`, or `pages/_app.tsx`):
39
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
- }
40
+ ```tsx
41
+ import "@seekho/ui/styles.css";
48
42
  ```
49
43
 
50
- ### 3. Import and use
44
+ > The CSS is pre-built and works with any Tailwind version (v3, v4, or no Tailwind at all) — no PostCSS configuration needed.
45
+
46
+ ### 4. Use components
51
47
 
52
48
  ```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
- }
49
+ import { Modal } from "@seekho/ui";
64
50
  ```
65
51
 
66
- ## Publishing
52
+ Refer to Storybook for usage examples of each component.
67
53
 
68
- ### npm (recommended for internal use)
54
+ ## Modal props
69
55
 
70
- ```bash
71
- # Login once
72
- npm login
56
+ | Prop | Type | Default | Description |
57
+ | ---- | ---- | ------- | ----------- |
58
+ | `show` | `boolean` | required | Controls visibility |
59
+ | `title` | `string` | `""` | Modal header title |
60
+ | `onHide` | `() => void` | — | Called on Cancel / Esc key |
61
+ | `handleConfirmation` | `() => void` | — | Called on Confirm / Enter key |
62
+ | `handleConfirmationBtnText` | `string` | `"Confirm"` | Confirm button label |
63
+ | `isLoading` | `boolean` | `false` | Shows spinner, disables buttons |
64
+ | `isWarning` | `boolean` | `false` | Red confirm button + warning icon |
65
+ | `isButtonsRequired` | `boolean` | `true` | Hide footer buttons if false |
66
+ | `className` | `string` | — | Extra classes on the modal panel |
67
+ | `children` | `ReactNode` | — | Modal body content |
73
68
 
74
- # Publish
75
- npm publish
69
+ ## Publishing
76
70
 
77
- # Or bump version first
78
- npm version patch # 0.1.0 → 0.1.1
71
+ ```bash
72
+ git add .
73
+ git commit -m "your message"
74
+ npm version patch # or minor / major
79
75
  npm publish
80
76
  ```
81
77
 
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
78
  ## Adding a new component
95
79
 
96
80
  ```text
97
81
  src/components/
98
82
  └── MyComponent/
99
- ├── MyComponent.tsx # component + types
83
+ ├── MyComponent.tsx # component + types
100
84
  ├── MyComponent.stories.tsx
101
- └── index.ts # re-export
85
+ └── index.ts # re-export
102
86
  ```
103
87
 
104
88
  Then add `export * from "./MyComponent"` to [src/components/index.ts](src/components/index.ts).
@@ -113,7 +97,7 @@ seekho-ui/
113
97
  │ ├── utils/ # Pure utility functions
114
98
  │ └── index.ts # Single entry point
115
99
  ├── .storybook/ # Storybook config
116
- ├── tsup.config.ts # Build config
117
- └── tailwind.config.js # Tailwind (for Storybook)
100
+ ├── postcss.config.js # PostCSS (Tailwind v3 for Storybook)
101
+ ├── tailwind.config.js # Tailwind config + brand colors
102
+ └── tsup.config.ts # Build config
118
103
  ```
119
- # seekho-ui