mirror of
https://github.com/shadcn-ui/ui.git
synced 2026-06-11 09:51:40 +00:00
feat: add --pointer option (#10488)
This commit is contained in:
@@ -14,7 +14,7 @@ import {
|
||||
import { ensureRegistriesInConfig } from "@/src/utils/registries"
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { runInit } from "./init"
|
||||
import { applyInitUrlOptions, initOptionsSchema, runInit } from "./init"
|
||||
|
||||
vi.mock("@/src/preflights/preflight-init", () => ({
|
||||
preFlightInit: vi.fn(),
|
||||
@@ -252,3 +252,32 @@ describe("runInit", () => {
|
||||
expect(postInit).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("init options", () => {
|
||||
it("parses pointer flags", () => {
|
||||
const result = initOptionsSchema.parse({
|
||||
...createInitOptions("/tmp/project"),
|
||||
pointer: true,
|
||||
})
|
||||
|
||||
expect(result.pointer).toBe(true)
|
||||
})
|
||||
|
||||
it("applies pointer to raw init urls", () => {
|
||||
const url = applyInitUrlOptions(
|
||||
new URL("https://ui.shadcn.com/init?preset=a0"),
|
||||
{ pointer: true }
|
||||
)
|
||||
|
||||
expect(url.searchParams.get("pointer")).toBe("true")
|
||||
})
|
||||
|
||||
it("removes pointer from raw init urls when disabled", () => {
|
||||
const url = applyInitUrlOptions(
|
||||
new URL("https://ui.shadcn.com/init?preset=a0&pointer=true"),
|
||||
{ pointer: false }
|
||||
)
|
||||
|
||||
expect(url.searchParams.has("pointer")).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -76,6 +76,7 @@ export const initOptionsSchema = z.object({
|
||||
isNewProject: z.boolean().default(false),
|
||||
cssVariables: z.boolean().default(true),
|
||||
rtl: z.boolean().optional(),
|
||||
pointer: z.boolean().optional(),
|
||||
base: z.enum(["radix", "base"]).optional(),
|
||||
template: z.string().optional(),
|
||||
monorepo: z.boolean().optional(),
|
||||
@@ -94,6 +95,25 @@ export const initOptionsSchema = z.object({
|
||||
iconLibrary: z.string().optional(),
|
||||
})
|
||||
|
||||
export function applyInitUrlOptions(
|
||||
url: URL,
|
||||
options: Pick<z.infer<typeof initOptionsSchema>, "rtl" | "pointer">
|
||||
) {
|
||||
if (options.rtl) {
|
||||
url.searchParams.set("rtl", "true")
|
||||
} else if (options.rtl === false) {
|
||||
url.searchParams.delete("rtl")
|
||||
}
|
||||
|
||||
if (options.pointer) {
|
||||
url.searchParams.set("pointer", "true")
|
||||
} else if (options.pointer === false) {
|
||||
url.searchParams.delete("pointer")
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
export const init = new Command()
|
||||
.name("init")
|
||||
.alias("create")
|
||||
@@ -125,6 +145,8 @@ export const init = new Command()
|
||||
.option("--no-css-variables", "do not use css variables for theming.")
|
||||
.option("--rtl", "enable RTL support.")
|
||||
.option("--no-rtl", "disable RTL support.")
|
||||
.option("--pointer", "enable pointer cursor for buttons.")
|
||||
.option("--no-pointer", "disable pointer cursor for buttons.")
|
||||
.option("--reinstall", "re-install existing UI components.")
|
||||
.option("--no-reinstall", "do not re-install existing UI components.")
|
||||
.action(async (components, opts) => {
|
||||
@@ -369,6 +391,7 @@ export const init = new Command()
|
||||
rtl: options.rtl ?? false,
|
||||
template: options.template,
|
||||
base: options.base!,
|
||||
pointer: options.pointer,
|
||||
})
|
||||
components = [result.url, ...components]
|
||||
presetBase = result.base
|
||||
@@ -379,11 +402,7 @@ export const init = new Command()
|
||||
|
||||
if (isUrl(presetArg)) {
|
||||
const url = new URL(presetArg)
|
||||
if (options.rtl) {
|
||||
url.searchParams.set("rtl", "true")
|
||||
} else if (options.rtl === false) {
|
||||
url.searchParams.delete("rtl")
|
||||
}
|
||||
applyInitUrlOptions(url, options)
|
||||
if (url.pathname === "/init" && presetArg.startsWith(SHADCN_URL)) {
|
||||
url.searchParams.set("track", "1")
|
||||
}
|
||||
@@ -406,7 +425,11 @@ export const init = new Command()
|
||||
base: "radix",
|
||||
rtl: options.rtl ?? false,
|
||||
},
|
||||
{ template: options.template, preset: presetArg }
|
||||
{
|
||||
template: options.template,
|
||||
preset: presetArg,
|
||||
pointer: options.pointer,
|
||||
}
|
||||
)
|
||||
presetBase = undefined
|
||||
} else {
|
||||
@@ -420,7 +443,7 @@ export const init = new Command()
|
||||
base: options.base ?? "radix",
|
||||
rtl: options.rtl ?? preset.rtl,
|
||||
},
|
||||
{ template: options.template }
|
||||
{ template: options.template, pointer: options.pointer }
|
||||
)
|
||||
presetBase = undefined
|
||||
}
|
||||
@@ -459,7 +482,7 @@ export const init = new Command()
|
||||
base: resolvedBase,
|
||||
rtl: options.rtl ?? false,
|
||||
},
|
||||
{ template: options.template }
|
||||
{ template: options.template, pointer: options.pointer }
|
||||
)
|
||||
components = [initUrl, ...components]
|
||||
}
|
||||
|
||||
@@ -31,9 +31,14 @@ describe("createPresetUrl", () => {
|
||||
})
|
||||
|
||||
it("should append search params when provided", () => {
|
||||
const url = resolveCreateUrl({ rtl: true, template: "next" })
|
||||
const url = resolveCreateUrl({
|
||||
rtl: true,
|
||||
pointer: true,
|
||||
template: "next",
|
||||
})
|
||||
const parsed = new URL(url)
|
||||
expect(parsed.searchParams.get("rtl")).toBe("true")
|
||||
expect(parsed.searchParams.get("pointer")).toBe("true")
|
||||
expect(parsed.searchParams.get("template")).toBe("next")
|
||||
})
|
||||
})
|
||||
@@ -117,4 +122,23 @@ describe("buildInitUrl", () => {
|
||||
const parsed = new URL(url)
|
||||
expect(parsed.searchParams.has("preset")).toBe(false)
|
||||
})
|
||||
|
||||
it("should include pointer when enabled", () => {
|
||||
const url = resolveInitUrl(mockPreset, { pointer: true })
|
||||
const parsed = new URL(url)
|
||||
expect(parsed.searchParams.get("pointer")).toBe("true")
|
||||
})
|
||||
|
||||
it("should not include pointer when disabled", () => {
|
||||
const url = resolveInitUrl(mockPreset, { pointer: false })
|
||||
const parsed = new URL(url)
|
||||
expect(parsed.searchParams.has("pointer")).toBe(false)
|
||||
})
|
||||
|
||||
it("should include pointer with preset codes", () => {
|
||||
const url = resolveInitUrl(mockPreset, { preset: "a0", pointer: true })
|
||||
const parsed = new URL(url)
|
||||
expect(parsed.searchParams.get("preset")).toBe("a0")
|
||||
expect(parsed.searchParams.get("pointer")).toBe("true")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -132,11 +132,12 @@ export function resolveCreateUrl(
|
||||
command: "create" | "init"
|
||||
template: string
|
||||
rtl: boolean
|
||||
pointer: boolean
|
||||
base: string
|
||||
}>
|
||||
) {
|
||||
const url = new URL(`${SHADCN_URL}/create`)
|
||||
const { rtl, ...params } = searchParams ?? {}
|
||||
const { rtl, pointer, ...params } = searchParams ?? {}
|
||||
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
if (value !== undefined) {
|
||||
@@ -149,6 +150,10 @@ export function resolveCreateUrl(
|
||||
url.searchParams.set("rtl", "true")
|
||||
}
|
||||
|
||||
if (pointer) {
|
||||
url.searchParams.set("pointer", "true")
|
||||
}
|
||||
|
||||
return url.toString()
|
||||
}
|
||||
|
||||
@@ -195,7 +200,12 @@ export function resolveInitUrl(
|
||||
menuColor: string
|
||||
radius: string
|
||||
},
|
||||
options?: { template?: string; preset?: string; only?: string }
|
||||
options?: {
|
||||
template?: string
|
||||
preset?: string
|
||||
only?: string
|
||||
pointer?: boolean
|
||||
}
|
||||
) {
|
||||
const params = new URLSearchParams({
|
||||
base: preset.base,
|
||||
@@ -232,6 +242,10 @@ export function resolveInitUrl(
|
||||
params.set("only", options.only)
|
||||
}
|
||||
|
||||
if (options?.pointer) {
|
||||
params.set("pointer", "true")
|
||||
}
|
||||
|
||||
// Signal the server to record this init run.
|
||||
params.set("track", "1")
|
||||
|
||||
@@ -256,6 +270,7 @@ export async function promptForPreset(options: {
|
||||
rtl: boolean
|
||||
base: string
|
||||
template?: string
|
||||
pointer?: boolean
|
||||
}) {
|
||||
const presets = Object.entries(DEFAULT_PRESETS)
|
||||
|
||||
@@ -285,6 +300,7 @@ export async function promptForPreset(options: {
|
||||
const createUrl = resolveCreateUrl({
|
||||
command: "init",
|
||||
rtl: options.rtl,
|
||||
pointer: options.pointer,
|
||||
base: options.base,
|
||||
...(options.template && { template: options.template }),
|
||||
})
|
||||
@@ -308,6 +324,7 @@ export async function promptForPreset(options: {
|
||||
{ ...preset, base: options.base, rtl: options.rtl },
|
||||
{
|
||||
template: options.template,
|
||||
pointer: options.pointer,
|
||||
}
|
||||
),
|
||||
base: options.base,
|
||||
|
||||
Reference in New Issue
Block a user