@neatui/vue 5.2.0 → 5.2.1

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,5 +1,18 @@
1
1
  # @neatui/vue 更新记录
2
2
 
3
+ ## 5.2.1
4
+
5
+ ### IFollowView(移动端底部面板)
6
+ - 面板改为从底部滑入滑出,暗层同步淡入淡出,使用 `cubic-bezier(0.32, 0.72, 0, 1)` 减速曲线;支持“减少动态效果”。
7
+ - 面板通过 Popover API 进入浏览器 top layer,不再依赖 z-index;不支持时回退为普通 fixed。
8
+ - 插槽内容撑满面板宽度,覆盖桌面端写死的宽度。
9
+ - 面板贴底,Home 指示条安全区以面板底色留白(需页面 viewport 设置 `viewport-fit=cover`)。
10
+ - 顶部圆角改为 `0.75em`;上方间距统一由标题栏提供;关闭按钮改用 CSS 图标 `ui-icon="close"`。
11
+ - 移动端样式只作用于本层面板盒子,不再影响插槽内其他 `ui-tips` 结构。
12
+
13
+ ### 依赖
14
+ - CSS 依赖更新为 `@neatui/css@^5.2.1`。
15
+
3
16
  ## 5.2.0
4
17
 
5
18
  ### Layer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neatui/vue",
3
- "version": "5.2.0",
3
+ "version": "5.2.1",
4
4
  "description": "basic component ui",
5
5
  "main": "./src/index.ts",
6
6
  "license": "MIT",
@@ -36,7 +36,7 @@
36
36
  "@fekit/scrollto": "^3.0.3",
37
37
  "@fekit/toast": "^2.7.2",
38
38
  "@fekit/utils": "^6.0.0",
39
- "@neatui/css": "^5.2.0",
39
+ "@neatui/css": "^5.2.1",
40
40
  "@rollup/plugin-commonjs": "^26.0.1",
41
41
  "@rollup/plugin-node-resolve": "^15.2.3",
42
42
  "@vue/compiler-sfc": "^3.4.30",
@@ -17,14 +17,14 @@
17
17
  @click="clickhide"
18
18
  >
19
19
  <div ref="box" :ui-tips-box="state.pos" :class="tipBoxClass" :style="tipBoxStyle">
20
- <div ui-hide=">mob" class="mob-follow-tips w-full n-ss" ui-flex="row xm">
20
+ <div ui-hide=">mob" class="mob-follow-tips w-full nx-sl nt-sl nb-ss" ui-flex="row xm">
21
21
  <div class="w-xs*s"></div>
22
22
  <div>
23
23
  <h5 class="bold">{{ title }}</h5>
24
24
  </div>
25
25
  <div class="w-xs*s" ui-flex="row rm">
26
26
  <button ui-btn="@a none s :square" @click="toggle">
27
- <i class="icon icon-close"></i>
27
+ <i ui-icon="close"></i>
28
28
  </button>
29
29
  </div>
30
30
  </div>
@@ -168,6 +168,47 @@
168
168
  },
169
169
  );
170
170
 
171
+ // 移动端底部面板放入浏览器 top layer(Popover API),无需比较 z-index 即可盖住整页;
172
+ // 桌面端不加 popover 属性,仍按 Follow 的祖先层级定位。不支持的浏览器退回普通 fixed。
173
+ const SHEET_LEAVE_MS = 500;
174
+ let sheetTimer: ReturnType<typeof setTimeout> | undefined;
175
+ const supportsPopover = () => typeof HTMLElement !== 'undefined' && 'showPopover' in HTMLElement.prototype;
176
+ const isSheet = () => props.mobile && window.matchMedia('(max-width: 799px)').matches;
177
+ const releaseSheet = () => {
178
+ clearTimeout(sheetTimer);
179
+ const el = tip.value;
180
+ if (!el?.hasAttribute('popover')) return;
181
+ try {
182
+ if (el.matches(':popover-open')) el.hidePopover();
183
+ } catch {}
184
+ el.removeAttribute('popover');
185
+ };
186
+ watch(
187
+ () => state.show,
188
+ (show) => {
189
+ const el = tip.value;
190
+ if (!el || !supportsPopover()) return;
191
+ clearTimeout(sheetTimer);
192
+ if (show) {
193
+ if (!isSheet()) return;
194
+ el.setAttribute('popover', 'manual');
195
+ try {
196
+ // 已打开时先关再开,使其移到 top layer 最上方。
197
+ if (el.matches(':popover-open')) el.hidePopover();
198
+ el.showPopover();
199
+ } catch {
200
+ el.removeAttribute('popover');
201
+ }
202
+ } else if (el.hasAttribute('popover')) {
203
+ // 等滑出动画结束再离开 top layer。
204
+ sheetTimer = setTimeout(() => {
205
+ if (!state.show) releaseSheet();
206
+ }, SHEET_LEAVE_MS);
207
+ }
208
+ },
209
+ { flush: 'post' },
210
+ );
211
+
171
212
  const hovershow = () => {
172
213
  if (props.event === 'hover') {
173
214
  state.hover = 1;
@@ -361,6 +402,7 @@
361
402
  document.removeEventListener('touchend', onTouchEnd);
362
403
  document.removeEventListener('touchcancel', onTouchCancel);
363
404
  parentChildren?.delete(containsFollow);
405
+ releaseSheet();
364
406
  outsideTouch = null;
365
407
  state.show = 0;
366
408
 
@@ -391,6 +433,17 @@
391
433
  </script>
392
434
  <style lang="scss">
393
435
  @use '@neatui/css/query' as neatui;
436
+ // 抵消 [popover] 的 UA 默认样式;放在媒体查询外,面板打开时切到桌面宽度也不会走样。
437
+ .fekit-follow-mobile[popover] {
438
+ margin: 0;
439
+ padding: 0;
440
+ border: 0;
441
+ overflow: visible;
442
+ color: inherit;
443
+ background: transparent;
444
+ max-width: none;
445
+ max-height: none;
446
+ }
394
447
  @include neatui.mob {
395
448
  .fekit-follow-mobile {
396
449
  position: fixed !important;
@@ -404,26 +457,52 @@
404
457
  align-content: center !important;
405
458
  inset: 0 0 0 0 !important;
406
459
 
460
+ // 底部面板:暗层淡入淡出,面板从底部滑入滑出(iOS 风格减速曲线)。
461
+ --follow-sheet-ease: cubic-bezier(0.32, 0.72, 0, 1);
462
+ background-color: rgba(0, 0, 0, 0);
463
+ transition: background-color 0.4s var(--follow-sheet-ease);
464
+
407
465
  &[ui-tips-view='1'] {
408
466
  background-color: rgba(0, 0, 0, 0.5);
409
467
  pointer-events: auto !important;
410
468
  }
411
469
 
412
- &[ui-tips] [ui-tips-box] {
470
+ &[ui-tips][ui-tips-view='1'] > [ui-tips-box] {
471
+ transform: translate3d(0, 0, 0) !important;
472
+ }
473
+
474
+ &[ui-tips] > [ui-tips-box] {
413
475
  position: relative !important;
414
476
 
415
477
  width: 100% !important;
416
- bottom: env(safe-area-inset-bottom) !important;
478
+ bottom: 0 !important;
479
+ // iPhone Home 指示条安全区:面板背景铺到屏幕底边,内容让出安全区。
480
+ // 用透明下边框而非 padding,避免覆盖各组件 tipBoxClass 的内边距(背景默认绘制到边框下)。
481
+ border-bottom: env(safe-area-inset-bottom, 0px) solid transparent !important;
417
482
  right: unset !important;
418
483
  top: unset !important;
419
484
  left: unset !important;
420
- transform: translate3d(0, 0.5em, 0);
485
+ border-radius: 0.75em 0.75em 0 0 !important;
486
+ // 上方间距统一由标题栏提供,忽略各组件 tipBoxClass 的上内边距(如 ny-sm)。
487
+ padding-top: 0 !important;
488
+ // 全程不透明,只做位移。
489
+ opacity: 1 !important;
490
+ transform: translate3d(0, 100%, 0) !important;
491
+ transition: transform 0.45s var(--follow-sheet-ease) !important;
492
+ will-change: transform;
421
493
 
422
494
  display: flex !important;
423
495
  flex-direction: column !important;
424
496
  justify-content: flex-end !important;
425
- align-items: center !important;
426
- align-content: center !important;
497
+ align-items: stretch !important;
498
+ align-content: stretch !important;
499
+
500
+ // 插槽内容撑满面板宽度,覆盖桌面端写死的宽度(如 w-ml*s)。
501
+ > * {
502
+ width: 100% !important;
503
+ max-width: 100%;
504
+ box-sizing: border-box;
505
+ }
427
506
 
428
507
  [ui-tips-arrow] {
429
508
  display: none;
@@ -431,9 +510,15 @@
431
510
  }
432
511
  }
433
512
 
513
+ @media (prefers-reduced-motion: reduce) {
514
+ .fekit-follow-mobile,
515
+ .fekit-follow-mobile[ui-tips] > [ui-tips-box] {
516
+ transition-duration: 0.01s !important;
517
+ }
518
+ }
519
+
434
520
  .mob-follow-tips {
435
521
  position: relative;
436
- padding-bottom: env(safe-area-inset-bottom, 16px);
437
522
  &::before {
438
523
  position: absolute;
439
524
  content: '';
@@ -442,7 +527,7 @@
442
527
  height: 3px;
443
528
  border-radius: 1em;
444
529
  background-color: var(--co-text-o-ss);
445
- top: 5px;
530
+ top: 6px;
446
531
  left: 50%;
447
532
  transform: translateX(-50%);
448
533
  }