@rdlabo/ionic-theme-ios27 1.2.0-3 → 1.2.0-4

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 (175) hide show
  1. package/README.md +15 -2
  2. package/dist/_virtual/_rolldown/runtime.js +13 -0
  3. package/dist/css/ionic-theme-ios27.css +1 -1
  4. package/dist/css/vertical-bars.css +1 -1
  5. package/dist/focus-controller/index.d.ts +4 -2
  6. package/dist/focus-controller/index.d.ts.map +1 -1
  7. package/dist/focus-controller/index.js +100 -57
  8. package/dist/focus-controller/index.js.map +1 -0
  9. package/dist/index.d.ts +10 -8
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +54 -60
  12. package/dist/index.js.map +1 -0
  13. package/dist/native/components/index.d.ts +13 -10
  14. package/dist/native/components/index.d.ts.map +1 -1
  15. package/dist/native/components/index.js +39 -41
  16. package/dist/native/components/index.js.map +1 -0
  17. package/dist/native/components/ion-back-button.d.ts +6 -1
  18. package/dist/native/components/ion-back-button.d.ts.map +1 -1
  19. package/dist/native/components/ion-back-button.js +19 -13
  20. package/dist/native/components/ion-back-button.js.map +1 -0
  21. package/dist/native/components/ion-button.d.ts +8 -2
  22. package/dist/native/components/ion-button.d.ts.map +1 -1
  23. package/dist/native/components/ion-button.js +22 -17
  24. package/dist/native/components/ion-button.js.map +1 -0
  25. package/dist/native/components/ion-buttons.d.ts +8 -2
  26. package/dist/native/components/ion-buttons.d.ts.map +1 -1
  27. package/dist/native/components/ion-buttons.js +29 -33
  28. package/dist/native/components/ion-buttons.js.map +1 -0
  29. package/dist/native/components/ion-fab.d.ts +6 -1
  30. package/dist/native/components/ion-fab.d.ts.map +1 -1
  31. package/dist/native/components/ion-fab.js +131 -151
  32. package/dist/native/components/ion-fab.js.map +1 -0
  33. package/dist/native/components/ion-menu-button.d.ts +8 -2
  34. package/dist/native/components/ion-menu-button.d.ts.map +1 -1
  35. package/dist/native/components/ion-menu-button.js +29 -19
  36. package/dist/native/components/ion-menu-button.js.map +1 -0
  37. package/dist/native/components/ion-segment.d.ts +6 -1
  38. package/dist/native/components/ion-segment.d.ts.map +1 -1
  39. package/dist/native/components/ion-segment.js +29 -23
  40. package/dist/native/components/ion-segment.js.map +1 -0
  41. package/dist/native/components/ion-tab-bar.d.ts +6 -1
  42. package/dist/native/components/ion-tab-bar.d.ts.map +1 -1
  43. package/dist/native/components/ion-tab-bar.js +49 -40
  44. package/dist/native/components/ion-tab-bar.js.map +1 -0
  45. package/dist/native/components/searchable-tabs.d.ts +17 -15
  46. package/dist/native/components/searchable-tabs.d.ts.map +1 -1
  47. package/dist/native/components/searchable-tabs.js +382 -434
  48. package/dist/native/components/searchable-tabs.js.map +1 -0
  49. package/dist/native/definitions.d.ts +118 -111
  50. package/dist/native/definitions.d.ts.map +1 -1
  51. package/dist/native/definitions.js +0 -1
  52. package/dist/native/foldable/fold-state.d.ts +5 -0
  53. package/dist/native/foldable/fold-state.d.ts.map +1 -0
  54. package/dist/native/foldable/fold-state.js +14 -0
  55. package/dist/native/foldable/fold-state.js.map +1 -0
  56. package/dist/native/foldable/types.d.ts +12 -0
  57. package/dist/native/foldable/types.d.ts.map +1 -0
  58. package/dist/native/foldable/types.js +0 -0
  59. package/dist/native/index.d.ts +6 -3
  60. package/dist/native/index.d.ts.map +1 -1
  61. package/dist/native/index.js +228 -204
  62. package/dist/native/index.js.map +1 -0
  63. package/dist/native/prehide.d.ts +4 -2
  64. package/dist/native/prehide.d.ts.map +1 -1
  65. package/dist/native/prehide.js +220 -242
  66. package/dist/native/prehide.js.map +1 -0
  67. package/dist/native/runtime.d.ts +3 -1
  68. package/dist/native/runtime.d.ts.map +1 -1
  69. package/dist/native/runtime.js +626 -730
  70. package/dist/native/runtime.js.map +1 -0
  71. package/dist/native/shared/candidate.d.ts +16 -11
  72. package/dist/native/shared/candidate.d.ts.map +1 -1
  73. package/dist/native/shared/candidate.js +96 -95
  74. package/dist/native/shared/candidate.js.map +1 -0
  75. package/dist/native/shared/crossfade.d.ts +6 -4
  76. package/dist/native/shared/crossfade.d.ts.map +1 -1
  77. package/dist/native/shared/crossfade.js +49 -45
  78. package/dist/native/shared/crossfade.js.map +1 -0
  79. package/dist/native/shared/dom.d.ts +7 -5
  80. package/dist/native/shared/dom.d.ts.map +1 -1
  81. package/dist/native/shared/dom.js +163 -211
  82. package/dist/native/shared/dom.js.map +1 -0
  83. package/dist/native/shared/events.d.ts +8 -6
  84. package/dist/native/shared/events.d.ts.map +1 -1
  85. package/dist/native/shared/events.js +12 -7
  86. package/dist/native/shared/events.js.map +1 -0
  87. package/dist/native/shared/icons.d.ts +4 -2
  88. package/dist/native/shared/icons.d.ts.map +1 -1
  89. package/dist/native/shared/icons.js +59 -53
  90. package/dist/native/shared/icons.js.map +1 -0
  91. package/dist/native/shared/modal.d.ts +3 -1
  92. package/dist/native/shared/modal.d.ts.map +1 -1
  93. package/dist/native/shared/modal.js +156 -139
  94. package/dist/native/shared/modal.js.map +1 -0
  95. package/dist/native/vertical-bars-web.d.ts +3 -1
  96. package/dist/native/vertical-bars-web.d.ts.map +1 -1
  97. package/dist/native/vertical-bars-web.js +332 -346
  98. package/dist/native/vertical-bars-web.js.map +1 -0
  99. package/dist/native-integration/index.d.ts +10 -8
  100. package/dist/native-integration/index.d.ts.map +1 -1
  101. package/dist/native-integration/index.js +102 -100
  102. package/dist/native-integration/index.js.map +1 -0
  103. package/dist/native-integration/transition.d.ts +3 -1
  104. package/dist/native-integration/transition.d.ts.map +1 -1
  105. package/dist/native-integration/transition.js +15 -6
  106. package/dist/native-integration/transition.js.map +1 -0
  107. package/dist/searchbar/index.d.ts +4 -2
  108. package/dist/searchbar/index.d.ts.map +1 -1
  109. package/dist/searchbar/index.js +64 -56
  110. package/dist/searchbar/index.js.map +1 -0
  111. package/dist/segment/index.d.ts +3 -1
  112. package/dist/segment/index.d.ts.map +1 -1
  113. package/dist/segment/index.js +465 -324
  114. package/dist/segment/index.js.map +1 -0
  115. package/dist/sheets-of-glass/animations.d.ts +13 -12
  116. package/dist/sheets-of-glass/animations.d.ts.map +1 -1
  117. package/dist/sheets-of-glass/animations.js +496 -306
  118. package/dist/sheets-of-glass/animations.js.map +1 -0
  119. package/dist/sheets-of-glass/index.d.ts +3 -1
  120. package/dist/sheets-of-glass/index.d.ts.map +1 -1
  121. package/dist/sheets-of-glass/index.js +294 -357
  122. package/dist/sheets-of-glass/index.js.map +1 -0
  123. package/dist/sheets-of-glass/interfaces.d.ts +11 -9
  124. package/dist/sheets-of-glass/interfaces.d.ts.map +1 -1
  125. package/dist/sheets-of-glass/interfaces.js +0 -1
  126. package/dist/tab-bar-searchable/animations/enter.d.ts +4 -2
  127. package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -1
  128. package/dist/tab-bar-searchable/animations/enter.js +50 -69
  129. package/dist/tab-bar-searchable/animations/enter.js.map +1 -0
  130. package/dist/tab-bar-searchable/animations/leave.d.ts +4 -2
  131. package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -1
  132. package/dist/tab-bar-searchable/animations/leave.js +48 -62
  133. package/dist/tab-bar-searchable/animations/leave.js.map +1 -0
  134. package/dist/tab-bar-searchable/index.d.ts +5 -3
  135. package/dist/tab-bar-searchable/index.d.ts.map +1 -1
  136. package/dist/tab-bar-searchable/index.js +123 -135
  137. package/dist/tab-bar-searchable/index.js.map +1 -0
  138. package/dist/transition/index.d.ts +9 -8
  139. package/dist/transition/index.d.ts.map +1 -1
  140. package/dist/transition/index.js +203 -192
  141. package/dist/transition/index.js.map +1 -0
  142. package/dist/transition/ios.transition.d.ts +7 -5
  143. package/dist/transition/ios.transition.d.ts.map +1 -1
  144. package/dist/transition/ios.transition.js +15 -11
  145. package/dist/transition/ios.transition.js.map +1 -0
  146. package/dist/utils.d.ts +10 -8
  147. package/dist/utils.d.ts.map +1 -1
  148. package/dist/utils.js +58 -66
  149. package/dist/utils.js.map +1 -0
  150. package/dist/vertical-bars.d.ts +5 -4
  151. package/dist/vertical-bars.js +4 -2
  152. package/docs/iphone-duo-with-original-theme.md +18 -29
  153. package/docs/iphone-duo.md +44 -204
  154. package/docs/native-ui-shell.md +10 -8
  155. package/docs/special-markup.md +2 -0
  156. package/docs/vertical-bars.md +283 -0
  157. package/ios/Sources/IonicNativeUIShellPlugin/Components/ShellVerticalBars.swift +34 -14
  158. package/ios/Sources/IonicNativeUIShellPlugin/Shared/ShellSnapshot.swift +2 -0
  159. package/ios/Tests/IonicNativeUIShellPluginTests/ShellSnapshotTests.swift +3 -0
  160. package/package.json +9 -6
  161. package/src/native/components/index.ts +3 -2
  162. package/src/native/components/ion-button.ts +3 -2
  163. package/src/native/components/ion-buttons.ts +4 -3
  164. package/src/native/components/ion-menu-button.ts +3 -2
  165. package/src/native/definitions.ts +22 -1
  166. package/src/native/foldable/fold-state.ts +9 -0
  167. package/src/native/foldable/types.ts +4 -0
  168. package/src/native/index.ts +17 -3
  169. package/src/native/prehide.ts +3 -1
  170. package/src/native/runtime.ts +2 -2
  171. package/src/native/shared/candidate.ts +39 -10
  172. package/src/native/shared/dom.ts +4 -4
  173. package/src/styles/vertical-bars.scss +4 -2
  174. package/src/vertical-bars.ts +9 -1
  175. package/dist/vertical-bars.d.ts.map +0 -1
@@ -1,329 +1,470 @@
1
- import { isNativeUIShell } from '../native-integration';
1
+ import { isNativeUIShell } from "../native-integration/index.js";
2
+ //#region src/segment/index.ts
2
3
  const selectionFrames = [
3
- [0, 0, 0, 0],
4
- [0.033, 0.08, 4, 3],
5
- [0.067, 0.27, 11, 7],
6
- [0.1, 0.47, 17, 10],
7
- [0.133, 0.64, 22, 11],
8
- [0.167, 0.76, 26, 10.5],
9
- [0.2, 0.84, 29.5, 9.5],
10
- [0.267, 0.935, 24, 7.5],
11
- [0.333, 0.994, 7.5, 3.5],
12
- [0.4, 1.022, -0.6, 2.9],
13
- [0.5, 1.024, -3.3, 2.5],
14
- [0.6, 1.015, -2, 1.5],
15
- [0.7, 1.002, -0.5, 0.4],
16
- [0.8, 1, 0, 0],
4
+ [
5
+ 0,
6
+ 0,
7
+ 0,
8
+ 0
9
+ ],
10
+ [
11
+ .033,
12
+ .08,
13
+ 4,
14
+ 3
15
+ ],
16
+ [
17
+ .067,
18
+ .27,
19
+ 11,
20
+ 7
21
+ ],
22
+ [
23
+ .1,
24
+ .47,
25
+ 17,
26
+ 10
27
+ ],
28
+ [
29
+ .133,
30
+ .64,
31
+ 22,
32
+ 11
33
+ ],
34
+ [
35
+ .167,
36
+ .76,
37
+ 26,
38
+ 10.5
39
+ ],
40
+ [
41
+ .2,
42
+ .84,
43
+ 29.5,
44
+ 9.5
45
+ ],
46
+ [
47
+ .267,
48
+ .935,
49
+ 24,
50
+ 7.5
51
+ ],
52
+ [
53
+ .333,
54
+ .994,
55
+ 7.5,
56
+ 3.5
57
+ ],
58
+ [
59
+ .4,
60
+ 1.022,
61
+ -.6,
62
+ 2.9
63
+ ],
64
+ [
65
+ .5,
66
+ 1.024,
67
+ -3.3,
68
+ 2.5
69
+ ],
70
+ [
71
+ .6,
72
+ 1.015,
73
+ -2,
74
+ 1.5
75
+ ],
76
+ [
77
+ .7,
78
+ 1.002,
79
+ -.5,
80
+ .4
81
+ ],
82
+ [
83
+ .8,
84
+ 1,
85
+ 0,
86
+ 0
87
+ ]
17
88
  ];
18
89
  const releaseFrames = [
19
- [0, 1, 1],
20
- [0.033, 1.01, 0.81],
21
- [0.067, 0.7, 0.205],
22
- [0.1, 0.51, -0.042],
23
- [0.133, 0.43, -0.101],
24
- [0.167, 0.189, -0.075],
25
- [0.2, 0.043, 0.027],
26
- [0.267, -0.154, 0.196],
27
- [0.333, -0.183, 0.236],
28
- [0.4, -0.123, 0.159],
29
- [0.5, -0.038, 0.05],
30
- [0.6, 0.005, -0.007],
31
- [0.7, 0.008, -0.01],
32
- [0.8, 0, 0],
90
+ [
91
+ 0,
92
+ 1,
93
+ 1
94
+ ],
95
+ [
96
+ .033,
97
+ 1.01,
98
+ .81
99
+ ],
100
+ [
101
+ .067,
102
+ .7,
103
+ .205
104
+ ],
105
+ [
106
+ .1,
107
+ .51,
108
+ -.042
109
+ ],
110
+ [
111
+ .133,
112
+ .43,
113
+ -.101
114
+ ],
115
+ [
116
+ .167,
117
+ .189,
118
+ -.075
119
+ ],
120
+ [
121
+ .2,
122
+ .043,
123
+ .027
124
+ ],
125
+ [
126
+ .267,
127
+ -.154,
128
+ .196
129
+ ],
130
+ [
131
+ .333,
132
+ -.183,
133
+ .236
134
+ ],
135
+ [
136
+ .4,
137
+ -.123,
138
+ .159
139
+ ],
140
+ [
141
+ .5,
142
+ -.038,
143
+ .05
144
+ ],
145
+ [
146
+ .6,
147
+ .005,
148
+ -.007
149
+ ],
150
+ [
151
+ .7,
152
+ .008,
153
+ -.01
154
+ ],
155
+ [
156
+ .8,
157
+ 0,
158
+ 0
159
+ ]
33
160
  ];
34
- export const registerSegmentEffect = (targetElement) => {
35
- const segment = targetElement;
36
- const doc = segment.ownerDocument;
37
- const win = doc.defaultView;
38
- if (!segment.classList.contains('ios') || !win || segment.matches('.ios27-enable-gesture, .ios-theme-disabled, .ios26-disabled'))
39
- return undefined;
40
- const reducedMotion = win.matchMedia('(prefers-reduced-motion: reduce)');
41
- if (reducedMotion.matches)
42
- return undefined;
43
- const lens = doc.createElement('div');
44
- lens.className = 'ios27-segment-lens';
45
- lens.setAttribute('aria-hidden', 'true');
46
- const edge = doc.createElement('div');
47
- edge.className = 'ios27-segment-edge';
48
- lens.append(edge);
49
- segment.append(lens);
50
- segment.classList.add('ios27-enable-gesture');
51
- const listeners = new AbortController();
52
- let animation;
53
- let containerRelease;
54
- let handoff = [];
55
- let destroyed = false;
56
- let pendingEnd = 0;
57
- let surfaceWidth = 1;
58
- let surfaceColor = 'transparent';
59
- let pointer;
60
- const buttons = () => Array.from(segment.querySelectorAll('ion-segment-button'));
61
- const selected = () => buttons().find((button) => button.value === segment.value);
62
- const rect = (button) => {
63
- const outer = segment.getBoundingClientRect();
64
- const box = button.getBoundingClientRect();
65
- const sx = outer.width / segment.offsetWidth || 1;
66
- const sy = outer.height / segment.offsetHeight || 1;
67
- return {
68
- x: (box.left + box.width / 2 - outer.left) / sx + segment.scrollLeft,
69
- y: (box.top + box.height / 2 - outer.top) / sy,
70
- width: Math.max(1, button.clientWidth - 4),
71
- height: button.clientHeight,
72
- };
73
- };
74
- const currentRect = () => {
75
- const outer = segment.getBoundingClientRect();
76
- const box = lens.getBoundingClientRect();
77
- const sx = outer.width / segment.offsetWidth || 1;
78
- const sy = outer.height / segment.offsetHeight || 1;
79
- return {
80
- x: (box.left + box.width / 2 - outer.left) / sx + segment.scrollLeft,
81
- y: (box.top + box.height / 2 - outer.top) / sy,
82
- width: box.width / sx,
83
- height: box.height / sy,
84
- };
85
- };
86
- const frame = (box) => ({
87
- transform: `translate3d(${box.x - box.width / 2}px, ${box.y - box.height / 2}px, 0)`,
88
- width: `${box.width}px`,
89
- height: `${box.height}px`,
90
- backgroundColor: `color-mix(in srgb, ${surfaceColor} ${100 - Math.max(0, Math.min(1, (box.width - surfaceWidth) / 24)) * 90}%, transparent)`,
91
- });
92
- const hide = () => {
93
- animation?.cancel();
94
- animation = undefined;
95
- lens.hidden = true;
96
- segment.classList.remove('ios27-animated');
97
- handoff.forEach((effect) => effect.cancel());
98
- handoff = [];
99
- };
100
- lens.hidden = true;
101
- const play = (frames, duration, hold = false, startTime = win.performance.now()) => {
102
- animation?.cancel();
103
- handoff.forEach((effect) => effect.cancel());
104
- handoff = [];
105
- const first = frames[0];
106
- if (first) {
107
- lens.style.transform = String(first['transform']);
108
- lens.style.width = String(first['width']);
109
- lens.style.height = String(first['height']);
110
- lens.style.backgroundColor = String(first['backgroundColor']);
111
- }
112
- segment.classList.add('ios27-animated');
113
- lens.hidden = false;
114
- const running = lens.animate(frames, { duration, easing: 'linear', fill: 'forwards' });
115
- running.startTime = startTime;
116
- animation = running;
117
- const restingWidth = hold ? surfaceWidth : parseFloat(String(frames[frames.length - 1]['width']));
118
- handoff = [
119
- edge.animate(frames.map((sample) => ({
120
- offset: sample.offset,
121
- opacity: Math.max(0, Math.min(1, (parseFloat(String(sample['width'])) - (surfaceWidth + (restingWidth - surfaceWidth) * (sample.offset ?? 0))) / 24)),
122
- })), { duration, fill: 'forwards' }),
123
- ];
124
- const indicator = selected()?.shadowRoot?.querySelector('[part="indicator"]');
125
- if (!hold && indicator) {
126
- const offset = Math.max(0, 1 - 200 / duration);
127
- handoff.push(lens.animate([{ opacity: 1 }, { opacity: 1, offset }, { opacity: 0 }], { duration, fill: 'forwards' }), indicator.animate([{ opacity: 0 }, { opacity: 0, offset }, { opacity: 1 }], { duration, fill: 'forwards' }));
128
- }
129
- handoff.forEach((effect) => (effect.startTime = startTime));
130
- if (!hold)
131
- void running.finished.then(() => {
132
- if (animation === running)
133
- hide();
134
- }, () => { });
135
- };
136
- const settle = (from, to, changed, startTime) => {
137
- const toolbar = segment.classList.contains('in-toolbar') && !segment.classList.contains('segment-expand');
138
- const frames = selectionFrames.map(([time, position, width, height], index) => {
139
- const remaining = 1 - position;
140
- const box = {
141
- x: from.x + (to.x - from.x) * position,
142
- y: from.y + (to.y - from.y) * position,
143
- width: Math.max(1, to.width + (from.width - to.width) * (changed ? remaining : releaseFrames[index][1]) + (changed ? width : 0)),
144
- height: Math.max(1, to.height +
145
- (from.height - to.height) * (changed ? remaining : releaseFrames[index][2]) +
146
- (changed ? height * (toolbar ? 1 : 1.1) : 0)),
147
- };
148
- return { ...frame(box), offset: time / 0.8 };
149
- });
150
- play(frames, 800, false, startTime);
151
- };
152
- const releasePressed = (from, to) => {
153
- const samples = [
154
- [0, 1],
155
- [0.033, 0.78],
156
- [0.067, 0.49],
157
- [0.1, 0.28],
158
- [0.133, 0.157],
159
- [0.167, 0.089],
160
- [0.2, 0.054],
161
- [0.233, 0.034],
162
- [0.267, 0.009],
163
- [0.317, 0],
164
- [0.43, -0.013],
165
- [0.65, 0],
166
- ];
167
- play(samples.map(([time, remaining]) => ({
168
- ...frame({
169
- ...to,
170
- width: to.width + (from.width - to.width) * remaining,
171
- height: to.height + (from.height - to.height) * remaining,
172
- }),
173
- offset: time / 0.65,
174
- })), 650);
175
- };
176
- const down = (event) => {
177
- if (pointer || event.button !== 0 || segment.disabled || reducedMotion.matches || isNativeUIShell(segment))
178
- return;
179
- const button = event.target.closest('ion-segment-button');
180
- const old = selected();
181
- if (!button || button.disabled || !old)
182
- return;
183
- containerRelease?.cancel();
184
- win.cancelAnimationFrame(pendingEnd);
185
- const rest = rect(old);
186
- surfaceWidth = rest.width;
187
- const style = win.getComputedStyle(old);
188
- surfaceColor = style.getPropertyValue('--indicator-color') || 'transparent';
189
- lens.style.borderRadius = style.getPropertyValue('--border-radius');
190
- const interrupted = !!animation;
191
- const from = interrupted ? currentRect() : rest;
192
- hide();
193
- pointer = {
194
- id: event.pointerId,
195
- startX: event.clientX,
196
- startedAt: win.performance.now(),
197
- from: rest,
198
- button: old,
199
- selected: old === button,
200
- moved: false,
201
- };
202
- segment.classList.add('ios27-segment-pressed');
203
- if (pointer.selected) {
204
- const samples = [
205
- [0, 0],
206
- [0.033, 0.133],
207
- [0.067, 0.43],
208
- [0.1, 0.67],
209
- [0.133, 0.83],
210
- [0.167, 0.92],
211
- [0.2, 0.97],
212
- [0.233, 1],
213
- ];
214
- play(samples.map(([time, expansion]) => ({
215
- ...frame({
216
- ...from,
217
- width: from.width + (rest.width + 24 - from.width) * expansion,
218
- height: from.height + (rest.height + 16 - from.height) * expansion,
219
- }),
220
- offset: time / 0.233,
221
- })), 233, true);
222
- }
223
- else if (interrupted) {
224
- play([frame(from), frame(from)], 1, true);
225
- }
226
- };
227
- const move = (event) => {
228
- if (!pointer || pointer.id !== event.pointerId || !pointer.selected || !segment.swipeGesture || segment.scrollable)
229
- return;
230
- const dx = (event.clientX - pointer.startX) / (segment.getBoundingClientRect().width / segment.offsetWidth || 1);
231
- if (Math.abs(dx) < 3 && !pointer.moved)
232
- return;
233
- pointer.moved = true;
234
- const centers = buttons()
235
- .filter((button) => !button.disabled)
236
- .map((button) => rect(button).x);
237
- const box = {
238
- ...pointer.from,
239
- x: Math.max(Math.min(...centers), Math.min(Math.max(...centers), pointer.from.x + dx)),
240
- width: pointer.from.width + 24,
241
- height: pointer.from.height + 16,
242
- };
243
- animation?.cancel();
244
- animation = undefined;
245
- Object.assign(lens.style, frame(box));
246
- };
247
- const end = (event) => {
248
- if (!pointer || pointer.id !== event.pointerId)
249
- return;
250
- const releasedAt = win.performance.now();
251
- const state = pointer;
252
- const from = !lens.hidden ? currentRect() : state.from;
253
- const scale = new DOMMatrixReadOnly(win.getComputedStyle(segment).transform).a;
254
- pointer = undefined;
255
- segment.classList.remove('ios27-segment-pressed');
256
- if (segment.matches('.in-toolbar:not(.segment-expand)') &&
257
- releasedAt - state.startedAt < 200 &&
258
- !reducedMotion.matches &&
259
- event.type !== 'pointercancel') {
260
- const held = (releasedAt - state.startedAt) / 1000;
261
- const amplitude = parseFloat(win.getComputedStyle(segment).getPropertyValue('--ios27-segment-scale')) - 1;
262
- const velocity = amplitude * Math.exp(-12.6 * held) * (12.6 ** 2 / 15.7 + 15.7) * Math.sin(15.7 * held);
263
- const frames = Array.from({ length: 37 }, (_, index) => {
264
- const time = index / 60;
265
- const displacement = Math.exp(-10 * time) * ((scale - 1) * Math.cos(15 * time) + ((velocity + 10 * (scale - 1)) / 15) * Math.sin(15 * time));
266
- return { transform: `scale(${index === 36 ? 1 : 1 + displacement})` };
267
- });
268
- const returning = segment.animate(frames, { duration: 600 });
269
- containerRelease = returning;
270
- void returning.finished.then(() => {
271
- if (containerRelease === returning)
272
- containerRelease = undefined;
273
- }, () => { });
274
- }
275
- pendingEnd = win.requestAnimationFrame(() => {
276
- pendingEnd = 0;
277
- if (destroyed || pointer)
278
- return;
279
- const next = selected();
280
- if (!next || reducedMotion.matches || event.type === 'pointercancel') {
281
- hide();
282
- return;
283
- }
284
- const changed = next !== state.button;
285
- const nextStyle = win.getComputedStyle(next);
286
- surfaceColor = nextStyle.getPropertyValue('--indicator-color') || 'transparent';
287
- lens.style.borderRadius = nextStyle.getPropertyValue('--border-radius');
288
- if (!changed && !state.moved && animation) {
289
- const pressing = animation;
290
- void pressing.finished.then(() => {
291
- if (!destroyed && !pointer && animation === pressing)
292
- releasePressed(currentRect(), rect(next));
293
- }, () => { });
294
- }
295
- else if (changed || !lens.hidden)
296
- settle(from, rect(next), changed && !state.moved, releasedAt);
297
- });
298
- };
299
- const abort = () => {
300
- containerRelease?.cancel();
301
- containerRelease = undefined;
302
- win.cancelAnimationFrame(pendingEnd);
303
- pendingEnd = 0;
304
- pointer = undefined;
305
- segment.classList.remove('ios27-segment-pressed');
306
- hide();
307
- };
308
- segment.addEventListener('pointerdown', down, { capture: true, signal: listeners.signal });
309
- doc.addEventListener('pointermove', move, { capture: true, signal: listeners.signal });
310
- doc.addEventListener('pointerup', end, { capture: true, signal: listeners.signal });
311
- doc.addEventListener('pointercancel', end, { capture: true, signal: listeners.signal });
312
- win.addEventListener('blur', abort, { signal: listeners.signal });
313
- win.addEventListener('resize', abort, { signal: listeners.signal });
314
- segment.addEventListener('ionSelect', () => {
315
- if (!pointer && !pendingEnd)
316
- abort();
317
- }, { signal: listeners.signal });
318
- reducedMotion.addEventListener('change', abort, { signal: listeners.signal });
319
- segment.addEventListener('nativeUIShellChange', abort, { signal: listeners.signal });
320
- return {
321
- destroy: () => {
322
- destroyed = true;
323
- listeners.abort();
324
- abort();
325
- segment.classList.remove('ios27-enable-gesture');
326
- lens.remove();
327
- },
328
- };
161
+ /** Visual-only enhancement: Ionic retains ownership of selection, gestures and events. */
162
+ const registerSegmentEffect = (targetElement) => {
163
+ const segment = targetElement;
164
+ const doc = segment.ownerDocument;
165
+ const win = doc.defaultView;
166
+ if (!segment.classList.contains("ios") || !win || segment.matches(".ios27-enable-gesture, .ios-theme-disabled, .ios26-disabled")) return void 0;
167
+ const reducedMotion = win.matchMedia("(prefers-reduced-motion: reduce)");
168
+ if (reducedMotion.matches) return void 0;
169
+ const lens = doc.createElement("div");
170
+ lens.className = "ios27-segment-lens";
171
+ lens.setAttribute("aria-hidden", "true");
172
+ const edge = doc.createElement("div");
173
+ edge.className = "ios27-segment-edge";
174
+ lens.append(edge);
175
+ segment.append(lens);
176
+ segment.classList.add("ios27-enable-gesture");
177
+ const listeners = new AbortController();
178
+ let animation;
179
+ let containerRelease;
180
+ let handoff = [];
181
+ let destroyed = false;
182
+ let pendingEnd = 0;
183
+ let surfaceWidth = 1;
184
+ let surfaceColor = "transparent";
185
+ let pointer;
186
+ const buttons = () => Array.from(segment.querySelectorAll("ion-segment-button"));
187
+ const selected = () => buttons().find((button) => button.value === segment.value);
188
+ const rect = (button) => {
189
+ const outer = segment.getBoundingClientRect();
190
+ const box = button.getBoundingClientRect();
191
+ const sx = outer.width / segment.offsetWidth || 1;
192
+ const sy = outer.height / segment.offsetHeight || 1;
193
+ return {
194
+ x: (box.left + box.width / 2 - outer.left) / sx + segment.scrollLeft,
195
+ y: (box.top + box.height / 2 - outer.top) / sy,
196
+ width: Math.max(1, button.clientWidth - 4),
197
+ height: button.clientHeight
198
+ };
199
+ };
200
+ const currentRect = () => {
201
+ const outer = segment.getBoundingClientRect();
202
+ const box = lens.getBoundingClientRect();
203
+ const sx = outer.width / segment.offsetWidth || 1;
204
+ const sy = outer.height / segment.offsetHeight || 1;
205
+ return {
206
+ x: (box.left + box.width / 2 - outer.left) / sx + segment.scrollLeft,
207
+ y: (box.top + box.height / 2 - outer.top) / sy,
208
+ width: box.width / sx,
209
+ height: box.height / sy
210
+ };
211
+ };
212
+ const frame = (box) => ({
213
+ transform: `translate3d(${box.x - box.width / 2}px, ${box.y - box.height / 2}px, 0)`,
214
+ width: `${box.width}px`,
215
+ height: `${box.height}px`,
216
+ backgroundColor: `color-mix(in srgb, ${surfaceColor} ${100 - Math.max(0, Math.min(1, (box.width - surfaceWidth) / 24)) * 90}%, transparent)`
217
+ });
218
+ const hide = () => {
219
+ animation?.cancel();
220
+ animation = void 0;
221
+ lens.hidden = true;
222
+ segment.classList.remove("ios27-animated");
223
+ handoff.forEach((effect) => effect.cancel());
224
+ handoff = [];
225
+ };
226
+ lens.hidden = true;
227
+ const play = (frames, duration, hold = false, startTime = win.performance.now()) => {
228
+ animation?.cancel();
229
+ handoff.forEach((effect) => effect.cancel());
230
+ handoff = [];
231
+ const first = frames[0];
232
+ if (first) {
233
+ lens.style.transform = String(first["transform"]);
234
+ lens.style.width = String(first["width"]);
235
+ lens.style.height = String(first["height"]);
236
+ lens.style.backgroundColor = String(first["backgroundColor"]);
237
+ }
238
+ segment.classList.add("ios27-animated");
239
+ lens.hidden = false;
240
+ const running = lens.animate(frames, {
241
+ duration,
242
+ easing: "linear",
243
+ fill: "forwards"
244
+ });
245
+ running.startTime = startTime;
246
+ animation = running;
247
+ const restingWidth = hold ? surfaceWidth : parseFloat(String(frames[frames.length - 1]["width"]));
248
+ handoff = [edge.animate(frames.map((sample) => ({
249
+ offset: sample.offset,
250
+ opacity: Math.max(0, Math.min(1, (parseFloat(String(sample["width"])) - (surfaceWidth + (restingWidth - surfaceWidth) * (sample.offset ?? 0))) / 24))
251
+ })), {
252
+ duration,
253
+ fill: "forwards"
254
+ })];
255
+ const indicator = selected()?.shadowRoot?.querySelector("[part=\"indicator\"]");
256
+ if (!hold && indicator) {
257
+ const offset = Math.max(0, 1 - 200 / duration);
258
+ handoff.push(lens.animate([
259
+ { opacity: 1 },
260
+ {
261
+ opacity: 1,
262
+ offset
263
+ },
264
+ { opacity: 0 }
265
+ ], {
266
+ duration,
267
+ fill: "forwards"
268
+ }), indicator.animate([
269
+ { opacity: 0 },
270
+ {
271
+ opacity: 0,
272
+ offset
273
+ },
274
+ { opacity: 1 }
275
+ ], {
276
+ duration,
277
+ fill: "forwards"
278
+ }));
279
+ }
280
+ handoff.forEach((effect) => effect.startTime = startTime);
281
+ if (!hold) running.finished.then(() => {
282
+ if (animation === running) hide();
283
+ }, () => {});
284
+ };
285
+ const settle = (from, to, changed, startTime) => {
286
+ const toolbar = segment.classList.contains("in-toolbar") && !segment.classList.contains("segment-expand");
287
+ const frames = selectionFrames.map(([time, position, width, height], index) => {
288
+ const remaining = 1 - position;
289
+ const box = {
290
+ x: from.x + (to.x - from.x) * position,
291
+ y: from.y + (to.y - from.y) * position,
292
+ width: Math.max(1, to.width + (from.width - to.width) * (changed ? remaining : releaseFrames[index][1]) + (changed ? width : 0)),
293
+ height: Math.max(1, to.height + (from.height - to.height) * (changed ? remaining : releaseFrames[index][2]) + (changed ? height * (toolbar ? 1 : 1.1) : 0))
294
+ };
295
+ return {
296
+ ...frame(box),
297
+ offset: time / .8
298
+ };
299
+ });
300
+ play(frames, 800, false, startTime);
301
+ };
302
+ const releasePressed = (from, to) => {
303
+ play([
304
+ [0, 1],
305
+ [.033, .78],
306
+ [.067, .49],
307
+ [.1, .28],
308
+ [.133, .157],
309
+ [.167, .089],
310
+ [.2, .054],
311
+ [.233, .034],
312
+ [.267, .009],
313
+ [.317, 0],
314
+ [.43, -.013],
315
+ [.65, 0]
316
+ ].map(([time, remaining]) => ({
317
+ ...frame({
318
+ ...to,
319
+ width: to.width + (from.width - to.width) * remaining,
320
+ height: to.height + (from.height - to.height) * remaining
321
+ }),
322
+ offset: time / .65
323
+ })), 650);
324
+ };
325
+ const down = (event) => {
326
+ if (pointer || event.button !== 0 || segment.disabled || reducedMotion.matches || isNativeUIShell(segment)) return;
327
+ const button = event.target.closest("ion-segment-button");
328
+ const old = selected();
329
+ if (!button || button.disabled || !old) return;
330
+ containerRelease?.cancel();
331
+ win.cancelAnimationFrame(pendingEnd);
332
+ const rest = rect(old);
333
+ surfaceWidth = rest.width;
334
+ const style = win.getComputedStyle(old);
335
+ surfaceColor = style.getPropertyValue("--indicator-color") || "transparent";
336
+ lens.style.borderRadius = style.getPropertyValue("--border-radius");
337
+ const interrupted = !!animation;
338
+ const from = interrupted ? currentRect() : rest;
339
+ hide();
340
+ pointer = {
341
+ id: event.pointerId,
342
+ startX: event.clientX,
343
+ startedAt: win.performance.now(),
344
+ from: rest,
345
+ button: old,
346
+ selected: old === button,
347
+ moved: false
348
+ };
349
+ segment.classList.add("ios27-segment-pressed");
350
+ if (pointer.selected) play([
351
+ [0, 0],
352
+ [.033, .133],
353
+ [.067, .43],
354
+ [.1, .67],
355
+ [.133, .83],
356
+ [.167, .92],
357
+ [.2, .97],
358
+ [.233, 1]
359
+ ].map(([time, expansion]) => ({
360
+ ...frame({
361
+ ...from,
362
+ width: from.width + (rest.width + 24 - from.width) * expansion,
363
+ height: from.height + (rest.height + 16 - from.height) * expansion
364
+ }),
365
+ offset: time / .233
366
+ })), 233, true);
367
+ else if (interrupted) play([frame(from), frame(from)], 1, true);
368
+ };
369
+ const move = (event) => {
370
+ if (!pointer || pointer.id !== event.pointerId || !pointer.selected || !segment.swipeGesture || segment.scrollable) return;
371
+ const dx = (event.clientX - pointer.startX) / (segment.getBoundingClientRect().width / segment.offsetWidth || 1);
372
+ if (Math.abs(dx) < 3 && !pointer.moved) return;
373
+ pointer.moved = true;
374
+ const centers = buttons().filter((button) => !button.disabled).map((button) => rect(button).x);
375
+ const box = {
376
+ ...pointer.from,
377
+ x: Math.max(Math.min(...centers), Math.min(Math.max(...centers), pointer.from.x + dx)),
378
+ width: pointer.from.width + 24,
379
+ height: pointer.from.height + 16
380
+ };
381
+ animation?.cancel();
382
+ animation = void 0;
383
+ Object.assign(lens.style, frame(box));
384
+ };
385
+ const end = (event) => {
386
+ if (!pointer || pointer.id !== event.pointerId) return;
387
+ const releasedAt = win.performance.now();
388
+ const state = pointer;
389
+ const from = !lens.hidden ? currentRect() : state.from;
390
+ const scale = new DOMMatrixReadOnly(win.getComputedStyle(segment).transform).a;
391
+ pointer = void 0;
392
+ segment.classList.remove("ios27-segment-pressed");
393
+ if (segment.matches(".in-toolbar:not(.segment-expand)") && releasedAt - state.startedAt < 200 && !reducedMotion.matches && event.type !== "pointercancel") {
394
+ const held = (releasedAt - state.startedAt) / 1e3;
395
+ const velocity = (parseFloat(win.getComputedStyle(segment).getPropertyValue("--ios27-segment-scale")) - 1) * Math.exp(-12.6 * held) * (12.6 ** 2 / 15.7 + 15.7) * Math.sin(15.7 * held);
396
+ const frames = Array.from({ length: 37 }, (_, index) => {
397
+ const time = index / 60;
398
+ const displacement = Math.exp(-10 * time) * ((scale - 1) * Math.cos(15 * time) + (velocity + 10 * (scale - 1)) / 15 * Math.sin(15 * time));
399
+ return { transform: `scale(${index === 36 ? 1 : 1 + displacement})` };
400
+ });
401
+ const returning = segment.animate(frames, { duration: 600 });
402
+ containerRelease = returning;
403
+ returning.finished.then(() => {
404
+ if (containerRelease === returning) containerRelease = void 0;
405
+ }, () => {});
406
+ }
407
+ pendingEnd = win.requestAnimationFrame(() => {
408
+ pendingEnd = 0;
409
+ if (destroyed || pointer) return;
410
+ const next = selected();
411
+ if (!next || reducedMotion.matches || event.type === "pointercancel") {
412
+ hide();
413
+ return;
414
+ }
415
+ const changed = next !== state.button;
416
+ const nextStyle = win.getComputedStyle(next);
417
+ surfaceColor = nextStyle.getPropertyValue("--indicator-color") || "transparent";
418
+ lens.style.borderRadius = nextStyle.getPropertyValue("--border-radius");
419
+ if (!changed && !state.moved && animation) {
420
+ const pressing = animation;
421
+ pressing.finished.then(() => {
422
+ if (!destroyed && !pointer && animation === pressing) releasePressed(currentRect(), rect(next));
423
+ }, () => {});
424
+ } else if (changed || !lens.hidden) settle(from, rect(next), changed && !state.moved, releasedAt);
425
+ });
426
+ };
427
+ const abort = () => {
428
+ containerRelease?.cancel();
429
+ containerRelease = void 0;
430
+ win.cancelAnimationFrame(pendingEnd);
431
+ pendingEnd = 0;
432
+ pointer = void 0;
433
+ segment.classList.remove("ios27-segment-pressed");
434
+ hide();
435
+ };
436
+ segment.addEventListener("pointerdown", down, {
437
+ capture: true,
438
+ signal: listeners.signal
439
+ });
440
+ doc.addEventListener("pointermove", move, {
441
+ capture: true,
442
+ signal: listeners.signal
443
+ });
444
+ doc.addEventListener("pointerup", end, {
445
+ capture: true,
446
+ signal: listeners.signal
447
+ });
448
+ doc.addEventListener("pointercancel", end, {
449
+ capture: true,
450
+ signal: listeners.signal
451
+ });
452
+ win.addEventListener("blur", abort, { signal: listeners.signal });
453
+ win.addEventListener("resize", abort, { signal: listeners.signal });
454
+ segment.addEventListener("ionSelect", () => {
455
+ if (!pointer && !pendingEnd) abort();
456
+ }, { signal: listeners.signal });
457
+ reducedMotion.addEventListener("change", abort, { signal: listeners.signal });
458
+ segment.addEventListener("nativeUIShellChange", abort, { signal: listeners.signal });
459
+ return { destroy: () => {
460
+ destroyed = true;
461
+ listeners.abort();
462
+ abort();
463
+ segment.classList.remove("ios27-enable-gesture");
464
+ lens.remove();
465
+ } };
329
466
  };
467
+ //#endregion
468
+ export { registerSegmentEffect };
469
+
470
+ //# sourceMappingURL=index.js.map