sard-uniapp 1.12.4 → 1.13.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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ # [1.13.0](https://github.com/sutras/sard-uniapp/compare/v1.12.5...v1.13.0) (2025-04-29)
2
+
3
+
4
+ ### Features
5
+
6
+ * button 组件新增 icon 功能 ([53a54f5](https://github.com/sutras/sard-uniapp/commit/53a54f5e55d698d10f5a5e8009b3cc4d1d4529ea))
7
+ * 新增 divider 组件 ([261b234](https://github.com/sutras/sard-uniapp/commit/261b234e0a963da6d3ff2dd85028df0d31ee812d))
8
+ * 新增 FloatingPanel 组件 ([f879433](https://github.com/sutras/sard-uniapp/commit/f879433a93afd93df58baeed227ddbb9da6b1730))
9
+
10
+
11
+
12
+ ## [1.12.5](https://github.com/sutras/sard-uniapp/compare/v1.12.4...v1.12.5) (2025-04-21)
13
+
14
+
15
+ ### Bug Fixes
16
+
17
+ * 修复 table 组件 sass 导入问题 ([2975683](https://github.com/sutras/sard-uniapp/commit/2975683448aa78612f1f40a7d25a0da41df091bf))
18
+
19
+
20
+
1
21
  ## [1.12.4](https://github.com/sutras/sard-uniapp/compare/v1.12.3...v1.12.4) (2025-04-14)
2
22
 
3
23
 
@@ -10,6 +10,7 @@ declare const __VLS_component: import("vue").DefineComponent<ButtonProps, {}, {}
10
10
  size: "mini" | "small" | "medium" | "large";
11
11
  type: "default" | "pale" | "mild" | "outline" | "text" | "pale-text";
12
12
  theme: "primary" | "secondary" | "success" | "info" | "warning" | "danger" | "neutral";
13
+ loadingType: "clock" | "circular";
13
14
  block: boolean;
14
15
  hoverStopPropagation: boolean;
15
16
  lang: string;
@@ -33,15 +33,18 @@
33
33
  @login="onLogin"
34
34
  >
35
35
  <view
36
- v-if="loading"
36
+ v-if="loading || icon"
37
37
  :class="
38
- classNames(
39
- bem.e('loading'),
40
- bem.em('loading', 'with-slot', !!$slots.default),
41
- )
38
+ classNames(bem.e('icon'), bem.em('icon', 'with-slot', !!$slots.default))
42
39
  "
43
40
  >
44
- <sar-loading :type="loadingType" />
41
+ <sar-loading v-if="loading" :type="loadingType" />
42
+ <sar-icon
43
+ v-else-if="icon"
44
+ :name="icon"
45
+ :family="iconFamily"
46
+ :size="iconSize"
47
+ />
45
48
  </view>
46
49
  <slot></slot>
47
50
  </button>
@@ -52,6 +55,7 @@ import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent }
52
55
  import { computed } from "vue";
53
56
  import { classNames, stringifyStyle, createBem } from "../../utils";
54
57
  import SarLoading from "../loading/loading.vue";
58
+ import SarIcon from "../icon/icon.vue";
55
59
  import { useFormContext } from "../form/common";
56
60
  import {
57
61
  defaultButtonProps
@@ -59,6 +63,7 @@ import {
59
63
  export default _defineComponent({
60
64
  components: {
61
65
  SarLoading,
66
+ SarIcon,
62
67
  },
63
68
  ...{
64
69
  options: {
@@ -81,6 +86,9 @@ export default _defineComponent({
81
86
  background: { type: String, required: false },
82
87
  block: { type: Boolean, required: false },
83
88
  inline: { type: Boolean, required: false },
89
+ icon: { type: String, required: false },
90
+ iconFamily: { type: String, required: false },
91
+ iconSize: { type: String, required: false },
84
92
  formType: { type: String, required: false },
85
93
  openType: { type: String, required: false },
86
94
  appParameter: { type: String, required: false },
@@ -156,7 +164,8 @@ export default _defineComponent({
156
164
  bem.m("round", props.round),
157
165
  bem.m("disabled", isDisabled.value),
158
166
  bem.m("loading", props.loading),
159
- bem.m("block", props.inline ? false : props.block),
167
+ bem.m("block", props.icon || props.inline ? false : props.block),
168
+ bem.m("iconic", !!props.icon || props.loading),
160
169
  props.rootClass
161
170
  );
162
171
  });
@@ -171,7 +180,7 @@ export default _defineComponent({
171
180
  });
172
181
  const __returned__ = { props, emit, bem, formContext, isDisabled, onClick, onGetphonenumber, onGetuserinfo, onError, onOpensetting, onLaunchapp, onContact, onChooseavatar, onAgreeprivacyauthorization, onAddgroupapp, onChooseaddress, onChooseinvoicetitle, onSubscribe, onLogin, buttonClass, buttonStyle, get classNames() {
173
182
  return classNames;
174
- }, SarLoading };
183
+ }, SarLoading, SarIcon };
175
184
  return __returned__;
176
185
  }
177
186
  });
@@ -13,6 +13,9 @@ export interface ButtonProps {
13
13
  background?: string;
14
14
  block?: boolean;
15
15
  inline?: boolean;
16
+ icon?: string;
17
+ iconFamily?: string;
18
+ iconSize?: string;
16
19
  formType?: string;
17
20
  openType?: string;
18
21
  appParameter?: string;
@@ -35,6 +38,7 @@ export declare const defaultButtonProps: {
35
38
  lang: string;
36
39
  showMessageCard: boolean;
37
40
  block: boolean;
41
+ loadingType: import("../loading").LoadingProps["type"];
38
42
  };
39
43
  export interface ButtonSlots {
40
44
  default?(props: Record<string, never>): any;
@@ -56,6 +56,14 @@
56
56
  var(--sar-button-border-radius-mini),
57
57
  var(--sar-button-font-size-mini)
58
58
  );
59
+
60
+ @include e(icon) {
61
+ font-size: var(--sar-button-icon-size-mini);
62
+ }
63
+ }
64
+
65
+ @include m-intersect(mini, iconic) {
66
+ min-width: var(--sar-button-height-mini);
59
67
  }
60
68
 
61
69
  @include m(small) {
@@ -66,6 +74,14 @@
66
74
  var(--sar-button-border-radius-sm),
67
75
  var(--sar-button-font-size-sm)
68
76
  );
77
+
78
+ @include e(icon) {
79
+ font-size: var(--sar-button-icon-size-sm);
80
+ }
81
+ }
82
+
83
+ @include m-intersect(small, iconic) {
84
+ min-width: var(--sar-button-height-sm);
69
85
  }
70
86
 
71
87
  @include m(medium) {
@@ -76,6 +92,14 @@
76
92
  var(--sar-button-border-radius),
77
93
  var(--sar-button-font-size)
78
94
  );
95
+
96
+ @include e(icon) {
97
+ font-size: var(--sar-button-icon-size);
98
+ }
99
+ }
100
+
101
+ @include m-intersect(medium, iconic) {
102
+ min-width: var(--sar-button-height);
79
103
  }
80
104
 
81
105
  @include m(large) {
@@ -86,6 +110,14 @@
86
110
  var(--sar-button-border-radius-lg),
87
111
  var(--sar-button-font-size-lg)
88
112
  );
113
+
114
+ @include e(icon) {
115
+ font-size: var(--sar-button-icon-size-lg);
116
+ }
117
+ }
118
+
119
+ @include m-intersect(large, iconic) {
120
+ min-width: var(--sar-button-height-lg);
89
121
  }
90
122
 
91
123
  // ## 显示边框
@@ -186,11 +218,12 @@
186
218
  cursor: var(--sar-button-loading-cursor);
187
219
  }
188
220
 
189
- @include e(loading) {
190
- display: flex;
221
+ // # 图标
222
+ @include e(icon) {
223
+ display: inline-flex;
191
224
 
192
225
  @include m(with-slot) {
193
- margin-right: var(--sar-button-loading-margin-right);
226
+ margin-right: var(--sar-button-icon-margin-right);
194
227
  }
195
228
  }
196
229
  }
@@ -5,24 +5,28 @@ page {
5
5
  --sar-button-padding-x-mini: 12rpx;
6
6
  --sar-button-border-radius-mini: var(--sar-rounded-sm);
7
7
  --sar-button-font-size-mini: var(--sar-text-sm);
8
+ --sar-button-icon-size-mini: var(--sar-text-sm);
8
9
 
9
10
  --sar-button-height-sm: 64rpx;
10
11
  --sar-button-padding-y-sm: 0;
11
- --sar-button-padding-x-sm: 24rpx;
12
+ --sar-button-padding-x-sm: 18rpx;
12
13
  --sar-button-border-radius-sm: var(--sar-rounded-sm);
13
14
  --sar-button-font-size-sm: var(--sar-text-sm);
15
+ --sar-button-icon-size-sm: var(--sar-text-base);
14
16
 
15
17
  --sar-button-height: 80rpx;
16
18
  --sar-button-padding-y: 0;
17
- --sar-button-padding-x: 32rpx;
19
+ --sar-button-padding-x: 22rpx;
18
20
  --sar-button-border-radius: var(--sar-rounded);
19
21
  --sar-button-font-size: var(--sar-text-base);
22
+ --sar-button-icon-size: 36rpx;
20
23
 
21
24
  --sar-button-height-lg: 96rpx;
22
25
  --sar-button-padding-y-lg: 0;
23
- --sar-button-padding-x-lg: 40rpx;
24
- --sar-button-border-radius-lg: var(--sar-rounded-lg);
26
+ --sar-button-padding-x-lg: 28rpx;
27
+ --sar-button-border-radius-lg: var(--sar-rounded);
25
28
  --sar-button-font-size-lg: var(--sar-text-lg);
29
+ --sar-button-icon-size-lg: 40rpx;
26
30
 
27
31
  --sar-button-line-height: var(--sar-leading-normal);
28
32
 
@@ -33,6 +37,6 @@ page {
33
37
 
34
38
  --sar-button-loading-opacity: var(--sar-loading-opacity);
35
39
  --sar-button-loading-cursor: var(--sar-loading-cursor);
36
- --sar-button-loading-margin-right: 0.5em;
40
+ --sar-button-icon-margin-right: 0.5em;
37
41
  }
38
42
  // #endvariables
@@ -34,6 +34,7 @@ import { type TagProps } from '../tag';
34
34
  import { type ToastProps } from '../toast';
35
35
  import { type TreeProps } from '../tree';
36
36
  import { type UploadPreviewProps, type UploadProps } from '../upload';
37
+ import { type DividerProps } from '../divider';
37
38
  type DeepPartial<T> = {
38
39
  [P in keyof T]?: T[P] extends Record<any, any> ? DeepPartial<T[P]> : T[P];
39
40
  };
@@ -64,6 +65,7 @@ export declare const defaultConfig: {
64
65
  lang: string;
65
66
  showMessageCard: boolean;
66
67
  block: boolean;
68
+ loadingType: LoadingProps["type"];
67
69
  };
68
70
  calendar: {
69
71
  type: CalendarProps["type"];
@@ -132,6 +134,11 @@ export declare const defaultConfig: {
132
134
  dialogAgent: {
133
135
  id: string;
134
136
  };
137
+ divider: {
138
+ type: DividerProps["type"];
139
+ hairline: boolean;
140
+ position: DividerProps["position"];
141
+ };
135
142
  dropdown: {
136
143
  direction: DropdownProps["direction"];
137
144
  disabled: boolean;
@@ -152,6 +159,11 @@ export declare const defaultConfig: {
152
159
  gapX: number;
153
160
  gapY: number;
154
161
  };
162
+ floatingPanel: {
163
+ duration: number;
164
+ contentDraggable: boolean;
165
+ safeAreaInsetBottom: boolean;
166
+ };
155
167
  form: {
156
168
  validateTrigger: FormProps["validateTrigger"];
157
169
  validateOnRuleChange: boolean;
@@ -27,6 +27,7 @@ export const defaultConfig = {
27
27
  lang: 'en',
28
28
  showMessageCard: false,
29
29
  block: true,
30
+ loadingType: undefined,
30
31
  },
31
32
  calendar: {
32
33
  type: 'single',
@@ -95,6 +96,11 @@ export const defaultConfig = {
95
96
  dialogAgent: {
96
97
  id: 'dialog',
97
98
  },
99
+ divider: {
100
+ type: 'solid',
101
+ hairline: true,
102
+ position: 'center',
103
+ },
98
104
  dropdown: {
99
105
  direction: 'down',
100
106
  disabled: false,
@@ -115,6 +121,11 @@ export const defaultConfig = {
115
121
  gapX: 24,
116
122
  gapY: 24,
117
123
  },
124
+ floatingPanel: {
125
+ duration: 300,
126
+ contentDraggable: true,
127
+ safeAreaInsetBottom: true,
128
+ },
118
129
  form: {
119
130
  validateTrigger: 'change',
120
131
  validateOnRuleChange: true,
@@ -0,0 +1,22 @@
1
+ import { type StyleValue } from 'vue';
2
+ export interface DividerProps {
3
+ rootStyle?: StyleValue;
4
+ rootClass?: string;
5
+ type?: 'solid' | 'dashed' | 'dotted';
6
+ hairline?: boolean;
7
+ position?: 'left' | 'right' | 'center';
8
+ vertical?: boolean;
9
+ }
10
+ export declare const defaultDividerProps: {
11
+ type: DividerProps["type"];
12
+ hairline: boolean;
13
+ position: DividerProps["position"];
14
+ };
15
+ export interface DividerSlots {
16
+ default?(props: Record<string, never>): any;
17
+ }
18
+ export interface DividerEmits {
19
+ (e: 'click', event: any): void;
20
+ }
21
+ export interface DividerExpose {
22
+ }
@@ -0,0 +1,2 @@
1
+ import { defaultConfig } from '../config';
2
+ export const defaultDividerProps = defaultConfig.divider;
@@ -0,0 +1,18 @@
1
+ import { type DividerProps, type DividerSlots } from './common';
2
+ declare function __VLS_template(): Readonly<DividerSlots> & DividerSlots;
3
+ declare const __VLS_component: import("vue").DefineComponent<DividerProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
4
+ click: (event: any) => any;
5
+ }, string, import("vue").PublicProps, Readonly<DividerProps> & Readonly<{
6
+ onClick?: ((event: any) => any) | undefined;
7
+ }>, {
8
+ type: "solid" | "dashed" | "dotted";
9
+ position: "left" | "right" | "center";
10
+ hairline: boolean;
11
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
12
+ declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
13
+ export default _default;
14
+ type __VLS_WithTemplateSlots<T, S> = T & {
15
+ new (): {
16
+ $slots: S;
17
+ };
18
+ };
@@ -0,0 +1,62 @@
1
+ <template>
2
+ <view :class="dividerClass" :style="dividerStyle">
3
+ <slot></slot>
4
+ </view>
5
+ </template>
6
+
7
+ <script>
8
+ import { useSlots as _useSlots, mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from "vue";
9
+ import { computed } from "vue";
10
+ import { classNames, stringifyStyle, createBem } from "../../utils";
11
+ import {
12
+ defaultDividerProps
13
+ } from "./common";
14
+ export default _defineComponent({
15
+ ...{
16
+ options: {
17
+ virtualHost: true,
18
+ styleIsolation: "shared"
19
+ }
20
+ },
21
+ __name: "divider",
22
+ props: _mergeDefaults({
23
+ rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
24
+ rootClass: { type: String, required: false },
25
+ type: { type: String, required: false },
26
+ hairline: { type: Boolean, required: false },
27
+ position: { type: String, required: false },
28
+ vertical: { type: Boolean, required: false }
29
+ }, defaultDividerProps),
30
+ emits: ["click"],
31
+ setup(__props, { expose: __expose }) {
32
+ const props = __props;
33
+ const slots = _useSlots();
34
+ const bem = createBem("divider");
35
+ __expose({});
36
+ const dividerClass = computed(() => {
37
+ return classNames(
38
+ bem.b(),
39
+ bem.m(props.position),
40
+ bem.m("vertical", props.vertical),
41
+ bem.m("hairline", props.hairline),
42
+ bem.m("only-line", !slots.default),
43
+ props.rootClass
44
+ );
45
+ });
46
+ const dividerStyle = computed(() => {
47
+ return stringifyStyle(
48
+ {
49
+ borderStyle: props.type
50
+ },
51
+ props.rootStyle
52
+ );
53
+ });
54
+ const __returned__ = { props, slots, bem, dividerClass, dividerStyle };
55
+ return __returned__;
56
+ }
57
+ });
58
+ </script>
59
+
60
+ <style lang="scss">
61
+ @import './index.scss';
62
+ </style>
@@ -0,0 +1 @@
1
+ export type { DividerProps, DividerSlots, DividerEmits, DividerExpose, } from './common';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,70 @@
1
+ @use '../style/base' as *;
2
+
3
+ @include bem(divider) {
4
+ @include b() {
5
+ @include universal;
6
+ flex-direction: row;
7
+ align-items: center;
8
+ margin: var(--sar-divider-margin-y) 0;
9
+ gap: var(--sar-divider-gap);
10
+ border-width: 0;
11
+ border-color: var(--sar-divider-border-color);
12
+ color: var(--sar-divider-color);
13
+
14
+ &::before,
15
+ &::after {
16
+ @include universal;
17
+ flex: 1;
18
+ border-color: inherit;
19
+ border-style: inherit;
20
+ border-width: 1px 0 0;
21
+ content: '';
22
+ }
23
+ }
24
+
25
+ @include m(only-line) {
26
+ &::after {
27
+ display: none;
28
+ }
29
+ }
30
+
31
+ @include m(left) {
32
+ &::before {
33
+ max-width: var(--sar-divider-left-width);
34
+ }
35
+ }
36
+
37
+ @include m(right) {
38
+ &::after {
39
+ max-width: var(--sar-divider-right-width);
40
+ }
41
+ }
42
+
43
+ @include m(vertical) {
44
+ display: inline-block;
45
+ height: var(--sar-divider-vertical-height);
46
+ margin: 0 var(--sar-divider-margin-x);
47
+ vertical-align: middle;
48
+
49
+ &::before {
50
+ height: 100%;
51
+ border-width: 0 0 0 1px;
52
+ }
53
+ &::after {
54
+ display: none;
55
+ }
56
+ }
57
+
58
+ @include m(hairline) {
59
+ &::before,
60
+ &::after {
61
+ transform: scaleY(0.5);
62
+ }
63
+ }
64
+
65
+ @include m-intersect(vertical, hairline) {
66
+ &::before {
67
+ transform: scaleX(0.5);
68
+ }
69
+ }
70
+ }
@@ -0,0 +1,14 @@
1
+ // #variables
2
+ page {
3
+ --sar-divider-margin-y: 32rpx;
4
+ --sar-divider-margin-x: 16rpx;
5
+ --sar-divider-gap: 32rpx;
6
+
7
+ --sar-divider-color: var(--sar-tertiary-color);
8
+ --sar-divider-border-color: var(--sar-border-color);
9
+ --sar-divider-left-width: 10%;
10
+ --sar-divider-right-width: 10%;
11
+
12
+ --sar-divider-vertical-height: 1em;
13
+ }
14
+ // #endvariables
@@ -0,0 +1,24 @@
1
+ import { type StyleValue } from 'vue';
2
+ export interface FloatingPanelProps {
3
+ rootStyle?: StyleValue;
4
+ rootClass?: string;
5
+ height?: number;
6
+ anchors?: number[];
7
+ duration?: number;
8
+ contentDraggable?: boolean;
9
+ safeAreaInsetBottom?: boolean;
10
+ }
11
+ export declare const defaultFloatingPanelProps: {
12
+ duration: number;
13
+ contentDraggable: boolean;
14
+ safeAreaInsetBottom: boolean;
15
+ };
16
+ export interface FloatingPanelSlots {
17
+ default?(props: Record<string, never>): any;
18
+ }
19
+ export interface FloatingPanelEmits {
20
+ (e: 'update:height', value: number): void;
21
+ (e: 'height-change', value: number): void;
22
+ }
23
+ export interface FloatingPanelExpose {
24
+ }
@@ -0,0 +1,2 @@
1
+ import { defaultConfig } from '../config';
2
+ export const defaultFloatingPanelProps = defaultConfig.floatingPanel;
@@ -0,0 +1,20 @@
1
+ import { type FloatingPanelProps, type FloatingPanelSlots } from './common';
2
+ declare function __VLS_template(): Readonly<FloatingPanelSlots> & FloatingPanelSlots;
3
+ declare const __VLS_component: import("vue").DefineComponent<FloatingPanelProps, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
4
+ "update:height": (value: number) => any;
5
+ "height-change": (value: number) => any;
6
+ }, string, import("vue").PublicProps, Readonly<FloatingPanelProps> & Readonly<{
7
+ "onUpdate:height"?: ((value: number) => any) | undefined;
8
+ "onHeight-change"?: ((value: number) => any) | undefined;
9
+ }>, {
10
+ duration: number;
11
+ contentDraggable: boolean;
12
+ safeAreaInsetBottom: boolean;
13
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
15
+ export default _default;
16
+ type __VLS_WithTemplateSlots<T, S> = T & {
17
+ new (): {
18
+ $slots: S;
19
+ };
20
+ };
@@ -0,0 +1,243 @@
1
+ <template>
2
+ <view
3
+ :class="floatingPanelClass"
4
+ :style="floatingPanelStyle"
5
+ @touchstart.passive="onTouchStart"
6
+ @touchmove.passive="onTouchMove"
7
+ @touchend="onTouchEnd"
8
+ @touchcancel="onTouchEnd"
9
+ @mousedown="onMouseDown"
10
+ >
11
+ <view :class="bem.e('header')">
12
+ <view :class="bem.e('header-bar')"></view>
13
+ </view>
14
+ <view :class="bem.e('body')">
15
+ <scroll-view
16
+ :scroll-y="canScroll"
17
+ style="height: 100%"
18
+ data-target="scroll-view"
19
+ @touchmove.stop.prevent="onTouchMove"
20
+ @scroll="onScroll"
21
+ >
22
+ <slot></slot>
23
+ </scroll-view>
24
+ </view>
25
+ </view>
26
+ </template>
27
+
28
+ <script>
29
+ import { mergeDefaults as _mergeDefaults, defineComponent as _defineComponent } from "vue";
30
+ import { computed, ref, watch } from "vue";
31
+ import {
32
+ classNames,
33
+ stringifyStyle,
34
+ createBem,
35
+ getWindowInfo,
36
+ isAlipay
37
+ } from "../../utils";
38
+ import {
39
+ defaultFloatingPanelProps
40
+ } from "./common";
41
+ import { useMouseDown } from "../../use";
42
+ import { useInitialVelocity } from "../../use/useInitialVelocity";
43
+ export default _defineComponent({
44
+ ...{
45
+ options: {
46
+ virtualHost: true,
47
+ styleIsolation: "shared"
48
+ }
49
+ },
50
+ __name: "floating-panel",
51
+ props: _mergeDefaults({
52
+ rootStyle: { type: [Boolean, null, String, Object, Array], required: false, skipCheck: true },
53
+ rootClass: { type: String, required: false },
54
+ height: { type: Number, required: false },
55
+ anchors: { type: Array, required: false },
56
+ duration: { type: Number, required: false },
57
+ contentDraggable: { type: Boolean, required: false },
58
+ safeAreaInsetBottom: { type: Boolean, required: false }
59
+ }, defaultFloatingPanelProps),
60
+ emits: ["update:height", "height-change"],
61
+ setup(__props, { expose: __expose, emit: __emit }) {
62
+ const props = __props;
63
+ const emit = __emit;
64
+ const bem = createBem("floating-panel");
65
+ const mergedAnchors = computed(() => {
66
+ const { windowHeight } = getWindowInfo();
67
+ return props.anchors || [100, ~~(windowHeight * 0.6)];
68
+ });
69
+ let prevY = 0;
70
+ let offsetY = ref(0);
71
+ const isDown = ref(false);
72
+ let target = "scroll";
73
+ watch(
74
+ mergedAnchors,
75
+ () => {
76
+ offsetY.value = mergedAnchors.value[0];
77
+ },
78
+ {
79
+ immediate: true
80
+ }
81
+ );
82
+ watch(
83
+ () => props.height,
84
+ () => {
85
+ if (props.height) {
86
+ offsetY.value = props.height;
87
+ }
88
+ },
89
+ {
90
+ immediate: true
91
+ }
92
+ );
93
+ const triggerChange = (value, touchEnd) => {
94
+ if (offsetY.value !== value) {
95
+ offsetY.value = value;
96
+ emit("update:height", offsetY.value);
97
+ if (touchEnd) {
98
+ emit("height-change", offsetY.value);
99
+ }
100
+ }
101
+ };
102
+ const canScroll = ref(false);
103
+ let scrollTop = 0;
104
+ let prevScrollTop = 0;
105
+ let targetLocked = false;
106
+ const onScroll = (event) => {
107
+ scrollTop = event.detail.scrollTop;
108
+ };
109
+ const initVelocity = useInitialVelocity();
110
+ const onTouchStart = (event) => {
111
+ isDown.value = true;
112
+ prevY = event.touches[0].clientY;
113
+ prevScrollTop = scrollTop;
114
+ canScroll.value = offsetY.value > mergedAnchors.value[0];
115
+ initVelocity.onStart({
116
+ x: event.touches[0].clientX,
117
+ y: event.touches[0].clientY
118
+ });
119
+ };
120
+ const onTouchMove = (event) => {
121
+ const currentTarget = event.currentTarget;
122
+ if (currentTarget.dataset?.target === "scroll-view") {
123
+ if (!props.contentDraggable || isAlipay) {
124
+ return;
125
+ }
126
+ }
127
+ const currentY = event.touches[0].clientY;
128
+ let deltaY = prevY - currentY;
129
+ prevY = currentY;
130
+ if (!targetLocked) {
131
+ targetLocked = true;
132
+ if (prevScrollTop === scrollTop) {
133
+ target = "panel";
134
+ canScroll.value = false;
135
+ } else {
136
+ target = "scroll";
137
+ canScroll.value = true;
138
+ }
139
+ prevScrollTop = scrollTop;
140
+ }
141
+ if (target === "panel") {
142
+ const anchors = mergedAnchors.value;
143
+ if (offsetY.value > anchors[anchors.length - 1] || offsetY.value < anchors[0]) {
144
+ deltaY /= 4;
145
+ }
146
+ triggerChange(offsetY.value + deltaY);
147
+ }
148
+ initVelocity.onMove({
149
+ x: event.touches[0].clientX,
150
+ y: event.touches[0].clientY
151
+ });
152
+ };
153
+ function getCorrectOffset(direction) {
154
+ const anchors = mergedAnchors.value;
155
+ const min = anchors[0];
156
+ const max = anchors[anchors.length - 1];
157
+ const current = offsetY.value;
158
+ if (current <= min) {
159
+ return min;
160
+ } else if (current >= max) {
161
+ return max;
162
+ }
163
+ for (let i = 1; i < anchors.length; i++) {
164
+ const below = anchors[i - 1];
165
+ const above = anchors[i];
166
+ if (current >= below && current <= above) {
167
+ if (direction) {
168
+ return direction === "up" ? above : below;
169
+ } else {
170
+ return above - current < current - below ? above : below;
171
+ }
172
+ }
173
+ }
174
+ return 0;
175
+ }
176
+ const onTouchEnd = () => {
177
+ if (target === "panel") {
178
+ const velocity = initVelocity.onEnd();
179
+ let value = 0;
180
+ if (Math.abs(velocity.y) > 0.4) {
181
+ value = getCorrectOffset(velocity.y > 0 ? "down" : "up");
182
+ } else {
183
+ value = getCorrectOffset();
184
+ }
185
+ triggerChange(value, true);
186
+ }
187
+ target = "scroll";
188
+ canScroll.value = true;
189
+ targetLocked = false;
190
+ isDown.value = false;
191
+ };
192
+ const onMouseDown = useMouseDown(onTouchStart, onTouchMove, onTouchEnd);
193
+ const floatingPanelClass = computed(() => {
194
+ return classNames(
195
+ bem.b(),
196
+ bem.m("safe", props.safeAreaInsetBottom),
197
+ props.rootClass
198
+ );
199
+ });
200
+ const floatingPanelStyle = computed(() => {
201
+ return stringifyStyle(
202
+ {
203
+ transform: `translate3d(0, calc(100% - ${offsetY.value}px), 0)`,
204
+ height: `${mergedAnchors.value[mergedAnchors.value.length - 1]}px`,
205
+ transition: isDown.value ? "none" : `transform ${props.duration}ms cubic-bezier(0.18, 0.89, 0.32, 1.28)`
206
+ },
207
+ props.rootStyle
208
+ );
209
+ });
210
+ __expose({});
211
+ const __returned__ = { props, emit, bem, mergedAnchors, get prevY() {
212
+ return prevY;
213
+ }, set prevY(v) {
214
+ prevY = v;
215
+ }, get offsetY() {
216
+ return offsetY;
217
+ }, set offsetY(v) {
218
+ offsetY = v;
219
+ }, isDown, get target() {
220
+ return target;
221
+ }, set target(v) {
222
+ target = v;
223
+ }, triggerChange, canScroll, get scrollTop() {
224
+ return scrollTop;
225
+ }, set scrollTop(v) {
226
+ scrollTop = v;
227
+ }, get prevScrollTop() {
228
+ return prevScrollTop;
229
+ }, set prevScrollTop(v) {
230
+ prevScrollTop = v;
231
+ }, get targetLocked() {
232
+ return targetLocked;
233
+ }, set targetLocked(v) {
234
+ targetLocked = v;
235
+ }, onScroll, initVelocity, onTouchStart, onTouchMove, getCorrectOffset, onTouchEnd, onMouseDown, floatingPanelClass, floatingPanelStyle };
236
+ return __returned__;
237
+ }
238
+ });
239
+ </script>
240
+
241
+ <style lang="scss">
242
+ @import './index.scss';
243
+ </style>
@@ -0,0 +1 @@
1
+ export type { FloatingPanelProps, FloatingPanelSlots, FloatingPanelEmits, FloatingPanelExpose, } from './common';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,55 @@
1
+ @use '../style/base' as *;
2
+
3
+ @include bem(floating-panel) {
4
+ @include b() {
5
+ @include universal;
6
+ position: fixed;
7
+ left: 0;
8
+ bottom: 0;
9
+ z-index: var(--sar-floating-panel-z-index);
10
+ width: 100vw;
11
+ border-top-left-radius: var(--sar-floating-panel-border-radius);
12
+ border-top-right-radius: var(--sar-floating-panel-border-radius);
13
+ background: var(--sar-floating-panel-bg);
14
+ will-change: transform;
15
+ touch-action: none;
16
+ transition-property: transform;
17
+
18
+ &::after {
19
+ position: absolute;
20
+ bottom: -100vh;
21
+ width: 100vw;
22
+ height: 100vh;
23
+ content: '';
24
+ background-color: inherit;
25
+ }
26
+
27
+ @include m(safe) {
28
+ padding-bottom: env(safe-area-inset-bottom);
29
+ }
30
+ }
31
+
32
+ @include e(header) {
33
+ @include universal;
34
+ flex: none;
35
+ justify-content: center;
36
+ align-items: center;
37
+ height: var(--sar-floating-panel-header-height);
38
+ user-select: none;
39
+ cursor: grab;
40
+ }
41
+
42
+ @include e(header-bar) {
43
+ @include universal;
44
+ width: var(--sar-floating-panel-bar-width);
45
+ height: var(--sar-floating-panel-bar-height);
46
+ background: var(--sar-floating-panel-bar-color);
47
+ border-radius: var(--sar-rounded-full);
48
+ }
49
+
50
+ @include e(body) {
51
+ @include universal;
52
+ flex: 1;
53
+ overflow: hidden;
54
+ }
55
+ }
@@ -0,0 +1,12 @@
1
+ // #variables
2
+ page {
3
+ --sar-floating-panel-z-index: 1000;
4
+ --sar-floating-panel-bg: var(--sar-emphasis-bg);
5
+ --sar-floating-panel-border-radius: var(--sar-rounded-xl);
6
+
7
+ --sar-floating-panel-header-height: 64rpx;
8
+ --sar-floating-panel-bar-width: 40rpx;
9
+ --sar-floating-panel-bar-height: 6rpx;
10
+ --sar-floating-panel-bar-color: var(--sar-tertiary-bg);
11
+ }
12
+ // #endvariables
@@ -25,8 +25,6 @@ $sar-loading-scale-number: 12;
25
25
  justify-content: center;
26
26
  width: 1em;
27
27
  height: 1em;
28
- min-width: var(--sar-loading-icon-min-width);
29
- min-height: var(--sar-loading-icon-min-height);
30
28
  }
31
29
 
32
30
  @include m-intersect(circular, animated) {
@@ -1,7 +1,5 @@
1
1
  // #variables
2
2
  page {
3
- --sar-loading-icon-min-width: 32rpx;
4
- --sar-loading-icon-min-height: 32rpx;
5
3
  --sar-loading-icon-animation-duration: 0.8s;
6
4
 
7
5
  --sar-loading-scale-width: 9.38%;
@@ -97,5 +97,5 @@ export default _defineComponent({
97
97
  </script>
98
98
 
99
99
  <style lang="scss">
100
- @use './index.scss';
100
+ @import './index.scss';
101
101
  </style>
package/global.d.ts CHANGED
@@ -28,11 +28,13 @@ declare module 'vue' {
28
28
  SarDatetimeRangePickerInput: typeof import('./components/datetime-range-picker-input/datetime-range-picker-input').default
29
29
  SarDialog: typeof import('./components/dialog/dialog').default
30
30
  SarDialogAgent: typeof import('./components/dialog-agent/dialog-agent').default
31
+ SarDivider: typeof import('./components/divider/divider').default
31
32
  SarDropdown: typeof import('./components/dropdown/dropdown').default
32
33
  SarDropdownItem: typeof import('./components/dropdown-item/dropdown-item').default
33
34
  SarEmpty: typeof import('./components/empty/empty').default
34
35
  SarFab: typeof import('./components/fab/fab').default
35
36
  SarFloatingBubble: typeof import('./components/floating-bubble/floating-bubble').default
37
+ SarFloatingPanel: typeof import('./components/floating-panel/floating-panel').default
36
38
  SarForm: typeof import('./components/form/form').default
37
39
  SarFormItem: typeof import('./components/form-item/form-item').default
38
40
  SarGrid: typeof import('./components/grid/grid').default
package/index.d.ts CHANGED
@@ -24,10 +24,12 @@ export * from './components/datetime-range-picker';
24
24
  export * from './components/datetime-range-picker-input';
25
25
  export * from './components/dialog';
26
26
  export * from './components/dialog-agent';
27
+ export * from './components/divider';
27
28
  export * from './components/dropdown';
28
29
  export * from './components/empty';
29
30
  export * from './components/fab';
30
31
  export * from './components/floating-bubble';
32
+ export * from './components/floating-panel';
31
33
  export * from './components/form';
32
34
  export * from './components/grid';
33
35
  export * from './components/icon';
package/index.js CHANGED
@@ -24,10 +24,12 @@ export * from './components/datetime-range-picker';
24
24
  export * from './components/datetime-range-picker-input';
25
25
  export * from './components/dialog';
26
26
  export * from './components/dialog-agent';
27
+ export * from './components/divider';
27
28
  export * from './components/dropdown';
28
29
  export * from './components/empty';
29
30
  export * from './components/fab';
30
31
  export * from './components/floating-bubble';
32
+ export * from './components/floating-panel';
31
33
  export * from './components/form';
32
34
  export * from './components/grid';
33
35
  export * from './components/icon';
package/index.scss CHANGED
@@ -15,10 +15,12 @@
15
15
  @use './components/collapse/variables.scss' as *;
16
16
  @use './components/datetime-range-picker/variables.scss' as *;
17
17
  @use './components/dialog/variables.scss' as *;
18
+ @use './components/divider/variables.scss' as *;
18
19
  @use './components/dropdown/variables.scss' as *;
19
20
  @use './components/empty/variables.scss' as *;
20
21
  @use './components/fab/variables.scss' as *;
21
22
  @use './components/floating-bubble/variables.scss' as *;
23
+ @use './components/floating-panel/variables.scss' as *;
22
24
  @use './components/form/variables.scss' as *;
23
25
  @use './components/grid/variables.scss' as *;
24
26
  @use './components/icon/variables.scss' as *;
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "id": "sard-uniapp",
3
3
  "name": "sard-uniapp",
4
4
  "displayName": "sard-uniapp",
5
- "version": "1.12.4",
5
+ "version": "1.13.0",
6
6
  "description": "sard-uniapp 是一套基于 Uniapp + Vue3 框架开发的兼容多端的 UI 组件库",
7
7
  "main": "index.js",
8
8
  "scripts": {