sard-uniapp 1.24.1 → 1.24.2
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/CHANGELOG.md +16 -0
- package/components/config/index.d.ts +6 -0
- package/components/config/index.js +5 -0
- package/components/count-down/README.md +8 -6
- package/components/count-down/common.d.ts +3 -0
- package/components/count-down/count-down.vue +20 -4
- package/components/count-down/index.scss +10 -0
- package/components/fab/README.md +27 -20
- package/components/fab/common.d.ts +17 -0
- package/components/fab/fab.d.ts +12 -0
- package/components/fab/fab.vue +80 -26
- package/components/fab/index.scss +35 -5
- package/components/floating-bubble/README.md +10 -9
- package/components/floating-bubble/common.d.ts +2 -0
- package/components/floating-bubble/floating-bubble.d.ts +1 -0
- package/components/floating-bubble/floating-bubble.vue +18 -127
- package/components/floating-bubble/useFloatingBubble.d.ts +43 -0
- package/components/floating-bubble/useFloatingBubble.js +137 -0
- package/components/popout/README.md +8 -8
- package/components/popout/common.d.ts +13 -2
- package/components/popout/popout.vue +75 -45
- package/components/tag/tag.vue +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## [1.24.2](https://github.com/sutras/sard-uniapp/compare/v1.24.1...v1.24.2) (2025-09-06)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **count-down:** 修复行内嵌套块的问题 ([fd8112c](https://github.com/sutras/sard-uniapp/commit/fd8112c3885c0069b34f6709c7190a298d680726))
|
|
7
|
+
* **popout:** 修正按钮插槽内容 ([7f29d80](https://github.com/sutras/sard-uniapp/commit/7f29d8059ac1dd342f85a5de54f42e867879ecfc))
|
|
8
|
+
* **tag:** 阻止点击关闭时的事件冒泡 ([4220f31](https://github.com/sutras/sard-uniapp/commit/4220f311f0c5564ae095db6863f415f483f7f22e))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **fab:** 添加拖拽功能 ([4d24318](https://github.com/sutras/sard-uniapp/commit/4d243186b4ba22ebe50722c67466eb702c88cbf5))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
1
17
|
## [1.24.1](https://github.com/sutras/sard-uniapp/compare/v1.24.0...v1.24.1) (2025-08-31)
|
|
2
18
|
|
|
3
19
|
|
|
@@ -13,6 +13,7 @@ import { type DatetimeRangePickerInputProps } from '../datetime-range-picker-inp
|
|
|
13
13
|
import { type DialogProps } from '../dialog';
|
|
14
14
|
import { type DividerProps } from '../divider';
|
|
15
15
|
import { type DropdownProps } from '../dropdown';
|
|
16
|
+
import { type FabProps } from '../fab';
|
|
16
17
|
import { type FloatingBubbleProps } from '../floating-bubble';
|
|
17
18
|
import { type FormProps } from '../form';
|
|
18
19
|
import { type GridProps } from '../grid';
|
|
@@ -202,8 +203,13 @@ export declare const defaultConfig: {
|
|
|
202
203
|
overlayClosable: boolean;
|
|
203
204
|
hideName: boolean;
|
|
204
205
|
duration: number;
|
|
206
|
+
draggable: boolean;
|
|
207
|
+
axis: FabProps["axis"];
|
|
208
|
+
gapX: number;
|
|
209
|
+
gapY: number;
|
|
205
210
|
};
|
|
206
211
|
floatingBubble: {
|
|
212
|
+
draggable: boolean;
|
|
207
213
|
axis: FloatingBubbleProps["axis"];
|
|
208
214
|
gapX: number;
|
|
209
215
|
gapY: number;
|
|
@@ -152,8 +152,13 @@ export const defaultConfig = {
|
|
|
152
152
|
overlayClosable: false,
|
|
153
153
|
hideName: false,
|
|
154
154
|
duration: 150,
|
|
155
|
+
draggable: false,
|
|
156
|
+
axis: 'y',
|
|
157
|
+
gapX: 24,
|
|
158
|
+
gapY: 24,
|
|
155
159
|
},
|
|
156
160
|
floatingBubble: {
|
|
161
|
+
draggable: true,
|
|
157
162
|
axis: 'y',
|
|
158
163
|
gapX: 24,
|
|
159
164
|
gapY: 24,
|
|
@@ -57,12 +57,14 @@ import CountDown from 'sard-uniapp/components/count-down/count-down.vue'
|
|
|
57
57
|
|
|
58
58
|
### CountDownProps
|
|
59
59
|
|
|
60
|
-
| 属性
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
60
|
+
| 属性 | 描述 | 类型 | 默认值 |
|
|
61
|
+
| ----------------------------- | ---------------------- | ---------- | ---------- |
|
|
62
|
+
| root-class <sup>1.24.2+</sup> | 组件根元素类名 | string | - |
|
|
63
|
+
| root-style <sup>1.24.2+</sup> | 组件根元素样式 | StyleValue | - |
|
|
64
|
+
| time | 倒计时总时长,单位毫秒 | number | 0 |
|
|
65
|
+
| auto-start | 是否自动开始倒计时 | boolean | true |
|
|
66
|
+
| format | 时间格式 | string | 'HH:mm:ss' |
|
|
67
|
+
| millisecond | 是否开启毫秒级别渲染 | boolean | false |
|
|
66
68
|
|
|
67
69
|
### CountDownSlots
|
|
68
70
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<view :class="countDownClass" :style="countDownStyle">
|
|
3
3
|
<slot :time="currentTime">
|
|
4
4
|
{{ formatTime(format, currentTime) }}
|
|
5
5
|
</slot>
|
|
6
|
-
</
|
|
6
|
+
</view>
|
|
7
7
|
</template>
|
|
8
8
|
|
|
9
9
|
<script>
|
|
@@ -14,7 +14,10 @@ import {
|
|
|
14
14
|
formatTime,
|
|
15
15
|
defaultCountDownProps
|
|
16
16
|
} from "./common";
|
|
17
|
+
import { classNames, stringifyStyle, createBem } from "../../utils";
|
|
17
18
|
/**
|
|
19
|
+
* @property {string} rootClass 组件根元素类名,默认值:-。
|
|
20
|
+
* @property {StyleValue} rootStyle 组件根元素样式,默认值:-。
|
|
18
21
|
* @property {number} time 倒计时总时长,单位毫秒,默认值:0。
|
|
19
22
|
* @property {boolean} autoStart 是否自动开始倒计时,默认值:true。
|
|
20
23
|
* @property {string} format 时间格式,默认值:'HH:mm:ss'。
|
|
@@ -31,6 +34,8 @@ export default _defineComponent({
|
|
|
31
34
|
},
|
|
32
35
|
__name: "count-down",
|
|
33
36
|
props: _mergeDefaults({
|
|
37
|
+
rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
|
|
38
|
+
rootClass: { type: String, required: false },
|
|
34
39
|
time: { type: Number, required: false },
|
|
35
40
|
autoStart: { type: Boolean, required: false },
|
|
36
41
|
format: { type: String, required: false },
|
|
@@ -40,6 +45,7 @@ export default _defineComponent({
|
|
|
40
45
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
41
46
|
const props = __props;
|
|
42
47
|
const emit = __emit;
|
|
48
|
+
const bem = createBem("count-down");
|
|
43
49
|
const remainTime = ref(props.time);
|
|
44
50
|
let endTime = 0;
|
|
45
51
|
let timer = null;
|
|
@@ -102,7 +108,13 @@ export default _defineComponent({
|
|
|
102
108
|
onBeforeUnmount(() => {
|
|
103
109
|
pause();
|
|
104
110
|
});
|
|
105
|
-
const
|
|
111
|
+
const countDownClass = computed(() => {
|
|
112
|
+
return classNames(bem.b(), props.rootClass);
|
|
113
|
+
});
|
|
114
|
+
const countDownStyle = computed(() => {
|
|
115
|
+
return stringifyStyle(props.rootStyle);
|
|
116
|
+
});
|
|
117
|
+
const __returned__ = { props, emit, bem, remainTime, get endTime() {
|
|
106
118
|
return endTime;
|
|
107
119
|
}, set endTime(v) {
|
|
108
120
|
endTime = v;
|
|
@@ -114,10 +126,14 @@ export default _defineComponent({
|
|
|
114
126
|
return paused;
|
|
115
127
|
}, set paused(v) {
|
|
116
128
|
paused = v;
|
|
117
|
-
}, tick, start, pause, reset, precisionTime, currentTime, get formatTime() {
|
|
129
|
+
}, tick, start, pause, reset, precisionTime, currentTime, countDownClass, countDownStyle, get formatTime() {
|
|
118
130
|
return formatTime;
|
|
119
131
|
} };
|
|
120
132
|
return __returned__;
|
|
121
133
|
}
|
|
122
134
|
});
|
|
123
135
|
</script>
|
|
136
|
+
|
|
137
|
+
<style lang="scss">
|
|
138
|
+
@import './index.scss';
|
|
139
|
+
</style>
|
package/components/fab/README.md
CHANGED
|
@@ -70,22 +70,28 @@ import Fab from 'sard-uniapp/components/fab/fab.vue'
|
|
|
70
70
|
|
|
71
71
|
### FabProps
|
|
72
72
|
|
|
73
|
-
| 属性
|
|
74
|
-
|
|
|
75
|
-
| root-class
|
|
76
|
-
| root-style
|
|
77
|
-
| top
|
|
78
|
-
| right
|
|
79
|
-
| bottom
|
|
80
|
-
| left
|
|
81
|
-
| color
|
|
82
|
-
| background
|
|
83
|
-
| icon
|
|
84
|
-
| icon-family
|
|
85
|
-
| item-list
|
|
86
|
-
| hide-name
|
|
87
|
-
| overlay-closable
|
|
88
|
-
| duration
|
|
73
|
+
| 属性 | 描述 | 类型 | 默认值 |
|
|
74
|
+
| ----------------------------------- | -------------------------------------------- | ------------------------------ | ------ |
|
|
75
|
+
| root-class | 组件根元素类名 | string | - |
|
|
76
|
+
| root-style | 组件根元素样式 | StyleValue | - |
|
|
77
|
+
| top | 设置距离窗口顶部的距离,优先级比 `bottom` 高 | string | - |
|
|
78
|
+
| right | 设置距离窗口右边的距离 | string | - |
|
|
79
|
+
| bottom | 设置距离窗口底部的距离 | string | - |
|
|
80
|
+
| left | 设置距离窗口左边的距离,优先级比 `right` 高 | string | - |
|
|
81
|
+
| color | 设置按钮图标的颜色 | string | - |
|
|
82
|
+
| background | 设置按钮的背景色 | string | - |
|
|
83
|
+
| icon | 设置入口按钮的图标 | string | - |
|
|
84
|
+
| icon-family | 设置入口按钮的图标族 | string | - |
|
|
85
|
+
| item-list | 设置扩展按钮 | FabItem[] | [] |
|
|
86
|
+
| hide-name | 是否隐藏按钮名称 | boolean | false |
|
|
87
|
+
| overlay-closable | 点击遮罩是否隐藏扩展按钮 | boolean | false |
|
|
88
|
+
| duration | 扩展按钮显隐动画时长,单位 ms | number | 150 |
|
|
89
|
+
| draggable <sup>1.24.2+</sup> | 是否可拖拽 | boolean | false |
|
|
90
|
+
| axis <sup>1.24.2+</sup> | 允许拖拽的方向轴 | 'x' \| 'y' \| 'both' \| 'none' | 'y' |
|
|
91
|
+
| magnet <sup>1.24.2+</sup> | 吸附到指定轴最近的一边 | 'x' \| 'y' | - |
|
|
92
|
+
| gap-x <sup>1.24.2+</sup> | 悬浮按钮与窗口左右两边的最小间距,单位为 px | number | 24 |
|
|
93
|
+
| gap-y <sup>1.24.2+</sup> | 悬浮按钮与窗口上下两边的最小间距,单位为 px | number | 24 |
|
|
94
|
+
| offset (v-model) <sup>1.24.2+</sup> | 控制悬浮按钮的位置 | { x: number; y: number } | - |
|
|
89
95
|
|
|
90
96
|
### FabSlots
|
|
91
97
|
|
|
@@ -95,10 +101,11 @@ import Fab from 'sard-uniapp/components/fab/fab.vue'
|
|
|
95
101
|
|
|
96
102
|
### FabEmits
|
|
97
103
|
|
|
98
|
-
| 事件
|
|
99
|
-
|
|
|
100
|
-
| click
|
|
101
|
-
| select
|
|
104
|
+
| 事件 | 描述 | 类型 |
|
|
105
|
+
| -------------------------------- | ---------------------------- | ------------------------------------------ |
|
|
106
|
+
| click | 点击入口按钮时触发 | (event: any) => void |
|
|
107
|
+
| select | 点击扩展按钮时触发 | (item: FabItem, index: number) => void |
|
|
108
|
+
| update:offset <sup>1.24.2+</sup> | 因用户拖拽导致位置改变时触发 | (offset: { x: number; y: number }) => void |
|
|
102
109
|
|
|
103
110
|
### FabItem
|
|
104
111
|
|
|
@@ -14,16 +14,33 @@ export interface FabProps {
|
|
|
14
14
|
hideName?: boolean;
|
|
15
15
|
overlayClosable?: boolean;
|
|
16
16
|
duration?: number;
|
|
17
|
+
draggable?: boolean;
|
|
18
|
+
axis?: 'x' | 'y' | 'both' | 'none';
|
|
19
|
+
magnet?: 'x' | 'y';
|
|
20
|
+
gapX?: number;
|
|
21
|
+
gapY?: number;
|
|
22
|
+
offset?: {
|
|
23
|
+
x: number;
|
|
24
|
+
y: number;
|
|
25
|
+
};
|
|
17
26
|
}
|
|
18
27
|
export declare const defaultFabProps: () => {
|
|
19
28
|
itemList: () => never[];
|
|
20
29
|
overlayClosable: boolean;
|
|
21
30
|
hideName: boolean;
|
|
22
31
|
duration: number;
|
|
32
|
+
draggable: boolean;
|
|
33
|
+
axis: FabProps["axis"];
|
|
34
|
+
gapX: number;
|
|
35
|
+
gapY: number;
|
|
23
36
|
};
|
|
24
37
|
export interface FabEmits {
|
|
25
38
|
(e: 'click', event: any): void;
|
|
26
39
|
(e: 'select', item: FabItem, index: number): void;
|
|
40
|
+
(e: 'update:offset', offset: {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
}): void;
|
|
27
44
|
}
|
|
28
45
|
export interface FabItem {
|
|
29
46
|
name?: string;
|
package/components/fab/fab.d.ts
CHANGED
|
@@ -2,13 +2,25 @@ import { type FabProps, FabItem } from './common';
|
|
|
2
2
|
declare const _default: import("vue").DefineComponent<FabProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
|
|
3
3
|
click: (event: any) => any;
|
|
4
4
|
select: (item: FabItem, index: number) => any;
|
|
5
|
+
"update:offset": (offset: {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
}) => any;
|
|
5
9
|
}, string, import("vue").PublicProps, Readonly<FabProps> & Readonly<{
|
|
6
10
|
onClick?: ((event: any) => any) | undefined;
|
|
7
11
|
onSelect?: ((item: FabItem, index: number) => any) | undefined;
|
|
12
|
+
"onUpdate:offset"?: ((offset: {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
}) => any) | undefined;
|
|
8
16
|
}>, {
|
|
17
|
+
axis: "x" | "y" | "both" | "none";
|
|
9
18
|
duration: number;
|
|
10
19
|
overlayClosable: boolean;
|
|
20
|
+
draggable: boolean;
|
|
11
21
|
itemList: FabItem[];
|
|
12
22
|
hideName: boolean;
|
|
23
|
+
gapX: number;
|
|
24
|
+
gapY: number;
|
|
13
25
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
26
|
export default _default;
|
package/components/fab/fab.vue
CHANGED
|
@@ -6,7 +6,21 @@
|
|
|
6
6
|
@click="onOverlayClick"
|
|
7
7
|
/>
|
|
8
8
|
|
|
9
|
-
<view
|
|
9
|
+
<view
|
|
10
|
+
:class="fabClass"
|
|
11
|
+
:style="fabStyle"
|
|
12
|
+
@touchstart="onTouchStart"
|
|
13
|
+
@touchmove.stop.prevent="onTouchMove"
|
|
14
|
+
@touchend="onTouchEnd"
|
|
15
|
+
@touchcancel="onTouchEnd"
|
|
16
|
+
@mousedown="onMouseDown"
|
|
17
|
+
>
|
|
18
|
+
<view :class="itemEntryClass" @click="onItemEntryClick">
|
|
19
|
+
<view :class="bem.e('item-btn')" :style="itemEntryBtnStyle">
|
|
20
|
+
<sar-icon :family="iconFamily || 'sari'" :name="icon || 'plus'" />
|
|
21
|
+
</view>
|
|
22
|
+
</view>
|
|
23
|
+
|
|
10
24
|
<view
|
|
11
25
|
:class="contentClass"
|
|
12
26
|
:style="contentStyle"
|
|
@@ -31,11 +45,6 @@
|
|
|
31
45
|
</view>
|
|
32
46
|
</view>
|
|
33
47
|
</view>
|
|
34
|
-
<view :class="itemEntryClass" @click="onItemEntryClick">
|
|
35
|
-
<view :class="bem.e('item-btn')" :style="itemEntryBtnStyle">
|
|
36
|
-
<sar-icon :family="iconFamily || 'sari'" :name="icon || 'plus'" />
|
|
37
|
-
</view>
|
|
38
|
-
</view>
|
|
39
48
|
</view>
|
|
40
49
|
</template>
|
|
41
50
|
|
|
@@ -49,6 +58,7 @@ import {
|
|
|
49
58
|
import { useTransition, useZIndex } from "../../use";
|
|
50
59
|
import SarIcon from "../icon/icon.vue";
|
|
51
60
|
import SarOverlay from "../overlay/overlay.vue";
|
|
61
|
+
import { useFloatingBubble } from "../floating-bubble/useFloatingBubble";
|
|
52
62
|
/**
|
|
53
63
|
* @property {string} rootClass 组件根元素类名,默认值:-。
|
|
54
64
|
* @property {StyleValue} rootStyle 组件根元素样式,默认值:-。
|
|
@@ -64,8 +74,15 @@ import SarOverlay from "../overlay/overlay.vue";
|
|
|
64
74
|
* @property {boolean} hideName 是否隐藏按钮名称,默认值:false。
|
|
65
75
|
* @property {boolean} overlayClosable 点击遮罩是否隐藏扩展按钮,默认值:false。
|
|
66
76
|
* @property {number} duration 扩展按钮显隐动画时长,单位 ms,默认值:150。
|
|
77
|
+
* @property {boolean} draggable 是否可拖拽,默认值:false。
|
|
78
|
+
* @property {'x' | 'y' | 'both' | 'none'} axis 允许拖拽的方向轴,默认值:'y'。
|
|
79
|
+
* @property {'x' | 'y'} magnet 吸附到指定轴最近的一边,默认值:-。
|
|
80
|
+
* @property {number} gapX 悬浮按钮与窗口左右两边的最小间距,单位为 px,默认值:24。
|
|
81
|
+
* @property {number} gapY 悬浮按钮与窗口上下两边的最小间距,单位为 px,默认值:24。
|
|
82
|
+
* @property {{ x: number; y: number }} offset 控制悬浮按钮的位置,默认值:-。
|
|
67
83
|
* @event {(event: any) => void} click 点击入口按钮时触发
|
|
68
84
|
* @event {(item: FabItem, index: number) => void} select 点击扩展按钮时触发
|
|
85
|
+
* @event {(offset: { x: number; y: number }) => void} update 因用户拖拽导致位置改变时触发
|
|
69
86
|
*/
|
|
70
87
|
export default _defineComponent({
|
|
71
88
|
components: {
|
|
@@ -93,9 +110,15 @@ export default _defineComponent({
|
|
|
93
110
|
itemList: { type: Array, required: false },
|
|
94
111
|
hideName: { type: Boolean, required: false },
|
|
95
112
|
overlayClosable: { type: Boolean, required: false },
|
|
96
|
-
duration: { type: Number, required: false }
|
|
113
|
+
duration: { type: Number, required: false },
|
|
114
|
+
draggable: { type: Boolean, required: false },
|
|
115
|
+
axis: { type: String, required: false },
|
|
116
|
+
magnet: { type: String, required: false },
|
|
117
|
+
gapX: { type: Number, required: false },
|
|
118
|
+
gapY: { type: Number, required: false },
|
|
119
|
+
offset: { type: Object, required: false }
|
|
97
120
|
}, defaultFabProps()),
|
|
98
|
-
emits: ["click", "select"],
|
|
121
|
+
emits: ["click", "select", "update:offset"],
|
|
99
122
|
setup(__props, { expose: __expose, emit: __emit }) {
|
|
100
123
|
__expose();
|
|
101
124
|
const props = __props;
|
|
@@ -111,6 +134,7 @@ export default _defineComponent({
|
|
|
111
134
|
})
|
|
112
135
|
);
|
|
113
136
|
const onItemEntryClick = (event) => {
|
|
137
|
+
if (stopBubbling.value) return;
|
|
114
138
|
if (props.itemList && props.itemList.length > 0) {
|
|
115
139
|
visible.value = !visible.value;
|
|
116
140
|
if (visible.value) {
|
|
@@ -120,6 +144,7 @@ export default _defineComponent({
|
|
|
120
144
|
emit("click", event);
|
|
121
145
|
};
|
|
122
146
|
const onItemClick = (item, index) => {
|
|
147
|
+
if (stopBubbling.value) return;
|
|
123
148
|
visible.value = false;
|
|
124
149
|
emit("select", item, index);
|
|
125
150
|
};
|
|
@@ -128,32 +153,51 @@ export default _defineComponent({
|
|
|
128
153
|
visible.value = false;
|
|
129
154
|
}
|
|
130
155
|
};
|
|
156
|
+
const {
|
|
157
|
+
onTouchStart,
|
|
158
|
+
onTouchMove,
|
|
159
|
+
onTouchEnd,
|
|
160
|
+
onMouseDown,
|
|
161
|
+
position,
|
|
162
|
+
initialized,
|
|
163
|
+
animated,
|
|
164
|
+
bubbleId,
|
|
165
|
+
stopBubbling,
|
|
166
|
+
windowWidth,
|
|
167
|
+
windowHeight
|
|
168
|
+
} = useFloatingBubble(props, emit, {
|
|
169
|
+
disabled: visible
|
|
170
|
+
});
|
|
171
|
+
const isTop = computed(() => {
|
|
172
|
+
return props.draggable ? position.value.y > windowHeight / 2 ? false : true : !isNullish(props.top);
|
|
173
|
+
});
|
|
174
|
+
const isLeft = computed(() => {
|
|
175
|
+
return props.draggable ? position.value.x > windowWidth / 2 ? false : true : !isNullish(props.left);
|
|
176
|
+
});
|
|
131
177
|
const fabClass = computed(() => {
|
|
132
178
|
return classNames(
|
|
133
179
|
bem.b(),
|
|
134
|
-
bem.m(
|
|
135
|
-
bem.m(
|
|
180
|
+
bem.m(isTop.value ? "top" : "bottom"),
|
|
181
|
+
bem.m(isLeft.value ? "left" : "right"),
|
|
136
182
|
bem.m("visible", visible.value),
|
|
137
|
-
|
|
183
|
+
bem.m("animated", animated.value),
|
|
184
|
+
bem.m("initialized", initialized.value),
|
|
185
|
+
bem.m("draggable", props.draggable),
|
|
186
|
+
props.rootClass,
|
|
187
|
+
bubbleId
|
|
138
188
|
);
|
|
139
189
|
});
|
|
140
190
|
const fabStyle = computed(() => {
|
|
141
191
|
return stringifyStyle(props.rootStyle, {
|
|
142
192
|
zIndex: visible.value ? zIndex.value : null,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
});
|
|
152
|
-
const contentStyle = computed(() => {
|
|
153
|
-
return stringifyStyle({
|
|
154
|
-
display: realVisible.value ? "flex" : "none",
|
|
155
|
-
transitionDuration: props.duration + "ms",
|
|
156
|
-
transformOrigin: `${isNullish(props.top) ? "bottom" : "top"} ${isNullish(props.left) ? "right" : "left"}`
|
|
193
|
+
...props.draggable ? {
|
|
194
|
+
transform: `translate3d(${position.value.x}px, ${position.value.y}px, 0)`
|
|
195
|
+
} : {
|
|
196
|
+
top: props.top,
|
|
197
|
+
left: props.left,
|
|
198
|
+
right: isLeft.value ? "auto" : props.right,
|
|
199
|
+
bottom: isTop.value ? "auto" : props.bottom
|
|
200
|
+
}
|
|
157
201
|
});
|
|
158
202
|
});
|
|
159
203
|
const itemEntryClass = computed(() => {
|
|
@@ -165,10 +209,20 @@ export default _defineComponent({
|
|
|
165
209
|
background: props.background
|
|
166
210
|
});
|
|
167
211
|
});
|
|
212
|
+
const contentClass = computed(() => {
|
|
213
|
+
return classNames(bem.e("content"), transitionClass.value);
|
|
214
|
+
});
|
|
215
|
+
const contentStyle = computed(() => {
|
|
216
|
+
return stringifyStyle({
|
|
217
|
+
display: realVisible.value ? "flex" : "none",
|
|
218
|
+
transitionDuration: props.duration + "ms",
|
|
219
|
+
transformOrigin: `${isTop.value ? "top" : "bottom"} ${isLeft.value ? "left" : "right"}`
|
|
220
|
+
});
|
|
221
|
+
});
|
|
168
222
|
const itemClass = computed(() => {
|
|
169
223
|
return classNames(bem.e("item"));
|
|
170
224
|
});
|
|
171
|
-
const __returned__ = { props, emit, bem, visible, zIndex, increaseZIndex, realVisible, transitionClass, onTransitionEnd, onItemEntryClick, onItemClick, onOverlayClick,
|
|
225
|
+
const __returned__ = { props, emit, bem, visible, zIndex, increaseZIndex, realVisible, transitionClass, onTransitionEnd, onItemEntryClick, onItemClick, onOverlayClick, onTouchStart, onTouchMove, onTouchEnd, onMouseDown, position, initialized, animated, bubbleId, stopBubbling, windowWidth, windowHeight, isTop, isLeft, fabClass, fabStyle, itemEntryClass, itemEntryBtnStyle, contentClass, contentStyle, itemClass, get stringifyStyle() {
|
|
172
226
|
return stringifyStyle;
|
|
173
227
|
}, SarIcon, SarOverlay };
|
|
174
228
|
return __returned__;
|
|
@@ -7,14 +7,21 @@
|
|
|
7
7
|
right: var(--sar-fab-right);
|
|
8
8
|
bottom: var(--sar-fab-bottom);
|
|
9
9
|
z-index: var(--sar-fab-z-index);
|
|
10
|
-
gap: var(--sar-fab-item-gap);
|
|
11
10
|
}
|
|
12
11
|
|
|
13
12
|
@include e(content) {
|
|
14
13
|
@include universal;
|
|
14
|
+
position: absolute;
|
|
15
15
|
gap: var(--sar-fab-item-gap);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
@include m(draggable) {
|
|
19
|
+
top: 0;
|
|
20
|
+
left: 0;
|
|
21
|
+
right: auto;
|
|
22
|
+
bottom: auto;
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
@include m(zoom-enter-from, zoom-leave-to) {
|
|
19
26
|
opacity: 0;
|
|
20
27
|
transform: scale(0.4);
|
|
@@ -26,7 +33,8 @@
|
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
@include m(zoom-enter-active, zoom-leave-active) {
|
|
29
|
-
transition:
|
|
36
|
+
transition:
|
|
37
|
+
transform var(--sar-fab-duration) ease-out,
|
|
30
38
|
opacity var(--sar-fab-duration) ease-out;
|
|
31
39
|
}
|
|
32
40
|
|
|
@@ -52,10 +60,12 @@
|
|
|
52
60
|
@include universal;
|
|
53
61
|
font-size: var(--sar-fab-item-name-font-size);
|
|
54
62
|
color: var(--sar-fab-item-name-color);
|
|
63
|
+
white-space: nowrap;
|
|
55
64
|
}
|
|
56
65
|
|
|
57
66
|
@include e(item-btn) {
|
|
58
67
|
@include universal;
|
|
68
|
+
flex: none;
|
|
59
69
|
justify-content: center;
|
|
60
70
|
align-items: center;
|
|
61
71
|
width: var(--sar-fab-item-btn-size);
|
|
@@ -68,11 +78,21 @@
|
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
@include m(top) {
|
|
71
|
-
|
|
81
|
+
@include e(content) {
|
|
82
|
+
top: calc(100% + var(--sar-fab-item-gap));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@include m(bottom) {
|
|
87
|
+
@include e(content) {
|
|
88
|
+
bottom: calc(100% + var(--sar-fab-item-gap));
|
|
89
|
+
}
|
|
72
90
|
}
|
|
73
91
|
|
|
74
92
|
@include m(left) {
|
|
75
|
-
|
|
93
|
+
@include e(content) {
|
|
94
|
+
left: 0;
|
|
95
|
+
}
|
|
76
96
|
|
|
77
97
|
@include e(item) {
|
|
78
98
|
flex-direction: row-reverse;
|
|
@@ -80,7 +100,9 @@
|
|
|
80
100
|
}
|
|
81
101
|
|
|
82
102
|
@include m(right) {
|
|
83
|
-
|
|
103
|
+
@include e(content) {
|
|
104
|
+
right: 0;
|
|
105
|
+
}
|
|
84
106
|
}
|
|
85
107
|
|
|
86
108
|
@include m(visible) {
|
|
@@ -92,4 +114,12 @@
|
|
|
92
114
|
}
|
|
93
115
|
}
|
|
94
116
|
}
|
|
117
|
+
|
|
118
|
+
@include m(initialized) {
|
|
119
|
+
opacity: 1;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@include m(animated) {
|
|
123
|
+
transition: transform var(--sar-fab-duration);
|
|
124
|
+
}
|
|
95
125
|
}
|
|
@@ -40,15 +40,16 @@ import FloatingBubble from 'sard-uniapp/components/floating-bubble/floating-bubb
|
|
|
40
40
|
|
|
41
41
|
### FloatingBubbleProps
|
|
42
42
|
|
|
43
|
-
| 属性
|
|
44
|
-
|
|
|
45
|
-
| root-class
|
|
46
|
-
| root-style
|
|
47
|
-
| axis
|
|
48
|
-
| magnet
|
|
49
|
-
| gap-x
|
|
50
|
-
| gap-y
|
|
51
|
-
| offset (v-model)
|
|
43
|
+
| 属性 | 描述 | 类型 | 默认值 |
|
|
44
|
+
| ---------------------------- | --------------------------------------- | ------------------------------ | ------ |
|
|
45
|
+
| root-class | 组件根元素类名 | string | - |
|
|
46
|
+
| root-style | 组件根元素样式 | StyleValue | - |
|
|
47
|
+
| axis | 允许拖拽的方向轴 | 'x' \| 'y' \| 'both' \| 'none' | 'y' |
|
|
48
|
+
| magnet | 吸附到指定轴最近的一边 | 'x' \| 'y' | - |
|
|
49
|
+
| gap-x | 气泡与窗口左右两边的最小间距,单位为 px | number | 24 |
|
|
50
|
+
| gap-y | 气泡与窗口上下两边的最小间距,单位为 px | number | 24 |
|
|
51
|
+
| offset (v-model) | 控制气泡的位置 | { x: number; y: number } | - |
|
|
52
|
+
| draggable <sup>1.24.2+</sup> | 是否可拖拽 | boolean | true |
|
|
52
53
|
|
|
53
54
|
### FloatingBubbleSlots
|
|
54
55
|
|
|
@@ -2,6 +2,7 @@ import { type StyleValue } from 'vue';
|
|
|
2
2
|
export interface FloatingBubbleProps {
|
|
3
3
|
rootStyle?: StyleValue;
|
|
4
4
|
rootClass?: string;
|
|
5
|
+
draggable?: boolean;
|
|
5
6
|
axis?: 'x' | 'y' | 'both' | 'none';
|
|
6
7
|
magnet?: 'x' | 'y';
|
|
7
8
|
gapX?: number;
|
|
@@ -12,6 +13,7 @@ export interface FloatingBubbleProps {
|
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
export declare const defaultFloatingBubbleProps: {
|
|
16
|
+
draggable: boolean;
|
|
15
17
|
axis: FloatingBubbleProps["axis"];
|
|
16
18
|
gapX: number;
|
|
17
19
|
gapY: number;
|
|
@@ -14,6 +14,7 @@ declare const __VLS_component: import("vue").DefineComponent<FloatingBubbleProps
|
|
|
14
14
|
}) => any) | undefined;
|
|
15
15
|
}>, {
|
|
16
16
|
axis: "x" | "y" | "both" | "none";
|
|
17
|
+
draggable: boolean;
|
|
17
18
|
gapX: number;
|
|
18
19
|
gapY: number;
|
|
19
20
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
@@ -15,20 +15,12 @@
|
|
|
15
15
|
|
|
16
16
|
<script>
|
|
17
17
|
import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from "vue";
|
|
18
|
-
import { computed
|
|
19
|
-
import {
|
|
20
|
-
classNames,
|
|
21
|
-
stringifyStyle,
|
|
22
|
-
createBem,
|
|
23
|
-
getBoundingClientRect,
|
|
24
|
-
uniqid,
|
|
25
|
-
getWindowInfo,
|
|
26
|
-
clamp
|
|
27
|
-
} from "../../utils";
|
|
18
|
+
import { computed } from "vue";
|
|
19
|
+
import { classNames, stringifyStyle, createBem } from "../../utils";
|
|
28
20
|
import {
|
|
29
21
|
defaultFloatingBubbleProps
|
|
30
22
|
} from "./common";
|
|
31
|
-
import {
|
|
23
|
+
import { useFloatingBubble } from "./useFloatingBubble";
|
|
32
24
|
/**
|
|
33
25
|
* @property {string} rootClass 组件根元素类名,默认值:-。
|
|
34
26
|
* @property {StyleValue} rootStyle 组件根元素样式,默认值:-。
|
|
@@ -37,6 +29,7 @@ import { useMouseDown, useTimeout } from "../../use";
|
|
|
37
29
|
* @property {number} gapX 气泡与窗口左右两边的最小间距,单位为 px,默认值:24。
|
|
38
30
|
* @property {number} gapY 气泡与窗口上下两边的最小间距,单位为 px,默认值:24。
|
|
39
31
|
* @property {{ x: number; y: number }} offset 控制气泡的位置,默认值:-。
|
|
32
|
+
* @property {boolean} draggable 是否可拖拽,默认值:true。
|
|
40
33
|
* @event {(event: any) => void} click 点击时触发
|
|
41
34
|
* @event {(offset: { x: number; y: number }) => void} update 因用户拖拽导致位置改变时触发
|
|
42
35
|
*/
|
|
@@ -51,6 +44,7 @@ export default _defineComponent({
|
|
|
51
44
|
props: _mergeDefaults({
|
|
52
45
|
rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
|
|
53
46
|
rootClass: { type: String, required: false },
|
|
47
|
+
draggable: { type: Boolean, required: false },
|
|
54
48
|
axis: { type: String, required: false },
|
|
55
49
|
magnet: { type: String, required: false },
|
|
56
50
|
gapX: { type: Number, required: false },
|
|
@@ -63,112 +57,16 @@ export default _defineComponent({
|
|
|
63
57
|
const props = __props;
|
|
64
58
|
const emit = __emit;
|
|
65
59
|
const bem = createBem("floating-bubble");
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
x: 0,
|
|
77
|
-
y: 0
|
|
78
|
-
});
|
|
79
|
-
watch(
|
|
80
|
-
() => props.offset,
|
|
81
|
-
() => {
|
|
82
|
-
if (props.offset) {
|
|
83
|
-
position.value = props.offset;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
const animated = ref(false);
|
|
88
|
-
const { start: nonAnimatedLater, stop: cancelNonAnimated } = useTimeout(() => {
|
|
89
|
-
animated.value = false;
|
|
90
|
-
}, 500);
|
|
91
|
-
function getMinX() {
|
|
92
|
-
return props.gapX;
|
|
93
|
-
}
|
|
94
|
-
function getMaxX() {
|
|
95
|
-
return windowWidth - props.gapX - bubbleRect.width;
|
|
96
|
-
}
|
|
97
|
-
function getMinY() {
|
|
98
|
-
return props.gapY + 44 + 25;
|
|
99
|
-
}
|
|
100
|
-
function getMaxY() {
|
|
101
|
-
return windowHeight - props.gapY - bubbleRect.height;
|
|
102
|
-
}
|
|
103
|
-
onMounted(async () => {
|
|
104
|
-
bubbleRect = await getBoundingClientRect(`.${bubbleId}`, instance);
|
|
105
|
-
position.value = props.offset ?? {
|
|
106
|
-
x: getMaxX(),
|
|
107
|
-
y: getMaxY()
|
|
108
|
-
};
|
|
109
|
-
bubbleRect = void 0;
|
|
110
|
-
initialized.value = true;
|
|
111
|
-
});
|
|
112
|
-
const onTouchStart = async (event) => {
|
|
113
|
-
cancelNonAnimated();
|
|
114
|
-
animated.value = false;
|
|
115
|
-
downCoord = {
|
|
116
|
-
x: event.touches[0].clientX,
|
|
117
|
-
y: event.touches[0].clientY
|
|
118
|
-
};
|
|
119
|
-
bubbleRect = await getBoundingClientRect(`.${bubbleId}`, instance);
|
|
120
|
-
};
|
|
121
|
-
const onTouchMove = (event) => {
|
|
122
|
-
if (!bubbleRect) {
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
let x = 0;
|
|
126
|
-
let y = 0;
|
|
127
|
-
if (props.axis === "none") {
|
|
128
|
-
x = getMaxX();
|
|
129
|
-
y = getMaxY();
|
|
130
|
-
} else {
|
|
131
|
-
const deltaX = event.touches[0].clientX - downCoord.x;
|
|
132
|
-
const deltaY = event.touches[0].clientY - downCoord.y;
|
|
133
|
-
x = bubbleRect.left + deltaX;
|
|
134
|
-
y = bubbleRect.top + deltaY;
|
|
135
|
-
x = clamp(x, getMinX(), getMaxX());
|
|
136
|
-
y = clamp(y, getMinY(), getMaxY());
|
|
137
|
-
if (props.axis === "y") {
|
|
138
|
-
x = getMaxX();
|
|
139
|
-
} else if (props.axis === "x") {
|
|
140
|
-
y = getMaxY();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const offset = {
|
|
144
|
-
x,
|
|
145
|
-
y
|
|
146
|
-
};
|
|
147
|
-
position.value = offset;
|
|
148
|
-
emit("update:offset", offset);
|
|
149
|
-
};
|
|
150
|
-
const onTouchEnd = () => {
|
|
151
|
-
if (bubbleRect) {
|
|
152
|
-
if (props.magnet) {
|
|
153
|
-
let { x, y } = position.value;
|
|
154
|
-
if (props.magnet === "x") {
|
|
155
|
-
x = x < (windowWidth - bubbleRect.width) / 2 ? getMinX() : getMaxX();
|
|
156
|
-
} else if (props.magnet === "y") {
|
|
157
|
-
y = y < (windowHeight - bubbleRect.height) / 2 ? getMinY() : getMaxY();
|
|
158
|
-
}
|
|
159
|
-
const offset = {
|
|
160
|
-
x,
|
|
161
|
-
y
|
|
162
|
-
};
|
|
163
|
-
position.value = offset;
|
|
164
|
-
emit("update:offset", offset);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
animated.value = true;
|
|
168
|
-
nonAnimatedLater();
|
|
169
|
-
bubbleRect = void 0;
|
|
170
|
-
};
|
|
171
|
-
const onMouseDown = useMouseDown(onTouchStart, onTouchMove, onTouchEnd);
|
|
60
|
+
const {
|
|
61
|
+
onTouchStart,
|
|
62
|
+
onTouchMove,
|
|
63
|
+
onTouchEnd,
|
|
64
|
+
onMouseDown,
|
|
65
|
+
position,
|
|
66
|
+
initialized,
|
|
67
|
+
animated,
|
|
68
|
+
bubbleId
|
|
69
|
+
} = useFloatingBubble(props, emit);
|
|
172
70
|
const onClick = (event) => {
|
|
173
71
|
emit("click", event);
|
|
174
72
|
};
|
|
@@ -182,19 +80,12 @@ export default _defineComponent({
|
|
|
182
80
|
);
|
|
183
81
|
});
|
|
184
82
|
const floatingBubbleStyle = computed(() => {
|
|
83
|
+
const { x, y } = position.value;
|
|
185
84
|
return stringifyStyle(props.rootStyle, {
|
|
186
|
-
transform: `translate3d(${
|
|
85
|
+
transform: `translate3d(${x}px, ${y}px, 0)`
|
|
187
86
|
});
|
|
188
87
|
});
|
|
189
|
-
const __returned__ = { props, emit, bem,
|
|
190
|
-
return bubbleRect;
|
|
191
|
-
}, set bubbleRect(v) {
|
|
192
|
-
bubbleRect = v;
|
|
193
|
-
}, windowWidth, windowHeight, get downCoord() {
|
|
194
|
-
return downCoord;
|
|
195
|
-
}, set downCoord(v) {
|
|
196
|
-
downCoord = v;
|
|
197
|
-
}, initialized, position, animated, nonAnimatedLater, cancelNonAnimated, getMinX, getMaxX, getMinY, getMaxY, onTouchStart, onTouchMove, onTouchEnd, onMouseDown, onClick, floatingBubbleClass, floatingBubbleStyle };
|
|
88
|
+
const __returned__ = { props, emit, bem, onTouchStart, onTouchMove, onTouchEnd, onMouseDown, position, initialized, animated, bubbleId, onClick, floatingBubbleClass, floatingBubbleStyle };
|
|
198
89
|
return __returned__;
|
|
199
90
|
}
|
|
200
91
|
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MaybeRef } from 'vue';
|
|
2
|
+
export interface UseFloatingBubbleProps {
|
|
3
|
+
draggable?: boolean;
|
|
4
|
+
gapX: number;
|
|
5
|
+
gapY: number;
|
|
6
|
+
axis: 'x' | 'y' | 'none' | 'both';
|
|
7
|
+
magnet?: 'x' | 'y';
|
|
8
|
+
offset?: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export interface UseFloatingBubbleEmits {
|
|
14
|
+
(e: 'update:offset', offset: {
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
}): void;
|
|
18
|
+
}
|
|
19
|
+
export interface UseFloatingBubbleOptions {
|
|
20
|
+
disabled?: MaybeRef<boolean>;
|
|
21
|
+
}
|
|
22
|
+
export declare function useFloatingBubble(props: UseFloatingBubbleProps, emit: UseFloatingBubbleEmits, options?: UseFloatingBubbleOptions): {
|
|
23
|
+
onTouchStart: (event: TouchEvent) => Promise<void>;
|
|
24
|
+
onTouchMove: (event: TouchEvent) => void;
|
|
25
|
+
onTouchEnd: () => void;
|
|
26
|
+
onMouseDown: (event: MouseEvent) => void;
|
|
27
|
+
position: import("vue").Ref<{
|
|
28
|
+
x: number;
|
|
29
|
+
y: number;
|
|
30
|
+
}, {
|
|
31
|
+
x: number;
|
|
32
|
+
y: number;
|
|
33
|
+
} | {
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
}>;
|
|
37
|
+
initialized: import("vue").Ref<boolean, boolean>;
|
|
38
|
+
animated: import("vue").Ref<boolean, boolean>;
|
|
39
|
+
bubbleId: string;
|
|
40
|
+
stopBubbling: import("vue").Ref<boolean, boolean>;
|
|
41
|
+
windowWidth: number;
|
|
42
|
+
windowHeight: number;
|
|
43
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { computed, getCurrentInstance, onMounted, ref, unref, watch, } from 'vue';
|
|
2
|
+
import { useMouseDown, useTimeout } from '../../use';
|
|
3
|
+
import { clamp, getBoundingClientRect, getWindowInfo, uniqid, } from '../../utils';
|
|
4
|
+
export function useFloatingBubble(props, emit, options = {}) {
|
|
5
|
+
const disabled = computed(() => !props.draggable || unref(options.disabled));
|
|
6
|
+
const instance = getCurrentInstance();
|
|
7
|
+
const bubbleId = uniqid();
|
|
8
|
+
const initialized = ref(false);
|
|
9
|
+
let bubbleRect;
|
|
10
|
+
const { windowWidth, windowHeight } = getWindowInfo();
|
|
11
|
+
let downCoord = {
|
|
12
|
+
x: 0,
|
|
13
|
+
y: 0,
|
|
14
|
+
};
|
|
15
|
+
const position = ref({
|
|
16
|
+
x: 0,
|
|
17
|
+
y: 0,
|
|
18
|
+
});
|
|
19
|
+
const animated = ref(false);
|
|
20
|
+
const stopBubbling = ref(false);
|
|
21
|
+
const { start: nonAnimatedLater, stop: cancelNonAnimated } = useTimeout(() => {
|
|
22
|
+
animated.value = false;
|
|
23
|
+
}, 500);
|
|
24
|
+
function getMinX() {
|
|
25
|
+
return props.gapX;
|
|
26
|
+
}
|
|
27
|
+
function getMaxX() {
|
|
28
|
+
return windowWidth - props.gapX - bubbleRect.width;
|
|
29
|
+
}
|
|
30
|
+
function getMinY() {
|
|
31
|
+
return props.gapY + 44 + 25;
|
|
32
|
+
}
|
|
33
|
+
function getMaxY() {
|
|
34
|
+
return windowHeight - props.gapY - bubbleRect.height;
|
|
35
|
+
}
|
|
36
|
+
const onTouchStart = async (event) => {
|
|
37
|
+
stopBubbling.value = false;
|
|
38
|
+
if (disabled.value)
|
|
39
|
+
return;
|
|
40
|
+
cancelNonAnimated();
|
|
41
|
+
animated.value = false;
|
|
42
|
+
downCoord = {
|
|
43
|
+
x: event.touches[0].clientX,
|
|
44
|
+
y: event.touches[0].clientY,
|
|
45
|
+
};
|
|
46
|
+
bubbleRect = await getBoundingClientRect(`.${bubbleId}`, instance);
|
|
47
|
+
};
|
|
48
|
+
const onTouchMove = (event) => {
|
|
49
|
+
if (disabled.value)
|
|
50
|
+
return;
|
|
51
|
+
if (!bubbleRect) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
let x = 0;
|
|
55
|
+
let y = 0;
|
|
56
|
+
if (props.axis === 'none') {
|
|
57
|
+
x = getMaxX();
|
|
58
|
+
y = getMaxY();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
const deltaX = event.touches[0].clientX - downCoord.x;
|
|
62
|
+
const deltaY = event.touches[0].clientY - downCoord.y;
|
|
63
|
+
const deviation = 10;
|
|
64
|
+
if (Math.abs(deltaX) > deviation || Math.abs(deltaY) > deviation) {
|
|
65
|
+
stopBubbling.value = true;
|
|
66
|
+
}
|
|
67
|
+
x = bubbleRect.left + deltaX;
|
|
68
|
+
y = bubbleRect.top + deltaY;
|
|
69
|
+
x = clamp(x, getMinX(), getMaxX());
|
|
70
|
+
y = clamp(y, getMinY(), getMaxY());
|
|
71
|
+
if (props.axis === 'y') {
|
|
72
|
+
x = getMaxX();
|
|
73
|
+
}
|
|
74
|
+
else if (props.axis === 'x') {
|
|
75
|
+
y = getMaxY();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const offset = {
|
|
79
|
+
x,
|
|
80
|
+
y,
|
|
81
|
+
};
|
|
82
|
+
position.value = offset;
|
|
83
|
+
emit('update:offset', offset);
|
|
84
|
+
};
|
|
85
|
+
const onTouchEnd = () => {
|
|
86
|
+
if (disabled.value)
|
|
87
|
+
return;
|
|
88
|
+
if (bubbleRect) {
|
|
89
|
+
if (props.magnet) {
|
|
90
|
+
let { x, y } = position.value;
|
|
91
|
+
if (props.magnet === 'x') {
|
|
92
|
+
x = x < (windowWidth - bubbleRect.width) / 2 ? getMinX() : getMaxX();
|
|
93
|
+
}
|
|
94
|
+
else if (props.magnet === 'y') {
|
|
95
|
+
y = y < (windowHeight - bubbleRect.height) / 2 ? getMinY() : getMaxY();
|
|
96
|
+
}
|
|
97
|
+
const offset = {
|
|
98
|
+
x,
|
|
99
|
+
y,
|
|
100
|
+
};
|
|
101
|
+
position.value = offset;
|
|
102
|
+
emit('update:offset', offset);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
animated.value = true;
|
|
106
|
+
nonAnimatedLater();
|
|
107
|
+
bubbleRect = undefined;
|
|
108
|
+
};
|
|
109
|
+
const onMouseDown = useMouseDown(onTouchStart, onTouchMove, onTouchEnd);
|
|
110
|
+
onMounted(async () => {
|
|
111
|
+
bubbleRect = await getBoundingClientRect(`.${bubbleId}`, instance);
|
|
112
|
+
position.value = props.offset ?? {
|
|
113
|
+
x: getMaxX(),
|
|
114
|
+
y: getMaxY(),
|
|
115
|
+
};
|
|
116
|
+
bubbleRect = undefined;
|
|
117
|
+
initialized.value = true;
|
|
118
|
+
});
|
|
119
|
+
watch(() => props.offset, () => {
|
|
120
|
+
if (props.offset) {
|
|
121
|
+
position.value = props.offset;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return {
|
|
125
|
+
onTouchStart,
|
|
126
|
+
onTouchMove,
|
|
127
|
+
onTouchEnd,
|
|
128
|
+
onMouseDown,
|
|
129
|
+
position,
|
|
130
|
+
initialized,
|
|
131
|
+
animated,
|
|
132
|
+
bubbleId,
|
|
133
|
+
stopBubbling,
|
|
134
|
+
windowWidth,
|
|
135
|
+
windowHeight,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -77,14 +77,14 @@ type PopoutBeforeClose = (
|
|
|
77
77
|
|
|
78
78
|
### PopoutSlots
|
|
79
79
|
|
|
80
|
-
| 插槽 | 描述 | 属性
|
|
81
|
-
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
82
|
-
| default | 自定义主体内容 | -
|
|
83
|
-
| title | 自定义标题 | -
|
|
84
|
-
| title-prepend <sup>1.19.2+</sup> | 自定义标题前面内容 | -
|
|
85
|
-
| cancel
|
|
86
|
-
| confirm
|
|
87
|
-
| visible | 同默认插槽,额外接收一些判断弹出框显示状态的属性,用于小程序端多节点渲染的性能问题;`whole` 当开始显示到完全隐藏时为真,`already` 当开始显示时到以后都为真 | { whole: boolean, already: boolean }
|
|
80
|
+
| 插槽 | 描述 | 属性 |
|
|
81
|
+
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
82
|
+
| default | 自定义主体内容 | - |
|
|
83
|
+
| title | 自定义标题 | - |
|
|
84
|
+
| title-prepend <sup>1.19.2+</sup> | 自定义标题前面内容 | - |
|
|
85
|
+
| cancel <sup>1.24.2+</sup> | 自定义取消按钮内容 | { onClick: () => void; loading: boolean; visible?: boolean; text: string } |
|
|
86
|
+
| confirm <sup>1.24.2+</sup> | 自定义确定按钮内容 | { onClick: () => void; loading: boolean; visible?: boolean; text: string; disabled?: boolean } |
|
|
87
|
+
| visible | 同默认插槽,额外接收一些判断弹出框显示状态的属性,用于小程序端多节点渲染的性能问题;`whole` 当开始显示到完全隐藏时为真,`already` 当开始显示时到以后都为真 | { whole: boolean, already: boolean } |
|
|
88
88
|
|
|
89
89
|
### PopoutEmits
|
|
90
90
|
|
|
@@ -34,8 +34,19 @@ export declare const defaultPopoutProps: {
|
|
|
34
34
|
export interface PopoutSlots {
|
|
35
35
|
default?(props: Record<string, never>): any;
|
|
36
36
|
title?(props: Record<string, never>): any;
|
|
37
|
-
cancel?(props:
|
|
38
|
-
|
|
37
|
+
cancel?(props: {
|
|
38
|
+
onClick: () => void;
|
|
39
|
+
loading: boolean;
|
|
40
|
+
visible?: boolean;
|
|
41
|
+
text: string;
|
|
42
|
+
}): any;
|
|
43
|
+
confirm?(props: {
|
|
44
|
+
onClick: () => void;
|
|
45
|
+
loading: boolean;
|
|
46
|
+
visible?: boolean;
|
|
47
|
+
text: string;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
}): any;
|
|
39
50
|
visible?(props: {
|
|
40
51
|
whole: boolean;
|
|
41
52
|
already: boolean;
|
|
@@ -12,18 +12,23 @@
|
|
|
12
12
|
<view :class="popoutClass" :style="popoutStyle" @transitionend.stop>
|
|
13
13
|
<view :class="classNames(bem.e('header'), bem.em('header', props.type))">
|
|
14
14
|
<view v-if="type === 'compact'" :class="bem.e('button-wrap')">
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
:root-class="classNames(bem.e('header-cancel'))"
|
|
15
|
+
<slot
|
|
16
|
+
name="cancel"
|
|
17
|
+
:on-click="onCancel"
|
|
19
18
|
:loading="loading.cancel"
|
|
20
|
-
|
|
21
|
-
@click="onCancel"
|
|
19
|
+
:text="mergedCancelText"
|
|
22
20
|
>
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
<sar-button
|
|
22
|
+
type="pale-text"
|
|
23
|
+
theme="neutral"
|
|
24
|
+
:root-class="classNames(bem.e('header-cancel'))"
|
|
25
|
+
:loading="loading.cancel"
|
|
26
|
+
block
|
|
27
|
+
@click="onCancel"
|
|
28
|
+
>
|
|
29
|
+
{{ mergedCancelText }}
|
|
30
|
+
</sar-button>
|
|
31
|
+
</slot>
|
|
27
32
|
</view>
|
|
28
33
|
<slot name="title-prepend"></slot>
|
|
29
34
|
<view :class="bem.e('title')">
|
|
@@ -33,19 +38,25 @@
|
|
|
33
38
|
<slot v-else-if="$slots.title" name="title"></slot>
|
|
34
39
|
</view>
|
|
35
40
|
<view v-if="type === 'compact'" :class="bem.e('button-wrap')">
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
:root-class="classNames(bem.e('header-confirm'))"
|
|
40
|
-
:loading="loading.confirm"
|
|
41
|
+
<slot
|
|
42
|
+
name="confirm"
|
|
43
|
+
:on-click="onConfirm"
|
|
41
44
|
:disabled="confirmDisabled"
|
|
42
|
-
|
|
43
|
-
|
|
45
|
+
:loading="loading.confirm"
|
|
46
|
+
:text="mergedConfirmText"
|
|
44
47
|
>
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
<sar-button
|
|
49
|
+
type="pale-text"
|
|
50
|
+
theme="primary"
|
|
51
|
+
:root-class="classNames(bem.e('header-confirm'))"
|
|
52
|
+
:loading="loading.confirm"
|
|
53
|
+
:disabled="confirmDisabled"
|
|
54
|
+
block
|
|
55
|
+
@click="onConfirm"
|
|
56
|
+
>
|
|
57
|
+
{{ mergedConfirmText }}
|
|
58
|
+
</sar-button>
|
|
59
|
+
</slot>
|
|
49
60
|
</view>
|
|
50
61
|
<view
|
|
51
62
|
v-if="type === 'loose' && showClose"
|
|
@@ -60,33 +71,46 @@
|
|
|
60
71
|
<slot></slot>
|
|
61
72
|
<slot name="visible" :whole="wholeVisible" :already="already"></slot>
|
|
62
73
|
<view v-if="showFooter && type === 'loose'" :class="bem.e('footer')">
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
round
|
|
74
|
+
<slot
|
|
75
|
+
name="cancel"
|
|
76
|
+
:on-click="onCancel"
|
|
77
|
+
:visible="showCancel"
|
|
68
78
|
:loading="loading.cancel"
|
|
69
|
-
|
|
70
|
-
@click="onCancel"
|
|
79
|
+
:text="mergedCancelText"
|
|
71
80
|
>
|
|
72
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
<sar-button
|
|
82
|
+
v-if="showCancel"
|
|
83
|
+
type="pale"
|
|
84
|
+
theme="primary"
|
|
85
|
+
round
|
|
86
|
+
:loading="loading.cancel"
|
|
87
|
+
block
|
|
88
|
+
@click="onCancel"
|
|
89
|
+
>
|
|
90
|
+
{{ mergedCancelText }}
|
|
91
|
+
</sar-button>
|
|
92
|
+
</slot>
|
|
93
|
+
<slot
|
|
94
|
+
name="confirm"
|
|
95
|
+
:visible="showConfirm"
|
|
96
|
+
:on-click="onConfirm"
|
|
82
97
|
:disabled="confirmDisabled"
|
|
83
|
-
|
|
84
|
-
|
|
98
|
+
:loading="loading.confirm"
|
|
99
|
+
:text="mergedConfirmText"
|
|
85
100
|
>
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
101
|
+
<sar-button
|
|
102
|
+
v-if="showConfirm"
|
|
103
|
+
type="default"
|
|
104
|
+
theme="primary"
|
|
105
|
+
round
|
|
106
|
+
:loading="loading.confirm"
|
|
107
|
+
:disabled="confirmDisabled"
|
|
108
|
+
block
|
|
109
|
+
@click="onConfirm"
|
|
110
|
+
>
|
|
111
|
+
{{ mergedConfirmText }}
|
|
112
|
+
</sar-button>
|
|
113
|
+
</slot>
|
|
90
114
|
</view>
|
|
91
115
|
</view>
|
|
92
116
|
</sar-popup>
|
|
@@ -269,13 +293,19 @@ export default _defineComponent({
|
|
|
269
293
|
const onCancel = () => {
|
|
270
294
|
perhapsClose("cancel");
|
|
271
295
|
};
|
|
296
|
+
const mergedConfirmText = computed(() => {
|
|
297
|
+
return props.confirmText || t("confirm");
|
|
298
|
+
});
|
|
299
|
+
const mergedCancelText = computed(() => {
|
|
300
|
+
return props.cancelText || t("cancel");
|
|
301
|
+
});
|
|
272
302
|
const popoutClass = computed(() => {
|
|
273
303
|
return classNames(bem.b(), props.rootClass);
|
|
274
304
|
});
|
|
275
305
|
const popoutStyle = computed(() => {
|
|
276
306
|
return stringifyStyle(props.rootStyle);
|
|
277
307
|
});
|
|
278
|
-
const __returned__ = { props, emit, bem, t, innerVisible, already, wholeVisible, onBeforeEnter, onAfterLeave, callVisibleHook, onVisibleHook, loading, readonlyLoading, asyncSet, perhapsClose, onOverlayClick, onCloseClick, onConfirm, onCancel, popoutClass, popoutStyle, get classNames() {
|
|
308
|
+
const __returned__ = { props, emit, bem, t, innerVisible, already, wholeVisible, onBeforeEnter, onAfterLeave, callVisibleHook, onVisibleHook, loading, readonlyLoading, asyncSet, perhapsClose, onOverlayClick, onCloseClick, onConfirm, onCancel, mergedConfirmText, mergedCancelText, popoutClass, popoutStyle, get classNames() {
|
|
279
309
|
return classNames;
|
|
280
310
|
}, SarPopup, SarButton, SarIcon };
|
|
281
311
|
return __returned__;
|
package/components/tag/tag.vue
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<view :class="tagClass" :style="tagStyle" @click="$emit('click', $event)">
|
|
3
3
|
<slot></slot>
|
|
4
|
-
<view
|
|
4
|
+
<view
|
|
5
|
+
v-if="closable"
|
|
6
|
+
:class="iconClass"
|
|
7
|
+
@click.stop="$emit('close', $event)"
|
|
8
|
+
>
|
|
5
9
|
<sar-icon family="sari" name="close" />
|
|
6
10
|
</view>
|
|
7
11
|
</view>
|