@opentiny/vue-renderless 3.28.2 → 3.30.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.
Files changed (64) hide show
  1. package/alert/vue.js +4 -1
  2. package/anchor/index.js +11 -1
  3. package/anchor/vue.js +2 -2
  4. package/base-select/index.js +6 -3
  5. package/base-select/vue.js +1 -1
  6. package/calendar-view/index.js +44 -2
  7. package/calendar-view/vue.js +6 -0
  8. package/date-range/index.js +1 -1
  9. package/dialog-box/vue.js +4 -1
  10. package/dialog-select/index.js +1 -1
  11. package/drawer/index.js +13 -1
  12. package/drawer/vue.js +13 -3
  13. package/fluent-editor/index.js +142 -10
  14. package/fluent-editor/vue.js +30 -8
  15. package/form-item/index.js +1 -1
  16. package/form-item/vue.js +11 -1
  17. package/grid-select/index.js +3 -3
  18. package/guide/index.js +17 -11
  19. package/guide/vue.js +2 -1
  20. package/input/vue.js +3 -2
  21. package/modal/index.js +11 -11
  22. package/modal/vue.js +3 -0
  23. package/notify/vue.js +4 -1
  24. package/package.json +3 -3
  25. package/picker/vue.js +3 -2
  26. package/qr-code/vue.js +3 -1
  27. package/rate/index.js +5 -4
  28. package/rate/vue.js +1 -1
  29. package/rich-text/index.js +91 -2
  30. package/rich-text/vue.js +11 -2
  31. package/search/vue.js +6 -1
  32. package/select/index.js +11 -3
  33. package/select/vue.js +5 -4
  34. package/statistic/index.js +48 -1
  35. package/statistic/vue.js +31 -11
  36. package/tabs-mf/index.js +10 -2
  37. package/tag-group/index.js +3 -0
  38. package/tag-input/index.js +91 -0
  39. package/tag-input/vue.js +72 -0
  40. package/time/index.js +22 -0
  41. package/time/vue.js +14 -2
  42. package/time-spinner/index.js +2 -2
  43. package/tree-select/index.js +49 -14
  44. package/types/action-menu.type.d.ts +4 -0
  45. package/types/alert.type.d.ts +2 -0
  46. package/types/anchor.type.d.ts +1 -1
  47. package/types/button-group.type.d.ts +5 -0
  48. package/types/date-picker.type.d.ts +1 -1
  49. package/types/dialog-box.type.d.ts +2 -0
  50. package/types/drawer.type.d.ts +7 -1
  51. package/types/{dropdown-item.type-b3ced3ce.d.ts → dropdown-item.type-f83b014f.d.ts} +1 -1
  52. package/types/dropdown-item.type.d.ts +1 -1
  53. package/types/dropdown-menu.type.d.ts +1 -1
  54. package/types/form-item.type.d.ts +1 -1
  55. package/types/{form.type-a54e1c06.d.ts → form.type-e0db2f7c.d.ts} +9 -0
  56. package/types/form.type.d.ts +1 -1
  57. package/types/modal.type.d.ts +2 -0
  58. package/types/popeditor.type.d.ts +2 -2
  59. package/types/rate.type.d.ts +5 -2
  60. package/types/search.type.d.ts +1 -0
  61. package/types/statistic.type.d.ts +17 -1
  62. package/types/tag-input.type.d.ts +113 -0
  63. package/types/tree-menu.type.d.ts +1 -1
  64. package/user/index.js +5 -5
package/form-item/vue.js CHANGED
@@ -32,9 +32,11 @@ import {
32
32
  handleMouseenter,
33
33
  handleMouseleave
34
34
  } from "./index";
35
+ import { nanoid } from "@opentiny/utils";
35
36
  const api = [
36
37
  "state",
37
38
  "validate",
39
+ "validateOrigin",
38
40
  "clearValidate",
39
41
  "resetField",
40
42
  "getRules",
@@ -58,6 +60,9 @@ const initState = ({
58
60
  inject,
59
61
  props
60
62
  }) => {
63
+ const uniqueId = nanoid.api.nanoid(8);
64
+ const errorId = `tiny-form-item-error-${uniqueId}`;
65
+ const labelId = `tiny-form-item-label-${uniqueId}`;
61
66
  const state = reactive({
62
67
  mode,
63
68
  validateState: "",
@@ -77,6 +82,9 @@ const initState = ({
77
82
  showTooltip: false,
78
83
  typeName: "",
79
84
  formInstance: inject("form"),
85
+ // 无障碍支持:为错误信息和标签生成唯一 ID
86
+ errorId,
87
+ labelId,
80
88
  labelFor: computed(() => props.for || props.prop || ""),
81
89
  labelStyle: computed(() => api2.computedLabelStyle()),
82
90
  valueStyle: computed(() => api2.computedValueStyle()),
@@ -142,6 +150,7 @@ const initState = ({
142
150
  return state;
143
151
  };
144
152
  const initApi = ({ api: api2, state, dispatch, broadcast, props, constants, vm, t, nextTick, slots }) => {
153
+ const validateOriginFunc = validate({ api: api2, props, state, t });
145
154
  Object.assign(api2, {
146
155
  state,
147
156
  dispatch,
@@ -170,7 +179,8 @@ const initApi = ({ api: api2, state, dispatch, broadcast, props, constants, vm,
170
179
  onFieldBlur: onFieldBlur(api2),
171
180
  onFieldChange: onFieldChange({ api: api2, state }),
172
181
  addValidateEvents: addValidateEvents({ api: api2, vm, props, state }),
173
- validate: wrapValidate({ validateFunc: validate({ api: api2, props, state, t }), props }),
182
+ validateOrigin: validateOriginFunc,
183
+ validate: wrapValidate({ validateFunc: validateOriginFunc, props }),
174
184
  getDisplayedValue: getDisplayedValue({ state }),
175
185
  clearDisplayedValue: clearDisplayedValue({ state }),
176
186
  handleLabelMouseenter: handleLabelMouseenter({ props, state, slots }),
@@ -31,7 +31,7 @@ const syncGridSelection = ({ props, vm, state, nextTick }) => () => {
31
31
  const fullData = (tableData == null ? void 0 : tableData.fullData) || [];
32
32
  if (props.multiple) {
33
33
  if (Array.isArray(state.modelValue) && state.modelValue.length > 0) {
34
- const rowsToSelect = fullData.filter((row) => state.modelValue.indexOf(row[props.valueField]) !== -1);
34
+ const rowsToSelect = fullData.filter((row) => state.modelValue.includes(row[props.valueField]));
35
35
  vm.$refs.gridRef.clearSelection();
36
36
  if (rowsToSelect.length > 0) {
37
37
  vm.$refs.gridRef.setSelection(rowsToSelect, true);
@@ -93,7 +93,7 @@ const filter = ({ props, state, vm }) => async (value) => {
93
93
  if (multiple) {
94
94
  const selectedIds = Array.isArray(state.selected) ? state.selected.map((sel) => sel[valueField]) : [];
95
95
  vm.$refs.gridRef.clearSelection();
96
- const selectedRows = data.filter((row) => selectedIds.indexOf(row[valueField]) !== -1);
96
+ const selectedRows = data.filter((row) => selectedIds.includes(row[valueField]));
97
97
  if (selectedRows.length > 0) {
98
98
  vm.$refs.gridRef.setSelection(selectedRows, true);
99
99
  }
@@ -441,7 +441,7 @@ const selectChange = ({ props, vm, emit, state, nextTick }) => ({ $table, select
441
441
  try {
442
442
  const tableData = vm.$refs.gridRef.getTableData();
443
443
  const fullData = (tableData == null ? void 0 : tableData.fullData) || [];
444
- const rowsToSelect = fullData.filter((row2) => currentValue.indexOf(row2[valueField]) !== -1);
444
+ const rowsToSelect = fullData.filter((row2) => currentValue.includes(row2[valueField]));
445
445
  vm.$refs.gridRef.clearSelection();
446
446
  if (rowsToSelect.length > 0) {
447
447
  vm.$refs.gridRef.setSelection(rowsToSelect, true);
package/guide/index.js CHANGED
@@ -103,7 +103,7 @@ const itemStep = (item, state, deepCopy, index, Shepherd) => {
103
103
  hightBox: item.hightBox || null,
104
104
  buttons: deepCopy[index].button,
105
105
  cancelIcon: {
106
- enabled: true
106
+ enabled: state.showClose
107
107
  },
108
108
  id: item.id,
109
109
  scrollTo: true,
@@ -122,16 +122,22 @@ const itemStep = (item, state, deepCopy, index, Shepherd) => {
122
122
  const currentStepElement = currentStep && currentStep.getElement();
123
123
  const footer = currentStepElement && currentStepElement.querySelector(".shepherd-footer");
124
124
  const cancelIcon = currentStepElement && currentStepElement.querySelector(".shepherd-header .shepherd-cancel-icon span");
125
- const cloesIcon = `<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
126
- <desc fill="none">
127
- Created with Pixso.
128
- </desc>
129
- <defs fill="none" />
130
- <g>
131
- <path id="path" d="M12.49 3.3C12.66 3.47 12.66 3.74 12.54 3.92L8.58 7.89L12.49 11.77C12.7 11.96 12.72 12.25 12.54 12.48C12.37 12.67 12.05 12.72 11.83 12.54L7.88 8.58L4 12.49C3.82 12.66 3.5 12.67 3.32 12.5C3.13 12.33 3.08 12.05 3.24 11.83L7.17 7.89L3.29 4C3.12 3.79 3.13 3.48 3.33 3.29C3.5 3.15 3.75 3.13 3.91 3.24L7.88 7.17L11.78 3.3C11.96 3.08 12.29 3.08 12.49 3.3Z" fill-opacity="1.000000" fill-rule="evenodd"/>
132
- </g>
133
- </svg>`;
134
- cancelIcon.innerHTML = cloesIcon;
125
+ if (cancelIcon) {
126
+ if (state.showClose) {
127
+ const cloesIcon = `<svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
128
+ <desc fill="none">
129
+ Created with Pixso.
130
+ </desc>
131
+ <defs fill="none" />
132
+ <g>
133
+ <path id="path" d="M12.49 3.3C12.66 3.47 12.66 3.74 12.54 3.92L8.58 7.89L12.49 11.77C12.7 11.96 12.72 12.25 12.54 12.48C12.37 12.67 12.05 12.72 11.83 12.54L7.88 8.58L4 12.49C3.82 12.66 3.5 12.67 3.32 12.5C3.13 12.33 3.08 12.05 3.24 11.83L7.17 7.89L3.29 4C3.12 3.79 3.13 3.48 3.33 3.29C3.5 3.15 3.75 3.13 3.91 3.24L7.88 7.17L11.78 3.3C11.96 3.08 12.29 3.08 12.49 3.3Z" fill-opacity="1.000000" fill-rule="evenodd"/>
134
+ </g>
135
+ </svg>`;
136
+ cancelIcon.innerHTML = cloesIcon;
137
+ } else {
138
+ cancelIcon.parentElement.style.display = "none";
139
+ }
140
+ }
135
141
  const progress = document.createElement("span");
136
142
  progress.classList.add("progress-style");
137
143
  progress.innerText = `${Shepherd.activeTour && Shepherd.activeTour.steps.indexOf(currentStep) + 1}/${Shepherd.activeTour && Shepherd.activeTour.steps.length}`;
package/guide/vue.js CHANGED
@@ -17,7 +17,8 @@ const renderless = (props, { reactive, onMounted, onBeforeUnmount, watch }, { de
17
17
  modalOverlayOpeningPadding: props.modalOverlayOpeningPadding,
18
18
  modalOverlayOpeningRadius: props.modalOverlayOpeningRadius,
19
19
  arrow: props.arrow,
20
- lightClass: props.lightClass
20
+ lightClass: props.lightClass,
21
+ showClose: props.showClose
21
22
  });
22
23
  let baseApi = {
23
24
  state,
package/input/vue.js CHANGED
@@ -41,7 +41,7 @@ import {
41
41
  } from "./index";
42
42
  import useStorageBox from "../tall-storage/vue-storage-box";
43
43
  import { on, off } from "@opentiny/utils";
44
- import { debounce } from "@opentiny/utils";
44
+ import { debounceBoth } from "@opentiny/utils";
45
45
  const api = [
46
46
  "blur",
47
47
  "showBox",
@@ -222,7 +222,8 @@ const mergeApi = ({
222
222
  }),
223
223
  handleFocus: handleFocus({ api: api2, emit, state }),
224
224
  handleInput: handleInput({ api: api2, emit, nextTick, state }),
225
- resizeTextarea: debounce(200, true, resizeTextarea({ api: api2, parent, vm, state, props })),
225
+ resizeTextarea: debounceBoth(200, resizeTextarea({ api: api2, parent, vm, state, props })),
226
+ // 抖动的首尾都要执行一次
226
227
  updateIconOffset: updateIconOffset(api2),
227
228
  calcTextareaHeight: calcTextareaHeight({
228
229
  api: api2,
package/modal/index.js CHANGED
@@ -87,12 +87,12 @@ const mouseEnterEvent = (state) => () => {
87
87
  };
88
88
  const mouseLeaveEvent = ({ api, props, state }) => () => {
89
89
  api.addMsgQueue();
90
- state.timer = window.setTimeout(
91
- () => {
90
+ const duration = parseFloat(props.duration);
91
+ if (duration > 0) {
92
+ state.timer = window.setTimeout(() => {
92
93
  api.close("close");
93
- },
94
- parseFloat(props.duration)
95
- );
94
+ }, duration);
95
+ }
96
96
  };
97
97
  const updateZindex = ({ state, props }) => () => {
98
98
  state.modalZindex = props.zIndex || PopupManager.nextZIndex();
@@ -157,12 +157,12 @@ const open = ({
157
157
  }, 10);
158
158
  if (state.isMsg) {
159
159
  api.addMsgQueue();
160
- state.timer = window.setTimeout(
161
- () => {
160
+ const duration = parseFloat(props.duration);
161
+ if (duration > 0) {
162
+ state.timer = window.setTimeout(() => {
162
163
  api.close(params.type);
163
- },
164
- parseFloat(props.duration)
165
- );
164
+ }, duration);
165
+ }
166
166
  } else {
167
167
  nextTick(() => {
168
168
  if (!isMobileFirstMode) {
@@ -233,7 +233,7 @@ const close = ({ emit, parent, props, state }) => (type) => {
233
233
  emit("update:modelValue", false);
234
234
  emit("hide", params);
235
235
  }
236
- }, 200);
236
+ }, 400);
237
237
  }
238
238
  };
239
239
  const handleGlobalKeydownEvent = (api) => (event) => {
package/modal/vue.js CHANGED
@@ -33,6 +33,7 @@ import {
33
33
  hideScrollbar,
34
34
  watchVisible
35
35
  } from "./index";
36
+ import { nanoid } from "@opentiny/utils";
36
37
  const api = [
37
38
  "state",
38
39
  "dragEvent",
@@ -59,6 +60,8 @@ const renderless = (props, { computed, onMounted, onBeforeUnmount, reactive, wat
59
60
  const api2 = {};
60
61
  const lockScrollClass = constants.SCROLL_LOCK_CLASS(mode);
61
62
  const state = reactive({
63
+ titleId: "tiny-modal-title-" + nanoid.api.nanoid(8),
64
+ contentId: "tiny-modal-content-" + nanoid.api.nanoid(8),
62
65
  emitter: emitter(),
63
66
  visible: false,
64
67
  contentVisible: false,
package/notify/vue.js CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  getPositionSide,
12
12
  getZindex
13
13
  } from "./index";
14
+ import { nanoid } from "@opentiny/utils";
14
15
  const api = [
15
16
  "state",
16
17
  "clearTimer",
@@ -37,7 +38,9 @@ const renderless = (props, { computed, onBeforeUnmount, onMounted, reactive, wat
37
38
  dangerouslyUseHTMLString: false,
38
39
  positionStyle: computed(() => api2.getOffsetStyle(state)),
39
40
  verticalProperty: computed(() => api2.getPositionSide(state)),
40
- customClass: computed(() => props.customClass)
41
+ customClass: computed(() => props.customClass),
42
+ titleId: `tiny-notify-title-${nanoid.api.nanoid(8)}`,
43
+ contentId: `tiny-notify-content-${nanoid.api.nanoid(8)}`
41
44
  });
42
45
  Object.assign(api2, {
43
46
  state,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opentiny/vue-renderless",
3
- "version": "3.28.2",
3
+ "version": "3.30.0",
4
4
  "description": "An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.",
5
5
  "author": "OpenTiny Team",
6
6
  "license": "MIT",
@@ -25,8 +25,8 @@
25
25
  ],
26
26
  "sideEffects": false,
27
27
  "dependencies": {
28
- "@opentiny/utils": "~3.28.0",
29
- "@opentiny/vue-hooks": "~3.28.0",
28
+ "@opentiny/utils": "~3.30.0",
29
+ "@opentiny/vue-hooks": "~3.30.0",
30
30
  "color": "4.2.3"
31
31
  },
32
32
  "devDependencies": {
package/picker/vue.js CHANGED
@@ -59,7 +59,7 @@ import {
59
59
  formatInputValue
60
60
  } from "./index";
61
61
  import { dateMobileToggle, timeMobileToggle, dateToTimeArray, timeArrayToDate, timeMobileConfirm } from "./mb";
62
- import { DATEPICKER } from "@opentiny/utils";
62
+ import { DATEPICKER, nanoid } from "@opentiny/utils";
63
63
  const api = [
64
64
  "state",
65
65
  "btnClick",
@@ -154,7 +154,8 @@ const initState = ({ api: api2, reactive, vm, computed, props, utils, parent, br
154
154
  innerWidth: 0,
155
155
  breakLine: computed(
156
156
  () => (state.innerWidth < 230 && state.type === "daterange" || state.innerWidth < 335 && state.type === "datetimerange") && state.displayValue && state.displayValue[1]
157
- )
157
+ ),
158
+ ariaPanelId: "panel-" + nanoid.api.nanoid(8)
158
159
  });
159
160
  return state;
160
161
  };
package/qr-code/vue.js CHANGED
@@ -21,7 +21,9 @@ const renderless = (props, { reactive, watch, onMounted }, { vm, emit }, { QRCod
21
21
  () => {
22
22
  api2.draw();
23
23
  api2.change();
24
- }
24
+ },
25
+ { deep: true }
26
+ // 添加深度监听,防止响应式失效
25
27
  );
26
28
  return api2;
27
29
  };
package/rate/index.js CHANGED
@@ -52,7 +52,9 @@ const selectValue = ({ emit, props, state }) => (value) => {
52
52
  if (props.disabled) {
53
53
  return;
54
54
  }
55
- if (props.allowHalf && state.pointerAtLeftHalf) {
55
+ if (props.clearable && props.modelValue === value) {
56
+ value = 0;
57
+ } else if (props.allowHalf && state.pointerAtLeftHalf) {
56
58
  value = state.currentValue;
57
59
  }
58
60
  emit("update:modelValue", value);
@@ -112,8 +114,7 @@ const showDecimalIcon = ({ props, state }) => (item) => {
112
114
  const showWhenAllowHalf = props.allowHalf && state.pointerAtLeftHalf && item - 0.5 <= state.currentValue && item > state.currentValue;
113
115
  return showWhenDisabled || showWhenAllowHalf;
114
116
  };
115
- const getIconStyle = ({ api, props, state }) => (item) => {
116
- const isHalf = api.showDecimalIcon(item);
117
+ const getIconStyle = ({ props, state }) => (item) => {
117
118
  const voidColor = props.disabled ? props.disabledVoidColor : props.voidColor;
118
119
  if (props.radio) {
119
120
  return {
@@ -122,7 +123,7 @@ const getIconStyle = ({ api, props, state }) => (item) => {
122
123
  };
123
124
  }
124
125
  return {
125
- fill: isHalf ? "transparent" : item <= state.currentValue ? state.activeColor : voidColor,
126
+ fill: item <= state.currentValue ? state.activeColor : voidColor,
126
127
  "font-size": props.size || "18px"
127
128
  };
128
129
  };
package/rate/vue.js CHANGED
@@ -120,7 +120,7 @@ const renderless = (props, { computed, reactive, toRefs, watch, onMounted, onUnm
120
120
  computedActiveColor: computedActiveColor(props),
121
121
  computedActiveClass: computedActiveClass(props),
122
122
  showDecimalIcon: showDecimalIcon({ props, state }),
123
- getIconStyle: getIconStyle({ api: api2, props, state })
123
+ getIconStyle: getIconStyle({ props, state })
124
124
  }, changeValue.api));
125
125
  return api2;
126
126
  };
@@ -106,6 +106,26 @@ const initQuill = ({ api, emit, props, vm, service, state, Quill, ImageDrop, Ima
106
106
  }
107
107
  emit("ready", state.quill);
108
108
  api.setToolbarTips();
109
+ api.setTooltipI18n();
110
+ state.tooltipI18nHandler = () => setTimeout(() => {
111
+ api.setTooltipI18n();
112
+ api.adjustTooltipPosition();
113
+ });
114
+ state.tooltipResizeHandler = () => api.adjustTooltipPosition();
115
+ vm.$el.addEventListener("click", state.tooltipI18nHandler);
116
+ vm.$el.addEventListener("keyup", state.tooltipI18nHandler);
117
+ window.addEventListener("resize", state.tooltipResizeHandler);
118
+ if (typeof MutationObserver !== "undefined") {
119
+ state.tooltipObserver = new MutationObserver(() => {
120
+ requestAnimationFrame(() => api.adjustTooltipPosition());
121
+ });
122
+ state.tooltipObserver.observe(vm.$el, {
123
+ childList: true,
124
+ subtree: true,
125
+ attributes: true,
126
+ attributeFilter: ["style", "class", "data-mode"]
127
+ });
128
+ }
109
129
  };
110
130
  const handleClick = ({ state, Quill }) => (event) => {
111
131
  const el = event.target;
@@ -138,6 +158,64 @@ const setToolbarTips = ({ t, vm }) => () => {
138
158
  });
139
159
  }
140
160
  };
161
+ const setTooltipI18n = ({ t, vm }) => () => {
162
+ const richTextEl = vm.$el;
163
+ const tips = richTextEl.querySelectorAll(".ql-container .ql-tooltip");
164
+ const getPlaceholderByMode = (mode) => {
165
+ if (mode === "video")
166
+ return t("ui.richText.enterVideo");
167
+ if (mode === "formula")
168
+ return t("ui.richText.enterFormula");
169
+ return t("ui.richText.enterLink");
170
+ };
171
+ if (tips.length) {
172
+ Array.prototype.slice.call(tips).forEach((tip) => {
173
+ const mode = tip.getAttribute("data-mode") || "link";
174
+ const input = tip.querySelector("input[type='text']");
175
+ tip.setAttribute("data-visit-url-text", `${t("ui.richText.visitUrl")}:`);
176
+ tip.setAttribute("data-edit-text", t("ui.richText.edit"));
177
+ tip.setAttribute("data-remove-text", t("ui.richText.remove"));
178
+ tip.setAttribute("data-save-text", t("ui.richText.save"));
179
+ tip.setAttribute("data-enter-link-text", `${t("ui.richText.enterLink")}:`);
180
+ tip.setAttribute("data-enter-formula-text", `${t("ui.richText.enterFormula")}:`);
181
+ tip.setAttribute("data-enter-video-text", `${t("ui.richText.enterVideo")}:`);
182
+ if (input) {
183
+ input.setAttribute("placeholder", getPlaceholderByMode(mode));
184
+ }
185
+ });
186
+ }
187
+ };
188
+ const adjustTooltipPosition = ({ vm }) => () => {
189
+ const container = vm.$el.querySelector(".ql-container");
190
+ const tips = vm.$el.querySelectorAll(".ql-container .ql-tooltip");
191
+ const minGap = 8;
192
+ if (!container) {
193
+ return;
194
+ }
195
+ const containerRect = container.getBoundingClientRect();
196
+ if (tips.length) {
197
+ Array.prototype.slice.call(tips).forEach((tip) => {
198
+ if (!tip.classList.contains("ql-editing")) {
199
+ return;
200
+ }
201
+ const offsetParent = tip.offsetParent || tip.parentElement;
202
+ if (!offsetParent) {
203
+ return;
204
+ }
205
+ const parentRect = offsetParent.getBoundingClientRect();
206
+ const currentLeft = parseFloat(tip.style.left || "0");
207
+ const safeCurrentLeft = Number.isFinite(currentLeft) ? currentLeft : 0;
208
+ const minLeft = containerRect.left - parentRect.left + minGap;
209
+ const maxLeft = containerRect.right - parentRect.left - tip.offsetWidth - minGap;
210
+ if (maxLeft <= minLeft) {
211
+ tip.style.left = `${minLeft}px`;
212
+ return;
213
+ }
214
+ const clampedLeft = Math.min(Math.max(safeCurrentLeft, minLeft), maxLeft);
215
+ tip.style.left = `${clampedLeft}px`;
216
+ });
217
+ }
218
+ };
141
219
  const setPlaceholder = ({ state, props }) => () => {
142
220
  if (state.quill) {
143
221
  state.quill.root.setAttribute("data-placeholder", props.options.placeholder);
@@ -220,17 +298,27 @@ const mounted = ({ api, props, state, i18n, watch }) => () => {
220
298
  api.initQuill();
221
299
  }
222
300
  if (i18n) {
223
- watch(() => i18n.locale, api.setToolbarTips);
301
+ watch(() => i18n.locale, () => {
302
+ api.setToolbarTips();
303
+ api.setTooltipI18n();
304
+ api.adjustTooltipPosition();
305
+ });
224
306
  }
225
307
  };
226
- const beforeUnmount = ({ api, state }) => () => {
308
+ const beforeUnmount = ({ api, state, vm }) => () => {
227
309
  state.quill.off("selection-change", api.selectionChange);
228
310
  state.quill.off("text-change", api.textChange);
229
311
  state.quill.root.removeEventListener("click", api.handleClick);
312
+ state.tooltipI18nHandler && vm.$el.removeEventListener("click", state.tooltipI18nHandler);
313
+ state.tooltipI18nHandler && vm.$el.removeEventListener("keyup", state.tooltipI18nHandler);
314
+ state.tooltipResizeHandler && window.removeEventListener("resize", state.tooltipResizeHandler);
315
+ state.tooltipObserver && state.tooltipObserver.disconnect();
316
+ state.tooltipObserver = null;
230
317
  state.quill = null;
231
318
  delete state.quill;
232
319
  };
233
320
  export {
321
+ adjustTooltipPosition,
234
322
  beforeUnmount,
235
323
  getFileUploadUrl,
236
324
  handleClick,
@@ -243,5 +331,6 @@ export {
243
331
  selectionChange,
244
332
  setPlaceholder,
245
333
  setToolbarTips,
334
+ setTooltipI18n,
246
335
  textChange
247
336
  };
package/rich-text/vue.js CHANGED
@@ -3,6 +3,8 @@ import {
3
3
  initContent,
4
4
  initQuill,
5
5
  setToolbarTips,
6
+ setTooltipI18n,
7
+ adjustTooltipPosition,
6
8
  setPlaceholder,
7
9
  getFileUploadUrl,
8
10
  selectionChange,
@@ -19,6 +21,8 @@ const api = [
19
21
  "initContent",
20
22
  "initQuill",
21
23
  "setToolbarTips",
24
+ "setTooltipI18n",
25
+ "adjustTooltipPosition",
22
26
  "setPlaceholder",
23
27
  "selectionChange",
24
28
  "textChange",
@@ -32,7 +36,10 @@ const initState = ({ reactive, props, computed, api: api2 }) => {
32
36
  content: props.modelValue || props.content,
33
37
  maxLength: computed(() => api2.maxLength()),
34
38
  pasteCanceled: false,
35
- isDisplayOnly: computed(() => api2.isDisplayOnly())
39
+ isDisplayOnly: computed(() => api2.isDisplayOnly()),
40
+ tooltipI18nHandler: null,
41
+ tooltipResizeHandler: null,
42
+ tooltipObserver: null
36
43
  });
37
44
  return state;
38
45
  };
@@ -44,12 +51,14 @@ const initApi = (args) => {
44
51
  initContent: initContent({ state, props, nextTick }),
45
52
  initQuill: initQuill({ service, emit, props, api: api2, state, vm, ImageDrop, FileUpload, ImageUpload, Quill }),
46
53
  setToolbarTips: setToolbarTips({ t, vm }),
54
+ setTooltipI18n: setTooltipI18n({ t, vm }),
55
+ adjustTooltipPosition: adjustTooltipPosition({ vm }),
47
56
  setPlaceholder: setPlaceholder({ state, props }),
48
57
  getFileUploadUrl: getFileUploadUrl({ service }),
49
58
  selectionChange: selectionChange({ emit, state }),
50
59
  textChange: textChange({ emit, vm, state, Modal, t }),
51
60
  mounted: mounted({ api: api2, props, state, i18n, watch }),
52
- beforeUnmount: beforeUnmount({ api: api2, state }),
61
+ beforeUnmount: beforeUnmount({ api: api2, state, vm }),
53
62
  maxLength: maxLength({ props, constants }),
54
63
  handlePaste: handlePaste({ state }),
55
64
  isDisplayOnly: isDisplayOnly({ state, props, parent, nextTick }),
package/search/vue.js CHANGED
@@ -17,6 +17,9 @@ import {
17
17
  searchEnterKey,
18
18
  emitInput
19
19
  } from "./index";
20
+ const generateUniqueId = () => {
21
+ return `search-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
22
+ };
20
23
  const api = [
21
24
  "state",
22
25
  "handleChange",
@@ -68,7 +71,9 @@ const renderless = (props, { computed, onBeforeUnmount, onMounted, reactive, toR
68
71
  }, formatSearchTypes2.state), {
69
72
  showClear: computed(() => props.clearable && (state.focus || state.hovering) && state.currentValue),
70
73
  formItemSize: computed(() => (parent.formItem || {}).formItemSize),
71
- searchSize: computed(() => props.size || state.formItemSize)
74
+ searchSize: computed(() => props.size || state.formItemSize),
75
+ instanceId: generateUniqueId()
76
+ // 生成唯一 ID,用于避免多个组件实例时 id 重复
72
77
  }));
73
78
  const api2 = __spreadValues({
74
79
  state,
package/select/index.js CHANGED
@@ -1131,6 +1131,11 @@ const watchVisible = ({ api, constants, emit, state, vm, props, isMobileFirstMod
1131
1131
  vm.$refs.scrollbar.handleScroll();
1132
1132
  }
1133
1133
  }
1134
+ if (value && state.device === "mb" && state.breakpoint === "default") {
1135
+ if (document.activeElement) {
1136
+ document.activeElement.blur();
1137
+ }
1138
+ }
1134
1139
  }, props.updateDelay);
1135
1140
  if (!value && props.shape === "filter") {
1136
1141
  state.softFocus = false;
@@ -1498,11 +1503,10 @@ const computedCurrentSizeMap = ({ state, designConfig }) => () => {
1498
1503
  const sizeMap = ((_a = designConfig == null ? void 0 : designConfig.state) == null ? void 0 : _a.sizeMap) || defaultSizeMap;
1499
1504
  return sizeMap[state.selectSize || "default"];
1500
1505
  };
1501
- const mounted = ({ api, parent, state, props, vm, designConfig }) => () => {
1506
+ const mounted = ({ api, parent, state, props, vm, designConfig, nextTick }) => () => {
1502
1507
  state.defaultCheckedKeys = props.multiple ? state.gridCheckedData : props.treeOp.defaultCheckedKeys || [];
1503
1508
  const parentEl = parent.$el;
1504
1509
  const inputEl = parentEl.querySelector('input[data-tag="tiny-input-inner"]');
1505
- const inputClientRect = inputEl && inputEl.getBoundingClientRect() || {};
1506
1510
  if (inputEl === document.activeElement) {
1507
1511
  document.activeElement.blur();
1508
1512
  }
@@ -1514,10 +1518,14 @@ const mounted = ({ api, parent, state, props, vm, designConfig }) => () => {
1514
1518
  if (vm.$refs.tags) {
1515
1519
  addResizeListener(vm.$refs.tags, api.resetInputHeight);
1516
1520
  }
1521
+ nextTick(() => {
1522
+ if (inputEl) {
1523
+ state.inputWidth = inputEl.offsetWidth || 0;
1524
+ }
1525
+ });
1517
1526
  if (props.remote && props.multiple) {
1518
1527
  api.resetInputHeight();
1519
1528
  }
1520
- state.inputWidth = inputClientRect.width;
1521
1529
  api.initQuery({ init: true }).then(() => {
1522
1530
  api.setSelected(true);
1523
1531
  if (props.modelValue && props.initLabel) {
package/select/vue.js CHANGED
@@ -116,10 +116,10 @@ import {
116
116
  computedCurrentSizeMap,
117
117
  watchOptionsWhenAutoSelect
118
118
  } from "./index";
119
- import { debounce, nanoid } from "@opentiny/utils";
119
+ import { debounce } from "@opentiny/utils";
120
120
  import { isNumber } from "@opentiny/utils";
121
121
  import { useUserAgent } from "@opentiny/vue-hooks";
122
- import { isServer } from "@opentiny/utils";
122
+ import { isServer, nanoid } from "@opentiny/utils";
123
123
  const api = [
124
124
  "state",
125
125
  "nodeCollapse",
@@ -288,7 +288,8 @@ const initState = ({
288
288
  currentSizeMap: computed(() => api2.computedCurrentSizeMap()),
289
289
  rootAutoTipConfig: computed(() => __spreadValues({
290
290
  content: state.displayOnlyContent,
291
- always: !!state.displayOnlyContent
291
+ always: !!state.displayOnlyContent,
292
+ popperClass: "tiny-select__popper-maxh-50"
292
293
  }, props.tooltipConfig)),
293
294
  ariaListId: "tiny-select-" + nanoid.api.nanoid(8)
294
295
  }));
@@ -506,7 +507,7 @@ const addApi = ({
506
507
  debouncRquest: debouncRquest({ api: api2, state, props }),
507
508
  defaultOnQueryChange: defaultOnQueryChange({ props, state, constants, api: api2, nextTick, vm }),
508
509
  queryChange: queryChange({ props, state, constants, api: api2, nextTick, vm }),
509
- mounted: mounted({ api: api2, parent, state, props, vm, designConfig }),
510
+ mounted: mounted({ api: api2, parent, state, props, vm, designConfig, nextTick }),
510
511
  unMount: unMount({ api: api2, parent, vm, state }),
511
512
  watchOptimizeOpts: watchOptimizeOpts({ props, state }),
512
513
  handleDropdownClick: handleDropdownClick({ props, vm, state, emit }),
@@ -8,7 +8,7 @@ const getIntegerAndDecimal = ({ props }) => () => {
8
8
  if (isFunction(props.formatter)) {
9
9
  return props.formatter(props.value);
10
10
  }
11
- if (!isNumber(props.value)) {
11
+ if (typeof props.value !== "number") {
12
12
  return props.value;
13
13
  }
14
14
  let displayValue = String(props.value).split(".");
@@ -26,7 +26,54 @@ const getIntegerAndDecimal = ({ props }) => () => {
26
26
  }
27
27
  return [integer, decimal].join(decimal ? "." : "");
28
28
  };
29
+ const animateValue = ({ props, state }) => () => {
30
+ if (!props.useAnimation)
31
+ return;
32
+ let AnimationId = null;
33
+ const start = props.startValue;
34
+ const end = Number(props.value);
35
+ const duration = props.duration;
36
+ if (Number.isNaN(end)) {
37
+ state.animatingValue = props.value;
38
+ return;
39
+ }
40
+ const startTime = performance.now();
41
+ const cancel = () => {
42
+ if (AnimationId) {
43
+ cancelAnimationFrame(AnimationId);
44
+ AnimationId = null;
45
+ }
46
+ };
47
+ if (AnimationId) {
48
+ cancel();
49
+ }
50
+ const getDecimalPlaces = (num) => {
51
+ if (Math.floor(num) === num)
52
+ return 0;
53
+ const decimalPart = num.toString().split(".")[1];
54
+ return decimalPart ? decimalPart.length : 0;
55
+ };
56
+ const decimalPlaces = getDecimalPlaces(end);
57
+ const multiplier = 10 ** decimalPlaces;
58
+ const animate = (currentTime) => {
59
+ const elapsed = currentTime - startTime;
60
+ const progress = Math.min(elapsed / duration, 1);
61
+ const easeOutExpo = (t) => t === 1 ? 1 : 1 - 2 ** (-10 * t);
62
+ const current = start + (end - start) * easeOutExpo(progress);
63
+ state.animatingValue = Math.round(current * multiplier) / multiplier;
64
+ if (progress < 1) {
65
+ AnimationId = requestAnimationFrame(animate);
66
+ } else {
67
+ state.animatingValue = end;
68
+ AnimationId = null;
69
+ }
70
+ };
71
+ AnimationId = requestAnimationFrame(() => {
72
+ AnimationId = requestAnimationFrame(animate);
73
+ });
74
+ };
29
75
  export {
76
+ animateValue,
30
77
  getIntegerAndDecimal,
31
78
  isNumber
32
79
  };