@dinachi/cli 0.6.1 → 0.6.3
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/LICENSE +21 -0
- package/dist/index.js +66 -54
- package/package.json +2 -2
- package/templates/alert-dialog/alert-dialog.tsx +3 -3
- package/templates/autocomplete/autocomplete.tsx +18 -12
- package/templates/button/button.tsx +3 -1
- package/templates/combobox/combobox.tsx +18 -12
- package/templates/menu/menu.tsx +20 -20
- package/templates/menubar/index.ts +1 -0
- package/templates/menubar/menubar.tsx +9 -0
- package/templates/popover/popover.tsx +14 -9
- package/templates/radio/radio.tsx +1 -1
- package/templates/select/select.tsx +11 -6
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DinachiUI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import path3 from "path";
|
|
|
11
11
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12
12
|
import ora from "ora";
|
|
13
13
|
import chalk from "chalk";
|
|
14
|
+
import prompts from "prompts";
|
|
14
15
|
|
|
15
16
|
// src/utils/registry.ts
|
|
16
17
|
import fs from "fs-extra";
|
|
@@ -374,25 +375,14 @@ var LOCK_FILE_MAP = [
|
|
|
374
375
|
{ file: "yarn.lock", manager: "yarn" },
|
|
375
376
|
{ file: "package-lock.json", manager: "npm" }
|
|
376
377
|
];
|
|
377
|
-
function
|
|
378
|
-
const
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
dirs.push(current);
|
|
382
|
-
const parent = path2.dirname(current);
|
|
383
|
-
if (parent === current) {
|
|
384
|
-
break;
|
|
378
|
+
function detectPackageManager(startDir = process.cwd()) {
|
|
379
|
+
for (const entry of LOCK_FILE_MAP) {
|
|
380
|
+
if (fs2.existsSync(path2.join(startDir, entry.file))) {
|
|
381
|
+
return entry.manager;
|
|
385
382
|
}
|
|
386
|
-
current = parent;
|
|
387
383
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
function detectManagerFromPackageJson(startDir) {
|
|
391
|
-
for (const dir of walkUpDirectories(startDir)) {
|
|
392
|
-
const packageJsonPath = path2.join(dir, "package.json");
|
|
393
|
-
if (!fs2.existsSync(packageJsonPath)) {
|
|
394
|
-
continue;
|
|
395
|
-
}
|
|
384
|
+
const packageJsonPath = path2.join(startDir, "package.json");
|
|
385
|
+
if (fs2.existsSync(packageJsonPath)) {
|
|
396
386
|
try {
|
|
397
387
|
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
|
|
398
388
|
const value = packageJson.packageManager ?? "";
|
|
@@ -401,21 +391,6 @@ function detectManagerFromPackageJson(startDir) {
|
|
|
401
391
|
if (value.startsWith("bun@")) return "bun";
|
|
402
392
|
if (value.startsWith("npm@")) return "npm";
|
|
403
393
|
} catch {
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return null;
|
|
408
|
-
}
|
|
409
|
-
function detectPackageManager(startDir = process.cwd()) {
|
|
410
|
-
const packageJsonManager = detectManagerFromPackageJson(startDir);
|
|
411
|
-
if (packageJsonManager) {
|
|
412
|
-
return packageJsonManager;
|
|
413
|
-
}
|
|
414
|
-
for (const dir of walkUpDirectories(startDir)) {
|
|
415
|
-
for (const entry of LOCK_FILE_MAP) {
|
|
416
|
-
if (fs2.existsSync(path2.join(dir, entry.file))) {
|
|
417
|
-
return entry.manager;
|
|
418
|
-
}
|
|
419
394
|
}
|
|
420
395
|
}
|
|
421
396
|
return "npm";
|
|
@@ -699,7 +674,8 @@ async function ensureTW4Plugin(deps, cssFilePath) {
|
|
|
699
674
|
let updated;
|
|
700
675
|
if (importMatch) {
|
|
701
676
|
updated = content.replace(importMatch[0], `${importMatch[0].trimEnd()}
|
|
702
|
-
${pluginLine}
|
|
677
|
+
${pluginLine}
|
|
678
|
+
`);
|
|
703
679
|
} else {
|
|
704
680
|
updated = `${pluginLine}
|
|
705
681
|
${content}`;
|
|
@@ -769,6 +745,19 @@ ${updatedContent}`;
|
|
|
769
745
|
function escapeRegex(value) {
|
|
770
746
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
771
747
|
}
|
|
748
|
+
function extractExportBlocks(content) {
|
|
749
|
+
const results = [];
|
|
750
|
+
const regex = /export\s+(?:type\s+)?{[^}]*}\s*from\s+['"]\.\/([^'"]+)['"]\s*;?/g;
|
|
751
|
+
let match;
|
|
752
|
+
while ((match = regex.exec(content)) !== null) {
|
|
753
|
+
results.push({ block: match[0].trimEnd(), modulePath: match[1] });
|
|
754
|
+
}
|
|
755
|
+
const starRegex = /export\s+\*\s+from\s+['"]\.\/([^'"]+)['"]\s*;?/g;
|
|
756
|
+
while ((match = starRegex.exec(content)) !== null) {
|
|
757
|
+
results.push({ block: match[0].trimEnd(), modulePath: match[1] });
|
|
758
|
+
}
|
|
759
|
+
return results;
|
|
760
|
+
}
|
|
772
761
|
async function handleIndexFile(sourcePath, targetPath, allFilesAdded, targetDir) {
|
|
773
762
|
const templateContent = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
|
|
774
763
|
if (!fs3.existsSync(targetPath)) {
|
|
@@ -777,28 +766,27 @@ async function handleIndexFile(sourcePath, targetPath, allFilesAdded, targetDir)
|
|
|
777
766
|
return;
|
|
778
767
|
}
|
|
779
768
|
const existingContent = await fs3.readFile(targetPath, "utf-8");
|
|
780
|
-
const
|
|
781
|
-
const
|
|
782
|
-
for (const
|
|
783
|
-
const match = line.match(/from\s+['"]\.\/([^'"]+)['"]/);
|
|
784
|
-
if (!match) {
|
|
785
|
-
continue;
|
|
786
|
-
}
|
|
787
|
-
const modulePath = match[1];
|
|
769
|
+
const exportBlocks = extractExportBlocks(templateContent);
|
|
770
|
+
const blocksToAppend = [];
|
|
771
|
+
for (const { block, modulePath } of exportBlocks) {
|
|
788
772
|
const modulePathPattern = new RegExp(`from\\s+['"]\\./${escapeRegex(modulePath)}['"]`);
|
|
789
773
|
if (modulePathPattern.test(existingContent)) {
|
|
790
774
|
continue;
|
|
791
775
|
}
|
|
792
|
-
|
|
776
|
+
const normalized = block.endsWith(";") ? block : `${block};`;
|
|
777
|
+
blocksToAppend.push(normalized);
|
|
793
778
|
}
|
|
794
|
-
if (
|
|
779
|
+
if (blocksToAppend.length === 0) {
|
|
795
780
|
return;
|
|
796
781
|
}
|
|
797
782
|
const updatedContent = `${existingContent.trimEnd()}
|
|
798
|
-
${
|
|
783
|
+
${blocksToAppend.join("\n")}
|
|
799
784
|
`;
|
|
800
785
|
await fs3.writeFile(targetPath, updatedContent);
|
|
801
|
-
|
|
786
|
+
const indexPath = path3.join(targetDir, "index.ts");
|
|
787
|
+
if (!allFilesAdded.some((f) => f.path === indexPath)) {
|
|
788
|
+
allFilesAdded.push({ name: "index.ts", path: indexPath });
|
|
789
|
+
}
|
|
802
790
|
}
|
|
803
791
|
var addCommand = new Command("add").description("Add a component to your project").argument("[components...]", "Names of the components to add (optional when using --all)").option("-y, --yes", "Skip confirmation prompts").option("-o, --overwrite", "Overwrite existing files").option("-a, --all", "Install all available components").option("--skip-install", "Skip package installation").action(
|
|
804
792
|
async (componentNames, options) => {
|
|
@@ -901,8 +889,19 @@ var addCommand = new Command("add").description("Add a component to your project
|
|
|
901
889
|
continue;
|
|
902
890
|
}
|
|
903
891
|
if (fs3.existsSync(targetPath) && !options.overwrite) {
|
|
904
|
-
spinner.
|
|
905
|
-
|
|
892
|
+
spinner.stop();
|
|
893
|
+
const { overwrite } = await prompts({
|
|
894
|
+
type: "confirm",
|
|
895
|
+
name: "overwrite",
|
|
896
|
+
message: `${file.name} already exists. Overwrite?`,
|
|
897
|
+
initial: false
|
|
898
|
+
});
|
|
899
|
+
if (!overwrite) {
|
|
900
|
+
console.log(` ${chalk.yellow("\u2298")} Skipped ${file.name}`);
|
|
901
|
+
spinner.start();
|
|
902
|
+
continue;
|
|
903
|
+
}
|
|
904
|
+
spinner.start();
|
|
906
905
|
}
|
|
907
906
|
const templateContent = stripTemplateDirective(await fs3.readFile(sourcePath, "utf-8"));
|
|
908
907
|
const rewrittenContent = rewriteTemplateImports(templateContent, targetPath, utilsFilePath, libDir);
|
|
@@ -996,7 +995,7 @@ import { Command as Command2 } from "commander";
|
|
|
996
995
|
import { execSync as execSync2 } from "child_process";
|
|
997
996
|
import fs4 from "fs-extra";
|
|
998
997
|
import path4 from "path";
|
|
999
|
-
import
|
|
998
|
+
import prompts2 from "prompts";
|
|
1000
999
|
import chalk2 from "chalk";
|
|
1001
1000
|
import ora2 from "ora";
|
|
1002
1001
|
function normalizeProjectPath(inputPath, projectRoot) {
|
|
@@ -1032,7 +1031,16 @@ function detectTailwindMajorVersion2(projectRoot) {
|
|
|
1032
1031
|
return 4;
|
|
1033
1032
|
}
|
|
1034
1033
|
}
|
|
1035
|
-
|
|
1034
|
+
var DINACHI_THEME_PREFIXES = ["--color-", "--radius-"];
|
|
1035
|
+
function extractPreservedThemeVars(css) {
|
|
1036
|
+
const themeMatch = css.match(/@theme\s+inline\s*\{([^}]*)\}/);
|
|
1037
|
+
if (!themeMatch) return [];
|
|
1038
|
+
return themeMatch[1].split("\n").map((line) => line.trim()).filter((line) => {
|
|
1039
|
+
if (!line || line.startsWith("//") || line.startsWith("/*")) return false;
|
|
1040
|
+
return line.startsWith("--") && !DINACHI_THEME_PREFIXES.some((p) => line.startsWith(p));
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
function getThemeCSS(tailwindMajor, mode, preservedThemeVars = []) {
|
|
1036
1044
|
const lightVars = `:root {
|
|
1037
1045
|
--background: oklch(0.986 0.0034 145.5499);
|
|
1038
1046
|
--foreground: oklch(0.1459 0.0497 142.4953);
|
|
@@ -1082,6 +1090,7 @@ function getThemeCSS(tailwindMajor, mode) {
|
|
|
1082
1090
|
parts.push('@import "tailwindcss";');
|
|
1083
1091
|
}
|
|
1084
1092
|
parts.push(lightVars, darkVars);
|
|
1093
|
+
const preservedLines = preservedThemeVars.length > 0 ? "\n" + preservedThemeVars.map((v) => ` ${v}`).join("\n") : "";
|
|
1085
1094
|
parts.push(`@theme inline {
|
|
1086
1095
|
--color-background: var(--background);
|
|
1087
1096
|
--color-foreground: var(--foreground);
|
|
@@ -1105,7 +1114,7 @@ function getThemeCSS(tailwindMajor, mode) {
|
|
|
1105
1114
|
--radius-sm: calc(var(--radius) - 4px);
|
|
1106
1115
|
--radius-md: calc(var(--radius) - 2px);
|
|
1107
1116
|
--radius-lg: var(--radius);
|
|
1108
|
-
--radius-xl: calc(var(--radius) + 4px)
|
|
1117
|
+
--radius-xl: calc(var(--radius) + 4px);${preservedLines}
|
|
1109
1118
|
}`);
|
|
1110
1119
|
parts.push(`@layer base {
|
|
1111
1120
|
* {
|
|
@@ -1148,9 +1157,10 @@ async function injectThemeCSS(cssFilePath, tailwindMajor) {
|
|
|
1148
1157
|
if (existing.includes("--primary:")) {
|
|
1149
1158
|
return { path: cssFilePath, skipped: true };
|
|
1150
1159
|
}
|
|
1160
|
+
const preservedVars = tailwindMajor >= 4 ? extractPreservedThemeVars(existing) : [];
|
|
1151
1161
|
const cleaned = stripConflictingCSS(existing);
|
|
1152
|
-
const theme2 = getThemeCSS(tailwindMajor, "append");
|
|
1153
|
-
const result = cleaned.length > 0 ? cleaned + "\n\n" + theme2 : getThemeCSS(tailwindMajor, "full");
|
|
1162
|
+
const theme2 = getThemeCSS(tailwindMajor, "append", preservedVars);
|
|
1163
|
+
const result = cleaned.length > 0 ? cleaned + "\n\n" + theme2 : getThemeCSS(tailwindMajor, "full", preservedVars);
|
|
1154
1164
|
await fs4.writeFile(cssFilePath, result);
|
|
1155
1165
|
return { path: cssFilePath, updated: true };
|
|
1156
1166
|
}
|
|
@@ -1329,7 +1339,9 @@ async function ensureAtAlias(projectRoot, srcDir, isTypeScript) {
|
|
|
1329
1339
|
}
|
|
1330
1340
|
}
|
|
1331
1341
|
const aliasTarget = srcDir === "." ? "*" : `${srcDir}/*`;
|
|
1332
|
-
const
|
|
1342
|
+
const aliasTargetAlt = srcDir === "." ? "./*" : `./${srcDir}/*`;
|
|
1343
|
+
const existingAlias = paths["@/*"]?.[0];
|
|
1344
|
+
const alreadyConfigured = compilerOptions.baseUrl === "." && existingAlias === aliasTarget || existingAlias === aliasTargetAlt;
|
|
1333
1345
|
if (alreadyConfigured) {
|
|
1334
1346
|
return { path: configPath };
|
|
1335
1347
|
}
|
|
@@ -1364,7 +1376,7 @@ var initCommand = new Command2("init").description("Initialize Dinachi UI in you
|
|
|
1364
1376
|
const projectConfig = detectProjectType();
|
|
1365
1377
|
console.log(chalk2.gray(`Detected ${projectConfig.framework} project`));
|
|
1366
1378
|
console.log();
|
|
1367
|
-
const response = await
|
|
1379
|
+
const response = await prompts2([
|
|
1368
1380
|
{
|
|
1369
1381
|
type: "text",
|
|
1370
1382
|
name: "componentsPath",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dinachi/cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "CLI for adding Dinachi UI components to your project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"repository": {
|
|
17
17
|
"type": "git",
|
|
18
|
-
"url": "git+https://github.com/
|
|
18
|
+
"url": "git+https://github.com/raymond-UI/dinachiUI.git"
|
|
19
19
|
},
|
|
20
20
|
"homepage": "https://dinachi.dev",
|
|
21
21
|
"bin": {
|
|
@@ -33,7 +33,7 @@ const AlertDialogBackdrop = React.forwardRef<
|
|
|
33
33
|
ref={ref}
|
|
34
34
|
className={cn(
|
|
35
35
|
"fixed inset-0 z-50 bg-black/80",
|
|
36
|
-
"data-
|
|
36
|
+
"data-starting-style:opacity-0 data-ending-style:opacity-0",
|
|
37
37
|
"transition-all duration-150",
|
|
38
38
|
className
|
|
39
39
|
)}
|
|
@@ -50,8 +50,8 @@ const AlertDialogPopup = React.forwardRef<
|
|
|
50
50
|
ref={ref}
|
|
51
51
|
className={cn(
|
|
52
52
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg sm:rounded-lg",
|
|
53
|
-
"data-
|
|
54
|
-
"data-
|
|
53
|
+
"data-starting-style:scale-95 data-starting-style:opacity-0",
|
|
54
|
+
"data-ending-style:scale-95 data-ending-style:opacity-0",
|
|
55
55
|
"transition-all duration-150",
|
|
56
56
|
className
|
|
57
57
|
)}
|
|
@@ -71,24 +71,30 @@ AutocompleteClear.displayName = "AutocompleteClear"
|
|
|
71
71
|
|
|
72
72
|
const AutocompleteContent = React.forwardRef<
|
|
73
73
|
React.ComponentRef<typeof AutocompletePrimitive.Popup>,
|
|
74
|
-
React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Popup>
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
React.ComponentPropsWithoutRef<typeof AutocompletePrimitive.Popup> & {
|
|
75
|
+
readonly portal?: boolean
|
|
76
|
+
}
|
|
77
|
+
>(({ className, portal = true, ...props }, ref) => {
|
|
78
|
+
const content = (
|
|
77
79
|
<AutocompletePrimitive.Positioner sideOffset={4}>
|
|
78
80
|
<AutocompletePrimitive.Popup
|
|
79
81
|
ref={ref}
|
|
80
82
|
className={cn(
|
|
81
|
-
"relative z-50 min-w-
|
|
82
|
-
"origin-
|
|
83
|
-
"data-
|
|
84
|
-
"data-
|
|
83
|
+
"relative z-50 min-w-48 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
|
|
84
|
+
"origin-(--transform-origin) outline-none",
|
|
85
|
+
"data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95",
|
|
86
|
+
"data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95",
|
|
85
87
|
className
|
|
86
88
|
)}
|
|
87
89
|
{...props}
|
|
88
90
|
/>
|
|
89
91
|
</AutocompletePrimitive.Positioner>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if (!portal) return content
|
|
95
|
+
|
|
96
|
+
return <AutocompletePortal>{content}</AutocompletePortal>
|
|
97
|
+
})
|
|
92
98
|
AutocompleteContent.displayName = "AutocompleteContent"
|
|
93
99
|
|
|
94
100
|
const AutocompleteList = React.forwardRef<
|
|
@@ -113,8 +119,8 @@ const AutocompleteItem = React.forwardRef<
|
|
|
113
119
|
ref={ref}
|
|
114
120
|
className={cn(
|
|
115
121
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
116
|
-
"data-
|
|
117
|
-
"data-
|
|
122
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
|
|
123
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
118
124
|
inset && "pl-8",
|
|
119
125
|
className
|
|
120
126
|
)}
|
|
@@ -151,7 +157,7 @@ const AutocompleteEmpty = React.forwardRef<
|
|
|
151
157
|
>(({ className, ...props }, ref) => (
|
|
152
158
|
<AutocompletePrimitive.Empty
|
|
153
159
|
ref={ref}
|
|
154
|
-
className={cn("
|
|
160
|
+
className={cn("p-2 text-center text-sm text-muted-foreground", className)}
|
|
155
161
|
{...props}
|
|
156
162
|
/>
|
|
157
163
|
))
|
|
@@ -39,11 +39,13 @@ export interface ButtonProps
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const Button = React.forwardRef<HTMLElement, ButtonProps>(
|
|
42
|
-
({ className, variant, size, ...props }, ref) => {
|
|
42
|
+
({ className, variant, size, render, nativeButton, ...props }, ref) => {
|
|
43
43
|
return (
|
|
44
44
|
<BaseButton
|
|
45
45
|
className={cn(buttonVariants({ variant, size, className }))}
|
|
46
46
|
ref={ref}
|
|
47
|
+
render={render}
|
|
48
|
+
nativeButton={render ? (nativeButton ?? false) : nativeButton}
|
|
47
49
|
{...props}
|
|
48
50
|
/>
|
|
49
51
|
)
|
|
@@ -71,24 +71,30 @@ ComboboxClear.displayName = "ComboboxClear"
|
|
|
71
71
|
|
|
72
72
|
const ComboboxContent = React.forwardRef<
|
|
73
73
|
React.ComponentRef<typeof ComboboxPrimitive.Popup>,
|
|
74
|
-
React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Popup>
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
React.ComponentPropsWithoutRef<typeof ComboboxPrimitive.Popup> & {
|
|
75
|
+
readonly portal?: boolean
|
|
76
|
+
}
|
|
77
|
+
>(({ className, portal = true, ...props }, ref) => {
|
|
78
|
+
const content = (
|
|
77
79
|
<ComboboxPrimitive.Positioner sideOffset={4}>
|
|
78
80
|
<ComboboxPrimitive.Popup
|
|
79
81
|
ref={ref}
|
|
80
82
|
className={cn(
|
|
81
|
-
"relative z-50 min-w-
|
|
82
|
-
"origin-
|
|
83
|
-
"data-
|
|
84
|
-
"data-
|
|
83
|
+
"relative z-50 min-w-48 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
|
|
84
|
+
"origin-(--transform-origin) outline-none",
|
|
85
|
+
"data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95",
|
|
86
|
+
"data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95",
|
|
85
87
|
className
|
|
86
88
|
)}
|
|
87
89
|
{...props}
|
|
88
90
|
/>
|
|
89
91
|
</ComboboxPrimitive.Positioner>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if (!portal) return content
|
|
95
|
+
|
|
96
|
+
return <ComboboxPortal>{content}</ComboboxPortal>
|
|
97
|
+
})
|
|
92
98
|
ComboboxContent.displayName = "ComboboxContent"
|
|
93
99
|
|
|
94
100
|
const ComboboxList = React.forwardRef<
|
|
@@ -113,8 +119,8 @@ const ComboboxItem = React.forwardRef<
|
|
|
113
119
|
ref={ref}
|
|
114
120
|
className={cn(
|
|
115
121
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
|
116
|
-
"data-
|
|
117
|
-
"data-
|
|
122
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
|
|
123
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
118
124
|
inset && "pl-10",
|
|
119
125
|
className
|
|
120
126
|
)}
|
|
@@ -156,7 +162,7 @@ const ComboboxEmpty = React.forwardRef<
|
|
|
156
162
|
>(({ className, ...props }, ref) => (
|
|
157
163
|
<ComboboxPrimitive.Empty
|
|
158
164
|
ref={ref}
|
|
159
|
-
className={cn("
|
|
165
|
+
className={cn("p-2 text-center text-sm text-muted-foreground", className)}
|
|
160
166
|
{...props}
|
|
161
167
|
/>
|
|
162
168
|
))
|
package/templates/menu/menu.tsx
CHANGED
|
@@ -30,8 +30,8 @@ const MenuTrigger = React.forwardRef<
|
|
|
30
30
|
"inline-flex h-10 items-center justify-center rounded-md border border-input bg-background px-4 py-2 text-sm font-medium",
|
|
31
31
|
"hover:bg-accent hover:text-accent-foreground",
|
|
32
32
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
33
|
-
"data-
|
|
34
|
-
"data-
|
|
33
|
+
"data-popup-open:bg-accent data-popup-open:text-accent-foreground",
|
|
34
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
35
35
|
className
|
|
36
36
|
)}
|
|
37
37
|
{...props}
|
|
@@ -75,10 +75,10 @@ const MenuContent = React.forwardRef<
|
|
|
75
75
|
<MenuPrimitive.Popup
|
|
76
76
|
ref={ref}
|
|
77
77
|
className={cn(
|
|
78
|
-
"z-50 min-w-
|
|
79
|
-
"origin-
|
|
80
|
-
"data-
|
|
81
|
-
"data-
|
|
78
|
+
"z-50 min-w-40 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
79
|
+
"origin-(--transform-origin) outline-none",
|
|
80
|
+
"data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95",
|
|
81
|
+
"data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95",
|
|
82
82
|
className
|
|
83
83
|
)}
|
|
84
84
|
{...props}
|
|
@@ -99,8 +99,8 @@ const MenuItem = React.forwardRef<
|
|
|
99
99
|
className={cn(
|
|
100
100
|
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
101
101
|
"focus:bg-accent focus:text-accent-foreground",
|
|
102
|
-
"data-
|
|
103
|
-
"data-
|
|
102
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
103
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
|
|
104
104
|
inset && "pl-8",
|
|
105
105
|
className
|
|
106
106
|
)}
|
|
@@ -118,8 +118,8 @@ const MenuCheckboxItem = React.forwardRef<
|
|
|
118
118
|
className={cn(
|
|
119
119
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
|
120
120
|
"focus:bg-accent focus:text-accent-foreground",
|
|
121
|
-
"data-
|
|
122
|
-
"data-
|
|
121
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
122
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
|
|
123
123
|
className
|
|
124
124
|
)}
|
|
125
125
|
checked={checked}
|
|
@@ -150,14 +150,14 @@ const MenuRadioItem = React.forwardRef<
|
|
|
150
150
|
className={cn(
|
|
151
151
|
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
|
|
152
152
|
"focus:bg-accent focus:text-accent-foreground",
|
|
153
|
-
"data-
|
|
154
|
-
"data-
|
|
153
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
154
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
|
|
155
155
|
className
|
|
156
156
|
)}
|
|
157
157
|
{...props}
|
|
158
158
|
>
|
|
159
159
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
|
160
|
-
<Circle className="h-2 w-2 fill-current data-
|
|
160
|
+
<Circle className="h-2 w-2 fill-current data-checked:block data-unchecked:hidden" />
|
|
161
161
|
</span>
|
|
162
162
|
{children}
|
|
163
163
|
</MenuPrimitive.RadioItem>
|
|
@@ -226,9 +226,9 @@ const MenuSubTrigger = React.forwardRef<
|
|
|
226
226
|
className={cn(
|
|
227
227
|
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
|
228
228
|
"focus:bg-accent focus:text-accent-foreground",
|
|
229
|
-
"data-
|
|
230
|
-
"data-
|
|
231
|
-
"data-
|
|
229
|
+
"data-highlighted:bg-accent data-highlighted:text-accent-foreground",
|
|
230
|
+
"data-popup-open:bg-accent data-popup-open:text-accent-foreground",
|
|
231
|
+
"data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
232
232
|
inset && "pl-8",
|
|
233
233
|
className
|
|
234
234
|
)}
|
|
@@ -249,10 +249,10 @@ const MenuSubContent = React.forwardRef<
|
|
|
249
249
|
<MenuPrimitive.Popup
|
|
250
250
|
ref={ref}
|
|
251
251
|
className={cn(
|
|
252
|
-
"z-50 min-w-
|
|
253
|
-
"origin-
|
|
254
|
-
"data-
|
|
255
|
-
"data-
|
|
252
|
+
"z-50 min-w-32 overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md",
|
|
253
|
+
"origin-(--transform-origin) outline-none",
|
|
254
|
+
"data-starting-style:animate-in data-starting-style:fade-in-0 data-starting-style:zoom-in-95",
|
|
255
|
+
"data-ending-style:animate-out data-ending-style:fade-out-0 data-ending-style:zoom-out-95",
|
|
256
256
|
className
|
|
257
257
|
)}
|
|
258
258
|
{...props}
|
|
@@ -175,6 +175,14 @@ const MenubarRadioItem = React.forwardRef<
|
|
|
175
175
|
))
|
|
176
176
|
MenubarRadioItem.displayName = "MenubarRadioItem"
|
|
177
177
|
|
|
178
|
+
const MenubarGroup = React.forwardRef<
|
|
179
|
+
React.ComponentRef<typeof Menu.Group>,
|
|
180
|
+
React.ComponentProps<typeof Menu.Group>
|
|
181
|
+
>(({ className, ...props }, ref) => (
|
|
182
|
+
<Menu.Group ref={ref} className={cn(className)} {...props} />
|
|
183
|
+
))
|
|
184
|
+
MenubarGroup.displayName = "MenubarGroup"
|
|
185
|
+
|
|
178
186
|
const MenubarLabel = React.forwardRef<
|
|
179
187
|
React.ComponentRef<typeof Menu.GroupLabel>,
|
|
180
188
|
React.ComponentProps<typeof Menu.GroupLabel> & {
|
|
@@ -293,6 +301,7 @@ export {
|
|
|
293
301
|
MenubarCheckboxItem,
|
|
294
302
|
MenubarRadioGroup,
|
|
295
303
|
MenubarRadioItem,
|
|
304
|
+
MenubarGroup,
|
|
296
305
|
MenubarLabel,
|
|
297
306
|
MenubarSeparator,
|
|
298
307
|
MenubarShortcut,
|
|
@@ -17,10 +17,11 @@ const PopoverContent = React.forwardRef<
|
|
|
17
17
|
readonly sideOffset?: number
|
|
18
18
|
readonly align?: "start" | "center" | "end"
|
|
19
19
|
readonly side?: "top" | "bottom" | "left" | "right"
|
|
20
|
+
readonly portal?: boolean
|
|
20
21
|
}
|
|
21
|
-
>(({ className, align = "center", side = "bottom", sideOffset = 8, ...props }, ref) =>
|
|
22
|
-
|
|
23
|
-
<PopoverPrimitive.Positioner
|
|
22
|
+
>(({ className, align = "center", side = "bottom", sideOffset = 8, portal = true, ...props }, ref) => {
|
|
23
|
+
const content = (
|
|
24
|
+
<PopoverPrimitive.Positioner
|
|
24
25
|
align={align}
|
|
25
26
|
side={side}
|
|
26
27
|
sideOffset={sideOffset}
|
|
@@ -29,17 +30,21 @@ const PopoverContent = React.forwardRef<
|
|
|
29
30
|
ref={ref}
|
|
30
31
|
className={cn(
|
|
31
32
|
"z-50 rounded-lg border bg-popover px-6 py-4 text-popover-foreground shadow-lg outline-none",
|
|
32
|
-
"origin-
|
|
33
|
-
"data-
|
|
34
|
-
"data-
|
|
33
|
+
"origin-(--transform-origin)",
|
|
34
|
+
"data-starting-style:scale-90 data-starting-style:opacity-0",
|
|
35
|
+
"data-ending-style:scale-90 data-ending-style:opacity-0",
|
|
35
36
|
"transition-[transform,opacity] duration-150",
|
|
36
37
|
className
|
|
37
38
|
)}
|
|
38
39
|
{...props}
|
|
39
40
|
/>
|
|
40
41
|
</PopoverPrimitive.Positioner>
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if (!portal) return content
|
|
45
|
+
|
|
46
|
+
return <PopoverPrimitive.Portal>{content}</PopoverPrimitive.Portal>
|
|
47
|
+
})
|
|
43
48
|
PopoverContent.displayName = "PopoverContent"
|
|
44
49
|
|
|
45
50
|
// Optimized PopoverArrow
|
|
@@ -113,7 +118,7 @@ const PopoverBackdrop = React.forwardRef<
|
|
|
113
118
|
ref={ref}
|
|
114
119
|
className={cn(
|
|
115
120
|
"fixed inset-0 z-40 bg-black/50",
|
|
116
|
-
"data-
|
|
121
|
+
"data-starting-style:opacity-0 data-ending-style:opacity-0",
|
|
117
122
|
"transition-opacity duration-150",
|
|
118
123
|
className
|
|
119
124
|
)}
|
|
@@ -21,7 +21,7 @@ const Radio = React.forwardRef<
|
|
|
21
21
|
<RadioPrimitive.Root
|
|
22
22
|
ref={ref}
|
|
23
23
|
className={cn(
|
|
24
|
-
"aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background",
|
|
24
|
+
"flex items-center justify-center aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background",
|
|
25
25
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
26
26
|
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
27
27
|
className
|
|
@@ -39,9 +39,10 @@ const SelectContent = React.forwardRef<
|
|
|
39
39
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Popup> & {
|
|
40
40
|
readonly position?: "item-aligned" | "popper"
|
|
41
41
|
readonly sideOffset?: number
|
|
42
|
+
readonly portal?: boolean
|
|
42
43
|
}
|
|
43
|
-
>(({ className, children, position = "popper", sideOffset = 4, ...props }, ref) =>
|
|
44
|
-
|
|
44
|
+
>(({ className, children, position = "popper", sideOffset = 4, portal = true, ...props }, ref) => {
|
|
45
|
+
const content = (
|
|
45
46
|
<SelectPrimitive.Positioner
|
|
46
47
|
sideOffset={sideOffset}
|
|
47
48
|
alignItemWithTrigger={position === "item-aligned"}
|
|
@@ -49,7 +50,7 @@ const SelectContent = React.forwardRef<
|
|
|
49
50
|
<SelectPrimitive.Popup
|
|
50
51
|
ref={ref}
|
|
51
52
|
className={cn(
|
|
52
|
-
"relative z-50 max-h-96 min-w-
|
|
53
|
+
"relative z-50 max-h-96 min-w-32 overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md",
|
|
53
54
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
54
55
|
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
55
56
|
position === "popper" &&
|
|
@@ -65,8 +66,12 @@ const SelectContent = React.forwardRef<
|
|
|
65
66
|
<SelectScrollDownArrow />
|
|
66
67
|
</SelectPrimitive.Popup>
|
|
67
68
|
</SelectPrimitive.Positioner>
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if (!portal) return content
|
|
72
|
+
|
|
73
|
+
return <SelectPrimitive.Portal>{content}</SelectPrimitive.Portal>
|
|
74
|
+
})
|
|
70
75
|
SelectContent.displayName = "SelectContent"
|
|
71
76
|
|
|
72
77
|
// Add scroll arrows for better UX with large lists
|
|
@@ -143,7 +148,7 @@ const SelectItem = React.forwardRef<
|
|
|
143
148
|
ref={ref}
|
|
144
149
|
className={cn(
|
|
145
150
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-2 text-sm outline-none",
|
|
146
|
-
"focus:bg-accent focus:text-accent-foreground data-
|
|
151
|
+
"focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
|
|
147
152
|
// Dynamic padding based on indicator presence and position
|
|
148
153
|
showIndicator && isLeftIndicator && "pl-8",
|
|
149
154
|
showIndicator && isRightIndicator && "pr-8",
|