@tuwaio/nova-core 0.0.5 → 0.0.7

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 CHANGED
@@ -32,7 +32,6 @@ Use this package to:
32
32
  2. **Import the core styles** into the root of your application's main CSS file (e.g., `globals.css`). **This step is crucial.**
33
33
 
34
34
  ```css
35
- @import "tailwindcss";
36
35
  @import '@tuwaio/nova-core/dist/index.css';
37
36
  ```
38
37
 
@@ -40,9 +39,9 @@ Use this package to:
40
39
 
41
40
  You can use the provided styles in two ways: directly with CSS variables (basic) or by integrating them into your theme (recommended).
42
41
 
43
- ### Basic Usage (Without `tailwind.config.js`)
42
+ ### Usage
44
43
 
45
- With Tailwind CSS v4, you can use the CSS variables from this package directly in your className as arbitrary values. A `tailwind.config.js` file is not required for this to work.
44
+ For example with Tailwind CSS v4, you can use the CSS variables from this package directly in your className as arbitrary values only need to @import tailwindcss in your .css file.
46
45
 
47
46
  ```tsx
48
47
  // You can use the variables directly
@@ -51,55 +50,6 @@ With Tailwind CSS v4, you can use the CSS variables from this package directly i
51
50
  </button>
52
51
  ```
53
52
 
54
- ### Recommended Usage (Theming with `tailwind.config.js`)
55
-
56
- While a config file is optional in Tailwind v4, creating one to map our CSS variables to Tailwind's theme allows you to use clean, semantic class names. This is the recommended approach for building a consistent design system.
57
-
58
- **1. Create or update your `tailwind.config.js`:**
59
-
60
- ```js
61
- // tailwind.config.js
62
- /** @type {import('tailwindcss').Config} */
63
- export default {
64
- content: [
65
- './src/**/*.{js,ts,jsx,tsx}',
66
- ],
67
- theme: {
68
- extend: {
69
- colors: {
70
- primary: 'var(--tuwa-color-primary)',
71
- secondary: 'var(--tuwa-color-secondary)',
72
- background: 'var(--tuwa-color-background)',
73
- foreground: 'var(--tuwa-color-foreground)',
74
- },
75
- // ... etc.
76
- },
77
- },
78
- plugins: [],
79
- };
80
- ```
81
-
82
- **2. Use semantic class names in your components:**
83
-
84
- Now your code becomes much cleaner and easier to read.
85
-
86
- ```tsx
87
- import { cn } from '@tuwaio/nova-core';
88
-
89
- export function Button({ intent, className, ...props }) {
90
- const buttonClasses = cn(
91
- 'px-4 py-2 rounded-md font-semibold transition-colors',
92
- {
93
- 'bg-primary text-white hover:opacity-90': intent === 'primary',
94
- 'bg-secondary text-foreground hover:opacity-90': intent === 'secondary',
95
- },
96
- className,
97
- );
98
-
99
- return <button className={buttonClasses} {...props} />;
100
- }
101
- ```
102
-
103
53
  ## Contributing
104
54
 
105
55
  Contributions are welcome! Please read our main **[Contribution Guidelines](https://github.com/TuwaIO/workflows/blob/main/CONTRIBUTING.md)**.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';var react=require('react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge');function g(r=2e3){let[o,e]=react.useState(false),[t,n]=react.useState(null),s=react.useCallback(async c=>{if(c)try{await navigator.clipboard.writeText(c),e(!0),n(null),setTimeout(()=>e(!1),r);}catch(l){let a=l instanceof Error?l:new Error("Failed to copy text.");console.error(a),n(a),setTimeout(()=>n(null),r);}},[r]);return {isCopied:o,copy:s,error:t}}function C(...r){return tailwindMerge.twMerge(clsx.clsx(r))}var i=r=>r&&typeof r=="object"&&!Array.isArray(r);function y(r,o){let e={...r};return i(r)&&i(o)&&Object.keys(o).forEach(t=>{let n=r[t],s=o[t];i(n)&&i(s)?e[t]=y(n,s):e[t]=s;}),e}function j(r,o,e){if(!r)return "";if(r.length<=o+e)return r;let t=r.slice(0,o),n=r.slice(r.length-e);return `${t}...${n}`}exports.cn=C;exports.deepMerge=y;exports.textCenterEllipsis=j;exports.useCopyToClipboard=g;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';var react=require('react'),clsx=require('clsx'),tailwindMerge=require('tailwind-merge');function w(e=2e3){let[o,t]=react.useState(false),[n,r]=react.useState(null),s=react.useCallback(async a=>{if(a)try{await navigator.clipboard.writeText(a),t(!0),r(null),setTimeout(()=>t(!1),e);}catch(c){let l=c instanceof Error?c:new Error("Failed to copy text.");console.error(l),r(l),setTimeout(()=>r(null),e);}},[e]);return {isCopied:o,copy:s,error:n}}function E(e){let o=r=>typeof window<"u"?window.matchMedia(r).matches:false,[t,n]=react.useState(o(e));return react.useEffect(()=>{let r=window.matchMedia(e),s=()=>n(r.matches);return s(),window.addEventListener("resize",s),()=>window.removeEventListener("resize",s)},[e]),t}function v(...e){return tailwindMerge.twMerge(clsx.clsx(e))}var i=e=>e&&typeof e=="object"&&!Array.isArray(e);function g(e,o){let t={...e};return i(e)&&i(o)&&Object.keys(o).forEach(n=>{let r=e[n],s=o[n];i(r)&&i(s)?t[n]=g(r,s):t[n]=s;}),t}function R(e,o,t){if(!e)return "";if(e.length<=o+t)return e;let n=e.slice(0,o),r=e.slice(e.length-t);return `${n}...${r}`}exports.cn=v;exports.deepMerge=g;exports.textCenterEllipsis=R;exports.useCopyToClipboard=w;exports.useMediaQuery=E;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useCopyToClipboard.ts","../src/utils/cn.ts","../src/utils/deepMerge.ts","../src/utils/textCenterEllipsis.ts"],"names":["useCopyToClipboard","timeout","isCopied","setIsCopied","useState","error","setError","copy","useCallback","text","e","copyError","cn","inputs","twMerge","clsx","isObject","item","deepMerge","target","source","output","key","targetValue","sourceValue","textCenterEllipsis","str","from","to","start","end"],"mappings":"qGA6BO,SAASA,CAAAA,CAAmBC,CAAAA,CAAU,GAAA,CAI3C,CACA,GAAM,CAACC,CAAAA,CAAUC,CAAW,CAAA,CAAIC,cAAAA,CAAS,KAAK,CAAA,CACxC,CAACC,CAAAA,CAAOC,CAAQ,CAAA,CAAIF,cAAAA,CAAuB,IAAI,CAAA,CAE/CG,EAAOC,iBAAAA,CACX,MAAOC,CAAAA,EAAiB,CACtB,GAAKA,CAAAA,CAEL,GAAI,CACF,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAUA,CAAI,EACxCN,CAAAA,CAAY,CAAA,CAAI,CAAA,CAChBG,CAAAA,CAAS,IAAI,CAAA,CAEb,WAAW,IAAMH,CAAAA,CAAY,CAAA,CAAK,CAAA,CAAGF,CAAO,EAC9C,OAASS,CAAAA,CAAG,CACV,IAAMC,CAAAA,CAAYD,CAAAA,YAAa,KAAA,CAAQA,EAAI,IAAI,KAAA,CAAM,sBAAsB,CAAA,CAC3E,OAAA,CAAQ,KAAA,CAAMC,CAAS,CAAA,CACvBL,CAAAA,CAASK,CAAS,CAAA,CAGlB,UAAA,CAAW,IAAML,EAAS,IAAI,CAAA,CAAGL,CAAO,EAC1C,CACF,CAAA,CACA,CAACA,CAAO,CACV,CAAA,CAEA,OAAO,CAAE,QAAA,CAAAC,EAAU,IAAA,CAAAK,CAAAA,CAAM,KAAA,CAAAF,CAAM,CACjC,CCpCO,SAASO,CAAAA,CAAAA,GAAMC,CAAAA,CAA8B,CAClD,OAAOC,qBAAAA,CAAQC,UAAKF,CAAM,CAAC,CAC7B,CChBA,IAAMG,EAAYC,CAAAA,EACTA,CAAAA,EAAQ,OAAOA,CAAAA,EAAS,QAAA,EAAY,CAAC,MAAM,OAAA,CAAQA,CAAI,CAAA,CAkBzD,SAASC,CAAAA,CAA4BC,CAAAA,CAAWC,EAAuB,CAE5E,IAAMC,CAAAA,CAAS,CAAE,GAAGF,CAAO,EAE3B,OAAIH,CAAAA,CAASG,CAAM,CAAA,EAAKH,CAAAA,CAASI,CAAM,GAErC,MAAA,CAAO,IAAA,CAAKA,CAAM,CAAA,CAAE,OAAA,CAASE,CAAAA,EAAQ,CACnC,IAAMC,CAAAA,CAAcJ,CAAAA,CAAOG,CAAc,CAAA,CACnCE,CAAAA,CAAcJ,EAAOE,CAAc,CAAA,CAGrCN,CAAAA,CAASO,CAAW,CAAA,EAAKP,CAAAA,CAASQ,CAAW,CAAA,CAG9CH,CAAAA,CAA+BC,CAAG,CAAA,CAAIJ,CAAAA,CAAUK,CAAAA,CAAaC,CAAW,CAAA,CAGxEH,CAAAA,CAA+BC,CAAG,CAAA,CAAIE,EAE3C,CAAC,EAGIH,CACT,CCjCO,SAASI,CAAAA,CAAmBC,CAAAA,CAAgCC,CAAAA,CAAcC,EAAoB,CACnG,GAAI,CAACF,CAAAA,CACH,OAAO,EAAA,CAIT,GAAIA,CAAAA,CAAI,MAAA,EAAUC,CAAAA,CAAOC,CAAAA,CACvB,OAAOF,CAAAA,CAGT,IAAMG,CAAAA,CAAQH,CAAAA,CAAI,KAAA,CAAM,CAAA,CAAGC,CAAI,CAAA,CACzBG,EAAMJ,CAAAA,CAAI,KAAA,CAAMA,CAAAA,CAAI,MAAA,CAASE,CAAE,CAAA,CAErC,OAAO,CAAA,EAAGC,CAAK,CAAA,GAAA,EAAMC,CAAG,CAAA,CAC1B","file":"index.cjs","sourcesContent":["/**\n * @file This file contains a custom React hook for copying text to the clipboard.\n */\n\nimport { useCallback, useState } from 'react';\n\n/**\n * A custom React hook that provides functionality to copy text to the clipboard.\n * It also manages a \"copied\" state with a timeout for user feedback.\n *\n * @param {number} [timeout=2000] - The duration in milliseconds to keep the `isCopied` state as true.\n * @returns {{\n * isCopied: boolean;\n * copy: (text: string) => Promise<void>;\n * error: Error | null;\n * }} An object containing the `isCopied` state, the `copy` function, and any potential error.\n *\n * @example\n * const MyComponent = () => {\n * const { isCopied, copy } = useCopyToClipboard();\n * const textToCopy = '0x123...';\n *\n * return (\n * <button onClick={() => copy(textToCopy)}>\n * {isCopied ? 'Copied!' : 'Copy Address'}\n * </button>\n * );\n * }\n */\nexport function useCopyToClipboard(timeout = 2000): {\n isCopied: boolean;\n copy: (text: string) => Promise<void>;\n error: Error | null;\n} {\n const [isCopied, setIsCopied] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const copy = useCallback(\n async (text: string) => {\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n setIsCopied(true);\n setError(null);\n\n setTimeout(() => setIsCopied(false), timeout);\n } catch (e) {\n const copyError = e instanceof Error ? e : new Error('Failed to copy text.');\n console.error(copyError);\n setError(copyError);\n\n // Reset error state after timeout as well\n setTimeout(() => setError(null), timeout);\n }\n },\n [timeout],\n );\n\n return { isCopied, copy, error };\n}\n","/**\n * @file This file contains a utility function for conditionally merging Tailwind CSS classes.\n */\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * A utility function to conditionally join class names together and resolve\n * conflicting Tailwind CSS classes.\n *\n * It combines the functionality of `clsx` and `tailwind-merge`.\n *\n * @param {...ClassValue[]} inputs - A list of class values to be combined.\n * This can include strings, numbers, objects, arrays, and booleans.\n * @returns {string} The final, merged class name string.\n *\n * @example\n * cn('p-4', 'bg-red-500', { 'font-bold': true }); // => 'p-4 bg-red-500 font-bold'\n * cn('p-2', 'p-4'); // => 'p-4' (tailwind-merge resolves the conflict)\n *\n * @see https://github.com/dcastil/tailwind-merge\n * @see https://github.com/lukeed/clsx\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * @file This file contains a utility for performing a deep (recursive) merge of two objects.\n */\n\n/**\n * Checks if the provided item is a plain object (i.e., not null and not an array).\n *\n * @param {any} item - The item to check.\n * @returns {item is Record<string, any>} True if the item is a plain object, otherwise false.\n */\nconst isObject = (item: any): item is Record<string, any> => {\n return item && typeof item === 'object' && !Array.isArray(item);\n};\n\n/**\n * Recursively merges the properties of a source object into a target object.\n * This function creates a new object and does not mutate the original target.\n *\n * @template T - The type of the objects being merged.\n * @param {T} target - The base object.\n * @param {Partial<T>} source - The object with properties to merge into the target.\n * @returns {T} A new object representing the merged result.\n *\n * @example\n * const defaults = { a: 1, b: { c: 2, d: 3 } };\n * const custom = { b: { c: 99 } };\n * const result = deepMerge(defaults, custom);\n * // result will be { a: 1, b: { c: 99, d: 3 } }\n */\nexport function deepMerge<T extends object>(target: T, source: Partial<T>): T {\n // Start with a shallow copy of the target to avoid mutation.\n const output = { ...target };\n\n if (isObject(target) && isObject(source)) {\n // Iterate over the keys in the source object.\n Object.keys(source).forEach((key) => {\n const targetValue = target[key as keyof T];\n const sourceValue = source[key as keyof T];\n\n // If the value is an object in both target and source, merge them recursively.\n if (isObject(targetValue) && isObject(sourceValue)) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n (output as Record<string, any>)[key] = deepMerge(targetValue, sourceValue);\n } else {\n // Otherwise, the source value overwrites the target value.\n (output as Record<string, any>)[key] = sourceValue;\n }\n });\n }\n\n return output;\n}\n","/**\n * @file This file contains a utility function for truncating a string with a center ellipsis.\n */\n\n/**\n * Truncates a string by showing a specified number of characters from the start and end,\n * with an ellipsis in the middle. If the string is too short to be truncated, it's returned as is.\n *\n * @param {string} str - The string to truncate.\n * @param {number} from - The number of characters to show from the beginning of the string.\n * @param {number} to - The number of characters to show from the end of the string.\n * @returns {string} The truncated string, or the original string if it's too short.\n *\n * @example\n * const hash = '0x1234567890abcdef1234567890abcdef';\n * textCenterEllipsis(hash, 6, 4); // => \"0x1234...cdef\"\n *\n * textCenterEllipsis('short', 6, 4); // => \"short\"\n */\nexport function textCenterEllipsis(str: string | undefined | null, from: number, to: number): string {\n if (!str) {\n return '';\n }\n\n // If the string is short enough, don't truncate it.\n if (str.length <= from + to) {\n return str;\n }\n\n const start = str.slice(0, from);\n const end = str.slice(str.length - to);\n\n return `${start}...${end}`;\n}\n"]}
1
+ {"version":3,"sources":["../src/hooks/useCopyToClipboard.ts","../src/hooks/useMediaQuery.ts","../src/utils/cn.ts","../src/utils/deepMerge.ts","../src/utils/textCenterEllipsis.ts"],"names":["useCopyToClipboard","timeout","isCopied","setIsCopied","useState","error","setError","copy","useCallback","text","e","copyError","useMediaQuery","query","getMatches","q","matches","setMatches","useEffect","media","listener","cn","inputs","twMerge","clsx","isObject","item","deepMerge","target","source","output","key","targetValue","sourceValue","textCenterEllipsis","str","from","to","start","end"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @file This file contains a custom React hook for copying text to the clipboard.\n */\n\nimport { useCallback, useState } from 'react';\n\n/**\n * A custom React hook that provides functionality to copy text to the clipboard.\n * It also manages a \"copied\" state with a timeout for user feedback.\n *\n * @param {number} [timeout=2000] - The duration in milliseconds to keep the `isCopied` state as true.\n * @returns {{\n * isCopied: boolean;\n * copy: (text: string) => Promise<void>;\n * error: Error | null;\n * }} An object containing the `isCopied` state, the `copy` function, and any potential error.\n *\n * @example\n * const MyComponent = () => {\n * const { isCopied, copy } = useCopyToClipboard();\n * const textToCopy = '0x123...';\n *\n * return (\n * <button onClick={() => copy(textToCopy)}>\n * {isCopied ? 'Copied!' : 'Copy Address'}\n * </button>\n * );\n * }\n */\nexport function useCopyToClipboard(timeout = 2000): {\n isCopied: boolean;\n copy: (text: string) => Promise<void>;\n error: Error | null;\n} {\n const [isCopied, setIsCopied] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const copy = useCallback(\n async (text: string) => {\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n setIsCopied(true);\n setError(null);\n\n setTimeout(() => setIsCopied(false), timeout);\n } catch (e) {\n const copyError = e instanceof Error ? e : new Error('Failed to copy text.');\n console.error(copyError);\n setError(copyError);\n\n // Reset error state after timeout as well\n setTimeout(() => setError(null), timeout);\n }\n },\n [timeout],\n );\n\n return { isCopied, copy, error };\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * A custom hook to detect if a media query matches the current screen dimensions.\n * Handles SSR gracefully.\n * @param {string} query - The media query string (e.g., '(max-width: 767px)').\n * @returns {boolean} Whether the query matches or not.\n */\nexport function useMediaQuery(query: string): boolean {\n const getMatches = (q: string): boolean => {\n if (typeof window !== 'undefined') {\n return window.matchMedia(q).matches;\n }\n return false;\n };\n\n const [matches, setMatches] = useState<boolean>(getMatches(query));\n\n useEffect(() => {\n const media = window.matchMedia(query);\n const listener = () => setMatches(media.matches);\n\n // Re-check on mount and subscribe to changes\n listener();\n window.addEventListener('resize', listener);\n\n return () => window.removeEventListener('resize', listener);\n }, [query]);\n\n return matches;\n}\n","/**\n * @file This file contains a utility function for conditionally merging Tailwind CSS classes.\n */\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * A utility function to conditionally join class names together and resolve\n * conflicting Tailwind CSS classes.\n *\n * It combines the functionality of `clsx` and `tailwind-merge`.\n *\n * @param {...ClassValue[]} inputs - A list of class values to be combined.\n * This can include strings, numbers, objects, arrays, and booleans.\n * @returns {string} The final, merged class name string.\n *\n * @example\n * cn('p-4', 'bg-red-500', { 'font-bold': true }); // => 'p-4 bg-red-500 font-bold'\n * cn('p-2', 'p-4'); // => 'p-4' (tailwind-merge resolves the conflict)\n *\n * @see https://github.com/dcastil/tailwind-merge\n * @see https://github.com/lukeed/clsx\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * @file This file contains a utility for performing a deep (recursive) merge of two objects.\n */\n\n/**\n * Checks if the provided item is a plain object (i.e., not null and not an array).\n *\n * @param {any} item - The item to check.\n * @returns {item is Record<string, any>} True if the item is a plain object, otherwise false.\n */\nconst isObject = (item: any): item is Record<string, any> => {\n return item && typeof item === 'object' && !Array.isArray(item);\n};\n\n/**\n * Recursively merges the properties of a source object into a target object.\n * This function creates a new object and does not mutate the original target.\n *\n * @template T - The type of the objects being merged.\n * @param {T} target - The base object.\n * @param {Partial<T>} source - The object with properties to merge into the target.\n * @returns {T} A new object representing the merged result.\n *\n * @example\n * const defaults = { a: 1, b: { c: 2, d: 3 } };\n * const custom = { b: { c: 99 } };\n * const result = deepMerge(defaults, custom);\n * // result will be { a: 1, b: { c: 99, d: 3 } }\n */\nexport function deepMerge<T extends object>(target: T, source: Partial<T>): T {\n // Start with a shallow copy of the target to avoid mutation.\n const output = { ...target };\n\n if (isObject(target) && isObject(source)) {\n // Iterate over the keys in the source object.\n Object.keys(source).forEach((key) => {\n const targetValue = target[key as keyof T];\n const sourceValue = source[key as keyof T];\n\n // If the value is an object in both target and source, merge them recursively.\n if (isObject(targetValue) && isObject(sourceValue)) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n (output as Record<string, any>)[key] = deepMerge(targetValue, sourceValue);\n } else {\n // Otherwise, the source value overwrites the target value.\n (output as Record<string, any>)[key] = sourceValue;\n }\n });\n }\n\n return output;\n}\n","/**\n * @file This file contains a utility function for truncating a string with a center ellipsis.\n */\n\n/**\n * Truncates a string by showing a specified number of characters from the start and end,\n * with an ellipsis in the middle. If the string is too short to be truncated, it's returned as is.\n *\n * @param {string} str - The string to truncate.\n * @param {number} from - The number of characters to show from the beginning of the string.\n * @param {number} to - The number of characters to show from the end of the string.\n * @returns {string} The truncated string, or the original string if it's too short.\n *\n * @example\n * const hash = '0x1234567890abcdef1234567890abcdef';\n * textCenterEllipsis(hash, 6, 4); // => \"0x1234...cdef\"\n *\n * textCenterEllipsis('short', 6, 4); // => \"short\"\n */\nexport function textCenterEllipsis(str: string | undefined | null, from: number, to: number): string {\n if (!str) {\n return '';\n }\n\n // If the string is short enough, don't truncate it.\n if (str.length <= from + to) {\n return str;\n }\n\n const start = str.slice(0, from);\n const end = str.slice(str.length - to);\n\n return `${start}...${end}`;\n}\n"]}
package/dist/index.css CHANGED
@@ -1,309 +1,73 @@
1
- /*! tailwindcss v4.1.12 | MIT License | https://tailwindcss.com */
2
- @layer properties;
3
- @layer theme, base, components, utilities;
4
- @layer theme {
5
- :root, :host {
6
- --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji",
7
- "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
8
- --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono",
9
- "Courier New", monospace;
10
- --color-red-500: oklch(63.7% 0.237 25.331);
11
- --color-white: #fff;
12
- --spacing: 0.25rem;
13
- --font-weight-semibold: 600;
14
- --font-weight-bold: 700;
15
- --radius-md: 0.375rem;
16
- --default-transition-duration: 150ms;
17
- --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
18
- --default-font-family: var(--font-sans);
19
- --default-mono-font-family: var(--font-mono);
20
- }
21
- }
22
- @layer base {
23
- *, ::after, ::before, ::backdrop, ::file-selector-button {
24
- box-sizing: border-box;
25
- margin: 0;
26
- padding: 0;
27
- border: 0 solid;
28
- }
29
- html, :host {
30
- line-height: 1.5;
31
- -webkit-text-size-adjust: 100%;
32
- -moz-tab-size: 4;
33
- -o-tab-size: 4;
34
- tab-size: 4;
35
- font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");
36
- font-feature-settings: var(--default-font-feature-settings, normal);
37
- font-variation-settings: var(--default-font-variation-settings, normal);
38
- -webkit-tap-highlight-color: transparent;
39
- }
40
- hr {
41
- height: 0;
42
- color: inherit;
43
- border-top-width: 1px;
44
- }
45
- abbr:where([title]) {
46
- -webkit-text-decoration: underline dotted;
47
- text-decoration: underline dotted;
48
- }
49
- h1, h2, h3, h4, h5, h6 {
50
- font-size: inherit;
51
- font-weight: inherit;
52
- }
53
- a {
54
- color: inherit;
55
- -webkit-text-decoration: inherit;
56
- text-decoration: inherit;
57
- }
58
- b, strong {
59
- font-weight: bolder;
60
- }
61
- code, kbd, samp, pre {
62
- font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);
63
- font-feature-settings: var(--default-mono-font-feature-settings, normal);
64
- font-variation-settings: var(--default-mono-font-variation-settings, normal);
65
- font-size: 1em;
66
- }
67
- small {
68
- font-size: 80%;
69
- }
70
- sub, sup {
71
- font-size: 75%;
72
- line-height: 0;
73
- position: relative;
74
- vertical-align: baseline;
75
- }
76
- sub {
77
- bottom: -0.25em;
78
- }
79
- sup {
80
- top: -0.5em;
81
- }
82
- table {
83
- text-indent: 0;
84
- border-color: inherit;
85
- border-collapse: collapse;
86
- }
87
- :-moz-focusring {
88
- outline: auto;
89
- }
90
- progress {
91
- vertical-align: baseline;
92
- }
93
- summary {
94
- display: list-item;
95
- }
96
- ol, ul, menu {
97
- list-style: none;
98
- }
99
- img, svg, video, canvas, audio, iframe, embed, object {
100
- display: block;
101
- vertical-align: middle;
102
- }
103
- img, video {
104
- max-width: 100%;
105
- height: auto;
106
- }
107
- button, input, select, optgroup, textarea, ::file-selector-button {
108
- font: inherit;
109
- font-feature-settings: inherit;
110
- font-variation-settings: inherit;
111
- letter-spacing: inherit;
112
- color: inherit;
113
- border-radius: 0;
114
- background-color: transparent;
115
- opacity: 1;
116
- }
117
- :where(select:is([multiple], [size])) optgroup {
118
- font-weight: bolder;
119
- }
120
- :where(select:is([multiple], [size])) optgroup option {
121
- padding-inline-start: 20px;
122
- }
123
- ::file-selector-button {
124
- margin-inline-end: 4px;
125
- }
126
- ::-moz-placeholder {
127
- opacity: 1;
128
- }
129
- ::placeholder {
130
- opacity: 1;
131
- }
132
- @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) {
133
- ::-moz-placeholder {
134
- color: currentcolor;
135
- @supports (color: color-mix(in lab, red, red)) {
136
- color: color-mix(in oklab, currentcolor 50%, transparent);
137
- }
138
- }
139
- ::placeholder {
140
- color: currentcolor;
141
- @supports (color: color-mix(in lab, red, red)) {
142
- color: color-mix(in oklab, currentcolor 50%, transparent);
143
- }
144
- }
145
- }
146
- textarea {
147
- resize: vertical;
148
- }
149
- ::-webkit-search-decoration {
150
- -webkit-appearance: none;
151
- }
152
- ::-webkit-date-and-time-value {
153
- min-height: 1lh;
154
- text-align: inherit;
155
- }
156
- ::-webkit-datetime-edit {
157
- display: inline-flex;
158
- }
159
- ::-webkit-datetime-edit-fields-wrapper {
160
- padding: 0;
161
- }
162
- ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field {
163
- padding-block: 0;
164
- }
165
- ::-webkit-calendar-picker-indicator {
166
- line-height: 1;
167
- }
168
- :-moz-ui-invalid {
169
- box-shadow: none;
170
- }
171
- button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button {
172
- -webkit-appearance: button;
173
- -moz-appearance: button;
174
- appearance: button;
175
- }
176
- ::-webkit-inner-spin-button, ::-webkit-outer-spin-button {
177
- height: auto;
178
- }
179
- [hidden]:where(:not([hidden="until-found"])) {
180
- display: none !important;
181
- }
182
- }
183
- @layer utilities {
184
- .contents {
185
- display: contents;
186
- }
187
- .truncate {
188
- overflow: hidden;
189
- text-overflow: ellipsis;
190
- white-space: nowrap;
191
- }
192
- .rounded-md {
193
- border-radius: var(--radius-md);
194
- }
195
- .bg-\[var\(--tuwa-color-primary\)\] {
196
- background-color: var(--tuwa-color-primary);
197
- }
198
- .bg-red-500 {
199
- background-color: var(--color-red-500);
200
- }
201
- .p-2 {
202
- padding: calc(var(--spacing) * 2);
203
- }
204
- .p-4 {
205
- padding: calc(var(--spacing) * 4);
206
- }
207
- .p-\[var\(--tuwa-spacing-md\)\] {
208
- padding: var(--tuwa-spacing-md);
209
- }
210
- .px-4 {
211
- padding-inline: calc(var(--spacing) * 4);
212
- }
213
- .py-2 {
214
- padding-block: calc(var(--spacing) * 2);
215
- }
216
- .font-bold {
217
- --tw-font-weight: var(--font-weight-bold);
218
- font-weight: var(--font-weight-bold);
219
- }
220
- .font-semibold {
221
- --tw-font-weight: var(--font-weight-semibold);
222
- font-weight: var(--font-weight-semibold);
223
- }
224
- .text-\[var\(--tuwa-color-foreground\)\] {
225
- color: var(--tuwa-color-foreground);
226
- }
227
- .text-white {
228
- color: var(--color-white);
229
- }
230
- .transition-colors {
231
- transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to;
232
- transition-timing-function: var(--tw-ease, var(--default-transition-timing-function));
233
- transition-duration: var(--tw-duration, var(--default-transition-duration));
234
- }
235
- .hover\:opacity-90 {
236
- &:hover {
237
- @media (hover: hover) {
238
- opacity: 90%;
239
- }
240
- }
241
- }
242
- }
243
1
  :root {
244
- --tuwa-success-bg: oklch(96.2% 0.044 156.743);
245
- --tuwa-success-text: oklch(52.7% 0.154 150.069);
246
- --tuwa-success-icon: oklch(72.3% 0.219 149.579);
247
- --tuwa-error-bg: oklch(93.6% 0.032 17.717);
248
- --tuwa-error-text: oklch(50.5% 0.213 27.518);
249
- --tuwa-error-icon: oklch(63.7% 0.237 25.331);
250
- --tuwa-pending-bg: oklch(97.3% 0.071 103.193);
251
- --tuwa-pending-text: oklch(47.6% 0.114 61.907);
252
- --tuwa-pending-icon: oklch(68.1% 0.162 75.834);
253
- --tuwa-info-bg: oklch(96.7% 0.003 264.542);
254
- --tuwa-info-text: oklch(44.6% 0.03 256.802);
255
- --tuwa-info-icon: oklch(55.1% 0.027 264.364);
256
- --tuwa-text-primary: oklch(21% 0.034 264.665);
257
- --tuwa-text-secondary: oklch(55.1% 0.027 264.364);
258
- --tuwa-text-tertiary: oklch(70.7% 0.022 261.325);
259
- --tuwa-text-accent: oklch(54.6% 0.245 262.881);
260
- --tuwa-text-on-accent: #fff;
261
- --tuwa-bg-primary: #fff;
262
- --tuwa-bg-secondary: oklch(98.5% 0.002 247.839);
263
- --tuwa-bg-muted: oklch(96.7% 0.003 264.542);
264
- --tuwa-border-primary: oklch(92.8% 0.006 264.531);
265
- --tuwa-border-secondary: oklch(96.7% 0.003 264.542);
266
- --tuwa-button-gradient-from: oklch(54.6% 0.245 262.881);
267
- --tuwa-button-gradient-to: oklch(55.8% 0.288 302.321);
268
- --tuwa-button-gradient-from-hover: oklch(48.8% 0.243 264.376);
269
- --tuwa-button-gradient-to-hover: oklch(49.6% 0.265 301.924);
2
+ --tuwa-success-bg: theme('colors.green.100');
3
+ --tuwa-success-text: theme('colors.green.700');
4
+ --tuwa-success-icon: theme('colors.green.500');
5
+
6
+ --tuwa-error-bg: theme('colors.red.100');
7
+ --tuwa-error-text: theme('colors.red.700');
8
+ --tuwa-error-icon: theme('colors.red.500');
9
+
10
+ --tuwa-pending-bg: theme('colors.yellow.100');
11
+ --tuwa-pending-text: theme('colors.yellow.800');
12
+ --tuwa-pending-icon: theme('colors.yellow.600');
13
+
14
+ --tuwa-info-bg: theme('colors.gray.100');
15
+ --tuwa-info-text: theme('colors.gray.600');
16
+ --tuwa-info-icon: theme('colors.gray.500');
17
+
18
+ --tuwa-text-primary: theme('colors.gray.900');
19
+ --tuwa-text-secondary: theme('colors.gray.500');
20
+ --tuwa-text-tertiary: theme('colors.gray.400');
21
+ --tuwa-text-accent: theme('colors.blue.600');
22
+ --tuwa-text-on-accent: theme('colors.white');
23
+
24
+ --tuwa-bg-primary: theme('colors.white');
25
+ --tuwa-bg-secondary: theme('colors.gray.50');
26
+ --tuwa-bg-muted: theme('colors.gray.100');
27
+
28
+ --tuwa-border-primary: theme('colors.gray.200');
29
+ --tuwa-border-secondary: theme('colors.gray.100');
30
+
31
+ --tuwa-button-gradient-from: theme('colors.blue.600');
32
+ --tuwa-button-gradient-to: theme('colors.purple.600');
33
+
34
+ --tuwa-button-gradient-from-hover: theme('colors.blue.700');
35
+ --tuwa-button-gradient-to-hover: theme('colors.purple.700');
270
36
  }
37
+
271
38
  .dark {
272
- --tuwa-success-bg: oklch(39.3% 0.095 152.535);
273
- --tuwa-success-text: oklch(87.1% 0.15 154.449);
274
- --tuwa-success-icon: oklch(79.2% 0.209 151.711);
275
- --tuwa-error-bg: oklch(39.6% 0.141 25.723);
276
- --tuwa-error-text: oklch(80.8% 0.114 19.571);
277
- --tuwa-error-icon: oklch(70.4% 0.191 22.216);
278
- --tuwa-pending-bg: oklch(42.1% 0.095 57.708);
279
- --tuwa-pending-text: oklch(90.5% 0.182 98.111);
280
- --tuwa-pending-icon: oklch(85.2% 0.199 91.936);
281
- --tuwa-info-bg: oklch(37.3% 0.034 259.733);
282
- --tuwa-info-text: oklch(87.2% 0.01 258.338);
283
- --tuwa-info-icon: oklch(70.7% 0.022 261.325);
284
- --tuwa-text-primary: oklch(98.5% 0.002 247.839);
285
- --tuwa-text-secondary: oklch(70.7% 0.022 261.325);
286
- --tuwa-text-tertiary: oklch(55.1% 0.027 264.364);
287
- --tuwa-text-accent: oklch(70.7% 0.165 254.624);
288
- --tuwa-text-on-accent: #fff;
289
- --tuwa-bg-primary: oklch(21% 0.034 264.665);
290
- --tuwa-bg-secondary: oklch(27.8% 0.033 256.848);
291
- --tuwa-bg-muted: oklch(37.3% 0.034 259.733);
292
- --tuwa-border-primary: oklch(37.3% 0.034 259.733);
293
- --tuwa-border-secondary: oklch(27.8% 0.033 256.848);
294
- --tuwa-button-gradient-from: oklch(62.3% 0.214 259.815);
295
- --tuwa-button-gradient-to: oklch(62.7% 0.265 303.9);
296
- --tuwa-button-gradient-from-hover: oklch(54.6% 0.245 262.881);
297
- --tuwa-button-gradient-to-hover: oklch(55.8% 0.288 302.321);
298
- }
299
- @property --tw-font-weight {
300
- syntax: "*";
301
- inherits: false;
302
- }
303
- @layer properties {
304
- @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
305
- *, ::before, ::after, ::backdrop {
306
- --tw-font-weight: initial;
307
- }
308
- }
39
+ --tuwa-success-bg: theme('colors.green.900');
40
+ --tuwa-success-text: theme('colors.green.300');
41
+ --tuwa-success-icon: theme('colors.green.400');
42
+
43
+ --tuwa-error-bg: theme('colors.red.900');
44
+ --tuwa-error-text: theme('colors.red.300');
45
+ --tuwa-error-icon: theme('colors.red.400');
46
+
47
+ --tuwa-pending-bg: theme('colors.yellow.900');
48
+ --tuwa-pending-text: theme('colors.yellow.300');
49
+ --tuwa-pending-icon: theme('colors.yellow.400');
50
+
51
+ --tuwa-info-bg: theme('colors.gray.700');
52
+ --tuwa-info-text: theme('colors.gray.300');
53
+ --tuwa-info-icon: theme('colors.gray.400');
54
+
55
+ --tuwa-text-primary: theme('colors.gray.50');
56
+ --tuwa-text-secondary: theme('colors.gray.400');
57
+ --tuwa-text-tertiary: theme('colors.gray.500');
58
+ --tuwa-text-accent: theme('colors.blue.400');
59
+ --tuwa-text-on-accent: theme('colors.white');
60
+
61
+ --tuwa-bg-primary: theme('colors.gray.900');
62
+ --tuwa-bg-secondary: theme('colors.gray.800');
63
+ --tuwa-bg-muted: theme('colors.gray.700');
64
+
65
+ --tuwa-border-primary: theme('colors.gray.700');
66
+ --tuwa-border-secondary: theme('colors.gray.800');
67
+
68
+ --tuwa-button-gradient-from: theme('colors.blue.500');
69
+ --tuwa-button-gradient-to: theme('colors.purple.500');
70
+
71
+ --tuwa-button-gradient-from-hover: theme('colors.blue.600');
72
+ --tuwa-button-gradient-to-hover: theme('colors.purple.600');
309
73
  }
package/dist/index.d.cts CHANGED
@@ -32,6 +32,14 @@ declare function useCopyToClipboard(timeout?: number): {
32
32
  error: Error | null;
33
33
  };
34
34
 
35
+ /**
36
+ * A custom hook to detect if a media query matches the current screen dimensions.
37
+ * Handles SSR gracefully.
38
+ * @param {string} query - The media query string (e.g., '(max-width: 767px)').
39
+ * @returns {boolean} Whether the query matches or not.
40
+ */
41
+ declare function useMediaQuery(query: string): boolean;
42
+
35
43
  /**
36
44
  * @file This file contains a utility function for conditionally merging Tailwind CSS classes.
37
45
  */
@@ -95,4 +103,4 @@ declare function deepMerge<T extends object>(target: T, source: Partial<T>): T;
95
103
  */
96
104
  declare function textCenterEllipsis(str: string | undefined | null, from: number, to: number): string;
97
105
 
98
- export { cn, deepMerge, textCenterEllipsis, useCopyToClipboard };
106
+ export { cn, deepMerge, textCenterEllipsis, useCopyToClipboard, useMediaQuery };
package/dist/index.d.ts CHANGED
@@ -32,6 +32,14 @@ declare function useCopyToClipboard(timeout?: number): {
32
32
  error: Error | null;
33
33
  };
34
34
 
35
+ /**
36
+ * A custom hook to detect if a media query matches the current screen dimensions.
37
+ * Handles SSR gracefully.
38
+ * @param {string} query - The media query string (e.g., '(max-width: 767px)').
39
+ * @returns {boolean} Whether the query matches or not.
40
+ */
41
+ declare function useMediaQuery(query: string): boolean;
42
+
35
43
  /**
36
44
  * @file This file contains a utility function for conditionally merging Tailwind CSS classes.
37
45
  */
@@ -95,4 +103,4 @@ declare function deepMerge<T extends object>(target: T, source: Partial<T>): T;
95
103
  */
96
104
  declare function textCenterEllipsis(str: string | undefined | null, from: number, to: number): string;
97
105
 
98
- export { cn, deepMerge, textCenterEllipsis, useCopyToClipboard };
106
+ export { cn, deepMerge, textCenterEllipsis, useCopyToClipboard, useMediaQuery };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import {useState,useCallback}from'react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';function g(r=2e3){let[o,e]=useState(false),[t,n]=useState(null),s=useCallback(async c=>{if(c)try{await navigator.clipboard.writeText(c),e(!0),n(null),setTimeout(()=>e(!1),r);}catch(l){let a=l instanceof Error?l:new Error("Failed to copy text.");console.error(a),n(a),setTimeout(()=>n(null),r);}},[r]);return {isCopied:o,copy:s,error:t}}function C(...r){return twMerge(clsx(r))}var i=r=>r&&typeof r=="object"&&!Array.isArray(r);function y(r,o){let e={...r};return i(r)&&i(o)&&Object.keys(o).forEach(t=>{let n=r[t],s=o[t];i(n)&&i(s)?e[t]=y(n,s):e[t]=s;}),e}function j(r,o,e){if(!r)return "";if(r.length<=o+e)return r;let t=r.slice(0,o),n=r.slice(r.length-e);return `${t}...${n}`}export{C as cn,y as deepMerge,j as textCenterEllipsis,g as useCopyToClipboard};//# sourceMappingURL=index.js.map
1
+ import {useState,useCallback,useEffect}from'react';import {clsx}from'clsx';import {twMerge}from'tailwind-merge';function w(e=2e3){let[o,t]=useState(false),[n,r]=useState(null),s=useCallback(async a=>{if(a)try{await navigator.clipboard.writeText(a),t(!0),r(null),setTimeout(()=>t(!1),e);}catch(c){let l=c instanceof Error?c:new Error("Failed to copy text.");console.error(l),r(l),setTimeout(()=>r(null),e);}},[e]);return {isCopied:o,copy:s,error:n}}function E(e){let o=r=>typeof window<"u"?window.matchMedia(r).matches:false,[t,n]=useState(o(e));return useEffect(()=>{let r=window.matchMedia(e),s=()=>n(r.matches);return s(),window.addEventListener("resize",s),()=>window.removeEventListener("resize",s)},[e]),t}function v(...e){return twMerge(clsx(e))}var i=e=>e&&typeof e=="object"&&!Array.isArray(e);function g(e,o){let t={...e};return i(e)&&i(o)&&Object.keys(o).forEach(n=>{let r=e[n],s=o[n];i(r)&&i(s)?t[n]=g(r,s):t[n]=s;}),t}function R(e,o,t){if(!e)return "";if(e.length<=o+t)return e;let n=e.slice(0,o),r=e.slice(e.length-t);return `${n}...${r}`}export{v as cn,g as deepMerge,R as textCenterEllipsis,w as useCopyToClipboard,E as useMediaQuery};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hooks/useCopyToClipboard.ts","../src/utils/cn.ts","../src/utils/deepMerge.ts","../src/utils/textCenterEllipsis.ts"],"names":["useCopyToClipboard","timeout","isCopied","setIsCopied","useState","error","setError","copy","useCallback","text","e","copyError","cn","inputs","twMerge","clsx","isObject","item","deepMerge","target","source","output","key","targetValue","sourceValue","textCenterEllipsis","str","from","to","start","end"],"mappings":"sGA6BO,SAASA,CAAAA,CAAmBC,CAAAA,CAAU,GAAA,CAI3C,CACA,GAAM,CAACC,CAAAA,CAAUC,CAAW,CAAA,CAAIC,QAAAA,CAAS,KAAK,CAAA,CACxC,CAACC,CAAAA,CAAOC,CAAQ,CAAA,CAAIF,QAAAA,CAAuB,IAAI,CAAA,CAE/CG,EAAOC,WAAAA,CACX,MAAOC,CAAAA,EAAiB,CACtB,GAAKA,CAAAA,CAEL,GAAI,CACF,MAAM,SAAA,CAAU,SAAA,CAAU,SAAA,CAAUA,CAAI,EACxCN,CAAAA,CAAY,CAAA,CAAI,CAAA,CAChBG,CAAAA,CAAS,IAAI,CAAA,CAEb,WAAW,IAAMH,CAAAA,CAAY,CAAA,CAAK,CAAA,CAAGF,CAAO,EAC9C,OAASS,CAAAA,CAAG,CACV,IAAMC,CAAAA,CAAYD,CAAAA,YAAa,KAAA,CAAQA,EAAI,IAAI,KAAA,CAAM,sBAAsB,CAAA,CAC3E,OAAA,CAAQ,KAAA,CAAMC,CAAS,CAAA,CACvBL,CAAAA,CAASK,CAAS,CAAA,CAGlB,UAAA,CAAW,IAAML,EAAS,IAAI,CAAA,CAAGL,CAAO,EAC1C,CACF,CAAA,CACA,CAACA,CAAO,CACV,CAAA,CAEA,OAAO,CAAE,QAAA,CAAAC,EAAU,IAAA,CAAAK,CAAAA,CAAM,KAAA,CAAAF,CAAM,CACjC,CCpCO,SAASO,CAAAA,CAAAA,GAAMC,CAAAA,CAA8B,CAClD,OAAOC,OAAAA,CAAQC,KAAKF,CAAM,CAAC,CAC7B,CChBA,IAAMG,EAAYC,CAAAA,EACTA,CAAAA,EAAQ,OAAOA,CAAAA,EAAS,QAAA,EAAY,CAAC,MAAM,OAAA,CAAQA,CAAI,CAAA,CAkBzD,SAASC,CAAAA,CAA4BC,CAAAA,CAAWC,EAAuB,CAE5E,IAAMC,CAAAA,CAAS,CAAE,GAAGF,CAAO,EAE3B,OAAIH,CAAAA,CAASG,CAAM,CAAA,EAAKH,CAAAA,CAASI,CAAM,GAErC,MAAA,CAAO,IAAA,CAAKA,CAAM,CAAA,CAAE,OAAA,CAASE,CAAAA,EAAQ,CACnC,IAAMC,CAAAA,CAAcJ,CAAAA,CAAOG,CAAc,CAAA,CACnCE,CAAAA,CAAcJ,EAAOE,CAAc,CAAA,CAGrCN,CAAAA,CAASO,CAAW,CAAA,EAAKP,CAAAA,CAASQ,CAAW,CAAA,CAG9CH,CAAAA,CAA+BC,CAAG,CAAA,CAAIJ,CAAAA,CAAUK,CAAAA,CAAaC,CAAW,CAAA,CAGxEH,CAAAA,CAA+BC,CAAG,CAAA,CAAIE,EAE3C,CAAC,EAGIH,CACT,CCjCO,SAASI,CAAAA,CAAmBC,CAAAA,CAAgCC,CAAAA,CAAcC,EAAoB,CACnG,GAAI,CAACF,CAAAA,CACH,OAAO,EAAA,CAIT,GAAIA,CAAAA,CAAI,MAAA,EAAUC,CAAAA,CAAOC,CAAAA,CACvB,OAAOF,CAAAA,CAGT,IAAMG,CAAAA,CAAQH,CAAAA,CAAI,KAAA,CAAM,CAAA,CAAGC,CAAI,CAAA,CACzBG,EAAMJ,CAAAA,CAAI,KAAA,CAAMA,CAAAA,CAAI,MAAA,CAASE,CAAE,CAAA,CAErC,OAAO,CAAA,EAAGC,CAAK,CAAA,GAAA,EAAMC,CAAG,CAAA,CAC1B","file":"index.js","sourcesContent":["/**\n * @file This file contains a custom React hook for copying text to the clipboard.\n */\n\nimport { useCallback, useState } from 'react';\n\n/**\n * A custom React hook that provides functionality to copy text to the clipboard.\n * It also manages a \"copied\" state with a timeout for user feedback.\n *\n * @param {number} [timeout=2000] - The duration in milliseconds to keep the `isCopied` state as true.\n * @returns {{\n * isCopied: boolean;\n * copy: (text: string) => Promise<void>;\n * error: Error | null;\n * }} An object containing the `isCopied` state, the `copy` function, and any potential error.\n *\n * @example\n * const MyComponent = () => {\n * const { isCopied, copy } = useCopyToClipboard();\n * const textToCopy = '0x123...';\n *\n * return (\n * <button onClick={() => copy(textToCopy)}>\n * {isCopied ? 'Copied!' : 'Copy Address'}\n * </button>\n * );\n * }\n */\nexport function useCopyToClipboard(timeout = 2000): {\n isCopied: boolean;\n copy: (text: string) => Promise<void>;\n error: Error | null;\n} {\n const [isCopied, setIsCopied] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const copy = useCallback(\n async (text: string) => {\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n setIsCopied(true);\n setError(null);\n\n setTimeout(() => setIsCopied(false), timeout);\n } catch (e) {\n const copyError = e instanceof Error ? e : new Error('Failed to copy text.');\n console.error(copyError);\n setError(copyError);\n\n // Reset error state after timeout as well\n setTimeout(() => setError(null), timeout);\n }\n },\n [timeout],\n );\n\n return { isCopied, copy, error };\n}\n","/**\n * @file This file contains a utility function for conditionally merging Tailwind CSS classes.\n */\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * A utility function to conditionally join class names together and resolve\n * conflicting Tailwind CSS classes.\n *\n * It combines the functionality of `clsx` and `tailwind-merge`.\n *\n * @param {...ClassValue[]} inputs - A list of class values to be combined.\n * This can include strings, numbers, objects, arrays, and booleans.\n * @returns {string} The final, merged class name string.\n *\n * @example\n * cn('p-4', 'bg-red-500', { 'font-bold': true }); // => 'p-4 bg-red-500 font-bold'\n * cn('p-2', 'p-4'); // => 'p-4' (tailwind-merge resolves the conflict)\n *\n * @see https://github.com/dcastil/tailwind-merge\n * @see https://github.com/lukeed/clsx\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * @file This file contains a utility for performing a deep (recursive) merge of two objects.\n */\n\n/**\n * Checks if the provided item is a plain object (i.e., not null and not an array).\n *\n * @param {any} item - The item to check.\n * @returns {item is Record<string, any>} True if the item is a plain object, otherwise false.\n */\nconst isObject = (item: any): item is Record<string, any> => {\n return item && typeof item === 'object' && !Array.isArray(item);\n};\n\n/**\n * Recursively merges the properties of a source object into a target object.\n * This function creates a new object and does not mutate the original target.\n *\n * @template T - The type of the objects being merged.\n * @param {T} target - The base object.\n * @param {Partial<T>} source - The object with properties to merge into the target.\n * @returns {T} A new object representing the merged result.\n *\n * @example\n * const defaults = { a: 1, b: { c: 2, d: 3 } };\n * const custom = { b: { c: 99 } };\n * const result = deepMerge(defaults, custom);\n * // result will be { a: 1, b: { c: 99, d: 3 } }\n */\nexport function deepMerge<T extends object>(target: T, source: Partial<T>): T {\n // Start with a shallow copy of the target to avoid mutation.\n const output = { ...target };\n\n if (isObject(target) && isObject(source)) {\n // Iterate over the keys in the source object.\n Object.keys(source).forEach((key) => {\n const targetValue = target[key as keyof T];\n const sourceValue = source[key as keyof T];\n\n // If the value is an object in both target and source, merge them recursively.\n if (isObject(targetValue) && isObject(sourceValue)) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n (output as Record<string, any>)[key] = deepMerge(targetValue, sourceValue);\n } else {\n // Otherwise, the source value overwrites the target value.\n (output as Record<string, any>)[key] = sourceValue;\n }\n });\n }\n\n return output;\n}\n","/**\n * @file This file contains a utility function for truncating a string with a center ellipsis.\n */\n\n/**\n * Truncates a string by showing a specified number of characters from the start and end,\n * with an ellipsis in the middle. If the string is too short to be truncated, it's returned as is.\n *\n * @param {string} str - The string to truncate.\n * @param {number} from - The number of characters to show from the beginning of the string.\n * @param {number} to - The number of characters to show from the end of the string.\n * @returns {string} The truncated string, or the original string if it's too short.\n *\n * @example\n * const hash = '0x1234567890abcdef1234567890abcdef';\n * textCenterEllipsis(hash, 6, 4); // => \"0x1234...cdef\"\n *\n * textCenterEllipsis('short', 6, 4); // => \"short\"\n */\nexport function textCenterEllipsis(str: string | undefined | null, from: number, to: number): string {\n if (!str) {\n return '';\n }\n\n // If the string is short enough, don't truncate it.\n if (str.length <= from + to) {\n return str;\n }\n\n const start = str.slice(0, from);\n const end = str.slice(str.length - to);\n\n return `${start}...${end}`;\n}\n"]}
1
+ {"version":3,"sources":["../src/hooks/useCopyToClipboard.ts","../src/hooks/useMediaQuery.ts","../src/utils/cn.ts","../src/utils/deepMerge.ts","../src/utils/textCenterEllipsis.ts"],"names":["useCopyToClipboard","timeout","isCopied","setIsCopied","useState","error","setError","copy","useCallback","text","e","copyError","useMediaQuery","query","getMatches","q","matches","setMatches","useEffect","media","listener","cn","inputs","twMerge","clsx","isObject","item","deepMerge","target","source","output","key","targetValue","sourceValue","textCenterEllipsis","str","from","to","start","end"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @file This file contains a custom React hook for copying text to the clipboard.\n */\n\nimport { useCallback, useState } from 'react';\n\n/**\n * A custom React hook that provides functionality to copy text to the clipboard.\n * It also manages a \"copied\" state with a timeout for user feedback.\n *\n * @param {number} [timeout=2000] - The duration in milliseconds to keep the `isCopied` state as true.\n * @returns {{\n * isCopied: boolean;\n * copy: (text: string) => Promise<void>;\n * error: Error | null;\n * }} An object containing the `isCopied` state, the `copy` function, and any potential error.\n *\n * @example\n * const MyComponent = () => {\n * const { isCopied, copy } = useCopyToClipboard();\n * const textToCopy = '0x123...';\n *\n * return (\n * <button onClick={() => copy(textToCopy)}>\n * {isCopied ? 'Copied!' : 'Copy Address'}\n * </button>\n * );\n * }\n */\nexport function useCopyToClipboard(timeout = 2000): {\n isCopied: boolean;\n copy: (text: string) => Promise<void>;\n error: Error | null;\n} {\n const [isCopied, setIsCopied] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const copy = useCallback(\n async (text: string) => {\n if (!text) return;\n\n try {\n await navigator.clipboard.writeText(text);\n setIsCopied(true);\n setError(null);\n\n setTimeout(() => setIsCopied(false), timeout);\n } catch (e) {\n const copyError = e instanceof Error ? e : new Error('Failed to copy text.');\n console.error(copyError);\n setError(copyError);\n\n // Reset error state after timeout as well\n setTimeout(() => setError(null), timeout);\n }\n },\n [timeout],\n );\n\n return { isCopied, copy, error };\n}\n","import { useEffect, useState } from 'react';\n\n/**\n * A custom hook to detect if a media query matches the current screen dimensions.\n * Handles SSR gracefully.\n * @param {string} query - The media query string (e.g., '(max-width: 767px)').\n * @returns {boolean} Whether the query matches or not.\n */\nexport function useMediaQuery(query: string): boolean {\n const getMatches = (q: string): boolean => {\n if (typeof window !== 'undefined') {\n return window.matchMedia(q).matches;\n }\n return false;\n };\n\n const [matches, setMatches] = useState<boolean>(getMatches(query));\n\n useEffect(() => {\n const media = window.matchMedia(query);\n const listener = () => setMatches(media.matches);\n\n // Re-check on mount and subscribe to changes\n listener();\n window.addEventListener('resize', listener);\n\n return () => window.removeEventListener('resize', listener);\n }, [query]);\n\n return matches;\n}\n","/**\n * @file This file contains a utility function for conditionally merging Tailwind CSS classes.\n */\n\nimport { type ClassValue, clsx } from 'clsx';\nimport { twMerge } from 'tailwind-merge';\n\n/**\n * A utility function to conditionally join class names together and resolve\n * conflicting Tailwind CSS classes.\n *\n * It combines the functionality of `clsx` and `tailwind-merge`.\n *\n * @param {...ClassValue[]} inputs - A list of class values to be combined.\n * This can include strings, numbers, objects, arrays, and booleans.\n * @returns {string} The final, merged class name string.\n *\n * @example\n * cn('p-4', 'bg-red-500', { 'font-bold': true }); // => 'p-4 bg-red-500 font-bold'\n * cn('p-2', 'p-4'); // => 'p-4' (tailwind-merge resolves the conflict)\n *\n * @see https://github.com/dcastil/tailwind-merge\n * @see https://github.com/lukeed/clsx\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","/**\n * @file This file contains a utility for performing a deep (recursive) merge of two objects.\n */\n\n/**\n * Checks if the provided item is a plain object (i.e., not null and not an array).\n *\n * @param {any} item - The item to check.\n * @returns {item is Record<string, any>} True if the item is a plain object, otherwise false.\n */\nconst isObject = (item: any): item is Record<string, any> => {\n return item && typeof item === 'object' && !Array.isArray(item);\n};\n\n/**\n * Recursively merges the properties of a source object into a target object.\n * This function creates a new object and does not mutate the original target.\n *\n * @template T - The type of the objects being merged.\n * @param {T} target - The base object.\n * @param {Partial<T>} source - The object with properties to merge into the target.\n * @returns {T} A new object representing the merged result.\n *\n * @example\n * const defaults = { a: 1, b: { c: 2, d: 3 } };\n * const custom = { b: { c: 99 } };\n * const result = deepMerge(defaults, custom);\n * // result will be { a: 1, b: { c: 99, d: 3 } }\n */\nexport function deepMerge<T extends object>(target: T, source: Partial<T>): T {\n // Start with a shallow copy of the target to avoid mutation.\n const output = { ...target };\n\n if (isObject(target) && isObject(source)) {\n // Iterate over the keys in the source object.\n Object.keys(source).forEach((key) => {\n const targetValue = target[key as keyof T];\n const sourceValue = source[key as keyof T];\n\n // If the value is an object in both target and source, merge them recursively.\n if (isObject(targetValue) && isObject(sourceValue)) {\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-expect-error\n (output as Record<string, any>)[key] = deepMerge(targetValue, sourceValue);\n } else {\n // Otherwise, the source value overwrites the target value.\n (output as Record<string, any>)[key] = sourceValue;\n }\n });\n }\n\n return output;\n}\n","/**\n * @file This file contains a utility function for truncating a string with a center ellipsis.\n */\n\n/**\n * Truncates a string by showing a specified number of characters from the start and end,\n * with an ellipsis in the middle. If the string is too short to be truncated, it's returned as is.\n *\n * @param {string} str - The string to truncate.\n * @param {number} from - The number of characters to show from the beginning of the string.\n * @param {number} to - The number of characters to show from the end of the string.\n * @returns {string} The truncated string, or the original string if it's too short.\n *\n * @example\n * const hash = '0x1234567890abcdef1234567890abcdef';\n * textCenterEllipsis(hash, 6, 4); // => \"0x1234...cdef\"\n *\n * textCenterEllipsis('short', 6, 4); // => \"short\"\n */\nexport function textCenterEllipsis(str: string | undefined | null, from: number, to: number): string {\n if (!str) {\n return '';\n }\n\n // If the string is short enough, don't truncate it.\n if (str.length <= from + to) {\n return str;\n }\n\n const start = str.slice(0, from);\n const end = str.slice(str.length - to);\n\n return `${start}...${end}`;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuwaio/nova-core",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "private": false,
5
5
  "author": "Oleksandr Tkach",
6
6
  "license": "Apache-2.0",