@sovereignfs/ui 0.21.2 → 0.23.0
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/Checkbox.module.css +97 -0
- package/dist/Dialog.module.css +8 -4
- package/dist/DragHandleRow.module.css +51 -0
- package/dist/FormField.module.css +5 -0
- package/dist/Input.module.css +10 -0
- package/dist/Select.module.css +9 -0
- package/dist/Textarea.module.css +39 -0
- package/dist/Toast.module.css +3 -3
- package/dist/Toggle.module.css +1 -1
- package/dist/index.d.ts +58 -5
- package/dist/index.js +220 -98
- package/dist/tokens/semantic.css +6 -0
- package/package.json +1 -1
- package/src/components/Checkbox/Checkbox.module.css +97 -0
- package/src/components/Checkbox/Checkbox.tsx +68 -0
- package/src/components/Dialog/Dialog.module.css +8 -4
- package/src/components/DragHandleRow/DragHandleRow.module.css +51 -0
- package/src/components/DragHandleRow/DragHandleRow.tsx +61 -0
- package/src/components/FormField/FormField.module.css +5 -0
- package/src/components/FormField/FormField.tsx +36 -11
- package/src/components/FormField/__tests__/FormField.test.tsx +83 -0
- package/src/components/Input/Input.module.css +10 -0
- package/src/components/Select/Select.module.css +9 -0
- package/src/components/Select/Select.tsx +10 -2
- package/src/components/Textarea/Textarea.module.css +39 -0
- package/src/components/Textarea/Textarea.tsx +15 -0
- package/src/components/Textarea/__tests__/Textarea.test.tsx +29 -0
- package/src/components/Toast/Toast.module.css +3 -3
- package/src/components/Toggle/Toggle.module.css +1 -1
- package/src/index.ts +7 -1
- package/src/scroll-lock.ts +8 -2
- package/src/stories/Checkbox.stories.tsx +45 -0
- package/src/stories/DesignSystemOverview.stories.tsx +64 -6
- package/src/stories/DragHandleRow.stories.tsx +41 -0
- package/src/stories/FormField.stories.tsx +8 -8
- package/src/stories/MobilePatterns.stories.tsx +4 -4
- package/src/stories/Textarea.stories.tsx +51 -0
- package/src/stories/TokenGallery.stories.tsx +2 -0
- package/src/tokens/semantic.css +6 -0
package/dist/index.js
CHANGED
|
@@ -770,9 +770,9 @@ function Select({ className, size = "md", children, ...rest }) {
|
|
|
770
770
|
return /* @__PURE__ */ jsxs23(
|
|
771
771
|
"div",
|
|
772
772
|
{
|
|
773
|
-
className: [styles6.wrapper, size === "sm" ? styles6.sm : void 0].filter(Boolean).join(" "),
|
|
773
|
+
className: [styles6.wrapper, size === "sm" ? styles6.sm : void 0, className].filter(Boolean).join(" "),
|
|
774
774
|
children: [
|
|
775
|
-
/* @__PURE__ */ jsx34("select", { className:
|
|
775
|
+
/* @__PURE__ */ jsx34("select", { className: styles6.select, ...rest, children }),
|
|
776
776
|
/* @__PURE__ */ jsx34(
|
|
777
777
|
"svg",
|
|
778
778
|
{
|
|
@@ -903,6 +903,14 @@ function Input({ type = "text", className, ...rest }) {
|
|
|
903
903
|
return /* @__PURE__ */ jsx38("input", { type, className: classes, ...rest });
|
|
904
904
|
}
|
|
905
905
|
|
|
906
|
+
// src/components/Textarea/Textarea.tsx
|
|
907
|
+
import styles11 from "./Textarea.module.css";
|
|
908
|
+
import { jsx as jsx39 } from "react/jsx-runtime";
|
|
909
|
+
function Textarea({ className, rows = 4, ...rest }) {
|
|
910
|
+
const classes = [styles11.textarea, className].filter(Boolean).join(" ");
|
|
911
|
+
return /* @__PURE__ */ jsx39("textarea", { rows, className: classes, ...rest });
|
|
912
|
+
}
|
|
913
|
+
|
|
906
914
|
// src/components/Dialog/Dialog.tsx
|
|
907
915
|
import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
908
916
|
|
|
@@ -910,17 +918,17 @@ import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
|
910
918
|
function lockBodyScroll() {
|
|
911
919
|
const count = parseInt(document.body.dataset.scrollLocks ?? "0", 10);
|
|
912
920
|
document.body.dataset.scrollLocks = String(count + 1);
|
|
913
|
-
if (count === 0) document.body.style.
|
|
921
|
+
if (count === 0) document.body.style.overflowY = "hidden";
|
|
914
922
|
}
|
|
915
923
|
function unlockBodyScroll() {
|
|
916
924
|
const next = Math.max(0, parseInt(document.body.dataset.scrollLocks ?? "0", 10) - 1);
|
|
917
925
|
document.body.dataset.scrollLocks = String(next);
|
|
918
|
-
if (next === 0) document.body.style.
|
|
926
|
+
if (next === 0) document.body.style.overflowY = "";
|
|
919
927
|
}
|
|
920
928
|
|
|
921
929
|
// src/components/Dialog/Dialog.tsx
|
|
922
|
-
import
|
|
923
|
-
import { jsx as
|
|
930
|
+
import styles12 from "./Dialog.module.css";
|
|
931
|
+
import { jsx as jsx40, jsxs as jsxs26 } from "react/jsx-runtime";
|
|
924
932
|
var FOCUSABLE = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
925
933
|
function Dialog({
|
|
926
934
|
open,
|
|
@@ -979,10 +987,10 @@ function Dialog({
|
|
|
979
987
|
return (
|
|
980
988
|
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
981
989
|
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
982
|
-
/* @__PURE__ */
|
|
990
|
+
/* @__PURE__ */ jsx40(
|
|
983
991
|
"div",
|
|
984
992
|
{
|
|
985
|
-
className:
|
|
993
|
+
className: styles12.scrim,
|
|
986
994
|
role: "presentation",
|
|
987
995
|
onClick: (e) => {
|
|
988
996
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -998,23 +1006,23 @@ function Dialog({
|
|
|
998
1006
|
"aria-modal": "true",
|
|
999
1007
|
"aria-label": ariaLabel ?? title,
|
|
1000
1008
|
tabIndex: -1,
|
|
1001
|
-
className: [
|
|
1009
|
+
className: [styles12.panel, styles12[size]].join(" "),
|
|
1002
1010
|
children: [
|
|
1003
|
-
/* @__PURE__ */ jsxs26("div", { className:
|
|
1004
|
-
title && /* @__PURE__ */
|
|
1005
|
-
/* @__PURE__ */
|
|
1011
|
+
/* @__PURE__ */ jsxs26("div", { className: styles12.mobileBar, children: [
|
|
1012
|
+
title && /* @__PURE__ */ jsx40("span", { className: styles12.mobileBarTitle, children: title }),
|
|
1013
|
+
/* @__PURE__ */ jsx40(
|
|
1006
1014
|
"button",
|
|
1007
1015
|
{
|
|
1008
1016
|
type: "button",
|
|
1009
|
-
className:
|
|
1017
|
+
className: styles12.mobileBarClose,
|
|
1010
1018
|
"aria-label": "Close",
|
|
1011
1019
|
onClick: onClose,
|
|
1012
1020
|
children: "\xD7"
|
|
1013
1021
|
}
|
|
1014
1022
|
)
|
|
1015
1023
|
] }),
|
|
1016
|
-
/* @__PURE__ */
|
|
1017
|
-
/* @__PURE__ */
|
|
1024
|
+
/* @__PURE__ */ jsx40("button", { type: "button", className: styles12.close, "aria-label": "Close", onClick: onClose, children: "\xD7" }),
|
|
1025
|
+
/* @__PURE__ */ jsx40("div", { className: styles12.content, children })
|
|
1018
1026
|
]
|
|
1019
1027
|
}
|
|
1020
1028
|
)
|
|
@@ -1025,8 +1033,8 @@ function Dialog({
|
|
|
1025
1033
|
|
|
1026
1034
|
// src/components/Drawer/Drawer.tsx
|
|
1027
1035
|
import { useEffect as useEffect3, useRef as useRef3 } from "react";
|
|
1028
|
-
import
|
|
1029
|
-
import { jsx as
|
|
1036
|
+
import styles13 from "./Drawer.module.css";
|
|
1037
|
+
import { jsx as jsx41 } from "react/jsx-runtime";
|
|
1030
1038
|
var FOCUSABLE2 = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
1031
1039
|
function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
1032
1040
|
const panelRef = useRef3(null);
|
|
@@ -1078,10 +1086,10 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1078
1086
|
return (
|
|
1079
1087
|
// role="presentation" removes the scrim from the AT (it is purely visual).
|
|
1080
1088
|
// e.target check lets clicks inside the panel bubble without triggering dismiss.
|
|
1081
|
-
/* @__PURE__ */
|
|
1089
|
+
/* @__PURE__ */ jsx41(
|
|
1082
1090
|
"div",
|
|
1083
1091
|
{
|
|
1084
|
-
className:
|
|
1092
|
+
className: styles13.scrim,
|
|
1085
1093
|
role: "presentation",
|
|
1086
1094
|
onClick: (e) => {
|
|
1087
1095
|
if (e.target === e.currentTarget) onClose();
|
|
@@ -1089,7 +1097,7 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1089
1097
|
onKeyDown: (e) => {
|
|
1090
1098
|
if (e.key === "Escape") onClose();
|
|
1091
1099
|
},
|
|
1092
|
-
children: /* @__PURE__ */
|
|
1100
|
+
children: /* @__PURE__ */ jsx41(
|
|
1093
1101
|
"div",
|
|
1094
1102
|
{
|
|
1095
1103
|
ref: panelRef,
|
|
@@ -1097,7 +1105,7 @@ function Drawer({ open, onClose, "aria-label": ariaLabel, children }) {
|
|
|
1097
1105
|
"aria-modal": "true",
|
|
1098
1106
|
"aria-label": ariaLabel,
|
|
1099
1107
|
tabIndex: -1,
|
|
1100
|
-
className:
|
|
1108
|
+
className: styles13.panel,
|
|
1101
1109
|
children
|
|
1102
1110
|
}
|
|
1103
1111
|
)
|
|
@@ -1117,8 +1125,8 @@ import {
|
|
|
1117
1125
|
useRef as useRef4,
|
|
1118
1126
|
useState
|
|
1119
1127
|
} from "react";
|
|
1120
|
-
import
|
|
1121
|
-
import { jsx as
|
|
1128
|
+
import styles14 from "./Toast.module.css";
|
|
1129
|
+
import { jsx as jsx42, jsxs as jsxs27 } from "react/jsx-runtime";
|
|
1122
1130
|
var ToastContext = createContext(null);
|
|
1123
1131
|
function useToast() {
|
|
1124
1132
|
const ctx = useContext(ToastContext);
|
|
@@ -1158,14 +1166,14 @@ function ToastProvider({ children }) {
|
|
|
1158
1166
|
const value = useMemo(() => ({ show, dismiss }), [show, dismiss]);
|
|
1159
1167
|
return /* @__PURE__ */ jsxs27(ToastContext.Provider, { value, children: [
|
|
1160
1168
|
children,
|
|
1161
|
-
/* @__PURE__ */
|
|
1169
|
+
/* @__PURE__ */ jsx42(
|
|
1162
1170
|
"ol",
|
|
1163
1171
|
{
|
|
1164
|
-
className:
|
|
1172
|
+
className: styles14.region,
|
|
1165
1173
|
"aria-label": "Notifications",
|
|
1166
1174
|
"aria-live": "polite",
|
|
1167
1175
|
"aria-atomic": "false",
|
|
1168
|
-
children: toasts.map((toast) => /* @__PURE__ */
|
|
1176
|
+
children: toasts.map((toast) => /* @__PURE__ */ jsx42(Toast, { ...toast, onDismiss: dismiss }, toast.id))
|
|
1169
1177
|
}
|
|
1170
1178
|
)
|
|
1171
1179
|
] });
|
|
@@ -1186,24 +1194,24 @@ function Toast({
|
|
|
1186
1194
|
exiting,
|
|
1187
1195
|
onDismiss
|
|
1188
1196
|
}) {
|
|
1189
|
-
const categoryClass =
|
|
1197
|
+
const categoryClass = styles14[category] ?? styles14.info;
|
|
1190
1198
|
const iconName = CATEGORY_ICON[category] ?? "info";
|
|
1191
1199
|
return /* @__PURE__ */ jsxs27(
|
|
1192
1200
|
"li",
|
|
1193
1201
|
{
|
|
1194
1202
|
role: "status",
|
|
1195
|
-
className: `${
|
|
1203
|
+
className: `${styles14.toast} ${String(categoryClass)} ${exiting ? styles14.exiting : ""}`,
|
|
1196
1204
|
children: [
|
|
1197
|
-
/* @__PURE__ */
|
|
1198
|
-
/* @__PURE__ */ jsxs27("div", { className:
|
|
1199
|
-
/* @__PURE__ */
|
|
1200
|
-
message && /* @__PURE__ */
|
|
1205
|
+
/* @__PURE__ */ jsx42("span", { className: styles14.icon, "aria-hidden": "true", children: /* @__PURE__ */ jsx42(Icon, { name: iconName, size: "sm", "aria-hidden": true }) }),
|
|
1206
|
+
/* @__PURE__ */ jsxs27("div", { className: styles14.body, children: [
|
|
1207
|
+
/* @__PURE__ */ jsx42("div", { className: styles14.title, children: title }),
|
|
1208
|
+
message && /* @__PURE__ */ jsx42("div", { className: styles14.message, children: message })
|
|
1201
1209
|
] }),
|
|
1202
|
-
/* @__PURE__ */
|
|
1210
|
+
/* @__PURE__ */ jsx42(
|
|
1203
1211
|
"button",
|
|
1204
1212
|
{
|
|
1205
1213
|
type: "button",
|
|
1206
|
-
className:
|
|
1214
|
+
className: styles14.close,
|
|
1207
1215
|
"aria-label": "Dismiss notification",
|
|
1208
1216
|
onClick: () => onDismiss(id),
|
|
1209
1217
|
children: "\u2715"
|
|
@@ -1215,12 +1223,12 @@ function Toast({
|
|
|
1215
1223
|
}
|
|
1216
1224
|
|
|
1217
1225
|
// src/components/Card/Card.tsx
|
|
1218
|
-
import
|
|
1219
|
-
import { jsx as
|
|
1226
|
+
import styles15 from "./Card.module.css";
|
|
1227
|
+
import { jsx as jsx43 } from "react/jsx-runtime";
|
|
1220
1228
|
var paddingClass = {
|
|
1221
|
-
sm:
|
|
1222
|
-
md:
|
|
1223
|
-
lg:
|
|
1229
|
+
sm: styles15.paddingSm,
|
|
1230
|
+
md: styles15.paddingMd,
|
|
1231
|
+
lg: styles15.paddingLg
|
|
1224
1232
|
};
|
|
1225
1233
|
function Card({
|
|
1226
1234
|
as: Tag = "div",
|
|
@@ -1230,118 +1238,126 @@ function Card({
|
|
|
1230
1238
|
children,
|
|
1231
1239
|
...rest
|
|
1232
1240
|
}) {
|
|
1233
|
-
const cls = [
|
|
1234
|
-
return /* @__PURE__ */
|
|
1241
|
+
const cls = [styles15.card, paddingClass[padding], interactive && styles15.interactive, className].filter(Boolean).join(" ");
|
|
1242
|
+
return /* @__PURE__ */ jsx43(Tag, { className: cls, ...rest, children });
|
|
1235
1243
|
}
|
|
1236
1244
|
|
|
1237
1245
|
// src/components/FormField/FormField.tsx
|
|
1238
1246
|
import { useId as useId2 } from "react";
|
|
1239
|
-
import
|
|
1240
|
-
import { jsx as
|
|
1247
|
+
import styles16 from "./FormField.module.css";
|
|
1248
|
+
import { jsx as jsx44, jsxs as jsxs28 } from "react/jsx-runtime";
|
|
1241
1249
|
function FormField({
|
|
1242
1250
|
label,
|
|
1243
1251
|
hint,
|
|
1244
1252
|
error,
|
|
1245
|
-
|
|
1253
|
+
id,
|
|
1246
1254
|
required = false,
|
|
1247
|
-
|
|
1248
|
-
|
|
1255
|
+
className,
|
|
1256
|
+
children
|
|
1249
1257
|
}) {
|
|
1250
|
-
const
|
|
1251
|
-
const
|
|
1252
|
-
const
|
|
1253
|
-
|
|
1254
|
-
|
|
1258
|
+
const generatedId = useId2();
|
|
1259
|
+
const fieldId = id ?? generatedId;
|
|
1260
|
+
const hintId = `${fieldId}-hint`;
|
|
1261
|
+
const errorId = `${fieldId}-error`;
|
|
1262
|
+
const describedBy = [hint && !error && hintId, error && errorId].filter(Boolean).join(" ") || void 0;
|
|
1263
|
+
const field = {
|
|
1264
|
+
id: fieldId,
|
|
1265
|
+
"aria-describedby": describedBy,
|
|
1266
|
+
"aria-invalid": error ? true : void 0,
|
|
1267
|
+
required: required || void 0
|
|
1268
|
+
};
|
|
1269
|
+
return /* @__PURE__ */ jsxs28("div", { className: [styles16.field, className].filter(Boolean).join(" "), children: [
|
|
1270
|
+
/* @__PURE__ */ jsxs28("label", { className: styles16.label, htmlFor: fieldId, children: [
|
|
1255
1271
|
label,
|
|
1256
|
-
required && /* @__PURE__ */
|
|
1272
|
+
required && /* @__PURE__ */ jsx44("span", { className: styles16.required, "aria-hidden": "true", children: "*" })
|
|
1257
1273
|
] }),
|
|
1258
|
-
|
|
1259
|
-
hint && !error && /* @__PURE__ */
|
|
1260
|
-
error && /* @__PURE__ */
|
|
1274
|
+
children(field),
|
|
1275
|
+
hint && !error && /* @__PURE__ */ jsx44("p", { id: hintId, className: styles16.hint, children: hint }),
|
|
1276
|
+
error && /* @__PURE__ */ jsx44("p", { id: errorId, className: styles16.error, role: "alert", children: error })
|
|
1261
1277
|
] });
|
|
1262
1278
|
}
|
|
1263
1279
|
|
|
1264
1280
|
// src/components/PageHeader/PageHeader.tsx
|
|
1265
|
-
import
|
|
1266
|
-
import { jsx as
|
|
1281
|
+
import styles17 from "./PageHeader.module.css";
|
|
1282
|
+
import { jsx as jsx45, jsxs as jsxs29 } from "react/jsx-runtime";
|
|
1267
1283
|
function PageHeader({ title, description, action, className }) {
|
|
1268
|
-
return /* @__PURE__ */ jsxs29("header", { className: [
|
|
1269
|
-
/* @__PURE__ */ jsxs29("div", { className:
|
|
1270
|
-
/* @__PURE__ */
|
|
1271
|
-
description && /* @__PURE__ */
|
|
1284
|
+
return /* @__PURE__ */ jsxs29("header", { className: [styles17.header, className].filter(Boolean).join(" "), children: [
|
|
1285
|
+
/* @__PURE__ */ jsxs29("div", { className: styles17.text, children: [
|
|
1286
|
+
/* @__PURE__ */ jsx45("h1", { className: styles17.title, children: title }),
|
|
1287
|
+
description && /* @__PURE__ */ jsx45("p", { className: styles17.description, children: description })
|
|
1272
1288
|
] }),
|
|
1273
|
-
action && /* @__PURE__ */
|
|
1289
|
+
action && /* @__PURE__ */ jsx45("div", { className: styles17.action, children: action })
|
|
1274
1290
|
] });
|
|
1275
1291
|
}
|
|
1276
1292
|
|
|
1277
1293
|
// src/components/EmptyState/EmptyState.tsx
|
|
1278
|
-
import
|
|
1279
|
-
import { jsx as
|
|
1294
|
+
import styles18 from "./EmptyState.module.css";
|
|
1295
|
+
import { jsx as jsx46, jsxs as jsxs30 } from "react/jsx-runtime";
|
|
1280
1296
|
function EmptyState({ icon, heading, description, action, className }) {
|
|
1281
|
-
return /* @__PURE__ */ jsxs30("div", { className: [
|
|
1282
|
-
icon && /* @__PURE__ */
|
|
1283
|
-
/* @__PURE__ */
|
|
1284
|
-
description && /* @__PURE__ */
|
|
1297
|
+
return /* @__PURE__ */ jsxs30("div", { className: [styles18.root, className].filter(Boolean).join(" "), children: [
|
|
1298
|
+
icon && /* @__PURE__ */ jsx46("span", { className: styles18.icon, children: /* @__PURE__ */ jsx46(Icon, { name: icon, size: "lg", "aria-hidden": true }) }),
|
|
1299
|
+
/* @__PURE__ */ jsx46("h2", { className: styles18.heading, children: heading }),
|
|
1300
|
+
description && /* @__PURE__ */ jsx46("p", { className: styles18.description, children: description }),
|
|
1285
1301
|
action
|
|
1286
1302
|
] });
|
|
1287
1303
|
}
|
|
1288
1304
|
|
|
1289
1305
|
// src/components/Spinner/Spinner.tsx
|
|
1290
|
-
import
|
|
1291
|
-
import { jsx as
|
|
1306
|
+
import styles19 from "./Spinner.module.css";
|
|
1307
|
+
import { jsx as jsx47 } from "react/jsx-runtime";
|
|
1292
1308
|
var sizeClass = {
|
|
1293
|
-
sm:
|
|
1294
|
-
md:
|
|
1295
|
-
lg:
|
|
1309
|
+
sm: styles19.sm,
|
|
1310
|
+
md: styles19.md,
|
|
1311
|
+
lg: styles19.lg
|
|
1296
1312
|
};
|
|
1297
1313
|
function Spinner({ size = "md", label = "Loading\u2026", className }) {
|
|
1298
|
-
return /* @__PURE__ */
|
|
1314
|
+
return /* @__PURE__ */ jsx47(
|
|
1299
1315
|
"span",
|
|
1300
1316
|
{
|
|
1301
1317
|
role: "status",
|
|
1302
1318
|
"aria-label": label,
|
|
1303
|
-
className: [
|
|
1319
|
+
className: [styles19.spinner, sizeClass[size], className].filter(Boolean).join(" ")
|
|
1304
1320
|
}
|
|
1305
1321
|
);
|
|
1306
1322
|
}
|
|
1307
1323
|
|
|
1308
1324
|
// src/components/Avatar/Avatar.tsx
|
|
1309
1325
|
import { useState as useState2 } from "react";
|
|
1310
|
-
import
|
|
1311
|
-
import { jsx as
|
|
1326
|
+
import styles20 from "./Avatar.module.css";
|
|
1327
|
+
import { jsx as jsx48 } from "react/jsx-runtime";
|
|
1312
1328
|
function initials(name) {
|
|
1313
1329
|
const parts = name.trim().split(/\s+/);
|
|
1314
1330
|
if (parts.length === 1) return (parts[0] ?? "").slice(0, 2).toUpperCase();
|
|
1315
1331
|
return ((parts[0]?.[0] ?? "") + (parts[parts.length - 1]?.[0] ?? "")).toUpperCase();
|
|
1316
1332
|
}
|
|
1317
1333
|
var sizeClass2 = {
|
|
1318
|
-
sm:
|
|
1319
|
-
md:
|
|
1320
|
-
lg:
|
|
1334
|
+
sm: styles20.sm,
|
|
1335
|
+
md: styles20.md,
|
|
1336
|
+
lg: styles20.lg
|
|
1321
1337
|
};
|
|
1322
1338
|
function Avatar({ name, src, size = "md", className }) {
|
|
1323
1339
|
const [imgFailed, setImgFailed] = useState2(false);
|
|
1324
|
-
const cls = [
|
|
1340
|
+
const cls = [styles20.avatar, sizeClass2[size], className].filter(Boolean).join(" ");
|
|
1325
1341
|
if (src && !imgFailed) {
|
|
1326
|
-
return /* @__PURE__ */
|
|
1342
|
+
return /* @__PURE__ */ jsx48("span", { className: cls, "aria-label": name, children: /* @__PURE__ */ jsx48("img", { src, alt: name, className: styles20.img, onError: () => setImgFailed(true) }) });
|
|
1327
1343
|
}
|
|
1328
|
-
return /* @__PURE__ */
|
|
1344
|
+
return /* @__PURE__ */ jsx48("span", { className: cls, "aria-label": name, children: initials(name) });
|
|
1329
1345
|
}
|
|
1330
1346
|
|
|
1331
1347
|
// src/components/NavTabs/NavTabs.tsx
|
|
1332
|
-
import
|
|
1333
|
-
import { jsx as
|
|
1348
|
+
import styles21 from "./NavTabs.module.css";
|
|
1349
|
+
import { jsx as jsx49 } from "react/jsx-runtime";
|
|
1334
1350
|
function NavTabs({ items, className, "aria-label": ariaLabel }) {
|
|
1335
|
-
return /* @__PURE__ */
|
|
1351
|
+
return /* @__PURE__ */ jsx49(
|
|
1336
1352
|
"nav",
|
|
1337
1353
|
{
|
|
1338
|
-
className: [
|
|
1354
|
+
className: [styles21.nav, className].filter(Boolean).join(" "),
|
|
1339
1355
|
"aria-label": ariaLabel ?? "Page navigation",
|
|
1340
|
-
children: items.map((item) => /* @__PURE__ */
|
|
1356
|
+
children: items.map((item) => /* @__PURE__ */ jsx49(
|
|
1341
1357
|
"a",
|
|
1342
1358
|
{
|
|
1343
1359
|
href: item.href,
|
|
1344
|
-
className: [
|
|
1360
|
+
className: [styles21.link, item.active && styles21.active].filter(Boolean).join(" "),
|
|
1345
1361
|
"aria-current": item.active ? "page" : void 0,
|
|
1346
1362
|
children: item.label
|
|
1347
1363
|
},
|
|
@@ -1353,27 +1369,132 @@ function NavTabs({ items, className, "aria-label": ariaLabel }) {
|
|
|
1353
1369
|
|
|
1354
1370
|
// src/components/Tooltip/Tooltip.tsx
|
|
1355
1371
|
import { useId as useId3 } from "react";
|
|
1356
|
-
import
|
|
1357
|
-
import { jsx as
|
|
1372
|
+
import styles22 from "./Tooltip.module.css";
|
|
1373
|
+
import { jsx as jsx50, jsxs as jsxs31 } from "react/jsx-runtime";
|
|
1358
1374
|
var sideClass = {
|
|
1359
|
-
top:
|
|
1360
|
-
bottom:
|
|
1361
|
-
left:
|
|
1362
|
-
right:
|
|
1375
|
+
top: styles22.top,
|
|
1376
|
+
bottom: styles22.bottom,
|
|
1377
|
+
left: styles22.left,
|
|
1378
|
+
right: styles22.right
|
|
1363
1379
|
};
|
|
1364
1380
|
function Tooltip({ content, children, side = "top" }) {
|
|
1365
1381
|
const tipId = useId3();
|
|
1366
|
-
return /* @__PURE__ */ jsxs31("span", { className:
|
|
1367
|
-
/* @__PURE__ */
|
|
1368
|
-
/* @__PURE__ */
|
|
1382
|
+
return /* @__PURE__ */ jsxs31("span", { className: styles22.wrapper, children: [
|
|
1383
|
+
/* @__PURE__ */ jsx50("span", { "aria-describedby": tipId, children }),
|
|
1384
|
+
/* @__PURE__ */ jsx50("span", { id: tipId, role: "tooltip", className: [styles22.tip, sideClass[side]].join(" "), children: content })
|
|
1369
1385
|
] });
|
|
1370
1386
|
}
|
|
1387
|
+
|
|
1388
|
+
// src/components/Checkbox/Checkbox.tsx
|
|
1389
|
+
import styles23 from "./Checkbox.module.css";
|
|
1390
|
+
import { jsx as jsx51, jsxs as jsxs32 } from "react/jsx-runtime";
|
|
1391
|
+
function Checkbox({
|
|
1392
|
+
checked,
|
|
1393
|
+
onChange,
|
|
1394
|
+
label,
|
|
1395
|
+
strikeThrough = false,
|
|
1396
|
+
disabled,
|
|
1397
|
+
className,
|
|
1398
|
+
id,
|
|
1399
|
+
...rest
|
|
1400
|
+
}) {
|
|
1401
|
+
const inputId = id ?? `checkbox-${label.toLowerCase().replace(/\s+/g, "-")}`;
|
|
1402
|
+
return /* @__PURE__ */ jsxs32("span", { className: [styles23.root, className].filter(Boolean).join(" "), children: [
|
|
1403
|
+
/* @__PURE__ */ jsxs32("span", { className: [styles23.box, checked ? styles23.checked : ""].filter(Boolean).join(" "), children: [
|
|
1404
|
+
/* @__PURE__ */ jsx51(
|
|
1405
|
+
"input",
|
|
1406
|
+
{
|
|
1407
|
+
...rest,
|
|
1408
|
+
id: inputId,
|
|
1409
|
+
type: "checkbox",
|
|
1410
|
+
checked,
|
|
1411
|
+
disabled,
|
|
1412
|
+
className: styles23.input,
|
|
1413
|
+
onChange: (e) => onChange(e.target.checked)
|
|
1414
|
+
}
|
|
1415
|
+
),
|
|
1416
|
+
checked && /* @__PURE__ */ jsx51("svg", { className: styles23.tick, viewBox: "0 0 10 8", fill: "none", "aria-hidden": "true", children: /* @__PURE__ */ jsx51(
|
|
1417
|
+
"path",
|
|
1418
|
+
{
|
|
1419
|
+
d: "M1 4l3 3 5-6",
|
|
1420
|
+
stroke: "currentColor",
|
|
1421
|
+
strokeWidth: "1.5",
|
|
1422
|
+
strokeLinecap: "round",
|
|
1423
|
+
strokeLinejoin: "round"
|
|
1424
|
+
}
|
|
1425
|
+
) })
|
|
1426
|
+
] }),
|
|
1427
|
+
/* @__PURE__ */ jsx51(
|
|
1428
|
+
"label",
|
|
1429
|
+
{
|
|
1430
|
+
htmlFor: inputId,
|
|
1431
|
+
className: [
|
|
1432
|
+
styles23.label,
|
|
1433
|
+
strikeThrough && checked ? styles23.struck : "",
|
|
1434
|
+
disabled ? styles23.disabled : ""
|
|
1435
|
+
].filter(Boolean).join(" "),
|
|
1436
|
+
children: label
|
|
1437
|
+
}
|
|
1438
|
+
)
|
|
1439
|
+
] });
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
// src/components/DragHandleRow/DragHandleRow.tsx
|
|
1443
|
+
import styles24 from "./DragHandleRow.module.css";
|
|
1444
|
+
import { jsx as jsx52, jsxs as jsxs33 } from "react/jsx-runtime";
|
|
1445
|
+
function DragHandleRow({
|
|
1446
|
+
children,
|
|
1447
|
+
handleProps,
|
|
1448
|
+
isDragging,
|
|
1449
|
+
className,
|
|
1450
|
+
...rest
|
|
1451
|
+
}) {
|
|
1452
|
+
return /* @__PURE__ */ jsxs33(
|
|
1453
|
+
"div",
|
|
1454
|
+
{
|
|
1455
|
+
...rest,
|
|
1456
|
+
className: [styles24.row, isDragging ? styles24.dragging : "", className].filter(Boolean).join(" "),
|
|
1457
|
+
children: [
|
|
1458
|
+
/* @__PURE__ */ jsx52(
|
|
1459
|
+
"button",
|
|
1460
|
+
{
|
|
1461
|
+
type: "button",
|
|
1462
|
+
"aria-label": "Drag to reorder",
|
|
1463
|
+
className: styles24.handle,
|
|
1464
|
+
tabIndex: -1,
|
|
1465
|
+
...handleProps,
|
|
1466
|
+
children: /* @__PURE__ */ jsx52(DragIcon, {})
|
|
1467
|
+
}
|
|
1468
|
+
),
|
|
1469
|
+
/* @__PURE__ */ jsx52("div", { className: styles24.content, children })
|
|
1470
|
+
]
|
|
1471
|
+
}
|
|
1472
|
+
);
|
|
1473
|
+
}
|
|
1474
|
+
function DragIcon() {
|
|
1475
|
+
return /* @__PURE__ */ jsx52(
|
|
1476
|
+
"svg",
|
|
1477
|
+
{
|
|
1478
|
+
width: "14",
|
|
1479
|
+
height: "14",
|
|
1480
|
+
viewBox: "0 0 14 14",
|
|
1481
|
+
fill: "none",
|
|
1482
|
+
"aria-hidden": "true",
|
|
1483
|
+
className: styles24.icon,
|
|
1484
|
+
children: [3, 7, 11].map(
|
|
1485
|
+
(cy) => [4, 10].map((cx) => /* @__PURE__ */ jsx52("circle", { cx, cy, r: 1.2, fill: "currentColor" }, `${cx}-${cy}`))
|
|
1486
|
+
)
|
|
1487
|
+
}
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1371
1490
|
export {
|
|
1372
1491
|
Avatar,
|
|
1373
1492
|
Badge,
|
|
1374
1493
|
Button,
|
|
1375
1494
|
Card,
|
|
1495
|
+
Checkbox,
|
|
1376
1496
|
Dialog,
|
|
1497
|
+
DragHandleRow,
|
|
1377
1498
|
Drawer,
|
|
1378
1499
|
EmptyState,
|
|
1379
1500
|
FormField,
|
|
@@ -1387,6 +1508,7 @@ export {
|
|
|
1387
1508
|
Spinner,
|
|
1388
1509
|
SystemBanner,
|
|
1389
1510
|
Tabs,
|
|
1511
|
+
Textarea,
|
|
1390
1512
|
ToastProvider,
|
|
1391
1513
|
Toggle,
|
|
1392
1514
|
Tooltip,
|
package/dist/tokens/semantic.css
CHANGED
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
|
|
26
26
|
--sv-color-accent: var(--sv-grey-900);
|
|
27
27
|
--sv-color-accent-hover: var(--sv-grey-700);
|
|
28
|
+
/* Derived from --sv-color-accent via color-mix, so it re-themes automatically —
|
|
29
|
+
no separate dark-mode override needed. Tinted background for badges/chips
|
|
30
|
+
paired with --sv-color-accent text. */
|
|
31
|
+
--sv-color-accent-subtle: color-mix(in srgb, var(--sv-color-accent) 12%, transparent);
|
|
28
32
|
|
|
29
33
|
--sv-color-focus-ring: var(--sv-grey-900);
|
|
30
34
|
|
|
@@ -56,6 +60,7 @@
|
|
|
56
60
|
--sv-shadow-popover:
|
|
57
61
|
0 4px 16px rgba(0, 0, 0, 0.14), 0 2px 6px rgba(0, 0, 0, 0.08); /* e2 — floating panels */
|
|
58
62
|
--sv-shadow-overlay: 0 10px 38px rgba(0, 0, 0, 0.18), 0 6px 12px rgba(0, 0, 0, 0.12);
|
|
63
|
+
--sv-shadow-control: 0 1px 3px rgba(0, 0, 0, 0.2); /* small interactive-control shadows, e.g. Toggle thumb */
|
|
59
64
|
|
|
60
65
|
/*
|
|
61
66
|
* Instance identity tokens (RFC 0027 / RFC 0032 rename). Distinct namespace from --sv-color-*:
|
|
@@ -108,4 +113,5 @@
|
|
|
108
113
|
--sv-shadow-hover: 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
109
114
|
--sv-shadow-popover: 0 4px 16px rgba(0, 0, 0, 0.5), 0 2px 6px rgba(0, 0, 0, 0.3);
|
|
110
115
|
--sv-shadow-overlay: 0 10px 38px rgba(0, 0, 0, 0.6), 0 6px 12px rgba(0, 0, 0, 0.5);
|
|
116
|
+
--sv-shadow-control: 0 1px 3px rgba(0, 0, 0, 0.5);
|
|
111
117
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
.root {
|
|
2
|
+
display: inline-flex;
|
|
3
|
+
align-items: center;
|
|
4
|
+
gap: var(--sv-space-2);
|
|
5
|
+
cursor: pointer;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/* The visual box */
|
|
9
|
+
.box {
|
|
10
|
+
position: relative;
|
|
11
|
+
flex-shrink: 0;
|
|
12
|
+
width: 18px;
|
|
13
|
+
height: 18px;
|
|
14
|
+
border-radius: var(--sv-radius-sm);
|
|
15
|
+
border: 1.5px solid var(--sv-color-border-strong);
|
|
16
|
+
background: var(--sv-color-surface);
|
|
17
|
+
transition:
|
|
18
|
+
background-color 0.15s ease,
|
|
19
|
+
border-color 0.15s ease;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.checked {
|
|
26
|
+
background-color: var(--sv-color-accent);
|
|
27
|
+
border-color: var(--sv-color-accent);
|
|
28
|
+
color: var(--sv-white);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Hide the native input visually but keep it accessible */
|
|
32
|
+
.input {
|
|
33
|
+
position: absolute;
|
|
34
|
+
inset: 0;
|
|
35
|
+
opacity: 0;
|
|
36
|
+
margin: 0;
|
|
37
|
+
width: 100%;
|
|
38
|
+
height: 100%;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.input:disabled {
|
|
43
|
+
cursor: not-allowed;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.input:focus-visible + .tick,
|
|
47
|
+
.input:focus-visible {
|
|
48
|
+
outline: none;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.box:has(.input:focus-visible) {
|
|
52
|
+
outline: 2px solid var(--sv-color-focus-ring);
|
|
53
|
+
outline-offset: 2px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tick {
|
|
57
|
+
width: 10px;
|
|
58
|
+
height: 8px;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Label */
|
|
63
|
+
.label {
|
|
64
|
+
font-size: var(--sv-font-size-sm);
|
|
65
|
+
color: var(--sv-color-text-primary);
|
|
66
|
+
cursor: pointer;
|
|
67
|
+
user-select: none;
|
|
68
|
+
transition: color 0.15s ease;
|
|
69
|
+
/* Strike-through animates via clip-path on a pseudo-element */
|
|
70
|
+
position: relative;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.label::after {
|
|
74
|
+
content: '';
|
|
75
|
+
position: absolute;
|
|
76
|
+
left: 0;
|
|
77
|
+
top: 50%;
|
|
78
|
+
width: 100%;
|
|
79
|
+
height: 1.5px;
|
|
80
|
+
background: var(--sv-color-text-muted);
|
|
81
|
+
transform: scaleX(0);
|
|
82
|
+
transform-origin: left center;
|
|
83
|
+
transition: transform 0.2s ease;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
.struck {
|
|
87
|
+
color: var(--sv-color-text-muted);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.struck::after {
|
|
91
|
+
transform: scaleX(1);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.disabled {
|
|
95
|
+
opacity: 0.5;
|
|
96
|
+
cursor: not-allowed;
|
|
97
|
+
}
|