weifuwu 0.40.0 → 0.50.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.
@@ -96,7 +96,7 @@ var Textarea = (_init, _ctx) => (props) => {
96
96
  };
97
97
 
98
98
  // src/components/Select/Select.ts
99
- var Select = (_init, _ctx) => (props) => {
99
+ var SelectNative = (_init, _ctx) => (props) => {
100
100
  const { label, value, options, placeholder, required, disabled, error, onChange, children } = props;
101
101
  const optionEls = [];
102
102
  if (placeholder) {
@@ -112,7 +112,7 @@ var Select = (_init, _ctx) => (props) => {
112
112
  value: value ?? "",
113
113
  required: required || void 0,
114
114
  disabled: disabled || void 0,
115
- onChange
115
+ onChange: (e) => onChange?.(e.target.value)
116
116
  }, children ?? optionEls);
117
117
  const wrapChildren = [];
118
118
  if (label) {
@@ -124,6 +124,82 @@ var Select = (_init, _ctx) => (props) => {
124
124
  if (error) wrapChildren.push(h("div", { class: "wf-select-err" }, error));
125
125
  return h("div", { class: `wf-select-wrap${error ? " wf-select--err" : ""}` }, wrapChildren);
126
126
  };
127
+ var SelectSearchable = (_init, ctx) => {
128
+ const $ = ctx.ui.$();
129
+ $.open = false;
130
+ $.keyword = "";
131
+ $.filteredOptions = [];
132
+ return (props) => {
133
+ const { label, value, options = [], placeholder, required, disabled, error, onChange, onSearch } = props;
134
+ const filtered = $.keyword ? options.filter((o) => o.label.toLowerCase().includes($.keyword.toLowerCase())) : options;
135
+ const selectedOption = options.find((o) => o.value === value);
136
+ const handleInput = async (keyword) => {
137
+ $.keyword = keyword;
138
+ $.open = true;
139
+ if (onSearch && keyword) {
140
+ const result = await onSearch(keyword);
141
+ if (result) $.filteredOptions = result;
142
+ }
143
+ };
144
+ const handleSelect = (opt) => {
145
+ if (opt.disabled) return;
146
+ $.keyword = "";
147
+ $.open = false;
148
+ onChange?.(opt.value);
149
+ };
150
+ const trigger = h("div", {
151
+ class: `wf-select-search-trigger${disabled ? " wf-select-search--disabled" : ""}${error ? " wf-select--err" : ""}`,
152
+ onClick: disabled ? void 0 : () => {
153
+ $.open = !$.open;
154
+ }
155
+ }, [
156
+ h("input", {
157
+ class: "wf-select-search-input",
158
+ type: "text",
159
+ value: $.open ? $.keyword : selectedOption?.label ?? "",
160
+ placeholder: selectedOption ? void 0 : placeholder ?? "",
161
+ disabled,
162
+ readOnly: !$.open || void 0,
163
+ onInput: (e) => handleInput(e.target.value),
164
+ onFocus: () => {
165
+ if (!disabled) $.open = true;
166
+ },
167
+ onBlur: () => {
168
+ setTimeout(() => {
169
+ $.open = false;
170
+ $.keyword = "";
171
+ }, 150);
172
+ }
173
+ })
174
+ ]);
175
+ const wrapChildren = [];
176
+ if (label) {
177
+ const labelContent = [label];
178
+ if (required) labelContent.push(h("span", { class: "wf-select-req" }, "*"));
179
+ wrapChildren.push(h("label", { class: "wf-select-label" }, labelContent));
180
+ }
181
+ const displayOptions = $.keyword && onSearch && $.filteredOptions.length > 0 ? $.filteredOptions : filtered;
182
+ const menuChildren = displayOptions.length > 0 ? displayOptions.map(
183
+ (opt) => h("div", {
184
+ class: `wf-select-search-opt${opt.value === value ? " wf-select-search-opt--sel" : ""}${opt.disabled ? " wf-select-search-opt--dis" : ""}`,
185
+ key: opt.value,
186
+ onMouseDown: (e) => {
187
+ e.preventDefault();
188
+ handleSelect(opt);
189
+ }
190
+ }, opt.label)
191
+ ) : $.keyword ? [h("div", { class: "wf-select-search-empty" }, "\u65E0\u5339\u914D")] : [];
192
+ const menu = $.open ? h("div", { class: "wf-select-search-menu" }, menuChildren) : null;
193
+ wrapChildren.push(h("div", { class: "wf-select-search" }, [trigger, menu].filter(Boolean)));
194
+ if (error) wrapChildren.push(h("div", { class: "wf-select-err" }, error));
195
+ return h("div", { class: `wf-select-wrap${error ? " wf-select--err" : ""}` }, wrapChildren);
196
+ };
197
+ };
198
+ var Select = (_init, ctx) => {
199
+ const nativeRender = SelectNative(_init, ctx);
200
+ const searchableRender = SelectSearchable(_init, ctx);
201
+ return (props) => props.searchable ? searchableRender(props) : nativeRender(props);
202
+ };
127
203
 
128
204
  // src/components/Checkbox/Checkbox.ts
129
205
  var Checkbox = (_init, _ctx) => (props) => {
@@ -192,31 +268,62 @@ var RadioGroup = (_init, _ctx) => (props) => {
192
268
  };
193
269
 
194
270
  // src/components/Table/Table.ts
271
+ function sortData(data, columns, sortKey, sortOrder) {
272
+ if (!sortKey || !sortOrder) return data;
273
+ const col = columns.find((c) => c.key === sortKey);
274
+ if (!col || !col.sortable) return data;
275
+ const sorted = [...data];
276
+ sorted.sort((a, b) => {
277
+ const va = a[sortKey];
278
+ const vb = b[sortKey];
279
+ const result = col.sorter ? col.sorter(va, vb) : String(va ?? "").localeCompare(String(vb ?? ""));
280
+ return sortOrder === "desc" ? -result : result;
281
+ });
282
+ return sorted;
283
+ }
195
284
  var Table = (_init, _ctx) => (props) => {
196
- const { data = [], columns, onRowClick } = props;
197
- const headerCells = columns.map(
198
- (col) => h("th", {
199
- class: "wf-table-th",
285
+ const { data = [], columns, onRowClick, sortKey, sortOrder, onSort, emptyText } = props;
286
+ const sortedData = sortData(data, columns, sortKey, sortOrder);
287
+ const headerCells = columns.map((col) => {
288
+ const isSorted = col.key === sortKey && sortOrder;
289
+ const isAsc = sortOrder === "asc";
290
+ let label = col.label;
291
+ if (col.sortable) {
292
+ label += isSorted ? isAsc ? " \u25B2" : " \u25BC" : " \u21C5";
293
+ }
294
+ return h("th", {
295
+ class: `wf-table-th${col.sortable ? " wf-table-th--sortable" : ""}${isSorted ? " wf-table-th--sorted" : ""}`,
200
296
  scope: "col",
201
- style: col.width ? { width: col.width } : void 0
202
- }, col.label)
203
- );
297
+ style: col.width ? { width: col.width } : void 0,
298
+ onClick: col.sortable && onSort ? () => onSort(col.key, isSorted && isAsc ? "desc" : "asc") : void 0
299
+ }, label);
300
+ });
204
301
  const headerRow = h("tr", { class: "wf-table-tr" }, headerCells);
205
302
  const thead = h("thead", { class: "wf-table-thead" }, headerRow);
206
- const bodyRows = data.map((row, i) => {
207
- const cells = columns.map((col) => {
208
- const val = row[col.key];
209
- const content = col.render ? col.render(val, row, i) : String(val ?? "");
210
- return h("td", { class: "wf-table-td" }, content);
303
+ let bodyRows;
304
+ if (sortedData.length === 0 && emptyText) {
305
+ const colspan = columns.length;
306
+ bodyRows = [
307
+ h(
308
+ "tr",
309
+ { class: "wf-table-tr" },
310
+ h("td", { class: "wf-table-td wf-table-empty", colspan, style: { textAlign: "center" } }, emptyText)
311
+ )
312
+ ];
313
+ } else {
314
+ bodyRows = sortedData.map((row, i) => {
315
+ const cells = columns.map((col) => {
316
+ const val = row[col.key];
317
+ const content = col.render ? col.render(val, row, i) : String(val ?? "");
318
+ return h("td", { class: "wf-table-td" }, content);
319
+ });
320
+ return h("tr", {
321
+ class: "wf-table-tr",
322
+ onClick: onRowClick ? () => onRowClick(row, i) : void 0,
323
+ style: onRowClick ? { cursor: "pointer" } : void 0
324
+ }, cells);
211
325
  });
212
- return h("tr", {
213
- class: "wf-table-tr",
214
- onClick: onRowClick ? (e) => {
215
- onRowClick(row, i);
216
- } : void 0,
217
- style: onRowClick ? { cursor: "pointer" } : void 0
218
- }, cells);
219
- });
326
+ }
220
327
  const tbody = h("tbody", { class: "wf-table-tbody" }, bodyRows);
221
328
  return h("table", { class: "wf-table" }, [thead, tbody]);
222
329
  };
@@ -291,16 +398,19 @@ function trapFocus(container) {
291
398
  // src/components/Modal/Modal.ts
292
399
  var Modal = (_props, ctx) => {
293
400
  let prevOpen;
294
- ctx.ui.onmounted((el) => {
295
- lockScroll();
296
- const cleanupFocus = trapFocus(el);
297
- return () => {
401
+ let focusCleanup;
402
+ const rootRef = (el) => {
403
+ if (el) {
404
+ lockScroll();
405
+ const modalEl = el.querySelector(".wf-modal") ?? el;
406
+ focusCleanup = trapFocus(modalEl);
407
+ } else {
298
408
  unlockScroll();
299
- cleanupFocus();
300
- };
301
- });
409
+ focusCleanup?.();
410
+ }
411
+ };
302
412
  return (props) => {
303
- const { open, title, onClose, children, footer } = props;
413
+ const { open, title, onClose, children, footer, width, closable = true } = props;
304
414
  const ML = ctx?.i18n?.components?.Modal ?? {};
305
415
  if (prevOpen !== open) {
306
416
  prevOpen = open;
@@ -310,19 +420,21 @@ var Modal = (_props, ctx) => {
310
420
  class: "wf-modal-overlay",
311
421
  onClick: onClose
312
422
  });
313
- const closeBtn = h("button", {
423
+ const closeBtn = closable ? h("button", {
314
424
  class: "wf-modal-close",
315
425
  onClick: onClose,
316
426
  type: "button"
317
- }, "\u2715");
318
- const titleEl = title ? h("div", { class: "wf-modal-header" }, [title, closeBtn]) : null;
427
+ }, "\u2715") : null;
428
+ const titleEl = title ? h("div", { class: "wf-modal-header" }, [title, closeBtn].filter(Boolean)) : null;
319
429
  const bodyEl = h("div", { class: "wf-modal-body" }, children);
320
430
  const footerEl = footer ? h("div", { class: "wf-modal-footer" }, footer) : null;
321
431
  const content = h("div", {
322
432
  class: "wf-modal-content",
323
- onClick: (e) => e.stopPropagation()
433
+ onClick: (e) => e.stopPropagation(),
434
+ style: width ? { minWidth: width, maxWidth: width } : void 0
324
435
  }, [titleEl, bodyEl, footerEl].filter(Boolean));
325
436
  const root = h("div", {
437
+ ref: rootRef,
326
438
  class: "wf-modal wf-modal--enter",
327
439
  role: "dialog",
328
440
  "aria-modal": "true",
@@ -333,13 +445,25 @@ var Modal = (_props, ctx) => {
333
445
  };
334
446
 
335
447
  // src/components/Toast/Toast.ts
448
+ function positionClass(pos) {
449
+ const map = {
450
+ "top-right": "wf-toast--tr",
451
+ "top-left": "wf-toast--tl",
452
+ "bottom-right": "wf-toast--br",
453
+ "bottom-left": "wf-toast--bl",
454
+ "top-center": "wf-toast--tc"
455
+ };
456
+ return map[pos] ?? "wf-toast--tr";
457
+ }
336
458
  var Toast = (_init, ctx) => (props) => {
337
- const { toasts = [], onRemove } = props;
338
- if (toasts.length === 0) return null;
339
- const items = toasts.map(
459
+ const { toasts = [], onRemove, position = "top-right", duration = 0, max = 0 } = props;
460
+ const visible = max > 0 && toasts.length > max ? toasts.slice(-max) : toasts;
461
+ if (visible.length === 0) return null;
462
+ const items = visible.map(
340
463
  (t) => h("div", {
341
464
  class: `wf-toast wf-toast--${t.type}`,
342
465
  key: t.id,
466
+ "data-duration": (t.duration ?? duration) || void 0,
343
467
  onClick: onRemove ? () => onRemove(t.id) : void 0
344
468
  }, [
345
469
  h("span", { class: "wf-toast-icon" }, iconFor(t.type)),
@@ -347,7 +471,10 @@ var Toast = (_init, ctx) => (props) => {
347
471
  ])
348
472
  );
349
473
  return createPortal(
350
- h("div", { class: "wf-toast-container" }, items),
474
+ h("div", {
475
+ class: `wf-toast-container ${positionClass(position)}`,
476
+ "data-max": max || void 0
477
+ }, items),
351
478
  "toast"
352
479
  );
353
480
  };
@@ -431,10 +558,7 @@ var Tabs = (_init, _ctx) => (props) => {
431
558
 
432
559
  // src/components/Dropdown/Dropdown.ts
433
560
  var Dropdown = (_init, ctx) => {
434
- let wrapEl;
435
- ctx.ui.onmounted((el) => {
436
- wrapEl = el;
437
- });
561
+ let wrapEl = null;
438
562
  return (props) => {
439
563
  const { trigger, items = [], open } = props;
440
564
  const pos = (() => {
@@ -459,7 +583,10 @@ var Dropdown = (_init, ctx) => {
459
583
  }, menuItems) : null;
460
584
  const portalContent = open ? createPortal(menu, "dropdown") : null;
461
585
  return h("div", {
462
- class: `wf-dropdown${open ? " wf-dropdown--open" : ""}`
586
+ class: `wf-dropdown${open ? " wf-dropdown--open" : ""}`,
587
+ ref: (el) => {
588
+ wrapEl = el;
589
+ }
463
590
  }, [trigger, portalContent].filter(Boolean));
464
591
  };
465
592
  };
@@ -607,15 +734,67 @@ var Steps = (_init, _ctx) => (props) => {
607
734
  };
608
735
 
609
736
  // src/components/Form/Form.ts
737
+ async function validateValues(values, rules) {
738
+ const errors = {};
739
+ for (const [field, fieldRules] of Object.entries(rules)) {
740
+ const val = values[field] ?? "";
741
+ for (const rule of fieldRules) {
742
+ if (rule.required && !String(val).trim()) {
743
+ errors[field] = rule.message;
744
+ break;
745
+ }
746
+ if (rule.pattern && !rule.pattern.test(String(val))) {
747
+ errors[field] = rule.message;
748
+ break;
749
+ }
750
+ if (rule.minLength != null && String(val).length < rule.minLength) {
751
+ errors[field] = rule.message;
752
+ break;
753
+ }
754
+ if (rule.maxLength != null && String(val).length > rule.maxLength) {
755
+ errors[field] = rule.message;
756
+ break;
757
+ }
758
+ if (rule.validator) {
759
+ const result = await rule.validator(val);
760
+ if (result !== true) {
761
+ errors[field] = typeof result === "string" ? result : rule.message;
762
+ break;
763
+ }
764
+ }
765
+ }
766
+ }
767
+ return errors;
768
+ }
610
769
  var Form = (_init, _ctx) => (props) => {
611
- const { onSubmit, children } = props;
612
- const handleSubmit = (e) => {
770
+ const { validation, onSubmit, onError, children } = props;
771
+ const handleSubmit = async (e) => {
613
772
  e.preventDefault();
614
- onSubmit?.();
773
+ const form = e.target;
774
+ const data = new FormData(form);
775
+ const values = {};
776
+ for (const [key, val] of data.entries()) {
777
+ if (key in values) {
778
+ const prev = values[key];
779
+ values[key] = Array.isArray(prev) ? [...prev, val] : [prev, val];
780
+ } else {
781
+ values[key] = val;
782
+ }
783
+ }
784
+ if (validation) {
785
+ const errors = await validateValues(values, validation);
786
+ if (Object.keys(errors).length > 0) {
787
+ onError?.(errors);
788
+ return;
789
+ }
790
+ onError?.({});
791
+ }
792
+ await onSubmit?.(values);
615
793
  };
616
794
  return h("form", {
617
795
  class: "wf-form",
618
- onSubmit: handleSubmit
796
+ onSubmit: handleSubmit,
797
+ noValidate: validation ? true : void 0
619
798
  }, children);
620
799
  };
621
800
 
@@ -807,6 +986,7 @@ var FileUpload = (_init, ctx) => {
807
986
  const updated = files.filter((_, idx) => idx !== i);
808
987
  onChange?.(updated);
809
988
  };
989
+ let fileInput = null;
810
990
  const inputEl = h("input", {
811
991
  type: "file",
812
992
  class: "wf-upload-input",
@@ -814,14 +994,14 @@ var FileUpload = (_init, ctx) => {
814
994
  multiple: multiple || void 0,
815
995
  disabled: disabled || void 0,
816
996
  onChange: handleChange,
817
- style: { display: "none" }
997
+ style: { display: "none" },
998
+ ref: (el) => {
999
+ fileInput = el;
1000
+ }
818
1001
  });
819
1002
  const dropZone = h("div", {
820
1003
  class: `wf-upload-zone${disabled ? " wf-upload-zone--disabled" : ""}${error ? " wf-upload-zone--err" : ""}`,
821
- onClick: disabled ? void 0 : () => {
822
- const input = document.querySelector(".wf-upload-input");
823
- input?.click();
824
- },
1004
+ onClick: disabled ? void 0 : () => fileInput?.click(),
825
1005
  ref: dragRef
826
1006
  }, children ?? h("div", { class: "wf-upload-placeholder" }, [
827
1007
  h("span", { class: "wf-upload-icon" }, "\u{1F4C1}"),
@@ -917,14 +1097,17 @@ var Tooltip = (_props, ctx) => {
917
1097
  // src/components/Drawer/Drawer.ts
918
1098
  var Drawer = (_props, ctx) => {
919
1099
  let prevOpen;
920
- ctx.ui.onmounted((el) => {
921
- lockScroll();
922
- const cleanupFocus = trapFocus(el);
923
- return () => {
1100
+ let focusCleanup;
1101
+ const rootRef = (el) => {
1102
+ if (el) {
1103
+ lockScroll();
1104
+ const panelEl = el.querySelector(".wf-drawer") ?? el;
1105
+ focusCleanup = trapFocus(panelEl);
1106
+ } else {
924
1107
  unlockScroll();
925
- cleanupFocus();
926
- };
927
- });
1108
+ focusCleanup?.();
1109
+ }
1110
+ };
928
1111
  return (props) => {
929
1112
  const { open, title, position = "right", onClose, children, footer } = props;
930
1113
  const DL = ctx?.i18n?.components?.Drawer ?? {};
@@ -950,10 +1133,15 @@ var Drawer = (_props, ctx) => {
950
1133
  onClick: (e) => e.stopPropagation()
951
1134
  }, [titleEl, bodyEl, footerEl].filter(Boolean));
952
1135
  const root = h("div", {
1136
+ ref: rootRef,
953
1137
  class: `wf-drawer wf-drawer--${position} wf-drawer--enter`,
954
1138
  role: "dialog",
955
1139
  "aria-modal": "true",
956
- "aria-label": title ?? (DL.ariaLabel ?? "\u4FA7\u8FB9\u9762\u677F")
1140
+ "aria-label": title ?? (DL.ariaLabel ?? "\u4FA7\u8FB9\u9762\u677F"),
1141
+ tabIndex: -1,
1142
+ onKeyDown: (e) => {
1143
+ if (e.key === "Escape") onClose?.();
1144
+ }
957
1145
  }, [overlay, panel]);
958
1146
  return createPortal(root, "drawer");
959
1147
  };
@@ -975,7 +1163,6 @@ var Popover = (_props, ctx) => {
975
1163
  };
976
1164
  const updatePos = (e) => {
977
1165
  pos = computeFixedPos(e.currentTarget, position, 6, true);
978
- ctx.ui.render();
979
1166
  };
980
1167
  const onClick = trigger === "click" && !disabled ? (e) => {
981
1168
  updatePos(e);
@@ -1003,7 +1190,7 @@ var Popover = (_props, ctx) => {
1003
1190
  }
1004
1191
  }) : null;
1005
1192
  const popover = isOpen ? h("div", {
1006
- class: "wf-popover wf-popover--enter",
1193
+ class: `wf-popover wf-popover--${position} wf-popover--enter`,
1007
1194
  style: { top: p.top, left: p.left },
1008
1195
  role: "tooltip"
1009
1196
  }, [
@@ -1030,6 +1217,20 @@ var Skeleton = (_init) => (props) => {
1030
1217
  `wf-skeleton--${variant}`,
1031
1218
  className
1032
1219
  ].filter(Boolean).join(" ");
1220
+ if (variant === "table") {
1221
+ const cols = props.cols ?? 3;
1222
+ const rows = Array.from({ length: lines }, (_, r) => {
1223
+ const cells = Array.from({ length: cols }, (_2, c) => {
1224
+ const cellW = r === lines - 1 && c === cols - 1 ? "40%" : void 0;
1225
+ return h("div", {
1226
+ class: "wf-skeleton wf-skeleton--text",
1227
+ style: cellW ? { width: cellW } : void 0
1228
+ });
1229
+ });
1230
+ return h("div", { class: "wf-skeleton-row" }, cells);
1231
+ });
1232
+ return h("div", { class: "wf-skeleton-table" }, rows);
1233
+ }
1033
1234
  if (lines <= 1) {
1034
1235
  return h("div", { class: cls, style: Object.keys(style).length ? style : void 0 });
1035
1236
  }
@@ -1066,20 +1267,19 @@ var InView = (_props, ctx) => {
1066
1267
  let inView = false;
1067
1268
  let entered = false;
1068
1269
  let io;
1069
- ctx.ui.onmounted((el) => {
1070
- const sentinel = el.querySelector(".wf-inview-pending");
1071
- if (!sentinel) return;
1072
- io = new IntersectionObserver((entries) => {
1073
- if (entries[0]?.isIntersecting) {
1074
- inView = true;
1075
- ctx.ui.render();
1076
- }
1077
- }, { threshold: _props.threshold ?? 0, rootMargin: _props.rootMargin ?? "0px" });
1078
- io.observe(sentinel);
1079
- return () => {
1270
+ const sentinelRef = (el) => {
1271
+ if (el) {
1272
+ io = new IntersectionObserver((entries) => {
1273
+ if (entries[0]?.isIntersecting) {
1274
+ inView = true;
1275
+ ctx.ui.render();
1276
+ }
1277
+ }, { threshold: _props.threshold ?? 0, rootMargin: _props.rootMargin ?? "0px" });
1278
+ io.observe(el);
1279
+ } else {
1080
1280
  io?.disconnect();
1081
- };
1082
- });
1281
+ }
1282
+ };
1083
1283
  return (props) => {
1084
1284
  if (inView) {
1085
1285
  if (!entered) {
@@ -1094,7 +1294,11 @@ var InView = (_props, ctx) => {
1094
1294
  return h("div", {
1095
1295
  class: "wf-inview wf-inview--pending"
1096
1296
  }, [
1097
- h("div", { class: "wf-inview-pending", style: { width: "1px", height: "1px", opacity: 0, pointerEvents: "none" } }),
1297
+ h("div", {
1298
+ class: "wf-inview-pending",
1299
+ style: { width: "1px", height: "1px", opacity: 0, pointerEvents: "none" },
1300
+ ref: sentinelRef
1301
+ }),
1098
1302
  placeholderEl
1099
1303
  ]);
1100
1304
  };
@@ -1192,10 +1396,7 @@ var DatePicker = (_props, ctx) => {
1192
1396
  let selDay = now.getDate();
1193
1397
  let rangeStart = null;
1194
1398
  let rangeEnd = null;
1195
- let inputEl;
1196
- ctx.ui.onmounted((el) => {
1197
- inputEl = el.querySelector(".wf-datepicker-input");
1198
- });
1399
+ let inputEl = null;
1199
1400
  return (props) => {
1200
1401
  const { mode = "date", value, onChange, placeholder = L.placeholder ?? "\u9009\u62E9\u65E5\u671F", disabled } = props;
1201
1402
  const isOpen = show;
@@ -1455,6 +1656,9 @@ var DatePicker = (_props, ctx) => {
1455
1656
  type: "text",
1456
1657
  placeholder,
1457
1658
  value: displayValue || "",
1659
+ ref: (el) => {
1660
+ inputEl = el;
1661
+ },
1458
1662
  readonly: true,
1459
1663
  disabled,
1460
1664
  onClick: toggle
@@ -2011,7 +2215,21 @@ var Editor = (_props, ctx) => {
2011
2215
  let tableHoverRow = -1;
2012
2216
  let tableHoverCol = -1;
2013
2217
  let sourceText = "";
2014
- let editorEl;
2218
+ let editorEl = null;
2219
+ let savedRange = null;
2220
+ const saveSelection = () => {
2221
+ const sel = window.getSelection();
2222
+ if (sel && sel.rangeCount > 0) savedRange = sel.getRangeAt(0);
2223
+ };
2224
+ const restoreSelection = () => {
2225
+ if (!editorEl || !savedRange) return;
2226
+ editorEl.focus();
2227
+ const sel = window.getSelection();
2228
+ if (sel) {
2229
+ sel.removeAllRanges();
2230
+ sel.addRange(savedRange);
2231
+ }
2232
+ };
2015
2233
  return (props) => {
2016
2234
  const { value = "", onChange, onUpload, placeholder = "", disabled = false, minHeight = "200px" } = props;
2017
2235
  const toolbarItems = props.toolbar ?? DEFAULT_TOOLBAR;
@@ -2042,6 +2260,7 @@ var Editor = (_props, ctx) => {
2042
2260
  return;
2043
2261
  }
2044
2262
  if (item === "link") {
2263
+ saveSelection();
2045
2264
  const sel = window.getSelection();
2046
2265
  if (!sel || sel.isCollapsed) {
2047
2266
  showLinkInput = true;
@@ -2070,7 +2289,7 @@ var Editor = (_props, ctx) => {
2070
2289
  showLinkInput = false;
2071
2290
  ctx.ui.render();
2072
2291
  if (!url) return;
2073
- if (editorEl) editorEl.focus();
2292
+ editorEl?.focus();
2074
2293
  exec("createLink", url);
2075
2294
  activeFormats = queryFormats();
2076
2295
  if (editorEl && onChange) emitChange(editorEl.innerHTML);
@@ -2107,7 +2326,7 @@ var Editor = (_props, ctx) => {
2107
2326
  showImageInput = false;
2108
2327
  imageUrl = "";
2109
2328
  ctx.ui.render();
2110
- if (editorEl) editorEl.focus();
2329
+ editorEl?.focus();
2111
2330
  insertImageFn(url);
2112
2331
  };
2113
2332
  const cancelImage = () => {
@@ -2120,7 +2339,7 @@ var Editor = (_props, ctx) => {
2120
2339
  tableHoverRow = -1;
2121
2340
  tableHoverCol = -1;
2122
2341
  ctx.ui.render();
2123
- if (editorEl) editorEl.focus();
2342
+ restoreSelection();
2124
2343
  insertTable(rows, cols);
2125
2344
  if (editorEl && onChange) emitChange(editorEl.innerHTML);
2126
2345
  };
@@ -2166,14 +2385,9 @@ var Editor = (_props, ctx) => {
2166
2385
  const val = e.target.value;
2167
2386
  onChange?.(val);
2168
2387
  };
2169
- const editorRef = (el) => {
2170
- if (el) editorEl = el;
2171
- return () => {
2172
- editorEl = void 0;
2173
- };
2174
- };
2175
2388
  const handleKeyUp = (e) => {
2176
2389
  if (disabled || !isRichMode) return;
2390
+ saveSelection();
2177
2391
  const isFormatShortcut = (e.ctrlKey || e.metaKey) && ["b", "i", "u"].includes(e.key.toLowerCase());
2178
2392
  if (isFormatShortcut) {
2179
2393
  activeFormats = queryFormats();
@@ -2183,6 +2397,7 @@ var Editor = (_props, ctx) => {
2183
2397
  };
2184
2398
  const handleMouseUp = () => {
2185
2399
  if (!isRichMode) return;
2400
+ saveSelection();
2186
2401
  activeFormats = queryFormats();
2187
2402
  ctx.ui.render();
2188
2403
  };
@@ -2209,10 +2424,7 @@ var Editor = (_props, ctx) => {
2209
2424
  h("button", {
2210
2425
  class: "wf-btn wf-btn--primary wf-btn--sm",
2211
2426
  type: "button",
2212
- onClick: () => {
2213
- const input = document.querySelector("[data-editor-link-input]");
2214
- confirmLink(input?.value ?? "");
2215
- }
2427
+ onClick: () => confirmLink(linkUrl)
2216
2428
  }, "\u786E\u5B9A")
2217
2429
  ]
2218
2430
  },
@@ -2273,10 +2485,7 @@ var Editor = (_props, ctx) => {
2273
2485
  class: "wf-btn wf-btn--primary wf-btn--sm",
2274
2486
  type: "button",
2275
2487
  disabled: imageUploading || void 0,
2276
- onClick: () => {
2277
- const input = document.querySelector("[data-image-input]");
2278
- confirmImageUrl(input?.value ?? "");
2279
- }
2488
+ onClick: () => confirmImageUrl(imageUrl)
2280
2489
  }, onUpload ? "\u63D2\u5165" : "\u786E\u5B9A")
2281
2490
  ]
2282
2491
  },
@@ -2299,7 +2508,9 @@ var Editor = (_props, ctx) => {
2299
2508
  onKeyUp: handleKeyUp,
2300
2509
  onMouseUp: handleMouseUp,
2301
2510
  onMouseDown: handleMouseDown,
2302
- ref: editorRef
2511
+ ref: (el) => {
2512
+ editorEl = el;
2513
+ }
2303
2514
  });
2304
2515
  } else {
2305
2516
  editorBody = h("textarea", {
@@ -2317,9 +2528,9 @@ var Editor = (_props, ctx) => {
2317
2528
  class: `wf-editor${disabled ? " wf-editor--disabled" : ""}`
2318
2529
  }, [
2319
2530
  !disabled && toolbarItems.length > 0 ? renderToolbar(toolbarItems, activeFormats, !isRichMode, handleToolbarItem, customRender) : null,
2531
+ editorBody,
2320
2532
  linkModal,
2321
2533
  imageModal,
2322
- editorBody,
2323
2534
  h("input", { type: "hidden", value, "aria-hidden": "true" })
2324
2535
  ]);
2325
2536
  };