@canvas-components/react-components 0.2.0 → 0.3.1

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { forwardRef, useRef, useEffect, useImperativeHandle, createContext, useState, useMemo, useLayoutEffect, useContext } from 'react';
2
+ import { forwardRef, useRef, useEffect, useImperativeHandle, createContext, useState, useMemo, useLayoutEffect, useContext, Fragment as Fragment$1 } from 'react';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import { createPortal } from 'react-dom';
5
5
 
@@ -26,7 +26,7 @@ function styleInject(css, { insertAt } = {}) {
26
26
  }
27
27
 
28
28
  // src/styles/variables.css
29
- styleInject(":root {\n --canvas-react-color-primary: #1677ff;\n --canvas-react-color-primary-hover: #4096ff;\n --canvas-react-color-border: #d9d9d9;\n --canvas-react-color-border-strong: #bfbfbf;\n --canvas-react-color-text: #1f2937;\n --canvas-react-color-text-muted: #6b7280;\n --canvas-react-color-bg: #ffffff;\n --canvas-react-color-bg-soft: #f5f5f5;\n --canvas-react-color-bg-hover: #f3f4f6;\n --canvas-react-color-bg-active: #e6f4ff;\n --canvas-react-shadow-focus: 0 0 0 2px rgba(22, 119, 255, 0.12);\n --canvas-react-shadow-popup: 0 8px 24px rgba(15, 23, 42, 0.16);\n --canvas-react-radius-sm: 6px;\n --canvas-react-radius-md: 8px;\n --canvas-react-control-height: 32px;\n --canvas-react-font-size: 14px;\n --canvas-react-control-padding-inline: 11px;\n --canvas-react-control-padding-block: 0px;\n --canvas-react-content-zoom: 1;\n}\n");
29
+ styleInject(":root {\n --canvas-react-color-primary: #1677ff;\n --canvas-react-color-primary-hover: #4096ff;\n --canvas-react-color-border: #d9d9d9;\n --canvas-react-color-border-strong: #bfbfbf;\n --canvas-react-color-text: #1f2937;\n --canvas-react-color-text-muted: #6b7280;\n --canvas-react-color-bg: #ffffff;\n --canvas-react-color-bg-soft: #f5f5f5;\n --canvas-react-color-bg-hover: #f3f4f6;\n --canvas-react-color-bg-active: #e6f4ff;\n --canvas-react-shadow-focus: 0 0 0 2px rgba(22, 119, 255, 0.12);\n --canvas-react-shadow-popup: 0 8px 24px rgba(15, 23, 42, 0.16);\n --canvas-react-radius-sm: 6px;\n --canvas-react-radius-md: 8px;\n --canvas-react-control-height: 32px;\n --canvas-react-font-size: 14px;\n --canvas-react-control-padding-inline: 11px;\n --canvas-react-control-padding-block: 0px;\n --canvas-react-content-zoom: 1;\n}\n*,\n*::before,\n*::after {\n scrollbar-width: thin;\n scrollbar-color: #aeb4bd transparent;\n}\n*::-webkit-scrollbar {\n width: 8px;\n height: 8px;\n}\n*::-webkit-scrollbar-track {\n background: transparent;\n}\n*::-webkit-scrollbar-thumb {\n min-height: 32px;\n border: 2px solid transparent;\n border-radius: 999px;\n background: #aeb4bd;\n background-clip: content-box;\n}\n*::-webkit-scrollbar-thumb:hover {\n background: #8f98a5;\n background-clip: content-box;\n}\n*::-webkit-scrollbar-corner {\n background: transparent;\n}\n");
30
30
 
31
31
  // src/shared/class-names.ts
32
32
  function joinClassNames(...values) {
@@ -1107,16 +1107,31 @@ var operators = [
1107
1107
  ];
1108
1108
  function ReactFilterPanel(props) {
1109
1109
  const { request } = props;
1110
- const [draft, setDraft] = useState(request?.value ?? null);
1110
+ const [draft, setDraft] = useState(() => createDraft(request?.value));
1111
1111
  const [tab, setTab] = useState(resolveTab(request?.value));
1112
1112
  const [options, setOptions] = useState(request?.options ?? []);
1113
1113
  const [loading, setLoading] = useState(Boolean(request?.loadOptions));
1114
+ const conditionListRef = useRef(null);
1115
+ const [conditionListOverflowing, setConditionListOverflowing] = useState(false);
1114
1116
  useEffect(() => {
1115
- setDraft(request?.value ?? null);
1117
+ setDraft(createDraft(request?.value));
1116
1118
  setTab(resolveTab(request?.value));
1117
1119
  setOptions(request?.options ?? []);
1118
1120
  setLoading(Boolean(request?.loadOptions));
1119
1121
  }, [request]);
1122
+ useLayoutEffect(() => {
1123
+ const element = conditionListRef.current;
1124
+ if (!element || tab !== "condition") {
1125
+ setConditionListOverflowing(false);
1126
+ return;
1127
+ }
1128
+ const measure = () => setConditionListOverflowing(element.scrollHeight > element.clientHeight + 1);
1129
+ measure();
1130
+ if (typeof ResizeObserver === "undefined") return;
1131
+ const observer = new ResizeObserver(measure);
1132
+ observer.observe(element);
1133
+ return () => observer.disconnect();
1134
+ }, [draft?.conditions, tab]);
1120
1135
  useEffect(() => {
1121
1136
  if (!request?.loadOptions) return;
1122
1137
  const controller = new AbortController();
@@ -1151,6 +1166,20 @@ function ReactFilterPanel(props) {
1151
1166
  const allChecked = visibleKeys.length > 0 && selectedCount === visibleKeys.length;
1152
1167
  const allCount = request.options.find((item) => item.kind === "all")?.count ?? visibleOptions.filter((item) => item.kind === "value").reduce((sum, item) => sum + item.count, 0);
1153
1168
  const update = (patch) => setDraft((current) => ({ ...current ?? request.value, ...patch }));
1169
+ const updateConditions = (conditions) => {
1170
+ const first = conditions[0] ?? createEmptyCondition();
1171
+ const second = conditions[1] ?? createEmptyCondition();
1172
+ update({
1173
+ conditions,
1174
+ filterType: "condition",
1175
+ conditionOperator: first.operator,
1176
+ conditionValue: first.value ?? "",
1177
+ conditionSecondValue: first.secondValue ?? "",
1178
+ secondConditionOperator: second.operator,
1179
+ secondConditionValue: second.value ?? "",
1180
+ secondConditionSecondValue: second.secondValue ?? ""
1181
+ });
1182
+ };
1154
1183
  const toggle = (keys, checked) => {
1155
1184
  const next = new Set(draft.selectedKeys ?? []);
1156
1185
  keys.forEach((key) => checked ? next.add(key) : next.delete(key));
@@ -1257,97 +1286,138 @@ function ReactFilterPanel(props) {
1257
1286
  )
1258
1287
  }
1259
1288
  ) })
1260
- ] }) : /* @__PURE__ */ jsxs("div", { style: { marginTop: 12, display: "grid", gap: 12, minHeight: 290, alignContent: "start", flex: "1 1 auto" }, children: [
1261
- /* @__PURE__ */ jsx(
1262
- ConditionEditor,
1263
- {
1264
- label: request.texts.firstConditionLabel,
1265
- operator: draft.conditionOperator ?? "none",
1266
- value: draft.conditionValue ?? "",
1267
- secondValue: draft.conditionSecondValue ?? "",
1268
- texts: request.texts,
1269
- onChange: (patch) => update(patch),
1270
- onSubmit: apply
1271
- }
1272
- ),
1273
- /* @__PURE__ */ jsxs("div", { children: [
1274
- /* @__PURE__ */ jsx("div", { style: fieldLabelStyle, children: request.texts.relationLabel }),
1275
- /* @__PURE__ */ jsx(
1276
- ReactSelect,
1277
- {
1278
- value: draft.conditionRelation === "or" ? "or" : "and",
1279
- allowClear: false,
1280
- options: [{ label: request.texts.relationAndLabel, value: "and" }, { label: request.texts.relationOrLabel, value: "or" }],
1281
- onChange: (value) => update({ conditionRelation: value, filterType: "condition" }),
1282
- style: { width: "100%" }
1283
- }
1284
- )
1285
- ] }),
1286
- /* @__PURE__ */ jsx(
1287
- ConditionEditor,
1289
+ ] }) : /* @__PURE__ */ jsx("div", { style: { marginTop: 12, display: "flex", flexDirection: "column", gap: 10, minHeight: 0, flex: "1 1 auto" }, children: /* @__PURE__ */ jsx(
1290
+ "div",
1291
+ {
1292
+ ref: conditionListRef,
1293
+ className: "react-filter-panel-condition-list",
1294
+ style: { ...conditionListStyle, overflowY: conditionListOverflowing ? "auto" : "hidden" },
1295
+ children: (draft.conditions ?? []).map((condition, index, conditions) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
1296
+ index > 0 ? /* @__PURE__ */ jsxs("div", { className: "react-filter-panel-condition-relation", style: conditionRowStyle, children: [
1297
+ /* @__PURE__ */ jsx("span", { style: inlineLabelStyle, children: request.texts.relationLabel }),
1298
+ /* @__PURE__ */ jsx(
1299
+ ReactSelect,
1300
+ {
1301
+ value: draft.conditionRelation === "or" ? "or" : "and",
1302
+ allowClear: false,
1303
+ options: [{ label: request.texts.relationAndLabel, value: "and" }, { label: request.texts.relationOrLabel, value: "or" }],
1304
+ onChange: (value) => update({ conditionRelation: value, filterType: "condition" }),
1305
+ style: { width: "100%" }
1306
+ }
1307
+ ),
1308
+ /* @__PURE__ */ jsx("span", {})
1309
+ ] }) : null,
1310
+ /* @__PURE__ */ jsx(
1311
+ ConditionEditor,
1312
+ {
1313
+ label: resolveConditionLabel(request.texts, index),
1314
+ condition,
1315
+ texts: request.texts,
1316
+ canDelete: conditions.length > 1,
1317
+ onDelete: () => updateConditions(conditions.filter((_item, itemIndex) => itemIndex !== index)),
1318
+ onChange: (nextCondition) => updateConditions(conditions.map((item, itemIndex) => itemIndex === index ? nextCondition : item)),
1319
+ onSubmit: apply
1320
+ }
1321
+ )
1322
+ ] }, index))
1323
+ }
1324
+ ) }),
1325
+ /* @__PURE__ */ jsxs("div", { className: "react-filter-panel-footer", style: footerStyle, children: [
1326
+ tab === "condition" ? /* @__PURE__ */ jsxs(
1327
+ "button",
1288
1328
  {
1289
- second: true,
1290
- label: request.texts.secondConditionLabel,
1291
- operator: draft.secondConditionOperator ?? "none",
1292
- value: draft.secondConditionValue ?? "",
1293
- secondValue: draft.secondConditionSecondValue ?? "",
1294
- texts: request.texts,
1295
- onChange: (patch) => update(patch),
1296
- onSubmit: apply
1329
+ type: "button",
1330
+ style: addConditionStyle,
1331
+ onClick: () => updateConditions([...draft.conditions ?? [], createEmptyCondition()]),
1332
+ children: [
1333
+ "+ ",
1334
+ request.texts.addConditionAction ?? "\u65B0\u589E\u6761\u4EF6"
1335
+ ]
1297
1336
  }
1298
- )
1299
- ] }),
1300
- /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
1301
- /* @__PURE__ */ jsx("button", { type: "button", style: actionStyle(false), onClick: () => {
1302
- request.onReset();
1303
- request.onClose();
1304
- }, children: request.texts.resetAction }),
1305
- /* @__PURE__ */ jsx("button", { type: "button", style: actionStyle(true), onClick: apply, children: request.texts.applyAction })
1337
+ ) : null,
1338
+ /* @__PURE__ */ jsxs("div", { className: "react-filter-panel-footer-actions", style: footerActionsStyle, children: [
1339
+ /* @__PURE__ */ jsx("button", { type: "button", style: actionStyle(false), onClick: () => {
1340
+ request.onReset();
1341
+ request.onClose();
1342
+ }, children: request.texts.resetAction }),
1343
+ /* @__PURE__ */ jsx("button", { type: "button", style: actionStyle(true), onClick: apply, children: request.texts.applyAction })
1344
+ ] })
1306
1345
  ] })
1307
1346
  ] })
1308
1347
  }
1309
1348
  );
1310
1349
  }
1311
1350
  function ConditionEditor(props) {
1312
- const prefix = props.second ? "secondCondition" : "condition";
1313
- const hidesValue = ["none", "blank", "notBlank"].includes(props.operator);
1314
- const needsSecond = ["between", "notBetween"].includes(props.operator);
1315
- return /* @__PURE__ */ jsxs("div", { children: [
1316
- /* @__PURE__ */ jsx("div", { style: fieldLabelStyle, children: props.label }),
1317
- /* @__PURE__ */ jsx(
1318
- ReactSelect,
1319
- {
1320
- value: props.operator,
1321
- options: operators,
1322
- allowClear: false,
1323
- onChange: (value) => props.onChange({ [`${prefix}Operator`]: value, filterType: "condition" }),
1324
- style: { width: "100%" }
1325
- }
1326
- ),
1327
- hidesValue ? null : /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: 8, marginTop: 8 }, children: [
1351
+ const hidesValue = ["none", "blank", "notBlank"].includes(props.condition.operator);
1352
+ const needsSecond = ["between", "notBetween"].includes(props.condition.operator);
1353
+ return /* @__PURE__ */ jsxs("div", { style: conditionEditorStyle, children: [
1354
+ /* @__PURE__ */ jsxs("div", { className: "react-filter-panel-condition-row", style: conditionRowStyle, children: [
1355
+ /* @__PURE__ */ jsx("span", { style: inlineLabelStyle, children: props.label }),
1356
+ /* @__PURE__ */ jsx(
1357
+ ReactSelect,
1358
+ {
1359
+ value: props.condition.operator,
1360
+ options: operators,
1361
+ allowClear: false,
1362
+ onChange: (value) => props.onChange({ ...props.condition, operator: value }),
1363
+ style: { width: "100%" }
1364
+ }
1365
+ ),
1366
+ props.canDelete ? /* @__PURE__ */ jsx(
1367
+ "button",
1368
+ {
1369
+ type: "button",
1370
+ title: `${props.texts.deleteConditionAction ?? "\u5220\u9664\u6761\u4EF6"} ${props.label}`,
1371
+ "aria-label": `${props.texts.deleteConditionAction ?? "\u5220\u9664\u6761\u4EF6"} ${props.label}`,
1372
+ style: deleteConditionStyle,
1373
+ onClick: props.onDelete,
1374
+ children: "\xD7"
1375
+ }
1376
+ ) : /* @__PURE__ */ jsx("span", {})
1377
+ ] }),
1378
+ hidesValue ? null : /* @__PURE__ */ jsxs("div", { style: conditionValueStyle, children: [
1328
1379
  /* @__PURE__ */ jsx(
1329
1380
  ReactInput,
1330
1381
  {
1331
- value: props.value,
1382
+ value: props.condition.value ?? "",
1332
1383
  placeholder: props.texts.firstValuePlaceholder,
1333
1384
  onPressEnter: props.onSubmit,
1334
- onChange: (event) => props.onChange({ [`${prefix}Value`]: event.target.value, filterType: "condition" }),
1385
+ onChange: (event) => props.onChange({ ...props.condition, value: event.target.value }),
1335
1386
  style: { width: "100%" }
1336
1387
  }
1337
1388
  ),
1338
1389
  needsSecond ? /* @__PURE__ */ jsx(
1339
1390
  ReactInput,
1340
1391
  {
1341
- value: props.secondValue,
1392
+ value: props.condition.secondValue ?? "",
1342
1393
  placeholder: props.texts.secondValuePlaceholder,
1343
1394
  onPressEnter: props.onSubmit,
1344
- onChange: (event) => props.onChange({ [`${prefix}SecondValue`]: event.target.value, filterType: "condition" }),
1395
+ onChange: (event) => props.onChange({ ...props.condition, secondValue: event.target.value }),
1345
1396
  style: { width: "100%" }
1346
1397
  }
1347
1398
  ) : null
1348
1399
  ] })
1349
1400
  ] });
1350
1401
  }
1402
+ function createDraft(value) {
1403
+ if (!value) return null;
1404
+ return { ...value, conditions: resolveDraftConditions(value) };
1405
+ }
1406
+ function resolveDraftConditions(value) {
1407
+ if (value.conditions?.length) return value.conditions.map((condition) => ({ ...condition }));
1408
+ return [
1409
+ { operator: value.conditionOperator ?? "none", value: value.conditionValue ?? "", secondValue: value.conditionSecondValue ?? "" },
1410
+ { operator: value.secondConditionOperator ?? "none", value: value.secondConditionValue ?? "", secondValue: value.secondConditionSecondValue ?? "" }
1411
+ ];
1412
+ }
1413
+ function createEmptyCondition() {
1414
+ return { operator: "none", value: "", secondValue: "" };
1415
+ }
1416
+ function resolveConditionLabel(texts, index) {
1417
+ if (index === 0) return texts.firstConditionLabel;
1418
+ if (index === 1) return texts.secondConditionLabel;
1419
+ return `${texts.conditionLabel ?? "\u6761\u4EF6"} ${index + 1}`;
1420
+ }
1351
1421
  function resolveTab(value) {
1352
1422
  return value?.filterType === "condition" || value?.conditionOperator && value.conditionOperator !== "none" ? "condition" : "value";
1353
1423
  }
@@ -1359,7 +1429,15 @@ function optionLabel(item, texts) {
1359
1429
  var checkLabelStyle = { display: "inline-flex", alignItems: "center", gap: 8, color: "#374151", fontSize: 14 };
1360
1430
  var optionStyle = { display: "flex", alignItems: "center", gap: 8, minHeight: 40, padding: "4px 8px", borderRadius: 6, color: "#374151", fontSize: 14, cursor: "pointer", boxSizing: "border-box" };
1361
1431
  var optionTextStyle = { display: "block", flex: "1 1 0", width: 0, minWidth: 0, overflow: "hidden", whiteSpace: "nowrap", textOverflow: "ellipsis" };
1362
- var fieldLabelStyle = { marginBottom: 6, fontSize: 12, color: "#6b7280" };
1432
+ var conditionListStyle = { display: "grid", gap: 8, minHeight: 0, flex: "1 1 auto", alignContent: "start", paddingRight: 3 };
1433
+ var conditionEditorStyle = { display: "grid", gap: 8 };
1434
+ var conditionRowStyle = { display: "grid", gridTemplateColumns: "56px minmax(0, 1fr) 22px", alignItems: "center", gap: 8 };
1435
+ var inlineLabelStyle = { color: "#6b7280", fontSize: 12, whiteSpace: "nowrap" };
1436
+ var conditionValueStyle = { display: "grid", gap: 8, marginLeft: 64, marginRight: 30 };
1437
+ var addConditionStyle = { height: 32, padding: "0 10px", border: "1px solid #91caff", borderRadius: 6, background: "#f0f7ff", color: "#1677ff", cursor: "pointer", whiteSpace: "nowrap" };
1438
+ var deleteConditionStyle = { width: 22, height: 22, padding: 0, border: 0, borderRadius: 4, background: "transparent", color: "#9ca3af", fontSize: 18, lineHeight: "20px", cursor: "pointer" };
1439
+ var footerStyle = { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8 };
1440
+ var footerActionsStyle = { display: "flex", gap: 8, marginLeft: "auto" };
1363
1441
  function tabStyle(active) {
1364
1442
  return { flex: 1, height: 32, border: active ? "1px solid #1677ff" : "1px solid #d9d9d9", borderRadius: 6, background: active ? "#e6f4ff" : "#fff", color: active ? "#1677ff" : "#374151", cursor: "pointer" };
1365
1443
  }
@@ -5252,7 +5330,7 @@ function useCompatibleId(prefix = "canvas-react-id") {
5252
5330
  }
5253
5331
 
5254
5332
  // src/components/modal/style.css
5255
- styleInject('@import "../../styles/variables.css";\n.canvas-react-modal-root,\n.canvas-react-modal-root * {\n box-sizing: border-box;\n}\n.canvas-react-modal-root {\n position: fixed;\n inset: 0;\n color: var(--canvas-react-color-text);\n font-size: var(--canvas-react-font-size);\n line-height: 1.5;\n}\n.canvas-react-modal-mask {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.45);\n}\n.canvas-react-modal-wrap {\n position: absolute;\n inset: 0;\n overflow: auto;\n padding: 100px 24px 24px;\n pointer-events: none;\n}\n.canvas-react-modal-root-centered .canvas-react-modal-wrap {\n display: flex;\n align-items: center;\n padding-top: 24px;\n}\n.canvas-react-modal-root-fullscreen .canvas-react-modal-wrap {\n padding: 0;\n overflow: hidden;\n}\n.canvas-react-modal {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 520px;\n max-width: calc(100vw - 48px);\n max-height: calc(100vh - 124px);\n margin: 0 auto;\n overflow: hidden;\n border-radius: var(--canvas-react-radius-md, 8px);\n background: var(--canvas-react-color-bg, #ffffff);\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.22);\n outline: none;\n pointer-events: auto;\n}\n.canvas-react-modal-root-centered .canvas-react-modal {\n max-height: calc(100vh - 48px);\n}\n.canvas-react-modal.canvas-react-modal-fullscreen {\n width: 100% !important;\n max-width: none;\n height: 100%;\n max-height: none;\n border-radius: 0;\n}\n.canvas-react-modal-header {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n min-height: 56px;\n padding: 0 16px 0 24px;\n border-bottom: 1px solid var(--canvas-react-color-border);\n}\n.canvas-react-modal-title {\n min-width: 0;\n flex: 1 1 auto;\n color: var(--canvas-react-color-text);\n font-size: 16px;\n font-weight: 600;\n overflow-wrap: anywhere;\n}\n.canvas-react-modal-tools {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 4px;\n margin-left: 16px;\n}\n.canvas-react-modal-tool {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: var(--canvas-react-radius-sm, 6px);\n background: transparent;\n color: var(--canvas-react-color-text-muted);\n cursor: pointer;\n transition: background-color 0.18s ease, color 0.18s ease;\n}\n.canvas-react-modal-tool:hover {\n background: var(--canvas-react-color-bg-hover);\n color: var(--canvas-react-color-text);\n}\n.canvas-react-modal-tool:focus-visible,\n.canvas-react-modal-button:focus-visible {\n outline: 2px solid var(--canvas-react-color-primary);\n outline-offset: 2px;\n}\n.canvas-react-modal-fullscreen-icon {\n position: relative;\n display: block;\n width: 15px;\n height: 15px;\n border: 1.5px solid currentColor;\n}\n.canvas-react-modal-fullscreen-icon-exit::before {\n position: absolute;\n inset: 3px -4px -4px 3px;\n border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor;\n content: "";\n}\n.canvas-react-modal-close-icon {\n position: relative;\n display: block;\n width: 16px;\n height: 16px;\n}\n.canvas-react-modal-close-icon::before,\n.canvas-react-modal-close-icon::after {\n position: absolute;\n top: 7px;\n left: 1px;\n width: 14px;\n height: 1.5px;\n border-radius: 1px;\n background: currentColor;\n content: "";\n}\n.canvas-react-modal-close-icon::before {\n transform: rotate(45deg);\n}\n.canvas-react-modal-close-icon::after {\n transform: rotate(-45deg);\n}\n.canvas-react-modal-body {\n min-height: 0;\n flex: 1 1 auto;\n overflow: auto;\n padding: 20px 24px;\n}\n.canvas-react-modal-footer {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 24px;\n border-top: 1px solid var(--canvas-react-color-border);\n}\n.canvas-react-modal-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: var(--canvas-react-control-height, 32px);\n min-width: 64px;\n padding: 0 var(--canvas-react-control-padding-inline, 11px);\n border: 1px solid var(--canvas-react-color-border);\n border-radius: var(--canvas-react-radius-sm, 6px);\n background: var(--canvas-react-color-bg);\n color: var(--canvas-react-color-text);\n font: inherit;\n line-height: 1;\n white-space: nowrap;\n cursor: pointer;\n transition:\n border-color 0.18s ease,\n background-color 0.18s ease,\n color 0.18s ease,\n opacity 0.18s ease;\n}\n.canvas-react-modal-button:hover {\n border-color: var(--canvas-react-color-primary-hover);\n color: var(--canvas-react-color-primary-hover);\n}\n.canvas-react-modal-button-primary {\n border-color: var(--canvas-react-color-primary);\n background: var(--canvas-react-color-primary);\n color: #ffffff;\n}\n.canvas-react-modal-button-primary:hover {\n border-color: var(--canvas-react-color-primary-hover);\n background: var(--canvas-react-color-primary-hover);\n color: #ffffff;\n}\n.canvas-react-modal-button:disabled {\n opacity: 0.56;\n cursor: not-allowed;\n}\n.canvas-react-modal-loading {\n width: 14px;\n height: 14px;\n border: 2px solid rgba(255, 255, 255, 0.45);\n border-top-color: #ffffff;\n border-radius: 50%;\n animation: canvas-react-modal-loading 0.8s linear infinite;\n}\n.canvas-react-modal-motion-enter-active,\n.canvas-react-modal-motion-leave-active {\n transition: opacity 0.2s ease;\n}\n.canvas-react-modal-motion-enter-active .canvas-react-modal,\n.canvas-react-modal-motion-leave-active .canvas-react-modal {\n transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n}\n.canvas-react-modal-motion-enter-from,\n.canvas-react-modal-motion-leave-to {\n opacity: 0;\n}\n.canvas-react-modal-motion-enter-from .canvas-react-modal,\n.canvas-react-modal-motion-leave-to .canvas-react-modal {\n opacity: 0;\n transform: scale(0.96) translateY(-8px);\n}\n@keyframes canvas-react-modal-loading {\n to {\n transform: rotate(360deg);\n }\n}\n@media (max-width: 575px) {\n .canvas-react-modal-wrap {\n padding: 24px 12px;\n }\n .canvas-react-modal {\n max-width: calc(100vw - 24px);\n max-height: calc(100vh - 48px);\n }\n .canvas-react-modal-header {\n padding-right: 12px;\n padding-left: 16px;\n }\n .canvas-react-modal-body {\n padding: 16px;\n }\n .canvas-react-modal-footer {\n padding: 12px 16px;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .canvas-react-modal-motion-enter-active,\n .canvas-react-modal-motion-leave-active,\n .canvas-react-modal-motion-enter-active .canvas-react-modal,\n .canvas-react-modal-motion-leave-active .canvas-react-modal {\n transition-duration: 0.01ms;\n }\n}\n');
5333
+ styleInject('@import "../../styles/variables.css";\n.canvas-react-modal-root,\n.canvas-react-modal-root * {\n box-sizing: border-box;\n}\n.canvas-react-modal-root {\n position: fixed;\n inset: 0;\n color: var(--canvas-react-color-text);\n font-size: var(--canvas-react-font-size);\n line-height: 1.5;\n}\n.canvas-react-modal-root-passthrough {\n pointer-events: none;\n}\n.canvas-react-modal-root-passthrough .canvas-react-modal {\n pointer-events: auto;\n}\n.canvas-react-modal-mask {\n position: absolute;\n inset: 0;\n background: rgba(0, 0, 0, 0.45);\n}\n.canvas-react-modal-wrap {\n position: absolute;\n inset: 0;\n overflow: auto;\n padding: 100px 24px 24px;\n pointer-events: none;\n}\n.canvas-react-modal-root-centered .canvas-react-modal-wrap {\n display: flex;\n align-items: center;\n padding-top: 24px;\n}\n.canvas-react-modal-root-fullscreen .canvas-react-modal-wrap {\n padding: 0;\n overflow: hidden;\n}\n.canvas-react-modal {\n position: relative;\n display: flex;\n flex-direction: column;\n width: 520px;\n max-width: calc(100vw - 48px);\n max-height: calc(100vh - 124px);\n margin: 0 auto;\n overflow: hidden;\n border-radius: var(--canvas-react-radius-md, 8px);\n background: var(--canvas-react-color-bg, #ffffff);\n box-shadow: 0 12px 40px rgba(15, 23, 42, 0.22);\n outline: none;\n pointer-events: auto;\n}\n.canvas-react-modal-root-centered .canvas-react-modal {\n max-height: calc(100vh - 48px);\n}\n.canvas-react-modal.canvas-react-modal-fullscreen {\n width: 100% !important;\n max-width: none;\n height: 100%;\n max-height: none;\n border-radius: 0;\n}\n.canvas-react-modal-header {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n min-height: 56px;\n padding: 0 16px 0 24px;\n border-bottom: 1px solid var(--canvas-react-color-border);\n}\n.canvas-react-modal-header-draggable {\n cursor: move;\n user-select: none;\n}\n.canvas-react-modal-title {\n min-width: 0;\n flex: 1 1 auto;\n color: var(--canvas-react-color-text);\n font-size: 16px;\n font-weight: 600;\n overflow-wrap: anywhere;\n}\n.canvas-react-modal-tools {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n gap: 4px;\n margin-left: 16px;\n}\n.canvas-react-modal-tool {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n padding: 0;\n border: 0;\n border-radius: var(--canvas-react-radius-sm, 6px);\n background: transparent;\n color: var(--canvas-react-color-text-muted);\n cursor: pointer;\n transition: background-color 0.18s ease, color 0.18s ease;\n}\n.canvas-react-modal-tool:hover {\n background: var(--canvas-react-color-bg-hover);\n color: var(--canvas-react-color-text);\n}\n.canvas-react-modal-tool:focus-visible,\n.canvas-react-modal-button:focus-visible {\n outline: 2px solid var(--canvas-react-color-primary);\n outline-offset: 2px;\n}\n.canvas-react-modal-fullscreen-icon {\n position: relative;\n display: block;\n width: 15px;\n height: 15px;\n border: 1.5px solid currentColor;\n}\n.canvas-react-modal-fullscreen-icon-exit::before {\n position: absolute;\n inset: 3px -4px -4px 3px;\n border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor;\n content: "";\n}\n.canvas-react-modal-close-icon {\n position: relative;\n display: block;\n width: 16px;\n height: 16px;\n}\n.canvas-react-modal-close-icon::before,\n.canvas-react-modal-close-icon::after {\n position: absolute;\n top: 7px;\n left: 1px;\n width: 14px;\n height: 1.5px;\n border-radius: 1px;\n background: currentColor;\n content: "";\n}\n.canvas-react-modal-close-icon::before {\n transform: rotate(45deg);\n}\n.canvas-react-modal-close-icon::after {\n transform: rotate(-45deg);\n}\n.canvas-react-modal-body {\n min-height: 0;\n flex: 1 1 auto;\n overflow: auto;\n padding: 20px 24px;\n}\n.canvas-react-modal-footer {\n display: flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n padding: 12px 24px;\n border-top: 1px solid var(--canvas-react-color-border);\n}\n.canvas-react-modal-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n height: var(--canvas-react-control-height, 32px);\n min-width: 64px;\n padding: 0 var(--canvas-react-control-padding-inline, 11px);\n border: 1px solid var(--canvas-react-color-border);\n border-radius: var(--canvas-react-radius-sm, 6px);\n background: var(--canvas-react-color-bg);\n color: var(--canvas-react-color-text);\n font: inherit;\n line-height: 1;\n white-space: nowrap;\n cursor: pointer;\n transition:\n border-color 0.18s ease,\n background-color 0.18s ease,\n color 0.18s ease,\n opacity 0.18s ease;\n}\n.canvas-react-modal-button:hover {\n border-color: var(--canvas-react-color-primary-hover);\n color: var(--canvas-react-color-primary-hover);\n}\n.canvas-react-modal-button-primary {\n border-color: var(--canvas-react-color-primary);\n background: var(--canvas-react-color-primary);\n color: #ffffff;\n}\n.canvas-react-modal-button-primary:hover {\n border-color: var(--canvas-react-color-primary-hover);\n background: var(--canvas-react-color-primary-hover);\n color: #ffffff;\n}\n.canvas-react-modal-button:disabled {\n opacity: 0.56;\n cursor: not-allowed;\n}\n.canvas-react-modal-loading {\n width: 14px;\n height: 14px;\n border: 2px solid rgba(255, 255, 255, 0.45);\n border-top-color: #ffffff;\n border-radius: 50%;\n animation: canvas-react-modal-loading 0.8s linear infinite;\n}\n.canvas-react-modal-motion-enter-active,\n.canvas-react-modal-motion-leave-active {\n transition: opacity 0.2s ease;\n}\n.canvas-react-modal-motion-enter-active .canvas-react-modal,\n.canvas-react-modal-motion-leave-active .canvas-react-modal {\n transition: opacity 0.2s ease, transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n}\n.canvas-react-modal-motion-enter-from,\n.canvas-react-modal-motion-leave-to {\n opacity: 0;\n}\n.canvas-react-modal-motion-enter-from .canvas-react-modal,\n.canvas-react-modal-motion-leave-to .canvas-react-modal {\n opacity: 0;\n transform: scale(0.96) translateY(-8px);\n}\n@keyframes canvas-react-modal-loading {\n to {\n transform: rotate(360deg);\n }\n}\n@media (max-width: 575px) {\n .canvas-react-modal-wrap {\n padding: 24px 12px;\n }\n .canvas-react-modal {\n max-width: calc(100vw - 24px);\n max-height: calc(100vh - 48px);\n }\n .canvas-react-modal-header {\n padding-right: 12px;\n padding-left: 16px;\n }\n .canvas-react-modal-body {\n padding: 16px;\n }\n .canvas-react-modal-footer {\n padding: 12px 16px;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .canvas-react-modal-motion-enter-active,\n .canvas-react-modal-motion-leave-active,\n .canvas-react-modal-motion-enter-active .canvas-react-modal,\n .canvas-react-modal-motion-leave-active .canvas-react-modal {\n transition-duration: 0.01ms;\n }\n}\n');
5256
5334
  var MODAL_MOTION_DURATION = 200;
5257
5335
  var FOCUSABLE_SELECTOR = [
5258
5336
  "a[href]",
@@ -5276,6 +5354,7 @@ function ReactModal(props) {
5276
5354
  closable = true,
5277
5355
  confirmLoading,
5278
5356
  defaultFullscreen = false,
5357
+ draggable = false,
5279
5358
  focusTriggerAfterClose = true,
5280
5359
  footer,
5281
5360
  fullscreen,
@@ -5304,7 +5383,9 @@ function ReactModal(props) {
5304
5383
  });
5305
5384
  const [innerFullscreen, setInnerFullscreen] = useState(defaultFullscreen);
5306
5385
  const [innerConfirmLoading, setInnerConfirmLoading] = useState(false);
5386
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
5307
5387
  const panelRef = useRef(null);
5388
+ const dragRef = useRef(null);
5308
5389
  const restoreFocusRef = useRef(null);
5309
5390
  const wasRenderedRef = useRef(false);
5310
5391
  const titleId = useCompatibleId(`${REACT_COMPONENT_PREFIX}-modal-title`);
@@ -5312,6 +5393,32 @@ function ReactModal(props) {
5312
5393
  const mergedFullscreen = controlledFullscreen ? Boolean(fullscreen) : innerFullscreen;
5313
5394
  const mergedConfirmLoading = confirmLoading ?? innerConfirmLoading;
5314
5395
  const hasTitle = title !== null && title !== void 0 && title !== false;
5396
+ useEffect(() => {
5397
+ if (!draggable || !transition.rendered) return;
5398
+ const handleMouseMove = (event) => {
5399
+ const drag = dragRef.current;
5400
+ if (!drag) return;
5401
+ setDragOffset({
5402
+ x: drag.offsetX + event.clientX - drag.startX,
5403
+ y: drag.offsetY + event.clientY - drag.startY
5404
+ });
5405
+ };
5406
+ const handleMouseUp = () => {
5407
+ dragRef.current = null;
5408
+ };
5409
+ window.addEventListener("mousemove", handleMouseMove);
5410
+ window.addEventListener("mouseup", handleMouseUp);
5411
+ return () => {
5412
+ window.removeEventListener("mousemove", handleMouseMove);
5413
+ window.removeEventListener("mouseup", handleMouseUp);
5414
+ };
5415
+ }, [draggable, transition.rendered]);
5416
+ useEffect(() => {
5417
+ if (!open) {
5418
+ dragRef.current = null;
5419
+ setDragOffset({ x: 0, y: 0 });
5420
+ }
5421
+ }, [open]);
5315
5422
  useEffect(() => {
5316
5423
  if (!transition.rendered) {
5317
5424
  if (wasRenderedRef.current) {
@@ -5328,11 +5435,13 @@ function ReactModal(props) {
5328
5435
  wasRenderedRef.current = true;
5329
5436
  restoreFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
5330
5437
  }
5331
- lockBodyScroll();
5332
- return unlockBodyScroll;
5333
- }, [afterClose, focusTriggerAfterClose, transition.rendered]);
5438
+ if (mask) {
5439
+ lockBodyScroll();
5440
+ return unlockBodyScroll;
5441
+ }
5442
+ }, [afterClose, focusTriggerAfterClose, mask, transition.rendered]);
5334
5443
  useEffect(() => {
5335
- if (!open || !transition.rendered) {
5444
+ if (!mask || !open || !transition.rendered) {
5336
5445
  return;
5337
5446
  }
5338
5447
  const frameId = window.requestAnimationFrame(() => {
@@ -5341,9 +5450,9 @@ function ReactModal(props) {
5341
5450
  return () => {
5342
5451
  window.cancelAnimationFrame(frameId);
5343
5452
  };
5344
- }, [open, transition.rendered]);
5453
+ }, [mask, open, transition.rendered]);
5345
5454
  useEffect(() => {
5346
- if (!transition.rendered) {
5455
+ if (!mask || !transition.rendered) {
5347
5456
  return;
5348
5457
  }
5349
5458
  const handleKeyDown = (event) => {
@@ -5361,7 +5470,7 @@ function ReactModal(props) {
5361
5470
  return () => {
5362
5471
  document.removeEventListener("keydown", handleKeyDown);
5363
5472
  };
5364
- }, [keyboard, onCancel, open, transition.rendered]);
5473
+ }, [keyboard, mask, onCancel, open, transition.rendered]);
5365
5474
  const setFullscreen = (nextFullscreen) => {
5366
5475
  if (!controlledFullscreen) {
5367
5476
  setInnerFullscreen(nextFullscreen);
@@ -5446,6 +5555,7 @@ function ReactModal(props) {
5446
5555
  {
5447
5556
  className: joinClassNames(
5448
5557
  `${REACT_COMPONENT_PREFIX}-modal-root`,
5558
+ !mask && `${REACT_COMPONENT_PREFIX}-modal-root-passthrough`,
5449
5559
  centered && `${REACT_COMPONENT_PREFIX}-modal-root-centered`,
5450
5560
  mergedFullscreen && `${REACT_COMPONENT_PREFIX}-modal-root-fullscreen`,
5451
5561
  transition.className,
@@ -5475,63 +5585,95 @@ function ReactModal(props) {
5475
5585
  mergedFullscreen && `${REACT_COMPONENT_PREFIX}-modal-fullscreen`,
5476
5586
  className
5477
5587
  ),
5478
- style: { width, ...style },
5588
+ style: {
5589
+ width,
5590
+ ...style,
5591
+ ...draggable && !mergedFullscreen ? {
5592
+ position: "relative",
5593
+ left: dragOffset.x,
5594
+ top: dragOffset.y
5595
+ } : void 0
5596
+ },
5479
5597
  role: "dialog",
5480
- "aria-modal": "true",
5598
+ "aria-modal": mask ? "true" : void 0,
5481
5599
  "aria-label": hasTitle ? void 0 : "\u5BF9\u8BDD\u6846",
5482
5600
  "aria-labelledby": hasTitle ? titleId : void 0,
5483
5601
  tabIndex: -1,
5484
5602
  children: [
5485
- hasTitle || closable || fullscreenable ? /* @__PURE__ */ jsxs("div", { className: `${REACT_COMPONENT_PREFIX}-modal-header`, children: [
5486
- /* @__PURE__ */ jsx(
5487
- "div",
5488
- {
5489
- id: hasTitle ? titleId : void 0,
5490
- className: `${REACT_COMPONENT_PREFIX}-modal-title`,
5491
- children: title
5492
- }
5493
- ),
5494
- /* @__PURE__ */ jsxs("div", { className: `${REACT_COMPONENT_PREFIX}-modal-tools`, children: [
5495
- fullscreenable ? /* @__PURE__ */ jsx(
5496
- "button",
5497
- {
5498
- type: "button",
5499
- className: `${REACT_COMPONENT_PREFIX}-modal-tool`,
5500
- title: mergedFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
5501
- "aria-label": mergedFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
5502
- "aria-pressed": mergedFullscreen,
5503
- onClick: () => setFullscreen(!mergedFullscreen),
5504
- children: /* @__PURE__ */ jsx(
5505
- "span",
5603
+ hasTitle || closable || fullscreenable ? /* @__PURE__ */ jsxs(
5604
+ "div",
5605
+ {
5606
+ className: joinClassNames(
5607
+ `${REACT_COMPONENT_PREFIX}-modal-header`,
5608
+ draggable && !mergedFullscreen ? `${REACT_COMPONENT_PREFIX}-modal-header-draggable` : void 0
5609
+ ),
5610
+ onMouseDown: (event) => {
5611
+ if (!draggable || mergedFullscreen || event.button !== 0) {
5612
+ return;
5613
+ }
5614
+ if (event.target.closest("button")) {
5615
+ return;
5616
+ }
5617
+ dragRef.current = {
5618
+ startX: event.clientX,
5619
+ startY: event.clientY,
5620
+ offsetX: dragOffset.x,
5621
+ offsetY: dragOffset.y
5622
+ };
5623
+ event.preventDefault();
5624
+ },
5625
+ children: [
5626
+ /* @__PURE__ */ jsx(
5627
+ "div",
5628
+ {
5629
+ id: hasTitle ? titleId : void 0,
5630
+ className: `${REACT_COMPONENT_PREFIX}-modal-title`,
5631
+ children: title
5632
+ }
5633
+ ),
5634
+ /* @__PURE__ */ jsxs("div", { className: `${REACT_COMPONENT_PREFIX}-modal-tools`, children: [
5635
+ fullscreenable ? /* @__PURE__ */ jsx(
5636
+ "button",
5506
5637
  {
5507
- className: joinClassNames(
5508
- `${REACT_COMPONENT_PREFIX}-modal-fullscreen-icon`,
5509
- mergedFullscreen && `${REACT_COMPONENT_PREFIX}-modal-fullscreen-icon-exit`
5510
- ),
5511
- "aria-hidden": "true"
5638
+ type: "button",
5639
+ className: `${REACT_COMPONENT_PREFIX}-modal-tool`,
5640
+ title: mergedFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
5641
+ "aria-label": mergedFullscreen ? "\u9000\u51FA\u5168\u5C4F" : "\u5168\u5C4F",
5642
+ "aria-pressed": mergedFullscreen,
5643
+ onClick: () => setFullscreen(!mergedFullscreen),
5644
+ children: /* @__PURE__ */ jsx(
5645
+ "span",
5646
+ {
5647
+ className: joinClassNames(
5648
+ `${REACT_COMPONENT_PREFIX}-modal-fullscreen-icon`,
5649
+ mergedFullscreen && `${REACT_COMPONENT_PREFIX}-modal-fullscreen-icon-exit`
5650
+ ),
5651
+ "aria-hidden": "true"
5652
+ }
5653
+ )
5512
5654
  }
5513
- )
5514
- }
5515
- ) : null,
5516
- closable ? /* @__PURE__ */ jsx(
5517
- "button",
5518
- {
5519
- type: "button",
5520
- className: `${REACT_COMPONENT_PREFIX}-modal-tool`,
5521
- title: "\u5173\u95ED",
5522
- "aria-label": "\u5173\u95ED",
5523
- onClick: onCancel,
5524
- children: /* @__PURE__ */ jsx(
5525
- "span",
5655
+ ) : null,
5656
+ closable ? /* @__PURE__ */ jsx(
5657
+ "button",
5526
5658
  {
5527
- className: `${REACT_COMPONENT_PREFIX}-modal-close-icon`,
5528
- "aria-hidden": "true"
5659
+ type: "button",
5660
+ className: `${REACT_COMPONENT_PREFIX}-modal-tool`,
5661
+ title: "\u5173\u95ED",
5662
+ "aria-label": "\u5173\u95ED",
5663
+ onClick: onCancel,
5664
+ children: /* @__PURE__ */ jsx(
5665
+ "span",
5666
+ {
5667
+ className: `${REACT_COMPONENT_PREFIX}-modal-close-icon`,
5668
+ "aria-hidden": "true"
5669
+ }
5670
+ )
5529
5671
  }
5530
- )
5531
- }
5532
- ) : null
5533
- ] })
5534
- ] }) : null,
5672
+ ) : null
5673
+ ] })
5674
+ ]
5675
+ }
5676
+ ) : null,
5535
5677
  /* @__PURE__ */ jsx(
5536
5678
  "div",
5537
5679
  {