hy-app 0.2.6 → 0.2.7

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.
@@ -4,7 +4,7 @@
4
4
  class="hy-transition"
5
5
  ref="u-transition"
6
6
  @tap.stop="clickHandler"
7
- :class="classes"
7
+ :class="[classes, customClass]"
8
8
  :style="[mergeStyle]"
9
9
  >
10
10
  <slot />
@@ -12,92 +12,85 @@
12
12
  </template>
13
13
 
14
14
  <script setup lang="ts">
15
- import {
16
- computed,
17
- type CSSProperties,
18
- ref,
19
- watch,
20
- nextTick,
21
- toRefs,
22
- } from "vue";
23
- import { sleep } from "../../utils";
24
- import defaultProps from "./props";
25
- import type IProps from "./typing";
15
+ import { computed, type CSSProperties, ref, watch, nextTick, toRefs } from 'vue'
16
+ import { sleep } from '../../utils'
17
+ import defaultProps from './props'
18
+ import type IProps from './typing'
26
19
 
27
- const props = withDefaults(defineProps<IProps>(), defaultProps);
28
- const { show, mode, duration } = toRefs(props);
20
+ const props = withDefaults(defineProps<IProps>(), defaultProps)
21
+ const { show, mode, duration, customStyle, timingFunction } = toRefs(props)
29
22
  const emit = defineEmits([
30
- "click",
31
- "beforeEnter",
32
- "enter",
33
- "afterEnter",
34
- "beforeLeave",
35
- "leave",
36
- "afterLeave",
37
- ]);
23
+ 'click',
24
+ 'beforeEnter',
25
+ 'enter',
26
+ 'afterEnter',
27
+ 'beforeLeave',
28
+ 'leave',
29
+ 'afterLeave',
30
+ ])
38
31
 
39
- const hasInit = ref<boolean>(false); // 是否显示/隐藏组件
40
- const viewStyle = ref<CSSProperties>({}); // 组件内部的样式
41
- const status = ref<string>(""); // 记录组件动画的状态
42
- const transitionEnded = ref<boolean>(false); // 组件是否结束的标记
43
- const display = ref<boolean>(false); // 组件是否展示
44
- const classes = ref<string>(""); // 应用的类名
32
+ const hasInit = ref<boolean>(false) // 是否显示/隐藏组件
33
+ const viewStyle = ref<CSSProperties>({}) // 组件内部的样式
34
+ const status = ref<string>('') // 记录组件动画的状态
35
+ const transitionEnded = ref<boolean>(false) // 组件是否结束的标记
36
+ const display = ref<boolean>(false) // 组件是否展示
37
+ const classes = ref<string>('') // 应用的类名
45
38
 
46
39
  // #ifndef APP-NVUE
47
40
  // 定义类名,通过给元素动态切换类名,赋予元素一定的css动画样式
48
41
  const getClassNames = (name: string) => ({
49
42
  enter: `u-${name}-enter u-${name}-enter-active`,
50
- "enter-to": `u-${name}-enter-to u-${name}-enter-active`,
43
+ 'enter-to': `u-${name}-enter-to u-${name}-enter-active`,
51
44
  leave: `u-${name}-leave u-${name}-leave-active`,
52
- "leave-to": `u-${name}-leave-to u-${name}-leave-active`,
53
- });
45
+ 'leave-to': `u-${name}-leave-to u-${name}-leave-active`,
46
+ })
54
47
  // #endif
55
48
 
56
49
  // #ifndef APP-NVUE
57
50
  // vue版本的组件进场处理
58
51
  const vueEnter = async () => {
59
52
  // 动画进入时的类名
60
- const classNames = getClassNames(mode.value);
53
+ const classNames = getClassNames(mode.value)
61
54
  // 定义状态和发出动画进入前事件
62
- status.value = "enter";
63
- emit("beforeEnter");
64
- hasInit.value = true;
65
- display.value = true;
66
- classes.value = classNames.enter;
67
- await nextTick();
55
+ status.value = 'enter'
56
+ emit('beforeEnter')
57
+ hasInit.value = true
58
+ display.value = true
59
+ classes.value = classNames.enter
60
+ await nextTick()
68
61
  {
69
62
  // https://github.com/umicro/uView2.0/issues/545
70
- await sleep(20);
63
+ await sleep(20)
71
64
  // 标识动画尚未结束
72
- emit("enter");
73
- transitionEnded.value = false;
65
+ emit('enter')
66
+ transitionEnded.value = false
74
67
  // 组件动画进入后触发的事件
75
- emit("afterEnter");
68
+ emit('afterEnter')
76
69
  // 赋予组件enter-to类名
77
- classes.value = classNames["enter-to"];
70
+ classes.value = classNames['enter-to']
78
71
  }
79
- };
72
+ }
80
73
  // 动画离场处理
81
74
  const vueLeave = async () => {
82
75
  // 如果不是展示状态,无需执行逻辑
83
- if (!display.value) return;
84
- const classNames = getClassNames(mode.value);
76
+ if (!display.value) return
77
+ const classNames = getClassNames(mode.value)
85
78
  // 标记离开状态和发出事件
86
- status.value = "leave";
87
- emit("beforeLeave");
79
+ status.value = 'leave'
80
+ emit('beforeLeave')
88
81
  // 获得类名
89
- classes.value = classNames.leave;
82
+ classes.value = classNames.leave
90
83
 
91
- await nextTick();
84
+ await nextTick()
92
85
  {
93
86
  // 动画正在离场的状态
94
- transitionEnded.value = false;
95
- emit("leave");
87
+ transitionEnded.value = false
88
+ emit('leave')
96
89
  // 组件执行动画,到了执行的执行时间后,执行一些额外处理
97
- setTimeout(() => onTransitionEnd(), duration.value);
98
- classes.value = classNames["leave-to"];
90
+ setTimeout(() => onTransitionEnd(), duration.value)
91
+ classes.value = classNames['leave-to']
99
92
  }
100
- };
93
+ }
101
94
  // #endif
102
95
 
103
96
  watch(
@@ -108,50 +101,49 @@ watch(
108
101
  // newVal ? nvueEnter() : nvueLeave()
109
102
  // #endif
110
103
  // #ifndef APP-NVUE
111
- newVal ? vueEnter() : vueLeave();
104
+ newVal ? vueEnter() : vueLeave()
112
105
  // #endif
113
106
  },
114
107
  { immediate: true },
115
- );
108
+ )
116
109
 
117
110
  const mergeStyle = computed(() => {
118
- const { duration, customStyle, timingFunction } = props;
119
111
  return {
120
112
  // #ifndef APP-NVUE
121
- transitionDuration: `${duration}ms`,
113
+ transitionDuration: `${duration.value}ms`,
122
114
  // display: `${this.display ? '' : 'none'}`,
123
- transitionTimingFunction: timingFunction,
115
+ transitionTimingFunction: timingFunction.value,
124
116
  // #endif
125
117
  // 避免自定义样式影响到动画属性,所以写在viewStyle前面
126
- ...customStyle,
118
+ ...customStyle.value,
127
119
  ...viewStyle,
128
- };
129
- });
120
+ }
121
+ })
130
122
 
131
123
  /**
132
124
  * @description 组件被点击发出事件
133
125
  * */
134
126
  const clickHandler = () => {
135
- emit("click");
136
- };
127
+ emit('click')
128
+ }
137
129
 
138
130
  const onTransitionEnd = () => {
139
131
  // 如果已经是结束的状态,无需再处理
140
- if (transitionEnded.value) return;
141
- transitionEnded.value = true;
132
+ if (transitionEnded.value) return
133
+ transitionEnded.value = true
142
134
  // 发出组件动画执行后的事件
143
- emit(status.value === "leave" ? "afterLeave" : "afterEnter");
135
+ emit(status.value === 'leave' ? 'afterLeave' : 'afterEnter')
144
136
  if (!show.value && display.value) {
145
- display.value = false;
146
- hasInit.value = false;
137
+ display.value = false
138
+ hasInit.value = false
147
139
  }
148
- };
140
+ }
149
141
  </script>
150
142
 
151
143
  <style lang="scss" scoped>
152
144
  /* #ifndef APP-NVUE */
153
145
  // vue版本动画相关的样式抽离在外部文件
154
- @import "./index.scss";
146
+ @import './index.scss';
155
147
  /* #endif */
156
148
 
157
149
  .hy-transition {
@@ -1,24 +1,28 @@
1
- import type { CSSProperties } from "vue";
1
+ import type { CSSProperties } from 'vue'
2
2
 
3
3
  export default interface HyTransitionProps {
4
4
  /**
5
5
  * @description 是否展示组件
6
6
  * */
7
- show: boolean;
7
+ show: boolean
8
8
  /**
9
9
  * @description 使用的动画模式(默认:fade)
10
10
  * */
11
- mode?: HyApp.TransitionMode;
11
+ mode?: HyApp.TransitionMode
12
12
  /**
13
13
  * @description 动画的执行时间,单位ms
14
14
  * */
15
- duration?: number;
15
+ duration?: number
16
16
  /**
17
17
  * @description 使用的动画过渡函数(默认:ease-out)
18
18
  * */
19
- timingFunction?: string;
19
+ timingFunction?: string
20
20
  /**
21
21
  * @description 定义需要用到的外部样式
22
22
  * */
23
- customStyle?: CSSProperties;
23
+ customStyle?: CSSProperties
24
+ /**
25
+ * @description 定义组件的类名
26
+ * */
27
+ customClass?: string
24
28
  }
@@ -1,2 +1,4 @@
1
- export * from "./useShare";
2
- export * from "./useTouch";
1
+ export * from './useShare'
2
+ export * from './useTouch'
3
+ export * from './usePopover'
4
+ export * from './useQueue'
@@ -0,0 +1,221 @@
1
+ import { CSSProperties, getCurrentInstance, ref } from 'vue'
2
+ import { getRect, isObject } from '../utils'
3
+ import { IOffset, IPlacementVo } from '@/package/components/hy-popover/typing'
4
+
5
+ export function usePopover(visibleArrow = true) {
6
+ const { proxy } = getCurrentInstance() as any
7
+ const popStyle = ref<CSSProperties>()
8
+ const arrowStyle = ref<string>('')
9
+ const showStyle = ref<string>('')
10
+ const arrowClass = ref<string>('')
11
+ const popWidth = ref<number>(0)
12
+ const popHeight = ref<number>(0)
13
+ const left = ref<number>(0)
14
+ const bottom = ref<number>(0)
15
+ const width = ref<number>(0)
16
+ const height = ref<number>(0)
17
+ const top = ref<number>(0)
18
+
19
+ function noop() {}
20
+
21
+ function init(placement: IPlacementVo, visibleArrow: boolean, selector: string) {
22
+ // 初始化 class
23
+ if (visibleArrow) {
24
+ const arrowClassArr = [
25
+ `hy-${selector}__arrow`,
26
+ placement === 'bottom' || placement === 'bottom-start' || placement === 'bottom-end'
27
+ ? `hy-${selector}__arrow-up`
28
+ : '',
29
+ placement === 'left' || placement === 'left-start' || placement === 'left-end'
30
+ ? `hy-${selector}__arrow-right`
31
+ : '',
32
+ placement === 'right' || placement === 'right-start' || placement === 'right-end'
33
+ ? `hy-${selector}__arrow-left`
34
+ : '',
35
+ placement === 'top' || placement === 'top-start' || placement === 'top-end'
36
+ ? `hy-${selector}__arrow-down`
37
+ : '',
38
+ ]
39
+ arrowClass.value = arrowClassArr.join(' ')
40
+ }
41
+
42
+ // 初始化数据获取
43
+ getRect('#target', false, proxy).then((rect) => {
44
+ if (!rect) return
45
+ left.value = rect.left as number
46
+ bottom.value = rect.bottom as number
47
+ width.value = rect.width as number
48
+ height.value = rect.height as number
49
+ top.value = rect.top as number
50
+ })
51
+ // 用透明度可在初始化时获取到pop尺寸
52
+ getRect('#pos', false, proxy).then((rect) => {
53
+ if (!rect) return
54
+ popWidth.value = rect.width as number
55
+ popHeight.value = rect.height as number
56
+ })
57
+ }
58
+
59
+ function control(placement: IPlacementVo, offset: IOffset) {
60
+ // arrow size
61
+ const arrowSize = visibleArrow ? 9 : 0
62
+ // 上下位(纵轴)对应的距离左边的距离
63
+ const verticalX = width.value / 2
64
+ // 上下位(纵轴)对应的距离底部的距离
65
+ const verticalY = arrowSize + height.value + 5
66
+ // 左右位(横轴)对应的距离左边的距离
67
+ const horizontalX = width.value + arrowSize + 5
68
+ // 左右位(横轴)对应的距离底部的距离
69
+ const horizontalY = height.value / 2
70
+
71
+ let offsetX = 0
72
+ let offsetY = 0
73
+ if (Array.isArray(offset)) {
74
+ offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset[0]
75
+ offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + (offset[1] ? offset[1] : offset[0])
76
+ } else if (isObject(offset)) {
77
+ offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset.x
78
+ offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset.y
79
+ } else {
80
+ offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset
81
+ offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset
82
+ }
83
+ // const offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset
84
+ // const offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset
85
+
86
+ const placements = new Map([
87
+ // 上
88
+ [
89
+ 'top',
90
+ [
91
+ {
92
+ left: `${verticalX}px`,
93
+ bottom: `${verticalY}px`,
94
+ transform: 'translateX(-50%)',
95
+ },
96
+ 'left: 50%;',
97
+ ],
98
+ ],
99
+ [
100
+ 'top-start',
101
+ [
102
+ {
103
+ left: `${offsetX}px`,
104
+ bottom: `${verticalY}px`,
105
+ },
106
+ `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`,
107
+ ],
108
+ ],
109
+ [
110
+ 'top-end',
111
+ [
112
+ {
113
+ right: `${offsetX}px`,
114
+ bottom: `${verticalY}px`,
115
+ },
116
+ `right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`,
117
+ ],
118
+ ],
119
+ // 下
120
+ [
121
+ 'bottom',
122
+ [
123
+ {
124
+ left: verticalX + 'px',
125
+ top: verticalY + 'px',
126
+ transform: 'translateX(-50%)',
127
+ },
128
+ 'left: 50%;',
129
+ ],
130
+ ],
131
+ [
132
+ 'bottom-start',
133
+ [
134
+ {
135
+ left: `${offsetX}px`,
136
+ top: `${verticalY}px`,
137
+ },
138
+ `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`,
139
+ ],
140
+ ],
141
+ [
142
+ 'bottom-end',
143
+ [
144
+ {
145
+ right: `${offsetX}px`,
146
+ top: `${verticalY}px`,
147
+ },
148
+ `right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`,
149
+ ],
150
+ ],
151
+ // 左
152
+ [
153
+ 'left',
154
+ [
155
+ {
156
+ right: `${horizontalX}px`,
157
+ top: `${horizontalY}px`,
158
+ transform: 'translateY(-50%)',
159
+ },
160
+ 'top: 50%',
161
+ ],
162
+ ],
163
+ [
164
+ 'left-start',
165
+ [
166
+ {
167
+ right: `${horizontalX}px`,
168
+ top: `${offsetY}px`,
169
+ },
170
+ `top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`,
171
+ ],
172
+ ],
173
+ [
174
+ 'left-end',
175
+ [
176
+ {
177
+ right: `${horizontalX}px`,
178
+ bottom: `${offsetY}px`,
179
+ },
180
+ `bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`,
181
+ ],
182
+ ],
183
+ // 右
184
+ [
185
+ 'right',
186
+ [
187
+ {
188
+ left: `${horizontalX}px`,
189
+ top: `${horizontalY}px`,
190
+ transform: 'translateY(-50%)',
191
+ },
192
+ 'top: 50%',
193
+ ],
194
+ ],
195
+ [
196
+ 'right-start',
197
+ [
198
+ {
199
+ left: `${horizontalX}px`,
200
+ top: `${offsetY}px`,
201
+ },
202
+ `top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`,
203
+ ],
204
+ ],
205
+ [
206
+ 'right-end',
207
+ [
208
+ {
209
+ left: `${horizontalX}px`,
210
+ bottom: `${offsetY}px`,
211
+ },
212
+ `bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`,
213
+ ],
214
+ ],
215
+ ])
216
+ popStyle.value = placements.get(placement)![0] as CSSProperties
217
+ arrowStyle.value = placements.get(placement)![1] as string
218
+ }
219
+
220
+ return { popStyle, arrowStyle, showStyle, arrowClass, init, control, noop }
221
+ }
@@ -0,0 +1,52 @@
1
+ import { type Ref, provide, ref } from 'vue'
2
+
3
+ export const queueKey = '__QUEUE_KEY__'
4
+
5
+ export interface Queue {
6
+ queue: Ref<any[]>
7
+ pushToQueue: (comp: any) => void
8
+ removeFromQueue: (comp: any) => void
9
+ closeOther: (comp: any) => void
10
+ closeOutside: () => void
11
+ }
12
+
13
+ export function useQueue() {
14
+ const queue = ref<any[]>([])
15
+
16
+ function pushToQueue(comp: any) {
17
+ queue.value.push(comp)
18
+ }
19
+
20
+ function removeFromQueue(comp: any) {
21
+ queue.value = queue.value.filter((item) => {
22
+ return item.$.uid !== comp.$.uid
23
+ })
24
+ }
25
+
26
+ function closeOther(comp: any) {
27
+ queue.value.forEach((item) => {
28
+ if (item.$.uid !== comp.$.uid) {
29
+ item.$.exposed.close()
30
+ }
31
+ })
32
+ }
33
+
34
+ function closeOutside() {
35
+ queue.value.forEach((item) => {
36
+ item.$.exposed.close()
37
+ })
38
+ }
39
+
40
+ provide(queueKey, {
41
+ queue,
42
+ pushToQueue,
43
+ removeFromQueue,
44
+ closeOther,
45
+ closeOutside
46
+ })
47
+
48
+ return {
49
+ closeOther,
50
+ closeOutside
51
+ }
52
+ }
@@ -129,6 +129,10 @@
129
129
  }
130
130
  }
131
131
 
132
+ /**
133
+ * 圆角形状
134
+ * @param $shape circle-半圆形,square-方形带圆角
135
+ */
132
136
  @mixin borderRadio($shape) {
133
137
  @include m($shape) {
134
138
  @if $shape == circle {
@@ -140,6 +144,90 @@
140
144
  }
141
145
  }
142
146
 
147
+ /**
148
+ * 正方形实现尖角样式,适用于背景不透明情况
149
+ * @param $size 正方形边长
150
+ * @param $bg 正方形背景颜色
151
+ * @param $z-index z-index属性值,不得大于外部包裹器
152
+ * @param $box-shadow 阴影
153
+ */
154
+ @mixin squareArrow($size, $bg, $z-index, $box-shadow) {
155
+ @include e(arrow) {
156
+ position: absolute;
157
+ width: $size;
158
+ height: $size;
159
+ z-index: $z-index;
160
+ }
161
+
162
+ @include e(arrow-down) {
163
+ transform: translateX(-50%);
164
+ bottom: 0;
165
+
166
+ &:after {
167
+ content: "";
168
+ width: $size;
169
+ height: $size;
170
+ background-color: $bg;
171
+ position: absolute;
172
+ left: 0;
173
+ bottom: calc(-1 * $size / 2);
174
+ transform: rotateZ(45deg);
175
+ box-shadow: $box-shadow;
176
+ }
177
+ }
178
+
179
+ @include e(arrow-up) {
180
+ transform: translateX(-50%);
181
+ top: 0;
182
+
183
+ &:after {
184
+ content: "";
185
+ width: $size;
186
+ height: $size;
187
+ background-color: $bg;
188
+ position: absolute;
189
+ left: 0;
190
+ top: calc(-1 * $size / 2);
191
+ transform: rotateZ(45deg);
192
+ box-shadow: $box-shadow;
193
+ }
194
+ }
195
+
196
+ @include e(arrow-left) {
197
+ transform: translateY(-50%);
198
+ left: 0;
199
+
200
+ &:after {
201
+ content: "";
202
+ width: $size;
203
+ height: $size;
204
+ background-color: $bg;
205
+ position: absolute;
206
+ left: calc(-1 * $size / 2);
207
+ top: 0;
208
+ transform: rotateZ(45deg);
209
+ box-shadow: $box-shadow;
210
+ }
211
+ }
212
+
213
+ @include e(arrow-right) {
214
+ transform: translateY(-50%);
215
+ right: 0;
216
+
217
+ &:after {
218
+ content: "";
219
+ width: $size;
220
+ height: $size;
221
+ background-color: $bg;
222
+ position: absolute;
223
+ right: calc(-1 * $size / 2);
224
+ top: 0;
225
+ transform: rotateZ(45deg);
226
+ box-shadow: $box-shadow;
227
+ }
228
+ }
229
+ }
230
+
143
231
 
144
232
  /* flex布局 */
145
233
  @mixin flex($direction: row) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "hy-app",
3
- "version": "0.2.6",
4
- "description": "修改模态框样式、加了摇一摇功能",
3
+ "version": "0.2.7",
4
+ "description": "feat: 气泡组件",
5
5
  "main": "./index.ts",
6
6
  "private": false,
7
7
  "scripts": {},