@ classmatejs/ 反应
一个管理 React 组件类名,变体和样式的工具.
# # 转换这个 # # Form this #
const SomeButton = ({ isLoading, ...props }) => {
const activeClass = isLoading
? "bg-blue-400 text-white"
: "bg-blue-800 text-blue-200";
return (
<button
{...props}
className={`transition-all mt-5 border-1 md:text-lg text-normal ${someConfig.transitionDurationEaseClass} ${activeClass} ${
props.className || ""
}`}
>
{props.children}
</button>
);
};
进
const SomeButton = cm.button`
text-normal
md:text-lg
mt-5
border-1
transition-all
${someConfig.transitionDurationEaseClass}
${({ $isLoading }) => $isLoading && "opacity-90 pointer-events-none"}
`;
特征
- 以名称为重点的类别组件
- 备选案文
- 扩展组件
- 变形组件
- 动态样式
- 类型脚本支持
- 试用了安全部门改革框架
- 类名合并
目录
开始
确定您已经安装 反应 ( > 16.8.0) 输入 项目.
npm i @classmatejs/react
# or
yarn add @classmatejs/react
对于捆绑的应用程序,您也可以将 ClassmateJS 安装为 dev 依赖:
npm i -D @classmatejs/react
# or
yarn add -D @classmatejs/react
对解决的软件包/图书馆或部署使用常规依赖
node_modules 仅安装生产依赖的运行时间.
基础
通过调用创建组件 cm 带有标签名称和模板文字
绳子.
import cm from "@classmatejs/react";
const Container = cm.div`
py-2
px-5
min-h-24
`;
// transforms to: <div className="py-2 px-5 min-h-24" />
使用道具
将道具传递到组件,并在模板字串和其中使用 组件道具验证.
// hey typescript
interface ButtonProps {
$isActive?: boolean;
$isLoading?: boolean;
}
const SomeButton = cm.button<ButtonProps>`
text-lg
mt-5
${({ $isActive }) => ($isActive ? "bg-blue-400 text-white" : "bg-blue-400 text-blue-200")}
${({ $isLoading }) => $isLoading && "opacity-90 pointer-events-none"}
`;
// transforms to <button className="text-lg mt-5 bg-blue-400 text-white opacity-90 pointer-events-none" />
前置输入道具 $
我们在道具上加注 $ 签名这很重要
将动态道具与我们传递给组件的道具区分开来.
这种模式还应避免与保留道具名称相冲突.
创建变量
通过将对象传递到 variants 密钥类似
简历。键应匹配
道具名称和值应该是返回字符串的函数。 你会的
也重新使用函数中的道具.
interface AlertProps {
$severity: "info" | "warning" | "error";
$isActive?: boolean;
}
const Alert = cm.div.variants<AlertProps>({
// optional
base: (p) => `
${p.isActive ? "custom-active" : "custom-inactive"}
p-4
rounded-md
`,
// required
variants: {
$severity: {
warning: "bg-yellow-100 text-yellow-800",
info: (p) =>
`bg-blue-100 text-blue-800 ${p.$isActive ? "shadow-lg" : ""}`,
error: (p) =>
`bg-red-100 text-red-800 ${p.$isActive ? "ring ring-red-500" : ""}`,
},
},
// optional - used if no variant was found
defaultVariant: {
$severity: "info",
},
});
export default () => <Alert $severity="info" $isActive />;
// outputs: <div className="custom-active p-4 rounded-md bg-blue-100 text-blue-800 shadow-lg" />
Typescript:具有第二种类型参数的分离基道具和变体
从上面看,我们也通过了 AlertProps 以各种变体为主
类型。 如果你想从变体中分离出基本道具,你可以通过一个
转到第二个类型 variants 函数,只允许在
这些变体.
interface AlertProps {
$isActive?: boolean;
}
interface AlertVariants {
$severity: "info" | "warning" | "error";
}
const Alert = cm.div.variants<AlertProps, AlertVariants>({
base: `p-4 rounded-md`,
variants: {
// in here there are only the keys from AlertVariants available
$severity: {
// you can use the props from AlertProps here again
warning: "bg-yellow-100 text-yellow-800",
info: (p) =>
`bg-blue-100 text-blue-800 ${p.$isActive ? "shadow-lg" : ""}`,
error: (p) =>
`bg-red-100 text-red-800 ${p.$isActive ? "ring ring-red-500" : ""}`,
},
},
// optional - used if no variant was found
defaultVariant: {
$severity: "info",
},
});
扩展
直接通过传递组件和标签名来扩展组件.
import MyOtherComponent from "./MyOtherComponent"; // () => <button className="text-lg mt-5" />
import cm from "@classmatejs/react";
const Container = cm.extend(MyOtherComponent)`
py-2
px-5
min-h-24
`;
// transforms to: <button className="text-lg mt-5 py-2 px-5 min-h-24" />
变形
变形使已经存在的同龄人成分成为另一个内在元素.
它保持原有的阶级逻辑 $ 道具, 但类型脚本验证道具
对新元素.
import cm from "@classmatejs/react";
const MyButton = cm.button<{ $toggleCta?: boolean }>`
absolute
min-w-300
${({ $toggleCta }) => ($toggleCta ? "bg-red-500" : "bg-blue-500")}
`;
export default () => <MyButton $_as="span" $toggleCta />;
// outputs: <span className="absolute min-w-300 bg-red-500" />
需要重用已转换组件时使用构建器 API, 或者添加更多 带有标记模板的类.
const SpanButton = cm.transform(MyButton).span;
const TextSpanButton = cm.transform(MyButton).span`
text-neutral
tracking-wide
`;
export default () => <TextSpanButton $toggleCta />;
// outputs: <span className="absolute min-w-300 bg-red-500 text-neutral tracking-wide" />
变形也与变相和扩展的同学组件相配合.
const VariantDiv = cm.div.variants<{ $size: "sm" | "md" }>({
base: "absolute min-w-300",
variants: {
$size: {
sm: "w-10 h-10",
md: "w-20 h-20",
},
},
defaultVariants: {
$size: "md",
},
});
const Main = cm.transform(VariantDiv).main`
text-neutral
tracking-wide
`;
export default () => <Main $size="sm" role="main" />;
// outputs: <main className="absolute min-w-300 w-10 h-10 text-neutral tracking-wide" role="main" />
cm.transform 只接受 v1 中的同学组件 。 常规反应
组件不能变换,因为同学不知道在哪里
课程随附.
添加 CSS 样式
您可以在模板文字字符串中使用 CSS 样式 style 函数.
此函数取一个带有 CSS 属性的对象并返回字符串 。 我们可以
使用之前的道具.
// Base:
const StyledButton = cm.button<{ $isDisabled: boolean }>`
text-blue
${(p) =>
p.style({
boxShadow: "0 0 5px rgba(0, 0, 0, 0.1)",
cursor: p.$isDisabled ? "not-allowed" : "pointer",
})}
`;
export default () => <StyledButton $isDisabled />;
// outputs: <button className="text-blue" style="box-shadow: 0 0 5px rgba(0, 0, 0, 0.1); cursor: not-allowed;" />
// Extended:
const BaseButton = cm.button<{ $isActive?: boolean }>`
${(p) =>
p.style({
backgroundColor: p.$isActive ? "green" : "red",
})}
`;
const ExtendedButton = cm.extend(BaseButton)<{ $isLoading?: boolean }>`
${(p) =>
p.style({
opacity: p.$isLoading ? 0.5 : 1,
pointerEvents: p.$isLoading ? "none" : "auto",
})}
`;
export default () => <ExtendedButton $isActive $isLoading />;
// outputs: <button className="bg-red" style="opacity: 0.5; pointer-events: none;" />
使用 React 组件 - useClassmate
当您需要在另一个 React 组件内创建同学组件时
(例如,当配置依赖于只运行时间值时),将
工厂 useClassmate。这可以记住结果,避免产生
每一个渲染器都有全新的组件.
import cm, { useClassmate } from "@classmatejs/react";
const WorkoutDay = ({ status }: { status: "completed" | "pending" }) => {
const StyledDay = useClassmate(
() =>
cm.div.variants({
base: "rounded border p-4 text-sm",
variants: {
$status: {
completed: "border-green-400 bg-green-50",
pending: "border-yellow-400 bg-yellow-50",
},
},
}),
[status], // recompute when dependencies change
);
return <StyledDay $status={status}>Workout details</StyledDay>;
};
依赖数组的行为
React.useMemo把工厂的东西都递过来 如果您期望组件在这些值变化时更新, 则关闭 .
添加逻辑头
使用 .logic() 在您的课前运行任意 JavaScript 一次
名称或变体是计算出来的。 返回值是浅浮回
道具,这样你就能得到 $ 道具, DOM 属性, 或者其他什么
组件需要没有额外的钩子.
type DayStatus = "completed" | "pending"
interface WorkoutProps {
workouts: unknown[]
allResolved: boolean
hasCompleted: boolean
hasSkipped: boolean
$status?: DayStatus
}
const WorkoutDay = cm.div
.logic<WorkoutProps>((props) => {
const status = deriveDayStatus(props)
return {
$status: status,
["data-status"]: status,
}
})
.variants<WorkoutProps, { $status: DayStatus }>({
base: "rounded border p-4",
variants: {
$status: {
completed: "bg-green-50 border-green-400",
pending: "bg-white border-slate-200",
},
},
})
// Consumers only pass raw workout data – the logic header derives $status for you.
<WorkoutDay workouts={workouts} allResolved hasCompleted hasSkipped={false} />
返回值从
.logic()因此以后的逻辑调用可以 引用早先的结果或推翻它们.
食谱 cm.extend
与 cm.extend中,您可以在任何基底 React 组件上再加新样式
甚至支持额外的道具。 这样就容易再使用
组件变化而不重复逻辑.
import { ArrowBigDown } from "lucide-react";
import cm from "@classmatejs/react";
const StyledLucideArrow = cm.extend(ArrowBigDown)`
md:-right-4.5
right-1
slide-in-r-20
`;
// ts: we can pass only props which are accessible on a `lucid-react` Component
export default () => <StyledLucideArrow stroke="3" />;
⚠️由于扩展第三方组件而出现问题,见: 扩展其他 lib 组件
现在,我们可以定义一个基础组件, 扩展它的附加样式和
类,并传递属性。 你可以把类型传给 extend 函数改为
获得自动补全和类型检查.
import cm from "@classmatejs/react";
interface StyledSliderItemBaseProps {
$active: boolean;
}
const StyledSliderItemBase = cm.button<StyledSliderItemBaseProps>`
absolute
h-full
w-full
left-0
top-0
${(p) => (p.$active ? "animate-in fade-in" : "animate-out fade-out")}
`;
interface NewStyledSliderItemProps extends StyledSliderItemBaseProps {
$secondBool: boolean;
}
const NewStyledSliderItemWithNewProps = cm.extend(
StyledSliderItemBase,
)<NewStyledSliderItemProps>`
rounded-lg
text-lg
${(p) => (p.$active ? "bg-blue" : "bg-red")}
${(p) => (p.$secondBool ? "text-underline" : "some-class-here")}
`;
export default () => (
<NewStyledSliderItemWithNewProps $active $secondBool={false} />
);
// outputs: <button className="absolute h-full w-full left-0 top-0 animate-in fade-in rounded-lg text-lg bg-blue" />
从变量扩展
interface ButtonProps extends InputHTMLAttributes<HTMLInputElement> {
$severity: "info" | "warning" | "error";
$isActive?: boolean;
}
const Alert = cm.input.variants<ButtonProps>({
base: "p-4",
variants: {
$severity: {
info: (p) =>
`bg-blue-100 text-blue-800 ${p.$isActive ? "shadow-lg" : ""}`,
},
},
});
const ExtendedButton = cm.extend(Alert)<{ $test: boolean }>`
${(p) => (p.$test ? "bg-green-100 text-green-800" : "")}
`;
export default () => <ExtendedButton $severity="info" $test />;
// outputs: <input className="p-4 bg-blue-100 text-blue-800 shadow-lg bg-green-100 text-green-800" />
道具的自动推断类型
通过通过组件,我们可以验证组件以接受与标签相关的标记
道具来. 如果你想依靠道具来获取特定元素,这个就有用了
没有 $ 前缀.
// if you pass rc component it's types are validated
const ExtendedButton = cm.extend(cm.button``)`
some-class
${(p) => (p.type === "submit" ? "font-normal" : "font-bold")}
`;
// infers the type of the input element + add new props
const MyInput = ({ ...props }: HTMLAttributes<HTMLInputElement>) => (
<input {...props} />
);
const StyledDiv = cm.extend(MyInput)<{ $trigger?: boolean }>`
bg-white
${(p) => (p.$trigger ? "!border-error" : "")}
${(p) => (p.type === "submit" ? "font-normal" : "font-bold")}
`;
扩展其他 lib 组件 / any 作为输入
不幸的是,我们无法直接推断 类型的组件,如果它是 any
或打字松动。 但我们可以用中间步骤 将类型传递到
extend 函数.
import { ComponentProps } from 'react'
import { MapContainer } from 'react-leaflet'
import { Field, FieldConfig } from 'formik'
import cm, { CmBaseComponent } from 'react-classmate'
// we need to cast the type to ComponentProps
type StyledMapContainerType = ComponentProps<typeof MapContainer>
const StyledMapContainer: CmBaseComponent<StyledMapContainerType> = cm.extend(MapContainer)`
absolute
h-full
w-full
text-white
outline-0
`
export const Component = () => <StyledMapContainer bounds={...} />
// or with Formik
import { Field, FieldConfig } from 'formik'
type FieldComponentProps = ComponentProps<'input'> & FieldConfig
const FieldComponent = ({ ...props }: FieldComponentProps) => <Field {...props} />
const StyledField = cm.extend(FieldComponent)<{ $error: boolean }>`
theme-form-field
w-full
....
${p => (p.$error ? '!border-error' : '')}
`
export const Component = () => <StyledField placeholder="placeholder" as="select" name="name" $error />
常见JS
如果您使用CommonJS, 您可以这样导入库 :
const cm = require("@classmatejs/react").default;
// or
const { default: cm } = require("@classmatejs/react");
尾风合并
反应类同位素使用 后风相接 在罩子下合并类名. 最后一个班的名字总能赢 所以你 可以使用它来覆盖类.
即将
- 错误/ 排除出错: 由 ref.current( use Ref) 设置的类名将被覆盖
一旦组件重新投递
- 在文档中至少需要一条小文章
cm.raw()财务报告和审定财务报表cm.raw.variants()仅用于使用rc类名的语法 (输出为字符串)- 备选案文
cm.extend - 名称为 commonJS 的 lib 导入( 目前仅是)
.default) -- -- 意味着我们需要 删除 ts 文件内命名的导出以不重复 IDE 导入 建议: -- -- 更改构建后脚本以删除命名的 Esm 导出 - 纳入更多的测试、侧重于安全部门改革和反应的基准
- 高级 IDE 集成
- 在徘徊时显示生成的默认类
- 从已使用的 libs 执行自动补全和工具提示