torch-glare 1.0.9 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/shared/detectPackageManager.d.ts +1 -1
- package/dist/src/shared/detectPackageManager.d.ts.map +1 -1
- package/dist/src/shared/detectPackageManager.js +5 -1
- package/dist/src/shared/detectPackageManager.js.map +1 -1
- package/dist/src/shared/tailwindInit.d.ts.map +1 -1
- package/dist/src/shared/tailwindInit.js +21 -173
- package/dist/src/shared/tailwindInit.js.map +1 -1
- package/docs/Cover.png +0 -0
- package/docs/README.md +207 -0
- package/lib/components/AlertDialog.tsx +29 -18
- package/lib/components/BadgeField.tsx +3 -3
- package/lib/components/ImageAttachment.tsx +2 -0
- package/lib/components/Input.tsx +12 -10
- package/lib/components/Table.tsx +2 -1
- package/lib/hooks/useClickOutside.tsx +2 -3
- package/lib/hooks/useTagSelection.tsx +43 -8
- package/package.json +3 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Detect the package manager used in the project.
|
|
3
|
-
* @returns {string} - The detected package manager (pnpm, yarn,
|
|
3
|
+
* @returns {string} - The detected package manager (pnpm, yarn, npm, bun, etc).
|
|
4
4
|
*/
|
|
5
5
|
export declare function detectPackageManager(): string;
|
|
6
6
|
//# sourceMappingURL=detectPackageManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detectPackageManager.d.ts","sourceRoot":"","sources":["../../../cli/src/shared/detectPackageManager.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"detectPackageManager.d.ts","sourceRoot":"","sources":["../../../cli/src/shared/detectPackageManager.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAO7C"}
|
|
@@ -2,7 +2,7 @@ import * as fs from "fs";
|
|
|
2
2
|
import path from "path";
|
|
3
3
|
/**
|
|
4
4
|
* Detect the package manager used in the project.
|
|
5
|
-
* @returns {string} - The detected package manager (pnpm, yarn,
|
|
5
|
+
* @returns {string} - The detected package manager (pnpm, yarn, npm, bun, etc).
|
|
6
6
|
*/
|
|
7
7
|
export function detectPackageManager() {
|
|
8
8
|
if (fs.existsSync(path.join(process.cwd(), "pnpm-lock.yaml")))
|
|
@@ -11,6 +11,10 @@ export function detectPackageManager() {
|
|
|
11
11
|
return "yarn";
|
|
12
12
|
if (fs.existsSync(path.join(process.cwd(), "package-lock.json")))
|
|
13
13
|
return "npm";
|
|
14
|
+
if (fs.existsSync(path.join(process.cwd(), "bun.lockb")))
|
|
15
|
+
return "bun";
|
|
16
|
+
if (fs.existsSync(path.join(process.cwd(), ".yarnrc.yml")))
|
|
17
|
+
return "yarn";
|
|
14
18
|
return "npm"; // Default to npm if no lock file is found
|
|
15
19
|
}
|
|
16
20
|
//# sourceMappingURL=detectPackageManager.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detectPackageManager.js","sourceRoot":"","sources":["../../../cli/src/shared/detectPackageManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAChC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7E,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACxE,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/E,OAAO,KAAK,CAAC,CAAC,0CAA0C;AAC5D,CAAC"}
|
|
1
|
+
{"version":3,"file":"detectPackageManager.js","sourceRoot":"","sources":["../../../cli/src/shared/detectPackageManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAChC,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7E,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IACxE,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,mBAAmB,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/E,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IACvE,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC1E,OAAO,KAAK,CAAC,CAAC,0CAA0C;AAC5D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwindInit.d.ts","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tailwindInit.d.ts","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,IAAI,IAAI,CAUnC"}
|
|
@@ -1,47 +1,17 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import fs from "fs";
|
|
3
1
|
import { execSync } from "child_process";
|
|
4
2
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
5
|
-
import
|
|
6
|
-
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
7
5
|
export function tailwindInit() {
|
|
6
|
+
const LessThanV4 = IsTailwindLessThanV4();
|
|
8
7
|
const dependencies = [
|
|
9
8
|
"tailwindcss-animate",
|
|
10
9
|
"tailwind-scrollbar-hide",
|
|
11
10
|
"glare-typography",
|
|
12
|
-
"mapping-color-system",
|
|
11
|
+
LessThanV4 ? "mapping-color-system" : "mapping-color-system-v4",
|
|
13
12
|
"glare-torch-mode",
|
|
14
13
|
];
|
|
15
14
|
installDependencies(dependencies);
|
|
16
|
-
const { depsNamesAndVersions } = getCurrentInstalledDependencies();
|
|
17
|
-
if (depsNamesAndVersions["tailwindcss"]) {
|
|
18
|
-
const tailwindVersion = depsNamesAndVersions["tailwindcss"];
|
|
19
|
-
if (isTailwindVersionLessThanV4(tailwindVersion)) {
|
|
20
|
-
if (!fs.existsSync(tailwindConfigPath)) {
|
|
21
|
-
createTailwindConfig();
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
modifyTailwindConfig();
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
console.error("✅ Tailwind CSS version is greater than v4 Not Supported");
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function generatePlugins() {
|
|
34
|
-
return `
|
|
35
|
-
plugin,
|
|
36
|
-
require('tailwindcss-animate'),
|
|
37
|
-
require('tailwind-scrollbar-hide'),
|
|
38
|
-
require('glare-typography'),
|
|
39
|
-
require('glare-torch-mode'),
|
|
40
|
-
function ({ addVariant }: any) {
|
|
41
|
-
addVariant("rtl", '&[dir="rtl"]');
|
|
42
|
-
addVariant("ltr", '&[dir="ltr"]');
|
|
43
|
-
},
|
|
44
|
-
`;
|
|
45
15
|
}
|
|
46
16
|
/**
|
|
47
17
|
* Installs dependencies using the detected package manager.
|
|
@@ -60,18 +30,20 @@ function installDependencies(dependencies = []) {
|
|
|
60
30
|
const latestDeps = dependencies.map(dep => `${dep}@latest`).join(" ");
|
|
61
31
|
switch (packageManager) {
|
|
62
32
|
case "pnpm":
|
|
63
|
-
installCommand = `pnpm add ${latestDeps}`;
|
|
33
|
+
installCommand = `pnpm add ${latestDeps} --prefer-offline`;
|
|
64
34
|
break;
|
|
65
35
|
case "yarn":
|
|
66
|
-
installCommand = `yarn add ${latestDeps}`;
|
|
36
|
+
installCommand = `yarn add ${latestDeps} --ignore-engines --ignore-platform --prefer-offline`;
|
|
67
37
|
break;
|
|
68
|
-
case "
|
|
69
|
-
|
|
70
|
-
installCommand = `npm install ${latestDeps}`;
|
|
38
|
+
case "bun":
|
|
39
|
+
installCommand = `bun add ${latestDeps} --no-save && bun install`;
|
|
71
40
|
break;
|
|
41
|
+
default:
|
|
42
|
+
installCommand = `npm install ${latestDeps} --legacy-peer-deps`;
|
|
72
43
|
}
|
|
73
44
|
try {
|
|
74
45
|
// Execute the install command
|
|
46
|
+
console.log(`Running: ${installCommand}`);
|
|
75
47
|
execSync(installCommand, { stdio: "inherit" });
|
|
76
48
|
console.log("✅ Dependencies installed successfully.");
|
|
77
49
|
}
|
|
@@ -85,144 +57,20 @@ function installDependencies(dependencies = []) {
|
|
|
85
57
|
else if (error.message.includes("not found")) {
|
|
86
58
|
console.error("💡 The package manager might not be installed. Please ensure it is installed and available in your PATH.");
|
|
87
59
|
}
|
|
88
|
-
else {
|
|
89
|
-
console.error("💡
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
function createTailwindConfig() {
|
|
94
|
-
const tailwindConfig = `
|
|
95
|
-
import type { Config } from "tailwindcss";
|
|
96
|
-
const { plugin, mappingVars } = require('mapping-color-system')
|
|
97
|
-
export default {
|
|
98
|
-
content: [
|
|
99
|
-
"./app/**/*.{js,ts,jsx,tsx}",
|
|
100
|
-
"./index.html",
|
|
101
|
-
"./src/**/*.{js,ts,jsx,tsx}",
|
|
102
|
-
"./pages/**/*.{js,ts,jsx,tsx,mdx}",
|
|
103
|
-
"./features/**/*.{js,ts,jsx,tsx,mdx}",
|
|
104
|
-
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
|
105
|
-
"./layout/**/*.{js,ts,jsx,tsx,mdx}",
|
|
106
|
-
],
|
|
107
|
-
theme: {
|
|
108
|
-
extend: {
|
|
109
|
-
colors: {
|
|
110
|
-
...mappingVars,
|
|
111
|
-
},
|
|
112
|
-
},
|
|
113
|
-
},
|
|
114
|
-
screens: {
|
|
115
|
-
sm: "600px",
|
|
116
|
-
md: "768px",
|
|
117
|
-
lg: "1024px",
|
|
118
|
-
xl: "1280px",
|
|
119
|
-
"2xl": "1536px",
|
|
120
|
-
},
|
|
121
|
-
plugins: [${generatePlugins()}],
|
|
122
|
-
}satisfies Config;
|
|
123
|
-
`;
|
|
124
|
-
fs.writeFileSync(tailwindConfigPath, tailwindConfig);
|
|
125
|
-
console.log("✅ Created tailwind.config.ts");
|
|
126
|
-
}
|
|
127
|
-
function modifyTailwindConfig() {
|
|
128
|
-
try {
|
|
129
|
-
// Read the current content
|
|
130
|
-
let tailwindConfigContent = fs.readFileSync(tailwindConfigPath, "utf-8");
|
|
131
|
-
console.log("📄 Read tailwind.config.ts file successfully");
|
|
132
|
-
// Make a backup of the original content for debugging
|
|
133
|
-
const originalContent = tailwindConfigContent;
|
|
134
|
-
// if required plugins are not installed, add them
|
|
135
|
-
if (!tailwindConfigContent.includes("glare-typography")) {
|
|
136
|
-
console.log("🔄 Adding plugins to tailwind config...");
|
|
137
|
-
tailwindConfigContent = modifyPlugins(tailwindConfigContent);
|
|
138
|
-
}
|
|
139
|
-
// if required mapping variables are not installed, add them
|
|
140
|
-
if (!tailwindConfigContent.includes("mappingVars")) {
|
|
141
|
-
console.log("🔄 Adding mapping variables to tailwind config...");
|
|
142
|
-
tailwindConfigContent = AddVariablesColors(tailwindConfigContent);
|
|
143
|
-
}
|
|
144
|
-
// Check if content was actually modified
|
|
145
|
-
if (originalContent === tailwindConfigContent) {
|
|
146
|
-
console.log("⚠️ No changes were made to the tailwind config");
|
|
60
|
+
else if (error.message.includes("ERESOLVE") || error.message.includes("peer dependency conflict")) {
|
|
61
|
+
console.error("💡 There are dependency conflicts. Try manually installing with 'npm install --force' or 'npm install --legacy-peer-deps'.");
|
|
147
62
|
}
|
|
148
63
|
else {
|
|
149
|
-
|
|
150
|
-
fs.writeFileSync(tailwindConfigPath, tailwindConfigContent);
|
|
151
|
-
console.log("✅ Modified tailwind.config.ts successfully");
|
|
64
|
+
console.error("💡 Check your internet connection and try again.");
|
|
152
65
|
}
|
|
153
66
|
}
|
|
154
|
-
catch (error) {
|
|
155
|
-
console.error("❌ Error modifying tailwind.config.ts:", error);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Checks if the installed Tailwind CSS version is less than v4.
|
|
160
|
-
* @param {string} version - The version string of Tailwind CSS.
|
|
161
|
-
* @returns {boolean} - True if the version is less than v4, otherwise false.
|
|
162
|
-
*/
|
|
163
|
-
function isTailwindVersionLessThanV4(version) {
|
|
164
|
-
if (!version) {
|
|
165
|
-
console.warn("⚠️ Tailwind CSS is not installed.");
|
|
166
|
-
return false; // Assume it needs to be installed
|
|
167
|
-
}
|
|
168
|
-
// Extract the major version number
|
|
169
|
-
const majorVersion = parseInt(version?.replace(/^[^0-9]*/, "").split(".")[0] || "3", 10);
|
|
170
|
-
return majorVersion < 4;
|
|
171
67
|
}
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
else {
|
|
180
|
-
// If plugins array exists but doesn't have our plugins
|
|
181
|
-
return tailwindConfigContent.replace(/plugins\s*:\s*\[/, `plugins: [${generatePlugins()}`);
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
catch (error) {
|
|
185
|
-
console.error("❌ Error modifying plugins:", error);
|
|
186
|
-
return tailwindConfigContent; // Return unchanged if error
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
const AddVariablesColors = (tailwindConfigContent) => {
|
|
190
|
-
try {
|
|
191
|
-
// Check if mappingVars import exists, add if not
|
|
192
|
-
if (!tailwindConfigContent.includes("mappingVars")) {
|
|
193
|
-
if (tailwindConfigContent.includes("const { plugin }")) {
|
|
194
|
-
// Update existing plugin import to include mappingVars
|
|
195
|
-
tailwindConfigContent = tailwindConfigContent.replace("const { plugin }", "const { plugin, mappingVars }");
|
|
196
|
-
}
|
|
197
|
-
else {
|
|
198
|
-
// Add the import if it doesn't exist
|
|
199
|
-
tailwindConfigContent = tailwindConfigContent.replace("import type { Config } from", "const { plugin, mappingVars } = require('mapping-color-system')\nimport type { Config } from");
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
// Add colors configuration
|
|
203
|
-
if (!tailwindConfigContent.includes("colors:")) {
|
|
204
|
-
// If theme.extend exists but no colors
|
|
205
|
-
if (tailwindConfigContent.includes("extend:")) {
|
|
206
|
-
tailwindConfigContent = tailwindConfigContent.replace(/extend\s*:\s*{/, "extend: {\n colors: {\n ...mappingVars,\n },");
|
|
207
|
-
}
|
|
208
|
-
// If theme exists but no extend
|
|
209
|
-
else if (tailwindConfigContent.includes("theme:")) {
|
|
210
|
-
tailwindConfigContent = tailwindConfigContent.replace(/theme\s*:\s*{/, "theme: {\n extend: {\n colors: {\n ...mappingVars,\n },\n },");
|
|
211
|
-
}
|
|
212
|
-
// If no theme at all
|
|
213
|
-
else {
|
|
214
|
-
tailwindConfigContent = tailwindConfigContent.replace(/content\s*:\s*\[/, "theme: {\n extend: {\n colors: {\n ...mappingVars,\n },\n },\n },\n content: [");
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
else if (!tailwindConfigContent.includes("...mappingVars")) {
|
|
218
|
-
// If colors exists but mappingVars not added
|
|
219
|
-
tailwindConfigContent = tailwindConfigContent.replace(/colors\s*:\s*{/, "colors: {\n ...mappingVars,");
|
|
220
|
-
}
|
|
221
|
-
return tailwindConfigContent;
|
|
222
|
-
}
|
|
223
|
-
catch (error) {
|
|
224
|
-
console.error("❌ Error adding mapping variables:", error);
|
|
225
|
-
return tailwindConfigContent; // Return unchanged if error
|
|
226
|
-
}
|
|
68
|
+
const IsTailwindLessThanV4 = () => {
|
|
69
|
+
const packageJson = fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8');
|
|
70
|
+
const parsedJson = JSON.parse(packageJson);
|
|
71
|
+
const tailwindVersion = parsedJson.devDependencies?.['tailwindcss'];
|
|
72
|
+
if (!tailwindVersion)
|
|
73
|
+
return false;
|
|
74
|
+
return tailwindVersion.startsWith('^3') || tailwindVersion.startsWith('3');
|
|
227
75
|
};
|
|
228
76
|
//# sourceMappingURL=tailwindInit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tailwindInit.js","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"tailwindInit.js","sourceRoot":"","sources":["../../../cli/src/shared/tailwindInit.ts"],"names":[],"mappings":"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"}
|
package/docs/Cover.png
ADDED
|
Binary file
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# TORCH Glare Components Library
|
|
2
|
+
|
|
3
|
+
Welcome to the **TORCH Glare Components Library**! This library provides a collection of reusable React components to help you build user interfaces efficiently. Additionally, a CLI tool (**torch-glare CLI**) is available to streamline component management.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
1. [Installation](#installation)
|
|
8
|
+
2. [Usage](#usage)
|
|
9
|
+
3. [CLI Commands](#cli-commands)
|
|
10
|
+
4. [Theming](#theming)
|
|
11
|
+
5. [Contributing](#contributing)
|
|
12
|
+
6. [License](#license)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## 1. Initialize Your Project
|
|
18
|
+
|
|
19
|
+
To install the TORCH Glare Components Library, run the following command:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npx torch-glare@latest init
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This command will:
|
|
26
|
+
- Create or modify the `tailwind.config.js` file to support Tailwind CSS for Tailwind versions less then 4.
|
|
27
|
+
- Generate a `torch.json` file where you can customize the installation path for components.
|
|
28
|
+
|
|
29
|
+
### Tailwind CSS Requirement
|
|
30
|
+
Ensure that Tailwind CSS is installed in your project before running the initialization command.
|
|
31
|
+
|
|
32
|
+
## 2. Add Essential Plugins for Tailwind CSS
|
|
33
|
+
|
|
34
|
+
If you're using Tailwind CSS version 4 or above, add the following plugins to your global CSS file:
|
|
35
|
+
|
|
36
|
+
```css
|
|
37
|
+
@import "tailwindcss";
|
|
38
|
+
/* Essential plugins */
|
|
39
|
+
@plugin 'glare-typography';
|
|
40
|
+
@plugin 'mapping-color-system';
|
|
41
|
+
@plugin 'glare-torch-mode';
|
|
42
|
+
@plugin 'tailwind-scrollbar-hide';
|
|
43
|
+
@plugin 'tailwindcss-animate';
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### 2. Add Remix Icon Library
|
|
48
|
+
Include the following in `index.html` or nextjs `layout.tsx` or meta data for icon support:
|
|
49
|
+
|
|
50
|
+
```html
|
|
51
|
+
<html>
|
|
52
|
+
<head>
|
|
53
|
+
<link
|
|
54
|
+
href="https://cdn.jsdelivr.net/npm/remixicon@4.5.0/fonts/remixicon.css"
|
|
55
|
+
rel="stylesheet"
|
|
56
|
+
/>
|
|
57
|
+
</head>
|
|
58
|
+
<body></body>
|
|
59
|
+
</html>
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 3. Configure Installation Path
|
|
64
|
+
|
|
65
|
+
Adjust the `glare.json` file to specify where you want to install components:
|
|
66
|
+
|
|
67
|
+
```json
|
|
68
|
+
{
|
|
69
|
+
"path": "./src" // The directory where components will be installed
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 4. Add Components
|
|
74
|
+
To add a specific component, run:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
npx torch-glare@latest add [component-name]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Or, to add components interactively:
|
|
81
|
+
|
|
82
|
+
```sh
|
|
83
|
+
npx torch-glare@latest add
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Usage
|
|
87
|
+
|
|
88
|
+
Once installed, import and use the components as needed:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
import React from "react";
|
|
92
|
+
import { Button } from "./components";
|
|
93
|
+
|
|
94
|
+
const App = () => {
|
|
95
|
+
return (
|
|
96
|
+
<div>
|
|
97
|
+
<Button >Hello.</Button>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export default App;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## CLI Commands
|
|
106
|
+
|
|
107
|
+
### Initialize Configuration
|
|
108
|
+
```sh
|
|
109
|
+
npx torch-glare@latest init
|
|
110
|
+
```
|
|
111
|
+
- Creates a `torch.json` configuration file.
|
|
112
|
+
- Create or modify `tailwind.config.ts` file for tailwind support.
|
|
113
|
+
|
|
114
|
+
### Add Components
|
|
115
|
+
```sh
|
|
116
|
+
npx torch-glare@latest add [component]
|
|
117
|
+
```
|
|
118
|
+
Adds a specific component or runs an interactive prompt if no name is provided.
|
|
119
|
+
|
|
120
|
+
### Add Hooks
|
|
121
|
+
```sh
|
|
122
|
+
npx torch-glare@latest hook [hook]
|
|
123
|
+
```
|
|
124
|
+
Adds a specific hook or runs an interactive prompt if no name is provided.
|
|
125
|
+
|
|
126
|
+
### Add Utilities
|
|
127
|
+
```sh
|
|
128
|
+
npx torch-glare@latest util [util]
|
|
129
|
+
```
|
|
130
|
+
Adds a specific utility or runs an interactive prompt if no name is provided.
|
|
131
|
+
|
|
132
|
+
### Providers
|
|
133
|
+
```sh
|
|
134
|
+
npx torch-glare@latest provider [provider]
|
|
135
|
+
```
|
|
136
|
+
Adds a specific provider or runs an interactive prompt if no name is provided.
|
|
137
|
+
|
|
138
|
+
### Update Installed Resources
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npx torch-glare@latest update
|
|
142
|
+
```
|
|
143
|
+
Updates all installed components, hooks, utilities, and providers.
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
## Theming
|
|
147
|
+
|
|
148
|
+
The TORCH Glare Components Library supports both light and dark themes. You can set a fixed theme for your components using the `theme` attribute.
|
|
149
|
+
|
|
150
|
+
### Setting a Fixed Theme
|
|
151
|
+
|
|
152
|
+
To apply a fixed theme (dark or light) to a component, add the `theme `attribute with the desired theme value:
|
|
153
|
+
|
|
154
|
+
```tsx
|
|
155
|
+
import React from "react";
|
|
156
|
+
import { Button } from "./components";
|
|
157
|
+
|
|
158
|
+
const App = () => {
|
|
159
|
+
return (
|
|
160
|
+
<div>
|
|
161
|
+
<Button theme="dark">Dark Theme Button</Button>
|
|
162
|
+
<Button theme="light">Light Theme Button</Button>
|
|
163
|
+
</div>
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
export default App;
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Global Theme
|
|
171
|
+
|
|
172
|
+
To apply a theme globally, wrap your application with the `ThemeProvider` and set the optional `defaultTheme` props:
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import { ThemeProvider } from "./components";
|
|
177
|
+
|
|
178
|
+
const App = () => {
|
|
179
|
+
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
|
|
180
|
+
<ThemeProvider defaultTheme="light" defaultThemeMode="TORCH">
|
|
181
|
+
<App />
|
|
182
|
+
</ThemeProvider>
|
|
183
|
+
);
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
export default App;
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Contributing
|
|
190
|
+
|
|
191
|
+
We welcome contributions! Follow these steps:
|
|
192
|
+
|
|
193
|
+
1. Fork the repository.
|
|
194
|
+
2. Create a new branch.
|
|
195
|
+
3. Implement your changes.
|
|
196
|
+
4. Commit with a clear message.
|
|
197
|
+
5. Push your changes and open a pull request.
|
|
198
|
+
|
|
199
|
+
### Contribution Guidelines
|
|
200
|
+
- Follow existing code style.
|
|
201
|
+
- Update documentation if necessary.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
This project is licensed under the **MIT License**.
|
|
206
|
+
|
|
207
|
+
|
|
@@ -4,8 +4,8 @@ import * as React from "react"
|
|
|
4
4
|
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
|
5
5
|
|
|
6
6
|
import { cn } from "../utils/cn"
|
|
7
|
-
import { buttonVariants } from "./Button"
|
|
8
|
-
import { ButtonVariant } from "
|
|
7
|
+
import { Button, buttonVariants } from "./Button"
|
|
8
|
+
import { ButtonVariant } from "../utils/types"
|
|
9
9
|
import { cva } from "class-variance-authority"
|
|
10
10
|
|
|
11
11
|
const StatusTextStyle = cva("", {
|
|
@@ -55,10 +55,18 @@ const AlertDialogContent = React.forwardRef<
|
|
|
55
55
|
<AlertDialogOverlay />
|
|
56
56
|
<AlertDialogPrimitive.Content
|
|
57
57
|
ref={ref}
|
|
58
|
-
className={cn(
|
|
59
|
-
|
|
60
|
-
"fixed left-[50%] top-[50%] z-50 grid w-full translate-x-[-50%] translate-y-[-50%]
|
|
61
|
-
"rounded-[16px] border-2 border-border-presentation-global-primary",
|
|
58
|
+
className={cn(
|
|
59
|
+
StatusTextStyle({ variant }),
|
|
60
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[800px] translate-x-[-50%] translate-y-[-50%]",
|
|
61
|
+
"gap-2 rounded-[16px] border-2 border-border-presentation-global-primary",
|
|
62
|
+
"bg-background-presentation-body-overlay-primary p-[12px]",
|
|
63
|
+
"text-content-presentation-global-primary",
|
|
64
|
+
"shadow-lg duration-200",
|
|
65
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out",
|
|
66
|
+
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
67
|
+
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
68
|
+
"data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%]",
|
|
69
|
+
"data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]",
|
|
62
70
|
className
|
|
63
71
|
)}
|
|
64
72
|
{...props}
|
|
@@ -120,11 +128,11 @@ const AlertDialogLabel = React.forwardRef<
|
|
|
120
128
|
return (
|
|
121
129
|
<AlertDialogPrimitive.Title
|
|
122
130
|
ref={ref}
|
|
123
|
-
className={cn("
|
|
131
|
+
className={cn("typography-display-medium-semibold", className)}
|
|
124
132
|
{...props}
|
|
125
133
|
>
|
|
126
|
-
<p className="first-letter:uppercase" >
|
|
127
|
-
<strong >{firstWord}</strong>
|
|
134
|
+
<p className="first-letter:uppercase typography-display-medium-semibold" >
|
|
135
|
+
<strong className="typography-display-medium-semibold" >{firstWord}</strong>
|
|
128
136
|
{restOfTitle.length > 0 && ' ' + restOfTitle}
|
|
129
137
|
</p>
|
|
130
138
|
</AlertDialogPrimitive.Title>
|
|
@@ -139,14 +147,15 @@ const AlertDialogDescription = React.forwardRef<
|
|
|
139
147
|
<AlertDialogPrimitive.Description
|
|
140
148
|
data-description=""
|
|
141
149
|
ref={ref}
|
|
142
|
-
className={cn(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
150
|
+
className={cn(
|
|
151
|
+
"typography-body-large-medium",
|
|
152
|
+
"bg-background-presentation-form-base",
|
|
153
|
+
"border border-border-presentation-global-primary",
|
|
154
|
+
"rounded-[8px] p-[24px_48px_48px_48px]",
|
|
155
|
+
className
|
|
156
|
+
)}
|
|
146
157
|
{...props}
|
|
147
|
-
|
|
148
|
-
{props.children}
|
|
149
|
-
</AlertDialogPrimitive.Description>
|
|
158
|
+
/>
|
|
150
159
|
))
|
|
151
160
|
AlertDialogDescription.displayName =
|
|
152
161
|
AlertDialogPrimitive.Description.displayName
|
|
@@ -166,8 +175,10 @@ const AlertDialogAction = React.forwardRef<
|
|
|
166
175
|
<AlertDialogPrimitive.Action
|
|
167
176
|
ref={ref}
|
|
168
177
|
className={cn(buttonVariants({ variant: variant, size: size, buttonType: buttonType }), className)}
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
asChild
|
|
179
|
+
>
|
|
180
|
+
<Button size={size} variant={variant} buttonType={buttonType} {...props} />
|
|
181
|
+
</AlertDialogPrimitive.Action>
|
|
171
182
|
))
|
|
172
183
|
AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
|
|
173
184
|
|
|
@@ -60,8 +60,8 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
|
|
|
60
60
|
// this is used to close the popover when the user clicks outside the input group
|
|
61
61
|
const inputGroupRef = useClickOutside((e) => {
|
|
62
62
|
if (
|
|
63
|
-
!inputGroupRef?.current?.contains(e
|
|
64
|
-
!popoverContentRef?.current?.contains(e
|
|
63
|
+
!inputGroupRef?.current?.contains(e?.target as Node) &&
|
|
64
|
+
!popoverContentRef?.current?.contains(e?.target as Node)
|
|
65
65
|
) setIsPopoverOpen(false);
|
|
66
66
|
else setIsPopoverOpen(true);
|
|
67
67
|
});
|
|
@@ -98,7 +98,7 @@ export const BadgeField = forwardRef<HTMLInputElement, Props>(
|
|
|
98
98
|
tabIndex={isPopoverOpen ? 0 : -1}
|
|
99
99
|
onKeyDown={handleKeyDown}
|
|
100
100
|
size={size === "XS" ? "S" : size}
|
|
101
|
-
ref={inputGroupRef}
|
|
101
|
+
ref={inputGroupRef as any}
|
|
102
102
|
onFocus={(e: any) => {
|
|
103
103
|
setDropDownListWidth(e.currentTarget.offsetWidth);
|
|
104
104
|
}}
|
|
@@ -46,6 +46,7 @@ const ImageAttachment = forwardRef<HTMLInputElement, Props>(
|
|
|
46
46
|
) => {
|
|
47
47
|
return (
|
|
48
48
|
<section
|
|
49
|
+
|
|
49
50
|
className={cn("flex items-center justify-center gap-1 w-full", className)}>
|
|
50
51
|
{children}
|
|
51
52
|
<Button
|
|
@@ -54,6 +55,7 @@ const ImageAttachment = forwardRef<HTMLInputElement, Props>(
|
|
|
54
55
|
as="label"
|
|
55
56
|
id={props.id}
|
|
56
57
|
variant="PrimeContStyle"
|
|
58
|
+
|
|
57
59
|
className={cn(dropZoneStyles({ active: isDropAreaActive }))}
|
|
58
60
|
containerClassName="flex-col"
|
|
59
61
|
>
|
package/lib/components/Input.tsx
CHANGED
|
@@ -11,17 +11,19 @@ interface InputGroupProps extends Omit<HTMLAttributes<HTMLDivElement>, "size"> {
|
|
|
11
11
|
className?: string;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
export const Group =
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
export const Group = forwardRef<HTMLDivElement, InputGroupProps>(
|
|
15
|
+
({ size = 'M', variant = "PresentationStyle", error = false, onTable = false, className, ...props }, ref) => {
|
|
16
|
+
return (
|
|
17
|
+
<div
|
|
18
|
+
className={cn(GroupStyles({ size, variant, error, onTable }), className)}
|
|
19
|
+
ref={ref}
|
|
20
|
+
{...props}>
|
|
21
|
+
</div>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
);
|
|
24
25
|
|
|
26
|
+
Group.displayName = "Group";
|
|
25
27
|
|
|
26
28
|
interface IconProps {
|
|
27
29
|
children: React.ReactNode;
|
package/lib/components/Table.tsx
CHANGED
|
@@ -188,9 +188,10 @@ const TableCell = React.forwardRef<
|
|
|
188
188
|
>
|
|
189
189
|
<div
|
|
190
190
|
className={cn(
|
|
191
|
-
"flex justify-start items-center gap-1 w-[200px] min-w-full overflow-hidden",
|
|
191
|
+
"flex justify-start items-center gap-1 w-[200px] min-w-full overflow-hidden has-input:bg-blue-200",
|
|
192
192
|
"[mask-image:linear-gradient(to_right,black_0%,black_0%,black_75%,transparent_100%)]",
|
|
193
193
|
"rtl:[mask-image:linear-gradient(to_left,black_0%,black_0%,black_75%,transparent_100%)]",
|
|
194
|
+
"[&:has(input)]:[mask-image:none]",
|
|
194
195
|
{ "w-auto justify-center": isDummy }, childrenClassName)}
|
|
195
196
|
>
|
|
196
197
|
{props.children}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { useEffect, useRef } from "react";
|
|
2
2
|
|
|
3
|
-
export function useClickOutside<T extends HTMLElement>(callback: (event?:
|
|
3
|
+
export function useClickOutside<T extends HTMLElement>(callback: (event?: MouseEvent | PointerEvent) => void) {
|
|
4
4
|
const ref = useRef<T>(null);
|
|
5
5
|
|
|
6
6
|
useEffect(() => {
|
|
7
|
-
function handleOutsideClick(event: MouseEvent) {
|
|
7
|
+
function handleOutsideClick(event: MouseEvent | PointerEvent) {
|
|
8
8
|
if (ref.current && !ref.current.contains(event.target as Node)) {
|
|
9
9
|
callback(event);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
13
|
document.addEventListener("mousedown", handleOutsideClick);
|
|
15
14
|
document.addEventListener("pointerdown", handleOutsideClick);
|
|
16
15
|
return () => {
|
|
@@ -12,17 +12,27 @@ export interface Tag {
|
|
|
12
12
|
export const useTagSelection = ({
|
|
13
13
|
Tags,
|
|
14
14
|
onTagsChange,
|
|
15
|
-
inputRef
|
|
15
|
+
inputRef,
|
|
16
|
+
singleSelect = false
|
|
16
17
|
}: {
|
|
17
18
|
Tags: Tag[],
|
|
18
19
|
onTagsChange?: (selectedTags: Tag[]) => void,
|
|
19
|
-
inputRef?: React.RefObject<HTMLInputElement | null
|
|
20
|
+
inputRef?: React.RefObject<HTMLInputElement | null>,
|
|
21
|
+
singleSelect?: boolean
|
|
20
22
|
}) => {
|
|
23
|
+
// Split initial tags into selected and unselected
|
|
24
|
+
const initialSelectedTags = Tags.filter(tag => tag.isSelected);
|
|
25
|
+
const initialUnselectedTags = Tags.filter(tag => !tag.isSelected);
|
|
26
|
+
|
|
21
27
|
// Initialize with available tags (excluding any initially selected ones)
|
|
22
|
-
const [tags, setTags] = useState<Tag[]>(
|
|
28
|
+
const [tags, setTags] = useState<Tag[]>(initialUnselectedTags);
|
|
23
29
|
|
|
24
30
|
// Initialize with any pre-selected tags
|
|
25
|
-
const [selectedTagsStack, setSelectedTagsStack] = useState<Tag[]>(
|
|
31
|
+
const [selectedTagsStack, setSelectedTagsStack] = useState<Tag[]>(
|
|
32
|
+
singleSelect && initialSelectedTags.length > 0
|
|
33
|
+
? [initialSelectedTags[0]]
|
|
34
|
+
: initialSelectedTags
|
|
35
|
+
);
|
|
26
36
|
const [searchTags, filterTagsBySearch] = useState('');
|
|
27
37
|
const [focusedTagIndex, setFocusedTagIndex] = useState<number | null>(null);
|
|
28
38
|
const [focusedPopoverIndex, setFocusedPopoverIndex] = useState<number | null>(null);
|
|
@@ -47,11 +57,29 @@ export const useTagSelection = ({
|
|
|
47
57
|
tag.name.toLowerCase().includes(searchTags.toLowerCase())
|
|
48
58
|
);
|
|
49
59
|
|
|
60
|
+
// Filter selected tags based on search input
|
|
61
|
+
const filteredSelectedTags = searchTags.length > 0
|
|
62
|
+
? selectedTagsStack.filter(tag =>
|
|
63
|
+
tag.name.toLowerCase().includes(searchTags.toLowerCase()))
|
|
64
|
+
: selectedTagsStack;
|
|
65
|
+
|
|
50
66
|
const handleSelectTag = (id: string) => {
|
|
51
67
|
const tagToSelect = tags.find(tag => tag.id === id);
|
|
52
68
|
if (tagToSelect) {
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
// If in single select mode, replace the current selection
|
|
70
|
+
if (singleSelect) {
|
|
71
|
+
// Move any currently selected tag back to available tags
|
|
72
|
+
if (selectedTagsStack.length > 0) {
|
|
73
|
+
const currentSelected = selectedTagsStack[0];
|
|
74
|
+
setTags(prev => [...prev, { ...currentSelected, isSelected: false }]);
|
|
75
|
+
}
|
|
76
|
+
setSelectedTagsStack([{ ...tagToSelect, isSelected: true }]);
|
|
77
|
+
setTags(prev => prev.filter(tag => tag.id !== id));
|
|
78
|
+
} else {
|
|
79
|
+
// Multi-select behavior (original)
|
|
80
|
+
setSelectedTagsStack(prev => [...prev, { ...tagToSelect, isSelected: true }]);
|
|
81
|
+
setTags(prev => prev.filter(tag => tag.id !== id));
|
|
82
|
+
}
|
|
55
83
|
}
|
|
56
84
|
filterTagsBySearch('');
|
|
57
85
|
setFocusedPopoverIndex(null);
|
|
@@ -68,8 +96,13 @@ export const useTagSelection = ({
|
|
|
68
96
|
|
|
69
97
|
// Reset the hook state with new data
|
|
70
98
|
const reset = (newTags: Tag[] = [], newSelectedTags: Tag[] = []) => {
|
|
99
|
+
// In single select mode, ensure we only have at most one selected tag
|
|
100
|
+
const selectedTags = singleSelect && newSelectedTags.length > 0
|
|
101
|
+
? [newSelectedTags[0]]
|
|
102
|
+
: newSelectedTags;
|
|
103
|
+
|
|
71
104
|
setTags(newTags);
|
|
72
|
-
setSelectedTagsStack(
|
|
105
|
+
setSelectedTagsStack(selectedTags);
|
|
73
106
|
filterTagsBySearch('');
|
|
74
107
|
setFocusedTagIndex(null);
|
|
75
108
|
setFocusedPopoverIndex(null);
|
|
@@ -169,11 +202,13 @@ export const useTagSelection = ({
|
|
|
169
202
|
setFocusedPopoverIndex,
|
|
170
203
|
filterTagsBySearch,
|
|
171
204
|
filteredTags,
|
|
205
|
+
filteredSelectedTags,
|
|
172
206
|
focusedTagIndex,
|
|
173
207
|
focusedPopoverIndex,
|
|
174
208
|
isPopoverOpen,
|
|
175
209
|
setIsPopoverOpen,
|
|
176
|
-
reset
|
|
210
|
+
reset,
|
|
211
|
+
singleSelect
|
|
177
212
|
};
|
|
178
213
|
};
|
|
179
214
|
|