Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rtl): add support #2059

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/www/public/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
},
"cssVariables": {
"type": "boolean"
},
"rtl": {
"type": "boolean"
}
},
"required": ["config", "css", "baseColor", "cssVariables"]
Expand Down
11 changes: 10 additions & 1 deletion packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,14 @@ export async function promptForConfig(
value: style.name,
})),
},
{
type: "toggle",
name: "rtl",
message: `Would you like to support ${highlight("RTL")}?`,
initial: defaultConfig?.tailwind?.rtl ?? false,
active: "yes",
inactive: "no",
},
{
type: "select",
name: "tailwindBaseColor",
Expand Down Expand Up @@ -164,8 +172,9 @@ export async function promptForConfig(
$schema: "https://ui.shadcn.com/schema.json",
style: options.style,
tailwind: {
config: options.tailwindConfig,
rtl: options.rtl,
css: options.tailwindCss,
config: options.tailwindConfig,
baseColor: options.tailwindBaseColor,
cssVariables: options.tailwindCssVariables,
},
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/utils/get-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ export const rawConfigSchema = z
rsc: z.coerce.boolean().default(false),
tsx: z.coerce.boolean().default(true),
tailwind: z.object({
config: z.string(),
css: z.string(),
config: z.string(),
baseColor: z.string(),
rtl: z.boolean().default(false),
cssVariables: z.boolean().default(true),
}),
aliases: z.object({
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/utils/transformers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { tmpdir } from "os"
import path from "path"
import { Config } from "@/src/utils/get-config"
import { registryBaseColorSchema } from "@/src/utils/registry/schema"
import { transformRtl } from "@/src/utils/transformers//transform-rtl"
import { transformCssVars } from "@/src/utils/transformers/transform-css-vars"
import { transformImport } from "@/src/utils/transformers/transform-import"
import { transformJsx } from "@/src/utils/transformers/transform-jsx"
Expand All @@ -27,6 +28,7 @@ const transformers: Transformer[] = [
transformImport,
transformRsc,
transformCssVars,
transformRtl,
]

const project = new Project({
Expand Down
76 changes: 76 additions & 0 deletions packages/cli/src/utils/transformers/transform-rtl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { Transformer } from "@/src/utils/transformers"
import { SyntaxKind } from "ts-morph"

import { splitClassName } from "./transform-css-vars"

// Transformer to update string literals for RTL support
export const transformRtl: Transformer = async ({ sourceFile, config }) => {
// If RTL support is not configured, do not transform the source file
if (!config.tailwind?.rtl) {
return sourceFile
}

// Iterate over all StringLiteral nodes in the file
sourceFile.getDescendantsOfKind(SyntaxKind.StringLiteral).forEach((node) => {
const textValue = node.getText()
if (textValue) {
// Apply direction mappings to each class name in the string
const updatedValue = applyRtlClassMappings(textValue)
// Replace the current node text with the updated classes text
node.replaceWithText(`"${updatedValue.trim()}"`)
}
})

return sourceFile
}

// Define mappings of class prefixes for right-to-left (RTL) support
const PREFIXES_MAP = new Map([
["pl-", "ps-"],
["ml-", "ms-"],
["pr-", "pe-"],
["mr-", "me-"],
["right-", "end-"],
["left-", "start-"],
["text-right", "text-end"],
["border-l-", "border-s-"],
["border-r-", "border-e-"],
["text-left", "text-start"],
])

// Define additional RTL specific class replacements
const ADDITIONAL_CLASSES = new Map([
["space-x-", "rtl:space-x-reverse"],
["divide-x-", "rtl:divide-x-reverse"],
])

// Apply RTL class mappings to a space-separated list of class names
export function applyRtlClassMappings(input: string): string {
// Split the input into individual class names
const classNames = input.split(" ")
const rtlSupported = new Set<string>()

for (let className of classNames) {
const [, value] = splitClassName(className)
// Find and apply prefix replacement, if exists
const rtlPrefix = Array.from(PREFIXES_MAP).find(([prefix]) => {
return value?.startsWith(prefix)
})

if (!rtlPrefix) {
rtlSupported.add(className)
continue
}

rtlSupported.add(className.replace(rtlPrefix[0], rtlPrefix[1]))
}

for (const [prefix, className] of Array.from(ADDITIONAL_CLASSES)) {
if (input.indexOf(prefix) !== -1) {
rtlSupported.add(className)
}
}

// Reassemble the class list and trim any excess whitespace
return Array.from(rtlSupported).join(" ").trim()
}
1 change: 1 addition & 0 deletions templates/next-template/components.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"tailwind": {
"rtl": true,
"config": "tailwind.config.js",
"css": "app/globals.css",
"baseColor": "slate",
Expand Down