sard 1.0.29 → 1.2.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 (108) hide show
  1. package/README.md +1 -2
  2. package/all-components.js +4 -0
  3. package/components/action-sheet/action-sheet-item.vue.js +1 -1
  4. package/components/action-sheet/common.d.ts +1 -1
  5. package/components/button/button.vue.js +1 -1
  6. package/components/calendar-popout/common.d.ts +1 -1
  7. package/components/cascader-popout/common.d.ts +1 -1
  8. package/components/checkbox-popout/common.d.ts +1 -1
  9. package/components/color-picker-popout/common.d.ts +1 -1
  10. package/components/crop-image/common.d.ts +1 -1
  11. package/components/danmaku/common.d.ts +38 -0
  12. package/components/danmaku/common.js +14 -0
  13. package/components/danmaku/danmaku.vue.d.ts +23 -0
  14. package/components/danmaku/danmaku.vue.js +370 -0
  15. package/components/danmaku/index.css +1 -0
  16. package/components/danmaku/index.d.ts +5 -0
  17. package/components/danmaku/index.js +6 -0
  18. package/components/datetime-picker-popout/common.d.ts +1 -1
  19. package/components/datetime-range-picker-popout/common.d.ts +1 -1
  20. package/components/descriptions/common.d.ts +8 -3
  21. package/components/descriptions/descriptions-item.vue.d.ts +0 -2
  22. package/components/descriptions/descriptions-item.vue.js +20 -21
  23. package/components/descriptions/descriptions.vue.js +56 -28
  24. package/components/descriptions/index.css +1 -1
  25. package/components/dialog/common.d.ts +1 -1
  26. package/components/dnd/dnd-item.vue.js +19 -9
  27. package/components/dnd/index.css +1 -1
  28. package/components/dropdown/common.d.ts +1 -1
  29. package/components/ellipsis/ellipsis.vue.js +62 -58
  30. package/components/ellipsis/index.css +1 -1
  31. package/components/image/image.vue.d.ts +1 -1
  32. package/components/index.d.ts +2 -0
  33. package/components/index.js +3 -1
  34. package/components/keyboard/keyboard.vue.d.ts +1 -1
  35. package/components/keyboard-popout/common.d.ts +1 -1
  36. package/components/keyboard-popout/keyboard-popout.vue.d.ts +1 -1
  37. package/components/list/index.css +1 -1
  38. package/components/load-more/load-more.vue.js +2 -2
  39. package/components/loading/loading.vue.js +1 -1
  40. package/components/motion/motion.vue.js +1 -1
  41. package/components/notify/common.d.ts +1 -1
  42. package/components/overlay/overlay.vue.js +6 -5
  43. package/components/picker/picker.vue.js +8 -5
  44. package/components/picker-popout/common.d.ts +1 -1
  45. package/components/picker-view/common.d.ts +22 -2
  46. package/components/picker-view/index.d.ts +2 -2
  47. package/components/picker-view/index.js +2 -2
  48. package/components/picker-view/picker-view-column.vue.d.ts +19 -0
  49. package/components/picker-view/picker-view-column.vue.js +167 -0
  50. package/components/picker-view/picker-view.vue.d.ts +55 -0
  51. package/components/picker-view/picker-view.vue.js +60 -0
  52. package/components/popout/common.d.ts +1 -1
  53. package/components/popout/popout.vue.js +2 -4
  54. package/components/popover/common.js +1 -1
  55. package/components/popover/popover.vue.js +11 -10
  56. package/components/popup/popup.vue.js +1 -1
  57. package/components/preview-image/common.d.ts +1 -1
  58. package/components/preview-image/preview-image.vue.d.ts +1 -1
  59. package/components/preview-image/preview-image.vue.js +4 -4
  60. package/components/pull-down-refresh/pull-down-refresh.vue.js +4 -7
  61. package/components/radio-popout/common.d.ts +1 -1
  62. package/components/rate/rate.vue.js +2 -2
  63. package/components/select-popout/common.d.ts +1 -1
  64. package/components/share-sheet/common.d.ts +1 -1
  65. package/components/skeleton/skeleton.vue.js +1 -1
  66. package/components/swiper/index.d.ts +2 -2
  67. package/components/swiper/index.js +2 -2
  68. package/components/swiper/swiper-item.vue.d.ts +11 -0
  69. package/components/swiper/swiper-item.vue.js +32 -0
  70. package/components/swiper/swiper.vue.d.ts +147 -0
  71. package/components/swiper/swiper.vue.js +51 -0
  72. package/components/swiper/useSwiper.d.ts +1 -2
  73. package/components/swiper/useSwiper.js +12 -12
  74. package/components/table/table.vue.js +1 -2
  75. package/components/text-shrink/common.d.ts +28 -0
  76. package/components/text-shrink/common.js +8 -0
  77. package/components/text-shrink/index.css +1 -0
  78. package/components/text-shrink/index.d.ts +5 -0
  79. package/components/text-shrink/index.js +6 -0
  80. package/components/text-shrink/text-shrink.vue.d.ts +16 -0
  81. package/components/text-shrink/text-shrink.vue.js +252 -0
  82. package/components/toast/common.d.ts +1 -1
  83. package/global.d.ts +2 -0
  84. package/index.css +1 -1
  85. package/index.js +3 -2
  86. package/package.json +1 -1
  87. package/utils/createImperativeComponent.d.ts +12 -5
  88. package/utils/createImperativeComponent.js +13 -37
  89. package/utils/imperative/agent-runtime.d.ts +28 -0
  90. package/utils/imperative/agent-runtime.js +33 -0
  91. package/utils/imperative/imperative-agent.vue.d.ts +35 -0
  92. package/utils/imperative/imperative-agent.vue.js +60 -0
  93. package/utils/index.d.ts +0 -1
  94. package/utils/index.js +1 -2
  95. package/utils/is.d.ts +4 -1
  96. package/utils/is.js +5 -3
  97. package/utils/number.d.ts +1 -1
  98. package/utils/number.js +20 -19
  99. package/components/picker-view/picker-view-column.d.ts +0 -3
  100. package/components/picker-view/picker-view-column.js +0 -139
  101. package/components/picker-view/picker-view.d.ts +0 -21
  102. package/components/picker-view/picker-view.js +0 -57
  103. package/components/swiper/swiper-item.d.ts +0 -3
  104. package/components/swiper/swiper-item.js +0 -34
  105. package/components/swiper/swiper.d.ts +0 -57
  106. package/components/swiper/swiper.js +0 -43
  107. package/utils/vnode.d.ts +0 -9
  108. package/utils/vnode.js +0 -69
@@ -0,0 +1,252 @@
1
+ import { createBem } from "../../utils/bem.js";
2
+ import { defaultTextShrinkProps } from "./common.js";
3
+ import { computed, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, defineComponent, mergeDefaults, normalizeClass, normalizeStyle, onBeforeUnmount, onMounted, openBlock, ref, toDisplayString, unref, useTemplateRef, watch } from "vue";
4
+ //#region packages/sard/components/text-shrink/text-shrink.vue
5
+ const _hoisted_1 = ["textContent"];
6
+ const MIN_FONT_SIZE = 1;
7
+ /** 二分搜索收敛精度(px) */
8
+ const SEARCH_EPSILON = .05;
9
+ /**
10
+ * 尺寸比较容差。
11
+ * 必须小于 1 个字号步进:单行渲染一旦溢出哪怕 0.5px,text-overflow 就会用省略号
12
+ * 吃掉末尾一整个字,视觉上像“根本没放下”。这里只留吸收浮点误差的余量。
13
+ */
14
+ const SIZE_TOLERANCE = .05;
15
+ /** 宽度变化容差:小于它视为同一宽度,跳过重复适配 */
16
+ const WIDTH_TOLERANCE = .5;
17
+ /** 单行按宽度外推分割点的次数上限,之后回到二分保证收敛 */
18
+ const INTERPOLATION_LIMIT = 2;
19
+ /** 首次读到的 computed font-size 不可信时的兜底基准 */
20
+ const FALLBACK_BASE_FONT_SIZE = 16;
21
+ /** 复用同一个 Range,避免每次测量都新建对象 */
22
+ var text_shrink_default = /*@__PURE__*/ defineComponent({
23
+ __name: "text-shrink",
24
+ props: /*@__PURE__*/ mergeDefaults({
25
+ text: {
26
+ type: String,
27
+ required: false
28
+ },
29
+ maxLines: {
30
+ type: Number,
31
+ required: false
32
+ },
33
+ baseFontSize: {
34
+ type: Number,
35
+ required: false
36
+ },
37
+ minFontSize: {
38
+ type: Number,
39
+ required: false
40
+ }
41
+ }, defaultTextShrinkProps),
42
+ setup(__props, { expose: __expose }) {
43
+ const props = __props;
44
+ const bem = createBem("text-shrink");
45
+ /** 缩放下限:浏览器可渲染的最小字号 */
46
+ const rootRef = useTemplateRef("root");
47
+ const measureRef = useTemplateRef("measure");
48
+ const textRef = useTemplateRef("text");
49
+ /** 当前生效字号,undefined 表示尚未完成首次适配 */
50
+ const fontSize = ref();
51
+ /** 是否停在缩放下限后仍然溢出(已按 maxLines 截断) */
52
+ const overflow = ref(false);
53
+ /** 首次适配完成前隐藏可见层,避免以错误字号闪一下 */
54
+ const ready = ref(false);
55
+ const lines = computed(() => {
56
+ const value = Math.floor(Number(props.maxLines));
57
+ return Number.isFinite(value) && value > 1 ? value : 1;
58
+ });
59
+ const singleLine = computed(() => lines.value === 1);
60
+ let frozenBaseFontSize;
61
+ function resolveBaseFontSize() {
62
+ const { baseFontSize } = props;
63
+ if (typeof baseFontSize === "number" && Number.isFinite(baseFontSize) && baseFontSize > 0) return baseFontSize;
64
+ if (frozenBaseFontSize !== void 0) return frozenBaseFontSize;
65
+ let value = FALLBACK_BASE_FONT_SIZE;
66
+ const root = rootRef.value;
67
+ if (root && typeof window !== "undefined") {
68
+ const measured = Number.parseFloat(window.getComputedStyle(root).fontSize);
69
+ if (Number.isFinite(measured) && measured > 0) value = measured;
70
+ }
71
+ frozenBaseFontSize = value;
72
+ return value;
73
+ }
74
+ /**
75
+ * 缩放下限:取 minFontSize,并夹在 [MIN_FONT_SIZE, baseFontSize] 之间。
76
+ * 传 0 / 负数 / NaN 一律回退到浏览器可渲染下限,避免出现 0px 的死循环。
77
+ */
78
+ function resolveMinFontSize(baseFontSize) {
79
+ const { minFontSize } = props;
80
+ return Math.min(baseFontSize, Math.max(MIN_FONT_SIZE, typeof minFontSize === "number" && Number.isFinite(minFontSize) && minFontSize > 0 ? minFontSize : MIN_FONT_SIZE));
81
+ }
82
+ let range;
83
+ function getRange() {
84
+ if (!range && typeof document !== "undefined") range = document.createRange();
85
+ return range;
86
+ }
87
+ /**
88
+ * 以指定字号测量一次:写入字号后只读一次几何。
89
+ * 单行读 Range 宽度(测量层此时不换行,宽度可以超出容器);
90
+ * 多行读 Range 的 rect 个数,一个文本节点每行对应一个 rect。
91
+ * 读写之间会强制一次布局,所以不需要等 Vue 更新。
92
+ */
93
+ function probeAt(size, available) {
94
+ const measure = measureRef.value;
95
+ const node = measure?.firstChild;
96
+ const selection = getRange();
97
+ if (!measure || !node || node.nodeType !== 3 || !selection) return {
98
+ fits: true,
99
+ width: 0
100
+ };
101
+ measure.style.fontSize = `${size}px`;
102
+ selection.selectNodeContents(node);
103
+ if (singleLine.value) {
104
+ const { width } = selection.getBoundingClientRect();
105
+ return {
106
+ fits: width <= available + SIZE_TOLERANCE,
107
+ width
108
+ };
109
+ }
110
+ return {
111
+ fits: selection.getClientRects().length <= lines.value,
112
+ width: 0
113
+ };
114
+ }
115
+ /**
116
+ * 在 (floorFontSize, baseFontSize] 区间内搜索能完整显示的最大字号。
117
+ * 单行先用「宽度 ∝ 字号」外推两次把区间夹紧,多行直接二分;
118
+ * 外推只决定下一个试探点,放不放得下始终由真实测量决定,精度不受影响。
119
+ */
120
+ function search(baseFontSize, available, floorFontSize) {
121
+ const atBase = probeAt(baseFontSize, available);
122
+ if (atBase.fits) return {
123
+ size: baseFontSize,
124
+ overflow: false
125
+ };
126
+ if (!probeAt(floorFontSize, available).fits) return {
127
+ size: floorFontSize,
128
+ overflow: true
129
+ };
130
+ let lower = floorFontSize;
131
+ let upper = baseFontSize;
132
+ let estimate = atBase.width > 0 ? baseFontSize * available / atBase.width : NaN;
133
+ let interpolations = 0;
134
+ while (upper - lower > SEARCH_EPSILON) {
135
+ const useEstimate = interpolations < INTERPOLATION_LIMIT && estimate > lower && estimate < upper;
136
+ if (useEstimate) interpolations += 1;
137
+ const middle = useEstimate ? estimate : (lower + upper) / 2;
138
+ const result = probeAt(middle, available);
139
+ if (!result.fits) upper = middle;
140
+ else if (result.width >= available - SIZE_TOLERANCE) {
141
+ lower = middle;
142
+ break;
143
+ } else lower = middle;
144
+ estimate = result.width > 0 ? middle * available / result.width : NaN;
145
+ }
146
+ const maxTenths = Math.floor(baseFontSize * 10);
147
+ let tenths = Math.floor(lower * 10);
148
+ while (tenths < maxTenths && probeAt((tenths + 1) / 10, available).fits) tenths += 1;
149
+ return {
150
+ size: Math.max(floorFontSize, tenths / 10),
151
+ overflow: false
152
+ };
153
+ }
154
+ function fit() {
155
+ const root = rootRef.value;
156
+ const text = textRef.value;
157
+ if (!root || !text) return;
158
+ if (root.clientWidth <= 0) return;
159
+ const available = text.getBoundingClientRect().width;
160
+ if (available <= 0) return;
161
+ const baseFontSize = Math.max(MIN_FONT_SIZE, resolveBaseFontSize());
162
+ const floorFontSize = resolveMinFontSize(baseFontSize);
163
+ const result = search(baseFontSize, available, floorFontSize);
164
+ fontSize.value = Math.max(floorFontSize, result.size);
165
+ overflow.value = result.overflow;
166
+ ready.value = true;
167
+ observedWidth = available;
168
+ }
169
+ const requestFrame = (callback) => typeof requestAnimationFrame === "function" ? requestAnimationFrame(callback) : setTimeout(callback, 16);
170
+ const cancelFrame = (handle) => {
171
+ if (typeof cancelAnimationFrame === "function") cancelAnimationFrame(handle);
172
+ else clearTimeout(handle);
173
+ };
174
+ let rafId = 0;
175
+ let disposed = false;
176
+ let resizeObserver;
177
+ /** 上一次适配时的宽度,用来跳过 ResizeObserver 的重复回调 */
178
+ let observedWidth = -1;
179
+ /** 同一帧内的多次触发合并成一次适配 */
180
+ function scheduleFit() {
181
+ if (disposed || rafId !== 0) return;
182
+ rafId = requestFrame(() => {
183
+ rafId = 0;
184
+ if (disposed) return;
185
+ fit();
186
+ });
187
+ }
188
+ watch(() => [
189
+ props.text,
190
+ props.maxLines,
191
+ props.baseFontSize,
192
+ props.minFontSize
193
+ ], () => {
194
+ scheduleFit();
195
+ });
196
+ onMounted(() => {
197
+ fit();
198
+ if (typeof ResizeObserver !== "undefined" && rootRef.value) {
199
+ resizeObserver = new ResizeObserver((entries) => {
200
+ const width = entries[0]?.contentRect.width ?? 0;
201
+ if (Math.abs(width - observedWidth) < WIDTH_TOLERANCE) return;
202
+ observedWidth = width;
203
+ scheduleFit();
204
+ });
205
+ resizeObserver.observe(rootRef.value);
206
+ }
207
+ if (typeof document !== "undefined" && document.fonts && document.fonts.status !== "loaded") document.fonts.ready.then(() => {
208
+ if (!disposed) scheduleFit();
209
+ });
210
+ });
211
+ onBeforeUnmount(() => {
212
+ disposed = true;
213
+ resizeObserver?.disconnect();
214
+ resizeObserver = void 0;
215
+ if (rafId !== 0) {
216
+ cancelFrame(rafId);
217
+ rafId = 0;
218
+ }
219
+ });
220
+ const rootClass = computed(() => [bem.b(), bem.is("measuring", !ready.value)]);
221
+ const measureClass = bem.e("measure");
222
+ const textClass = computed(() => [bem.e("text"), singleLine.value ? bem.em("text", "nowrap") : bem.em("text", "clamp")]);
223
+ const textStyle = computed(() => {
224
+ const style = {};
225
+ if (fontSize.value !== void 0) style.fontSize = `${fontSize.value}px`;
226
+ if (!singleLine.value) style["-webkit-line-clamp"] = lines.value;
227
+ return style;
228
+ });
229
+ __expose({
230
+ fontSize,
231
+ overflow,
232
+ refit: scheduleFit
233
+ });
234
+ return (_ctx, _cache) => {
235
+ return openBlock(), createElementBlock("div", {
236
+ ref: "root",
237
+ class: normalizeClass(rootClass.value)
238
+ }, [createCommentVNode(" 文本层:可见文本 + 隐藏测量层。\n 测量层之所以放在文本层内部,是为了让所有可继承的排版属性(font-*、letter-spacing、\n word-spacing、white-space、word-break、text-transform…)由继承自动对齐:\n 兄弟节点之间不会互相继承,那种结构只能在 JS 里逐个搬运属性,搬漏一个就量不准。\n 测量层绝对定位、脱离文档流,不影响可见文本的布局与固有宽度。\n 用 v-text 而不是插值,保证它的内容永远是一个文本节点,Range 才能直接选它来数行。 "), createElementVNode("div", {
239
+ ref: "text",
240
+ class: normalizeClass(textClass.value),
241
+ style: normalizeStyle(textStyle.value)
242
+ }, [createElementVNode("div", {
243
+ ref: "measure",
244
+ class: normalizeClass(unref(measureClass)),
245
+ "aria-hidden": "true",
246
+ textContent: toDisplayString(props.text)
247
+ }, null, 10, _hoisted_1), createTextVNode(" " + toDisplayString(props.text), 1)], 6)], 2);
248
+ };
249
+ }
250
+ });
251
+ //#endregion
252
+ export { text_shrink_default as default };
@@ -1,5 +1,5 @@
1
1
  import { type DefaultProps } from '../config';
2
- import type { MotionEmits } from '../motion';
2
+ import type { MotionEmits } from '../motion/common';
3
3
  export interface ToastProps {
4
4
  type?: 'text' | 'loading' | 'success' | 'fail';
5
5
  title?: string | number;
package/global.d.ts CHANGED
@@ -32,6 +32,7 @@ declare module 'vue' {
32
32
  SCountDown: typeof import('./components/count-down/count-down').default
33
33
  SCountTo: typeof import('./components/count-to/count-to').default
34
34
  SCropImage: typeof import('./components/crop-image/crop-image').default
35
+ SDanmaku: typeof import('./components/danmaku/danmaku').default
35
36
  SDateStrip: typeof import('./components/date-strip/date-strip').default
36
37
  SDatetimePicker: typeof import('./components/datetime-picker/datetime-picker').default
37
38
  SDatetimePickerInput: typeof import('./components/datetime-picker-input/datetime-picker-input').default
@@ -151,6 +152,7 @@ declare module 'vue' {
151
152
  STabs: typeof import('./components/tabs/tabs').default
152
153
  STag: typeof import('./components/tag/tag').default
153
154
  SText: typeof import('./components/text/text').default
155
+ STextShrink: typeof import('./components/text-shrink/text-shrink').default
154
156
  STimeline: typeof import('./components/timeline/timeline').default
155
157
  STimelineItem: typeof import('./components/timeline/timeline-item').default
156
158
  SToast: typeof import('./components/toast/toast').default