@neoptocom/neopto-ui 1.6.3 → 1.6.5
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/README.md +1 -1
- package/dist/index.cjs +63 -10
- package/dist/index.d.cts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +63 -10
- package/package.json +1 -1
- package/src/components/Breadcrumb.docs.mdx +3 -0
- package/src/components/Breadcrumb.stories.tsx +3 -0
- package/src/components/Button.docs.mdx +3 -0
- package/src/components/Button.stories.tsx +3 -0
- package/src/components/Calendar.tsx +1 -1
- package/src/components/Card.docs.mdx +3 -0
- package/src/components/Chip.tsx +87 -12
- package/src/components/DateInput.tsx +1 -1
- package/src/stories/Chip.stories.tsx +47 -0
- package/src/stories/DateInput.stories.tsx +21 -0
- package/src/styles/library.css +0 -1
- package/src/styles/tailwind.css +0 -1
package/README.md
CHANGED
|
@@ -195,7 +195,7 @@ The library uses a comprehensive design token system:
|
|
|
195
195
|
|
|
196
196
|
## 📚 Documentation
|
|
197
197
|
|
|
198
|
-
Visit our [Storybook documentation](https://neoptocom.github.io/neopto-ui/) for:
|
|
198
|
+
Visit our [Storybook documentation](https://neoptocom.github.io/neopto-ui/storybook-static) for:
|
|
199
199
|
|
|
200
200
|
- Interactive component playground
|
|
201
201
|
- Design system guidelines
|
package/dist/index.cjs
CHANGED
|
@@ -1179,13 +1179,64 @@ var Button = React11__namespace.forwardRef(
|
|
|
1179
1179
|
}
|
|
1180
1180
|
);
|
|
1181
1181
|
Button.displayName = "Button";
|
|
1182
|
+
function getTextColorForBackground(bgClassName) {
|
|
1183
|
+
if (!bgClassName) return "";
|
|
1184
|
+
const lightColors = [
|
|
1185
|
+
"white",
|
|
1186
|
+
"gray-50",
|
|
1187
|
+
"gray-100",
|
|
1188
|
+
"slate-50",
|
|
1189
|
+
"slate-100",
|
|
1190
|
+
"zinc-50",
|
|
1191
|
+
"zinc-100",
|
|
1192
|
+
"neutral-50",
|
|
1193
|
+
"neutral-100",
|
|
1194
|
+
"stone-50",
|
|
1195
|
+
"stone-100"
|
|
1196
|
+
];
|
|
1197
|
+
const darkColors = [
|
|
1198
|
+
"black",
|
|
1199
|
+
"gray-900",
|
|
1200
|
+
"gray-950",
|
|
1201
|
+
"slate-900",
|
|
1202
|
+
"slate-950",
|
|
1203
|
+
"zinc-900",
|
|
1204
|
+
"zinc-950",
|
|
1205
|
+
"neutral-900",
|
|
1206
|
+
"neutral-950",
|
|
1207
|
+
"stone-900",
|
|
1208
|
+
"stone-950"
|
|
1209
|
+
];
|
|
1210
|
+
for (const lightColor of lightColors) {
|
|
1211
|
+
if (bgClassName.includes(lightColor)) {
|
|
1212
|
+
return "text-gray-900";
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
for (const darkColor of darkColors) {
|
|
1216
|
+
if (bgClassName.includes(darkColor)) {
|
|
1217
|
+
return "text-white";
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
const shadeMatch = bgClassName.match(/-(\d{2,3})/);
|
|
1221
|
+
if (shadeMatch) {
|
|
1222
|
+
const shade = parseInt(shadeMatch[1], 10);
|
|
1223
|
+
if (shade >= 100 && shade <= 400) {
|
|
1224
|
+
return "text-gray-900";
|
|
1225
|
+
}
|
|
1226
|
+
if (shade >= 500) {
|
|
1227
|
+
return "text-white";
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
return "text-white";
|
|
1231
|
+
}
|
|
1182
1232
|
function Chip({
|
|
1183
1233
|
variant = "success",
|
|
1184
1234
|
icon,
|
|
1185
1235
|
className = "",
|
|
1186
1236
|
label,
|
|
1187
|
-
backgroundColor,
|
|
1188
1237
|
textColor,
|
|
1238
|
+
bgClassName,
|
|
1239
|
+
title,
|
|
1189
1240
|
style,
|
|
1190
1241
|
onDelete,
|
|
1191
1242
|
...props
|
|
@@ -1198,28 +1249,30 @@ function Chip({
|
|
|
1198
1249
|
light: "bg-[var(--muted)] text-[var(--fg)]",
|
|
1199
1250
|
dark: "bg-[var(--surface)] text-[var(--fg)]"
|
|
1200
1251
|
};
|
|
1201
|
-
const
|
|
1202
|
-
const
|
|
1252
|
+
const hasCustomTextColor = !!textColor;
|
|
1253
|
+
const hasBgClassName = !!bgClassName;
|
|
1254
|
+
const colorClasses = hasBgClassName ? "" : hasCustomTextColor ? "" : variantCls[variant];
|
|
1255
|
+
const autoTextColor = hasBgClassName && !textColor ? getTextColorForBackground(bgClassName) : "";
|
|
1203
1256
|
const mergedStyle = {
|
|
1204
1257
|
...style,
|
|
1205
|
-
...backgroundColor && { backgroundColor },
|
|
1206
1258
|
...textColor && { color: textColor }
|
|
1207
1259
|
};
|
|
1208
1260
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1209
1261
|
"div",
|
|
1210
1262
|
{
|
|
1211
|
-
className: [base, colorClasses, className].join(" "),
|
|
1263
|
+
className: [base, colorClasses, bgClassName, autoTextColor, className].join(" "),
|
|
1212
1264
|
style: mergedStyle,
|
|
1265
|
+
title,
|
|
1213
1266
|
...props,
|
|
1214
1267
|
children: [
|
|
1215
|
-
icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm", className: "mr-0.5" }) : null,
|
|
1216
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
|
|
1268
|
+
icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: icon, size: "sm", className: "mr-0.5 flex-shrink-0" }) : null,
|
|
1269
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: label }),
|
|
1217
1270
|
onDelete ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1218
1271
|
"button",
|
|
1219
1272
|
{
|
|
1220
1273
|
type: "button",
|
|
1221
1274
|
onClick: onDelete,
|
|
1222
|
-
className: "ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30",
|
|
1275
|
+
className: "ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30 flex-shrink-0",
|
|
1223
1276
|
"aria-label": "Remove",
|
|
1224
1277
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { name: "close", size: "sm" })
|
|
1225
1278
|
}
|
|
@@ -1628,7 +1681,7 @@ function Calendar({
|
|
|
1628
1681
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
|
|
1629
1682
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
1630
1683
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1631
|
-
/* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "title-md",
|
|
1684
|
+
/* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "title-md", className: "text-[var(--fg)]", children: MONTHS[currentMonth.getMonth()] }),
|
|
1632
1685
|
/* @__PURE__ */ jsxRuntime.jsx(Typo, { variant: "title-md", bold: "medium", className: "text-cyan-500", children: currentMonth.getFullYear() })
|
|
1633
1686
|
] }),
|
|
1634
1687
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -1824,7 +1877,7 @@ var DateInput = React11__namespace.forwardRef(
|
|
|
1824
1877
|
...props
|
|
1825
1878
|
}
|
|
1826
1879
|
),
|
|
1827
|
-
showCalendar && !disabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-20 mt-2 w-full max-w-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", showDecorations: false, lightImage: lightBackgroundImage, darkImage: darkBackgroundImage, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1880
|
+
showCalendar && !disabled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute z-20 mt-2 w-full max-w-sm", children: /* @__PURE__ */ jsxRuntime.jsx(Card, { className: "p-4", showDecorations: false, variant: "app-background", lightImage: lightBackgroundImage, darkImage: darkBackgroundImage, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1828
1881
|
Calendar,
|
|
1829
1882
|
{
|
|
1830
1883
|
selectedDate: inputValue !== placeholder && parseDate(inputValue) && isValidDate(parseDate(inputValue)) ? parseDate(inputValue) : /* @__PURE__ */ new Date(),
|
package/dist/index.d.cts
CHANGED
|
@@ -292,14 +292,16 @@ type ChipProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
292
292
|
variant?: "warning" | "success" | "error" | "light" | "dark";
|
|
293
293
|
icon?: string;
|
|
294
294
|
label?: string;
|
|
295
|
-
/** Custom background color (overrides variant) */
|
|
296
|
-
backgroundColor?: string;
|
|
297
295
|
/** Custom text color (overrides variant) */
|
|
298
296
|
textColor?: string;
|
|
297
|
+
/** Tailwind class for background color (overrides variant) */
|
|
298
|
+
bgClassName?: string;
|
|
299
|
+
/** Title attribute for tooltip */
|
|
300
|
+
title?: string;
|
|
299
301
|
/** Optional handler to render a delete affordance */
|
|
300
302
|
onDelete?: React.MouseEventHandler<HTMLButtonElement>;
|
|
301
303
|
};
|
|
302
|
-
declare function Chip({ variant, icon, className, label,
|
|
304
|
+
declare function Chip({ variant, icon, className, label, textColor, bgClassName, title, style, onDelete, ...props }: ChipProps): react_jsx_runtime.JSX.Element;
|
|
303
305
|
|
|
304
306
|
type CounterProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> & {
|
|
305
307
|
value?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -292,14 +292,16 @@ type ChipProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
292
292
|
variant?: "warning" | "success" | "error" | "light" | "dark";
|
|
293
293
|
icon?: string;
|
|
294
294
|
label?: string;
|
|
295
|
-
/** Custom background color (overrides variant) */
|
|
296
|
-
backgroundColor?: string;
|
|
297
295
|
/** Custom text color (overrides variant) */
|
|
298
296
|
textColor?: string;
|
|
297
|
+
/** Tailwind class for background color (overrides variant) */
|
|
298
|
+
bgClassName?: string;
|
|
299
|
+
/** Title attribute for tooltip */
|
|
300
|
+
title?: string;
|
|
299
301
|
/** Optional handler to render a delete affordance */
|
|
300
302
|
onDelete?: React.MouseEventHandler<HTMLButtonElement>;
|
|
301
303
|
};
|
|
302
|
-
declare function Chip({ variant, icon, className, label,
|
|
304
|
+
declare function Chip({ variant, icon, className, label, textColor, bgClassName, title, style, onDelete, ...props }: ChipProps): react_jsx_runtime.JSX.Element;
|
|
303
305
|
|
|
304
306
|
type CounterProps = Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> & {
|
|
305
307
|
value?: number;
|
package/dist/index.js
CHANGED
|
@@ -1158,13 +1158,64 @@ var Button = React11.forwardRef(
|
|
|
1158
1158
|
}
|
|
1159
1159
|
);
|
|
1160
1160
|
Button.displayName = "Button";
|
|
1161
|
+
function getTextColorForBackground(bgClassName) {
|
|
1162
|
+
if (!bgClassName) return "";
|
|
1163
|
+
const lightColors = [
|
|
1164
|
+
"white",
|
|
1165
|
+
"gray-50",
|
|
1166
|
+
"gray-100",
|
|
1167
|
+
"slate-50",
|
|
1168
|
+
"slate-100",
|
|
1169
|
+
"zinc-50",
|
|
1170
|
+
"zinc-100",
|
|
1171
|
+
"neutral-50",
|
|
1172
|
+
"neutral-100",
|
|
1173
|
+
"stone-50",
|
|
1174
|
+
"stone-100"
|
|
1175
|
+
];
|
|
1176
|
+
const darkColors = [
|
|
1177
|
+
"black",
|
|
1178
|
+
"gray-900",
|
|
1179
|
+
"gray-950",
|
|
1180
|
+
"slate-900",
|
|
1181
|
+
"slate-950",
|
|
1182
|
+
"zinc-900",
|
|
1183
|
+
"zinc-950",
|
|
1184
|
+
"neutral-900",
|
|
1185
|
+
"neutral-950",
|
|
1186
|
+
"stone-900",
|
|
1187
|
+
"stone-950"
|
|
1188
|
+
];
|
|
1189
|
+
for (const lightColor of lightColors) {
|
|
1190
|
+
if (bgClassName.includes(lightColor)) {
|
|
1191
|
+
return "text-gray-900";
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
for (const darkColor of darkColors) {
|
|
1195
|
+
if (bgClassName.includes(darkColor)) {
|
|
1196
|
+
return "text-white";
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
const shadeMatch = bgClassName.match(/-(\d{2,3})/);
|
|
1200
|
+
if (shadeMatch) {
|
|
1201
|
+
const shade = parseInt(shadeMatch[1], 10);
|
|
1202
|
+
if (shade >= 100 && shade <= 400) {
|
|
1203
|
+
return "text-gray-900";
|
|
1204
|
+
}
|
|
1205
|
+
if (shade >= 500) {
|
|
1206
|
+
return "text-white";
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
return "text-white";
|
|
1210
|
+
}
|
|
1161
1211
|
function Chip({
|
|
1162
1212
|
variant = "success",
|
|
1163
1213
|
icon,
|
|
1164
1214
|
className = "",
|
|
1165
1215
|
label,
|
|
1166
|
-
backgroundColor,
|
|
1167
1216
|
textColor,
|
|
1217
|
+
bgClassName,
|
|
1218
|
+
title,
|
|
1168
1219
|
style,
|
|
1169
1220
|
onDelete,
|
|
1170
1221
|
...props
|
|
@@ -1177,28 +1228,30 @@ function Chip({
|
|
|
1177
1228
|
light: "bg-[var(--muted)] text-[var(--fg)]",
|
|
1178
1229
|
dark: "bg-[var(--surface)] text-[var(--fg)]"
|
|
1179
1230
|
};
|
|
1180
|
-
const
|
|
1181
|
-
const
|
|
1231
|
+
const hasCustomTextColor = !!textColor;
|
|
1232
|
+
const hasBgClassName = !!bgClassName;
|
|
1233
|
+
const colorClasses = hasBgClassName ? "" : hasCustomTextColor ? "" : variantCls[variant];
|
|
1234
|
+
const autoTextColor = hasBgClassName && !textColor ? getTextColorForBackground(bgClassName) : "";
|
|
1182
1235
|
const mergedStyle = {
|
|
1183
1236
|
...style,
|
|
1184
|
-
...backgroundColor && { backgroundColor },
|
|
1185
1237
|
...textColor && { color: textColor }
|
|
1186
1238
|
};
|
|
1187
1239
|
return /* @__PURE__ */ jsxs(
|
|
1188
1240
|
"div",
|
|
1189
1241
|
{
|
|
1190
|
-
className: [base, colorClasses, className].join(" "),
|
|
1242
|
+
className: [base, colorClasses, bgClassName, autoTextColor, className].join(" "),
|
|
1191
1243
|
style: mergedStyle,
|
|
1244
|
+
title,
|
|
1192
1245
|
...props,
|
|
1193
1246
|
children: [
|
|
1194
|
-
icon ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm", className: "mr-0.5" }) : null,
|
|
1195
|
-
/* @__PURE__ */ jsx("span", { children: label }),
|
|
1247
|
+
icon ? /* @__PURE__ */ jsx(Icon, { name: icon, size: "sm", className: "mr-0.5 flex-shrink-0" }) : null,
|
|
1248
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: label }),
|
|
1196
1249
|
onDelete ? /* @__PURE__ */ jsx(
|
|
1197
1250
|
"button",
|
|
1198
1251
|
{
|
|
1199
1252
|
type: "button",
|
|
1200
1253
|
onClick: onDelete,
|
|
1201
|
-
className: "ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30",
|
|
1254
|
+
className: "ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30 flex-shrink-0",
|
|
1202
1255
|
"aria-label": "Remove",
|
|
1203
1256
|
children: /* @__PURE__ */ jsx(Icon, { name: "close", size: "sm" })
|
|
1204
1257
|
}
|
|
@@ -1607,7 +1660,7 @@ function Calendar({
|
|
|
1607
1660
|
return /* @__PURE__ */ jsxs("div", { className: "w-full", children: [
|
|
1608
1661
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
1609
1662
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1610
|
-
/* @__PURE__ */ jsx(Typo, { variant: "title-md",
|
|
1663
|
+
/* @__PURE__ */ jsx(Typo, { variant: "title-md", className: "text-[var(--fg)]", children: MONTHS[currentMonth.getMonth()] }),
|
|
1611
1664
|
/* @__PURE__ */ jsx(Typo, { variant: "title-md", bold: "medium", className: "text-cyan-500", children: currentMonth.getFullYear() })
|
|
1612
1665
|
] }),
|
|
1613
1666
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
@@ -1803,7 +1856,7 @@ var DateInput = React11.forwardRef(
|
|
|
1803
1856
|
...props
|
|
1804
1857
|
}
|
|
1805
1858
|
),
|
|
1806
|
-
showCalendar && !disabled && /* @__PURE__ */ jsx("div", { className: "absolute z-20 mt-2 w-full max-w-sm", children: /* @__PURE__ */ jsx(Card, { className: "p-4", showDecorations: false, lightImage: lightBackgroundImage, darkImage: darkBackgroundImage, children: /* @__PURE__ */ jsx(
|
|
1859
|
+
showCalendar && !disabled && /* @__PURE__ */ jsx("div", { className: "absolute z-20 mt-2 w-full max-w-sm", children: /* @__PURE__ */ jsx(Card, { className: "p-4", showDecorations: false, variant: "app-background", lightImage: lightBackgroundImage, darkImage: darkBackgroundImage, children: /* @__PURE__ */ jsx(
|
|
1807
1860
|
Calendar,
|
|
1808
1861
|
{
|
|
1809
1862
|
selectedDate: inputValue !== placeholder && parseDate(inputValue) && isValidDate(parseDate(inputValue)) ? parseDate(inputValue) : /* @__PURE__ */ new Date(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neoptocom/neopto-ui",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.5",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A modern React component library built with Tailwind CSS v4 and TypeScript. Features dark mode, design tokens, and comprehensive Storybook documentation. Requires Tailwind v4+.",
|
|
6
6
|
"keywords": [
|
|
@@ -119,7 +119,7 @@ export default function Calendar({
|
|
|
119
119
|
{/* Header */}
|
|
120
120
|
<div className="flex items-center justify-between mb-4">
|
|
121
121
|
<div className="flex items-center gap-2">
|
|
122
|
-
<Typo variant="title-md"
|
|
122
|
+
<Typo variant="title-md" className="text-[var(--fg)]">
|
|
123
123
|
{MONTHS[currentMonth.getMonth()]}
|
|
124
124
|
</Typo>
|
|
125
125
|
<Typo variant="title-md" bold="medium" className="text-cyan-500">
|
package/src/components/Chip.tsx
CHANGED
|
@@ -5,21 +5,92 @@ export type ChipProps = React.HTMLAttributes<HTMLDivElement> & {
|
|
|
5
5
|
variant?: "warning" | "success" | "error" | "light" | "dark";
|
|
6
6
|
icon?: string;
|
|
7
7
|
label?: string;
|
|
8
|
-
/** Custom background color (overrides variant) */
|
|
9
|
-
backgroundColor?: string;
|
|
10
8
|
/** Custom text color (overrides variant) */
|
|
11
9
|
textColor?: string;
|
|
10
|
+
/** Tailwind class for background color (overrides variant) */
|
|
11
|
+
bgClassName?: string;
|
|
12
|
+
/** Title attribute for tooltip */
|
|
13
|
+
title?: string;
|
|
12
14
|
/** Optional handler to render a delete affordance */
|
|
13
15
|
onDelete?: React.MouseEventHandler<HTMLButtonElement>;
|
|
14
16
|
};
|
|
15
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Detects if a Tailwind background class represents a light or dark background
|
|
20
|
+
* and returns the appropriate text color class for optimal contrast.
|
|
21
|
+
*/
|
|
22
|
+
function getTextColorForBackground(bgClassName: string | undefined): string {
|
|
23
|
+
if (!bgClassName) return "";
|
|
24
|
+
|
|
25
|
+
// Named light colors - use dark text
|
|
26
|
+
const lightColors = [
|
|
27
|
+
"white",
|
|
28
|
+
"gray-50",
|
|
29
|
+
"gray-100",
|
|
30
|
+
"slate-50",
|
|
31
|
+
"slate-100",
|
|
32
|
+
"zinc-50",
|
|
33
|
+
"zinc-100",
|
|
34
|
+
"neutral-50",
|
|
35
|
+
"neutral-100",
|
|
36
|
+
"stone-50",
|
|
37
|
+
"stone-100"
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
// Named dark colors - use white text
|
|
41
|
+
const darkColors = [
|
|
42
|
+
"black",
|
|
43
|
+
"gray-900",
|
|
44
|
+
"gray-950",
|
|
45
|
+
"slate-900",
|
|
46
|
+
"slate-950",
|
|
47
|
+
"zinc-900",
|
|
48
|
+
"zinc-950",
|
|
49
|
+
"neutral-900",
|
|
50
|
+
"neutral-950",
|
|
51
|
+
"stone-900",
|
|
52
|
+
"stone-950"
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// Check for named colors
|
|
56
|
+
for (const lightColor of lightColors) {
|
|
57
|
+
if (bgClassName.includes(lightColor)) {
|
|
58
|
+
return "text-gray-900";
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const darkColor of darkColors) {
|
|
63
|
+
if (bgClassName.includes(darkColor)) {
|
|
64
|
+
return "text-white";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Extract shade number from patterns like bg-blue-500, bg-purple-200, etc.
|
|
69
|
+
const shadeMatch = bgClassName.match(/-(\d{2,3})/);
|
|
70
|
+
if (shadeMatch) {
|
|
71
|
+
const shade = parseInt(shadeMatch[1], 10);
|
|
72
|
+
// Shades 100-400 are typically light, use dark text
|
|
73
|
+
if (shade >= 100 && shade <= 400) {
|
|
74
|
+
return "text-gray-900";
|
|
75
|
+
}
|
|
76
|
+
// Shades 500-950 are typically dark, use white text
|
|
77
|
+
if (shade >= 500) {
|
|
78
|
+
return "text-white";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Default to white text if pattern is unclear (safer for most backgrounds)
|
|
83
|
+
return "text-white";
|
|
84
|
+
}
|
|
85
|
+
|
|
16
86
|
export default function Chip({
|
|
17
87
|
variant = "success",
|
|
18
88
|
icon,
|
|
19
89
|
className = "",
|
|
20
90
|
label,
|
|
21
|
-
backgroundColor,
|
|
22
91
|
textColor,
|
|
92
|
+
bgClassName,
|
|
93
|
+
title,
|
|
23
94
|
style,
|
|
24
95
|
onDelete,
|
|
25
96
|
...props
|
|
@@ -37,30 +108,34 @@ export default function Chip({
|
|
|
37
108
|
dark: "bg-[var(--surface)] text-[var(--fg)]"
|
|
38
109
|
};
|
|
39
110
|
|
|
40
|
-
// Use
|
|
41
|
-
const
|
|
42
|
-
const
|
|
111
|
+
// Use bgClassName if provided, otherwise use custom text color, otherwise use variant styles
|
|
112
|
+
const hasCustomTextColor = !!textColor;
|
|
113
|
+
const hasBgClassName = !!bgClassName;
|
|
114
|
+
const colorClasses = hasBgClassName ? "" : hasCustomTextColor ? "" : variantCls[variant];
|
|
115
|
+
|
|
116
|
+
// Automatically detect text color for custom background classes
|
|
117
|
+
const autoTextColor = hasBgClassName && !textColor ? getTextColorForBackground(bgClassName) : "";
|
|
43
118
|
|
|
44
|
-
// Merge custom
|
|
119
|
+
// Merge custom text color into style prop
|
|
45
120
|
const mergedStyle: React.CSSProperties = {
|
|
46
121
|
...style,
|
|
47
|
-
...(backgroundColor && { backgroundColor }),
|
|
48
122
|
...(textColor && { color: textColor })
|
|
49
123
|
};
|
|
50
124
|
|
|
51
125
|
return (
|
|
52
126
|
<div
|
|
53
|
-
className={[base, colorClasses, className].join(" ")}
|
|
127
|
+
className={[base, colorClasses, bgClassName, autoTextColor, className].join(" ")}
|
|
54
128
|
style={mergedStyle}
|
|
129
|
+
title={title}
|
|
55
130
|
{...props}
|
|
56
131
|
>
|
|
57
|
-
{icon ? <Icon name={icon} size="sm" className="mr-0.5" /> : null}
|
|
58
|
-
<span>{label}</span>
|
|
132
|
+
{icon ? <Icon name={icon} size="sm" className="mr-0.5 flex-shrink-0" /> : null}
|
|
133
|
+
<span className="truncate">{label}</span>
|
|
59
134
|
{onDelete ? (
|
|
60
135
|
<button
|
|
61
136
|
type="button"
|
|
62
137
|
onClick={onDelete}
|
|
63
|
-
className="ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30"
|
|
138
|
+
className="ml-1 flex h-4 w-4 items-center justify-center rounded-full transition-colors hover:bg-black/10 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-black/30 flex-shrink-0"
|
|
64
139
|
aria-label="Remove"
|
|
65
140
|
>
|
|
66
141
|
<Icon name="close" size="sm" />
|
|
@@ -206,7 +206,7 @@ export const DateInput = React.forwardRef<HTMLInputElement, DateInputProps>(
|
|
|
206
206
|
|
|
207
207
|
{showCalendar && !disabled && (
|
|
208
208
|
<div className="absolute z-20 mt-2 w-full max-w-sm">
|
|
209
|
-
<Card className="p-4" showDecorations={false} lightImage={lightBackgroundImage} darkImage={darkBackgroundImage}>
|
|
209
|
+
<Card className="p-4" showDecorations={false} variant="app-background" lightImage={lightBackgroundImage} darkImage={darkBackgroundImage}>
|
|
210
210
|
<Calendar
|
|
211
211
|
selectedDate={
|
|
212
212
|
inputValue !== placeholder &&
|
|
@@ -48,4 +48,51 @@ export const Deletable: Story = {
|
|
|
48
48
|
)
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
export const CustomBackgroundColor: Story = {
|
|
52
|
+
render: () => (
|
|
53
|
+
<div className="space-y-4">
|
|
54
|
+
<div>
|
|
55
|
+
<p className="text-sm text-gray-600 mb-2">Dark backgrounds (auto white text):</p>
|
|
56
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
57
|
+
<Chip bgClassName="bg-blue-500" label="Blue 500" title="Custom blue background" />
|
|
58
|
+
<Chip bgClassName="bg-purple-500" label="Purple 500" title="Custom purple background" />
|
|
59
|
+
<Chip bgClassName="bg-pink-500" label="Pink 500" title="Custom pink background" />
|
|
60
|
+
<Chip bgClassName="bg-indigo-500" label="Indigo 500" title="Custom indigo background" />
|
|
61
|
+
<Chip bgClassName="bg-teal-500" label="Teal 500" title="Custom teal background" />
|
|
62
|
+
<Chip bgClassName="bg-orange-500" label="Orange 500" title="Custom orange background" />
|
|
63
|
+
<Chip bgClassName="bg-cyan-500" label="Cyan 500" title="Custom cyan background" />
|
|
64
|
+
<Chip bgClassName="bg-emerald-500" label="Emerald 500" title="Custom emerald background" />
|
|
65
|
+
<Chip bgClassName="bg-red-600" label="Red 600" title="Custom red background" />
|
|
66
|
+
<Chip bgClassName="bg-gray-900" label="Gray 900" title="Custom gray background" />
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
<div>
|
|
70
|
+
<p className="text-sm text-gray-600 mb-2">Light backgrounds (auto dark text):</p>
|
|
71
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
72
|
+
<Chip bgClassName="bg-blue-200" label="Blue 200" title="Light blue background" />
|
|
73
|
+
<Chip bgClassName="bg-purple-200" label="Purple 200" title="Light purple background" />
|
|
74
|
+
<Chip bgClassName="bg-pink-200" label="Pink 200" title="Light pink background" />
|
|
75
|
+
<Chip bgClassName="bg-indigo-200" label="Indigo 200" title="Light indigo background" />
|
|
76
|
+
<Chip bgClassName="bg-teal-200" label="Teal 200" title="Light teal background" />
|
|
77
|
+
<Chip bgClassName="bg-orange-200" label="Orange 200" title="Light orange background" />
|
|
78
|
+
<Chip bgClassName="bg-cyan-200" label="Cyan 200" title="Light cyan background" />
|
|
79
|
+
<Chip bgClassName="bg-emerald-200" label="Emerald 200" title="Light emerald background" />
|
|
80
|
+
<Chip bgClassName="bg-yellow-300" label="Yellow 300" title="Light yellow background" />
|
|
81
|
+
<Chip bgClassName="bg-white" label="White" title="White background" />
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
)
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const CustomBackgroundWithIcon: Story = {
|
|
89
|
+
render: () => (
|
|
90
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
91
|
+
<Chip bgClassName="bg-blue-500" icon="check" label="Completed" title="Task completed" />
|
|
92
|
+
<Chip bgClassName="bg-purple-500" icon="star" label="Featured" title="Featured item" />
|
|
93
|
+
<Chip bgClassName="bg-pink-500" icon="favorite" label="Favorite" title="Marked as favorite" />
|
|
94
|
+
<Chip bgClassName="bg-indigo-500" icon="schedule" label="Scheduled" title="Scheduled for later" />
|
|
95
|
+
</div>
|
|
96
|
+
)
|
|
97
|
+
};
|
|
51
98
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
3
|
import { DateInput } from "../components/DateInput";
|
|
4
|
+
import * as assets from "../assets";
|
|
4
5
|
|
|
5
6
|
const meta: Meta<typeof DateInput> = {
|
|
6
7
|
title: "Components/DateInput",
|
|
@@ -134,3 +135,23 @@ export const Multiple: Story = {
|
|
|
134
135
|
}
|
|
135
136
|
};
|
|
136
137
|
|
|
138
|
+
export const WithBackgroundImages: Story = {
|
|
139
|
+
render: () => {
|
|
140
|
+
const [date, setDate] = React.useState<Date | null>(null);
|
|
141
|
+
return (
|
|
142
|
+
<div className="flex flex-col gap-4 w-96">
|
|
143
|
+
<DateInput
|
|
144
|
+
label="Select date"
|
|
145
|
+
value={date}
|
|
146
|
+
onChange={(d) => setDate(d)}
|
|
147
|
+
lightBackgroundImage={assets.bgLight}
|
|
148
|
+
darkBackgroundImage={assets.bgDark}
|
|
149
|
+
/>
|
|
150
|
+
<p className="text-xs text-[var(--muted-fg)]">
|
|
151
|
+
Calendar popup uses background images that adapt to light/dark mode.
|
|
152
|
+
</p>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
package/src/styles/library.css
CHANGED