untitledui 0.1.2 → 0.1.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/dist/index.mjs +19 -0
- package/package.json +10 -8
- package/templates/default/package.json +20 -21
- package/templates/default/src/app/home-screen.tsx +2 -3
- package/templates/default/src/app/layout.tsx +5 -13
- package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx +1 -1
- package/templates/default/src/components/shared/buttons/button.tsx +2 -1
- package/templates/default/src/components/shared/dropdown/dropdown.tsx +27 -18
- package/templates/default/src/components/shared/form/hook-form.tsx +75 -0
- package/templates/default/src/components/shared/{inputs/input → input}/index.tsx +3 -3
- package/templates/default/src/components/shared/{inputs/input → input}/input-payment.tsx +4 -4
- package/templates/default/src/components/shared/{inputs/input → input}/input-with-button.tsx +4 -4
- package/templates/default/src/components/shared/{inputs/input → input}/input-with-dropdown.tsx +4 -4
- package/templates/default/src/components/shared/{inputs/input → input}/input-with-prefix.tsx +4 -4
- package/templates/default/src/components/shared/progress-indicators/progress-indicators.tsx +2 -2
- package/templates/default/src/components/shared/{input-dropdown → select}/combobox.tsx +9 -9
- package/templates/default/src/components/shared/{input-dropdown → select}/multi-select.tsx +166 -166
- package/templates/default/src/components/shared/{input-dropdown → select}/popover.tsx +2 -2
- package/templates/default/src/components/shared/select/select-item.tsx +70 -0
- package/templates/default/src/components/shared/{input-dropdown/select.tsx → select/select-native.tsx} +2 -2
- package/templates/default/src/components/shared/select/select.tsx +143 -0
- package/templates/default/src/components/shared/slider/slider.tsx +2 -2
- package/templates/default/src/components/shared/{inputs/textarea → textarea}/textarea.tsx +2 -2
- package/templates/default/src/providers/theme.tsx +1 -1
- package/templates/default/src/styles/globals.css +3 -1
- package/templates/default/src/styles/theme.css +392 -380
- package/templates/default/src/styles/typography.css +20 -20
- package/dist/commands/add.js +0 -339
- package/dist/commands/init.js +0 -436
- package/dist/helper/download-tar-api.js +0 -129
- package/dist/helper/download-tar.js +0 -81
- package/dist/helper/find-css-file.js +0 -19
- package/dist/helper/formatText.js +0 -37
- package/dist/helper/get-components-api.js +0 -47
- package/dist/helper/get-components-list.js +0 -62
- package/dist/helper/get-components.js +0 -19
- package/dist/helper/get-config.js +0 -163
- package/dist/helper/get-package-info.js +0 -99
- package/dist/helper/get-pkg-manager.js +0 -16
- package/dist/helper/get-project.js +0 -176
- package/dist/helper/install-template.js +0 -29
- package/dist/helper/match-color-css.js +0 -82
- package/dist/helper/update-color-css.js +0 -134
- package/dist/index.js +0 -25
- package/dist/package.json +0 -50
- package/dist/res/components.json +0 -520
- package/dist/res/config.json +0 -3
- package/templates/default/src/components/shared/input-dropdown/dropdown-item.tsx +0 -98
- package/templates/default/src/components/shared/input-dropdown/input-dropdown.tsx +0 -172
- package/templates/default/src/fonts/GeistMonoVF.woff +0 -0
- package/templates/default/src/fonts/GeistVF.woff +0 -0
- package/templates/default/src/styles/colors.css +0 -805
- /package/templates/default/src/components/shared/{inputs → file-upload-trigger}/file-upload-trigger.tsx +0 -0
- /package/templates/default/src/components/shared/{inputs/form → form}/form.tsx +0 -0
- /package/templates/default/src/components/shared/{inputs → input}/hint-text.tsx +0 -0
- /package/templates/default/src/components/shared/{inputs → input}/label.tsx +0 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{Command as $0}from"commander";import R3 from"async-retry";import v from"chalk";import{Command as s3}from"commander";import{execa as i}from"execa";import*as _ from"fs";import h from"ora";import a3 from"os";import*as A from"path";import d from"prompts";import{Project as p3}from"ts-morph";import{posix as x3,sep as T3}from"path";import{Readable as y3,pipeline as S3}from"stream";import{promisify as C3}from"util";import{x as k3}from"tar";import b3 from"node-fetch";var F3=C3(S3);async function V3(z,W){try{let Q=await O3(W);await F3(Q,k3({cwd:z,strip:2,filter:(Y)=>{return Y.split(T3).join(x3.sep).includes(W.template||"basic-form")}}))}catch(Q){throw new Error(`Failed to download or extract repository from API: ${Q instanceof Error?Q.message:Q}`)}}async function O3(z){try{let Q=await b3("https://untitledui-docs.vercel.app/api/download-repo",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/octet-stream"},body:JSON.stringify(z)});if(Q.status===403||Q.status===404)throw new Error(z.key?"License key is invalid or expired":"Repository not found");if(!Q.ok)throw new Error(`Failed to download from API. Status: ${Q.status} ${Q.statusText}`);if(!Q.body)throw new Error("Response body is empty");return y3.from(Q.body)}catch(Q){throw new Error(`Error downloading tarball: ${Q instanceof Error?Q.message:Q}`)}}async function m(z){let W=`https://untitledui-docs.vercel.app/api/validate-key?key=${z}`;try{return(await b3(W)).status===200}catch{return!1}}import P3 from"node-fetch";async function Q3(z,W,Q){try{return await(await P3("https://untitledui-docs.vercel.app/api/components",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({type:z,components:W,key:Q})})).json()}catch(X){return console.error(X),null}}import L3 from"node-fetch";async function M3(z,W=""){let Q=`https://untitledui-docs.vercel.app/api/components/list?key=${W}&type=${z}`;try{let X=await(await L3(Q)).json();if(!X?.components?.length)return null;return X}catch(Y){return console.error(Y),null}}async function J3(z=""){let W=`https://untitledui-docs.vercel.app/api/components/list?key=${z}`;try{let Y=await(await L3(W)).json();if(!Y?.types?.length)return null;return Y}catch(Q){return console.error(Q),null}}import l3 from"fast-glob";import*as H3 from"fs";import*as X3 from"path";import{Project as c3}from"ts-morph";import{loadConfig as I3}from"tsconfig-paths";import g3 from"prettier";async function G3(z,W="typescript"){try{return await g3.format(z,{parser:W})}catch(Q){return console.error("Error formatting with Prettier:",Q),z}}import n from"fast-glob";import*as c from"fs";import*as r from"path";import{loadConfig as m3}from"tsconfig-paths";var w=["**/node_modules/**",".next","public","dist","build"];async function s(z){let W=c.existsSync(r.resolve(z,"src")),Q=c.existsSync(r.resolve(z,`${W?"src/":""}app`)),[Y,X,V,B]=await Promise.all([n.glob("**/{next,vite,astro}.config.*|gatsby-config.*",{cwd:z,deep:2,ignore:w}),h3(z),d3(z),f3(z)]),b={framework:"other",isTsx:X,tailwindFile:V||null,aliasPrefix:B,isSrcDir:W,isUsingAppDir:Q};if(Y.find((j)=>j.startsWith("next.config."))?.length)return b.framework=Q?"next-app":"next-pages",b;else if(Y?.length||c.existsSync(r.resolve(z,"package.json")))return b.framework="other",b;return null}async function h3(z){return(await n.glob("tsconfig.*",{cwd:z,deep:2,ignore:w})).length>0}async function d3(z){let W=await n.glob("tailwind.config.*",{cwd:z,deep:2,ignore:w});if(!W.length)return null;return W[0]}async function f3(z){let W=await m3(z);if(W?.resultType==="failed"||!Object.keys(W.paths).length)return null;let Q={};for(let[Y,X]of Object.entries(W.paths)){let V=Y.replace(/\/\*$/,"/");if(X.some((B)=>B.includes("/app/*")))Q.appPrefix=V;else if(X.some((B)=>B.includes("/components/*")))Q.componentsPrefix=V;else if(X.some((B)=>B.includes("/utils/*")))Q.utilsPrefix=V;else if(X.some((B)=>B.includes("/styles/*")))Q.stylesPrefix=V;else if(X.some((B)=>B.includes("./*")||B.includes("/src/*")))Q.srcPrefix=V}return Q||null}function a(z){let W=I3(z),Q=l3.sync(["tailwind.config.*","**/globals.css","**/{layout,_app}.tsx","package.json"],{cwd:z,deep:4,absolute:!0,onlyFiles:!0,ignore:w}),Y={tailwindFile:Q.find((X)=>X.includes("tailwind.config.")),cssFile:Q.find((X)=>X.includes("globals.css")),layoutFile:Q.find((X)=>X.includes("layout")),appFile:Q.find((X)=>X.includes("_app")),packageJson:Q.find((X)=>X.includes("package.json")),tsConfig:W?.resultType==="success"?W?.configFileAbsolutePath:void 0};if(W.resultType==="failed")throw new Error(`Failed to load tsconfig.json. ${W.message??""}`.trim());return Y}function Y3(z,W,Q){if(z.includes("components")){if(W?.componentsPrefix)return z.replace(/@\/components\//,X3.join(W?.componentsPrefix,Q?Q.replace(/components\//,""):"","/"));if(Q)return z.replace(/@\/components\//,X3.join(W?.srcPrefix||"@/",Q,"/"))}if(z.includes("app")&&W?.appPrefix)return z.replace(/^@\/app\//,W?.appPrefix);if(z.includes("utils")&&W?.utilsPrefix)return z.replace(/^@\/utils\//,W?.utilsPrefix);if(z.includes("styles")&&W?.stylesPrefix)return z.replace(/^@\/styles\//,W?.stylesPrefix);if(W?.srcPrefix)return z.replace(/^@\//,W?.srcPrefix);return z}async function j3(z,W,Q="@/*"){let Y=await I3(z);if(Y?.resultType==="failed")return null;let X={};if(!Object.keys(Y.paths).length){Y.paths={[Q]:[`./${W?"src/":""}*`]},X.srcPrefix=Q.replace(/\/\*$/,"");let V=await H3.promises.readFile(Y.configFileAbsolutePath,"utf-8"),B;if(!V.includes('"paths":'))B=V.replace(/"compilerOptions":\s*{/,`"compilerOptions": {
|
|
3
|
+
"paths": ${JSON.stringify(Y.paths)},`);else B=V.replace(/"paths":\s*{[^}]*}/,`"paths": ${JSON.stringify(Y.paths)}`);let b=new c3,j=await G3(B,"json");return b.createSourceFile(Y.configFileAbsolutePath,j,{overwrite:!0}),await b.save(),X}return X||null}import r3 from"node-fetch";async function n3(){try{return await(await r3("https://untitledui-docs.vercel.app/api/get-pkg")).json()}catch(W){return console.error(W),null}}async function p({dependencies:z,devDependencies:W,shouldThrow:Q=!0}){let Y=await n3();if(!Y)if(Q)throw new Error("package.json not found");else return null;let X=[],V=[];if(z)z.forEach((B)=>{if(Y.dependencies?.[B])X.push(`${B}@${Y.dependencies[B]}`)});if(W)W.forEach((B)=>{if(Y.devDependencies?.[B])V.push(`${B}@${Y.devDependencies[B]}`)});return{dependencies:X,devDependencies:V}}function u(){if("bun/1.2.4 npm/? node/v22.6.0 darwin arm64".startsWith("yarn"))return"yarn";if("bun/1.2.4 npm/? node/v22.6.0 darwin arm64".startsWith("pnpm"))return"pnpm";if("bun/1.2.4 npm/? node/v22.6.0 darwin arm64".startsWith("bun"))return"bun";return"npm"}var i3=A.join(a3.homedir(),".untitledui"),k=A.join(i3,"config.json"),$={components:[],path:"",type:void 0,license:""};if(_.existsSync(k)){let z=JSON.parse(_.readFileSync(k,"utf-8"));$.license=z.license}var o=(z)=>{if(z.aborted)process.stdout.write("\x1B[?25h"),process.stdout.write(`
|
|
4
|
+
`),process.exit(1)},_3=new s3().name("add").description("add a component to your project").argument("[components...]","the components to add").option("-o, --overwrite","overwrite existing files.",!1).option("-a, --all","add all available components",!1).option("-p, --path <path>","the path to add the component to.").option("-t, --type <shared|marketing|shared-assets|application|foundations>","the type of the component to add.").option("-l, --license <license-key>","Add a license key for adding components.").action(async(z,W)=>{if(z)$.components=z;if(W)$.overwrite=W.overwrite,$.all=W.all,$.path=W.path,$.license=W.license||$.license;try{await o3($)}catch(Q){console.error(v.red(Q))}});async function o3(z){let W=h().start(),Q=process.cwd();if(!_.existsSync(A.resolve(Q,"package.json")))W.warn("This command should be run in a project directory."),process.exit(0);let X=await s(Q);if($.license){let Z=await m($.license);if(console.log(Z,$),!Z)W.fail("Invalid license key"),process.exit(0);if(!_.existsSync(k)){let R=A.dirname(k);_.mkdirSync(R,{recursive:!0}),_.writeFileSync(k,JSON.stringify({license:$.license},null,2))}if(JSON.parse(_.readFileSync(k,"utf-8")).license!==$.license)_.writeFileSync(k,JSON.stringify({license:$.license},null,2))}W.stop();let V=[];if($.components.length){let Z=await Q3($.type,$.components,$.license);if(Z?.pro?.length)Z?.pro?.forEach((M)=>console.log(`The ${v.yellowBright(M.split("/")[1])} component is only available for PRO users. Get access to the PRO at ${v.cyan("https://www.untitledui.com/buy/untitled-ui")}`)),process.exit(0);if(!Z?.components.length)console.log("No components found"),process.exit(0);V.push(...Z.components)}if(!$?.type&&!$?.components.length){let Z=await J3($.license);if(!Z)console.log("No component types found"),process.exit(0);let M=await d({type:"select",name:"type",onState:o,message:`What type of ${v.cyan("component")} are you adding?`,choices:Z?.types.map((R)=>({title:R,value:R}))});$.type=M.type}if(!$?.path){let Z=await d({type:"text",name:"path",onState:o,message:`Where would you like to add the ${v.cyan("components")}?`,initial:"components"});$.path=Z.path}if(X&&!X?.aliasPrefix){let Z=/^[^*"]+\/\*\s*$/,M=await d({type:"text",name:"aliasPrefix",onState:o,initial:"@/*",message:`What is the ${v.cyan("import alias")} for your project?`,validate:(R)=>Z.test(R)?!0:"Import alias must follow the pattern <prefix>/*"});X.aliasPrefix=await j3(Q,X?.isSrcDir,M?.aliasPrefix)}if(!$?.components.length){let Z=await M3($?.type,$.license);if(!Z)console.log("No components found"),process.exit(0);let M=await d({type:"multiselect",name:"components",onState:o,message:`Which ${v.cyan("components")} would you like to add?`,choices:Z?.components?.map((R)=>({title:R||"example",value:R||"example",selected:$.components.includes(R)})),instructions:!1,hint:"- Space to select. Return to submit"});if($.components=M.components,!M.components||M.components.length===0)console.log("No option selected. Exiting..."),process.exit(0)}if(!$.components?.length)W.warn("No components selected. Exiting."),process.exit(0);let B=a(Q),b=new Set,j=new Set,q=new Set,J=new p3({tsConfigFilePath:B?.tsConfig});if($.type&&$.components.length){let Z=await Q3($.type,$.components,$.license);if(!Z?.components.length)console.log("No components found"),process.exit(0);V.push(...Z.components)}V.forEach((Z)=>{let M=h(`Adding ${Z.name}`).start(),R=Z.files;Z.dependencies.forEach((E)=>j.add(E)),Z.devDependencies.forEach((E)=>q.add(E));try{R?.forEach(async({path:E,code:T})=>{let D=A.join(process.cwd(),`${X?.isSrcDir&&"src"}`,E.replace(/components\//,$.path+"/")),g=A.dirname(D);if(_.existsSync(D)&&!$.overwrite){if(_.readFileSync(D,"utf-8")!==T)b.add({code:T,path:D})}else{_.mkdirSync(g,{recursive:!0}),_.writeFileSync(D,T);let O=J.addSourceFileAtPath(A.resolve(D));O.getImportDeclarations().forEach((G)=>{let S=G.getModuleSpecifierValue();G.setModuleSpecifier(Y3(S,X?.aliasPrefix,$.path))}),await O.save()}}),M.succeed(`${v.green(Z.name)} is added`)}catch(E){M.fail(`
|
|
5
|
+
Failed to add component`),console.error(v.red(E)),process.exit(0)}});let L=await p({dependencies:Array.from(j),devDependencies:Array.from(q),shouldThrow:!0}),N=u();if(L?.dependencies?.length){let Z=h("Installing dependencies").start();await R3(()=>i(N,[N==="npm"?"install":"add",...Array.from(L.dependencies)]).catch(async(M)=>{if(M.message.includes("peer"))Z.warn("Dependency conflict detected. Retrying with --legacy-peer-deps..."),Z.start("Installing dependencies with --legacy-peer-deps flag"),await i(N,[N==="npm"?"install":"add",...Array.from(L.dependencies),"--legacy-peer-deps"])}),{retries:1}),Z.succeed("Dependencies are installed")}if(L?.devDependencies?.length){let Z=h("Installing devDependencies").start();await R3(()=>i(N,[N==="npm"?"install":"add",...Array.from(L.devDependencies)]).catch(async(M)=>{if(M.message.includes("peer"))Z.warn("DevDependency conflict detected. Retrying with --legacy-peer-deps..."),Z.start("Installing devDependencies with --legacy-peer-deps flag"),await i(N,[N==="npm"?"install":"add",...Array.from(L.devDependencies),"--legacy-peer-deps"])}),{retries:1}),Z.succeed("DevDependencies are installed")}if(b.size&&!$?.overwrite)if(console.log(`
|
|
6
|
+
Following files already exist in the directory.`),b.forEach((M)=>{console.log(v.green(`- ${A.relative(process.cwd(),M.path)}`))}),(await d({type:"confirm",name:"overwrite",message:"Do you want to overwrite the existing files?",initial:!0})).overwrite){let M=h("Overwriting files").start();b.forEach(async(R)=>{_.writeFileSync(R.path,R.code);let E=J.addSourceFileAtPath(A.resolve(R.path));E.getImportDeclarations().forEach((T)=>{let D=T.getModuleSpecifierValue();T.setModuleSpecifier(Y3(D,X?.aliasPrefix,$.path))}),await E.save()}),M.succeed("Files are overwritten"),process.exit(1)}else console.log(`Use ${v.cyan("--overwrite")} or ${v.cyan("-o")} to overwrite existing files, or refer to the documentation ${v.cyan("https://untitled.xyz/docs")} for manual installation. The rest of the files are added.`),process.exit(0)}import v3 from"async-retry";import I from"chalk";import{Command as e3}from"commander";import{execa as W3}from"execa";import U3 from"fast-glob";import H from"fs";import f from"ora";import W0 from"os";import*as K from"path";import l from"prompts";import{Project as z0}from"ts-morph";import t3 from"fast-glob";function N3(z){let W=t3.sync("**/**/theme.css",{onlyFiles:!0,absolute:!0,cwd:z,ignore:w});return W.length?W[0]:null}import t from"fs";import E3 from"path";function Z3(z,W){if(!t.existsSync(W))t.mkdirSync(W,{recursive:!0});let Q=t.readdirSync(z,{withFileTypes:!0});for(let Y of Q){let X=E3.join(z,Y.name),V=E3.join(W,Y.name);if(Y.isDirectory())Z3(X,V);else t.copyFileSync(X,V)}}import $3 from"chalk";import*as y from"fs";import*as e from"path";function q3(z){if(!y.existsSync(e.resolve(z)))console.log($3.red(`Error: CSS file not found at ${z}`)),process.exit(1);let W=y.readFileSync(e.resolve(z),"utf-8"),Q=/(--color-[a-zA-Z-]+-\d{1,3}):\s*(rgb\([^)]+\))/g,Y={},X;while((X=Q.exec(W))!==null)if(X[1]&&X[2])Y[X[1]]=X[2];return Y}function B3(z,W,Q){let Y=e.resolve(Q);if(!y.existsSync(Y)){console.log($3.red(`Error: CSS file not found at ${Y}`));return}let X=y.readFileSync(Y,"utf-8"),V=q3(Q),B={},b={};for(let[q,J]of Object.entries(V))if(q.startsWith(`--color-${z}-`)){let L=q.replace(`--color-${z}-`,"");B[L]=q}else if(q.startsWith(`--color-${W}-`)){let L=q.replace(`--color-${W}-`,"");b[L]=J}let j=!1;for(let[q,J]of Object.entries(B))if(b[q]){let L=b[q],N=new RegExp(`(${J}):\\s*rgb\\([^)]*\\);?`,"g");if(N.test(X))X=X.replace(N,`$1: ${L};`),j=!0;else console.log($3.yellow(`No match found for ${J}`))}if(j)y.writeFileSync(Y,X,"utf-8")}var __dirname="/Users/deebov/Developer/untitledui/react/packages/cli/commands",Q0=K.join(W0.homedir(),".untitledui"),F=K.join(Q0,"config.json"),A3="default",X0=["with-stripe","otp","magic-link"],x="",U={template:"",color:""};if(H.existsSync(F)){let z=JSON.parse(H.readFileSync(F,"utf-8"));U.license=z.license}var z3=(z)=>{if(z.aborted)process.stdout.write("\x1B[?25h"),process.stdout.write(`
|
|
7
|
+
`),process.exit(1)},u3=new e3().name("init").description("initialize a new project").argument("[directory]").usage("[directory] [options]").helpOption("-h, --help","Display this help message.").option("-t, --template <starter-kit>","Specify a template for the project.").option("-c, --color <color-name>","Specify a color for the project.").option("-o, --overwrite","Overwrite existing files.",!1).option("-l, --license <license-key>","Add a license key to download the repository.").action(async(z,W)=>{if(z)x=z;if(W)U.color=W.color,U.template=W.template,U.overwrite=W.overwrite,U.license=W.license||U.license;try{await Y0(W)}catch(Q){console.error(I.red(Q))}});async function Y0(z){let W=process.cwd(),Q=H.existsSync(K.resolve(W,"package.json")),Y=K.resolve(K.join(__dirname,"../templates/default")),X=N3(Y),V=q3(X??""),B=Array.from(new Set(Object.keys(V).map((q)=>q?.split("--color-")?.[1]?.replace(/-\d{1,3}/,"")))),b=f().start(),j=await s(W);if(U.license){if(!await m(U.license))b.fail("Invalid license key"),process.exit(0);if(!H.existsSync(F)){let L=K.dirname(F);H.mkdirSync(L,{recursive:!0}),H.writeFileSync(F,JSON.stringify({license:U.license},null,2))}if(JSON.parse(H.readFileSync(F,"utf-8")).license!==U.license)H.writeFileSync(F,JSON.stringify({license:U.license},null,2))}if(!Q){if(b.stop(),!x){let q=await l({onState:z3,type:"text",name:"path",message:"What is your project named?",initial:"untitled-ui"});if(typeof q.path==="string")x=q.path.trim()}if(H.existsSync(K.resolve(x)))b.fail(I.red("Directory already exists!")),process.exit(1);if(!U.template){let q=await l({type:"select",name:"template",onState:z3,message:`Which ${I.cyan("starter kit")} would you like to use?`,choices:[{title:"default-template",value:A3},{title:"basic-form",value:"basic-form"},{title:"magic-link",value:"magic-link"},{title:"next-intl",value:"next-intl"},{title:"otp",value:"otp"},{title:"react-hook-form",value:"react-hook-form"},{title:"with-stripe",value:"with-stripe"}]});if(U.template=q.template,X0.includes(U.template)&&!U.license){let J=await l({type:"text",name:"license",onState:z3,message:`Enter the license key to download the ${I.cyan(U.template)} template:`});if(U.license=J.license,!await m(J.license))b.fail("Invalid license key"),process.exit(0)}}b.succeed("Template is selected: "+I.green(U.template))}else if(j?.framework==="other")b.fail("Unsupported project framework"),console.log(`Please refer to the documentation ${I.cyan("https://untitled.xyz/docs")} for supported frameworks or proceed with manual installation.`),process.exit(1);else b.succeed(I.yellow("Detected Next.js project, proceeding with the setup..."));if(!U.color){let q=await l({type:"select",name:"color",onState:z3,initial:z.color??"",message:`Which ${I.cyan("color")} would you like to use as the ${I.cyanBright("brand")} color?`,choices:B.map((J)=>({title:J,value:J}))});U.color=q.color}if(x&&!Q){let q=K.resolve(x);console.log(`
|
|
8
|
+
Creating a new project in ${I.blue(x)}`);let J=f("Downloading and extracting the repository...").start();try{if(H.mkdirSync(q,{recursive:!0}),U.template===A3){let Z=K.resolve(K.join(__dirname,"../templates/",U.template));Z3(Z,q)}else await v3(()=>V3(q,{username:"a-peak-works",repo:"starter-kits",branch:"master",template:U.template,key:U.license}),{retries:2});J.succeed("Files are downloaded and extracted successfully!");let L=f({text:"Installing dependencies..."}).start(),N=U3.sync(["**/styles/theme.css"],{cwd:q,absolute:!0,onlyFiles:!0})[0];B3("brand",U.color,N??""),await v3(()=>W3("sh",["-c",`cd ${x} && ${u()} install && git init`]).catch(async(Z)=>{if(Z.message.includes("peer"))L.warn("Dependency conflict detected. Retrying with --legacy-peer-deps..."),L.start("Installing dependencies with --legacy-peer-deps flag"),await W3("sh",["-c",`cd ${x} && ${u()==="npm"?"npm --legacy-peer-deps":u()} install && git init`])}),{retries:1}),L.succeed("Dependencies installed"),console.log(`
|
|
9
|
+
Your project is ready, to get staretd run the following commands:
|
|
10
|
+
|
|
11
|
+
cd ${I.cyan(x)}
|
|
12
|
+
${I.cyan(u())} run dev`),process.exit(1)}catch(L){if(J.fail(I.red(`
|
|
13
|
+
Failed to download and extract the repository`)),L instanceof Error)console.error(L.message);else console.error(`
|
|
14
|
+
`);H.rmdirSync(q,{recursive:!0}),process.exit(0)}}else{let q=new Set,J=a(W),L=K.resolve(K.join(__dirname,"../templates/default")),N=U3.sync(["**/{styles,plugins}/**","postcss.config.*"],{cwd:K.join(L,"src"),onlyFiles:!0,ignore:w.filter((G)=>!G.includes("public"))}),Z=await p({dependencies:["tailwindcss","tailwindcss-animate","@tailwindcss/typography","tailwindcss-react-aria-components","@designbycode/tailwindcss-mask-image"],devDependencies:["@tailwindcss/postcss","postcss"]});if(N.forEach((G)=>{let S=G.includes("postcss.config"),P=K.resolve(K.join(L,"src"),G),C=K.resolve(process.cwd(),S?G:`${j?.isSrcDir?"src":""}/${G}`);if(H.existsSync(C)){if(U?.overwrite)H.copyFileSync(P,C);else{let D3=H.readFileSync(C,"utf-8"),w3=H.readFileSync(P,"utf-8");if(D3!==w3)q.add({targetFile:C,sourceFile:P})}return}H.mkdirSync(K.dirname(C),{recursive:!0}),H.writeFileSync(C,""),H.copyFileSync(K.resolve(K.join(L,"src"),G),C)}),q.size&&!U?.overwrite)if(console.log(`
|
|
15
|
+
`),b.fail("Following files already exist in the directory."),q.forEach((S)=>{console.log(`- ${I.green(S.targetFile)}`)}),(await l({type:"confirm",name:"overwrite",message:"Do you want to overwrite the existing files?",initial:!0})).overwrite){let S=f("Overwriting files").start();q.forEach((P)=>{H.copyFileSync(P.sourceFile,P.targetFile)}),S.succeed("Files are overwritten"),process.exit(1)}else console.log(`Use ${I.cyan("--overwrite")} or ${I.cyan("-o")} to overwrite existing files, or refer to the documentation ${I.cyan("https://untitled.xyz/docs")} for manual installation. The rest of the files are added.`),process.exit(0);if(J?.tailwindFile)console.log(`
|
|
16
|
+
Tailwind config file exists in the project directory. You can add it to your globals.css as follows:`),console.log(`
|
|
17
|
+
${I.cyan(`@config "../${j?.isSrcDir&&"../"}${K.relative(process.cwd(),J.tailwindFile)}";`)}
|
|
18
|
+
`);let M=J?.layoutFile||J?.appFile||"",E=new z0({tsConfigFilePath:K.resolve(J?.tsConfig||"")}).addSourceFileAtPath(K.resolve(M)),T=["colors.css","globals.css","inter.css","text-styles.css","theme.css","typography.css"];E.getImportDeclarations().filter((G)=>T.includes(G.getModuleSpecifierValue())).forEach((G)=>G.remove());let D=K.relative(K.resolve(process.cwd(),`${j?.isSrcDir&&"src"}`),M).split("/").length;E.addImportDeclarations(N.filter((G)=>G.includes("styles")).map((G)=>({moduleSpecifier:`${j?.aliasPrefix?.stylesPrefix||j?.aliasPrefix?.srcPrefix||"../".repeat(D-1)}${j?.aliasPrefix?.stylesPrefix?G?.split("styles/")[1]:G}`}))),E.saveSync();let g=f(),O=U3.sync(["**/styles/theme.css"],{cwd:W,absolute:!0,onlyFiles:!0,ignore:w});if(!O?.length)return b.fail("Failed to copy theme.css file");if(B3("brand",U.color,O[0]??""),!Z)g.fail("Failed to get dependencies from package.json"),process.exit(1);g.start("Installing dependencies"),await W3(u(),[u()==="npm"?"install":"add",...Z.dependencies],{}),await W3(u(),[u()==="npm"?"install":"add","-D",...Z.devDependencies],{}),g.succeed("Dependencies installed"),b.succeed(I.green("Files are extracted successfully!")),console.log(`
|
|
19
|
+
Your project is ready, you can now start adding components.`),process.exit(1)}}var K3={name:"untitledui",version:"0.1.3",main:"dist/index.mjs",description:"Untitled UI CLI",type:"module",publishConfig:{access:"public"},scripts:{test:'echo "Error: no test specified" && exit 1',dev:"bun build --entrypoints ./index.ts --entry-naming=[name].mjs --outdir=dist --target=node --minify --packages=external --env=inline --define=process.env.API_URL=http://localhost:3000/api --watch",build:"bun build --entrypoints ./index.ts --entry-naming=[name].mjs --outdir=dist --target=node --minify --packages=external --env=inline --define=process.env.API_URL=https://untitledui-docs.vercel.app/api","publish:npm":"bun run build && npm publish --access public",start:"node dist/index.js"},repository:{type:"git",url:"https://github.com/a-peak-works/untitledui-tailwind.git"},bugs:{url:"https://github.com/a-peak-works/untitledui-tailwind/issues"},homepage:"https://github.com/a-peak-works/untitledui-tailwind#readme",keywords:["untitled-ui","cli","tailwindcss","nextjs"],files:["dist","templates"],author:"",license:"MIT",bin:{untitledui:"dist/index.mjs"},exports:"./dist/index.mjs",dependencies:{"async-retry":"^1.3.3",chalk:"^5.4.1",commander:"^13.1.0",execa:"^7.0.0","fast-glob":"^3.3.3","node-fetch":"^3.3.2",ora:"^8.2.0",prettier:"^3.5.3",prompts:"^2.4.2",tar:"^7.4.3","ts-morph":"^25.0.1","tsconfig-paths":"^4.2.0","update-check":"^1.5.4"},devDependencies:{"@types/async-retry":"^1.4.9","@types/prompts":"^2.4.9","@types/tar":"^6.1.13","type-fest":"^4.37.0",typescript:"^5.8.2"}};process.on("SIGINT",()=>process.exit(0));process.on("SIGTERM",()=>process.exit(0));async function q0(){let z=new $0().name(K3.name).version(K3.version);z.addCommand(u3).addCommand(_3),z.parse()}q0();
|
package/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "untitledui",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"main": "dist/index.
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"main": "dist/index.mjs",
|
|
5
5
|
"description": "Untitled UI CLI",
|
|
6
|
+
"type": "module",
|
|
6
7
|
"publishConfig": {
|
|
7
8
|
"access": "public"
|
|
8
9
|
},
|
|
9
|
-
"type": "module",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
12
|
-
"dev": "bun build --entrypoints ./index.ts --outdir=dist --target=node --minify --packages=external --watch",
|
|
13
|
-
"build": "bun build --entrypoints ./index.ts --outdir=dist --target=node --minify --packages=external",
|
|
12
|
+
"dev": "bun build --entrypoints ./index.ts --entry-naming=[name].mjs --outdir=dist --target=node --minify --packages=external --env=inline --define=process.env.API_URL=http://localhost:3000/api --watch",
|
|
13
|
+
"build": "bun build --entrypoints ./index.ts --entry-naming=[name].mjs --outdir=dist --target=node --minify --packages=external --env=inline --define=process.env.API_URL=https://untitledui-docs.vercel.app/api",
|
|
14
14
|
"publish:npm": "bun run build && npm publish --access public",
|
|
15
15
|
"start": "node dist/index.js"
|
|
16
16
|
},
|
|
@@ -35,15 +35,16 @@
|
|
|
35
35
|
"author": "",
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"bin": {
|
|
38
|
-
"untitledui": "dist/index.
|
|
38
|
+
"untitledui": "dist/index.mjs"
|
|
39
39
|
},
|
|
40
|
-
"exports": "./dist/index.
|
|
40
|
+
"exports": "./dist/index.mjs",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"async-retry": "^1.3.3",
|
|
43
43
|
"chalk": "^5.4.1",
|
|
44
44
|
"commander": "^13.1.0",
|
|
45
|
-
"execa": "^
|
|
45
|
+
"execa": "^7.0.0",
|
|
46
46
|
"fast-glob": "^3.3.3",
|
|
47
|
+
"node-fetch": "^3.3.2",
|
|
47
48
|
"ora": "^8.2.0",
|
|
48
49
|
"prettier": "^3.5.3",
|
|
49
50
|
"prompts": "^2.4.2",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"devDependencies": {
|
|
56
57
|
"@types/async-retry": "^1.4.9",
|
|
57
58
|
"@types/prompts": "^2.4.9",
|
|
59
|
+
"@types/tar": "^6.1.13",
|
|
58
60
|
"type-fest": "^4.37.0",
|
|
59
61
|
"typescript": "^5.8.2"
|
|
60
62
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "default-template",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
-
"repository": "https://github.com/a-peak-works/starter-kits.git",
|
|
5
4
|
"author": "a-peak-works",
|
|
6
5
|
"license": "MIT",
|
|
7
6
|
"scripts": {
|
|
@@ -12,46 +11,46 @@
|
|
|
12
11
|
},
|
|
13
12
|
"dependencies": {
|
|
14
13
|
"@designbycode/tailwindcss-mask-image": "^2.0.3",
|
|
15
|
-
"@react-aria/utils": "^3.
|
|
14
|
+
"@react-aria/utils": "^3.28.1",
|
|
16
15
|
"@react-stately/utils": "^3.10.5",
|
|
17
|
-
"@tailwindcss/postcss": "^4.0.
|
|
16
|
+
"@tailwindcss/postcss": "^4.0.14",
|
|
18
17
|
"@tailwindcss/typography": "^0.5.16",
|
|
19
18
|
"@untitledui/icons": "^0.0.15",
|
|
20
19
|
"input-otp": "^1.4.2",
|
|
21
20
|
"motion": "^11.18.2",
|
|
22
|
-
"next": "15.
|
|
23
|
-
"next-themes": "^0.4.
|
|
21
|
+
"next": "^15.2.3",
|
|
22
|
+
"next-themes": "^0.4.6",
|
|
24
23
|
"qr-code-styling": "^1.9.1",
|
|
25
24
|
"react": "19.0.0",
|
|
26
|
-
"react-aria": "^3.
|
|
27
|
-
"react-aria-components": "^1.
|
|
25
|
+
"react-aria": "^3.38.1",
|
|
26
|
+
"react-aria-components": "^1.7.1",
|
|
28
27
|
"react-dom": "19.0.0",
|
|
29
28
|
"react-hotkeys-hook": "^4.6.1",
|
|
30
29
|
"sonner": "^1.7.4",
|
|
31
|
-
"tailwind-merge": "^3.0.
|
|
32
|
-
"tailwindcss": "^4.0.
|
|
30
|
+
"tailwind-merge": "^3.0.2",
|
|
31
|
+
"tailwindcss": "^4.0.14",
|
|
33
32
|
"tailwindcss-animate": "^1.0.7",
|
|
34
|
-
"tailwindcss-react-aria-components": "^
|
|
33
|
+
"tailwindcss-react-aria-components": "^2.0.0"
|
|
35
34
|
},
|
|
36
35
|
"devDependencies": {
|
|
37
36
|
"@eslint/eslintrc": "^3.3.0",
|
|
38
37
|
"@trivago/prettier-plugin-sort-imports": "^5.2.2",
|
|
39
|
-
"@types/node": "^
|
|
40
|
-
"@types/react": "^
|
|
41
|
-
"@types/react-dom": "^
|
|
42
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
43
|
-
"@typescript-eslint/parser": "^8.
|
|
44
|
-
"eslint": "^9.
|
|
38
|
+
"@types/node": "^22.13.10",
|
|
39
|
+
"@types/react": "^19.0.12",
|
|
40
|
+
"@types/react-dom": "^19.0.4",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
|
42
|
+
"@typescript-eslint/parser": "^8.27.0",
|
|
43
|
+
"eslint": "^9.22.0",
|
|
45
44
|
"eslint-config-next": "15.0.3",
|
|
46
|
-
"eslint-config-prettier": "^10.
|
|
45
|
+
"eslint-config-prettier": "^10.1.1",
|
|
47
46
|
"eslint-plugin-import": "^2.31.0",
|
|
48
47
|
"eslint-plugin-prettier": "^5.2.3",
|
|
49
48
|
"eslint-plugin-simple-import-sort": "^12.1.1",
|
|
50
49
|
"eslint-plugin-unused-imports": "^4.1.4",
|
|
51
|
-
"postcss": "^8.5.
|
|
50
|
+
"postcss": "^8.5.3",
|
|
52
51
|
"postcss-loader": "^8.1.1",
|
|
53
|
-
"prettier": "^3.5.
|
|
52
|
+
"prettier": "^3.5.3",
|
|
54
53
|
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
55
|
-
"typescript": "^5.
|
|
54
|
+
"typescript": "^5.8.2"
|
|
56
55
|
}
|
|
57
56
|
}
|
|
@@ -10,8 +10,8 @@ import Link from "next/link";
|
|
|
10
10
|
import { toast } from "sonner";
|
|
11
11
|
import { BadgeGroup } from "@/components/shared/badges/badge-groups";
|
|
12
12
|
import Button from "@/components/shared/buttons/button";
|
|
13
|
-
import { Form } from "@/components/shared/
|
|
14
|
-
import { Input } from "@/components/shared/
|
|
13
|
+
import { Form } from "@/components/shared/form/form";
|
|
14
|
+
import { Input } from "@/components/shared/input";
|
|
15
15
|
import { cx } from "@/components/utils";
|
|
16
16
|
import Spiral from "../../public/marketing/spirals.webp";
|
|
17
17
|
|
|
@@ -19,7 +19,6 @@ const Header = dynamic(() => import("@/components/marketing/header-navigation/co
|
|
|
19
19
|
|
|
20
20
|
export const HomeScreen = () => {
|
|
21
21
|
const { systemTheme } = useTheme();
|
|
22
|
-
console.log(systemTheme);
|
|
23
22
|
|
|
24
23
|
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
|
|
25
24
|
event.preventDefault();
|
|
@@ -1,22 +1,14 @@
|
|
|
1
1
|
import type { Metadata, Viewport } from "next";
|
|
2
|
-
import
|
|
2
|
+
import { Inter } from "next/font/google";
|
|
3
3
|
import { cx } from "@/components/utils";
|
|
4
4
|
import { Theme } from "@/providers/theme";
|
|
5
5
|
import "@/styles/globals.css";
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
variable: "--font-geist-sans",
|
|
10
|
-
weight: "100 900",
|
|
7
|
+
const inter = Inter({
|
|
8
|
+
subsets: ["latin"],
|
|
11
9
|
display: "swap",
|
|
10
|
+
variable: "--font-inter",
|
|
12
11
|
});
|
|
13
|
-
const geistMono = localFont({
|
|
14
|
-
src: "../fonts/GeistMonoVF.woff",
|
|
15
|
-
variable: "--font-geist-mono",
|
|
16
|
-
weight: "100 900",
|
|
17
|
-
display: "swap",
|
|
18
|
-
});
|
|
19
|
-
|
|
20
12
|
export const metadata: Metadata = {
|
|
21
13
|
title: "Create Next App",
|
|
22
14
|
description: "Generated by create next app",
|
|
@@ -34,7 +26,7 @@ export default function RootLayout({
|
|
|
34
26
|
}>) {
|
|
35
27
|
return (
|
|
36
28
|
<html lang="en" suppressHydrationWarning>
|
|
37
|
-
<body className={cx(
|
|
29
|
+
<body className={cx(inter.variable, "light-mode bg-primary antialiased dark:dark-mode")}>
|
|
38
30
|
<Theme>{children}</Theme>
|
|
39
31
|
</body>
|
|
40
32
|
</html>
|
package/templates/default/src/components/marketing/header-navigation/dropdown-header-navigation.tsx
CHANGED
|
@@ -42,7 +42,7 @@ export const DropdownMenuSimple = () => {
|
|
|
42
42
|
<nav className="overflow-hidden rounded-2xl bg-primary py-2 shadow-xs ring-1 ring-border-secondary md:p-2 md:shadow-lg">
|
|
43
43
|
<ul className="flex flex-col gap-0.5">
|
|
44
44
|
{items.map(({ title, subtitle, href, Icon }) => (
|
|
45
|
-
<li>
|
|
45
|
+
<li key={href}>
|
|
46
46
|
<NavMenuItemLink icon={Icon} title={title} subtitle={subtitle} href={href} />
|
|
47
47
|
</li>
|
|
48
48
|
))}
|
|
@@ -11,7 +11,7 @@ import { isReactComponent } from "@/components/utils/isReactComponent";
|
|
|
11
11
|
export const styles = sortCx({
|
|
12
12
|
common: {
|
|
13
13
|
root: [
|
|
14
|
-
"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-brand transition duration-100 ease-linear before:absolute focus:outline-2 focus:outline-offset-2",
|
|
14
|
+
"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-brand transition duration-100 ease-linear before:absolute focus-visible:outline-2 focus-visible:outline-offset-2",
|
|
15
15
|
// Disabled styles
|
|
16
16
|
"disabled:cursor-not-allowed disabled:text-fg-disabled",
|
|
17
17
|
// Icon styles
|
|
@@ -209,6 +209,7 @@ const Button = ({
|
|
|
209
209
|
} else {
|
|
210
210
|
props = {
|
|
211
211
|
type: rest.type || "button",
|
|
212
|
+
isPending: loading,
|
|
212
213
|
isDisabled: disabled,
|
|
213
214
|
onPress: (event: PressEvent) => {
|
|
214
215
|
// @ts-expect-error FIX ME
|
|
@@ -13,10 +13,13 @@ import { Button as AriaButton, Header, Menu, MenuItem, MenuSection, MenuTrigger,
|
|
|
13
13
|
import { cx } from "@/components/utils";
|
|
14
14
|
|
|
15
15
|
interface DropdownItemProps extends MenuItemProps {
|
|
16
|
+
/** The label of the item to be displayed. */
|
|
16
17
|
label?: string;
|
|
17
|
-
|
|
18
|
+
/** An addon to be displayed on the right side of the item. */
|
|
18
19
|
addon?: string;
|
|
20
|
+
/** If true, the item will not have any styles. */
|
|
19
21
|
unstyled?: boolean;
|
|
22
|
+
/** An icon to be displayed on the left side of the item. */
|
|
20
23
|
icon?: FC<{ className?: string }>;
|
|
21
24
|
}
|
|
22
25
|
|
|
@@ -28,36 +31,40 @@ const DropdownItem = ({ label, children, addon, icon: Icon, unstyled, ...props }
|
|
|
28
31
|
return (
|
|
29
32
|
<MenuItem
|
|
30
33
|
{...props}
|
|
31
|
-
|
|
32
|
-
textValue={label}
|
|
33
|
-
className={(values) =>
|
|
34
|
+
className={(state) =>
|
|
34
35
|
cx(
|
|
35
36
|
"group block cursor-pointer px-1.5 py-px outline-hidden",
|
|
36
|
-
|
|
37
|
-
typeof props.className === "function" ? props.className(
|
|
37
|
+
state.isDisabled && "cursor-not-allowed",
|
|
38
|
+
typeof props.className === "function" ? props.className(state) : props.className,
|
|
38
39
|
)
|
|
39
40
|
}
|
|
40
41
|
>
|
|
41
|
-
{(
|
|
42
|
+
{(state) => (
|
|
42
43
|
<div
|
|
43
44
|
className={cx(
|
|
44
45
|
"relative flex items-center rounded-md px-2.5 py-2 outline-focus-ring transition duration-100 ease-linear",
|
|
45
|
-
!isDisabled && "group-hover:bg-primary_hover",
|
|
46
|
-
isFocused && "bg-primary_hover",
|
|
47
|
-
isFocusVisible && "outline-2 -outline-offset-2",
|
|
46
|
+
!state.isDisabled && "group-hover:bg-primary_hover",
|
|
47
|
+
state.isFocused && "bg-primary_hover",
|
|
48
|
+
state.isFocusVisible && "outline-2 -outline-offset-2",
|
|
48
49
|
)}
|
|
49
50
|
>
|
|
50
|
-
{Icon && <Icon className={cx("mr-2 size-4 shrink-0", isDisabled ? "text-fg-disabled" : "text-fg-quaternary")} aria-hidden="true" />}
|
|
51
|
+
{Icon && <Icon className={cx("mr-2 size-4 shrink-0", state.isDisabled ? "text-fg-disabled" : "text-fg-quaternary")} aria-hidden="true" />}
|
|
51
52
|
|
|
52
|
-
<span
|
|
53
|
-
{
|
|
53
|
+
<span
|
|
54
|
+
className={cx(
|
|
55
|
+
"grow truncate tt-sm-semi",
|
|
56
|
+
state.isDisabled ? "text-disabled" : "text-secondary",
|
|
57
|
+
state.isFocused && "text-secondary_hover",
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{label || (typeof children === "function" ? children(state) : children)}
|
|
54
61
|
</span>
|
|
55
62
|
|
|
56
63
|
{addon && (
|
|
57
64
|
<span
|
|
58
65
|
className={cx(
|
|
59
66
|
"ml-3 shrink-0 rounded px-1 py-px tt-xs-md ring-1 ring-border-secondary ring-inset",
|
|
60
|
-
isDisabled ? "text-disabled" : "text-quaternary",
|
|
67
|
+
state.isDisabled ? "text-disabled" : "text-quaternary",
|
|
61
68
|
)}
|
|
62
69
|
>
|
|
63
70
|
{addon}
|
|
@@ -74,10 +81,12 @@ interface DropdownMenuProps<T extends object> extends AriaMenuProps<T> {}
|
|
|
74
81
|
const DropdownMenu = <T extends object>(props: DropdownMenuProps<T>) => {
|
|
75
82
|
return (
|
|
76
83
|
<Menu
|
|
77
|
-
selectionMode="single"
|
|
78
84
|
disallowEmptySelection
|
|
85
|
+
selectionMode="single"
|
|
79
86
|
{...props}
|
|
80
|
-
className={
|
|
87
|
+
className={(state) =>
|
|
88
|
+
cx("h-min overflow-y-auto py-1 outline-hidden select-none", typeof props.className === "function" ? props.className(state) : props.className)
|
|
89
|
+
}
|
|
81
90
|
/>
|
|
82
91
|
);
|
|
83
92
|
};
|
|
@@ -93,9 +102,9 @@ const DropdownPopover = (props: DropdownPopoverProps) => {
|
|
|
93
102
|
cx(
|
|
94
103
|
"w-[248px] rounded-lg bg-primary shadow-lg ring-1 ring-border-secondary_alt will-change-transform",
|
|
95
104
|
state.isEntering &&
|
|
96
|
-
"ease-out animate-in
|
|
105
|
+
"duration-150 ease-out animate-in fade-in zoom-in-95 placement-right:origin-left placement-right:slide-in-from-left-0.5 placement-top:origin-bottom placement-top:slide-in-from-bottom-0.5 placement-bottom:origin-top placement-bottom:slide-in-from-top-0.5",
|
|
97
106
|
state.isExiting &&
|
|
98
|
-
"
|
|
107
|
+
"duration-100 ease-in animate-out fade-out zoom-out-95 placement-right:origin-left placement-right:slide-out-to-left-0.5 placement-top:origin-bottom placement-top:slide-out-to-bottom-0.5 placement-bottom:origin-top placement-bottom:slide-out-to-top-0.5",
|
|
99
108
|
typeof props.className === "function" ? props.className(state) : props.className,
|
|
100
109
|
)
|
|
101
110
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
import { createContext, useContext, useId } from "react";
|
|
3
|
+
import { Form as AriaForm } from "react-aria-components";
|
|
4
|
+
import type { Control, FieldPath, FieldValues, UseControllerReturn, UseFormReturn } from "react-hook-form";
|
|
5
|
+
import { FormProvider, useController, useFormContext } from "react-hook-form";
|
|
6
|
+
|
|
7
|
+
interface FormProps<TFieldValues extends FieldValues = FieldValues> extends ComponentPropsWithoutRef<typeof AriaForm> {
|
|
8
|
+
form: UseFormReturn<TFieldValues>;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface FormFieldProps<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> {
|
|
13
|
+
name: TName;
|
|
14
|
+
control: Control<TFieldValues>;
|
|
15
|
+
children: ReactNode | ((control: UseControllerReturn<TFieldValues, TName>) => ReactNode);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface FormFieldContextValues<TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>> {
|
|
19
|
+
id: string;
|
|
20
|
+
name: TName;
|
|
21
|
+
control?: UseControllerReturn<TFieldValues, TName>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FormFieldContext = createContext<FormFieldContextValues>({} as FormFieldContextValues);
|
|
25
|
+
|
|
26
|
+
export const useFormFieldContext = () => {
|
|
27
|
+
const context = useContext(FormFieldContext);
|
|
28
|
+
const { getFieldState, formState } = useFormContext();
|
|
29
|
+
const fieldState = getFieldState(context.name, formState);
|
|
30
|
+
|
|
31
|
+
if (!context) {
|
|
32
|
+
throw new Error("The 'useFormContext' hook must be used within a '<FormField />'");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return { ...context, ...fieldState };
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const HookForm = <TFieldValues extends FieldValues = FieldValues>({ form, ...props }: FormProps<TFieldValues>) => {
|
|
39
|
+
return (
|
|
40
|
+
<FormProvider {...form}>
|
|
41
|
+
<AriaForm {...props} />
|
|
42
|
+
</FormProvider>
|
|
43
|
+
);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
HookForm.displayName = "HookForm";
|
|
47
|
+
|
|
48
|
+
export const FormField = <TFieldValues extends FieldValues = FieldValues, TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>>({
|
|
49
|
+
children,
|
|
50
|
+
...props
|
|
51
|
+
}: FormFieldProps<TFieldValues, TName>) => {
|
|
52
|
+
const id = "form-item-" + useId();
|
|
53
|
+
const control = useController(props);
|
|
54
|
+
const withValidationBehavior = {
|
|
55
|
+
...control,
|
|
56
|
+
field: {
|
|
57
|
+
...control.field,
|
|
58
|
+
validationBehavior: "aria",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<FormFieldContext.Provider
|
|
64
|
+
value={{
|
|
65
|
+
id,
|
|
66
|
+
name: props.name,
|
|
67
|
+
control: control as UseControllerReturn<FieldValues, TName>,
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{children && (typeof children === "function" ? children(withValidationBehavior) : children)}
|
|
71
|
+
</FormFieldContext.Provider>
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
FormField.displayName = "FormField";
|
|
@@ -4,8 +4,8 @@ import type { ComponentType, HTMLAttributes, ReactNode, Ref } from "react";
|
|
|
4
4
|
import { HelpCircle, InfoCircle } from "@untitledui/icons";
|
|
5
5
|
import type { TextFieldProps as AriaTextFieldProps } from "react-aria-components";
|
|
6
6
|
import { Input as AriaInput, TextField as AriaTextField, Group } from "react-aria-components";
|
|
7
|
-
import HintText from "@/components/shared/
|
|
8
|
-
import Label from "@/components/shared/
|
|
7
|
+
import HintText from "@/components/shared/input/hint-text";
|
|
8
|
+
import Label from "@/components/shared/input/label";
|
|
9
9
|
import { Tooltip, TooltipTrigger } from "@/components/shared/tooltips/tooltips";
|
|
10
10
|
import { cx, sortCx } from "@/components/utils";
|
|
11
11
|
|
|
@@ -130,7 +130,7 @@ export const InputBase = ({ size = "sm", placeholder, icon: Icon, isDisabled, is
|
|
|
130
130
|
)}
|
|
131
131
|
aria-hidden="true"
|
|
132
132
|
>
|
|
133
|
-
{shortcut
|
|
133
|
+
{typeof shortcut === "string" ? shortcut : "⌘K"}
|
|
134
134
|
</span>
|
|
135
135
|
</div>
|
|
136
136
|
)}
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
import React, { useState } from "react";
|
|
4
4
|
import { TextField } from "react-aria-components";
|
|
5
5
|
import { AmexIcon, DiscoverIcon, MastercardIcon, UnionPayIcon, VisaIcon } from "@/components/foundations/payment-icons";
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import Label from "@/components/shared/
|
|
6
|
+
import type { InputBaseProps } from "@/components/shared/input";
|
|
7
|
+
import { InputBase } from "@/components/shared/input";
|
|
8
|
+
import HintText from "@/components/shared/input/hint-text";
|
|
9
|
+
import Label from "@/components/shared/input/label";
|
|
10
10
|
import { cx } from "@/components/utils";
|
|
11
11
|
|
|
12
12
|
const cardTypes = [
|
package/templates/default/src/components/shared/{inputs/input → input}/input-with-button.tsx
RENAMED
|
@@ -6,10 +6,10 @@ import { Copy01 } from "@untitledui/icons";
|
|
|
6
6
|
import { TextField } from "react-aria-components";
|
|
7
7
|
import type { CommonProps } from "@/components/shared/buttons/button";
|
|
8
8
|
import Button from "@/components/shared/buttons/button";
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import Label from "@/components/shared/
|
|
9
|
+
import type { InputBaseProps } from "@/components/shared/input";
|
|
10
|
+
import { InputBase } from "@/components/shared/input";
|
|
11
|
+
import HintText from "@/components/shared/input/hint-text";
|
|
12
|
+
import Label from "@/components/shared/input/label";
|
|
13
13
|
import { cx } from "@/components/utils";
|
|
14
14
|
|
|
15
15
|
interface InputWithButtonProps extends Omit<InputBaseProps, "icon"> {
|
package/templates/default/src/components/shared/{inputs/input → input}/input-with-dropdown.tsx
RENAMED
|
@@ -4,10 +4,10 @@ import React from "react";
|
|
|
4
4
|
import { ChevronDown } from "@untitledui/icons";
|
|
5
5
|
import type { Key } from "react-aria-components";
|
|
6
6
|
import { TextField } from "react-aria-components";
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import Label from "@/components/shared/
|
|
7
|
+
import type { InputBaseProps } from "@/components/shared/input";
|
|
8
|
+
import { InputBase } from "@/components/shared/input";
|
|
9
|
+
import HintText from "@/components/shared/input/hint-text";
|
|
10
|
+
import Label from "@/components/shared/input/label";
|
|
11
11
|
import { cx, sortCx } from "@/components/utils";
|
|
12
12
|
|
|
13
13
|
interface SelectorComponentProps {
|
package/templates/default/src/components/shared/{inputs/input → input}/input-with-prefix.tsx
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import type { HTMLAttributes } from "react";
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import Label from "@/components/shared/
|
|
4
|
+
import type { InputBaseProps } from "@/components/shared/input";
|
|
5
|
+
import { InputBase, TextField } from "@/components/shared/input";
|
|
6
|
+
import HintText from "@/components/shared/input/hint-text";
|
|
7
|
+
import Label from "@/components/shared/input/label";
|
|
8
8
|
import { cx } from "@/components/utils";
|
|
9
9
|
|
|
10
10
|
interface InputPrefixProps extends HTMLAttributes<HTMLDivElement> {
|
|
@@ -61,7 +61,7 @@ export const ProgressBarTextTopFloating = ({ value, min = 0, max = 100, valueFor
|
|
|
61
61
|
<ProgressBar min={min} max={max} value={value} />
|
|
62
62
|
<div
|
|
63
63
|
style={{ left: `${percentage}%` }}
|
|
64
|
-
className="absolute -top-2 -translate-x-1/2 -translate-y-full rounded-lg bg-primary_alt px-3 py-2 shadow-lg ring-
|
|
64
|
+
className="absolute -top-2 -translate-x-1/2 -translate-y-full rounded-lg bg-primary_alt px-3 py-2 ring-1 shadow-lg ring-border-secondary_alt"
|
|
65
65
|
>
|
|
66
66
|
<div className="tt-xs-semi text-secondary tabular-nums">{valueFormatter ? valueFormatter(value, percentage) : `${percentage}%`}</div>
|
|
67
67
|
</div>
|
|
@@ -77,7 +77,7 @@ export const ProgressBarTextBottomFloating = ({ value, min = 0, max = 100, value
|
|
|
77
77
|
<ProgressBar min={min} max={max} value={value} />
|
|
78
78
|
<div
|
|
79
79
|
style={{ left: `${percentage}%` }}
|
|
80
|
-
className="absolute -bottom-2 -translate-x-1/2 translate-y-full rounded-lg bg-primary_alt px-3 py-2 shadow-lg ring-
|
|
80
|
+
className="absolute -bottom-2 -translate-x-1/2 translate-y-full rounded-lg bg-primary_alt px-3 py-2 ring-1 shadow-lg ring-border-secondary_alt"
|
|
81
81
|
>
|
|
82
82
|
<div className="tt-xs-semi text-secondary">{valueFormatter ? valueFormatter(value, percentage) : `${percentage}%`}</div>
|
|
83
83
|
</div>
|
|
@@ -15,21 +15,21 @@ import {
|
|
|
15
15
|
import { useHotkeys } from "react-hotkeys-hook";
|
|
16
16
|
import { cx } from "@/components/utils";
|
|
17
17
|
import { useResizeObserver } from "@/hooks/use-resize-observer";
|
|
18
|
-
import HintText from "../
|
|
19
|
-
import Label from "../
|
|
20
|
-
import { type CommonProps, SelectContext, type SelectValueType, sizes } from "./input-dropdown";
|
|
18
|
+
import HintText from "../input/hint-text";
|
|
19
|
+
import Label from "../input/label";
|
|
21
20
|
import { ComboBoxTagsValue } from "./multi-select";
|
|
22
21
|
import { Popover } from "./popover";
|
|
22
|
+
import { type CommonProps, SelectContext, type SelectItemType, sizes } from "./select";
|
|
23
23
|
|
|
24
24
|
type ComboBoxTypes = "search" | "tags";
|
|
25
25
|
|
|
26
|
-
interface ComboBoxProps extends Omit<AriaComboBoxProps<
|
|
26
|
+
interface ComboBoxProps extends Omit<AriaComboBoxProps<SelectItemType>, "children" | "items">, RefAttributes<HTMLDivElement>, CommonProps {
|
|
27
27
|
shortcut?: boolean;
|
|
28
28
|
type?: ComboBoxTypes;
|
|
29
|
-
items?:
|
|
29
|
+
items?: SelectItemType[];
|
|
30
30
|
popoverClassName?: string;
|
|
31
31
|
shortcutClassName?: string;
|
|
32
|
-
children: AriaListBoxProps<
|
|
32
|
+
children: AriaListBoxProps<SelectItemType>["children"];
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
interface ComboBoxValueProps extends RefAttributes<HTMLDivElement> {
|
|
@@ -109,8 +109,6 @@ export const ComboBox = ({ type = "search", placeholder = "Select", shortcut = t
|
|
|
109
109
|
const placeholderRef = useRef<HTMLDivElement>(null);
|
|
110
110
|
const [popoverWidth, setPopoverWidth] = useState("");
|
|
111
111
|
|
|
112
|
-
const itemsWithId = items?.map((item) => ({ ...item, id: item.value }));
|
|
113
|
-
|
|
114
112
|
// Resize observer for popover width
|
|
115
113
|
const onResize = useCallback(() => {
|
|
116
114
|
if (!placeholderRef.current) return;
|
|
@@ -149,7 +147,9 @@ export const ComboBox = ({ type = "search", placeholder = "Select", shortcut = t
|
|
|
149
147
|
/>
|
|
150
148
|
|
|
151
149
|
<Popover size={size} triggerRef={placeholderRef} style={{ width: popoverWidth }} className={rest.popoverClassName}>
|
|
152
|
-
<AriaListBox {
|
|
150
|
+
<AriaListBox items={items} className="size-full outline-hidden">
|
|
151
|
+
{children}
|
|
152
|
+
</AriaListBox>
|
|
153
153
|
</Popover>
|
|
154
154
|
|
|
155
155
|
{rest.hint && <HintText isInvalid={state.isInvalid}>{rest.hint}</HintText>}
|