@greypan/web-ui 6.4.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/README.CN.md +211 -50
  2. package/README.md +222 -61
  3. package/dist/assets/glass.js +1 -1
  4. package/dist/assets/glass.js.map +1 -1
  5. package/dist/assets/selection-control.js +6 -0
  6. package/dist/assets/selection-control.js.map +1 -0
  7. package/dist/assets/toast-containers.js.map +1 -1
  8. package/dist/components/autocomplete/index.d.ts +19 -5
  9. package/dist/components/autocomplete/index.js +142 -108
  10. package/dist/components/autocomplete/index.js.map +1 -1
  11. package/dist/components/autocomplete/style.js +1 -1
  12. package/dist/components/autocomplete/style.js.map +1 -1
  13. package/dist/components/avatar/index.js +4 -4
  14. package/dist/components/badge/style.js.map +1 -1
  15. package/dist/components/button/index.js +15 -15
  16. package/dist/components/button/style.js.map +1 -1
  17. package/dist/components/button-group/index.js +5 -5
  18. package/dist/components/checkbox/index.d.ts +9 -2
  19. package/dist/components/checkbox/index.js +50 -26
  20. package/dist/components/checkbox/index.js.map +1 -1
  21. package/dist/components/checkbox/style.js +1 -1
  22. package/dist/components/checkbox/style.js.map +1 -1
  23. package/dist/components/checkbox-group/index.d.ts +3 -0
  24. package/dist/components/checkbox-group/index.js +27 -16
  25. package/dist/components/checkbox-group/index.js.map +1 -1
  26. package/dist/components/checkbox-group/style.js +1 -1
  27. package/dist/components/checkbox-group/style.js.map +1 -1
  28. package/dist/components/collapse/index.js.map +1 -1
  29. package/dist/components/context-menu/index.d.ts +10 -0
  30. package/dist/components/context-menu/index.js +51 -34
  31. package/dist/components/context-menu/index.js.map +1 -1
  32. package/dist/components/dialog/index.d.ts +6 -0
  33. package/dist/components/dialog/index.js +49 -21
  34. package/dist/components/dialog/index.js.map +1 -1
  35. package/dist/components/dialog/style.js +1 -1
  36. package/dist/components/dialog/style.js.map +1 -1
  37. package/dist/components/drawer/index.d.ts +22 -4
  38. package/dist/components/drawer/index.js +141 -110
  39. package/dist/components/drawer/index.js.map +1 -1
  40. package/dist/components/drawer/style.js +1 -1
  41. package/dist/components/drawer/style.js.map +1 -1
  42. package/dist/components/dropdown/index.d.ts +21 -1
  43. package/dist/components/dropdown/index.js +77 -51
  44. package/dist/components/dropdown/index.js.map +1 -1
  45. package/dist/components/editable-text/index.d.ts +471 -0
  46. package/dist/components/editable-text/index.js +206 -0
  47. package/dist/components/editable-text/index.js.map +1 -0
  48. package/dist/components/editable-text/style.js +6 -0
  49. package/dist/components/editable-text/style.js.map +1 -0
  50. package/dist/components/image-preview/index.js +103 -80
  51. package/dist/components/image-preview/index.js.map +1 -1
  52. package/dist/components/image-preview/style.js +1 -1
  53. package/dist/components/image-preview/style.js.map +1 -1
  54. package/dist/components/index.d.ts +1 -0
  55. package/dist/components/index.js +39 -38
  56. package/dist/components/input/index.d.ts +2 -0
  57. package/dist/components/input/index.js +12 -6
  58. package/dist/components/input/index.js.map +1 -1
  59. package/dist/components/input/style.js.map +1 -1
  60. package/dist/components/input-number/index.js +15 -15
  61. package/dist/components/layout/index.js +9 -9
  62. package/dist/components/layout/index.js.map +1 -1
  63. package/dist/components/layout/style.js +1 -1
  64. package/dist/components/layout/style.js.map +1 -1
  65. package/dist/components/popover/index.d.ts +8 -2
  66. package/dist/components/popover/index.js +53 -55
  67. package/dist/components/popover/index.js.map +1 -1
  68. package/dist/components/radio/index.d.ts +1 -1
  69. package/dist/components/radio/index.js +22 -21
  70. package/dist/components/radio/index.js.map +1 -1
  71. package/dist/components/radio/style.js +1 -1
  72. package/dist/components/radio/style.js.map +1 -1
  73. package/dist/components/radio-group/index.d.ts +3 -0
  74. package/dist/components/radio-group/index.js +27 -16
  75. package/dist/components/radio-group/index.js.map +1 -1
  76. package/dist/components/radio-group/style.js +1 -1
  77. package/dist/components/radio-group/style.js.map +1 -1
  78. package/dist/components/segmented/index.d.ts +4 -1
  79. package/dist/components/segmented/index.js +42 -29
  80. package/dist/components/segmented/index.js.map +1 -1
  81. package/dist/components/segmented/style.js +1 -1
  82. package/dist/components/segmented/style.js.map +1 -1
  83. package/dist/components/segmented-trigger/style.js +1 -1
  84. package/dist/components/segmented-trigger/style.js.map +1 -1
  85. package/dist/components/select/index.d.ts +2 -0
  86. package/dist/components/select/index.js +38 -34
  87. package/dist/components/select/index.js.map +1 -1
  88. package/dist/components/slider/index.js +11 -11
  89. package/dist/components/slider/index.js.map +1 -1
  90. package/dist/components/slider/style.js +1 -1
  91. package/dist/components/slider/style.js.map +1 -1
  92. package/dist/components/svg-draw-lines/index.d.ts +13 -2
  93. package/dist/components/svg-draw-lines/index.js +44 -31
  94. package/dist/components/svg-draw-lines/index.js.map +1 -1
  95. package/dist/components/switch/index.js +12 -12
  96. package/dist/components/switch/index.js.map +1 -1
  97. package/dist/components/switch/style.js.map +1 -1
  98. package/dist/components/textarea/index.js +6 -6
  99. package/dist/components/textarea/index.js.map +1 -1
  100. package/dist/components/textarea/style.js.map +1 -1
  101. package/dist/components/theme/index.d.ts +15 -0
  102. package/dist/components/theme/index.js +178 -21
  103. package/dist/components/theme/index.js.map +1 -1
  104. package/dist/components/theme/style.js +1 -1
  105. package/dist/components/theme/style.js.map +1 -1
  106. package/dist/components/toast/index.d.ts +1 -1
  107. package/dist/components/toast/manager.d.ts +1 -3
  108. package/dist/components/toast/manager.js +92 -49
  109. package/dist/components/toast/manager.js.map +1 -1
  110. package/dist/components/toast/style.js.map +1 -1
  111. package/dist/components/toast/toast.d.ts +37 -0
  112. package/dist/components/toast/toast.js +74 -21
  113. package/dist/components/toast/toast.js.map +1 -1
  114. package/dist/components/toast/types.d.ts +0 -4
  115. package/dist/components/tooltip/index.d.ts +6 -1
  116. package/dist/components/tooltip/index.js +21 -19
  117. package/dist/components/tooltip/index.js.map +1 -1
  118. package/dist/shared/gesture/drag-gesture.js +66 -64
  119. package/dist/shared/gesture/drag-gesture.js.map +1 -1
  120. package/dist/shared/gesture/physics.js +7 -29
  121. package/dist/shared/gesture/physics.js.map +1 -1
  122. package/dist/shared/menu-behavior/index.js +3 -4
  123. package/dist/shared/menu-behavior/index.js.map +1 -1
  124. package/dist/shared/option-portal/combobox-shell.js +48 -7
  125. package/dist/shared/option-portal/combobox-shell.js.map +1 -1
  126. package/dist/shared/option-portal/index.js +9 -9
  127. package/dist/shared/overlay/anchored-panel.js +36 -31
  128. package/dist/shared/overlay/anchored-panel.js.map +1 -1
  129. package/dist/shared/overlay/native-dialog-presence.js +16 -0
  130. package/dist/shared/overlay/native-dialog-presence.js.map +1 -1
  131. package/dist/shared/overlay/nested-drawer-layers.js +7 -4
  132. package/dist/shared/overlay/nested-drawer-layers.js.map +1 -1
  133. package/dist/shared/overlay/open-overlay.js +198 -0
  134. package/dist/shared/overlay/open-overlay.js.map +1 -0
  135. package/dist/shared/overlay/presence.js +19 -22
  136. package/dist/shared/overlay/presence.js.map +1 -1
  137. package/dist/shared/theme/duration.js +9 -0
  138. package/dist/shared/theme/duration.js.map +1 -0
  139. package/dist/shared/theme/reduced-motion.js +15 -0
  140. package/dist/shared/theme/reduced-motion.js.map +1 -0
  141. package/dist/shared/theme/root-sync.js +42 -0
  142. package/dist/shared/theme/root-sync.js.map +1 -0
  143. package/package.json +1 -1
  144. package/dist/shared/overlay/composition.js +0 -57
  145. package/dist/shared/overlay/composition.js.map +0 -1
  146. package/dist/shared/overlay/lifecycle.js +0 -41
  147. package/dist/shared/overlay/lifecycle.js.map +0 -1
  148. package/dist/vite.svg +0 -1
@@ -1,12 +1,12 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.149.0/helpers/esm/decorate.js";
2
2
  import t from "../../assets/glass.js";
3
- import n from "../../assets/overlay-motion.js";
4
- import { normalizeLiteral as r, normalizeNumber as i } from "../../shared/normalize/index.js";
3
+ import { normalizeLiteral as n, normalizeNumber as r } from "../../shared/normalize/index.js";
4
+ import i from "../../assets/overlay-motion.js";
5
5
  import { dispatchOpenChangeEvent as a } from "../../shared/open-state/index.js";
6
6
  import { defineAnchoredPanel as o } from "../../shared/overlay/anchored-panel.js";
7
- import { defineOverlayPortal as s } from "../../shared/overlay/portal.js";
8
- import { UserChangeController as c } from "../../shared/events/user-change.js";
9
- import { defineOverlayLifecycle as l } from "../../shared/overlay/lifecycle.js";
7
+ import { defineOpenOverlay as s } from "../../shared/overlay/open-overlay.js";
8
+ import { defineOverlayPortal as c } from "../../shared/overlay/portal.js";
9
+ import { UserChangeController as l } from "../../shared/events/user-change.js";
10
10
  import { FLOATING_PLACEMENTS as u } from "../../shared/overlay/placement-props.js";
11
11
  import d from "./style.js";
12
12
  import { LitElement as f, html as p, unsafeCSS as m } from "lit";
@@ -14,9 +14,9 @@ import { customElement as h, property as g } from "lit/decorators.js";
14
14
  //#region src/components/tooltip/index.ts
15
15
  var _ = 0, v = class extends f {
16
16
  constructor(...e) {
17
- super(...e), this._placement = "top", this._showDelay = 200, this._hideDelay = 100, this.disabled = !1, this._offset = 6, this.content = "", this.open = !1, this.portal = !1, this._isCountedVisible = !1, this._userOpenChange = new c(), this._shouldOpenInstantly = !0, this._lifecycle = l().make({
18
- isConnected: () => this.isConnected,
19
- isOpen: () => this.open
17
+ super(...e), this._placement = "top", this._showDelay = 200, this._hideDelay = 100, this.disabled = !1, this._offset = 6, this.content = "", this.open = !1, this.portal = !1, this._isCountedVisible = !1, this._userOpenChange = new l(), this._shouldOpenInstantly = !0, this._overlay = s().make({
18
+ requestClose: () => this._hide(),
19
+ isConnected: () => this.isConnected
20
20
  }), this._panel = o().make({
21
21
  getAnchor: () => this.shadowRoot?.querySelector(".tooltip-trigger") ?? null,
22
22
  getLocalPanel: () => this.shadowRoot?.querySelector(".tooltip-panel") ?? null,
@@ -26,7 +26,9 @@ var _ = 0, v = class extends f {
26
26
  strategy: this.portal ? "fixed" : "absolute"
27
27
  }),
28
28
  isPortal: () => this.portal,
29
- createPortal: () => this._createPortal()
29
+ createPortal: () => this._createPortal(),
30
+ openOverlay: this._overlay,
31
+ arbitration: "none"
30
32
  }), this._onPointerEnter = (e) => {
31
33
  if (e.pointerType === "touch" || this.disabled) return;
32
34
  clearTimeout(this._hideTimer);
@@ -43,7 +45,7 @@ var _ = 0, v = class extends f {
43
45
  static {
44
46
  this.styles = [
45
47
  m(t),
46
- m(n),
48
+ m(i),
47
49
  m(d)
48
50
  ];
49
51
  }
@@ -52,43 +54,43 @@ var _ = 0, v = class extends f {
52
54
  }
53
55
  set placement(e) {
54
56
  let t = this._placement;
55
- this._placement = r(e, u, "top"), this.requestUpdate("placement", t);
57
+ this._placement = n(e, u, "top"), this.requestUpdate("placement", t);
56
58
  }
57
59
  get showDelay() {
58
60
  return this._showDelay;
59
61
  }
60
62
  set showDelay(e) {
61
63
  let t = this._showDelay;
62
- this._showDelay = i(e, 0, 5e3, 200), this.requestUpdate("showDelay", t);
64
+ this._showDelay = r(e, 0, 5e3, 200), this.requestUpdate("showDelay", t);
63
65
  }
64
66
  get hideDelay() {
65
67
  return this._hideDelay;
66
68
  }
67
69
  set hideDelay(e) {
68
70
  let t = this._hideDelay;
69
- this._hideDelay = i(e, 0, 5e3, 100), this.requestUpdate("hideDelay", t);
71
+ this._hideDelay = r(e, 0, 5e3, 100), this.requestUpdate("hideDelay", t);
70
72
  }
71
73
  get offset() {
72
74
  return this._offset;
73
75
  }
74
76
  set offset(e) {
75
77
  let t = this._offset;
76
- this._offset = i(e, 0, 100, 6), this.requestUpdate("offset", t);
78
+ this._offset = r(e, 0, 100, 6), this.requestUpdate("offset", t);
77
79
  }
78
80
  get isOpen() {
79
81
  return this.open;
80
82
  }
81
83
  connectedCallback() {
82
- super.connectedCallback(), this._lifecycle.resume(), this.addEventListener("pointerenter", this._onPointerEnter), this.addEventListener("pointerleave", this._onPointerLeave), this.addEventListener("focusin", this._onFocusIn), this.addEventListener("focusout", this._onFocusOut);
84
+ super.connectedCallback(), this._overlay.resume(), this.addEventListener("pointerenter", this._onPointerEnter), this.addEventListener("pointerleave", this._onPointerLeave), this.addEventListener("focusin", this._onFocusIn), this.addEventListener("focusout", this._onFocusOut);
83
85
  }
84
86
  disconnectedCallback() {
85
- super.disconnectedCallback(), this.removeEventListener("pointerenter", this._onPointerEnter), this.removeEventListener("pointerleave", this._onPointerLeave), this.removeEventListener("focusin", this._onFocusIn), this.removeEventListener("focusout", this._onFocusOut), clearTimeout(this._showTimer), clearTimeout(this._hideTimer), this._lifecycle.dispose(), this._syncVisibleTooltipCount(!1), this._panel.dispose();
87
+ super.disconnectedCallback(), this.removeEventListener("pointerenter", this._onPointerEnter), this.removeEventListener("pointerleave", this._onPointerLeave), this.removeEventListener("focusin", this._onFocusIn), this.removeEventListener("focusout", this._onFocusOut), clearTimeout(this._showTimer), clearTimeout(this._hideTimer), this._overlay.suspend(), this._syncVisibleTooltipCount(!1), this._panel.dispose();
86
88
  }
87
89
  firstUpdated() {
88
90
  this.open && (this._openOverlay(this._shouldOpenInstantly), this._shouldOpenInstantly = !0);
89
91
  }
90
92
  updated(e) {
91
- e.has("portal") || e.has("overlayContainer") ? (this._lifecycle.invalidate(), this._lifecycle.scheduleFrame(() => this._reconfigureOverlay())) : (e.has("placement") || e.has("offset")) && requestAnimationFrame(() => this._panel.updatePosition()), e.has("open") && (this._syncVisibleTooltipCount(this.open), this.open ? (this._openOverlay(this._shouldOpenInstantly), this._shouldOpenInstantly = !0) : (this._lifecycle.invalidate(), this._closeOverlay()), this._userOpenChange.consume() && this._dispatchChange(this.open)), e.has("content") && this._syncPortalContent();
93
+ e.has("portal") || e.has("overlayContainer") ? (this._overlay.invalidate(), this._overlay.scheduleFrame(() => this._reconfigureOverlay())) : (e.has("placement") || e.has("offset")) && requestAnimationFrame(() => this._panel.updatePosition()), e.has("open") && (this._syncVisibleTooltipCount(this.open), this.open ? (this._openOverlay(this._shouldOpenInstantly), this._shouldOpenInstantly = !0) : (this._overlay.invalidate(), this._closeOverlay()), this._userOpenChange.consume() && this._dispatchChange(this.open)), e.has("content") && this._syncPortalContent();
92
94
  }
93
95
  _show(e = !1) {
94
96
  this.open ||= (this._shouldOpenInstantly = e, this._userOpenChange.mark(), !0);
@@ -100,10 +102,10 @@ var _ = 0, v = class extends f {
100
102
  this._panel.open(e);
101
103
  }
102
104
  _createPortal() {
103
- let e = s().make({
105
+ let e = c().make({
104
106
  container: this.overlayContainer,
105
107
  target: this,
106
- style: `${t}\n${n}\n${d}`,
108
+ style: `${t}\n${i}\n${d}`,
107
109
  className: "tooltip-panel portal wui-glass wui-floating-panel",
108
110
  migrateAddedNodes: (e) => e.filter((e) => e instanceof Element && e.getAttribute("slot") === "content")
109
111
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/tooltip/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { defineOverlayLifecycle } from '@/shared/overlay/lifecycle'\nimport { FLOATING_PLACEMENTS } from '@/shared/overlay/placement-props'\nimport { defineOverlayPortal } from '@/shared/overlay/portal'\nimport type { OverlayContainer, OverlayPortal } from '@/shared/overlay/portal'\n\nimport style from './style.css?inline'\n\nlet visibleTooltipCount = 0\n\n@customElement('web-ui-tooltip')\nexport class WebUiTooltip extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, FLOATING_PLACEMENTS, 'top')\n this.requestUpdate('placement', old)\n }\n private _placement: Placement = 'top'\n\n @property({ type: Number, attribute: 'show-delay' })\n get showDelay(): number {\n return this._showDelay\n }\n set showDelay(v: number) {\n const old = this._showDelay\n this._showDelay = normalizeNumber(v, 0, 5000, 200)\n this.requestUpdate('showDelay', old)\n }\n private _showDelay = 200\n\n @property({ type: Number, attribute: 'hide-delay' })\n get hideDelay(): number {\n return this._hideDelay\n }\n set hideDelay(v: number) {\n const old = this._hideDelay\n this._hideDelay = normalizeNumber(v, 0, 5000, 100)\n this.requestUpdate('hideDelay', old)\n }\n private _hideDelay = 100\n\n @property({ type: Boolean, reflect: true }) disabled = false\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n const old = this._offset\n this._offset = normalizeNumber(v, 0, 100, 6)\n this.requestUpdate('offset', old)\n }\n private _offset = 6\n\n @property({ type: String }) content = ''\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n\n private _showTimer?: ReturnType<typeof setTimeout>\n private _hideTimer?: ReturnType<typeof setTimeout>\n private _isCountedVisible = false\n private readonly _userOpenChange = new UserChangeController()\n private _shouldOpenInstantly = true\n private _portal?: OverlayPortal\n private readonly _lifecycle = defineOverlayLifecycle().make({\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n private readonly _panel = defineAnchoredPanel().make({\n getAnchor: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-panel') ?? null,\n getPositioning: () => ({\n placement: this.placement,\n offset: this.offset,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal()\n })\n\n get isOpen(): boolean {\n return this.open\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._lifecycle.resume()\n this.addEventListener('pointerenter', this._onPointerEnter)\n this.addEventListener('pointerleave', this._onPointerLeave)\n this.addEventListener('focusin', this._onFocusIn)\n this.addEventListener('focusout', this._onFocusOut)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('pointerenter', this._onPointerEnter)\n this.removeEventListener('pointerleave', this._onPointerLeave)\n this.removeEventListener('focusin', this._onFocusIn)\n this.removeEventListener('focusout', this._onFocusOut)\n clearTimeout(this._showTimer)\n clearTimeout(this._hideTimer)\n this._lifecycle.dispose()\n this._syncVisibleTooltipCount(false)\n this._panel.dispose()\n }\n\n override firstUpdated() {\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n }\n }\n\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('portal') || changed.has('overlayContainer')) {\n // 同帧 open + reconfigure 可能重复建事务;invalidate 保证 reconfigure 是当前唯一帧回调。\n this._lifecycle.invalidate()\n this._lifecycle.scheduleFrame(() => this._reconfigureOverlay())\n } else if (changed.has('placement') || changed.has('offset'))\n requestAnimationFrame(() => this._panel.updatePosition())\n\n if (changed.has('open')) {\n this._syncVisibleTooltipCount(this.open)\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n } else {\n this._lifecycle.invalidate()\n void this._closeOverlay()\n }\n if (this._userOpenChange.consume()) this._dispatchChange(this.open)\n }\n if (changed.has('content')) this._syncPortalContent()\n }\n\n private _onPointerEnter = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._hideTimer)\n // 已有 tooltip 在场时(指针滑到相邻目标):既不等延迟,也不播入场动画。\n const isRepeat = visibleTooltipCount > 0\n this._showTimer = setTimeout(() => this._show(isRepeat), isRepeat ? 0 : this.showDelay)\n }\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hideTimer = setTimeout(() => this._hide(), this.hideDelay)\n }\n\n private _onFocusIn = (event: FocusEvent) => {\n if (this.disabled) return\n if (!(event.target instanceof Element) || !event.target.matches(':focus-visible')) return\n\n clearTimeout(this._hideTimer)\n this._show(true)\n }\n\n private _onFocusOut = () => {\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hide()\n }\n\n private _show(isInstant = false) {\n if (this.open) return\n this._shouldOpenInstantly = isInstant\n this._userOpenChange.mark()\n this.open = true\n }\n\n private _hide() {\n if (!this.open) return\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _openOverlay(isInstant = false) {\n this._panel.open(isInstant)\n }\n\n private _createPortal(): OverlayPortal {\n const portal = defineOverlayPortal().make({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'tooltip-panel portal wui-glass wui-floating-panel',\n // 已迁移节点被框架删除后的解除追踪是 portal 内建默认行为,这里无需 onContentChange\n // 打开期实时渲染:新增的 slot=content 元素实时迁入面板(注释锚点无法携带\n // slot 属性,天然留在宿主作为框架 patch 的插入基准)。\n migrateAddedNodes: addedNodes =>\n addedNodes.filter(node => node instanceof Element && node.getAttribute('slot') === 'content')\n })\n this._portal = portal\n if (this.content) {\n const text = document.createElement('span')\n text.className = 'tooltip-text'\n text.textContent = this.content\n portal.panel.append(text)\n } else portal.moveContent(Array.from(this.querySelectorAll('[slot=\"content\"]')))\n portal.panel.setAttribute('role', 'tooltip')\n return portal\n }\n\n private async _closeOverlay() {\n await this._panel.close(() => this.open)\n }\n\n private _reconfigureOverlay() {\n this._panel.reconfigure(this.open)\n }\n\n private _syncPortalContent() {\n if (!this.portal || !this.open) return\n const localPanel = this._panel.getPanel()\n if (!localPanel) return\n const panel = this._portal?.panel ?? localPanel\n const text = localPanel.querySelector<HTMLElement>('.tooltip-text')\n if (text) {\n text.textContent = this.content\n return\n }\n if (!this.content) return\n panel.replaceChildren()\n const nextText = document.createElement('span')\n nextText.className = 'tooltip-text'\n nextText.textContent = this.content\n panel.append(nextText)\n }\n\n private _dispatchChange(open: boolean) {\n dispatchOpenChangeEvent(this, open)\n }\n\n private _syncVisibleTooltipCount(isVisible: boolean) {\n if (isVisible && !this._isCountedVisible) {\n visibleTooltipCount += 1\n this._isCountedVisible = true\n } else if (!isVisible && this._isCountedVisible) {\n visibleTooltipCount = Math.max(0, visibleTooltipCount - 1)\n this._isCountedVisible = false\n }\n }\n\n override render() {\n return html`\n <div class=\"tooltip-anchor\">\n <div class=\"tooltip-trigger\"><slot></slot></div>\n <div class=\"tooltip-panel wui-floating-panel wui-glass\" hidden role=\"tooltip\">\n ${this.content ? html`<span class=\"tooltip-text\">${this.content}</span>` : html`<slot name=\"content\"></slot>`}\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-tooltip': WebUiTooltip\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/tooltip/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { dispatchOpenChangeEvent } from '@/shared/open-state'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { defineOpenOverlay } from '@/shared/overlay/open-overlay'\nimport { FLOATING_PLACEMENTS } from '@/shared/overlay/placement-props'\nimport { defineOverlayPortal } from '@/shared/overlay/portal'\nimport type { OverlayContainer, OverlayPortal } from '@/shared/overlay/portal'\n\nimport style from './style.css?inline'\n\nlet visibleTooltipCount = 0\n\n@customElement('web-ui-tooltip')\nexport class WebUiTooltip extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, FLOATING_PLACEMENTS, 'top')\n this.requestUpdate('placement', old)\n }\n private _placement: Placement = 'top'\n\n @property({ type: Number, attribute: 'show-delay' })\n get showDelay(): number {\n return this._showDelay\n }\n set showDelay(v: number) {\n const old = this._showDelay\n this._showDelay = normalizeNumber(v, 0, 5000, 200)\n this.requestUpdate('showDelay', old)\n }\n private _showDelay = 200\n\n @property({ type: Number, attribute: 'hide-delay' })\n get hideDelay(): number {\n return this._hideDelay\n }\n set hideDelay(v: number) {\n const old = this._hideDelay\n this._hideDelay = normalizeNumber(v, 0, 5000, 100)\n this.requestUpdate('hideDelay', old)\n }\n private _hideDelay = 100\n\n @property({ type: Boolean, reflect: true }) disabled = false\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n const old = this._offset\n this._offset = normalizeNumber(v, 0, 100, 6)\n this.requestUpdate('offset', old)\n }\n private _offset = 6\n\n @property({ type: String }) content = ''\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n\n private _showTimer?: ReturnType<typeof setTimeout>\n private _hideTimer?: ReturnType<typeof setTimeout>\n private _isCountedVisible = false\n private readonly _userOpenChange = new UserChangeController()\n private _shouldOpenInstantly = true\n private _portal?: OverlayPortal\n /**\n * 实例作用域的帧事务入口。`requestClose` 是关闭入口的唯一接线:面板的\n * `arbitration: 'none'` 让 tooltip 在组合树里但永不成候选,因此这条**现在不会被\n * 模块调用**。保留它是不想把「怎么关」藏起来,也让仲裁策略变化时依然成立。\n */\n private readonly _overlay = defineOpenOverlay().make({\n requestClose: () => this._hide(),\n isConnected: () => this.isConnected\n })\n private readonly _panel = defineAnchoredPanel().make({\n getAnchor: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-panel') ?? null,\n getPositioning: () => ({\n placement: this.placement,\n offset: this.offset,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal(),\n openOverlay: this._overlay,\n // tooltip 在逻辑组合树里(面板迁移后仍要判对「谁在谁里面」),但从不作为 Escape\n // 候选:按键穿过它落到下层浮层,与重构前一致。\n arbitration: 'none'\n })\n\n get isOpen(): boolean {\n return this.open\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._overlay.resume()\n this.addEventListener('pointerenter', this._onPointerEnter)\n this.addEventListener('pointerleave', this._onPointerLeave)\n this.addEventListener('focusin', this._onFocusIn)\n this.addEventListener('focusout', this._onFocusOut)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('pointerenter', this._onPointerEnter)\n this.removeEventListener('pointerleave', this._onPointerLeave)\n this.removeEventListener('focusin', this._onFocusIn)\n this.removeEventListener('focusout', this._onFocusOut)\n clearTimeout(this._showTimer)\n clearTimeout(this._hideTimer)\n // 停止帧调度;撤销登记由 _panel.dispose() 完成(句柄归它持有)。\n this._overlay.suspend()\n this._syncVisibleTooltipCount(false)\n this._panel.dispose()\n }\n\n override firstUpdated() {\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n }\n }\n\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('portal') || changed.has('overlayContainer')) {\n // 同帧 open + reconfigure 可能重复建事务;invalidate 保证 reconfigure 是当前唯一帧回调。\n this._overlay.invalidate()\n this._overlay.scheduleFrame(() => this._reconfigureOverlay())\n } else if (changed.has('placement') || changed.has('offset'))\n requestAnimationFrame(() => this._panel.updatePosition())\n\n if (changed.has('open')) {\n this._syncVisibleTooltipCount(this.open)\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n } else {\n this._overlay.invalidate()\n void this._closeOverlay()\n }\n if (this._userOpenChange.consume()) this._dispatchChange(this.open)\n }\n if (changed.has('content')) this._syncPortalContent()\n }\n\n private _onPointerEnter = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._hideTimer)\n // 已有 tooltip 在场时(指针滑到相邻目标):既不等延迟,也不播入场动画。\n const isRepeat = visibleTooltipCount > 0\n this._showTimer = setTimeout(() => this._show(isRepeat), isRepeat ? 0 : this.showDelay)\n }\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hideTimer = setTimeout(() => this._hide(), this.hideDelay)\n }\n\n private _onFocusIn = (event: FocusEvent) => {\n if (this.disabled) return\n if (!(event.target instanceof Element) || !event.target.matches(':focus-visible')) return\n\n clearTimeout(this._hideTimer)\n this._show(true)\n }\n\n private _onFocusOut = () => {\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hide()\n }\n\n private _show(isInstant = false) {\n if (this.open) return\n this._shouldOpenInstantly = isInstant\n this._userOpenChange.mark()\n this.open = true\n }\n\n private _hide() {\n if (!this.open) return\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _openOverlay(isInstant = false) {\n this._panel.open(isInstant)\n }\n\n private _createPortal(): OverlayPortal {\n const portal = defineOverlayPortal().make({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'tooltip-panel portal wui-glass wui-floating-panel',\n // 已迁移节点被框架删除后的解除追踪是 portal 内建默认行为,这里无需 onContentChange\n // 打开期实时渲染:新增的 slot=content 元素实时迁入面板(注释锚点无法携带\n // slot 属性,天然留在宿主作为框架 patch 的插入基准)。\n migrateAddedNodes: addedNodes =>\n addedNodes.filter(node => node instanceof Element && node.getAttribute('slot') === 'content')\n })\n this._portal = portal\n if (this.content) {\n const text = document.createElement('span')\n text.className = 'tooltip-text'\n text.textContent = this.content\n portal.panel.append(text)\n } else portal.moveContent(Array.from(this.querySelectorAll('[slot=\"content\"]')))\n portal.panel.setAttribute('role', 'tooltip')\n return portal\n }\n\n private async _closeOverlay() {\n await this._panel.close(() => this.open)\n }\n\n private _reconfigureOverlay() {\n this._panel.reconfigure(this.open)\n }\n\n private _syncPortalContent() {\n if (!this.portal || !this.open) return\n const localPanel = this._panel.getPanel()\n if (!localPanel) return\n const panel = this._portal?.panel ?? localPanel\n const text = localPanel.querySelector<HTMLElement>('.tooltip-text')\n if (text) {\n text.textContent = this.content\n return\n }\n if (!this.content) return\n panel.replaceChildren()\n const nextText = document.createElement('span')\n nextText.className = 'tooltip-text'\n nextText.textContent = this.content\n panel.append(nextText)\n }\n\n private _dispatchChange(open: boolean) {\n dispatchOpenChangeEvent(this, open)\n }\n\n private _syncVisibleTooltipCount(isVisible: boolean) {\n if (isVisible && !this._isCountedVisible) {\n visibleTooltipCount += 1\n this._isCountedVisible = true\n } else if (!isVisible && this._isCountedVisible) {\n visibleTooltipCount = Math.max(0, visibleTooltipCount - 1)\n this._isCountedVisible = false\n }\n }\n\n override render() {\n return html`\n <div class=\"tooltip-anchor\">\n <div class=\"tooltip-trigger\"><slot></slot></div>\n <div class=\"tooltip-panel wui-floating-panel wui-glass\" hidden role=\"tooltip\">\n ${this.content ? html`<span class=\"tooltip-text\">${this.content}</span>` : html`<slot name=\"content\"></slot>`}\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-tooltip': WebUiTooltip\n }\n}\n"],"mappings":"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"}
@@ -1,32 +1,32 @@
1
1
  function e(e, t) {
2
- let n = typeof PointerEvent < "u" && e instanceof PointerEvent, r = n ? e.currentTarget || e.target : e, { axis: i = "both", threshold: a = 0, onStart: o, onMove: s, onEnd: c, onTap: l, onCancel: u } = t, d = null, f = 0, p = 0, m = a <= 0, h = [], g = null, _ = null;
3
- function v(e) {
2
+ let n = typeof PointerEvent < "u" && e instanceof PointerEvent, r = n ? e.currentTarget || e.target : e, { axis: i = "both", threshold: a = 0, calibrateOnFirstMove: o = !1, onStart: s, onMove: c, onEnd: l, onTap: u, onCancel: d } = t, f = null, p = 0, m = 0, h = 0, g = a <= 0, _ = !1, v = [], y = null, b = null;
3
+ function x(e) {
4
4
  e.cancelable && e.preventDefault();
5
5
  }
6
- function y(e) {
6
+ function S(e) {
7
7
  let t = /* @__PURE__ */ new Set(), n = typeof e.composedPath == "function" ? e.composedPath() : [];
8
8
  for (let e of n) {
9
9
  let n = e.nodeType;
10
- (n === Node.ELEMENT_NODE || n === Node.DOCUMENT_FRAGMENT_NODE) && (e.addEventListener("touchmove", v, {
10
+ (n === Node.ELEMENT_NODE || n === Node.DOCUMENT_FRAGMENT_NODE) && (e.addEventListener("touchmove", x, {
11
11
  capture: !0,
12
12
  passive: !1
13
13
  }), t.add(e));
14
14
  }
15
- _ = t;
15
+ b = t;
16
16
  }
17
- function b() {
18
- if (_) {
19
- for (let e of _) e.removeEventListener("touchmove", v, !0);
20
- _ = null;
17
+ function C() {
18
+ if (b) {
19
+ for (let e of b) e.removeEventListener("touchmove", x, !0);
20
+ b = null;
21
21
  }
22
22
  }
23
- function x() {
24
- if (h.length < 2) return {
23
+ function w() {
24
+ if (v.length < 2) return {
25
25
  vx: 0,
26
26
  vy: 0,
27
27
  v: 0
28
28
  };
29
- let e = h[0], t = h[h.length - 1], n = t.t - e.t;
29
+ let e = v[0], t = v[v.length - 1], n = t.t - e.t;
30
30
  if (n < 2) return {
31
31
  vx: 0,
32
32
  vy: 0,
@@ -39,88 +39,89 @@ function e(e, t) {
39
39
  v: o
40
40
  };
41
41
  }
42
- function S() {
43
- window.removeEventListener("pointermove", O, !0), window.removeEventListener("pointerup", k, !0), window.removeEventListener("pointercancel", A, !0), window.removeEventListener("lostpointercapture", j, !0);
42
+ function T() {
43
+ window.removeEventListener("pointermove", j, !0), window.removeEventListener("pointerup", M, !0), window.removeEventListener("pointercancel", N, !0), window.removeEventListener("lostpointercapture", P, !0);
44
44
  }
45
- function C() {
46
- window.addEventListener("pointermove", O, !0), window.addEventListener("pointerup", k, !0), window.addEventListener("pointercancel", A, !0), window.addEventListener("lostpointercapture", j, !0);
45
+ function E() {
46
+ window.addEventListener("pointermove", j, !0), window.addEventListener("pointerup", M, !0), window.addEventListener("pointercancel", N, !0), window.addEventListener("lostpointercapture", P, !0);
47
47
  }
48
- function w() {
49
- r && (r.removeEventListener("pointermove", O), r.removeEventListener("pointerup", k), r.removeEventListener("pointercancel", A), r.removeEventListener("lostpointercapture", j));
48
+ function D() {
49
+ r && (r.removeEventListener("pointermove", j), r.removeEventListener("pointerup", M), r.removeEventListener("pointercancel", N), r.removeEventListener("lostpointercapture", P));
50
50
  }
51
- function T() {
52
- r && (r.addEventListener("pointermove", O), r.addEventListener("pointerup", k), r.addEventListener("pointercancel", A), r.addEventListener("lostpointercapture", j));
51
+ function O() {
52
+ r && (r.addEventListener("pointermove", j), r.addEventListener("pointerup", M), r.addEventListener("pointercancel", N), r.addEventListener("lostpointercapture", P));
53
53
  }
54
- function E() {
55
- if (d !== null && r && typeof r.hasPointerCapture == "function") try {
56
- r.hasPointerCapture(d) && r.releasePointerCapture(d);
54
+ function k() {
55
+ if (f !== null && r && typeof r.hasPointerCapture == "function") try {
56
+ r.hasPointerCapture(f) && r.releasePointerCapture(f);
57
57
  } catch {}
58
- d = null, m = a <= 0, h = [], g = null, S(), w(), b();
58
+ f = null, g = a <= 0, _ = !1, v = [], y = null, T(), D(), C();
59
59
  }
60
- function D(e) {
61
- if (d !== null || e.pointerType === "touch" && e.isPrimary === !1) return;
60
+ function A(e) {
61
+ if (f !== null || e.pointerType === "touch" && e.isPrimary === !1) return;
62
62
  let t = {
63
63
  pointerId: e.pointerId,
64
64
  clientX: e.clientX,
65
65
  clientY: e.clientY,
66
66
  timeStamp: e.timeStamp
67
67
  };
68
- if (o?.(t, e) !== !1 && (d = e.pointerId, f = e.clientX, p = e.clientY, m = a <= 0, h = [{
68
+ if (s?.(t, e) !== !1 && (f = e.pointerId, p = e.clientX, m = e.clientY, h = e.timeStamp, g = a <= 0, _ = !1, v = [{
69
69
  t: e.timeStamp,
70
70
  x: e.clientX,
71
71
  y: e.clientY
72
- }], g = null, C(), T(), y(e), m)) try {
72
+ }], y = null, E(), O(), S(e), g)) try {
73
73
  r?.setPointerCapture?.(e.pointerId);
74
74
  } catch {}
75
75
  }
76
- function O(e) {
77
- if (d !== e.pointerId || g === e) return;
78
- g = e;
79
- let t = e.clientX - f, n = e.clientY - p;
80
- if (!m) {
76
+ function j(e) {
77
+ if (f !== e.pointerId || y === e) return;
78
+ if (y = e, !g) {
79
+ let t = e.clientX - p, n = e.clientY - m;
81
80
  if ((i === "x" ? Math.abs(t) : i === "y" ? Math.abs(n) : Math.hypot(t, n)) < a) return;
82
81
  if (i === "x" && Math.abs(n) > Math.abs(t)) {
83
- M();
82
+ F();
84
83
  return;
85
84
  }
86
85
  if (i === "y" && Math.abs(t) > Math.abs(n)) {
87
- M();
86
+ F();
88
87
  return;
89
88
  }
90
- m = !0;
89
+ g = !0, h = e.timeStamp;
91
90
  try {
92
91
  r?.setPointerCapture?.(e.pointerId);
93
92
  } catch {}
94
93
  }
94
+ o && !_ && (_ = !0, p = e.clientX, m = e.clientY, h = e.timeStamp);
95
+ let t = e.clientX - p, n = e.clientY - m;
95
96
  e.cancelable && e.preventDefault();
96
- let o = e.timeStamp;
97
- for (h.push({
98
- t: o,
97
+ let s = e.timeStamp;
98
+ for (v.push({
99
+ t: s,
99
100
  x: e.clientX,
100
101
  y: e.clientY
101
- }); h.length > 2 && o - h[0].t > 100;) h.shift();
102
- let { vx: c, vy: l, v: u } = x(), _ = {
102
+ }); v.length > 2 && s - v[0].t > 100;) v.shift();
103
+ let { vx: l, vy: u, v: d } = w(), b = {
103
104
  pointerId: e.pointerId,
104
105
  clientX: e.clientX,
105
106
  clientY: e.clientY,
106
107
  deltaX: t,
107
108
  deltaY: n,
108
- velocityX: c,
109
- velocityY: l,
110
- velocity: u,
111
- timeStamp: o
109
+ velocityX: l,
110
+ velocityY: u,
111
+ velocity: d,
112
+ timeStamp: s
112
113
  };
113
- s?.(_, e);
114
+ c?.(b, e);
114
115
  }
115
- function k(e) {
116
- if (d !== e.pointerId) return;
116
+ function M(e) {
117
+ if (f !== e.pointerId) return;
117
118
  let t = e.timeStamp;
118
- for (h.push({
119
+ for (v.push({
119
120
  t,
120
121
  x: e.clientX,
121
122
  y: e.clientY
122
- }); h.length > 2 && t - h[0].t > 100;) h.shift();
123
- let n = e.clientX - f, r = e.clientY - p, { vx: i, vy: a, v: o } = x(), s = {
123
+ }); v.length > 2 && t - v[0].t > 100;) v.shift();
124
+ let n = e.clientX - p, r = e.clientY - m, { vx: i, vy: a, v: o } = w(), s = {
124
125
  pointerId: e.pointerId,
125
126
  clientX: e.clientX,
126
127
  clientY: e.clientY,
@@ -129,24 +130,25 @@ function e(e, t) {
129
130
  velocityX: i,
130
131
  velocityY: a,
131
132
  velocity: o,
132
- timeStamp: e.timeStamp
133
- }, g = m;
134
- E(), g ? c?.(s, e) : (l?.(s, e), u?.(e));
133
+ timeStamp: e.timeStamp,
134
+ duration: Math.max(0, e.timeStamp - h)
135
+ }, c = g;
136
+ k(), c ? l?.(s, e) : (u?.(s, e), d?.(e));
135
137
  }
136
- function A(e) {
137
- d === e.pointerId && (E(), u?.(e));
138
+ function N(e) {
139
+ f === e.pointerId && (k(), d?.(e));
138
140
  }
139
- function j(e) {
140
- d === e.pointerId && e.target === r && (E(), u?.(e));
141
+ function P(e) {
142
+ f === e.pointerId && e.target === r && (k(), d?.(e));
141
143
  }
142
- function M() {
143
- d !== null && (E(), u?.());
144
+ function F() {
145
+ f !== null && (k(), d?.());
144
146
  }
145
- return n ? D(e) : r && (r.addEventListener("pointerdown", D), r.addEventListener("pointermove", O), r.addEventListener("pointerup", k), r.addEventListener("pointercancel", A)), {
146
- isDragging: () => d !== null,
147
- cancel: M,
147
+ return n ? A(e) : r && (r.addEventListener("pointerdown", A), r.addEventListener("pointermove", j), r.addEventListener("pointerup", M), r.addEventListener("pointercancel", N)), {
148
+ isDragging: () => f !== null,
149
+ cancel: F,
148
150
  destroy: () => {
149
- M(), !n && r && (r.removeEventListener("pointerdown", D), r.removeEventListener("pointermove", O), r.removeEventListener("pointerup", k), r.removeEventListener("pointercancel", A));
151
+ F(), !n && r && (r.removeEventListener("pointerdown", A), r.removeEventListener("pointermove", j), r.removeEventListener("pointerup", M), r.removeEventListener("pointercancel", N));
150
152
  }
151
153
  };
152
154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"drag-gesture.js","names":[],"sources":["../../../src/shared/gesture/drag-gesture.ts"],"sourcesContent":["/**\n * 跨平台/跨浏览器 Pointer 拖拽手势生命周期管理器\n *\n * 封装:\n * 1. setPointerCapture 安全捕获与 window 兜底监听(防 pointer 丢失/离开视口悬挂)\n * 2. 多点触控副指针与未聚焦指针过滤\n * 3. 意图死区(threshold)与滑动轴向判定\n * 4. 100ms 滑动窗口平滑速度估算(Velocity px/s)\n * 5. Window / Handle 跨层事件去重(防重复采样)\n */\n\nconst VELOCITY_WINDOW_MS = 100\n\nexport interface DragSample {\n t: number\n x: number\n y: number\n}\n\nexport interface DragStartInfo {\n pointerId: number\n clientX: number\n clientY: number\n timeStamp: number\n}\n\nexport interface DragMoveInfo {\n pointerId: number\n clientX: number\n clientY: number\n deltaX: number\n deltaY: number\n velocityX: number\n velocityY: number\n velocity: number // 沿配置主轴(若为 both 则为欧氏速度)\n timeStamp: number\n}\n\nexport interface DragEndInfo {\n pointerId: number\n clientX: number\n clientY: number\n deltaX: number\n deltaY: number\n velocityX: number\n velocityY: number\n velocity: number\n timeStamp: number\n}\n\nexport interface DragGestureOptions {\n /** 允许拖拽的轴向,默认 'both' */\n axis?: 'x' | 'y' | 'both'\n /** 拖拽启动的意图死区阈值(像素)。默认 0(即时跟手) */\n threshold?: number\n /** 手势启动回调。返回 false 可取消本次拖拽 */\n onStart?: (info: DragStartInfo, event: PointerEvent) => boolean | void\n /** 拖拽移动回调 */\n onMove?: (info: DragMoveInfo, event: PointerEvent) => void\n /** 正常松手回调 */\n onEnd?: (info: DragEndInfo, event: PointerEvent) => void\n /** 未通过意图阈值的 pointerup 回调,供点击定位类控件提交一次 tap */\n onTap?: (info: DragEndInfo, event: PointerEvent) => void\n /** 手势取消/被打断回调(如 pointercancel、外部强行 cancel) */\n onCancel?: (event?: PointerEvent) => void\n}\n\nexport interface DragGestureHandle {\n /** 当前是否有正在进行的拖拽手势 */\n isDragging: () => boolean\n /** 外部强制取消当前拖拽(恢复初态并触发 onCancel) */\n cancel: () => void\n /** 彻底销毁手势监听器与解绑 DOM */\n destroy: () => void\n}\n\n/**\n * 为指定元素挂载统一的 Pointer 拖拽手势,或在已有的 pointerdown 事件中启动手势\n */\nexport function attachDragGesture(\n targetOrEvent: HTMLElement | PointerEvent,\n options: DragGestureOptions\n): DragGestureHandle {\n const isEvent = typeof PointerEvent !== 'undefined' && targetOrEvent instanceof PointerEvent\n const target: HTMLElement | null = isEvent\n ? (targetOrEvent.currentTarget as HTMLElement | null) || (targetOrEvent.target as HTMLElement | null)\n : (targetOrEvent as HTMLElement)\n\n const { axis = 'both', threshold = 0, onStart, onMove, onEnd, onTap, onCancel } = options\n\n let activePointerId: number | null = null\n let startX = 0\n let startY = 0\n let isThresholdPassed = threshold <= 0\n let samples: DragSample[] = []\n let handledMoveEvent: PointerEvent | null = null\n let touchGuardTargets: Set<EventTarget> | null = null\n\n /**\n * iOS Safari 滚动接管兜底:\n * WebKit 对 shadow DOM 内 touch-action 的交集计算存在怪癖,触摸落在深层子元素\n * (如 slider thumb、segmented-trigger 的 shadow 内容)时,声明在手势元素上的\n * touch-action: none 可能不生效,iOS 会把触摸判为滚动意图并在越过阈值前触发\n * pointercancel,拖拽因此中断。非被动 touchmove + preventDefault 是 WebKit 也\n * 尊重的最后手段:页面显式处理触摸移动后,滚动识别器不会获胜,也就不会接管。\n */\n function handleTouchMove(e: Event) {\n if (e.cancelable) e.preventDefault()\n }\n\n /**\n * 沿 pointerdown 的 composedPath 在每个 Element/ShadowRoot 挂非被动 touchmove\n * 监听。TouchEvent 不像 PointerEvent 那样可组合:非 composed 的 touchmove 只在\n * 落点所在的 shadow tree 内传播(连 host 都不经过),所以单一手势元素上的监听\n * 对 segmented-trigger 这类深层 shadow 内容完全失效。composedPath 覆盖落点的\n * 全部 shadow tree 链,无论 touchmove 在哪一层传播,路径上必有我们挂载的节点\n * 能收到事件并 preventDefault。document/window 不在挂载范围:touchmove 到不了\n * 它们,挂上也只是死代码。\n */\n function attachTouchGuards(e: PointerEvent) {\n const targets = new Set<EventTarget>()\n const path = typeof e.composedPath === 'function' ? e.composedPath() : []\n for (const node of path) {\n const type = (node as Node).nodeType\n if (type === Node.ELEMENT_NODE || type === Node.DOCUMENT_FRAGMENT_NODE) {\n node.addEventListener('touchmove', handleTouchMove, { capture: true, passive: false })\n targets.add(node)\n }\n }\n touchGuardTargets = targets\n }\n\n function detachTouchGuards() {\n if (!touchGuardTargets) return\n for (const node of touchGuardTargets) {\n node.removeEventListener('touchmove', handleTouchMove, true)\n }\n touchGuardTargets = null\n }\n\n function estimateVelocity(): { vx: number; vy: number; v: number } {\n if (samples.length < 2) return { vx: 0, vy: 0, v: 0 }\n const first = samples[0]\n const last = samples[samples.length - 1]\n const dt = last.t - first.t\n // 采样跨度不足 2ms(同帧/同毫秒的合成或极端快速事件)时不可信,置 0;\n // 2-8ms 的超短轻扫仍按真实跨度上报速度,避免「快速轻扫」被误判为无速度\n // 而回弹(旧守卫 <8ms 会把真实超短轻扫的速度清零)。\n if (dt < 2) return { vx: 0, vy: 0, v: 0 }\n const vx = ((last.x - first.x) / dt) * 1000\n const vy = ((last.y - first.y) / dt) * 1000\n let v = 0\n if (axis === 'x') v = vx\n else if (axis === 'y') v = vy\n else v = Math.hypot(vx, vy)\n return { vx, vy, v }\n }\n\n function detachWindowListeners() {\n window.removeEventListener('pointermove', handlePointerMove, true)\n window.removeEventListener('pointerup', handlePointerUp, true)\n window.removeEventListener('pointercancel', handlePointerCancel, true)\n window.removeEventListener('lostpointercapture', handleLostPointerCapture, true)\n }\n\n function attachWindowListeners() {\n window.addEventListener('pointermove', handlePointerMove, true)\n window.addEventListener('pointerup', handlePointerUp, true)\n window.addEventListener('pointercancel', handlePointerCancel, true)\n window.addEventListener('lostpointercapture', handleLostPointerCapture, true)\n }\n\n function detachTargetListeners() {\n if (!target) return\n target.removeEventListener('pointermove', handlePointerMove)\n target.removeEventListener('pointerup', handlePointerUp)\n target.removeEventListener('pointercancel', handlePointerCancel)\n target.removeEventListener('lostpointercapture', handleLostPointerCapture)\n }\n\n function attachTargetListeners() {\n if (!target) return\n target.addEventListener('pointermove', handlePointerMove)\n target.addEventListener('pointerup', handlePointerUp)\n target.addEventListener('pointercancel', handlePointerCancel)\n target.addEventListener('lostpointercapture', handleLostPointerCapture)\n }\n\n function resetState() {\n if (activePointerId !== null && target && typeof target.hasPointerCapture === 'function') {\n try {\n if (target.hasPointerCapture(activePointerId)) {\n target.releasePointerCapture(activePointerId)\n }\n } catch {\n // ignore\n }\n }\n activePointerId = null\n isThresholdPassed = threshold <= 0\n samples = []\n handledMoveEvent = null\n detachWindowListeners()\n detachTargetListeners()\n detachTouchGuards()\n }\n\n function handlePointerDown(e: PointerEvent) {\n if (activePointerId !== null) return\n // 真实触控环境下的多点触控副指针不启动手势(合成事件或鼠标环境缺省时不拦截)\n if (e.pointerType === 'touch' && e.isPrimary === false) return\n\n const startInfo: DragStartInfo = {\n pointerId: e.pointerId,\n clientX: e.clientX,\n clientY: e.clientY,\n timeStamp: e.timeStamp\n }\n\n if (onStart?.(startInfo, e) === false) return\n\n activePointerId = e.pointerId\n startX = e.clientX\n startY = e.clientY\n isThresholdPassed = threshold <= 0\n samples = [{ t: e.timeStamp, x: e.clientX, y: e.clientY }]\n handledMoveEvent = null\n\n attachWindowListeners()\n attachTargetListeners()\n attachTouchGuards(e)\n\n // 仅在无意图死区时立即捕获指针;若存在死区,延迟至确认拖拽后捕获,避免干扰子元素原生点击\n if (isThresholdPassed) {\n try {\n target?.setPointerCapture?.(e.pointerId)\n } catch {\n // 忽略合成指针或不可捕获上下文(如测试环境)\n }\n }\n }\n\n function handlePointerMove(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n if (handledMoveEvent === e) return\n handledMoveEvent = e\n\n const deltaX = e.clientX - startX\n const deltaY = e.clientY - startY\n\n if (!isThresholdPassed) {\n const distance = axis === 'x' ? Math.abs(deltaX) : axis === 'y' ? Math.abs(deltaY) : Math.hypot(deltaX, deltaY)\n if (distance < threshold) return\n\n // 若限定了 X 轴且 Y 轴位移明显更大,视为滚动意图而非拖拽\n if (axis === 'x' && Math.abs(deltaY) > Math.abs(deltaX)) {\n cancel()\n return\n }\n // 若限定了 Y 轴且 X 轴位移明显更大,放弃拖拽\n if (axis === 'y' && Math.abs(deltaX) > Math.abs(deltaY)) {\n cancel()\n return\n }\n isThresholdPassed = true\n try {\n target?.setPointerCapture?.(e.pointerId)\n } catch {\n // 忽略合成指针\n }\n }\n\n // 确认拖拽后阻止默认滚动/文字选中\n if (e.cancelable) e.preventDefault()\n\n const now = e.timeStamp\n samples.push({ t: now, x: e.clientX, y: e.clientY })\n while (samples.length > 2 && now - samples[0].t > VELOCITY_WINDOW_MS) {\n samples.shift()\n }\n\n const { vx, vy, v } = estimateVelocity()\n\n const moveInfo: DragMoveInfo = {\n pointerId: e.pointerId,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX,\n deltaY,\n velocityX: vx,\n velocityY: vy,\n velocity: v,\n timeStamp: now\n }\n\n onMove?.(moveInfo, e)\n }\n\n function handlePointerUp(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n\n const now = e.timeStamp\n samples.push({ t: now, x: e.clientX, y: e.clientY })\n while (samples.length > 2 && now - samples[0].t > VELOCITY_WINDOW_MS) {\n samples.shift()\n }\n\n const deltaX = e.clientX - startX\n const deltaY = e.clientY - startY\n const { vx, vy, v } = estimateVelocity()\n\n const endInfo: DragEndInfo = {\n pointerId: e.pointerId,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX,\n deltaY,\n velocityX: vx,\n velocityY: vy,\n velocity: v,\n timeStamp: e.timeStamp\n }\n\n const hadPassedThreshold = isThresholdPassed\n resetState()\n\n if (hadPassedThreshold) {\n onEnd?.(endInfo, e)\n } else {\n onTap?.(endInfo, e)\n onCancel?.(e)\n }\n }\n\n function handlePointerCancel(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n resetState()\n onCancel?.(e)\n }\n\n /**\n * lostpointercapture 不能当作 pointercancel:touch 指针在 pointerdown 时会被浏览器\n * 隐式捕获到命中元素(如 slider thumb、segmented-trigger 内容),组件随后\n * setPointerCapture(target) 把捕获转手到手势元素,命中元素先收到 lostpointercapture——\n * 这是正常转手而非取消,若直接走 cancel 会把刚启动的拖拽杀死(值只走一步即冻结)。\n * 只有手势元素自身意外丢失捕获(如元素被移出文档)才应取消。\n *\n * resetState 内 releasePointerCapture 产生的 lostpointercapture 会在 activePointerId\n * 置空之后异步到达,被 pointerId 守卫挡住,不会误伤。\n */\n function handleLostPointerCapture(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n if (e.target !== target) return\n resetState()\n onCancel?.(e)\n }\n\n function cancel() {\n if (activePointerId === null) return\n resetState()\n onCancel?.()\n }\n\n if (isEvent) {\n handlePointerDown(targetOrEvent)\n } else if (target) {\n target.addEventListener('pointerdown', handlePointerDown)\n target.addEventListener('pointermove', handlePointerMove)\n target.addEventListener('pointerup', handlePointerUp)\n target.addEventListener('pointercancel', handlePointerCancel)\n }\n\n return {\n isDragging: () => activePointerId !== null,\n cancel,\n destroy: () => {\n cancel()\n if (!isEvent && target) {\n target.removeEventListener('pointerdown', handlePointerDown)\n target.removeEventListener('pointermove', handlePointerMove)\n target.removeEventListener('pointerup', handlePointerUp)\n target.removeEventListener('pointercancel', handlePointerCancel)\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drag-gesture.js","names":[],"sources":["../../../src/shared/gesture/drag-gesture.ts"],"sourcesContent":["/**\n * 跨平台/跨浏览器 Pointer 拖拽手势生命周期管理器\n *\n * 封装:\n * 1. setPointerCapture 安全捕获与 window 兜底监听(防 pointer 丢失/离开视口悬挂)\n * 2. 多点触控副指针与未聚焦指针过滤\n * 3. 意图死区(threshold)与滑动轴向判定\n * 4. 可选的零点校准(calibrateOnFirstMove:首个有效 move 重置零点与判定时钟)\n * 5. 100ms 滑动窗口平滑速度估算(Velocity px/s)\n * 6. Window / Handle 跨层事件去重(防重复采样)\n */\n\nconst VELOCITY_WINDOW_MS = 100\n\nexport interface DragSample {\n t: number\n x: number\n y: number\n}\n\nexport interface DragStartInfo {\n pointerId: number\n clientX: number\n clientY: number\n timeStamp: number\n}\n\nexport interface DragMoveInfo {\n pointerId: number\n clientX: number\n clientY: number\n deltaX: number\n deltaY: number\n velocityX: number\n velocityY: number\n velocity: number // 沿配置主轴(若为 both 则为欧氏速度)\n timeStamp: number\n}\n\nexport interface DragEndInfo {\n pointerId: number\n clientX: number\n clientY: number\n deltaX: number\n deltaY: number\n velocityX: number\n velocityY: number\n velocity: number\n timeStamp: number\n /**\n * 手势从**被识别**(越过意图死区;无死区时为 pointerdown)到本次释放的总时长(ms)。\n * `velocity*` 是滑动窗口内的瞬时速度,只描述最后一小段轨迹;需要「整段手势的平均速度」\n * (如判定甩动意图)时用它做分母。\n */\n duration: number\n}\n\nexport interface DragGestureOptions {\n /** 允许拖拽的轴向,默认 'both' */\n axis?: 'x' | 'y' | 'both'\n /** 拖拽启动的意图死区阈值(像素)。默认 0(即时跟手) */\n threshold?: number\n /**\n * 首个有效 pointermove 时把拖拽零点与判定时钟一起重置到该点,默认 false。\n * 吸收「按下 → 首个 move」之间的空隙(触摸输入的首个 move 常已相对按下点偏移),\n * 否则这段位移会在首个 move 一次性兑现而让元素跳。开启后 `DragMoveInfo/DragEndInfo`\n * 的 `delta*` 与 `duration` 都以该点为零点与起点,代价是这段位移被丢弃。\n * 有意图死区时校准落在**越过死区的那次 move**(此时 `startTimeStamp` 的既有重置与之同值)。\n */\n calibrateOnFirstMove?: boolean\n /** 手势启动回调。返回 false 可取消本次拖拽 */\n onStart?: (info: DragStartInfo, event: PointerEvent) => boolean | void\n /** 拖拽移动回调 */\n onMove?: (info: DragMoveInfo, event: PointerEvent) => void\n /** 正常松手回调 */\n onEnd?: (info: DragEndInfo, event: PointerEvent) => void\n /** 未通过意图阈值的 pointerup 回调,供点击定位类控件提交一次 tap */\n onTap?: (info: DragEndInfo, event: PointerEvent) => void\n /** 手势取消/被打断回调(如 pointercancel、外部强行 cancel) */\n onCancel?: (event?: PointerEvent) => void\n}\n\nexport interface DragGestureHandle {\n /** 当前是否有正在进行的拖拽手势 */\n isDragging: () => boolean\n /** 外部强制取消当前拖拽(恢复初态并触发 onCancel) */\n cancel: () => void\n /** 彻底销毁手势监听器与解绑 DOM */\n destroy: () => void\n}\n\n/**\n * 为指定元素挂载统一的 Pointer 拖拽手势,或在已有的 pointerdown 事件中启动手势\n */\nexport function attachDragGesture(\n targetOrEvent: HTMLElement | PointerEvent,\n options: DragGestureOptions\n): DragGestureHandle {\n const isEvent = typeof PointerEvent !== 'undefined' && targetOrEvent instanceof PointerEvent\n const target: HTMLElement | null = isEvent\n ? (targetOrEvent.currentTarget as HTMLElement | null) || (targetOrEvent.target as HTMLElement | null)\n : (targetOrEvent as HTMLElement)\n\n const {\n axis = 'both',\n threshold = 0,\n calibrateOnFirstMove = false,\n onStart,\n onMove,\n onEnd,\n onTap,\n onCancel\n } = options\n\n let activePointerId: number | null = null\n let startX = 0\n let startY = 0\n let startTimeStamp = 0\n let isThresholdPassed = threshold <= 0\n let isCalibrated = false\n let samples: DragSample[] = []\n let handledMoveEvent: PointerEvent | null = null\n let touchGuardTargets: Set<EventTarget> | null = null\n\n /**\n * iOS Safari 滚动接管兜底:\n * WebKit 对 shadow DOM 内 touch-action 的交集计算存在怪癖,触摸落在深层子元素\n * (如 slider thumb、segmented-trigger 的 shadow 内容)时,声明在手势元素上的\n * touch-action: none 可能不生效,iOS 会把触摸判为滚动意图并在越过阈值前触发\n * pointercancel,拖拽因此中断。非被动 touchmove + preventDefault 是 WebKit 也\n * 尊重的最后手段:页面显式处理触摸移动后,滚动识别器不会获胜,也就不会接管。\n */\n function handleTouchMove(e: Event) {\n if (e.cancelable) e.preventDefault()\n }\n\n /**\n * 沿 pointerdown 的 composedPath 在每个 Element/ShadowRoot 挂非被动 touchmove\n * 监听。TouchEvent 不像 PointerEvent 那样可组合:非 composed 的 touchmove 只在\n * 落点所在的 shadow tree 内传播(连 host 都不经过),所以单一手势元素上的监听\n * 对 segmented-trigger 这类深层 shadow 内容完全失效。composedPath 覆盖落点的\n * 全部 shadow tree 链,无论 touchmove 在哪一层传播,路径上必有我们挂载的节点\n * 能收到事件并 preventDefault。document/window 不在挂载范围:touchmove 到不了\n * 它们,挂上也只是死代码。\n */\n function attachTouchGuards(e: PointerEvent) {\n const targets = new Set<EventTarget>()\n const path = typeof e.composedPath === 'function' ? e.composedPath() : []\n for (const node of path) {\n const type = (node as Node).nodeType\n if (type === Node.ELEMENT_NODE || type === Node.DOCUMENT_FRAGMENT_NODE) {\n node.addEventListener('touchmove', handleTouchMove, { capture: true, passive: false })\n targets.add(node)\n }\n }\n touchGuardTargets = targets\n }\n\n function detachTouchGuards() {\n if (!touchGuardTargets) return\n for (const node of touchGuardTargets) {\n node.removeEventListener('touchmove', handleTouchMove, true)\n }\n touchGuardTargets = null\n }\n\n function estimateVelocity(): { vx: number; vy: number; v: number } {\n if (samples.length < 2) return { vx: 0, vy: 0, v: 0 }\n const first = samples[0]\n const last = samples[samples.length - 1]\n const dt = last.t - first.t\n // 采样跨度不足 2ms(同帧/同毫秒的合成或极端快速事件)时不可信,置 0;\n // 2-8ms 的超短轻扫仍按真实跨度上报速度,避免「快速轻扫」被误判为无速度\n // 而回弹(旧守卫 <8ms 会把真实超短轻扫的速度清零)。\n if (dt < 2) return { vx: 0, vy: 0, v: 0 }\n const vx = ((last.x - first.x) / dt) * 1000\n const vy = ((last.y - first.y) / dt) * 1000\n let v = 0\n if (axis === 'x') v = vx\n else if (axis === 'y') v = vy\n else v = Math.hypot(vx, vy)\n return { vx, vy, v }\n }\n\n function detachWindowListeners() {\n window.removeEventListener('pointermove', handlePointerMove, true)\n window.removeEventListener('pointerup', handlePointerUp, true)\n window.removeEventListener('pointercancel', handlePointerCancel, true)\n window.removeEventListener('lostpointercapture', handleLostPointerCapture, true)\n }\n\n function attachWindowListeners() {\n window.addEventListener('pointermove', handlePointerMove, true)\n window.addEventListener('pointerup', handlePointerUp, true)\n window.addEventListener('pointercancel', handlePointerCancel, true)\n window.addEventListener('lostpointercapture', handleLostPointerCapture, true)\n }\n\n function detachTargetListeners() {\n if (!target) return\n target.removeEventListener('pointermove', handlePointerMove)\n target.removeEventListener('pointerup', handlePointerUp)\n target.removeEventListener('pointercancel', handlePointerCancel)\n target.removeEventListener('lostpointercapture', handleLostPointerCapture)\n }\n\n function attachTargetListeners() {\n if (!target) return\n target.addEventListener('pointermove', handlePointerMove)\n target.addEventListener('pointerup', handlePointerUp)\n target.addEventListener('pointercancel', handlePointerCancel)\n target.addEventListener('lostpointercapture', handleLostPointerCapture)\n }\n\n function resetState() {\n if (activePointerId !== null && target && typeof target.hasPointerCapture === 'function') {\n try {\n if (target.hasPointerCapture(activePointerId)) {\n target.releasePointerCapture(activePointerId)\n }\n } catch {\n // ignore\n }\n }\n activePointerId = null\n isThresholdPassed = threshold <= 0\n isCalibrated = false\n samples = []\n handledMoveEvent = null\n detachWindowListeners()\n detachTargetListeners()\n detachTouchGuards()\n }\n\n function handlePointerDown(e: PointerEvent) {\n if (activePointerId !== null) return\n // 真实触控环境下的多点触控副指针不启动手势(合成事件或鼠标环境缺省时不拦截)\n if (e.pointerType === 'touch' && e.isPrimary === false) return\n\n const startInfo: DragStartInfo = {\n pointerId: e.pointerId,\n clientX: e.clientX,\n clientY: e.clientY,\n timeStamp: e.timeStamp\n }\n\n if (onStart?.(startInfo, e) === false) return\n\n activePointerId = e.pointerId\n startX = e.clientX\n startY = e.clientY\n startTimeStamp = e.timeStamp\n isThresholdPassed = threshold <= 0\n isCalibrated = false\n samples = [{ t: e.timeStamp, x: e.clientX, y: e.clientY }]\n handledMoveEvent = null\n\n attachWindowListeners()\n attachTargetListeners()\n attachTouchGuards(e)\n\n // 仅在无意图死区时立即捕获指针;若存在死区,延迟至确认拖拽后捕获,避免干扰子元素原生点击\n if (isThresholdPassed) {\n try {\n target?.setPointerCapture?.(e.pointerId)\n } catch {\n // 忽略合成指针或不可捕获上下文(如测试环境)\n }\n }\n }\n\n function handlePointerMove(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n if (handledMoveEvent === e) return\n handledMoveEvent = e\n\n if (!isThresholdPassed) {\n const rawDeltaX = e.clientX - startX\n const rawDeltaY = e.clientY - startY\n const distance =\n axis === 'x' ? Math.abs(rawDeltaX) : axis === 'y' ? Math.abs(rawDeltaY) : Math.hypot(rawDeltaX, rawDeltaY)\n if (distance < threshold) return\n\n // 若限定了 X 轴且 Y 轴位移明显更大,视为滚动意图而非拖拽\n if (axis === 'x' && Math.abs(rawDeltaY) > Math.abs(rawDeltaX)) {\n cancel()\n return\n }\n if (axis === 'y' && Math.abs(rawDeltaX) > Math.abs(rawDeltaY)) {\n cancel()\n return\n }\n isThresholdPassed = true\n // 手势从「被识别」这一刻起算:有意图死区时,pointerdown 之后到越过死区之前的\n // 悬停不属于拖拽轨迹,计进 duration 会稀释整段手势的平均速度。\n startTimeStamp = e.timeStamp\n try {\n target?.setPointerCapture?.(e.pointerId)\n } catch {\n // 忽略合成指针\n }\n }\n\n // 首个有效 move 的基准校准:零点与时钟一起重置到当前位置。位移与时长都必须在这之后\n // 重新求(校准改写了零点),否则首个 move 会拿到校准前的旧位移。\n if (calibrateOnFirstMove && !isCalibrated) {\n isCalibrated = true\n startX = e.clientX\n startY = e.clientY\n startTimeStamp = e.timeStamp\n }\n\n const deltaX = e.clientX - startX\n const deltaY = e.clientY - startY\n\n // 确认拖拽后阻止默认滚动/文字选中\n if (e.cancelable) e.preventDefault()\n\n const now = e.timeStamp\n samples.push({ t: now, x: e.clientX, y: e.clientY })\n while (samples.length > 2 && now - samples[0].t > VELOCITY_WINDOW_MS) {\n samples.shift()\n }\n\n const { vx, vy, v } = estimateVelocity()\n\n const moveInfo: DragMoveInfo = {\n pointerId: e.pointerId,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX,\n deltaY,\n velocityX: vx,\n velocityY: vy,\n velocity: v,\n timeStamp: now\n }\n\n onMove?.(moveInfo, e)\n }\n\n function handlePointerUp(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n\n const now = e.timeStamp\n samples.push({ t: now, x: e.clientX, y: e.clientY })\n while (samples.length > 2 && now - samples[0].t > VELOCITY_WINDOW_MS) {\n samples.shift()\n }\n\n const deltaX = e.clientX - startX\n const deltaY = e.clientY - startY\n const { vx, vy, v } = estimateVelocity()\n\n const endInfo: DragEndInfo = {\n pointerId: e.pointerId,\n clientX: e.clientX,\n clientY: e.clientY,\n deltaX,\n deltaY,\n velocityX: vx,\n velocityY: vy,\n velocity: v,\n timeStamp: e.timeStamp,\n // 时间戳同源(都是事件 timeStamp),合成事件序列可能倒退或为 0,钳到非负。\n duration: Math.max(0, e.timeStamp - startTimeStamp)\n }\n\n const hadPassedThreshold = isThresholdPassed\n resetState()\n\n if (hadPassedThreshold) {\n onEnd?.(endInfo, e)\n } else {\n onTap?.(endInfo, e)\n onCancel?.(e)\n }\n }\n\n function handlePointerCancel(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n resetState()\n onCancel?.(e)\n }\n\n /**\n * lostpointercapture 不能当作 pointercancel:touch 指针在 pointerdown 时会被浏览器\n * 隐式捕获到命中元素(如 slider thumb、segmented-trigger 内容),组件随后\n * setPointerCapture(target) 把捕获转手到手势元素,命中元素先收到 lostpointercapture——\n * 这是正常转手而非取消,若直接走 cancel 会把刚启动的拖拽杀死(值只走一步即冻结)。\n * 只有手势元素自身意外丢失捕获(如元素被移出文档)才应取消。\n *\n * resetState 内 releasePointerCapture 产生的 lostpointercapture 会在 activePointerId\n * 置空之后异步到达,被 pointerId 守卫挡住,不会误伤。\n */\n function handleLostPointerCapture(e: PointerEvent) {\n if (activePointerId !== e.pointerId) return\n if (e.target !== target) return\n resetState()\n onCancel?.(e)\n }\n\n function cancel() {\n if (activePointerId === null) return\n resetState()\n onCancel?.()\n }\n\n if (isEvent) {\n handlePointerDown(targetOrEvent)\n } else if (target) {\n target.addEventListener('pointerdown', handlePointerDown)\n target.addEventListener('pointermove', handlePointerMove)\n target.addEventListener('pointerup', handlePointerUp)\n target.addEventListener('pointercancel', handlePointerCancel)\n }\n\n return {\n isDragging: () => activePointerId !== null,\n cancel,\n destroy: () => {\n cancel()\n if (!isEvent && target) {\n target.removeEventListener('pointerdown', handlePointerDown)\n target.removeEventListener('pointermove', handlePointerMove)\n target.removeEventListener('pointerup', handlePointerUp)\n target.removeEventListener('pointercancel', handlePointerCancel)\n }\n }\n }\n}\n"],"mappings":"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"}
@@ -1,20 +1,8 @@
1
1
  //#region src/shared/gesture/physics.ts
2
- var e = {
3
- close: {
4
- stiffness: 260,
5
- damping: 34,
6
- maxSamples: 19
7
- },
8
- rebound: {
9
- stiffness: 220,
10
- damping: 22,
11
- maxSamples: 29
12
- }
13
- };
14
- function t(e, t, n) {
2
+ function e(e, t, n) {
15
3
  return Math.min(n, Math.max(t, e));
16
4
  }
17
- function n(e, t) {
5
+ function t(e, t) {
18
6
  if (t.length === 0) return -1;
19
7
  let n = Infinity, r = 0;
20
8
  for (let i = 0; i < t.length; i++) {
@@ -23,23 +11,13 @@ function n(e, t) {
23
11
  }
24
12
  return r;
25
13
  }
26
- function r(e, n, r) {
27
- return r <= n ? 0 : (t(e, n, r) - n) / (r - n);
28
- }
29
- function i(e, t, n = .15) {
30
- if (e >= 0) return e;
31
- let r = e * n;
32
- return Math.max(r, -t);
14
+ function n(t, n, r) {
15
+ return r <= n ? 0 : (e(t, n, r) - n) / (r - n);
33
16
  }
34
- function a(e, t, n, r, i = 16) {
35
- let a = i / 2e3, o = e, s = n, c = [o];
36
- for (let e = 0; e < r.maxSamples * 2; e++) {
37
- let n = -r.stiffness * (o - t) - r.damping * s;
38
- if (s += n * a, o += s * a, e % 2 == 1 && c.push(o), Math.abs(o - t) < .5 && Math.abs(s) < 40) break;
39
- }
40
- return c.push(t), c;
17
+ function r(e) {
18
+ return e >= 0 ? e : Math.sign(e) * Math.abs(e) ** .5;
41
19
  }
42
20
  //#endregion
43
- export { e as SPRING_PRESETS, t as clamp, r as normalizeProgress, i as rubberband, n as snapToNearest, a as springOffsets };
21
+ export { e as clamp, r as dampOverscroll, n as normalizeProgress, t as snapToNearest };
44
22
 
45
23
  //# sourceMappingURL=physics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"physics.js","names":[],"sources":["../../../src/shared/gesture/physics.ts"],"sourcesContent":["/**\n * 物理运动与数学辅助函数(阻尼、钳制与半隐式欧拉弹簧轨迹生成)\n */\n\nexport interface SpringParams {\n stiffness: number\n damping: number\n maxSamples: number\n}\n\nexport const SPRING_PRESETS = {\n close: { stiffness: 260, damping: 34, maxSamples: 19 },\n rebound: { stiffness: 220, damping: 22, maxSamples: 29 }\n} as const satisfies Record<string, SpringParams>\n\n/**\n * 钳制数值在 [min, max] 范围内\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\n/**\n * 寻找与目标值距离最近的采样点索引。\n * @param value 目标数值\n * @param points 采样点数组\n * @returns 最近采样点的下标(空数组返回 -1)\n */\nexport function snapToNearest(value: number, points: number[]): number {\n if (points.length === 0) return -1\n let minDistance = Infinity\n let nearestIndex = 0\n for (let i = 0; i < points.length; i++) {\n const dist = Math.abs(value - points[i])\n if (dist < minDistance) {\n minDistance = dist\n nearestIndex = i\n }\n }\n return nearestIndex\n}\n\n/**\n * 将数值钳制并归一化到 [0, 1] 区间(0 对应 min,1 对应 max)。\n * @param value 输入值\n * @param min 最小值\n * @param max 最大值\n */\nexport function normalizeProgress(value: number, min: number, max: number): number {\n if (max <= min) return 0\n const clamped = clamp(value, min, max)\n return (clamped - min) / (max - min)\n}\n\n/**\n * 阻尼橡皮筋函数:超出边界部分施加衰减阻尼\n * @param offset 当前位移(正数正常,负数为拉伸超出边界)\n * @param maxDistance 最大拉伸上限(绝对值)\n * @param factor 线性衰减因子,默认 0.15\n */\nexport function rubberband(offset: number, maxDistance: number, factor = 0.15): number {\n if (offset >= 0) return offset\n const resisted = offset * factor\n return Math.max(resisted, -maxDistance)\n}\n\n/**\n * 半隐式欧拉积分弹簧轨迹,返回均匀时间间隔的位置采样(末尾附加精确终点)。\n * @param from 起始位置\n * @param to 目标位置\n * @param velocity 初始速度(px/s)\n * @param spring 弹簧刚度与阻尼参数\n * @param sampleMs 采样时间间隔(毫秒,默认 16ms 对应 60fps)\n */\nexport function springOffsets(\n from: number,\n to: number,\n velocity: number,\n spring: SpringParams,\n sampleMs = 16\n): number[] {\n const dt = sampleMs / 2000\n let x = from\n let v = velocity\n const samples = [x]\n for (let i = 0; i < spring.maxSamples * 2; i++) {\n const acceleration = -spring.stiffness * (x - to) - spring.damping * v\n v += acceleration * dt\n x += v * dt\n if (i % 2 === 1) samples.push(x)\n if (Math.abs(x - to) < 0.5 && Math.abs(v) < 40) break\n }\n samples.push(to)\n return samples\n}\n"],"mappings":";AAUA,IAAa,IAAiB;CAC5B,OAAO;EAAE,WAAW;EAAK,SAAS;EAAI,YAAY;CAAG;CACrD,SAAS;EAAE,WAAW;EAAK,SAAS;EAAI,YAAY;CAAG;AACzD;AAKA,SAAgB,EAAM,GAAe,GAAa,GAAqB;CACrE,OAAO,KAAK,IAAI,GAAK,KAAK,IAAI,GAAK,CAAK,CAAC;AAC3C;AAQA,SAAgB,EAAc,GAAe,GAA0B;CACrE,IAAI,EAAO,WAAW,GAAG,OAAO;CAChC,IAAI,IAAc,UACd,IAAe;CACnB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK;EACtC,IAAM,IAAO,KAAK,IAAI,IAAQ,EAAO,EAAE;EACvC,AAAI,IAAO,MACT,IAAc,GACd,IAAe;CAEnB;CACA,OAAO;AACT;AAQA,SAAgB,EAAkB,GAAe,GAAa,GAAqB;CAGjF,OAFI,KAAO,IAAY,KACP,EAAM,GAAO,GAAK,CAC1B,IAAU,MAAQ,IAAM;AAClC;AAQA,SAAgB,EAAW,GAAgB,GAAqB,IAAS,KAAc;CACrF,IAAI,KAAU,GAAG,OAAO;CACxB,IAAM,IAAW,IAAS;CAC1B,OAAO,KAAK,IAAI,GAAU,CAAC,CAAW;AACxC;AAUA,SAAgB,EACd,GACA,GACA,GACA,GACA,IAAW,IACD;CACV,IAAM,IAAK,IAAW,KAClB,IAAI,GACJ,IAAI,GACF,IAAU,CAAC,CAAC;CAClB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,aAAa,GAAG,KAAK;EAC9C,IAAM,IAAe,CAAC,EAAO,aAAa,IAAI,KAAM,EAAO,UAAU;EAIrE,IAHA,KAAK,IAAe,GACpB,KAAK,IAAI,GACL,IAAI,KAAM,KAAG,EAAQ,KAAK,CAAC,GAC3B,KAAK,IAAI,IAAI,CAAE,IAAI,MAAO,KAAK,IAAI,CAAC,IAAI,IAAI;CAClD;CAEA,OADA,EAAQ,KAAK,CAAE,GACR;AACT"}
1
+ {"version":3,"file":"physics.js","names":[],"sources":["../../../src/shared/gesture/physics.ts"],"sourcesContent":["/**\n * 物理运动与数学辅助函数(阻尼、钳制与归一化)\n */\n\n/**\n * 钳制数值在 [min, max] 范围内\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value))\n}\n\n/**\n * 寻找与目标值距离最近的采样点索引。\n * @param value 目标数值\n * @param points 采样点数组\n * @returns 最近采样点的下标(空数组返回 -1)\n */\nexport function snapToNearest(value: number, points: number[]): number {\n if (points.length === 0) return -1\n let minDistance = Infinity\n let nearestIndex = 0\n for (let i = 0; i < points.length; i++) {\n const dist = Math.abs(value - points[i])\n if (dist < minDistance) {\n minDistance = dist\n nearestIndex = i\n }\n }\n return nearestIndex\n}\n\n/**\n * 将数值钳制并归一化到 [0, 1] 区间(0 对应 min,1 对应 max)。\n * @param value 输入值\n * @param min 最小值\n * @param max 最大值\n */\nexport function normalizeProgress(value: number, min: number, max: number): number {\n if (max <= min) return 0\n const clamped = clamp(value, min, max)\n return (clamped - min) / (max - min)\n}\n\n/**\n * 「不允许方向」的过冲阻尼。\n *\n * 允许方向原样 1:1 通过(由调用方保证传入的符号空间正确);反向按平方根压缩,压缩比\n * 随位移增大而下降、天然自限幅(400px 过冲只剩 20px),因此不需要额外的位移上限。\n *\n * 它阻尼的是**本次手势自零点起的增量**,调用方再把它叠加到抓取瞬间的基准位移上\n * (`base + damp(delta)`);直接阻尼总位移会把基准位移也一起压缩。\n * @param delta 自手势零点起、沿拖拽主轴的增量;正值 = 允许方向\n */\nexport function dampOverscroll(delta: number): number {\n if (delta >= 0) return delta\n return Math.sign(delta) * Math.abs(delta) ** 0.5\n}\n"],"mappings":";AAOA,SAAgB,EAAM,GAAe,GAAa,GAAqB;CACrE,OAAO,KAAK,IAAI,GAAK,KAAK,IAAI,GAAK,CAAK,CAAC;AAC3C;AAQA,SAAgB,EAAc,GAAe,GAA0B;CACrE,IAAI,EAAO,WAAW,GAAG,OAAO;CAChC,IAAI,IAAc,UACd,IAAe;CACnB,KAAK,IAAI,IAAI,GAAG,IAAI,EAAO,QAAQ,KAAK;EACtC,IAAM,IAAO,KAAK,IAAI,IAAQ,EAAO,EAAE;EACvC,AAAI,IAAO,MACT,IAAc,GACd,IAAe;CAEnB;CACA,OAAO;AACT;AAQA,SAAgB,EAAkB,GAAe,GAAa,GAAqB;CAGjF,OAFI,KAAO,IAAY,KACP,EAAM,GAAO,GAAK,CAC1B,IAAU,MAAQ,IAAM;AAClC;AAYA,SAAgB,EAAe,GAAuB;CAEpD,OADI,KAAS,IAAU,IAChB,KAAK,KAAK,CAAK,IAAI,KAAK,IAAI,CAAK,KAAK;AAC/C"}
@@ -44,10 +44,6 @@ function o(e, t) {
44
44
  };
45
45
  }
46
46
  function s(e, t) {
47
- if (t.key === "Escape") {
48
- e.closeDeepestOrAll(), t.preventDefault();
49
- return;
50
- }
51
47
  let r = e.getFocusedItem(t), i = r ? e.getLevelOf(r) : void 0;
52
48
  if (i === void 0) return;
53
49
  let a = e.getEnabledItems(i), o = r ? a.indexOf(r) : -1;
@@ -103,6 +99,9 @@ function u(t) {
103
99
  },
104
100
  disposeAll() {
105
101
  n.forEach((e) => t.dispose(e)), n.clear();
102
+ },
103
+ closing() {
104
+ return [...n.values()];
106
105
  }
107
106
  };
108
107
  }